UNPKG

@sports-alliance/sports-lib

Version:

A Library to for importing / exporting and processing GPX, TCX, FIT and JSON files from services such as Strava, Movescount, Garmin, Polar etc

486 lines (485 loc) 40.4 kB
"use strict"; var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { if (k2 === undefined) k2 = k; var desc = Object.getOwnPropertyDescriptor(m, k); if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { desc = { enumerable: true, get: function() { return m[k]; } }; } Object.defineProperty(o, k2, desc); }) : (function(o, m, k, k2) { if (k2 === undefined) k2 = k; o[k2] = m[k]; })); var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { Object.defineProperty(o, "default", { enumerable: true, value: v }); }) : function(o, v) { o["default"] = v; }); var __importStar = (this && this.__importStar) || (function () { var ownKeys = function(o) { ownKeys = Object.getOwnPropertyNames || function (o) { var ar = []; for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k; return ar; }; return ownKeys(o); }; return function (mod) { if (mod && mod.__esModule) return mod; var result = {}; if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]); __setModuleDefault(result, mod); return result; }; })(); var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } return new (P || (P = Promise))(function (resolve, reject) { function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } step((generator = generator.apply(thisArg, _arguments || [])).next()); }); }; Object.defineProperty(exports, "__esModule", { value: true }); const fs = __importStar(require("fs")); const path = __importStar(require("path")); const importer_fit_1 = require("../adapters/importers/fit/importer.fit"); const activity_utilities_1 = require("./activity.utilities"); const activity_1 = require("../../activities/activity"); const activity_types_1 = require("../../activities/activity.types"); const creator_1 = require("../../creators/creator"); const stream_1 = require("../../streams/stream"); const data_air_power_avg_1 = require("../../data/data.air-power-avg"); const data_air_power_max_1 = require("../../data/data.air-power-max"); const data_air_power_min_1 = require("../../data/data.air-power-min"); const data_vertical_speed_1 = require("../../data/data.vertical-speed"); const data_vertical_speed_avg_1 = require("../../data/data.vertical-speed-avg"); const data_vertical_speed_max_1 = require("../../data/data.vertical-speed-max"); const data_vertical_speed_min_1 = require("../../data/data.vertical-speed-min"); const data_ground_contact_time_max_1 = require("../../data/data.ground-contact-time-max"); const data_ground_contact_time_min_1 = require("../../data/data.ground-contact-time-min"); const data_vertical_oscillation_max_1 = require("../../data/data.vertical-oscillation-max"); const data_vertical_oscillation_min_1 = require("../../data/data.vertical-oscillation-min"); const data_grade_adjusted_pace_max_1 = require("../../data/data.grade-adjusted-pace-max"); const data_grade_adjusted_pace_min_1 = require("../../data/data.grade-adjusted-pace-min"); const data_grade_adjusted_speed_max_1 = require("../../data/data.grade-adjusted-speed-max"); const data_grade_adjusted_speed_min_1 = require("../../data/data.grade-adjusted-speed-min"); const data_pace_max_1 = require("../../data/data.pace-max"); const data_pace_min_1 = require("../../data/data.pace-min"); const data_pace_1 = require("../../data/data.pace"); const data_swim_pace_max_1 = require("../../data/data.swim-pace-max"); const data_swim_pace_min_1 = require("../../data/data.swim-pace-min"); const data_temperature_min_1 = require("../../data/data.temperature-min"); const data_altitude_avg_1 = require("../../data/data.altitude-avg"); const data_grade_adjusted_pace_1 = require("../../data/data.grade-adjusted-pace"); const data_jump_event_1 = require("../../data/data.jump-event"); const data_duration_1 = require("../../data/data.duration"); const data_pause_1 = require("../../data/data.pause"); const data_jump_stats_1 = require("../../data/data.jump-stats"); const data_leg_stiffness_min_1 = require("../../data/data.leg-stiffness-min"); const data_leg_stiffness_max_1 = require("../../data/data.leg-stiffness-max"); const data_vertical_ratio_min_1 = require("../../data/data.vertical-ratio-min"); const data_vertical_ratio_max_1 = require("../../data/data.vertical-ratio-max"); const data_evpe_1 = require("../../data/data.evpe"); const data_evpe_min_1 = require("../../data/data.evpe-min"); const data_evpe_max_1 = require("../../data/data.evpe-max"); const data_evpe_avg_1 = require("../../data/data.evpe-avg"); const data_ehpe_1 = require("../../data/data.ehpe"); const data_ehpe_min_1 = require("../../data/data.ehpe-min"); const data_ehpe_max_1 = require("../../data/data.ehpe-max"); const data_ehpe_avg_1 = require("../../data/data.ehpe-avg"); const data_distance_1 = require("../../data/data.distance"); const data_satellite_5_best_snr_1 = require("../../data/data.satellite-5-best-snr"); const data_satellite_5_best_snr_min_1 = require("../../data/data.satellite-5-best-snr-min"); const data_satellite_5_best_snr_max_1 = require("../../data/data.satellite-5-best-snr-max"); const data_satellite_5_best_snr_avg_1 = require("../../data/data.satellite-5-best-snr-avg"); const data_number_of_satellites_1 = require("../../data/data.number-of-satellites"); const data_number_of_satellites_min_1 = require("../../data/data.number-of-satellites-min"); const data_number_of_satellites_max_1 = require("../../data/data.number-of-satellites-max"); const data_number_of_satellites_avg_1 = require("../../data/data.number-of-satellites-avg"); const data_power_normalized_1 = require("../../data/data.power-normalized"); const data_store_1 = require("../../data/data.store"); const helpers_1 = require("./helpers"); const user_unit_settings_interface_1 = require("../../users/settings/user.unit.settings.interface"); const data_speed_1 = require("../../data/data.speed"); const toArrayBuffer = (filePath) => { const fileContent = fs.readFileSync(filePath); return fileContent.buffer.slice(fileContent.byteOffset, fileContent.byteOffset + fileContent.byteLength); }; const loadActivity = (relativeFixturePath) => __awaiter(void 0, void 0, void 0, function* () { const fitFilePath = path.join(__dirname, relativeFixturePath); const event = yield importer_fit_1.EventImporterFIT.getFromArrayBuffer(toArrayBuffer(fitFilePath)); const activity = event.getFirstActivity(); activity_utilities_1.ActivityUtilities.generateMissingStreamsAndStatsForActivity(activity); return activity; }); const getStatValue = (activity, type) => { const stat = activity.getStat(type); if (!stat) throw new Error(`Missing stat for type ${type}`); return stat.getValue(); }; const ensureMinStatFromStream = (activity, statType, streamType, StatCtor) => { const stat = activity.getStat(statType); if (stat && stat.getValue() !== Infinity) return stat; const stream = activity.getStreamData(streamType); if (!stream || stream.length === 0) return null; const finiteValues = stream.filter((v) => Number.isFinite(v)); if (!finiteValues.length) return null; const min = Math.min(...finiteValues); const newStat = new StatCtor(min); activity.addStat(newStat); return newStat; }; const milesDistanceSettings = { speedUnits: [], swimPaceUnits: [], paceUnits: [], gradeAdjustedSpeedUnits: [], gradeAdjustedPaceUnits: [], verticalSpeedUnits: [], distanceUnits: user_unit_settings_interface_1.DistanceUnits.Miles }; const kilometersDistanceSettings = { speedUnits: [], swimPaceUnits: [], paceUnits: [], gradeAdjustedSpeedUnits: [], gradeAdjustedPaceUnits: [], verticalSpeedUnits: [], distanceUnits: user_unit_settings_interface_1.DistanceUnits.Kilometers }; const mphSpeedSettings = { speedUnits: [data_speed_1.DataSpeedMilesPerHour.type], swimPaceUnits: [], paceUnits: [], gradeAdjustedSpeedUnits: [], gradeAdjustedPaceUnits: [], verticalSpeedUnits: [], distanceUnits: user_unit_settings_interface_1.DistanceUnits.Kilometers }; describe('ActivityUtilities summary aggregation integration', () => { describe('distance unit conversion integration', () => { it('converts parsed total distance stat to miles when distanceUnits is miles', () => __awaiter(void 0, void 0, void 0, function* () { const activity = yield loadActivity('../../specs/fixtures/runs/fit/6860622783.fit'); const distance = activity.getStat(data_distance_1.DataDistance.type); expect(distance).toBeDefined(); const converted = data_store_1.DynamicDataLoader.getUnitBasedDataFromDataInstance(distance, milesDistanceSettings); expect(converted).toHaveLength(1); expect(converted[0].getType()).toBe(data_distance_1.DataDistanceMiles.type); expect(converted[0].getValue()).toBeCloseTo((0, helpers_1.convertMetersToMiles)(distance.getValue()), 10); expect(converted[0].getDisplayUnit()).toBe('mi'); })); it('converts jump distance summary stats to imperial display only in miles mode', () => { const activityA = new activity_1.Activity(new Date(0), new Date(10000), activity_types_1.ActivityTypes.MountainBiking, new creator_1.Creator('test')); const activityB = new activity_1.Activity(new Date(20000), new Date(30000), activity_types_1.ActivityTypes.MountainBiking, new creator_1.Creator('test')); activityA.addEvent(new data_jump_event_1.DataJumpEvent(1, { distance: 2, height: 0.5, score: 10, hang_time: 0.2, speed: 5, rotations: 0 })); activityA.addEvent(new data_jump_event_1.DataJumpEvent(2, { distance: 4, height: 0.7, score: 12, hang_time: 0.3, speed: 6, rotations: 1 })); activityB.addEvent(new data_jump_event_1.DataJumpEvent(3, { distance: 6, height: 0.8, score: 14, hang_time: 0.4, speed: 7, rotations: 1 })); activityB.addEvent(new data_jump_event_1.DataJumpEvent(4, { distance: 8, height: 1.0, score: 16, hang_time: 0.5, speed: 8, rotations: 2 })); activity_utilities_1.ActivityUtilities.generateMissingStreamsAndStatsForActivity(activityA); activity_utilities_1.ActivityUtilities.generateMissingStreamsAndStatsForActivity(activityB); const summaryStats = activity_utilities_1.ActivityUtilities.getSummaryStatsForActivities([activityA, activityB]); const jumpDistanceAvg = summaryStats.find(s => s.getType() === data_jump_stats_1.DataJumpDistanceAvg.type); expect(jumpDistanceAvg).toBeDefined(); const milesConverted = data_store_1.DynamicDataLoader.getUnitBasedDataFromDataInstance(jumpDistanceAvg, milesDistanceSettings); expect(milesConverted).toHaveLength(1); expect(milesConverted[0].getType()).toBe(data_distance_1.DataDistanceFeet.type); expect(milesConverted[0].getValue()).toBeCloseTo((0, helpers_1.convertMetersToFeet)(jumpDistanceAvg.getValue()), 10); expect(milesConverted[0].getDisplayUnit()).toBe('ft'); const kilometersConverted = data_store_1.DynamicDataLoader.getUnitBasedDataFromDataInstance(jumpDistanceAvg, kilometersDistanceSettings); expect(kilometersConverted).toHaveLength(1); expect(kilometersConverted[0].getType()).toBe(data_jump_stats_1.DataJumpDistanceAvg.type); }); it('converts jump speed summary stats to selected speed unit display variants', () => { const activityA = new activity_1.Activity(new Date(0), new Date(10000), activity_types_1.ActivityTypes.MountainBiking, new creator_1.Creator('test')); const activityB = new activity_1.Activity(new Date(20000), new Date(30000), activity_types_1.ActivityTypes.MountainBiking, new creator_1.Creator('test')); activityA.addEvent(new data_jump_event_1.DataJumpEvent(1, { distance: 2, height: 0.5, score: 10, hang_time: 0.2, speed: 5, rotations: 0 })); activityA.addEvent(new data_jump_event_1.DataJumpEvent(2, { distance: 4, height: 0.7, score: 12, hang_time: 0.3, speed: 6, rotations: 1 })); activityB.addEvent(new data_jump_event_1.DataJumpEvent(3, { distance: 6, height: 0.8, score: 14, hang_time: 0.4, speed: 7, rotations: 1 })); activityB.addEvent(new data_jump_event_1.DataJumpEvent(4, { distance: 8, height: 1.0, score: 16, hang_time: 0.5, speed: 8, rotations: 2 })); activity_utilities_1.ActivityUtilities.generateMissingStreamsAndStatsForActivity(activityA); activity_utilities_1.ActivityUtilities.generateMissingStreamsAndStatsForActivity(activityB); const summaryStats = activity_utilities_1.ActivityUtilities.getSummaryStatsForActivities([activityA, activityB]); const jumpSpeedAvg = summaryStats.find(s => s.getType() === data_jump_stats_1.DataJumpSpeedAvg.type); expect(jumpSpeedAvg).toBeDefined(); const converted = data_store_1.DynamicDataLoader.getUnitBasedDataFromDataInstance(jumpSpeedAvg, mphSpeedSettings); expect(converted).toHaveLength(1); expect(converted[0].getType()).toBe(data_jump_stats_1.DataJumpSpeedAvgMilesPerHour.type); expect(converted[0].getDisplayUnit()).toBe('mph'); expect(converted[0].getValue()).toBeCloseTo((0, helpers_1.convertSpeedToSpeedInMilesPerHour)(jumpSpeedAvg.getValue()), 10); }); it('converts jump speed min and max summary stats to jump-specific speed unit variants', () => { const activityA = new activity_1.Activity(new Date(0), new Date(10000), activity_types_1.ActivityTypes.MountainBiking, new creator_1.Creator('test')); const activityB = new activity_1.Activity(new Date(20000), new Date(30000), activity_types_1.ActivityTypes.MountainBiking, new creator_1.Creator('test')); activityA.addEvent(new data_jump_event_1.DataJumpEvent(1, { distance: 2, height: 0.5, score: 10, hang_time: 0.2, speed: 5, rotations: 0 })); activityA.addEvent(new data_jump_event_1.DataJumpEvent(2, { distance: 4, height: 0.7, score: 12, hang_time: 0.3, speed: 6, rotations: 1 })); activityB.addEvent(new data_jump_event_1.DataJumpEvent(3, { distance: 6, height: 0.8, score: 14, hang_time: 0.4, speed: 7, rotations: 1 })); activityB.addEvent(new data_jump_event_1.DataJumpEvent(4, { distance: 8, height: 1.0, score: 16, hang_time: 0.5, speed: 8, rotations: 2 })); activity_utilities_1.ActivityUtilities.generateMissingStreamsAndStatsForActivity(activityA); activity_utilities_1.ActivityUtilities.generateMissingStreamsAndStatsForActivity(activityB); const summaryStats = activity_utilities_1.ActivityUtilities.getSummaryStatsForActivities([activityA, activityB]); const jumpSpeedMin = summaryStats.find(s => s.getType() === data_jump_stats_1.DataJumpSpeedMin.type); const jumpSpeedMax = summaryStats.find(s => s.getType() === data_jump_stats_1.DataJumpSpeedMax.type); expect(jumpSpeedMin).toBeDefined(); expect(jumpSpeedMax).toBeDefined(); const convertedMin = data_store_1.DynamicDataLoader.getUnitBasedDataFromDataInstance(jumpSpeedMin, mphSpeedSettings); const convertedMax = data_store_1.DynamicDataLoader.getUnitBasedDataFromDataInstance(jumpSpeedMax, mphSpeedSettings); expect(convertedMin).toHaveLength(1); expect(convertedMin[0].getType()).toBe(data_jump_stats_1.DataJumpSpeedMinMilesPerHour.type); expect(convertedMin[0].getDisplayUnit()).toBe('mph'); expect(convertedMin[0].getValue()).toBeCloseTo((0, helpers_1.convertSpeedToSpeedInMilesPerHour)(jumpSpeedMin.getValue()), 10); expect(convertedMax).toHaveLength(1); expect(convertedMax[0].getType()).toBe(data_jump_stats_1.DataJumpSpeedMaxMilesPerHour.type); expect(convertedMax[0].getDisplayUnit()).toBe('mph'); expect(convertedMax[0].getValue()).toBeCloseTo((0, helpers_1.convertSpeedToSpeedInMilesPerHour)(jumpSpeedMax.getValue()), 10); }); }); describe('getSummaryStatsForActivities', () => { it('aggregates Power Normalized across selected activities', () => { const a1 = new activity_1.Activity(new Date(0), new Date(10000), activity_types_1.ActivityTypes.Cycling, new creator_1.Creator('test')); a1.addStat(new data_duration_1.DataDuration(10000)); a1.addStat(new data_pause_1.DataPause(0)); a1.addStat(new data_distance_1.DataDistance(1000)); a1.addStat(new data_power_normalized_1.DataPowerNormalized(210)); const a2 = new activity_1.Activity(new Date(0), new Date(15000), activity_types_1.ActivityTypes.Cycling, new creator_1.Creator('test')); a2.addStat(new data_duration_1.DataDuration(15000)); a2.addStat(new data_pause_1.DataPause(0)); a2.addStat(new data_distance_1.DataDistance(1500)); a2.addStat(new data_power_normalized_1.DataPowerNormalized(190)); const stats = activity_utilities_1.ActivityUtilities.getSummaryStatsForActivities([a1, a2]); const normalizedPower = stats.find(s => s.getType() === data_power_normalized_1.DataPowerNormalized.type); expect(normalizedPower).toBeDefined(); expect(normalizedPower.getValue()).toBe(200); }); it('aggregates Air Power stats when only one activity has the stream', () => __awaiter(void 0, void 0, void 0, function* () { const activityWithAir = yield loadActivity('../../specs/fixtures/runs/fit/6860622783.fit'); const activityWithoutAir = yield loadActivity('../../specs/fixtures/runs/fit/2067489619.fit'); const stats = activity_utilities_1.ActivityUtilities.getSummaryStatsForActivities([activityWithAir, activityWithoutAir]); const avg = stats.find(s => s.getType() === data_air_power_avg_1.DataAirPowerAvg.type); const min = stats.find(s => s.getType() === data_air_power_min_1.DataAirPowerMin.type); const max = stats.find(s => s.getType() === data_air_power_max_1.DataAirPowerMax.type); expect(avg).toBeDefined(); expect(min).toBeDefined(); expect(max).toBeDefined(); const expectedAvg = getStatValue(activityWithAir, data_air_power_avg_1.DataAirPowerAvg.type); const expectedMin = getStatValue(activityWithAir, data_air_power_min_1.DataAirPowerMin.type); const expectedMax = getStatValue(activityWithAir, data_air_power_max_1.DataAirPowerMax.type); expect(avg.getValue()).toBe(expectedAvg); expect(min.getValue()).toBe(expectedMin); expect(max.getValue()).toBe(expectedMax); })); it('generates and aggregates EVPE, Satellite 5 Best SNR and Number of Satellites min/max/avg', () => { var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m; const a1 = new activity_1.Activity(new Date(0), new Date(10000), activity_types_1.ActivityTypes.Running, new creator_1.Creator('test')); a1.addStream(new stream_1.Stream(data_evpe_1.DataEVPE.type, [4.0, 4.5, 5.0])); a1.addStream(new stream_1.Stream(data_ehpe_1.DataEHPE.type, [3.0, 3.6, 4.2])); a1.addStream(new stream_1.Stream(data_satellite_5_best_snr_1.DataSatellite5BestSNR.type, [30, 32, 31])); a1.addStream(new stream_1.Stream(data_number_of_satellites_1.DataNumberOfSatellites.type, [8, 9, 10])); activity_utilities_1.ActivityUtilities.generateMissingStreamsAndStatsForActivity(a1); const a2 = new activity_1.Activity(new Date(0), new Date(10000), activity_types_1.ActivityTypes.Running, new creator_1.Creator('test')); a2.addStream(new stream_1.Stream(data_evpe_1.DataEVPE.type, [3.5, 4.2, 4.8])); a2.addStream(new stream_1.Stream(data_ehpe_1.DataEHPE.type, [2.8, 3.2, 3.8])); a2.addStream(new stream_1.Stream(data_satellite_5_best_snr_1.DataSatellite5BestSNR.type, [33, 34, 35])); a2.addStream(new stream_1.Stream(data_number_of_satellites_1.DataNumberOfSatellites.type, [10, 11, 12])); activity_utilities_1.ActivityUtilities.generateMissingStreamsAndStatsForActivity(a2); const stats = activity_utilities_1.ActivityUtilities.getSummaryStatsForActivities([a1, a2]); const getSummary = (type) => stats.find(s => s.getType() === type); expect((_a = getSummary(data_evpe_min_1.DataEVPEMin.type)) === null || _a === void 0 ? void 0 : _a.getValue()).toBe(Math.min(4.0, 3.5)); expect((_b = getSummary(data_evpe_max_1.DataEVPEMax.type)) === null || _b === void 0 ? void 0 : _b.getValue()).toBe(Math.max(5.0, 4.8)); expect((_c = getSummary(data_evpe_avg_1.DataEVPEAvg.type)) === null || _c === void 0 ? void 0 : _c.getValue()).toBeCloseTo((4.5 + 4.1666666667) / 2, 10); expect((_d = getSummary(data_ehpe_min_1.DataEHPEMin.type)) === null || _d === void 0 ? void 0 : _d.getValue()).toBe(Math.min(3.0, 2.8)); expect((_e = getSummary(data_ehpe_max_1.DataEHPEMax.type)) === null || _e === void 0 ? void 0 : _e.getValue()).toBe(Math.max(4.2, 3.8)); expect((_f = getSummary(data_ehpe_avg_1.DataEHPEAvg.type)) === null || _f === void 0 ? void 0 : _f.getValue()).toBeCloseTo((3.6 + 3.2666666667) / 2, 10); expect((_g = getSummary(data_satellite_5_best_snr_min_1.DataSatellite5BestSNRMin.type)) === null || _g === void 0 ? void 0 : _g.getValue()).toBe(Math.min(30, 33)); expect((_h = getSummary(data_satellite_5_best_snr_max_1.DataSatellite5BestSNRMax.type)) === null || _h === void 0 ? void 0 : _h.getValue()).toBe(Math.max(32, 35)); expect((_j = getSummary(data_satellite_5_best_snr_avg_1.DataSatellite5BestSNRAvg.type)) === null || _j === void 0 ? void 0 : _j.getValue()).toBeCloseTo((31 + 34) / 2, 10); expect((_k = getSummary(data_number_of_satellites_min_1.DataNumberOfSatellitesMin.type)) === null || _k === void 0 ? void 0 : _k.getValue()).toBe(Math.min(8, 10)); expect((_l = getSummary(data_number_of_satellites_max_1.DataNumberOfSatellitesMax.type)) === null || _l === void 0 ? void 0 : _l.getValue()).toBe(Math.max(10, 12)); expect((_m = getSummary(data_number_of_satellites_avg_1.DataNumberOfSatellitesAvg.type)) === null || _m === void 0 ? void 0 : _m.getValue()).toBeCloseTo((9 + 11) / 2, 10); }); it('aggregates Vertical Speed min/max across activities', () => __awaiter(void 0, void 0, void 0, function* () { const a1 = yield loadActivity('../../specs/fixtures/runs/fit/6909950168.fit'); const a2 = yield loadActivity('../../specs/fixtures/runs/fit/6910052863.fit'); const stats = activity_utilities_1.ActivityUtilities.getSummaryStatsForActivities([a1, a2]); const min = stats.find(s => s.getType() === data_vertical_speed_min_1.DataVerticalSpeedMin.type); const max = stats.find(s => s.getType() === data_vertical_speed_max_1.DataVerticalSpeedMax.type); expect(min).toBeDefined(); expect(max).toBeDefined(); expect(min.getValue()).toBe(Math.min(getStatValue(a1, data_vertical_speed_min_1.DataVerticalSpeedMin.type), getStatValue(a2, data_vertical_speed_min_1.DataVerticalSpeedMin.type))); expect(max.getValue()).toBe(Math.max(getStatValue(a1, data_vertical_speed_max_1.DataVerticalSpeedMax.type), getStatValue(a2, data_vertical_speed_max_1.DataVerticalSpeedMax.type))); })); it('creates base vertical-speed unit stats from average vertical speed', () => __awaiter(void 0, void 0, void 0, function* () { const activity = yield loadActivity('../../specs/fixtures/runs/fit/6909950168.fit'); const avg = activity.getStat(data_vertical_speed_avg_1.DataVerticalSpeedAvg.type); const base = activity.getStat(data_vertical_speed_1.DataVerticalSpeed.type); const kph = activity.getStat(data_vertical_speed_1.DataVerticalSpeedKilometerPerHour.type); expect(avg).toBeDefined(); expect(base).toBeDefined(); expect(kph).toBeDefined(); expect(base.getValue()).toBeCloseTo(avg.getValue(), 10); expect(kph.getValue()).toBeCloseTo((0, helpers_1.convertSpeedToSpeedInKilometersPerHour)(avg.getValue()), 10); })); it('aggregates Ground Contact Time min/max across activities', () => __awaiter(void 0, void 0, void 0, function* () { const a1 = yield loadActivity('../../specs/fixtures/runs/fit/6782987395.fit'); const a2 = yield loadActivity('../../specs/fixtures/runs/fit/6860622783.fit'); const stats = activity_utilities_1.ActivityUtilities.getSummaryStatsForActivities([a1, a2]); const min = stats.find(s => s.getType() === data_ground_contact_time_min_1.DataGroundContactTimeMin.type); const max = stats.find(s => s.getType() === data_ground_contact_time_max_1.DataGroundContactTimeMax.type); expect(min).toBeDefined(); expect(max).toBeDefined(); expect(min.getValue()).toBe(Math.min(getStatValue(a1, data_ground_contact_time_min_1.DataGroundContactTimeMin.type), getStatValue(a2, data_ground_contact_time_min_1.DataGroundContactTimeMin.type))); expect(max.getValue()).toBe(Math.max(getStatValue(a1, data_ground_contact_time_max_1.DataGroundContactTimeMax.type), getStatValue(a2, data_ground_contact_time_max_1.DataGroundContactTimeMax.type))); })); it('aggregates Vertical Oscillation min/max across activities', () => __awaiter(void 0, void 0, void 0, function* () { const a1 = yield loadActivity('../../specs/fixtures/runs/fit/6860622783.fit'); const a2 = yield loadActivity('../../specs/fixtures/runs/fit/6916663933.fit'); const stats = activity_utilities_1.ActivityUtilities.getSummaryStatsForActivities([a1, a2]); const min = stats.find(s => s.getType() === data_vertical_oscillation_min_1.DataVerticalOscillationMin.type); const max = stats.find(s => s.getType() === data_vertical_oscillation_max_1.DataVerticalOscillationMax.type); expect(min).toBeDefined(); expect(max).toBeDefined(); expect(min.getValue()).toBe(Math.min(getStatValue(a1, data_vertical_oscillation_min_1.DataVerticalOscillationMin.type), getStatValue(a2, data_vertical_oscillation_min_1.DataVerticalOscillationMin.type))); expect(max.getValue()).toBe(Math.max(getStatValue(a1, data_vertical_oscillation_max_1.DataVerticalOscillationMax.type), getStatValue(a2, data_vertical_oscillation_max_1.DataVerticalOscillationMax.type))); })); it('aggregates Leg Stiffness min/max across activities', () => __awaiter(void 0, void 0, void 0, function* () { const a1 = yield loadActivity('../../specs/fixtures/runs/fit/6782987395.fit'); const a2 = yield loadActivity('../../specs/fixtures/runs/fit/6860622783.fit'); const stats = activity_utilities_1.ActivityUtilities.getSummaryStatsForActivities([a1, a2]); const min = stats.find(s => s.getType() === data_leg_stiffness_min_1.DataLegStiffnessMin.type); const max = stats.find(s => s.getType() === data_leg_stiffness_max_1.DataLegStiffnessMax.type); expect(min).toBeDefined(); expect(max).toBeDefined(); expect(min.getValue()).toBe(Math.min(getStatValue(a1, data_leg_stiffness_min_1.DataLegStiffnessMin.type), getStatValue(a2, data_leg_stiffness_min_1.DataLegStiffnessMin.type))); expect(max.getValue()).toBe(Math.max(getStatValue(a1, data_leg_stiffness_max_1.DataLegStiffnessMax.type), getStatValue(a2, data_leg_stiffness_max_1.DataLegStiffnessMax.type))); })); it('aggregates Vertical Ratio min/max across activities', () => __awaiter(void 0, void 0, void 0, function* () { const a1 = yield loadActivity('../../specs/fixtures/runs/fit/6860622783.fit'); const a2 = yield loadActivity('../../specs/fixtures/runs/fit/6916663933.fit'); const stats = activity_utilities_1.ActivityUtilities.getSummaryStatsForActivities([a1, a2]); const min = stats.find(s => s.getType() === data_vertical_ratio_min_1.DataVerticalRatioMin.type); const max = stats.find(s => s.getType() === data_vertical_ratio_max_1.DataVerticalRatioMax.type); expect(min).toBeDefined(); expect(max).toBeDefined(); expect(min.getValue()).toBe(Math.min(getStatValue(a1, data_vertical_ratio_min_1.DataVerticalRatioMin.type), getStatValue(a2, data_vertical_ratio_min_1.DataVerticalRatioMin.type))); expect(max.getValue()).toBe(Math.max(getStatValue(a1, data_vertical_ratio_max_1.DataVerticalRatioMax.type), getStatValue(a2, data_vertical_ratio_max_1.DataVerticalRatioMax.type))); })); it('aggregates Grade Adjusted Pace min/max across activities', () => __awaiter(void 0, void 0, void 0, function* () { const a1 = yield loadActivity('../../specs/fixtures/runs/fit/2067489619.fit'); const a2 = yield loadActivity('../../specs/fixtures/runs/fit/6910052863.fit'); ensureMinStatFromStream(a1, data_grade_adjusted_pace_min_1.DataGradeAdjustedPaceMin.type, data_grade_adjusted_pace_1.DataGradeAdjustedPace.type, data_grade_adjusted_pace_min_1.DataGradeAdjustedPaceMin); ensureMinStatFromStream(a2, data_grade_adjusted_pace_min_1.DataGradeAdjustedPaceMin.type, data_grade_adjusted_pace_1.DataGradeAdjustedPace.type, data_grade_adjusted_pace_min_1.DataGradeAdjustedPaceMin); const stats = activity_utilities_1.ActivityUtilities.getSummaryStatsForActivities([a1, a2]); const min = stats.find(s => s.getType() === data_grade_adjusted_pace_min_1.DataGradeAdjustedPaceMin.type); const max = stats.find(s => s.getType() === data_grade_adjusted_pace_max_1.DataGradeAdjustedPaceMax.type); expect(min).toBeDefined(); expect(max).toBeDefined(); expect(min.getValue()).toBe(Math.min(getStatValue(a1, data_grade_adjusted_pace_min_1.DataGradeAdjustedPaceMin.type), getStatValue(a2, data_grade_adjusted_pace_min_1.DataGradeAdjustedPaceMin.type))); expect(max.getValue()).toBe(Math.max(getStatValue(a1, data_grade_adjusted_pace_max_1.DataGradeAdjustedPaceMax.type), getStatValue(a2, data_grade_adjusted_pace_max_1.DataGradeAdjustedPaceMax.type))); })); it('aggregates Grade Adjusted Speed min/max across activities', () => __awaiter(void 0, void 0, void 0, function* () { const a1 = yield loadActivity('../../specs/fixtures/runs/fit/2067489619.fit'); const a2 = yield loadActivity('../../specs/fixtures/runs/fit/6916663933.fit'); const stats = activity_utilities_1.ActivityUtilities.getSummaryStatsForActivities([a1, a2]); const min = stats.find(s => s.getType() === data_grade_adjusted_speed_min_1.DataGradeAdjustedSpeedMin.type); const max = stats.find(s => s.getType() === data_grade_adjusted_speed_max_1.DataGradeAdjustedSpeedMax.type); expect(min).toBeDefined(); expect(max).toBeDefined(); expect(min.getValue()).toBe(Math.min(getStatValue(a1, data_grade_adjusted_speed_min_1.DataGradeAdjustedSpeedMin.type), getStatValue(a2, data_grade_adjusted_speed_min_1.DataGradeAdjustedSpeedMin.type))); expect(max.getValue()).toBe(Math.max(getStatValue(a1, data_grade_adjusted_speed_max_1.DataGradeAdjustedSpeedMax.type), getStatValue(a2, data_grade_adjusted_speed_max_1.DataGradeAdjustedSpeedMax.type))); })); it('aggregates Pace min/max across activities', () => __awaiter(void 0, void 0, void 0, function* () { const a1 = yield loadActivity('../../specs/fixtures/runs/fit/6909950168.fit'); const a2 = yield loadActivity('../../specs/fixtures/runs/fit/6916728382.fit'); ensureMinStatFromStream(a1, data_pace_min_1.DataPaceMin.type, data_pace_1.DataPace.type, data_pace_min_1.DataPaceMin); ensureMinStatFromStream(a2, data_pace_min_1.DataPaceMin.type, data_pace_1.DataPace.type, data_pace_min_1.DataPaceMin); const stats = activity_utilities_1.ActivityUtilities.getSummaryStatsForActivities([a1, a2]); const min = stats.find(s => s.getType() === data_pace_min_1.DataPaceMin.type); const max = stats.find(s => s.getType() === data_pace_max_1.DataPaceMax.type); expect(min).toBeDefined(); expect(max).toBeDefined(); expect(min.getValue()).toBe(Math.min(getStatValue(a1, data_pace_min_1.DataPaceMin.type), getStatValue(a2, data_pace_min_1.DataPaceMin.type))); expect(max.getValue()).toBe(Math.max(getStatValue(a1, data_pace_max_1.DataPaceMax.type), getStatValue(a2, data_pace_max_1.DataPaceMax.type))); })); it('aggregates Swim Pace min/max across activities', () => __awaiter(void 0, void 0, void 0, function* () { const a1 = yield loadActivity('../../specs/fixtures/swim/fit/6021532030.fit'); const a2 = yield loadActivity('../../specs/fixtures/swim/fit/6688025408.fit'); const stats = activity_utilities_1.ActivityUtilities.getSummaryStatsForActivities([a1, a2]); const min = stats.find(s => s.getType() === data_swim_pace_min_1.DataSwimPaceMin.type); const max = stats.find(s => s.getType() === data_swim_pace_max_1.DataSwimPaceMax.type); expect(min).toBeDefined(); expect(max).toBeDefined(); expect(min.getValue()).toBe(Math.min(getStatValue(a1, data_swim_pace_min_1.DataSwimPaceMin.type), getStatValue(a2, data_swim_pace_min_1.DataSwimPaceMin.type))); expect(max.getValue()).toBe(Math.max(getStatValue(a1, data_swim_pace_max_1.DataSwimPaceMax.type), getStatValue(a2, data_swim_pace_max_1.DataSwimPaceMax.type))); })); it('aggregates Temperature min across activities', () => __awaiter(void 0, void 0, void 0, function* () { const a1 = yield loadActivity('../../specs/fixtures/runs/fit/6782987395.fit'); const a2 = yield loadActivity('../../specs/fixtures/runs/fit/6909950168.fit'); const stats = activity_utilities_1.ActivityUtilities.getSummaryStatsForActivities([a1, a2]); const min = stats.find(s => s.getType() === data_temperature_min_1.DataTemperatureMin.type); expect(min).toBeDefined(); expect(min.getValue()).toBe(Math.min(getStatValue(a1, data_temperature_min_1.DataTemperatureMin.type), getStatValue(a2, data_temperature_min_1.DataTemperatureMin.type))); })); it('aggregates Altitude average across activities', () => __awaiter(void 0, void 0, void 0, function* () { const a1 = yield loadActivity('../../specs/fixtures/runs/fit/6782987395.fit'); const a2 = yield loadActivity('../../specs/fixtures/runs/fit/6916728382.fit'); const stats = activity_utilities_1.ActivityUtilities.getSummaryStatsForActivities([a1, a2]); const avg = stats.find(s => s.getType() === data_altitude_avg_1.DataAltitudeAvg.type); expect(avg).toBeDefined(); const expectedAvg = (getStatValue(a1, data_altitude_avg_1.DataAltitudeAvg.type) + getStatValue(a2, data_altitude_avg_1.DataAltitudeAvg.type)) / 2; expect(avg.getValue()).toBe(expectedAvg); })); }); describe('generateMissingStreamsAndStatsForActivity', () => { const createJumpEvent = (timestamp, values) => { var _a, _b; return new data_jump_event_1.DataJumpEvent(timestamp, { distance: (_a = values.distance) !== null && _a !== void 0 ? _a : 0, height: values.height, score: (_b = values.score) !== null && _b !== void 0 ? _b : 0, hang_time: values.hang_time, speed: values.speed, rotations: values.rotations }); }; it('derives missing jump min/max/avg stats from jump events', () => { const activity = new activity_1.Activity(new Date(0), new Date(10000), activity_types_1.ActivityTypes.MountainBiking, new creator_1.Creator('test')); activity.addEvent(createJumpEvent(1, { distance: 2, hang_time: 0.4, speed: 6, rotations: 1, score: 50, height: 0.8 })); activity.addEvent(createJumpEvent(2, { distance: 4, hang_time: 0.6, speed: 8, rotations: 0, score: 70, height: 1.2 })); activity_utilities_1.ActivityUtilities.generateMissingStreamsAndStatsForActivity(activity); expect(activity.getStat(data_jump_stats_1.DataJumpDistanceMin.type).getValue()).toBe(2); expect(activity.getStat(data_jump_stats_1.DataJumpDistanceMax.type).getValue()).toBe(4); expect(activity.getStat(data_jump_stats_1.DataJumpDistanceAvg.type).getValue()).toBeCloseTo(3, 10); expect(activity.getStat(data_jump_stats_1.DataJumpHangTimeMin.type).getValue()).toBeCloseTo(0.4, 10); expect(activity.getStat(data_jump_stats_1.DataJumpHangTimeMax.type).getValue()).toBeCloseTo(0.6, 10); expect(activity.getStat(data_jump_stats_1.DataJumpHangTimeAvg.type).getValue()).toBeCloseTo(0.5, 10); expect(activity.getStat(data_jump_stats_1.DataJumpSpeedMin.type).getValue()).toBe(6); expect(activity.getStat(data_jump_stats_1.DataJumpSpeedMax.type).getValue()).toBe(8); expect(activity.getStat(data_jump_stats_1.DataJumpSpeedAvg.type).getValue()).toBeCloseTo(7, 10); expect(activity.getStat(data_jump_stats_1.DataJumpRotationsMin.type).getValue()).toBe(0); expect(activity.getStat(data_jump_stats_1.DataJumpRotationsMax.type).getValue()).toBe(1); expect(activity.getStat(data_jump_stats_1.DataJumpRotationsAvg.type).getValue()).toBeCloseTo(0.5, 10); expect(activity.getStat(data_jump_stats_1.DataJumpScoreMin.type).getValue()).toBe(50); expect(activity.getStat(data_jump_stats_1.DataJumpScoreMax.type).getValue()).toBe(70); expect(activity.getStat(data_jump_stats_1.DataJumpScoreAvg.type).getValue()).toBeCloseTo(60, 10); expect(activity.getStat(data_jump_stats_1.DataJumpHeightMin.type).getValue()).toBeCloseTo(0.8, 10); expect(activity.getStat(data_jump_stats_1.DataJumpHeightMax.type).getValue()).toBeCloseTo(1.2, 10); expect(activity.getStat(data_jump_stats_1.DataJumpHeightAvg.type).getValue()).toBeCloseTo(1, 10); }); it('emits jump averages with zero values when count is greater than zero', () => { const activity = new activity_1.Activity(new Date(0), new Date(10000), activity_types_1.ActivityTypes.MountainBiking, new creator_1.Creator('test')); activity.addEvent(createJumpEvent(1, { distance: 0, hang_time: 0, speed: 0, rotations: 0, score: 0, height: 0 })); activity_utilities_1.ActivityUtilities.generateMissingStreamsAndStatsForActivity(activity); expect(activity.getStat(data_jump_stats_1.DataJumpDistanceAvg.type).getValue()).toBe(0); expect(activity.getStat(data_jump_stats_1.DataJumpHangTimeAvg.type).getValue()).toBe(0); expect(activity.getStat(data_jump_stats_1.DataJumpSpeedAvg.type).getValue()).toBe(0); expect(activity.getStat(data_jump_stats_1.DataJumpRotationsAvg.type).getValue()).toBe(0); expect(activity.getStat(data_jump_stats_1.DataJumpScoreAvg.type).getValue()).toBe(0); expect(activity.getStat(data_jump_stats_1.DataJumpHeightAvg.type).getValue()).toBe(0); }); }); });