import uuid from datetime import date from typing import Optional from fastapi import APIRouter, Depends, HTTPException, Query from sqlalchemy.ext.asyncio import AsyncSession from sqlalchemy import select from app.database import get_db from app.middleware.auth import get_current_user, require_any_role from app.models.daily_note import DailyNote from app.models.user import User from app.schemas.daily_note import DailyNoteCreate, DailyNoteUpdate, DailyNoteOut from app.utils.timezone import today_cst, parse_date from app.utils.edit_log import compute_diff, append_entry, init_entry router = APIRouter(prefix="/daily-notes", tags=["DailyNotes"]) async def _enrich(note: DailyNote, db: AsyncSession) -> dict: mgr = await db.execute(select(User.name).where(User.id == note.manager_id)) return { "id": note.id, "manager_id": note.manager_id, "note_date": note.note_date, "category": note.category, "content": note.content, "time_range": note.time_range, "edit_log": note.edit_log or [], "created_at": note.created_at, "updated_at": note.updated_at, "manager_name": mgr.scalar_one_or_none(), } @router.get("/") async def list_notes( date_from: Optional[str] = Query(None), date_to: Optional[str] = Query(None), current_user: dict = Depends(get_current_user), db: AsyncSession = Depends(get_db), ): query = select(DailyNote) if current_user["role"] == "manager": query = query.where(DailyNote.manager_id == uuid.UUID(current_user["user_id"])) if date_from: query = query.where(DailyNote.note_date >= parse_date(date_from)) if date_to: query = query.where(DailyNote.note_date <= parse_date(date_to)) query = query.order_by(DailyNote.note_date.desc(), DailyNote.created_at.desc()).limit(100) result = await db.execute(query) return [await _enrich(n, db) for n in result.scalars().all()] @router.get("/today") async def list_today_notes( current_user: dict = Depends(get_current_user), db: AsyncSession = Depends(get_db), ): query = select(DailyNote).where(DailyNote.note_date == today_cst()) if current_user["role"] == "manager": query = query.where(DailyNote.manager_id == uuid.UUID(current_user["user_id"])) result = await db.execute(query) notes = [await _enrich(n, db) for n in result.scalars().all()] return {"count": len(notes), "notes": notes} @router.get("/{note_id}") async def get_note( note_id: str, current_user: dict = Depends(get_current_user), db: AsyncSession = Depends(get_db), ): result = await db.execute(select(DailyNote).where(DailyNote.id == note_id)) note = result.scalar_one_or_none() if not note: raise HTTPException(status_code=404, detail="Not found") if current_user["role"] == "manager" and str(note.manager_id) != current_user["user_id"]: raise HTTPException(status_code=403, detail="Access denied") return await _enrich(note, db) @router.post("/") async def create_note( data: DailyNoteCreate, current_user: dict = Depends(require_any_role), db: AsyncSession = Depends(get_db), ): note = DailyNote( manager_id=uuid.UUID(current_user["user_id"]), note_date=parse_date(data.note_date), category=data.category, content=data.content, time_range=data.time_range, ) init_entry(note, current_user["name"]) db.add(note) await db.commit() await db.refresh(note) return await _enrich(note, db) @router.put("/{note_id}") async def update_note( note_id: str, data: DailyNoteUpdate, current_user: dict = Depends(require_any_role), db: AsyncSession = Depends(get_db), ): result = await db.execute(select(DailyNote).where(DailyNote.id == note_id)) note = result.scalar_one_or_none() if not note: raise HTTPException(status_code=404, detail="Not found") if current_user["role"] == "manager" and str(note.manager_id) != current_user["user_id"]: raise HTTPException(status_code=403, detail="Access denied") old_snapshot = {"note_date": str(note.note_date), "category": note.category, "content": note.content, "time_range": note.time_range} update_data = data.model_dump(exclude_unset=True) if "note_date" in update_data and update_data["note_date"]: update_data["note_date"] = parse_date(update_data["note_date"]) for k, v in update_data.items(): setattr(note, k, v) new_snapshot = {"note_date": str(note.note_date), "category": note.category, "content": note.content, "time_range": note.time_range} changes = compute_diff(old_snapshot, new_snapshot) if changes: append_entry(note, current_user["name"], changes, getattr(data, "edit_reason", None)) await db.commit() await db.refresh(note) return await _enrich(note, db) @router.delete("/{note_id}") async def delete_note( note_id: str, current_user: dict = Depends(require_any_role), db: AsyncSession = Depends(get_db), ): result = await db.execute(select(DailyNote).where(DailyNote.id == note_id)) note = result.scalar_one_or_none() if not note: raise HTTPException(status_code=404, detail="Not found") if current_user["role"] == "manager" and str(note.manager_id) != current_user["user_id"]: raise HTTPException(status_code=403, detail="Access denied") await db.delete(note) await db.commit() return {"detail": "deleted"}