@aneuhold/be-ts-db-lib 4.2.12 → 4.2.14
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/CHANGELOG.md +19 -0
- package/lib/repositories/workout/WorkoutExerciseRepository.d.ts +11 -1
- package/lib/repositories/workout/WorkoutExerciseRepository.d.ts.map +1 -1
- package/lib/repositories/workout/WorkoutExerciseRepository.js +181 -1
- package/lib/repositories/workout/WorkoutExerciseRepository.js.map +1 -1
- package/lib/repositories/workout/WorkoutExerciseRepository.ts +231 -2
- package/lib/repositories/workout/WorkoutMuscleGroupRepository.d.ts +10 -1
- package/lib/repositories/workout/WorkoutMuscleGroupRepository.d.ts.map +1 -1
- package/lib/repositories/workout/WorkoutMuscleGroupRepository.js +247 -1
- package/lib/repositories/workout/WorkoutMuscleGroupRepository.js.map +1 -1
- package/lib/repositories/workout/WorkoutMuscleGroupRepository.ts +285 -2
- package/lib/validators/workout/ExerciseValidator.d.ts.map +1 -1
- package/lib/validators/workout/ExerciseValidator.js +4 -4
- package/lib/validators/workout/ExerciseValidator.js.map +1 -1
- package/lib/validators/workout/ExerciseValidator.ts +6 -5
- package/lib/validators/workout/SessionExerciseValidator.d.ts.map +1 -1
- package/lib/validators/workout/SessionExerciseValidator.js +3 -8
- package/lib/validators/workout/SessionExerciseValidator.js.map +1 -1
- package/lib/validators/workout/SessionExerciseValidator.ts +5 -10
- package/package.json +5 -5
package/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,23 @@ All notable changes to this project will be documented in this file.
|
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
|
6
6
|
and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html).
|
|
7
7
|
|
|
8
|
+
## 🔖 [4.2.14] (2026-02-28)
|
|
9
|
+
|
|
10
|
+
### 🏗️ Changed
|
|
11
|
+
|
|
12
|
+
- Updated dependencies: now requires `@aneuhold/be-ts-lib@^3.1.1` and `@aneuhold/core-ts-db-lib@^4.1.13`.
|
|
13
|
+
|
|
14
|
+
## 🔖 [4.2.13] (2026-02-28)
|
|
15
|
+
|
|
16
|
+
### ✅ Added
|
|
17
|
+
|
|
18
|
+
- Added `WorkoutExerciseRepository.buildExerciseCTOsForUser()` to build `WorkoutExerciseCTO` objects for all of a user's exercises using parallel MongoDB aggregation pipelines (exercise + equipment + best calibration + best set; last accumulation session + first set).
|
|
19
|
+
- Added `WorkoutMuscleGroupRepository.buildMuscleGroupVolumeCTOsForUser()` to build `WorkoutMuscleGroupVolumeCTO` objects for all of a user's muscle groups using a volume aggregation pipeline across completed mesocycles.
|
|
20
|
+
|
|
21
|
+
### 🏗️ Changed
|
|
22
|
+
|
|
23
|
+
- Updated dependencies: now requires `@aneuhold/be-ts-lib@^3.1.0`, `@aneuhold/core-ts-db-lib@^4.1.12`, and `@aneuhold/core-ts-lib@^2.4.1`.
|
|
24
|
+
|
|
8
25
|
## 🔖 [4.2.12] (2026-02-23)
|
|
9
26
|
|
|
10
27
|
### 🏗️ Changed
|
|
@@ -291,6 +308,8 @@ Updated dependencies: now requires `@aneuhold/core-ts-db-lib@^3.0.0`, `@aneuhold
|
|
|
291
308
|
|
|
292
309
|
<!-- Link References -->
|
|
293
310
|
|
|
311
|
+
[4.2.14]: https://github.com/aneuhold/ts-libs/compare/be-ts-db-lib-v4.2.13...be-ts-db-lib-v4.2.14
|
|
312
|
+
[4.2.13]: https://github.com/aneuhold/ts-libs/compare/be-ts-db-lib-v4.2.12...be-ts-db-lib-v4.2.13
|
|
294
313
|
[4.2.12]: https://github.com/aneuhold/ts-libs/compare/be-ts-db-lib-v4.2.11...be-ts-db-lib-v4.2.12
|
|
295
314
|
[4.2.11]: https://github.com/aneuhold/ts-libs/compare/be-ts-db-lib-v4.2.10...be-ts-db-lib-v4.2.11
|
|
296
315
|
[4.2.10]: https://github.com/aneuhold/ts-libs/compare/be-ts-db-lib-v4.2.9...be-ts-db-lib-v4.2.10
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import type { User, WorkoutExercise } from '@aneuhold/core-ts-db-lib';
|
|
1
|
+
import type { User, WorkoutExercise, WorkoutExerciseCTO } from '@aneuhold/core-ts-db-lib';
|
|
2
|
+
import type { UUID } from 'crypto';
|
|
2
3
|
import type { RepoListeners } from '../../services/RepoSubscriptionService.js';
|
|
3
4
|
import WorkoutBaseWithUserIdRepository from './WorkoutBaseWithUserIdRepository.js';
|
|
4
5
|
/**
|
|
@@ -10,5 +11,14 @@ export default class WorkoutExerciseRepository extends WorkoutBaseWithUserIdRepo
|
|
|
10
11
|
static getListenersForUserRepo(): RepoListeners<User>;
|
|
11
12
|
protected setupSubscribers(): void;
|
|
12
13
|
static getRepo(): WorkoutExerciseRepository;
|
|
14
|
+
/**
|
|
15
|
+
* Builds {@link WorkoutExerciseCTO} objects for all exercises belonging to a user.
|
|
16
|
+
* Uses two parallel MongoDB aggregation pipelines:
|
|
17
|
+
* - Pipeline A: exercise + equipmentType + bestCalibration + bestSet
|
|
18
|
+
* - Pipeline B: lastSessionExercise + lastFirstSet (from most recent non-deload session)
|
|
19
|
+
*
|
|
20
|
+
* @param userId The user whose exercise CTOs to build.
|
|
21
|
+
*/
|
|
22
|
+
buildExerciseCTOsForUser(userId: UUID): Promise<WorkoutExerciseCTO[]>;
|
|
13
23
|
}
|
|
14
24
|
//# sourceMappingURL=WorkoutExerciseRepository.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"WorkoutExerciseRepository.d.ts","sourceRoot":"","sources":["../../../src/repositories/workout/WorkoutExerciseRepository.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,
|
|
1
|
+
{"version":3,"file":"WorkoutExerciseRepository.d.ts","sourceRoot":"","sources":["../../../src/repositories/workout/WorkoutExerciseRepository.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,IAAI,EAEJ,eAAe,EAEf,kBAAkB,EAGnB,MAAM,0BAA0B,CAAC;AAWlC,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,QAAQ,CAAC;AACnC,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,2CAA2C,CAAC;AAE/E,OAAO,+BAA+B,MAAM,sCAAsC,CAAC;AAGnF;;GAEG;AACH,MAAM,CAAC,OAAO,OAAO,yBAA0B,SAAQ,+BAA+B,CAAC,eAAe,CAAC;IACrG,OAAO,CAAC,MAAM,CAAC,iBAAiB,CAAC,CAA4B;IAE7D,OAAO;IAIP,MAAM,CAAC,uBAAuB,IAAI,aAAa,CAAC,IAAI,CAAC;IAYrD,SAAS,CAAC,gBAAgB,IAAI,IAAI;WAIpB,OAAO,IAAI,yBAAyB;IAOlD;;;;;;;OAOG;IACG,wBAAwB,CAAC,MAAM,EAAE,IAAI,GAAG,OAAO,CAAC,kBAAkB,EAAE,CAAC;CA0M5E"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { WorkoutExercise_docType } from '@aneuhold/core-ts-db-lib';
|
|
1
|
+
import { WorkoutEquipmentType_docType, WorkoutExercise_docType, WorkoutExerciseCalibration_docType, WorkoutExerciseCalibrationService, WorkoutExerciseCTOSchema, WorkoutSession_docType, WorkoutSessionExercise_docType, WorkoutSet_docType } from '@aneuhold/core-ts-db-lib';
|
|
2
2
|
import WorkoutExerciseValidator from '../../validators/workout/ExerciseValidator.js';
|
|
3
3
|
import WorkoutBaseWithUserIdRepository from './WorkoutBaseWithUserIdRepository.js';
|
|
4
4
|
import WorkoutExerciseCalibrationRepository from './WorkoutExerciseCalibrationRepository.js';
|
|
@@ -30,5 +30,185 @@ export default class WorkoutExerciseRepository extends WorkoutBaseWithUserIdRepo
|
|
|
30
30
|
}
|
|
31
31
|
return WorkoutExerciseRepository.singletonInstance;
|
|
32
32
|
}
|
|
33
|
+
/**
|
|
34
|
+
* Builds {@link WorkoutExerciseCTO} objects for all exercises belonging to a user.
|
|
35
|
+
* Uses two parallel MongoDB aggregation pipelines:
|
|
36
|
+
* - Pipeline A: exercise + equipmentType + bestCalibration + bestSet
|
|
37
|
+
* - Pipeline B: lastSessionExercise + lastFirstSet (from most recent non-deload session)
|
|
38
|
+
*
|
|
39
|
+
* @param userId The user whose exercise CTOs to build.
|
|
40
|
+
*/
|
|
41
|
+
async buildExerciseCTOsForUser(userId) {
|
|
42
|
+
const collection = await this.getCollection();
|
|
43
|
+
const collName = this.collectionName;
|
|
44
|
+
// Pipeline A: Exercise base + equipmentType + bestCalibration + bestSet
|
|
45
|
+
const pipelineAPromise = collection
|
|
46
|
+
.aggregate([
|
|
47
|
+
// Start with all exercises for this user
|
|
48
|
+
{ $match: { docType: WorkoutExercise_docType, userId } },
|
|
49
|
+
// Join equipment type by workoutEquipmentTypeId
|
|
50
|
+
{
|
|
51
|
+
$lookup: {
|
|
52
|
+
from: collName,
|
|
53
|
+
let: { equipId: '$workoutEquipmentTypeId' },
|
|
54
|
+
pipeline: [
|
|
55
|
+
{
|
|
56
|
+
$match: {
|
|
57
|
+
$expr: { $eq: ['$_id', '$$equipId'] },
|
|
58
|
+
docType: WorkoutEquipmentType_docType
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
],
|
|
62
|
+
as: '_equipArr'
|
|
63
|
+
}
|
|
64
|
+
},
|
|
65
|
+
// Find the best calibration by highest estimated 1RM
|
|
66
|
+
{
|
|
67
|
+
$lookup: {
|
|
68
|
+
from: collName,
|
|
69
|
+
let: { exId: '$_id' },
|
|
70
|
+
pipeline: [
|
|
71
|
+
{
|
|
72
|
+
$match: {
|
|
73
|
+
$expr: { $eq: ['$workoutExerciseId', '$$exId'] },
|
|
74
|
+
docType: WorkoutExerciseCalibration_docType
|
|
75
|
+
}
|
|
76
|
+
},
|
|
77
|
+
{
|
|
78
|
+
$addFields: {
|
|
79
|
+
_1rm: WorkoutExerciseCalibrationService.get1RMMongoExpr('$weight', '$reps')
|
|
80
|
+
}
|
|
81
|
+
},
|
|
82
|
+
{ $sort: { _1rm: -1 } },
|
|
83
|
+
{ $limit: 1 },
|
|
84
|
+
{ $project: { _1rm: 0 } }
|
|
85
|
+
],
|
|
86
|
+
as: '_bestCalArr'
|
|
87
|
+
}
|
|
88
|
+
},
|
|
89
|
+
// Find the best completed set by highest estimated 1RM
|
|
90
|
+
{
|
|
91
|
+
$lookup: {
|
|
92
|
+
from: collName,
|
|
93
|
+
let: { exId: '$_id' },
|
|
94
|
+
pipeline: [
|
|
95
|
+
{
|
|
96
|
+
$match: {
|
|
97
|
+
$expr: { $eq: ['$workoutExerciseId', '$$exId'] },
|
|
98
|
+
docType: WorkoutSet_docType,
|
|
99
|
+
actualWeight: { $ne: null },
|
|
100
|
+
actualReps: { $ne: null }
|
|
101
|
+
}
|
|
102
|
+
},
|
|
103
|
+
{
|
|
104
|
+
$addFields: {
|
|
105
|
+
_1rm: WorkoutExerciseCalibrationService.get1RMMongoExpr('$actualWeight', '$actualReps')
|
|
106
|
+
}
|
|
107
|
+
},
|
|
108
|
+
{ $sort: { _1rm: -1 } },
|
|
109
|
+
{ $limit: 1 },
|
|
110
|
+
{ $project: { _1rm: 0 } }
|
|
111
|
+
],
|
|
112
|
+
as: '_bestSetArr'
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
])
|
|
116
|
+
.toArray();
|
|
117
|
+
// Pipeline B: lastSessionExercise + lastFirstSet per exercise
|
|
118
|
+
const pipelineBPromise = collection
|
|
119
|
+
.aggregate([
|
|
120
|
+
// Start with all completed sessions for this user
|
|
121
|
+
{ $match: { docType: WorkoutSession_docType, userId, complete: true } },
|
|
122
|
+
// Most recent sessions first
|
|
123
|
+
{ $sort: { startTime: -1 } },
|
|
124
|
+
// Join each session's session exercises
|
|
125
|
+
{
|
|
126
|
+
$lookup: {
|
|
127
|
+
from: collName,
|
|
128
|
+
let: { sessId: '$_id' },
|
|
129
|
+
pipeline: [
|
|
130
|
+
{
|
|
131
|
+
$match: {
|
|
132
|
+
$expr: { $eq: ['$workoutSessionId', '$$sessId'] },
|
|
133
|
+
docType: WorkoutSessionExercise_docType
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
],
|
|
137
|
+
as: '_se'
|
|
138
|
+
}
|
|
139
|
+
},
|
|
140
|
+
// One row per session exercise (unwind coalescence optimization)
|
|
141
|
+
{ $unwind: '$_se' },
|
|
142
|
+
// Check for non-deload sets (plannedRir is not null)
|
|
143
|
+
{
|
|
144
|
+
$lookup: {
|
|
145
|
+
from: collName,
|
|
146
|
+
let: { seId: '$_se._id' },
|
|
147
|
+
pipeline: [
|
|
148
|
+
{
|
|
149
|
+
$match: {
|
|
150
|
+
$expr: { $eq: ['$workoutSessionExerciseId', '$$seId'] },
|
|
151
|
+
docType: WorkoutSet_docType,
|
|
152
|
+
plannedRir: { $ne: null }
|
|
153
|
+
}
|
|
154
|
+
},
|
|
155
|
+
{ $limit: 1 }
|
|
156
|
+
],
|
|
157
|
+
as: '_nonDeload'
|
|
158
|
+
}
|
|
159
|
+
},
|
|
160
|
+
// Keep only session exercises with at least one non-deload set
|
|
161
|
+
{ $match: { '_nonDeload.0': { $exists: true } } },
|
|
162
|
+
// Per exercise, keep only the most recent session exercise ($first after sort)
|
|
163
|
+
{
|
|
164
|
+
$group: {
|
|
165
|
+
_id: '$_se.workoutExerciseId',
|
|
166
|
+
lastSessionExercise: { $first: '$_se' }
|
|
167
|
+
}
|
|
168
|
+
},
|
|
169
|
+
// Join the first set from that session exercise's setOrder
|
|
170
|
+
{
|
|
171
|
+
$lookup: {
|
|
172
|
+
from: collName,
|
|
173
|
+
let: { firstSetId: { $arrayElemAt: ['$lastSessionExercise.setOrder', 0] } },
|
|
174
|
+
pipeline: [
|
|
175
|
+
{
|
|
176
|
+
$match: {
|
|
177
|
+
$expr: { $eq: ['$_id', '$$firstSetId'] },
|
|
178
|
+
docType: WorkoutSet_docType
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
],
|
|
182
|
+
as: '_lastFirstSetArr'
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
])
|
|
186
|
+
.toArray();
|
|
187
|
+
const [rawExercises, rawLastSessions] = await Promise.all([pipelineAPromise, pipelineBPromise]);
|
|
188
|
+
// Build lookup map from Pipeline B
|
|
189
|
+
const lastSessionMap = new Map();
|
|
190
|
+
for (const row of rawLastSessions) {
|
|
191
|
+
lastSessionMap.set(row._id, {
|
|
192
|
+
lastSessionExercise: row.lastSessionExercise,
|
|
193
|
+
lastFirstSet: row._lastFirstSetArr[0] ?? null
|
|
194
|
+
});
|
|
195
|
+
}
|
|
196
|
+
// Assemble and validate CTOs
|
|
197
|
+
return rawExercises.map((raw) => {
|
|
198
|
+
const { _equipArr, _bestCalArr, _bestSetArr, ...exerciseFields } = raw;
|
|
199
|
+
if (_equipArr.length === 0) {
|
|
200
|
+
throw new Error(`Equipment type not found for exercise ${raw._id}`);
|
|
201
|
+
}
|
|
202
|
+
const lastData = lastSessionMap.get(raw._id);
|
|
203
|
+
return WorkoutExerciseCTOSchema.parse({
|
|
204
|
+
...exerciseFields,
|
|
205
|
+
equipmentType: _equipArr[0],
|
|
206
|
+
bestCalibration: _bestCalArr[0] ?? null,
|
|
207
|
+
bestSet: _bestSetArr[0] ?? null,
|
|
208
|
+
lastSessionExercise: lastData?.lastSessionExercise ?? null,
|
|
209
|
+
lastFirstSet: lastData?.lastFirstSet ?? null
|
|
210
|
+
});
|
|
211
|
+
});
|
|
212
|
+
}
|
|
33
213
|
}
|
|
34
214
|
//# sourceMappingURL=WorkoutExerciseRepository.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"WorkoutExerciseRepository.js","sourceRoot":"","sources":["../../../src/repositories/workout/WorkoutExerciseRepository.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"WorkoutExerciseRepository.js","sourceRoot":"","sources":["../../../src/repositories/workout/WorkoutExerciseRepository.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,5 +1,23 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
|
|
1
|
+
import type {
|
|
2
|
+
User,
|
|
3
|
+
WorkoutEquipmentType,
|
|
4
|
+
WorkoutExercise,
|
|
5
|
+
WorkoutExerciseCalibration,
|
|
6
|
+
WorkoutExerciseCTO,
|
|
7
|
+
WorkoutSessionExercise,
|
|
8
|
+
WorkoutSet
|
|
9
|
+
} from '@aneuhold/core-ts-db-lib';
|
|
10
|
+
import {
|
|
11
|
+
WorkoutEquipmentType_docType,
|
|
12
|
+
WorkoutExercise_docType,
|
|
13
|
+
WorkoutExerciseCalibration_docType,
|
|
14
|
+
WorkoutExerciseCalibrationService,
|
|
15
|
+
WorkoutExerciseCTOSchema,
|
|
16
|
+
WorkoutSession_docType,
|
|
17
|
+
WorkoutSessionExercise_docType,
|
|
18
|
+
WorkoutSet_docType
|
|
19
|
+
} from '@aneuhold/core-ts-db-lib';
|
|
20
|
+
import type { UUID } from 'crypto';
|
|
3
21
|
import type { RepoListeners } from '../../services/RepoSubscriptionService.js';
|
|
4
22
|
import WorkoutExerciseValidator from '../../validators/workout/ExerciseValidator.js';
|
|
5
23
|
import WorkoutBaseWithUserIdRepository from './WorkoutBaseWithUserIdRepository.js';
|
|
@@ -37,4 +55,215 @@ export default class WorkoutExerciseRepository extends WorkoutBaseWithUserIdRepo
|
|
|
37
55
|
}
|
|
38
56
|
return WorkoutExerciseRepository.singletonInstance;
|
|
39
57
|
}
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Builds {@link WorkoutExerciseCTO} objects for all exercises belonging to a user.
|
|
61
|
+
* Uses two parallel MongoDB aggregation pipelines:
|
|
62
|
+
* - Pipeline A: exercise + equipmentType + bestCalibration + bestSet
|
|
63
|
+
* - Pipeline B: lastSessionExercise + lastFirstSet (from most recent non-deload session)
|
|
64
|
+
*
|
|
65
|
+
* @param userId The user whose exercise CTOs to build.
|
|
66
|
+
*/
|
|
67
|
+
async buildExerciseCTOsForUser(userId: UUID): Promise<WorkoutExerciseCTO[]> {
|
|
68
|
+
/** Raw shape of Pipeline A results. */
|
|
69
|
+
interface PipelineARow extends WorkoutExercise {
|
|
70
|
+
_equipArr: WorkoutEquipmentType[];
|
|
71
|
+
_bestCalArr: WorkoutExerciseCalibration[];
|
|
72
|
+
_bestSetArr: WorkoutSet[];
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/** Raw shape of Pipeline B results. */
|
|
76
|
+
interface PipelineBRow {
|
|
77
|
+
_id: string;
|
|
78
|
+
lastSessionExercise: WorkoutSessionExercise;
|
|
79
|
+
_lastFirstSetArr: WorkoutSet[];
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
const collection = await this.getCollection();
|
|
83
|
+
const collName = this.collectionName;
|
|
84
|
+
|
|
85
|
+
// Pipeline A: Exercise base + equipmentType + bestCalibration + bestSet
|
|
86
|
+
const pipelineAPromise = collection
|
|
87
|
+
.aggregate<PipelineARow>([
|
|
88
|
+
// Start with all exercises for this user
|
|
89
|
+
{ $match: { docType: WorkoutExercise_docType, userId } },
|
|
90
|
+
// Join equipment type by workoutEquipmentTypeId
|
|
91
|
+
{
|
|
92
|
+
$lookup: {
|
|
93
|
+
from: collName,
|
|
94
|
+
let: { equipId: '$workoutEquipmentTypeId' },
|
|
95
|
+
pipeline: [
|
|
96
|
+
{
|
|
97
|
+
$match: {
|
|
98
|
+
$expr: { $eq: ['$_id', '$$equipId'] },
|
|
99
|
+
docType: WorkoutEquipmentType_docType
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
],
|
|
103
|
+
as: '_equipArr'
|
|
104
|
+
}
|
|
105
|
+
},
|
|
106
|
+
// Find the best calibration by highest estimated 1RM
|
|
107
|
+
{
|
|
108
|
+
$lookup: {
|
|
109
|
+
from: collName,
|
|
110
|
+
let: { exId: '$_id' },
|
|
111
|
+
pipeline: [
|
|
112
|
+
{
|
|
113
|
+
$match: {
|
|
114
|
+
$expr: { $eq: ['$workoutExerciseId', '$$exId'] },
|
|
115
|
+
docType: WorkoutExerciseCalibration_docType
|
|
116
|
+
}
|
|
117
|
+
},
|
|
118
|
+
{
|
|
119
|
+
$addFields: {
|
|
120
|
+
_1rm: WorkoutExerciseCalibrationService.get1RMMongoExpr('$weight', '$reps')
|
|
121
|
+
}
|
|
122
|
+
},
|
|
123
|
+
{ $sort: { _1rm: -1 } },
|
|
124
|
+
{ $limit: 1 },
|
|
125
|
+
{ $project: { _1rm: 0 } }
|
|
126
|
+
],
|
|
127
|
+
as: '_bestCalArr'
|
|
128
|
+
}
|
|
129
|
+
},
|
|
130
|
+
// Find the best completed set by highest estimated 1RM
|
|
131
|
+
{
|
|
132
|
+
$lookup: {
|
|
133
|
+
from: collName,
|
|
134
|
+
let: { exId: '$_id' },
|
|
135
|
+
pipeline: [
|
|
136
|
+
{
|
|
137
|
+
$match: {
|
|
138
|
+
$expr: { $eq: ['$workoutExerciseId', '$$exId'] },
|
|
139
|
+
docType: WorkoutSet_docType,
|
|
140
|
+
actualWeight: { $ne: null },
|
|
141
|
+
actualReps: { $ne: null }
|
|
142
|
+
}
|
|
143
|
+
},
|
|
144
|
+
{
|
|
145
|
+
$addFields: {
|
|
146
|
+
_1rm: WorkoutExerciseCalibrationService.get1RMMongoExpr(
|
|
147
|
+
'$actualWeight',
|
|
148
|
+
'$actualReps'
|
|
149
|
+
)
|
|
150
|
+
}
|
|
151
|
+
},
|
|
152
|
+
{ $sort: { _1rm: -1 } },
|
|
153
|
+
{ $limit: 1 },
|
|
154
|
+
{ $project: { _1rm: 0 } }
|
|
155
|
+
],
|
|
156
|
+
as: '_bestSetArr'
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
])
|
|
160
|
+
.toArray();
|
|
161
|
+
|
|
162
|
+
// Pipeline B: lastSessionExercise + lastFirstSet per exercise
|
|
163
|
+
const pipelineBPromise = collection
|
|
164
|
+
.aggregate<PipelineBRow>([
|
|
165
|
+
// Start with all completed sessions for this user
|
|
166
|
+
{ $match: { docType: WorkoutSession_docType, userId, complete: true } },
|
|
167
|
+
// Most recent sessions first
|
|
168
|
+
{ $sort: { startTime: -1 } },
|
|
169
|
+
// Join each session's session exercises
|
|
170
|
+
{
|
|
171
|
+
$lookup: {
|
|
172
|
+
from: collName,
|
|
173
|
+
let: { sessId: '$_id' },
|
|
174
|
+
pipeline: [
|
|
175
|
+
{
|
|
176
|
+
$match: {
|
|
177
|
+
$expr: { $eq: ['$workoutSessionId', '$$sessId'] },
|
|
178
|
+
docType: WorkoutSessionExercise_docType
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
],
|
|
182
|
+
as: '_se'
|
|
183
|
+
}
|
|
184
|
+
},
|
|
185
|
+
// One row per session exercise (unwind coalescence optimization)
|
|
186
|
+
{ $unwind: '$_se' },
|
|
187
|
+
// Check for non-deload sets (plannedRir is not null)
|
|
188
|
+
{
|
|
189
|
+
$lookup: {
|
|
190
|
+
from: collName,
|
|
191
|
+
let: { seId: '$_se._id' },
|
|
192
|
+
pipeline: [
|
|
193
|
+
{
|
|
194
|
+
$match: {
|
|
195
|
+
$expr: { $eq: ['$workoutSessionExerciseId', '$$seId'] },
|
|
196
|
+
docType: WorkoutSet_docType,
|
|
197
|
+
plannedRir: { $ne: null }
|
|
198
|
+
}
|
|
199
|
+
},
|
|
200
|
+
{ $limit: 1 }
|
|
201
|
+
],
|
|
202
|
+
as: '_nonDeload'
|
|
203
|
+
}
|
|
204
|
+
},
|
|
205
|
+
// Keep only session exercises with at least one non-deload set
|
|
206
|
+
{ $match: { '_nonDeload.0': { $exists: true } } },
|
|
207
|
+
// Per exercise, keep only the most recent session exercise ($first after sort)
|
|
208
|
+
{
|
|
209
|
+
$group: {
|
|
210
|
+
_id: '$_se.workoutExerciseId',
|
|
211
|
+
lastSessionExercise: { $first: '$_se' }
|
|
212
|
+
}
|
|
213
|
+
},
|
|
214
|
+
// Join the first set from that session exercise's setOrder
|
|
215
|
+
{
|
|
216
|
+
$lookup: {
|
|
217
|
+
from: collName,
|
|
218
|
+
let: { firstSetId: { $arrayElemAt: ['$lastSessionExercise.setOrder', 0] } },
|
|
219
|
+
pipeline: [
|
|
220
|
+
{
|
|
221
|
+
$match: {
|
|
222
|
+
$expr: { $eq: ['$_id', '$$firstSetId'] },
|
|
223
|
+
docType: WorkoutSet_docType
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
],
|
|
227
|
+
as: '_lastFirstSetArr'
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
])
|
|
231
|
+
.toArray();
|
|
232
|
+
|
|
233
|
+
const [rawExercises, rawLastSessions] = await Promise.all([pipelineAPromise, pipelineBPromise]);
|
|
234
|
+
|
|
235
|
+
// Build lookup map from Pipeline B
|
|
236
|
+
const lastSessionMap = new Map<
|
|
237
|
+
string,
|
|
238
|
+
{
|
|
239
|
+
lastSessionExercise: WorkoutSessionExercise;
|
|
240
|
+
lastFirstSet: WorkoutSet | null;
|
|
241
|
+
}
|
|
242
|
+
>();
|
|
243
|
+
for (const row of rawLastSessions) {
|
|
244
|
+
lastSessionMap.set(row._id, {
|
|
245
|
+
lastSessionExercise: row.lastSessionExercise,
|
|
246
|
+
lastFirstSet: row._lastFirstSetArr[0] ?? null
|
|
247
|
+
});
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
// Assemble and validate CTOs
|
|
251
|
+
return rawExercises.map((raw) => {
|
|
252
|
+
const { _equipArr, _bestCalArr, _bestSetArr, ...exerciseFields } = raw;
|
|
253
|
+
if (_equipArr.length === 0) {
|
|
254
|
+
throw new Error(`Equipment type not found for exercise ${raw._id}`);
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
const lastData = lastSessionMap.get(raw._id as string);
|
|
258
|
+
|
|
259
|
+
return WorkoutExerciseCTOSchema.parse({
|
|
260
|
+
...exerciseFields,
|
|
261
|
+
equipmentType: _equipArr[0],
|
|
262
|
+
bestCalibration: _bestCalArr[0] ?? null,
|
|
263
|
+
bestSet: _bestSetArr[0] ?? null,
|
|
264
|
+
lastSessionExercise: lastData?.lastSessionExercise ?? null,
|
|
265
|
+
lastFirstSet: lastData?.lastFirstSet ?? null
|
|
266
|
+
});
|
|
267
|
+
});
|
|
268
|
+
}
|
|
40
269
|
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import type { User, WorkoutMuscleGroup } from '@aneuhold/core-ts-db-lib';
|
|
1
|
+
import type { User, WorkoutMuscleGroup, WorkoutMuscleGroupVolumeCTO } from '@aneuhold/core-ts-db-lib';
|
|
2
|
+
import type { UUID } from 'crypto';
|
|
2
3
|
import type { RepoListeners } from '../../services/RepoSubscriptionService.js';
|
|
3
4
|
import WorkoutBaseWithUserIdRepository from './WorkoutBaseWithUserIdRepository.js';
|
|
4
5
|
/**
|
|
@@ -16,5 +17,13 @@ export default class WorkoutMuscleGroupRepository extends WorkoutBaseWithUserIdR
|
|
|
16
17
|
* Gets the singleton instance of the {@link WorkoutMuscleGroupRepository}.
|
|
17
18
|
*/
|
|
18
19
|
static getRepo(): WorkoutMuscleGroupRepository;
|
|
20
|
+
/**
|
|
21
|
+
* Builds {@link WorkoutMuscleGroupVolumeCTO} objects for all muscle groups
|
|
22
|
+
* belonging to a user. Fetches muscle groups directly, then runs an
|
|
23
|
+
* aggregation pipeline for volume history across completed mesocycles.
|
|
24
|
+
*
|
|
25
|
+
* @param userId The user whose muscle group volume CTOs to build.
|
|
26
|
+
*/
|
|
27
|
+
buildMuscleGroupVolumeCTOsForUser(userId: UUID): Promise<WorkoutMuscleGroupVolumeCTO[]>;
|
|
19
28
|
}
|
|
20
29
|
//# sourceMappingURL=WorkoutMuscleGroupRepository.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"WorkoutMuscleGroupRepository.d.ts","sourceRoot":"","sources":["../../../src/repositories/workout/WorkoutMuscleGroupRepository.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,
|
|
1
|
+
{"version":3,"file":"WorkoutMuscleGroupRepository.d.ts","sourceRoot":"","sources":["../../../src/repositories/workout/WorkoutMuscleGroupRepository.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,IAAI,EACJ,kBAAkB,EAClB,2BAA2B,EAC5B,MAAM,0BAA0B,CAAC;AAUlC,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,QAAQ,CAAC;AACnC,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,2CAA2C,CAAC;AAE/E,OAAO,+BAA+B,MAAM,sCAAsC,CAAC;AAEnF;;GAEG;AACH,MAAM,CAAC,OAAO,OAAO,4BAA6B,SAAQ,+BAA+B,CAAC,kBAAkB,CAAC;IAC3G,OAAO,CAAC,MAAM,CAAC,iBAAiB,CAAC,CAA+B;IAEhE;;OAEG;IACH,OAAO;IAIP,MAAM,CAAC,uBAAuB,IAAI,aAAa,CAAC,IAAI,CAAC;IAYrD,SAAS,CAAC,gBAAgB,IAAI,IAAI;IAElC;;OAEG;WACW,OAAO,IAAI,4BAA4B;IAOrD;;;;;;OAMG;IACG,iCAAiC,CAAC,MAAM,EAAE,IAAI,GAAG,OAAO,CAAC,2BAA2B,EAAE,CAAC;CAsQ9F"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { WorkoutMuscleGroup_docType } from '@aneuhold/core-ts-db-lib';
|
|
1
|
+
import { WorkoutExercise_docType, WorkoutMesocycle_docType, WorkoutMicrocycle_docType, WorkoutMuscleGroup_docType, WorkoutMuscleGroupVolumeCTOSchema, WorkoutSession_docType, WorkoutSessionExercise_docType } from '@aneuhold/core-ts-db-lib';
|
|
2
2
|
import WorkoutMuscleGroupValidator from '../../validators/workout/MuscleGroupValidator.js';
|
|
3
3
|
import WorkoutBaseWithUserIdRepository from './WorkoutBaseWithUserIdRepository.js';
|
|
4
4
|
/**
|
|
@@ -33,5 +33,251 @@ export default class WorkoutMuscleGroupRepository extends WorkoutBaseWithUserIdR
|
|
|
33
33
|
}
|
|
34
34
|
return WorkoutMuscleGroupRepository.singletonInstance;
|
|
35
35
|
}
|
|
36
|
+
/**
|
|
37
|
+
* Builds {@link WorkoutMuscleGroupVolumeCTO} objects for all muscle groups
|
|
38
|
+
* belonging to a user. Fetches muscle groups directly, then runs an
|
|
39
|
+
* aggregation pipeline for volume history across completed mesocycles.
|
|
40
|
+
*
|
|
41
|
+
* @param userId The user whose muscle group volume CTOs to build.
|
|
42
|
+
*/
|
|
43
|
+
async buildMuscleGroupVolumeCTOsForUser(userId) {
|
|
44
|
+
const collection = await this.getCollection();
|
|
45
|
+
const collName = this.collectionName;
|
|
46
|
+
// Part A: fetch all muscle groups
|
|
47
|
+
const muscleGroupsPromise = this.getAllForUser(userId);
|
|
48
|
+
// Part B: aggregation pipeline for volume data
|
|
49
|
+
const volumePromise = collection
|
|
50
|
+
.aggregate([
|
|
51
|
+
// Start with completed mesocycles for this user
|
|
52
|
+
{
|
|
53
|
+
$match: {
|
|
54
|
+
docType: WorkoutMesocycle_docType,
|
|
55
|
+
userId,
|
|
56
|
+
completedDate: { $type: 'date' }
|
|
57
|
+
}
|
|
58
|
+
},
|
|
59
|
+
// Most recent first
|
|
60
|
+
{ $sort: { completedDate: -1 } },
|
|
61
|
+
// Limit to 10 most recent
|
|
62
|
+
{ $limit: 10 },
|
|
63
|
+
// Join microcycles, sorted by start date
|
|
64
|
+
{
|
|
65
|
+
$lookup: {
|
|
66
|
+
from: collName,
|
|
67
|
+
let: { mesoId: '$_id' },
|
|
68
|
+
pipeline: [
|
|
69
|
+
{
|
|
70
|
+
$match: {
|
|
71
|
+
$expr: { $eq: ['$workoutMesocycleId', '$$mesoId'] },
|
|
72
|
+
docType: WorkoutMicrocycle_docType
|
|
73
|
+
}
|
|
74
|
+
},
|
|
75
|
+
{ $sort: { startDate: 1 } }
|
|
76
|
+
],
|
|
77
|
+
as: '_microcycles'
|
|
78
|
+
}
|
|
79
|
+
},
|
|
80
|
+
// One row per microcycle, tracking index for starting/peak logic
|
|
81
|
+
{ $unwind: { path: '$_microcycles', includeArrayIndex: '_microIdx' } },
|
|
82
|
+
// Join sessions for each microcycle
|
|
83
|
+
{
|
|
84
|
+
$lookup: {
|
|
85
|
+
from: collName,
|
|
86
|
+
let: { microId: '$_microcycles._id' },
|
|
87
|
+
pipeline: [
|
|
88
|
+
{
|
|
89
|
+
$match: {
|
|
90
|
+
$expr: { $eq: ['$workoutMicrocycleId', '$$microId'] },
|
|
91
|
+
docType: WorkoutSession_docType
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
],
|
|
95
|
+
as: '_sessions'
|
|
96
|
+
}
|
|
97
|
+
},
|
|
98
|
+
// One row per session (drop empty microcycles)
|
|
99
|
+
{ $unwind: { path: '$_sessions', preserveNullAndEmptyArrays: false } },
|
|
100
|
+
// Join session exercises
|
|
101
|
+
{
|
|
102
|
+
$lookup: {
|
|
103
|
+
from: collName,
|
|
104
|
+
let: { sessId: '$_sessions._id' },
|
|
105
|
+
pipeline: [
|
|
106
|
+
{
|
|
107
|
+
$match: {
|
|
108
|
+
$expr: { $eq: ['$workoutSessionId', '$$sessId'] },
|
|
109
|
+
docType: WorkoutSessionExercise_docType
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
],
|
|
113
|
+
as: '_se'
|
|
114
|
+
}
|
|
115
|
+
},
|
|
116
|
+
// One row per session exercise
|
|
117
|
+
{ $unwind: '$_se' },
|
|
118
|
+
// Join exercise doc to get muscle group arrays
|
|
119
|
+
{
|
|
120
|
+
$lookup: {
|
|
121
|
+
from: collName,
|
|
122
|
+
let: { exId: '$_se.workoutExerciseId' },
|
|
123
|
+
pipeline: [
|
|
124
|
+
{
|
|
125
|
+
$match: {
|
|
126
|
+
$expr: { $eq: ['$_id', '$$exId'] },
|
|
127
|
+
docType: WorkoutExercise_docType
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
],
|
|
131
|
+
as: '_exercise'
|
|
132
|
+
}
|
|
133
|
+
},
|
|
134
|
+
// One row per exercise
|
|
135
|
+
{ $unwind: '$_exercise' },
|
|
136
|
+
// Combine primary + secondary muscle group IDs
|
|
137
|
+
{
|
|
138
|
+
$addFields: {
|
|
139
|
+
_muscleGroups: {
|
|
140
|
+
$concatArrays: [
|
|
141
|
+
{ $ifNull: ['$_exercise.primaryMuscleGroups', []] },
|
|
142
|
+
{ $ifNull: ['$_exercise.secondaryMuscleGroups', []] }
|
|
143
|
+
]
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
},
|
|
147
|
+
// One row per (mesocycle, microcycle, session exercise, muscle group)
|
|
148
|
+
{ $unwind: '$_muscleGroups' },
|
|
149
|
+
// First group: per (mesocycle, muscleGroup, microcycleIndex) — count sets, sum metrics
|
|
150
|
+
{
|
|
151
|
+
$group: {
|
|
152
|
+
_id: {
|
|
153
|
+
mesocycleId: '$_id',
|
|
154
|
+
muscleGroupId: '$_muscleGroups',
|
|
155
|
+
microIdx: '$_microIdx'
|
|
156
|
+
},
|
|
157
|
+
setCount: { $sum: { $size: { $ifNull: ['$_se.setOrder', []] } } },
|
|
158
|
+
rsmSum: {
|
|
159
|
+
$sum: {
|
|
160
|
+
$cond: [
|
|
161
|
+
{
|
|
162
|
+
$and: [
|
|
163
|
+
{ $ne: [{ $ifNull: ['$_se.rsm.mindMuscleConnection', null] }, null] },
|
|
164
|
+
{ $ne: [{ $ifNull: ['$_se.rsm.pump', null] }, null] },
|
|
165
|
+
{ $ne: [{ $ifNull: ['$_se.rsm.disruption', null] }, null] }
|
|
166
|
+
]
|
|
167
|
+
},
|
|
168
|
+
{
|
|
169
|
+
$add: ['$_se.rsm.mindMuscleConnection', '$_se.rsm.pump', '$_se.rsm.disruption']
|
|
170
|
+
},
|
|
171
|
+
0
|
|
172
|
+
]
|
|
173
|
+
}
|
|
174
|
+
},
|
|
175
|
+
rsmCount: {
|
|
176
|
+
$sum: {
|
|
177
|
+
$cond: [
|
|
178
|
+
{
|
|
179
|
+
$and: [
|
|
180
|
+
{ $ne: [{ $ifNull: ['$_se.rsm.mindMuscleConnection', null] }, null] },
|
|
181
|
+
{ $ne: [{ $ifNull: ['$_se.rsm.pump', null] }, null] },
|
|
182
|
+
{ $ne: [{ $ifNull: ['$_se.rsm.disruption', null] }, null] }
|
|
183
|
+
]
|
|
184
|
+
},
|
|
185
|
+
1,
|
|
186
|
+
0
|
|
187
|
+
]
|
|
188
|
+
}
|
|
189
|
+
},
|
|
190
|
+
sorenessSum: {
|
|
191
|
+
$sum: {
|
|
192
|
+
$cond: [
|
|
193
|
+
{ $ne: [{ $ifNull: ['$_se.sorenessScore', null] }, null] },
|
|
194
|
+
'$_se.sorenessScore',
|
|
195
|
+
0
|
|
196
|
+
]
|
|
197
|
+
}
|
|
198
|
+
},
|
|
199
|
+
sorenessCount: {
|
|
200
|
+
$sum: {
|
|
201
|
+
$cond: [{ $ne: [{ $ifNull: ['$_se.sorenessScore', null] }, null] }, 1, 0]
|
|
202
|
+
}
|
|
203
|
+
},
|
|
204
|
+
performanceSum: {
|
|
205
|
+
$sum: {
|
|
206
|
+
$cond: [
|
|
207
|
+
{ $ne: [{ $ifNull: ['$_se.performanceScore', null] }, null] },
|
|
208
|
+
'$_se.performanceScore',
|
|
209
|
+
0
|
|
210
|
+
]
|
|
211
|
+
}
|
|
212
|
+
},
|
|
213
|
+
performanceCount: {
|
|
214
|
+
$sum: {
|
|
215
|
+
$cond: [{ $ne: [{ $ifNull: ['$_se.performanceScore', null] }, null] }, 1, 0]
|
|
216
|
+
}
|
|
217
|
+
},
|
|
218
|
+
recoveryCount: {
|
|
219
|
+
$sum: { $cond: [{ $eq: ['$_se.isRecoveryExercise', true] }, 1, 0] }
|
|
220
|
+
},
|
|
221
|
+
cycleType: { $first: '$cycleType' },
|
|
222
|
+
completedDate: { $first: '$completedDate' }
|
|
223
|
+
}
|
|
224
|
+
},
|
|
225
|
+
// Sort by microcycle index so $first gives the starting microcycle
|
|
226
|
+
{ $sort: { '_id.microIdx': 1 } },
|
|
227
|
+
// Second group: per (mesocycle, muscleGroup) — starting/peak set counts, aggregate metrics
|
|
228
|
+
{
|
|
229
|
+
$group: {
|
|
230
|
+
_id: {
|
|
231
|
+
mesocycleId: '$_id.mesocycleId',
|
|
232
|
+
muscleGroupId: '$_id.muscleGroupId'
|
|
233
|
+
},
|
|
234
|
+
startingSetCount: { $first: '$setCount' },
|
|
235
|
+
peakSetCount: { $max: '$setCount' },
|
|
236
|
+
rsmSum: { $sum: '$rsmSum' },
|
|
237
|
+
rsmCount: { $sum: '$rsmCount' },
|
|
238
|
+
sorenessSum: { $sum: '$sorenessSum' },
|
|
239
|
+
sorenessCount: { $sum: '$sorenessCount' },
|
|
240
|
+
performanceSum: { $sum: '$performanceSum' },
|
|
241
|
+
performanceCount: { $sum: '$performanceCount' },
|
|
242
|
+
recoverySessionCount: { $sum: '$recoveryCount' },
|
|
243
|
+
cycleType: { $first: '$cycleType' },
|
|
244
|
+
completedDate: { $first: '$completedDate' }
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
])
|
|
248
|
+
.toArray();
|
|
249
|
+
const [muscleGroups, volumeRows] = await Promise.all([muscleGroupsPromise, volumePromise]);
|
|
250
|
+
// Group volume rows by muscleGroupId
|
|
251
|
+
const historyMap = new Map();
|
|
252
|
+
for (const row of volumeRows) {
|
|
253
|
+
const mgId = row._id.muscleGroupId;
|
|
254
|
+
if (!historyMap.has(mgId)) {
|
|
255
|
+
historyMap.set(mgId, []);
|
|
256
|
+
}
|
|
257
|
+
const arr = historyMap.get(mgId);
|
|
258
|
+
if (arr) {
|
|
259
|
+
arr.push(row);
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
// Assemble CTOs
|
|
263
|
+
return muscleGroups.map((mg) => {
|
|
264
|
+
const rows = historyMap.get(mg._id) ?? [];
|
|
265
|
+
const mesocycleHistory = rows.map((row) => ({
|
|
266
|
+
mesocycleId: row._id.mesocycleId,
|
|
267
|
+
cycleType: row.cycleType,
|
|
268
|
+
startingSetCount: row.startingSetCount,
|
|
269
|
+
peakSetCount: row.peakSetCount,
|
|
270
|
+
avgRsm: row.rsmCount > 0 ? row.rsmSum / row.rsmCount : null,
|
|
271
|
+
avgSorenessScore: row.sorenessCount > 0 ? row.sorenessSum / row.sorenessCount : null,
|
|
272
|
+
avgPerformanceScore: row.performanceCount > 0 ? row.performanceSum / row.performanceCount : null,
|
|
273
|
+
recoverySessionCount: row.recoverySessionCount,
|
|
274
|
+
completedDate: row.completedDate
|
|
275
|
+
}));
|
|
276
|
+
return WorkoutMuscleGroupVolumeCTOSchema.parse({
|
|
277
|
+
...mg,
|
|
278
|
+
mesocycleHistory
|
|
279
|
+
});
|
|
280
|
+
});
|
|
281
|
+
}
|
|
36
282
|
}
|
|
37
283
|
//# sourceMappingURL=WorkoutMuscleGroupRepository.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"WorkoutMuscleGroupRepository.js","sourceRoot":"","sources":["../../../src/repositories/workout/WorkoutMuscleGroupRepository.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"WorkoutMuscleGroupRepository.js","sourceRoot":"","sources":["../../../src/repositories/workout/WorkoutMuscleGroupRepository.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,5 +1,18 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
|
|
1
|
+
import type {
|
|
2
|
+
User,
|
|
3
|
+
WorkoutMuscleGroup,
|
|
4
|
+
WorkoutMuscleGroupVolumeCTO
|
|
5
|
+
} from '@aneuhold/core-ts-db-lib';
|
|
6
|
+
import {
|
|
7
|
+
WorkoutExercise_docType,
|
|
8
|
+
WorkoutMesocycle_docType,
|
|
9
|
+
WorkoutMicrocycle_docType,
|
|
10
|
+
WorkoutMuscleGroup_docType,
|
|
11
|
+
WorkoutMuscleGroupVolumeCTOSchema,
|
|
12
|
+
WorkoutSession_docType,
|
|
13
|
+
WorkoutSessionExercise_docType
|
|
14
|
+
} from '@aneuhold/core-ts-db-lib';
|
|
15
|
+
import type { UUID } from 'crypto';
|
|
3
16
|
import type { RepoListeners } from '../../services/RepoSubscriptionService.js';
|
|
4
17
|
import WorkoutMuscleGroupValidator from '../../validators/workout/MuscleGroupValidator.js';
|
|
5
18
|
import WorkoutBaseWithUserIdRepository from './WorkoutBaseWithUserIdRepository.js';
|
|
@@ -40,4 +53,274 @@ export default class WorkoutMuscleGroupRepository extends WorkoutBaseWithUserIdR
|
|
|
40
53
|
}
|
|
41
54
|
return WorkoutMuscleGroupRepository.singletonInstance;
|
|
42
55
|
}
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Builds {@link WorkoutMuscleGroupVolumeCTO} objects for all muscle groups
|
|
59
|
+
* belonging to a user. Fetches muscle groups directly, then runs an
|
|
60
|
+
* aggregation pipeline for volume history across completed mesocycles.
|
|
61
|
+
*
|
|
62
|
+
* @param userId The user whose muscle group volume CTOs to build.
|
|
63
|
+
*/
|
|
64
|
+
async buildMuscleGroupVolumeCTOsForUser(userId: UUID): Promise<WorkoutMuscleGroupVolumeCTO[]> {
|
|
65
|
+
/** Raw shape of volume pipeline results (grouped by mesocycle + muscle group). */
|
|
66
|
+
interface VolumeRow {
|
|
67
|
+
_id: { mesocycleId: string; muscleGroupId: string };
|
|
68
|
+
startingSetCount: number;
|
|
69
|
+
peakSetCount: number;
|
|
70
|
+
rsmSum: number;
|
|
71
|
+
rsmCount: number;
|
|
72
|
+
sorenessSum: number;
|
|
73
|
+
sorenessCount: number;
|
|
74
|
+
performanceSum: number;
|
|
75
|
+
performanceCount: number;
|
|
76
|
+
recoverySessionCount: number;
|
|
77
|
+
cycleType: string;
|
|
78
|
+
completedDate: Date | null;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
const collection = await this.getCollection();
|
|
82
|
+
const collName = this.collectionName;
|
|
83
|
+
|
|
84
|
+
// Part A: fetch all muscle groups
|
|
85
|
+
const muscleGroupsPromise = this.getAllForUser(userId);
|
|
86
|
+
|
|
87
|
+
// Part B: aggregation pipeline for volume data
|
|
88
|
+
const volumePromise = collection
|
|
89
|
+
.aggregate<VolumeRow>([
|
|
90
|
+
// Start with completed mesocycles for this user
|
|
91
|
+
{
|
|
92
|
+
$match: {
|
|
93
|
+
docType: WorkoutMesocycle_docType,
|
|
94
|
+
userId,
|
|
95
|
+
completedDate: { $type: 'date' }
|
|
96
|
+
}
|
|
97
|
+
},
|
|
98
|
+
// Most recent first
|
|
99
|
+
{ $sort: { completedDate: -1 } },
|
|
100
|
+
// Limit to 10 most recent
|
|
101
|
+
{ $limit: 10 },
|
|
102
|
+
// Join microcycles, sorted by start date
|
|
103
|
+
{
|
|
104
|
+
$lookup: {
|
|
105
|
+
from: collName,
|
|
106
|
+
let: { mesoId: '$_id' },
|
|
107
|
+
pipeline: [
|
|
108
|
+
{
|
|
109
|
+
$match: {
|
|
110
|
+
$expr: { $eq: ['$workoutMesocycleId', '$$mesoId'] },
|
|
111
|
+
docType: WorkoutMicrocycle_docType
|
|
112
|
+
}
|
|
113
|
+
},
|
|
114
|
+
{ $sort: { startDate: 1 } }
|
|
115
|
+
],
|
|
116
|
+
as: '_microcycles'
|
|
117
|
+
}
|
|
118
|
+
},
|
|
119
|
+
// One row per microcycle, tracking index for starting/peak logic
|
|
120
|
+
{ $unwind: { path: '$_microcycles', includeArrayIndex: '_microIdx' } },
|
|
121
|
+
// Join sessions for each microcycle
|
|
122
|
+
{
|
|
123
|
+
$lookup: {
|
|
124
|
+
from: collName,
|
|
125
|
+
let: { microId: '$_microcycles._id' },
|
|
126
|
+
pipeline: [
|
|
127
|
+
{
|
|
128
|
+
$match: {
|
|
129
|
+
$expr: { $eq: ['$workoutMicrocycleId', '$$microId'] },
|
|
130
|
+
docType: WorkoutSession_docType
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
],
|
|
134
|
+
as: '_sessions'
|
|
135
|
+
}
|
|
136
|
+
},
|
|
137
|
+
// One row per session (drop empty microcycles)
|
|
138
|
+
{ $unwind: { path: '$_sessions', preserveNullAndEmptyArrays: false } },
|
|
139
|
+
// Join session exercises
|
|
140
|
+
{
|
|
141
|
+
$lookup: {
|
|
142
|
+
from: collName,
|
|
143
|
+
let: { sessId: '$_sessions._id' },
|
|
144
|
+
pipeline: [
|
|
145
|
+
{
|
|
146
|
+
$match: {
|
|
147
|
+
$expr: { $eq: ['$workoutSessionId', '$$sessId'] },
|
|
148
|
+
docType: WorkoutSessionExercise_docType
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
],
|
|
152
|
+
as: '_se'
|
|
153
|
+
}
|
|
154
|
+
},
|
|
155
|
+
// One row per session exercise
|
|
156
|
+
{ $unwind: '$_se' },
|
|
157
|
+
// Join exercise doc to get muscle group arrays
|
|
158
|
+
{
|
|
159
|
+
$lookup: {
|
|
160
|
+
from: collName,
|
|
161
|
+
let: { exId: '$_se.workoutExerciseId' },
|
|
162
|
+
pipeline: [
|
|
163
|
+
{
|
|
164
|
+
$match: {
|
|
165
|
+
$expr: { $eq: ['$_id', '$$exId'] },
|
|
166
|
+
docType: WorkoutExercise_docType
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
],
|
|
170
|
+
as: '_exercise'
|
|
171
|
+
}
|
|
172
|
+
},
|
|
173
|
+
// One row per exercise
|
|
174
|
+
{ $unwind: '$_exercise' },
|
|
175
|
+
// Combine primary + secondary muscle group IDs
|
|
176
|
+
{
|
|
177
|
+
$addFields: {
|
|
178
|
+
_muscleGroups: {
|
|
179
|
+
$concatArrays: [
|
|
180
|
+
{ $ifNull: ['$_exercise.primaryMuscleGroups', []] },
|
|
181
|
+
{ $ifNull: ['$_exercise.secondaryMuscleGroups', []] }
|
|
182
|
+
]
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
},
|
|
186
|
+
// One row per (mesocycle, microcycle, session exercise, muscle group)
|
|
187
|
+
{ $unwind: '$_muscleGroups' },
|
|
188
|
+
// First group: per (mesocycle, muscleGroup, microcycleIndex) — count sets, sum metrics
|
|
189
|
+
{
|
|
190
|
+
$group: {
|
|
191
|
+
_id: {
|
|
192
|
+
mesocycleId: '$_id',
|
|
193
|
+
muscleGroupId: '$_muscleGroups',
|
|
194
|
+
microIdx: '$_microIdx'
|
|
195
|
+
},
|
|
196
|
+
setCount: { $sum: { $size: { $ifNull: ['$_se.setOrder', []] } } },
|
|
197
|
+
rsmSum: {
|
|
198
|
+
$sum: {
|
|
199
|
+
$cond: [
|
|
200
|
+
{
|
|
201
|
+
$and: [
|
|
202
|
+
{ $ne: [{ $ifNull: ['$_se.rsm.mindMuscleConnection', null] }, null] },
|
|
203
|
+
{ $ne: [{ $ifNull: ['$_se.rsm.pump', null] }, null] },
|
|
204
|
+
{ $ne: [{ $ifNull: ['$_se.rsm.disruption', null] }, null] }
|
|
205
|
+
]
|
|
206
|
+
},
|
|
207
|
+
{
|
|
208
|
+
$add: ['$_se.rsm.mindMuscleConnection', '$_se.rsm.pump', '$_se.rsm.disruption']
|
|
209
|
+
},
|
|
210
|
+
0
|
|
211
|
+
]
|
|
212
|
+
}
|
|
213
|
+
},
|
|
214
|
+
rsmCount: {
|
|
215
|
+
$sum: {
|
|
216
|
+
$cond: [
|
|
217
|
+
{
|
|
218
|
+
$and: [
|
|
219
|
+
{ $ne: [{ $ifNull: ['$_se.rsm.mindMuscleConnection', null] }, null] },
|
|
220
|
+
{ $ne: [{ $ifNull: ['$_se.rsm.pump', null] }, null] },
|
|
221
|
+
{ $ne: [{ $ifNull: ['$_se.rsm.disruption', null] }, null] }
|
|
222
|
+
]
|
|
223
|
+
},
|
|
224
|
+
1,
|
|
225
|
+
0
|
|
226
|
+
]
|
|
227
|
+
}
|
|
228
|
+
},
|
|
229
|
+
sorenessSum: {
|
|
230
|
+
$sum: {
|
|
231
|
+
$cond: [
|
|
232
|
+
{ $ne: [{ $ifNull: ['$_se.sorenessScore', null] }, null] },
|
|
233
|
+
'$_se.sorenessScore',
|
|
234
|
+
0
|
|
235
|
+
]
|
|
236
|
+
}
|
|
237
|
+
},
|
|
238
|
+
sorenessCount: {
|
|
239
|
+
$sum: {
|
|
240
|
+
$cond: [{ $ne: [{ $ifNull: ['$_se.sorenessScore', null] }, null] }, 1, 0]
|
|
241
|
+
}
|
|
242
|
+
},
|
|
243
|
+
performanceSum: {
|
|
244
|
+
$sum: {
|
|
245
|
+
$cond: [
|
|
246
|
+
{ $ne: [{ $ifNull: ['$_se.performanceScore', null] }, null] },
|
|
247
|
+
'$_se.performanceScore',
|
|
248
|
+
0
|
|
249
|
+
]
|
|
250
|
+
}
|
|
251
|
+
},
|
|
252
|
+
performanceCount: {
|
|
253
|
+
$sum: {
|
|
254
|
+
$cond: [{ $ne: [{ $ifNull: ['$_se.performanceScore', null] }, null] }, 1, 0]
|
|
255
|
+
}
|
|
256
|
+
},
|
|
257
|
+
recoveryCount: {
|
|
258
|
+
$sum: { $cond: [{ $eq: ['$_se.isRecoveryExercise', true] }, 1, 0] }
|
|
259
|
+
},
|
|
260
|
+
cycleType: { $first: '$cycleType' },
|
|
261
|
+
completedDate: { $first: '$completedDate' }
|
|
262
|
+
}
|
|
263
|
+
},
|
|
264
|
+
// Sort by microcycle index so $first gives the starting microcycle
|
|
265
|
+
{ $sort: { '_id.microIdx': 1 } },
|
|
266
|
+
// Second group: per (mesocycle, muscleGroup) — starting/peak set counts, aggregate metrics
|
|
267
|
+
{
|
|
268
|
+
$group: {
|
|
269
|
+
_id: {
|
|
270
|
+
mesocycleId: '$_id.mesocycleId',
|
|
271
|
+
muscleGroupId: '$_id.muscleGroupId'
|
|
272
|
+
},
|
|
273
|
+
startingSetCount: { $first: '$setCount' },
|
|
274
|
+
peakSetCount: { $max: '$setCount' },
|
|
275
|
+
rsmSum: { $sum: '$rsmSum' },
|
|
276
|
+
rsmCount: { $sum: '$rsmCount' },
|
|
277
|
+
sorenessSum: { $sum: '$sorenessSum' },
|
|
278
|
+
sorenessCount: { $sum: '$sorenessCount' },
|
|
279
|
+
performanceSum: { $sum: '$performanceSum' },
|
|
280
|
+
performanceCount: { $sum: '$performanceCount' },
|
|
281
|
+
recoverySessionCount: { $sum: '$recoveryCount' },
|
|
282
|
+
cycleType: { $first: '$cycleType' },
|
|
283
|
+
completedDate: { $first: '$completedDate' }
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
])
|
|
287
|
+
.toArray();
|
|
288
|
+
|
|
289
|
+
const [muscleGroups, volumeRows] = await Promise.all([muscleGroupsPromise, volumePromise]);
|
|
290
|
+
|
|
291
|
+
// Group volume rows by muscleGroupId
|
|
292
|
+
const historyMap = new Map<string, VolumeRow[]>();
|
|
293
|
+
for (const row of volumeRows) {
|
|
294
|
+
const mgId = row._id.muscleGroupId;
|
|
295
|
+
if (!historyMap.has(mgId)) {
|
|
296
|
+
historyMap.set(mgId, []);
|
|
297
|
+
}
|
|
298
|
+
const arr = historyMap.get(mgId);
|
|
299
|
+
if (arr) {
|
|
300
|
+
arr.push(row);
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
// Assemble CTOs
|
|
305
|
+
return muscleGroups.map((mg) => {
|
|
306
|
+
const rows = historyMap.get(mg._id as string) ?? [];
|
|
307
|
+
const mesocycleHistory = rows.map((row) => ({
|
|
308
|
+
mesocycleId: row._id.mesocycleId,
|
|
309
|
+
cycleType: row.cycleType,
|
|
310
|
+
startingSetCount: row.startingSetCount,
|
|
311
|
+
peakSetCount: row.peakSetCount,
|
|
312
|
+
avgRsm: row.rsmCount > 0 ? row.rsmSum / row.rsmCount : null,
|
|
313
|
+
avgSorenessScore: row.sorenessCount > 0 ? row.sorenessSum / row.sorenessCount : null,
|
|
314
|
+
avgPerformanceScore:
|
|
315
|
+
row.performanceCount > 0 ? row.performanceSum / row.performanceCount : null,
|
|
316
|
+
recoverySessionCount: row.recoverySessionCount,
|
|
317
|
+
completedDate: row.completedDate
|
|
318
|
+
}));
|
|
319
|
+
|
|
320
|
+
return WorkoutMuscleGroupVolumeCTOSchema.parse({
|
|
321
|
+
...mg,
|
|
322
|
+
mesocycleHistory
|
|
323
|
+
});
|
|
324
|
+
});
|
|
325
|
+
}
|
|
43
326
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ExerciseValidator.d.ts","sourceRoot":"","sources":["../../../src/validators/workout/ExerciseValidator.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,0BAA0B,CAAC;AAQhE,OAAO,KAAK,mBAAmB,MAAM,mCAAmC,CAAC;AACzE,OAAO,UAAU,MAAM,qBAAqB,CAAC;AAE7C,MAAM,CAAC,OAAO,OAAO,wBAAyB,SAAQ,UAAU,CAAC,eAAe,CAAC;;cAK/D,8BAA8B,CAC5C,WAAW,EAAE,eAAe,EAC5B,IAAI,CAAC,EAAE,mBAAmB,GACzB,OAAO,CAAC,IAAI,CAAC;cAyCA,iCAAiC,CAC/C,eAAe,EAAE,OAAO,CAAC,eAAe,CAAC,EACzC,IAAI,CAAC,EAAE,mBAAmB,GACzB,OAAO,CAAC,IAAI,CAAC;IAoDV,sBAAsB,CAAC,MAAM,EAAE,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC;
|
|
1
|
+
{"version":3,"file":"ExerciseValidator.d.ts","sourceRoot":"","sources":["../../../src/validators/workout/ExerciseValidator.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,0BAA0B,CAAC;AAQhE,OAAO,KAAK,mBAAmB,MAAM,mCAAmC,CAAC;AACzE,OAAO,UAAU,MAAM,qBAAqB,CAAC;AAE7C,MAAM,CAAC,OAAO,OAAO,wBAAyB,SAAQ,UAAU,CAAC,eAAe,CAAC;;cAK/D,8BAA8B,CAC5C,WAAW,EAAE,eAAe,EAC5B,IAAI,CAAC,EAAE,mBAAmB,GACzB,OAAO,CAAC,IAAI,CAAC;cAyCA,iCAAiC,CAC/C,eAAe,EAAE,OAAO,CAAC,eAAe,CAAC,EACzC,IAAI,CAAC,EAAE,mBAAmB,GACzB,OAAO,CAAC,IAAI,CAAC;IAoDV,sBAAsB,CAAC,MAAM,EAAE,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC;CAgE7D"}
|
|
@@ -96,15 +96,15 @@ export default class WorkoutExerciseValidator extends IValidator {
|
|
|
96
96
|
DR.logger.error(`Workout Exercise with ID: ${exercise._id} has no valid associated user.`);
|
|
97
97
|
return true;
|
|
98
98
|
}
|
|
99
|
+
if (!allEquipmentTypeIds[exercise.workoutEquipmentTypeId]) {
|
|
100
|
+
DR.logger.error(`Workout Exercise with ID: ${exercise._id} references non-existent equipment type: ${exercise.workoutEquipmentTypeId}.`);
|
|
101
|
+
return true;
|
|
102
|
+
}
|
|
99
103
|
return false;
|
|
100
104
|
},
|
|
101
105
|
additionalValidation: (exercise) => {
|
|
102
106
|
const updatedDoc = { ...exercise };
|
|
103
107
|
const errors = [];
|
|
104
|
-
// Check equipment type
|
|
105
|
-
if (!allEquipmentTypeIds[exercise.workoutEquipmentTypeId]) {
|
|
106
|
-
errors.push(`Equipment type with ID: ${exercise.workoutEquipmentTypeId} does not exist.`);
|
|
107
|
-
}
|
|
108
108
|
// Check primary muscle groups
|
|
109
109
|
const invalidPrimaryMuscleGroups = exercise.primaryMuscleGroups.filter((id) => !allMuscleGroupIds[id]);
|
|
110
110
|
if (invalidPrimaryMuscleGroups.length > 0) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ExerciseValidator.js","sourceRoot":"","sources":["../../../src/validators/workout/ExerciseValidator.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"ExerciseValidator.js","sourceRoot":"","sources":["../../../src/validators/workout/ExerciseValidator.ts"],"names":[],"mappings":"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"}
|
|
@@ -131,17 +131,18 @@ export default class WorkoutExerciseValidator extends IValidator<WorkoutExercise
|
|
|
131
131
|
);
|
|
132
132
|
return true;
|
|
133
133
|
}
|
|
134
|
+
if (!allEquipmentTypeIds[exercise.workoutEquipmentTypeId]) {
|
|
135
|
+
DR.logger.error(
|
|
136
|
+
`Workout Exercise with ID: ${exercise._id} references non-existent equipment type: ${exercise.workoutEquipmentTypeId}.`
|
|
137
|
+
);
|
|
138
|
+
return true;
|
|
139
|
+
}
|
|
134
140
|
return false;
|
|
135
141
|
},
|
|
136
142
|
additionalValidation: (exercise: WorkoutExercise) => {
|
|
137
143
|
const updatedDoc = { ...exercise };
|
|
138
144
|
const errors: string[] = [];
|
|
139
145
|
|
|
140
|
-
// Check equipment type
|
|
141
|
-
if (!allEquipmentTypeIds[exercise.workoutEquipmentTypeId]) {
|
|
142
|
-
errors.push(`Equipment type with ID: ${exercise.workoutEquipmentTypeId} does not exist.`);
|
|
143
|
-
}
|
|
144
|
-
|
|
145
146
|
// Check primary muscle groups
|
|
146
147
|
const invalidPrimaryMuscleGroups = exercise.primaryMuscleGroups.filter(
|
|
147
148
|
(id) => !allMuscleGroupIds[id]
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"SessionExerciseValidator.d.ts","sourceRoot":"","sources":["../../../src/validators/workout/SessionExerciseValidator.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,0BAA0B,CAAC;AAQvE,OAAO,KAAK,mBAAmB,MAAM,mCAAmC,CAAC;AACzE,OAAO,UAAU,MAAM,qBAAqB,CAAC;AAE7C,MAAM,CAAC,OAAO,OAAO,+BAAgC,SAAQ,UAAU,CAAC,sBAAsB,CAAC;;cAK7E,8BAA8B,CAC5C,kBAAkB,EAAE,sBAAsB,EAC1C,IAAI,CAAC,EAAE,mBAAmB,GACzB,OAAO,CAAC,IAAI,CAAC;cA8BA,iCAAiC,CAC/C,sBAAsB,EAAE,OAAO,CAAC,sBAAsB,CAAC,EACvD,IAAI,CAAC,EAAE,mBAAmB,GACzB,OAAO,CAAC,IAAI,CAAC;IAwCV,sBAAsB,CAAC,MAAM,EAAE,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC;
|
|
1
|
+
{"version":3,"file":"SessionExerciseValidator.d.ts","sourceRoot":"","sources":["../../../src/validators/workout/SessionExerciseValidator.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,0BAA0B,CAAC;AAQvE,OAAO,KAAK,mBAAmB,MAAM,mCAAmC,CAAC;AACzE,OAAO,UAAU,MAAM,qBAAqB,CAAC;AAE7C,MAAM,CAAC,OAAO,OAAO,+BAAgC,SAAQ,UAAU,CAAC,sBAAsB,CAAC;;cAK7E,8BAA8B,CAC5C,kBAAkB,EAAE,sBAAsB,EAC1C,IAAI,CAAC,EAAE,mBAAmB,GACzB,OAAO,CAAC,IAAI,CAAC;cA8BA,iCAAiC,CAC/C,sBAAsB,EAAE,OAAO,CAAC,sBAAsB,CAAC,EACvD,IAAI,CAAC,EAAE,mBAAmB,GACzB,OAAO,CAAC,IAAI,CAAC;IAwCV,sBAAsB,CAAC,MAAM,EAAE,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC;CAwC7D"}
|
|
@@ -83,16 +83,11 @@ export default class WorkoutSessionExerciseValidator extends IValidator {
|
|
|
83
83
|
DR.logger.error(`Workout Session Exercise with ID: ${sessionExercise._id} has no valid associated session.`);
|
|
84
84
|
return true;
|
|
85
85
|
}
|
|
86
|
-
return false;
|
|
87
|
-
},
|
|
88
|
-
additionalValidation: (sessionExercise) => {
|
|
89
|
-
const updatedDoc = { ...sessionExercise };
|
|
90
|
-
const errors = [];
|
|
91
|
-
// Check exercise
|
|
92
86
|
if (!allExerciseIds[sessionExercise.workoutExerciseId]) {
|
|
93
|
-
|
|
87
|
+
DR.logger.error(`Workout Session Exercise with ID: ${sessionExercise._id} references non-existent exercise: ${sessionExercise.workoutExerciseId}.`);
|
|
88
|
+
return true;
|
|
94
89
|
}
|
|
95
|
-
return
|
|
90
|
+
return false;
|
|
96
91
|
},
|
|
97
92
|
deletionFunction: async (docIdsToDelete) => {
|
|
98
93
|
await sessionExerciseRepo.deleteList(docIdsToDelete);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"SessionExerciseValidator.js","sourceRoot":"","sources":["../../../src/validators/workout/SessionExerciseValidator.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"SessionExerciseValidator.js","sourceRoot":"","sources":["../../../src/validators/workout/SessionExerciseValidator.ts"],"names":[],"mappings":"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"}
|
|
@@ -114,18 +114,13 @@ export default class WorkoutSessionExerciseValidator extends IValidator<WorkoutS
|
|
|
114
114
|
);
|
|
115
115
|
return true;
|
|
116
116
|
}
|
|
117
|
-
return false;
|
|
118
|
-
},
|
|
119
|
-
additionalValidation: (sessionExercise: WorkoutSessionExercise) => {
|
|
120
|
-
const updatedDoc = { ...sessionExercise };
|
|
121
|
-
const errors: string[] = [];
|
|
122
|
-
|
|
123
|
-
// Check exercise
|
|
124
117
|
if (!allExerciseIds[sessionExercise.workoutExerciseId]) {
|
|
125
|
-
|
|
118
|
+
DR.logger.error(
|
|
119
|
+
`Workout Session Exercise with ID: ${sessionExercise._id} references non-existent exercise: ${sessionExercise.workoutExerciseId}.`
|
|
120
|
+
);
|
|
121
|
+
return true;
|
|
126
122
|
}
|
|
127
|
-
|
|
128
|
-
return { updatedDoc, errors };
|
|
123
|
+
return false;
|
|
129
124
|
},
|
|
130
125
|
deletionFunction: async (docIdsToDelete: UUID[]) => {
|
|
131
126
|
await sessionExerciseRepo.deleteList(docIdsToDelete);
|
package/package.json
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"name": "@aneuhold/be-ts-db-lib",
|
|
3
3
|
"author": "Anton G. Neuhold Jr.",
|
|
4
4
|
"license": "MIT",
|
|
5
|
-
"version": "4.2.
|
|
5
|
+
"version": "4.2.14",
|
|
6
6
|
"description": "A backend database library meant to actually interact with various databases in personal projects",
|
|
7
7
|
"packageManager": "pnpm@10.25.0",
|
|
8
8
|
"type": "module",
|
|
@@ -58,16 +58,16 @@
|
|
|
58
58
|
"MongoDB"
|
|
59
59
|
],
|
|
60
60
|
"dependencies": {
|
|
61
|
-
"@aneuhold/be-ts-lib": "^3.
|
|
62
|
-
"@aneuhold/core-ts-db-lib": "^4.1.
|
|
63
|
-
"@aneuhold/core-ts-lib": "^2.4.
|
|
61
|
+
"@aneuhold/be-ts-lib": "^3.1.1",
|
|
62
|
+
"@aneuhold/core-ts-db-lib": "^4.1.13",
|
|
63
|
+
"@aneuhold/core-ts-lib": "^2.4.1",
|
|
64
64
|
"bson": "^7.0.0",
|
|
65
65
|
"mongodb": "^7.0.0",
|
|
66
66
|
"uuid": "^13.0.0",
|
|
67
67
|
"zod": "^4.1.13"
|
|
68
68
|
},
|
|
69
69
|
"devDependencies": {
|
|
70
|
-
"@aneuhold/local-npm-registry": "^0.2.
|
|
70
|
+
"@aneuhold/local-npm-registry": "^0.2.28",
|
|
71
71
|
"@aneuhold/main-scripts": "^2.8.3",
|
|
72
72
|
"@types/node": "^25.0.2",
|
|
73
73
|
"@types/node-fetch": "^2.6.13",
|