92 lines
2.8 KiB
Dart
92 lines
2.8 KiB
Dart
import 'dart:convert';
|
|
|
|
import 'package:html/parser.dart' as html;
|
|
|
|
import '../../domain/exam_schedule.dart';
|
|
|
|
class SchoolExamAdapter {
|
|
static ExamSchedule extract(String content) {
|
|
try {
|
|
if (utf8.encode(content).length > 2 * 1024 * 1024) {
|
|
throw const FormatException();
|
|
}
|
|
final document = html.parse(content);
|
|
final selected = document.querySelector('#xqbh option[selected]');
|
|
final term = selected?.attributes['value'];
|
|
final table = document.querySelector('#sample-table-1');
|
|
const headers = [
|
|
'id',
|
|
'序号',
|
|
'学号',
|
|
'姓名',
|
|
'课程代码',
|
|
'课程名称',
|
|
'考核方式',
|
|
'考生编号',
|
|
'考场顺序号',
|
|
'考场',
|
|
'座位号',
|
|
'考试时间',
|
|
];
|
|
if (selected == null ||
|
|
term == null ||
|
|
table == null ||
|
|
!RegExp(r'^\d+$').hasMatch(term)) {
|
|
throw const FormatException();
|
|
}
|
|
final rows = table.querySelectorAll('tr');
|
|
if (rows.isEmpty || rows.first.children.length != headers.length) {
|
|
throw const FormatException();
|
|
}
|
|
for (var i = 0; i < headers.length; i++) {
|
|
if (rows.first.children[i].text.trim() != headers[i]) {
|
|
throw const FormatException();
|
|
}
|
|
}
|
|
final records = <ExamRecord>[];
|
|
var explicitlyEmpty = false;
|
|
for (final row in rows.skip(1)) {
|
|
// DataTables can insert a single explicit no-data placeholder.
|
|
if (row.children.length == 1 &&
|
|
row.children.first.attributes['colspan'] == '12' &&
|
|
const [
|
|
'没有数据',
|
|
'暂无数据',
|
|
'无数据',
|
|
'No data available in table',
|
|
].contains(row.text.trim())) {
|
|
explicitlyEmpty = true;
|
|
continue;
|
|
}
|
|
if (row.children.length != 12 ||
|
|
row.querySelector('script,style') != null) {
|
|
throw const FormatException();
|
|
}
|
|
final cells = row.children.map((c) => c.text.trim()).toList();
|
|
records.add(
|
|
ExamRecord(
|
|
code: cells[4],
|
|
name: cells[5],
|
|
assessment: cells[6],
|
|
candidate: cells[7],
|
|
sequence: cells[8],
|
|
room: cells[9],
|
|
seat: cells[10],
|
|
time: cells[11],
|
|
),
|
|
);
|
|
}
|
|
if (records.isEmpty && !explicitlyEmpty) throw const FormatException();
|
|
return ExamSchedule.fromJson(
|
|
ExamSchedule(
|
|
semesterLabel: selected.text.trim(),
|
|
termId: term,
|
|
records: records,
|
|
).toJson(),
|
|
);
|
|
} catch (_) {
|
|
throw const FormatException('未识别到完整的考试查询表,已有考试信息保持不变。');
|
|
}
|
|
}
|
|
}
|