hevy-shared 1.0.1038 → 1.0.1039
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 +36 -43
- package/built/hevyTrainer.js +74 -65
- package/built/index.d.ts +105 -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, ExerciseType } 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,40 @@ 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 declare const isTrainerResistanceExerciseType: (exerciseType: ExerciseType) => exerciseType is "weight_reps" | "reps_only" | "bodyweight_reps" | "bodyweight_assisted_reps";
|
|
185
|
+
export declare const isTrainerCardioExerciseType: (exerciseType: ExerciseType) => exerciseType is "duration" | "distance_duration" | "floors_duration" | "steps_duration";
|
|
186
|
+
export interface TrainerProgramWorkoutTemplate {
|
|
187
|
+
name: TrainerWorkoutTemplateName;
|
|
184
188
|
exercises: TrainerProgramExercise[];
|
|
185
|
-
notes?: string;
|
|
186
189
|
}
|
|
187
|
-
export interface
|
|
188
|
-
|
|
189
|
-
routines: TrainerProgramRoutine[];
|
|
190
|
+
export interface GeneratedTrainerProgram {
|
|
191
|
+
workoutTemplates: TrainerProgramWorkoutTemplate[];
|
|
190
192
|
}
|
|
191
193
|
export declare const getTrainerSetCount: (trainerAlgorithmSettings: TrainerAlgorithmSettings, goal: TrainingGoal, frequency: WeeklyTrainingFrequency) => number;
|
|
192
|
-
export declare const getTrainerRepRange: (trainerAlgorithmSettings: TrainerAlgorithmSettings, goal: TrainingGoal, exerciseCategory: HevyTrainerExerciseCategory) =>
|
|
194
|
+
export declare const getTrainerRepRange: (trainerAlgorithmSettings: TrainerAlgorithmSettings, goal: TrainingGoal, exerciseCategory: HevyTrainerExerciseCategory) => AlgorithmRepRange;
|
|
193
195
|
export declare const getTrainerRestTimerSeconds: (trainerAlgorithmSettings: TrainerAlgorithmSettings, goal: TrainingGoal, length: RestTimerLength, exerciseCategory: HevyTrainerExerciseCategory) => number;
|
|
194
196
|
/**
|
|
195
197
|
* Normalizes the exercise category to a HevyTrainerExerciseCategory
|
|
@@ -243,23 +245,14 @@ export interface ExercisePrescriptionError {
|
|
|
243
245
|
focusMuscle?: SimplifiedMuscleGroup;
|
|
244
246
|
};
|
|
245
247
|
}
|
|
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
248
|
export interface TrainerProgramResult {
|
|
256
249
|
success: true;
|
|
257
|
-
program:
|
|
250
|
+
program: GeneratedTrainerProgram;
|
|
258
251
|
}
|
|
259
252
|
export interface TrainerProgramError {
|
|
260
253
|
success: false;
|
|
261
254
|
errors: ExercisePrescriptionError[];
|
|
262
|
-
partialProgram:
|
|
255
|
+
partialProgram: GeneratedTrainerProgram;
|
|
263
256
|
}
|
|
264
257
|
export type TrainerProgramAttempt = TrainerProgramResult | TrainerProgramError;
|
|
265
258
|
/**
|
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.isTrainerCardioExerciseType = exports.isTrainerResistanceExerciseType = 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,18 @@ 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;
|
|
206
|
+
const isTrainerResistanceExerciseType = (exerciseType) => exerciseType === 'weight_reps' ||
|
|
207
|
+
exerciseType === 'bodyweight_reps' ||
|
|
208
|
+
exerciseType === 'bodyweight_assisted_reps' ||
|
|
209
|
+
exerciseType === 'reps_only';
|
|
210
|
+
exports.isTrainerResistanceExerciseType = isTrainerResistanceExerciseType;
|
|
211
|
+
const isTrainerCardioExerciseType = (exerciseType) => exerciseType === 'distance_duration' ||
|
|
212
|
+
exerciseType === 'duration' ||
|
|
213
|
+
exerciseType === 'floors_duration' ||
|
|
214
|
+
exerciseType === 'steps_duration';
|
|
215
|
+
exports.isTrainerCardioExerciseType = isTrainerCardioExerciseType;
|
|
204
216
|
const getTrainerSetCount = (trainerAlgorithmSettings, goal, frequency) => {
|
|
205
217
|
return trainerAlgorithmSettings.sets[exports.frequencyMap[frequency]][goal];
|
|
206
218
|
};
|
|
@@ -291,12 +303,12 @@ const MAX_BARBELL_EXERCISES_FOR_ONCE_PER_WEEK = 3;
|
|
|
291
303
|
* - Only enforce the cap when the user has at least one "barbell substitute" equipment available
|
|
292
304
|
* (otherwise we allow barbell exercises to avoid running out of viable options)
|
|
293
305
|
*/
|
|
294
|
-
const isBarbellExerciseAllowed = (exercise,
|
|
306
|
+
const isBarbellExerciseAllowed = (exercise, workoutBarbellExerciseCount, userEquipments, frequency) => {
|
|
295
307
|
const isCandidateBarbell = exercise.equipment_category === 'barbell';
|
|
296
308
|
const isOncePerWeek = frequency === 1;
|
|
297
309
|
if (!isCandidateBarbell || !isOncePerWeek)
|
|
298
310
|
return true;
|
|
299
|
-
const isAtOrOverLimit =
|
|
311
|
+
const isAtOrOverLimit = workoutBarbellExerciseCount >= MAX_BARBELL_EXERCISES_FOR_ONCE_PER_WEEK;
|
|
300
312
|
if (!isAtOrOverLimit)
|
|
301
313
|
return true;
|
|
302
314
|
const barbellSubstitutes = [
|
|
@@ -314,7 +326,7 @@ const isBarbellExerciseAllowed = (exercise, routineBarbellExerciseCount, userEqu
|
|
|
314
326
|
const isExerciseUsed = (exercise, context) => {
|
|
315
327
|
var _a, _b, _c;
|
|
316
328
|
return (((_a = context.programUsedExerciseIds) === null || _a === void 0 ? void 0 : _a.has(exercise.id)) ||
|
|
317
|
-
((_b = context.
|
|
329
|
+
((_b = context.workoutUsedExerciseIds) === null || _b === void 0 ? void 0 : _b.has(exercise.id)) ||
|
|
318
330
|
((_c = context.excludedExerciseIds) === null || _c === void 0 ? void 0 : _c.has(exercise.id)) ||
|
|
319
331
|
false);
|
|
320
332
|
};
|
|
@@ -393,16 +405,16 @@ const getPrioritySortedExercises = (exercisePriorities, exerciseStore) => {
|
|
|
393
405
|
return sortedExercises;
|
|
394
406
|
};
|
|
395
407
|
exports.getPrioritySortedExercises = getPrioritySortedExercises;
|
|
396
|
-
const getMuscleGroup = ({ muscleGroupPrescription, programFocusMuscleExerciseCount, focusMuscle,
|
|
408
|
+
const getMuscleGroup = ({ muscleGroupPrescription, programFocusMuscleExerciseCount, focusMuscle, workoutTemplateName, }) => {
|
|
397
409
|
if (muscleGroupPrescription === 'focus_muscle') {
|
|
398
|
-
// If the user has selected a focus muscle and it is allowed for the
|
|
410
|
+
// If the user has selected a focus muscle and it is allowed for the workout template,
|
|
399
411
|
// we return the focus muscle extra exercise
|
|
400
412
|
if (!!focusMuscle &&
|
|
401
|
-
isFocusMuscleExtraExerciseAllowed(
|
|
413
|
+
isFocusMuscleExtraExerciseAllowed(workoutTemplateName, focusMuscle)) {
|
|
402
414
|
const n = _1.simplifiedMuscleGroupToMuscleGroups[focusMuscle].length;
|
|
403
415
|
return _1.simplifiedMuscleGroupToMuscleGroups[focusMuscle][programFocusMuscleExerciseCount % n];
|
|
404
416
|
}
|
|
405
|
-
// If the user has not selected a focus muscle or it is not allowed for the
|
|
417
|
+
// If the user has not selected a focus muscle or it is not allowed for the workout template
|
|
406
418
|
// we skip this extra exercise for the focus muscle in the program
|
|
407
419
|
return undefined;
|
|
408
420
|
}
|
|
@@ -420,7 +432,7 @@ const isExerciseMatch = (exercise, criteria) => {
|
|
|
420
432
|
const levelMatch = (_b = (_a = exercise.level) === null || _a === void 0 ? void 0 : _a.includes(criteria.level)) !== null && _b !== void 0 ? _b : false;
|
|
421
433
|
const goalMatch = (_d = (_c = exercise.goal) === null || _c === void 0 ? void 0 : _c.includes(criteria.goal)) !== null && _d !== void 0 ? _d : false;
|
|
422
434
|
const equipmentMatch = (0, exports.isEquipmentCompatible)(exercise, criteria.equipments);
|
|
423
|
-
const barbellLimitOk = isBarbellExerciseAllowed(exercise, criteria.
|
|
435
|
+
const barbellLimitOk = isBarbellExerciseAllowed(exercise, criteria.workoutBarbellExerciseCount, criteria.equipments, criteria.frequency);
|
|
424
436
|
return (categoryMatch && levelMatch && goalMatch && equipmentMatch && barbellLimitOk);
|
|
425
437
|
};
|
|
426
438
|
/**
|
|
@@ -465,8 +477,8 @@ const pickTrainerExerciseWithTrace = (params) => {
|
|
|
465
477
|
programUsedExerciseIds: context.programUsedExerciseIds,
|
|
466
478
|
excludedExerciseIds: context.excludedExerciseIds,
|
|
467
479
|
};
|
|
468
|
-
const
|
|
469
|
-
|
|
480
|
+
const workoutContext = {
|
|
481
|
+
workoutUsedExerciseIds: context.workoutUsedExerciseIds,
|
|
470
482
|
excludedExerciseIds: context.excludedExerciseIds,
|
|
471
483
|
};
|
|
472
484
|
// Pass 1
|
|
@@ -480,10 +492,10 @@ const pickTrainerExerciseWithTrace = (params) => {
|
|
|
480
492
|
if (exercise)
|
|
481
493
|
return { exercise, trace: Object.assign(Object.assign({}, trace), { selectedPass: 1 }) };
|
|
482
494
|
// Pass 2
|
|
483
|
-
exercise = findMatchingExercise(exercises, criteria,
|
|
495
|
+
exercise = findMatchingExercise(exercises, criteria, workoutContext);
|
|
484
496
|
trace.entries.push({
|
|
485
497
|
pass: 2,
|
|
486
|
-
label: 'exact match; not used in
|
|
498
|
+
label: 'exact match; not used in the same workout (reuse allowed in program)',
|
|
487
499
|
candidatePoolSize: exercises.length,
|
|
488
500
|
selectedExerciseId: exercise === null || exercise === void 0 ? void 0 : exercise.id,
|
|
489
501
|
});
|
|
@@ -500,20 +512,20 @@ const pickTrainerExerciseWithTrace = (params) => {
|
|
|
500
512
|
if (exercise)
|
|
501
513
|
return { exercise, trace: Object.assign(Object.assign({}, trace), { selectedPass: 3 }) };
|
|
502
514
|
// Pass 4
|
|
503
|
-
exercise = findAlternativeIsolationExercise(exercises, criteria,
|
|
515
|
+
exercise = findAlternativeIsolationExercise(exercises, criteria, workoutContext);
|
|
504
516
|
trace.entries.push({
|
|
505
517
|
pass: 4,
|
|
506
|
-
label: 'handpicked alternative isolation; not used in
|
|
518
|
+
label: 'handpicked alternative isolation; not used in the same workout (reuse allowed in program)',
|
|
507
519
|
candidatePoolSize: exercises.length,
|
|
508
520
|
selectedExerciseId: exercise === null || exercise === void 0 ? void 0 : exercise.id,
|
|
509
521
|
});
|
|
510
522
|
if (exercise)
|
|
511
523
|
return { exercise, trace: Object.assign(Object.assign({}, trace), { selectedPass: 4 }) };
|
|
512
524
|
// Pass 5
|
|
513
|
-
exercise = findMatchingExercise(exercises, Object.assign(Object.assign({}, criteria), { exerciseCategory: 'all' }),
|
|
525
|
+
exercise = findMatchingExercise(exercises, Object.assign(Object.assign({}, criteria), { exerciseCategory: 'all' }), workoutContext);
|
|
514
526
|
trace.entries.push({
|
|
515
527
|
pass: 5,
|
|
516
|
-
label: 'match regardless of isolation or compound; not used in
|
|
528
|
+
label: 'match regardless of isolation or compound; not used in the same workout (reuse allowed in program)',
|
|
517
529
|
candidatePoolSize: exercises.length,
|
|
518
530
|
selectedExerciseId: exercise === null || exercise === void 0 ? void 0 : exercise.id,
|
|
519
531
|
});
|
|
@@ -523,10 +535,10 @@ const pickTrainerExerciseWithTrace = (params) => {
|
|
|
523
535
|
const secondaryMuscleExercises = Object.values(sortedExercises)
|
|
524
536
|
.flat()
|
|
525
537
|
.filter((e) => e.other_muscles.includes(criteria.muscleGroup));
|
|
526
|
-
exercise = findMatchingExercise(secondaryMuscleExercises, Object.assign(Object.assign({}, criteria), { exerciseCategory: 'all' }),
|
|
538
|
+
exercise = findMatchingExercise(secondaryMuscleExercises, Object.assign(Object.assign({}, criteria), { exerciseCategory: 'all' }), workoutContext);
|
|
527
539
|
trace.entries.push({
|
|
528
540
|
pass: 6,
|
|
529
|
-
label: 'secondary muscle match; regardless of isolation or compound; not used in
|
|
541
|
+
label: 'secondary muscle match; regardless of isolation or compound; not used in the same workout (reuse allowed in program)',
|
|
530
542
|
candidatePoolSize: secondaryMuscleExercises.length,
|
|
531
543
|
selectedExerciseId: exercise === null || exercise === void 0 ? void 0 : exercise.id,
|
|
532
544
|
});
|
|
@@ -542,11 +554,11 @@ function pickTrainerExercise(params) {
|
|
|
542
554
|
}
|
|
543
555
|
const CARDIO_TRACE_INDEX_BEFORE_TEMPLATE = -1;
|
|
544
556
|
exports.DEFAULT_TRAINER_CARDIO_ATTACHMENT_DURATION_SECONDS = 600;
|
|
545
|
-
/** Inserts optional cardio before or after template exercises for one
|
|
546
|
-
const
|
|
557
|
+
/** Inserts optional cardio before or after template exercises for one workout template. */
|
|
558
|
+
const attachOptionalCardioToWorkoutTemplate = ({ cardioPreference, sortedExercises, criteria, debugExerciseSelectionTrace, exerciseSelectionTraces, workoutTemplate, templatePrescriptionCount, workoutExercises, }) => {
|
|
547
559
|
if (cardioPreference === 'no-cardio')
|
|
548
560
|
return;
|
|
549
|
-
// Cardio is reusable across
|
|
561
|
+
// Cardio is reusable across workout templates, so program/workoutTemplate used-id contexts are
|
|
550
562
|
// intentionally omitted — only the global excluded set applies.
|
|
551
563
|
const selectionParams = {
|
|
552
564
|
sortedExercises,
|
|
@@ -557,7 +569,7 @@ const attachOptionalCardioToRoutine = ({ cardioPreference, sortedExercises, crit
|
|
|
557
569
|
equipments: criteria.equipments,
|
|
558
570
|
muscleGroup: 'cardio',
|
|
559
571
|
exerciseCategory: 'all',
|
|
560
|
-
|
|
572
|
+
workoutBarbellExerciseCount: 0,
|
|
561
573
|
},
|
|
562
574
|
context: { excludedExerciseIds: criteria.excludedExerciseIds },
|
|
563
575
|
};
|
|
@@ -567,17 +579,18 @@ const attachOptionalCardioToRoutine = ({ cardioPreference, sortedExercises, crit
|
|
|
567
579
|
return;
|
|
568
580
|
const slot = {
|
|
569
581
|
kind: 'cardio',
|
|
570
|
-
|
|
571
|
-
|
|
582
|
+
exercise_template: cardioExercise,
|
|
583
|
+
duration_seconds: exports.DEFAULT_TRAINER_CARDIO_ATTACHMENT_DURATION_SECONDS,
|
|
584
|
+
set_count: 1,
|
|
572
585
|
};
|
|
573
586
|
if (cardioPreference === 'workout-start')
|
|
574
|
-
|
|
587
|
+
workoutExercises.unshift(slot);
|
|
575
588
|
else
|
|
576
|
-
|
|
589
|
+
workoutExercises.push(slot);
|
|
577
590
|
if (debugExerciseSelectionTrace) {
|
|
578
591
|
exerciseSelectionTraces.push({
|
|
579
592
|
traceKind: 'cardio',
|
|
580
|
-
|
|
593
|
+
workoutTemplate,
|
|
581
594
|
prescriptionIndex: cardioPreference === 'workout-start'
|
|
582
595
|
? CARDIO_TRACE_INDEX_BEFORE_TEMPLATE
|
|
583
596
|
: templatePrescriptionCount,
|
|
@@ -592,28 +605,27 @@ function generateProgram(params) {
|
|
|
592
605
|
var _a, _b;
|
|
593
606
|
const { trainerAlgorithmSettings, frequency, goal, level, equipments, workoutDurationMinutes, restTimerLength, exerciseStore, focusMuscle, excludedExerciseIds, debugExerciseSelectionTrace, cardioPreference, } = params;
|
|
594
607
|
const exerciseSelectionTraces = [];
|
|
595
|
-
const
|
|
608
|
+
const workoutTemplates = exports.programSplits[frequency];
|
|
596
609
|
const program = {
|
|
597
|
-
|
|
598
|
-
routines: [],
|
|
610
|
+
workoutTemplates: [],
|
|
599
611
|
};
|
|
600
612
|
// TODO: Rename sortedExercises to sortedExercisesByMuscleGroup or something similar
|
|
601
613
|
const sortedExercises = (0, exports.getPrioritySortedExercises)(trainerAlgorithmSettings.exercise_priorities, exerciseStore);
|
|
602
614
|
const programUsedExerciseIds = new Set();
|
|
603
615
|
let programFocusMuscleExerciseCount = 0;
|
|
604
616
|
const allErrors = [];
|
|
605
|
-
for (const
|
|
606
|
-
const
|
|
607
|
-
let
|
|
608
|
-
const
|
|
609
|
-
const
|
|
610
|
-
for (let prescriptionIndex = 0; prescriptionIndex <
|
|
611
|
-
const exercisePrescription =
|
|
617
|
+
for (const workout of workoutTemplates) {
|
|
618
|
+
const workoutTemplate = trainerAlgorithmSettings.templates[workout];
|
|
619
|
+
let workoutBarbellExerciseCount = 0;
|
|
620
|
+
const workoutUsedExerciseIds = new Set();
|
|
621
|
+
const workoutExercises = [];
|
|
622
|
+
for (let prescriptionIndex = 0; prescriptionIndex < workoutTemplate.exercises.length; prescriptionIndex++) {
|
|
623
|
+
const exercisePrescription = workoutTemplate.exercises[prescriptionIndex];
|
|
612
624
|
const muscleGroup = getMuscleGroup({
|
|
613
625
|
muscleGroupPrescription: exercisePrescription.muscle_group,
|
|
614
626
|
programFocusMuscleExerciseCount,
|
|
615
627
|
focusMuscle,
|
|
616
|
-
|
|
628
|
+
workoutTemplateName: workout,
|
|
617
629
|
});
|
|
618
630
|
// If the muscle group is not found, skip the exercise
|
|
619
631
|
if (!muscleGroup) {
|
|
@@ -632,7 +644,7 @@ function generateProgram(params) {
|
|
|
632
644
|
exerciseCategory: exercisePrescription.category,
|
|
633
645
|
equipments,
|
|
634
646
|
muscleGroup,
|
|
635
|
-
|
|
647
|
+
workoutBarbellExerciseCount,
|
|
636
648
|
level,
|
|
637
649
|
goal,
|
|
638
650
|
};
|
|
@@ -643,7 +655,7 @@ function generateProgram(params) {
|
|
|
643
655
|
criteria,
|
|
644
656
|
context: {
|
|
645
657
|
programUsedExerciseIds,
|
|
646
|
-
|
|
658
|
+
workoutUsedExerciseIds,
|
|
647
659
|
excludedExerciseIds,
|
|
648
660
|
},
|
|
649
661
|
withTrace: true,
|
|
@@ -651,7 +663,7 @@ function generateProgram(params) {
|
|
|
651
663
|
exercise = selection.exercise;
|
|
652
664
|
exerciseSelectionTraces.push({
|
|
653
665
|
traceKind: 'template',
|
|
654
|
-
|
|
666
|
+
workoutTemplate: workout,
|
|
655
667
|
prescriptionIndex,
|
|
656
668
|
prescription: exercisePrescription,
|
|
657
669
|
resolvedMuscleGroup: muscleGroup,
|
|
@@ -666,30 +678,29 @@ function generateProgram(params) {
|
|
|
666
678
|
criteria,
|
|
667
679
|
context: {
|
|
668
680
|
programUsedExerciseIds,
|
|
669
|
-
|
|
681
|
+
workoutUsedExerciseIds,
|
|
670
682
|
excludedExerciseIds,
|
|
671
683
|
},
|
|
672
684
|
});
|
|
673
685
|
}
|
|
674
686
|
if (!!exercise) {
|
|
675
687
|
programUsedExerciseIds.add(exercise.id);
|
|
676
|
-
|
|
688
|
+
workoutUsedExerciseIds.add(exercise.id);
|
|
677
689
|
if (exercise.equipment_category === 'barbell')
|
|
678
|
-
|
|
690
|
+
workoutBarbellExerciseCount++;
|
|
679
691
|
if (exercisePrescription.muscle_group === 'focus_muscle')
|
|
680
692
|
programFocusMuscleExerciseCount++;
|
|
681
693
|
const repRange = (0, exports.getTrainerRepRange)(trainerAlgorithmSettings, goal, exercisePrescription.category);
|
|
682
|
-
|
|
694
|
+
workoutExercises.push({
|
|
683
695
|
kind: 'resistance',
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
notes: (_b = trainerAlgorithmSettings.exercise_notes[exercise.id]) !== null && _b !== void 0 ? _b : '',
|
|
696
|
+
exercise_template: exercise,
|
|
697
|
+
set_count: (0, exports.getTrainerSetCount)(trainerAlgorithmSettings, goal, frequency),
|
|
698
|
+
warmup_set_count: (_b = exercisePrescription.warmup_set_count) !== null && _b !== void 0 ? _b : 0,
|
|
699
|
+
rep_range: {
|
|
700
|
+
start: repRange.rep_range_start,
|
|
701
|
+
end: repRange.rep_range_end,
|
|
702
|
+
},
|
|
703
|
+
rest_seconds: (0, exports.getTrainerRestTimerSeconds)(trainerAlgorithmSettings, goal, restTimerLength, exercisePrescription.category),
|
|
693
704
|
});
|
|
694
705
|
}
|
|
695
706
|
else {
|
|
@@ -707,23 +718,21 @@ function generateProgram(params) {
|
|
|
707
718
|
});
|
|
708
719
|
}
|
|
709
720
|
}
|
|
710
|
-
|
|
721
|
+
attachOptionalCardioToWorkoutTemplate({
|
|
711
722
|
cardioPreference,
|
|
712
723
|
sortedExercises,
|
|
713
724
|
criteria: { equipments, level, goal, frequency, excludedExerciseIds },
|
|
714
725
|
debugExerciseSelectionTrace,
|
|
715
726
|
exerciseSelectionTraces,
|
|
716
|
-
|
|
717
|
-
templatePrescriptionCount:
|
|
718
|
-
|
|
727
|
+
workoutTemplate: workout,
|
|
728
|
+
templatePrescriptionCount: workoutTemplate.exercises.length,
|
|
729
|
+
workoutExercises,
|
|
719
730
|
});
|
|
720
|
-
program.
|
|
721
|
-
name:
|
|
722
|
-
|
|
723
|
-
exercises: routineExercises,
|
|
731
|
+
program.workoutTemplates.push({
|
|
732
|
+
name: workout,
|
|
733
|
+
exercises: workoutExercises,
|
|
724
734
|
});
|
|
725
735
|
}
|
|
726
|
-
// Return result based on whether there were errors
|
|
727
736
|
if (allErrors.length > 0) {
|
|
728
737
|
const result = {
|
|
729
738
|
success: false,
|