getStudentProfile method

Future<StudentProfileDto> getStudentProfile()

Fetches student status (學籍基本資料) from the basis data page.

Extracts personal information including bilingual names, DOB, program, and department. Handles specific formatting for mixed Chinese/English strings.

Implementation

Future<StudentProfileDto> getStudentProfile() async {
  final response = await _studentQueryDio.get('QryBasisData.jsp');
  final document = parse(response.data);

  final table = document.querySelector('table');
  if (table == null) {
    throw FormatException('No table found in QryBasisData.jsp');
  }

  // Build a map from English header text to the cell value.
  // Data rows have 2 TH (Chinese label, English label) + 1 TD (value).
  final fields = <String, String?>{};
  for (final row in table.querySelectorAll('tr')) {
    final ths = row.querySelectorAll('th');
    final tds = row.querySelectorAll('td');
    if (ths.length != 2 || tds.length != 1) continue;

    final key = ths[1].text.trim();
    // English Name has an inline <div> note; extract the first text node.
    if (key == 'English Name') {
      fields[key] = tds[0].nodes
          .where((node) => node.nodeType == Node.TEXT_NODE)
          .firstOrNull
          ?.text
          ?.trim();
    } else {
      fields[key] = _parseCellText(tds[0]);
    }
  }

  // Date of Birth: "92年05月12日 2003/5/12" — extract Western date.
  final dobMatch = RegExp(
    r'(\d{4})/(\d{1,2})/(\d{1,2})',
  ).firstMatch(fields['Date of Birth'] ?? '');
  final dateOfBirth = dobMatch != null
      ? DateTime(
          int.parse(dobMatch.group(1)!),
          int.parse(dobMatch.group(2)!),
          int.parse(dobMatch.group(3)!),
        )
      : null;

  // Split mixed Chinese+English text at the first Latin character.
  // e.g. "四年制大學部Four-Year Program" → ("四年制大學部", "Four-Year Program")
  (String?, String?) splitZhEn(String? text) {
    if (text == null) return (null, null);
    final i = text.indexOf(RegExp(r'[A-Za-z]'));
    if (i <= 0) return (text, null);
    return (text.substring(0, i).trim(), text.substring(i).trim());
  }

  final (programZh, programEn) = splitZhEn(fields['Program']);
  final (departmentZh, departmentEn) = splitZhEn(
    fields['Department/Graduate Institute'],
  );

  return (
    chineseName: fields['Chinese Name'],
    englishName: fields['English Name'],
    dateOfBirth: dateOfBirth,
    programZh: programZh,
    programEn: programEn,
    departmentZh: departmentZh,
    departmentEn: departmentEn,
  );
}