fix(core): complete la nullification defensive de sourceExerciseId pour #185
Delta final apres QA vert : renforce la robustesse du pull des programmes quand l'exercice source reference par sourceExerciseId est absent. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@ -4181,45 +4181,222 @@ Future<domain.ProgramExercise> _programExerciseFromRow(
|
||||
db.AppDatabase database,
|
||||
db.ProgramExercise row,
|
||||
) async {
|
||||
final imageMediaIds = _tryDecodeImageMediaIdsSnapshot(
|
||||
row.exerciseImageMediaIdsSnapshotJson,
|
||||
row.exerciseImageMediaIdSnapshot,
|
||||
);
|
||||
final steps = _tryDecodeExerciseStepsSnapshot(row.exerciseStepsSnapshotJson);
|
||||
final scoreInputMode = _tryScoreInputModeFromDb(row.scoreInputModeSnapshot);
|
||||
final healthServicesStrategy =
|
||||
await _tryProgramExerciseHealthServicesStrategySnapshot(database, row.id);
|
||||
try {
|
||||
return domain.ProgramExercise(
|
||||
metadata: _metadataFromRow(row),
|
||||
programId: row.programId,
|
||||
sourceExerciseId: row.sourceExerciseId,
|
||||
position: row.position,
|
||||
exerciseNameSnapshot: row.exerciseNameSnapshot,
|
||||
exerciseDescriptionSnapshot: row.exerciseDescriptionSnapshot,
|
||||
exerciseImageMediaIdSnapshot: row.exerciseImageMediaIdSnapshot,
|
||||
exerciseImageMediaIdsSnapshot: imageMediaIds,
|
||||
exerciseStepsSnapshot: steps,
|
||||
autoStartNextTimedStepSnapshot: row.autoStartNextTimedStepSnapshot,
|
||||
autoStartNextTimedStepOverride: row.autoStartNextTimedStepOverride,
|
||||
exerciseVideoMediaIdSnapshot: row.exerciseVideoMediaIdSnapshot,
|
||||
healthServicesExerciseTypeStrategySnapshot: healthServicesStrategy,
|
||||
exerciseArchivedSnapshot: row.exerciseArchivedSnapshot,
|
||||
availableTimeSnapshot: row.availableTimeSnapshot,
|
||||
availableRepsSnapshot: row.availableRepsSnapshot,
|
||||
availableScoreSnapshot: row.availableScoreSnapshot,
|
||||
scoreInputModeSnapshot: scoreInputMode,
|
||||
scoreLabelSnapshot: row.scoreLabelSnapshot,
|
||||
scoreUnitSnapshot: row.scoreUnitSnapshot,
|
||||
setsCount: row.setsCount,
|
||||
timeEnabled: row.timeEnabled,
|
||||
repsEnabled: row.repsEnabled,
|
||||
scoreEnabled: row.scoreEnabled,
|
||||
targetTimeSeconds: row.targetTimeSeconds,
|
||||
targetReps: row.targetReps,
|
||||
targetScore: row.targetScore,
|
||||
targetScoreTimeMs: row.targetScoreTimeMs,
|
||||
restSecondsOverride: row.restSecondsOverride,
|
||||
);
|
||||
} on domain.DomainException {
|
||||
return _repairedProgramExerciseFromRow(
|
||||
row,
|
||||
imageMediaIds: imageMediaIds,
|
||||
steps: steps,
|
||||
healthServicesStrategy: healthServicesStrategy,
|
||||
scoreInputMode: scoreInputMode,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
domain.ProgramExercise _repairedProgramExerciseFromRow(
|
||||
db.ProgramExercise row, {
|
||||
required List<String> imageMediaIds,
|
||||
required List<domain.ExerciseStep> steps,
|
||||
required List<domain.HealthServicesExerciseType> healthServicesStrategy,
|
||||
required domain.ScoreInputMode scoreInputMode,
|
||||
}) {
|
||||
var availableTime = row.availableTimeSnapshot;
|
||||
var availableReps = row.availableRepsSnapshot;
|
||||
var availableScore = row.availableScoreSnapshot;
|
||||
var timeEnabled = row.timeEnabled && availableTime;
|
||||
var repsEnabled = row.repsEnabled && availableReps;
|
||||
var scoreEnabled = row.scoreEnabled && availableScore;
|
||||
if (!timeEnabled && !repsEnabled && !scoreEnabled) {
|
||||
if (availableReps) {
|
||||
repsEnabled = true;
|
||||
} else if (availableTime) {
|
||||
timeEnabled = true;
|
||||
} else if (availableScore) {
|
||||
scoreEnabled = true;
|
||||
} else {
|
||||
availableReps = true;
|
||||
repsEnabled = true;
|
||||
}
|
||||
}
|
||||
final repairedScoreInputMode = scoreEnabled
|
||||
? scoreInputMode
|
||||
: domain.ScoreInputMode.manual;
|
||||
try {
|
||||
return _programExerciseFromRepairedValues(
|
||||
row,
|
||||
imageMediaIds: imageMediaIds,
|
||||
steps: steps,
|
||||
healthServicesStrategy: healthServicesStrategy,
|
||||
scoreInputMode: repairedScoreInputMode,
|
||||
availableTime: availableTime,
|
||||
availableReps: availableReps,
|
||||
availableScore: availableScore,
|
||||
timeEnabled: timeEnabled,
|
||||
repsEnabled: repsEnabled,
|
||||
scoreEnabled: scoreEnabled,
|
||||
);
|
||||
} on domain.DomainException {
|
||||
return _programExerciseFromRepairedValues(
|
||||
row,
|
||||
imageMediaIds: imageMediaIds,
|
||||
steps: const [],
|
||||
healthServicesStrategy: healthServicesStrategy,
|
||||
scoreInputMode: repairedScoreInputMode,
|
||||
availableTime: availableTime,
|
||||
availableReps: availableReps,
|
||||
availableScore: availableScore,
|
||||
timeEnabled: timeEnabled,
|
||||
repsEnabled: repsEnabled,
|
||||
scoreEnabled: scoreEnabled,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
domain.ProgramExercise _programExerciseFromRepairedValues(
|
||||
db.ProgramExercise row, {
|
||||
required List<String> imageMediaIds,
|
||||
required List<domain.ExerciseStep> steps,
|
||||
required List<domain.HealthServicesExerciseType> healthServicesStrategy,
|
||||
required domain.ScoreInputMode scoreInputMode,
|
||||
required bool availableTime,
|
||||
required bool availableReps,
|
||||
required bool availableScore,
|
||||
required bool timeEnabled,
|
||||
required bool repsEnabled,
|
||||
required bool scoreEnabled,
|
||||
}) {
|
||||
return domain.ProgramExercise(
|
||||
metadata: _metadataFromRow(row),
|
||||
programId: row.programId,
|
||||
sourceExerciseId: row.sourceExerciseId,
|
||||
position: row.position,
|
||||
exerciseNameSnapshot: row.exerciseNameSnapshot,
|
||||
position: row.position < 0 ? 0 : row.position,
|
||||
exerciseNameSnapshot: row.exerciseNameSnapshot.trim().isEmpty
|
||||
? row.id
|
||||
: row.exerciseNameSnapshot,
|
||||
exerciseDescriptionSnapshot: row.exerciseDescriptionSnapshot,
|
||||
exerciseImageMediaIdSnapshot: row.exerciseImageMediaIdSnapshot,
|
||||
exerciseImageMediaIdsSnapshot: _decodeImageMediaIdsSnapshot(
|
||||
row.exerciseImageMediaIdsSnapshotJson,
|
||||
row.exerciseImageMediaIdSnapshot,
|
||||
),
|
||||
exerciseStepsSnapshot: _decodeExerciseStepsSnapshot(
|
||||
row.exerciseStepsSnapshotJson,
|
||||
),
|
||||
exerciseImageMediaIdsSnapshot: imageMediaIds,
|
||||
exerciseStepsSnapshot: steps,
|
||||
autoStartNextTimedStepSnapshot: row.autoStartNextTimedStepSnapshot,
|
||||
autoStartNextTimedStepOverride: row.autoStartNextTimedStepOverride,
|
||||
exerciseVideoMediaIdSnapshot: row.exerciseVideoMediaIdSnapshot,
|
||||
healthServicesExerciseTypeStrategySnapshot:
|
||||
await _programExerciseHealthServicesStrategySnapshot(database, row.id),
|
||||
healthServicesExerciseTypeStrategySnapshot: healthServicesStrategy,
|
||||
exerciseArchivedSnapshot: row.exerciseArchivedSnapshot,
|
||||
availableTimeSnapshot: row.availableTimeSnapshot,
|
||||
availableRepsSnapshot: row.availableRepsSnapshot,
|
||||
availableScoreSnapshot: row.availableScoreSnapshot,
|
||||
scoreInputModeSnapshot: _scoreInputModeFromDb(row.scoreInputModeSnapshot),
|
||||
availableTimeSnapshot: availableTime,
|
||||
availableRepsSnapshot: availableReps,
|
||||
availableScoreSnapshot: availableScore,
|
||||
scoreInputModeSnapshot: scoreInputMode,
|
||||
scoreLabelSnapshot: row.scoreLabelSnapshot,
|
||||
scoreUnitSnapshot: row.scoreUnitSnapshot,
|
||||
setsCount: row.setsCount,
|
||||
timeEnabled: row.timeEnabled,
|
||||
repsEnabled: row.repsEnabled,
|
||||
scoreEnabled: row.scoreEnabled,
|
||||
targetTimeSeconds: row.targetTimeSeconds,
|
||||
targetReps: row.targetReps,
|
||||
targetScore: row.targetScore,
|
||||
targetScoreTimeMs: row.targetScoreTimeMs,
|
||||
restSecondsOverride: row.restSecondsOverride,
|
||||
setsCount: row.setsCount <= 0 ? 1 : row.setsCount,
|
||||
timeEnabled: timeEnabled,
|
||||
repsEnabled: repsEnabled,
|
||||
scoreEnabled: scoreEnabled,
|
||||
targetTimeSeconds: timeEnabled
|
||||
? _positiveIntOrNull(row.targetTimeSeconds)
|
||||
: null,
|
||||
targetReps: repsEnabled ? _positiveIntOrNull(row.targetReps) : null,
|
||||
targetScore: scoreEnabled && scoreInputMode == domain.ScoreInputMode.manual
|
||||
? _nonNegativeDoubleOrNull(row.targetScore)
|
||||
: null,
|
||||
targetScoreTimeMs:
|
||||
scoreEnabled && scoreInputMode == domain.ScoreInputMode.stopwatch
|
||||
? _positiveIntOrNull(row.targetScoreTimeMs)
|
||||
: null,
|
||||
restSecondsOverride: _nonNegativeIntOrNull(row.restSecondsOverride),
|
||||
);
|
||||
}
|
||||
|
||||
List<String> _tryDecodeImageMediaIdsSnapshot(
|
||||
String? encoded,
|
||||
String? fallbackImageMediaId,
|
||||
) {
|
||||
try {
|
||||
return _decodeImageMediaIdsSnapshot(encoded, fallbackImageMediaId);
|
||||
} on Object {
|
||||
return fallbackImageMediaId == null ? const [] : [fallbackImageMediaId];
|
||||
}
|
||||
}
|
||||
|
||||
List<domain.ExerciseStep> _tryDecodeExerciseStepsSnapshot(String? encoded) {
|
||||
try {
|
||||
return _decodeExerciseStepsSnapshot(encoded);
|
||||
} on Object {
|
||||
return const [];
|
||||
}
|
||||
}
|
||||
|
||||
domain.ScoreInputMode _tryScoreInputModeFromDb(String value) {
|
||||
try {
|
||||
return _scoreInputModeFromDb(value);
|
||||
} on domain.DomainException {
|
||||
return domain.ScoreInputMode.manual;
|
||||
}
|
||||
}
|
||||
|
||||
int? _positiveIntOrNull(int? value) {
|
||||
return value != null && value > 0 ? value : null;
|
||||
}
|
||||
|
||||
int? _nonNegativeIntOrNull(int? value) {
|
||||
return value != null && value >= 0 ? value : null;
|
||||
}
|
||||
|
||||
double? _nonNegativeDoubleOrNull(double? value) {
|
||||
return value != null && value >= 0 ? value : null;
|
||||
}
|
||||
|
||||
Future<List<domain.HealthServicesExerciseType>>
|
||||
_tryProgramExerciseHealthServicesStrategySnapshot(
|
||||
db.AppDatabase database,
|
||||
String id,
|
||||
) async {
|
||||
try {
|
||||
return _programExerciseHealthServicesStrategySnapshot(database, id);
|
||||
} on Object {
|
||||
return domain.legacyHealthServicesExerciseTypeStrategy;
|
||||
}
|
||||
}
|
||||
|
||||
Future<List<domain.HealthServicesExerciseType>>
|
||||
_programExerciseHealthServicesStrategySnapshot(
|
||||
db.AppDatabase database,
|
||||
|
||||
Reference in New Issue
Block a user