feat(statistiques): implémentation backend statistiques de progression (ticket #82)
Lots B1+B2+B3 : schéma/domaine, requêtes d'agrégation et ports/use cases pour les statistiques de progression, avec tests associés. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@ -27,6 +27,232 @@ final class StarterContent {
|
||||
final WorkoutTemplate workoutTemplate;
|
||||
}
|
||||
|
||||
enum ProgressionPeriod { fourWeeks, threeMonths, all }
|
||||
|
||||
enum ProgressionMeasure { manualScore, stopwatchScore, reps, time }
|
||||
|
||||
enum ProgressionRangeLabelKind { sinceDate, sinceFirstSession, none }
|
||||
|
||||
enum ProgressionSeriesState {
|
||||
ready,
|
||||
singlePoint,
|
||||
emptyForPeriod,
|
||||
emptyButHasAllTimeData,
|
||||
noGraphableMeasure,
|
||||
textScoreOnly,
|
||||
}
|
||||
|
||||
enum ProgressionSeriesSummaryKind { bestLast, totals, none }
|
||||
|
||||
final class ProgressionDateRange {
|
||||
const ProgressionDateRange({required this.startedAt, required this.endedAt});
|
||||
|
||||
final DateTime? startedAt;
|
||||
final DateTime endedAt;
|
||||
}
|
||||
|
||||
final class ProgressionGlobalStatsData {
|
||||
const ProgressionGlobalStatsData({
|
||||
required this.completedSessionCount,
|
||||
required this.totalActiveMs,
|
||||
required this.activeWeekStarts,
|
||||
required this.hasAnyCompletedHistory,
|
||||
this.firstCompletedSessionAt,
|
||||
});
|
||||
|
||||
final int completedSessionCount;
|
||||
final int totalActiveMs;
|
||||
final List<DateTime> activeWeekStarts;
|
||||
final bool hasAnyCompletedHistory;
|
||||
final DateTime? firstCompletedSessionAt;
|
||||
}
|
||||
|
||||
final class ProgressionExerciseOptionData {
|
||||
const ProgressionExerciseOptionData({
|
||||
required this.exerciseKey,
|
||||
required this.nameSnapshot,
|
||||
required this.isArchived,
|
||||
required this.lastPerformedAt,
|
||||
});
|
||||
|
||||
final String exerciseKey;
|
||||
final String nameSnapshot;
|
||||
final bool isArchived;
|
||||
final DateTime lastPerformedAt;
|
||||
}
|
||||
|
||||
final class ProgressionMeasureOptionData {
|
||||
const ProgressionMeasureOptionData({
|
||||
required this.measure,
|
||||
required this.label,
|
||||
this.scoreLabel,
|
||||
this.scoreUnit,
|
||||
required this.lowerIsBetter,
|
||||
});
|
||||
|
||||
final ProgressionMeasure measure;
|
||||
final String label;
|
||||
final String? scoreLabel;
|
||||
final String? scoreUnit;
|
||||
final bool lowerIsBetter;
|
||||
}
|
||||
|
||||
final class ProgressionExerciseSeriesData {
|
||||
const ProgressionExerciseSeriesData({
|
||||
required this.exerciseKey,
|
||||
required this.exerciseNameSnapshot,
|
||||
required this.measure,
|
||||
required this.points,
|
||||
required this.hasAnyAllTimeData,
|
||||
required this.hasAnyCompletedExerciseResult,
|
||||
});
|
||||
|
||||
final String exerciseKey;
|
||||
final String exerciseNameSnapshot;
|
||||
final ProgressionMeasureOptionData measure;
|
||||
final List<ProgressionPointData> points;
|
||||
final bool hasAnyAllTimeData;
|
||||
final bool hasAnyCompletedExerciseResult;
|
||||
}
|
||||
|
||||
final class ProgressionPointData {
|
||||
const ProgressionPointData({
|
||||
required this.workoutHistoryId,
|
||||
required this.workoutNameSnapshot,
|
||||
required this.startedAt,
|
||||
required this.value,
|
||||
required this.rawValue,
|
||||
});
|
||||
|
||||
final String workoutHistoryId;
|
||||
final String workoutNameSnapshot;
|
||||
final DateTime startedAt;
|
||||
final double value;
|
||||
final Object rawValue;
|
||||
}
|
||||
|
||||
abstract interface class ProgressionStatsRepository {
|
||||
Future<ProgressionGlobalStatsData> readGlobalStats(
|
||||
ProgressionDateRange range,
|
||||
);
|
||||
Future<List<ProgressionExerciseOptionData>> listExerciseOptions(
|
||||
ProgressionDateRange range,
|
||||
);
|
||||
Future<List<ProgressionMeasureOptionData>> listMeasureOptions({
|
||||
required ProgressionDateRange range,
|
||||
required String exerciseKey,
|
||||
});
|
||||
Future<ProgressionExerciseSeriesData> readExerciseSeries({
|
||||
required ProgressionDateRange range,
|
||||
required String exerciseKey,
|
||||
required ProgressionMeasure measure,
|
||||
});
|
||||
}
|
||||
|
||||
final class ProgressionOverview {
|
||||
const ProgressionOverview({
|
||||
required this.period,
|
||||
required this.rangeLabelKind,
|
||||
required this.completedSessionCount,
|
||||
required this.totalActiveMs,
|
||||
required this.activeWeekCount,
|
||||
required this.totalWeekCount,
|
||||
required this.hasAnyCompletedHistory,
|
||||
required this.exerciseOptions,
|
||||
});
|
||||
|
||||
final ProgressionPeriod period;
|
||||
final ProgressionRangeLabelKind rangeLabelKind;
|
||||
final int completedSessionCount;
|
||||
final int totalActiveMs;
|
||||
final int activeWeekCount;
|
||||
final int? totalWeekCount;
|
||||
final bool hasAnyCompletedHistory;
|
||||
final List<ProgressionExerciseOption> exerciseOptions;
|
||||
}
|
||||
|
||||
final class ProgressionExerciseOption {
|
||||
const ProgressionExerciseOption({
|
||||
required this.exerciseKey,
|
||||
required this.nameSnapshot,
|
||||
required this.isArchived,
|
||||
required this.lastPerformedAt,
|
||||
required this.measures,
|
||||
});
|
||||
|
||||
final String exerciseKey;
|
||||
final String nameSnapshot;
|
||||
final bool isArchived;
|
||||
final DateTime lastPerformedAt;
|
||||
final List<ProgressionMeasureOption> measures;
|
||||
}
|
||||
|
||||
final class ProgressionMeasureOption {
|
||||
const ProgressionMeasureOption({
|
||||
required this.measure,
|
||||
required this.label,
|
||||
this.scoreLabel,
|
||||
this.scoreUnit,
|
||||
required this.lowerIsBetter,
|
||||
});
|
||||
|
||||
final ProgressionMeasure measure;
|
||||
final String label;
|
||||
final String? scoreLabel;
|
||||
final String? scoreUnit;
|
||||
final bool lowerIsBetter;
|
||||
}
|
||||
|
||||
final class ProgressionExerciseSeries {
|
||||
const ProgressionExerciseSeries({
|
||||
required this.exerciseKey,
|
||||
required this.exerciseNameSnapshot,
|
||||
required this.measure,
|
||||
required this.points,
|
||||
required this.summary,
|
||||
required this.state,
|
||||
});
|
||||
|
||||
final String exerciseKey;
|
||||
final String exerciseNameSnapshot;
|
||||
final ProgressionMeasureOption measure;
|
||||
final List<ProgressionPoint> points;
|
||||
final ProgressionSeriesSummary summary;
|
||||
final ProgressionSeriesState state;
|
||||
}
|
||||
|
||||
final class ProgressionPoint {
|
||||
const ProgressionPoint({
|
||||
required this.workoutHistoryId,
|
||||
required this.workoutNameSnapshot,
|
||||
required this.startedAt,
|
||||
required this.value,
|
||||
required this.rawValue,
|
||||
});
|
||||
|
||||
final String workoutHistoryId;
|
||||
final String workoutNameSnapshot;
|
||||
final DateTime startedAt;
|
||||
final double value;
|
||||
final Object rawValue;
|
||||
}
|
||||
|
||||
final class ProgressionSeriesSummary {
|
||||
const ProgressionSeriesSummary({
|
||||
required this.kind,
|
||||
this.best,
|
||||
this.last,
|
||||
this.recentTotal,
|
||||
this.lastSessionTotal,
|
||||
});
|
||||
|
||||
final ProgressionSeriesSummaryKind kind;
|
||||
final Object? best;
|
||||
final Object? last;
|
||||
final Object? recentTotal;
|
||||
final Object? lastSessionTotal;
|
||||
}
|
||||
|
||||
abstract interface class StarterSeedStateRepository {
|
||||
Future<int> readAppliedStarterSeedVersion();
|
||||
Future<void> writeAppliedStarterSeedVersion(int version, DateTime appliedAt);
|
||||
|
||||
Reference in New Issue
Block a user