"""Admin CRUD for food_name_mappings.""" from __future__ import annotations from typing import Optional from fastapi import APIRouter, Depends, HTTPException from pydantic import BaseModel from auth import require_admin from data_layer.food_mapping import upsert_food_mapping from db import get_cursor, get_db, r2d router = APIRouter(prefix="/api/admin/food-mappings", tags=["admin", "food-mappings"]) class FoodMappingCreate(BaseModel): source_name: str food_id: str profile_id: Optional[str] = None source_system: str = "fddb" @router.get("") def list_food_mappings( profile_id: Optional[str] = None, global_only: bool = False, session: dict = Depends(require_admin), ): with get_db() as conn: cur = get_cursor(conn) q = """ SELECT m.id, m.source_name_raw, m.source_name_normalized, m.food_id, m.profile_id, m.source, m.created_at, m.updated_at, f.name_de AS food_name_de, f.bls_code, f.catalog_kind FROM food_name_mappings m JOIN food_catalog f ON f.id = m.food_id """ conds, params = [], [] if global_only: conds.append("m.profile_id IS NULL") elif profile_id: conds.append("m.profile_id = %s") params.append(profile_id) if conds: q += " WHERE " + " AND ".join(conds) q += " ORDER BY m.source_name_normalized" cur.execute(q, params) return [r2d(r) for r in cur.fetchall()] @router.get("/stats/coverage") def mapping_coverage(session: dict = Depends(require_admin)): with get_db() as conn: cur = get_cursor(conn) cur.execute( """ SELECT COUNT(*) AS total_items, COUNT(food_id) AS mapped_items, COUNT(*) - COUNT(food_id) AS unmapped_items, COUNT(DISTINCT source_name_normalized) AS unique_names, COUNT(DISTINCT CASE WHEN food_id IS NULL THEN source_name_normalized END) AS unmapped_names FROM nutrition_items """ ) return r2d(cur.fetchone()) @router.post("") def create_food_mapping(body: FoodMappingCreate, session: dict = Depends(require_admin)): with get_db() as conn: cur = get_cursor(conn) cur.execute("SELECT id FROM food_catalog WHERE id = %s", (body.food_id,)) if not cur.fetchone(): raise HTTPException(404, "Lebensmittel nicht gefunden") mid = upsert_food_mapping( cur, source_name_raw=body.source_name, food_id=body.food_id, profile_id=body.profile_id or None, source="admin", source_system=body.source_system, ) cur.execute( """ SELECT m.*, f.name_de AS food_name_de, f.bls_code FROM food_name_mappings m JOIN food_catalog f ON f.id = m.food_id WHERE m.id = %s """, (mid,), ) return r2d(cur.fetchone()) @router.delete("/{mapping_id}") def delete_food_mapping(mapping_id: int, session: dict = Depends(require_admin)): with get_db() as conn: cur = get_cursor(conn) cur.execute("DELETE FROM food_name_mappings WHERE id = %s RETURNING id", (mapping_id,)) if not cur.fetchone(): raise HTTPException(404, "Mapping nicht gefunden") return {"ok": True}