Skip to content

Commit

Permalink
Merge pull request #566 from Dieterbe/nutrition-cleanup
Browse files Browse the repository at this point in the history
Nutrition cleanup & further enhancements
  • Loading branch information
rolandgeider authored May 12, 2024
2 parents 49823a8 + ef78bc5 commit 478464f
Show file tree
Hide file tree
Showing 23 changed files with 1,099 additions and 768 deletions.
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@
.pub-cache/
.pub/
/build/
**/failures/*.png


# Web related
lib/generated_plugin_registrant.dart
Expand Down
2 changes: 2 additions & 0 deletions lib/helpers/colors.dart
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@ const LIST_OF_COLORS3 = [
Color(0xFFFFA600),
];

const COLOR_SURPLUS = Color.fromARGB(255, 231, 71, 71);

Iterable<Color> generateChartColors(int nrOfItems) sync* {
final List<Color> colors;

Expand Down
8 changes: 8 additions & 0 deletions lib/l10n/app_en.arb
Original file line number Diff line number Diff line change
Expand Up @@ -352,6 +352,14 @@
"@weekAverage": {
"description": "Header for the column of '7 day average' nutritional values, i.e. what was logged last week"
},
"surplus": "surplus",
"@surplus": {
"description": "Caloric surplus (either planned or unplanned)"
},
"deficit": "deficit",
"@deficit": {
"description": "Caloric deficit (either planned or unplanned)"
},
"difference": "Difference",
"@difference": {},
"percentEnergy": "Percent of energy",
Expand Down
1 change: 0 additions & 1 deletion lib/models/nutrition/meal.dart
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,6 @@ class Meal {

this.mealItems = mealItems ?? [];
this.diaryEntries = diaryEntries ?? [];
time = time ?? TimeOfDay.fromDateTime(DateTime.now());
this.name = name ?? '';
}

Expand Down
161 changes: 161 additions & 0 deletions lib/models/nutrition/nutritional_goals.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,161 @@
/*
* This file is part of wger Workout Manager <https://github.com/wger-project>.
* 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 <http://www.gnu.org/licenses/>.
*/

import 'package:wger/helpers/consts.dart';
import 'package:wger/models/nutrition/nutritional_values.dart';

class NutritionalGoals {
double? energy = 0;
double? protein = 0;
double? carbohydrates = 0;
double? carbohydratesSugar = 0;
double? fat = 0;
double? fatSaturated = 0;
double? fibres = 0;
double? sodium = 0;

NutritionalGoals({
this.energy,
this.protein,
this.carbohydrates,
this.carbohydratesSugar,
this.fat,
this.fatSaturated,
this.fibres,
this.sodium,
}) {
// infer values where we can
if (energy == null) {
if (protein != null && carbohydrates != null && fat != null) {
energy =
protein! * ENERGY_PROTEIN + carbohydrates! * ENERGY_CARBOHYDRATES + fat! * ENERGY_FAT;
}
return;
}
// TODO: input validation when the user modifies/creates the plan, to assure energy is high enough
if (protein == null && carbohydrates != null && fat != null) {
protein =
(energy! - carbohydrates! * ENERGY_CARBOHYDRATES - fat! * ENERGY_FAT) / ENERGY_PROTEIN;
assert(protein! > 0);
} else if (carbohydrates == null && protein != null && fat != null) {
carbohydrates =
(energy! - protein! * ENERGY_PROTEIN - fat! * ENERGY_FAT) / ENERGY_CARBOHYDRATES;
assert(carbohydrates! > 0);
} else if (fat == null && protein != null && carbohydrates != null) {
fat = (energy! - protein! * ENERGY_PROTEIN - carbohydrates! * ENERGY_CARBOHYDRATES) /
ENERGY_FAT;
assert(fat! > 0);
}
}

NutritionalGoals operator /(double v) {
return NutritionalGoals(
energy: energy != null ? energy! / v : null,
protein: protein != null ? protein! / v : null,
carbohydrates: carbohydrates != null ? carbohydrates! / v : null,
carbohydratesSugar: carbohydratesSugar != null ? carbohydratesSugar! / v : null,
fat: fat != null ? fat! / v : null,
fatSaturated: fatSaturated != null ? fatSaturated! / v : null,
fibres: fibres != null ? fibres! / v : null,
sodium: sodium != null ? sodium! / v : null,
);
}

bool isComplete() {
return energy != null && protein != null && carbohydrates != null && fat != null;
}

/// Convert goals into values.
/// This turns unset goals into values of 0.
/// Only use this if you know what you're doing, e.g. if isComplete() is true
NutritionalValues toValues() {
return NutritionalValues.values(
energy ?? 0,
protein ?? 0,
carbohydrates ?? 0,
carbohydratesSugar ?? 0,
fat ?? 0,
fatSaturated ?? 0,
fibres ?? 0,
sodium ?? 0,
);
}

/// Calculates the percentage each macro nutrient adds to the total energy
NutritionalGoals energyPercentage() {
final goals = NutritionalGoals();
// when you create a new plan or meal, somehow goals like energy is set to 0
// whereas strictly speaking it should be null. However,
// we know the intention so treat 0 as null here.
if (energy == null || energy == 0) {
return goals;
}

if (protein != null) {
goals.protein = (100 * protein! * ENERGY_PROTEIN) / energy!;
}
if (carbohydrates != null) {
goals.carbohydrates = (100 * carbohydrates! * ENERGY_CARBOHYDRATES) / energy!;
}
if (fat != null) {
goals.fat = (100 * fat! * ENERGY_FAT) / energy!;
}
return goals;
}

double? prop(String name) {
return switch (name) {
'energy' => energy,
'protein' => protein,
'carbohydrates' => carbohydrates,
'carbohydratesSugar' => carbohydratesSugar,
'fat' => fat,
'fatSaturated' => fatSaturated,
'fibres' => fibres,
'sodium' => sodium,
_ => 0,
};
}

@override
String toString() {
return 'e: $energy, p: $protein, c: $carbohydrates, cS: $carbohydratesSugar, f: $fat, fS: $fatSaturated, fi: $fibres, s: $sodium';
}

@override
//ignore: avoid_equals_and_hash_code_on_mutable_classes
int get hashCode => Object.hash(
energy, protein, carbohydrates, carbohydratesSugar, fat, fatSaturated, fibres, sodium);

@override
// ignore: avoid_equals_and_hash_code_on_mutable_classes
bool operator ==(Object other) {
if (identical(this, other)) {
return true;
}
return other is NutritionalGoals &&
other.energy == energy &&
other.protein == protein &&
other.carbohydrates == carbohydrates &&
other.carbohydratesSugar == carbohydratesSugar &&
other.fat == fat &&
other.fatSaturated == fatSaturated &&
other.fibres == fibres &&
other.sodium == sodium;
}
}
57 changes: 24 additions & 33 deletions lib/models/nutrition/nutritional_plan.dart
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import 'package:wger/helpers/json.dart';
import 'package:wger/models/nutrition/log.dart';
import 'package:wger/models/nutrition/meal.dart';
import 'package:wger/models/nutrition/meal_item.dart';
import 'package:wger/models/nutrition/nutritional_goals.dart';
import 'package:wger/models/nutrition/nutritional_values.dart';

part 'nutritional_plan.g.dart';
Expand Down Expand Up @@ -107,19 +108,32 @@ class NutritionalPlan {
/// note that (some of) this is already done on the server. It might be better
/// to read it from there, but on the other hand we might want to do more locally
/// so that a mostly offline mode is possible.
NutritionalValues get plannedNutritionalValues {
NutritionalGoals get nutritionalGoals {
// If there are set goals, they take preference over any meals
if (hasAnyGoals) {
final out = NutritionalValues();

out.energy = goalEnergy != null ? goalEnergy!.toDouble() : 0;
out.fat = goalFat != null ? goalFat!.toDouble() : 0;
out.carbohydrates = goalCarbohydrates != null ? goalCarbohydrates!.toDouble() : 0;
out.protein = goalProtein != null ? goalProtein!.toDouble() : 0;
return out;
return NutritionalGoals(
energy: goalEnergy?.toDouble(),
fat: goalFat?.toDouble(),
protein: goalProtein?.toDouble(),
carbohydrates: goalCarbohydrates?.toDouble(),
);
}

return meals.fold(NutritionalValues(), (a, b) => a + b.plannedNutritionalValues);
// if there are no set goals and no defined meals, the goals are still undefined
if (meals.isEmpty) {
return NutritionalGoals();
}
// otherwise, add up all the nutritional values of the meals and use that as goals
final sumValues = meals.fold(NutritionalValues(), (a, b) => a + b.plannedNutritionalValues);
return NutritionalGoals(
energy: sumValues.energy,
fat: sumValues.fat,
protein: sumValues.protein,
carbohydrates: sumValues.carbohydrates,
carbohydratesSugar: sumValues.carbohydratesSugar,
fatSaturated: sumValues.fatSaturated,
fibres: sumValues.fibres,
sodium: sumValues.sodium,
);
}

NutritionalValues get loggedNutritionalValuesToday {
Expand All @@ -138,28 +152,6 @@ class NutritionalPlan {
.fold(NutritionalValues(), (a, b) => a + b.nutritionalValues);
}

/// Calculates the percentage each macro nutrient adds to the total energy
BaseNutritionalValues energyPercentage(NutritionalValues values) {
return BaseNutritionalValues(
values.protein > 0 ? ((values.protein * ENERGY_PROTEIN * 100) / values.energy) : 0,
values.carbohydrates > 0
? ((values.carbohydrates * ENERGY_CARBOHYDRATES * 100) / values.energy)
: 0,
values.fat > 0 ? ((values.fat * ENERGY_FAT * 100) / values.energy) : 0,
);
}

/// Calculates the grams per body-kg for each macro nutrient
BaseNutritionalValues gPerBodyKg(num weight, NutritionalValues values) {
assert(weight > 0);

return BaseNutritionalValues(
values.protein / weight,
values.carbohydrates / weight,
values.fat / weight,
);
}

Map<DateTime, NutritionalValues> get logEntriesValues {
final out = <DateTime, NutritionalValues>{};
for (final log in diaryEntries) {
Expand Down Expand Up @@ -220,7 +212,6 @@ class NutritionalPlan {
id: PSEUDO_MEAL_ID,
plan: id,
name: name,
time: null,
diaryEntries: diaryEntries.where((e) => e.mealId == null).toList(),
);
}
Expand Down
8 changes: 0 additions & 8 deletions lib/models/nutrition/nutritional_values.dart
Original file line number Diff line number Diff line change
Expand Up @@ -106,11 +106,3 @@ class NutritionalValues {
int get hashCode => Object.hash(
energy, protein, carbohydrates, carbohydratesSugar, fat, fatSaturated, fibres, sodium);
}

class BaseNutritionalValues {
double protein = 0;
double carbohydrates = 0;
double fat = 0;

BaseNutritionalValues(this.protein, this.carbohydrates, this.fat);
}
Loading

0 comments on commit 478464f

Please sign in to comment.