105 lines
3.4 KiB
Dart
105 lines
3.4 KiB
Dart
import '../domain/course.dart';
|
|
import '../domain/school_term.dart';
|
|
import '../domain/school_identity.dart';
|
|
|
|
import 'dart:convert';
|
|
|
|
class ImportDraft {
|
|
const ImportDraft({
|
|
required this.courses,
|
|
required this.explicitlyEmpty,
|
|
required this.source,
|
|
required this.parserVersion,
|
|
this.semesterLabel,
|
|
this.schoolTerm,
|
|
});
|
|
final List<Course> courses;
|
|
final bool explicitlyEmpty;
|
|
final CourseSource source;
|
|
final String parserVersion;
|
|
final String? semesterLabel;
|
|
final SchoolTerm? schoolTerm;
|
|
}
|
|
|
|
class JsonScheduleImport {
|
|
static ImportDraft parse(String text) {
|
|
try {
|
|
if (utf8.encode(text).length > 2 * 1024 * 1024) {
|
|
throw const FormatException('文件不能超过 2 MB');
|
|
}
|
|
final json = jsonDecode(text);
|
|
if (json is! Map ||
|
|
json['schemaVersion'] != 1 ||
|
|
json['courses'] is! List ||
|
|
(json['isDemo'] != null && json['isDemo'] is! bool) ||
|
|
(json['explicitlyEmpty'] != null &&
|
|
json['explicitlyEmpty'] is! bool)) {
|
|
throw const FormatException('请选择版本 1 的课表 JSON 文件');
|
|
}
|
|
final source = json['isDemo'] == true
|
|
? CourseSource.demo
|
|
: CourseSource.file;
|
|
final courses = (json['courses'] as List)
|
|
.map(
|
|
(c) => Course.fromJson(
|
|
Map<String, dynamic>.from(c as Map),
|
|
source: source,
|
|
),
|
|
)
|
|
.toList();
|
|
if (courses.map((c) => c.id).toSet().length != courses.length) {
|
|
throw const FormatException('课程标识重复');
|
|
}
|
|
final termIds = courses
|
|
.map((c) => RegExp(r'^uestc-(\d+)-').firstMatch(c.id)?.group(1))
|
|
.whereType<String>()
|
|
.toSet();
|
|
final term = json['schoolTerm'] != null
|
|
? SchoolTerm.fromJson(
|
|
Map<String, dynamic>.from(json['schoolTerm'] as Map),
|
|
)
|
|
: termIds.length == 1
|
|
? SchoolTerm(int.parse(termIds.single))
|
|
: null;
|
|
if (termIds.length > 1 ||
|
|
(term != null &&
|
|
termIds.any(
|
|
(id) =>
|
|
id != term.id.toString() ||
|
|
term.school != SupportedSchool.uestcGraduate,
|
|
))) {
|
|
throw const FormatException('文件中包含不同学期的课程');
|
|
}
|
|
final undergraduateIds = courses
|
|
.map(
|
|
(c) =>
|
|
RegExp(r'^uestc-undergraduate-(\d+)-')
|
|
.firstMatch(c.id)
|
|
?.group(1),
|
|
)
|
|
.whereType<String>()
|
|
.toSet();
|
|
if (undergraduateIds.isNotEmpty &&
|
|
(term?.school != SupportedSchool.uestcUndergraduate ||
|
|
undergraduateIds.any((id) => id != term!.id.toString())) ||
|
|
courses.any((c) => c.endPeriod > 11) &&
|
|
(source == CourseSource.demo ||
|
|
term?.school != SupportedSchool.uestcUndergraduate)) {
|
|
throw const FormatException('本科课表来源或学期无效');
|
|
}
|
|
return ImportDraft(
|
|
courses: List.unmodifiable(courses),
|
|
explicitlyEmpty: json['explicitlyEmpty'] == true,
|
|
source: source,
|
|
parserVersion: 'json-v1',
|
|
schoolTerm: source == CourseSource.demo ? null : term,
|
|
semesterLabel: term?.label,
|
|
);
|
|
} on FormatException {
|
|
rethrow;
|
|
} catch (_) {
|
|
throw const FormatException('课表文件格式无效');
|
|
}
|
|
}
|
|
}
|