Skip to content

Commit

Permalink
Merge pull request #69 from IamMuuo/dev
Browse files Browse the repository at this point in the history
Exam timetable
  • Loading branch information
IamMuuo authored Apr 6, 2024
2 parents 027e3eb + 1a0624d commit 946d5d7
Show file tree
Hide file tree
Showing 11 changed files with 698 additions and 68 deletions.
6 changes: 4 additions & 2 deletions lib/constants/common.dart
Original file line number Diff line number Diff line change
Expand Up @@ -82,12 +82,14 @@ void showCustomSnackbar(
String message, {
IconData? icon,
Color? iconColor,
Color? backgroundColor = Colors.white,
Color? colorText = Colors.black,
}) {
Get.snackbar(
title,
message,
colorText: Colors.black,
backgroundColor: Colors.white,
colorText: colorText,
backgroundColor: backgroundColor,
icon: Icon(icon ?? Icons.info),
);
}
23 changes: 12 additions & 11 deletions lib/constants/tools.dart
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,18 @@ import 'package:get/get.dart';
import 'package:academia/exports/barrel.dart';

final List<Map<String, dynamic>> allTools = [
{
"id": 8,
"name": "Exam Timetable",
"action": "Show exam timetable",
"image": "assets/images/exam_timetable.png",
"ontap": () {
Get.to(const ExamTimeTablePage());
},
"description":
"Exams around the corner? Don't panic we've got you covered with the timetable",
},

{
"id": 1,
"name": "GPA Calculator",
Expand Down Expand Up @@ -139,17 +151,6 @@ final List<Map<String, dynamic>> allTools = [
"description":
"Curious to know how many classes you have missed this semester, this might be the tool",
},
{
"id": 8,
"name": "Exam Timetable",
"action": "Show exam timetable",
"image": "assets/images/exam_timetable.png",
"ontap": () {
Get.to(const ExamTimeTablePage());
},
"description":
"Exams around the corner? Don't panic we've got you covered with the timetable",
},
{
"id": 9,
"name": "Task Manager",
Expand Down
2 changes: 2 additions & 0 deletions lib/exports/barrel.dart
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
export 'package:flutter/material.dart';
export 'package:academia/models/exam.dart';
export 'package:academia/constants/common.dart';
export 'package:ionicons/ionicons.dart';
export 'package:academia/widgets/academia_app_bar.dart';
Expand Down Expand Up @@ -53,6 +54,7 @@ export 'package:academia/pages/tasks_pages/edittask_page.dart';
export 'package:academia/pages/tasks_pages/newtask_page.dart';
export 'package:academia/pages/tasks_pages/taskinfo_page.dart';
export 'package:magnet/src/magnet_utils.dart';
export 'package:magnet/src/magnet_exams.dart';
export 'package:academia/tools/time_line/time_line_page.dart';
export 'package:academia/tools/stories/notifications_story_page.dart';
export 'package:academia/controllers/controllers.dart';
1 change: 1 addition & 0 deletions lib/main.dart
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ void main() async {
Hive.registerAdapter(ScheduleAdapter());
Hive.registerAdapter(CoursesAdapter());
Hive.registerAdapter(TaskAdapter());
Hive.registerAdapter(ExamAdapter());
appDB = await Hive.openBox(dbName);

// Init settings controller
Expand Down
66 changes: 66 additions & 0 deletions lib/models/exam.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
import 'package:hive/hive.dart';

part 'exam.g.dart'; // This file will be generated by the build_runner

@HiveType(typeId: 4)
class Exam {
@HiveField(0)
String courseCode;

@HiveField(1)
String day;

@HiveField(2)
String time;

@HiveField(3)
String venue;

@HiveField(4)
String hrs;

@HiveField(5)
String? invigilator;

@HiveField(6)
String? coordinator;

@HiveField(7)
String? campus;

Exam({
required this.courseCode,
required this.day,
required this.time,
required this.venue,
required this.hrs,
this.invigilator,
this.coordinator,
this.campus,
});

// Factory constructor to create an Exam object from a Map
factory Exam.fromJson(Map<String, dynamic> json) {
return Exam(
courseCode: json['course_code'],
day: json['day'],
time: json['time'],
venue: json['venue'],
hrs: json['hrs'],
invigilator: json['invigilator'],
coordinator: json['coordinator'],
campus: json['campus'],
);
}

// Method to convert an Exam object into a Map
Map<String, dynamic> toJson() {
return {
'course_code': courseCode,
'day': day,
'time': time,
'venue': venue,
'hrs': hrs,
};
}
}
62 changes: 62 additions & 0 deletions lib/models/exam.g.dart

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

160 changes: 119 additions & 41 deletions lib/tools/exam_timetable/exams_timetable_controller.dart
Original file line number Diff line number Diff line change
@@ -1,61 +1,139 @@
import 'package:academia/constants/common.dart';
import 'package:academia/models/courses.dart';
import 'package:flutter/material.dart';
import 'package:academia/exports/barrel.dart';
import 'package:get/get.dart';
import 'package:intl/intl.dart';

class ExamsTimeTableController extends GetxController {
var index = (-1).obs;
var hasExams = false.obs;
var isLoading = false.obs;

Future<List<dynamic>> fetchExamTimeTable(String units,
{bool athi = true}) async {
isLoading.value = true;
late List<Map<String, dynamic>> quotes = [];
List<Exam> exams = [];

Future<void> fetchRandomQuote() async {
try {
var fetchedUnits = await magnet.fetchExamTimeTabale(units);
isLoading.value = false;

return fetchedUnits;
quotes = await magnet.fetchRandomQuotes();
index.value = 0;
} catch (e) {
Get.snackbar(
"Oh Snap!",
"Something went wrong while attempting to fetch your exam timetable, please check your network connection and try again",
icon: const Icon(
Icons.network_ping,
),
maxWidth: 500,
showCustomSnackbar(
"Error",
e.toString(),
colorText: Colors.red,
backgroundColor: Colors.grey,
);
debugPrint(e.toString());
}
}

isLoading.value = false;
return [];
void nextQuote() {
if (quotes.isNotEmpty && index.value < 49) {
index.value++;
} else if (index.value == 49) {
fetchRandomQuote().then((value) => value); // Do nothing
}
}

Future<void> addFetchedUnits(List<dynamic> fetchedUnits) async {
await appDB.put("exam_timetable", fetchedUnits);
void previousQuote() {
if (quotes.isNotEmpty && index.value > 0) {
index.value--;
} else if (index.value == 0) {
fetchRandomQuote().then((value) => value); // Do nothing
}
}

@override
void onInit() async {
debugPrint(appDB.get("exam_timetable").toString());

if (true) {
var courses = appDB.get("timetable");
String payload = "";
for (Courses c in courses) {
payload =
"$payload ${c.name!.replaceAll("-", " ")}${c.section!.split('-')[0]},";
}

debugPrint(payload);
var fetchedUnits = await fetchExamTimeTable(payload.trim());
await addFetchedUnits(fetchedUnits);
hasExams.value = fetchedUnits.isNotEmpty;
Future<List<Exam>> fetchExams(List<String> units) async {
final examData = (await magnet.fetchExam(units))
.map((e) => Exam.fromJson(e))
.toList()
.cast<Exam>();

examData.sort((a, b) {
final formatter = DateFormat('EEEE dd/MM/yy');
final aDate = formatter.parse(a.day.title());
final bDate = formatter.parse(b.day.title());

// Compare the dates first
final dateComparison = aDate.compareTo(bDate);
if (dateComparison != 0) return dateComparison;

// If the dates are the same, compare the times
final aTimeRange = a.time.split('-');
final bTimeRange = b.time.split('-');
final aStartTime = DateFormat('h:mma').parse(aTimeRange[0]);
final bStartTime = DateFormat('h:mma').parse(bTimeRange[0]);

return aStartTime.compareTo(bStartTime);
});

return examData;
}

Future<void> addExamToStorage(Exam exam) async {
exams = await appDB.get("exams").toList().cast<Exam>();
exams.add(exam);
exams.sort((a, b) {
final formatter = DateFormat('EEEE dd/MM/yy');
final aDate = formatter.parse(a.day.title());
final bDate = formatter.parse(b.day.title());

// Compare the dates first
final dateComparison = aDate.compareTo(bDate);
if (dateComparison != 0) return dateComparison;

// If the dates are the same, compare the times
final aTimeRange = a.time.split('-');
final bTimeRange = b.time.split('-');
final aStartTime = DateFormat('h:mma').parse(aTimeRange[0]);
final bStartTime = DateFormat('h:mma').parse(bTimeRange[0]);

return aStartTime.compareTo(bStartTime);
});

await appDB.put("exams", exams);

// trigger a data refersh
hasExams.value = false;
hasExams.value = true;
}

Future<void> removeExamFromStorage(Exam exam) async {
exams = await appDB.get("exams").toList().cast<Exam>();
exams.remove(exam);

if (exams.isEmpty) {
await appDB.delete("exams");
// trigger a data refersh
hasExams.value = false;
} else {
await appDB.put("exams", exams);
// trigger a data refersh
hasExams.value = false;
hasExams.value = true;
}
}

debugPrint("do I have exams : ${hasExams.value}");
@override
Future<void> onInit() async {
await fetchRandomQuote();
hasExams.value = false;

// Check if the local database has exams
if (appDB.containsKey("exams")) {
hasExams.value = true;
// load the exams
exams = await appDB.get("exams").toList().cast<Exam>();
} else {
// load the units
final List<Courses> courses =
await appDB.get("timetable").toList().cast<Courses>();
List<String> courseTitles = courses
.map((e) =>
"${e.name?.replaceAll('-', '')}${e.section?.split('-')[0]}")
.toList();
print(courseTitles);

// fetch from server
exams = await fetchExams(courseTitles);
await appDB.put("exams", exams);
hasExams.value = exams.isNotEmpty;
}
super.onInit();
}
}
Loading

0 comments on commit 946d5d7

Please sign in to comment.