@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
JavaScript
"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);
});
});
});