Rework exercise database

This is now separated into individual tables and methods so that they can be
tested separately
This commit is contained in:
Roland Geider
2023-12-26 17:03:20 +01:00
parent 65ba2275ae
commit c5cbb4f851
31 changed files with 2447 additions and 755 deletions

View File

@@ -20,8 +20,8 @@ Widget createGymModeScreen({locale = 'en'}) {
final mockExerciseProvider = MockExercisesProvider();
when(mockExerciseProvider.findExerciseBaseById(1)).thenReturn(bases[0]); // bench press
when(mockExerciseProvider.findExerciseBaseById(6)).thenReturn(bases[5]); // side raises
when(mockExerciseProvider.findExerciseById(1)).thenReturn(bases[0]); // bench press
when(mockExerciseProvider.findExerciseById(6)).thenReturn(bases[5]); // side raises
//when(mockExerciseProvider.findExerciseBaseById(2)).thenReturn(bases[1]); // crunches
//when(mockExerciseProvider.findExerciseBaseById(3)).thenReturn(bases[2]); // dead lift

View File

@@ -1,7 +1,8 @@
import 'dart:developer';
import 'package:get_it/get_it.dart';
import 'package:wger/database/exercise_DB/exercise_database.dart';
import 'package:wger/database/exercises/exercise_database.dart';
import 'package:wger/database/ingredients/ingredients_database.dart';
final locator = GetIt.asNewInstance();
@@ -14,7 +15,9 @@ class ServiceLocator {
Future<void> _initDB() async {
final exerciseDB = ExerciseDatabase();
final ingredientDB = IngredientDatabase();
locator.registerSingleton<ExerciseDatabase>(exerciseDB);
locator.registerSingleton<IngredientDatabase>(ingredientDB);
}
Future<void> configure() async {

View File

@@ -1,44 +0,0 @@
import 'dart:io';
import 'package:drift/drift.dart';
import 'package:drift/native.dart';
import 'package:path/path.dart' as p;
import 'package:path_provider/path_provider.dart';
import 'package:wger/database/exercise_DB/type_converters.dart';
import 'package:wger/models/exercises/base.dart';
import 'package:wger/models/exercises/category.dart';
import 'package:wger/models/exercises/equipment.dart';
import 'package:wger/models/exercises/language.dart';
import 'package:wger/models/exercises/muscle.dart';
import 'package:wger/models/exercises/variation.dart';
part 'exercise_database.g.dart';
@DataClassName('ExerciseTable')
class ExerciseTableItems extends Table {
IntColumn get id => integer().autoIncrement()();
TextColumn get exercisebase => text().map(const ExerciseBaseConverter()).nullable()();
TextColumn get muscle => text().map(const MuscleConverter()).nullable()();
TextColumn get category => text().map(const ExerciseCategoryConverter()).nullable()();
TextColumn get variation => text().map(const VariationConverter()).nullable()();
TextColumn get language => text().map(const LanguageConverter()).nullable()();
TextColumn get equipment => text().map(const EquipmentConverter()).nullable()();
DateTimeColumn get expiresIn => dateTime().nullable()();
}
@DriftDatabase(tables: [ExerciseTableItems])
class ExerciseDatabase extends _$ExerciseDatabase {
ExerciseDatabase() : super(_openConnection());
@override
// TODO: implement schemaVersion
int get schemaVersion => 1;
}
LazyDatabase _openConnection() {
return LazyDatabase(() async {
final dbFolder = await getApplicationDocumentsDirectory();
final file = File(p.join(dbFolder.path, 'db.sqlite'));
return NativeDatabase.createInBackground(file);
});
}

View File

@@ -1,423 +0,0 @@
// GENERATED CODE - DO NOT MODIFY BY HAND
part of 'exercise_database.dart';
// ignore_for_file: type=lint
class $ExerciseTableItemsTable extends ExerciseTableItems
with TableInfo<$ExerciseTableItemsTable, ExerciseTable> {
@override
final GeneratedDatabase attachedDatabase;
final String? _alias;
$ExerciseTableItemsTable(this.attachedDatabase, [this._alias]);
static const VerificationMeta _idMeta = const VerificationMeta('id');
@override
late final GeneratedColumn<int> id = GeneratedColumn<int>('id', aliasedName, false,
hasAutoIncrement: true,
type: DriftSqlType.int,
requiredDuringInsert: false,
defaultConstraints: GeneratedColumn.constraintIsAlways('PRIMARY KEY AUTOINCREMENT'));
static const VerificationMeta _exercisebaseMeta = const VerificationMeta('exercisebase');
@override
late final GeneratedColumnWithTypeConverter<ExerciseBase?, String> exercisebase =
GeneratedColumn<String>('exercisebase', aliasedName, true,
type: DriftSqlType.string, requiredDuringInsert: false)
.withConverter<ExerciseBase?>($ExerciseTableItemsTable.$converterexercisebasen);
static const VerificationMeta _muscleMeta = const VerificationMeta('muscle');
@override
late final GeneratedColumnWithTypeConverter<Muscle?, String> muscle = GeneratedColumn<String>(
'muscle', aliasedName, true,
type: DriftSqlType.string, requiredDuringInsert: false)
.withConverter<Muscle?>($ExerciseTableItemsTable.$convertermusclen);
static const VerificationMeta _categoryMeta = const VerificationMeta('category');
@override
late final GeneratedColumnWithTypeConverter<ExerciseCategory?, String> category =
GeneratedColumn<String>('category', aliasedName, true,
type: DriftSqlType.string, requiredDuringInsert: false)
.withConverter<ExerciseCategory?>($ExerciseTableItemsTable.$convertercategoryn);
static const VerificationMeta _variationMeta = const VerificationMeta('variation');
@override
late final GeneratedColumnWithTypeConverter<Variation?, String> variation =
GeneratedColumn<String>('variation', aliasedName, true,
type: DriftSqlType.string, requiredDuringInsert: false)
.withConverter<Variation?>($ExerciseTableItemsTable.$convertervariationn);
static const VerificationMeta _languageMeta = const VerificationMeta('language');
@override
late final GeneratedColumnWithTypeConverter<Language?, String> language = GeneratedColumn<String>(
'language', aliasedName, true,
type: DriftSqlType.string, requiredDuringInsert: false)
.withConverter<Language?>($ExerciseTableItemsTable.$converterlanguagen);
static const VerificationMeta _equipmentMeta = const VerificationMeta('equipment');
@override
late final GeneratedColumnWithTypeConverter<Equipment?, String> equipment =
GeneratedColumn<String>('equipment', aliasedName, true,
type: DriftSqlType.string, requiredDuringInsert: false)
.withConverter<Equipment?>($ExerciseTableItemsTable.$converterequipmentn);
static const VerificationMeta _expiresInMeta = const VerificationMeta('expiresIn');
@override
late final GeneratedColumn<DateTime> expiresIn = GeneratedColumn<DateTime>(
'expires_in', aliasedName, true,
type: DriftSqlType.dateTime, requiredDuringInsert: false);
@override
List<GeneratedColumn> get $columns =>
[id, exercisebase, muscle, category, variation, language, equipment, expiresIn];
@override
String get aliasedName => _alias ?? actualTableName;
@override
String get actualTableName => $name;
static const String $name = 'exercise_table_items';
@override
VerificationContext validateIntegrity(Insertable<ExerciseTable> instance,
{bool isInserting = false}) {
final context = VerificationContext();
final data = instance.toColumns(true);
if (data.containsKey('id')) {
context.handle(_idMeta, id.isAcceptableOrUnknown(data['id']!, _idMeta));
}
context.handle(_exercisebaseMeta, const VerificationResult.success());
context.handle(_muscleMeta, const VerificationResult.success());
context.handle(_categoryMeta, const VerificationResult.success());
context.handle(_variationMeta, const VerificationResult.success());
context.handle(_languageMeta, const VerificationResult.success());
context.handle(_equipmentMeta, const VerificationResult.success());
if (data.containsKey('expires_in')) {
context.handle(
_expiresInMeta, expiresIn.isAcceptableOrUnknown(data['expires_in']!, _expiresInMeta));
}
return context;
}
@override
Set<GeneratedColumn> get $primaryKey => {id};
@override
ExerciseTable map(Map<String, dynamic> data, {String? tablePrefix}) {
final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : '';
return ExerciseTable(
id: attachedDatabase.typeMapping.read(DriftSqlType.int, data['${effectivePrefix}id'])!,
exercisebase: $ExerciseTableItemsTable.$converterexercisebasen.fromSql(attachedDatabase
.typeMapping
.read(DriftSqlType.string, data['${effectivePrefix}exercisebase'])),
muscle: $ExerciseTableItemsTable.$convertermusclen.fromSql(
attachedDatabase.typeMapping.read(DriftSqlType.string, data['${effectivePrefix}muscle'])),
category: $ExerciseTableItemsTable.$convertercategoryn.fromSql(attachedDatabase.typeMapping
.read(DriftSqlType.string, data['${effectivePrefix}category'])),
variation: $ExerciseTableItemsTable.$convertervariationn.fromSql(attachedDatabase.typeMapping
.read(DriftSqlType.string, data['${effectivePrefix}variation'])),
language: $ExerciseTableItemsTable.$converterlanguagen.fromSql(attachedDatabase.typeMapping
.read(DriftSqlType.string, data['${effectivePrefix}language'])),
equipment: $ExerciseTableItemsTable.$converterequipmentn.fromSql(attachedDatabase.typeMapping
.read(DriftSqlType.string, data['${effectivePrefix}equipment'])),
expiresIn: attachedDatabase.typeMapping
.read(DriftSqlType.dateTime, data['${effectivePrefix}expires_in']),
);
}
@override
$ExerciseTableItemsTable createAlias(String alias) {
return $ExerciseTableItemsTable(attachedDatabase, alias);
}
static TypeConverter<ExerciseBase, String> $converterexercisebase = const ExerciseBaseConverter();
static TypeConverter<ExerciseBase?, String?> $converterexercisebasen =
NullAwareTypeConverter.wrap($converterexercisebase);
static TypeConverter<Muscle, String> $convertermuscle = const MuscleConverter();
static TypeConverter<Muscle?, String?> $convertermusclen =
NullAwareTypeConverter.wrap($convertermuscle);
static TypeConverter<ExerciseCategory, String> $convertercategory =
const ExerciseCategoryConverter();
static TypeConverter<ExerciseCategory?, String?> $convertercategoryn =
NullAwareTypeConverter.wrap($convertercategory);
static TypeConverter<Variation, String> $convertervariation = const VariationConverter();
static TypeConverter<Variation?, String?> $convertervariationn =
NullAwareTypeConverter.wrap($convertervariation);
static TypeConverter<Language, String> $converterlanguage = const LanguageConverter();
static TypeConverter<Language?, String?> $converterlanguagen =
NullAwareTypeConverter.wrap($converterlanguage);
static TypeConverter<Equipment, String> $converterequipment = const EquipmentConverter();
static TypeConverter<Equipment?, String?> $converterequipmentn =
NullAwareTypeConverter.wrap($converterequipment);
}
class ExerciseTable extends DataClass implements Insertable<ExerciseTable> {
final int id;
final ExerciseBase? exercisebase;
final Muscle? muscle;
final ExerciseCategory? category;
final Variation? variation;
final Language? language;
final Equipment? equipment;
final DateTime? expiresIn;
const ExerciseTable(
{required this.id,
this.exercisebase,
this.muscle,
this.category,
this.variation,
this.language,
this.equipment,
this.expiresIn});
@override
Map<String, Expression> toColumns(bool nullToAbsent) {
final map = <String, Expression>{};
map['id'] = Variable<int>(id);
if (!nullToAbsent || exercisebase != null) {
final converter = $ExerciseTableItemsTable.$converterexercisebasen;
map['exercisebase'] = Variable<String>(converter.toSql(exercisebase));
}
if (!nullToAbsent || muscle != null) {
final converter = $ExerciseTableItemsTable.$convertermusclen;
map['muscle'] = Variable<String>(converter.toSql(muscle));
}
if (!nullToAbsent || category != null) {
final converter = $ExerciseTableItemsTable.$convertercategoryn;
map['category'] = Variable<String>(converter.toSql(category));
}
if (!nullToAbsent || variation != null) {
final converter = $ExerciseTableItemsTable.$convertervariationn;
map['variation'] = Variable<String>(converter.toSql(variation));
}
if (!nullToAbsent || language != null) {
final converter = $ExerciseTableItemsTable.$converterlanguagen;
map['language'] = Variable<String>(converter.toSql(language));
}
if (!nullToAbsent || equipment != null) {
final converter = $ExerciseTableItemsTable.$converterequipmentn;
map['equipment'] = Variable<String>(converter.toSql(equipment));
}
if (!nullToAbsent || expiresIn != null) {
map['expires_in'] = Variable<DateTime>(expiresIn);
}
return map;
}
ExerciseTableItemsCompanion toCompanion(bool nullToAbsent) {
return ExerciseTableItemsCompanion(
id: Value(id),
exercisebase:
exercisebase == null && nullToAbsent ? const Value.absent() : Value(exercisebase),
muscle: muscle == null && nullToAbsent ? const Value.absent() : Value(muscle),
category: category == null && nullToAbsent ? const Value.absent() : Value(category),
variation: variation == null && nullToAbsent ? const Value.absent() : Value(variation),
language: language == null && nullToAbsent ? const Value.absent() : Value(language),
equipment: equipment == null && nullToAbsent ? const Value.absent() : Value(equipment),
expiresIn: expiresIn == null && nullToAbsent ? const Value.absent() : Value(expiresIn),
);
}
factory ExerciseTable.fromJson(Map<String, dynamic> json, {ValueSerializer? serializer}) {
serializer ??= driftRuntimeOptions.defaultSerializer;
return ExerciseTable(
id: serializer.fromJson<int>(json['id']),
exercisebase: serializer.fromJson<ExerciseBase?>(json['exercisebase']),
muscle: serializer.fromJson<Muscle?>(json['muscle']),
category: serializer.fromJson<ExerciseCategory?>(json['category']),
variation: serializer.fromJson<Variation?>(json['variation']),
language: serializer.fromJson<Language?>(json['language']),
equipment: serializer.fromJson<Equipment?>(json['equipment']),
expiresIn: serializer.fromJson<DateTime?>(json['expiresIn']),
);
}
@override
Map<String, dynamic> toJson({ValueSerializer? serializer}) {
serializer ??= driftRuntimeOptions.defaultSerializer;
return <String, dynamic>{
'id': serializer.toJson<int>(id),
'exercisebase': serializer.toJson<ExerciseBase?>(exercisebase),
'muscle': serializer.toJson<Muscle?>(muscle),
'category': serializer.toJson<ExerciseCategory?>(category),
'variation': serializer.toJson<Variation?>(variation),
'language': serializer.toJson<Language?>(language),
'equipment': serializer.toJson<Equipment?>(equipment),
'expiresIn': serializer.toJson<DateTime?>(expiresIn),
};
}
ExerciseTable copyWith(
{int? id,
Value<ExerciseBase?> exercisebase = const Value.absent(),
Value<Muscle?> muscle = const Value.absent(),
Value<ExerciseCategory?> category = const Value.absent(),
Value<Variation?> variation = const Value.absent(),
Value<Language?> language = const Value.absent(),
Value<Equipment?> equipment = const Value.absent(),
Value<DateTime?> expiresIn = const Value.absent()}) =>
ExerciseTable(
id: id ?? this.id,
exercisebase: exercisebase.present ? exercisebase.value : this.exercisebase,
muscle: muscle.present ? muscle.value : this.muscle,
category: category.present ? category.value : this.category,
variation: variation.present ? variation.value : this.variation,
language: language.present ? language.value : this.language,
equipment: equipment.present ? equipment.value : this.equipment,
expiresIn: expiresIn.present ? expiresIn.value : this.expiresIn,
);
@override
String toString() {
return (StringBuffer('ExerciseTable(')
..write('id: $id, ')
..write('exercisebase: $exercisebase, ')
..write('muscle: $muscle, ')
..write('category: $category, ')
..write('variation: $variation, ')
..write('language: $language, ')
..write('equipment: $equipment, ')
..write('expiresIn: $expiresIn')
..write(')'))
.toString();
}
@override
int get hashCode =>
Object.hash(id, exercisebase, muscle, category, variation, language, equipment, expiresIn);
@override
bool operator ==(Object other) =>
identical(this, other) ||
(other is ExerciseTable &&
other.id == this.id &&
other.exercisebase == this.exercisebase &&
other.muscle == this.muscle &&
other.category == this.category &&
other.variation == this.variation &&
other.language == this.language &&
other.equipment == this.equipment &&
other.expiresIn == this.expiresIn);
}
class ExerciseTableItemsCompanion extends UpdateCompanion<ExerciseTable> {
final Value<int> id;
final Value<ExerciseBase?> exercisebase;
final Value<Muscle?> muscle;
final Value<ExerciseCategory?> category;
final Value<Variation?> variation;
final Value<Language?> language;
final Value<Equipment?> equipment;
final Value<DateTime?> expiresIn;
const ExerciseTableItemsCompanion({
this.id = const Value.absent(),
this.exercisebase = const Value.absent(),
this.muscle = const Value.absent(),
this.category = const Value.absent(),
this.variation = const Value.absent(),
this.language = const Value.absent(),
this.equipment = const Value.absent(),
this.expiresIn = const Value.absent(),
});
ExerciseTableItemsCompanion.insert({
this.id = const Value.absent(),
this.exercisebase = const Value.absent(),
this.muscle = const Value.absent(),
this.category = const Value.absent(),
this.variation = const Value.absent(),
this.language = const Value.absent(),
this.equipment = const Value.absent(),
this.expiresIn = const Value.absent(),
});
static Insertable<ExerciseTable> custom({
Expression<int>? id,
Expression<String>? exercisebase,
Expression<String>? muscle,
Expression<String>? category,
Expression<String>? variation,
Expression<String>? language,
Expression<String>? equipment,
Expression<DateTime>? expiresIn,
}) {
return RawValuesInsertable({
if (id != null) 'id': id,
if (exercisebase != null) 'exercisebase': exercisebase,
if (muscle != null) 'muscle': muscle,
if (category != null) 'category': category,
if (variation != null) 'variation': variation,
if (language != null) 'language': language,
if (equipment != null) 'equipment': equipment,
if (expiresIn != null) 'expires_in': expiresIn,
});
}
ExerciseTableItemsCompanion copyWith(
{Value<int>? id,
Value<ExerciseBase?>? exercisebase,
Value<Muscle?>? muscle,
Value<ExerciseCategory?>? category,
Value<Variation?>? variation,
Value<Language?>? language,
Value<Equipment?>? equipment,
Value<DateTime?>? expiresIn}) {
return ExerciseTableItemsCompanion(
id: id ?? this.id,
exercisebase: exercisebase ?? this.exercisebase,
muscle: muscle ?? this.muscle,
category: category ?? this.category,
variation: variation ?? this.variation,
language: language ?? this.language,
equipment: equipment ?? this.equipment,
expiresIn: expiresIn ?? this.expiresIn,
);
}
@override
Map<String, Expression> toColumns(bool nullToAbsent) {
final map = <String, Expression>{};
if (id.present) {
map['id'] = Variable<int>(id.value);
}
if (exercisebase.present) {
final converter = $ExerciseTableItemsTable.$converterexercisebasen;
map['exercisebase'] = Variable<String>(converter.toSql(exercisebase.value));
}
if (muscle.present) {
final converter = $ExerciseTableItemsTable.$convertermusclen;
map['muscle'] = Variable<String>(converter.toSql(muscle.value));
}
if (category.present) {
final converter = $ExerciseTableItemsTable.$convertercategoryn;
map['category'] = Variable<String>(converter.toSql(category.value));
}
if (variation.present) {
final converter = $ExerciseTableItemsTable.$convertervariationn;
map['variation'] = Variable<String>(converter.toSql(variation.value));
}
if (language.present) {
final converter = $ExerciseTableItemsTable.$converterlanguagen;
map['language'] = Variable<String>(converter.toSql(language.value));
}
if (equipment.present) {
final converter = $ExerciseTableItemsTable.$converterequipmentn;
map['equipment'] = Variable<String>(converter.toSql(equipment.value));
}
if (expiresIn.present) {
map['expires_in'] = Variable<DateTime>(expiresIn.value);
}
return map;
}
@override
String toString() {
return (StringBuffer('ExerciseTableItemsCompanion(')
..write('id: $id, ')
..write('exercisebase: $exercisebase, ')
..write('muscle: $muscle, ')
..write('category: $category, ')
..write('variation: $variation, ')
..write('language: $language, ')
..write('equipment: $equipment, ')
..write('expiresIn: $expiresIn')
..write(')'))
.toString();
}
}
abstract class _$ExerciseDatabase extends GeneratedDatabase {
_$ExerciseDatabase(QueryExecutor e) : super(e);
late final $ExerciseTableItemsTable exerciseTableItems = $ExerciseTableItemsTable(this);
@override
Iterable<TableInfo<Table, Object?>> get allTables =>
allSchemaEntities.whereType<TableInfo<Table, Object?>>();
@override
List<DatabaseSchemaEntity> get allSchemaEntities => [exerciseTableItems];
}

View File

@@ -0,0 +1,80 @@
import 'dart:io';
import 'package:drift/drift.dart';
import 'package:drift/native.dart';
import 'package:path/path.dart' as p;
import 'package:path_provider/path_provider.dart';
import 'package:wger/database/exercises/type_converters.dart';
import 'package:wger/models/exercises/category.dart';
import 'package:wger/models/exercises/equipment.dart';
import 'package:wger/models/exercises/language.dart';
import 'package:wger/models/exercises/muscle.dart';
part 'exercise_database.g.dart';
@DataClassName('ExerciseTable')
class Exercises extends Table {
IntColumn get id => integer()();
TextColumn get data => text()();
// TextColumn get exercisedata => text().map(const ExerciseBaseConverter())();
DateTimeColumn get lastUpdate => dateTime()();
}
@DataClassName('MuscleTable')
class Muscles extends Table {
IntColumn get id => integer()();
TextColumn get data => text().map(const MuscleConverter())();
}
@DataClassName('CategoryTable')
class Categories extends Table {
IntColumn get id => integer()();
TextColumn get data => text().map(const ExerciseCategoryConverter())();
}
@DataClassName('LanguagesTable')
class Languages extends Table {
IntColumn get id => integer()();
TextColumn get data => text().map(const LanguageConverter())();
}
@DataClassName('EquipmentTable')
class Equipments extends Table {
IntColumn get id => integer()();
TextColumn get data => text().map(const EquipmentConverter())();
}
@DriftDatabase(tables: [Exercises, Muscles, Equipments, Categories, Languages])
class ExerciseDatabase extends _$ExerciseDatabase {
ExerciseDatabase() : super(_openConnection());
// Named constructor for creating in-memory database
ExerciseDatabase.inMemory(super.e);
@override
// TODO: implement schemaVersion
int get schemaVersion => 1;
Future<void> deleteEverything() {
return transaction(() async {
for (final table in allTables) {
await delete(table).go();
}
});
}
}
LazyDatabase _openConnection() {
return LazyDatabase(() async {
final dbFolder = await getApplicationDocumentsDirectory();
final file = File(p.join(dbFolder.path, 'exercises.sqlite'));
return NativeDatabase.createInBackground(file);
});
}

View File

@@ -0,0 +1,938 @@
// GENERATED CODE - DO NOT MODIFY BY HAND
part of 'exercise_database.dart';
// ignore_for_file: type=lint
class $ExercisesTable extends Exercises with TableInfo<$ExercisesTable, ExerciseTable> {
@override
final GeneratedDatabase attachedDatabase;
final String? _alias;
$ExercisesTable(this.attachedDatabase, [this._alias]);
static const VerificationMeta _idMeta = const VerificationMeta('id');
@override
late final GeneratedColumn<int> id = GeneratedColumn<int>('id', aliasedName, false,
type: DriftSqlType.int, requiredDuringInsert: true);
static const VerificationMeta _dataMeta = const VerificationMeta('data');
@override
late final GeneratedColumn<String> data = GeneratedColumn<String>('data', aliasedName, false,
type: DriftSqlType.string, requiredDuringInsert: true);
static const VerificationMeta _lastUpdateMeta = const VerificationMeta('lastUpdate');
@override
late final GeneratedColumn<DateTime> lastUpdate = GeneratedColumn<DateTime>(
'last_update', aliasedName, false,
type: DriftSqlType.dateTime, requiredDuringInsert: true);
@override
List<GeneratedColumn> get $columns => [id, data, lastUpdate];
@override
String get aliasedName => _alias ?? actualTableName;
@override
String get actualTableName => $name;
static const String $name = 'exercises';
@override
VerificationContext validateIntegrity(Insertable<ExerciseTable> instance,
{bool isInserting = false}) {
final context = VerificationContext();
final data = instance.toColumns(true);
if (data.containsKey('id')) {
context.handle(_idMeta, id.isAcceptableOrUnknown(data['id']!, _idMeta));
} else if (isInserting) {
context.missing(_idMeta);
}
if (data.containsKey('data')) {
context.handle(_dataMeta, this.data.isAcceptableOrUnknown(data['data']!, _dataMeta));
} else if (isInserting) {
context.missing(_dataMeta);
}
if (data.containsKey('last_update')) {
context.handle(
_lastUpdateMeta, lastUpdate.isAcceptableOrUnknown(data['last_update']!, _lastUpdateMeta));
} else if (isInserting) {
context.missing(_lastUpdateMeta);
}
return context;
}
@override
Set<GeneratedColumn> get $primaryKey => const {};
@override
ExerciseTable map(Map<String, dynamic> data, {String? tablePrefix}) {
final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : '';
return ExerciseTable(
id: attachedDatabase.typeMapping.read(DriftSqlType.int, data['${effectivePrefix}id'])!,
data: attachedDatabase.typeMapping.read(DriftSqlType.string, data['${effectivePrefix}data'])!,
lastUpdate: attachedDatabase.typeMapping
.read(DriftSqlType.dateTime, data['${effectivePrefix}last_update'])!,
);
}
@override
$ExercisesTable createAlias(String alias) {
return $ExercisesTable(attachedDatabase, alias);
}
}
class ExerciseTable extends DataClass implements Insertable<ExerciseTable> {
final int id;
final String data;
final DateTime lastUpdate;
const ExerciseTable({required this.id, required this.data, required this.lastUpdate});
@override
Map<String, Expression> toColumns(bool nullToAbsent) {
final map = <String, Expression>{};
map['id'] = Variable<int>(id);
map['data'] = Variable<String>(data);
map['last_update'] = Variable<DateTime>(lastUpdate);
return map;
}
ExercisesCompanion toCompanion(bool nullToAbsent) {
return ExercisesCompanion(
id: Value(id),
data: Value(data),
lastUpdate: Value(lastUpdate),
);
}
factory ExerciseTable.fromJson(Map<String, dynamic> json, {ValueSerializer? serializer}) {
serializer ??= driftRuntimeOptions.defaultSerializer;
return ExerciseTable(
id: serializer.fromJson<int>(json['id']),
data: serializer.fromJson<String>(json['data']),
lastUpdate: serializer.fromJson<DateTime>(json['lastUpdate']),
);
}
@override
Map<String, dynamic> toJson({ValueSerializer? serializer}) {
serializer ??= driftRuntimeOptions.defaultSerializer;
return <String, dynamic>{
'id': serializer.toJson<int>(id),
'data': serializer.toJson<String>(data),
'lastUpdate': serializer.toJson<DateTime>(lastUpdate),
};
}
ExerciseTable copyWith({int? id, String? data, DateTime? lastUpdate}) => ExerciseTable(
id: id ?? this.id,
data: data ?? this.data,
lastUpdate: lastUpdate ?? this.lastUpdate,
);
@override
String toString() {
return (StringBuffer('ExerciseTable(')
..write('id: $id, ')
..write('data: $data, ')
..write('lastUpdate: $lastUpdate')
..write(')'))
.toString();
}
@override
int get hashCode => Object.hash(id, data, lastUpdate);
@override
bool operator ==(Object other) =>
identical(this, other) ||
(other is ExerciseTable &&
other.id == this.id &&
other.data == this.data &&
other.lastUpdate == this.lastUpdate);
}
class ExercisesCompanion extends UpdateCompanion<ExerciseTable> {
final Value<int> id;
final Value<String> data;
final Value<DateTime> lastUpdate;
final Value<int> rowid;
const ExercisesCompanion({
this.id = const Value.absent(),
this.data = const Value.absent(),
this.lastUpdate = const Value.absent(),
this.rowid = const Value.absent(),
});
ExercisesCompanion.insert({
required int id,
required String data,
required DateTime lastUpdate,
this.rowid = const Value.absent(),
}) : id = Value(id),
data = Value(data),
lastUpdate = Value(lastUpdate);
static Insertable<ExerciseTable> custom({
Expression<int>? id,
Expression<String>? data,
Expression<DateTime>? lastUpdate,
Expression<int>? rowid,
}) {
return RawValuesInsertable({
if (id != null) 'id': id,
if (data != null) 'data': data,
if (lastUpdate != null) 'last_update': lastUpdate,
if (rowid != null) 'rowid': rowid,
});
}
ExercisesCompanion copyWith(
{Value<int>? id, Value<String>? data, Value<DateTime>? lastUpdate, Value<int>? rowid}) {
return ExercisesCompanion(
id: id ?? this.id,
data: data ?? this.data,
lastUpdate: lastUpdate ?? this.lastUpdate,
rowid: rowid ?? this.rowid,
);
}
@override
Map<String, Expression> toColumns(bool nullToAbsent) {
final map = <String, Expression>{};
if (id.present) {
map['id'] = Variable<int>(id.value);
}
if (data.present) {
map['data'] = Variable<String>(data.value);
}
if (lastUpdate.present) {
map['last_update'] = Variable<DateTime>(lastUpdate.value);
}
if (rowid.present) {
map['rowid'] = Variable<int>(rowid.value);
}
return map;
}
@override
String toString() {
return (StringBuffer('ExercisesCompanion(')
..write('id: $id, ')
..write('data: $data, ')
..write('lastUpdate: $lastUpdate, ')
..write('rowid: $rowid')
..write(')'))
.toString();
}
}
class $MusclesTable extends Muscles with TableInfo<$MusclesTable, MuscleTable> {
@override
final GeneratedDatabase attachedDatabase;
final String? _alias;
$MusclesTable(this.attachedDatabase, [this._alias]);
static const VerificationMeta _idMeta = const VerificationMeta('id');
@override
late final GeneratedColumn<int> id = GeneratedColumn<int>('id', aliasedName, false,
type: DriftSqlType.int, requiredDuringInsert: true);
static const VerificationMeta _dataMeta = const VerificationMeta('data');
@override
late final GeneratedColumnWithTypeConverter<Muscle, String> data = GeneratedColumn<String>(
'data', aliasedName, false,
type: DriftSqlType.string, requiredDuringInsert: true)
.withConverter<Muscle>($MusclesTable.$converterdata);
@override
List<GeneratedColumn> get $columns => [id, data];
@override
String get aliasedName => _alias ?? actualTableName;
@override
String get actualTableName => $name;
static const String $name = 'muscles';
@override
VerificationContext validateIntegrity(Insertable<MuscleTable> instance,
{bool isInserting = false}) {
final context = VerificationContext();
final data = instance.toColumns(true);
if (data.containsKey('id')) {
context.handle(_idMeta, id.isAcceptableOrUnknown(data['id']!, _idMeta));
} else if (isInserting) {
context.missing(_idMeta);
}
context.handle(_dataMeta, const VerificationResult.success());
return context;
}
@override
Set<GeneratedColumn> get $primaryKey => const {};
@override
MuscleTable map(Map<String, dynamic> data, {String? tablePrefix}) {
final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : '';
return MuscleTable(
id: attachedDatabase.typeMapping.read(DriftSqlType.int, data['${effectivePrefix}id'])!,
data: $MusclesTable.$converterdata.fromSql(
attachedDatabase.typeMapping.read(DriftSqlType.string, data['${effectivePrefix}data'])!),
);
}
@override
$MusclesTable createAlias(String alias) {
return $MusclesTable(attachedDatabase, alias);
}
static TypeConverter<Muscle, String> $converterdata = const MuscleConverter();
}
class MuscleTable extends DataClass implements Insertable<MuscleTable> {
final int id;
final Muscle data;
const MuscleTable({required this.id, required this.data});
@override
Map<String, Expression> toColumns(bool nullToAbsent) {
final map = <String, Expression>{};
map['id'] = Variable<int>(id);
{
final converter = $MusclesTable.$converterdata;
map['data'] = Variable<String>(converter.toSql(data));
}
return map;
}
MusclesCompanion toCompanion(bool nullToAbsent) {
return MusclesCompanion(
id: Value(id),
data: Value(data),
);
}
factory MuscleTable.fromJson(Map<String, dynamic> json, {ValueSerializer? serializer}) {
serializer ??= driftRuntimeOptions.defaultSerializer;
return MuscleTable(
id: serializer.fromJson<int>(json['id']),
data: serializer.fromJson<Muscle>(json['data']),
);
}
@override
Map<String, dynamic> toJson({ValueSerializer? serializer}) {
serializer ??= driftRuntimeOptions.defaultSerializer;
return <String, dynamic>{
'id': serializer.toJson<int>(id),
'data': serializer.toJson<Muscle>(data),
};
}
MuscleTable copyWith({int? id, Muscle? data}) => MuscleTable(
id: id ?? this.id,
data: data ?? this.data,
);
@override
String toString() {
return (StringBuffer('MuscleTable(')
..write('id: $id, ')
..write('data: $data')
..write(')'))
.toString();
}
@override
int get hashCode => Object.hash(id, data);
@override
bool operator ==(Object other) =>
identical(this, other) ||
(other is MuscleTable && other.id == this.id && other.data == this.data);
}
class MusclesCompanion extends UpdateCompanion<MuscleTable> {
final Value<int> id;
final Value<Muscle> data;
final Value<int> rowid;
const MusclesCompanion({
this.id = const Value.absent(),
this.data = const Value.absent(),
this.rowid = const Value.absent(),
});
MusclesCompanion.insert({
required int id,
required Muscle data,
this.rowid = const Value.absent(),
}) : id = Value(id),
data = Value(data);
static Insertable<MuscleTable> custom({
Expression<int>? id,
Expression<String>? data,
Expression<int>? rowid,
}) {
return RawValuesInsertable({
if (id != null) 'id': id,
if (data != null) 'data': data,
if (rowid != null) 'rowid': rowid,
});
}
MusclesCompanion copyWith({Value<int>? id, Value<Muscle>? data, Value<int>? rowid}) {
return MusclesCompanion(
id: id ?? this.id,
data: data ?? this.data,
rowid: rowid ?? this.rowid,
);
}
@override
Map<String, Expression> toColumns(bool nullToAbsent) {
final map = <String, Expression>{};
if (id.present) {
map['id'] = Variable<int>(id.value);
}
if (data.present) {
final converter = $MusclesTable.$converterdata;
map['data'] = Variable<String>(converter.toSql(data.value));
}
if (rowid.present) {
map['rowid'] = Variable<int>(rowid.value);
}
return map;
}
@override
String toString() {
return (StringBuffer('MusclesCompanion(')
..write('id: $id, ')
..write('data: $data, ')
..write('rowid: $rowid')
..write(')'))
.toString();
}
}
class $EquipmentsTable extends Equipments with TableInfo<$EquipmentsTable, EquipmentTable> {
@override
final GeneratedDatabase attachedDatabase;
final String? _alias;
$EquipmentsTable(this.attachedDatabase, [this._alias]);
static const VerificationMeta _idMeta = const VerificationMeta('id');
@override
late final GeneratedColumn<int> id = GeneratedColumn<int>('id', aliasedName, false,
type: DriftSqlType.int, requiredDuringInsert: true);
static const VerificationMeta _dataMeta = const VerificationMeta('data');
@override
late final GeneratedColumnWithTypeConverter<Equipment, String> data = GeneratedColumn<String>(
'data', aliasedName, false,
type: DriftSqlType.string, requiredDuringInsert: true)
.withConverter<Equipment>($EquipmentsTable.$converterdata);
@override
List<GeneratedColumn> get $columns => [id, data];
@override
String get aliasedName => _alias ?? actualTableName;
@override
String get actualTableName => $name;
static const String $name = 'equipments';
@override
VerificationContext validateIntegrity(Insertable<EquipmentTable> instance,
{bool isInserting = false}) {
final context = VerificationContext();
final data = instance.toColumns(true);
if (data.containsKey('id')) {
context.handle(_idMeta, id.isAcceptableOrUnknown(data['id']!, _idMeta));
} else if (isInserting) {
context.missing(_idMeta);
}
context.handle(_dataMeta, const VerificationResult.success());
return context;
}
@override
Set<GeneratedColumn> get $primaryKey => const {};
@override
EquipmentTable map(Map<String, dynamic> data, {String? tablePrefix}) {
final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : '';
return EquipmentTable(
id: attachedDatabase.typeMapping.read(DriftSqlType.int, data['${effectivePrefix}id'])!,
data: $EquipmentsTable.$converterdata.fromSql(
attachedDatabase.typeMapping.read(DriftSqlType.string, data['${effectivePrefix}data'])!),
);
}
@override
$EquipmentsTable createAlias(String alias) {
return $EquipmentsTable(attachedDatabase, alias);
}
static TypeConverter<Equipment, String> $converterdata = const EquipmentConverter();
}
class EquipmentTable extends DataClass implements Insertable<EquipmentTable> {
final int id;
final Equipment data;
const EquipmentTable({required this.id, required this.data});
@override
Map<String, Expression> toColumns(bool nullToAbsent) {
final map = <String, Expression>{};
map['id'] = Variable<int>(id);
{
final converter = $EquipmentsTable.$converterdata;
map['data'] = Variable<String>(converter.toSql(data));
}
return map;
}
EquipmentsCompanion toCompanion(bool nullToAbsent) {
return EquipmentsCompanion(
id: Value(id),
data: Value(data),
);
}
factory EquipmentTable.fromJson(Map<String, dynamic> json, {ValueSerializer? serializer}) {
serializer ??= driftRuntimeOptions.defaultSerializer;
return EquipmentTable(
id: serializer.fromJson<int>(json['id']),
data: serializer.fromJson<Equipment>(json['data']),
);
}
@override
Map<String, dynamic> toJson({ValueSerializer? serializer}) {
serializer ??= driftRuntimeOptions.defaultSerializer;
return <String, dynamic>{
'id': serializer.toJson<int>(id),
'data': serializer.toJson<Equipment>(data),
};
}
EquipmentTable copyWith({int? id, Equipment? data}) => EquipmentTable(
id: id ?? this.id,
data: data ?? this.data,
);
@override
String toString() {
return (StringBuffer('EquipmentTable(')
..write('id: $id, ')
..write('data: $data')
..write(')'))
.toString();
}
@override
int get hashCode => Object.hash(id, data);
@override
bool operator ==(Object other) =>
identical(this, other) ||
(other is EquipmentTable && other.id == this.id && other.data == this.data);
}
class EquipmentsCompanion extends UpdateCompanion<EquipmentTable> {
final Value<int> id;
final Value<Equipment> data;
final Value<int> rowid;
const EquipmentsCompanion({
this.id = const Value.absent(),
this.data = const Value.absent(),
this.rowid = const Value.absent(),
});
EquipmentsCompanion.insert({
required int id,
required Equipment data,
this.rowid = const Value.absent(),
}) : id = Value(id),
data = Value(data);
static Insertable<EquipmentTable> custom({
Expression<int>? id,
Expression<String>? data,
Expression<int>? rowid,
}) {
return RawValuesInsertable({
if (id != null) 'id': id,
if (data != null) 'data': data,
if (rowid != null) 'rowid': rowid,
});
}
EquipmentsCompanion copyWith({Value<int>? id, Value<Equipment>? data, Value<int>? rowid}) {
return EquipmentsCompanion(
id: id ?? this.id,
data: data ?? this.data,
rowid: rowid ?? this.rowid,
);
}
@override
Map<String, Expression> toColumns(bool nullToAbsent) {
final map = <String, Expression>{};
if (id.present) {
map['id'] = Variable<int>(id.value);
}
if (data.present) {
final converter = $EquipmentsTable.$converterdata;
map['data'] = Variable<String>(converter.toSql(data.value));
}
if (rowid.present) {
map['rowid'] = Variable<int>(rowid.value);
}
return map;
}
@override
String toString() {
return (StringBuffer('EquipmentsCompanion(')
..write('id: $id, ')
..write('data: $data, ')
..write('rowid: $rowid')
..write(')'))
.toString();
}
}
class $CategoriesTable extends Categories with TableInfo<$CategoriesTable, CategoryTable> {
@override
final GeneratedDatabase attachedDatabase;
final String? _alias;
$CategoriesTable(this.attachedDatabase, [this._alias]);
static const VerificationMeta _idMeta = const VerificationMeta('id');
@override
late final GeneratedColumn<int> id = GeneratedColumn<int>('id', aliasedName, false,
type: DriftSqlType.int, requiredDuringInsert: true);
static const VerificationMeta _dataMeta = const VerificationMeta('data');
@override
late final GeneratedColumnWithTypeConverter<ExerciseCategory, String> data =
GeneratedColumn<String>('data', aliasedName, false,
type: DriftSqlType.string, requiredDuringInsert: true)
.withConverter<ExerciseCategory>($CategoriesTable.$converterdata);
@override
List<GeneratedColumn> get $columns => [id, data];
@override
String get aliasedName => _alias ?? actualTableName;
@override
String get actualTableName => $name;
static const String $name = 'categories';
@override
VerificationContext validateIntegrity(Insertable<CategoryTable> instance,
{bool isInserting = false}) {
final context = VerificationContext();
final data = instance.toColumns(true);
if (data.containsKey('id')) {
context.handle(_idMeta, id.isAcceptableOrUnknown(data['id']!, _idMeta));
} else if (isInserting) {
context.missing(_idMeta);
}
context.handle(_dataMeta, const VerificationResult.success());
return context;
}
@override
Set<GeneratedColumn> get $primaryKey => const {};
@override
CategoryTable map(Map<String, dynamic> data, {String? tablePrefix}) {
final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : '';
return CategoryTable(
id: attachedDatabase.typeMapping.read(DriftSqlType.int, data['${effectivePrefix}id'])!,
data: $CategoriesTable.$converterdata.fromSql(
attachedDatabase.typeMapping.read(DriftSqlType.string, data['${effectivePrefix}data'])!),
);
}
@override
$CategoriesTable createAlias(String alias) {
return $CategoriesTable(attachedDatabase, alias);
}
static TypeConverter<ExerciseCategory, String> $converterdata = const ExerciseCategoryConverter();
}
class CategoryTable extends DataClass implements Insertable<CategoryTable> {
final int id;
final ExerciseCategory data;
const CategoryTable({required this.id, required this.data});
@override
Map<String, Expression> toColumns(bool nullToAbsent) {
final map = <String, Expression>{};
map['id'] = Variable<int>(id);
{
final converter = $CategoriesTable.$converterdata;
map['data'] = Variable<String>(converter.toSql(data));
}
return map;
}
CategoriesCompanion toCompanion(bool nullToAbsent) {
return CategoriesCompanion(
id: Value(id),
data: Value(data),
);
}
factory CategoryTable.fromJson(Map<String, dynamic> json, {ValueSerializer? serializer}) {
serializer ??= driftRuntimeOptions.defaultSerializer;
return CategoryTable(
id: serializer.fromJson<int>(json['id']),
data: serializer.fromJson<ExerciseCategory>(json['data']),
);
}
@override
Map<String, dynamic> toJson({ValueSerializer? serializer}) {
serializer ??= driftRuntimeOptions.defaultSerializer;
return <String, dynamic>{
'id': serializer.toJson<int>(id),
'data': serializer.toJson<ExerciseCategory>(data),
};
}
CategoryTable copyWith({int? id, ExerciseCategory? data}) => CategoryTable(
id: id ?? this.id,
data: data ?? this.data,
);
@override
String toString() {
return (StringBuffer('CategoryTable(')
..write('id: $id, ')
..write('data: $data')
..write(')'))
.toString();
}
@override
int get hashCode => Object.hash(id, data);
@override
bool operator ==(Object other) =>
identical(this, other) ||
(other is CategoryTable && other.id == this.id && other.data == this.data);
}
class CategoriesCompanion extends UpdateCompanion<CategoryTable> {
final Value<int> id;
final Value<ExerciseCategory> data;
final Value<int> rowid;
const CategoriesCompanion({
this.id = const Value.absent(),
this.data = const Value.absent(),
this.rowid = const Value.absent(),
});
CategoriesCompanion.insert({
required int id,
required ExerciseCategory data,
this.rowid = const Value.absent(),
}) : id = Value(id),
data = Value(data);
static Insertable<CategoryTable> custom({
Expression<int>? id,
Expression<String>? data,
Expression<int>? rowid,
}) {
return RawValuesInsertable({
if (id != null) 'id': id,
if (data != null) 'data': data,
if (rowid != null) 'rowid': rowid,
});
}
CategoriesCompanion copyWith({Value<int>? id, Value<ExerciseCategory>? data, Value<int>? rowid}) {
return CategoriesCompanion(
id: id ?? this.id,
data: data ?? this.data,
rowid: rowid ?? this.rowid,
);
}
@override
Map<String, Expression> toColumns(bool nullToAbsent) {
final map = <String, Expression>{};
if (id.present) {
map['id'] = Variable<int>(id.value);
}
if (data.present) {
final converter = $CategoriesTable.$converterdata;
map['data'] = Variable<String>(converter.toSql(data.value));
}
if (rowid.present) {
map['rowid'] = Variable<int>(rowid.value);
}
return map;
}
@override
String toString() {
return (StringBuffer('CategoriesCompanion(')
..write('id: $id, ')
..write('data: $data, ')
..write('rowid: $rowid')
..write(')'))
.toString();
}
}
class $LanguagesTable extends Languages with TableInfo<$LanguagesTable, LanguagesTable> {
@override
final GeneratedDatabase attachedDatabase;
final String? _alias;
$LanguagesTable(this.attachedDatabase, [this._alias]);
static const VerificationMeta _idMeta = const VerificationMeta('id');
@override
late final GeneratedColumn<int> id = GeneratedColumn<int>('id', aliasedName, false,
type: DriftSqlType.int, requiredDuringInsert: true);
static const VerificationMeta _dataMeta = const VerificationMeta('data');
@override
late final GeneratedColumnWithTypeConverter<Language, String> data = GeneratedColumn<String>(
'data', aliasedName, false,
type: DriftSqlType.string, requiredDuringInsert: true)
.withConverter<Language>($LanguagesTable.$converterdata);
@override
List<GeneratedColumn> get $columns => [id, data];
@override
String get aliasedName => _alias ?? actualTableName;
@override
String get actualTableName => $name;
static const String $name = 'languages';
@override
VerificationContext validateIntegrity(Insertable<LanguagesTable> instance,
{bool isInserting = false}) {
final context = VerificationContext();
final data = instance.toColumns(true);
if (data.containsKey('id')) {
context.handle(_idMeta, id.isAcceptableOrUnknown(data['id']!, _idMeta));
} else if (isInserting) {
context.missing(_idMeta);
}
context.handle(_dataMeta, const VerificationResult.success());
return context;
}
@override
Set<GeneratedColumn> get $primaryKey => const {};
@override
LanguagesTable map(Map<String, dynamic> data, {String? tablePrefix}) {
final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : '';
return LanguagesTable(
id: attachedDatabase.typeMapping.read(DriftSqlType.int, data['${effectivePrefix}id'])!,
data: $LanguagesTable.$converterdata.fromSql(
attachedDatabase.typeMapping.read(DriftSqlType.string, data['${effectivePrefix}data'])!),
);
}
@override
$LanguagesTable createAlias(String alias) {
return $LanguagesTable(attachedDatabase, alias);
}
static TypeConverter<Language, String> $converterdata = const LanguageConverter();
}
class LanguagesTable extends DataClass implements Insertable<LanguagesTable> {
final int id;
final Language data;
const LanguagesTable({required this.id, required this.data});
@override
Map<String, Expression> toColumns(bool nullToAbsent) {
final map = <String, Expression>{};
map['id'] = Variable<int>(id);
{
final converter = $LanguagesTable.$converterdata;
map['data'] = Variable<String>(converter.toSql(data));
}
return map;
}
LanguagesCompanion toCompanion(bool nullToAbsent) {
return LanguagesCompanion(
id: Value(id),
data: Value(data),
);
}
factory LanguagesTable.fromJson(Map<String, dynamic> json, {ValueSerializer? serializer}) {
serializer ??= driftRuntimeOptions.defaultSerializer;
return LanguagesTable(
id: serializer.fromJson<int>(json['id']),
data: serializer.fromJson<Language>(json['data']),
);
}
@override
Map<String, dynamic> toJson({ValueSerializer? serializer}) {
serializer ??= driftRuntimeOptions.defaultSerializer;
return <String, dynamic>{
'id': serializer.toJson<int>(id),
'data': serializer.toJson<Language>(data),
};
}
LanguagesTable copyWith({int? id, Language? data}) => LanguagesTable(
id: id ?? this.id,
data: data ?? this.data,
);
@override
String toString() {
return (StringBuffer('LanguagesTable(')
..write('id: $id, ')
..write('data: $data')
..write(')'))
.toString();
}
@override
int get hashCode => Object.hash(id, data);
@override
bool operator ==(Object other) =>
identical(this, other) ||
(other is LanguagesTable && other.id == this.id && other.data == this.data);
}
class LanguagesCompanion extends UpdateCompanion<LanguagesTable> {
final Value<int> id;
final Value<Language> data;
final Value<int> rowid;
const LanguagesCompanion({
this.id = const Value.absent(),
this.data = const Value.absent(),
this.rowid = const Value.absent(),
});
LanguagesCompanion.insert({
required int id,
required Language data,
this.rowid = const Value.absent(),
}) : id = Value(id),
data = Value(data);
static Insertable<LanguagesTable> custom({
Expression<int>? id,
Expression<String>? data,
Expression<int>? rowid,
}) {
return RawValuesInsertable({
if (id != null) 'id': id,
if (data != null) 'data': data,
if (rowid != null) 'rowid': rowid,
});
}
LanguagesCompanion copyWith({Value<int>? id, Value<Language>? data, Value<int>? rowid}) {
return LanguagesCompanion(
id: id ?? this.id,
data: data ?? this.data,
rowid: rowid ?? this.rowid,
);
}
@override
Map<String, Expression> toColumns(bool nullToAbsent) {
final map = <String, Expression>{};
if (id.present) {
map['id'] = Variable<int>(id.value);
}
if (data.present) {
final converter = $LanguagesTable.$converterdata;
map['data'] = Variable<String>(converter.toSql(data.value));
}
if (rowid.present) {
map['rowid'] = Variable<int>(rowid.value);
}
return map;
}
@override
String toString() {
return (StringBuffer('LanguagesCompanion(')
..write('id: $id, ')
..write('data: $data, ')
..write('rowid: $rowid')
..write(')'))
.toString();
}
}
abstract class _$ExerciseDatabase extends GeneratedDatabase {
_$ExerciseDatabase(QueryExecutor e) : super(e);
late final $ExercisesTable exercises = $ExercisesTable(this);
late final $MusclesTable muscles = $MusclesTable(this);
late final $EquipmentsTable equipments = $EquipmentsTable(this);
late final $CategoriesTable categories = $CategoriesTable(this);
late final $LanguagesTable languages = $LanguagesTable(this);
@override
Iterable<TableInfo<Table, Object?>> get allTables =>
allSchemaEntities.whereType<TableInfo<Table, Object?>>();
@override
List<DatabaseSchemaEntity> get allSchemaEntities =>
[exercises, muscles, equipments, categories, languages];
}

View File

@@ -1,10 +1,6 @@
import 'dart:convert';
import 'package:drift/drift.dart';
import 'package:flutter/src/widgets/framework.dart';
// import 'package:path/path.dart';
import 'package:provider/provider.dart';
import 'package:wger/exceptions/no_such_entry_exception.dart';
import 'package:wger/models/exercises/alias.dart';
import 'package:wger/models/exercises/base.dart';
import 'package:wger/models/exercises/category.dart';
@@ -16,25 +12,6 @@ import 'package:wger/models/exercises/muscle.dart';
import 'package:wger/models/exercises/translation.dart';
import 'package:wger/models/exercises/variation.dart';
import 'package:wger/models/exercises/video.dart';
import 'package:wger/providers/auth.dart';
import 'package:wger/providers/base_provider.dart';
// List<Language> _languages = [];
// Future<void> fetchAndSetLanguages(BuildContext context) async {
// final baseProvider= WgerBaseProvider(Provider.of<AuthProvider>(context, listen: false))
// final languageData = await baseProvider.fetchPaginated(baseProvider.makeUrl('language'));
// for (final language in languageData) {
// _languages.add(Language.fromJson(language));
// }
// }
// Language findLanguageById(int id) {
// return _languages.firstWhere(
// (language) => language.id == id,
// orElse: () => throw NoSuchEntryException(),
// );
// }
class ExerciseBaseConverter extends TypeConverter<ExerciseBase, String> {
const ExerciseBaseConverter();
@@ -50,20 +27,18 @@ class ExerciseBaseConverter extends TypeConverter<ExerciseBase, String> {
final images = baseData['images'].map((e) => ExerciseImage.fromJson(e)).toList();
final videos = baseData['videos'].map((e) => Video.fromJson(e)).toList();
final List<Translation> exercises = [];
final List<Translation> translations = [];
for (final exerciseData in baseData['translations']) {
final exercise = Translation(
final translation = Translation(
id: exerciseData['id'],
name: exerciseData['name'],
description: exerciseData['description'],
baseId: baseData['id'],
);
exercise.aliases =
exerciseData['aliases'].map((e) => Alias.fromJson(e)).toList().cast<Alias>();
exercise.notes =
exerciseData['notes'].map((e) => Comment.fromJson(e)).toList().cast<Comment>();
exercise.language = Language.fromJson(exerciseData['languageObj']);
exercises.add(exercise);
translation.aliases = exerciseData['aliases'].map((e) => Alias.fromJson(e)).toList();
translation.notes = exerciseData['notes'].map((e) => Comment.fromJson(e)).toList();
translation.language = Language.fromJson(exerciseData['languageObj']);
translations.add(translation);
}
final exerciseBase = ExerciseBase(
@@ -76,7 +51,7 @@ class ExerciseBaseConverter extends TypeConverter<ExerciseBase, String> {
equipment: equipment.cast<Equipment>(),
category: category,
images: images.cast<ExerciseImage>(),
exercises: exercises,
exercises: translations,
videos: videos.cast<Video>(),
);
return exerciseBase;

View File

@@ -0,0 +1,34 @@
import 'dart:io';
import 'package:drift/drift.dart';
import 'package:drift/native.dart';
import 'package:path/path.dart' as p;
import 'package:path_provider/path_provider.dart';
part 'ingredients_database.g.dart';
@DataClassName('IngredientTable')
class Ingredients extends Table {
IntColumn get id => integer()();
TextColumn get data => text()();
DateTimeColumn get lastUpdate => dateTime()();
}
@DriftDatabase(tables: [Ingredients])
class IngredientDatabase extends _$IngredientDatabase {
IngredientDatabase() : super(_openConnection());
@override
// TODO: implement schemaVersion
int get schemaVersion => 1;
}
LazyDatabase _openConnection() {
return LazyDatabase(() async {
final dbFolder = await getApplicationDocumentsDirectory();
final file = File(p.join(dbFolder.path, 'ingredients.sqlite'));
return NativeDatabase.createInBackground(file);
});
}

View File

@@ -0,0 +1,221 @@
// GENERATED CODE - DO NOT MODIFY BY HAND
part of 'ingredients_database.dart';
// ignore_for_file: type=lint
class $IngredientsTable extends Ingredients with TableInfo<$IngredientsTable, IngredientTable> {
@override
final GeneratedDatabase attachedDatabase;
final String? _alias;
$IngredientsTable(this.attachedDatabase, [this._alias]);
static const VerificationMeta _idMeta = const VerificationMeta('id');
@override
late final GeneratedColumn<int> id = GeneratedColumn<int>('id', aliasedName, false,
type: DriftSqlType.int, requiredDuringInsert: true);
static const VerificationMeta _dataMeta = const VerificationMeta('data');
@override
late final GeneratedColumn<String> data = GeneratedColumn<String>('data', aliasedName, false,
type: DriftSqlType.string, requiredDuringInsert: true);
static const VerificationMeta _lastUpdateMeta = const VerificationMeta('lastUpdate');
@override
late final GeneratedColumn<DateTime> lastUpdate = GeneratedColumn<DateTime>(
'last_update', aliasedName, false,
type: DriftSqlType.dateTime, requiredDuringInsert: true);
@override
List<GeneratedColumn> get $columns => [id, data, lastUpdate];
@override
String get aliasedName => _alias ?? actualTableName;
@override
String get actualTableName => $name;
static const String $name = 'ingredients';
@override
VerificationContext validateIntegrity(Insertable<IngredientTable> instance,
{bool isInserting = false}) {
final context = VerificationContext();
final data = instance.toColumns(true);
if (data.containsKey('id')) {
context.handle(_idMeta, id.isAcceptableOrUnknown(data['id']!, _idMeta));
} else if (isInserting) {
context.missing(_idMeta);
}
if (data.containsKey('data')) {
context.handle(_dataMeta, this.data.isAcceptableOrUnknown(data['data']!, _dataMeta));
} else if (isInserting) {
context.missing(_dataMeta);
}
if (data.containsKey('last_update')) {
context.handle(
_lastUpdateMeta, lastUpdate.isAcceptableOrUnknown(data['last_update']!, _lastUpdateMeta));
} else if (isInserting) {
context.missing(_lastUpdateMeta);
}
return context;
}
@override
Set<GeneratedColumn> get $primaryKey => const {};
@override
IngredientTable map(Map<String, dynamic> data, {String? tablePrefix}) {
final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : '';
return IngredientTable(
id: attachedDatabase.typeMapping.read(DriftSqlType.int, data['${effectivePrefix}id'])!,
data: attachedDatabase.typeMapping.read(DriftSqlType.string, data['${effectivePrefix}data'])!,
lastUpdate: attachedDatabase.typeMapping
.read(DriftSqlType.dateTime, data['${effectivePrefix}last_update'])!,
);
}
@override
$IngredientsTable createAlias(String alias) {
return $IngredientsTable(attachedDatabase, alias);
}
}
class IngredientTable extends DataClass implements Insertable<IngredientTable> {
final int id;
final String data;
final DateTime lastUpdate;
const IngredientTable({required this.id, required this.data, required this.lastUpdate});
@override
Map<String, Expression> toColumns(bool nullToAbsent) {
final map = <String, Expression>{};
map['id'] = Variable<int>(id);
map['data'] = Variable<String>(data);
map['last_update'] = Variable<DateTime>(lastUpdate);
return map;
}
IngredientsCompanion toCompanion(bool nullToAbsent) {
return IngredientsCompanion(
id: Value(id),
data: Value(data),
lastUpdate: Value(lastUpdate),
);
}
factory IngredientTable.fromJson(Map<String, dynamic> json, {ValueSerializer? serializer}) {
serializer ??= driftRuntimeOptions.defaultSerializer;
return IngredientTable(
id: serializer.fromJson<int>(json['id']),
data: serializer.fromJson<String>(json['data']),
lastUpdate: serializer.fromJson<DateTime>(json['lastUpdate']),
);
}
@override
Map<String, dynamic> toJson({ValueSerializer? serializer}) {
serializer ??= driftRuntimeOptions.defaultSerializer;
return <String, dynamic>{
'id': serializer.toJson<int>(id),
'data': serializer.toJson<String>(data),
'lastUpdate': serializer.toJson<DateTime>(lastUpdate),
};
}
IngredientTable copyWith({int? id, String? data, DateTime? lastUpdate}) => IngredientTable(
id: id ?? this.id,
data: data ?? this.data,
lastUpdate: lastUpdate ?? this.lastUpdate,
);
@override
String toString() {
return (StringBuffer('IngredientTable(')
..write('id: $id, ')
..write('data: $data, ')
..write('lastUpdate: $lastUpdate')
..write(')'))
.toString();
}
@override
int get hashCode => Object.hash(id, data, lastUpdate);
@override
bool operator ==(Object other) =>
identical(this, other) ||
(other is IngredientTable &&
other.id == this.id &&
other.data == this.data &&
other.lastUpdate == this.lastUpdate);
}
class IngredientsCompanion extends UpdateCompanion<IngredientTable> {
final Value<int> id;
final Value<String> data;
final Value<DateTime> lastUpdate;
final Value<int> rowid;
const IngredientsCompanion({
this.id = const Value.absent(),
this.data = const Value.absent(),
this.lastUpdate = const Value.absent(),
this.rowid = const Value.absent(),
});
IngredientsCompanion.insert({
required int id,
required String data,
required DateTime lastUpdate,
this.rowid = const Value.absent(),
}) : id = Value(id),
data = Value(data),
lastUpdate = Value(lastUpdate);
static Insertable<IngredientTable> custom({
Expression<int>? id,
Expression<String>? data,
Expression<DateTime>? lastUpdate,
Expression<int>? rowid,
}) {
return RawValuesInsertable({
if (id != null) 'id': id,
if (data != null) 'data': data,
if (lastUpdate != null) 'last_update': lastUpdate,
if (rowid != null) 'rowid': rowid,
});
}
IngredientsCompanion copyWith(
{Value<int>? id, Value<String>? data, Value<DateTime>? lastUpdate, Value<int>? rowid}) {
return IngredientsCompanion(
id: id ?? this.id,
data: data ?? this.data,
lastUpdate: lastUpdate ?? this.lastUpdate,
rowid: rowid ?? this.rowid,
);
}
@override
Map<String, Expression> toColumns(bool nullToAbsent) {
final map = <String, Expression>{};
if (id.present) {
map['id'] = Variable<int>(id.value);
}
if (data.present) {
map['data'] = Variable<String>(data.value);
}
if (lastUpdate.present) {
map['last_update'] = Variable<DateTime>(lastUpdate.value);
}
if (rowid.present) {
map['rowid'] = Variable<int>(rowid.value);
}
return map;
}
@override
String toString() {
return (StringBuffer('IngredientsCompanion(')
..write('id: $id, ')
..write('data: $data, ')
..write('lastUpdate: $lastUpdate, ')
..write('rowid: $rowid')
..write(')'))
.toString();
}
}
abstract class _$IngredientDatabase extends GeneratedDatabase {
_$IngredientDatabase(QueryExecutor e) : super(e);
late final $IngredientsTable ingredients = $IngredientsTable(this);
@override
Iterable<TableInfo<Table, Object?>> get allTables =>
allSchemaEntities.whereType<TableInfo<Table, Object?>>();
@override
List<DatabaseSchemaEntity> get allSchemaEntities => [ingredients];
}

View File

@@ -51,6 +51,10 @@ const SUBMIT_BUTTON_KEY_NAME = 'submit-button';
/// Local Preferences keys
const PREFS_EXERCISES = 'exerciseData';
const PREFS_LAST_UPDATED_MUSCLES = 'lastUpdatedMuscles';
const PREFS_LAST_UPDATED_EQUIPMENT = 'lastUpdatedEquipment';
const PREFS_LAST_UPDATED_CATEGORIES = 'lastUpdatedCategories';
const PREFS_LAST_UPDATED_LANGUAGES = 'lastUpdatedLanguages';
const PREFS_EXERCISE_CACHE_VERSION = 'cacheVersion';
const PREFS_INGREDIENTS = 'ingredientData';

View File

@@ -16,18 +16,12 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
import 'package:drift/drift.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
import 'package:flutter_zxing/flutter_zxing.dart';
import 'package:provider/provider.dart';
import 'package:wger/core/locator.dart';
import 'package:wger/database/exercise_DB/exercise_database.dart';
import 'package:wger/models/exercises/base.dart';
import 'package:wger/models/exercises/category.dart';
import 'package:wger/models/exercises/equipment.dart';
import 'package:wger/models/exercises/muscle.dart';
import 'package:wger/providers/add_exercise.dart';
import 'package:wger/providers/base_provider.dart';
import 'package:wger/providers/body_weight.dart';

View File

@@ -45,6 +45,9 @@ class ExerciseBase extends Equatable {
@JsonKey(required: true, name: 'last_update')
final DateTime? lastUpdate;
@JsonKey(required: true, name: 'last_update_global')
final DateTime? lastUpdateGlobal;
@JsonKey(required: true, name: 'category')
late int categoryId;
@@ -54,7 +57,7 @@ class ExerciseBase extends Equatable {
@JsonKey(required: true, name: 'muscles')
List<int> musclesIds = [];
@JsonKey(includeFromJson: false, includeToJson: true, name: 'muscless')
@JsonKey(includeFromJson: false, includeToJson: false)
List<Muscle> muscles = [];
@JsonKey(required: true, name: 'muscles_secondary')
@@ -66,16 +69,16 @@ class ExerciseBase extends Equatable {
@JsonKey(required: true, name: 'equipment')
List<int> equipmentIds = [];
@JsonKey(includeFromJson: false, includeToJson: true, name: 'equipments')
@JsonKey(includeFromJson: false, includeToJson: false)
List<Equipment> equipment = [];
@JsonKey(includeFromJson: false, includeToJson: true)
@JsonKey(includeFromJson: false, includeToJson: false)
List<ExerciseImage> images = [];
@JsonKey(includeFromJson: true, includeToJson: true)
@JsonKey(includeFromJson: true, includeToJson: false)
List<Translation> translations = [];
@JsonKey(includeFromJson: false, includeToJson: true)
@JsonKey(includeFromJson: false, includeToJson: false)
List<Video> videos = [];
ExerciseBase({
@@ -83,6 +86,7 @@ class ExerciseBase extends Equatable {
this.uuid,
this.created,
this.lastUpdate,
this.lastUpdateGlobal,
this.variationId,
List<Muscle>? muscles,
List<Muscle>? musclesSecondary,
@@ -114,9 +118,9 @@ class ExerciseBase extends Equatable {
equipmentIds = equipment.map((e) => e.id).toList();
}
if (exercises == null) {
print("Exercises are NULL");
}
// if (exercises == null) {
// print("Exercises are NULL");
// }
if (exercises != null) {
translations = exercises;

View File

@@ -15,6 +15,7 @@ ExerciseBase _$ExerciseBaseFromJson(Map<String, dynamic> json) {
'variations',
'created',
'last_update',
'last_update_global',
'category',
'muscles',
'muscles_secondary',
@@ -26,6 +27,9 @@ ExerciseBase _$ExerciseBaseFromJson(Map<String, dynamic> json) {
uuid: json['uuid'] as String?,
created: json['created'] == null ? null : DateTime.parse(json['created'] as String),
lastUpdate: json['last_update'] == null ? null : DateTime.parse(json['last_update'] as String),
lastUpdateGlobal: json['last_update_global'] == null
? null
: DateTime.parse(json['last_update_global'] as String),
variationId: json['variations'] as int?,
category: json['categories'] == null
? null
@@ -47,15 +51,11 @@ Map<String, dynamic> _$ExerciseBaseToJson(ExerciseBase instance) => <String, dyn
'variations': instance.variationId,
'created': instance.created?.toIso8601String(),
'last_update': instance.lastUpdate?.toIso8601String(),
'last_update_global': instance.lastUpdateGlobal?.toIso8601String(),
'category': instance.categoryId,
'categories': instance.category?.toJson(),
'muscles': instance.musclesIds,
'muscless': instance.muscles.map((e) => e.toJson()).toList(),
'muscles_secondary': instance.musclesSecondaryIds,
'musclesSecondary': instance.musclesSecondary.map((e) => e.toJson()).toList(),
'equipment': instance.equipmentIds,
'equipments': instance.equipment.map((e) => e.toJson()).toList(),
'images': instance.images.map((e) => e.toJson()).toList(),
'translations': instance.translations.map((e) => e.toJson()).toList(),
'videos': instance.videos.map((e) => e.toJson()).toList(),
};

View File

@@ -4,7 +4,6 @@ import 'package:wger/models/exercises/equipment.dart';
import 'package:wger/models/exercises/exercise_model.dart';
import 'package:wger/models/exercises/image.dart';
import 'package:wger/models/exercises/muscle.dart';
import 'package:wger/models/exercises/translation.dart';
import 'package:wger/models/exercises/video.dart';
part 'exercise_base_data.freezed.dart';
@@ -15,6 +14,12 @@ class ExerciseBaseData with _$ExerciseBaseData {
factory ExerciseBaseData({
required int id,
required String uuid,
// ignore: invalid_annotation_target
@JsonKey(name: 'created') required DateTime created,
// ignore: invalid_annotation_target
@JsonKey(name: 'last_update') required DateTime lastUpdate,
// ignore: invalid_annotation_target
@JsonKey(name: 'last_update_global') required DateTime lastUpdateGlobal,
required ExerciseCategory category,
required List<Muscle> muscles,
// ignore: invalid_annotation_target

View File

@@ -21,7 +21,14 @@ ExerciseBaseData _$ExerciseBaseDataFromJson(Map<String, dynamic> json) {
/// @nodoc
mixin _$ExerciseBaseData {
int get id => throw _privateConstructorUsedError;
String get uuid => throw _privateConstructorUsedError;
String get uuid => throw _privateConstructorUsedError; // ignore: invalid_annotation_target
@JsonKey(name: 'created')
DateTime get created => throw _privateConstructorUsedError; // ignore: invalid_annotation_target
@JsonKey(name: 'last_update')
DateTime get lastUpdate =>
throw _privateConstructorUsedError; // ignore: invalid_annotation_target
@JsonKey(name: 'last_update_global')
DateTime get lastUpdateGlobal => throw _privateConstructorUsedError;
ExerciseCategory get category => throw _privateConstructorUsedError;
List<Muscle> get muscles =>
throw _privateConstructorUsedError; // ignore: invalid_annotation_target
@@ -45,6 +52,9 @@ abstract class $ExerciseBaseDataCopyWith<$Res> {
$Res call(
{int id,
String uuid,
@JsonKey(name: 'created') DateTime created,
@JsonKey(name: 'last_update') DateTime lastUpdate,
@JsonKey(name: 'last_update_global') DateTime lastUpdateGlobal,
ExerciseCategory category,
List<Muscle> muscles,
@JsonKey(name: 'muscles_secondary') List<Muscle> musclesSecondary,
@@ -69,6 +79,9 @@ class _$ExerciseBaseDataCopyWithImpl<$Res, $Val extends ExerciseBaseData>
$Res call({
Object? id = null,
Object? uuid = null,
Object? created = null,
Object? lastUpdate = null,
Object? lastUpdateGlobal = null,
Object? category = null,
Object? muscles = null,
Object? musclesSecondary = null,
@@ -86,6 +99,18 @@ class _$ExerciseBaseDataCopyWithImpl<$Res, $Val extends ExerciseBaseData>
? _value.uuid
: uuid // ignore: cast_nullable_to_non_nullable
as String,
created: null == created
? _value.created
: created // ignore: cast_nullable_to_non_nullable
as DateTime,
lastUpdate: null == lastUpdate
? _value.lastUpdate
: lastUpdate // ignore: cast_nullable_to_non_nullable
as DateTime,
lastUpdateGlobal: null == lastUpdateGlobal
? _value.lastUpdateGlobal
: lastUpdateGlobal // ignore: cast_nullable_to_non_nullable
as DateTime,
category: null == category
? _value.category
: category // ignore: cast_nullable_to_non_nullable
@@ -128,6 +153,9 @@ abstract class _$$ExerciseBaseDataImplCopyWith<$Res> implements $ExerciseBaseDat
$Res call(
{int id,
String uuid,
@JsonKey(name: 'created') DateTime created,
@JsonKey(name: 'last_update') DateTime lastUpdate,
@JsonKey(name: 'last_update_global') DateTime lastUpdateGlobal,
ExerciseCategory category,
List<Muscle> muscles,
@JsonKey(name: 'muscles_secondary') List<Muscle> musclesSecondary,
@@ -150,6 +178,9 @@ class __$$ExerciseBaseDataImplCopyWithImpl<$Res>
$Res call({
Object? id = null,
Object? uuid = null,
Object? created = null,
Object? lastUpdate = null,
Object? lastUpdateGlobal = null,
Object? category = null,
Object? muscles = null,
Object? musclesSecondary = null,
@@ -167,6 +198,18 @@ class __$$ExerciseBaseDataImplCopyWithImpl<$Res>
? _value.uuid
: uuid // ignore: cast_nullable_to_non_nullable
as String,
created: null == created
? _value.created
: created // ignore: cast_nullable_to_non_nullable
as DateTime,
lastUpdate: null == lastUpdate
? _value.lastUpdate
: lastUpdate // ignore: cast_nullable_to_non_nullable
as DateTime,
lastUpdateGlobal: null == lastUpdateGlobal
? _value.lastUpdateGlobal
: lastUpdateGlobal // ignore: cast_nullable_to_non_nullable
as DateTime,
category: null == category
? _value.category
: category // ignore: cast_nullable_to_non_nullable
@@ -205,6 +248,9 @@ class _$ExerciseBaseDataImpl implements _ExerciseBaseData {
_$ExerciseBaseDataImpl(
{required this.id,
required this.uuid,
@JsonKey(name: 'created') required this.created,
@JsonKey(name: 'last_update') required this.lastUpdate,
@JsonKey(name: 'last_update_global') required this.lastUpdateGlobal,
required this.category,
required final List<Muscle> muscles,
@JsonKey(name: 'muscles_secondary') required final List<Muscle> musclesSecondary,
@@ -226,6 +272,18 @@ class _$ExerciseBaseDataImpl implements _ExerciseBaseData {
final int id;
@override
final String uuid;
// ignore: invalid_annotation_target
@override
@JsonKey(name: 'created')
final DateTime created;
// ignore: invalid_annotation_target
@override
@JsonKey(name: 'last_update')
final DateTime lastUpdate;
// ignore: invalid_annotation_target
@override
@JsonKey(name: 'last_update_global')
final DateTime lastUpdateGlobal;
@override
final ExerciseCategory category;
final List<Muscle> _muscles;
@@ -281,7 +339,7 @@ class _$ExerciseBaseDataImpl implements _ExerciseBaseData {
@override
String toString() {
return 'ExerciseBaseData(id: $id, uuid: $uuid, category: $category, muscles: $muscles, musclesSecondary: $musclesSecondary, equipment: $equipment, exercises: $exercises, images: $images, videos: $videos)';
return 'ExerciseBaseData(id: $id, uuid: $uuid, created: $created, lastUpdate: $lastUpdate, lastUpdateGlobal: $lastUpdateGlobal, category: $category, muscles: $muscles, musclesSecondary: $musclesSecondary, equipment: $equipment, exercises: $exercises, images: $images, videos: $videos)';
}
@override
@@ -291,6 +349,10 @@ class _$ExerciseBaseDataImpl implements _ExerciseBaseData {
other is _$ExerciseBaseDataImpl &&
(identical(other.id, id) || other.id == id) &&
(identical(other.uuid, uuid) || other.uuid == uuid) &&
(identical(other.created, created) || other.created == created) &&
(identical(other.lastUpdate, lastUpdate) || other.lastUpdate == lastUpdate) &&
(identical(other.lastUpdateGlobal, lastUpdateGlobal) ||
other.lastUpdateGlobal == lastUpdateGlobal) &&
(identical(other.category, category) || other.category == category) &&
const DeepCollectionEquality().equals(other._muscles, _muscles) &&
const DeepCollectionEquality().equals(other._musclesSecondary, _musclesSecondary) &&
@@ -306,6 +368,9 @@ class _$ExerciseBaseDataImpl implements _ExerciseBaseData {
runtimeType,
id,
uuid,
created,
lastUpdate,
lastUpdateGlobal,
category,
const DeepCollectionEquality().hash(_muscles),
const DeepCollectionEquality().hash(_musclesSecondary),
@@ -332,6 +397,9 @@ abstract class _ExerciseBaseData implements ExerciseBaseData {
factory _ExerciseBaseData(
{required final int id,
required final String uuid,
@JsonKey(name: 'created') required final DateTime created,
@JsonKey(name: 'last_update') required final DateTime lastUpdate,
@JsonKey(name: 'last_update_global') required final DateTime lastUpdateGlobal,
required final ExerciseCategory category,
required final List<Muscle> muscles,
@JsonKey(name: 'muscles_secondary') required final List<Muscle> musclesSecondary,
@@ -346,6 +414,15 @@ abstract class _ExerciseBaseData implements ExerciseBaseData {
int get id;
@override
String get uuid;
@override // ignore: invalid_annotation_target
@JsonKey(name: 'created')
DateTime get created;
@override // ignore: invalid_annotation_target
@JsonKey(name: 'last_update')
DateTime get lastUpdate;
@override // ignore: invalid_annotation_target
@JsonKey(name: 'last_update_global')
DateTime get lastUpdateGlobal;
@override
ExerciseCategory get category;
@override

View File

@@ -10,6 +10,9 @@ _$ExerciseBaseDataImpl _$$ExerciseBaseDataImplFromJson(Map<String, dynamic> json
_$ExerciseBaseDataImpl(
id: json['id'] as int,
uuid: json['uuid'] as String,
created: DateTime.parse(json['created'] as String),
lastUpdate: DateTime.parse(json['last_update'] as String),
lastUpdateGlobal: DateTime.parse(json['last_update_global'] as String),
category: ExerciseCategory.fromJson(json['category'] as Map<String, dynamic>),
muscles: (json['muscles'] as List<dynamic>)
.map((e) => Muscle.fromJson(e as Map<String, dynamic>))
@@ -35,6 +38,9 @@ Map<String, dynamic> _$$ExerciseBaseDataImplToJson(_$ExerciseBaseDataImpl instan
<String, dynamic>{
'id': instance.id,
'uuid': instance.uuid,
'created': instance.created.toIso8601String(),
'last_update': instance.lastUpdate.toIso8601String(),
'last_update_global': instance.lastUpdateGlobal.toIso8601String(),
'category': instance.category,
'muscles': instance.muscles,
'muscles_secondary': instance.musclesSecondary,

View File

@@ -34,7 +34,7 @@ class Muscle extends Equatable {
@JsonKey(required: true, name: 'name_en')
final String nameEn;
@JsonKey(name: 'is_front', required: true)
@JsonKey(required: true, name: 'is_front')
final bool isFront;
const Muscle({
@@ -46,6 +46,7 @@ class Muscle extends Equatable {
// Boilerplate
factory Muscle.fromJson(Map<String, dynamic> json) => _$MuscleFromJson(json);
Map<String, dynamic> toJson() => _$MuscleToJson(this);
@override

View File

@@ -22,24 +22,20 @@ import 'dart:developer';
import 'package:drift/drift.dart';
import 'package:flutter/material.dart';
import 'package:json_annotation/json_annotation.dart';
import 'package:shared_preferences/shared_preferences.dart';
import 'package:wger/core/locator.dart';
import 'package:wger/database/exercise_DB/exercise_database.dart';
import 'package:wger/database/exercises/exercise_database.dart';
import 'package:wger/exceptions/no_such_entry_exception.dart';
import 'package:wger/helpers/consts.dart';
import 'package:wger/models/exercises/alias.dart';
import 'package:wger/models/exercises/base.dart';
import 'package:wger/models/exercises/category.dart';
import 'package:wger/models/exercises/comment.dart';
import 'package:wger/models/exercises/equipment.dart';
import 'package:wger/models/exercises/exercise_base_data.dart';
import 'package:wger/models/exercises/image.dart';
import 'package:wger/models/exercises/language.dart';
import 'package:wger/models/exercises/muscle.dart';
import 'package:wger/models/exercises/translation.dart';
import 'package:wger/models/exercises/variation.dart';
import 'package:wger/models/exercises/video.dart';
import 'package:wger/providers/base_provider.dart';
class ExercisesProvider with ChangeNotifier {
@@ -50,14 +46,14 @@ class ExercisesProvider with ChangeNotifier {
static const EXERCISE_CACHE_DAYS = 7;
static const CACHE_VERSION = 4;
static const _exerciseBaseInfoUrlPath = 'exercisebaseinfo';
static const _exerciseSearchPath = 'exercise/search';
static const exerciseBaseInfoUrlPath = 'exercisebaseinfo';
static const exerciseSearchPath = 'exercise/search';
static const _exerciseVariationsUrlPath = 'variation';
static const _categoriesUrlPath = 'exercisecategory';
static const _musclesUrlPath = 'muscle';
static const _equipmentUrlPath = 'equipment';
static const _languageUrlPath = 'language';
static const exerciseVariationsUrlPath = 'variation';
static const categoriesUrlPath = 'exercisecategory';
static const musclesUrlPath = 'muscle';
static const equipmentUrlPath = 'equipment';
static const languageUrlPath = 'language';
List<ExerciseBase> _exerciseBases = [];
@@ -188,7 +184,7 @@ class ExercisesProvider with ChangeNotifier {
}
/// Find exercise base by ID
ExerciseBase findExerciseBaseById(int id) {
ExerciseBase findExerciseById(int id) {
return _exerciseBases.firstWhere(
(base) => base.id == id,
orElse: () => throw NoSuchEntryException(),
@@ -242,45 +238,50 @@ class ExercisesProvider with ChangeNotifier {
);
}
Future<void> fetchAndSetCategories() async {
final categories = await baseProvider.fetchPaginated(baseProvider.makeUrl(_categoriesUrlPath));
Future<void> fetchAndSetCategoriesFromApi() async {
final categories = await baseProvider.fetchPaginated(baseProvider.makeUrl(categoriesUrlPath));
for (final category in categories) {
_categories.add(ExerciseCategory.fromJson(category));
}
}
Future<void> fetchAndSetVariations() async {
Future<void> fetchAndSetVariationsFromApi() async {
final variations =
await baseProvider.fetchPaginated(baseProvider.makeUrl(_exerciseVariationsUrlPath));
await baseProvider.fetchPaginated(baseProvider.makeUrl(exerciseVariationsUrlPath));
for (final variation in variations) {
_variations.add(Variation.fromJson(variation));
}
}
Future<void> fetchAndSetMuscles() async {
final muscles = await baseProvider.fetchPaginated(baseProvider.makeUrl(_musclesUrlPath));
Future<void> fetchAndSetMusclesFromApi() async {
final muscles = await baseProvider.fetchPaginated(baseProvider.makeUrl(musclesUrlPath));
for (final muscle in muscles) {
_muscles.add(Muscle.fromJson(muscle));
}
}
Future<void> fetchAndSetEquipment() async {
final equipments = await baseProvider.fetchPaginated(baseProvider.makeUrl(_equipmentUrlPath));
Future<void> fetchAndSetEquipmentsFromApi() async {
final equipments = await baseProvider.fetchPaginated(baseProvider.makeUrl(equipmentUrlPath));
for (final equipment in equipments) {
_equipment.add(Equipment.fromJson(equipment));
}
}
Future<void> fetchAndSetLanguages() async {
final languageData = await baseProvider.fetchPaginated(baseProvider.makeUrl(_languageUrlPath));
Future<void> fetchAndSetLanguagesFromApi() async {
final languageData = await baseProvider.fetchPaginated(baseProvider.makeUrl(languageUrlPath));
for (final language in languageData) {
_languages.add(Language.fromJson(language));
}
}
Future<Language> fetchAndSetLanguageFromApi(int id) async {
final language = await baseProvider.fetch(baseProvider.makeUrl(languageUrlPath, id: id));
return Language.fromJson(language);
}
/// Returns the exercise with the given ID
///
/// If the exercise is not known locally, it is fetched from the server.
@@ -288,52 +289,79 @@ class ExercisesProvider with ChangeNotifier {
/// regular not-async getById method can be used
Future<ExerciseBase> fetchAndSetExerciseBase(int exerciseBaseId) async {
try {
return findExerciseBaseById(exerciseBaseId);
return findExerciseById(exerciseBaseId);
} on NoSuchEntryException {
final baseData = await baseProvider.fetch(
baseProvider.makeUrl(_exerciseBaseInfoUrlPath, id: exerciseBaseId),
baseProvider.makeUrl(exerciseBaseInfoUrlPath, id: exerciseBaseId),
);
final newBase = readExerciseBaseFromBaseInfo(ExerciseBaseData.fromJson(baseData));
final exercise = readExerciseBaseFromBaseInfo(ExerciseBaseData.fromJson(baseData));
final database = locator<ExerciseDatabase>();
// TODO: save to cache. Since we can't easily generate the JSON, perhaps just reload?
_exerciseBases.add(newBase);
return newBase;
final exerciseDb = await (database.select(database.exercises)
..where((e) => e.id.equals(baseData['id'])))
.getSingleOrNull();
// New exercise, insert
if (exerciseDb == null) {
database.into(database.exercises).insert(
ExercisesCompanion.insert(
id: baseData['id'],
data: jsonEncode(baseData),
lastUpdate: DateTime.parse(baseData['last_update_global']),
),
);
}
// If there were updates on the server, update
final lastUpdateApi = DateTime.parse(baseData['last_update_global']);
if (exerciseDb != null && lastUpdateApi.isAfter(exerciseDb.lastUpdate)) {
(database.update(database.exercises)..where((e) => e.id.equals(baseData['id']))).write(
ExercisesCompanion(
id: baseData['id'],
data: Value(jsonEncode(baseData)),
lastUpdate: Value(DateTime.parse(baseData['last_update_global'])),
),
);
}
_exerciseBases.add(exercise);
return exercise;
}
}
/// Parses the response from the exercisebaseinfo endpoint and returns
/// Parses the response from the "exercisebaseinfo" endpoint and returns
/// a full exercise base
ExerciseBase readExerciseBaseFromBaseInfo(ExerciseBaseData baseData) {
final List<Translation> exercises = [];
for (final exerciseData in baseData.exercises) {
final exercise = Translation(
id: exerciseData.id,
uuid: exerciseData.uuid,
name: exerciseData.name,
description: exerciseData.description,
final List<Translation> translations = [];
for (final translationData in baseData.exercises) {
final translation = Translation(
id: translationData.id,
uuid: translationData.uuid,
name: translationData.name,
description: translationData.description,
baseId: baseData.id,
);
exercise.aliases = exerciseData.aliases
.map((e) => Alias(exerciseId: exercise.id ?? 0, alias: e.alias))
.toList()
.cast<Alias>();
exercise.notes = exerciseData.notes;
exercise.language = findLanguageById(exerciseData.languageId);
exercises.add(exercise);
translation.aliases = translationData.aliases
.map((e) => Alias(exerciseId: translation.id ?? 0, alias: e.alias))
.toList();
translation.notes = translationData.notes;
translation.language = findLanguageById(translationData.languageId);
translations.add(translation);
}
final exerciseBase = ExerciseBase(
id: baseData.id,
uuid: baseData.uuid,
created: null,
//creationDate: toDate(baseData['creation_date']),
created: baseData.created,
lastUpdate: baseData.lastUpdate,
lastUpdateGlobal: baseData.lastUpdateGlobal,
musclesSecondary: baseData.muscles,
muscles: baseData.muscles,
equipment: baseData.equipment,
category: baseData.category,
images: baseData.images,
exercises: exercises,
exercises: translations,
videos: baseData.videos,
);
@@ -342,117 +370,285 @@ class ExercisesProvider with ChangeNotifier {
/// Checks the required cache version
///
/// This is needed since the content of the exercise cache can change and we need
/// to invalidate it as a result
/// This is needed since the content of the exercise cache (the API response)
/// can change and we need to invalidate it as a result
Future<void> checkExerciseCacheVersion() async {
final prefs = await SharedPreferences.getInstance();
final database = locator<ExerciseDatabase>();
if (prefs.containsKey(PREFS_EXERCISE_CACHE_VERSION)) {
final cacheVersion = prefs.getInt(PREFS_EXERCISE_CACHE_VERSION);
final cacheVersion = prefs.getInt(PREFS_EXERCISE_CACHE_VERSION)!;
// Cache has has a different version, reset
if ((cacheVersion ?? 0) != CACHE_VERSION) {
database.delete(database.exerciseTableItems).go();
await prefs.remove(PREFS_EXERCISES);
if (cacheVersion != CACHE_VERSION) {
database.delete(database.exercises).go();
}
await prefs.setInt(PREFS_EXERCISE_CACHE_VERSION, CACHE_VERSION);
// Cache has no version key, reset
// Note: this is only needed for very old apps that update and could probably
// be just removed in the future
} else {
await prefs.remove(PREFS_EXERCISES);
database.delete(database.exerciseTableItems).go();
database.delete(database.exercises).go();
await prefs.setInt(PREFS_EXERCISE_CACHE_VERSION, CACHE_VERSION);
}
}
Future<void> fetchAndSetExercises() async {
Future<void> initCacheTimesLocalPrefs() async {
final prefs = await SharedPreferences.getInstance();
// TODO: The exercise data was previously saved in PREFS_EXERCISES. This
// can now be deleted. After some time when we can be sure all users
// have updated their app, we can also remove this line and the
// PREFS_EXERCISES constant
if (prefs.containsKey(PREFS_EXERCISES)) {
prefs.remove(PREFS_EXERCISES);
}
final initDate = DateTime(2023, 1, 1).toIso8601String();
if (!prefs.containsKey(PREFS_LAST_UPDATED_MUSCLES)) {
await prefs.setString(PREFS_LAST_UPDATED_MUSCLES, initDate);
}
if (!prefs.containsKey(PREFS_LAST_UPDATED_EQUIPMENT)) {
await prefs.setString(PREFS_LAST_UPDATED_EQUIPMENT, initDate);
}
if (!prefs.containsKey(PREFS_LAST_UPDATED_LANGUAGES)) {
await prefs.setString(PREFS_LAST_UPDATED_LANGUAGES, initDate);
}
if (!prefs.containsKey(PREFS_LAST_UPDATED_CATEGORIES)) {
await prefs.setString(PREFS_LAST_UPDATED_CATEGORIES, initDate);
}
}
Future<void> clearAllCachesAndPrefs() async {
final database = locator<ExerciseDatabase>();
await database.deleteEverything();
await initCacheTimesLocalPrefs();
}
/// Loads all needed data for the exercises from the local cache, or if not available,
/// from the API:
/// - Muscles
/// - Categories
/// - Languages
/// - Equipment
/// - Exercises
Future<void> fetchAndSetInitialData() async {
clear();
// Load exercises from cache, if available
final database = locator<ExerciseDatabase>();
// Only uncomment if need to delete the table, (only for testing purposes).
// await database.delete(database.exerciseTableItems).go();
// Fetch the list of rows from ExercisesDataTable. ExerciseTable is the Type of the Row
final List<ExerciseTable> items = await database.select(database.exerciseTableItems).get();
final prefs = await SharedPreferences.getInstance();
await initCacheTimesLocalPrefs();
await checkExerciseCacheVersion();
final cacheData = json.decode(prefs.getString(PREFS_EXERCISES) ?? '{}');
if (items.isNotEmpty) {
if (DateTime.parse(cacheData['expiresIn']).isAfter(DateTime.now())) {
for (final element in items) {
if (element.equipment != null) {
_equipment.add(element.equipment!);
}
if (element.muscle != null) {
_muscles.add(element.muscle!);
}
if (element.variation != null) {
_variations.add(element.variation!);
}
if (element.language != null) {
_languages.add(element.language!);
}
if (element.category != null) {
_categories.add(element.category!);
}
if (element.exercisebase != null) {
_exerciseBases.add(element.exercisebase!);
}
}
_initFilters();
log("Read ${_exerciseBases.length} exercises from cache. Valid till ${cacheData['expiresIn']}");
// Load categories, muscles, equipment and languages
await Future.wait([
fetchAndSetMuscles(database),
fetchAndSetCategories(database),
fetchAndSetLanguages(database),
fetchAndSetEquipments(database),
]);
await fetchAndSetExercises(database);
_initFilters();
notifyListeners();
}
/// Fetches and sets the available exercises
///
/// We first try to read from the local DB, and from the API if the data is too old
Future<void> fetchAndSetExercises(ExerciseDatabase database,
{bool forceDeleteCache = false}) async {
if (forceDeleteCache) {
await database.delete(database.exercises).go();
}
final exercises = await database.select(database.exercises).get();
log('Loaded ${exercises.length} exercises from cache');
_exerciseBases = exercises
.map((e) => readExerciseBaseFromBaseInfo(ExerciseBaseData.fromJson(json.decode(e.data))))
.toList();
// updateExerciseCache(database);
}
Future<void> updateExerciseCache(ExerciseDatabase database) async {
final data = await Future.wait<dynamic>([
baseProvider.fetch(baseProvider.makeUrl(exerciseBaseInfoUrlPath, query: {'limit': '1000'})),
// TODO: variations!
//fetchAndSetVariationsFromApi(),
]);
final List<dynamic> exercisesData = data[0]['results'];
final exerciseBaseData = exercisesData.map((e) => ExerciseBaseData.fromJson(e)).toList();
_exerciseBases = exerciseBaseData.map((e) => readExerciseBaseFromBaseInfo(e)).toList();
// Insert new entries and update ones that have been edited
Future.forEach(exercisesData, (exerciseData) async {
final exercise = await (database.select(database.exercises)
..where((e) => e.id.equals(exerciseData['id'])))
.getSingleOrNull();
// New exercise, insert
if (exercise == null) {
database.into(database.exercises).insert(
ExercisesCompanion.insert(
id: exerciseData['id'],
data: jsonEncode(exerciseData),
lastUpdate: DateTime.parse(exerciseData['last_update_global']),
),
);
}
// If there were updates on the server, update
final lastUpdateApi = DateTime.parse(exerciseData['last_update_global']);
if (exercise != null && lastUpdateApi.isAfter(exercise.lastUpdate)) {
// TODO: timezones 🥳
print(
'Exercise ${exercise.id}: update API $lastUpdateApi | Update DB: ${exercise.lastUpdate}');
(database.update(database.exercises)..where((e) => e.id.equals(exerciseData['id']))).write(
ExercisesCompanion(
id: Value(exerciseData['id']),
data: Value(jsonEncode(exerciseData)),
lastUpdate: Value(DateTime.parse(exerciseData['last_update_global'])),
),
);
}
});
}
/// Fetches and sets the available muscles
///
/// We first try to read from the local DB, and from the API if the data is too old
Future<void> fetchAndSetMuscles(ExerciseDatabase database) async {
final prefs = await SharedPreferences.getInstance();
var validTill = DateTime.parse(prefs.getString(PREFS_LAST_UPDATED_MUSCLES)!);
// Cache still valid, return it
if (validTill.isAfter(DateTime.now())) {
final muscles = await database.select(database.muscles).get();
if (muscles.isNotEmpty) {
_muscles = muscles.map((e) => e.data).toList();
log('Loaded ${_muscles.length} muscles from cache');
return;
}
}
// Load categories, muscles, equipment and languages
final data = await Future.wait<dynamic>([
baseProvider.fetch(baseProvider.makeUrl(_exerciseBaseInfoUrlPath, query: {'limit': '1000'})),
fetchAndSetCategories(),
fetchAndSetMuscles(),
fetchAndSetEquipment(),
fetchAndSetLanguages(),
fetchAndSetVariations(),
]);
final List<dynamic> exerciseData = data[0]['results'];
// Fetch from API and save to DB
await fetchAndSetMusclesFromApi();
await database.delete(database.muscles).go();
await Future.forEach(_muscles, (e) async {
await database.into(database.muscles).insert(
MusclesCompanion.insert(
id: e.id,
data: e,
),
);
});
validTill = DateTime.now().add(const Duration(days: EXERCISE_CACHE_DAYS));
await prefs.setString(PREFS_LAST_UPDATED_MUSCLES, validTill.toIso8601String());
log('Wrote ${_muscles.length} muscles from cache. Valid till ${validTill}');
}
final List<ExerciseBaseData> exerciseBaseData =
exerciseData.map((e) => ExerciseBaseData.fromJson(e)).toList();
/// Fetches and sets the available categories
///
/// We first try to read from the local DB, and from the API if the data is too old
Future<void> fetchAndSetCategories(ExerciseDatabase database) async {
final prefs = await SharedPreferences.getInstance();
var validTill = DateTime.parse(prefs.getString(PREFS_LAST_UPDATED_CATEGORIES)!);
_exerciseBases =
exerciseBaseData.map((e) => readExerciseBaseFromBaseInfo(e)).toList().cast<ExerciseBase>();
try {
// Save the result to the cache
for (int i = 0; i < _exerciseBases.length; i++) {
await database.into(database.exerciseTableItems).insert(
ExerciseTableItemsCompanion.insert(
category: (i < _categories.length) ? Value(_categories[i]) : const Value(null),
equipment: (i < _equipment.length) ? Value(_equipment[i]) : const Value(null),
exercisebase:
(i < _exerciseBases.length) ? Value(_exerciseBases[i]) : const Value(null),
muscle: (i < _muscles.length) ? Value(_muscles[i]) : const Value(null),
variation: (i < _variations.length) ? Value(_variations[i]) : const Value(null),
language: (i < _languages.length) ? Value(_languages[i]) : const Value(null),
),
);
// Cache still valid, return it
if (validTill.isAfter(DateTime.now())) {
final categories = await database.select(database.categories).get();
if (categories.isNotEmpty) {
_categories = categories.map((e) => e.data).toList();
log('Loaded ${categories.length} categories from cache');
return;
}
// final List<ExerciseTable> items = await database.select(database.exerciseTableItems).get();
final cacheData = {
'expiresIn':
DateTime.now().add(const Duration(days: EXERCISE_CACHE_DAYS)).toIso8601String(),
};
log("Saved ${_exerciseBases.length} exercises to cache. Valid till ${cacheData['expiresIn']}");
await prefs.setString(PREFS_EXERCISES, json.encode(cacheData));
_initFilters();
notifyListeners();
} on MissingRequiredKeysException catch (error) {
log(error.missingKeys.toString());
rethrow;
}
// Fetch from API and save to DB
await fetchAndSetCategoriesFromApi();
await database.delete(database.categories).go();
await Future.forEach(_categories, (e) async {
await database.into(database.categories).insert(
CategoriesCompanion.insert(
id: e.id,
data: e,
),
);
});
validTill = DateTime.now().add(const Duration(days: EXERCISE_CACHE_DAYS));
await prefs.setString(PREFS_LAST_UPDATED_CATEGORIES, validTill.toIso8601String());
}
/// Fetches and sets the available languages
///
/// We first try to read from the local DB, and from the API if the data is too old
Future<void> fetchAndSetLanguages(ExerciseDatabase database) async {
final prefs = await SharedPreferences.getInstance();
var validTill = DateTime.parse(prefs.getString(PREFS_LAST_UPDATED_LANGUAGES)!);
// Cache still valid, return it
if (validTill.isAfter(DateTime.now())) {
final languages = await database.select(database.languages).get();
if (languages.isNotEmpty) {
_languages = languages.map((e) => e.data).toList();
return;
}
}
// Fetch from API and save to DB
await fetchAndSetLanguagesFromApi();
await database.delete(database.languages).go();
await Future.forEach(_languages, (e) async {
await database.into(database.languages).insert(
LanguagesCompanion.insert(
id: e.id,
data: e,
),
);
});
validTill = DateTime.now().add(const Duration(days: EXERCISE_CACHE_DAYS));
await prefs.setString(PREFS_LAST_UPDATED_LANGUAGES, validTill.toIso8601String());
}
/// Fetches and sets the available equipment
///
/// We first try to read from the local DB, and from the API if the data is too old
Future<void> fetchAndSetEquipments(ExerciseDatabase database) async {
final prefs = await SharedPreferences.getInstance();
var validTill = DateTime.parse(prefs.getString(PREFS_LAST_UPDATED_EQUIPMENT)!);
// Cache still valid, return it
if (validTill.isAfter(DateTime.now())) {
final equipments = await database.select(database.equipments).get();
if (equipments.isNotEmpty) {
_equipment = equipments.map((e) => e.data).toList();
log('Loaded ${equipment.length} equipment from cache');
return;
}
}
// Fetch from API and save to DB
await fetchAndSetEquipmentsFromApi();
await database.delete(database.equipments).go();
await Future.forEach(_equipment, (e) async {
await database.into(database.equipments).insert(
EquipmentsCompanion.insert(
id: e.id,
data: e,
),
);
});
validTill = DateTime.now().add(const Duration(days: EXERCISE_CACHE_DAYS));
await prefs.setString(PREFS_LAST_UPDATED_EQUIPMENT, validTill.toIso8601String());
}
/// Searches for an exercise
@@ -473,7 +669,7 @@ class ExercisesProvider with ChangeNotifier {
// Send the request
final result = await baseProvider.fetch(
baseProvider.makeUrl(
_exerciseSearchPath,
exerciseSearchPath,
query: {'term': name, 'language': languages.join(',')},
),
);

View File

@@ -90,7 +90,7 @@ class _HomeTabsScreenState extends State<HomeTabsScreen> with SingleTickerProvid
userProvider.fetchAndSetProfile(),
workoutPlansProvider.fetchAndSetUnits(),
nutritionPlansProvider.fetchIngredientsFromCache(),
exercisesProvider.fetchAndSetExercises(),
exercisesProvider.fetchAndSetInitialData(),
]);
} catch (e) {
log('fire! fire!');

View File

@@ -86,7 +86,7 @@ class _GymModeState extends State<GymMode> {
var firstPage = true;
for (final setting in set.settingsComputed) {
final exerciseBase = Provider.of<ExercisesProvider>(context, listen: false)
.findExerciseBaseById(setting.exerciseBaseId);
.findExerciseById(setting.exerciseBaseId);
if (firstPage) {
_exercisePages[exerciseBase.uuid!] = currentPage;
@@ -114,7 +114,7 @@ class _GymModeState extends State<GymMode> {
var firstPage = true;
for (final setting in set.settingsComputed) {
final ratioCompleted = currentElement / _totalElements;
final exerciseBase = exerciseProvider.findExerciseBaseById(setting.exerciseBaseId);
final exerciseBase = exerciseProvider.findExerciseById(setting.exerciseBaseId);
currentElement++;
if (firstPage) {

View File

@@ -245,10 +245,10 @@ packages:
dependency: "direct main"
description:
name: collection
sha256: f092b211a4319e98e5ff58223576de6c2803db36221657b46c82574721240687
sha256: ee67cb0715911d28db6bf4af1026078bd6f0128b07a5f66fb2ed94ec6783c09a
url: "https://pub.dev"
source: hosted
version: "1.17.2"
version: "1.18.0"
convert:
dependency: transitive
description:
@@ -824,10 +824,10 @@ packages:
dependency: transitive
description:
name: meta
sha256: "3c74dbf8763d36539f114c799d8a2d87343b5067e9d796ca22b5eb8437090ee3"
sha256: a6e590c838b18133bb482a2745ad77c5bb7715fb0451209e1a7567d416678b8e
url: "https://pub.dev"
source: hosted
version: "1.9.1"
version: "1.10.0"
mime:
dependency: transitive
description:
@@ -968,10 +968,10 @@ packages:
dependency: transitive
description:
name: platform
sha256: "4a451831508d7d6ca779f7ac6e212b4023dd5a7d08a27a63da33756410e32b76"
sha256: ae68c7bfcd7383af3629daafb32fb4e8681c7154428da4febcff06200585f102
url: "https://pub.dev"
source: hosted
version: "3.1.0"
version: "3.1.2"
plugin_platform_interface:
dependency: transitive
description:
@@ -1213,18 +1213,18 @@ packages:
dependency: transitive
description:
name: stack_trace
sha256: c3c7d8edb15bee7f0f74debd4b9c5f3c2ea86766fe4178eb2a18eb30a0bdaed5
sha256: "73713990125a6d93122541237550ee3352a2d84baad52d375a4cad2eb9b7ce0b"
url: "https://pub.dev"
source: hosted
version: "1.11.0"
version: "1.11.1"
stream_channel:
dependency: transitive
description:
name: stream_channel
sha256: "83615bee9045c1d322bbbd1ba209b7a749c2cbcdcb3fdd1df8eb488b3279c1c8"
sha256: ba2aa5d8cc609d96bbb2899c28934f9e1af5cddbd60a827822ea467161eb54e7
url: "https://pub.dev"
source: hosted
version: "2.1.1"
version: "2.1.2"
stream_transform:
dependency: transitive
description:
@@ -1269,10 +1269,10 @@ packages:
dependency: transitive
description:
name: test_api
sha256: "75760ffd7786fffdfb9597c35c5b27eaeec82be8edfb6d71d32651128ed7aab8"
sha256: "5c2f730018264d276c20e4f1503fd1308dfbbae39ec8ee63c5236311ac06954b"
url: "https://pub.dev"
source: hosted
version: "0.6.0"
version: "0.6.1"
timing:
dependency: transitive
description:
@@ -1445,10 +1445,10 @@ packages:
dependency: transitive
description:
name: vm_service
sha256: c620a6f783fa22436da68e42db7ebbf18b8c44b9a46ab911f666ff09ffd9153f
sha256: c538be99af830f478718b51630ec1b6bee5e74e52c8a802d328d9e71d35d2583
url: "https://pub.dev"
source: hosted
version: "11.7.1"
version: "11.10.0"
watcher:
dependency: transitive
description:
@@ -1461,10 +1461,10 @@ packages:
dependency: transitive
description:
name: web
sha256: dc8ccd225a2005c1be616fe02951e2e342092edf968cf0844220383757ef8f10
sha256: afe077240a270dcfd2aafe77602b4113645af95d0ad31128cc02bce5ac5d5152
url: "https://pub.dev"
source: hosted
version: "0.1.4-beta"
version: "0.3.0"
web_socket_channel:
dependency: transitive
description:
@@ -1514,5 +1514,5 @@ packages:
source: hosted
version: "3.1.2"
sdks:
dart: ">=3.1.0 <4.0.0"
dart: ">=3.2.0-194.0.dev <4.0.0"
flutter: ">=3.13.0"

View File

@@ -0,0 +1,417 @@
import 'dart:convert';
import 'package:drift/native.dart';
import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:mockito/mockito.dart';
import 'package:shared_preferences/shared_preferences.dart';
import 'package:wger/database/exercises/exercise_database.dart';
import 'package:wger/helpers/consts.dart';
import 'package:wger/models/exercises/muscle.dart';
import 'package:wger/providers/exercises.dart';
import '../../test_data/exercises.dart';
import '../fixtures/fixture_reader.dart';
import '../measurements/measurement_provider_test.mocks.dart';
void main() {
late MockWgerBaseProvider mockBaseProvider;
late ExercisesProvider provider;
late ExerciseDatabase database;
const String categoryUrl = 'exercisecategory';
const String exerciseBaseInfoUrl = 'exercisebaseinfo';
const String muscleUrl = 'muscle';
const String equipmentUrl = 'equipment';
const String languageUrl = 'language';
final Uri tCategoryEntriesUri = Uri(
scheme: 'http',
host: 'localhost',
path: 'api/v2/$categoryUrl/',
);
final Uri texerciseBaseInfoUri = Uri(
scheme: 'http',
host: 'localhost',
path: 'api/v2/$exerciseBaseInfoUrl/',
);
final Uri tMuscleEntriesUri = Uri(
scheme: 'http',
host: 'localhost',
path: 'api/v2/$muscleUrl/',
);
final Uri tEquipmentEntriesUri = Uri(
scheme: 'http',
host: 'localhost',
path: 'api/v2/$equipmentUrl/',
);
final Uri tLanguageEntriesUri = Uri(
scheme: 'http',
host: 'localhost',
path: 'api/v2/$languageUrl/',
);
const muscle1 = Muscle(id: 1, name: 'Biceps brachii', nameEn: 'Biceps', isFront: true);
const muscle2 = Muscle(id: 2, name: 'Anterior deltoid', nameEn: 'Biceps', isFront: true);
const muscle3 = Muscle(id: 4, name: 'Biceps femoris', nameEn: 'Hamstrings', isFront: false);
final Map<String, dynamic> tCategoryMap = jsonDecode(
fixture('exercises/category_entries.json'),
);
final Map<String, dynamic> tMuscleMap = jsonDecode(
fixture('exercises/muscles_entries.json'),
);
final Map<String, dynamic> tEquipmentMap = jsonDecode(
fixture('exercises/equipment_entries.json'),
);
final Map<String, dynamic> tLanguageMap = jsonDecode(
fixture('exercises/language_entries.json'),
);
final Map<String, dynamic> tExerciseBaseInfoMap = jsonDecode(
fixture('exercises/exercisebaseinfo_response.json'),
);
setUp(() {
mockBaseProvider = MockWgerBaseProvider();
provider = ExercisesProvider(mockBaseProvider);
database = ExerciseDatabase.inMemory(NativeDatabase.memory());
WidgetsFlutterBinding.ensureInitialized();
SharedPreferences.setMockInitialValues({});
// Mock categories
when(mockBaseProvider.makeUrl(categoryUrl)).thenReturn(tCategoryEntriesUri);
when(mockBaseProvider.fetchPaginated(tCategoryEntriesUri))
.thenAnswer((_) => Future.value(tCategoryMap['results']));
// Mock muscles
when(mockBaseProvider.makeUrl(muscleUrl)).thenReturn(tMuscleEntriesUri);
when(mockBaseProvider.fetchPaginated(tMuscleEntriesUri))
.thenAnswer((_) => Future.value(tMuscleMap['results']));
// Mock equipment
when(mockBaseProvider.makeUrl(equipmentUrl)).thenReturn(tEquipmentEntriesUri);
when(mockBaseProvider.fetchPaginated(tEquipmentEntriesUri))
.thenAnswer((_) => Future.value(tEquipmentMap['results']));
// Mock languages
when(mockBaseProvider.makeUrl(languageUrl, query: anyNamed('query')))
.thenReturn(tLanguageEntriesUri);
when(mockBaseProvider.fetchPaginated(tLanguageEntriesUri))
.thenAnswer((_) => Future.value(tLanguageMap['results']));
// Mock base info response
when(mockBaseProvider.makeUrl(exerciseBaseInfoUrl)).thenReturn(texerciseBaseInfoUri);
when(mockBaseProvider.fetch(texerciseBaseInfoUri))
.thenAnswer((_) => Future.value(tExerciseBaseInfoMap));
});
tearDown(() async {
await database.close();
});
group('Muscles', () {
test('that fetched data from the API is written to the DB', () async {
// Arrange
final prefs = await SharedPreferences.getInstance();
await provider.initCacheTimesLocalPrefs();
// Act
await provider.fetchAndSetMuscles(database);
// Assert
final updateTime = DateTime.parse(prefs.getString(PREFS_LAST_UPDATED_MUSCLES)!);
final valid = DateTime.now().add(const Duration(days: ExercisesProvider.EXERCISE_CACHE_DAYS));
expect(
DateTime(updateTime.year, updateTime.month, updateTime.day),
DateTime(valid.year, valid.month, valid.day),
);
final muscles = await database.select(database.muscles).get();
verify(mockBaseProvider.fetchPaginated(any));
expect(muscles[0].id, 2);
expect(muscles[0].data, muscle2);
expect(muscles[1].id, 1);
expect(muscles[1].data, muscle1);
expect(muscles[2].id, 4);
expect(muscles[2].data, muscle3);
expect(provider.muscles[0], muscle2);
expect(provider.muscles[1], muscle1);
expect(provider.muscles[2], muscle3);
});
test('that if there is already valid data in the DB, the API is not hit', () async {
// Arrange
final prefs = await SharedPreferences.getInstance();
await provider.initCacheTimesLocalPrefs();
final valid = DateTime.now().add(const Duration(days: 1));
prefs.setString(PREFS_LAST_UPDATED_MUSCLES, valid.toIso8601String());
await database
.into(database.muscles)
.insert(MusclesCompanion.insert(id: muscle1.id, data: muscle1));
await database
.into(database.muscles)
.insert(MusclesCompanion.insert(id: muscle2.id, data: muscle2));
// Act
await provider.fetchAndSetMuscles(database);
// Assert
final updateTime = DateTime.parse(prefs.getString(PREFS_LAST_UPDATED_MUSCLES)!);
expect(
DateTime(updateTime.year, updateTime.month, updateTime.day),
DateTime(valid.year, valid.month, valid.day),
);
expect(provider.muscles[0], muscle1);
expect(provider.muscles[1], muscle2);
verifyNever(mockBaseProvider.fetchPaginated(any));
});
});
group('Languages', () {
test('that fetched data from the API is written to the DB', () async {
// Arrange
final prefs = await SharedPreferences.getInstance();
await provider.initCacheTimesLocalPrefs();
// Act
await provider.fetchAndSetLanguages(database);
// Assert
final updateTime = DateTime.parse(prefs.getString(PREFS_LAST_UPDATED_LANGUAGES)!);
final valid = DateTime.now().add(const Duration(days: ExercisesProvider.EXERCISE_CACHE_DAYS));
expect(
DateTime(updateTime.year, updateTime.month, updateTime.day),
DateTime(valid.year, valid.month, valid.day),
);
final languages = await database.select(database.languages).get();
verify(mockBaseProvider.fetchPaginated(any));
expect(languages[0].id, tLanguage1.id);
expect(languages[0].data, tLanguage1);
expect(languages[1].id, tLanguage2.id);
expect(languages[1].data, tLanguage2);
expect(languages[2].id, tLanguage4.id);
expect(languages[2].data, tLanguage4);
expect(languages[3].id, tLanguage3.id);
expect(languages[3].data, tLanguage3);
expect(provider.languages[0], tLanguage1);
expect(provider.languages[1], tLanguage2);
expect(provider.languages[2], tLanguage4);
expect(provider.languages[3], tLanguage3);
});
test('that if there is already valid data in the DB, the API is not hit', () async {
// Arrange
final prefs = await SharedPreferences.getInstance();
await provider.initCacheTimesLocalPrefs();
final valid = DateTime.now().add(const Duration(days: 1));
prefs.setString(PREFS_LAST_UPDATED_LANGUAGES, valid.toIso8601String());
await database
.into(database.languages)
.insert(LanguagesCompanion.insert(id: tLanguage1.id, data: tLanguage1));
await database
.into(database.languages)
.insert(LanguagesCompanion.insert(id: tLanguage2.id, data: tLanguage2));
// Act
await provider.fetchAndSetLanguages(database);
// Assert
final updateTime = DateTime.parse(prefs.getString(PREFS_LAST_UPDATED_LANGUAGES)!);
expect(
DateTime(updateTime.year, updateTime.month, updateTime.day),
DateTime(valid.year, valid.month, valid.day),
);
expect(provider.languages[0], tLanguage1);
expect(provider.languages[1], tLanguage2);
verifyNever(mockBaseProvider.fetchPaginated(any));
});
});
group('Categories', () {
test('that fetched data from the API is written to the DB', () async {
// Arrange
final prefs = await SharedPreferences.getInstance();
await provider.initCacheTimesLocalPrefs();
// Act
await provider.fetchAndSetCategories(database);
// Assert
final updateTime = DateTime.parse(prefs.getString(PREFS_LAST_UPDATED_CATEGORIES)!);
final valid = DateTime.now().add(const Duration(days: ExercisesProvider.EXERCISE_CACHE_DAYS));
expect(
DateTime(updateTime.year, updateTime.month, updateTime.day),
DateTime(valid.year, valid.month, valid.day),
);
final categories = await database.select(database.categories).get();
verify(mockBaseProvider.fetchPaginated(any));
expect(categories[0].id, tCategory1.id);
expect(categories[0].data, tCategory1);
expect(categories[1].id, tCategory2.id);
expect(categories[1].data, tCategory2);
expect(provider.categories[0], tCategory1);
expect(provider.categories[1], tCategory2);
});
test('that if there is already valid data in the DB, the API is not hit', () async {
// Arrange
final prefs = await SharedPreferences.getInstance();
await provider.initCacheTimesLocalPrefs();
final valid = DateTime.now().add(const Duration(days: 1));
prefs.setString(PREFS_LAST_UPDATED_CATEGORIES, valid.toIso8601String());
await database
.into(database.categories)
.insert(CategoriesCompanion.insert(id: tCategory1.id, data: tCategory1));
await database
.into(database.categories)
.insert(CategoriesCompanion.insert(id: tCategory2.id, data: tCategory2));
// Act
await provider.fetchAndSetCategories(database);
// Assert
final updateTime = DateTime.parse(prefs.getString(PREFS_LAST_UPDATED_CATEGORIES)!);
expect(
DateTime(updateTime.year, updateTime.month, updateTime.day),
DateTime(valid.year, valid.month, valid.day),
);
expect(provider.categories[0], tCategory1);
expect(provider.categories[1], tCategory2);
verifyNever(mockBaseProvider.fetchPaginated(any));
});
});
group('Equipment', () {
test('that fetched data from the API is written to the DB', () async {
// Arrange
final prefs = await SharedPreferences.getInstance();
await provider.initCacheTimesLocalPrefs();
// Act
await provider.fetchAndSetEquipments(database);
// Assert
final updateTime = DateTime.parse(prefs.getString(PREFS_LAST_UPDATED_EQUIPMENT)!);
final valid = DateTime.now().add(const Duration(days: ExercisesProvider.EXERCISE_CACHE_DAYS));
expect(
DateTime(updateTime.year, updateTime.month, updateTime.day),
DateTime(valid.year, valid.month, valid.day),
);
final equipmentList = await database.select(database.equipments).get();
verify(mockBaseProvider.fetchPaginated(any));
expect(equipmentList[0].id, tEquipment1.id);
expect(equipmentList[0].data, tEquipment1);
expect(equipmentList[1].id, tEquipment2.id);
expect(equipmentList[1].data, tEquipment2);
expect(provider.equipment[0], tEquipment1);
expect(provider.equipment[1], tEquipment2);
expect(provider.equipment[2], tEquipment3);
expect(provider.equipment[3], tEquipment4);
});
test('that if there is already valid data in the DB, the API is not hit', () async {
// Arrange
final prefs = await SharedPreferences.getInstance();
await provider.initCacheTimesLocalPrefs();
final valid = DateTime.now().add(const Duration(days: 1));
prefs.setString(PREFS_LAST_UPDATED_EQUIPMENT, valid.toIso8601String());
await database
.into(database.equipments)
.insert(EquipmentsCompanion.insert(id: tEquipment1.id, data: tEquipment1));
await database
.into(database.equipments)
.insert(EquipmentsCompanion.insert(id: tCategory2.id, data: tEquipment2));
// Act
await provider.fetchAndSetEquipments(database);
// Assert
final updateTime = DateTime.parse(prefs.getString(PREFS_LAST_UPDATED_EQUIPMENT)!);
expect(
DateTime(updateTime.year, updateTime.month, updateTime.day),
DateTime(valid.year, valid.month, valid.day),
);
expect(provider.equipment[0], tEquipment1);
expect(provider.equipment[1], tEquipment2);
verifyNever(mockBaseProvider.fetchPaginated(any));
});
});
group('Exercises', () {
test('that if there is already valid data in the DB, the API is not hit', () async {
// Arrange
final prefs = await SharedPreferences.getInstance();
await provider.initCacheTimesLocalPrefs();
final valid = DateTime.now().add(const Duration(days: 1));
prefs.setString(PREFS_LAST_UPDATED_LANGUAGES, valid.toIso8601String());
await database.into(database.exercises).insert(
ExercisesCompanion.insert(
id: tExerciseBaseInfoMap['id'],
data: json.encode(tExerciseBaseInfoMap),
lastUpdate: DateTime.parse(tExerciseBaseInfoMap['last_update_global']),
),
);
await database
.into(database.languages)
.insert(LanguagesCompanion.insert(id: tLanguage1.id, data: tLanguage1));
await database
.into(database.languages)
.insert(LanguagesCompanion.insert(id: tLanguage2.id, data: tLanguage2));
await database
.into(database.languages)
.insert(LanguagesCompanion.insert(id: tLanguage3.id, data: tLanguage3));
// Act
await provider.fetchAndSetLanguages(database);
await provider.fetchAndSetExercises(database);
// Assert
expect(provider.bases[0].id, 9);
expect(provider.bases[0].uuid, '1b020b3a-3732-4c7e-92fd-a0cec90ed69b');
verifyNever(mockBaseProvider.fetchPaginated(any));
});
});
}

View File

@@ -21,7 +21,7 @@ void main() {
path: 'api/v2/$exerciseBaseInfoUrl/9/',
);
final Map<String, dynamic> tExerciseBaseInfoMap = jsonDecode(
final Map<String, dynamic> tExerciseInfoMap = jsonDecode(
fixture('exercises/exercisebaseinfo_response.json'),
);
@@ -36,7 +36,7 @@ void main() {
mockBaseProvider.makeUrl(exerciseBaseInfoUrl, id: 9),
).thenReturn(tExerciseBaseInfoUri);
when(mockBaseProvider.fetch(tExerciseBaseInfoUri))
.thenAnswer((_) => Future.value(tExerciseBaseInfoMap));
.thenAnswer((_) => Future.value(tExerciseInfoMap));
});
group('Correctly loads and parses data from the server', () {
@@ -63,7 +63,7 @@ void main() {
// arrange and act
final base =
provider.readExerciseBaseFromBaseInfo(ExerciseBaseData.fromJson(tExerciseBaseInfoMap));
provider.readExerciseBaseFromBaseInfo(ExerciseBaseData.fromJson(tExerciseInfoMap));
// assert
expect(base.id, 9);
@@ -76,8 +76,9 @@ void main() {
'Obliquus externus abdominis',
]);
expect(base.musclesSecondary.map((e) => e.name), [
'Anterior deltoid',
'Trapezius',
'Biceps femoris',
'Brachialis',
'Obliquus externus abdominis',
]);
expect(base.images.map((e) => e.uuid), [
'1f5d2b2f-d4ea-4eeb-9377-56176465e08d',

View File

@@ -6,11 +6,11 @@ import 'package:shared_preferences/shared_preferences.dart';
import 'package:wger/exceptions/no_such_entry_exception.dart';
import 'package:wger/models/exercises/category.dart';
import 'package:wger/models/exercises/equipment.dart';
import 'package:wger/models/exercises/language.dart';
import 'package:wger/models/exercises/muscle.dart';
import 'package:wger/providers/exercises.dart';
import '../../test_data/exercises.dart' as data;
import '../../test_data/exercises.dart';
import '../fixtures/fixture_reader.dart';
import '../measurements/measurement_provider_test.mocks.dart';
@@ -63,8 +63,6 @@ void main() {
const category1 = ExerciseCategory(id: 1, name: 'Arms');
const muscle1 = Muscle(id: 1, name: 'Biceps brachii', nameEn: 'Biceps', isFront: true);
const equipment1 = Equipment(id: 1, name: 'Barbell');
const language1 = Language(id: 1, shortName: 'de', fullName: 'Deutsch');
final Map<String, dynamic> tCategoryMap = jsonDecode(
fixture('exercises/category_entries.json'),
@@ -116,7 +114,7 @@ void main() {
group('findCategoryById()', () {
test('should return a category for an id', () async {
// arrange
await provider.fetchAndSetCategories();
await provider.fetchAndSetCategoriesFromApi();
// act
final result = provider.findCategoryById(1);
@@ -134,7 +132,7 @@ void main() {
group('findMuscleById()', () {
test('should return a muscle for an id', () async {
// arrange
await provider.fetchAndSetMuscles();
await provider.fetchAndSetMusclesFromApi();
// act
final result = provider.findMuscleById(1);
@@ -152,13 +150,13 @@ void main() {
group('findEquipmentById()', () {
test('should return an equipment for an id', () async {
// arrange
await provider.fetchAndSetEquipment();
await provider.fetchAndSetEquipmentsFromApi();
// act
final result = provider.findEquipmentById(1);
// assert
expect(result, equipment1);
expect(result, tEquipment1);
});
test('should throw a NoResultException if no equipment is found', () {
@@ -170,13 +168,13 @@ void main() {
group('findLanguageById()', () {
test('should return a language for an id', () async {
// arrange
await provider.fetchAndSetLanguages();
await provider.fetchAndSetLanguagesFromApi();
// act
final result = provider.findLanguageById(1);
// assert
expect(result, language1);
expect(result, tLanguage1);
});
test('should throw a NoResultException if no equipment is found', () {
@@ -302,7 +300,7 @@ void main() {
// arrange
final Filters tFilters = filters.copyWith(
exerciseCategories: filters.exerciseCategories.copyWith(items: {data.tCategory2: true}),
equipment: filters.equipment.copyWith(items: {equipment1: true}),
equipment: filters.equipment.copyWith(items: {tEquipment1: true}),
);
// act

View File

@@ -3,13 +3,13 @@
"next": null,
"previous": null,
"results": [
{
"id": 1,
"name": "Arms"
},
{
"id": 2,
"name": "Abs"
}
{
"id": 1,
"name": "Arms"
},
{
"id": 2,
"name": "Legs"
}
]
}

View File

@@ -5,18 +5,18 @@
"results": [
{
"id": 1,
"name": "Barbell"
},
{
"id": 8,
"name": "Bench"
},
{
"id": 3,
"id": 2,
"name": "Dumbbell"
},
{
"id": 4,
"id": 3,
"name": "Bench"
},
{
"id": 10,
"name": "Gym mat"
}
]

View File

@@ -14,19 +14,19 @@
"full_name": "English"
},
{
"id": 4,
"id": 12,
"short_name": "es",
"full_name": "Español"
},
{
"id": 12,
"id": 3,
"short_name": "fr",
"full_name": "Français"
},
{
"id": 13,
"short_name": "it",
"full_name": "Italian"
"full_name": "Italiano"
}
]
}

View File

@@ -75,8 +75,8 @@ void main() {
}
testWidgets('Test the widgets on the gym mode screen', (WidgetTester tester) async {
when(mockExerciseProvider.findExerciseBaseById(1)).thenReturn(bases[0]);
when(mockExerciseProvider.findExerciseBaseById(6)).thenReturn(bases[5]);
when(mockExerciseProvider.findExerciseById(1)).thenReturn(bases[0]);
when(mockExerciseProvider.findExerciseById(6)).thenReturn(bases[5]);
await tester.pumpWidget(createHomeScreen());
await tester.tap(find.byType(TextButton));

View File

@@ -4,10 +4,11 @@
// ignore_for_file: no_leading_underscores_for_library_prefixes
import 'dart:async' as _i10;
import 'dart:ui' as _i13;
import 'dart:ui' as _i14;
import 'package:http/http.dart' as _i3;
import 'package:mockito/mockito.dart' as _i1;
import 'package:wger/database/exercises/exercise_database.dart' as _i13;
import 'package:wger/models/exercises/base.dart' as _i5;
import 'package:wger/models/exercises/category.dart' as _i6;
import 'package:wger/models/exercises/equipment.dart' as _i7;
@@ -414,15 +415,15 @@ class MockExercisesProvider extends _i1.Mock implements _i11.ExercisesProvider {
);
@override
_i5.ExerciseBase findExerciseBaseById(int? id) => (super.noSuchMethod(
_i5.ExerciseBase findExerciseById(int? id) => (super.noSuchMethod(
Invocation.method(
#findExerciseBaseById,
#findExerciseById,
[id],
),
returnValue: _FakeExerciseBase_5(
this,
Invocation.method(
#findExerciseBaseById,
#findExerciseById,
[id],
),
),
@@ -503,9 +504,9 @@ class MockExercisesProvider extends _i1.Mock implements _i11.ExercisesProvider {
) as _i9.Language);
@override
_i10.Future<void> fetchAndSetCategories() => (super.noSuchMethod(
_i10.Future<void> fetchAndSetCategoriesFromApi() => (super.noSuchMethod(
Invocation.method(
#fetchAndSetCategories,
#fetchAndSetCategoriesFromApi,
[],
),
returnValue: _i10.Future<void>.value(),
@@ -513,9 +514,9 @@ class MockExercisesProvider extends _i1.Mock implements _i11.ExercisesProvider {
) as _i10.Future<void>);
@override
_i10.Future<void> fetchAndSetVariations() => (super.noSuchMethod(
_i10.Future<void> fetchAndSetVariationsFromApi() => (super.noSuchMethod(
Invocation.method(
#fetchAndSetVariations,
#fetchAndSetVariationsFromApi,
[],
),
returnValue: _i10.Future<void>.value(),
@@ -523,9 +524,9 @@ class MockExercisesProvider extends _i1.Mock implements _i11.ExercisesProvider {
) as _i10.Future<void>);
@override
_i10.Future<void> fetchAndSetMuscles() => (super.noSuchMethod(
_i10.Future<void> fetchAndSetMusclesFromApi() => (super.noSuchMethod(
Invocation.method(
#fetchAndSetMuscles,
#fetchAndSetMusclesFromApi,
[],
),
returnValue: _i10.Future<void>.value(),
@@ -533,9 +534,9 @@ class MockExercisesProvider extends _i1.Mock implements _i11.ExercisesProvider {
) as _i10.Future<void>);
@override
_i10.Future<void> fetchAndSetEquipment() => (super.noSuchMethod(
_i10.Future<void> fetchAndSetEquipmentsFromApi() => (super.noSuchMethod(
Invocation.method(
#fetchAndSetEquipment,
#fetchAndSetEquipmentsFromApi,
[],
),
returnValue: _i10.Future<void>.value(),
@@ -543,15 +544,30 @@ class MockExercisesProvider extends _i1.Mock implements _i11.ExercisesProvider {
) as _i10.Future<void>);
@override
_i10.Future<void> fetchAndSetLanguages() => (super.noSuchMethod(
_i10.Future<void> fetchAndSetLanguagesFromApi() => (super.noSuchMethod(
Invocation.method(
#fetchAndSetLanguages,
#fetchAndSetLanguagesFromApi,
[],
),
returnValue: _i10.Future<void>.value(),
returnValueForMissingStub: _i10.Future<void>.value(),
) as _i10.Future<void>);
@override
_i10.Future<_i9.Language> fetchAndSetLanguageFromApi(int? id) => (super.noSuchMethod(
Invocation.method(
#fetchAndSetLanguageFromApi,
[id],
),
returnValue: _i10.Future<_i9.Language>.value(_FakeLanguage_9(
this,
Invocation.method(
#fetchAndSetLanguageFromApi,
[id],
),
)),
) as _i10.Future<_i9.Language>);
@override
_i10.Future<_i5.ExerciseBase> fetchAndSetExerciseBase(int? exerciseBaseId) => (super.noSuchMethod(
Invocation.method(
@@ -594,10 +610,95 @@ class MockExercisesProvider extends _i1.Mock implements _i11.ExercisesProvider {
) as _i10.Future<void>);
@override
_i10.Future<void> fetchAndSetExercises() => (super.noSuchMethod(
_i10.Future<void> initCacheTimesLocalPrefs() => (super.noSuchMethod(
Invocation.method(
#initCacheTimesLocalPrefs,
[],
),
returnValue: _i10.Future<void>.value(),
returnValueForMissingStub: _i10.Future<void>.value(),
) as _i10.Future<void>);
@override
_i10.Future<void> clearAllCachesAndPrefs() => (super.noSuchMethod(
Invocation.method(
#clearAllCachesAndPrefs,
[],
),
returnValue: _i10.Future<void>.value(),
returnValueForMissingStub: _i10.Future<void>.value(),
) as _i10.Future<void>);
@override
_i10.Future<void> fetchAndSetInitialData() => (super.noSuchMethod(
Invocation.method(
#fetchAndSetInitialData,
[],
),
returnValue: _i10.Future<void>.value(),
returnValueForMissingStub: _i10.Future<void>.value(),
) as _i10.Future<void>);
@override
_i10.Future<void> fetchAndSetExercises(
_i13.ExerciseDatabase? database, {
bool? forceDeleteCache = false,
}) =>
(super.noSuchMethod(
Invocation.method(
#fetchAndSetExercises,
[],
[database],
{#forceDeleteCache: forceDeleteCache},
),
returnValue: _i10.Future<void>.value(),
returnValueForMissingStub: _i10.Future<void>.value(),
) as _i10.Future<void>);
@override
_i10.Future<void> updateExerciseCache(_i13.ExerciseDatabase? database) => (super.noSuchMethod(
Invocation.method(
#updateExerciseCache,
[database],
),
returnValue: _i10.Future<void>.value(),
returnValueForMissingStub: _i10.Future<void>.value(),
) as _i10.Future<void>);
@override
_i10.Future<void> fetchAndSetMuscles(_i13.ExerciseDatabase? database) => (super.noSuchMethod(
Invocation.method(
#fetchAndSetMuscles,
[database],
),
returnValue: _i10.Future<void>.value(),
returnValueForMissingStub: _i10.Future<void>.value(),
) as _i10.Future<void>);
@override
_i10.Future<void> fetchAndSetCategories(_i13.ExerciseDatabase? database) => (super.noSuchMethod(
Invocation.method(
#fetchAndSetCategories,
[database],
),
returnValue: _i10.Future<void>.value(),
returnValueForMissingStub: _i10.Future<void>.value(),
) as _i10.Future<void>);
@override
_i10.Future<void> fetchAndSetLanguages(_i13.ExerciseDatabase? database) => (super.noSuchMethod(
Invocation.method(
#fetchAndSetLanguages,
[database],
),
returnValue: _i10.Future<void>.value(),
returnValueForMissingStub: _i10.Future<void>.value(),
) as _i10.Future<void>);
@override
_i10.Future<void> fetchAndSetEquipments(_i13.ExerciseDatabase? database) => (super.noSuchMethod(
Invocation.method(
#fetchAndSetEquipments,
[database],
),
returnValue: _i10.Future<void>.value(),
returnValueForMissingStub: _i10.Future<void>.value(),
@@ -622,7 +723,7 @@ class MockExercisesProvider extends _i1.Mock implements _i11.ExercisesProvider {
) as _i10.Future<List<_i5.ExerciseBase>>);
@override
void addListener(_i13.VoidCallback? listener) => super.noSuchMethod(
void addListener(_i14.VoidCallback? listener) => super.noSuchMethod(
Invocation.method(
#addListener,
[listener],
@@ -631,7 +732,7 @@ class MockExercisesProvider extends _i1.Mock implements _i11.ExercisesProvider {
);
@override
void removeListener(_i13.VoidCallback? listener) => super.noSuchMethod(
void removeListener(_i14.VoidCallback? listener) => super.noSuchMethod(
Invocation.method(
#removeListener,
[listener],

View File

@@ -4,18 +4,19 @@
// ignore_for_file: no_leading_underscores_for_library_prefixes
import 'dart:async' as _i19;
import 'dart:ui' as _i21;
import 'dart:ui' as _i22;
import 'package:http/http.dart' as _i9;
import 'package:mockito/mockito.dart' as _i1;
import 'package:mockito/src/dummies.dart' as _i24;
import 'package:mockito/src/dummies.dart' as _i25;
import 'package:wger/database/exercises/exercise_database.dart' as _i21;
import 'package:wger/models/exercises/base.dart' as _i3;
import 'package:wger/models/exercises/category.dart' as _i4;
import 'package:wger/models/exercises/equipment.dart' as _i5;
import 'package:wger/models/exercises/exercise_base_data.dart' as _i20;
import 'package:wger/models/exercises/language.dart' as _i7;
import 'package:wger/models/exercises/muscle.dart' as _i6;
import 'package:wger/models/exercises/translation.dart' as _i23;
import 'package:wger/models/exercises/translation.dart' as _i24;
import 'package:wger/models/workouts/day.dart' as _i13;
import 'package:wger/models/workouts/log.dart' as _i17;
import 'package:wger/models/workouts/repetition_unit.dart' as _i11;
@@ -27,7 +28,7 @@ import 'package:wger/models/workouts/workout_plan.dart' as _i12;
import 'package:wger/providers/auth.dart' as _i8;
import 'package:wger/providers/base_provider.dart' as _i2;
import 'package:wger/providers/exercises.dart' as _i18;
import 'package:wger/providers/workout_plans.dart' as _i22;
import 'package:wger/providers/workout_plans.dart' as _i23;
// ignore_for_file: type=lint
// ignore_for_file: avoid_redundant_argument_values
@@ -344,15 +345,15 @@ class MockExercisesProvider extends _i1.Mock implements _i18.ExercisesProvider {
);
@override
_i3.ExerciseBase findExerciseBaseById(int? id) => (super.noSuchMethod(
_i3.ExerciseBase findExerciseById(int? id) => (super.noSuchMethod(
Invocation.method(
#findExerciseBaseById,
#findExerciseById,
[id],
),
returnValue: _FakeExerciseBase_1(
this,
Invocation.method(
#findExerciseBaseById,
#findExerciseById,
[id],
),
),
@@ -433,9 +434,9 @@ class MockExercisesProvider extends _i1.Mock implements _i18.ExercisesProvider {
) as _i7.Language);
@override
_i19.Future<void> fetchAndSetCategories() => (super.noSuchMethod(
_i19.Future<void> fetchAndSetCategoriesFromApi() => (super.noSuchMethod(
Invocation.method(
#fetchAndSetCategories,
#fetchAndSetCategoriesFromApi,
[],
),
returnValue: _i19.Future<void>.value(),
@@ -443,9 +444,9 @@ class MockExercisesProvider extends _i1.Mock implements _i18.ExercisesProvider {
) as _i19.Future<void>);
@override
_i19.Future<void> fetchAndSetVariations() => (super.noSuchMethod(
_i19.Future<void> fetchAndSetVariationsFromApi() => (super.noSuchMethod(
Invocation.method(
#fetchAndSetVariations,
#fetchAndSetVariationsFromApi,
[],
),
returnValue: _i19.Future<void>.value(),
@@ -453,9 +454,9 @@ class MockExercisesProvider extends _i1.Mock implements _i18.ExercisesProvider {
) as _i19.Future<void>);
@override
_i19.Future<void> fetchAndSetMuscles() => (super.noSuchMethod(
_i19.Future<void> fetchAndSetMusclesFromApi() => (super.noSuchMethod(
Invocation.method(
#fetchAndSetMuscles,
#fetchAndSetMusclesFromApi,
[],
),
returnValue: _i19.Future<void>.value(),
@@ -463,9 +464,9 @@ class MockExercisesProvider extends _i1.Mock implements _i18.ExercisesProvider {
) as _i19.Future<void>);
@override
_i19.Future<void> fetchAndSetEquipment() => (super.noSuchMethod(
_i19.Future<void> fetchAndSetEquipmentsFromApi() => (super.noSuchMethod(
Invocation.method(
#fetchAndSetEquipment,
#fetchAndSetEquipmentsFromApi,
[],
),
returnValue: _i19.Future<void>.value(),
@@ -473,15 +474,30 @@ class MockExercisesProvider extends _i1.Mock implements _i18.ExercisesProvider {
) as _i19.Future<void>);
@override
_i19.Future<void> fetchAndSetLanguages() => (super.noSuchMethod(
_i19.Future<void> fetchAndSetLanguagesFromApi() => (super.noSuchMethod(
Invocation.method(
#fetchAndSetLanguages,
#fetchAndSetLanguagesFromApi,
[],
),
returnValue: _i19.Future<void>.value(),
returnValueForMissingStub: _i19.Future<void>.value(),
) as _i19.Future<void>);
@override
_i19.Future<_i7.Language> fetchAndSetLanguageFromApi(int? id) => (super.noSuchMethod(
Invocation.method(
#fetchAndSetLanguageFromApi,
[id],
),
returnValue: _i19.Future<_i7.Language>.value(_FakeLanguage_5(
this,
Invocation.method(
#fetchAndSetLanguageFromApi,
[id],
),
)),
) as _i19.Future<_i7.Language>);
@override
_i19.Future<_i3.ExerciseBase> fetchAndSetExerciseBase(int? exerciseBaseId) => (super.noSuchMethod(
Invocation.method(
@@ -524,10 +540,95 @@ class MockExercisesProvider extends _i1.Mock implements _i18.ExercisesProvider {
) as _i19.Future<void>);
@override
_i19.Future<void> fetchAndSetExercises() => (super.noSuchMethod(
_i19.Future<void> initCacheTimesLocalPrefs() => (super.noSuchMethod(
Invocation.method(
#initCacheTimesLocalPrefs,
[],
),
returnValue: _i19.Future<void>.value(),
returnValueForMissingStub: _i19.Future<void>.value(),
) as _i19.Future<void>);
@override
_i19.Future<void> clearAllCachesAndPrefs() => (super.noSuchMethod(
Invocation.method(
#clearAllCachesAndPrefs,
[],
),
returnValue: _i19.Future<void>.value(),
returnValueForMissingStub: _i19.Future<void>.value(),
) as _i19.Future<void>);
@override
_i19.Future<void> fetchAndSetInitialData() => (super.noSuchMethod(
Invocation.method(
#fetchAndSetInitialData,
[],
),
returnValue: _i19.Future<void>.value(),
returnValueForMissingStub: _i19.Future<void>.value(),
) as _i19.Future<void>);
@override
_i19.Future<void> fetchAndSetExercises(
_i21.ExerciseDatabase? database, {
bool? forceDeleteCache = false,
}) =>
(super.noSuchMethod(
Invocation.method(
#fetchAndSetExercises,
[],
[database],
{#forceDeleteCache: forceDeleteCache},
),
returnValue: _i19.Future<void>.value(),
returnValueForMissingStub: _i19.Future<void>.value(),
) as _i19.Future<void>);
@override
_i19.Future<void> updateExerciseCache(_i21.ExerciseDatabase? database) => (super.noSuchMethod(
Invocation.method(
#updateExerciseCache,
[database],
),
returnValue: _i19.Future<void>.value(),
returnValueForMissingStub: _i19.Future<void>.value(),
) as _i19.Future<void>);
@override
_i19.Future<void> fetchAndSetMuscles(_i21.ExerciseDatabase? database) => (super.noSuchMethod(
Invocation.method(
#fetchAndSetMuscles,
[database],
),
returnValue: _i19.Future<void>.value(),
returnValueForMissingStub: _i19.Future<void>.value(),
) as _i19.Future<void>);
@override
_i19.Future<void> fetchAndSetCategories(_i21.ExerciseDatabase? database) => (super.noSuchMethod(
Invocation.method(
#fetchAndSetCategories,
[database],
),
returnValue: _i19.Future<void>.value(),
returnValueForMissingStub: _i19.Future<void>.value(),
) as _i19.Future<void>);
@override
_i19.Future<void> fetchAndSetLanguages(_i21.ExerciseDatabase? database) => (super.noSuchMethod(
Invocation.method(
#fetchAndSetLanguages,
[database],
),
returnValue: _i19.Future<void>.value(),
returnValueForMissingStub: _i19.Future<void>.value(),
) as _i19.Future<void>);
@override
_i19.Future<void> fetchAndSetEquipments(_i21.ExerciseDatabase? database) => (super.noSuchMethod(
Invocation.method(
#fetchAndSetEquipments,
[database],
),
returnValue: _i19.Future<void>.value(),
returnValueForMissingStub: _i19.Future<void>.value(),
@@ -552,7 +653,7 @@ class MockExercisesProvider extends _i1.Mock implements _i18.ExercisesProvider {
) as _i19.Future<List<_i3.ExerciseBase>>);
@override
void addListener(_i21.VoidCallback? listener) => super.noSuchMethod(
void addListener(_i22.VoidCallback? listener) => super.noSuchMethod(
Invocation.method(
#addListener,
[listener],
@@ -561,7 +662,7 @@ class MockExercisesProvider extends _i1.Mock implements _i18.ExercisesProvider {
);
@override
void removeListener(_i21.VoidCallback? listener) => super.noSuchMethod(
void removeListener(_i22.VoidCallback? listener) => super.noSuchMethod(
Invocation.method(
#removeListener,
[listener],
@@ -752,7 +853,7 @@ class MockWgerBaseProvider extends _i1.Mock implements _i2.WgerBaseProvider {
/// A class which mocks [WorkoutPlansProvider].
///
/// See the documentation for Mockito's code generation for more information.
class MockWorkoutPlansProvider extends _i1.Mock implements _i22.WorkoutPlansProvider {
class MockWorkoutPlansProvider extends _i1.Mock implements _i23.WorkoutPlansProvider {
MockWorkoutPlansProvider() {
_i1.throwOnMissingStub(this);
}
@@ -1098,7 +1199,7 @@ class MockWorkoutPlansProvider extends _i1.Mock implements _i22.WorkoutPlansProv
@override
_i19.Future<String> fetchSmartText(
_i14.Set? workoutSet,
_i23.Translation? exercise,
_i24.Translation? exercise,
) =>
(super.noSuchMethod(
Invocation.method(
@@ -1108,7 +1209,7 @@ class MockWorkoutPlansProvider extends _i1.Mock implements _i22.WorkoutPlansProv
exercise,
],
),
returnValue: _i19.Future<String>.value(_i24.dummyValue<String>(
returnValue: _i19.Future<String>.value(_i25.dummyValue<String>(
this,
Invocation.method(
#fetchSmartText,
@@ -1195,7 +1296,7 @@ class MockWorkoutPlansProvider extends _i1.Mock implements _i22.WorkoutPlansProv
) as _i19.Future<void>);
@override
void addListener(_i21.VoidCallback? listener) => super.noSuchMethod(
void addListener(_i22.VoidCallback? listener) => super.noSuchMethod(
Invocation.method(
#addListener,
[listener],
@@ -1204,7 +1305,7 @@ class MockWorkoutPlansProvider extends _i1.Mock implements _i22.WorkoutPlansProv
);
@override
void removeListener(_i21.VoidCallback? listener) => super.noSuchMethod(
void removeListener(_i22.VoidCallback? listener) => super.noSuchMethod(
Invocation.method(
#removeListener,
[listener],

View File

@@ -26,6 +26,8 @@ import 'package:wger/models/exercises/translation.dart';
const tLanguage1 = Language(id: 1, shortName: 'de', fullName: 'Deutsch');
const tLanguage2 = Language(id: 2, shortName: 'en', fullName: 'English');
const tLanguage3 = Language(id: 3, shortName: 'fr', fullName: 'Français');
const tLanguage4 = Language(id: 12, shortName: 'es', fullName: 'Español');
const tLanguage5 = Language(id: 13, shortName: 'it', fullName: 'Italiano');
const testLanguages = [tLanguage1, tLanguage2, tLanguage3];
const tMuscle1 = Muscle(id: 1, name: 'Flutterus maximus', nameEn: 'Glutes', isFront: true);
@@ -41,8 +43,9 @@ const tCategory5 = ExerciseCategory(id: 5, name: 'Calves');
const testCategories = [tCategory1, tCategory2, tCategory3, tCategory4, tCategory5];
const tEquipment1 = Equipment(id: 1, name: 'Bench');
const tEquipment2 = Equipment(id: 1, name: 'Dumbbell');
const tEquipment3 = Equipment(id: 2, name: 'Bench');
const tEquipment2 = Equipment(id: 2, name: 'Dumbbell');
const tEquipment3 = Equipment(id: 3, name: 'Bench');
const tEquipment4 = Equipment(id: 10, name: 'Gym mat');
const testEquipment = [tEquipment1, tEquipment2, tEquipment3];
final benchPress = ExerciseBase(