""" Diagnose: Was liegt in activity_session_metrics (EAV) vs. activity_log? Ausführung (mit gesetzten DB_*-Variablen wie die App, z. B. aus .env): cd backend python scripts/inspect_activity_eav.py Lokal ohne Docker-Hostname: z. B. ``set DB_HOST=127.0.0.1`` (Windows) / ``export DB_HOST=127.0.0.1``, Port/User/Pass wie in der laufenden Postgres-Instanz. Im Backend-Container (Compose-Service meist ``backend``, Arbeitsverzeichnis ``/app``): docker compose exec backend python /app/scripts/inspect_activity_eav.py Optional: python scripts/inspect_activity_eav.py --limit 30 python scripts/inspect_activity_eav.py --profile python scripts/inspect_activity_eav.py --activity Keine Schreibzugriffe — nur SELECT. """ from __future__ import annotations import argparse import os import sys # backend/ als Import-Root _BACKEND_ROOT = os.path.abspath(os.path.join(os.path.dirname(__file__), "..")) if _BACKEND_ROOT not in sys.path: sys.path.insert(0, _BACKEND_ROOT) def _val_row(r: dict) -> str | None: dt = r.get("data_type") if dt == "integer": v = r.get("value_int") return str(v) if v is not None else None if dt == "float": v = r.get("value_num") return str(v) if v is not None else None if dt == "string": v = r.get("value_text") return repr(v) if v is not None else None if dt == "boolean": v = r.get("value_bool") return str(v) if v is not None else None return None def main() -> None: parser = argparse.ArgumentParser(description="EAV activity_session_metrics inspizieren") parser.add_argument("--limit", type=int, default=40, help="Zeilen Report A/B") parser.add_argument("--profile", type=str, default=None, help="profile_id filtern") parser.add_argument("--activity", type=str, default=None, help="activity_log.id (einzelne Session)") args = parser.parse_args() from db import get_db, get_cursor if args.activity: with get_db() as conn: with get_cursor(conn) as cur: cur.execute( """ SELECT al.id, al.profile_id, al.date, al.start_time, al.source, al.training_category, al.training_type_id, al.activity_type, al.duration_min, al.kcal_active, al.hr_avg, al.hr_max, al.distance_km FROM activity_log al WHERE al.id = %s::uuid """, (args.activity,), ) h = cur.fetchone() if not h: print("activity_log: keine Zeile für diese id") return print("=== activity_log (Kopfzeile) ===") for k, v in dict(h).items(): print(f" {k}: {v}") cur.execute( """ SELECT m.id AS metric_id, tp.key, tp.data_type, tp.source_field, m.value_num, m.value_int, m.value_text, m.value_bool, m.updated_at FROM activity_session_metrics m JOIN training_parameters tp ON tp.id = m.training_parameter_id WHERE m.activity_log_id = %s::uuid ORDER BY tp.key """, (args.activity,), ) rows = cur.fetchall() print(f"\n=== activity_session_metrics ({len(rows)} Zeilen) ===") for r in rows: d = dict(r) print( f" {d['key']} ({d['data_type']}) " f"value={_val_row(d)!r} source_field={d.get('source_field')!r} " f"updated_at={d.get('updated_at')}" ) if not rows: print(" (keine EAV-Zeilen)") return prof_filter = "" if args.profile: prof_filter = " AND al.profile_id = %s::uuid " params_a: tuple = (args.profile, args.limit) if args.profile else (args.limit,) params_b: tuple = (args.profile, args.limit) if args.profile else (args.limit,) q_recent_eav = f""" SELECT al.id AS activity_id, al.profile_id, al.date, al.start_time, al.source, al.training_type_id, al.training_category, tp.key AS parameter_key, tp.data_type, tp.source_field AS tp_source_field, m.value_num, m.value_int, m.value_text, m.value_bool, m.updated_at FROM activity_session_metrics m JOIN activity_log al ON al.id = m.activity_log_id JOIN training_parameters tp ON tp.id = m.training_parameter_id WHERE 1=1 {prof_filter} ORDER BY m.updated_at DESC NULLS LAST, al.date DESC, al.start_time DESC LIMIT %s """ q_csv_no_eav = f""" SELECT al.id AS activity_id, al.profile_id, al.date, al.start_time, al.source, al.training_type_id, al.training_category, (SELECT COUNT(*) FROM activity_session_metrics m WHERE m.activity_log_id = al.id) AS eav_count FROM activity_log al WHERE al.source = 'csv' {prof_filter} ORDER BY al.date DESC, al.start_time DESC LIMIT %s """ with get_db() as conn: with get_cursor(conn) as cur: print("=== A) Neueste EAV-Zeilen (join activity_log + training_parameters) ===\n") cur.execute(q_recent_eav, params_a) for r in cur.fetchall(): d = dict(r) v = _val_row(d) print( f"{d['date']} {d['start_time']} | {d['activity_id']} | src={d['source']!r} | " f"type={d['training_type_id']} cat={d['training_category']!r} | " f"{d['parameter_key']}={v!r} (tp.source_field={d.get('tp_source_field')!r})" ) print("\n=== B) Neueste CSV-importierte Sessions: EAV-Anzahl pro Zeile ===\n") cur.execute(q_csv_no_eav, params_b) for r in cur.fetchall(): d = dict(r) print( f"{d['date']} {d['start_time']} | {d['activity_id']} | " f"type={d['training_type_id']} | eav_count={d['eav_count']}" ) print("\nFertig. Für eine Session im Detail: --activity ") if __name__ == "__main__": main()