hisham-cmd's picture
Full Sync: Explicit Commit Operations (Pure & Optimized)
b2be963 verified
Raw
History Blame Contribute Delete
7.73 kB
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"])
@router.get("", response_model=APIResponse)
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)
@router.post("/admin", response_model=APIResponse)
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)
@router.get("/admin", response_model=APIResponse)
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)
@router.put("/admin/{section_id}", response_model=APIResponse)
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)
@router.delete("/admin/{section_id}", response_model=APIResponse)
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)
@router.post("/admin/reorder", response_model=APIResponse)
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)