getTeacher method
- required String teacherId,
- required SemesterDto semester,
Fetches detailed information about a specific teacher.
Returns teacher profile information including department, title, and
office hours for the given teacherId in a specific semester.
The teacherId should be a teacher code obtained from the teacher.id
field of a ScheduleDto.
Implementation
Future<TeacherDto> getTeacher({
required String teacherId,
required SemesterDto semester,
}) async {
final queryParams = {
'year': semester.year,
'sem': semester.term,
'code': teacherId,
};
final (profileResponse, officeHoursResponse) = await (
_courseDio.get(
'Teach.jsp',
queryParameters: {'format': '-3', ...queryParams},
),
_courseDio.get(
'Teach.jsp',
queryParameters: {'format': '-6', ...queryParams},
),
).wait;
// Parse format=-3: profile header
// Structure: <th colspan="24"><a>dept</a> title name hours <a>office hours link</a></th>
final profileDoc = parse(profileResponse.data);
final headerTh = profileDoc.querySelector('table tr:first-child th');
ReferenceDto? department;
String? title;
String? nameZh;
double? teachingHours;
if (headerTh != null) {
final anchors = headerTh.querySelectorAll('a');
if (anchors.isNotEmpty) {
final deptAnchor = anchors.first;
final deptHref = deptAnchor.attributes['href'];
final deptCode = deptHref != null
? Uri.parse(deptHref).queryParameters['code']
: null;
department = (id: deptCode, name: deptAnchor.text.trim());
}
// Parse text segments: "dept title name XX.XX 小時 office hours link"
final fullText = headerTh.text.trim();
final segments = fullText
.split(RegExp(r'\s{2,}'))
.where((s) => s.isNotEmpty)
.toList();
if (segments.length >= 4) {
title = segments[1];
nameZh = segments[2];
final hoursMatch = RegExp(r'([\d.]+)\s*小時').firstMatch(segments[3]);
teachingHours = hoursMatch != null
? double.tryParse(hoursMatch.group(1)!)
: null;
}
}
// Parse format=-6: office hours
// Structure: plain text with <br> separators
final officeDoc = parse(officeHoursResponse.data);
final bodyText = officeDoc.body?.text ?? '';
final lines = bodyText.split(RegExp(r'\n')).map((l) => l.trim()).toList();
String? nameEn;
final officeHours = <OfficeHourDto>[];
String? officeHoursNote;
for (final line in lines) {
// Parse instructor line: "教師姓名(Instructor) 陸元平(Luh Yuan-Ping)"
if (line.contains('Instructor')) {
final nameMatch = RegExp(r'\(([A-Za-z\s\-]+)\)$').firstMatch(line);
nameEn = nameMatch?.group(1);
}
// Parse office hours: "星期三(Wed) 10:00 ~ 13:00"
final hourMatch = RegExp(
r'星期[一二三四五六日]\((\w+)\)\s*(\d{1,2}:\d{2})\s*~\s*(\d{1,2}:\d{2})',
).firstMatch(line);
if (hourMatch != null) {
final dayCode = hourMatch.group(1)!;
final day = _parseDayOfWeek(dayCode);
final start = _parseTime(hourMatch.group(2)!);
final end = _parseTime(hourMatch.group(3)!);
if (day != null && start != null && end != null) {
officeHours.add((day: day, startTime: start, endTime: end));
}
}
// Parse note: "備 註(Note) ..."
if (line.contains('Note)')) {
final noteMatch = RegExp(r'Note\)\s*(.+)$').firstMatch(line);
officeHoursNote = noteMatch?.group(1);
}
}
return (
department: department,
title: title,
nameZh: nameZh,
nameEn: nameEn,
teachingHours: teachingHours,
officeHours: officeHours.isNotEmpty ? officeHours : null,
officeHoursNote: officeHoursNote,
);
}