refactor(csv_parser): Update training type resolution to use existing database cursor
- Modified `_resolve_training_type_for_activity` to accept a database cursor, improving efficiency and avoiding potential deadlocks during CSV imports. - Introduced `get_training_type_for_activity_with_cursor` to handle training type resolution with an existing cursor, streamlining database interactions. - Updated related calls in the activity import logic to utilize the new function, ensuring consistent behavior across the application.
This commit is contained in:
parent
a9bd3faabb
commit
894ee1dd02
|
|
@ -31,11 +31,11 @@ except Exception: # pragma: no cover
|
|||
_EVALUATION_AVAILABLE = False
|
||||
|
||||
|
||||
def _resolve_training_type_for_activity(activity_type: str, profile_id: str):
|
||||
"""Lazy import — ermöglicht Tests ohne Laden von routers.activity (bcrypt)."""
|
||||
from routers.activity import get_training_type_for_activity
|
||||
def _resolve_training_type_for_activity(cur, activity_type: str, profile_id: str):
|
||||
"""Lazy import — gleicher DB-Cursor wie der Import (kein verschachteltes get_db / Pool-Deadlock)."""
|
||||
from routers.activity import get_training_type_for_activity_with_cursor
|
||||
|
||||
return get_training_type_for_activity(activity_type, profile_id)
|
||||
return get_training_type_for_activity_with_cursor(cur, activity_type, profile_id)
|
||||
|
||||
|
||||
def coerce_date(val: Any) -> dt.date | None:
|
||||
|
|
@ -873,7 +873,7 @@ def _import_activity(
|
|||
|
||||
wtype = str(activity_type).strip()
|
||||
training_type_id, training_category, training_subcategory = _resolve_training_type_for_activity(
|
||||
wtype, profile_id
|
||||
cur, wtype, profile_id
|
||||
)
|
||||
|
||||
iso = date_d.isoformat()
|
||||
|
|
|
|||
9
backend/migrations/051_blood_pressure_source_csv.sql
Normal file
9
backend/migrations/051_blood_pressure_source_csv.sql
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
-- Universal-CSV-Import schreibt source = 'csv' (siehe csv_parser/executor _import_blood_pressure).
|
||||
|
||||
ALTER TABLE blood_pressure_log DROP CONSTRAINT IF EXISTS blood_pressure_log_source_check;
|
||||
|
||||
ALTER TABLE blood_pressure_log ADD CONSTRAINT blood_pressure_log_source_check
|
||||
CHECK (source IN ('manual', 'omron', 'apple_health', 'withings', 'csv'));
|
||||
|
||||
COMMENT ON COLUMN blood_pressure_log.source IS
|
||||
'manual | omron | apple_health | withings | csv (Universal-CSV-Import)';
|
||||
|
|
@ -198,6 +198,43 @@ def activity_stats(x_profile_id: Optional[str]=Header(default=None), session: di
|
|||
return {"count":len(rows),"total_kcal":round(total_kcal),"total_min":round(total_min),"by_type":by_type}
|
||||
|
||||
|
||||
def get_training_type_for_activity_with_cursor(cur, activity_type: str, profile_id: str | None = None):
|
||||
"""
|
||||
Wie get_training_type_for_activity, aber mit bestehendem Cursor (z. B. Universal-CSV-Import).
|
||||
Vermeidet verschachteltes get_db() — bei maxconn=1 sonst Deadlock auf dem Connection-Pool.
|
||||
"""
|
||||
if profile_id:
|
||||
cur.execute(
|
||||
"""
|
||||
SELECT m.training_type_id, t.category, t.subcategory
|
||||
FROM activity_type_mappings m
|
||||
JOIN training_types t ON m.training_type_id = t.id
|
||||
WHERE m.activity_type = %s AND m.profile_id = %s
|
||||
LIMIT 1
|
||||
""",
|
||||
(activity_type, profile_id),
|
||||
)
|
||||
row = cur.fetchone()
|
||||
if row:
|
||||
return (row["training_type_id"], row["category"], row["subcategory"])
|
||||
|
||||
cur.execute(
|
||||
"""
|
||||
SELECT m.training_type_id, t.category, t.subcategory
|
||||
FROM activity_type_mappings m
|
||||
JOIN training_types t ON m.training_type_id = t.id
|
||||
WHERE m.activity_type = %s AND m.profile_id IS NULL
|
||||
LIMIT 1
|
||||
""",
|
||||
(activity_type,),
|
||||
)
|
||||
row = cur.fetchone()
|
||||
if row:
|
||||
return (row["training_type_id"], row["category"], row["subcategory"])
|
||||
|
||||
return (None, None, None)
|
||||
|
||||
|
||||
def get_training_type_for_activity(activity_type: str, profile_id: str = None):
|
||||
"""
|
||||
Map activity_type to training_type_id using database mappings.
|
||||
|
|
@ -211,33 +248,7 @@ def get_training_type_for_activity(activity_type: str, profile_id: str = None):
|
|||
"""
|
||||
with get_db() as conn:
|
||||
cur = get_cursor(conn)
|
||||
|
||||
# Try user-specific mapping first
|
||||
if profile_id:
|
||||
cur.execute("""
|
||||
SELECT m.training_type_id, t.category, t.subcategory
|
||||
FROM activity_type_mappings m
|
||||
JOIN training_types t ON m.training_type_id = t.id
|
||||
WHERE m.activity_type = %s AND m.profile_id = %s
|
||||
LIMIT 1
|
||||
""", (activity_type, profile_id))
|
||||
row = cur.fetchone()
|
||||
if row:
|
||||
return (row['training_type_id'], row['category'], row['subcategory'])
|
||||
|
||||
# Try global mapping
|
||||
cur.execute("""
|
||||
SELECT m.training_type_id, t.category, t.subcategory
|
||||
FROM activity_type_mappings m
|
||||
JOIN training_types t ON m.training_type_id = t.id
|
||||
WHERE m.activity_type = %s AND m.profile_id IS NULL
|
||||
LIMIT 1
|
||||
""", (activity_type,))
|
||||
row = cur.fetchone()
|
||||
if row:
|
||||
return (row['training_type_id'], row['category'], row['subcategory'])
|
||||
|
||||
return (None, None, None)
|
||||
return get_training_type_for_activity_with_cursor(cur, activity_type, profile_id)
|
||||
|
||||
|
||||
@router.get("/uncategorized")
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user