claude refactor
This commit is contained in:
@@ -0,0 +1,24 @@
|
||||
import '../../../../api/mhsl/customTimetableEvent/customTimetableEvent.dart';
|
||||
import '../../../../api/webuntis/queries/getTimetable/getTimetableResponse.dart';
|
||||
|
||||
sealed class ArbitraryAppointment {
|
||||
const ArbitraryAppointment();
|
||||
|
||||
T when<T>({
|
||||
required T Function(GetTimetableResponseObject lesson) webuntis,
|
||||
required T Function(CustomTimetableEvent event) custom,
|
||||
}) => switch (this) {
|
||||
WebuntisAppointment(:final lesson) => webuntis(lesson),
|
||||
CustomAppointment(:final event) => custom(event),
|
||||
};
|
||||
}
|
||||
|
||||
class WebuntisAppointment extends ArbitraryAppointment {
|
||||
final GetTimetableResponseObject lesson;
|
||||
const WebuntisAppointment(this.lesson);
|
||||
}
|
||||
|
||||
class CustomAppointment extends ArbitraryAppointment {
|
||||
final CustomTimetableEvent event;
|
||||
const CustomAppointment(this.event);
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
import 'lesson_status.dart';
|
||||
|
||||
class LessonColor {
|
||||
static const Color cancelled = Color(0xff000000);
|
||||
static const Color irregular = Color(0xff8F19B3);
|
||||
static const Color teacherChanged = Color(0xFF29639B);
|
||||
static const Color parseFallback = Color(0xff404040);
|
||||
|
||||
static Color forStatus(LessonStatus status, ColorScheme scheme) {
|
||||
switch (status) {
|
||||
case LessonStatus.cancelled:
|
||||
return cancelled;
|
||||
case LessonStatus.irregular:
|
||||
return irregular;
|
||||
case LessonStatus.teacherChanged:
|
||||
return teacherChanged;
|
||||
case LessonStatus.past:
|
||||
case LessonStatus.regular:
|
||||
return scheme.primary;
|
||||
case LessonStatus.ongoing:
|
||||
return Color.from(
|
||||
alpha: scheme.primary.a,
|
||||
red: 200 / 255,
|
||||
green: scheme.primary.g,
|
||||
blue: scheme.primary.b,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
import '../../../../api/webuntis/queries/getTimetable/getTimetableResponse.dart';
|
||||
|
||||
enum LessonStatus {
|
||||
cancelled,
|
||||
irregular,
|
||||
teacherChanged,
|
||||
past,
|
||||
ongoing,
|
||||
regular,
|
||||
}
|
||||
|
||||
class LessonStatusClassifier {
|
||||
static LessonStatus classify(GetTimetableResponseObject lesson, DateTime startTime, DateTime endTime, DateTime now) {
|
||||
if (lesson.code == 'cancelled') return LessonStatus.cancelled;
|
||||
if (lesson.code == 'irregular' || (lesson.te.isNotEmpty && lesson.te.first.id == 0)) return LessonStatus.irregular;
|
||||
if (lesson.te.any((t) => t.orgname != null)) return LessonStatus.teacherChanged;
|
||||
if (endTime.isBefore(now)) return LessonStatus.past;
|
||||
if (startTime.isBefore(now) && endTime.isAfter(now)) return LessonStatus.ongoing;
|
||||
return LessonStatus.regular;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,136 @@
|
||||
import 'package:collection/collection.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:syncfusion_flutter_calendar/calendar.dart';
|
||||
|
||||
import '../../../../api/mhsl/customTimetableEvent/customTimetableEvent.dart';
|
||||
import '../../../../api/webuntis/queries/getRooms/getRoomsResponse.dart';
|
||||
import '../../../../api/webuntis/queries/getSubjects/getSubjectsResponse.dart';
|
||||
import '../../../../api/webuntis/queries/getTimetable/getTimetableResponse.dart';
|
||||
import '../../../../storage/timetable/timetableSettings.dart';
|
||||
import '../../../../storage/timetable/timetable_name_mode.dart';
|
||||
import '../custom_events/custom_event_colors.dart';
|
||||
import 'arbitrary_appointment.dart';
|
||||
import 'lesson_color.dart';
|
||||
import 'lesson_status.dart';
|
||||
import 'webuntis_time.dart';
|
||||
|
||||
class TimetableAppointmentFactory {
|
||||
final List<GetTimetableResponseObject> lessons;
|
||||
final List<CustomTimetableEvent> customEvents;
|
||||
final GetRoomsResponse rooms;
|
||||
final GetSubjectsResponse subjects;
|
||||
final TimetableSettings settings;
|
||||
final ColorScheme colorScheme;
|
||||
final DateTime now;
|
||||
|
||||
TimetableAppointmentFactory({
|
||||
required this.lessons,
|
||||
required this.customEvents,
|
||||
required this.rooms,
|
||||
required this.subjects,
|
||||
required this.settings,
|
||||
required this.colorScheme,
|
||||
required this.now,
|
||||
});
|
||||
|
||||
List<Appointment> build() {
|
||||
final source = settings.connectDoubleLessons ? _mergeAdjacentLessons(lessons) : lessons;
|
||||
return [
|
||||
...source.map(_lessonToAppointment),
|
||||
...customEvents.map(_customEventToAppointment),
|
||||
];
|
||||
}
|
||||
|
||||
Appointment _lessonToAppointment(GetTimetableResponseObject lesson) {
|
||||
try {
|
||||
final startTime = WebuntisTime.parse(lesson.date, lesson.startTime);
|
||||
final endTime = WebuntisTime.parse(lesson.date, lesson.endTime);
|
||||
final status = LessonStatusClassifier.classify(lesson, startTime, endTime, now);
|
||||
|
||||
return Appointment(
|
||||
id: WebuntisAppointment(lesson),
|
||||
startTime: startTime,
|
||||
endTime: endTime,
|
||||
subject: _subjectName(lesson),
|
||||
location: _locationLabel(lesson),
|
||||
notes: lesson.activityType,
|
||||
color: LessonColor.forStatus(status, colorScheme),
|
||||
);
|
||||
} catch (_) {
|
||||
return Appointment(
|
||||
id: WebuntisAppointment(lesson),
|
||||
startTime: WebuntisTime.parse(lesson.date, lesson.startTime),
|
||||
endTime: WebuntisTime.parse(lesson.date, lesson.endTime),
|
||||
subject: 'Änderung',
|
||||
notes: lesson.info,
|
||||
location: 'Unbekannt',
|
||||
color: LessonColor.parseFallback,
|
||||
startTimeZone: '',
|
||||
endTimeZone: '',
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Appointment _customEventToAppointment(CustomTimetableEvent event) => Appointment(
|
||||
id: CustomAppointment(event),
|
||||
startTime: event.startDate,
|
||||
endTime: event.endDate,
|
||||
location: event.description,
|
||||
subject: event.title,
|
||||
recurrenceRule: event.rrule,
|
||||
color: TimetableColors.getColorFromString(event.color ?? TimetableColors.defaultColor.name),
|
||||
startTimeZone: '',
|
||||
endTimeZone: '',
|
||||
);
|
||||
|
||||
String _subjectName(GetTimetableResponseObject lesson) {
|
||||
final subject = subjects.result.firstWhereOrNull((s) => s.id == lesson.su.firstOrNull?.id);
|
||||
if (subject == null) return 'Unbekannt';
|
||||
return switch (settings.timetableNameMode) {
|
||||
TimetableNameMode.name => subject.name,
|
||||
TimetableNameMode.longName => subject.longName,
|
||||
TimetableNameMode.alternateName => subject.alternateName,
|
||||
};
|
||||
}
|
||||
|
||||
String _locationLabel(GetTimetableResponseObject lesson) {
|
||||
final roomName = rooms.result.firstWhereOrNull((r) => r.id == lesson.ro.firstOrNull?.id)?.name ?? 'Unbekannt';
|
||||
final teacherName = lesson.te.firstOrNull?.longname ?? 'Unbekannt';
|
||||
return '$roomName\n$teacherName';
|
||||
}
|
||||
|
||||
// Pure: returns a new list, does not mutate input.
|
||||
static List<GetTimetableResponseObject> _mergeAdjacentLessons(
|
||||
List<GetTimetableResponseObject> input, {
|
||||
Duration maxGap = const Duration(minutes: 5),
|
||||
}) {
|
||||
if (input.isEmpty) return const [];
|
||||
|
||||
final sorted = [...input]..sort((a, b) =>
|
||||
WebuntisTime.parse(a.date, a.startTime).compareTo(WebuntisTime.parse(b.date, b.startTime)));
|
||||
|
||||
final merged = <GetTimetableResponseObject>[sorted.first];
|
||||
for (var i = 1; i < sorted.length; i++) {
|
||||
final previous = merged.last;
|
||||
final current = sorted[i];
|
||||
if (_canMerge(previous, current, maxGap)) {
|
||||
previous.endTime = current.endTime;
|
||||
} else {
|
||||
merged.add(current);
|
||||
}
|
||||
}
|
||||
return merged;
|
||||
}
|
||||
|
||||
static bool _canMerge(GetTimetableResponseObject a, GetTimetableResponseObject b, Duration maxGap) {
|
||||
final aSubject = a.su.firstOrNull?.id;
|
||||
final bSubject = b.su.firstOrNull?.id;
|
||||
if (aSubject == null || bSubject == null || aSubject != bSubject) return false;
|
||||
if (a.ro.firstOrNull?.id != b.ro.firstOrNull?.id) return false;
|
||||
if (a.te.firstOrNull?.id != b.te.firstOrNull?.id) return false;
|
||||
if (a.code != b.code) return false;
|
||||
|
||||
final gap = WebuntisTime.parse(b.date, b.startTime).difference(WebuntisTime.parse(a.date, a.endTime));
|
||||
return gap <= maxGap;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
import 'package:intl/intl.dart';
|
||||
|
||||
class WebuntisTime {
|
||||
static final DateFormat _dateFormat = DateFormat('yyyyMMdd');
|
||||
|
||||
static DateTime parse(int date, int time) {
|
||||
final timeString = time.toString().padLeft(4, '0');
|
||||
return DateTime.parse('$date ${timeString.substring(0, 2)}:${timeString.substring(2, 4)}');
|
||||
}
|
||||
|
||||
static int formatDate(DateTime date) => int.parse(_dateFormat.format(date));
|
||||
|
||||
static String dateKey(DateTime date) => _dateFormat.format(date);
|
||||
}
|
||||
Reference in New Issue
Block a user