feat(exécution): dernière performance et meilleur score par exercice (ticket #81)
Ajoute la migration Drift v17 (colonne sourceExerciseIdSnapshot sur workout_history_set_results et workout_history_step_results, index associés), le port ExercisePerformanceReferenceRepository et son implémentation Drift, et le use case ExercisePerformanceReferenceUseCase (dernière performance + meilleur score par métrique active pour un exercice donné). Validé GO par Main : dart analyze propre (1 lint mineur de style), 178 tests (4 échecs préexistants sans rapport avec ce lot). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@ -3329,6 +3329,9 @@ final class CloseWorkoutSessionUseCase {
|
||||
final stepResults = await sessionRepository.listExerciseStepResults(
|
||||
sessionId,
|
||||
);
|
||||
final snapshots = _exerciseSnapshotsById(
|
||||
session.resolvedTemplateSnapshotJson,
|
||||
);
|
||||
final historyId = ids.newId();
|
||||
final historyResults = _historyResultsFromActiveResults(
|
||||
historyId: historyId,
|
||||
@ -3341,6 +3344,7 @@ final class CloseWorkoutSessionUseCase {
|
||||
final historyStepResults = _historyStepResultsFromActiveResults(
|
||||
historyId: historyId,
|
||||
results: stepResults,
|
||||
resolvedTemplateSnapshotJson: session.resolvedTemplateSnapshotJson,
|
||||
now: now,
|
||||
ids: ids,
|
||||
originDeviceId: originDeviceId,
|
||||
@ -3377,6 +3381,8 @@ final class CloseWorkoutSessionUseCase {
|
||||
'scoreInputModeSnapshot': result.scoreInputModeSnapshot.name,
|
||||
'scoreLabelSnapshot': result.scoreLabelSnapshot,
|
||||
'scoreUnitSnapshot': result.scoreUnitSnapshot,
|
||||
'sourceExerciseIdSnapshot':
|
||||
snapshots[result.exerciseSnapshotId]?.sourceExerciseId,
|
||||
'status': result.status.name,
|
||||
},
|
||||
)
|
||||
@ -3407,6 +3413,8 @@ final class CloseWorkoutSessionUseCase {
|
||||
'actualScore': result.actualScore,
|
||||
'actualScoreTimeMs': result.actualScoreTimeMs,
|
||||
'note': result.note,
|
||||
'sourceExerciseIdSnapshot':
|
||||
snapshots[result.exerciseSnapshotId]?.sourceExerciseId,
|
||||
},
|
||||
)
|
||||
.toList(),
|
||||
@ -3571,6 +3579,59 @@ void _validateExerciseSteps(List<ExerciseStep> steps) {
|
||||
}
|
||||
}
|
||||
|
||||
final class ExercisePerformanceReferenceUseCase {
|
||||
const ExercisePerformanceReferenceUseCase({required this.repository});
|
||||
|
||||
final ExercisePerformanceReferenceRepository repository;
|
||||
|
||||
Future<ExercisePerformanceReference> getExercisePerformanceReference({
|
||||
required String exerciseId,
|
||||
required ActivePerformanceMeasures activeMeasures,
|
||||
required int currentSetIndex,
|
||||
}) async {
|
||||
final hasAnyHistory = await repository.hasAnyCompletedHistoryForExercise(
|
||||
exerciseId,
|
||||
);
|
||||
if (!activeMeasures.hasAny) {
|
||||
return ExercisePerformanceReference(
|
||||
hasAnyHistoryForExercise: hasAnyHistory,
|
||||
);
|
||||
}
|
||||
|
||||
final last = await repository.findLatestSetPerformance(
|
||||
exerciseId: exerciseId,
|
||||
activeMeasures: activeMeasures,
|
||||
currentSetIndex: currentSetIndex,
|
||||
);
|
||||
final recordMetric = _recordMetricFor(activeMeasures);
|
||||
final record = recordMetric == null
|
||||
? null
|
||||
: await repository.findBestMetricPerformance(
|
||||
exerciseId: exerciseId,
|
||||
metric: recordMetric,
|
||||
scoreInputMode: activeMeasures.scoreInputMode,
|
||||
);
|
||||
return ExercisePerformanceReference(
|
||||
hasAnyHistoryForExercise: hasAnyHistory,
|
||||
last: last,
|
||||
record: record,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
PerformanceMetric? _recordMetricFor(ActivePerformanceMeasures measures) {
|
||||
if (measures.scoreEnabled) {
|
||||
return PerformanceMetric.score;
|
||||
}
|
||||
if (measures.repsEnabled) {
|
||||
return PerformanceMetric.reps;
|
||||
}
|
||||
if (measures.timeEnabled) {
|
||||
return PerformanceMetric.time;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
void _validateAuthInput({required String email, required String password}) {
|
||||
final normalizedEmail = email.trim();
|
||||
final hasBasicEmailShape = RegExp(
|
||||
@ -4055,6 +4116,7 @@ List<WorkoutHistorySetResult> _historyResultsFromActiveResults({
|
||||
result.scoreLabelSnapshot ?? snapshot?.scoreLabelSnapshot,
|
||||
scoreUnitSnapshot:
|
||||
result.scoreUnitSnapshot ?? snapshot?.scoreUnitSnapshot,
|
||||
sourceExerciseIdSnapshot: snapshot?.sourceExerciseId,
|
||||
startedAt: result.startedAt,
|
||||
completedAt: result.completedAt,
|
||||
status: result.status,
|
||||
@ -4089,6 +4151,7 @@ Map<String, _ResolvedExerciseSnapshot> _exerciseSnapshotsById(
|
||||
exerciseSnapshotId: id,
|
||||
programNameSnapshot: programName,
|
||||
exerciseNameSnapshot: exercise['exerciseNameSnapshot'] as String? ?? id,
|
||||
sourceExerciseId: exercise['sourceExerciseId'] as String?,
|
||||
timeEnabled: exercise['timeEnabled'] == true,
|
||||
repsEnabled: exercise['repsEnabled'] == true,
|
||||
scoreEnabled: exercise['scoreEnabled'] == true,
|
||||
@ -4118,6 +4181,7 @@ final class _ResolvedExerciseSnapshot {
|
||||
required this.exerciseSnapshotId,
|
||||
required this.programNameSnapshot,
|
||||
required this.exerciseNameSnapshot,
|
||||
this.sourceExerciseId,
|
||||
required this.timeEnabled,
|
||||
required this.repsEnabled,
|
||||
required this.scoreEnabled,
|
||||
@ -4136,6 +4200,7 @@ final class _ResolvedExerciseSnapshot {
|
||||
final String exerciseSnapshotId;
|
||||
final String programNameSnapshot;
|
||||
final String exerciseNameSnapshot;
|
||||
final String? sourceExerciseId;
|
||||
final bool timeEnabled;
|
||||
final bool repsEnabled;
|
||||
final bool scoreEnabled;
|
||||
@ -4181,11 +4246,14 @@ List<ExerciseStep> _exerciseStepsFromSnapshot(Object? value) {
|
||||
List<WorkoutHistoryStepResult> _historyStepResultsFromActiveResults({
|
||||
required String historyId,
|
||||
required List<ActiveExerciseStepResult> results,
|
||||
required String resolvedTemplateSnapshotJson,
|
||||
required DateTime now,
|
||||
required IdGenerator ids,
|
||||
required String originDeviceId,
|
||||
}) {
|
||||
final snapshots = _exerciseSnapshotsById(resolvedTemplateSnapshotJson);
|
||||
return results.map((result) {
|
||||
final snapshot = snapshots[result.exerciseSnapshotId];
|
||||
return WorkoutHistoryStepResult(
|
||||
metadata: _newMetadata(ids, originDeviceId, now),
|
||||
workoutHistoryId: historyId,
|
||||
@ -4214,6 +4282,7 @@ List<WorkoutHistoryStepResult> _historyStepResultsFromActiveResults({
|
||||
actualScore: result.actualScore,
|
||||
actualScoreTimeMs: result.actualScoreTimeMs,
|
||||
note: result.note,
|
||||
sourceExerciseIdSnapshot: snapshot?.sourceExerciseId,
|
||||
);
|
||||
}).toList();
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user