TICKET-006 (backend): OFF normalization, scan/search/refresh, restore-on-rescan
- services/off.py: single normalizer module (kcal/kJ fallback, not-found
rule); fetch_product/search_off degrade gracefully on upstream errors
(503/timeout → None / []), never 500; User-Agent + timeouts on all calls
- routers/off.py: thin routes for GET /api/off/product/{barcode},
GET /api/off/search, POST /api/off/refresh/{food_id} (404 unknown id,
400 no-barcode)
- services/foods.py: restore-on-rescan (§3.1) — barcode collision on a
soft-deleted food clears deleted_at + updates row instead of 409;
GET /api/foods/recent ordered by most-recent daily_log appearance,
deduped, soft-deleted excluded
- tests: httpx mocked at the boundary (MockTransport, no real OFF);
48 new tests (normalizer unit + router + restore + recent + error paths)
- 152 passing (was 104)
This commit is contained in:
@@ -10,6 +10,7 @@ from services.foods import (
|
||||
create_food,
|
||||
delete_food,
|
||||
get_food,
|
||||
get_recent_foods,
|
||||
list_foods,
|
||||
update_food,
|
||||
)
|
||||
@@ -17,6 +18,17 @@ from services.foods import (
|
||||
router = APIRouter(prefix="/api/foods", tags=["foods"])
|
||||
|
||||
|
||||
@router.get("/recent", response_model=list[FoodRead])
|
||||
def _recent_foods(
|
||||
limit: int = Query(default=10, le=50),
|
||||
db: Session = Depends(get_db),
|
||||
):
|
||||
"""Foods ordered by most recent appearance in daily_log, deduplicated.
|
||||
Soft-deleted foods excluded. Ordered by most-recently-LOGGED, not by
|
||||
foods.created_at."""
|
||||
return get_recent_foods(db, limit=limit)
|
||||
|
||||
|
||||
@router.get("", response_model=list[FoodRead])
|
||||
def _list_foods(
|
||||
q: str | None = None,
|
||||
|
||||
Reference in New Issue
Block a user