first prototype commit

This commit is contained in:
2026-08-09 21:45:06 -04:00
commit 40613a00d5
36 changed files with 4371 additions and 0 deletions
+132
View File
@@ -0,0 +1,132 @@
from fastapi import APIRouter, Depends, HTTPException, status
from sqlalchemy.orm import Session
from typing import List, Optional
from datetime import datetime, timezone
from backend.database import get_db
from backend.models import User, ChoreType, ChoreCompletion, UserRole, RecurrenceType
from backend.schemas import ChoreTypeOut, ChoreTypeCreate, ChoreTypeUpdate, ChoreCompletionCreate, ChoreCompletionOut
from backend.routers.auth import get_current_user, get_admin_user
from backend.config import get_current_week_identifier
router = APIRouter(prefix="/api/chores", tags=["chores"])
@router.get("/types", response_model=List[ChoreTypeOut])
def list_chore_types(db: Session = Depends(get_db), current_user: User = Depends(get_current_user)):
return db.query(ChoreType).order_by(ChoreType.id.asc()).all()
@router.post("/types", response_model=ChoreTypeOut)
def create_chore_type(chore_in: ChoreTypeCreate, db: Session = Depends(get_db), admin: User = Depends(get_admin_user)):
if chore_in.star_reward < 1 or chore_in.star_reward > 5:
raise HTTPException(status_code=400, detail="Star reward must be between 1 and 5")
if chore_in.recurrence not in [r.value for r in RecurrenceType]:
raise HTTPException(status_code=400, detail=f"Invalid recurrence type. Must be daily, weekly, or spontaneous")
new_chore = ChoreType(
title=chore_in.title,
description=chore_in.description,
star_reward=chore_in.star_reward,
recurrence=chore_in.recurrence,
icon=chore_in.icon
)
db.add(new_chore)
db.commit()
db.refresh(new_chore)
return new_chore
@router.patch("/types/{chore_id}", response_model=ChoreTypeOut)
def update_chore_type(chore_id: int, chore_in: ChoreTypeUpdate, db: Session = Depends(get_db), admin: User = Depends(get_admin_user)):
chore = db.query(ChoreType).filter(ChoreType.id == chore_id).first()
if not chore:
raise HTTPException(status_code=404, detail="Chore type not found")
if chore_in.title is not None:
chore.title = chore_in.title
if chore_in.description is not None:
chore.description = chore_in.description
if chore_in.star_reward is not None:
if chore_in.star_reward < 1 or chore_in.star_reward > 5:
raise HTTPException(status_code=400, detail="Star reward must be between 1 and 5")
chore.star_reward = chore_in.star_reward
if chore_in.recurrence is not None:
if chore_in.recurrence not in [r.value for r in RecurrenceType]:
raise HTTPException(status_code=400, detail="Invalid recurrence type")
chore.recurrence = chore_in.recurrence
if chore_in.icon is not None:
chore.icon = chore_in.icon
db.commit()
db.refresh(chore)
return chore
@router.delete("/types/{chore_id}")
def delete_chore_type(chore_id: int, db: Session = Depends(get_db), admin: User = Depends(get_admin_user)):
chore = db.query(ChoreType).filter(ChoreType.id == chore_id).first()
if not chore:
raise HTTPException(status_code=404, detail="Chore type not found")
db.delete(chore)
db.commit()
return {"message": "Chore type deleted successfully", "id": chore_id}
@router.post("/complete", response_model=ChoreCompletionOut)
def complete_chore(payload: ChoreCompletionCreate, db: Session = Depends(get_db), current_user: User = Depends(get_current_user)):
chore_type = db.query(ChoreType).filter(ChoreType.id == payload.chore_type_id).first()
if not chore_type:
raise HTTPException(status_code=404, detail="Chore type not found")
week_id = get_current_week_identifier()
now_utc = datetime.now(timezone.utc)
completion = ChoreCompletion(
chore_type_id=chore_type.id,
user_id=current_user.id,
completed_at=now_utc,
stars_earned=chore_type.star_reward,
week_identifier=week_id,
notes=payload.notes
)
db.add(completion)
db.commit()
db.refresh(completion)
return ChoreCompletionOut(
id=completion.id,
chore_type_id=completion.chore_type_id,
user_id=completion.user_id,
completed_at=completion.completed_at,
stars_earned=completion.stars_earned,
week_identifier=completion.week_identifier,
notes=completion.notes,
user_name=current_user.name,
user_avatar=current_user.avatar_url,
chore_title=chore_type.title,
chore_icon=chore_type.icon
)
@router.get("/completions", response_model=List[ChoreCompletionOut])
def get_recent_completions(week: Optional[str] = None, user_id: Optional[int] = None, limit: int = 50, db: Session = Depends(get_db), current_user: User = Depends(get_current_user)):
query = db.query(ChoreCompletion)
if week:
query = query.filter(ChoreCompletion.week_identifier == week)
if user_id:
query = query.filter(ChoreCompletion.user_id == user_id)
completions = query.order_by(ChoreCompletion.completed_at.desc()).limit(limit).all()
result = []
for c in completions:
result.append(ChoreCompletionOut(
id=c.id,
chore_type_id=c.chore_type_id,
user_id=c.user_id,
completed_at=c.completed_at,
stars_earned=c.stars_earned,
week_identifier=c.week_identifier,
notes=c.notes,
user_name=c.user.name if c.user else "Unknown",
user_avatar=c.user.avatar_url if c.user else None,
chore_title=c.chore_type.title if c.chore_type else "Chore",
chore_icon=c.chore_type.icon if c.chore_type else "CheckSquare"
))
return result