getGradeRanking method

Future<List<GradeRankingDto>> getGradeRanking()

Fetches grade ranking data for all semesters.

Returns ranking entries including class, group, and department levels. Semester information is spans multiple ranking type rows using rowspan.

Implementation

Future<List<GradeRankingDto>> getGradeRanking() async {
  final response = await _studentQueryDio.get('QryRank.jsp');
  final document = parse(response.data);

  final table = document.querySelector('table');
  if (table == null) return [];

  final semesterPattern = RegExp(r'(\d+)\s*-\s*(\d+)');
  final results = <GradeRankingDto>[];
  SemesterDto? currentSemester;
  var currentEntries = <GradeRankingEntryDto>[];

  for (final row in table.querySelectorAll('tr')) {
    final cells = row.querySelectorAll('td').toList();

    int dataStart;
    if (cells.length == 8) {
      // Starting new semester block
      if (currentSemester != null && currentEntries.isNotEmpty) {
        results.add((semester: currentSemester, entries: currentEntries));
        currentEntries = [];
      }
      final semesterText = cells[0].nodes
          .where((node) => node.nodeType == Node.TEXT_NODE)
          .firstOrNull
          ?.text;
      final match = semesterPattern.firstMatch(semesterText ?? '');
      if (match == null) continue;
      currentSemester = (
        year: int.parse(match.group(1)!),
        term: int.parse(match.group(2)!),
      );
      dataStart = 1;
    } else if (cells.length == 7) {
      dataStart = 0;
    } else {
      continue;
    }

    final type = _parseRankingType(cells[dataStart].text);
    if (type == null) continue;

    final semesterRank = int.tryParse(cells[dataStart + 1].text.trim());
    final semesterTotal = int.tryParse(cells[dataStart + 2].text.trim());
    final grandTotalRank = int.tryParse(cells[dataStart + 4].text.trim());
    final grandTotalTotal = int.tryParse(cells[dataStart + 5].text.trim());

    if (semesterRank == null ||
        semesterTotal == null ||
        grandTotalRank == null ||
        grandTotalTotal == null) {
      continue;
    }

    currentEntries.add((
      type: type,
      semesterRank: semesterRank,
      semesterTotal: semesterTotal,
      grandTotalRank: grandTotalRank,
      grandTotalTotal: grandTotalTotal,
    ));
  }

  if (currentSemester != null && currentEntries.isNotEmpty) {
    results.add((semester: currentSemester, entries: currentEntries));
  }

  return results;
}