Update models due to changes in exercise api response changes

This commit is contained in:
Roland Geider
2025-01-30 21:25:15 +01:00
parent 4bd16f5c0d
commit 3e6ee0b082
16 changed files with 71 additions and 75 deletions

View File

@@ -78,7 +78,7 @@ class ExerciseDatabase extends _$ExerciseDatabase {
/// Note that this needs to be bumped if the JSON response from the server changes
@override
int get schemaVersion => 1;
int get schemaVersion => 2;
/// There is not really a migration strategy. If we bump the version
/// number, delete everything and recreate the new tables. The provider

View File

@@ -25,15 +25,16 @@ class Alias {
@JsonKey(required: true)
final int? id;
@JsonKey(name: 'exercise')
final int? exerciseId;
@JsonKey(name: 'translation')
final int? translationId;
@JsonKey(required: true)
final String alias;
const Alias({this.id, required this.exerciseId, required this.alias});
const Alias({this.id, required this.translationId, required this.alias});
// Boilerplate
factory Alias.fromJson(Map<String, dynamic> json) => _$AliasFromJson(json);
Map<String, dynamic> toJson() => _$AliasToJson(this);
}

View File

@@ -13,13 +13,13 @@ Alias _$AliasFromJson(Map<String, dynamic> json) {
);
return Alias(
id: (json['id'] as num?)?.toInt(),
exerciseId: (json['exercise'] as num?)?.toInt(),
translationId: (json['translation'] as num?)?.toInt(),
alias: json['alias'] as String,
);
}
Map<String, dynamic> _$AliasToJson(Alias instance) => <String, dynamic>{
'id': instance.id,
'exercise': instance.exerciseId,
'translation': instance.translationId,
'alias': instance.alias,
};

View File

@@ -25,19 +25,20 @@ class Comment {
@JsonKey(required: true)
final int id;
@JsonKey(name: 'exercise')
final int exerciseId;
@JsonKey(name: 'translation')
final int translationId;
@JsonKey(required: true)
final String comment;
const Comment({
required this.id,
required this.exerciseId,
required this.translationId,
required this.comment,
});
// Boilerplate
factory Comment.fromJson(Map<String, dynamic> json) => _$CommentFromJson(json);
Map<String, dynamic> toJson() => _$CommentToJson(this);
}

View File

@@ -13,13 +13,13 @@ Comment _$CommentFromJson(Map<String, dynamic> json) {
);
return Comment(
id: (json['id'] as num).toInt(),
exerciseId: (json['exercise'] as num).toInt(),
translationId: (json['translation'] as num).toInt(),
comment: json['comment'] as String,
);
}
Map<String, dynamic> _$CommentToJson(Comment instance) => <String, dynamic>{
'id': instance.id,
'exercise': instance.exerciseId,
'translation': instance.translationId,
'comment': instance.comment,
};

View File

@@ -28,8 +28,8 @@ class ExerciseImage {
@JsonKey(required: true)
final String uuid;
@JsonKey(required: true, name: 'exercise_base')
final int exerciseBaseId;
@JsonKey(required: true, name: 'exercise')
final int exerciseId;
@JsonKey(required: true, name: 'image')
final String url;
@@ -40,13 +40,14 @@ class ExerciseImage {
const ExerciseImage({
required this.id,
required this.uuid,
required this.exerciseBaseId,
required this.exerciseId,
required this.url,
required this.isMain,
});
// Boilerplate
factory ExerciseImage.fromJson(Map<String, dynamic> json) => _$ExerciseImageFromJson(json);
Map<String, dynamic> toJson() => _$ExerciseImageToJson(this);
@override

View File

@@ -9,12 +9,12 @@ part of 'image.dart';
ExerciseImage _$ExerciseImageFromJson(Map<String, dynamic> json) {
$checkKeys(
json,
requiredKeys: const ['id', 'uuid', 'exercise_base', 'image'],
requiredKeys: const ['id', 'uuid', 'exercise', 'image'],
);
return ExerciseImage(
id: (json['id'] as num).toInt(),
uuid: json['uuid'] as String,
exerciseBaseId: (json['exercise_base'] as num).toInt(),
exerciseId: (json['exercise'] as num).toInt(),
url: json['image'] as String,
isMain: json['is_main'] as bool? ?? false,
);
@@ -23,7 +23,7 @@ ExerciseImage _$ExerciseImageFromJson(Map<String, dynamic> json) {
Map<String, dynamic> _$ExerciseImageToJson(ExerciseImage instance) => <String, dynamic>{
'id': instance.id,
'uuid': instance.uuid,
'exercise_base': instance.exerciseBaseId,
'exercise': instance.exerciseId,
'image': instance.url,
'is_main': instance.isMain,
};

View File

@@ -42,7 +42,7 @@ class Translation extends Equatable {
@JsonKey(required: true, name: 'created')
final DateTime? created;
@JsonKey(required: true, name: 'exercise_base')
@JsonKey(required: true, name: 'exercise')
late int? exerciseId;
@JsonKey(required: true)

View File

@@ -9,15 +9,7 @@ part of 'translation.dart';
Translation _$TranslationFromJson(Map<String, dynamic> json) {
$checkKeys(
json,
requiredKeys: const [
'id',
'uuid',
'language',
'created',
'exercise_base',
'name',
'description'
],
requiredKeys: const ['id', 'uuid', 'language', 'created', 'exercise', 'name', 'description'],
);
return Translation(
id: (json['id'] as num?)?.toInt(),
@@ -25,7 +17,7 @@ Translation _$TranslationFromJson(Map<String, dynamic> json) {
created: json['created'] == null ? null : DateTime.parse(json['created'] as String),
name: json['name'] as String,
description: json['description'] as String,
exerciseId: (json['exercise_base'] as num?)?.toInt(),
exerciseId: (json['exercise'] as num?)?.toInt(),
)
..languageId = (json['language'] as num).toInt()
..notes = (json['notes'] as List<dynamic>)
@@ -41,7 +33,7 @@ Map<String, dynamic> _$TranslationToJson(Translation instance) => <String, dynam
'uuid': instance.uuid,
'language': instance.languageId,
'created': instance.created?.toIso8601String(),
'exercise_base': instance.exerciseId,
'exercise': instance.exerciseId,
'name': instance.name,
'description': instance.description,
};

View File

@@ -32,8 +32,8 @@ class Video {
@JsonKey(name: 'video', required: true)
final String url;
@JsonKey(name: 'exercise_base', required: true)
final int base;
@JsonKey(name: 'exercise', required: true)
final int exerciseId;
@JsonKey(required: true)
final int size;
@@ -62,7 +62,7 @@ class Video {
const Video({
required this.id,
required this.uuid,
required this.base,
required this.exerciseId,
required this.size,
required this.url,
required this.duration,
@@ -76,5 +76,6 @@ class Video {
// Boilerplate
factory Video.fromJson(Map<String, dynamic> json) => _$VideoFromJson(json);
Map<String, dynamic> toJson() => _$VideoToJson(this);
}

View File

@@ -13,7 +13,7 @@ Video _$VideoFromJson(Map<String, dynamic> json) {
'id',
'uuid',
'video',
'exercise_base',
'exercise',
'size',
'duration',
'width',
@@ -27,7 +27,7 @@ Video _$VideoFromJson(Map<String, dynamic> json) {
return Video(
id: (json['id'] as num).toInt(),
uuid: json['uuid'] as String,
base: (json['exercise_base'] as num).toInt(),
exerciseId: (json['exercise'] as num).toInt(),
size: (json['size'] as num).toInt(),
url: json['video'] as String,
duration: stringToNum(json['duration'] as String?),
@@ -44,7 +44,7 @@ Map<String, dynamic> _$VideoToJson(Video instance) => <String, dynamic>{
'id': instance.id,
'uuid': instance.uuid,
'video': instance.url,
'exercise_base': instance.base,
'exercise': instance.exerciseId,
'size': instance.size,
'duration': numToString(instance.duration),
'width': instance.width,

View File

@@ -37,7 +37,7 @@ class AddExerciseProvider with ChangeNotifier {
List<Muscle> _primaryMuscles = [];
List<Muscle> _secondaryMuscles = [];
static const _exerciseBaseUrlPath = 'exercise-base';
static const _exerciseUrlPath = 'exercise-base';
static const _imagesUrlPath = 'exerciseimage';
static const _exerciseTranslationUrlPath = 'exercise-translation';
static const _exerciseAliasPath = 'exercisealias';
@@ -215,13 +215,13 @@ class AddExerciseProvider with ChangeNotifier {
}
Future<Exercise> addExerciseBase() async {
final Uri postUri = baseProvider.makeUrl(_exerciseBaseUrlPath);
final Uri postUri = baseProvider.makeUrl(_exerciseUrlPath);
final Map<String, dynamic> newBaseMap = await baseProvider.post(exercise.toJson(), postUri);
final Exercise newExerciseBase = Exercise.fromJson(newBaseMap);
final Exercise newExercise = Exercise.fromJson(newBaseMap);
notifyListeners();
return newExerciseBase;
return newExercise;
}
Future<Variation> addVariation() async {
@@ -235,13 +235,13 @@ class AddExerciseProvider with ChangeNotifier {
return newVariation;
}
Future<void> addImages(Exercise base) async {
Future<void> addImages(Exercise exercise) async {
for (final image in _exerciseImages) {
final request = http.MultipartRequest('POST', baseProvider.makeUrl(_imagesUrlPath));
request.headers.addAll(baseProvider.getDefaultHeaders(includeAuth: true));
request.files.add(await http.MultipartFile.fromPath('image', image.path));
request.fields['exercise_base'] = base.id!.toString();
request.fields['exercise'] = exercise.id!.toString();
request.fields['style'] = EXERCISE_IMAGE_ART_STYLE.PHOTO.index.toString();
await request.send();
@@ -261,7 +261,7 @@ class AddExerciseProvider with ChangeNotifier {
}
Future<Alias> addExerciseAlias(String name, int exerciseId) async {
final alias = Alias(exerciseId: exerciseId, alias: name);
final alias = Alias(translationId: exerciseId, alias: name);
final Uri postUri = baseProvider.makeUrl(_exerciseAliasPath);
final Alias newAlias = Alias.fromJson(await baseProvider.post(alias.toJson(), postUri));

View File

@@ -27,15 +27,15 @@ class ExerciseDetailScreen extends StatelessWidget {
@override
Widget build(BuildContext context) {
final exerciseBase = ModalRoute.of(context)!.settings.arguments as Exercise;
final exercise = ModalRoute.of(context)!.settings.arguments as Exercise;
return Scaffold(
appBar: AppBar(
title: Text(exerciseBase.getTranslation(Localizations.localeOf(context).languageCode).name),
title: Text(exercise.getTranslation(Localizations.localeOf(context).languageCode).name),
),
body: Padding(
padding: const EdgeInsets.symmetric(horizontal: 10),
child: ExerciseDetail(exerciseBase),
child: ExerciseDetail(exercise),
),
);
}

View File

@@ -267,8 +267,8 @@ class MockAddExerciseProvider extends _i1.Mock implements _i8.AddExerciseProvide
) as _i14.Future<_i5.Variation>);
@override
_i14.Future<void> addImages(_i3.Exercise? base) => (super.noSuchMethod(
Invocation.method(#addImages, [base]),
_i14.Future<void> addImages(_i3.Exercise? exercise) => (super.noSuchMethod(
Invocation.method(#addImages, [exercise]),
returnValue: _i14.Future<void>.value(),
returnValueForMissingStub: _i14.Future<void>.value(),
) as _i14.Future<void>);

View File

@@ -79,7 +79,7 @@ void main() {
final Map<String, dynamic> tLanguageMap = jsonDecode(
fixture('exercises/language_entries.json'),
);
final Map<String, dynamic> tExerciseBaseInfoMap = jsonDecode(
final Map<String, dynamic> tExerciseInfoMap = jsonDecode(
fixture('exercises/exercisebaseinfo_response.json'),
);
@@ -120,12 +120,12 @@ void main() {
// Mock base info response
when(mockBaseProvider.makeUrl(exerciseBaseInfoUrl)).thenReturn(tExerciseInfoUri);
when(mockBaseProvider.fetch(tExerciseInfoUri)).thenAnswer(
(_) => Future.value(tExerciseBaseInfoMap),
(_) => Future.value(tExerciseInfoMap),
);
when(mockBaseProvider.makeUrl(exerciseBaseInfoUrl, id: 9)).thenReturn(tExerciseInfoDetailUri);
when(mockBaseProvider.fetch(tExerciseInfoDetailUri)).thenAnswer(
(_) => Future.value(tExerciseBaseInfoMap),
(_) => Future.value(tExerciseInfoMap),
);
});
@@ -392,9 +392,9 @@ void main() {
await database.into(database.exercises).insert(
ExercisesCompanion.insert(
id: tExerciseBaseInfoMap['id'],
data: json.encode(tExerciseBaseInfoMap),
lastUpdate: DateTime.parse(tExerciseBaseInfoMap['last_update_global']),
id: tExerciseInfoMap['id'],
data: json.encode(tExerciseInfoMap),
lastUpdate: DateTime.parse(tExerciseInfoMap['last_update_global']),
lastFetched: DateTime.now(),
),
);
@@ -424,9 +424,9 @@ void main() {
provider.languages = testLanguages;
await database.into(database.exercises).insert(
ExercisesCompanion.insert(
id: tExerciseBaseInfoMap['id'],
data: json.encode(tExerciseBaseInfoMap),
lastUpdate: DateTime.parse(tExerciseBaseInfoMap['last_update_global']),
id: tExerciseInfoMap['id'],
data: json.encode(tExerciseInfoMap),
lastUpdate: DateTime.parse(tExerciseInfoMap['last_update_global']),
lastFetched: DateTime.now().subtract(const Duration(hours: 1)),
),
);
@@ -449,9 +449,9 @@ void main() {
provider.languages = testLanguages;
await database.into(database.exercises).insert(
ExercisesCompanion.insert(
id: tExerciseBaseInfoMap['id'],
data: json.encode(tExerciseBaseInfoMap),
lastUpdate: DateTime.parse(tExerciseBaseInfoMap['last_update_global']),
id: tExerciseInfoMap['id'],
data: json.encode(tExerciseInfoMap),
lastUpdate: DateTime.parse(tExerciseInfoMap['last_update_global']),
lastFetched: DateTime.now().subtract(const Duration(days: 10)),
),
);
@@ -475,7 +475,7 @@ void main() {
test('fetching a known exercise - needed API refresh - new data from API', () async {
// Arrange
provider.languages = testLanguages;
final newData = Map.from(tExerciseBaseInfoMap);
final newData = Map.from(tExerciseInfoMap);
newData['uuid'] = 'bf6d5557-1c49-48fd-922e-75d11f81d4eb';
await database.into(database.exercises).insert(

View File

@@ -69,7 +69,7 @@
{
"id": 1,
"uuid": "1f5d2b2f-d4ea-4eeb-9377-56176465e08d",
"exercise_base": 9,
"exercise": 9,
"image": "http://localhost:8000/media/exercise-images/9/1f5d2b2f-d4ea-4eeb-9377-56176465e08d.jpg",
"is_main": true,
"status": "2",
@@ -78,7 +78,7 @@
{
"id": 2,
"uuid": "ab645585-26ef-4992-a9ec-15425687ece9",
"exercise_base": 9,
"exercise": 9,
"image": "http://localhost:8000/media/exercise-images/9/ab645585-26ef-4992-a9ec-15425687ece9.jpg",
"is_main": false,
"status": "2",
@@ -87,7 +87,7 @@
{
"id": 3,
"uuid": "d8aa5990-bb47-4111-9823-e2fbd98fe07f",
"exercise_base": 9,
"exercise": 9,
"image": "http://localhost:8000/media/exercise-images/9/d8aa5990-bb47-4111-9823-e2fbd98fe07f.jpg",
"is_main": false,
"status": "2",
@@ -96,7 +96,7 @@
{
"id": 4,
"uuid": "49a159e1-1e00-409a-81c9-b4d4489fbd67",
"exercise_base": 9,
"exercise": 9,
"image": "http://localhost:8000/media/exercise-images/9/49a159e1-1e00-409a-81c9-b4d4489fbd67.jpg",
"is_main": false,
"status": "2",
@@ -107,8 +107,8 @@
{
"id": 2,
"uuid": "63e996e9-a772-4ca5-9d09-8b4be03f6be4",
"exercise_base": 258,
"exercise_base_uuid": "6260e3aa-e46b-4b4b-8ada-58bfd0922d3a",
"exercise": 258,
"exercise_uuid": "6260e3aa-e46b-4b4b-8ada-58bfd0922d3a",
"video": "http://localhost:8000/media/exercise-video/258/63e996e9-a772-4ca5-9d09-8b4be03f6be4.MOV",
"is_main": false,
"size": 0,
@@ -136,22 +136,22 @@
}
],
"name": "2 Handed Kettlebell Swing",
"exercise_base": 9,
"exercise": 9,
"description": "<p>Two Handed Russian Style Kettlebell swing</p>\n<p> </p>\n<p><strong style=\"\">Pizza</strong> (Italian: [ˈpittsa], Neapolitan: [ˈpittsə]) is a dish of Italian origin consisting of a usually round, flat base of leavened wheat-based dough topped with tomatoes, cheese, and often various other ingredients (such as anchovies, mushrooms, onions, olives, pineapple, meat, etc.), which is then baked at a high temperature, traditionally in a wood-fired oven.[1] A small pizza is sometimes called a pizzetta. A person who makes pizza is known as a <strong style=\"\">pizzaiolo</strong>.</p>",
"notes": [
{
"id": 134,
"exercise": 345,
"translation": 345,
"comment": "it's important to do the exercise correctly"
},
{
"id": 135,
"exercise": 345,
"translation": 345,
"comment": "put a lot of effort into this exercise"
},
{
"id": 136,
"exercise": 345,
"translation": 345,
"comment": "have fun"
}
],
@@ -178,17 +178,17 @@
}
],
"name": "Kettlebell Con Dos Manos",
"exercise_base": 9,
"exercise": 9,
"description": "<p>Cualquier descripción aquí...</p>\n<p>La <em style=\"\"><strong>pizza</strong></em> es una preparación culinaria que consiste en un pan plano, habitualmente de forma circular, elaborado con harina de trigo, levadura, agua y sal (a veces aceite de oliva) que tradicionalmente se cubre con salsa de tomate y mozzarella y se hornea a alta temperatura en un horno de leña.234 El lugar donde se venden pizzas se denomina «pizzería» y al obrador, «pizzero» (<em style=\"\">pizzaiolo</em> en italiano). Aunque se considera que su origen está en la gastronomía italiana,3 particularmente la napolitana,2 su consumo está extendido a casi todos los países del mundo con diversas variantes locales, que incorporan distintos ingredientes para cubrir la masa.1 Junto con la hamburguesa, la pizza está considerada la comida más difundida del mundo,56 como consecuencia de la diáspora italiana que se estableció en América a lo largo del siglo xx principalmente en Nueva York,3 Buenos Aires o Chicago.78</p>",
"notes": [
{
"id": 137,
"exercise": 856,
"translation": 856,
"comment": "haz el ejercicio correctamente"
},
{
"id": 138,
"exercise": 856,
"translation": 856,
"comment": "pasatelo bien!"
}
],
@@ -209,12 +209,12 @@
"uuid": "2fe5f04b-5c9d-448c-a973-3fad6ddd4f74",
"aliases": [],
"name": "Zweihändiges Kettlebell",
"exercise_base": 9,
"exercise": 9,
"description": "<p>Irgendeine Beschreibung hier...</p>\n<p>Eine <strong style=\"\">Pizza</strong> (Aussprache [ˈpɪtsa], ital. [ˈpitːsa], deutscher Plural: <em style=\"\">die Pizzas</em> oder <em style=\"\">die Pizzen</em>[1]) ist ein vor dem Backen würzig belegtes Fladenbrot aus einfachem Hefeteig aus der italienischen Küche. Die heutige international verbreitete Variante mit Tomatensauce und Käse als Basis stammt vermutlich aus Neapel. 2017 wurde die neapolitanische Kunst des Pizzabäckers (<em style=\"\">Art of Neapolitan Pizzaiuolo</em>) von der UNESCO in die repräsentative Liste des immateriellen Kulturerbes der Menschheit aufgenommen.[2]</p>",
"notes": [
{
"id": 139,
"exercise": 855,
"translation": 855,
"comment": "mach die Übung richtig"
}
],