"""Editorial plans and scheduled posts router. Manages editorial plans (posting schedules) and individual scheduled post entries. """ from datetime import datetime from fastapi import APIRouter, Depends, HTTPException, Query from sqlalchemy.orm import Session from ..auth import get_current_user from ..database import get_db from ..models import EditorialPlan, ScheduledPost, User from ..plan_limits import get_plan from ..schemas import ( EditorialPlanCreate, EditorialPlanResponse, EditorialPlanUpdate, ScheduledPostCreate, ScheduledPostResponse, ) router = APIRouter( prefix="/api/plans", tags=["plans"], ) # === Editorial Plans === @router.get("/", response_model=list[EditorialPlanResponse]) def list_plans( character_id: int | None = Query(None), db: Session = Depends(get_db), current_user: User = Depends(get_current_user), ): """List all editorial plans, optionally filtered by character.""" query = db.query(EditorialPlan).filter(EditorialPlan.user_id == current_user.id) if character_id is not None: query = query.filter(EditorialPlan.character_id == character_id) return query.order_by(EditorialPlan.created_at.desc()).all() @router.get("/scheduled", response_model=list[ScheduledPostResponse]) def list_all_scheduled_posts( platform: str | None = Query(None), status: str | None = Query(None), date_from: datetime | None = Query(None), date_after: datetime | None = Query(None), db: Session = Depends(get_db), current_user: User = Depends(get_current_user), ): """Get all scheduled posts across all plans with optional filters.""" # Join with plans to filter by user user_plan_ids = [ p.id for p in db.query(EditorialPlan.id).filter(EditorialPlan.user_id == current_user.id).all() ] query = db.query(ScheduledPost).filter( (ScheduledPost.plan_id.in_(user_plan_ids)) | (ScheduledPost.plan_id == None) ) if platform is not None: query = query.filter(ScheduledPost.platform == platform) if status is not None: query = query.filter(ScheduledPost.status == status) if date_from is not None: query = query.filter(ScheduledPost.scheduled_at >= date_from) if date_after is not None: query = query.filter(ScheduledPost.scheduled_at <= date_after) return query.order_by(ScheduledPost.scheduled_at).all() @router.get("/{plan_id}", response_model=EditorialPlanResponse) def get_plan( plan_id: int, db: Session = Depends(get_db), current_user: User = Depends(get_current_user), ): """Get a single editorial plan by ID.""" plan = ( db.query(EditorialPlan) .filter(EditorialPlan.id == plan_id, EditorialPlan.user_id == current_user.id) .first() ) if not plan: raise HTTPException(status_code=404, detail="Editorial plan not found") return plan @router.post("/", response_model=EditorialPlanResponse, status_code=201) def create_plan( data: EditorialPlanCreate, db: Session = Depends(get_db), current_user: User = Depends(get_current_user), ): """Create a new editorial plan.""" plan_limits = get_plan(current_user) if not plan_limits.get("auto_plans"): raise HTTPException( status_code=403, detail={"message": "Piani automatici disponibili solo con Pro.", "upgrade_required": True}, ) plan = EditorialPlan(**data.model_dump()) plan.user_id = current_user.id db.add(plan) db.commit() db.refresh(plan) return plan @router.put("/{plan_id}", response_model=EditorialPlanResponse) def update_plan( plan_id: int, data: EditorialPlanUpdate, db: Session = Depends(get_db), current_user: User = Depends(get_current_user), ): """Update an editorial plan.""" plan = ( db.query(EditorialPlan) .filter(EditorialPlan.id == plan_id, EditorialPlan.user_id == current_user.id) .first() ) if not plan: raise HTTPException(status_code=404, detail="Editorial plan not found") update_data = data.model_dump(exclude_unset=True) for key, value in update_data.items(): setattr(plan, key, value) plan.updated_at = datetime.utcnow() db.commit() db.refresh(plan) return plan @router.delete("/{plan_id}", status_code=204) def delete_plan( plan_id: int, db: Session = Depends(get_db), current_user: User = Depends(get_current_user), ): """Delete an editorial plan and its associated scheduled posts.""" plan = ( db.query(EditorialPlan) .filter(EditorialPlan.id == plan_id, EditorialPlan.user_id == current_user.id) .first() ) if not plan: raise HTTPException(status_code=404, detail="Editorial plan not found") db.query(ScheduledPost).filter(ScheduledPost.plan_id == plan_id).delete() db.delete(plan) db.commit() @router.post("/{plan_id}/toggle", response_model=EditorialPlanResponse) def toggle_plan( plan_id: int, db: Session = Depends(get_db), current_user: User = Depends(get_current_user), ): """Toggle the is_active status of an editorial plan.""" plan = ( db.query(EditorialPlan) .filter(EditorialPlan.id == plan_id, EditorialPlan.user_id == current_user.id) .first() ) if not plan: raise HTTPException(status_code=404, detail="Editorial plan not found") plan.is_active = not plan.is_active plan.updated_at = datetime.utcnow() db.commit() db.refresh(plan) return plan # === Scheduled Posts === @router.get("/{plan_id}/schedule", response_model=list[ScheduledPostResponse]) def get_plan_scheduled_posts( plan_id: int, db: Session = Depends(get_db), current_user: User = Depends(get_current_user), ): """Get all scheduled posts for a specific plan.""" plan = ( db.query(EditorialPlan) .filter(EditorialPlan.id == plan_id, EditorialPlan.user_id == current_user.id) .first() ) if not plan: raise HTTPException(status_code=404, detail="Editorial plan not found") return ( db.query(ScheduledPost) .filter(ScheduledPost.plan_id == plan_id) .order_by(ScheduledPost.scheduled_at) .all() ) @router.post("/schedule", response_model=ScheduledPostResponse, status_code=201) def schedule_post( data: ScheduledPostCreate, db: Session = Depends(get_db), current_user: User = Depends(get_current_user), ): """Manually schedule a post.""" scheduled = ScheduledPost(**data.model_dump()) db.add(scheduled) db.commit() db.refresh(scheduled) return scheduled