Spaces:
Sleeping
Sleeping
| from typing import List, Optional | |
| from fastapi import APIRouter, Depends, HTTPException, status | |
| from sqlalchemy.orm import Session, joinedload | |
| from sqlalchemy import desc, asc, func | |
| from app.schemas.product import ProductListItem | |
| from app.db.base import get_db | |
| from app.models.home import HomeSection | |
| from app.models.product import Product, Category | |
| from app.schemas.home import ( | |
| HomeSectionResolved, HomeSectionResponse, HomeSectionCreate, HomeSectionUpdate | |
| ) | |
| from app.schemas.api_response import APIResponse | |
| from app.core.security import get_current_user | |
| from app.api.auth import get_current_admin_user | |
| from app.models.user import User | |
| router = APIRouter(prefix="/home-sections", tags=["Home Sections"]) | |
| async def list_home_sections(db: Session = Depends(get_db)): | |
| """ | |
| Public endpoint to get active home sections with resolved products. | |
| """ | |
| sections = db.query(HomeSection).filter( | |
| HomeSection.is_active == True | |
| ).order_by(HomeSection.display_order.asc()).all() | |
| resolved_sections = [] | |
| for section in sections: | |
| products = [] | |
| if section.section_type == "MANUAL" and section.selected_product_ids: | |
| # Fetch specific products by ID | |
| products = db.query(Product).filter( | |
| Product.id.in_(section.selected_product_ids), | |
| Product.is_active == True, | |
| Product.deleted_at == None | |
| ).options(joinedload(Product.images), joinedload(Product.category)).all() | |
| # Maintain the manual order | |
| id_map = {p.id: p for p in products} | |
| products = [id_map[pid] for pid in section.selected_product_ids if pid in id_map] | |
| elif section.section_type == "AUTOMATIC" and section.rule: | |
| rule = section.rule | |
| def get_products(rule_dict): | |
| query = db.query(Product).filter( | |
| Product.is_active == True, | |
| Product.deleted_at == None | |
| ).options(joinedload(Product.images), joinedload(Product.category)) | |
| # Apply filters from rule | |
| if "category_id" in rule_dict: | |
| cat_id = rule_dict["category_id"] | |
| # Get all subcategory IDs recursively | |
| sub_ids = db.query(Category.id).filter( | |
| (Category.id == cat_id) | (Category.parent_id == cat_id) | |
| ).all() | |
| # Flatten the list of tuples | |
| all_cat_ids = [r[0] for r in sub_ids] | |
| query = query.filter(Product.category_id.in_(all_cat_ids)) | |
| if "is_featured" in rule_dict: | |
| query = query.filter(Product.is_featured == rule_dict["is_featured"]) | |
| # Apply sorting | |
| sort_by = rule_dict.get("sort_by", "created_at") | |
| sort_order = rule_dict.get("sort_order", "desc") | |
| if sort_by == "price": | |
| query = query.order_by(asc(Product.price) if sort_order == "asc" else desc(Product.price)) | |
| elif sort_by == "rating": | |
| query = query.order_by(desc(Product.rating)) | |
| elif sort_by == "discount": | |
| query = query.filter(Product.compare_price > Product.price) | |
| query = query.order_by(desc((Product.compare_price - Product.price) / Product.compare_price)) | |
| else: | |
| query = query.order_by(desc(Product.created_at) if sort_order == "desc" else asc(Product.created_at)) | |
| limit = rule_dict.get("limit", 8) | |
| return query.limit(limit).all() | |
| products = get_products(rule) | |
| # Fallback: If no products found for a specific rule (like is_featured), try a broader query | |
| if not products: | |
| fallback_rule = rule.copy() | |
| if "is_featured" in fallback_rule: | |
| del fallback_rule["is_featured"] | |
| fallback_rule["sort_by"] = "rating" | |
| products = get_products(fallback_rule) | |
| elif "category_id" in fallback_rule: | |
| # If category is empty, we don't fallback to other categories, but maybe the user wants to see something? | |
| # For now, let's keep category strict but recursive. | |
| pass | |
| # Add primary image_url to each product for the ProductListItem schema | |
| for p in products: | |
| p.image_url = p.images[0].image_url if p.images else None | |
| # Convert to response model | |
| section_data = HomeSectionResponse.model_validate(section).model_dump() | |
| section_data["products"] = [ProductListItem.model_validate(p) for p in products] | |
| resolved_sections.append(section_data) | |
| return APIResponse(isSuccess=True, value=resolved_sections, statusCode=200) | |
| async def create_home_section( | |
| section_in: HomeSectionCreate, | |
| db: Session = Depends(get_db), | |
| current_user: User = Depends(get_current_admin_user) | |
| ): | |
| section = HomeSection(**section_in.dict()) | |
| db.add(section) | |
| db.commit() | |
| db.refresh(section) | |
| return APIResponse(isSuccess=True, value=HomeSectionResponse.model_validate(section), statusCode=201) | |
| async def admin_list_sections( | |
| db: Session = Depends(get_db), | |
| current_user: User = Depends(get_current_admin_user) | |
| ): | |
| sections = db.query(HomeSection).order_by(HomeSection.display_order.asc()).all() | |
| return APIResponse(isSuccess=True, value=[HomeSectionResponse.model_validate(s) for s in sections], statusCode=200) | |
| async def update_home_section( | |
| section_id: int, | |
| section_in: HomeSectionUpdate, | |
| db: Session = Depends(get_db), | |
| current_user: User = Depends(get_current_admin_user) | |
| ): | |
| section = db.query(HomeSection).filter(HomeSection.id == section_id).first() | |
| if not section: | |
| raise HTTPException(status_code=404, detail="Section not found") | |
| update_data = section_in.dict(exclude_unset=True) | |
| for field, value in update_data.items(): | |
| setattr(section, field, value) | |
| db.commit() | |
| db.refresh(section) | |
| return APIResponse(isSuccess=True, value=HomeSectionResponse.model_validate(section), statusCode=200) | |
| async def delete_home_section( | |
| section_id: int, | |
| db: Session = Depends(get_db), | |
| current_user: User = Depends(get_current_admin_user) | |
| ): | |
| section = db.query(HomeSection).filter(HomeSection.id == section_id).first() | |
| if not section: | |
| raise HTTPException(status_code=404, detail="Section not found") | |
| db.delete(section) | |
| db.commit() | |
| return APIResponse(isSuccess=True, value={"message": "Section deleted"}, statusCode=200) | |
| async def reorder_sections( | |
| orders: List[dict], # List of {"id": 1, "display_order": 0} | |
| db: Session = Depends(get_db), | |
| current_user: User = Depends(get_current_admin_user) | |
| ): | |
| for item in orders: | |
| db.query(HomeSection).filter(HomeSection.id == item["id"]).update( | |
| {"display_order": item["display_order"]} | |
| ) | |
| db.commit() | |
| return APIResponse(isSuccess=True, value={"message": "Order updated"}, statusCode=200) | |