hevy-shared 1.0.1048 → 1.0.1050
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/built/hevyTrainer.d.ts +34 -43
- package/built/hevyTrainer.js +64 -65
- package/built/index.d.ts +113 -8
- package/built/index.js +1 -1
- package/built/tests/hevyTrainer.test.js +62 -75
- package/built/tests/testUtils.js +1 -0
- package/built/workout/index.d.ts +3 -3
- package/built/workout/index.js +3 -2
- package/built/workout/normalizedWorkout.d.ts +1 -0
- package/built/workout/postWorkoutRequest.d.ts +1 -0
- package/built/workout/publicWorkout.d.ts +3 -1
- package/built/workout/userWorkout.d.ts +29 -1
- package/built/workout/workout.d.ts +37 -1
- package/package.json +1 -1
- package/built/geography.d.ts +0 -1
- package/built/geography.js +0 -15
- package/built/tests/geography.test.d.ts +0 -1
- package/built/tests/geography.test.js +0 -25
package/built/hevyTrainer.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { WeeklyTrainingFrequency, TrainingGoal, TrainingLevel, SimplifiedMuscleGroup, MuscleGroup, LibraryExercise, ExerciseCategory, GranularEquipment, HevyTrainerProgramEquipment, RestTimerLength, CardioPreference } from '.';
|
|
1
|
+
import { WeeklyTrainingFrequency, TrainingGoal, TrainingLevel, SimplifiedMuscleGroup, MuscleGroup, LibraryExercise, ExerciseCategory, GranularEquipment, HevyTrainerProgramEquipment, RestTimerLength, CardioPreference, StrictRepRange } from '.';
|
|
2
2
|
export type HevyTrainerExerciseCategory = (typeof hevyTrainerExerciseCategories)[number];
|
|
3
|
-
export type
|
|
3
|
+
export type TrainerWorkoutTemplateName = (typeof workoutTemplateNames)[number];
|
|
4
4
|
export declare const workoutDurationOptions: readonly [40, 60, 80];
|
|
5
5
|
export type WorkoutDurationMinutes = (typeof workoutDurationOptions)[number];
|
|
6
6
|
export declare const trainerGymTypes: readonly ["home_gym", "garage_gym", "commercial_gym", "full_gym"];
|
|
@@ -16,12 +16,12 @@ export declare const trainerEquipmentToGranularEquipments: (equipments: HevyTrai
|
|
|
16
16
|
export declare const granularEquipmentsToTrainerEquipments: (granularEquipments: GranularEquipment[]) => HevyTrainerProgramEquipment[];
|
|
17
17
|
export declare const hevyTrainerExerciseCategories: readonly ["compound", "isolation"];
|
|
18
18
|
export declare const defaultDurationPerFrequency: Record<WeeklyTrainingFrequency, WorkoutDurationMinutes>;
|
|
19
|
-
export declare const
|
|
19
|
+
export declare const workoutTemplateNames: readonly ["full_body_1", "full_body_2_a", "full_body_2_b", "full_body_3_a", "full_body_3_b", "full_body_3_c", "upper_1_a", "lower_1_a", "upper_1_b", "lower_1_b", "push_1", "pull_1", "legs_1", "upper_2", "lower_2", "push_2_a", "pull_2_a", "legs_2_a", "push_2_b", "pull_2_b", "legs_2_b"];
|
|
20
20
|
export type exerciseId = string;
|
|
21
21
|
export interface ExerciseSelectionCriteria {
|
|
22
22
|
exerciseCategory: HevyTrainerExerciseCategory | 'all';
|
|
23
23
|
equipments: GranularEquipment[];
|
|
24
|
-
|
|
24
|
+
workoutBarbellExerciseCount: number;
|
|
25
25
|
level: TrainingLevel;
|
|
26
26
|
goal: TrainingGoal;
|
|
27
27
|
muscleGroup: MuscleGroup;
|
|
@@ -29,7 +29,7 @@ export interface ExerciseSelectionCriteria {
|
|
|
29
29
|
}
|
|
30
30
|
export interface ExerciseSelectionContext {
|
|
31
31
|
programUsedExerciseIds?: Set<string>;
|
|
32
|
-
|
|
32
|
+
workoutUsedExerciseIds?: Set<string>;
|
|
33
33
|
excludedExerciseIds?: Set<string>;
|
|
34
34
|
}
|
|
35
35
|
export interface ExerciseSelectionParams<T extends HevyTrainerLibraryExercise> {
|
|
@@ -69,7 +69,7 @@ export interface ProgramGenerationParams<T extends HevyTrainerLibraryExercise> {
|
|
|
69
69
|
}
|
|
70
70
|
export interface TemplateExerciseSelectionTraceRecord {
|
|
71
71
|
traceKind: 'template';
|
|
72
|
-
|
|
72
|
+
workoutTemplate: TrainerWorkoutTemplateName;
|
|
73
73
|
prescriptionIndex: number;
|
|
74
74
|
prescription: ExercisePrescription;
|
|
75
75
|
resolvedMuscleGroup: MuscleGroup;
|
|
@@ -79,7 +79,7 @@ export interface TemplateExerciseSelectionTraceRecord {
|
|
|
79
79
|
}
|
|
80
80
|
export interface CardioExerciseSelectionTraceRecord {
|
|
81
81
|
traceKind: 'cardio';
|
|
82
|
-
|
|
82
|
+
workoutTemplate: TrainerWorkoutTemplateName;
|
|
83
83
|
prescriptionIndex: number;
|
|
84
84
|
selectedExerciseId?: string;
|
|
85
85
|
equipments: GranularEquipment[];
|
|
@@ -92,19 +92,19 @@ export type TrainerProgramAttemptWithTraces = TrainerProgramAttempt & {
|
|
|
92
92
|
};
|
|
93
93
|
export type FrequencyString = 'one_day' | 'two_days' | 'three_days' | 'four_days' | 'five_days' | 'six_days';
|
|
94
94
|
export declare const frequencyMap: Record<WeeklyTrainingFrequency, FrequencyString>;
|
|
95
|
-
export declare const programSplits: Record<WeeklyTrainingFrequency,
|
|
95
|
+
export declare const programSplits: Record<WeeklyTrainingFrequency, TrainerWorkoutTemplateName[]>;
|
|
96
96
|
export type SetsPerGoal = {
|
|
97
97
|
[key in TrainingGoal]: number;
|
|
98
98
|
};
|
|
99
99
|
export type SetsPerFrequency = {
|
|
100
100
|
[key in FrequencyString]: SetsPerGoal;
|
|
101
101
|
};
|
|
102
|
-
export interface
|
|
102
|
+
export interface AlgorithmRepRange {
|
|
103
103
|
rep_range_start: number;
|
|
104
104
|
rep_range_end: number;
|
|
105
105
|
}
|
|
106
106
|
export type RepRangesPerCategory = {
|
|
107
|
-
[key in HevyTrainerExerciseCategory]:
|
|
107
|
+
[key in HevyTrainerExerciseCategory]: AlgorithmRepRange;
|
|
108
108
|
};
|
|
109
109
|
export type RepRanges = {
|
|
110
110
|
[key in TrainingGoal]: RepRangesPerCategory;
|
|
@@ -135,10 +135,11 @@ export type ExerciseReplacements = {
|
|
|
135
135
|
};
|
|
136
136
|
export interface WorkoutTemplate {
|
|
137
137
|
exercises: ExercisePrescription[];
|
|
138
|
+
/** @deprecated Because this is english-only and not translatable so we are not using it currently */
|
|
138
139
|
notes?: string;
|
|
139
140
|
}
|
|
140
141
|
export type Templates = {
|
|
141
|
-
[key in
|
|
142
|
+
[key in TrainerWorkoutTemplateName]: WorkoutTemplate;
|
|
142
143
|
};
|
|
143
144
|
export interface BackofficeTrainerPreset {
|
|
144
145
|
id?: number;
|
|
@@ -157,39 +158,38 @@ export interface TrainerAlgorithmSettings {
|
|
|
157
158
|
exercise_notes: ExerciseNotes;
|
|
158
159
|
exercise_replacements: ExerciseReplacements;
|
|
159
160
|
}
|
|
160
|
-
/** Resistance prescriptions from templates (sets, reps, rest, notes). */
|
|
161
161
|
export interface TrainerProgramResistanceExercise {
|
|
162
162
|
kind: 'resistance';
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
repRangeStart: number;
|
|
169
|
-
repRangeEnd: number;
|
|
170
|
-
restTimerSeconds: number;
|
|
171
|
-
notes?: string;
|
|
163
|
+
exercise_template: HevyTrainerLibraryExercise;
|
|
164
|
+
set_count: number;
|
|
165
|
+
warmup_set_count: number;
|
|
166
|
+
rep_range: StrictRepRange;
|
|
167
|
+
rest_seconds: number;
|
|
172
168
|
}
|
|
173
|
-
/** Optional cardio attachment — no prescription semantics beyond exercise choice. */
|
|
174
169
|
export interface TrainerProgramCardioExercise {
|
|
175
170
|
kind: 'cardio';
|
|
176
|
-
|
|
177
|
-
|
|
171
|
+
exercise_template: HevyTrainerLibraryExercise;
|
|
172
|
+
set_count: 1;
|
|
173
|
+
duration_seconds: number;
|
|
178
174
|
}
|
|
179
|
-
export
|
|
175
|
+
export interface TrainerProgramOtherExercise {
|
|
176
|
+
kind: 'other';
|
|
177
|
+
exercise_template: HevyTrainerLibraryExercise;
|
|
178
|
+
set_count: number;
|
|
179
|
+
}
|
|
180
|
+
export type TrainerProgramExercise = TrainerProgramResistanceExercise | TrainerProgramCardioExercise | TrainerProgramOtherExercise;
|
|
180
181
|
export declare const isTrainerProgramResistanceExercise: (exercise: TrainerProgramExercise) => exercise is TrainerProgramResistanceExercise;
|
|
181
182
|
export declare const isTrainerProgramCardioExercise: (exercise: TrainerProgramExercise) => exercise is TrainerProgramCardioExercise;
|
|
182
|
-
export
|
|
183
|
-
|
|
183
|
+
export declare const isTrainerProgramOtherExercise: (exercise: TrainerProgramExercise) => exercise is TrainerProgramOtherExercise;
|
|
184
|
+
export interface TrainerProgramWorkoutTemplate {
|
|
185
|
+
name: TrainerWorkoutTemplateName;
|
|
184
186
|
exercises: TrainerProgramExercise[];
|
|
185
|
-
notes?: string;
|
|
186
187
|
}
|
|
187
|
-
export interface
|
|
188
|
-
|
|
189
|
-
routines: TrainerProgramRoutine[];
|
|
188
|
+
export interface GeneratedTrainerProgram {
|
|
189
|
+
workoutTemplates: TrainerProgramWorkoutTemplate[];
|
|
190
190
|
}
|
|
191
191
|
export declare const getTrainerSetCount: (trainerAlgorithmSettings: TrainerAlgorithmSettings, goal: TrainingGoal, frequency: WeeklyTrainingFrequency) => number;
|
|
192
|
-
export declare const getTrainerRepRange: (trainerAlgorithmSettings: TrainerAlgorithmSettings, goal: TrainingGoal, exerciseCategory: HevyTrainerExerciseCategory) =>
|
|
192
|
+
export declare const getTrainerRepRange: (trainerAlgorithmSettings: TrainerAlgorithmSettings, goal: TrainingGoal, exerciseCategory: HevyTrainerExerciseCategory) => AlgorithmRepRange;
|
|
193
193
|
export declare const getTrainerRestTimerSeconds: (trainerAlgorithmSettings: TrainerAlgorithmSettings, goal: TrainingGoal, length: RestTimerLength, exerciseCategory: HevyTrainerExerciseCategory) => number;
|
|
194
194
|
/**
|
|
195
195
|
* Normalizes the exercise category to a HevyTrainerExerciseCategory
|
|
@@ -243,23 +243,14 @@ export interface ExercisePrescriptionError {
|
|
|
243
243
|
focusMuscle?: SimplifiedMuscleGroup;
|
|
244
244
|
};
|
|
245
245
|
}
|
|
246
|
-
export interface TrainerProgramExerciseResult {
|
|
247
|
-
success: true;
|
|
248
|
-
exercise: TrainerProgramExercise;
|
|
249
|
-
}
|
|
250
|
-
export interface TrainerProgramExerciseError {
|
|
251
|
-
success: false;
|
|
252
|
-
error: ExercisePrescriptionError;
|
|
253
|
-
}
|
|
254
|
-
export type TrainerProgramExerciseAttempt = TrainerProgramExerciseResult | TrainerProgramExerciseError;
|
|
255
246
|
export interface TrainerProgramResult {
|
|
256
247
|
success: true;
|
|
257
|
-
program:
|
|
248
|
+
program: GeneratedTrainerProgram;
|
|
258
249
|
}
|
|
259
250
|
export interface TrainerProgramError {
|
|
260
251
|
success: false;
|
|
261
252
|
errors: ExercisePrescriptionError[];
|
|
262
|
-
partialProgram:
|
|
253
|
+
partialProgram: GeneratedTrainerProgram;
|
|
263
254
|
}
|
|
264
255
|
export type TrainerProgramAttempt = TrainerProgramResult | TrainerProgramError;
|
|
265
256
|
/**
|
package/built/hevyTrainer.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.DEFAULT_TRAINER_CARDIO_ATTACHMENT_DURATION_SECONDS = exports.getPrioritySortedExercises = exports.isEquipmentCompatible = exports.normalizeExerciseCategory = exports.getTrainerRestTimerSeconds = exports.getTrainerRepRange = exports.getTrainerSetCount = exports.isTrainerProgramCardioExercise = exports.isTrainerProgramResistanceExercise = exports.programSplits = exports.frequencyMap = exports.
|
|
3
|
+
exports.DEFAULT_TRAINER_CARDIO_ATTACHMENT_DURATION_SECONDS = exports.getPrioritySortedExercises = exports.isEquipmentCompatible = exports.normalizeExerciseCategory = exports.getTrainerRestTimerSeconds = exports.getTrainerRepRange = exports.getTrainerSetCount = exports.isTrainerProgramOtherExercise = exports.isTrainerProgramCardioExercise = exports.isTrainerProgramResistanceExercise = exports.programSplits = exports.frequencyMap = exports.workoutTemplateNames = exports.defaultDurationPerFrequency = exports.hevyTrainerExerciseCategories = exports.granularEquipmentsToTrainerEquipments = exports.trainerEquipmentToGranularEquipments = exports.granularEquipmentDefaults = exports.trainerGymTypes = exports.workoutDurationOptions = void 0;
|
|
4
4
|
exports.pickTrainerExercise = pickTrainerExercise;
|
|
5
5
|
exports.generateProgram = generateProgram;
|
|
6
6
|
const _1 = require(".");
|
|
@@ -152,7 +152,7 @@ exports.defaultDurationPerFrequency = {
|
|
|
152
152
|
5: 40,
|
|
153
153
|
6: 40,
|
|
154
154
|
};
|
|
155
|
-
exports.
|
|
155
|
+
exports.workoutTemplateNames = [
|
|
156
156
|
// Full body 1x
|
|
157
157
|
'full_body_1',
|
|
158
158
|
// Full body 2x
|
|
@@ -201,6 +201,8 @@ const isTrainerProgramResistanceExercise = (exercise) => exercise.kind === 'resi
|
|
|
201
201
|
exports.isTrainerProgramResistanceExercise = isTrainerProgramResistanceExercise;
|
|
202
202
|
const isTrainerProgramCardioExercise = (exercise) => exercise.kind === 'cardio';
|
|
203
203
|
exports.isTrainerProgramCardioExercise = isTrainerProgramCardioExercise;
|
|
204
|
+
const isTrainerProgramOtherExercise = (exercise) => exercise.kind === 'other';
|
|
205
|
+
exports.isTrainerProgramOtherExercise = isTrainerProgramOtherExercise;
|
|
204
206
|
const getTrainerSetCount = (trainerAlgorithmSettings, goal, frequency) => {
|
|
205
207
|
return trainerAlgorithmSettings.sets[exports.frequencyMap[frequency]][goal];
|
|
206
208
|
};
|
|
@@ -291,12 +293,12 @@ const MAX_BARBELL_EXERCISES_FOR_ONCE_PER_WEEK = 3;
|
|
|
291
293
|
* - Only enforce the cap when the user has at least one "barbell substitute" equipment available
|
|
292
294
|
* (otherwise we allow barbell exercises to avoid running out of viable options)
|
|
293
295
|
*/
|
|
294
|
-
const isBarbellExerciseAllowed = (exercise,
|
|
296
|
+
const isBarbellExerciseAllowed = (exercise, workoutBarbellExerciseCount, userEquipments, frequency) => {
|
|
295
297
|
const isCandidateBarbell = exercise.equipment_category === 'barbell';
|
|
296
298
|
const isOncePerWeek = frequency === 1;
|
|
297
299
|
if (!isCandidateBarbell || !isOncePerWeek)
|
|
298
300
|
return true;
|
|
299
|
-
const isAtOrOverLimit =
|
|
301
|
+
const isAtOrOverLimit = workoutBarbellExerciseCount >= MAX_BARBELL_EXERCISES_FOR_ONCE_PER_WEEK;
|
|
300
302
|
if (!isAtOrOverLimit)
|
|
301
303
|
return true;
|
|
302
304
|
const barbellSubstitutes = [
|
|
@@ -314,7 +316,7 @@ const isBarbellExerciseAllowed = (exercise, routineBarbellExerciseCount, userEqu
|
|
|
314
316
|
const isExerciseUsed = (exercise, context) => {
|
|
315
317
|
var _a, _b, _c;
|
|
316
318
|
return (((_a = context.programUsedExerciseIds) === null || _a === void 0 ? void 0 : _a.has(exercise.id)) ||
|
|
317
|
-
((_b = context.
|
|
319
|
+
((_b = context.workoutUsedExerciseIds) === null || _b === void 0 ? void 0 : _b.has(exercise.id)) ||
|
|
318
320
|
((_c = context.excludedExerciseIds) === null || _c === void 0 ? void 0 : _c.has(exercise.id)) ||
|
|
319
321
|
false);
|
|
320
322
|
};
|
|
@@ -393,16 +395,16 @@ const getPrioritySortedExercises = (exercisePriorities, exerciseStore) => {
|
|
|
393
395
|
return sortedExercises;
|
|
394
396
|
};
|
|
395
397
|
exports.getPrioritySortedExercises = getPrioritySortedExercises;
|
|
396
|
-
const getMuscleGroup = ({ muscleGroupPrescription, programFocusMuscleExerciseCount, focusMuscle,
|
|
398
|
+
const getMuscleGroup = ({ muscleGroupPrescription, programFocusMuscleExerciseCount, focusMuscle, workoutTemplateName, }) => {
|
|
397
399
|
if (muscleGroupPrescription === 'focus_muscle') {
|
|
398
|
-
// If the user has selected a focus muscle and it is allowed for the
|
|
400
|
+
// If the user has selected a focus muscle and it is allowed for the workout template,
|
|
399
401
|
// we return the focus muscle extra exercise
|
|
400
402
|
if (!!focusMuscle &&
|
|
401
|
-
isFocusMuscleExtraExerciseAllowed(
|
|
403
|
+
isFocusMuscleExtraExerciseAllowed(workoutTemplateName, focusMuscle)) {
|
|
402
404
|
const n = _1.simplifiedMuscleGroupToMuscleGroups[focusMuscle].length;
|
|
403
405
|
return _1.simplifiedMuscleGroupToMuscleGroups[focusMuscle][programFocusMuscleExerciseCount % n];
|
|
404
406
|
}
|
|
405
|
-
// If the user has not selected a focus muscle or it is not allowed for the
|
|
407
|
+
// If the user has not selected a focus muscle or it is not allowed for the workout template
|
|
406
408
|
// we skip this extra exercise for the focus muscle in the program
|
|
407
409
|
return undefined;
|
|
408
410
|
}
|
|
@@ -420,7 +422,7 @@ const isExerciseMatch = (exercise, criteria) => {
|
|
|
420
422
|
const levelMatch = (_b = (_a = exercise.level) === null || _a === void 0 ? void 0 : _a.includes(criteria.level)) !== null && _b !== void 0 ? _b : false;
|
|
421
423
|
const goalMatch = (_d = (_c = exercise.goal) === null || _c === void 0 ? void 0 : _c.includes(criteria.goal)) !== null && _d !== void 0 ? _d : false;
|
|
422
424
|
const equipmentMatch = (0, exports.isEquipmentCompatible)(exercise, criteria.equipments);
|
|
423
|
-
const barbellLimitOk = isBarbellExerciseAllowed(exercise, criteria.
|
|
425
|
+
const barbellLimitOk = isBarbellExerciseAllowed(exercise, criteria.workoutBarbellExerciseCount, criteria.equipments, criteria.frequency);
|
|
424
426
|
return (categoryMatch && levelMatch && goalMatch && equipmentMatch && barbellLimitOk);
|
|
425
427
|
};
|
|
426
428
|
/**
|
|
@@ -465,8 +467,8 @@ const pickTrainerExerciseWithTrace = (params) => {
|
|
|
465
467
|
programUsedExerciseIds: context.programUsedExerciseIds,
|
|
466
468
|
excludedExerciseIds: context.excludedExerciseIds,
|
|
467
469
|
};
|
|
468
|
-
const
|
|
469
|
-
|
|
470
|
+
const workoutContext = {
|
|
471
|
+
workoutUsedExerciseIds: context.workoutUsedExerciseIds,
|
|
470
472
|
excludedExerciseIds: context.excludedExerciseIds,
|
|
471
473
|
};
|
|
472
474
|
// Pass 1
|
|
@@ -480,10 +482,10 @@ const pickTrainerExerciseWithTrace = (params) => {
|
|
|
480
482
|
if (exercise)
|
|
481
483
|
return { exercise, trace: Object.assign(Object.assign({}, trace), { selectedPass: 1 }) };
|
|
482
484
|
// Pass 2
|
|
483
|
-
exercise = findMatchingExercise(exercises, criteria,
|
|
485
|
+
exercise = findMatchingExercise(exercises, criteria, workoutContext);
|
|
484
486
|
trace.entries.push({
|
|
485
487
|
pass: 2,
|
|
486
|
-
label: 'exact match; not used in
|
|
488
|
+
label: 'exact match; not used in the same workout (reuse allowed in program)',
|
|
487
489
|
candidatePoolSize: exercises.length,
|
|
488
490
|
selectedExerciseId: exercise === null || exercise === void 0 ? void 0 : exercise.id,
|
|
489
491
|
});
|
|
@@ -500,20 +502,20 @@ const pickTrainerExerciseWithTrace = (params) => {
|
|
|
500
502
|
if (exercise)
|
|
501
503
|
return { exercise, trace: Object.assign(Object.assign({}, trace), { selectedPass: 3 }) };
|
|
502
504
|
// Pass 4
|
|
503
|
-
exercise = findAlternativeIsolationExercise(exercises, criteria,
|
|
505
|
+
exercise = findAlternativeIsolationExercise(exercises, criteria, workoutContext);
|
|
504
506
|
trace.entries.push({
|
|
505
507
|
pass: 4,
|
|
506
|
-
label: 'handpicked alternative isolation; not used in
|
|
508
|
+
label: 'handpicked alternative isolation; not used in the same workout (reuse allowed in program)',
|
|
507
509
|
candidatePoolSize: exercises.length,
|
|
508
510
|
selectedExerciseId: exercise === null || exercise === void 0 ? void 0 : exercise.id,
|
|
509
511
|
});
|
|
510
512
|
if (exercise)
|
|
511
513
|
return { exercise, trace: Object.assign(Object.assign({}, trace), { selectedPass: 4 }) };
|
|
512
514
|
// Pass 5
|
|
513
|
-
exercise = findMatchingExercise(exercises, Object.assign(Object.assign({}, criteria), { exerciseCategory: 'all' }),
|
|
515
|
+
exercise = findMatchingExercise(exercises, Object.assign(Object.assign({}, criteria), { exerciseCategory: 'all' }), workoutContext);
|
|
514
516
|
trace.entries.push({
|
|
515
517
|
pass: 5,
|
|
516
|
-
label: 'match regardless of isolation or compound; not used in
|
|
518
|
+
label: 'match regardless of isolation or compound; not used in the same workout (reuse allowed in program)',
|
|
517
519
|
candidatePoolSize: exercises.length,
|
|
518
520
|
selectedExerciseId: exercise === null || exercise === void 0 ? void 0 : exercise.id,
|
|
519
521
|
});
|
|
@@ -523,10 +525,10 @@ const pickTrainerExerciseWithTrace = (params) => {
|
|
|
523
525
|
const secondaryMuscleExercises = Object.values(sortedExercises)
|
|
524
526
|
.flat()
|
|
525
527
|
.filter((e) => e.other_muscles.includes(criteria.muscleGroup));
|
|
526
|
-
exercise = findMatchingExercise(secondaryMuscleExercises, Object.assign(Object.assign({}, criteria), { exerciseCategory: 'all' }),
|
|
528
|
+
exercise = findMatchingExercise(secondaryMuscleExercises, Object.assign(Object.assign({}, criteria), { exerciseCategory: 'all' }), workoutContext);
|
|
527
529
|
trace.entries.push({
|
|
528
530
|
pass: 6,
|
|
529
|
-
label: 'secondary muscle match; regardless of isolation or compound; not used in
|
|
531
|
+
label: 'secondary muscle match; regardless of isolation or compound; not used in the same workout (reuse allowed in program)',
|
|
530
532
|
candidatePoolSize: secondaryMuscleExercises.length,
|
|
531
533
|
selectedExerciseId: exercise === null || exercise === void 0 ? void 0 : exercise.id,
|
|
532
534
|
});
|
|
@@ -542,11 +544,11 @@ function pickTrainerExercise(params) {
|
|
|
542
544
|
}
|
|
543
545
|
const CARDIO_TRACE_INDEX_BEFORE_TEMPLATE = -1;
|
|
544
546
|
exports.DEFAULT_TRAINER_CARDIO_ATTACHMENT_DURATION_SECONDS = 600;
|
|
545
|
-
/** Inserts optional cardio before or after template exercises for one
|
|
546
|
-
const
|
|
547
|
+
/** Inserts optional cardio before or after template exercises for one workout template. */
|
|
548
|
+
const attachOptionalCardioToWorkoutTemplate = ({ cardioPreference, sortedExercises, criteria, debugExerciseSelectionTrace, exerciseSelectionTraces, workoutTemplate, templatePrescriptionCount, workoutExercises, }) => {
|
|
547
549
|
if (cardioPreference === 'no-cardio')
|
|
548
550
|
return;
|
|
549
|
-
// Cardio is reusable across
|
|
551
|
+
// Cardio is reusable across workout templates, so program/workoutTemplate used-id contexts are
|
|
550
552
|
// intentionally omitted — only the global excluded set applies.
|
|
551
553
|
const selectionParams = {
|
|
552
554
|
sortedExercises,
|
|
@@ -557,7 +559,7 @@ const attachOptionalCardioToRoutine = ({ cardioPreference, sortedExercises, crit
|
|
|
557
559
|
equipments: criteria.equipments,
|
|
558
560
|
muscleGroup: 'cardio',
|
|
559
561
|
exerciseCategory: 'all',
|
|
560
|
-
|
|
562
|
+
workoutBarbellExerciseCount: 0,
|
|
561
563
|
},
|
|
562
564
|
context: { excludedExerciseIds: criteria.excludedExerciseIds },
|
|
563
565
|
};
|
|
@@ -567,17 +569,18 @@ const attachOptionalCardioToRoutine = ({ cardioPreference, sortedExercises, crit
|
|
|
567
569
|
return;
|
|
568
570
|
const slot = {
|
|
569
571
|
kind: 'cardio',
|
|
570
|
-
|
|
571
|
-
|
|
572
|
+
exercise_template: cardioExercise,
|
|
573
|
+
duration_seconds: exports.DEFAULT_TRAINER_CARDIO_ATTACHMENT_DURATION_SECONDS,
|
|
574
|
+
set_count: 1,
|
|
572
575
|
};
|
|
573
576
|
if (cardioPreference === 'workout-start')
|
|
574
|
-
|
|
577
|
+
workoutExercises.unshift(slot);
|
|
575
578
|
else
|
|
576
|
-
|
|
579
|
+
workoutExercises.push(slot);
|
|
577
580
|
if (debugExerciseSelectionTrace) {
|
|
578
581
|
exerciseSelectionTraces.push({
|
|
579
582
|
traceKind: 'cardio',
|
|
580
|
-
|
|
583
|
+
workoutTemplate,
|
|
581
584
|
prescriptionIndex: cardioPreference === 'workout-start'
|
|
582
585
|
? CARDIO_TRACE_INDEX_BEFORE_TEMPLATE
|
|
583
586
|
: templatePrescriptionCount,
|
|
@@ -592,28 +595,27 @@ function generateProgram(params) {
|
|
|
592
595
|
var _a, _b;
|
|
593
596
|
const { trainerAlgorithmSettings, frequency, goal, level, equipments, workoutDurationMinutes, restTimerLength, exerciseStore, focusMuscle, excludedExerciseIds, debugExerciseSelectionTrace, cardioPreference, } = params;
|
|
594
597
|
const exerciseSelectionTraces = [];
|
|
595
|
-
const
|
|
598
|
+
const workoutTemplates = exports.programSplits[frequency];
|
|
596
599
|
const program = {
|
|
597
|
-
|
|
598
|
-
routines: [],
|
|
600
|
+
workoutTemplates: [],
|
|
599
601
|
};
|
|
600
602
|
// TODO: Rename sortedExercises to sortedExercisesByMuscleGroup or something similar
|
|
601
603
|
const sortedExercises = (0, exports.getPrioritySortedExercises)(trainerAlgorithmSettings.exercise_priorities, exerciseStore);
|
|
602
604
|
const programUsedExerciseIds = new Set();
|
|
603
605
|
let programFocusMuscleExerciseCount = 0;
|
|
604
606
|
const allErrors = [];
|
|
605
|
-
for (const
|
|
606
|
-
const
|
|
607
|
-
let
|
|
608
|
-
const
|
|
609
|
-
const
|
|
610
|
-
for (let prescriptionIndex = 0; prescriptionIndex <
|
|
611
|
-
const exercisePrescription =
|
|
607
|
+
for (const workout of workoutTemplates) {
|
|
608
|
+
const workoutTemplate = trainerAlgorithmSettings.templates[workout];
|
|
609
|
+
let workoutBarbellExerciseCount = 0;
|
|
610
|
+
const workoutUsedExerciseIds = new Set();
|
|
611
|
+
const workoutExercises = [];
|
|
612
|
+
for (let prescriptionIndex = 0; prescriptionIndex < workoutTemplate.exercises.length; prescriptionIndex++) {
|
|
613
|
+
const exercisePrescription = workoutTemplate.exercises[prescriptionIndex];
|
|
612
614
|
const muscleGroup = getMuscleGroup({
|
|
613
615
|
muscleGroupPrescription: exercisePrescription.muscle_group,
|
|
614
616
|
programFocusMuscleExerciseCount,
|
|
615
617
|
focusMuscle,
|
|
616
|
-
|
|
618
|
+
workoutTemplateName: workout,
|
|
617
619
|
});
|
|
618
620
|
// If the muscle group is not found, skip the exercise
|
|
619
621
|
if (!muscleGroup) {
|
|
@@ -632,7 +634,7 @@ function generateProgram(params) {
|
|
|
632
634
|
exerciseCategory: exercisePrescription.category,
|
|
633
635
|
equipments,
|
|
634
636
|
muscleGroup,
|
|
635
|
-
|
|
637
|
+
workoutBarbellExerciseCount,
|
|
636
638
|
level,
|
|
637
639
|
goal,
|
|
638
640
|
};
|
|
@@ -643,7 +645,7 @@ function generateProgram(params) {
|
|
|
643
645
|
criteria,
|
|
644
646
|
context: {
|
|
645
647
|
programUsedExerciseIds,
|
|
646
|
-
|
|
648
|
+
workoutUsedExerciseIds,
|
|
647
649
|
excludedExerciseIds,
|
|
648
650
|
},
|
|
649
651
|
withTrace: true,
|
|
@@ -651,7 +653,7 @@ function generateProgram(params) {
|
|
|
651
653
|
exercise = selection.exercise;
|
|
652
654
|
exerciseSelectionTraces.push({
|
|
653
655
|
traceKind: 'template',
|
|
654
|
-
|
|
656
|
+
workoutTemplate: workout,
|
|
655
657
|
prescriptionIndex,
|
|
656
658
|
prescription: exercisePrescription,
|
|
657
659
|
resolvedMuscleGroup: muscleGroup,
|
|
@@ -666,30 +668,29 @@ function generateProgram(params) {
|
|
|
666
668
|
criteria,
|
|
667
669
|
context: {
|
|
668
670
|
programUsedExerciseIds,
|
|
669
|
-
|
|
671
|
+
workoutUsedExerciseIds,
|
|
670
672
|
excludedExerciseIds,
|
|
671
673
|
},
|
|
672
674
|
});
|
|
673
675
|
}
|
|
674
676
|
if (!!exercise) {
|
|
675
677
|
programUsedExerciseIds.add(exercise.id);
|
|
676
|
-
|
|
678
|
+
workoutUsedExerciseIds.add(exercise.id);
|
|
677
679
|
if (exercise.equipment_category === 'barbell')
|
|
678
|
-
|
|
680
|
+
workoutBarbellExerciseCount++;
|
|
679
681
|
if (exercisePrescription.muscle_group === 'focus_muscle')
|
|
680
682
|
programFocusMuscleExerciseCount++;
|
|
681
683
|
const repRange = (0, exports.getTrainerRepRange)(trainerAlgorithmSettings, goal, exercisePrescription.category);
|
|
682
|
-
|
|
684
|
+
workoutExercises.push({
|
|
683
685
|
kind: 'resistance',
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
notes: (_b = trainerAlgorithmSettings.exercise_notes[exercise.id]) !== null && _b !== void 0 ? _b : '',
|
|
686
|
+
exercise_template: exercise,
|
|
687
|
+
set_count: (0, exports.getTrainerSetCount)(trainerAlgorithmSettings, goal, frequency),
|
|
688
|
+
warmup_set_count: (_b = exercisePrescription.warmup_set_count) !== null && _b !== void 0 ? _b : 0,
|
|
689
|
+
rep_range: {
|
|
690
|
+
start: repRange.rep_range_start,
|
|
691
|
+
end: repRange.rep_range_end,
|
|
692
|
+
},
|
|
693
|
+
rest_seconds: (0, exports.getTrainerRestTimerSeconds)(trainerAlgorithmSettings, goal, restTimerLength, exercisePrescription.category),
|
|
693
694
|
});
|
|
694
695
|
}
|
|
695
696
|
else {
|
|
@@ -707,23 +708,21 @@ function generateProgram(params) {
|
|
|
707
708
|
});
|
|
708
709
|
}
|
|
709
710
|
}
|
|
710
|
-
|
|
711
|
+
attachOptionalCardioToWorkoutTemplate({
|
|
711
712
|
cardioPreference,
|
|
712
713
|
sortedExercises,
|
|
713
714
|
criteria: { equipments, level, goal, frequency, excludedExerciseIds },
|
|
714
715
|
debugExerciseSelectionTrace,
|
|
715
716
|
exerciseSelectionTraces,
|
|
716
|
-
|
|
717
|
-
templatePrescriptionCount:
|
|
718
|
-
|
|
717
|
+
workoutTemplate: workout,
|
|
718
|
+
templatePrescriptionCount: workoutTemplate.exercises.length,
|
|
719
|
+
workoutExercises,
|
|
719
720
|
});
|
|
720
|
-
program.
|
|
721
|
-
name:
|
|
722
|
-
|
|
723
|
-
exercises: routineExercises,
|
|
721
|
+
program.workoutTemplates.push({
|
|
722
|
+
name: workout,
|
|
723
|
+
exercises: workoutExercises,
|
|
724
724
|
});
|
|
725
725
|
}
|
|
726
|
-
// Return result based on whether there were errors
|
|
727
726
|
if (allErrors.length > 0) {
|
|
728
727
|
const result = {
|
|
729
728
|
success: false,
|