feat(data): modèle de données Drift pour la persistance locale
Définit les tables Drift et les identifiants locaux (local_id.dart, tables.dart), régénère app_database.g.dart. build_runner, flutter analyze et build APK debug validés. Versionne les fichiers générés .g.dart : projet solo sans pipeline CI qui relance build_runner, on privilégie un clone immédiatement buildable. Retire la règle **/*.g.dart (et freezed/gr.dart, non utilisés) du .gitignore en conséquence. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@ -1,16 +1,35 @@
|
||||
import 'package:drift/drift.dart';
|
||||
import 'package:drift_flutter/drift_flutter.dart';
|
||||
|
||||
final class AppDatabase extends GeneratedDatabase {
|
||||
AppDatabase(DatabaseConnection connection) : super.connect(connection);
|
||||
import 'tables.dart';
|
||||
|
||||
part 'app_database.g.dart';
|
||||
|
||||
@DriftDatabase(
|
||||
tables: [
|
||||
ActiveRestStates,
|
||||
ActiveSetResults,
|
||||
ActiveWorkoutSessions,
|
||||
ChangeLogEntries,
|
||||
Exercises,
|
||||
MediaAssets,
|
||||
ProgramExercises,
|
||||
Programs,
|
||||
WorkoutHistories,
|
||||
WorkoutHistorySetResults,
|
||||
WorkoutTemplateExerciseOverrides,
|
||||
WorkoutTemplatePrograms,
|
||||
WorkoutTemplates,
|
||||
],
|
||||
)
|
||||
final class AppDatabase extends _$AppDatabase {
|
||||
AppDatabase(super.executor);
|
||||
|
||||
factory AppDatabase.open() {
|
||||
return AppDatabase(
|
||||
driftDatabase(
|
||||
name: 'gametime',
|
||||
native: const DriftNativeOptions(
|
||||
shareAcrossIsolates: true,
|
||||
),
|
||||
native: const DriftNativeOptions(shareAcrossIsolates: true),
|
||||
),
|
||||
);
|
||||
}
|
||||
@ -19,5 +38,95 @@ final class AppDatabase extends GeneratedDatabase {
|
||||
int get schemaVersion => 1;
|
||||
|
||||
@override
|
||||
Iterable<TableInfo<Table, dynamic>> get allTables => const [];
|
||||
MigrationStrategy get migration {
|
||||
return MigrationStrategy(
|
||||
onCreate: (migrator) async {
|
||||
await migrator.createAll();
|
||||
await _createIndexes();
|
||||
},
|
||||
beforeOpen: (details) async {
|
||||
await customStatement('PRAGMA foreign_keys = ON');
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
Future<void> _createIndexes() async {
|
||||
for (final tableName in _syncableTableNames) {
|
||||
await customStatement(
|
||||
'CREATE INDEX IF NOT EXISTS idx_${tableName}_deleted_at '
|
||||
'ON $tableName (deleted_at)',
|
||||
);
|
||||
await customStatement(
|
||||
'CREATE INDEX IF NOT EXISTS idx_${tableName}_updated_at '
|
||||
'ON $tableName (updated_at)',
|
||||
);
|
||||
await customStatement(
|
||||
'CREATE INDEX IF NOT EXISTS idx_${tableName}_sync_state '
|
||||
'ON $tableName (sync_state)',
|
||||
);
|
||||
await customStatement(
|
||||
'CREATE INDEX IF NOT EXISTS idx_${tableName}_local_revision '
|
||||
'ON $tableName (local_revision)',
|
||||
);
|
||||
}
|
||||
|
||||
await customStatement(
|
||||
'CREATE INDEX IF NOT EXISTS idx_program_exercises_program_id '
|
||||
'ON program_exercises (program_id)',
|
||||
);
|
||||
await customStatement(
|
||||
'CREATE INDEX IF NOT EXISTS idx_workout_template_programs_template_id '
|
||||
'ON workout_template_programs (workout_template_id)',
|
||||
);
|
||||
await customStatement(
|
||||
'CREATE INDEX IF NOT EXISTS idx_active_set_results_session_id '
|
||||
'ON active_set_results (active_workout_session_id)',
|
||||
);
|
||||
await customStatement(
|
||||
'CREATE INDEX IF NOT EXISTS idx_active_rest_states_session_id '
|
||||
'ON active_rest_states (active_workout_session_id)',
|
||||
);
|
||||
await customStatement(
|
||||
'CREATE UNIQUE INDEX IF NOT EXISTS '
|
||||
'idx_active_workout_sessions_single_open '
|
||||
'ON active_workout_sessions ((1)) '
|
||||
'WHERE deleted_at IS NULL '
|
||||
"AND status IN ('running', 'paused', 'savedExit')",
|
||||
);
|
||||
await customStatement(
|
||||
'CREATE INDEX IF NOT EXISTS idx_workout_history_started_at '
|
||||
'ON workout_history (started_at)',
|
||||
);
|
||||
await customStatement(
|
||||
'CREATE INDEX IF NOT EXISTS idx_workout_history_set_results_history_id '
|
||||
'ON workout_history_set_results (workout_history_id)',
|
||||
);
|
||||
await customStatement(
|
||||
'CREATE INDEX IF NOT EXISTS idx_change_log_entity '
|
||||
'ON change_log (entity_type, entity_id)',
|
||||
);
|
||||
await customStatement(
|
||||
'CREATE INDEX IF NOT EXISTS idx_change_log_local_revision '
|
||||
'ON change_log (local_revision)',
|
||||
);
|
||||
await customStatement(
|
||||
'CREATE INDEX IF NOT EXISTS idx_change_log_synced_at '
|
||||
'ON change_log (synced_at)',
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const _syncableTableNames = [
|
||||
'active_rest_states',
|
||||
'active_set_results',
|
||||
'active_workout_sessions',
|
||||
'exercises',
|
||||
'media_assets',
|
||||
'program_exercises',
|
||||
'programs',
|
||||
'workout_history',
|
||||
'workout_history_set_results',
|
||||
'workout_template_exercise_overrides',
|
||||
'workout_template_programs',
|
||||
'workout_templates',
|
||||
];
|
||||
|
||||
25878
lib/infrastructure/local/app_database.g.dart
Normal file
25878
lib/infrastructure/local/app_database.g.dart
Normal file
File diff suppressed because it is too large
Load Diff
@ -1 +1,2 @@
|
||||
export 'app_database.dart';
|
||||
export 'local_id.dart';
|
||||
|
||||
28
lib/infrastructure/local/local_id.dart
Normal file
28
lib/infrastructure/local/local_id.dart
Normal file
@ -0,0 +1,28 @@
|
||||
import 'dart:math';
|
||||
|
||||
const _crockfordBase32 = '0123456789ABCDEFGHJKMNPQRSTVWXYZ';
|
||||
|
||||
final class LocalIdGenerator {
|
||||
LocalIdGenerator({Random? random}) : _random = random ?? Random.secure();
|
||||
|
||||
final Random _random;
|
||||
|
||||
String newUlid({DateTime? now}) {
|
||||
final timestamp = (now ?? DateTime.now().toUtc()).millisecondsSinceEpoch;
|
||||
final buffer = StringBuffer();
|
||||
|
||||
var remainingTimestamp = timestamp;
|
||||
final timestampChars = List<String>.filled(10, '0');
|
||||
for (var index = 9; index >= 0; index--) {
|
||||
timestampChars[index] = _crockfordBase32[remainingTimestamp & 0x1F];
|
||||
remainingTimestamp >>= 5;
|
||||
}
|
||||
buffer.writeAll(timestampChars);
|
||||
|
||||
for (var index = 0; index < 16; index++) {
|
||||
buffer.write(_crockfordBase32[_random.nextInt(32)]);
|
||||
}
|
||||
|
||||
return buffer.toString();
|
||||
}
|
||||
}
|
||||
381
lib/infrastructure/local/tables.dart
Normal file
381
lib/infrastructure/local/tables.dart
Normal file
@ -0,0 +1,381 @@
|
||||
import 'package:drift/drift.dart';
|
||||
|
||||
abstract class SyncableTable extends Table {
|
||||
TextColumn get id => text()();
|
||||
DateTimeColumn get createdAt => dateTime()();
|
||||
DateTimeColumn get updatedAt => dateTime()();
|
||||
DateTimeColumn get deletedAt => dateTime().nullable()();
|
||||
IntColumn get schemaVersion => integer().withDefault(const Constant(1))();
|
||||
TextColumn get syncState => text().customConstraint(
|
||||
"NOT NULL CHECK (sync_state IN ('localOnly', 'dirty', 'synced', "
|
||||
"'deleted'))",
|
||||
)();
|
||||
IntColumn get localRevision =>
|
||||
integer().customConstraint('NOT NULL CHECK (local_revision >= 0)')();
|
||||
TextColumn get originDeviceId => text().withLength(min: 1)();
|
||||
TextColumn get futureOwnerProfileId => text().nullable()();
|
||||
DateTimeColumn get lastSyncedAt => dateTime().nullable()();
|
||||
TextColumn get remoteRevision => text().nullable()();
|
||||
|
||||
@override
|
||||
Set<Column> get primaryKey => {id};
|
||||
|
||||
@override
|
||||
List<String> get customConstraints => const [];
|
||||
}
|
||||
|
||||
class MediaAssets extends SyncableTable {
|
||||
@override
|
||||
String get tableName => 'media_assets';
|
||||
|
||||
TextColumn get kind => text()();
|
||||
TextColumn get localUri => text().withLength(min: 1)();
|
||||
TextColumn get mimeType => text().nullable()();
|
||||
IntColumn get sizeBytes => integer().nullable()();
|
||||
IntColumn get width => integer().nullable()();
|
||||
IntColumn get height => integer().nullable()();
|
||||
IntColumn get durationMs => integer().nullable()();
|
||||
TextColumn get checksum => text().nullable()();
|
||||
TextColumn get remoteUri => text().nullable()();
|
||||
TextColumn get thumbnailLocalUri => text().nullable()();
|
||||
|
||||
@override
|
||||
List<String> get customConstraints => [
|
||||
"CHECK (kind IN ('image', 'video'))",
|
||||
'CHECK (size_bytes IS NULL OR size_bytes >= 0)',
|
||||
'CHECK (width IS NULL OR width > 0)',
|
||||
'CHECK (height IS NULL OR height > 0)',
|
||||
'CHECK (duration_ms IS NULL OR duration_ms >= 0)',
|
||||
];
|
||||
}
|
||||
|
||||
class Exercises extends SyncableTable {
|
||||
@override
|
||||
String get tableName => 'exercises';
|
||||
|
||||
TextColumn get name => text().withLength(min: 1)();
|
||||
TextColumn get description => text().nullable()();
|
||||
@ReferenceName('exerciseImageReferences')
|
||||
TextColumn get imageMediaId =>
|
||||
text().nullable().references(MediaAssets, #id)();
|
||||
|
||||
@ReferenceName('exerciseVideoReferences')
|
||||
TextColumn get videoMediaId =>
|
||||
text().nullable().references(MediaAssets, #id)();
|
||||
BoolColumn get hasTimeMeasure => boolean()();
|
||||
BoolColumn get hasRepsMeasure => boolean()();
|
||||
BoolColumn get hasScoreMeasure => boolean()();
|
||||
TextColumn get scoreLabel => text().nullable()();
|
||||
TextColumn get scoreUnit => text().nullable()();
|
||||
DateTimeColumn get archivedAt => dateTime().nullable()();
|
||||
|
||||
@override
|
||||
List<String> get customConstraints => [
|
||||
'CHECK (has_time_measure OR has_reps_measure OR has_score_measure)',
|
||||
'CHECK (NOT has_score_measure OR (score_label IS NOT NULL '
|
||||
'AND length(trim(score_label)) > 0 AND score_unit IS NOT NULL '
|
||||
'AND length(trim(score_unit)) > 0))',
|
||||
];
|
||||
}
|
||||
|
||||
class Programs extends SyncableTable {
|
||||
@override
|
||||
String get tableName => 'programs';
|
||||
|
||||
TextColumn get name => text().withLength(min: 1)();
|
||||
IntColumn get defaultRestSeconds => integer()();
|
||||
|
||||
@override
|
||||
List<String> get customConstraints => ['CHECK (default_rest_seconds >= 0)'];
|
||||
}
|
||||
|
||||
class ProgramExercises extends SyncableTable {
|
||||
@override
|
||||
String get tableName => 'program_exercises';
|
||||
|
||||
TextColumn get programId => text().references(Programs, #id)();
|
||||
TextColumn get sourceExerciseId =>
|
||||
text().nullable().references(Exercises, #id)();
|
||||
IntColumn get position => integer()();
|
||||
TextColumn get exerciseNameSnapshot => text().withLength(min: 1)();
|
||||
TextColumn get exerciseDescriptionSnapshot => text().nullable()();
|
||||
@ReferenceName('programExerciseImageSnapshotReferences')
|
||||
TextColumn get exerciseImageMediaIdSnapshot =>
|
||||
text().nullable().references(MediaAssets, #id)();
|
||||
|
||||
@ReferenceName('programExerciseVideoSnapshotReferences')
|
||||
TextColumn get exerciseVideoMediaIdSnapshot =>
|
||||
text().nullable().references(MediaAssets, #id)();
|
||||
BoolColumn get exerciseArchivedSnapshot =>
|
||||
boolean().withDefault(const Constant(false))();
|
||||
BoolColumn get availableTimeSnapshot => boolean()();
|
||||
BoolColumn get availableRepsSnapshot => boolean()();
|
||||
BoolColumn get availableScoreSnapshot => boolean()();
|
||||
TextColumn get scoreLabelSnapshot => text().nullable()();
|
||||
TextColumn get scoreUnitSnapshot => text().nullable()();
|
||||
IntColumn get setsCount => integer()();
|
||||
BoolColumn get timeEnabled => boolean()();
|
||||
BoolColumn get repsEnabled => boolean()();
|
||||
BoolColumn get scoreEnabled => boolean()();
|
||||
IntColumn get targetTimeSeconds => integer().nullable()();
|
||||
IntColumn get targetReps => integer().nullable()();
|
||||
RealColumn get targetScore => real().nullable()();
|
||||
IntColumn get restSecondsOverride => integer().nullable()();
|
||||
|
||||
@override
|
||||
List<String> get customConstraints => [
|
||||
'UNIQUE (program_id, position)',
|
||||
'CHECK (position >= 0)',
|
||||
'CHECK (sets_count > 0)',
|
||||
'CHECK (time_enabled OR reps_enabled OR score_enabled)',
|
||||
'CHECK (NOT time_enabled OR available_time_snapshot)',
|
||||
'CHECK (NOT reps_enabled OR available_reps_snapshot)',
|
||||
'CHECK (NOT score_enabled OR available_score_snapshot)',
|
||||
'CHECK (target_time_seconds IS NULL OR target_time_seconds > 0)',
|
||||
'CHECK (target_reps IS NULL OR target_reps > 0)',
|
||||
'CHECK (target_score IS NULL OR target_score >= 0)',
|
||||
'CHECK (rest_seconds_override IS NULL OR rest_seconds_override >= 0)',
|
||||
'CHECK (target_time_seconds IS NULL OR time_enabled)',
|
||||
'CHECK (target_reps IS NULL OR reps_enabled)',
|
||||
'CHECK (target_score IS NULL OR score_enabled)',
|
||||
'CHECK (NOT available_score_snapshot OR (score_label_snapshot '
|
||||
'IS NOT NULL AND length(trim(score_label_snapshot)) > 0 '
|
||||
'AND score_unit_snapshot IS NOT NULL '
|
||||
'AND length(trim(score_unit_snapshot)) > 0))',
|
||||
];
|
||||
}
|
||||
|
||||
class WorkoutTemplates extends SyncableTable {
|
||||
@override
|
||||
String get tableName => 'workout_templates';
|
||||
|
||||
TextColumn get name => text().withLength(min: 1)();
|
||||
DateTimeColumn get lastStartedAt => dateTime().nullable()();
|
||||
}
|
||||
|
||||
class WorkoutTemplatePrograms extends SyncableTable {
|
||||
@override
|
||||
String get tableName => 'workout_template_programs';
|
||||
|
||||
TextColumn get workoutTemplateId =>
|
||||
text().references(WorkoutTemplates, #id)();
|
||||
TextColumn get sourceProgramId =>
|
||||
text().nullable().references(Programs, #id)();
|
||||
IntColumn get position => integer()();
|
||||
TextColumn get programNameSnapshot => text().withLength(min: 1)();
|
||||
IntColumn get defaultRestSecondsSnapshot => integer()();
|
||||
TextColumn get programSnapshotJson => text().withLength(min: 1)();
|
||||
|
||||
@override
|
||||
List<String> get customConstraints => [
|
||||
'UNIQUE (workout_template_id, position)',
|
||||
'CHECK (position >= 0)',
|
||||
'CHECK (default_rest_seconds_snapshot >= 0)',
|
||||
];
|
||||
}
|
||||
|
||||
class WorkoutTemplateExerciseOverrides extends SyncableTable {
|
||||
@override
|
||||
String get tableName => 'workout_template_exercise_overrides';
|
||||
|
||||
TextColumn get workoutTemplateProgramId =>
|
||||
text().references(WorkoutTemplatePrograms, #id)();
|
||||
TextColumn get snapshotProgramExerciseId => text().withLength(min: 1)();
|
||||
IntColumn get setsCountOverride => integer().nullable()();
|
||||
IntColumn get targetTimeSecondsOverride => integer().nullable()();
|
||||
IntColumn get targetRepsOverride => integer().nullable()();
|
||||
RealColumn get targetScoreOverride => real().nullable()();
|
||||
|
||||
@override
|
||||
List<String> get customConstraints => [
|
||||
'UNIQUE (workout_template_program_id, snapshot_program_exercise_id)',
|
||||
'CHECK (sets_count_override IS NULL OR sets_count_override > 0)',
|
||||
'CHECK (target_time_seconds_override IS NULL OR '
|
||||
'target_time_seconds_override > 0)',
|
||||
'CHECK (target_reps_override IS NULL OR target_reps_override > 0)',
|
||||
'CHECK (target_score_override IS NULL OR target_score_override >= 0)',
|
||||
];
|
||||
}
|
||||
|
||||
class ActiveWorkoutSessions extends SyncableTable {
|
||||
@override
|
||||
String get tableName => 'active_workout_sessions';
|
||||
|
||||
TextColumn get sourceWorkoutTemplateId =>
|
||||
text().nullable().references(WorkoutTemplates, #id)();
|
||||
TextColumn get status => text()();
|
||||
DateTimeColumn get startedAt => dateTime()();
|
||||
DateTimeColumn get pausedAt => dateTime().nullable()();
|
||||
DateTimeColumn get endedAt => dateTime().nullable()();
|
||||
DateTimeColumn get lastPersistedAt => dateTime()();
|
||||
IntColumn get elapsedActiveMs => integer()();
|
||||
IntColumn get currentProgramIndex => integer()();
|
||||
IntColumn get currentExerciseIndex => integer()();
|
||||
IntColumn get currentSetIndex => integer()();
|
||||
TextColumn get resolvedTemplateSnapshotJson => text().withLength(min: 1)();
|
||||
|
||||
@override
|
||||
List<String> get customConstraints => [
|
||||
"CHECK (status IN ('running', 'paused', 'savedExit', 'completed', "
|
||||
"'abandoned'))",
|
||||
'CHECK (elapsed_active_ms >= 0)',
|
||||
'CHECK (current_program_index >= 0)',
|
||||
'CHECK (current_exercise_index >= 0)',
|
||||
'CHECK (current_set_index >= 0)',
|
||||
];
|
||||
}
|
||||
|
||||
class ActiveSetResults extends SyncableTable {
|
||||
@override
|
||||
String get tableName => 'active_set_results';
|
||||
|
||||
TextColumn get activeWorkoutSessionId =>
|
||||
text().references(ActiveWorkoutSessions, #id)();
|
||||
TextColumn get programSnapshotId => text().withLength(min: 1)();
|
||||
TextColumn get exerciseSnapshotId => text().withLength(min: 1)();
|
||||
IntColumn get programIndex => integer()();
|
||||
IntColumn get exerciseIndex => integer()();
|
||||
IntColumn get setIndex => integer()();
|
||||
DateTimeColumn get startedAt => dateTime().nullable()();
|
||||
DateTimeColumn get completedAt => dateTime().nullable()();
|
||||
IntColumn get actualTimeMs => integer().nullable()();
|
||||
IntColumn get actualReps => integer().nullable()();
|
||||
RealColumn get actualScore => real().nullable()();
|
||||
TextColumn get scoreLabelSnapshot => text().nullable()();
|
||||
TextColumn get scoreUnitSnapshot => text().nullable()();
|
||||
TextColumn get note => text().nullable()();
|
||||
|
||||
@override
|
||||
List<String> get customConstraints => [
|
||||
'UNIQUE (active_workout_session_id, program_index, exercise_index, '
|
||||
'set_index)',
|
||||
'CHECK (program_index >= 0)',
|
||||
'CHECK (exercise_index >= 0)',
|
||||
'CHECK (set_index >= 0)',
|
||||
'CHECK (actual_time_ms IS NULL OR actual_time_ms >= 0)',
|
||||
'CHECK (actual_reps IS NULL OR actual_reps >= 0)',
|
||||
'CHECK (actual_score IS NULL OR actual_score >= 0)',
|
||||
'CHECK (actual_score IS NULL OR (score_label_snapshot IS NOT NULL '
|
||||
'AND length(trim(score_label_snapshot)) > 0 '
|
||||
'AND score_unit_snapshot IS NOT NULL '
|
||||
'AND length(trim(score_unit_snapshot)) > 0))',
|
||||
];
|
||||
}
|
||||
|
||||
class ActiveRestStates extends SyncableTable {
|
||||
@override
|
||||
String get tableName => 'active_rest_states';
|
||||
|
||||
TextColumn get activeWorkoutSessionId =>
|
||||
text().references(ActiveWorkoutSessions, #id)();
|
||||
IntColumn get afterProgramIndex => integer()();
|
||||
IntColumn get afterExerciseIndex => integer()();
|
||||
IntColumn get afterSetIndex => integer()();
|
||||
IntColumn get plannedRestSeconds => integer()();
|
||||
IntColumn get adjustedRestSeconds => integer()();
|
||||
DateTimeColumn get startedAt => dateTime()();
|
||||
DateTimeColumn get endedAt => dateTime().nullable()();
|
||||
DateTimeColumn get skippedAt => dateTime().nullable()();
|
||||
|
||||
@override
|
||||
List<String> get customConstraints => [
|
||||
'CHECK (after_program_index >= 0)',
|
||||
'CHECK (after_exercise_index >= 0)',
|
||||
'CHECK (after_set_index >= 0)',
|
||||
'CHECK (planned_rest_seconds >= 0)',
|
||||
'CHECK (adjusted_rest_seconds >= 0)',
|
||||
'CHECK (ended_at IS NULL OR skipped_at IS NULL)',
|
||||
];
|
||||
}
|
||||
|
||||
class WorkoutHistories extends SyncableTable {
|
||||
@override
|
||||
String get tableName => 'workout_history';
|
||||
|
||||
TextColumn get sourceWorkoutTemplateId =>
|
||||
text().nullable().references(WorkoutTemplates, #id)();
|
||||
TextColumn get sourceActiveWorkoutSessionId =>
|
||||
text().nullable().references(ActiveWorkoutSessions, #id)();
|
||||
TextColumn get nameSnapshot => text().withLength(min: 1)();
|
||||
DateTimeColumn get startedAt => dateTime()();
|
||||
DateTimeColumn get endedAt => dateTime()();
|
||||
IntColumn get totalActiveMs => integer()();
|
||||
BoolColumn get completed => boolean()();
|
||||
TextColumn get historySnapshotJson => text().withLength(min: 1)();
|
||||
|
||||
@override
|
||||
List<String> get customConstraints => ['CHECK (total_active_ms >= 0)'];
|
||||
}
|
||||
|
||||
class WorkoutHistorySetResults extends SyncableTable {
|
||||
@override
|
||||
String get tableName => 'workout_history_set_results';
|
||||
|
||||
TextColumn get workoutHistoryId => text().references(WorkoutHistories, #id)();
|
||||
TextColumn get programSnapshotId => text().withLength(min: 1)();
|
||||
TextColumn get exerciseSnapshotId => text().withLength(min: 1)();
|
||||
IntColumn get programIndex => integer()();
|
||||
IntColumn get exerciseIndex => integer()();
|
||||
IntColumn get setIndex => integer()();
|
||||
TextColumn get programNameSnapshot => text().withLength(min: 1)();
|
||||
TextColumn get exerciseNameSnapshot => text().withLength(min: 1)();
|
||||
BoolColumn get timeEnabledSnapshot => boolean()();
|
||||
BoolColumn get repsEnabledSnapshot => boolean()();
|
||||
BoolColumn get scoreEnabledSnapshot => boolean()();
|
||||
IntColumn get targetTimeSecondsSnapshot => integer().nullable()();
|
||||
IntColumn get targetRepsSnapshot => integer().nullable()();
|
||||
RealColumn get targetScoreSnapshot => real().nullable()();
|
||||
IntColumn get actualTimeMs => integer().nullable()();
|
||||
IntColumn get actualReps => integer().nullable()();
|
||||
RealColumn get actualScore => real().nullable()();
|
||||
TextColumn get scoreLabelSnapshot => text().nullable()();
|
||||
TextColumn get scoreUnitSnapshot => text().nullable()();
|
||||
DateTimeColumn get startedAt => dateTime().nullable()();
|
||||
DateTimeColumn get completedAt => dateTime().nullable()();
|
||||
|
||||
@override
|
||||
List<String> get customConstraints => [
|
||||
'UNIQUE (workout_history_id, program_index, exercise_index, '
|
||||
'set_index)',
|
||||
'CHECK (program_index >= 0)',
|
||||
'CHECK (exercise_index >= 0)',
|
||||
'CHECK (set_index >= 0)',
|
||||
'CHECK (time_enabled_snapshot OR reps_enabled_snapshot OR '
|
||||
'score_enabled_snapshot)',
|
||||
'CHECK (target_time_seconds_snapshot IS NULL OR '
|
||||
'target_time_seconds_snapshot > 0)',
|
||||
'CHECK (target_reps_snapshot IS NULL OR target_reps_snapshot > 0)',
|
||||
'CHECK (target_score_snapshot IS NULL OR target_score_snapshot >= 0)',
|
||||
'CHECK (actual_time_ms IS NULL OR actual_time_ms >= 0)',
|
||||
'CHECK (actual_reps IS NULL OR actual_reps >= 0)',
|
||||
'CHECK (actual_score IS NULL OR actual_score >= 0)',
|
||||
'CHECK (actual_score IS NULL OR (score_label_snapshot IS NOT NULL '
|
||||
'AND length(trim(score_label_snapshot)) > 0 '
|
||||
'AND score_unit_snapshot IS NOT NULL '
|
||||
'AND length(trim(score_unit_snapshot)) > 0))',
|
||||
];
|
||||
}
|
||||
|
||||
class ChangeLogEntries extends Table {
|
||||
@override
|
||||
String get tableName => 'change_log';
|
||||
|
||||
TextColumn get id => text()();
|
||||
TextColumn get entityType => text().withLength(min: 1)();
|
||||
TextColumn get entityId => text().withLength(min: 1)();
|
||||
TextColumn get operation => text()();
|
||||
TextColumn get payloadHash => text().nullable()();
|
||||
IntColumn get localRevision => integer()();
|
||||
TextColumn get originDeviceId => text().withLength(min: 1)();
|
||||
DateTimeColumn get createdAt => dateTime()();
|
||||
DateTimeColumn get syncedAt => dateTime().nullable()();
|
||||
|
||||
@override
|
||||
Set<Column> get primaryKey => {id};
|
||||
|
||||
@override
|
||||
List<String> get customConstraints => const [
|
||||
"CHECK (operation IN ('insert', 'update', 'softDelete', 'restore'))",
|
||||
'CHECK (local_revision >= 0)',
|
||||
];
|
||||
}
|
||||
Reference in New Issue
Block a user