forked from firka/firka
Refactor logging in live activity manager and backend client; streamline lesson data handling and improve break event detection logic
This commit is contained in:
@@ -73,7 +73,9 @@ class LiveActivityBackendClient {
|
||||
|
||||
_logger.info('Registering device with backend. Sending ${lessonsData.length} lessons.');
|
||||
if (_logger.isLoggable(Level.FINE)) {
|
||||
lessonsData.forEach((lesson) => _logger.fine(' Lesson data: ${lesson}'));
|
||||
for (var lesson in lessonsData) {
|
||||
_logger.fine(' Lesson data: $lesson');
|
||||
}
|
||||
}
|
||||
|
||||
final response = await _dio.post(
|
||||
@@ -126,7 +128,9 @@ class LiveActivityBackendClient {
|
||||
|
||||
_logger.info('Updating timetable with backend. Sending ${lessonsData.length} lessons.');
|
||||
if (_logger.isLoggable(Level.FINE)) {
|
||||
lessonsData.forEach((lesson) => _logger.fine(' Lesson data: ${lesson}'));
|
||||
for (var lesson in lessonsData) {
|
||||
_logger.fine(' Lesson data: $lesson');
|
||||
}
|
||||
}
|
||||
|
||||
final requestData = {
|
||||
|
||||
@@ -4,15 +4,6 @@ import 'package:flutter/services.dart';
|
||||
import 'package:firka/helpers/api/model/timetable.dart';
|
||||
import 'package:logging/logging.dart';
|
||||
|
||||
class _ActivityState {
|
||||
final Lesson? currentLesson;
|
||||
final Lesson? nextLesson;
|
||||
final bool isBreak;
|
||||
final String? mode;
|
||||
|
||||
_ActivityState({this.currentLesson, this.nextLesson, this.isBreak = false, this.mode});
|
||||
}
|
||||
|
||||
class LiveActivityManager {
|
||||
static const MethodChannel _channel = MethodChannel('firka.app/live_activity');
|
||||
static final Logger _logger = Logger('LiveActivityManager');
|
||||
@@ -244,179 +235,4 @@ class LiveActivityManager {
|
||||
};
|
||||
return payload;
|
||||
}
|
||||
|
||||
static _ActivityState _findCurrentActivityState(List<Lesson> lessons, DateTime now) {
|
||||
lessons.sort((a, b) => a.start.compareTo(b.start));
|
||||
|
||||
for (int i = 0; i < lessons.length; i++) {
|
||||
final lesson = lessons[i];
|
||||
final lessonStart = DateTime(now.year, now.month, now.day, lesson.start.hour, lesson.start.minute);
|
||||
final lessonEnd = DateTime(now.year, now.month, now.day, lesson.end.hour, lesson.end.minute);
|
||||
|
||||
if (i == 0 && now.isBefore(lessonStart)) {
|
||||
if (lessonStart.difference(now).inHours < 2) {
|
||||
return _ActivityState(currentLesson: lesson, mode: 'beforeSchool');
|
||||
}
|
||||
}
|
||||
|
||||
if (now.isAfter(lessonStart) && now.isBefore(lessonEnd)) {
|
||||
final correctedLesson = Lesson(
|
||||
uid: lesson.uid,
|
||||
date: lesson.date,
|
||||
start: lessonStart,
|
||||
end: lessonEnd,
|
||||
name: lesson.name,
|
||||
type: lesson.type,
|
||||
state: lesson.state,
|
||||
lessonNumber: lesson.lessonNumber,
|
||||
roomName: lesson.roomName,
|
||||
teacher: lesson.teacher,
|
||||
theme: lesson.theme,
|
||||
substituteTeacher: lesson.substituteTeacher,
|
||||
canStudentEditHomework: lesson.canStudentEditHomework,
|
||||
isHomeworkComplete: lesson.isHomeworkComplete,
|
||||
attachments: lesson.attachments,
|
||||
isDigitalLesson: lesson.isDigitalLesson,
|
||||
digitalSupportDeviceTypeList: lesson.digitalSupportDeviceTypeList,
|
||||
createdAt: lesson.createdAt,
|
||||
lastModifiedAt: lesson.lastModifiedAt
|
||||
);
|
||||
|
||||
final nextLesson = i + 1 < lessons.length ? lessons[i + 1] : null;
|
||||
Lesson? correctedNextLesson;
|
||||
if (nextLesson != null) {
|
||||
correctedNextLesson = Lesson(
|
||||
uid: nextLesson.uid, date: nextLesson.date,
|
||||
start: DateTime(now.year, now.month, now.day, nextLesson.start.hour, nextLesson.start.minute),
|
||||
end: DateTime(now.year, now.month, now.day, nextLesson.end.hour, nextLesson.end.minute),
|
||||
name: nextLesson.name, type: nextLesson.type, state: nextLesson.state,
|
||||
lessonNumber: nextLesson.lessonNumber, roomName: nextLesson.roomName, teacher: nextLesson.teacher,
|
||||
theme: nextLesson.theme, substituteTeacher: nextLesson.substituteTeacher,
|
||||
canStudentEditHomework: nextLesson.canStudentEditHomework, isHomeworkComplete: nextLesson.isHomeworkComplete,
|
||||
attachments: nextLesson.attachments, isDigitalLesson: nextLesson.isDigitalLesson,
|
||||
digitalSupportDeviceTypeList: nextLesson.digitalSupportDeviceTypeList,
|
||||
createdAt: nextLesson.createdAt, lastModifiedAt: nextLesson.lastModifiedAt
|
||||
);
|
||||
}
|
||||
|
||||
return _ActivityState(
|
||||
currentLesson: correctedLesson,
|
||||
nextLesson: correctedNextLesson,
|
||||
);
|
||||
}
|
||||
|
||||
if (i + 1 < lessons.length) {
|
||||
final nextLesson = lessons[i + 1];
|
||||
final nextLessonStart = DateTime(now.year, now.month, now.day, nextLesson.start.hour, nextLesson.start.minute);
|
||||
if (now.isAfter(lessonEnd) && now.isBefore(nextLessonStart)) {
|
||||
final breakLesson = Lesson(
|
||||
uid: 'break-${lesson.uid}',
|
||||
date: lesson.date,
|
||||
start: lessonEnd,
|
||||
end: nextLessonStart,
|
||||
name: 'Szünet',
|
||||
type: lesson.type,
|
||||
state: lesson.state,
|
||||
canStudentEditHomework: false,
|
||||
isHomeworkComplete: false,
|
||||
attachments: [],
|
||||
isDigitalLesson: false,
|
||||
digitalSupportDeviceTypeList: [],
|
||||
createdAt: now,
|
||||
lastModifiedAt: now,
|
||||
);
|
||||
return _ActivityState(currentLesson: breakLesson, nextLesson: nextLesson, isBreak: true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (lessons.isNotEmpty) {
|
||||
final lastLesson = lessons.last;
|
||||
final lastLessonEnd = DateTime(now.year, now.month, now.day, lastLesson.end.hour, lastLesson.end.minute);
|
||||
final afterSchoolBreakEnd = lastLessonEnd.add(const Duration(minutes: 15));
|
||||
|
||||
if (now.isAfter(lastLessonEnd) && now.isBefore(afterSchoolBreakEnd)) {
|
||||
final breakLesson = Lesson(
|
||||
uid: 'break-after-school',
|
||||
date: lastLesson.date,
|
||||
start: lastLessonEnd,
|
||||
end: afterSchoolBreakEnd,
|
||||
name: 'Szünet',
|
||||
type: lastLesson.type,
|
||||
state: lastLesson.state,
|
||||
canStudentEditHomework: false,
|
||||
isHomeworkComplete: false,
|
||||
attachments: [],
|
||||
isDigitalLesson: false,
|
||||
digitalSupportDeviceTypeList: [],
|
||||
createdAt: now,
|
||||
lastModifiedAt: now,
|
||||
);
|
||||
return _ActivityState(currentLesson: breakLesson, isBreak: true);
|
||||
}
|
||||
}
|
||||
|
||||
return _ActivityState();
|
||||
}
|
||||
|
||||
static Future<void> updateActivityFromTimetable(
|
||||
List<Lesson> todayLessons,
|
||||
String studentName,
|
||||
String schoolName,
|
||||
) async {
|
||||
if (!Platform.isIOS) return;
|
||||
|
||||
final now = DateTime.now();
|
||||
_logger.info("Checking for activity update at ${now.toIso8601String()}");
|
||||
|
||||
final lessons = todayLessons.where((lesson) {
|
||||
return !(lesson.state.name?.toLowerCase().contains('törölt') ?? false);
|
||||
}).toList();
|
||||
|
||||
if (lessons.isEmpty) {
|
||||
_logger.info("No lessons today. Backend will control activity state.");
|
||||
return;
|
||||
}
|
||||
|
||||
final schoolLessons = lessons.where((lesson) {
|
||||
final type = lesson.type.name?.toLowerCase() ?? '';
|
||||
return !type.contains('tanevrendje');
|
||||
}).toList();
|
||||
|
||||
if (schoolLessons.isEmpty) {
|
||||
_logger.info("No school lessons today (only break events). Backend will control activity state.");
|
||||
return;
|
||||
}
|
||||
|
||||
final state = _findCurrentActivityState(schoolLessons, now);
|
||||
|
||||
_logger.info("Current state: lesson=${state.currentLesson?.name}, break=${state.isBreak}, mode=${state.mode}");
|
||||
|
||||
await _syncActivityState();
|
||||
|
||||
if (state.currentLesson != null) {
|
||||
if (_isActivityActive) {
|
||||
_logger.info("Updating existing activity.");
|
||||
await updateActivity(
|
||||
currentLesson: state.currentLesson!,
|
||||
nextLesson: state.nextLesson,
|
||||
isBreak: state.isBreak,
|
||||
mode: state.mode,
|
||||
);
|
||||
} else {
|
||||
_logger.info("Starting new activity.");
|
||||
await startActivity(
|
||||
studentName: studentName,
|
||||
schoolName: schoolName,
|
||||
currentLesson: state.currentLesson!,
|
||||
nextLesson: state.nextLesson,
|
||||
isBreak: state.isBreak,
|
||||
mode: state.mode,
|
||||
);
|
||||
}
|
||||
} else {
|
||||
_logger.info("No current lesson or break. Ending activity if running.");
|
||||
await endAllActivities();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -44,7 +44,7 @@ class LiveActivityService {
|
||||
/// Get current bellDelay value from settings
|
||||
static double? _getCurrentBellDelay() {
|
||||
try {
|
||||
if (!initDone || initData.settings == null) {
|
||||
if (!initDone) {
|
||||
return null;
|
||||
}
|
||||
final bellDelaySetting = initData.settings.group("settings")
|
||||
@@ -60,10 +60,10 @@ class LiveActivityService {
|
||||
/// If client is provided, use it directly instead of initData.client
|
||||
static String? _getCurrentStudentId({KretaClient? client}) {
|
||||
try {
|
||||
if (client != null && client.model != null) {
|
||||
if (client != null) {
|
||||
return client.model.studentId;
|
||||
}
|
||||
if (!initDone || initData.client == null || initData.client.model == null) {
|
||||
if (!initDone) {
|
||||
return null;
|
||||
}
|
||||
return initData.client.model.studentId;
|
||||
@@ -149,7 +149,7 @@ class LiveActivityService {
|
||||
/// Get current language code from settings
|
||||
static String? _getCurrentLanguageCode() {
|
||||
try {
|
||||
if (!initDone || initData.settings == null) {
|
||||
if (!initDone) {
|
||||
return 'hu';
|
||||
}
|
||||
|
||||
@@ -281,7 +281,7 @@ class LiveActivityService {
|
||||
static bool _hasRemainingLessonsToday(List<Lesson> lessons) {
|
||||
final now = DateTime.now();
|
||||
final todayLessons = lessons.where((lesson) {
|
||||
final uid = lesson.uid?.toLowerCase() ?? '';
|
||||
final uid = lesson.uid.toLowerCase();
|
||||
return lesson.date == now.toIso8601String().split('T').first &&
|
||||
lesson.end.isAfter(now) &&
|
||||
(uid.contains('orarendiora') ||
|
||||
@@ -302,10 +302,6 @@ class LiveActivityService {
|
||||
|
||||
try {
|
||||
final client = initData.client;
|
||||
if (client == null || client.model == null) {
|
||||
_logger.warning('Background fetch skipped: no client available');
|
||||
return false;
|
||||
}
|
||||
|
||||
final enabled = await isEnabled(initData.settings, client);
|
||||
if (!enabled) {
|
||||
@@ -389,7 +385,7 @@ class LiveActivityService {
|
||||
attachments: firstLesson.attachments,
|
||||
isDigitalLesson: firstLesson.isDigitalLesson,
|
||||
digitalSupportDeviceTypeList: firstLesson.digitalSupportDeviceTypeList,
|
||||
createdAt: firstLesson.createdAt ?? firstLesson.lastModifiedAt ?? DateTime.now(),
|
||||
createdAt: firstLesson.createdAt,
|
||||
lastModifiedAt: firstLesson.lastModifiedAt,
|
||||
);
|
||||
|
||||
@@ -695,69 +691,136 @@ class LiveActivityService {
|
||||
return;
|
||||
}
|
||||
|
||||
_logger.info('Searching for first school day of next week');
|
||||
final nextWeekStart = endOfWeek.add(const Duration(days: 1));
|
||||
final nextWeekEnd = nextWeekStart.add(const Duration(days: 6));
|
||||
|
||||
bool foundFirstSchoolDay = false;
|
||||
for (int dayOffset = 1; dayOffset <= 5; dayOffset++) {
|
||||
final candidateDay = endOfWeek.add(Duration(days: dayOffset));
|
||||
_logger.info('[GlobalSearch] onUserLogin: Checking next week (${nextWeekStart.toString().split(' ')[0]} - ${nextWeekEnd.toString().split(' ')[0]}) for events');
|
||||
|
||||
if (candidateDay.weekday == DateTime.saturday || candidateDay.weekday == DateTime.sunday) {
|
||||
continue;
|
||||
}
|
||||
List<Lesson> nextWeekBreakEvents = [];
|
||||
try {
|
||||
final nextWeekResponse = await client.getTimeTable(nextWeekStart, nextWeekEnd, forceCache: false);
|
||||
|
||||
try {
|
||||
final candidateDayEnd = candidateDay.add(const Duration(days: 1));
|
||||
final response = await client.getTimeTable(candidateDay, candidateDayEnd, forceCache: false);
|
||||
if (nextWeekResponse.response != null) {
|
||||
nextWeekBreakEvents = nextWeekResponse.response!.where((lesson) {
|
||||
final uid = lesson.uid.toLowerCase();
|
||||
final name = lesson.name.toLowerCase();
|
||||
return uid.contains('tanevrendjeesemeny') &&
|
||||
!name.contains('tanítási nap') &&
|
||||
(name.contains('szünet') ||
|
||||
name.contains('pihenőnap') ||
|
||||
name.contains('munkaszüneti') ||
|
||||
name.contains('ünnepnap') ||
|
||||
name.contains('tanítás nélküli') ||
|
||||
name.contains('nem órarendi nap'));
|
||||
}).toList();
|
||||
|
||||
if (response.response != null && response.response!.isNotEmpty) {
|
||||
final schoolLessons = response.response!.where((lesson) {
|
||||
final uid = lesson.uid.toLowerCase();
|
||||
return uid.contains('orarendiora') || uid.contains('tanitasiora') || uid.contains('uresora');
|
||||
}).toList();
|
||||
|
||||
if (schoolLessons.isNotEmpty) {
|
||||
schoolLessons.sort((a, b) => a.start.compareTo(b.start));
|
||||
final firstLesson = schoolLessons.first;
|
||||
|
||||
final notificationLesson = Lesson(
|
||||
uid: '${firstLesson.uid}__FOR_NOTIFICATION_ONLY',
|
||||
date: firstLesson.date,
|
||||
start: firstLesson.start,
|
||||
end: firstLesson.end,
|
||||
name: firstLesson.name,
|
||||
lessonNumber: firstLesson.lessonNumber,
|
||||
teacher: firstLesson.teacher,
|
||||
theme: firstLesson.theme,
|
||||
roomName: firstLesson.roomName,
|
||||
substituteTeacher: firstLesson.substituteTeacher,
|
||||
type: firstLesson.type,
|
||||
state: firstLesson.state,
|
||||
canStudentEditHomework: firstLesson.canStudentEditHomework,
|
||||
isHomeworkComplete: firstLesson.isHomeworkComplete,
|
||||
attachments: firstLesson.attachments,
|
||||
isDigitalLesson: firstLesson.isDigitalLesson,
|
||||
digitalSupportDeviceTypeList: firstLesson.digitalSupportDeviceTypeList,
|
||||
createdAt: firstLesson.createdAt ?? firstLesson.lastModifiedAt ?? DateTime.now(),
|
||||
lastModifiedAt: firstLesson.lastModifiedAt,
|
||||
);
|
||||
|
||||
allLessons.add(notificationLesson);
|
||||
|
||||
const dayNames = ['Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday'];
|
||||
final dayName = dayNames[candidateDay.weekday - 1];
|
||||
_logger.info('Added first lesson from next week $dayName (${firstLesson.name}) marked for notification scheduling only');
|
||||
|
||||
foundFirstSchoolDay = true;
|
||||
break;
|
||||
}
|
||||
if (nextWeekBreakEvents.isNotEmpty) {
|
||||
_logger.info('[GlobalSearch] onUserLogin: Found ${nextWeekBreakEvents.length} break event(s) in next week - triggering global searcher');
|
||||
}
|
||||
} catch (e) {
|
||||
_logger.warning('Failed to fetch lessons for day offset $dayOffset: $e');
|
||||
}
|
||||
} catch (e) {
|
||||
_logger.warning('[GlobalSearch] onUserLogin: Could not fetch next week: $e');
|
||||
}
|
||||
|
||||
if (!foundFirstSchoolDay) {
|
||||
_logger.info('No school lessons found in next week for push notification scheduling');
|
||||
if (nextWeekBreakEvents.isNotEmpty) {
|
||||
nextWeekBreakEvents.sort((a, b) => a.start.compareTo(b.start));
|
||||
final firstBreakEvent = nextWeekBreakEvents.first;
|
||||
|
||||
_logger.info('[GlobalSearch] onUserLogin: Triggering global break searcher from ${firstBreakEvent.date.split('T')[0]}');
|
||||
|
||||
final searchResult = await _globalBreakSearcher(
|
||||
client: client,
|
||||
searchStartDate: nextWeekStart,
|
||||
);
|
||||
|
||||
allLessons.addAll(searchResult.allLessons);
|
||||
|
||||
_logger.info('[GlobalSearch] onUserLogin: Global searcher returned ${searchResult.allLessons.length} lessons');
|
||||
|
||||
if (searchResult.firstSchoolDayAfterBreak != null) {
|
||||
final notificationLesson = Lesson(
|
||||
uid: '${searchResult.firstSchoolDayAfterBreak!.uid}__FOR_NOTIFICATION_ONLY',
|
||||
date: searchResult.firstSchoolDayAfterBreak!.date,
|
||||
start: searchResult.firstSchoolDayAfterBreak!.start,
|
||||
end: searchResult.firstSchoolDayAfterBreak!.end,
|
||||
name: searchResult.firstSchoolDayAfterBreak!.name,
|
||||
lessonNumber: searchResult.firstSchoolDayAfterBreak!.lessonNumber,
|
||||
teacher: searchResult.firstSchoolDayAfterBreak!.teacher,
|
||||
theme: searchResult.firstSchoolDayAfterBreak!.theme,
|
||||
roomName: searchResult.firstSchoolDayAfterBreak!.roomName,
|
||||
substituteTeacher: searchResult.firstSchoolDayAfterBreak!.substituteTeacher,
|
||||
type: searchResult.firstSchoolDayAfterBreak!.type,
|
||||
state: searchResult.firstSchoolDayAfterBreak!.state,
|
||||
canStudentEditHomework: searchResult.firstSchoolDayAfterBreak!.canStudentEditHomework,
|
||||
isHomeworkComplete: searchResult.firstSchoolDayAfterBreak!.isHomeworkComplete,
|
||||
attachments: searchResult.firstSchoolDayAfterBreak!.attachments,
|
||||
isDigitalLesson: searchResult.firstSchoolDayAfterBreak!.isDigitalLesson,
|
||||
digitalSupportDeviceTypeList: searchResult.firstSchoolDayAfterBreak!.digitalSupportDeviceTypeList,
|
||||
createdAt: searchResult.firstSchoolDayAfterBreak!.createdAt,
|
||||
lastModifiedAt: searchResult.firstSchoolDayAfterBreak!.lastModifiedAt,
|
||||
);
|
||||
|
||||
allLessons.add(notificationLesson);
|
||||
_logger.info('[GlobalSearch] onUserLogin: Added first school day after break for push notification: ${notificationLesson.date.split('T')[0]}');
|
||||
}
|
||||
} else {
|
||||
_logger.info('[GlobalSearch] onUserLogin: No break events in next week, searching for first school day...');
|
||||
|
||||
bool foundFirstSchoolDay = false;
|
||||
for (int dayOffset = 1; dayOffset <= 14; dayOffset++) {
|
||||
final candidateDay = endOfWeek.add(Duration(days: dayOffset));
|
||||
|
||||
try {
|
||||
final candidateDayEnd = candidateDay.add(const Duration(days: 1));
|
||||
final response = await client.getTimeTable(candidateDay, candidateDayEnd, forceCache: false);
|
||||
|
||||
if (response.response != null && response.response!.isNotEmpty) {
|
||||
final schoolLessons = response.response!.where((lesson) {
|
||||
final uid = lesson.uid.toLowerCase();
|
||||
return uid.contains('orarendiora') || uid.contains('tanitasiora') || uid.contains('uresora');
|
||||
}).toList();
|
||||
|
||||
if (schoolLessons.isNotEmpty) {
|
||||
schoolLessons.sort((a, b) => a.start.compareTo(b.start));
|
||||
final firstLesson = schoolLessons.first;
|
||||
|
||||
final markedLesson = Lesson(
|
||||
uid: '${firstLesson.uid}__FOR_NOTIFICATION_ONLY',
|
||||
date: firstLesson.date,
|
||||
start: firstLesson.start,
|
||||
end: firstLesson.end,
|
||||
name: firstLesson.name,
|
||||
lessonNumber: firstLesson.lessonNumber,
|
||||
teacher: firstLesson.teacher,
|
||||
theme: firstLesson.theme,
|
||||
roomName: firstLesson.roomName,
|
||||
substituteTeacher: firstLesson.substituteTeacher,
|
||||
type: firstLesson.type,
|
||||
state: firstLesson.state,
|
||||
canStudentEditHomework: firstLesson.canStudentEditHomework,
|
||||
isHomeworkComplete: firstLesson.isHomeworkComplete,
|
||||
attachments: firstLesson.attachments,
|
||||
isDigitalLesson: firstLesson.isDigitalLesson,
|
||||
digitalSupportDeviceTypeList: firstLesson.digitalSupportDeviceTypeList,
|
||||
createdAt: firstLesson.createdAt,
|
||||
lastModifiedAt: firstLesson.lastModifiedAt,
|
||||
);
|
||||
|
||||
allLessons.add(markedLesson);
|
||||
_logger.info('[GlobalSearch] onUserLogin: Found first school day for push notification: ${candidateDay.toString().split(' ')[0]}');
|
||||
|
||||
foundFirstSchoolDay = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
_logger.warning('[GlobalSearch] onUserLogin: Could not fetch day offset $dayOffset: $e');
|
||||
}
|
||||
}
|
||||
|
||||
if (!foundFirstSchoolDay) {
|
||||
_logger.info('[GlobalSearch] onUserLogin: No school lessons found in next 14 days for push notification scheduling');
|
||||
}
|
||||
}
|
||||
|
||||
final deviceToken = await _getOrWaitDeviceToken();
|
||||
@@ -933,201 +996,136 @@ class LiveActivityService {
|
||||
}
|
||||
}
|
||||
|
||||
bool foundFirstSchoolDay = false;
|
||||
for (int dayOffset = 1; dayOffset <= 5; dayOffset++) {
|
||||
final candidateDay = endOfWeek.add(Duration(days: dayOffset));
|
||||
final nextWeekStart = endOfWeek.add(const Duration(days: 1));
|
||||
final nextWeekEnd = nextWeekStart.add(const Duration(days: 6));
|
||||
|
||||
if (candidateDay.weekday == DateTime.saturday || candidateDay.weekday == DateTime.sunday) {
|
||||
continue;
|
||||
}
|
||||
_logger.info('[GlobalSearch] Checking next week (${nextWeekStart.toString().split(' ')[0]} - ${nextWeekEnd.toString().split(' ')[0]}) for events');
|
||||
|
||||
try {
|
||||
final candidateDayEnd = candidateDay.add(const Duration(days: 1));
|
||||
final response = await client.getTimeTable(candidateDay, candidateDayEnd, forceCache: false);
|
||||
List<Lesson> nextWeekBreakEvents = [];
|
||||
try {
|
||||
final nextWeekResponse = await client.getTimeTable(nextWeekStart, nextWeekEnd, forceCache: false);
|
||||
|
||||
if (response.response != null && response.response!.isNotEmpty) {
|
||||
final schoolLessons = response.response!.where((lesson) {
|
||||
final uid = lesson.uid.toLowerCase();
|
||||
return uid.contains('orarendiora') || uid.contains('tanitasiora') || uid.contains('uresora');
|
||||
}).toList();
|
||||
if (nextWeekResponse.response != null) {
|
||||
nextWeekBreakEvents = nextWeekResponse.response!.where((lesson) {
|
||||
final uid = lesson.uid.toLowerCase();
|
||||
final name = lesson.name.toLowerCase();
|
||||
return uid.contains('tanevrendjeesemeny') &&
|
||||
!name.contains('tanítási nap') &&
|
||||
(name.contains('szünet') ||
|
||||
name.contains('pihenőnap') ||
|
||||
name.contains('munkaszüneti') ||
|
||||
name.contains('ünnepnap') ||
|
||||
name.contains('tanítás nélküli') ||
|
||||
name.contains('nem órarendi nap'));
|
||||
}).toList();
|
||||
|
||||
if (schoolLessons.isNotEmpty) {
|
||||
schoolLessons.sort((a, b) => a.start.compareTo(b.start));
|
||||
final firstLesson = schoolLessons.first;
|
||||
|
||||
final markedLesson = Lesson(
|
||||
uid: '${firstLesson.uid}__FOR_NOTIFICATION_ONLY',
|
||||
date: firstLesson.date,
|
||||
start: firstLesson.start,
|
||||
end: firstLesson.end,
|
||||
name: firstLesson.name,
|
||||
lessonNumber: firstLesson.lessonNumber,
|
||||
teacher: firstLesson.teacher,
|
||||
theme: firstLesson.theme,
|
||||
roomName: firstLesson.roomName,
|
||||
substituteTeacher: firstLesson.substituteTeacher,
|
||||
type: firstLesson.type,
|
||||
state: firstLesson.state,
|
||||
canStudentEditHomework: firstLesson.canStudentEditHomework,
|
||||
isHomeworkComplete: firstLesson.isHomeworkComplete,
|
||||
attachments: firstLesson.attachments,
|
||||
isDigitalLesson: firstLesson.isDigitalLesson,
|
||||
digitalSupportDeviceTypeList: firstLesson.digitalSupportDeviceTypeList,
|
||||
createdAt: firstLesson.createdAt ?? firstLesson.lastModifiedAt ?? DateTime.now(),
|
||||
lastModifiedAt: firstLesson.lastModifiedAt,
|
||||
);
|
||||
|
||||
allLessons.add(markedLesson);
|
||||
|
||||
const dayNames = ['Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday'];
|
||||
final dayName = dayNames[candidateDay.weekday - 1];
|
||||
_logger.info('Added first lesson from next week $dayName (${firstLesson.name}) marked for notification scheduling only');
|
||||
|
||||
foundFirstSchoolDay = true;
|
||||
break;
|
||||
}
|
||||
if (nextWeekBreakEvents.isNotEmpty) {
|
||||
_logger.info('[GlobalSearch] Found ${nextWeekBreakEvents.length} break event(s) in next week - triggering global searcher');
|
||||
}
|
||||
} catch (e) {
|
||||
_logger.warning('Could not fetch lessons for day offset $dayOffset: $e');
|
||||
}
|
||||
} catch (e) {
|
||||
_logger.warning('[GlobalSearch] Could not fetch next week: $e');
|
||||
}
|
||||
|
||||
if (!foundFirstSchoolDay) {
|
||||
_logger.info('No school lessons found in next week for push notification scheduling');
|
||||
}
|
||||
if (nextWeekBreakEvents.isNotEmpty) {
|
||||
nextWeekBreakEvents.sort((a, b) => a.start.compareTo(b.start));
|
||||
final firstBreakEvent = nextWeekBreakEvents.first;
|
||||
|
||||
final breakEventsInWeek = allLessons.where((lesson) {
|
||||
final uid = lesson.uid.toLowerCase();
|
||||
final name = lesson.name.toLowerCase();
|
||||
_logger.info('[GlobalSearch] Triggering global break searcher from ${firstBreakEvent.date.split('T')[0]}');
|
||||
|
||||
return uid.contains('tanevrendjeesemeny') &&
|
||||
!name.contains('tanítási nap') &&
|
||||
(name.contains('szünet') ||
|
||||
name.contains('pihenőnap') ||
|
||||
name.contains('munkaszüneti') ||
|
||||
name.contains('ünnepnap') ||
|
||||
name.contains('tanítás nélküli') ||
|
||||
name.contains('nem órarendi nap'));
|
||||
}).toList();
|
||||
final searchResult = await _globalBreakSearcher(
|
||||
client: client,
|
||||
searchStartDate: nextWeekStart,
|
||||
);
|
||||
|
||||
if (breakEventsInWeek.isNotEmpty) {
|
||||
final firstBreak = breakEventsInWeek.first;
|
||||
final breakName = firstBreak.name.toLowerCase();
|
||||
allLessons.addAll(searchResult.allLessons);
|
||||
|
||||
String? breakType;
|
||||
if (breakName.contains('őszi')) {
|
||||
breakType = 'őszi';
|
||||
} else if (breakName.contains('téli')) {
|
||||
breakType = 'téli';
|
||||
} else if (breakName.contains('tavaszi')) {
|
||||
breakType = 'tavaszi';
|
||||
} else if (breakName.contains('nyári')) {
|
||||
breakType = 'nyári';
|
||||
_logger.info('[GlobalSearch] Global searcher returned ${searchResult.allLessons.length} lessons');
|
||||
|
||||
if (searchResult.firstSchoolDayAfterBreak != null) {
|
||||
final notificationLesson = Lesson(
|
||||
uid: '${searchResult.firstSchoolDayAfterBreak!.uid}__FOR_NOTIFICATION_ONLY',
|
||||
date: searchResult.firstSchoolDayAfterBreak!.date,
|
||||
start: searchResult.firstSchoolDayAfterBreak!.start,
|
||||
end: searchResult.firstSchoolDayAfterBreak!.end,
|
||||
name: searchResult.firstSchoolDayAfterBreak!.name,
|
||||
lessonNumber: searchResult.firstSchoolDayAfterBreak!.lessonNumber,
|
||||
teacher: searchResult.firstSchoolDayAfterBreak!.teacher,
|
||||
theme: searchResult.firstSchoolDayAfterBreak!.theme,
|
||||
roomName: searchResult.firstSchoolDayAfterBreak!.roomName,
|
||||
substituteTeacher: searchResult.firstSchoolDayAfterBreak!.substituteTeacher,
|
||||
type: searchResult.firstSchoolDayAfterBreak!.type,
|
||||
state: searchResult.firstSchoolDayAfterBreak!.state,
|
||||
canStudentEditHomework: searchResult.firstSchoolDayAfterBreak!.canStudentEditHomework,
|
||||
isHomeworkComplete: searchResult.firstSchoolDayAfterBreak!.isHomeworkComplete,
|
||||
attachments: searchResult.firstSchoolDayAfterBreak!.attachments,
|
||||
isDigitalLesson: searchResult.firstSchoolDayAfterBreak!.isDigitalLesson,
|
||||
digitalSupportDeviceTypeList: searchResult.firstSchoolDayAfterBreak!.digitalSupportDeviceTypeList,
|
||||
createdAt: searchResult.firstSchoolDayAfterBreak!.createdAt,
|
||||
lastModifiedAt: searchResult.firstSchoolDayAfterBreak!.lastModifiedAt,
|
||||
);
|
||||
|
||||
allLessons.add(notificationLesson);
|
||||
_logger.info('[GlobalSearch] Added first school day after break for push notification: ${notificationLesson.date.split('T')[0]}');
|
||||
}
|
||||
} else {
|
||||
_logger.info('[GlobalSearch] No break events in next week, searching for first school day...');
|
||||
|
||||
if (breakType != null) {
|
||||
_logger.info('Found $breakType break in current week, fetching all break days...');
|
||||
|
||||
final extendedStart = startOfWeek.subtract(const Duration(days: 28));
|
||||
final extendedEnd = endOfWeek.add(const Duration(days: 28));
|
||||
bool foundFirstSchoolDay = false;
|
||||
for (int dayOffset = 1; dayOffset <= 14; dayOffset++) {
|
||||
final candidateDay = endOfWeek.add(Duration(days: dayOffset));
|
||||
|
||||
try {
|
||||
final extendedResponse = await client.getTimeTable(extendedStart, extendedEnd);
|
||||
if (extendedResponse.response != null) {
|
||||
final allBreakEventsOfType = extendedResponse.response!.where((lesson) {
|
||||
final uid = lesson.uid.toLowerCase();
|
||||
final name = lesson.name.toLowerCase();
|
||||
final candidateDayEnd = candidateDay.add(const Duration(days: 1));
|
||||
final response = await client.getTimeTable(candidateDay, candidateDayEnd, forceCache: false);
|
||||
|
||||
return uid.contains('tanevrendjeesemeny') &&
|
||||
!name.contains('tanítási nap') &&
|
||||
name.contains(breakType!);
|
||||
if (response.response != null && response.response!.isNotEmpty) {
|
||||
final schoolLessons = response.response!.where((lesson) {
|
||||
final uid = lesson.uid.toLowerCase();
|
||||
return uid.contains('orarendiora') || uid.contains('tanitasiora') || uid.contains('uresora');
|
||||
}).toList();
|
||||
|
||||
int addedCount = 0;
|
||||
for (final breakEvent in allBreakEventsOfType) {
|
||||
if (!allLessons.any((l) => l.uid == breakEvent.uid && l.date == breakEvent.date)) {
|
||||
allLessons.add(breakEvent);
|
||||
addedCount++;
|
||||
_logger.info('[Firka] [INFO] Added break day: ${breakEvent.date} - ${breakEvent.name}');
|
||||
}
|
||||
}
|
||||
if (schoolLessons.isNotEmpty) {
|
||||
schoolLessons.sort((a, b) => a.start.compareTo(b.start));
|
||||
final firstLesson = schoolLessons.first;
|
||||
|
||||
if (addedCount > 0) {
|
||||
_logger.info('[Firka] [INFO] Added $addedCount $breakType break day(s) to timetable for backend processing');
|
||||
}
|
||||
final markedLesson = Lesson(
|
||||
uid: '${firstLesson.uid}__FOR_NOTIFICATION_ONLY',
|
||||
date: firstLesson.date,
|
||||
start: firstLesson.start,
|
||||
end: firstLesson.end,
|
||||
name: firstLesson.name,
|
||||
lessonNumber: firstLesson.lessonNumber,
|
||||
teacher: firstLesson.teacher,
|
||||
theme: firstLesson.theme,
|
||||
roomName: firstLesson.roomName,
|
||||
substituteTeacher: firstLesson.substituteTeacher,
|
||||
type: firstLesson.type,
|
||||
state: firstLesson.state,
|
||||
canStudentEditHomework: firstLesson.canStudentEditHomework,
|
||||
isHomeworkComplete: firstLesson.isHomeworkComplete,
|
||||
attachments: firstLesson.attachments,
|
||||
isDigitalLesson: firstLesson.isDigitalLesson,
|
||||
digitalSupportDeviceTypeList: firstLesson.digitalSupportDeviceTypeList,
|
||||
createdAt: firstLesson.createdAt,
|
||||
lastModifiedAt: firstLesson.lastModifiedAt,
|
||||
);
|
||||
|
||||
if (allBreakEventsOfType.isNotEmpty) {
|
||||
allBreakEventsOfType.sort((a, b) => a.start.compareTo(b.start));
|
||||
final lastBreakDay = allBreakEventsOfType.last.start;
|
||||
allLessons.add(markedLesson);
|
||||
_logger.info('[GlobalSearch] Found first school day for push notification: ${candidateDay.toString().split(' ')[0]}');
|
||||
|
||||
_logger.info('Searching for first school day after $breakType break (after ${lastBreakDay.toIso8601String().split('T')[0]})...');
|
||||
|
||||
bool foundFirstSchoolDayAfterBreak = false;
|
||||
for (int dayOffset = 1; dayOffset <= 14; dayOffset++) {
|
||||
final candidateDay = lastBreakDay.add(Duration(days: dayOffset));
|
||||
|
||||
if (candidateDay.weekday == DateTime.saturday || candidateDay.weekday == DateTime.sunday) {
|
||||
continue;
|
||||
}
|
||||
|
||||
try {
|
||||
final candidateDayEnd = candidateDay.add(const Duration(days: 1));
|
||||
final response = await client.getTimeTable(candidateDay, candidateDayEnd, forceCache: false);
|
||||
|
||||
if (response.response != null && response.response!.isNotEmpty) {
|
||||
final schoolLessons = response.response!.where((lesson) {
|
||||
final uid = lesson.uid.toLowerCase();
|
||||
return uid.contains('orarendiora') || uid.contains('tanitasiora') || uid.contains('uresora');
|
||||
}).toList();
|
||||
|
||||
if (schoolLessons.isNotEmpty) {
|
||||
schoolLessons.sort((a, b) => a.start.compareTo(b.start));
|
||||
final firstLesson = schoolLessons.first;
|
||||
|
||||
final notificationLesson = Lesson(
|
||||
uid: '${firstLesson.uid}__FOR_NOTIFICATION_ONLY',
|
||||
date: firstLesson.date,
|
||||
start: firstLesson.start,
|
||||
end: firstLesson.end,
|
||||
name: firstLesson.name,
|
||||
lessonNumber: firstLesson.lessonNumber,
|
||||
teacher: firstLesson.teacher,
|
||||
theme: firstLesson.theme,
|
||||
roomName: firstLesson.roomName,
|
||||
substituteTeacher: firstLesson.substituteTeacher,
|
||||
type: firstLesson.type,
|
||||
state: firstLesson.state,
|
||||
canStudentEditHomework: firstLesson.canStudentEditHomework,
|
||||
isHomeworkComplete: firstLesson.isHomeworkComplete,
|
||||
attachments: firstLesson.attachments,
|
||||
isDigitalLesson: firstLesson.isDigitalLesson,
|
||||
digitalSupportDeviceTypeList: firstLesson.digitalSupportDeviceTypeList,
|
||||
createdAt: firstLesson.createdAt ?? firstLesson.lastModifiedAt ?? DateTime.now(),
|
||||
lastModifiedAt: firstLesson.lastModifiedAt,
|
||||
);
|
||||
|
||||
allLessons.add(notificationLesson);
|
||||
|
||||
_logger.info('Added first lesson after $breakType break: ${firstLesson.date.split('T')[0]} - ${firstLesson.name} (marked for notification scheduling only)');
|
||||
|
||||
foundFirstSchoolDayAfterBreak = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
_logger.warning('Could not fetch lessons after break for day offset $dayOffset: $e');
|
||||
}
|
||||
}
|
||||
|
||||
if (!foundFirstSchoolDayAfterBreak) {
|
||||
_logger.warning('No school day found within 14 days after $breakType break');
|
||||
}
|
||||
foundFirstSchoolDay = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
_logger.warning('Could not fetch extended break events: $e');
|
||||
_logger.warning('[GlobalSearch] Could not fetch day offset $dayOffset: $e');
|
||||
}
|
||||
}
|
||||
|
||||
if (!foundFirstSchoolDay) {
|
||||
_logger.info('[GlobalSearch] No school lessons found in next 14 days for push notification scheduling');
|
||||
}
|
||||
}
|
||||
|
||||
if (allLessons.isEmpty) {
|
||||
@@ -1523,20 +1521,16 @@ class LiveActivityService {
|
||||
final client = initData.client;
|
||||
final settingsStore = initData.settings;
|
||||
|
||||
if (client != null) {
|
||||
final studentResp = await client.getStudent();
|
||||
final studentName = studentResp.response?.name ?? client.model?.studentId ?? 'Student';
|
||||
final studentResp = await client.getStudent();
|
||||
final studentName = studentResp.response?.name ?? client.model.studentId ?? 'Student';
|
||||
|
||||
await checkAndUpdateTimetable(
|
||||
client: client,
|
||||
studentName: studentName,
|
||||
settingsStore: settingsStore,
|
||||
forceUpdate: true,
|
||||
);
|
||||
_logger.info('Timetable fetch completed after re-enabling notifications');
|
||||
} else {
|
||||
_logger.warning('Cannot fetch timetable: client is null');
|
||||
}
|
||||
await checkAndUpdateTimetable(
|
||||
client: client,
|
||||
studentName: studentName,
|
||||
settingsStore: settingsStore,
|
||||
forceUpdate: true,
|
||||
);
|
||||
_logger.info('Timetable fetch completed after re-enabling notifications');
|
||||
} catch (e) {
|
||||
_logger.severe('Error fetching timetable after re-enabling notifications: $e');
|
||||
}
|
||||
@@ -1567,7 +1561,7 @@ class LiveActivityService {
|
||||
/// Get current morning notification enabled value from settings
|
||||
static bool? _getCurrentMorningNotificationEnabled() {
|
||||
try {
|
||||
if (!initDone || initData.settings == null) {
|
||||
if (!initDone) {
|
||||
return null;
|
||||
}
|
||||
final setting = initData.settings.group("settings")
|
||||
@@ -1582,7 +1576,7 @@ class LiveActivityService {
|
||||
/// Get current morning notification time value from settings
|
||||
static double? _getCurrentMorningNotificationTime() {
|
||||
try {
|
||||
if (!initDone || initData.settings == null) {
|
||||
if (!initDone) {
|
||||
return null;
|
||||
}
|
||||
final setting = initData.settings.group("settings")
|
||||
@@ -1593,4 +1587,103 @@ class LiveActivityService {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/// Global break searcher result
|
||||
static Future<_GlobalSearchResult> _globalBreakSearcher({
|
||||
required KretaClient client,
|
||||
required DateTime searchStartDate,
|
||||
}) async {
|
||||
List<Lesson> allLessons = [];
|
||||
Lesson? lastBreakDay;
|
||||
Lesson? firstSchoolDayAfterBreak;
|
||||
|
||||
DateTime currentSearchDate = searchStartDate;
|
||||
int weeksSearched = 0;
|
||||
const maxWeeks = 26;
|
||||
|
||||
_logger.info('[GlobalBreakSearcher] Starting search from ${currentSearchDate.toString().split(' ')[0]}');
|
||||
|
||||
while (weeksSearched < maxWeeks) {
|
||||
final weekStart = currentSearchDate;
|
||||
final weekEnd = weekStart.add(const Duration(days: 6));
|
||||
|
||||
_logger.info('[GlobalBreakSearcher] Fetching week ${weeksSearched + 1}: ${weekStart.toString().split(' ')[0]} - ${weekEnd.toString().split(' ')[0]}');
|
||||
|
||||
try {
|
||||
final response = await client.getTimeTable(weekStart, weekEnd, forceCache: false);
|
||||
|
||||
if (response.response != null && response.response!.isNotEmpty) {
|
||||
final weekLessons = response.response!;
|
||||
|
||||
final breakEvents = weekLessons.where((lesson) {
|
||||
final uid = lesson.uid.toLowerCase();
|
||||
final name = lesson.name.toLowerCase();
|
||||
return uid.contains('tanevrendjeesemeny') &&
|
||||
!name.contains('tanítási nap') &&
|
||||
(name.contains('pihenőnap') ||
|
||||
name.contains('munkaszüneti') ||
|
||||
name.contains('ünnepnap') ||
|
||||
name.contains('tanítás nélküli') ||
|
||||
name.contains('nem órarendi nap'));
|
||||
}).toList();
|
||||
|
||||
final schoolLessons = weekLessons.where((lesson) {
|
||||
final uid = lesson.uid.toLowerCase();
|
||||
return uid.contains('orarendiora') || uid.contains('tanitasiora') || uid.contains('uresora');
|
||||
}).toList();
|
||||
|
||||
allLessons.addAll(weekLessons);
|
||||
|
||||
if (breakEvents.isNotEmpty) {
|
||||
breakEvents.sort((a, b) => a.start.compareTo(b.start));
|
||||
lastBreakDay = breakEvents.last;
|
||||
_logger.info('[GlobalBreakSearcher] Found ${breakEvents.length} break event(s) in week ${weeksSearched + 1}, last: ${lastBreakDay.name} on ${lastBreakDay.date.split('T')[0]}');
|
||||
} else if (schoolLessons.isNotEmpty) {
|
||||
schoolLessons.sort((a, b) => a.start.compareTo(b.start));
|
||||
firstSchoolDayAfterBreak = schoolLessons.first;
|
||||
_logger.info('[GlobalBreakSearcher] Found first school day after break: ${firstSchoolDayAfterBreak.name} on ${firstSchoolDayAfterBreak.date.split('T')[0]}');
|
||||
break;
|
||||
} else {
|
||||
_logger.info('[GlobalBreakSearcher] Week ${weeksSearched + 1} is empty, continuing search...');
|
||||
}
|
||||
} else {
|
||||
_logger.info('[GlobalBreakSearcher] Week ${weeksSearched + 1} returned no data, continuing search...');
|
||||
}
|
||||
} catch (e) {
|
||||
_logger.warning('[GlobalBreakSearcher] Error fetching week ${weeksSearched + 1}: $e');
|
||||
}
|
||||
|
||||
currentSearchDate = currentSearchDate.add(const Duration(days: 7));
|
||||
weeksSearched++;
|
||||
|
||||
if (lastBreakDay != null && firstSchoolDayAfterBreak != null) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (weeksSearched >= maxWeeks) {
|
||||
_logger.warning('[GlobalBreakSearcher] Reached maximum search limit ($maxWeeks weeks)');
|
||||
}
|
||||
|
||||
_logger.info('[GlobalBreakSearcher] Search completed: ${allLessons.length} lessons found, last break: ${lastBreakDay?.date.split('T')[0] ?? 'none'}, first school day: ${firstSchoolDayAfterBreak?.date.split('T')[0] ?? 'none'}');
|
||||
|
||||
return _GlobalSearchResult(
|
||||
allLessons: allLessons,
|
||||
lastBreakDay: lastBreakDay,
|
||||
firstSchoolDayAfterBreak: firstSchoolDayAfterBreak,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// Result of global break searcher
|
||||
class _GlobalSearchResult {
|
||||
final List<Lesson> allLessons;
|
||||
final Lesson? lastBreakDay;
|
||||
final Lesson? firstSchoolDayAfterBreak;
|
||||
|
||||
_GlobalSearchResult({
|
||||
required this.allLessons,
|
||||
this.lastBreakDay,
|
||||
this.firstSchoolDayAfterBreak,
|
||||
});
|
||||
}
|
||||
Reference in New Issue
Block a user