@dgpholdings/greatoak-shared 1.2.86 → 1.2.88
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/README.md +148 -148
- package/dist/__mocks__/catalog.fixture.d.ts +2 -0
- package/dist/__mocks__/catalog.fixture.js +208 -0
- package/dist/__mocks__/exercises.mock.d.ts +4 -11
- package/dist/__mocks__/exercises.mock.js +82 -41
- package/dist/__mocks__/sessions.mock.d.ts +28 -0
- package/dist/__mocks__/sessions.mock.js +394 -0
- package/dist/__mocks__/testIds.d.ts +9 -0
- package/dist/__mocks__/testIds.js +13 -0
- package/dist/__mocks__/user.mock.js +3 -1
- package/dist/constants/goalJourney.d.ts +108 -0
- package/dist/constants/goalJourney.js +443 -0
- package/dist/constants/index.d.ts +1 -0
- package/dist/constants/index.js +1 -0
- package/dist/types/TApiAiExerciseAnalysis.d.ts +2 -1
- package/dist/types/TApiClientConstellation.d.ts +33 -0
- package/dist/types/TApiClientConstellation.js +13 -0
- package/dist/types/TApiExercise.d.ts +5 -3
- package/dist/types/TApiUser.d.ts +2 -0
- package/dist/types/index.d.ts +1 -0
- package/dist/utils/adoptionEngine/scaleProPlan.util.js +9 -4
- package/dist/utils/constellation/computeNormalisedLoad.d.ts +48 -0
- package/dist/utils/constellation/computeNormalisedLoad.js +150 -0
- package/dist/utils/constellation/computeNormalisedLoad.test.d.ts +1 -0
- package/dist/utils/constellation/computeNormalisedLoad.test.js +218 -0
- package/dist/utils/constellation/evaluateConstellation.d.ts +27 -0
- package/dist/utils/constellation/evaluateConstellation.js +135 -0
- package/dist/utils/constellation/evaluateConstellation.test.d.ts +1 -0
- package/dist/utils/constellation/evaluateConstellation.test.js +93 -0
- package/dist/utils/constellation/index.d.ts +18 -0
- package/dist/utils/constellation/index.js +29 -0
- package/dist/utils/constellation/levelThresholds.d.ts +99 -0
- package/dist/utils/constellation/levelThresholds.js +123 -0
- package/dist/utils/constellation/starFoundation.d.ts +25 -0
- package/dist/utils/constellation/starFoundation.js +54 -0
- package/dist/utils/constellation/starFoundation.test.d.ts +1 -0
- package/dist/utils/constellation/starFoundation.test.js +75 -0
- package/dist/utils/constellation/stars/consistency.d.ts +29 -0
- package/dist/utils/constellation/stars/consistency.js +142 -0
- package/dist/utils/constellation/stars/consistency.test.d.ts +1 -0
- package/dist/utils/constellation/stars/consistency.test.js +94 -0
- package/dist/utils/constellation/stars/lowerBody.d.ts +17 -0
- package/dist/utils/constellation/stars/lowerBody.js +30 -0
- package/dist/utils/constellation/stars/pull.d.ts +11 -0
- package/dist/utils/constellation/stars/pull.js +24 -0
- package/dist/utils/constellation/stars/push.d.ts +11 -0
- package/dist/utils/constellation/stars/push.js +24 -0
- package/dist/utils/constellation/stars/quality.d.ts +19 -0
- package/dist/utils/constellation/stars/quality.js +98 -0
- package/dist/utils/constellation/stars/quality.test.d.ts +1 -0
- package/dist/utils/constellation/stars/quality.test.js +113 -0
- package/dist/utils/constellation/stars/recovery.d.ts +29 -0
- package/dist/utils/constellation/stars/recovery.js +169 -0
- package/dist/utils/constellation/stars/recovery.test.d.ts +1 -0
- package/dist/utils/constellation/stars/recovery.test.js +131 -0
- package/dist/utils/constellation/strengthStar.test.d.ts +1 -0
- package/dist/utils/constellation/strengthStar.test.js +190 -0
- package/dist/utils/constellation/strengthStarHelpers.d.ts +41 -0
- package/dist/utils/constellation/strengthStarHelpers.js +104 -0
- package/dist/utils/constellation/types.d.ts +124 -0
- package/dist/utils/constellation/types.js +18 -0
- package/dist/utils/exerciseRecord/recordValidator.integration.test.js +1 -1
- package/dist/utils/exerciseRecord/recordValidator.js +1 -1
- package/dist/utils/exerciseRecord/recordValidator.test.js +8 -8
- package/dist/utils/index.d.ts +5 -3
- package/dist/utils/index.js +1 -0
- package/dist/utils/scoringWorkout/calculateQualityScore.d.ts +59 -36
- package/dist/utils/scoringWorkout/calculateQualityScore.js +234 -233
- package/dist/utils/scoringWorkout/computeMuscleFatigueMap.d.ts +8 -5
- package/dist/utils/scoringWorkout/computeMuscleFatigueMap.js +72 -88
- package/dist/utils/scoringWorkout/constants.d.ts +20 -6
- package/dist/utils/scoringWorkout/constants.js +23 -9
- package/dist/utils/scoringWorkout/helpers.d.ts +7 -0
- package/dist/utils/scoringWorkout/helpers.js +24 -18
- package/dist/utils/scoringWorkout/index.d.ts +12 -8
- package/dist/utils/scoringWorkout/index.js +23 -15
- package/dist/utils/scoringWorkout/parseRecords.js +4 -3
- package/dist/utils/scoringWorkout/scoringWorkout.integration.test.js +223 -185
- package/dist/utils/scoringWorkout/types.d.ts +34 -14
- package/package.json +31 -31
- package/dist/utils/exerciseRecord/__mocks__/exercises.mock.d.ts +0 -30
- package/dist/utils/exerciseRecord/__mocks__/exercises.mock.js +0 -138
- package/dist/utils/scaleProPlan.util.d.ts +0 -9
- package/dist/utils/scaleProPlan.util.js +0 -139
- package/dist/utils/scoring/calculateCalories.d.ts +0 -67
- package/dist/utils/scoring/calculateCalories.js +0 -345
- package/dist/utils/scoring/calculateMuscleFatiue.d.ts +0 -67
- package/dist/utils/scoring/calculateMuscleFatiue.js +0 -310
- package/dist/utils/scoring/calculateQualityScore.d.ts +0 -71
- package/dist/utils/scoring/calculateQualityScore.js +0 -334
- package/dist/utils/scoring/calculateTotalVolume.d.ts +0 -15
- package/dist/utils/scoring/calculateTotalVolume.js +0 -73
- package/dist/utils/scoring/constants.d.ts +0 -211
- package/dist/utils/scoring/constants.js +0 -247
- package/dist/utils/scoring/helpers.d.ts +0 -119
- package/dist/utils/scoring/helpers.js +0 -229
- package/dist/utils/scoring/index.d.ts +0 -28
- package/dist/utils/scoring/index.js +0 -47
- package/dist/utils/scoring/parseRecords.d.ts +0 -98
- package/dist/utils/scoring/parseRecords.js +0 -284
- package/dist/utils/scoring/types.d.ts +0 -86
- package/dist/utils/scoring/types.js +0 -11
- package/dist/utils/scoring.utils.d.ts +0 -14
- package/dist/utils/scoring.utils.js +0 -243
- /package/dist/utils/scoringWorkout/{calculateMuscleFatiue.d.ts → calculateMuscleFatigue.d.ts} +0 -0
- /package/dist/utils/scoringWorkout/{calculateMuscleFatiue.js → calculateMuscleFatigue.js} +0 -0
|
@@ -1,243 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
/*
|
|
3
|
-
* Exercise Scoring System Documentation
|
|
4
|
-
*
|
|
5
|
-
* OVERVIEW:
|
|
6
|
-
* Evaluates workout performance (0-100) with overall score and muscle fatigue ratings.
|
|
7
|
-
* Designed to motivate users by quantifying effort and tracking muscle engagement.
|
|
8
|
-
*
|
|
9
|
-
* SCORE COMPOSITION:
|
|
10
|
-
* - Volume (35%): Total work (weight×reps, duration×difficulty, distance×speed)
|
|
11
|
-
* - Intensity (35%): Effort level (relative weight, rep ranges, speed, RPE when provided)
|
|
12
|
-
* - Quality (30%): Set consistency and performance maintenance
|
|
13
|
-
*
|
|
14
|
-
* SCORE INTERPRETATION:
|
|
15
|
-
* 90-100: Exceptional - peak performance with high volume/intensity/consistency
|
|
16
|
-
* 75-89: Excellent - strong effort with good form and progression
|
|
17
|
-
* 60-74: Good - solid baseline performance, room for improvement
|
|
18
|
-
* 45-59: Moderate - acceptable but consider increasing intensity/volume
|
|
19
|
-
* 0-44: Light - warm-up sets or recovery day
|
|
20
|
-
*
|
|
21
|
-
* MUSCLE FATIGUE (0-100):
|
|
22
|
-
* Primary muscles get 70% fatigue impact, secondary muscles get 30%
|
|
23
|
-
* 80-100: Fully fatigued - heavily worked, needs recovery
|
|
24
|
-
* 60-79: High fatigue - significant work, approaching limit
|
|
25
|
-
* 40-59: Moderate fatigue - good activation, can handle more
|
|
26
|
-
* 20-39: Light fatigue - warmed up, plenty capacity remaining
|
|
27
|
-
* 0-19: Minimal fatigue - barely engaged
|
|
28
|
-
*
|
|
29
|
-
* QUALITY SCORE EXPLAINED:
|
|
30
|
-
* Measures how well performance is maintained across sets (starts at 85 points)
|
|
31
|
-
* - Performance drop <10% between sets: +5 points
|
|
32
|
-
* - Performance drop 10-20%: +2 points
|
|
33
|
-
* - Performance drop >30%: -10 points
|
|
34
|
-
* - Consistent RIR/RPE between sets: +3 points
|
|
35
|
-
* - Completing 3+ sets: +5 points, 4+ sets: +8 total points
|
|
36
|
-
*
|
|
37
|
-
* KEY FACTORS:
|
|
38
|
-
* - User fitness level (higher level = higher expectations)
|
|
39
|
-
* - Exercise difficulty (harder exercises boost score)
|
|
40
|
-
* - RPE (1-10): When provided, adjusts intensity (higher RPE = higher intensity)
|
|
41
|
-
* - RIR (0+): When provided, checks consistency between sets
|
|
42
|
-
* - Fatigue multiplier (later sets get 8% bonus per set for accumulated fatigue)
|
|
43
|
-
*/
|
|
44
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
45
|
-
exports.calculateExerciseScore = void 0;
|
|
46
|
-
const time_util_1 = require("./time.util");
|
|
47
|
-
const scoring_1 = require("./scoring");
|
|
48
|
-
const calculateExerciseScore = (param) => {
|
|
49
|
-
const { exercise, record, user } = param;
|
|
50
|
-
// Filter only completed sets
|
|
51
|
-
const completedSets = record.filter((r) => r.isDone);
|
|
52
|
-
if (completedSets.length === 0) {
|
|
53
|
-
return { score: 0, muscleScores: {} };
|
|
54
|
-
}
|
|
55
|
-
// Calculate base performance metrics
|
|
56
|
-
const totalVolume = (0, scoring_1.calculateTotalVolume)(completedSets, user);
|
|
57
|
-
const avgIntensity = calculateAverageIntensity(completedSets, exercise, user);
|
|
58
|
-
const setQuality = calculateSetQuality(completedSets);
|
|
59
|
-
// Calculate muscle fatigue scores
|
|
60
|
-
const muscleScores = calculateMuscleScores(exercise, completedSets, totalVolume, user);
|
|
61
|
-
// Final score composition
|
|
62
|
-
const volumeComponent = normalizeVolume(totalVolume, exercise, user) * 0.35;
|
|
63
|
-
const intensityComponent = avgIntensity * 0.35;
|
|
64
|
-
const qualityComponent = setQuality * 0.3;
|
|
65
|
-
const score = Math.round(volumeComponent + intensityComponent + qualityComponent);
|
|
66
|
-
return {
|
|
67
|
-
score: Math.min(100, Math.max(0, score)),
|
|
68
|
-
muscleScores,
|
|
69
|
-
};
|
|
70
|
-
};
|
|
71
|
-
exports.calculateExerciseScore = calculateExerciseScore;
|
|
72
|
-
const calculateAverageIntensity = (record, exercise, user) => {
|
|
73
|
-
const intensities = record.map((set, index) => {
|
|
74
|
-
let baseIntensity = 0;
|
|
75
|
-
if (set.type === "weight-reps") {
|
|
76
|
-
const weight = parseFloat(set.kg) || 0;
|
|
77
|
-
const reps = parseFloat(set.reps) || 0;
|
|
78
|
-
const userWeight = user.weightKg || 70;
|
|
79
|
-
const relativeLoad = weight / userWeight;
|
|
80
|
-
const repIntensity = getRepIntensity(reps);
|
|
81
|
-
baseIntensity = (relativeLoad * 40 + repIntensity * 60) / 100;
|
|
82
|
-
// RPE adjustment (only if provided and valid)
|
|
83
|
-
if (set.rpe && set.rpe !== "0") {
|
|
84
|
-
const rpe = parseFloat(set.rpe);
|
|
85
|
-
if (rpe > 0 && rpe <= 10) {
|
|
86
|
-
baseIntensity *= 0.5 + rpe / 20; // Scale from 0.55x to 1.0x
|
|
87
|
-
}
|
|
88
|
-
}
|
|
89
|
-
}
|
|
90
|
-
else if (set.type === "reps-only") {
|
|
91
|
-
const reps = parseFloat(set.reps) || 0;
|
|
92
|
-
baseIntensity = getRepIntensity(reps) / 100;
|
|
93
|
-
if (set.workDurationSecs) {
|
|
94
|
-
const tempo = (reps * 3) / set.workDurationSecs;
|
|
95
|
-
baseIntensity *= Math.min(1.2, Math.max(0.8, tempo));
|
|
96
|
-
}
|
|
97
|
-
// RPE adjustment for bodyweight exercises
|
|
98
|
-
if (set.rpe && set.rpe !== "0") {
|
|
99
|
-
const rpe = parseFloat(set.rpe);
|
|
100
|
-
if (rpe > 0 && rpe <= 10) {
|
|
101
|
-
baseIntensity *= 0.5 + rpe / 20;
|
|
102
|
-
}
|
|
103
|
-
}
|
|
104
|
-
}
|
|
105
|
-
else if (set.type === "duration") {
|
|
106
|
-
const duration = (0, time_util_1.mmssToSecs)(set.durationMmSs);
|
|
107
|
-
const weight = parseFloat(set.auxWeightKg) || 0;
|
|
108
|
-
baseIntensity = Math.min(100, duration * 1.5) / 100;
|
|
109
|
-
if (weight > 0) {
|
|
110
|
-
baseIntensity *= 1 + weight / 100;
|
|
111
|
-
}
|
|
112
|
-
}
|
|
113
|
-
else if (set.type === "cardio-machine" || set.type === "cardio-free") {
|
|
114
|
-
const duration = (0, time_util_1.mmssToSecs)(set.durationMmSs);
|
|
115
|
-
if (set.type === "cardio-machine") {
|
|
116
|
-
const avgSpeed = (parseFloat(set.speedMin) + parseFloat(set.speedMax)) / 2 || 0;
|
|
117
|
-
baseIntensity = Math.min(100, avgSpeed * 5.5) / 100;
|
|
118
|
-
}
|
|
119
|
-
else {
|
|
120
|
-
const distance = parseFloat(set.distance) || 0;
|
|
121
|
-
const speedKmh = duration > 0 ? distance / (duration / 3600) : 0;
|
|
122
|
-
baseIntensity = Math.min(100, speedKmh * 5.5) / 100;
|
|
123
|
-
}
|
|
124
|
-
}
|
|
125
|
-
// Apply fatigue factor for later sets (8% bonus per set)
|
|
126
|
-
const fatigueMultiplier = 1 + index * 0.08;
|
|
127
|
-
return Math.min(100, baseIntensity * fatigueMultiplier * 100);
|
|
128
|
-
});
|
|
129
|
-
const avgIntensity = intensities.reduce((a, b) => a + b, 0) / intensities.length;
|
|
130
|
-
const difficultyBonus = exercise.difficultyLevel * 5;
|
|
131
|
-
return Math.min(100, avgIntensity + difficultyBonus);
|
|
132
|
-
};
|
|
133
|
-
const calculateSetQuality = (record) => {
|
|
134
|
-
if (record.length === 0)
|
|
135
|
-
return 0;
|
|
136
|
-
if (record.length === 1)
|
|
137
|
-
return 75;
|
|
138
|
-
let qualityScore = 85; // Base score
|
|
139
|
-
// Check performance drop-off between sets
|
|
140
|
-
for (let i = 1; i < record.length; i++) {
|
|
141
|
-
const curr = record[i];
|
|
142
|
-
const prev = record[i - 1];
|
|
143
|
-
if (curr.type === "weight-reps" && prev.type === "weight-reps") {
|
|
144
|
-
const currWork = parseFloat(curr.reps) * parseFloat(curr.kg);
|
|
145
|
-
const prevWork = parseFloat(prev.reps) * parseFloat(prev.kg);
|
|
146
|
-
const dropOff = prevWork > 0 ? 1 - currWork / prevWork : 0;
|
|
147
|
-
if (dropOff < 0.1)
|
|
148
|
-
qualityScore += 5; // Excellent maintenance
|
|
149
|
-
else if (dropOff < 0.2)
|
|
150
|
-
qualityScore += 2; // Good maintenance
|
|
151
|
-
else if (dropOff > 0.3)
|
|
152
|
-
qualityScore -= 10; // Poor pacing
|
|
153
|
-
}
|
|
154
|
-
else if (curr.type === "reps-only" && prev.type === "reps-only") {
|
|
155
|
-
const currReps = parseFloat(curr.reps);
|
|
156
|
-
const prevReps = parseFloat(prev.reps);
|
|
157
|
-
const dropOff = prevReps > 0 ? 1 - currReps / prevReps : 0;
|
|
158
|
-
if (dropOff < 0.15)
|
|
159
|
-
qualityScore += 5;
|
|
160
|
-
else if (dropOff > 0.3)
|
|
161
|
-
qualityScore -= 8;
|
|
162
|
-
}
|
|
163
|
-
// Check effort consistency using either RIR or RPE (whichever is provided)
|
|
164
|
-
// RIR consistency check
|
|
165
|
-
if ("rir" in curr &&
|
|
166
|
-
"rir" in prev &&
|
|
167
|
-
curr.rir &&
|
|
168
|
-
prev.rir &&
|
|
169
|
-
curr.rir !== "0" &&
|
|
170
|
-
prev.rir !== "0") {
|
|
171
|
-
const currRir = parseFloat(curr.rir);
|
|
172
|
-
const prevRir = parseFloat(prev.rir);
|
|
173
|
-
if (!isNaN(currRir) && !isNaN(prevRir)) {
|
|
174
|
-
const rirDiff = Math.abs(currRir - prevRir);
|
|
175
|
-
if (rirDiff <= 1)
|
|
176
|
-
qualityScore += 3; // Consistent effort
|
|
177
|
-
}
|
|
178
|
-
}
|
|
179
|
-
// RPE consistency check (alternative to RIR)
|
|
180
|
-
else if (curr.rpe && prev.rpe && curr.rpe !== "0" && prev.rpe !== "0") {
|
|
181
|
-
const currRpe = parseFloat(curr.rpe);
|
|
182
|
-
const prevRpe = parseFloat(prev.rpe);
|
|
183
|
-
if (!isNaN(currRpe) && !isNaN(prevRpe)) {
|
|
184
|
-
const rpeDiff = Math.abs(currRpe - prevRpe);
|
|
185
|
-
if (rpeDiff <= 1)
|
|
186
|
-
qualityScore += 3; // Consistent effort
|
|
187
|
-
}
|
|
188
|
-
}
|
|
189
|
-
}
|
|
190
|
-
// Bonus for multiple quality sets
|
|
191
|
-
if (record.length >= 3)
|
|
192
|
-
qualityScore += 5;
|
|
193
|
-
if (record.length >= 4)
|
|
194
|
-
qualityScore += 3;
|
|
195
|
-
return Math.min(100, Math.max(0, qualityScore));
|
|
196
|
-
};
|
|
197
|
-
const calculateMuscleScores = (exercise, record, totalVolume, user) => {
|
|
198
|
-
const muscleScores = {};
|
|
199
|
-
const volumePerSet = totalVolume / record.length;
|
|
200
|
-
// Normalize volume by bodyweight if available
|
|
201
|
-
const normalizedVolumePerSet = user.weightKg
|
|
202
|
-
? volumePerSet / (user.weightKg * 10)
|
|
203
|
-
: volumePerSet / 500; // Fallback to absolute scale
|
|
204
|
-
// Primary muscles take 70% of the fatigue
|
|
205
|
-
exercise.primaryMuscles.forEach((muscle) => {
|
|
206
|
-
const baseFatigue = Math.min(100, normalizedVolumePerSet * 70);
|
|
207
|
-
const setMultiplier = Math.min(1.5, 1 + (record.length - 1) * 0.15);
|
|
208
|
-
muscleScores[muscle] = Math.min(100, baseFatigue * setMultiplier);
|
|
209
|
-
});
|
|
210
|
-
// Secondary muscles take 30% of the fatigue
|
|
211
|
-
exercise.secondaryMuscles.forEach((muscle) => {
|
|
212
|
-
const baseFatigue = Math.min(100, normalizedVolumePerSet * 30);
|
|
213
|
-
const setMultiplier = Math.min(1.3, 1 + (record.length - 1) * 0.1);
|
|
214
|
-
muscleScores[muscle] = Math.min(100, baseFatigue * setMultiplier);
|
|
215
|
-
});
|
|
216
|
-
return muscleScores;
|
|
217
|
-
};
|
|
218
|
-
const normalizeVolume = (totalVolume, exercise, user) => {
|
|
219
|
-
const userWeight = user.weightKg || 70;
|
|
220
|
-
const fitnessLevel = user.fitnessLevel || 2;
|
|
221
|
-
// Expected volume baseline
|
|
222
|
-
const baseExpected = userWeight * 15 * fitnessLevel;
|
|
223
|
-
const difficultyAdjustment = 1 + exercise.difficultyLevel / 8;
|
|
224
|
-
const expectedVolume = baseExpected * difficultyAdjustment;
|
|
225
|
-
// Sigmoid normalization for smooth scoring
|
|
226
|
-
const ratio = totalVolume / expectedVolume;
|
|
227
|
-
return 100 / (1 + Math.exp(-3 * (ratio - 0.8)));
|
|
228
|
-
};
|
|
229
|
-
const getRepIntensity = (reps) => {
|
|
230
|
-
if (reps <= 3)
|
|
231
|
-
return 95;
|
|
232
|
-
if (reps <= 5)
|
|
233
|
-
return 87;
|
|
234
|
-
if (reps <= 8)
|
|
235
|
-
return 80;
|
|
236
|
-
if (reps <= 12)
|
|
237
|
-
return 70;
|
|
238
|
-
if (reps <= 15)
|
|
239
|
-
return 65;
|
|
240
|
-
if (reps <= 20)
|
|
241
|
-
return 60;
|
|
242
|
-
return 50;
|
|
243
|
-
};
|
/package/dist/utils/scoringWorkout/{calculateMuscleFatiue.d.ts → calculateMuscleFatigue.d.ts}
RENAMED
|
File without changes
|
|
File without changes
|