Skip to content

Commit d1cfce7

Browse files
committed
adjust older code for 'current plan' and showing weight during plan
1 parent 7986df3 commit d1cfce7

File tree

2 files changed

+68
-23
lines changed

2 files changed

+68
-23
lines changed

lib/providers/nutrition.dart

Lines changed: 40 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -66,13 +66,30 @@ class NutritionPlansProvider with ChangeNotifier {
6666
ingredients = [];
6767
}
6868

69-
/// Returns the current active nutritional plan. At the moment this is just
70-
/// the latest, but this might change in the future.
69+
/// Returns the current active nutritional plan.
70+
/// A plan is considered active if:
71+
/// - Its start date is before now
72+
/// - Its end date is after now or not set
73+
/// If multiple plans match these criteria, the one with the most recent creation date is returned.
7174
NutritionalPlan? get currentPlan {
72-
if (_plans.isNotEmpty) {
73-
return _plans.first;
75+
if (_plans.isEmpty) {
76+
return null;
7477
}
75-
return null;
78+
79+
final now = DateTime.now();
80+
final activePlans = _plans.where((plan) {
81+
final isAfterStart = plan.startDate.isBefore(now);
82+
final isBeforeEnd = plan.endDate == null || plan.endDate!.isAfter(now);
83+
return isAfterStart && isBeforeEnd;
84+
}).toList();
85+
86+
if (activePlans.isEmpty) {
87+
return null;
88+
}
89+
90+
// Sort by creation date (newest first) and return the first one
91+
activePlans.sort((a, b) => b.creationDate.compareTo(a.creationDate));
92+
return activePlans.first;
7693
}
7794

7895
NutritionalPlan findById(int id) {
@@ -109,7 +126,8 @@ class NutritionPlansProvider with ChangeNotifier {
109126

110127
/// Fetches and sets all plans fully, i.e. with all corresponding child objects
111128
Future<void> fetchAndSetAllPlansFull() async {
112-
final data = await baseProvider.fetchPaginated(baseProvider.makeUrl(_nutritionalPlansPath));
129+
final data = await baseProvider
130+
.fetchPaginated(baseProvider.makeUrl(_nutritionalPlansPath));
113131
await Future.wait(data.map((e) => fetchAndSetPlanFull(e['id'])).toList());
114132
}
115133

@@ -170,7 +188,8 @@ class NutritionPlansProvider with ChangeNotifier {
170188
// Logs
171189
await fetchAndSetLogs(plan);
172190
for (final meal in meals) {
173-
meal.diaryEntries = plan.diaryEntries.where((e) => e.mealId == meal.id).toList();
191+
meal.diaryEntries =
192+
plan.diaryEntries.where((e) => e.mealId == meal.id).toList();
174193
}
175194

176195
// ... and done
@@ -204,7 +223,8 @@ class NutritionPlansProvider with ChangeNotifier {
204223
_plans.removeAt(existingPlanIndex);
205224
notifyListeners();
206225

207-
final response = await baseProvider.deleteRequest(_nutritionalPlansPath, id);
226+
final response =
227+
await baseProvider.deleteRequest(_nutritionalPlansPath, id);
208228

209229
if (response.statusCode >= 400) {
210230
_plans.insert(existingPlanIndex, existingPlan);
@@ -284,7 +304,8 @@ class NutritionPlansProvider with ChangeNotifier {
284304
notifyListeners();
285305

286306
// Try to delete
287-
final response = await baseProvider.deleteRequest(_mealItemPath, mealItem.id!);
307+
final response =
308+
await baseProvider.deleteRequest(_mealItemPath, mealItem.id!);
288309
if (response.statusCode >= 400) {
289310
meal.mealItems.insert(mealItemIndex, existingMealItem);
290311
notifyListeners();
@@ -299,7 +320,8 @@ class NutritionPlansProvider with ChangeNotifier {
299320
/// Fetch and return an ingredient
300321
///
301322
/// If the ingredient is not known locally, it is fetched from the server
302-
Future<Ingredient> fetchIngredient(int ingredientId, {IngredientDatabase? database}) async {
323+
Future<Ingredient> fetchIngredient(int ingredientId,
324+
{IngredientDatabase? database}) async {
303325
database ??= this.database;
304326
Ingredient ingredient;
305327

@@ -317,9 +339,11 @@ class NutritionPlansProvider with ChangeNotifier {
317339
_logger.info("Loaded ingredient '${ingredient.name}' from db cache");
318340

319341
// Prune old entries
320-
if (DateTime.now()
321-
.isAfter(ingredientDb.lastFetched.add(const Duration(days: DAYS_TO_CACHE)))) {
322-
(database.delete(database.ingredients)..where((i) => i.id.equals(ingredientId))).go();
342+
if (DateTime.now().isAfter(ingredientDb.lastFetched
343+
.add(const Duration(days: DAYS_TO_CACHE)))) {
344+
(database.delete(database.ingredients)
345+
..where((i) => i.id.equals(ingredientId)))
346+
.go();
323347
}
324348
} else {
325349
final data = await baseProvider.fetch(
@@ -347,7 +371,9 @@ class NutritionPlansProvider with ChangeNotifier {
347371
final ingredientDb = await database.select(database.ingredients).get();
348372
_logger.info('Read ${ingredientDb.length} ingredients from db cache');
349373
if (ingredientDb.isNotEmpty) {
350-
ingredients = ingredientDb.map((e) => Ingredient.fromJson(jsonDecode(e.data))).toList();
374+
ingredients = ingredientDb
375+
.map((e) => Ingredient.fromJson(jsonDecode(e.data)))
376+
.toList();
351377
}
352378
}
353379

lib/widgets/measurements/helpers.dart

Lines changed: 28 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -21,11 +21,16 @@ List<Widget> getOverviewWidgets(
2121
height: 220,
2222
child: MeasurementChartWidgetFl(raw, unit, avgs: avg),
2323
),
24-
if (avg.isNotEmpty) MeasurementOverallChangeWidget(avg.first, avg.last, unit),
24+
if (avg.isNotEmpty)
25+
MeasurementOverallChangeWidget(avg.first, avg.last, unit),
2526
const SizedBox(height: 8),
2627
];
2728
}
2829

30+
// TODO(dieter): i'm not sure if this handles well the case where weights were not logged consistently
31+
// e.g. if the plan runs for a month, but the first point is after 3 weeks.
32+
// and the last (non-included) point was *right* before the startDate.
33+
// wouldn't it be better to interpolate the missing points?
2934
List<Widget> getOverviewWidgetsSeries(
3035
String name,
3136
List<MeasurementChartEntry> entriesAll,
@@ -35,7 +40,10 @@ List<Widget> getOverviewWidgetsSeries(
3540
BuildContext context,
3641
) {
3742
final monthAgo = DateTime.now().subtract(const Duration(days: 30));
38-
final showPlan = plan != null && entriesAll.any((e) => e.date.isAfter(plan.creationDate));
43+
final showPlan = plan != null &&
44+
entriesAll.any((e) =>
45+
e.date.isAfter(plan.startDate) &&
46+
(plan.endDate == null || e.date.isBefore(plan.endDate!)));
3947

4048
return [
4149
...getOverviewWidgets(
@@ -47,9 +55,18 @@ List<Widget> getOverviewWidgetsSeries(
4755
),
4856
if (showPlan)
4957
...getOverviewWidgets(
50-
AppLocalizations.of(context).chartDuringPlanTitle(name, plan.description),
51-
entriesAll.where((e) => e.date.isAfter(plan.creationDate)).toList(),
52-
entries7dAvg.where((e) => e.date.isAfter(plan.creationDate)).toList(),
58+
AppLocalizations.of(context)
59+
.chartDuringPlanTitle(name, plan.description),
60+
entriesAll
61+
.where((e) =>
62+
e.date.isAfter(plan.startDate) &&
63+
(plan.endDate == null || e.date.isBefore(plan.endDate!)))
64+
.toList(),
65+
entries7dAvg
66+
.where((e) =>
67+
e.date.isAfter(plan.startDate) &&
68+
(plan.endDate == null || e.date.isBefore(plan.endDate!)))
69+
.toList(),
5370
unit,
5471
context,
5572
),
@@ -58,13 +75,15 @@ List<Widget> getOverviewWidgetsSeries(
5875
// then let's show a separate chart just focusing on the last 30 days,
5976
// if there is data for it.
6077
if (entriesAll.isNotEmpty &&
61-
entriesAll.first.date.isBefore(entriesAll.last.date.subtract(const Duration(days: 75))) &&
78+
entriesAll.first.date.isBefore(
79+
entriesAll.last.date.subtract(const Duration(days: 75))) &&
6280
(plan == null ||
6381
(showPlan &&
6482
entriesAll
65-
.firstWhere((e) => e.date.isAfter(plan.creationDate))
83+
.firstWhere((e) => e.date.isAfter(plan.startDate))
6684
.date
67-
.isBefore(entriesAll.last.date.subtract(const Duration(days: 30))))) &&
85+
.isBefore(entriesAll.last.date
86+
.subtract(const Duration(days: 30))))) &&
6887
entriesAll.any((e) => e.date.isAfter(monthAgo)))
6988
...getOverviewWidgets(
7089
AppLocalizations.of(context).chart30DaysTitle(name),
@@ -92,7 +111,7 @@ List<Widget> getOverviewWidgetsSeries(
92111
];
93112
}
94113

95-
// return the raw and average meaasurements for a "sensible range"
114+
// return the raw and average measurements for a "sensible range"
96115
// a sensible range is something relatively recent, which is most relevant
97116
// for the user to track their progress, but a range should always include
98117
// at least 5 points, and if not we chose a bigger one.

0 commit comments

Comments
 (0)