running-tools

A collection of tools for runners and their coaches
git clone https://git.ashermorgan.net/running-tools/
Log | Files | Refs | README

commit 783bec010a56da3ca441605b034b29225180849a
parent 281a018f690d1e8415c766c65ae3ffd1f933e439
Author: Asher Morgan <59518073+ashermorgan@users.noreply.github.com>
Date:   Thu, 29 May 2025 19:33:16 -0700

Implement workoutTargetToString util function

Diffstat:
Msrc/utils/calculators.js | 23++++++++++-------------
Msrc/utils/targets.js | 20+++++++++++++++++++-
Mtests/unit/utils/targets.spec.js | 38++++++++++++++++++++++++++++++++++++++
3 files changed, 67 insertions(+), 14 deletions(-)

diff --git a/src/utils/calculators.js b/src/utils/calculators.js @@ -1,6 +1,7 @@ import { formatDuration, formatNumber } from '@/utils/format'; import * as paceUtils from '@/utils/paces'; import * as raceUtils from '@/utils/races'; +import { workoutTargetToString } from '@/utils/targets'; import { DISTANCE_UNITS, convertDistance, getDefaultDistanceUnit } from '@/utils/units'; /** @@ -145,34 +146,30 @@ export function calculateRaceStats(input) { * @returns {Object} The result */ export function calculateWorkoutResults(input, target, options, preciseDurations = true) { + // Initialize distance and time variables const d1 = convertDistance(input.distanceValue, input.distanceUnit, 'meters'); const t1 = input.time; const d3 = convertDistance(target.splitValue, target.splitUnit, 'meters'); let d2, t2, t3; - // Calculate pace - let key = formatNumber(target.splitValue, 0, 2, false) + ' ' + - DISTANCE_UNITS[target.splitUnit].symbol; + // Calculate result if (target.type === 'distance') { // Convert target distance into meters d2 = convertDistance(target.distanceValue, target.distanceUnit, 'meters'); + + // Get workout split prediction t2 = raceUtils.predictTime(d1, input.time, d2, options.model, options.riegelExponent); - if (target.distanceValue != target.splitValue || target.distanceUnit != target.splitUnit) { - key += ' @ ' + formatNumber(target.distanceValue, 0, 2, false) + ' ' + - DISTANCE_UNITS[target.distanceUnit].symbol; - } } else { t2 = target.time; - d2 = raceUtils.predictDistance(t1, d1, t2, options.model, - options.riegelExponent); - key += ' @ ' + formatDuration(target.time, 3, 2, false); - } + // Get workout split prediction + d2 = raceUtils.predictDistance(t1, d1, t2, options.model, options.riegelExponent); + } t3 = paceUtils.calculateTime(d2, t2, d3); - // Calculate time + // Return result return { - key: key, + key: workoutTargetToString(target), value: formatDuration(t3, 3, preciseDurations ? 2 : 0, true), pace: '', // Pace not used in workout calculator result: 'value', diff --git a/src/utils/targets.js b/src/utils/targets.js @@ -1,4 +1,5 @@ -import { convertDistance } from '@/utils/units'; +import { formatDuration, formatNumber } from '@/utils/format'; +import { DISTANCE_UNITS, convertDistance } from '@/utils/units'; /** * Sort an array of targets @@ -16,6 +17,23 @@ export function sort(targets) { ]; } +/** + * Generate a string description of a workout target + * @param {Object} target The workout target + * @return {String} The string description + */ +export function workoutTargetToString(target) { + let result = formatNumber(target.splitValue, 0, 2, false) + ' ' + + DISTANCE_UNITS[target.splitUnit].symbol; + if (target.type === 'time') { + result += ' @ ' + formatDuration(target.time, 3, 2, false); + } else if (target.distanceValue != target.splitValue || target.distanceUnit != target.splitUnit) { + result += ' @ ' + formatNumber(target.distanceValue, 0, 2, false) + ' ' + + DISTANCE_UNITS[target.distanceUnit].symbol; + } + return result; +} + export const defaultTargetSets = { '_pace_targets': { name: 'Common Pace Targets', diff --git a/tests/unit/utils/targets.spec.js b/tests/unit/utils/targets.spec.js @@ -19,3 +19,41 @@ describe('sort method', () => { expect(targets.sort(input)).to.deep.equal(expected); }); }); + +describe('workoutTargetToString method', () => { + test('should correctly stringify time target', () => { + // Initialize original and stringified target + const input = { + splitValue: 1600, splitUnit: 'meters', + type: 'time', time: 3600, + }; + const expected = '1600 m @ 1:00:00'; + + // Assert sort method sorts targets correctly + expect(targets.workoutTargetToString(input)).to.deep.equal(expected); + }); + + test('should correctly stringify distance target', () => { + // Initialize original and stringified target + const input = { + splitValue: 800, splitUnit: 'meters', + type: 'distance', distanceValue: 5, distanceUnit: 'kilometers', + }; + const expected = '800 m @ 5 km'; + + // Assert sort method sorts targets correctly + expect(targets.workoutTargetToString(input)).to.deep.equal(expected); + }); + + test('should correctly stringify race target', () => { + // Initialize original and stringified target + const input = { + splitValue: 5, splitUnit: 'kilometers', + type: 'distance', distanceValue: 5, distanceUnit: 'kilometers', + }; + const expected = '5 km'; + + // Assert sort method sorts targets correctly + expect(targets.workoutTargetToString(input)).to.deep.equal(expected); + }); +});