@aneuhold/core-ts-db-lib 4.1.8 → 4.1.9
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 +14 -0
- package/lib/services/workout/Mesocycle/WorkoutMesocycleService.d.ts +39 -0
- package/lib/services/workout/Mesocycle/WorkoutMesocycleService.d.ts.map +1 -1
- package/lib/services/workout/Mesocycle/WorkoutMesocycleService.js +98 -4
- package/lib/services/workout/Mesocycle/WorkoutMesocycleService.js.map +1 -1
- package/lib/services/workout/Mesocycle/WorkoutMesocycleService.ts +127 -4
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,19 @@ 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.1.9] (2026-02-22)
|
|
9
|
+
|
|
10
|
+
### ✅ Added
|
|
11
|
+
|
|
12
|
+
- Added `WorkoutMesocycleService.calculateProjectedEndDate()` to compute a mesocycle's projected end from its microcycles or planned parameters.
|
|
13
|
+
- Added `WorkoutMesocycleService.shiftMesocycleDates()` to shift all dates of a mesocycle and its child documents in place by a given number of days.
|
|
14
|
+
- Added `WorkoutMesocycleService.detectMesocycleOverlap()` to detect overlapping date ranges across a set of mesocycles.
|
|
15
|
+
- Added `WorkoutMesocycleService.getEarliestAllowedStartDate()` to determine the earliest valid start date for a new mesocycle based on existing ones.
|
|
16
|
+
|
|
17
|
+
### 🏗️ Changed
|
|
18
|
+
|
|
19
|
+
- Refactored internal date handling in `WorkoutMesocycleService` to use existing `Date` references directly instead of wrapping them in `new Date()`.
|
|
20
|
+
|
|
8
21
|
## 🔖 [4.1.8] (2026-02-22)
|
|
9
22
|
|
|
10
23
|
### 🏗️ Changed
|
|
@@ -287,6 +300,7 @@ Updated dependency: now requires `@aneuhold/core-ts-lib@^2.3.11`.
|
|
|
287
300
|
|
|
288
301
|
<!-- Link References -->
|
|
289
302
|
|
|
303
|
+
[4.1.9]: https://github.com/aneuhold/ts-libs/compare/core-ts-db-lib-v4.1.8...core-ts-db-lib-v4.1.9
|
|
290
304
|
[4.1.8]: https://github.com/aneuhold/ts-libs/compare/core-ts-db-lib-v4.1.7...core-ts-db-lib-v4.1.8
|
|
291
305
|
[4.1.7]: https://github.com/aneuhold/ts-libs/compare/core-ts-db-lib-v4.1.6...core-ts-db-lib-v4.1.7
|
|
292
306
|
[4.1.6]: https://github.com/aneuhold/ts-libs/compare/core-ts-db-lib-v4.1.5...core-ts-db-lib-v4.1.6
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { UUID } from 'crypto';
|
|
1
2
|
import type { WorkoutEquipmentType } from '../../../documents/workout/WorkoutEquipmentType.js';
|
|
2
3
|
import type { WorkoutExercise } from '../../../documents/workout/WorkoutExercise.js';
|
|
3
4
|
import type { WorkoutExerciseCalibration } from '../../../documents/workout/WorkoutExerciseCalibration.js';
|
|
@@ -42,6 +43,44 @@ export default class WorkoutMesocycleService {
|
|
|
42
43
|
sessionExercises?: DocumentOperations<WorkoutSessionExercise>;
|
|
43
44
|
sets?: DocumentOperations<WorkoutSet>;
|
|
44
45
|
};
|
|
46
|
+
/**
|
|
47
|
+
* Calculates the projected end date of a mesocycle based on its microcycles.
|
|
48
|
+
* If microcycles exist, uses the last microcycle's endDate. Otherwise,
|
|
49
|
+
* calculates from the mesocycle's start date and planned parameters.
|
|
50
|
+
*
|
|
51
|
+
* @param mesocycle The mesocycle to calculate the end date for.
|
|
52
|
+
* @param microcycles The microcycles belonging to this mesocycle (pre-filtered).
|
|
53
|
+
*/
|
|
54
|
+
static calculateProjectedEndDate(mesocycle: WorkoutMesocycle, microcycles: WorkoutMicrocycle[]): Date | null;
|
|
55
|
+
/**
|
|
56
|
+
* Shifts all dates in a mesocycle and its child documents by the given number of days.
|
|
57
|
+
* Mutates the passed-in objects in place.
|
|
58
|
+
*
|
|
59
|
+
* @param mesocycle The mesocycle to shift. Its startDate is mutated directly.
|
|
60
|
+
* @param microcycles The microcycles belonging to this mesocycle (pre-filtered). Mutated in place.
|
|
61
|
+
* @param sessions The sessions belonging to these microcycles (pre-filtered). Mutated in place.
|
|
62
|
+
* @param daysDelta The number of days to shift (positive = forward, negative = backward).
|
|
63
|
+
*/
|
|
64
|
+
static shiftMesocycleDates(mesocycle: WorkoutMesocycle, microcycles: WorkoutMicrocycle[], sessions: WorkoutSession[], daysDelta: number): void;
|
|
65
|
+
/**
|
|
66
|
+
* Detects whether any mesocycles in the provided list have overlapping date ranges.
|
|
67
|
+
* Two mesocycles overlap if their projected date ranges intersect.
|
|
68
|
+
*
|
|
69
|
+
* @param mesocycles The mesocycles to check.
|
|
70
|
+
* @param mesocycleToMicrocyclesMap A map from mesocycle ID to its microcycles.
|
|
71
|
+
*/
|
|
72
|
+
static detectMesocycleOverlap(mesocycles: WorkoutMesocycle[], mesocycleToMicrocyclesMap: Map<UUID, WorkoutMicrocycle[]>): {
|
|
73
|
+
hasOverlap: boolean;
|
|
74
|
+
overlappingPairs: Array<[UUID, UUID]>;
|
|
75
|
+
};
|
|
76
|
+
/**
|
|
77
|
+
* Returns the earliest allowed start date for a new mesocycle. This is the later
|
|
78
|
+
* of: the projected end of the last existing mesocycle, or today.
|
|
79
|
+
*
|
|
80
|
+
* @param existingMesocycles All existing mesocycles.
|
|
81
|
+
* @param mesocycleToMicrocyclesMap A map from mesocycle ID to its microcycles.
|
|
82
|
+
*/
|
|
83
|
+
static getEarliestAllowedStartDate(existingMesocycles: WorkoutMesocycle[], mesocycleToMicrocyclesMap: Map<UUID, WorkoutMicrocycle[]>): Date;
|
|
45
84
|
/**
|
|
46
85
|
* Cleans up incomplete microcycles and their associated documents.
|
|
47
86
|
*
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"WorkoutMesocycleService.d.ts","sourceRoot":"","sources":["../../../../src/services/workout/Mesocycle/WorkoutMesocycleService.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"WorkoutMesocycleService.d.ts","sourceRoot":"","sources":["../../../../src/services/workout/Mesocycle/WorkoutMesocycleService.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,QAAQ,CAAC;AACnC,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,oDAAoD,CAAC;AAC/F,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,+CAA+C,CAAC;AACrF,OAAO,KAAK,EAAE,0BAA0B,EAAE,MAAM,0DAA0D,CAAC;AAC3G,OAAO,EAAa,KAAK,gBAAgB,EAAE,MAAM,gDAAgD,CAAC;AAClG,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,iDAAiD,CAAC;AAEzF,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,8CAA8C,CAAC;AACnF,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,sDAAsD,CAAC;AACnG,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,0CAA0C,CAAC;AAC3E,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,0BAA0B,CAAC;AAInE;;GAEG;AACH,MAAM,CAAC,OAAO,OAAO,uBAAuB;IAC1C;;;;;;;;;;;;;;;;;;;;;;;OAuBG;IACH,MAAM,CAAC,yBAAyB,CAC9B,SAAS,EAAE,gBAAgB,EAC3B,YAAY,EAAE,0BAA0B,EAAE,EAC1C,SAAS,EAAE,eAAe,EAAE,EAC5B,cAAc,EAAE,oBAAoB,EAAE,EACtC,mBAAmB,GAAE,iBAAiB,EAAO,EAC7C,gBAAgB,GAAE,cAAc,EAAO,EACvC,wBAAwB,GAAE,sBAAsB,EAAO,EACvD,YAAY,GAAE,UAAU,EAAO,EAC/B,SAAS,CAAC,EAAE,IAAI,GACf;QACD,eAAe,CAAC,EAAE,OAAO,CAAC,gBAAgB,CAAC,CAAC;QAC5C,WAAW,CAAC,EAAE,kBAAkB,CAAC,iBAAiB,CAAC,CAAC;QACpD,QAAQ,CAAC,EAAE,kBAAkB,CAAC,cAAc,CAAC,CAAC;QAC9C,gBAAgB,CAAC,EAAE,kBAAkB,CAAC,sBAAsB,CAAC,CAAC;QAC9D,IAAI,CAAC,EAAE,kBAAkB,CAAC,UAAU,CAAC,CAAC;KACvC;IAwHD;;;;;;;OAOG;IACH,MAAM,CAAC,yBAAyB,CAC9B,SAAS,EAAE,gBAAgB,EAC3B,WAAW,EAAE,iBAAiB,EAAE,GAC/B,IAAI,GAAG,IAAI;IAgBd;;;;;;;;OAQG;IACH,MAAM,CAAC,mBAAmB,CACxB,SAAS,EAAE,gBAAgB,EAC3B,WAAW,EAAE,iBAAiB,EAAE,EAChC,QAAQ,EAAE,cAAc,EAAE,EAC1B,SAAS,EAAE,MAAM,GAChB,IAAI;IAeP;;;;;;OAMG;IACH,MAAM,CAAC,sBAAsB,CAC3B,UAAU,EAAE,gBAAgB,EAAE,EAC9B,yBAAyB,EAAE,GAAG,CAAC,IAAI,EAAE,iBAAiB,EAAE,CAAC,GACxD;QAAE,UAAU,EAAE,OAAO,CAAC;QAAC,gBAAgB,EAAE,KAAK,CAAC,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC,CAAA;KAAE;IA8BjE;;;;;;OAMG;IACH,MAAM,CAAC,2BAA2B,CAChC,kBAAkB,EAAE,gBAAgB,EAAE,EACtC,yBAAyB,EAAE,GAAG,CAAC,IAAI,EAAE,iBAAiB,EAAE,CAAC,GACxD,IAAI;IAiBP;;;;;;OAMG;IACH,OAAO,CAAC,MAAM,CAAC,4BAA4B;CA4F5C"}
|
|
@@ -57,12 +57,12 @@ export default class WorkoutMesocycleService {
|
|
|
57
57
|
const startMicrocycleIndex = context.microcyclesInOrder.length;
|
|
58
58
|
let currentDate;
|
|
59
59
|
if (startMicrocycleIndex === 0) {
|
|
60
|
-
currentDate = startDate
|
|
60
|
+
currentDate = startDate ?? new Date();
|
|
61
61
|
}
|
|
62
62
|
else {
|
|
63
63
|
// Continue from where the last existing microcycle ended
|
|
64
64
|
const lastExistingMicrocycle = context.microcyclesInOrder[startMicrocycleIndex - 1];
|
|
65
|
-
currentDate =
|
|
65
|
+
currentDate = lastExistingMicrocycle.endDate;
|
|
66
66
|
}
|
|
67
67
|
// Generate remaining microcycles
|
|
68
68
|
for (let microcycleIndex = startMicrocycleIndex; microcycleIndex < totalMicrocycles; microcycleIndex++) {
|
|
@@ -74,7 +74,7 @@ export default class WorkoutMesocycleService {
|
|
|
74
74
|
const microcycle = WorkoutMicrocycleSchema.parse({
|
|
75
75
|
userId: mesocycle.userId,
|
|
76
76
|
workoutMesocycleId: mesocycle._id,
|
|
77
|
-
startDate:
|
|
77
|
+
startDate: currentDate,
|
|
78
78
|
endDate: DateService.addDays(currentDate, mesocycle.plannedMicrocycleLengthInDays)
|
|
79
79
|
});
|
|
80
80
|
context.addMicrocycle(microcycle);
|
|
@@ -85,7 +85,7 @@ export default class WorkoutMesocycleService {
|
|
|
85
85
|
isDeloadMicrocycle
|
|
86
86
|
});
|
|
87
87
|
// Move to next microcycle
|
|
88
|
-
currentDate =
|
|
88
|
+
currentDate = microcycle.endDate;
|
|
89
89
|
}
|
|
90
90
|
return {
|
|
91
91
|
mesocycleUpdate: undefined,
|
|
@@ -107,6 +107,100 @@ export default class WorkoutMesocycleService {
|
|
|
107
107
|
sets: { create: context.setsToCreate, update: [], delete: cleanupResult.setsToDelete }
|
|
108
108
|
};
|
|
109
109
|
}
|
|
110
|
+
/**
|
|
111
|
+
* Calculates the projected end date of a mesocycle based on its microcycles.
|
|
112
|
+
* If microcycles exist, uses the last microcycle's endDate. Otherwise,
|
|
113
|
+
* calculates from the mesocycle's start date and planned parameters.
|
|
114
|
+
*
|
|
115
|
+
* @param mesocycle The mesocycle to calculate the end date for.
|
|
116
|
+
* @param microcycles The microcycles belonging to this mesocycle (pre-filtered).
|
|
117
|
+
*/
|
|
118
|
+
static calculateProjectedEndDate(mesocycle, microcycles) {
|
|
119
|
+
const sorted = [...microcycles].sort((a, b) => a.startDate.getTime() - b.startDate.getTime());
|
|
120
|
+
if (sorted.length > 0) {
|
|
121
|
+
return sorted[sorted.length - 1].endDate;
|
|
122
|
+
}
|
|
123
|
+
if (mesocycle.startDate == null) {
|
|
124
|
+
return null;
|
|
125
|
+
}
|
|
126
|
+
const totalMicrocycles = mesocycle.plannedMicrocycleCount ?? 6;
|
|
127
|
+
const totalDays = totalMicrocycles * mesocycle.plannedMicrocycleLengthInDays;
|
|
128
|
+
return DateService.addDays(mesocycle.startDate, totalDays);
|
|
129
|
+
}
|
|
130
|
+
/**
|
|
131
|
+
* Shifts all dates in a mesocycle and its child documents by the given number of days.
|
|
132
|
+
* Mutates the passed-in objects in place.
|
|
133
|
+
*
|
|
134
|
+
* @param mesocycle The mesocycle to shift. Its startDate is mutated directly.
|
|
135
|
+
* @param microcycles The microcycles belonging to this mesocycle (pre-filtered). Mutated in place.
|
|
136
|
+
* @param sessions The sessions belonging to these microcycles (pre-filtered). Mutated in place.
|
|
137
|
+
* @param daysDelta The number of days to shift (positive = forward, negative = backward).
|
|
138
|
+
*/
|
|
139
|
+
static shiftMesocycleDates(mesocycle, microcycles, sessions, daysDelta) {
|
|
140
|
+
if (mesocycle.startDate != null) {
|
|
141
|
+
mesocycle.startDate = DateService.addDays(mesocycle.startDate, daysDelta);
|
|
142
|
+
}
|
|
143
|
+
for (const microcycle of microcycles) {
|
|
144
|
+
microcycle.startDate = DateService.addDays(microcycle.startDate, daysDelta);
|
|
145
|
+
microcycle.endDate = DateService.addDays(microcycle.endDate, daysDelta);
|
|
146
|
+
}
|
|
147
|
+
for (const session of sessions) {
|
|
148
|
+
session.startTime = DateService.addDays(session.startTime, daysDelta);
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
/**
|
|
152
|
+
* Detects whether any mesocycles in the provided list have overlapping date ranges.
|
|
153
|
+
* Two mesocycles overlap if their projected date ranges intersect.
|
|
154
|
+
*
|
|
155
|
+
* @param mesocycles The mesocycles to check.
|
|
156
|
+
* @param mesocycleToMicrocyclesMap A map from mesocycle ID to its microcycles.
|
|
157
|
+
*/
|
|
158
|
+
static detectMesocycleOverlap(mesocycles, mesocycleToMicrocyclesMap) {
|
|
159
|
+
const overlappingPairs = [];
|
|
160
|
+
const ranges = [];
|
|
161
|
+
for (const mesocycle of mesocycles) {
|
|
162
|
+
const start = mesocycle.startDate;
|
|
163
|
+
if (start == null) {
|
|
164
|
+
continue;
|
|
165
|
+
}
|
|
166
|
+
const microcycles = mesocycleToMicrocyclesMap.get(mesocycle._id) ?? [];
|
|
167
|
+
const end = this.calculateProjectedEndDate(mesocycle, microcycles);
|
|
168
|
+
if (end == null) {
|
|
169
|
+
continue;
|
|
170
|
+
}
|
|
171
|
+
ranges.push({ id: mesocycle._id, start, end });
|
|
172
|
+
}
|
|
173
|
+
ranges.sort((a, b) => a.start.getTime() - b.start.getTime());
|
|
174
|
+
for (let i = 0; i < ranges.length; i++) {
|
|
175
|
+
for (let j = i + 1; j < ranges.length; j++) {
|
|
176
|
+
if (ranges[j].start.getTime() < ranges[i].end.getTime()) {
|
|
177
|
+
overlappingPairs.push([ranges[i].id, ranges[j].id]);
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
return { hasOverlap: overlappingPairs.length > 0, overlappingPairs };
|
|
182
|
+
}
|
|
183
|
+
/**
|
|
184
|
+
* Returns the earliest allowed start date for a new mesocycle. This is the later
|
|
185
|
+
* of: the projected end of the last existing mesocycle, or today.
|
|
186
|
+
*
|
|
187
|
+
* @param existingMesocycles All existing mesocycles.
|
|
188
|
+
* @param mesocycleToMicrocyclesMap A map from mesocycle ID to its microcycles.
|
|
189
|
+
*/
|
|
190
|
+
static getEarliestAllowedStartDate(existingMesocycles, mesocycleToMicrocyclesMap) {
|
|
191
|
+
let latestEnd = new Date();
|
|
192
|
+
for (const mesocycle of existingMesocycles) {
|
|
193
|
+
if (mesocycle.completedDate != null) {
|
|
194
|
+
continue;
|
|
195
|
+
}
|
|
196
|
+
const microcycles = mesocycleToMicrocyclesMap.get(mesocycle._id) ?? [];
|
|
197
|
+
const projectedEnd = this.calculateProjectedEndDate(mesocycle, microcycles);
|
|
198
|
+
if (projectedEnd != null && projectedEnd.getTime() > latestEnd.getTime()) {
|
|
199
|
+
latestEnd = projectedEnd;
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
return latestEnd;
|
|
203
|
+
}
|
|
110
204
|
/**
|
|
111
205
|
* Cleans up incomplete microcycles and their associated documents.
|
|
112
206
|
*
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"WorkoutMesocycleService.js","sourceRoot":"","sources":["../../../../src/services/workout/Mesocycle/WorkoutMesocycleService.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,uBAAuB,CAAC;AAKpD,OAAO,EAAE,SAAS,EAAyB,MAAM,gDAAgD,CAAC;AAElG,OAAO,EAAE,uBAAuB,EAAE,MAAM,iDAAiD,CAAC;AAK1F,OAAO,wBAAwB,MAAM,2CAA2C,CAAC;AACjF,OAAO,2BAA2B,MAAM,kCAAkC,CAAC;AAE3E;;GAEG;AACH,MAAM,CAAC,OAAO,OAAO,uBAAuB;IAC1C;;;;;;;;;;;;;;;;;;;;;;;OAuBG;IACH,MAAM,CAAC,yBAAyB,CAC9B,SAA2B,EAC3B,YAA0C,EAC1C,SAA4B,EAC5B,cAAsC,EACtC,sBAA2C,EAAE,EAC7C,mBAAqC,EAAE,EACvC,2BAAqD,EAAE,EACvD,eAA6B,EAAE,EAC/B,SAAgB;QAQhB,4FAA4F;QAC5F,6FAA6F;QAC7F,+BAA+B;QAC/B,IAAI,SAAS,CAAC,SAAS,KAAK,SAAS,CAAC,QAAQ,EAAE,CAAC;YAC/C,OAAO,EAAE,CAAC;QACZ,CAAC;QAED,0DAA0D;QAC1D,MAAM,aAAa,GAAG,IAAI,CAAC,4BAA4B,CACrD,SAAS,EACT,mBAAmB,EACnB,gBAAgB,EAChB,wBAAwB,EACxB,YAAY,CACb,CAAC;QAEF,4CAA4C;QAC5C,MAAM,gBAAgB,GAAG,mBAAmB,CAAC,MAAM,CACjD,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,aAAa,CAAC,mBAAmB,CAAC,QAAQ,CAAC,CAAC,CAAC,GAAG,CAAC,CAC1D,CAAC;QACF,MAAM,aAAa,GAAG,gBAAgB,CAAC,MAAM,CAC3C,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,aAAa,CAAC,gBAAgB,CAAC,QAAQ,CAAC,CAAC,CAAC,GAAG,CAAC,CACvD,CAAC;QACF,MAAM,qBAAqB,GAAG,wBAAwB,CAAC,MAAM,CAC3D,CAAC,EAAE,EAAE,EAAE,CAAC,CAAC,aAAa,CAAC,wBAAwB,CAAC,QAAQ,CAAC,EAAE,CAAC,GAAG,CAAC,CACjE,CAAC;QACF,MAAM,SAAS,GAAG,YAAY,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,aAAa,CAAC,YAAY,CAAC,QAAQ,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC;QAE1F,0CAA0C;QAC1C,MAAM,OAAO,GAAG,IAAI,2BAA2B,CAC7C,SAAS,EACT,YAAY,EACZ,SAAS,EACT,cAAc,EACd,gBAAgB,EAChB,aAAa,EACb,qBAAqB,EACrB,SAAS,CACV,CAAC;QAEF,2EAA2E;QAC3E,mEAAmE;QACnE,OAAO,CAAC,8BAA8B,CACpC,wBAAwB,CAAC,iCAAiC,CACxD,SAAS,CAAC,gCAAgC,EAC1C,OAAO,CAAC,cAAc,EACtB,OAAO,CAAC,WAAW,CACpB,CACF,CAAC;QAEF,6FAA6F;QAC7F,MAAM,gBAAgB,GAAG,SAAS,CAAC,sBAAsB,IAAI,CAAC,CAAC;QAC/D,MAAM,qBAAqB,GAAG,gBAAgB,GAAG,CAAC,CAAC;QAEnD,0CAA0C;QAC1C,MAAM,oBAAoB,GAAG,OAAO,CAAC,kBAAkB,CAAC,MAAM,CAAC;QAC/D,IAAI,WAAiB,CAAC;QAEtB,IAAI,oBAAoB,KAAK,CAAC,EAAE,CAAC;YAC/B,WAAW,GAAG,SAAS,
|
|
1
|
+
{"version":3,"file":"WorkoutMesocycleService.js","sourceRoot":"","sources":["../../../../src/services/workout/Mesocycle/WorkoutMesocycleService.ts"],"names":[],"mappings":"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"}
|
|
@@ -117,11 +117,11 @@ export default class WorkoutMesocycleService {
|
|
|
117
117
|
let currentDate: Date;
|
|
118
118
|
|
|
119
119
|
if (startMicrocycleIndex === 0) {
|
|
120
|
-
currentDate = startDate
|
|
120
|
+
currentDate = startDate ?? new Date();
|
|
121
121
|
} else {
|
|
122
122
|
// Continue from where the last existing microcycle ended
|
|
123
123
|
const lastExistingMicrocycle = context.microcyclesInOrder[startMicrocycleIndex - 1];
|
|
124
|
-
currentDate =
|
|
124
|
+
currentDate = lastExistingMicrocycle.endDate;
|
|
125
125
|
}
|
|
126
126
|
|
|
127
127
|
// Generate remaining microcycles
|
|
@@ -140,7 +140,7 @@ export default class WorkoutMesocycleService {
|
|
|
140
140
|
const microcycle = WorkoutMicrocycleSchema.parse({
|
|
141
141
|
userId: mesocycle.userId,
|
|
142
142
|
workoutMesocycleId: mesocycle._id,
|
|
143
|
-
startDate:
|
|
143
|
+
startDate: currentDate,
|
|
144
144
|
endDate: DateService.addDays(currentDate, mesocycle.plannedMicrocycleLengthInDays)
|
|
145
145
|
});
|
|
146
146
|
context.addMicrocycle(microcycle);
|
|
@@ -153,7 +153,7 @@ export default class WorkoutMesocycleService {
|
|
|
153
153
|
});
|
|
154
154
|
|
|
155
155
|
// Move to next microcycle
|
|
156
|
-
currentDate =
|
|
156
|
+
currentDate = microcycle.endDate;
|
|
157
157
|
}
|
|
158
158
|
|
|
159
159
|
return {
|
|
@@ -177,6 +177,129 @@ export default class WorkoutMesocycleService {
|
|
|
177
177
|
};
|
|
178
178
|
}
|
|
179
179
|
|
|
180
|
+
/**
|
|
181
|
+
* Calculates the projected end date of a mesocycle based on its microcycles.
|
|
182
|
+
* If microcycles exist, uses the last microcycle's endDate. Otherwise,
|
|
183
|
+
* calculates from the mesocycle's start date and planned parameters.
|
|
184
|
+
*
|
|
185
|
+
* @param mesocycle The mesocycle to calculate the end date for.
|
|
186
|
+
* @param microcycles The microcycles belonging to this mesocycle (pre-filtered).
|
|
187
|
+
*/
|
|
188
|
+
static calculateProjectedEndDate(
|
|
189
|
+
mesocycle: WorkoutMesocycle,
|
|
190
|
+
microcycles: WorkoutMicrocycle[]
|
|
191
|
+
): Date | null {
|
|
192
|
+
const sorted = [...microcycles].sort((a, b) => a.startDate.getTime() - b.startDate.getTime());
|
|
193
|
+
|
|
194
|
+
if (sorted.length > 0) {
|
|
195
|
+
return sorted[sorted.length - 1].endDate;
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
if (mesocycle.startDate == null) {
|
|
199
|
+
return null;
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
const totalMicrocycles = mesocycle.plannedMicrocycleCount ?? 6;
|
|
203
|
+
const totalDays = totalMicrocycles * mesocycle.plannedMicrocycleLengthInDays;
|
|
204
|
+
return DateService.addDays(mesocycle.startDate, totalDays);
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
/**
|
|
208
|
+
* Shifts all dates in a mesocycle and its child documents by the given number of days.
|
|
209
|
+
* Mutates the passed-in objects in place.
|
|
210
|
+
*
|
|
211
|
+
* @param mesocycle The mesocycle to shift. Its startDate is mutated directly.
|
|
212
|
+
* @param microcycles The microcycles belonging to this mesocycle (pre-filtered). Mutated in place.
|
|
213
|
+
* @param sessions The sessions belonging to these microcycles (pre-filtered). Mutated in place.
|
|
214
|
+
* @param daysDelta The number of days to shift (positive = forward, negative = backward).
|
|
215
|
+
*/
|
|
216
|
+
static shiftMesocycleDates(
|
|
217
|
+
mesocycle: WorkoutMesocycle,
|
|
218
|
+
microcycles: WorkoutMicrocycle[],
|
|
219
|
+
sessions: WorkoutSession[],
|
|
220
|
+
daysDelta: number
|
|
221
|
+
): void {
|
|
222
|
+
if (mesocycle.startDate != null) {
|
|
223
|
+
mesocycle.startDate = DateService.addDays(mesocycle.startDate, daysDelta);
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
for (const microcycle of microcycles) {
|
|
227
|
+
microcycle.startDate = DateService.addDays(microcycle.startDate, daysDelta);
|
|
228
|
+
microcycle.endDate = DateService.addDays(microcycle.endDate, daysDelta);
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
for (const session of sessions) {
|
|
232
|
+
session.startTime = DateService.addDays(session.startTime, daysDelta);
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
/**
|
|
237
|
+
* Detects whether any mesocycles in the provided list have overlapping date ranges.
|
|
238
|
+
* Two mesocycles overlap if their projected date ranges intersect.
|
|
239
|
+
*
|
|
240
|
+
* @param mesocycles The mesocycles to check.
|
|
241
|
+
* @param mesocycleToMicrocyclesMap A map from mesocycle ID to its microcycles.
|
|
242
|
+
*/
|
|
243
|
+
static detectMesocycleOverlap(
|
|
244
|
+
mesocycles: WorkoutMesocycle[],
|
|
245
|
+
mesocycleToMicrocyclesMap: Map<UUID, WorkoutMicrocycle[]>
|
|
246
|
+
): { hasOverlap: boolean; overlappingPairs: Array<[UUID, UUID]> } {
|
|
247
|
+
const overlappingPairs: Array<[UUID, UUID]> = [];
|
|
248
|
+
|
|
249
|
+
const ranges: Array<{ id: UUID; start: Date; end: Date }> = [];
|
|
250
|
+
for (const mesocycle of mesocycles) {
|
|
251
|
+
const start = mesocycle.startDate;
|
|
252
|
+
if (start == null) {
|
|
253
|
+
continue;
|
|
254
|
+
}
|
|
255
|
+
const microcycles = mesocycleToMicrocyclesMap.get(mesocycle._id) ?? [];
|
|
256
|
+
const end = this.calculateProjectedEndDate(mesocycle, microcycles);
|
|
257
|
+
if (end == null) {
|
|
258
|
+
continue;
|
|
259
|
+
}
|
|
260
|
+
ranges.push({ id: mesocycle._id, start, end });
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
ranges.sort((a, b) => a.start.getTime() - b.start.getTime());
|
|
264
|
+
|
|
265
|
+
for (let i = 0; i < ranges.length; i++) {
|
|
266
|
+
for (let j = i + 1; j < ranges.length; j++) {
|
|
267
|
+
if (ranges[j].start.getTime() < ranges[i].end.getTime()) {
|
|
268
|
+
overlappingPairs.push([ranges[i].id, ranges[j].id]);
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
return { hasOverlap: overlappingPairs.length > 0, overlappingPairs };
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
/**
|
|
277
|
+
* Returns the earliest allowed start date for a new mesocycle. This is the later
|
|
278
|
+
* of: the projected end of the last existing mesocycle, or today.
|
|
279
|
+
*
|
|
280
|
+
* @param existingMesocycles All existing mesocycles.
|
|
281
|
+
* @param mesocycleToMicrocyclesMap A map from mesocycle ID to its microcycles.
|
|
282
|
+
*/
|
|
283
|
+
static getEarliestAllowedStartDate(
|
|
284
|
+
existingMesocycles: WorkoutMesocycle[],
|
|
285
|
+
mesocycleToMicrocyclesMap: Map<UUID, WorkoutMicrocycle[]>
|
|
286
|
+
): Date {
|
|
287
|
+
let latestEnd = new Date();
|
|
288
|
+
|
|
289
|
+
for (const mesocycle of existingMesocycles) {
|
|
290
|
+
if (mesocycle.completedDate != null) {
|
|
291
|
+
continue;
|
|
292
|
+
}
|
|
293
|
+
const microcycles = mesocycleToMicrocyclesMap.get(mesocycle._id) ?? [];
|
|
294
|
+
const projectedEnd = this.calculateProjectedEndDate(mesocycle, microcycles);
|
|
295
|
+
if (projectedEnd != null && projectedEnd.getTime() > latestEnd.getTime()) {
|
|
296
|
+
latestEnd = projectedEnd;
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
return latestEnd;
|
|
301
|
+
}
|
|
302
|
+
|
|
180
303
|
/**
|
|
181
304
|
* Cleans up incomplete microcycles and their associated documents.
|
|
182
305
|
*
|
package/package.json
CHANGED