Skip to content

Commit

Permalink
Task statistics page
Browse files Browse the repository at this point in the history
  • Loading branch information
denitdao committed May 1, 2022
1 parent 744a739 commit c546192
Show file tree
Hide file tree
Showing 5 changed files with 139 additions and 17 deletions.
20 changes: 13 additions & 7 deletions lib/pages/teacher/task_statistics_page/task_statistics_cubit.dart
Original file line number Diff line number Diff line change
@@ -1,13 +1,10 @@
import 'package:bloc/bloc.dart';
import 'package:either_dart/either.dart';
import 'package:flutter/foundation.dart';
import 'package:formz/formz.dart';
import 'package:freezed_annotation/freezed_annotation.dart';
import 'package:injectable/injectable.dart';
import 'package:task_manager/core/injection/injection.dart';
import 'package:task_manager/models/enums/external_data_status.dart';
import 'package:task_manager/models/forms/non_empty_date_input.dart';
import 'package:task_manager/models/forms/text_input.dart';
import 'package:task_manager/models/task.dart';
import 'package:task_manager/models/user_task_status.dart';
import 'package:task_manager/repositories/stats_repository.dart';
Expand Down Expand Up @@ -69,10 +66,19 @@ class TaskStatisticsCubit extends Cubit<TaskStatisticsState> {
..sort();

List<dynamic> completionMap = [];
completionMap.add({
'x': state.task!.createdAt.toLocal(),
'y': 0,
});
if (completionDates.isNotEmpty) {
if (state.task!.startDate.isBefore(completionDates.first!)) {
completionMap.add({
'x': state.task!.startDate.toLocal(),
'y': 0,
});
}
} else {
completionMap.add({
'x': state.task!.startDate.toLocal(),
'y': 0,
});
}
completionDates.forEachIndexed((index, date) {
completionMap.add({
'x': date!.toLocal(),
Expand Down
122 changes: 114 additions & 8 deletions lib/pages/teacher/task_statistics_page/task_statistics_page.dart
Original file line number Diff line number Diff line change
@@ -1,18 +1,13 @@
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:formz/formz.dart';
import 'package:intl/intl.dart';
import 'package:syncfusion_flutter_charts/charts.dart';
import 'package:task_manager/constants/supabase_constants.dart';
import 'package:syncfusion_flutter_gauges/gauges.dart';
import 'package:task_manager/core/auth/teacher_auth_required_state.dart';
import 'package:task_manager/core/injection/injection.dart';
import 'package:task_manager/models/enums/external_data_status.dart';
import 'package:task_manager/models/task.dart';
import 'package:task_manager/models/user_task_status.dart';
import 'package:task_manager/pages/teacher/task_edit_page/task_edit_cubit.dart';
import 'package:task_manager/pages/teacher/task_statistics_page/task_statistics_cubit.dart';
import 'package:task_manager/theme/light_color.dart';
import 'package:task_manager/widgets/date_picker.dart';

class TaskStatisticsPage extends StatefulWidget {
const TaskStatisticsPage({Key? key, required this.id}) : super(key: key);
Expand Down Expand Up @@ -86,6 +81,10 @@ class _TaskOverview extends StatelessWidget {
padding: EdgeInsets.fromLTRB(0, 12, 0, 12),
child: _CompletionPerformanceChart(),
),
const Padding(
padding: EdgeInsets.fromLTRB(20, 0, 20, 12),
child: _CompletionPerformanceGauge(),
),
const Padding(
padding: EdgeInsets.fromLTRB(0, 12, 0, 12),
child: Divider(
Expand Down Expand Up @@ -147,21 +146,128 @@ class _CompletionPerformanceChart extends StatelessWidget {
primaryXAxis: DateTimeAxis(
edgeLabelPlacement: EdgeLabelPlacement.shift,
rangePadding: ChartRangePadding.round,
interval: 2,
plotBands: <PlotBand>[
PlotBand(
isVisible: true,
start: state.task!.startDate.toLocal(),
end: state.task!.dueDate.toLocal(),
color: Colors.deepPurple,
opacity: 0.1,
),
],
),
primaryYAxis: NumericAxis(
numberFormat: NumberFormat.percentPattern(),
rangePadding: ChartRangePadding.round,
maximum: 1,
minimum: 0,
axisLine: const AxisLine(width: 0),
majorTickLines: const MajorTickLines(size: 0),
),
series: <ChartSeries>[
LineSeries<dynamic, DateTime>(
dataSource: state.taskPerformance,
xValueMapper: (value, _) => value['x'],
yValueMapper: (value, _) => value['y'],
dataLabelSettings: const DataLabelSettings(isVisible: true),
)
markerSettings: const MarkerSettings(
isVisible: true,
borderWidth: 1,
width: 4,
height: 4,
),
trendlines: state.taskPerformance.length <= 1
? null
: <Trendline>[
Trendline(
color: Colors.green,
type: TrendlineType.linear,
width: 1,
dashArray: <double>[5, 5],
),
],
),
],
tooltipBehavior: TooltipBehavior(header: '', enable: true),
);
},
);
}
}

class _CompletionPerformanceGauge extends StatelessWidget {
const _CompletionPerformanceGauge({Key? key}) : super(key: key);

@override
Widget build(BuildContext context) {
return BlocBuilder<TaskStatisticsCubit, TaskStatisticsState>(
builder: (context, state) {
return SfLinearGauge(
animateAxis: true,
showLabels: false,
showTicks: false,
minimum: 0.0,
maximum: 100.0,
orientation: LinearGaugeOrientation.horizontal,
markerPointers: <LinearMarkerPointer>[
LinearShapePointer(
value: _findLinearProgress(state.task!, state.taskPerformance),
height: 10,
width: 10,
position: LinearElementPosition.cross,
shapeType: LinearShapePointerType.circle),
],
ranges: const <LinearGaugeRange>[
LinearGaugeRange(
startValue: 0,
endValue: 33,
position: LinearElementPosition.cross,
color: Colors.redAccent,
),
LinearGaugeRange(
startValue: 33,
endValue: 66,
position: LinearElementPosition.cross,
color: Colors.orangeAccent,
),
LinearGaugeRange(
startValue: 66,
endValue: 100,
position: LinearElementPosition.cross,
color: Colors.lightGreen,
),
],
);
},
);
}

double _findLinearProgress(Task task, List<dynamic> dates) {
if (task.studentsOverall == 0 || dates.isEmpty) {
return 50;
}
var timeRange = task.dueDate.difference(task.startDate);
var timeLeft = task.dueDate.difference(DateTime.now());

var timeRatio = timeLeft.inMilliseconds / timeRange.inMilliseconds * 100;
var taskRatio =
(task.studentsOverall - task.completedBy) / task.studentsOverall * 100;

if (timeLeft.inMilliseconds > 0) { // not over
if (task.studentsOverall == task.completedBy) {
return 100;
} else {
return 75 + timeRatio - taskRatio;
}
} else { // over due date
if (task.studentsOverall == task.completedBy) {
var timeToLastAfterDeadline = task.dueDate.difference(dates.last['x']);
var timeLastAfterDeadlineRatio = timeToLastAfterDeadline.inMilliseconds / timeRange.inMilliseconds * -100;

return 100 - timeLastAfterDeadlineRatio;
} else {
return 33 - taskRatio;
}
}
}
}
4 changes: 3 additions & 1 deletion lib/repositories/task_repository.dart
Original file line number Diff line number Diff line change
Expand Up @@ -160,10 +160,12 @@ class TaskRepository {

Future<Either<String, List<Task>>> getAllTasksByStudentInRange(
String studentId, int range) async {
var now = DateTime.now();
var finalDay = DateTime(now.year, now.month, now.day).add(Duration(days: range - 1));
final response = await supabase
.rpc('get_all_tasks_by_student', params: {'student_id': studentId})
.eq('deleted', false)
.lte('due_date', DateTime.now().add(Duration(days: range)))
.lte('due_date', finalDay)
.order('due_date', ascending: true)
.execute();

Expand Down
9 changes: 8 additions & 1 deletion pubspec.lock
Original file line number Diff line number Diff line change
Expand Up @@ -736,7 +736,14 @@ packages:
name: syncfusion_flutter_core
url: "https://pub.dartlang.org"
source: hosted
version: "20.1.50"
version: "20.1.51"
syncfusion_flutter_gauges:
dependency: "direct main"
description:
name: syncfusion_flutter_gauges
url: "https://pub.dartlang.org"
source: hosted
version: "20.1.51"
term_glyph:
dependency: transitive
description:
Expand Down
1 change: 1 addition & 0 deletions pubspec.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ dependencies:
equatable: ^2.0.3
formz: ^0.4.1
syncfusion_flutter_charts: ^20.1.50
syncfusion_flutter_gauges: ^20.1.51

dev_dependencies:
flutter_test:
Expand Down

0 comments on commit c546192

Please sign in to comment.