from fastapi import APIRouter, Depends, HTTPException, Query from sqlalchemy import text from sqlalchemy.orm import Session from typing import Any from app.database import get_session from app.auth import get_current_user router = APIRouter() def _row(r) -> dict: return dict(r._mapping) @router.get("") def list_checklists( session: Session = Depends(get_session), _: str = Depends(get_current_user), ) -> list[dict]: rows = session.execute(text("SELECT * FROM checklists ORDER BY name")).fetchall() return [_row(r) for r in rows] @router.post("", status_code=201) def create_checklist( body: dict[str, Any], session: Session = Depends(get_session), _: str = Depends(get_current_user), ) -> dict: row = session.execute(text(""" INSERT INTO checklists (name, scope) VALUES (:name, :scope) RETURNING * """), {"name": body.get("name"), "scope": body.get("scope", "colony")}).fetchone() session.commit() return _row(row) @router.delete("/{id}", status_code=204) def delete_checklist( id: int, session: Session = Depends(get_session), _: str = Depends(get_current_user), ): session.execute(text("DELETE FROM checklists WHERE id = :id"), {"id": id}) session.commit() @router.get("/{id}/items") def list_items( id: int, session: Session = Depends(get_session), _: str = Depends(get_current_user), ) -> list[dict]: rows = session.execute(text(""" SELECT * FROM checklist_items WHERE checklist_id = :id ORDER BY position """), {"id": id}).fetchall() return [_row(r) for r in rows] @router.post("/{id}/items", status_code=201) def create_item( id: int, body: dict[str, Any], session: Session = Depends(get_session), _: str = Depends(get_current_user), ) -> dict: row = session.execute(text(""" INSERT INTO checklist_items (checklist_id, text, position) VALUES (:checklist_id, :text, :position) RETURNING * """), { "checklist_id": id, "text": body.get("text"), "position": body.get("position", 0), }).fetchone() session.commit() return _row(row) @router.delete("/items/{item_id}", status_code=204) def delete_item( item_id: int, session: Session = Depends(get_session), _: str = Depends(get_current_user), ): session.execute(text("DELETE FROM checklist_items WHERE id = :id"), {"id": item_id}) session.commit() @router.get("/{id}/completions") def list_completions( id: int, scope_id: int = Query(...), session: Session = Depends(get_session), _: str = Depends(get_current_user), ) -> list[dict]: rows = session.execute(text(""" SELECT cc.*, ci.text, ci.position FROM checklist_completions cc JOIN checklist_items ci ON ci.id = cc.item_id WHERE cc.checklist_id = :checklist_id AND cc.scope_id = :scope_id ORDER BY ci.position """), {"checklist_id": id, "scope_id": scope_id}).fetchall() return [_row(r) for r in rows] @router.post("/{id}/completions/toggle") def toggle_completion( id: int, body: dict[str, Any], session: Session = Depends(get_session), _: str = Depends(get_current_user), ) -> dict: item_id = body.get("item_id") scope_id = body.get("scope_id") date = body.get("date") completed_by = body.get("completed_by") notes = body.get("notes") existing = session.execute(text(""" SELECT id FROM checklist_completions WHERE checklist_id = :checklist_id AND item_id = :item_id AND scope_id = :scope_id """), {"checklist_id": id, "item_id": item_id, "scope_id": scope_id}).fetchone() if existing is not None: session.execute( text("DELETE FROM checklist_completions WHERE id = :id"), {"id": existing._mapping["id"]}, ) session.commit() return {"toggled": False} else: session.execute(text(""" INSERT INTO checklist_completions (checklist_id, item_id, scope_id, date, completed_by, notes) VALUES (:checklist_id, :item_id, :scope_id, :date, :completed_by, :notes) """), { "checklist_id": id, "item_id": item_id, "scope_id": scope_id, "date": date, "completed_by": completed_by, "notes": notes, }) session.commit() return {"toggled": True}