From 6cc96310c53c77653a9bbb3b2adf54cdb154bd26 Mon Sep 17 00:00:00 2001 From: Roland Geider Date: Fri, 12 Sep 2025 14:37:32 +0200 Subject: [PATCH] Simplify currentPlan --- lib/models/nutrition/nutritional_plan.dart | 4 +- lib/models/nutrition/nutritional_plan.g.dart | 3 +- lib/providers/nutrition.dart | 25 ++--- pubspec.lock | 4 +- test/nutrition/nutrition_provider_test.dart | 104 +++++++++++++++++++ 5 files changed, 117 insertions(+), 23 deletions(-) diff --git a/lib/models/nutrition/nutritional_plan.dart b/lib/models/nutrition/nutritional_plan.dart index bfb66a28..054acb27 100644 --- a/lib/models/nutrition/nutritional_plan.dart +++ b/lib/models/nutrition/nutritional_plan.dart @@ -74,7 +74,7 @@ class NutritionalPlan { NutritionalPlan({ this.id, required this.description, - required this.creationDate, + DateTime? creationDate, required this.startDate, this.endDate, this.onlyLogging = false, @@ -85,7 +85,7 @@ class NutritionalPlan { this.goalFiber, List? meals, List? diaryEntries, - }) { + }) : creationDate = creationDate ?? DateTime.now() { this.meals = meals ?? []; this.diaryEntries = diaryEntries ?? []; } diff --git a/lib/models/nutrition/nutritional_plan.g.dart b/lib/models/nutrition/nutritional_plan.g.dart index 0c67b9d9..742d3435 100644 --- a/lib/models/nutrition/nutritional_plan.g.dart +++ b/lib/models/nutrition/nutritional_plan.g.dart @@ -26,7 +26,8 @@ NutritionalPlan _$NutritionalPlanFromJson(Map json) { return NutritionalPlan( id: (json['id'] as num?)?.toInt(), description: json['description'] as String, - creationDate: DateTime.parse(json['creation_date'] as String), + creationDate: + json['creation_date'] == null ? null : DateTime.parse(json['creation_date'] as String), startDate: DateTime.parse(json['start'] as String), endDate: json['end'] == null ? null : DateTime.parse(json['end'] as String), onlyLogging: json['only_logging'] as bool? ?? false, diff --git a/lib/providers/nutrition.dart b/lib/providers/nutrition.dart index 2e03c4b6..251c1f4f 100644 --- a/lib/providers/nutrition.dart +++ b/lib/providers/nutrition.dart @@ -18,6 +18,7 @@ import 'dart:convert'; +import 'package:collection/collection.dart'; import 'package:flutter/material.dart'; import 'package:logging/logging.dart'; import 'package:wger/core/locator.dart'; @@ -72,25 +73,13 @@ class NutritionPlansProvider with ChangeNotifier { /// - Its end date is after now or not set /// If multiple plans match these criteria, the one with the most recent creation date is returned. NutritionalPlan? get currentPlan { - if (_plans.isEmpty) { - return null; - } - final now = DateTime.now(); - final activePlans = _plans.where((plan) { - final isAfterStart = plan.startDate.isBefore(now); - final isBeforeEnd = plan.endDate == null || plan.endDate!.isAfter(now); - return isAfterStart && isBeforeEnd; - }).toList(); - - if (activePlans.isEmpty) { - return null; - } - - // Sort by creation date (newest first) and return the first one - // TODO: this should already be done on _plans. this whole function can be a firstWhere() ? - activePlans.sort((a, b) => b.creationDate.compareTo(a.creationDate)); - return activePlans.first; + return _plans + .where((plan) => + plan.startDate.isBefore(now) && (plan.endDate == null || plan.endDate!.isAfter(now))) + .toList() + .sorted((a, b) => b.creationDate.compareTo(a.creationDate)) + .firstOrNull; } NutritionalPlan findById(int id) { diff --git a/pubspec.lock b/pubspec.lock index 4acfa56d..f3aa2015 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -744,10 +744,10 @@ packages: dependency: transitive description: name: leak_tracker - sha256: "8dcda04c3fc16c14f48a7bb586d4be1f0d1572731b6d81d51772ef47c02081e0" + sha256: "33e2e26bdd85a0112ec15400c8cbffea70d0f9c3407491f672a2fad47915e2de" url: "https://pub.dev" source: hosted - version: "11.0.1" + version: "11.0.2" leak_tracker_flutter_testing: dependency: transitive description: diff --git a/test/nutrition/nutrition_provider_test.dart b/test/nutrition/nutrition_provider_test.dart index 2b8fb842..40cae73d 100644 --- a/test/nutrition/nutrition_provider_test.dart +++ b/test/nutrition/nutrition_provider_test.dart @@ -5,12 +5,14 @@ import 'package:flutter_test/flutter_test.dart'; import 'package:mockito/mockito.dart'; import 'package:wger/database/ingredients/ingredients_database.dart'; import 'package:wger/models/nutrition/ingredient.dart'; +import 'package:wger/models/nutrition/nutritional_plan.dart'; import 'package:wger/providers/nutrition.dart'; import '../fixtures/fixture_reader.dart'; import '../measurements/measurement_provider_test.mocks.dart'; void main() { + final now = DateTime.now(); late NutritionPlansProvider nutritionProvider; late MockWgerBaseProvider mockWgerBaseProvider; late IngredientDatabase database; @@ -110,6 +112,108 @@ void main() { }); }); + group('currentPlan', () { + test('gibt den aktiven Plan zurück, wenn nur einer aktiv ist', () { + final plan = NutritionalPlan( + id: 1, + description: 'Aktiver Plan', + startDate: now.subtract(const Duration(days: 1)), + endDate: now.add(const Duration(days: 1)), + creationDate: now.subtract(const Duration(days: 2)), + ); + nutritionProvider = NutritionPlansProvider(mockWgerBaseProvider, [plan], database: database); + expect(nutritionProvider.currentPlan, equals(plan)); + }); + + test('gibt den neuesten aktiven Plan zurück, wenn mehrere aktiv sind', () { + final olderPlan = NutritionalPlan( + id: 1, + description: 'Älterer aktiver Plan', + startDate: now.subtract(const Duration(days: 10)), + endDate: now.add(const Duration(days: 10)), + creationDate: now.subtract(const Duration(days: 10)), + ); + final newerPlan = NutritionalPlan( + id: 2, + description: 'Neuerer aktiver Plan', + startDate: now.subtract(const Duration(days: 5)), + endDate: now.add(const Duration(days: 5)), + creationDate: now.subtract(const Duration(days: 2)), + ); + nutritionProvider = + NutritionPlansProvider(mockWgerBaseProvider, [olderPlan, newerPlan], database: database); + expect(nutritionProvider.currentPlan, equals(newerPlan)); + }); + }); + + group('currentPlan correctly returns the active plan', () { + test('no plans available -> null', () { + nutritionProvider = NutritionPlansProvider(mockWgerBaseProvider, [], database: database); + expect(nutritionProvider.currentPlan, isNull); + }); + + test('no active plan -> null', () { + final plans = [ + NutritionalPlan( + id: 1, + description: 'plan 1', + startDate: now.subtract(const Duration(days: 30)), + endDate: now.subtract(const Duration(days: 5)), + ), + NutritionalPlan( + id: 2, + description: 'plan 2', + startDate: now.add(const Duration(days: 100)), + endDate: now.add(const Duration(days: 50)), + ), + ]; + nutritionProvider = NutritionPlansProvider(mockWgerBaseProvider, plans, database: database); + expect(nutritionProvider.currentPlan, isNull); + }); + + test('active plan exists -> return it', () { + final plan = NutritionalPlan( + description: 'Active plan', + startDate: now.subtract(const Duration(days: 10)), + endDate: now.add(const Duration(days: 10)), + ); + nutritionProvider = NutritionPlansProvider(mockWgerBaseProvider, [plan], database: database); + expect(nutritionProvider.currentPlan, equals(plan)); + }); + + test('inactive plans are ignored', () { + final inactivePlan = NutritionalPlan( + description: 'Inactive plan', + startDate: now.subtract(const Duration(days: 10)), + endDate: now.add(const Duration(days: 5)), + ); + final plan = NutritionalPlan( + description: 'Active plan', + startDate: now.subtract(const Duration(days: 10)), + endDate: now.add(const Duration(days: 10)), + ); + nutritionProvider = + NutritionPlansProvider(mockWgerBaseProvider, [plan, inactivePlan], database: database); + expect(nutritionProvider.currentPlan, equals(plan)); + }); + + test('several active plans exists -> return newest', () { + final olderPlan = NutritionalPlan( + description: 'Old active plan', + startDate: now.subtract(const Duration(days: 10)), + endDate: now.add(const Duration(days: 10)), + ); + final newerPlan = NutritionalPlan( + description: 'Newer active plan', + startDate: now.subtract(const Duration(days: 5)), + endDate: now.add(const Duration(days: 5)), + ); + nutritionProvider = + NutritionPlansProvider(mockWgerBaseProvider, [olderPlan, newerPlan], database: database); + expect(nutritionProvider.currentPlan, equals(newerPlan)); + }); + }); + group('Ingredient cache DB', () { test('that if there is already valid data in the DB, the API is not hit', () async { // Arrange