mitai-jinkendo/backend/routers/admin_activity_attribute_profiles.py
Lars 48508c164e
All checks were successful
Deploy Development / deploy (push) Successful in 59s
Build Test / pytest-backend (push) Successful in 4s
Build Test / lint-backend (push) Successful in 0s
Build Test / build-frontend (push) Successful in 16s
feat: Add Activity Session Metrics functionality
- Introduced Activity Session Metrics for enhanced tracking of session data.
- Updated backend to support new API endpoints for managing session metrics.
- Added new Pydantic models for activity metrics and replaced metrics functionality.
- Enhanced data layer to include session metrics in recent training session data.
- Updated documentation to reflect changes in session metrics handling.
2026-04-14 11:49:14 +02:00

185 lines
6.4 KiB
Python

"""
Admin: training_category_parameter + training_type_parameter (attribute profiles).
Agent guide: .claude/docs/technical/ACTIVITY_SESSION_METRICS_EAV_AGENT_GUIDE.md
"""
from typing import Optional
from fastapi import APIRouter, Depends, HTTPException, Query
from pydantic import BaseModel, Field
from auth import require_admin
from db import get_db, get_cursor, r2d
router = APIRouter(prefix="/api/admin", tags=["admin", "activity-attribute-profiles"])
class CategoryParameterCreate(BaseModel):
training_category: str = Field(..., min_length=1, max_length=50)
training_parameter_id: int
sort_order: int = 0
required: bool = False
ui_group: Optional[str] = Field(None, max_length=50)
class TypeParameterCreate(BaseModel):
training_type_id: int
training_parameter_id: int
sort_order: Optional[int] = None
required: Optional[bool] = None
ui_group: Optional[str] = Field(None, max_length=50)
@router.get("/training-category-parameters")
def admin_list_category_parameters(
category: Optional[str] = Query(None, description="Filter: training_types.category"),
session: dict = Depends(require_admin),
):
with get_db() as conn:
cur = get_cursor(conn)
if category:
cur.execute(
"""
SELECT tcp.*, tp.key AS parameter_key, tp.name_de AS parameter_name_de
FROM training_category_parameter tcp
JOIN training_parameters tp ON tp.id = tcp.training_parameter_id
WHERE tcp.training_category = %s
ORDER BY tcp.sort_order, tp.key
""",
(category.strip(),),
)
else:
cur.execute(
"""
SELECT tcp.*, tp.key AS parameter_key, tp.name_de AS parameter_name_de
FROM training_category_parameter tcp
JOIN training_parameters tp ON tp.id = tcp.training_parameter_id
ORDER BY tcp.training_category, tcp.sort_order, tp.key
"""
)
return [r2d(r) for r in cur.fetchall()]
@router.post("/training-category-parameters")
def admin_add_category_parameter(
body: CategoryParameterCreate,
session: dict = Depends(require_admin),
):
cat = body.training_category.strip()
with get_db() as conn:
cur = get_cursor(conn)
cur.execute("SELECT id FROM training_parameters WHERE id = %s", (body.training_parameter_id,))
if not cur.fetchone():
raise HTTPException(404, "training_parameter_id unbekannt")
try:
cur.execute(
"""
INSERT INTO training_category_parameter (
training_category, training_parameter_id, sort_order, required, ui_group
) VALUES (%s,%s,%s,%s,%s)
RETURNING id
""",
(cat, body.training_parameter_id, body.sort_order, body.required, body.ui_group),
)
new_id = cur.fetchone()["id"]
conn.commit()
except Exception as e:
conn.rollback()
if "uq_training_category_parameter" in str(e).lower() or "unique" in str(e).lower():
raise HTTPException(409, "Zuordnung existiert bereits") from e
raise HTTPException(400, str(e)) from e
return {"id": new_id}
@router.delete("/training-category-parameters/{link_id}")
def admin_delete_category_parameter(
link_id: int,
session: dict = Depends(require_admin),
):
with get_db() as conn:
cur = get_cursor(conn)
cur.execute(
"DELETE FROM training_category_parameter WHERE id = %s RETURNING id",
(link_id,),
)
if not cur.fetchone():
raise HTTPException(404, "Eintrag nicht gefunden")
conn.commit()
return {"ok": True}
@router.get("/training-type-parameters")
def admin_list_type_parameters(
training_type_id: int = Query(..., ge=1),
session: dict = Depends(require_admin),
):
with get_db() as conn:
cur = get_cursor(conn)
cur.execute(
"""
SELECT ttp.*, tp.key AS parameter_key, tp.name_de AS parameter_name_de
FROM training_type_parameter ttp
JOIN training_parameters tp ON tp.id = ttp.training_parameter_id
WHERE ttp.training_type_id = %s
ORDER BY ttp.sort_order NULLS LAST, tp.key
""",
(training_type_id,),
)
return [r2d(r) for r in cur.fetchall()]
@router.post("/training-type-parameters")
def admin_add_type_parameter(
body: TypeParameterCreate,
session: dict = Depends(require_admin),
):
with get_db() as conn:
cur = get_cursor(conn)
cur.execute("SELECT id FROM training_types WHERE id = %s", (body.training_type_id,))
if not cur.fetchone():
raise HTTPException(404, "training_type_id unbekannt")
cur.execute("SELECT id FROM training_parameters WHERE id = %s", (body.training_parameter_id,))
if not cur.fetchone():
raise HTTPException(404, "training_parameter_id unbekannt")
try:
cur.execute(
"""
INSERT INTO training_type_parameter (
training_type_id, training_parameter_id, sort_order, required, ui_group
) VALUES (%s,%s,%s,%s,%s)
RETURNING id
""",
(
body.training_type_id,
body.training_parameter_id,
body.sort_order,
body.required,
body.ui_group,
),
)
new_id = cur.fetchone()["id"]
conn.commit()
except Exception as e:
conn.rollback()
if "uq_training_type_parameter" in str(e).lower() or "unique" in str(e).lower():
raise HTTPException(409, "Zuordnung existiert bereits") from e
raise HTTPException(400, str(e)) from e
return {"id": new_id}
@router.delete("/training-type-parameters/{link_id}")
def admin_delete_type_parameter(
link_id: int,
session: dict = Depends(require_admin),
):
with get_db() as conn:
cur = get_cursor(conn)
cur.execute(
"DELETE FROM training_type_parameter WHERE id = %s RETURNING id",
(link_id,),
)
if not cur.fetchone():
raise HTTPException(404, "Eintrag nicht gefunden")
conn.commit()
return {"ok": True}