TICKET-003: Daily log write path

- POST/PUT/DELETE /api/log, GET /api/log?date= with embedded food
- sort_order auto-appends per day; hard delete for log entries
- Soft-deleted foods rejected for new logs (404) but render in history
- Full suite green (87 passed)
This commit is contained in:
Craig
2026-07-26 13:15:46 +01:00
parent 601c0d461e
commit 87d7eca468
4 changed files with 564 additions and 12 deletions
+35 -12
View File
@@ -1,24 +1,47 @@
"""Daily log router (spec §3.3)."""
"""Daily log router (spec §3.3). Thin handlers — business logic in services/log.py."""
from datetime import date
from fastapi import APIRouter, Depends
from sqlalchemy import select
from fastapi import APIRouter, Depends, HTTPException
from sqlalchemy.orm import Session
from database import get_db
from models import DailyLogEntry
from schemas import LogEntryRead
from schemas import LogEntryCreate, LogEntryRead, LogEntryUpdate
from services import log as svc
router = APIRouter(prefix="/api/log", tags=["log"])
@router.get("", response_model=list[LogEntryRead])
def get_log(date: date, db: Session = Depends(get_db)):
"""All entries for a client-supplied date (spec §8.1 rule 8)."""
stmt = (
select(DailyLogEntry)
.where(DailyLogEntry.date == date)
.order_by(DailyLogEntry.sort_order, DailyLogEntry.id)
)
return db.scalars(stmt).all()
"""All entries for a client-supplied date, ordered by sort_order then id,
each with the referenced food embedded (§3.3)."""
return svc.get_log_entries(db, date)
@router.post("", response_model=LogEntryRead, status_code=201)
def create_log_entry(data: LogEntryCreate, db: Session = Depends(get_db)):
"""Add a log entry. food_id must reference a non-deleted food.
sort_order defaults to end of day. Unknown or soft-deleted food → 404."""
try:
return svc.create_log_entry(db, data)
except svc.FoodNotAvailableError as e:
raise HTTPException(status_code=404, detail=str(e))
@router.put("/{entry_id}", response_model=LogEntryRead)
def update_log_entry(entry_id: int, data: LogEntryUpdate, db: Session = Depends(get_db)):
"""Update quantity, meal_slot, and/or sort_order.
Only provided fields change; send {"meal_slot": null} to clear it."""
result = svc.update_log_entry(db, entry_id, data)
if result is None:
raise HTTPException(status_code=404, detail="Log entry not found")
return result
@router.delete("/{entry_id}")
def delete_log_entry(entry_id: int, db: Session = Depends(get_db)):
"""Hard-delete a log entry (§2.3 has no soft-delete)."""
if not svc.delete_log_entry(db, entry_id):
raise HTTPException(status_code=404, detail="Log entry not found")
return {"status": "ok"}