Files
yolkbook/backend/routers/flock.py
derekc 9709283d7a Fix remaining code quality and infrastructure items
- admin.py: remove unused get_current_user import
- feed.py, flock.py, other.py: add IntegrityError handling on POST/PUT
  endpoints; duplicate submissions now return 409 instead of crashing with
  a 500 error
- stats.py: extract magic numbers into named module-level constants
  (DAYS_ROLLING, DAYS_SHORT, PRECISION_AVG, PRECISION_HEN, PRECISION_COST);
  add return type annotations to _total_feed_cost and _total_other_cost;
  normalize both helpers to always return Decimal so budget_stats no longer
  needs Decimal(str(...)) workarounds; simplify _cpe/_cpd helpers
- dashboard.js: read --green CSS variable at runtime instead of hardcoding
  the hex value so chart color stays in sync with the stylesheet
- docker-compose.yml: add healthcheck to api service (polls /api/health
  every 30s) so Docker knows when the API is unhealthy; add password
  strength guidance comment above the db service

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-18 00:18:58 -07:00

114 lines
3.3 KiB
Python

from datetime import date
from typing import Optional
from fastapi import APIRouter, Depends, HTTPException
from sqlalchemy import select
from sqlalchemy.exc import IntegrityError
from sqlalchemy.orm import Session
from database import get_db
from models import FlockHistory, User
from schemas import FlockHistoryCreate, FlockHistoryUpdate, FlockHistoryOut
from auth import get_current_user
router = APIRouter(prefix="/api/flock", tags=["flock"])
@router.get("", response_model=list[FlockHistoryOut])
def list_flock_history(
db: Session = Depends(get_db),
current_user: User = Depends(get_current_user),
):
q = (
select(FlockHistory)
.where(FlockHistory.user_id == current_user.id)
.order_by(FlockHistory.date.desc())
)
return db.scalars(q).all()
@router.get("/current", response_model=Optional[FlockHistoryOut])
def get_current_flock(
db: Session = Depends(get_db),
current_user: User = Depends(get_current_user),
):
q = (
select(FlockHistory)
.where(FlockHistory.user_id == current_user.id)
.order_by(FlockHistory.date.desc())
.limit(1)
)
return db.scalars(q).first()
@router.get("/at/{target_date}", response_model=Optional[FlockHistoryOut])
def get_flock_at_date(
target_date: date,
db: Session = Depends(get_db),
current_user: User = Depends(get_current_user),
):
q = (
select(FlockHistory)
.where(FlockHistory.user_id == current_user.id)
.where(FlockHistory.date <= target_date)
.order_by(FlockHistory.date.desc())
.limit(1)
)
return db.scalars(q).first()
@router.post("", response_model=FlockHistoryOut, status_code=201)
def create_flock_entry(
body: FlockHistoryCreate,
db: Session = Depends(get_db),
current_user: User = Depends(get_current_user),
):
record = FlockHistory(**body.model_dump(), user_id=current_user.id)
db.add(record)
try:
db.commit()
except IntegrityError:
db.rollback()
raise HTTPException(status_code=409, detail=f"A flock entry for {body.date} already exists.")
db.refresh(record)
return record
@router.put("/{record_id}", response_model=FlockHistoryOut)
def update_flock_entry(
record_id: int,
body: FlockHistoryUpdate,
db: Session = Depends(get_db),
current_user: User = Depends(get_current_user),
):
record = db.scalars(
select(FlockHistory)
.where(FlockHistory.id == record_id, FlockHistory.user_id == current_user.id)
).first()
if not record:
raise HTTPException(status_code=404, detail="Record not found")
for field, value in body.model_dump(exclude_none=True).items():
setattr(record, field, value)
try:
db.commit()
except IntegrityError:
db.rollback()
raise HTTPException(status_code=409, detail="A flock entry for that date already exists.")
db.refresh(record)
return record
@router.delete("/{record_id}", status_code=204)
def delete_flock_entry(
record_id: int,
db: Session = Depends(get_db),
current_user: User = Depends(get_current_user),
):
record = db.scalars(
select(FlockHistory)
.where(FlockHistory.id == record_id, FlockHistory.user_id == current_user.id)
).first()
if not record:
raise HTTPException(status_code=404, detail="Record not found")
db.delete(record)
db.commit()