running-tools

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

commit b5ad7c6a08b2298e0e13beff4c48c0e7890ca366
parent c5af1776ef05a6ba2546f95ffac2c2628f7bb9f3
Author: Asher Morgan <59518073+ashermorgan@users.noreply.github.com>
Date:   Mon, 24 Mar 2025 13:38:13 -0700

Round durations in Batch Calculator

Diffstat:
Msrc/utils/calculators.js | 26++++++++++++++++----------
Msrc/views/BatchCalculator.vue | 7++++---
Msrc/views/PaceCalculator.vue | 2+-
Msrc/views/RaceCalculator.vue | 2+-
Msrc/views/WorkoutCalculator.vue | 2+-
Mtests/e2e/batch-calculator.spec.js | 36++++++++++++++++++------------------
Mtests/e2e/cross-calculator.spec.js | 12++++++------
Mtests/unit/views/BatchCalculator.spec.js | 2+-
8 files changed, 48 insertions(+), 41 deletions(-)

diff --git a/src/utils/calculators.js b/src/utils/calculators.js @@ -7,20 +7,21 @@ import { DISTANCE_UNITS, convertDistance, getDefaultDistanceUnit } from '@/utils * Format a distance/time result as a key/value result * @param {Object} result The distance/time result * @param {String} defaultUnitSystem The default unit system (imperial or metric) + * @param {Boolean} preciseDurations Whether to return precise, unrounded, durations * @returns {Object} The key/value result */ -export function formatDistTimeResult(result, defaultUnitSystem) { +export function formatDistTimeResult(result, defaultUnitSystem, preciseDurations = true) { // Calculate numerical pace const pace = result.time / convertDistance(result.distanceValue, result.distanceUnit, getDefaultDistanceUnit(defaultUnitSystem)); return { // Convert distance to key string - key: formatNumber(result.distanceValue, 0, 2, result.result === 'distance') + ' ' - + DISTANCE_UNITS[result.distanceUnit].symbol, + key: formatNumber(result.distanceValue, 0, 2, result.result === 'distance') + ' ' + + DISTANCE_UNITS[result.distanceUnit].symbol, // Convert time to time string - value: formatDuration(result.time, 3, 2, result.result === 'time'), + value: formatDuration(result.time, 3, preciseDurations ? 2 : 0, result.result === 'time'), // Convert pace to pace string pace: formatDuration(pace, 3, 0, true) + ' / ' @@ -39,9 +40,10 @@ export function formatDistTimeResult(result, defaultUnitSystem) { * @param {Object} input The input pace * @param {Object} target The pace target * @param {String} defaultUnitSystem The default unit system (imperial or metric) + * @param {Boolean} preciseDurations Whether to return precise, unrounded, durations * @returns {Object} The result */ -export function calculatePaceResults(input, target, defaultUnitSystem) { +export function calculatePaceResults(input, target, defaultUnitSystem, preciseDurations = true) { const result = { distanceValue: target.distanceValue, distanceUnit: target.distanceUnit, @@ -69,7 +71,7 @@ export function calculatePaceResults(input, target, defaultUnitSystem) { } // Return result - return formatDistTimeResult(result, defaultUnitSystem); + return formatDistTimeResult(result, defaultUnitSystem, preciseDurations); } /** @@ -78,9 +80,12 @@ export function calculatePaceResults(input, target, defaultUnitSystem) { * @param {Object} target The race target * @param {Object} options The race prediction options * @param {String} defaultUnitSystem The default unit system (imperial or metric) + * @param {Boolean} preciseDurations Whether to return precise, unrounded, durations * @returns {Object} The result */ -export function calculateRaceResults(input, target, options, defaultUnitSystem) { +export function calculateRaceResults(input, target, options, defaultUnitSystem, + preciseDurations = true) { + const result = { distanceValue: target.distanceValue, distanceUnit: target.distanceUnit, @@ -112,7 +117,7 @@ export function calculateRaceResults(input, target, options, defaultUnitSystem) } // Return result - return formatDistTimeResult(result, defaultUnitSystem); + return formatDistTimeResult(result, defaultUnitSystem, preciseDurations); } /** @@ -136,9 +141,10 @@ export function calculateRaceStats(input) { * @param {Object} input The input race * @param {Object} target The workout target * @param {Object} options The race prediction options + * @param {Boolean} preciseDurations Whether to return precise, unrounded, durations * @returns {Object} The result */ -export function calculateWorkoutResults(input, target, options) { +export function calculateWorkoutResults(input, target, options, preciseDurations = true) { const d1 = convertDistance(input.distanceValue, input.distanceUnit, 'meters'); const t1 = input.time; const d3 = convertDistance(target.splitValue, target.splitUnit, 'meters'); @@ -165,7 +171,7 @@ export function calculateWorkoutResults(input, target, options) { // Calculate time return { key: key, - value: formatDuration(t3, 3, 2, true), + value: formatDuration(t3, 3, preciseDurations ? 2 : 0, true), pace: '', // Pace not used in workout calculator result: 'value', sort: t3, diff --git a/src/views/BatchCalculator.vue b/src/views/BatchCalculator.vue @@ -195,11 +195,12 @@ const advancedOptions = computed(() => { */ const calculateResult = computed(() => { if (options.value.calculator === 'pace') { - return (x,y) => calcUtils.calculatePaceResults(x, y, defaultUnitSystem.value); + return (x,y) => calcUtils.calculatePaceResults(x, y, defaultUnitSystem.value, false); } else if (options.value.calculator === 'race') { - return (x,y) => calcUtils.calculateRaceResults(x, y, raceOptions.value, defaultUnitSystem.value); + return (x,y) => calcUtils.calculateRaceResults(x, y, raceOptions.value, defaultUnitSystem.value, + false); } else { - return (x,y) => calcUtils.calculateWorkoutResults(x, y, workoutOptions.value); + return (x,y) => calcUtils.calculateWorkoutResults(x, y, workoutOptions.value, false); } }); </script> diff --git a/src/views/PaceCalculator.vue b/src/views/PaceCalculator.vue @@ -25,7 +25,7 @@ <h2>Equivalent Paces</h2> <single-output-table class="output" :calculate-result="x => - calculatePaceResults(input, x, defaultUnitSystem)" + calculatePaceResults(input, x, defaultUnitSystem, true)" :targets="targetSets[selectedTargetSet] ? targetSets[selectedTargetSet].targets : []"/> </div> </template> diff --git a/src/views/RaceCalculator.vue b/src/views/RaceCalculator.vue @@ -43,7 +43,7 @@ <h2>Equivalent Race Results</h2> <single-output-table class="output" show-pace - :calculate-result="x => calculateRaceResults(input, x, options, defaultUnitSystem)" + :calculate-result="x => calculateRaceResults(input, x, options, defaultUnitSystem, true)" :targets="targetSets[selectedTargetSet] ? targetSets[selectedTargetSet].targets : []"/> </div> </template> diff --git a/src/views/WorkoutCalculator.vue b/src/views/WorkoutCalculator.vue @@ -26,7 +26,7 @@ <h2>Workout Splits</h2> <single-output-table class="output" - :calculate-result="x => calculateWorkoutResults(input, x, options)" + :calculate-result="x => calculateWorkoutResults(input, x, options, true)" :targets="targetSets[selectedTargetSet] ? targetSets[selectedTargetSet].targets : []"/> </div> </template> diff --git a/tests/e2e/batch-calculator.spec.js b/tests/e2e/batch-calculator.spec.js @@ -33,10 +33,10 @@ test('Batch calculator', async ({ page }) => { await expect(page.getByRole('row').nth(0).getByRole('cell').nth(2)).toHaveText('800 m @ 5 km'); await expect(page.getByRole('row').nth(0).getByRole('cell')).toHaveCount(5); await expect(page.getByRole('row').nth(1).getByRole('cell').nth(0)).toHaveText('10:30'); - await expect(page.getByRole('row').nth(1).getByRole('cell').nth(2)).toHaveText('2:41.21'); + await expect(page.getByRole('row').nth(1).getByRole('cell').nth(2)).toHaveText('2:41'); await expect(page.getByRole('row').nth(1).getByRole('cell')).toHaveCount(5); await expect(page.getByRole('row').nth(15).getByRole('cell').nth(0)).toHaveText('12:50'); - await expect(page.getByRole('row').nth(15).getByRole('cell').nth(2)).toHaveText('3:16.90'); + await expect(page.getByRole('row').nth(15).getByRole('cell').nth(2)).toHaveText('3:17'); await expect(page.getByRole('row').nth(15).getByRole('cell')).toHaveCount(5); await expect(page.getByRole('row')).toHaveCount(16); @@ -49,10 +49,10 @@ test('Batch calculator', async ({ page }) => { await expect(page.getByRole('row').nth(0).getByRole('cell').nth(2)).toHaveText('800 m @ 5 km'); await expect(page.getByRole('row').nth(0).getByRole('cell')).toHaveCount(5); await expect(page.getByRole('row').nth(1).getByRole('cell').nth(0)).toHaveText('10:30'); - await expect(page.getByRole('row').nth(1).getByRole('cell').nth(2)).toHaveText('2:40.78'); + await expect(page.getByRole('row').nth(1).getByRole('cell').nth(2)).toHaveText('2:41'); await expect(page.getByRole('row').nth(1).getByRole('cell')).toHaveCount(5); await expect(page.getByRole('row').nth(15).getByRole('cell').nth(0)).toHaveText('12:50'); - await expect(page.getByRole('row').nth(15).getByRole('cell').nth(2)).toHaveText('3:16.51'); + await expect(page.getByRole('row').nth(15).getByRole('cell').nth(2)).toHaveText('3:17'); await expect(page.getByRole('row').nth(15).getByRole('cell')).toHaveCount(5); await expect(page.getByRole('row')).toHaveCount(16); @@ -66,11 +66,11 @@ test('Batch calculator', async ({ page }) => { await expect(page.getByRole('row').nth(0).getByRole('cell').nth(28)).toHaveText('10:00'); await expect(page.getByRole('row').nth(0).getByRole('cell')).toHaveCount(31); await expect(page.getByRole('row').nth(1).getByRole('cell').nth(0)).toHaveText('10:30'); - await expect(page.getByRole('row').nth(1).getByRole('cell').nth(6)).toHaveText('2:36.58'); + await expect(page.getByRole('row').nth(1).getByRole('cell').nth(6)).toHaveText('2:37'); await expect(page.getByRole('row').nth(1).getByRole('cell').nth(28)).toHaveText('1.90 mi'); await expect(page.getByRole('row').nth(1).getByRole('cell')).toHaveCount(31); await expect(page.getByRole('row').nth(15).getByRole('cell').nth(0)).toHaveText('12:50'); - await expect(page.getByRole('row').nth(15).getByRole('cell').nth(6)).toHaveText('3:11.38'); + await expect(page.getByRole('row').nth(15).getByRole('cell').nth(6)).toHaveText('3:11'); await expect(page.getByRole('row').nth(15).getByRole('cell').nth(28)).toHaveText('1.56 mi'); await expect(page.getByRole('row').nth(15).getByRole('cell')).toHaveCount(31); await expect(page.getByRole('row')).toHaveCount(16); @@ -87,11 +87,11 @@ test('Batch calculator', async ({ page }) => { await expect(page.getByRole('row').nth(0).getByRole('cell').nth(28)).toHaveText('10:00'); await expect(page.getByRole('row').nth(0).getByRole('cell')).toHaveCount(31); await expect(page.getByRole('row').nth(1).getByRole('cell').nth(0)).toHaveText('10:30'); - await expect(page.getByRole('row').nth(1).getByRole('cell').nth(6)).toHaveText('2:36.58'); + await expect(page.getByRole('row').nth(1).getByRole('cell').nth(6)).toHaveText('2:37'); await expect(page.getByRole('row').nth(1).getByRole('cell').nth(28)).toHaveText('3.07 km'); await expect(page.getByRole('row').nth(1).getByRole('cell')).toHaveCount(31); await expect(page.getByRole('row').nth(15).getByRole('cell').nth(0)).toHaveText('12:50'); - await expect(page.getByRole('row').nth(15).getByRole('cell').nth(6)).toHaveText('3:11.38'); + await expect(page.getByRole('row').nth(15).getByRole('cell').nth(6)).toHaveText('3:11'); await expect(page.getByRole('row').nth(15).getByRole('cell').nth(28)).toHaveText('2.51 km'); await expect(page.getByRole('row').nth(15).getByRole('cell')).toHaveCount(31); await expect(page.getByRole('row')).toHaveCount(16); @@ -104,10 +104,10 @@ test('Batch calculator', async ({ page }) => { await expect(page.getByRole('row').nth(0).getByRole('cell').nth(2)).toHaveText('800 m'); await expect(page.getByRole('row').nth(0).getByRole('cell')).toHaveCount(17); await expect(page.getByRole('row').nth(1).getByRole('cell').nth(0)).toHaveText('10:30'); - await expect(page.getByRole('row').nth(1).getByRole('cell').nth(2)).toHaveText('2:14.60'); + await expect(page.getByRole('row').nth(1).getByRole('cell').nth(2)).toHaveText('2:15'); await expect(page.getByRole('row').nth(1).getByRole('cell')).toHaveCount(17); await expect(page.getByRole('row').nth(15).getByRole('cell').nth(0)).toHaveText('12:50'); - await expect(page.getByRole('row').nth(15).getByRole('cell').nth(2)).toHaveText('2:43.62'); + await expect(page.getByRole('row').nth(15).getByRole('cell').nth(2)).toHaveText('2:44'); await expect(page.getByRole('row').nth(15).getByRole('cell')).toHaveCount(17); await expect(page.getByRole('row')).toHaveCount(16); @@ -120,10 +120,10 @@ test('Batch calculator', async ({ page }) => { await expect(page.getByRole('row').nth(0).getByRole('cell').nth(2)).toHaveText('800 m'); await expect(page.getByRole('row').nth(0).getByRole('cell')).toHaveCount(17); await expect(page.getByRole('row').nth(1).getByRole('cell').nth(0)).toHaveText('10:30'); - await expect(page.getByRole('row').nth(1).getByRole('cell').nth(2)).toHaveText('2:11.72'); + await expect(page.getByRole('row').nth(1).getByRole('cell').nth(2)).toHaveText('2:12'); await expect(page.getByRole('row').nth(1).getByRole('cell')).toHaveCount(17); await expect(page.getByRole('row').nth(15).getByRole('cell').nth(0)).toHaveText('12:50'); - await expect(page.getByRole('row').nth(15).getByRole('cell').nth(2)).toHaveText('2:40.09'); + await expect(page.getByRole('row').nth(15).getByRole('cell').nth(2)).toHaveText('2:40'); await expect(page.getByRole('row').nth(15).getByRole('cell')).toHaveCount(17); await expect(page.getByRole('row')).toHaveCount(16); @@ -135,10 +135,10 @@ test('Batch calculator', async ({ page }) => { await expect(page.getByRole('row').nth(0).getByRole('cell').nth(2)).toHaveText('800 m'); await expect(page.getByRole('row').nth(0).getByRole('cell')).toHaveCount(17); await expect(page.getByRole('row').nth(1).getByRole('cell').nth(0)).toHaveText('10:30'); - await expect(page.getByRole('row').nth(1).getByRole('cell').nth(2)).toHaveText('2:11.72'); + await expect(page.getByRole('row').nth(1).getByRole('cell').nth(2)).toHaveText('2:12'); await expect(page.getByRole('row').nth(1).getByRole('cell')).toHaveCount(17); await expect(page.getByRole('row').nth(15).getByRole('cell').nth(0)).toHaveText('12:50'); - await expect(page.getByRole('row').nth(15).getByRole('cell').nth(2)).toHaveText('2:40.09'); + await expect(page.getByRole('row').nth(15).getByRole('cell').nth(2)).toHaveText('2:40'); await expect(page.getByRole('row').nth(15).getByRole('cell')).toHaveCount(17); await expect(page.getByRole('row')).toHaveCount(16); @@ -149,11 +149,11 @@ test('Batch calculator', async ({ page }) => { await expect(page.getByRole('row').nth(0).getByRole('cell').nth(28)).toHaveText('10:00'); await expect(page.getByRole('row').nth(0).getByRole('cell')).toHaveCount(31); await expect(page.getByRole('row').nth(1).getByRole('cell').nth(0)).toHaveText('10:30'); - await expect(page.getByRole('row').nth(1).getByRole('cell').nth(6)).toHaveText('2:36.58'); + await expect(page.getByRole('row').nth(1).getByRole('cell').nth(6)).toHaveText('2:37'); await expect(page.getByRole('row').nth(1).getByRole('cell').nth(28)).toHaveText('3.07 km'); await expect(page.getByRole('row').nth(1).getByRole('cell')).toHaveCount(31); await expect(page.getByRole('row').nth(15).getByRole('cell').nth(0)).toHaveText('12:50'); - await expect(page.getByRole('row').nth(15).getByRole('cell').nth(6)).toHaveText('3:11.38'); + await expect(page.getByRole('row').nth(15).getByRole('cell').nth(6)).toHaveText('3:11'); await expect(page.getByRole('row').nth(15).getByRole('cell').nth(28)).toHaveText('2.51 km'); await expect(page.getByRole('row').nth(15).getByRole('cell')).toHaveCount(31); await expect(page.getByRole('row')).toHaveCount(16); @@ -164,10 +164,10 @@ test('Batch calculator', async ({ page }) => { await expect(page.getByRole('row').nth(0).getByRole('cell').nth(2)).toHaveText('800 m @ 5 km'); await expect(page.getByRole('row').nth(0).getByRole('cell')).toHaveCount(5); await expect(page.getByRole('row').nth(1).getByRole('cell').nth(0)).toHaveText('10:30'); - await expect(page.getByRole('row').nth(1).getByRole('cell').nth(2)).toHaveText('2:40.78'); + await expect(page.getByRole('row').nth(1).getByRole('cell').nth(2)).toHaveText('2:41'); await expect(page.getByRole('row').nth(1).getByRole('cell')).toHaveCount(5); await expect(page.getByRole('row').nth(15).getByRole('cell').nth(0)).toHaveText('12:50'); - await expect(page.getByRole('row').nth(15).getByRole('cell').nth(2)).toHaveText('3:16.51'); + await expect(page.getByRole('row').nth(15).getByRole('cell').nth(2)).toHaveText('3:17'); await expect(page.getByRole('row').nth(15).getByRole('cell')).toHaveCount(5); await expect(page.getByRole('row')).toHaveCount(16); }); diff --git a/tests/e2e/cross-calculator.spec.js b/tests/e2e/cross-calculator.spec.js @@ -123,10 +123,10 @@ test('Cross-calculator', async ({ page }) => { await expect(page.getByRole('row').nth(0).getByRole('cell').nth(2)).toHaveText('800 m'); await expect(page.getByRole('row').nth(0).getByRole('cell')).toHaveCount(17); await expect(page.getByRole('row').nth(1).getByRole('cell').nth(0)).toHaveText('10:30'); - await expect(page.getByRole('row').nth(1).getByRole('cell').nth(2)).toHaveText('2:24.04'); + await expect(page.getByRole('row').nth(1).getByRole('cell').nth(2)).toHaveText('2:24'); await expect(page.getByRole('row').nth(1).getByRole('cell')).toHaveCount(17); await expect(page.getByRole('row').nth(15).getByRole('cell').nth(0)).toHaveText('12:50'); - await expect(page.getByRole('row').nth(15).getByRole('cell').nth(2)).toHaveText('2:56.05'); + await expect(page.getByRole('row').nth(15).getByRole('cell').nth(2)).toHaveText('2:56'); await expect(page.getByRole('row').nth(15).getByRole('cell')).toHaveCount(17); await expect(page.getByRole('row')).toHaveCount(16); @@ -136,10 +136,10 @@ test('Cross-calculator', async ({ page }) => { await expect(page.getByRole('row').nth(0).getByRole('cell').nth(2)).toHaveText('800 m'); await expect(page.getByRole('row').nth(0).getByRole('cell')).toHaveCount(4); await expect(page.getByRole('row').nth(1).getByRole('cell').nth(0)).toHaveText('10:30'); - await expect(page.getByRole('row').nth(1).getByRole('cell').nth(2)).toHaveText('2:36.58'); + await expect(page.getByRole('row').nth(1).getByRole('cell').nth(2)).toHaveText('2:37'); await expect(page.getByRole('row').nth(1).getByRole('cell')).toHaveCount(4); await expect(page.getByRole('row').nth(15).getByRole('cell').nth(0)).toHaveText('12:50'); - await expect(page.getByRole('row').nth(15).getByRole('cell').nth(2)).toHaveText('3:11.38'); + await expect(page.getByRole('row').nth(15).getByRole('cell').nth(2)).toHaveText('3:11'); await expect(page.getByRole('row').nth(15).getByRole('cell')).toHaveCount(4); await expect(page.getByRole('row')).toHaveCount(16); @@ -149,10 +149,10 @@ test('Cross-calculator', async ({ page }) => { await expect(page.getByRole('row').nth(0).getByRole('cell').nth(2)).toHaveText('800 m @ 5 km'); await expect(page.getByRole('row').nth(0).getByRole('cell')).toHaveCount(5); await expect(page.getByRole('row').nth(1).getByRole('cell').nth(0)).toHaveText('10:30'); - await expect(page.getByRole('row').nth(1).getByRole('cell').nth(2)).toHaveText('2:41.92'); + await expect(page.getByRole('row').nth(1).getByRole('cell').nth(2)).toHaveText('2:42'); await expect(page.getByRole('row').nth(1).getByRole('cell')).toHaveCount(5); await expect(page.getByRole('row').nth(15).getByRole('cell').nth(0)).toHaveText('12:50'); - await expect(page.getByRole('row').nth(15).getByRole('cell').nth(2)).toHaveText('3:16.97'); + await expect(page.getByRole('row').nth(15).getByRole('cell').nth(2)).toHaveText('3:17'); await expect(page.getByRole('row').nth(15).getByRole('cell')).toHaveCount(5); await expect(page.getByRole('row')).toHaveCount(16); diff --git a/tests/unit/views/BatchCalculator.spec.js b/tests/unit/views/BatchCalculator.spec.js @@ -412,7 +412,7 @@ test('should correctly calculate outputs', async () => { const workoutTarget = { type: 'time', time: 3600, splitValue: 1, splitUnit: 'miles' }; const result = calculate(input, workoutTarget); expect(result.key).to.equal('1 mi @ 1:00:00'); - expect(result.value).to.equal('5:53.07'); + expect(result.value).to.equal('5:53'); expect(result.pace).to.equal(''); expect(result.sort).to.be.closeTo(353.07, 0.01); expect(result.result).to.equal('value');