From fcc607043e2afa5b07c5d65dad334e29b3bec388 Mon Sep 17 00:00:00 2001 From: 98spag Date: Tue, 30 May 2023 22:09:16 +0200 Subject: [PATCH] feat: add weeekly ranking list and average calories --- .../statistics_today_component.dart | 1 - lib/android/config/format_helper.dart | 15 ++ lib/android/config/statistics.dart | 106 +++++++----- lib/android/pages/nav_pages/main_page.dart | 2 + .../pages/nav_pages/progress_page.dart | 152 +++++++----------- lib/main.dart | 1 + 6 files changed, 139 insertions(+), 138 deletions(-) diff --git a/lib/android/components/meal_page_text/statistics_today_component.dart b/lib/android/components/meal_page_text/statistics_today_component.dart index 6916795..1808eaf 100644 --- a/lib/android/components/meal_page_text/statistics_today_component.dart +++ b/lib/android/components/meal_page_text/statistics_today_component.dart @@ -34,7 +34,6 @@ class StatisticsPercentage extends StatelessWidget { child: ValueListenableBuilder>( valueListenable: StatisticsService.instance.ingredients, builder: (context, value, child) { - print(value); return Column( mainAxisAlignment: MainAxisAlignment.spaceEvenly, children: [ diff --git a/lib/android/config/format_helper.dart b/lib/android/config/format_helper.dart index 098156b..18f5b2f 100644 --- a/lib/android/config/format_helper.dart +++ b/lib/android/config/format_helper.dart @@ -6,6 +6,11 @@ String getFoodListStringByFood(String foodName, int count, int calories){ return limitedText; } +String getWeeklyRankingString(String foodName){ + int maxWidth = 50; + String limitedText = foodName.length > maxWidth ? "${foodName.substring(0, maxWidth - 3)} ... " : foodName; + return limitedText; +} Map> getMapOfDistinctElementsWithCounterAndCalories(List foods){ Map> resultMap = >{}; @@ -17,4 +22,14 @@ Map> getMapOfDistinctElementsWithCounterAndCalories(List } } return resultMap; +} + +List getListOfDistinctElements(List foods){ + List result = []; + for(int i = 0; i < foods.length;i++){ + if(!result.any((element) => element.id == foods[i].id)){ + result.add(foods[i]); + } + } + return result; } \ No newline at end of file diff --git a/lib/android/config/statistics.dart b/lib/android/config/statistics.dart index 2d17e42..87291c3 100644 --- a/lib/android/config/statistics.dart +++ b/lib/android/config/statistics.dart @@ -4,6 +4,7 @@ import 'package:ernaehrung/android/config/cast_helper.dart'; import 'package:flutter/cupertino.dart'; import 'package:hive/hive.dart'; import '../models/food.dart'; +import 'format_helper.dart'; class StatisticsService { @@ -18,60 +19,66 @@ class StatisticsService { final String reducedStatisticsBoxName = 'STATISTICS_REDUCED'; final String mainStatisticsBoxName = 'STATISTICS_MAIN'; + final String progressStatisticsBoxName = 'STATISTICS_PROGRESS'; ValueNotifier eatenCalories = ValueNotifier(0); ValueNotifier> ingredients = ValueNotifier>([0,0,0]); + ValueNotifier dailyAverageForCurrentWeek = ValueNotifier(0); + ValueNotifier> weeklyCaloryRanking = ValueNotifier>([]); initBoxes()async{ Box reducedBox = Hive.box(reducedStatisticsBoxName); - putIfKeyNotExists(reducedBox, 'FRÜHSTÜCK', []); - putIfKeyNotExists(reducedBox, 'MITTAGESSEN', []); - putIfKeyNotExists(reducedBox, 'ABENDESSEN', []); + Box progressBox = Hive.box(progressStatisticsBoxName); + + putIfKeyNotExists([reducedBox,progressBox], 'FRÜHSTÜCK', []); + putIfKeyNotExists([reducedBox,progressBox], 'MITTAGESSEN', []); + putIfKeyNotExists([reducedBox,progressBox], 'ABENDESSEN', []); updateReducedBoxByTimespan(TimeSpan.day); } - - void putIfKeyNotExists(Box box, String key, dynamic value) { - if (!box.containsKey(key)) { - box.put(key, value); + void putIfKeyNotExists(List boxes, String key, dynamic value) { + for(int i = 0; i < boxes.length;i++){ + if (!boxes[i].containsKey(key)) { + boxes[i].put(key, value); + } } } updateReducedBoxByTimespan(TimeSpan timeSpan){ clearReducedBoxBeforeUpdate(); - DateTime now = DateTime.now(); - int timestamp = now.millisecondsSinceEpoch.toInt() ~/ 1000; + int timestamp = getTimestampFromNow(); switch(timeSpan){ case TimeSpan.day: - getNewFoodAndUpdateReducedBoxByTimestamp(timestamp); + getNewFoodAndUpdateBoxByTimestampAndBox(timestamp, Hive.box(reducedStatisticsBoxName)); break; case TimeSpan.week: List currentWeek = getTimestampsByTimestampAndTimespan(TimeSpan.week,timestamp); for(int i = 0;i < currentWeek.length;i++){ - getNewFoodAndUpdateReducedBoxByTimestamp(currentWeek[i]); + getNewFoodAndUpdateBoxByTimestampAndBox(currentWeek[i],Hive.box(reducedStatisticsBoxName)); } break; case TimeSpan.month: List currentMonth = getTimestampsByTimestampAndTimespan(TimeSpan.month,timestamp); for(int i = 0;i < currentMonth.length;i++){ - getNewFoodAndUpdateReducedBoxByTimestamp(currentMonth[i]); + getNewFoodAndUpdateBoxByTimestampAndBox(currentMonth[i],Hive.box(reducedStatisticsBoxName)); } break; } - updateCalculationsAndNotfiyListeners(); + updateCalculationsAndNotfiyListenersForPorgressStatistics(); } - void updateCalculationsAndNotfiyListeners(){ - eatenCalories.value = getAllEatenCaloriesForTodayStatistics(); + void updateCalculationsAndNotfiyListenersForPorgressStatistics(){ + eatenCalories.value = getAllEatenCaloriesByBox(Hive.box(reducedStatisticsBoxName)); eatenCalories.notifyListeners(); ingredients.value = getAllEatenIngredientsForTodayStatistics(); ingredients.notifyListeners(); } - void getNewFoodAndUpdateReducedBoxByTimestamp(int timestamp){ + + void getNewFoodAndUpdateBoxByTimestampAndBox(int timestamp, Box box){ Map> newFood = getFoodMapForGivenTimestampFromMainBox(timestamp); if(newFood.keys.isNotEmpty){ - setElementsOfReducedBox(newFood); + setElementsOfBoxByBox(newFood, box); } } @@ -98,8 +105,7 @@ class StatisticsService { } } - setElementsOfReducedBox(Map> newFood){ - Box box = Hive.box(reducedStatisticsBoxName); + setElementsOfBoxByBox(Map> newFood,Box box){ Iterable keys = newFood.keys; for(int i = 0; i < keys.length;i++){ List alreadyExisting = castDynamicToListFood(box.get(keys.elementAt(i))); @@ -187,8 +193,7 @@ class StatisticsService { return randomTimestamp; } - int getAllEatenCaloriesForTodayStatistics(){ - Box box = Hive.box(reducedStatisticsBoxName); + int getAllEatenCaloriesByBox(Box box){ num sum = 0; for(int i = 0; i < box.keys.length;i++){ for(Food food in box.get(box.keys.elementAt(i))){ @@ -213,33 +218,50 @@ class StatisticsService { return [fat as double,protein as double,carbs as double]; } - num getAllCaloriesByBoxAndTimestamp(Box box,int timestamp){ - Map> valueMap = castDynamicMap(box.get(timestamp)); - num sum = 0; - for(var mealType in valueMap.keys){ - if(valueMap.containsKey(mealType)){ - List values = valueMap[mealType]!; - for(var value in values){ - sum += value.calories; + void updateCalculationsAndNotfiyListenersForTodayStatistics(){ + dailyAverageForCurrentWeek.value = getAverageCaloriesForCurrentWeekOnDailyBasis(); + dailyAverageForCurrentWeek.notifyListeners(); + weeklyCaloryRanking.value = getWeeklyCaloryRanking(); + weeklyCaloryRanking.notifyListeners(); + } + + void updateProgressBoxValues(){ + Box box = Hive.box(progressStatisticsBoxName); + box.clear(); + int timestamp = getTimestampFromNow(); + List currentWeek = getTimestampsByTimestampAndTimespan(TimeSpan.week,timestamp); + for(int i = 0;i < currentWeek.length;i++){ + getNewFoodAndUpdateBoxByTimestampAndBox(currentWeek[i],box); + } + updateCalculationsAndNotfiyListenersForTodayStatistics(); + } + + int getAverageCaloriesForCurrentWeekOnDailyBasis(){ + Box box = Hive.box(progressStatisticsBoxName); + return getAllEatenCaloriesByBox(box)~/7; + } + + List getWeeklyCaloryRanking(){ + int timestamp = getTimestampFromNow(); + List currentWeek = getTimestampsByTimestampAndTimespan(TimeSpan.week,timestamp); + List allFoodsOfWeek = []; + for(int i = 0;i < currentWeek.length;i++){ + Map> foodFromMainBox = getFoodMapForGivenTimestampFromMainBox(currentWeek[i]); + Iterable keys = foodFromMainBox.keys; + if(keys.isNotEmpty){ + for(int i = 0; i < keys.length;i++ ){ + allFoodsOfWeek.addAll(foodFromMainBox[keys.elementAt(i)] as Iterable); } } } - return sum; + allFoodsOfWeek.sort((a, b) => b.calories - a.calories); + return getListOfDistinctElements(allFoodsOfWeek); } - num getCaloriesByTimestampAndMealTypeAndBox(Box box,DateTime date, String mealType){ - int timestamp = date.millisecondsSinceEpoch.toInt() ~/ 1000; - Map> valueMap = castDynamicMap(box.get(timestamp)); - num sum = 0; - if(valueMap.containsKey(mealType)){ - List values = valueMap[mealType]!; - for(var value in values){ - sum += value.calories; - } - } - return sum; + getTimestampFromNow(){ + DateTime now = DateTime.now(); + return now.millisecondsSinceEpoch.toInt() ~/ 1000; } - showItems(){ print("Statistics.dart - showItems() - ITEMS"); //Hive.box(boxName).clear(); diff --git a/lib/android/pages/nav_pages/main_page.dart b/lib/android/pages/nav_pages/main_page.dart index 641d58e..2543c92 100644 --- a/lib/android/pages/nav_pages/main_page.dart +++ b/lib/android/pages/nav_pages/main_page.dart @@ -26,6 +26,8 @@ class MainPageState extends State { currentIndex = index; if(currentIndex == 1){ StatisticsService.instance.updateReducedBoxByTimespan(TimeSpan.day); + }else if(currentIndex == 2){ + StatisticsService.instance.updateProgressBoxValues(); } pages[currentIndex]; }); diff --git a/lib/android/pages/nav_pages/progress_page.dart b/lib/android/pages/nav_pages/progress_page.dart index ebfd779..287626b 100644 --- a/lib/android/pages/nav_pages/progress_page.dart +++ b/lib/android/pages/nav_pages/progress_page.dart @@ -1,9 +1,12 @@ import 'package:ernaehrung/android/components/meal_page_text/secondary_big_text_component.dart'; import 'package:ernaehrung/android/components/meal_page_text/secondary_text_component.dart'; import 'package:ernaehrung/android/components/meal_page_text/title_component.dart'; +import 'package:ernaehrung/android/config/statistics.dart'; import 'package:fl_chart/fl_chart.dart'; import 'package:flutter/material.dart'; +import '../../config/format_helper.dart'; + class ProgressPage extends StatelessWidget { final String title; final Color backgroundColor = const Color(0xff47a44b); @@ -28,109 +31,68 @@ class ProgressPage extends StatelessWidget { padding: const EdgeInsets.symmetric(vertical: 16, horizontal: 8), child: Column( children: [ - Container( - height: 284, - decoration: BoxDecoration( - border: Border.all(), - ), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - const TitleComponent("Kalorien"), - const SizedBox( - height: 16, + ValueListenableBuilder( + valueListenable: StatisticsService.instance.dailyAverageForCurrentWeek, + builder: (context, value, child) { + return Container( + height: 284, + decoration: BoxDecoration( + border: Border.all(), ), - const SecondaryTextComponent("Durchschnittlich"), - SecondaryBigTextComponent(1235.toString()), - SizedBox( - height: 200, - child: BarChart( - BarChartData( - barTouchData: barTouchData, - titlesData: titlesData, - borderData: borderData, - barGroups: barGroups, - gridData: FlGridData(show: false), - alignment: BarChartAlignment.spaceAround, - maxY: 200, + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + const TitleComponent("Kalorien"), + const SizedBox( + height: 10, ), - ), - ) - ], - ), + const SecondaryTextComponent("Durchschnittlich"), + SecondaryBigTextComponent(value.toString()), + SizedBox( + height: 200, + child: BarChart( + BarChartData( + barTouchData: barTouchData, + titlesData: titlesData, + borderData: borderData, + barGroups: barGroups, + gridData: FlGridData(show: false), + alignment: BarChartAlignment.spaceAround, + maxY: 200, + ), + ), + ) + ], + ), + ); + }, + ), + ValueListenableBuilder( + valueListenable: StatisticsService.instance.weeklyCaloryRanking, + builder: (context, value, child) { + return Column( + //TODO: Loop through the values and display them dynamically + children: [ + const TitleComponent("Lebensmittel mit dem höchsten Kaloriengehalt"), + const SizedBox(height: 24,), + for (var item in value.toSet()) + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + SecondaryTextComponent(getWeeklyRankingString(item.name)), + SecondaryTextComponent(item.calories.toString()), + ], + ), + ], + ); + }, ), - Container( - margin: const EdgeInsets.symmetric(vertical: 16, horizontal: 0), - child: Column( - //TODO: Aussortieren und mit einer Schleife drüber gehen - //bzw. die Daten aus der Hivebox ziehen und sortieren - children: [ - const TitleComponent( - "Lebensmittel mit dem höchsten Kaloriengehalt"), - const SizedBox(height: 24,), - Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: const [ - SecondaryTextComponent("Haferflocker"), - SecondaryTextComponent("670 Kalorien"), - ], - ), - const SizedBox(height: 24,), - Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: const [ - SecondaryTextComponent("Haferflocker"), - SecondaryTextComponent("670 Kalorien"), - ], - ), - const SizedBox(height: 24,), - Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: const [ - SecondaryTextComponent("Haferflocker"), - SecondaryTextComponent("670 Kalorien"), - ], - ), - const SizedBox(height: 24,), - Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: const [ - SecondaryTextComponent("Haferflocker"), - SecondaryTextComponent("670 Kalorien"), - ], - ), - const SizedBox(height: 24,), - Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: const [ - SecondaryTextComponent("Haferflocker"), - SecondaryTextComponent("670 Kalorien"), - ], - ), - const SizedBox(height: 24,), - Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: const [ - SecondaryTextComponent("Haferflocker"), - SecondaryTextComponent("670 Kalorien"), - ], - ), - const SizedBox(height: 24,), - Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: const [ - SecondaryTextComponent("Haferflocker"), - SecondaryTextComponent("670 Kalorien"), - ], - ) - ], - ), - ) ], ), ), ), ); + } BarTouchData get barTouchData => BarTouchData( diff --git a/lib/main.dart b/lib/main.dart index 8275278..48fd8b8 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -18,6 +18,7 @@ void main() async { //await Hive.deleteFromDisk(); await Hive.openBox('STATISTICS_REDUCED'); + await Hive.openBox('STATISTICS_PROGRESS'); await Hive.openBox('STATISTICS_MAIN'); await Hive.openBox('TODAY'); setupTodayBox();