fix: Apple Health import - German names + duplicate detection
Issue 1: Automatic training type mapping didn't work - Root cause: Only English workout names were mapped - Solution: Added 20+ German workout type mappings: - "Traditionelles Krafttraining" → hypertrophy - "Outdoor Spaziergang" → walk - "Innenräume Spaziergang" → walk - "Matrial Arts" → technique (handles typo) - "Cardio Dance" → dance - "Geist & Körper" → yoga - Plus: Laufen, Gehen, Radfahren, Schwimmen, etc. Issue 2: Reimporting CSV created duplicates without training types - Root cause: Import always did INSERT with new UUID, no duplicate check - Solution: Check if entry exists (profile_id + date + start_time) - If exists: UPDATE with new data + training type mapping - If new: INSERT as before - Handles multiple workouts per day (different start times) - "Skipped" count now includes updated entries Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
4d9ef5b33b
commit
a4bd738e6f
|
|
@ -122,7 +122,9 @@ def get_training_type_for_apple_health(workout_type: str):
|
|||
cur = get_cursor(conn)
|
||||
|
||||
# Mapping: Apple Health Workout Type → training_type subcategory
|
||||
# Supports English and German workout names
|
||||
mapping = {
|
||||
# English
|
||||
'running': 'running',
|
||||
'walking': 'walk',
|
||||
'hiking': 'walk',
|
||||
|
|
@ -141,6 +143,33 @@ def get_training_type_for_apple_health(workout_type: str):
|
|||
'cooldown': 'regeneration',
|
||||
'meditation': 'meditation',
|
||||
'mindfulness': 'mindfulness',
|
||||
|
||||
# German (Deutsch)
|
||||
'laufen': 'running',
|
||||
'gehen': 'walk',
|
||||
'wandern': 'walk',
|
||||
'outdoor spaziergang': 'walk',
|
||||
'innenräume spaziergang': 'walk',
|
||||
'spaziergang': 'walk',
|
||||
'radfahren': 'cycling',
|
||||
'schwimmen': 'swimming',
|
||||
'traditionelles krafttraining': 'hypertrophy',
|
||||
'funktionelles krafttraining': 'functional',
|
||||
'hochintensives intervalltraining': 'hiit',
|
||||
'yoga': 'yoga',
|
||||
'kampfsport': 'technique',
|
||||
'matrial arts': 'technique', # Common typo in Apple Health
|
||||
'boxen': 'sparring',
|
||||
'rudern': 'rowing',
|
||||
'tanzen': 'dance',
|
||||
'cardio dance': 'dance',
|
||||
'core training': 'functional',
|
||||
'flexibilität': 'static',
|
||||
'abwärmen': 'regeneration',
|
||||
'cooldown': 'regeneration',
|
||||
'meditation': 'meditation',
|
||||
'achtsamkeit': 'mindfulness',
|
||||
'geist & körper': 'yoga', # Mind & Body → Yoga category
|
||||
}
|
||||
|
||||
subcategory = mapping.get(workout_type.lower())
|
||||
|
|
@ -255,6 +284,42 @@ async def import_activity_csv(file: UploadFile=File(...), x_profile_id: Optional
|
|||
training_type_id, training_category, training_subcategory = get_training_type_for_apple_health(wtype)
|
||||
|
||||
try:
|
||||
# Check if entry already exists (duplicate detection by date + start_time)
|
||||
cur.execute("""
|
||||
SELECT id FROM activity_log
|
||||
WHERE profile_id = %s AND date = %s AND start_time = %s
|
||||
""", (pid, date, start))
|
||||
existing = cur.fetchone()
|
||||
|
||||
if existing:
|
||||
# Update existing entry (e.g., to add training type mapping)
|
||||
cur.execute("""
|
||||
UPDATE activity_log
|
||||
SET end_time = %s,
|
||||
activity_type = %s,
|
||||
duration_min = %s,
|
||||
kcal_active = %s,
|
||||
kcal_resting = %s,
|
||||
hr_avg = %s,
|
||||
hr_max = %s,
|
||||
distance_km = %s,
|
||||
training_type_id = %s,
|
||||
training_category = %s,
|
||||
training_subcategory = %s
|
||||
WHERE id = %s
|
||||
""", (
|
||||
row.get('End',''), wtype, duration_min,
|
||||
kj(row.get('Aktive Energie (kJ)','')),
|
||||
kj(row.get('Ruheeinträge (kJ)','')),
|
||||
tf(row.get('Durchschn. Herzfrequenz (count/min)','')),
|
||||
tf(row.get('Max. Herzfrequenz (count/min)','')),
|
||||
tf(row.get('Distanz (km)','')),
|
||||
training_type_id, training_category, training_subcategory,
|
||||
existing['id']
|
||||
))
|
||||
skipped += 1 # Count as skipped (not newly inserted)
|
||||
else:
|
||||
# Insert new entry
|
||||
cur.execute("""INSERT INTO activity_log
|
||||
(id,profile_id,date,start_time,end_time,activity_type,duration_min,kcal_active,kcal_resting,
|
||||
hr_avg,hr_max,distance_km,source,training_type_id,training_category,training_subcategory,created)
|
||||
|
|
@ -266,5 +331,7 @@ async def import_activity_csv(file: UploadFile=File(...), x_profile_id: Optional
|
|||
tf(row.get('Distanz (km)','')),
|
||||
training_type_id,training_category,training_subcategory))
|
||||
inserted+=1
|
||||
except: skipped+=1
|
||||
except Exception as e:
|
||||
logger.warning(f"Import row failed: {e}")
|
||||
skipped+=1
|
||||
return {"inserted":inserted,"skipped":skipped,"message":f"{inserted} Trainings importiert"}
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user