import 'package:flutter/material.dart'; import 'package:font_awesome_flutter/font_awesome_flutter.dart'; import 'package:intl/intl.dart'; import 'package:tasktracker/AvgDay.dart'; import 'package:tasktracker/main.dart'; import 'Data.dart'; import 'DebugHelper.dart'; import 'User.dart' as User; class AnalyticsPage extends StatefulWidget { const AnalyticsPage({Key? key}) : super(key: key); @override State createState() => _AnalyticsPageState(); } class _AnalyticsPageState extends State { @override Widget build(BuildContext context) { // for (var value in avgActs) { // Debug.LogResponse('${value.taskType.name} : ${DateFormat('HH:mm').format(value.startTime)} - ${DateFormat('HH:mm').format(value.endTime)}'); // } return Scaffold( appBar: AppBar(title: Row( children: [FaIcon(FontAwesomeIcons.chartLine),SizedBox(width: 15,), Text("Analytics")], )), drawer: navDrawer(context, 1), body: Container( padding: EdgeInsets.all(8), child:Column( children: [ Card( child: Container( padding: EdgeInsets.symmetric(horizontal: 15, vertical: 5), child: Column( children: [ Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Text('Average Day',style: TextStyle(fontSize: 17)), MaterialButton( color: Colors.green, onPressed: (){ Navigator.of(context).push(MaterialPageRoute(builder: (context)=> AvgDayPage())); }, child: Text('More...'), ) ], ), Divider(), ], ), ) ) ], ) ) ); } } class AnalyticTools{ static double dailyThreshold = 0.9; static int activityGroupingThreshold = 4; static List getAverageDayActs(DateTimeRange range){ int totalDays = range.end.difference(range.start).inDays; Map> avgActs = >{}; Map> acts = >{}; List listToReturn = []; //PASS 1: Split Activities and mark their time range User.activities.forEach((element) { if(element.startTime.isAfter(range.start) && element.endTime.isBefore(range.end)){ //Eligible activity to calculate if(acts.containsKey(element.taskType)){ acts[element.taskType]!.add(DateTimeRange(start: element.startTime, end: element.endTime)); }else{ acts.putIfAbsent(element.taskType, () => [DateTimeRange(start: element.startTime, end: element.endTime)]); } } }); Debug.Log('Analysing ${acts.length} activities'); //PASS 2: Calculate avg Time Range for each Activity acts.forEach((key, value) { if(value.length < totalDays * dailyThreshold){ //Not a frequent Activity, }else{ //Iterate through the time ranges of this activity for (var _actTime in value) { DateTime _start = DateTime(0,0,0,_actTime.start.hour, _actTime.start.minute); DateTime _end = DateTime(0,0,0,_actTime.end.hour, _actTime.end.minute); if(_start.isAfter(_end)){Debug.LogError('${key.name} start after end? : $_start, $_end'); continue;} DateTimeRange actTime = DateTimeRange(start: _start, end: _end); if(!avgActs.containsKey(key)){ avgActs.putIfAbsent(key, () => []); } if(avgActs[key]!.isEmpty){ //No need to check groups. No groups exists. avgActs[key]!.add(AvgActData(actTime, 1, 1)); }else{ //Check for the closest time group bool foundGroup = false; for(int i = 0; i < avgActs[key]!.length; i++){ if(foundGroup){continue;} DateTimeRange avgTime = avgActs[key]![i].avgRange; int count = avgActs[key]![i].count; if(actTime.start.difference(avgTime.start).inHours.abs() < activityGroupingThreshold && actTime.end.difference(avgTime.end).inHours.abs() < activityGroupingThreshold){ foundGroup = true; int curStartMin = (avgTime.start.hour * 60) + avgTime.start.minute; int curEndMin = (avgTime.end.hour * 60) + avgTime.end.minute; int newStartMin = (actTime.start.hour * 60) + actTime.start.minute; int newEndMin = (actTime.end.hour * 60) + actTime.end.minute; int avgStartMin = ((curStartMin + newStartMin)/2).toInt(); int avgEndMin = ((curEndMin + newEndMin)/2).toInt(); Debug.Log('Avged : ${key.name} [$i] : ${avgStartMin} - ${avgEndMin}'); if(avgStartMin > avgEndMin){ //Malfunctioned Debug.LogError('Start is after End? Wut???\nAvged : ${key.name}[$i] : ${avgStartMin} - ${avgEndMin}'); }else { DateTime baseline = DateTime(0, 0, 0, 0, 0); // avgActs[key]!.keys.toList()[i] = // DateTimeRange(start: baseline.add(Duration(minutes: avgStartMin)), end: baseline.add(Duration(minutes: avgEndMin))); // avgActs[key]!.values.toList()[i]++; //avgActs[key]!.removeWhere((key, value) => key==avgTime); avgActs[key]![i].avgRange = DateTimeRange(start: baseline.add(Duration(minutes: avgStartMin)), end: baseline.add(Duration(minutes: avgEndMin))); avgActs[key]![i].count++; } break; } } if(!foundGroup){ avgActs[key]!.add(AvgActData(actTime, 1, 1)); } } } } }); //PASS 3: Prepare the Activity list to return avgActs.forEach((key, value) { value.sort((a,b) => b.count.compareTo(a.count)); value.forEach((element) { Activity thisAct = Activity(key, element.avgRange.start, element.avgRange.end); Debug.LogResponse( '${thisAct.taskType.name} (${element.count}) -> (${element.dailyCountAvg}) : ${DateFormat('HH:mm').format(thisAct.startTime)} - ${DateFormat('HH:mm').format( thisAct.endTime)}'); if(element.count < totalDays * dailyThreshold/2){ Debug.Log('Ignoring due to DailyThreshold, ${element.count} < ${totalDays * dailyThreshold/2}'); }else { listToReturn.add(thisAct); } }); // value.forEach((act, count) { // // }); }); return listToReturn; } } class AvgActData{ AvgActData(this.avgRange, this.count, this.dailyCountAvg); DateTimeRange avgRange; int count; double dailyCountAvg; }