Fix multi-sport activity import (#289)#313
Merged
tcgoetz merged 5 commits intoMay 14, 2026
Merged
Conversation
Multi-sport activities (triathlon / brick sessions) are stored by Garmin as a single FIT file containing multiple `session` messages plus child sessions for transitions. Previously the importer wrote every session under the bare parent activity_id, so the last session silently overwrote all the others and laps/records/HR zones/devices ended up attached to the wrong activity (or dropped entirely when the hr_zones_timer processing order created empty rows that blocked the real lap writes). This change detects multi-sport files by checking `len(fit_file.session) > 1` and, when detected: - creates one activity row per session with a suffixed id `<parent>_<n>` (e.g. 22638574127_1 ... _5 for swim→T1→run→T2→swim) - derives each session's `stop_time` from `start_time + total_elapsed_time` (Garmin reuses the parent start_time as every child's `timestamp`, which would otherwise result in stop_time <= start_time) - partitions laps by session using each session's `num_laps` and rewrites `lap_num` to be session-local (0..N per session), so every child has its own contiguous lap sequence - partitions records (GPS trackpoints) by matching their timestamp against each session's time window - remaps session/lap HR-zone writes to the correct child activity_id - associates device entries with every child activity `_write_lap_entry` was also switched to `ActivityLaps.s_insert_or_update` so lap data correctly coalesces with the hr_zones_timer rows that the generic message dispatcher creates earlier in the import; the same `stop_time` correction applies when the file is multi-sport. Single-session FIT files go through the exact same code path as before. Extends and supersedes the single-session subset fixed in tcgoetz#310 (same root cause for `stop_time`, lap-ordering race, and the add-vs-insert-or-update issue). Fixes tcgoetz#289
Owner
|
Please re-target this Pr against the develop branch. |
Author
|
hello @tcgoetz , base branch update to develop! thanks |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Multi-sport activities (triathlon / brick sessions) are stored by Garmin as a single FIT file containing multiple
sessionmessages plus child sessions for transitions. Previously the importer wrote every session under the bare parent activity_id, so the last session silently overwrote all the others and laps/records/HR zones/devices ended up attached to the wrong activity (or dropped entirely when the hr_zones_timer processing order created empty rows that blocked the real lap writes).This change detects multi-sport files by checking
len(fit_file.session) > 1and, when detected:<parent>_<n>(e.g. 22638574127_1 ... _5 for swim→T1→run→T2→swim)stop_timefromstart_time + total_elapsed_time(Garmin reuses the parent start_time as every child'stimestamp, which would otherwise result in stop_time <= start_time)num_lapsand rewriteslap_numto be session-local (0..N per session), so every child has its own contiguous lap sequence_write_lap_entrywas also switched toActivityLaps.s_insert_or_updateso lap data correctly coalesces with the hr_zones_timer rows that the generic message dispatcher creates earlier in the import; the samestop_timecorrection applies when the file is multi-sport.Single-session FIT files go through the exact same code path as before.
Extends and supersedes the single-session subset fixed in #310 (same root cause for
stop_time, lap-ordering race, and the add-vs-insert-or-update issue).Fixes #289