feat: add weeekly ranking list and average calories

feature/chart-progress-page
98spag 2023-05-30 22:09:16 +02:00
parent cc1ea62d2d
commit fcc607043e
6 changed files with 139 additions and 138 deletions

View File

@ -34,7 +34,6 @@ class StatisticsPercentage extends StatelessWidget {
child: ValueListenableBuilder<List<double>>( child: ValueListenableBuilder<List<double>>(
valueListenable: StatisticsService.instance.ingredients, valueListenable: StatisticsService.instance.ingredients,
builder: (context, value, child) { builder: (context, value, child) {
print(value);
return Column( return Column(
mainAxisAlignment: MainAxisAlignment.spaceEvenly, mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [ children: [

View File

@ -6,6 +6,11 @@ String getFoodListStringByFood(String foodName, int count, int calories){
return limitedText; return limitedText;
} }
String getWeeklyRankingString(String foodName){
int maxWidth = 50;
String limitedText = foodName.length > maxWidth ? "${foodName.substring(0, maxWidth - 3)} ... " : foodName;
return limitedText;
}
Map<String,List<int>> getMapOfDistinctElementsWithCounterAndCalories(List<Food> foods){ Map<String,List<int>> getMapOfDistinctElementsWithCounterAndCalories(List<Food> foods){
Map<String,List<int>> resultMap = <String,List<int>>{}; Map<String,List<int>> resultMap = <String,List<int>>{};
@ -18,3 +23,13 @@ Map<String,List<int>> getMapOfDistinctElementsWithCounterAndCalories(List<Food>
} }
return resultMap; return resultMap;
} }
List<Food> getListOfDistinctElements(List<Food> foods){
List<Food> result = [];
for(int i = 0; i < foods.length;i++){
if(!result.any((element) => element.id == foods[i].id)){
result.add(foods[i]);
}
}
return result;
}

View File

@ -4,6 +4,7 @@ import 'package:ernaehrung/android/config/cast_helper.dart';
import 'package:flutter/cupertino.dart'; import 'package:flutter/cupertino.dart';
import 'package:hive/hive.dart'; import 'package:hive/hive.dart';
import '../models/food.dart'; import '../models/food.dart';
import 'format_helper.dart';
class StatisticsService { class StatisticsService {
@ -18,60 +19,66 @@ class StatisticsService {
final String reducedStatisticsBoxName = 'STATISTICS_REDUCED'; final String reducedStatisticsBoxName = 'STATISTICS_REDUCED';
final String mainStatisticsBoxName = 'STATISTICS_MAIN'; final String mainStatisticsBoxName = 'STATISTICS_MAIN';
final String progressStatisticsBoxName = 'STATISTICS_PROGRESS';
ValueNotifier<int> eatenCalories = ValueNotifier<int>(0); ValueNotifier<int> eatenCalories = ValueNotifier<int>(0);
ValueNotifier<List<double>> ingredients = ValueNotifier<List<double>>([0,0,0]); ValueNotifier<List<double>> ingredients = ValueNotifier<List<double>>([0,0,0]);
ValueNotifier<int> dailyAverageForCurrentWeek = ValueNotifier<int>(0);
ValueNotifier<List<Food>> weeklyCaloryRanking = ValueNotifier<List<Food>>([]);
initBoxes()async{ initBoxes()async{
Box reducedBox = Hive.box(reducedStatisticsBoxName); Box reducedBox = Hive.box(reducedStatisticsBoxName);
putIfKeyNotExists(reducedBox, 'FRÜHSTÜCK', []); Box progressBox = Hive.box(progressStatisticsBoxName);
putIfKeyNotExists(reducedBox, 'MITTAGESSEN', []);
putIfKeyNotExists(reducedBox, 'ABENDESSEN', []); putIfKeyNotExists([reducedBox,progressBox], 'FRÜHSTÜCK', []);
putIfKeyNotExists([reducedBox,progressBox], 'MITTAGESSEN', []);
putIfKeyNotExists([reducedBox,progressBox], 'ABENDESSEN', []);
updateReducedBoxByTimespan(TimeSpan.day); updateReducedBoxByTimespan(TimeSpan.day);
} }
void putIfKeyNotExists(List<Box> boxes, String key, dynamic value) {
void putIfKeyNotExists(Box box, String key, dynamic value) { for(int i = 0; i < boxes.length;i++){
if (!box.containsKey(key)) { if (!boxes[i].containsKey(key)) {
box.put(key, value); boxes[i].put(key, value);
}
} }
} }
updateReducedBoxByTimespan(TimeSpan timeSpan){ updateReducedBoxByTimespan(TimeSpan timeSpan){
clearReducedBoxBeforeUpdate(); clearReducedBoxBeforeUpdate();
DateTime now = DateTime.now(); int timestamp = getTimestampFromNow();
int timestamp = now.millisecondsSinceEpoch.toInt() ~/ 1000;
switch(timeSpan){ switch(timeSpan){
case TimeSpan.day: case TimeSpan.day:
getNewFoodAndUpdateReducedBoxByTimestamp(timestamp); getNewFoodAndUpdateBoxByTimestampAndBox(timestamp, Hive.box(reducedStatisticsBoxName));
break; break;
case TimeSpan.week: case TimeSpan.week:
List<int> currentWeek = getTimestampsByTimestampAndTimespan(TimeSpan.week,timestamp); List<int> currentWeek = getTimestampsByTimestampAndTimespan(TimeSpan.week,timestamp);
for(int i = 0;i < currentWeek.length;i++){ for(int i = 0;i < currentWeek.length;i++){
getNewFoodAndUpdateReducedBoxByTimestamp(currentWeek[i]); getNewFoodAndUpdateBoxByTimestampAndBox(currentWeek[i],Hive.box(reducedStatisticsBoxName));
} }
break; break;
case TimeSpan.month: case TimeSpan.month:
List<int> currentMonth = getTimestampsByTimestampAndTimespan(TimeSpan.month,timestamp); List<int> currentMonth = getTimestampsByTimestampAndTimespan(TimeSpan.month,timestamp);
for(int i = 0;i < currentMonth.length;i++){ for(int i = 0;i < currentMonth.length;i++){
getNewFoodAndUpdateReducedBoxByTimestamp(currentMonth[i]); getNewFoodAndUpdateBoxByTimestampAndBox(currentMonth[i],Hive.box(reducedStatisticsBoxName));
} }
break; break;
} }
updateCalculationsAndNotfiyListeners(); updateCalculationsAndNotfiyListenersForPorgressStatistics();
} }
void updateCalculationsAndNotfiyListeners(){ void updateCalculationsAndNotfiyListenersForPorgressStatistics(){
eatenCalories.value = getAllEatenCaloriesForTodayStatistics(); eatenCalories.value = getAllEatenCaloriesByBox(Hive.box(reducedStatisticsBoxName));
eatenCalories.notifyListeners(); eatenCalories.notifyListeners();
ingredients.value = getAllEatenIngredientsForTodayStatistics(); ingredients.value = getAllEatenIngredientsForTodayStatistics();
ingredients.notifyListeners(); ingredients.notifyListeners();
} }
void getNewFoodAndUpdateReducedBoxByTimestamp(int timestamp){
void getNewFoodAndUpdateBoxByTimestampAndBox(int timestamp, Box box){
Map<String,List<Food>> newFood = getFoodMapForGivenTimestampFromMainBox(timestamp); Map<String,List<Food>> newFood = getFoodMapForGivenTimestampFromMainBox(timestamp);
if(newFood.keys.isNotEmpty){ if(newFood.keys.isNotEmpty){
setElementsOfReducedBox(newFood); setElementsOfBoxByBox(newFood, box);
} }
} }
@ -98,8 +105,7 @@ class StatisticsService {
} }
} }
setElementsOfReducedBox(Map<String,List<Food>> newFood){ setElementsOfBoxByBox(Map<String,List<Food>> newFood,Box box){
Box box = Hive.box(reducedStatisticsBoxName);
Iterable<String> keys = newFood.keys; Iterable<String> keys = newFood.keys;
for(int i = 0; i < keys.length;i++){ for(int i = 0; i < keys.length;i++){
List<Food> alreadyExisting = castDynamicToListFood(box.get(keys.elementAt(i))); List<Food> alreadyExisting = castDynamicToListFood(box.get(keys.elementAt(i)));
@ -187,8 +193,7 @@ class StatisticsService {
return randomTimestamp; return randomTimestamp;
} }
int getAllEatenCaloriesForTodayStatistics(){ int getAllEatenCaloriesByBox(Box box){
Box box = Hive.box(reducedStatisticsBoxName);
num sum = 0; num sum = 0;
for(int i = 0; i < box.keys.length;i++){ for(int i = 0; i < box.keys.length;i++){
for(Food food in box.get(box.keys.elementAt(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]; return [fat as double,protein as double,carbs as double];
} }
num getAllCaloriesByBoxAndTimestamp(Box box,int timestamp){ void updateCalculationsAndNotfiyListenersForTodayStatistics(){
Map<String, List<Food>> valueMap = castDynamicMap(box.get(timestamp)); dailyAverageForCurrentWeek.value = getAverageCaloriesForCurrentWeekOnDailyBasis();
num sum = 0; dailyAverageForCurrentWeek.notifyListeners();
for(var mealType in valueMap.keys){ weeklyCaloryRanking.value = getWeeklyCaloryRanking();
if(valueMap.containsKey(mealType)){ weeklyCaloryRanking.notifyListeners();
List<Food> values = valueMap[mealType]!; }
for(var value in values){
sum += value.calories; void updateProgressBoxValues(){
Box box = Hive.box(progressStatisticsBoxName);
box.clear();
int timestamp = getTimestampFromNow();
List<int> 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<Food> getWeeklyCaloryRanking(){
int timestamp = getTimestampFromNow();
List<int> currentWeek = getTimestampsByTimestampAndTimespan(TimeSpan.week,timestamp);
List<Food> allFoodsOfWeek = [];
for(int i = 0;i < currentWeek.length;i++){
Map<String,List<Food>> foodFromMainBox = getFoodMapForGivenTimestampFromMainBox(currentWeek[i]);
Iterable<String> keys = foodFromMainBox.keys;
if(keys.isNotEmpty){
for(int i = 0; i < keys.length;i++ ){
allFoodsOfWeek.addAll(foodFromMainBox[keys.elementAt(i)] as Iterable<Food>);
} }
} }
} }
return sum; allFoodsOfWeek.sort((a, b) => b.calories - a.calories);
return getListOfDistinctElements(allFoodsOfWeek);
} }
num getCaloriesByTimestampAndMealTypeAndBox(Box box,DateTime date, String mealType){ getTimestampFromNow(){
int timestamp = date.millisecondsSinceEpoch.toInt() ~/ 1000; DateTime now = DateTime.now();
Map<String, List<Food>> valueMap = castDynamicMap(box.get(timestamp)); return now.millisecondsSinceEpoch.toInt() ~/ 1000;
num sum = 0;
if(valueMap.containsKey(mealType)){
List<Food> values = valueMap[mealType]!;
for(var value in values){
sum += value.calories;
}
}
return sum;
} }
showItems(){ showItems(){
print("Statistics.dart - showItems() - ITEMS"); print("Statistics.dart - showItems() - ITEMS");
//Hive.box(boxName).clear(); //Hive.box(boxName).clear();

View File

@ -26,6 +26,8 @@ class MainPageState extends State<MainPage> {
currentIndex = index; currentIndex = index;
if(currentIndex == 1){ if(currentIndex == 1){
StatisticsService.instance.updateReducedBoxByTimespan(TimeSpan.day); StatisticsService.instance.updateReducedBoxByTimespan(TimeSpan.day);
}else if(currentIndex == 2){
StatisticsService.instance.updateProgressBoxValues();
} }
pages[currentIndex]; pages[currentIndex];
}); });

View File

@ -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_big_text_component.dart';
import 'package:ernaehrung/android/components/meal_page_text/secondary_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/components/meal_page_text/title_component.dart';
import 'package:ernaehrung/android/config/statistics.dart';
import 'package:fl_chart/fl_chart.dart'; import 'package:fl_chart/fl_chart.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import '../../config/format_helper.dart';
class ProgressPage extends StatelessWidget { class ProgressPage extends StatelessWidget {
final String title; final String title;
final Color backgroundColor = const Color(0xff47a44b); final Color backgroundColor = const Color(0xff47a44b);
@ -28,109 +31,68 @@ class ProgressPage extends StatelessWidget {
padding: const EdgeInsets.symmetric(vertical: 16, horizontal: 8), padding: const EdgeInsets.symmetric(vertical: 16, horizontal: 8),
child: Column( child: Column(
children: [ children: [
Container( ValueListenableBuilder(
height: 284, valueListenable: StatisticsService.instance.dailyAverageForCurrentWeek,
decoration: BoxDecoration( builder: (context, value, child) {
border: Border.all(), return Container(
), height: 284,
child: Column( decoration: BoxDecoration(
crossAxisAlignment: CrossAxisAlignment.start, border: Border.all(),
children: [
const TitleComponent("Kalorien"),
const SizedBox(
height: 16,
), ),
const SecondaryTextComponent("Durchschnittlich"), child: Column(
SecondaryBigTextComponent(1235.toString()), crossAxisAlignment: CrossAxisAlignment.start,
SizedBox( children: [
height: 200, const TitleComponent("Kalorien"),
child: BarChart( const SizedBox(
BarChartData( height: 10,
barTouchData: barTouchData,
titlesData: titlesData,
borderData: borderData,
barGroups: barGroups,
gridData: FlGridData(show: false),
alignment: BarChartAlignment.spaceAround,
maxY: 200,
), ),
), 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( BarTouchData get barTouchData => BarTouchData(

View File

@ -18,6 +18,7 @@ void main() async {
//await Hive.deleteFromDisk(); //await Hive.deleteFromDisk();
await Hive.openBox('STATISTICS_REDUCED'); await Hive.openBox('STATISTICS_REDUCED');
await Hive.openBox('STATISTICS_PROGRESS');
await Hive.openBox('STATISTICS_MAIN'); await Hive.openBox('STATISTICS_MAIN');
await Hive.openBox('TODAY'); await Hive.openBox('TODAY');
setupTodayBox(); setupTodayBox();