@sports-alliance/sports-lib 7.0.7 → 7.0.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.editorconfig +14 -0
- package/.eslintignore +4 -0
- package/.eslintrc.js +9 -0
- package/.prettierignore +25 -0
- package/.prettierrc.json +12 -0
- package/CODE_OF_CONDUCT.md +76 -0
- package/jest.config.js +23 -0
- package/lib/esm/index.js +12034 -152
- package/package.json +3 -2
- package/tsconfig.cjs.json +7 -0
- package/tsconfig.esm.json +8 -0
- package/tsconfig.lib.json +8 -0
- package/lib/esm/activities/activity-parsing-options.js +0 -19
- package/lib/esm/activities/activity.interface.js +0 -1
- package/lib/esm/activities/activity.js +0 -281
- package/lib/esm/activities/activity.json.interface.js +0 -1
- package/lib/esm/activities/activity.spec.js +0 -348
- package/lib/esm/activities/activity.types.js +0 -1046
- package/lib/esm/activities/activityt-types.spec.js +0 -16
- package/lib/esm/activities/devices/device.interface.js +0 -1
- package/lib/esm/activities/devices/device.js +0 -26
- package/lib/esm/activities/devices/device.json.interface.js +0 -1
- package/lib/esm/constants/constants.js +0 -8
- package/lib/esm/creators/creator.interface.js +0 -1
- package/lib/esm/creators/creator.js +0 -37
- package/lib/esm/creators/creator.json.interface.js +0 -1
- package/lib/esm/creators/creator.spec.js +0 -25
- package/lib/esm/data/data-active-lap.js +0 -4
- package/lib/esm/data/data-active-lengths.js +0 -4
- package/lib/esm/data/data-aerobic-training-effect.js +0 -8
- package/lib/esm/data/data-anaerobic-training-effect.js +0 -8
- package/lib/esm/data/data-stance-time-balance-left.js +0 -4
- package/lib/esm/data/data-stance-time-balance-right.js +0 -4
- package/lib/esm/data/data-stance-time-balance.js +0 -3
- package/lib/esm/data/data-store.spec.js +0 -78
- package/lib/esm/data/data-total-cycles.js +0 -4
- package/lib/esm/data/data.absolute-pressure.js +0 -5
- package/lib/esm/data/data.accumulated-power.js +0 -8
- package/lib/esm/data/data.activity-types.js +0 -7
- package/lib/esm/data/data.air-power-avg.js +0 -4
- package/lib/esm/data/data.air-power-max.js +0 -4
- package/lib/esm/data/data.air-power-min.js +0 -4
- package/lib/esm/data/data.air-power.js +0 -8
- package/lib/esm/data/data.alti-baro-profile.js +0 -4
- package/lib/esm/data/data.altitude-avg.js +0 -4
- package/lib/esm/data/data.altitude-gps.js +0 -5
- package/lib/esm/data/data.altitude-max.js +0 -4
- package/lib/esm/data/data.altitude-min.js +0 -4
- package/lib/esm/data/data.altitude-smooth.js +0 -4
- package/lib/esm/data/data.altitude.js +0 -8
- package/lib/esm/data/data.array.js +0 -13
- package/lib/esm/data/data.ascent-time.js +0 -4
- package/lib/esm/data/data.ascent.js +0 -4
- package/lib/esm/data/data.auto-lap-distance.js +0 -5
- package/lib/esm/data/data.auto-lap-duration.js +0 -4
- package/lib/esm/data/data.auto-lap-used.js +0 -7
- package/lib/esm/data/data.auto-pause-used.js +0 -7
- package/lib/esm/data/data.avg-stride-length.js +0 -4
- package/lib/esm/data/data.balance.js +0 -6
- package/lib/esm/data/data.bare.js +0 -4
- package/lib/esm/data/data.battery-charge.js +0 -8
- package/lib/esm/data/data.battery-consumption.js +0 -8
- package/lib/esm/data/data.battery-current.js +0 -8
- package/lib/esm/data/data.battery-life-estimation.js +0 -4
- package/lib/esm/data/data.battery-voltage.js +0 -8
- package/lib/esm/data/data.bike-pod-used.js +0 -7
- package/lib/esm/data/data.boolean.js +0 -9
- package/lib/esm/data/data.cadence-avg.js +0 -4
- package/lib/esm/data/data.cadence-max.js +0 -4
- package/lib/esm/data/data.cadence-min.js +0 -4
- package/lib/esm/data/data.cadence.js +0 -8
- package/lib/esm/data/data.cycling-dynamics.js +0 -3
- package/lib/esm/data/data.cycling-position.js +0 -37
- package/lib/esm/data/data.cycling-seated-time.js +0 -5
- package/lib/esm/data/data.cycling-standing-time.js +0 -5
- package/lib/esm/data/data.descent-time.js +0 -4
- package/lib/esm/data/data.descent.js +0 -4
- package/lib/esm/data/data.description.js +0 -4
- package/lib/esm/data/data.device-location.js +0 -4
- package/lib/esm/data/data.device-names.js +0 -7
- package/lib/esm/data/data.distance.js +0 -15
- package/lib/esm/data/data.duration.js +0 -56
- package/lib/esm/data/data.ehpe.js +0 -5
- package/lib/esm/data/data.enabled-navigation-systems.js +0 -4
- package/lib/esm/data/data.end-altitude.js +0 -4
- package/lib/esm/data/data.end-position.js +0 -4
- package/lib/esm/data/data.energy.js +0 -8
- package/lib/esm/data/data.epoc.js +0 -5
- package/lib/esm/data/data.event.js +0 -3
- package/lib/esm/data/data.evpe.js +0 -4
- package/lib/esm/data/data.feeling.js +0 -15
- package/lib/esm/data/data.foot-pod-used.js +0 -7
- package/lib/esm/data/data.form-power.js +0 -4
- package/lib/esm/data/data.fused-altitude.js +0 -7
- package/lib/esm/data/data.fused-location.js +0 -7
- package/lib/esm/data/data.gnss-distance.js +0 -4
- package/lib/esm/data/data.grade-adjusted-pace-avg.js +0 -15
- package/lib/esm/data/data.grade-adjusted-pace-max.js +0 -15
- package/lib/esm/data/data.grade-adjusted-pace-min.js +0 -15
- package/lib/esm/data/data.grade-adjusted-pace.js +0 -25
- package/lib/esm/data/data.grade-adjusted-speed-avg.js +0 -46
- package/lib/esm/data/data.grade-adjusted-speed-max.js +0 -46
- package/lib/esm/data/data.grade-adjusted-speed-min.js +0 -46
- package/lib/esm/data/data.grade-adjusted-speed.js +0 -78
- package/lib/esm/data/data.grade-smooth.js +0 -7
- package/lib/esm/data/data.grade.js +0 -7
- package/lib/esm/data/data.ground-time.js +0 -8
- package/lib/esm/data/data.heart-rate-avg.js +0 -4
- package/lib/esm/data/data.heart-rate-max.js +0 -4
- package/lib/esm/data/data.heart-rate-min.js +0 -4
- package/lib/esm/data/data.heart-rate-used.js +0 -7
- package/lib/esm/data/data.heart-rate-zone-five-duration.js +0 -4
- package/lib/esm/data/data.heart-rate-zone-four-duration.js +0 -4
- package/lib/esm/data/data.heart-rate-zone-one-duration.js +0 -4
- package/lib/esm/data/data.heart-rate-zone-three-duration.js +0 -4
- package/lib/esm/data/data.heart-rate-zone-two-duration.js +0 -4
- package/lib/esm/data/data.heart-rate.js +0 -8
- package/lib/esm/data/data.ibi.js +0 -8
- package/lib/esm/data/data.interface.js +0 -5
- package/lib/esm/data/data.js +0 -62
- package/lib/esm/data/data.json.interface.js +0 -1
- package/lib/esm/data/data.latitude-degrees.js +0 -5
- package/lib/esm/data/data.left-balance.js +0 -4
- package/lib/esm/data/data.leg-stiffness.js +0 -16
- package/lib/esm/data/data.longitude-degrees.js +0 -5
- package/lib/esm/data/data.moving-time.js +0 -4
- package/lib/esm/data/data.number-of-satellites.js +0 -7
- package/lib/esm/data/data.number-of.samples.js +0 -4
- package/lib/esm/data/data.number.js +0 -13
- package/lib/esm/data/data.pace-avg.js +0 -8
- package/lib/esm/data/data.pace-max.js +0 -8
- package/lib/esm/data/data.pace-min.js +0 -8
- package/lib/esm/data/data.pace.js +0 -46
- package/lib/esm/data/data.pause.js +0 -4
- package/lib/esm/data/data.peak-epoc.js +0 -7
- package/lib/esm/data/data.peak-training-effect.js +0 -4
- package/lib/esm/data/data.percent.js +0 -4
- package/lib/esm/data/data.pool-length.js +0 -5
- package/lib/esm/data/data.position.interface.js +0 -1
- package/lib/esm/data/data.position.js +0 -18
- package/lib/esm/data/data.power-avg.js +0 -4
- package/lib/esm/data/data.power-down.js +0 -4
- package/lib/esm/data/data.power-intensity-factor.js +0 -5
- package/lib/esm/data/data.power-left.js +0 -4
- package/lib/esm/data/data.power-max.js +0 -4
- package/lib/esm/data/data.power-min.js +0 -4
- package/lib/esm/data/data.power-normalized.js +0 -4
- package/lib/esm/data/data.power-pedal-smoothness-left.js +0 -4
- package/lib/esm/data/data.power-pedal-smoothness-right.js +0 -4
- package/lib/esm/data/data.power-pod-used.js +0 -7
- package/lib/esm/data/data.power-right.js +0 -4
- package/lib/esm/data/data.power-torque-effectiveness-left.js +0 -4
- package/lib/esm/data/data.power-torque-effectiveness-right.js +0 -4
- package/lib/esm/data/data.power-training-stress-score.js +0 -5
- package/lib/esm/data/data.power-up.js +0 -4
- package/lib/esm/data/data.power-work.js +0 -5
- package/lib/esm/data/data.power-zone-five-duration.js +0 -4
- package/lib/esm/data/data.power-zone-four-duration.js +0 -4
- package/lib/esm/data/data.power-zone-one-duration.js +0 -4
- package/lib/esm/data/data.power-zone-three-duration.js +0 -4
- package/lib/esm/data/data.power-zone-two-duration.js +0 -4
- package/lib/esm/data/data.power.js +0 -8
- package/lib/esm/data/data.recovery-time.js +0 -7
- package/lib/esm/data/data.rider-position-change-event.js +0 -8
- package/lib/esm/data/data.right-balance.js +0 -4
- package/lib/esm/data/data.rpe.js +0 -23
- package/lib/esm/data/data.satellite-5-best-snr.js +0 -4
- package/lib/esm/data/data.sea-level-pressure.js +0 -5
- package/lib/esm/data/data.spec.js +0 -25
- package/lib/esm/data/data.speed-avg.js +0 -46
- package/lib/esm/data/data.speed-max.js +0 -46
- package/lib/esm/data/data.speed-min.js +0 -46
- package/lib/esm/data/data.speed-zone-five-duration.js +0 -4
- package/lib/esm/data/data.speed-zone-four-duration.js +0 -4
- package/lib/esm/data/data.speed-zone-one-duration.js +0 -4
- package/lib/esm/data/data.speed-zone-three-duration.js +0 -4
- package/lib/esm/data/data.speed-zone-two-duration.js +0 -4
- package/lib/esm/data/data.speed.js +0 -88
- package/lib/esm/data/data.sport-profile-name.js +0 -4
- package/lib/esm/data/data.stance-time.js +0 -5
- package/lib/esm/data/data.start-altitude.js +0 -4
- package/lib/esm/data/data.start-event.js +0 -4
- package/lib/esm/data/data.start-position.js +0 -4
- package/lib/esm/data/data.step-length.js +0 -4
- package/lib/esm/data/data.steps-old.js +0 -4
- package/lib/esm/data/data.steps.js +0 -4
- package/lib/esm/data/data.stop-all-event.js +0 -4
- package/lib/esm/data/data.stop-event.js +0 -4
- package/lib/esm/data/data.store.export.spec.js +0 -66
- package/lib/esm/data/data.store.js +0 -1213
- package/lib/esm/data/data.string.js +0 -13
- package/lib/esm/data/data.stryd-altitude.js +0 -4
- package/lib/esm/data/data.stryd-distance.js +0 -4
- package/lib/esm/data/data.stryd-speed.js +0 -4
- package/lib/esm/data/data.swim-pace-avg.js +0 -8
- package/lib/esm/data/data.swim-pace-max.js +0 -8
- package/lib/esm/data/data.swim-pace-min.js +0 -8
- package/lib/esm/data/data.swim-pace.js +0 -19
- package/lib/esm/data/data.swolf-25m.js +0 -4
- package/lib/esm/data/data.swolf-50m.js +0 -4
- package/lib/esm/data/data.target-distance.js +0 -4
- package/lib/esm/data/data.target-heart-rate-zone.js +0 -4
- package/lib/esm/data/data.target-power-zone.js +0 -4
- package/lib/esm/data/data.target-speed-zone.js +0 -4
- package/lib/esm/data/data.target-time.js +0 -4
- package/lib/esm/data/data.temperature-avg.js +0 -4
- package/lib/esm/data/data.temperature-max.js +0 -4
- package/lib/esm/data/data.temperature-min.js +0 -4
- package/lib/esm/data/data.temperature.js +0 -8
- package/lib/esm/data/data.time.js +0 -6
- package/lib/esm/data/data.timer-time.js +0 -4
- package/lib/esm/data/data.vertical-oscillation.js +0 -5
- package/lib/esm/data/data.vertical-ratio.js +0 -5
- package/lib/esm/data/data.vertical-speed-avg.js +0 -53
- package/lib/esm/data/data.vertical-speed-max.js +0 -53
- package/lib/esm/data/data.vertical-speed-min.js +0 -53
- package/lib/esm/data/data.vertical-speed.js +0 -103
- package/lib/esm/data/data.vo2-max.js +0 -7
- package/lib/esm/data/ibi/data.ibi.filters.js +0 -52
- package/lib/esm/data/ibi/data.ibi.js +0 -98
- package/lib/esm/data/ibi/data.ibi.spec.js +0 -66
- package/lib/esm/duration/duration.class.abstract.js +0 -36
- package/lib/esm/duration/duration.class.interface.js +0 -1
- package/lib/esm/errors/duration-exceeded-event-lib.error.js +0 -9
- package/lib/esm/errors/empty-event-sports-libs.error.js +0 -9
- package/lib/esm/errors/event-lib.error.js +0 -8
- package/lib/esm/errors/lib.error.js +0 -6
- package/lib/esm/errors/parsing-event-lib.error.js +0 -9
- package/lib/esm/events/adapters/exporters/exporter.gpx.js +0 -75
- package/lib/esm/events/adapters/exporters/exporter.interface.js +0 -1
- package/lib/esm/events/adapters/exporters/exporter.json.js +0 -20
- package/lib/esm/events/adapters/file-type.enum.js +0 -7
- package/lib/esm/events/adapters/importers/fit/importer.fit.ant-plus.device.names.js +0 -25
- package/lib/esm/events/adapters/importers/fit/importer.fit.coros.device.names.js +0 -5
- package/lib/esm/events/adapters/importers/fit/importer.fit.development.device.names.js +0 -4
- package/lib/esm/events/adapters/importers/fit/importer.fit.garmin.device.names.js +0 -462
- package/lib/esm/events/adapters/importers/fit/importer.fit.garmin.profile.data.js +0 -877
- package/lib/esm/events/adapters/importers/fit/importer.fit.garmin.profile.mapper.js +0 -106
- package/lib/esm/events/adapters/importers/fit/importer.fit.garmin.profile.mapper.spec.js +0 -32
- package/lib/esm/events/adapters/importers/fit/importer.fit.hammerhead.device.names.js +0 -4
- package/lib/esm/events/adapters/importers/fit/importer.fit.integration.spec.js +0 -44
- package/lib/esm/events/adapters/importers/fit/importer.fit.js +0 -978
- package/lib/esm/events/adapters/importers/fit/importer.fit.lezyne.device.names.js +0 -3
- package/lib/esm/events/adapters/importers/fit/importer.fit.magellan.device.names.js +0 -5
- package/lib/esm/events/adapters/importers/fit/importer.fit.mapper.js +0 -231
- package/lib/esm/events/adapters/importers/fit/importer.fit.saris.device.names.js +0 -3
- package/lib/esm/events/adapters/importers/fit/importer.fit.spec.js +0 -326
- package/lib/esm/events/adapters/importers/fit/importer.fit.srm.device.names.js +0 -7
- package/lib/esm/events/adapters/importers/fit/importer.fit.suunto.device.names.js +0 -16
- package/lib/esm/events/adapters/importers/fit/importer.fit.wahoo.device.names.js +0 -10
- package/lib/esm/events/adapters/importers/gpx/gx-parser.js +0 -36
- package/lib/esm/events/adapters/importers/gpx/importer.gpx.integration.spec.js +0 -44
- package/lib/esm/events/adapters/importers/gpx/importer.gpx.js +0 -102
- package/lib/esm/events/adapters/importers/gpx/importer.gpx.mapper.js +0 -142
- package/lib/esm/events/adapters/importers/gpx/importer.gpx.spec.js +0 -71
- package/lib/esm/events/adapters/importers/json/importer.json.js +0 -160
- package/lib/esm/events/adapters/importers/sample-info.interface.js +0 -1
- package/lib/esm/events/adapters/importers/suunto/importer.suunto.activity.ids.js +0 -85
- package/lib/esm/events/adapters/importers/suunto/importer.suunto.device.names.js +0 -26
- package/lib/esm/events/adapters/importers/suunto/importer.suunto.integration.spec.js +0 -44
- package/lib/esm/events/adapters/importers/suunto/importer.suunto.json.js +0 -717
- package/lib/esm/events/adapters/importers/suunto/importer.suunto.sml.js +0 -125
- package/lib/esm/events/adapters/importers/tcx/importer.tcx.integration.spec.js +0 -46
- package/lib/esm/events/adapters/importers/tcx/importer.tcx.js +0 -442
- package/lib/esm/events/adapters/importers/tcx/importer.tcx.mapper.js +0 -91
- package/lib/esm/events/adapters/importers/tcx/utils.tcx.js +0 -36
- package/lib/esm/events/event.interface.js +0 -1
- package/lib/esm/events/event.js +0 -123
- package/lib/esm/events/event.json.interface.js +0 -1
- package/lib/esm/events/event.spec.js +0 -51
- package/lib/esm/events/utilities/activity.utilities.js +0 -1841
- package/lib/esm/events/utilities/activity.utilities.spec.js +0 -373
- package/lib/esm/events/utilities/event.utilities.js +0 -57
- package/lib/esm/events/utilities/grade-calculator/grade-calculator.js +0 -121
- package/lib/esm/events/utilities/grade-calculator/grade-calculator.spec.js +0 -93
- package/lib/esm/events/utilities/grade-calculator/low-pass-filter.js +0 -86
- package/lib/esm/events/utilities/helpers.js +0 -162
- package/lib/esm/geodesy/adapters/adapter.interface.js +0 -1
- package/lib/esm/geodesy/adapters/geolib.adapter.js +0 -28
- package/lib/esm/id/id.abstract.class.js +0 -9
- package/lib/esm/id/id.class.interface.js +0 -1
- package/lib/esm/id/id.class.spec.js +0 -13
- package/lib/esm/intensity-zones/intensity-zones.interface.js +0 -1
- package/lib/esm/intensity-zones/intensity-zones.js +0 -29
- package/lib/esm/intensity-zones/intensity-zones.json.interface.js +0 -1
- package/lib/esm/intensity-zones/intensity-zones.spec.js +0 -30
- package/lib/esm/laps/lap.interface.js +0 -1
- package/lib/esm/laps/lap.js +0 -29
- package/lib/esm/laps/lap.json.interface.js +0 -1
- package/lib/esm/laps/lap.types.js +0 -52
- package/lib/esm/meta-data/event-meta-data.interface.js +0 -6
- package/lib/esm/meta-data/meta-data.js +0 -57
- package/lib/esm/meta-data/meta-data.json.interface.js +0 -1
- package/lib/esm/privacy/privacy.class.interface.js +0 -5
- package/lib/esm/serializable/serializable.class.interface.js +0 -1
- package/lib/esm/service-tokens/oauth1-service-token.interface.js +0 -1
- package/lib/esm/service-tokens/oauth2-service-token.interface.js +0 -1
- package/lib/esm/specs/activities-parsing.integration.spec.js +0 -1847
- package/lib/esm/specs/activity-duration-stream.integration.spec.js +0 -57
- package/lib/esm/specs/fixtures/streams/strava/rides/3171472783.json +0 -52534
- package/lib/esm/specs/fixtures/streams/strava/rides/3171487458.json +0 -78818
- package/lib/esm/specs/fixtures/streams/strava/rides/343080886.json +0 -105090
- package/lib/esm/specs/fixtures/streams/strava/rides/5910143591.json +0 -110711
- package/lib/esm/specs/fixtures/streams/strava/runs/2451375851.json +0 -74846
- package/lib/esm/specs/fixtures/streams/strava/runs/2709634581.json +0 -66817
- package/lib/esm/specs/fixtures/streams/strava/runs/3156040843.json +0 -17594
- package/lib/esm/specs/fixtures/streams/strava/runs/3182900697.json +0 -17322
- package/lib/esm/specs/fixtures/streams/strava/runs/3183465494.json +0 -20463
- package/lib/esm/specs/fixtures/streams/strava/runs/3183490558.json +0 -58202
- package/lib/esm/specs/spec-utils.js +0 -159
- package/lib/esm/specs/strava-streams-compliance.spec.js +0 -951
- package/lib/esm/stats/stats.class.abstract.js +0 -32
- package/lib/esm/stats/stats.class.interface.js +0 -1
- package/lib/esm/stats/stats.json.interface.js +0 -1
- package/lib/esm/streams/compressed.stream.interface.js +0 -12
- package/lib/esm/streams/ibi-stream.js +0 -43
- package/lib/esm/streams/low-pass.stream.filter.js +0 -9
- package/lib/esm/streams/stream.filter.interface.js +0 -1
- package/lib/esm/streams/stream.interface.js +0 -1
- package/lib/esm/streams/stream.js +0 -72
- package/lib/esm/streams/stream.spec.js +0 -168
- package/lib/esm/tiles/tile.settings.interface.js +0 -41
- package/lib/esm/users/settings/dashboard/user.dashboard.settings.interface.js +0 -13
- package/lib/esm/users/settings/user.app.settings.interface.js +0 -5
- package/lib/esm/users/settings/user.chart.settings.interface.js +0 -23
- package/lib/esm/users/settings/user.map.settings.interface.js +0 -22
- package/lib/esm/users/settings/user.my-tracks.settings.interface.js +0 -1
- package/lib/esm/users/settings/user.settings.interface.js +0 -1
- package/lib/esm/users/settings/user.stats-settings.interface.js +0 -1
- package/lib/esm/users/settings/user.summaries.settings.interface.js +0 -1
- package/lib/esm/users/settings/user.unit.settings.interface.js +0 -73
- package/lib/esm/users/user.account.privileges.interface.js +0 -1
- package/lib/esm/users/user.export-to-csv.settings.interface.js +0 -1
- package/lib/esm/users/user.interface.js +0 -1
- package/lib/esm/users/user.js +0 -38
- package/lib/esm/users/user.service.meta.interface.js +0 -1
|
@@ -1,978 +0,0 @@
|
|
|
1
|
-
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
2
|
-
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
3
|
-
return new (P || (P = Promise))(function (resolve, reject) {
|
|
4
|
-
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
5
|
-
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
6
|
-
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
7
|
-
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
8
|
-
});
|
|
9
|
-
};
|
|
10
|
-
import { Event } from '../../../event';
|
|
11
|
-
import { Activity } from '../../../../activities/activity';
|
|
12
|
-
import { Lap } from '../../../../laps/lap';
|
|
13
|
-
import { Creator } from '../../../../creators/creator';
|
|
14
|
-
import { ActivityTypes, ActivityTypesMoving } from '../../../../activities/activity.types';
|
|
15
|
-
import { DataDuration } from '../../../../data/data.duration';
|
|
16
|
-
import { DataEnergy } from '../../../../data/data.energy';
|
|
17
|
-
import { DataDistance } from '../../../../data/data.distance';
|
|
18
|
-
import { ImporterFitSuuntoDeviceNames } from './importer.fit.suunto.device.names';
|
|
19
|
-
import { GarminProfileMapper } from './importer.fit.garmin.profile.mapper';
|
|
20
|
-
import { DataPause } from '../../../../data/data.pause';
|
|
21
|
-
import { DataCadenceAvg } from '../../../../data/data.cadence-avg';
|
|
22
|
-
import { DataPowerAvg } from '../../../../data/data.power-avg';
|
|
23
|
-
import { DataSpeedAvg } from '../../../../data/data.speed-avg';
|
|
24
|
-
import { DataCadenceMax } from '../../../../data/data.cadence-max';
|
|
25
|
-
import { DataPowerMax } from '../../../../data/data.power-max';
|
|
26
|
-
import { DataAscent } from '../../../../data/data.ascent';
|
|
27
|
-
import { DataDescent } from '../../../../data/data.descent';
|
|
28
|
-
import { DataHeartRateAvg } from '../../../../data/data.heart-rate-avg';
|
|
29
|
-
import { DataHeartRateMax } from '../../../../data/data.heart-rate-max';
|
|
30
|
-
import { DataSpeedMax } from '../../../../data/data.speed-max';
|
|
31
|
-
import { LapTypes } from '../../../../laps/lap.types';
|
|
32
|
-
import { DataHeartRateMin } from '../../../../data/data.heart-rate-min';
|
|
33
|
-
import { DataPowerMin } from '../../../../data/data.power-min';
|
|
34
|
-
import { DataAerobicTrainingEffect } from '../../../../data/data-aerobic-training-effect';
|
|
35
|
-
import { FITSampleMapper } from './importer.fit.mapper';
|
|
36
|
-
import { isNumber, isNumberOrString } from '../../../utilities/helpers';
|
|
37
|
-
import { EventUtilities } from '../../../utilities/event.utilities';
|
|
38
|
-
import { IBIStream } from '../../../../streams/ibi-stream';
|
|
39
|
-
import { Device } from '../../../../activities/devices/device';
|
|
40
|
-
import { ImporterFitAntPlusDeviceNames } from './importer.fit.ant-plus.device.names';
|
|
41
|
-
import { DataRecoveryTime } from '../../../../data/data.recovery-time';
|
|
42
|
-
import { DataPeakEPOC } from '../../../../data/data.peak-epoc';
|
|
43
|
-
import { DataFeeling } from '../../../../data/data.feeling';
|
|
44
|
-
import { DataTemperatureMax } from '../../../../data/data.temperature-max';
|
|
45
|
-
import { DataTemperatureMin } from '../../../../data/data.temperature-min';
|
|
46
|
-
import { DataTemperatureAvg } from '../../../../data/data.temperature-avg';
|
|
47
|
-
import { DataSpeedMin } from '../../../../data/data.speed-min';
|
|
48
|
-
import { DataCadenceMin } from '../../../../data/data.cadence-min';
|
|
49
|
-
import { DataSWOLF25m } from '../../../../data/data.swolf-25m';
|
|
50
|
-
import { DataDescription } from '../../../../data/data.description';
|
|
51
|
-
import { DataVO2Max } from '../../../../data/data.vo2-max';
|
|
52
|
-
import { IntensityZones } from '../../../../intensity-zones/intensity-zones';
|
|
53
|
-
import { DataHeartRate } from '../../../../data/data.heart-rate';
|
|
54
|
-
import { DataPower } from '../../../../data/data.power';
|
|
55
|
-
import { DataSpeed } from '../../../../data/data.speed';
|
|
56
|
-
import { DataHeartRateZoneOneDuration } from '../../../../data/data.heart-rate-zone-one-duration';
|
|
57
|
-
import { DataHeartRateZoneTwoDuration } from '../../../../data/data.heart-rate-zone-two-duration';
|
|
58
|
-
import { DataHeartRateZoneThreeDuration } from '../../../../data/data.heart-rate-zone-three-duration';
|
|
59
|
-
import { DataHeartRateZoneFourDuration } from '../../../../data/data.heart-rate-zone-four-duration';
|
|
60
|
-
import { DataHeartRateZoneFiveDuration } from '../../../../data/data.heart-rate-zone-five-duration';
|
|
61
|
-
import { DataPowerZoneOneDuration } from '../../../../data/data.power-zone-one-duration';
|
|
62
|
-
import { DataPowerZoneTwoDuration } from '../../../../data/data.power-zone-two-duration';
|
|
63
|
-
import { DataPowerZoneThreeDuration } from '../../../../data/data.power-zone-three-duration';
|
|
64
|
-
import { DataPowerZoneFourDuration } from '../../../../data/data.power-zone-four-duration';
|
|
65
|
-
import { DataPowerZoneFiveDuration } from '../../../../data/data.power-zone-five-duration';
|
|
66
|
-
import { DataSpeedZoneOneDuration } from '../../../../data/data.speed-zone-one-duration';
|
|
67
|
-
import { DataSpeedZoneTwoDuration } from '../../../../data/data.speed-zone-two-duration';
|
|
68
|
-
import { DataSpeedZoneThreeDuration } from '../../../../data/data.speed-zone-three-duration';
|
|
69
|
-
import { DataSpeedZoneFourDuration } from '../../../../data/data.speed-zone-four-duration';
|
|
70
|
-
import { DataSpeedZoneFiveDuration } from '../../../../data/data.speed-zone-five-duration';
|
|
71
|
-
import { EmptyEventLibError } from '../../../../errors/empty-event-sports-libs.error';
|
|
72
|
-
import { DataStartEvent } from '../../../../data/data.start-event';
|
|
73
|
-
import { DataStopEvent } from '../../../../data/data.stop-event';
|
|
74
|
-
import { DataStopAllEvent } from '../../../../data/data.stop-all-event';
|
|
75
|
-
import { DataMovingTime } from '../../../../data/data.moving-time';
|
|
76
|
-
import { ActivityUtilities } from '../../../utilities/activity.utilities';
|
|
77
|
-
import { DataTimerTime } from '../../../../data/data.timer-time';
|
|
78
|
-
import { DataTotalCycles } from '../../../../data/data-total-cycles';
|
|
79
|
-
import { DataPoolLength } from '../../../../data/data.pool-length';
|
|
80
|
-
import { DataActiveLengths } from '../../../../data/data-active-lengths';
|
|
81
|
-
import { DataActiveLap } from '../../../../data/data-active-lap';
|
|
82
|
-
import { DataSWOLF50m } from '../../../../data/data.swolf-50m';
|
|
83
|
-
import { FileType } from '../../file-type.enum';
|
|
84
|
-
import { DataPowerTorqueEffectivenessLeft } from '../../../../data/data.power-torque-effectiveness-left';
|
|
85
|
-
import { DataPowerTorqueEffectivenessRight } from '../../../../data/data.power-torque-effectiveness-right';
|
|
86
|
-
import { DataPowerPedalSmoothnessLeft } from '../../../../data/data.power-pedal-smoothness-left';
|
|
87
|
-
import { DataPowerPedalSmoothnessRight } from '../../../../data/data.power-pedal-smoothness-right';
|
|
88
|
-
import { DataPowerNormalized } from '../../../../data/data.power-normalized';
|
|
89
|
-
import { DataPowerIntensityFactor } from '../../../../data/data.power-intensity-factor';
|
|
90
|
-
import { DataPowerTrainingStressScore } from '../../../../data/data.power-training-stress-score';
|
|
91
|
-
import { DataPowerWork } from '../../../../data/data.power-work';
|
|
92
|
-
import { DataCyclingStandingTime } from '../../../../data/data.cycling-standing-time';
|
|
93
|
-
import { DataCyclingSeatedTime } from '../../../../data/data.cycling-seated-time';
|
|
94
|
-
import { RiderPosition } from '../../../../data/data.cycling-position';
|
|
95
|
-
import { DataRiderPositionChangeEvent } from '../../../../data/data.rider-position-change-event';
|
|
96
|
-
import { DataStanceTime } from '../../../../data/data.stance-time';
|
|
97
|
-
import { DataVerticalOscillation } from '../../../../data/data.vertical-oscillation';
|
|
98
|
-
import { DataVerticalRatio } from '../../../../data/data.vertical-ratio';
|
|
99
|
-
import { DataAvgStrideLength } from '../../../../data/data.avg-stride-length';
|
|
100
|
-
import { DataAnaerobicTrainingEffect } from '../../../../data/data-anaerobic-training-effect';
|
|
101
|
-
import { ImporterFitWahooDeviceNames } from './importer.fit.wahoo.device.names';
|
|
102
|
-
import { ImporterFitCorosDeviceNames } from './importer.fit.coros.device.names';
|
|
103
|
-
import { ImporterFitSrmDeviceNames } from './importer.fit.srm.device.names';
|
|
104
|
-
import { ActivityParsingOptions } from '../../../../activities/activity-parsing-options';
|
|
105
|
-
import { ImporterFitHammerheadDeviceNames } from './importer.fit.hammerhead.device.names';
|
|
106
|
-
import { ImporterFitLezyneDeviceNames } from './importer.fit.lezyne.device.names';
|
|
107
|
-
import { ImporterFitMagellanDeviceNames } from './importer.fit.magellan.device.names';
|
|
108
|
-
import { ImporterFitSarisDeviceNames } from './importer.fit.saris.device.names';
|
|
109
|
-
import { ParsingEventLibError } from '../../../../errors/parsing-event-lib.error';
|
|
110
|
-
import { DataPowerDown } from '../../../../data/data.power-down';
|
|
111
|
-
import { DataPowerUp } from '../../../../data/data.power-up';
|
|
112
|
-
import { ImporterFitDevelopmentDeviceNames } from './importer.fit.development.device.names';
|
|
113
|
-
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
|
114
|
-
// @ts-ignore
|
|
115
|
-
import FitFileParser from 'fit-file-parser';
|
|
116
|
-
// Threshold to detect that session.timestamp are not trustable (when exceeding 15% of session.total_elapsed_time)
|
|
117
|
-
const INVALID_DATES_ELAPSED_TIME_RATIO_THRESHOLD = 1.15;
|
|
118
|
-
export class EventImporterFIT {
|
|
119
|
-
static getFromArrayBuffer(arrayBuffer_1) {
|
|
120
|
-
return __awaiter(this, arguments, void 0, function* (arrayBuffer, options = ActivityParsingOptions.DEFAULT, name = 'New Event') {
|
|
121
|
-
return new Promise((resolve, reject) => {
|
|
122
|
-
const fitFileParser = new FitFileParser({
|
|
123
|
-
force: true,
|
|
124
|
-
speedUnit: 'm/s',
|
|
125
|
-
lengthUnit: 'm',
|
|
126
|
-
temperatureUnit: 'celsius',
|
|
127
|
-
elapsedRecordField: false,
|
|
128
|
-
mode: 'both'
|
|
129
|
-
});
|
|
130
|
-
fitFileParser.parse(arrayBuffer, (error, fitDataObject) => {
|
|
131
|
-
var _a;
|
|
132
|
-
if (error) {
|
|
133
|
-
// For now, assume any error from parser on this file means it's broken/empty in a way we treat as EmptyEventLibError
|
|
134
|
-
// to satisfy existing tests. Or ideally we wrap in a generic EventLibError.
|
|
135
|
-
// But test expects EmptyEventLibError.
|
|
136
|
-
reject(new EmptyEventLibError());
|
|
137
|
-
return;
|
|
138
|
-
}
|
|
139
|
-
if (!fitDataObject || !fitDataObject.sessions) {
|
|
140
|
-
reject(new EmptyEventLibError());
|
|
141
|
-
return;
|
|
142
|
-
}
|
|
143
|
-
// Check if we have length data at the top level (new parser behavior or missing mapping)
|
|
144
|
-
if (fitDataObject.lengths && fitDataObject.lengths.length > 0) {
|
|
145
|
-
(_a = fitDataObject.sessions) === null || _a === void 0 ? void 0 : _a.forEach((session) => {
|
|
146
|
-
var _a;
|
|
147
|
-
const sessionStartTime = new Date(session.start_time).getTime();
|
|
148
|
-
const sessionEndTime = sessionStartTime + (session.total_elapsed_time || 0) * 1000;
|
|
149
|
-
session.lengths = fitDataObject.lengths.filter((length) => {
|
|
150
|
-
const lengthTime = new Date(length.timestamp || length.start_time).getTime();
|
|
151
|
-
return lengthTime >= sessionStartTime && lengthTime < sessionEndTime;
|
|
152
|
-
});
|
|
153
|
-
// Also distribute to laps
|
|
154
|
-
(_a = session.laps) === null || _a === void 0 ? void 0 : _a.forEach((lap) => {
|
|
155
|
-
const lapStartTime = new Date(lap.start_time).getTime();
|
|
156
|
-
const lapEndTime = lapStartTime + (lap.total_elapsed_time || 0) * 1000;
|
|
157
|
-
lap.lengths = fitDataObject.lengths.filter((length) => {
|
|
158
|
-
const lengthTime = new Date(length.timestamp || length.start_time).getTime();
|
|
159
|
-
return lengthTime >= lapStartTime && lengthTime < lapEndTime;
|
|
160
|
-
});
|
|
161
|
-
});
|
|
162
|
-
});
|
|
163
|
-
}
|
|
164
|
-
// Iterate over the sessions and create their activities
|
|
165
|
-
const activities = fitDataObject.sessions.map((sessionObject) => {
|
|
166
|
-
// Get the activity from the sessionObject
|
|
167
|
-
const activity = this.getActivityFromSessionObject(sessionObject, fitDataObject, options);
|
|
168
|
-
// Go over the laps
|
|
169
|
-
sessionObject.laps.forEach((sessionLapObject, index) => {
|
|
170
|
-
activity.addLap(this.getLapFromSessionLapObject(sessionLapObject, activity, index));
|
|
171
|
-
});
|
|
172
|
-
// Go over the hr zone info
|
|
173
|
-
if (sessionObject.time_in_hr_zone && sessionObject.time_in_hr_zone.length) {
|
|
174
|
-
// Add the stats
|
|
175
|
-
if (isNumber(sessionObject.time_in_hr_zone[0])) {
|
|
176
|
-
activity.addStat(new DataHeartRateZoneOneDuration(sessionObject.time_in_hr_zone[0]));
|
|
177
|
-
}
|
|
178
|
-
if (isNumber(sessionObject.time_in_hr_zone[1])) {
|
|
179
|
-
activity.addStat(new DataHeartRateZoneTwoDuration(sessionObject.time_in_hr_zone[1]));
|
|
180
|
-
}
|
|
181
|
-
if (isNumber(sessionObject.time_in_hr_zone[2])) {
|
|
182
|
-
activity.addStat(new DataHeartRateZoneThreeDuration(sessionObject.time_in_hr_zone[2]));
|
|
183
|
-
}
|
|
184
|
-
if (isNumber(sessionObject.time_in_hr_zone[3])) {
|
|
185
|
-
activity.addStat(new DataHeartRateZoneFourDuration(sessionObject.time_in_hr_zone[3]));
|
|
186
|
-
}
|
|
187
|
-
if (isNumber(sessionObject.time_in_hr_zone[4])) {
|
|
188
|
-
activity.addStat(new DataHeartRateZoneFiveDuration(sessionObject.time_in_hr_zone[4]));
|
|
189
|
-
}
|
|
190
|
-
const hrIntensityZones = new IntensityZones(DataHeartRate.type);
|
|
191
|
-
hrIntensityZones.zone1Duration = sessionObject.time_in_hr_zone[0] || 0;
|
|
192
|
-
hrIntensityZones.zone2Duration = sessionObject.time_in_hr_zone[1] || 0;
|
|
193
|
-
hrIntensityZones.zone3Duration = sessionObject.time_in_hr_zone[2] || 0;
|
|
194
|
-
hrIntensityZones.zone4Duration = sessionObject.time_in_hr_zone[3] || 0;
|
|
195
|
-
hrIntensityZones.zone5Duration = sessionObject.time_in_hr_zone[4] || 0;
|
|
196
|
-
activity.intensityZones.push(hrIntensityZones);
|
|
197
|
-
}
|
|
198
|
-
// Go over the power zone info
|
|
199
|
-
if (sessionObject.time_in_power_zone && sessionObject.time_in_power_zone.length) {
|
|
200
|
-
if (isNumber(sessionObject.time_in_power_zone[0])) {
|
|
201
|
-
activity.addStat(new DataPowerZoneOneDuration(sessionObject.time_in_power_zone[0]));
|
|
202
|
-
}
|
|
203
|
-
if (isNumber(sessionObject.time_in_power_zone[1])) {
|
|
204
|
-
activity.addStat(new DataPowerZoneTwoDuration(sessionObject.time_in_power_zone[1]));
|
|
205
|
-
}
|
|
206
|
-
if (isNumber(sessionObject.time_in_power_zone[2])) {
|
|
207
|
-
activity.addStat(new DataPowerZoneThreeDuration(sessionObject.time_in_power_zone[2]));
|
|
208
|
-
}
|
|
209
|
-
if (isNumber(sessionObject.time_in_power_zone[3])) {
|
|
210
|
-
activity.addStat(new DataPowerZoneFourDuration(sessionObject.time_in_power_zone[3]));
|
|
211
|
-
}
|
|
212
|
-
if (isNumber(sessionObject.time_in_power_zone[4])) {
|
|
213
|
-
activity.addStat(new DataPowerZoneFiveDuration(sessionObject.time_in_power_zone[4]));
|
|
214
|
-
}
|
|
215
|
-
const powerIntensityZones = new IntensityZones(DataPower.type);
|
|
216
|
-
powerIntensityZones.zone1Duration = sessionObject.time_in_power_zone[0] || 0;
|
|
217
|
-
powerIntensityZones.zone2Duration = sessionObject.time_in_power_zone[1] || 0;
|
|
218
|
-
powerIntensityZones.zone3Duration = sessionObject.time_in_power_zone[2] || 0;
|
|
219
|
-
powerIntensityZones.zone4Duration = sessionObject.time_in_power_zone[3] || 0;
|
|
220
|
-
powerIntensityZones.zone5Duration = sessionObject.time_in_power_zone[4] || 0;
|
|
221
|
-
activity.intensityZones.push(powerIntensityZones);
|
|
222
|
-
}
|
|
223
|
-
// Go over the speed zone info
|
|
224
|
-
if (sessionObject.time_in_speed_zone && sessionObject.time_in_speed_zone.length) {
|
|
225
|
-
if (isNumber(sessionObject.time_in_speed_zone[0])) {
|
|
226
|
-
activity.addStat(new DataSpeedZoneOneDuration(sessionObject.time_in_speed_zone[0]));
|
|
227
|
-
}
|
|
228
|
-
if (isNumber(sessionObject.time_in_speed_zone[1])) {
|
|
229
|
-
activity.addStat(new DataSpeedZoneTwoDuration(sessionObject.time_in_speed_zone[1]));
|
|
230
|
-
}
|
|
231
|
-
if (isNumber(sessionObject.time_in_speed_zone[2])) {
|
|
232
|
-
activity.addStat(new DataSpeedZoneThreeDuration(sessionObject.time_in_speed_zone[2]));
|
|
233
|
-
}
|
|
234
|
-
if (isNumber(sessionObject.time_in_speed_zone[3])) {
|
|
235
|
-
activity.addStat(new DataSpeedZoneFourDuration(sessionObject.time_in_speed_zone[3]));
|
|
236
|
-
}
|
|
237
|
-
if (isNumber(sessionObject.time_in_speed_zone[4])) {
|
|
238
|
-
activity.addStat(new DataSpeedZoneFiveDuration(sessionObject.time_in_speed_zone[4]));
|
|
239
|
-
}
|
|
240
|
-
const speedIntensityZones = new IntensityZones(DataSpeed.type);
|
|
241
|
-
speedIntensityZones.zone1Duration = sessionObject.time_in_speed_zone[0] || 0;
|
|
242
|
-
speedIntensityZones.zone2Duration = sessionObject.time_in_speed_zone[1] || 0;
|
|
243
|
-
speedIntensityZones.zone3Duration = sessionObject.time_in_speed_zone[2] || 0;
|
|
244
|
-
speedIntensityZones.zone4Duration = sessionObject.time_in_speed_zone[3] || 0;
|
|
245
|
-
speedIntensityZones.zone5Duration = sessionObject.time_in_speed_zone[4] || 0;
|
|
246
|
-
activity.intensityZones.push(speedIntensityZones);
|
|
247
|
-
}
|
|
248
|
-
// Add the events
|
|
249
|
-
fitDataObject.events
|
|
250
|
-
.filter((activityEvent) => {
|
|
251
|
-
return activityEvent.timestamp >= activity.startDate && activityEvent.timestamp <= activity.endDate;
|
|
252
|
-
})
|
|
253
|
-
.forEach((activityEvent) => {
|
|
254
|
-
if (activityEvent.event === 'timer') {
|
|
255
|
-
switch (activityEvent.event_type) {
|
|
256
|
-
case 'start':
|
|
257
|
-
activity.addEvent(new DataStartEvent(activity.getDateIndex(activityEvent.timestamp)));
|
|
258
|
-
break;
|
|
259
|
-
case 'stop':
|
|
260
|
-
activity.addEvent(new DataStopEvent(activity.getDateIndex(activityEvent.timestamp)));
|
|
261
|
-
break;
|
|
262
|
-
case 'stop_all':
|
|
263
|
-
activity.addEvent(new DataStopAllEvent(activity.getDateIndex(activityEvent.timestamp)));
|
|
264
|
-
break;
|
|
265
|
-
default:
|
|
266
|
-
break;
|
|
267
|
-
}
|
|
268
|
-
}
|
|
269
|
-
else if (activityEvent.event === 'power_down') {
|
|
270
|
-
activity.addEvent(new DataPowerDown(activity.getDateIndex(activityEvent.timestamp)));
|
|
271
|
-
}
|
|
272
|
-
else if (activityEvent.event === 'power_up') {
|
|
273
|
-
activity.addEvent(new DataPowerUp(activity.getDateIndex(activityEvent.timestamp)));
|
|
274
|
-
}
|
|
275
|
-
else if (activityEvent.event === 'rider_position_change') {
|
|
276
|
-
const positionChange = activityEvent.data;
|
|
277
|
-
if (positionChange === RiderPosition.SEATED ||
|
|
278
|
-
positionChange === RiderPosition.STANDING ||
|
|
279
|
-
positionChange === RiderPosition.TRANSITION_TO_SEATED ||
|
|
280
|
-
positionChange === RiderPosition.TRANSITION_TO_STANDING) {
|
|
281
|
-
activity.addEvent(new DataRiderPositionChangeEvent(activity.getDateIndex(activityEvent.timestamp), positionChange));
|
|
282
|
-
}
|
|
283
|
-
}
|
|
284
|
-
});
|
|
285
|
-
// Get the samples..
|
|
286
|
-
// Test if activity is lengths based
|
|
287
|
-
// Indeed when based on lengths, an activity do not provides samples under records object (e.g. Pool swimming activities)
|
|
288
|
-
// Note: this is how Strava generate streams for this kind of activities
|
|
289
|
-
const isLengthsBased = this.isLengthsBased(sessionObject);
|
|
290
|
-
const samples = isLengthsBased
|
|
291
|
-
? this.generateSamplesFromLengths(sessionObject, options)
|
|
292
|
-
: fitDataObject.records.filter((record) => {
|
|
293
|
-
return record.timestamp >= activity.startDate && record.timestamp <= activity.endDate;
|
|
294
|
-
});
|
|
295
|
-
// Setup sample info which could be use when getting sample values
|
|
296
|
-
const hasPowerMeter = samples.findIndex((sample) => Number.isFinite(isNumber(sample.power) ? sample.power : isNumber(sample.Power) ? sample.Power : sample.RP_Power)) !== -1;
|
|
297
|
-
const samplesInfo = { hasPowerMeter: hasPowerMeter };
|
|
298
|
-
FITSampleMapper.forEach(sampleMapping => {
|
|
299
|
-
// @todo not sure if we need to check for number only ...
|
|
300
|
-
const subjectSamples = (samples.filter((sample) => isNumber(sampleMapping.getSampleValue(sample, samplesInfo))));
|
|
301
|
-
if (subjectSamples.length) {
|
|
302
|
-
// When we create a stream here it has the length of the activity elapsed time (end-start) filled with nulls.
|
|
303
|
-
// We keep nulls in order to preserve the array length.
|
|
304
|
-
activity.addStream(activity.createStream(sampleMapping.dataType));
|
|
305
|
-
subjectSamples.forEach(subjectSample => {
|
|
306
|
-
activity.addDataToStream(sampleMapping.dataType, new Date(subjectSample.timestamp), sampleMapping.getSampleValue(subjectSample, samplesInfo));
|
|
307
|
-
});
|
|
308
|
-
}
|
|
309
|
-
});
|
|
310
|
-
return activity;
|
|
311
|
-
});
|
|
312
|
-
// If there are no activities to parse ....
|
|
313
|
-
if (!activities.length) {
|
|
314
|
-
reject(new EmptyEventLibError());
|
|
315
|
-
return;
|
|
316
|
-
}
|
|
317
|
-
// Get the HRV to IBI if exist
|
|
318
|
-
if (fitDataObject.hrv && fitDataObject.hrv.length) {
|
|
319
|
-
activities.forEach((activity) => {
|
|
320
|
-
let timeSum = 0;
|
|
321
|
-
const ibiData = fitDataObject.hrv
|
|
322
|
-
.reduce((ibiArray, hrvRecord) => ibiArray.concat(hrvRecord.time), [])
|
|
323
|
-
.map((ibi) => ibi * 1000)
|
|
324
|
-
.filter((ibi) => {
|
|
325
|
-
// debugger;
|
|
326
|
-
// Some Garmin devices return a record of 65.535 (65535) for some reason so exlcude those
|
|
327
|
-
if (ibi === 65535) {
|
|
328
|
-
// timeSum += ibi;
|
|
329
|
-
return false;
|
|
330
|
-
}
|
|
331
|
-
timeSum += ibi;
|
|
332
|
-
const ibiDataDate = new Date(activities[0].startDate.getTime() + timeSum);
|
|
333
|
-
return ibiDataDate >= activity.startDate && ibiDataDate <= activity.endDate;
|
|
334
|
-
});
|
|
335
|
-
// set the IBI
|
|
336
|
-
activity.addStream(new IBIStream(ibiData));
|
|
337
|
-
});
|
|
338
|
-
}
|
|
339
|
-
// Parse the device infos
|
|
340
|
-
if (fitDataObject.device_infos && fitDataObject.device_infos.length) {
|
|
341
|
-
activities.forEach(activity => {
|
|
342
|
-
activity.creator.devices = this.getDeviceInfos(fitDataObject.device_infos);
|
|
343
|
-
});
|
|
344
|
-
}
|
|
345
|
-
// Create an event
|
|
346
|
-
// @todo check if the start and end date can derive from the file
|
|
347
|
-
const event = new Event(name, activities[0].startDate, activities[activities.length - 1].endDate, FileType.FIT);
|
|
348
|
-
activities.forEach(activity => event.addActivity(activity));
|
|
349
|
-
// debugger;
|
|
350
|
-
EventUtilities.generateStatsForAll(event);
|
|
351
|
-
// debugger;
|
|
352
|
-
resolve(event);
|
|
353
|
-
});
|
|
354
|
-
});
|
|
355
|
-
});
|
|
356
|
-
}
|
|
357
|
-
/**
|
|
358
|
-
* Tell if an activity is lengths based (e.g. Pool swimming activities)
|
|
359
|
-
* @param sessionObject
|
|
360
|
-
* @private
|
|
361
|
-
*/
|
|
362
|
-
static isLengthsBased(sessionObject) {
|
|
363
|
-
var _a;
|
|
364
|
-
return ((_a = sessionObject.laps) === null || _a === void 0 ? void 0 : _a.filter((lap) => { var _a; return (_a = lap.lengths) === null || _a === void 0 ? void 0 : _a.length; }).length) > 1;
|
|
365
|
-
}
|
|
366
|
-
/**
|
|
367
|
-
* Generate streams samples based on lengths on an activity
|
|
368
|
-
* When based on lengths, an activity do not provides sample under records object
|
|
369
|
-
* @param sessionObject
|
|
370
|
-
* @param options
|
|
371
|
-
* @private
|
|
372
|
-
*/
|
|
373
|
-
static generateSamplesFromLengths(sessionObject, options) {
|
|
374
|
-
if (!this.isLengthsBased(sessionObject)) {
|
|
375
|
-
throw new ParsingEventLibError('Trying to get samples from activities lengths, but no lengths is available');
|
|
376
|
-
}
|
|
377
|
-
let samples = [];
|
|
378
|
-
// Loop on every laps to catch every lengths where data is (speed, cadence, hr, ...)
|
|
379
|
-
sessionObject.laps.forEach((lap) => {
|
|
380
|
-
var _a;
|
|
381
|
-
// Loop on every laps
|
|
382
|
-
if ((_a = lap.lengths) === null || _a === void 0 ? void 0 : _a.length) {
|
|
383
|
-
// Get length in meters from lap total distance and total number of lengths
|
|
384
|
-
// We will use it to generate the distance stream below
|
|
385
|
-
const lengthMeters = lap.total_distance / lap.lengths.length;
|
|
386
|
-
// For each length of every laps build the streams data we will need for a later use
|
|
387
|
-
lap.lengths.forEach((length) => {
|
|
388
|
-
// Resolve start/end date of current length
|
|
389
|
-
const lengthStartDate = length.start_time;
|
|
390
|
-
const lengthDuration = (length.total_timer_time || length.total_elapsed_time || 0);
|
|
391
|
-
const lengthEndDate = new Date(lengthStartDate.getTime() + lengthDuration * 1000);
|
|
392
|
-
// We check if length is valid comparing to max activity duration
|
|
393
|
-
if (lengthDuration > options.maxActivityDurationDays * 24 * 60 * 60) {
|
|
394
|
-
return;
|
|
395
|
-
}
|
|
396
|
-
if (lengthEndDate.getTime() <= lengthStartDate.getTime()) {
|
|
397
|
-
return;
|
|
398
|
-
}
|
|
399
|
-
// Generate a stream from length start date to end date filled by null values
|
|
400
|
-
const streamLength = ActivityUtilities.getDataLength(lengthStartDate, lengthEndDate);
|
|
401
|
-
let lengthStream = Array(streamLength).fill(null);
|
|
402
|
-
// Define distance step to be used for distance stream
|
|
403
|
-
const lengthStepSize = lengthMeters / (lengthStream.length - 1);
|
|
404
|
-
// Generate the length stream based on data we have on current length
|
|
405
|
-
lengthStream = lengthStream.map((value, index) => {
|
|
406
|
-
var _a;
|
|
407
|
-
return {
|
|
408
|
-
timestamp: new Date(lengthStartDate.getTime() + index * 1000),
|
|
409
|
-
distance: (((_a = samples[samples.length - 1]) === null || _a === void 0 ? void 0 : _a.distance) || 0) + lengthStepSize * index,
|
|
410
|
-
speed: length.avg_speed || lap.avg_speed,
|
|
411
|
-
cadence: length.avg_cadence || length.avg_swimming_cadence || lap.avg_cadence,
|
|
412
|
-
heart_rate: length.avg_heart_rate || lap.avg_heart_rate
|
|
413
|
-
};
|
|
414
|
-
});
|
|
415
|
-
// Append to existing samples
|
|
416
|
-
samples = samples.concat(lengthStream);
|
|
417
|
-
});
|
|
418
|
-
}
|
|
419
|
-
});
|
|
420
|
-
return samples;
|
|
421
|
-
}
|
|
422
|
-
static getDeviceInfos(deviceInfos) {
|
|
423
|
-
return deviceInfos.map((deviceInfo) => {
|
|
424
|
-
const device = new Device(deviceInfo.device_type);
|
|
425
|
-
device.index = deviceInfo.device_index;
|
|
426
|
-
device.name = deviceInfo.product_name || ImporterFitAntPlusDeviceNames[deviceInfo.ant_device_number] || deviceInfo.ant_device_number;
|
|
427
|
-
device.batteryStatus = deviceInfo.battery_status;
|
|
428
|
-
device.batteryLevel = isNumber(deviceInfo.battery_level) ? deviceInfo.battery_level : deviceInfo.battery_soc;
|
|
429
|
-
device.batteryVoltage = deviceInfo.battery_voltage;
|
|
430
|
-
device.manufacturer = deviceInfo.manufacturer;
|
|
431
|
-
device.serialNumber = deviceInfo.serial_number;
|
|
432
|
-
device.product = deviceInfo.product;
|
|
433
|
-
device.swInfo = deviceInfo.software_version;
|
|
434
|
-
device.hwInfo = deviceInfo.hardware_version;
|
|
435
|
-
device.antDeviceNumber = deviceInfo.ant_device_number;
|
|
436
|
-
device.antTransmissionType = deviceInfo.ant_transmission_type;
|
|
437
|
-
device.antNetwork = deviceInfo.ant_network;
|
|
438
|
-
device.sourceType = deviceInfo.source_type;
|
|
439
|
-
device.cumOperatingTime = deviceInfo.cum_operating_time;
|
|
440
|
-
return device;
|
|
441
|
-
});
|
|
442
|
-
}
|
|
443
|
-
static getLapFromSessionLapObject(sessionLapObject, activity, lapIndex) {
|
|
444
|
-
var _a, _b, _c;
|
|
445
|
-
const startDate = (sessionLapObject === null || sessionLapObject === void 0 ? void 0 : sessionLapObject.start_time) ||
|
|
446
|
-
((_a = sessionLapObject === null || sessionLapObject === void 0 ? void 0 : sessionLapObject.records[0]) === null || _a === void 0 ? void 0 : _a.timestamp) ||
|
|
447
|
-
((sessionLapObject === null || sessionLapObject === void 0 ? void 0 : sessionLapObject.total_elapsed_time) &&
|
|
448
|
-
new Date(sessionLapObject.timestamp.getTime() - sessionLapObject.total_elapsed_time * 1000)) ||
|
|
449
|
-
null;
|
|
450
|
-
const endDate = (sessionLapObject === null || sessionLapObject === void 0 ? void 0 : sessionLapObject.timestamp) ||
|
|
451
|
-
(((_b = sessionLapObject.records) === null || _b === void 0 ? void 0 : _b.length) > 0 &&
|
|
452
|
-
((_c = sessionLapObject.records[sessionLapObject.records.length - 1]) === null || _c === void 0 ? void 0 : _c.timestamp)) ||
|
|
453
|
-
(sessionLapObject.start_time &&
|
|
454
|
-
sessionLapObject.total_elapsed_time &&
|
|
455
|
-
new Date(sessionLapObject.start_time.getTime() + sessionLapObject.total_elapsed_time * 1000)) ||
|
|
456
|
-
null;
|
|
457
|
-
const lap = new Lap(startDate, endDate, // Some dont have a timestamp
|
|
458
|
-
lapIndex + 1, LapTypes[sessionLapObject.lap_trigger] || LapTypes.unknown);
|
|
459
|
-
// Set the calories
|
|
460
|
-
if (sessionLapObject.total_calories) {
|
|
461
|
-
lap.addStat(new DataEnergy(sessionLapObject.total_calories));
|
|
462
|
-
}
|
|
463
|
-
// Add stats to the lap
|
|
464
|
-
this.getStatsFromObject(sessionLapObject, activity, true).forEach(stat => lap.addStat(stat));
|
|
465
|
-
return lap;
|
|
466
|
-
}
|
|
467
|
-
static getActivityFromSessionObject(sessionObject, fitDataObject, options) {
|
|
468
|
-
/**
|
|
469
|
-
* Provides start/end date based on records available in given session object first, then in parent fit object
|
|
470
|
-
*/
|
|
471
|
-
const getStartEndDatesFromRecords = (sessionObject, fitDataObject) => {
|
|
472
|
-
var _a, _b, _c, _d, _e, _f, _g, _h;
|
|
473
|
-
let startDate = null;
|
|
474
|
-
let endDate = null;
|
|
475
|
-
// Try to get from session first
|
|
476
|
-
if (((_a = sessionObject === null || sessionObject === void 0 ? void 0 : sessionObject.laps) === null || _a === void 0 ? void 0 : _a.length) > 0 && // Current session has laps
|
|
477
|
-
((_b = sessionObject.laps[0].records) === null || _b === void 0 ? void 0 : _b.length) > 0 && // Current session has records in first lap
|
|
478
|
-
((_c = sessionObject.laps[sessionObject.laps.length - 1].records) === null || _c === void 0 ? void 0 : _c.length) > 0 // Current session has records in last lap
|
|
479
|
-
) {
|
|
480
|
-
const firstLapRecords = sessionObject.laps[0].records;
|
|
481
|
-
const firstRecordTimeStamp = (_d = firstLapRecords[0]) === null || _d === void 0 ? void 0 : _d.timestamp;
|
|
482
|
-
if (firstRecordTimeStamp) {
|
|
483
|
-
startDate = firstRecordTimeStamp;
|
|
484
|
-
}
|
|
485
|
-
const lastLapRecords = sessionObject.laps[sessionObject.laps.length - 1].records;
|
|
486
|
-
const lastRecordTimeStamp = (_e = lastLapRecords[lastLapRecords.length - 1]) === null || _e === void 0 ? void 0 : _e.timestamp;
|
|
487
|
-
if (lastRecordTimeStamp) {
|
|
488
|
-
endDate = lastRecordTimeStamp;
|
|
489
|
-
}
|
|
490
|
-
}
|
|
491
|
-
// Then from parent fit object first
|
|
492
|
-
if ((!startDate || !endDate) && ((_f = fitDataObject.records) === null || _f === void 0 ? void 0 : _f.length)) {
|
|
493
|
-
startDate = ((_g = fitDataObject.records[0]) === null || _g === void 0 ? void 0 : _g.timestamp) || null;
|
|
494
|
-
endDate = ((_h = fitDataObject.records[fitDataObject.records.length - 1]) === null || _h === void 0 ? void 0 : _h.timestamp) || null;
|
|
495
|
-
}
|
|
496
|
-
return [startDate, endDate];
|
|
497
|
-
};
|
|
498
|
-
// For some unknown reasons... the fit provided total_timer_time & total_elapsed_time could be inverted..
|
|
499
|
-
// Just invert fields if that's the case
|
|
500
|
-
this.swapTimesIfRequired(sessionObject);
|
|
501
|
-
// Start finding out total elapsed time from fit dedicated fields
|
|
502
|
-
const totalElapsedTime = sessionObject.total_elapsed_time || sessionObject.total_timer_time || 0;
|
|
503
|
-
// Pick start/end date values
|
|
504
|
-
let startDate = sessionObject.start_time || getStartEndDatesFromRecords(sessionObject, fitDataObject)[0] || null;
|
|
505
|
-
let endDate = sessionObject.timestamp ||
|
|
506
|
-
(startDate && totalElapsedTime && new Date(startDate.getTime() + totalElapsedTime * 1000)) ||
|
|
507
|
-
getStartEndDatesFromRecords(sessionObject, fitDataObject)[1] ||
|
|
508
|
-
null;
|
|
509
|
-
// Some fit files have wrong dates for session.timestamp && session.start_time and those miss an elapsed time
|
|
510
|
-
// Get dates from records in that case
|
|
511
|
-
if (!totalElapsedTime // Elapsed time missing
|
|
512
|
-
) {
|
|
513
|
-
const [startDateResult, endDateResult] = getStartEndDatesFromRecords(sessionObject, fitDataObject);
|
|
514
|
-
if (startDateResult) {
|
|
515
|
-
startDate = startDateResult;
|
|
516
|
-
}
|
|
517
|
-
if (endDateResult) {
|
|
518
|
-
endDate = endDateResult;
|
|
519
|
-
}
|
|
520
|
-
}
|
|
521
|
-
// Now verify the start/end date compliance,
|
|
522
|
-
// If for some reason this happens, get from records too
|
|
523
|
-
if (endDate <= startDate) {
|
|
524
|
-
const [startDateResult, endDateResult] = getStartEndDatesFromRecords(sessionObject, fitDataObject);
|
|
525
|
-
if (startDateResult) {
|
|
526
|
-
startDate = startDateResult;
|
|
527
|
-
}
|
|
528
|
-
if (endDateResult) {
|
|
529
|
-
endDate = endDateResult;
|
|
530
|
-
}
|
|
531
|
-
}
|
|
532
|
-
const elapsedTimeFromDates = (+endDate - +startDate) / 1000; // Get elapsed calculated from dates
|
|
533
|
-
// Test case where sometime elapsed time (calculated from dates) can be very high comparing to computed totalElapsedTime
|
|
534
|
-
// If elapsed time (calculated from dates) is detected as "strange" then use elapsed time from fit fields instead
|
|
535
|
-
// @see test case implying 'fixtures/rides/fit/5319808632.fit' fit file
|
|
536
|
-
if (elapsedTimeFromDates / totalElapsedTime > INVALID_DATES_ELAPSED_TIME_RATIO_THRESHOLD) {
|
|
537
|
-
if (totalElapsedTime) {
|
|
538
|
-
endDate = new Date(sessionObject.start_time.getTime() + totalElapsedTime * 1000);
|
|
539
|
-
}
|
|
540
|
-
}
|
|
541
|
-
// Re-test potential updated activity duration against max accepted duration
|
|
542
|
-
if (+endDate - +startDate > options.maxActivityDurationDays * 24 * 60 * 60 * 1000) {
|
|
543
|
-
endDate = new Date(sessionObject.start_time.getTime() + totalElapsedTime * 1000);
|
|
544
|
-
}
|
|
545
|
-
if (!startDate || !endDate) {
|
|
546
|
-
throw new ParsingEventLibError('Cannot parse start and end dates');
|
|
547
|
-
}
|
|
548
|
-
else {
|
|
549
|
-
// Create an activity
|
|
550
|
-
const activity = new Activity(startDate, endDate, this.getActivityTypeFromSessionObject(sessionObject), this.getCreatorFromFitDataObject(fitDataObject), options);
|
|
551
|
-
// Set the activity stats
|
|
552
|
-
this.getStatsFromObject(sessionObject, activity, false).forEach(stat => activity.addStat(stat));
|
|
553
|
-
return activity;
|
|
554
|
-
}
|
|
555
|
-
}
|
|
556
|
-
/**
|
|
557
|
-
* For some unknown reasons... the fit provided total_timer_time & total_elapsed_time could be inverted..
|
|
558
|
-
* Just swap them if that's the case
|
|
559
|
-
*/
|
|
560
|
-
static swapTimesIfRequired(object) {
|
|
561
|
-
if (isNumber(object.total_timer_time) &&
|
|
562
|
-
isNumber(object.total_elapsed_time) &&
|
|
563
|
-
object.total_elapsed_time < object.total_timer_time) {
|
|
564
|
-
const realTimerTime = object.total_elapsed_time;
|
|
565
|
-
const realElapsedTime = object.total_timer_time;
|
|
566
|
-
object.total_timer_time = realTimerTime;
|
|
567
|
-
object.total_elapsed_time = realElapsedTime;
|
|
568
|
-
}
|
|
569
|
-
}
|
|
570
|
-
static getActivityTypeFromSessionObject(session) {
|
|
571
|
-
const activityTypeKey = session.sub_sport && session.sub_sport !== 'generic' ? `${session.sport}_${session.sub_sport}` : session.sport;
|
|
572
|
-
let activityType = ActivityTypes[activityTypeKey];
|
|
573
|
-
if (!activityType || activityType === ActivityTypes.unknown) {
|
|
574
|
-
// Fallback to Garmin SDK mappings
|
|
575
|
-
const sportName = GarminProfileMapper.getSportName(session.sport);
|
|
576
|
-
const subSportName = session.sub_sport && session.sub_sport !== 'generic'
|
|
577
|
-
? GarminProfileMapper.getSubSportName(session.sub_sport)
|
|
578
|
-
: null;
|
|
579
|
-
if (sportName || subSportName) {
|
|
580
|
-
// Try to find in ActivityTypes using the name from Garmin SDK
|
|
581
|
-
const nameKey = subSportName ? `${sportName}_${subSportName}` : sportName;
|
|
582
|
-
activityType =
|
|
583
|
-
ActivityTypes[nameKey] ||
|
|
584
|
-
ActivityTypes[sportName] ||
|
|
585
|
-
nameKey;
|
|
586
|
-
}
|
|
587
|
-
}
|
|
588
|
-
return activityType || session.sport || ActivityTypes.unknown;
|
|
589
|
-
}
|
|
590
|
-
// @todo move this to a mapper
|
|
591
|
-
static getStatsFromObject(object, activity, isLap) {
|
|
592
|
-
const stats = [];
|
|
593
|
-
// For some unknown reasons... the fit provided total_timer_time & total_elapsed_time could be inverted..
|
|
594
|
-
// Just invert fields if that's the case
|
|
595
|
-
this.swapTimesIfRequired(object);
|
|
596
|
-
// TOTAL ELAPSED TIME on Object (activity, lap...)
|
|
597
|
-
let elapsedTime = 0;
|
|
598
|
-
if (isNumber(object.total_elapsed_time)) {
|
|
599
|
-
elapsedTime = object.total_elapsed_time;
|
|
600
|
-
}
|
|
601
|
-
else if ((object.timestamp - object.start_time) / 1000) {
|
|
602
|
-
elapsedTime = (object.timestamp - object.start_time) / 1000;
|
|
603
|
-
}
|
|
604
|
-
// 0 should be not included aha it's not legit to have a 0 for total timer time
|
|
605
|
-
// And that typically is a device error we should look at the samples
|
|
606
|
-
// Since start and end date are inclusive for sample size eg at time [0] there can be a value
|
|
607
|
-
if (!elapsedTime) {
|
|
608
|
-
elapsedTime = ActivityUtilities.getDataLength(activity.startDate, activity.endDate) - 1;
|
|
609
|
-
}
|
|
610
|
-
stats.push(new DataDuration(Math.round(elapsedTime * 100) / 100));
|
|
611
|
-
// TOTAL TIMER TIME on Object (activity, lap...)
|
|
612
|
-
let timerTime = 0;
|
|
613
|
-
if (isNumber(object.total_timer_time)) {
|
|
614
|
-
timerTime = object.total_timer_time;
|
|
615
|
-
}
|
|
616
|
-
// If timer time is unknown then assign elapsedTime value
|
|
617
|
-
if (!timerTime) {
|
|
618
|
-
timerTime = elapsedTime;
|
|
619
|
-
}
|
|
620
|
-
stats.push(new DataTimerTime(Math.round(timerTime * 100) / 100));
|
|
621
|
-
// Moving TIME on Object (activity, lap...)
|
|
622
|
-
let movingTime = 0;
|
|
623
|
-
if (object.lengths && object.lengths.length > 0) {
|
|
624
|
-
object.lengths.forEach((lengthVal) => {
|
|
625
|
-
if (lengthVal.length_type === 'active') {
|
|
626
|
-
movingTime += lengthVal.total_timer_time;
|
|
627
|
-
}
|
|
628
|
-
});
|
|
629
|
-
}
|
|
630
|
-
else if (object.records && object.records.length > 0) {
|
|
631
|
-
const speedThreshold = ActivityTypesMoving.getSpeedThreshold(activity.type);
|
|
632
|
-
object.records.forEach((record, index) => {
|
|
633
|
-
var _a;
|
|
634
|
-
if ((record.speed || record.enhanced_speed) > speedThreshold) {
|
|
635
|
-
const previousRecordTime = ((_a = object.records[index - 1]) === null || _a === void 0 ? void 0 : _a.timestamp) || object.start_time || object.records[0].timestamp;
|
|
636
|
-
movingTime += (record.timestamp.getTime() - previousRecordTime.getTime()) / 1000;
|
|
637
|
-
}
|
|
638
|
-
});
|
|
639
|
-
}
|
|
640
|
-
if (isLap) {
|
|
641
|
-
// In case we moved (distance > 0) & moving time is invalid, then set it to timer time value
|
|
642
|
-
if (object.total_distance > 0 && (!movingTime || movingTime > timerTime)) {
|
|
643
|
-
movingTime = timerTime;
|
|
644
|
-
}
|
|
645
|
-
}
|
|
646
|
-
// Append moving stat only if moving time has been detected
|
|
647
|
-
// We need that to compute total global moving time later
|
|
648
|
-
if (movingTime > 0) {
|
|
649
|
-
stats.push(new DataMovingTime(Math.round(movingTime * 100) / 100));
|
|
650
|
-
}
|
|
651
|
-
// Pause TIME on Object (activity, lap...)
|
|
652
|
-
const pause = elapsedTime > movingTime && movingTime > 0 ? Math.round((elapsedTime - movingTime) * 100) / 100 : 0;
|
|
653
|
-
stats.push(new DataPause(pause));
|
|
654
|
-
const getStatValue = (obj, keys) => {
|
|
655
|
-
for (const key of keys) {
|
|
656
|
-
if (isNumberOrString(obj[key])) {
|
|
657
|
-
return obj[key];
|
|
658
|
-
}
|
|
659
|
-
}
|
|
660
|
-
return null;
|
|
661
|
-
};
|
|
662
|
-
const avgSpeed = getStatValue(object, ['enhanced_avg_speed', 'EnhancedAvgSpeed', 'avg_speed', 'AvgSpeed']);
|
|
663
|
-
const totalDistance = getStatValue(object, ['total_distance', 'TotalDistance']);
|
|
664
|
-
// Assign is active lap status
|
|
665
|
-
stats.push(new DataActiveLap(!!(totalDistance || avgSpeed)));
|
|
666
|
-
if (totalDistance !== null) {
|
|
667
|
-
stats.push(new DataDistance(totalDistance));
|
|
668
|
-
}
|
|
669
|
-
else {
|
|
670
|
-
stats.push(new DataDistance(0));
|
|
671
|
-
}
|
|
672
|
-
// Heart Rate
|
|
673
|
-
if (isNumberOrString(object.avg_heart_rate)) {
|
|
674
|
-
stats.push(new DataHeartRateAvg(object.avg_heart_rate));
|
|
675
|
-
}
|
|
676
|
-
if (isNumberOrString(object.min_heart_rate)) {
|
|
677
|
-
stats.push(new DataHeartRateMin(object.min_heart_rate));
|
|
678
|
-
}
|
|
679
|
-
if (isNumberOrString(object.max_heart_rate)) {
|
|
680
|
-
stats.push(new DataHeartRateMax(object.max_heart_rate));
|
|
681
|
-
}
|
|
682
|
-
// Cadence
|
|
683
|
-
if (isNumberOrString(object.avg_cadence)) {
|
|
684
|
-
stats.push(new DataCadenceAvg(object.avg_cadence));
|
|
685
|
-
}
|
|
686
|
-
if (isNumberOrString(object.min_cadence)) {
|
|
687
|
-
stats.push(new DataCadenceMin(object.min_cadence));
|
|
688
|
-
}
|
|
689
|
-
if (isNumberOrString(object.max_cadence)) {
|
|
690
|
-
stats.push(new DataCadenceMax(object.max_cadence));
|
|
691
|
-
}
|
|
692
|
-
// Power
|
|
693
|
-
if (isNumberOrString(object.avg_power)) {
|
|
694
|
-
stats.push(new DataPowerAvg(object.avg_power));
|
|
695
|
-
}
|
|
696
|
-
if (isNumberOrString(object.min_power)) {
|
|
697
|
-
stats.push(new DataPowerMin(object.min_power));
|
|
698
|
-
}
|
|
699
|
-
if (isNumberOrString(object.max_power)) {
|
|
700
|
-
stats.push(new DataPowerMax(object.max_power));
|
|
701
|
-
}
|
|
702
|
-
if (Number.isFinite(object.normalized_power)) {
|
|
703
|
-
stats.push(new DataPowerNormalized(object.normalized_power));
|
|
704
|
-
}
|
|
705
|
-
if (Number.isFinite(object.intensity_factor)) {
|
|
706
|
-
stats.push(new DataPowerIntensityFactor(object.intensity_factor));
|
|
707
|
-
}
|
|
708
|
-
if (Number.isFinite(object.training_stress_score)) {
|
|
709
|
-
stats.push(new DataPowerTrainingStressScore(object.training_stress_score));
|
|
710
|
-
}
|
|
711
|
-
if (Number.isFinite(object.total_work)) {
|
|
712
|
-
stats.push(new DataPowerWork(Math.round(object.total_work / 1000)));
|
|
713
|
-
}
|
|
714
|
-
if (Number.isFinite(object.avg_left_torque_effectiveness)) {
|
|
715
|
-
stats.push(new DataPowerTorqueEffectivenessLeft(object.avg_left_torque_effectiveness));
|
|
716
|
-
}
|
|
717
|
-
if (Number.isFinite(object.avg_right_torque_effectiveness)) {
|
|
718
|
-
stats.push(new DataPowerTorqueEffectivenessRight(object.avg_right_torque_effectiveness));
|
|
719
|
-
}
|
|
720
|
-
if (Number.isFinite(object.avg_left_pedal_smoothness)) {
|
|
721
|
-
stats.push(new DataPowerPedalSmoothnessLeft(object.avg_left_pedal_smoothness));
|
|
722
|
-
}
|
|
723
|
-
if (Number.isFinite(object.avg_right_pedal_smoothness)) {
|
|
724
|
-
stats.push(new DataPowerPedalSmoothnessRight(object.avg_right_pedal_smoothness));
|
|
725
|
-
}
|
|
726
|
-
// Speed
|
|
727
|
-
if (avgSpeed !== null) {
|
|
728
|
-
stats.push(new DataSpeedAvg(avgSpeed));
|
|
729
|
-
}
|
|
730
|
-
const minSpeed = getStatValue(object, ['enhanced_min_speed', 'EnhancedMinSpeed', 'min_speed', 'MinSpeed']);
|
|
731
|
-
if (minSpeed !== null) {
|
|
732
|
-
stats.push(new DataSpeedMin(minSpeed));
|
|
733
|
-
}
|
|
734
|
-
const maxSpeed = getStatValue(object, ['enhanced_max_speed', 'EnhancedMaxSpeed', 'max_speed', 'MaxSpeed']);
|
|
735
|
-
if (maxSpeed !== null) {
|
|
736
|
-
stats.push(new DataSpeedMax(maxSpeed));
|
|
737
|
-
}
|
|
738
|
-
// Temperature
|
|
739
|
-
if (isNumberOrString(object.avg_temperature)) {
|
|
740
|
-
stats.push(new DataTemperatureAvg(object.avg_temperature));
|
|
741
|
-
}
|
|
742
|
-
if (isNumberOrString(object.min_temperature)) {
|
|
743
|
-
stats.push(new DataTemperatureMin(object.min_temperature));
|
|
744
|
-
}
|
|
745
|
-
if (isNumberOrString(object.max_temperature)) {
|
|
746
|
-
stats.push(new DataTemperatureMax(object.max_temperature));
|
|
747
|
-
}
|
|
748
|
-
// Ascent
|
|
749
|
-
const ascent = getStatValue(object, ['total_ascent', 'TotalAscent']);
|
|
750
|
-
if (ascent !== null) {
|
|
751
|
-
stats.push(new DataAscent(ascent));
|
|
752
|
-
}
|
|
753
|
-
// Descent
|
|
754
|
-
const descent = getStatValue(object, ['total_descent', 'TotalDescent']);
|
|
755
|
-
if (descent !== null) {
|
|
756
|
-
stats.push(new DataDescent(descent));
|
|
757
|
-
}
|
|
758
|
-
// Calories
|
|
759
|
-
if (isNumberOrString(object.total_calories)) {
|
|
760
|
-
stats.push(new DataEnergy(object.total_calories));
|
|
761
|
-
}
|
|
762
|
-
// Total training effect = Aerobic training effect
|
|
763
|
-
if (isNumberOrString(object.total_training_effect)) {
|
|
764
|
-
stats.push(new DataAerobicTrainingEffect(object.total_training_effect));
|
|
765
|
-
}
|
|
766
|
-
// Total training anaerobic effect
|
|
767
|
-
if (isNumberOrString(object.total_anaerobic_effect)) {
|
|
768
|
-
stats.push(new DataAnaerobicTrainingEffect(object.total_anaerobic_effect));
|
|
769
|
-
}
|
|
770
|
-
// Vo2Max
|
|
771
|
-
if (isNumberOrString(object.estimated_vo2_max)) {
|
|
772
|
-
stats.push(new DataVO2Max(object.estimated_vo2_max));
|
|
773
|
-
}
|
|
774
|
-
// Peak Epoc
|
|
775
|
-
if (isNumberOrString(object.peak_epoc)) {
|
|
776
|
-
stats.push(new DataPeakEPOC(object.peak_epoc));
|
|
777
|
-
}
|
|
778
|
-
// Recovery time
|
|
779
|
-
if (isNumberOrString(object.recovery_time)) {
|
|
780
|
-
stats.push(new DataRecoveryTime(object.recovery_time));
|
|
781
|
-
}
|
|
782
|
-
// Feeling
|
|
783
|
-
if (isNumberOrString(object.feeling)) {
|
|
784
|
-
stats.push(new DataFeeling(object.feeling));
|
|
785
|
-
}
|
|
786
|
-
// Pool length
|
|
787
|
-
if (isNumberOrString(object.pool_length)) {
|
|
788
|
-
let poolLength = object.pool_length;
|
|
789
|
-
if (object.pool_length_unit) {
|
|
790
|
-
poolLength = object.pool_length_unit.match(/metric/i) ? object.pool_length : object.pool_length * 0.9144; // Convert to meters from yards when not metric
|
|
791
|
-
}
|
|
792
|
-
stats.push(new DataPoolLength(poolLength));
|
|
793
|
-
}
|
|
794
|
-
// Average SWOLF in 25m and 50m pool
|
|
795
|
-
if ((activity.type === ActivityTypes.Swimming || activity.type === ActivityTypes.OpenWaterSwimming) &&
|
|
796
|
-
(isNumberOrString(object.avg_speed) || isNumberOrString(object.enhanced_avg_speed)) &&
|
|
797
|
-
isNumberOrString(object.avg_cadence)) {
|
|
798
|
-
const avgPace100m = 100 / (object.avg_speed || object.enhanced_avg_speed);
|
|
799
|
-
if (Number.isFinite(avgPace100m) && Number.isFinite(object.avg_cadence)) {
|
|
800
|
-
const avgCadence = object.avg_cadence;
|
|
801
|
-
const swolf25m = ActivityUtilities.computeSwimSwolf(avgPace100m, avgCadence, 25);
|
|
802
|
-
stats.push(new DataSWOLF25m(swolf25m));
|
|
803
|
-
const swolf50m = ActivityUtilities.computeSwimSwolf(avgPace100m, avgCadence, 50);
|
|
804
|
-
stats.push(new DataSWOLF50m(swolf50m));
|
|
805
|
-
}
|
|
806
|
-
}
|
|
807
|
-
// Active lengths
|
|
808
|
-
if (isNumberOrString(object.num_active_lengths)) {
|
|
809
|
-
stats.push(new DataActiveLengths(object.num_active_lengths));
|
|
810
|
-
}
|
|
811
|
-
// Total cycle
|
|
812
|
-
if (isNumberOrString(object.total_cycles)) {
|
|
813
|
-
stats.push(new DataTotalCycles(object.total_cycles));
|
|
814
|
-
}
|
|
815
|
-
// Description
|
|
816
|
-
if (isNumberOrString(object.description)) {
|
|
817
|
-
stats.push(new DataDescription(object.description));
|
|
818
|
-
}
|
|
819
|
-
// Cycling dynamics
|
|
820
|
-
if (Number.isFinite(object.time_standing)) {
|
|
821
|
-
const standingTime = Math.round(object.time_standing);
|
|
822
|
-
stats.push(new DataCyclingStandingTime(standingTime));
|
|
823
|
-
const seatedTime = Math.round(timerTime - standingTime);
|
|
824
|
-
stats.push(new DataCyclingSeatedTime(seatedTime));
|
|
825
|
-
}
|
|
826
|
-
// Running dynamics
|
|
827
|
-
if (Number.isFinite(object.avg_stance_time)) {
|
|
828
|
-
stats.push(new DataStanceTime(object.avg_stance_time));
|
|
829
|
-
}
|
|
830
|
-
if (Number.isFinite(object.avg_vertical_oscillation)) {
|
|
831
|
-
stats.push(new DataVerticalOscillation(object.avg_vertical_oscillation));
|
|
832
|
-
}
|
|
833
|
-
if (Number.isFinite(object.avg_vertical_ratio)) {
|
|
834
|
-
stats.push(new DataVerticalRatio(object.avg_vertical_ratio));
|
|
835
|
-
}
|
|
836
|
-
if (Number.isFinite(object.avg_step_length)) {
|
|
837
|
-
const avgStrideLengthMeters = object.avg_step_length / 1000;
|
|
838
|
-
stats.push(new DataAvgStrideLength(Math.round(avgStrideLengthMeters * 100) / 100));
|
|
839
|
-
}
|
|
840
|
-
return stats;
|
|
841
|
-
}
|
|
842
|
-
static getCreatorFromFitDataObject(fitDataObject) {
|
|
843
|
-
const toStartCase = (str) => {
|
|
844
|
-
return str.replace(/\w\S*/g, txt => txt.charAt(0).toUpperCase() + txt.substr(1).toLowerCase());
|
|
845
|
-
};
|
|
846
|
-
const formatDeviceName = (manufacturer, productName, recognizedName, recognizedBrand, isDevelopment = false) => {
|
|
847
|
-
let name = '';
|
|
848
|
-
if (recognizedBrand && recognizedName) {
|
|
849
|
-
name = `${toStartCase(recognizedBrand)} ${recognizedName}`;
|
|
850
|
-
}
|
|
851
|
-
else if (recognizedBrand && !recognizedName && productName) {
|
|
852
|
-
if (productName.match(new RegExp(`${recognizedBrand}`, 'gi'))) {
|
|
853
|
-
productName = productName.replace(new RegExp(`${recognizedBrand}`, 'gi'), '').trim();
|
|
854
|
-
}
|
|
855
|
-
name = `${toStartCase(recognizedBrand)} ${productName}`;
|
|
856
|
-
}
|
|
857
|
-
else if (recognizedBrand && !recognizedName && !productName) {
|
|
858
|
-
name = `${toStartCase(recognizedBrand)}`;
|
|
859
|
-
}
|
|
860
|
-
else if (manufacturer && !recognizedBrand && !recognizedName && !productName && !isDevelopment) {
|
|
861
|
-
const formattedManufacturer = manufacturer.replace(new RegExp('[-_]', 'gi'), ' ').trim();
|
|
862
|
-
name = `${toStartCase(formattedManufacturer)}`;
|
|
863
|
-
}
|
|
864
|
-
else if (!recognizedBrand && recognizedName) {
|
|
865
|
-
name = `${recognizedName}`;
|
|
866
|
-
}
|
|
867
|
-
else {
|
|
868
|
-
name = 'Unknown';
|
|
869
|
-
}
|
|
870
|
-
return name;
|
|
871
|
-
};
|
|
872
|
-
let creator;
|
|
873
|
-
let recognizedName = null;
|
|
874
|
-
const manufacturer = fitDataObject.file_ids[0].manufacturer;
|
|
875
|
-
const productId = fitDataObject.file_ids[0].product || null;
|
|
876
|
-
const productName = fitDataObject.file_ids[0].product_name || null;
|
|
877
|
-
switch (manufacturer) {
|
|
878
|
-
case 'suunto': {
|
|
879
|
-
recognizedName = ImporterFitSuuntoDeviceNames[productId];
|
|
880
|
-
creator = new Creator(formatDeviceName(manufacturer, productName, recognizedName, 'Suunto'), productId);
|
|
881
|
-
break;
|
|
882
|
-
}
|
|
883
|
-
case 'coros': {
|
|
884
|
-
recognizedName = ImporterFitCorosDeviceNames[productId];
|
|
885
|
-
creator = new Creator(formatDeviceName(manufacturer, productName, recognizedName, 'Coros'), productId);
|
|
886
|
-
break;
|
|
887
|
-
}
|
|
888
|
-
case 'garmin': {
|
|
889
|
-
recognizedName = GarminProfileMapper.getDeviceName(productId);
|
|
890
|
-
creator = new Creator(formatDeviceName(manufacturer, productName, recognizedName, 'Garmin'), productId);
|
|
891
|
-
break;
|
|
892
|
-
}
|
|
893
|
-
case 'wahoo_fitness': {
|
|
894
|
-
recognizedName = ImporterFitWahooDeviceNames[productId];
|
|
895
|
-
creator = new Creator(formatDeviceName(manufacturer, productName, recognizedName, 'Wahoo'), productId);
|
|
896
|
-
break;
|
|
897
|
-
}
|
|
898
|
-
case 'hammerhead': {
|
|
899
|
-
recognizedName = ImporterFitHammerheadDeviceNames[productId];
|
|
900
|
-
creator = new Creator(formatDeviceName(manufacturer, productName, recognizedName, 'Hammerhead'), productId);
|
|
901
|
-
break;
|
|
902
|
-
}
|
|
903
|
-
case 'lezyne': {
|
|
904
|
-
recognizedName = ImporterFitLezyneDeviceNames[productId];
|
|
905
|
-
creator = new Creator(formatDeviceName(manufacturer, productName, recognizedName, 'Lezyne'), productId);
|
|
906
|
-
break;
|
|
907
|
-
}
|
|
908
|
-
case 'magellan': {
|
|
909
|
-
recognizedName = ImporterFitMagellanDeviceNames[productId];
|
|
910
|
-
creator = new Creator(formatDeviceName(manufacturer, productName, recognizedName, 'Magellan'), productId);
|
|
911
|
-
break;
|
|
912
|
-
}
|
|
913
|
-
case 'saris': {
|
|
914
|
-
recognizedName = ImporterFitSarisDeviceNames[productId];
|
|
915
|
-
creator = new Creator(formatDeviceName(manufacturer, productName, recognizedName, 'Saris'), productId);
|
|
916
|
-
break;
|
|
917
|
-
}
|
|
918
|
-
case 'srm': {
|
|
919
|
-
recognizedName = ImporterFitSrmDeviceNames[productId];
|
|
920
|
-
creator = new Creator(formatDeviceName(manufacturer, productName, recognizedName, 'SRM'), productId);
|
|
921
|
-
break;
|
|
922
|
-
}
|
|
923
|
-
case 'zwift': {
|
|
924
|
-
recognizedName = 'Zwift';
|
|
925
|
-
creator = new Creator(recognizedName);
|
|
926
|
-
break;
|
|
927
|
-
}
|
|
928
|
-
case 'virtualtraining': {
|
|
929
|
-
recognizedName = 'Rouvy';
|
|
930
|
-
creator = new Creator(recognizedName);
|
|
931
|
-
break;
|
|
932
|
-
}
|
|
933
|
-
case 'the_sufferfest': {
|
|
934
|
-
recognizedName = `Wahoo SYSTM`;
|
|
935
|
-
creator = new Creator(recognizedName, productId);
|
|
936
|
-
break;
|
|
937
|
-
}
|
|
938
|
-
case 'stryd': {
|
|
939
|
-
recognizedName = `Stryd`;
|
|
940
|
-
creator = new Creator(recognizedName, productId, fitDataObject.file_creator.software_version, fitDataObject.file_creator.hardware_version, fitDataObject.file_ids[0].serial_number);
|
|
941
|
-
break;
|
|
942
|
-
}
|
|
943
|
-
case 'development': {
|
|
944
|
-
recognizedName = ImporterFitDevelopmentDeviceNames[productId];
|
|
945
|
-
creator = new Creator(formatDeviceName(manufacturer, productName, recognizedName, null, true), productId);
|
|
946
|
-
creator.isRecognized = typeof recognizedName === 'string' || recognizedName === null;
|
|
947
|
-
break;
|
|
948
|
-
}
|
|
949
|
-
default: {
|
|
950
|
-
// Try to find if it's a numeric Garmin mapping that was missed
|
|
951
|
-
const manufacturerName = typeof manufacturer === 'number' ? GarminProfileMapper.getManufacturerName(manufacturer) : manufacturer;
|
|
952
|
-
if (manufacturerName === 'garmin') {
|
|
953
|
-
recognizedName = GarminProfileMapper.getDeviceName(productId);
|
|
954
|
-
}
|
|
955
|
-
creator = new Creator(formatDeviceName(manufacturer, productName, recognizedName, manufacturerName === 'garmin' ? 'Garmin' : null), productId);
|
|
956
|
-
}
|
|
957
|
-
}
|
|
958
|
-
creator.manufacturer = manufacturer;
|
|
959
|
-
creator.isRecognized = creator.isRecognized || !!recognizedName;
|
|
960
|
-
if (fitDataObject.file_creator && isNumberOrString(fitDataObject.file_creator.hardware_version)) {
|
|
961
|
-
creator.hwInfo = String(fitDataObject.file_creator.hardware_version);
|
|
962
|
-
}
|
|
963
|
-
if (fitDataObject.file_creator && isNumberOrString(fitDataObject.file_creator.software_version)) {
|
|
964
|
-
creator.swInfo = String(fitDataObject.file_creator.software_version);
|
|
965
|
-
}
|
|
966
|
-
else if (fitDataObject.device_info && isNumberOrString(fitDataObject.device_info.software_version)) {
|
|
967
|
-
creator.swInfo = String(fitDataObject.device_info.software_version);
|
|
968
|
-
}
|
|
969
|
-
if (fitDataObject.file_ids[0] && isNumberOrString(fitDataObject.file_ids[0].serial_number)) {
|
|
970
|
-
creator.serialNumber = fitDataObject.file_ids[0].serial_number;
|
|
971
|
-
}
|
|
972
|
-
// If creator name is a number ONLY (e.g. product number), then flag it as 'Unknown'
|
|
973
|
-
if (Number.isFinite(creator.name) || creator.name.match(/^\d+$/)) {
|
|
974
|
-
creator.name = `Unknown`;
|
|
975
|
-
}
|
|
976
|
-
return creator;
|
|
977
|
-
}
|
|
978
|
-
}
|