Spaces:
Sleeping
Sleeping
| from datetime import datetime | |
| from typing import Optional | |
| from fastapi import APIRouter, Depends, HTTPException | |
| from pydantic import BaseModel, Field | |
| from sqlalchemy.orm import Session | |
| from app.database.database import get_db | |
| from app.database.models import Goal, User | |
| router = APIRouter(prefix="/api/goals", tags=["Goals"]) | |
| def _resolve_user(db: Session, user_id: Optional[str]) -> str: | |
| if user_id: | |
| return user_id | |
| user = db.query(User).first() | |
| if not user: | |
| raise HTTPException(status_code=404, detail="No users found. Seed the database first.") | |
| return user.id | |
| class ContributeRequest(BaseModel): | |
| amount: float = Field(gt=0) | |
| def list_goals(user_id: Optional[str] = None, db: Session = Depends(get_db)): | |
| uid = _resolve_user(db, user_id) | |
| goals = db.query(Goal).filter(Goal.user_id == uid).order_by(Goal.target_date.asc()).all() | |
| items = [] | |
| total_target = 0.0 | |
| total_saved = 0.0 | |
| for g in goals: | |
| progress = min(100.0, (g.current_amount / g.target_amount * 100) if g.target_amount > 0 else 0) | |
| total_target += g.target_amount | |
| total_saved += g.current_amount | |
| days_left = None | |
| if g.target_date: | |
| days_left = max(0, (g.target_date.replace(tzinfo=None) - datetime.utcnow()).days) | |
| plan = g.ai_generated_plan or {} | |
| items.append({ | |
| "id": g.id, | |
| "title": g.title, | |
| "target_amount": g.target_amount, | |
| "current_amount": g.current_amount, | |
| "progress_percent": round(progress, 1), | |
| "target_date": g.target_date.isoformat() if g.target_date else None, | |
| "days_left": days_left, | |
| "monthly_contribution": plan.get("monthly_contribution"), | |
| "months_remaining": plan.get("months_remaining"), | |
| "on_track": progress >= 50 or (days_left is not None and days_left > 180), | |
| }) | |
| return { | |
| "goals": items, | |
| "summary": { | |
| "count": len(items), | |
| "total_target": round(total_target, 2), | |
| "total_saved": round(total_saved, 2), | |
| "overall_progress": round(total_saved / total_target * 100, 1) if total_target > 0 else 0, | |
| }, | |
| } | |
| def contribute_to_goal( | |
| goal_id: str, | |
| body: ContributeRequest, | |
| user_id: Optional[str] = None, | |
| db: Session = Depends(get_db), | |
| ): | |
| uid = _resolve_user(db, user_id) | |
| goal = db.query(Goal).filter(Goal.id == goal_id, Goal.user_id == uid).first() | |
| if not goal: | |
| raise HTTPException(status_code=404, detail="Goal not found") | |
| goal.current_amount = min(goal.target_amount, goal.current_amount + body.amount) | |
| db.commit() | |
| db.refresh(goal) | |
| progress = min(100.0, goal.current_amount / goal.target_amount * 100) if goal.target_amount > 0 else 0 | |
| return { | |
| "id": goal.id, | |
| "current_amount": goal.current_amount, | |
| "progress_percent": round(progress, 1), | |
| "completed": goal.current_amount >= goal.target_amount, | |
| } | |