"""Public API endpoints for kiosk view (no authentication required).""" from fastapi import APIRouter, Depends, HTTPException, status from sqlalchemy.orm import Session from typing import List, Optional from datetime import date from app.core.database import get_db from app.models.user import User from app.models.chore import Chore from app.models.chore_assignment import ChoreAssignment from app.models.chore_completion_log import ChoreCompletionLog router = APIRouter() @router.get("/users") async def get_public_users(db: Session = Depends(get_db)): """Get all active users (public endpoint for kiosk).""" users = db.query(User).filter(User.is_active == True).all() return [ { "id": user.id, "username": user.username, "full_name": user.full_name, "avatar_url": user.avatar_url, "birthday": user.birthday, "is_active": user.is_active, } for user in users ] @router.get("/chores") async def get_public_chores( user_id: Optional[int] = None, exclude_birthdays: bool = False, db: Session = Depends(get_db) ): """ Get chores with optional filtering (public endpoint for kiosk). - user_id: Filter by assigned user - exclude_birthdays: Hide chores for users whose birthday is today """ query = db.query(Chore) # Filter by user if specified if user_id: query = query.join(ChoreAssignment).filter( ChoreAssignment.user_id == user_id ) chores = query.all() # Filter out birthday chores if requested if exclude_birthdays: today = date.today() filtered_chores = [] for chore in chores: # Get all assignments for this chore assignments = db.query(ChoreAssignment).filter( ChoreAssignment.chore_id == chore.id ).all() # Check if any assigned user has birthday today has_birthday = False for assignment in assignments: user = db.query(User).filter(User.id == assignment.user_id).first() if user and user.birthday: if user.birthday.month == today.month and user.birthday.day == today.day: has_birthday = True break if not has_birthday: filtered_chores.append(chore) chores = filtered_chores # Build response with assigned users info result = [] for chore in chores: # Get assignments for this chore assignments = db.query(ChoreAssignment).filter( ChoreAssignment.chore_id == chore.id ).all() assigned_users = [] for assignment in assignments: user = db.query(User).filter(User.id == assignment.user_id).first() if user: assigned_users.append({ "id": user.id, "username": user.username, "full_name": user.full_name, "avatar_url": user.avatar_url, "birthday": user.birthday, "completed_at": assignment.completed_at }) chore_dict = { "id": chore.id, "title": chore.title, "description": chore.description, "room": chore.room, "frequency": chore.frequency, "points": chore.points, "image_url": chore.image_url, "assignment_type": chore.assignment_type, "status": chore.status, "due_date": chore.due_date, "created_at": chore.created_at, "updated_at": chore.updated_at, "completed_at": chore.completed_at, "assigned_users": assigned_users, "assigned_user_id": None # Legacy field } result.append(chore_dict) return result @router.post("/chores/{chore_id}/complete") async def complete_public_chore( chore_id: int, user_id: int, helper_ids: Optional[List[int]] = None, db: Session = Depends(get_db) ): """ Mark a chore as complete for a specific user (public endpoint for kiosk). Query params: - user_id: The user completing the chore - helper_ids: Optional list of other user IDs who helped """ # Get chore chore = db.query(Chore).filter(Chore.id == chore_id).first() if not chore: raise HTTPException( status_code=status.HTTP_404_NOT_FOUND, detail="Chore not found" ) # Get user user = db.query(User).filter(User.id == user_id).first() if not user: raise HTTPException( status_code=status.HTTP_404_NOT_FOUND, detail="User not found" ) # Get assignment assignment = db.query(ChoreAssignment).filter( ChoreAssignment.chore_id == chore_id, ChoreAssignment.user_id == user_id ).first() if not assignment: raise HTTPException( status_code=status.HTTP_404_NOT_FOUND, detail="User not assigned to this chore" ) # Mark as complete from datetime import datetime completion_time = datetime.now() assignment.completed_at = completion_time # CREATE COMPLETION LOG ENTRY completion_log = ChoreCompletionLog( chore_id=chore_id, user_id=user_id, completed_at=completion_time, notes=None # Kiosk doesn't support notes yet ) db.add(completion_log) # If helpers provided, mark them as completed too if helper_ids: for helper_id in helper_ids: helper_assignment = db.query(ChoreAssignment).filter( ChoreAssignment.chore_id == chore_id, ChoreAssignment.user_id == helper_id ).first() # Create assignment if doesn't exist (helper claimed they helped) if not helper_assignment: helper_assignment = ChoreAssignment( chore_id=chore_id, user_id=helper_id, completed_at=datetime.now() ) db.add(helper_assignment) else: helper_assignment.completed_at = datetime.now() # CREATE COMPLETION LOG FOR HELPER helper_log = ChoreCompletionLog( chore_id=chore_id, user_id=helper_id, completed_at=datetime.now(), notes=f"Helped {user.full_name or user.username}" ) db.add(helper_log) # Check if chore is complete based on assignment_type all_assignments = db.query(ChoreAssignment).filter( ChoreAssignment.chore_id == chore_id ).all() if chore.assignment_type == "any_one": # Only one person needs to complete if assignment.completed_at: chore.status = 'completed' chore.completed_at = datetime.now() else: # all_assigned # All assigned must complete all_complete = all([a.completed_at is not None for a in all_assignments]) if all_complete: chore.status = 'completed' chore.completed_at = datetime.now() else: chore.status = 'in_progress' db.commit() return { "message": "Chore marked as complete", "chore_id": chore_id, "user_id": user_id, "helper_ids": helper_ids or [], "chore_status": chore.status } @router.post("/chores/{chore_id}/claim") async def claim_public_chore( chore_id: int, user_id: int, db: Session = Depends(get_db) ): """ Claim an available chore (add user as assigned). Query params: - user_id: The user claiming the chore """ # Get chore chore = db.query(Chore).filter(Chore.id == chore_id).first() if not chore: raise HTTPException( status_code=status.HTTP_404_NOT_FOUND, detail="Chore not found" ) # Get user user = db.query(User).filter(User.id == user_id).first() if not user: raise HTTPException( status_code=status.HTTP_404_NOT_FOUND, detail="User not found" ) # Check if already assigned existing = db.query(ChoreAssignment).filter( ChoreAssignment.chore_id == chore_id, ChoreAssignment.user_id == user_id ).first() if existing: return { "message": "Already assigned to this chore", "chore_id": chore_id, "user_id": user_id } # Create assignment assignment = ChoreAssignment( chore_id=chore_id, user_id=user_id ) db.add(assignment) # Update chore status if needed if chore.status == 'pending': chore.status = 'in_progress' db.commit() return { "message": "Chore claimed successfully", "chore_id": chore_id, "user_id": user_id }