fix: sleep import groups segments by gap instead of date boundary
All checks were successful
Deploy Development / deploy (push) Successful in 48s
Build Test / lint-backend (push) Successful in 0s
Build Test / build-frontend (push) Successful in 13s

Problem: Segments crossing midnight were split into different nights
- 22:30-23:15 (21.03) → assigned to 21.03
- 00:30-02:45 (22.03) → assigned to 22.03
But both belong to the same night (21/22.03)!

Solution: Gap-based grouping
- Sort segments chronologically
- Group segments with gap < 2 hours
- Night date = wake_time.date() (last segment's end date)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Lars 2026-03-22 12:09:25 +01:00
parent b1a92c01fc
commit 9a9c597187

View File

@ -484,13 +484,20 @@ async def import_apple_health_sleep(
'phase': phase_en 'phase': phase_en
}) })
# Group by night (wake date) # Sort segments chronologically
nights = {} segments.sort(key=lambda s: s['start'])
for seg in segments:
wake_date = seg['end'].date() # Date of waking up
if wake_date not in nights: # Group segments into nights (gap-based)
nights[wake_date] = { # If gap between segments > 2 hours → new night
nights = []
current_night = None
for seg in segments:
# Start new night if:
# 1. First segment
# 2. Gap > 2 hours since last segment
if current_night is None or (seg['start'] - current_night['wake_time']).total_seconds() > 7200:
current_night = {
'bedtime': seg['start'], 'bedtime': seg['start'],
'wake_time': seg['end'], 'wake_time': seg['end'],
'segments': [], 'segments': [],
@ -499,21 +506,28 @@ async def import_apple_health_sleep(
'light_minutes': 0, 'light_minutes': 0,
'awake_minutes': 0 'awake_minutes': 0
} }
nights.append(current_night)
night = nights[wake_date] # Add segment to current night
night['segments'].append(seg) current_night['segments'].append(seg)
night['wake_time'] = max(night['wake_time'], seg['end']) # Latest wake time current_night['wake_time'] = max(current_night['wake_time'], seg['end'])
night['bedtime'] = min(night['bedtime'], seg['start']) # Earliest bed time current_night['bedtime'] = min(current_night['bedtime'], seg['start'])
# Sum phases # Sum phases
if seg['phase'] == 'deep': if seg['phase'] == 'deep':
night['deep_minutes'] += seg['duration_min'] current_night['deep_minutes'] += seg['duration_min']
elif seg['phase'] == 'rem': elif seg['phase'] == 'rem':
night['rem_minutes'] += seg['duration_min'] current_night['rem_minutes'] += seg['duration_min']
elif seg['phase'] == 'light': elif seg['phase'] == 'light':
night['light_minutes'] += seg['duration_min'] current_night['light_minutes'] += seg['duration_min']
elif seg['phase'] == 'awake': elif seg['phase'] == 'awake':
night['awake_minutes'] += seg['duration_min'] current_night['awake_minutes'] += seg['duration_min']
# Convert nights list to dict with wake_date as key
nights_dict = {}
for night in nights:
wake_date = night['wake_time'].date() # Date when you woke up
nights_dict[wake_date] = night
# Insert nights # Insert nights
imported = 0 imported = 0
@ -522,7 +536,7 @@ async def import_apple_health_sleep(
with get_db() as conn: with get_db() as conn:
cur = get_cursor(conn) cur = get_cursor(conn)
for date, night in nights.items(): for date, night in nights_dict.items():
# Calculate total duration (sum of all phases) # Calculate total duration (sum of all phases)
duration_minutes = ( duration_minutes = (
night['deep_minutes'] + night['deep_minutes'] +
@ -593,6 +607,6 @@ async def import_apple_health_sleep(
return { return {
"imported": imported, "imported": imported,
"skipped": skipped, "skipped": skipped,
"total_nights": len(nights), "total_nights": len(nights_dict),
"message": f"{imported} Nächte importiert, {skipped} übersprungen (manuelle Einträge)" "message": f"{imported} Nächte importiert, {skipped} übersprungen (manuelle Einträge)"
} }