getCourseTable method

Future<List<ScheduleDto>> getCourseTable({
  1. required String username,
  2. required SemesterDto semester,
})

Fetches the course schedule table for a specific student and semester.

Returns a list of course offerings enrolled by the student, including:

  • Course details (name, credits, hours)
  • Schedule information (days, periods, classroom)
  • Teacher and class information
  • Enrollment status and remarks

The username should be a student ID, and semester should be obtained from getCourseSemesterList.

Throws an Exception if no courses are found for the given semester.

Implementation

Future<List<ScheduleDto>> getCourseTable({
  required String username,
  required SemesterDto semester,
}) async {
  final response = await _courseDio.get(
    'Select.jsp',
    queryParameters: {
      'format': '-2',
      'code': username,
      'year': semester.year,
      'sem': semester.term,
    },
  );

  final document = parse(response.data);

  // There are two tables in the document; the first one is a timetable grid
  // The second table is a list with one course per row, we'll extract that
  final courseSelectionTable = document.querySelectorAll('table')[1];

  // Find all rows except the first two header rows and the last summary row
  final tableRows = courseSelectionTable.querySelectorAll('tr');
  final trimmedTableRows = tableRows.sublist(2, tableRows.length - 1);
  if (trimmedTableRows.isEmpty) {
    throw Exception('No courses found in the selection table.');
  }

  return trimmedTableRows.map((row) {
    final cells = row.children;

    // Extract basic course information
    final number = _parseCellText(cells[0]);
    final course = _parseCellRef(cells[1]);
    final phase = int.tryParse(cells[2].text.trim());
    final credits = double.tryParse(cells[3].text.trim());
    final hours = int.tryParse(cells[4].text.trim());
    final type = _parseCellText(cells[5]);
    final teacher = _parseCellRef(cells[6]);
    final classes = _parseCellRefs(cells[7]);

    // Parse schedule from day columns (indices 8-14)
    final schedule = <(DayOfWeek, Period)>[];
    final days = DayOfWeek.values;

    for (var i = 0; i < days.length; i++) {
      final dayText = _parseCellText(cells[8 + i]);
      if (dayText == null) continue;

      // Parse period codes (e.g., "7 8" or "8 9 A") and skip invalid ones
      final periods = dayText
          .split(' ')
          .map(
            (e) => Period.values.firstWhereOrNull((p) => p.code == e.trim()),
          )
          .whereType<Period>()
          .toList();
      final scheduleOfDay = periods.map((p) => (days[i], p));
      schedule.addAll(scheduleOfDay);
    }

    final classroom = _parseCellRef(cells[15]);
    final status = _parseCellText(cells[16]);
    final language = _parseCellText(cells[17]);
    final syllabusId = _parseCellRef(cells[18]).id;
    final remarks = _parseCellText(cells[19]);

    return (
      number: number,
      course: course,
      phase: phase,
      credits: credits,
      hours: hours,
      type: type,
      teacher: teacher,
      classes: classes,
      schedule: schedule.isEmpty ? null : schedule,
      classroom: classroom,
      status: status,
      language: language,
      syllabusId: syllabusId,
      remarks: remarks,
    );
  }).toList();
}