diff --git a/lib/main.dart b/lib/main.dart index 0a076ed3..dbdfcb7d 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -22,6 +22,7 @@ import 'package:provider/provider.dart'; import 'package:wger/providers/body_weight.dart'; import 'package:wger/providers/exercises.dart'; import 'package:wger/providers/gallery.dart'; +import 'package:wger/providers/measurements.dart'; import 'package:wger/providers/nutrition.dart'; import 'package:wger/providers/workout_plans.dart'; import 'package:wger/screens/auth_screen.dart'; @@ -78,6 +79,12 @@ class MyApp extends StatelessWidget { update: (context, auth, previous) => previous != null ? previous : NutritionPlansProvider(auth, []), ), + ChangeNotifierProxyProvider( + create: (context) => + MeasurementProvider(Provider.of(context, listen: false)), + update: (context, auth, previous) => + previous != null ? previous : MeasurementProvider(auth), + ), ChangeNotifierProxyProvider( create: (context) => BodyWeightProvider(Provider.of(context, listen: false), []), diff --git a/lib/models/measurements/measurement_category.dart b/lib/models/measurements/measurement_category.dart index 18be478d..1aa9d6b4 100644 --- a/lib/models/measurements/measurement_category.dart +++ b/lib/models/measurements/measurement_category.dart @@ -15,7 +15,7 @@ class MeasurementCategory { final String unit; @JsonKey(ignore: true) - List measurementEntries = []; + List entries = []; MeasurementCategory({ required this.id, @@ -23,7 +23,7 @@ class MeasurementCategory { required this.unit, List? measurementEntries, }) { - this.measurementEntries = measurementEntries ?? []; + this.entries = measurementEntries ?? []; } // Boilerplate diff --git a/lib/providers/measurements.dart b/lib/providers/measurements.dart new file mode 100644 index 00000000..c68e48e2 --- /dev/null +++ b/lib/providers/measurements.dart @@ -0,0 +1,77 @@ +/* + * This file is part of wger Workout Manager . + * Copyright (C) 2020, 2021 wger Team + * + * wger Workout Manager is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +import 'package:flutter/material.dart'; +import 'package:http/http.dart' as http; +import 'package:wger/models/measurements/measurement_category.dart'; +import 'package:wger/models/measurements/measurement_entry.dart'; +import 'package:wger/providers/auth.dart'; +import 'package:wger/providers/base_provider.dart'; + +class MeasurementProvider extends WgerBaseProvider with ChangeNotifier { + static const _categoryUrl = 'measurement-category'; + static const _entryUrl = 'measurement'; + + List _categories = []; + + MeasurementProvider(AuthProvider auth, [http.Client? client]) : super(auth, client); + + List get categories { + return [..._categories]; + } + + /// Clears all lists + clear() { + _categories = []; + } + + /// Finds the category by ID + MeasurementCategory findCategoryById(int id) { + return _categories.firstWhere((category) => category.id == id); + } + + /// Fetches and sets the categories from the server (no entries) + Future> fetchAndSetCategories() async { + // Process the response + final data = await fetch(makeUrl(_categoryUrl)); + final List loadedEntries = []; + for (final entry in data['results']) { + loadedEntries.add(MeasurementCategory.fromJson(entry)); + } + + _categories = loadedEntries; + notifyListeners(); + return _categories; + } + + /// Fetches and sets the measurement entries for the given category + Future fetchAndSetCategoryEntries(int id) async { + final category = findCategoryById(id); + + // Process the response + final data = await fetch(makeUrl(_entryUrl, query: {'category': category.id.toString()})); + final List loadedEntries = []; + for (final entry in data['results']) { + loadedEntries.add(MeasurementEntry.fromJson(entry)); + } + category.entries = loadedEntries; + notifyListeners(); + + return category; + } +} diff --git a/test/measurement_category_test.dart b/test/measurement_category_test.dart index 3ea91313..b7abbdf2 100644 --- a/test/measurement_category_test.dart +++ b/test/measurement_category_test.dart @@ -43,11 +43,11 @@ void main() { expect(category.id, tMeasurementCategory.id); expect(category.name, tMeasurementCategory.name); expect(category.unit, tMeasurementCategory.unit); - expect(entry.id, tMeasurementCategory.measurementEntries[0].id); - expect(entry.category, tMeasurementCategory.measurementEntries[0].category); - expect(entry.date, tMeasurementCategory.measurementEntries[0].date); - expect(entry.value, tMeasurementCategory.measurementEntries[0].value); - expect(entry.notes, tMeasurementCategory.measurementEntries[0].notes); + expect(entry.id, tMeasurementCategory.entries[0].id); + expect(entry.category, tMeasurementCategory.entries[0].category); + expect(entry.date, tMeasurementCategory.entries[0].date); + expect(entry.value, tMeasurementCategory.entries[0].value); + expect(entry.notes, tMeasurementCategory.entries[0].notes); }); test('should convert a MeasurementCategory object to a JSON map', () { diff --git a/test/providers/measurement_provider_test.dart b/test/providers/measurement_provider_test.dart new file mode 100644 index 00000000..7840b8da --- /dev/null +++ b/test/providers/measurement_provider_test.dart @@ -0,0 +1,127 @@ +/* + * This file is part of wger Workout Manager . + * Copyright (C) 2020, 2021 wger Team + * + * wger Workout Manager is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * wger Workout Manager is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +import 'package:flutter_test/flutter_test.dart'; +import 'package:http/http.dart' as http; +import 'package:mockito/mockito.dart'; +import 'package:wger/providers/measurements.dart'; + +import '../base_provider_test.mocks.dart'; +import '../utils.dart'; + +void main() { + group('Test the measurement provider', () { + final categoryResponse = '''{ + "count": 2, + "next": null, + "previous": null, + "results": [ + {"id": 1, "name": "Strength", "unit": "kN"}, + {"id": 2, "name": "Biceps", "unit": "cm"} + ] + }'''; + + final entriesResponse = '''{ + "count": 6, + "next": null, + "previous": null, + "results": [ + { + "id": 1, + "category": 1, + "date": "2021-07-21", + "value": 10, + "notes": "Some important notes" + }, + { + "id": 2, + "category": 1, + "date": "2021-07-10", + "value": 15.00, + "notes": "" + }, + { + "id": 6, + "category": 2, + "date": "2021-07-10", + "value": 85, + "notes": "" + }, + { + "id": 5, + "category": 2, + "date": "2021-07-01", + "value": 80, + "notes": "" + }, + { + "id": 3, + "category": 1, + "date": "2021-06-20", + "value": 18.80, + "notes": "" + }, + { + "id": 4, + "category": 1, + "date": "2021-06-01", + "value": 5.00, + "notes": "" + } + ] + }'''; + + final mockHttp = MockClient(); + + when(mockHttp.get(Uri.parse('https://localhost/api/v2/measurement-category/'), + headers: anyNamed('headers'))) + .thenAnswer((_) => Future.value(http.Response(categoryResponse, 200))); + + when(mockHttp.get(Uri.parse('https://localhost/api/v2/measurement/?category=1'), + headers: anyNamed('headers'))) + .thenAnswer((_) => Future.value(http.Response(entriesResponse, 200))); + + var mockMeasurement = MeasurementProvider(testAuthProvider, mockHttp); + + test('Test fetching categories', () async { + await mockMeasurement.fetchAndSetCategories(); + expect(mockMeasurement.categories.length, 2); + + final category = mockMeasurement.categories.first; + expect(category.id, 1); + expect(category.name, 'Strength'); + expect(category.unit, 'kN'); + }); + + test('Test fetching entries', () async { + await mockMeasurement.fetchAndSetCategories(); + var category = mockMeasurement.categories.first; + expect(category.entries.length, 0); + + category = await mockMeasurement.fetchAndSetCategoryEntries(category.id); + expect(category.entries.length, 6); + final entry = category.entries.first; + + expect(entry.id, 1); + expect(entry.category, 1); + expect(entry.value, 10); + expect(entry.date, DateTime(2021, 7, 21)); + expect(entry.notes, 'Some important notes'); + }); + }); +}