Skip to content

Commit

Permalink
added more stats and moved them to basics tab chart mode
Browse files Browse the repository at this point in the history
  • Loading branch information
WhyAsh5114 committed Sep 1, 2024
1 parent f84fa95 commit 59f7216
Show file tree
Hide file tree
Showing 5 changed files with 102 additions and 34 deletions.
39 changes: 33 additions & 6 deletions src/lib/utils/mesocycleUtils.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { arrayAverage, averagePercentageChange } from '$lib/utils';
import type { WorkoutExercise, WorkoutsOfMesocycle } from './types';
import { getExerciseVolume } from './workoutUtils';
import type { Mesocycle, Workout, WorkoutExercise, WorkoutsOfMesocycle } from './types';
import { getExerciseVolume, getWorkoutVolume } from './workoutUtils';

type GroupedExercisesByMuscleGroup = {
muscleGroup: string;
Expand All @@ -24,7 +24,7 @@ type GroupedExercisesByName = {
}[];

export function groupExercisesByName(exercises: WorkoutExercise[]): GroupedExercisesByName {
return Object.entries(Object.groupBy(exercises ?? [], ({ name }) => name)).map(([name, exercises]) => ({
return Object.entries(Object.groupBy(exercises, ({ name }) => name)).map(([name, exercises]) => ({
name,
performances: exercises ?? []
}));
Expand All @@ -41,7 +41,7 @@ export function getAveragePercentageChangeOfExercisePerformances(
return averagePercentageChange(exerciseVolumes);
}

export function generatePerformanceChanges(workoutsOfMesocycle: WorkoutsOfMesocycle) {
export function generatePerformanceChangesPerMuscleGroup(workoutsOfMesocycle: WorkoutsOfMesocycle) {
const allExercises = workoutsOfMesocycle.flatMap((wm) => wm.workout.workoutExercises);
const groupedExercisesByMuscleGroup = groupExercisesByMuscleGroup(allExercises);

Expand All @@ -52,13 +52,40 @@ export function generatePerformanceChanges(workoutsOfMesocycle: WorkoutsOfMesocy

const performanceChangesPerMuscleGroups = fullyGroupedExercises.map(({ exercises, muscleGroup }) => ({
muscleGroup,
averageIncrease: arrayAverage(
averagePercentageChange: arrayAverage(
exercises.map(({ performances }) =>
getAveragePercentageChangeOfExercisePerformances(performances, workoutsOfMesocycle)
)
)
}));

performanceChangesPerMuscleGroups.sort((a, b) => a.averageIncrease - b.averageIncrease);
performanceChangesPerMuscleGroups.sort((a, b) => a.averagePercentageChange - b.averagePercentageChange);
return performanceChangesPerMuscleGroups;
}

type GroupedWorkoutsBySplitDayName = {
splitDayName: string;
workouts: Workout[];
}[];

export function groupWorkoutsBySplitDayName(
workoutsOfMesocycle: WorkoutsOfMesocycle,
splitDays: Mesocycle['mesocycleExerciseSplitDays']
): GroupedWorkoutsBySplitDayName {
const groupedObject = Object.groupBy(workoutsOfMesocycle, ({ splitDayIndex }) => splitDayIndex);

return Object.entries(groupedObject).map(([splitDayIndex, workoutsOfMesocycle]) => ({
splitDayName: splitDays[Number(splitDayIndex)].name,
workouts: (workoutsOfMesocycle ?? []).map((wm) => wm.workout)
}));
}

export function generatePerformanceChangesPerSplitDay(mesocycle: Mesocycle) {
const workoutsOfMesocycle = mesocycle.workoutsOfMesocycle.filter((wm) => wm.workoutStatus === null);
const groupedWorkouts = groupWorkoutsBySplitDayName(workoutsOfMesocycle, mesocycle.mesocycleExerciseSplitDays);

return groupedWorkouts.map(({ splitDayName, workouts }) => ({
splitDayName,
averagePercentageChange: averagePercentageChange(workouts.map((workout) => getWorkoutVolume(workout)))
}));
}
6 changes: 4 additions & 2 deletions src/lib/utils/types.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
import type { RouterOutputs } from '$lib/trpc/router';

export type WorkoutsOfMesocycle = NonNullable<RouterOutputs['mesocycles']['findById']>['workoutsOfMesocycle'];
export type WorkoutExercise = WorkoutsOfMesocycle[number]['workout']['workoutExercises'][number];
export type Mesocycle = NonNullable<RouterOutputs['mesocycles']['findById']>;
export type WorkoutsOfMesocycle = Mesocycle['workoutsOfMesocycle'];
export type Workout = WorkoutsOfMesocycle[number]['workout'];
export type WorkoutExercise = Workout['workoutExercises'][number];
20 changes: 12 additions & 8 deletions src/lib/utils/workoutUtils.ts
Original file line number Diff line number Diff line change
@@ -1,17 +1,21 @@
import { arraySum } from '$lib/utils';
import type { WorkoutExercise } from './types';

export function getExerciseVolume(workoutExercise: WorkoutExercise, userBodyweight: number) {
return arraySum(
workoutExercise.sets.map((set) => getSetVolume(set, userBodyweight, workoutExercise.bodyweightFraction))
);
}
import type { Workout, WorkoutExercise } from './types';

export function getSetVolume(
set: WorkoutExercise['sets'][number],
userBodyweight: number,
bodyweightFraction: number | null
) {
// TODO: Mini sets?
// TODO: Mini sets?
return (set.reps + set.RIR) * set.load + (bodyweightFraction ?? 0) * userBodyweight;
}

export function getExerciseVolume(workoutExercise: WorkoutExercise, userBodyweight: number) {
return arraySum(
workoutExercise.sets.map((set) => getSetVolume(set, userBodyweight, workoutExercise.bodyweightFraction))
);
}

export function getWorkoutVolume(workout: Workout) {
return arraySum(workout.workoutExercises.map((exercise) => getExerciseVolume(exercise, workout.userBodyweight)));
}
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
<script lang="ts">
import * as Card from '$lib/components/ui/card';
import { arraySum, convertCamelCaseToNormal } from '$lib/utils';
import { generatePerformanceChanges } from '$lib/utils/mesocycleUtils';
import {
generatePerformanceChangesPerMuscleGroup,
generatePerformanceChangesPerSplitDay
} from '$lib/utils/mesocycleUtils';
import type { FullMesocycle } from '../+layout.server';
let { mesocycle }: { mesocycle: FullMesocycle } = $props();
Expand Down Expand Up @@ -33,7 +36,11 @@
return mesocycle.mesocycleExerciseSplitDays[parseInt(mostOccurring)].name;
});
const sortedByPerformanceChangeMuscleGroups = $derived(generatePerformanceChanges(mesocycle.workoutsOfMesocycle));
const performanceChangesPerMuscleGroups = $derived(
generatePerformanceChangesPerMuscleGroup(mesocycle.workoutsOfMesocycle)
);
const performanceChangesPerSplitDay = $derived(generatePerformanceChangesPerSplitDay(mesocycle));
</script>

{#if mesocycle.workoutsOfMesocycle.length}
Expand Down Expand Up @@ -74,13 +81,13 @@
<Card.Content class="p-4 pt-0">
<div class="text-2xl font-bold">
{convertCamelCaseToNormal(
sortedByPerformanceChangeMuscleGroups[sortedByPerformanceChangeMuscleGroups.length - 1].muscleGroup
performanceChangesPerMuscleGroups[performanceChangesPerMuscleGroups.length - 1].muscleGroup
)}
</div>
<p class="text-xs text-muted-foreground">
{sortedByPerformanceChangeMuscleGroups[
sortedByPerformanceChangeMuscleGroups.length - 1
].averageIncrease.toFixed(2)}% cyclic increase
{performanceChangesPerMuscleGroups[
performanceChangesPerMuscleGroups.length - 1
].averagePercentageChange.toFixed(2)}% cyclic increase
</p>
</Card.Content>
</Card.Root>
Expand All @@ -91,19 +98,43 @@
</Card.Header>
<Card.Content class="p-4 pt-0">
<div class="text-2xl font-bold">
{convertCamelCaseToNormal(sortedByPerformanceChangeMuscleGroups[0].muscleGroup)}
{convertCamelCaseToNormal(performanceChangesPerMuscleGroups[0].muscleGroup)}
</div>
<p class="text-xs text-muted-foreground">
{performanceChangesPerMuscleGroups[0].averagePercentageChange.toFixed(2)}% cyclic increase
</p>
</Card.Content>
</Card.Root>

<Card.Root>
<Card.Header class="flex flex-row items-center justify-between space-y-0 p-4 pb-1.5">
<Card.Title class="text-sm font-medium">Best day</Card.Title>
</Card.Header>
<Card.Content class="p-4 pt-0">
<div class="text-2xl font-bold">
{performanceChangesPerSplitDay[performanceChangesPerSplitDay.length - 1].splitDayName}
</div>
<p class="text-xs text-muted-foreground">
{sortedByPerformanceChangeMuscleGroups[0].averageIncrease.toFixed(2)}% cyclic increase
{performanceChangesPerSplitDay[performanceChangesPerSplitDay.length - 1].averagePercentageChange.toFixed(2)}%
cyclic increase
</p>
</Card.Content>
</Card.Root>

<Card.Root>
<Card.Header class="flex flex-row items-center justify-between space-y-0 p-4 pb-1.5">
<Card.Title class="text-sm font-medium">Worst day</Card.Title>
</Card.Header>
<Card.Content class="p-4 pt-0">
<div class="text-2xl font-bold">
{performanceChangesPerSplitDay[0].splitDayName}
</div>
<p class="text-xs text-muted-foreground">
{performanceChangesPerSplitDay[0].averagePercentageChange.toFixed(2)}% cyclic increase
</p>
</Card.Content>
</Card.Root>
</div>
{:else}
<div class="muted-text-box">No workouts created</div>
<div class="muted-text-box">No workouts for stat generation</div>
{/if}

<br />
TODO: Maybe put these stats in basics tab chart mode, makes more sense that way
<br /><br />
TODO: Workouts list
12 changes: 8 additions & 4 deletions src/routes/mesocycles/[mesocycleId]/+page.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,8 @@
import MesocycleExerciseSplitStats from './(components)/MesocycleExerciseSplitStats.svelte';
import MesocycleSkeleton from './(components)/MesocycleSkeleton.svelte';
import MesocycleSplitTab from './(components)/MesocycleSplitTab.svelte';
import MesocycleStats from './(components)/MesocycleStats.svelte';
import MesocycleVolumeTab from './(components)/MesocycleVolumeTab.svelte';
import MesocycleWorkoutsTab from './(components)/MesocycleWorkoutsTab.svelte';
import type { FullMesocycle } from './+layout.server';
let { data } = $props();
Expand All @@ -25,7 +25,7 @@
});
</script>

<H2 showChartIcon={selectedTabValue !== 'basics'} bind:chartMode>View mesocycle</H2>
<H2 showChartIcon bind:chartMode>View mesocycle</H2>

{#if mesocycle === 'loading'}
<MesocycleSkeleton />
Expand All @@ -38,7 +38,11 @@
<Tabs.Trigger value="workouts">Workouts</Tabs.Trigger>
</Tabs.List>
<Tabs.Content value="basics">
<MesocycleBasicsTab {mesocycle} />
{#if !chartMode}
<MesocycleBasicsTab {mesocycle} />
{:else}
<MesocycleStats {mesocycle} />
{/if}
</Tabs.Content>
<Tabs.Content value="split">
{#if !chartMode}
Expand All @@ -62,7 +66,7 @@
</Tabs.Content>
<Tabs.Content class="grow" value="workouts">
{#if !chartMode}
<MesocycleWorkoutsTab {mesocycle} />
<MesocycleStats {mesocycle} />
{:else}
TODO: charts that show progression
{/if}
Expand Down

0 comments on commit 59f7216

Please sign in to comment.