refreshStudents method

Future<void> refreshStudents(
  1. int courseOfferingId
)

Fetches the fresh roster from I-School Plus and writes it to the DB.

The watchStudents stream automatically emits the updated value. Network errors propagate to the caller.

Not every course-system offering exists on I-School Plus (e.g. internships, or special entries with no offering number) — these resolve to an empty roster. The fetch timestamp is recorded either way.

Implementation

Future<void> refreshStudents(int courseOfferingId) async {
  final offering = await (_database.select(
    _database.courseOfferings,
  )..where((o) => o.id.equals(courseOfferingId))).getSingleOrNull();
  if (offering == null) return;

  final number = offering.number;
  final dtos = number == null
      ? const <StudentDto>[]
      : await _authRepository.withAuth(() async {
          // Resolve the offering number to its internal I-School Plus handle.
          final courses = await _iSchoolPlusService.getCourseList();
          for (final course in courses) {
            if (course.courseNumber == number) {
              return _iSchoolPlusService.getStudents(course);
            }
          }
          // Offering not available on I-School Plus.
          return <StudentDto>[];
        }, sso: [.iSchoolPlusService]);

  await _database.transaction(() async {
    // Replace the roster for this offering.
    await (_database.delete(
      _database.courseOfferingStudents,
    )..where((s) => s.courseOffering.equals(courseOfferingId))).go();

    for (final dto in dtos) {
      // Students are keyed by their ID; skip rows missing one.
      if (dto.id case final studentId?) {
        final studentRowId = await _database.upsertStudent(
          studentId: studentId,
          name: dto.name,
        );
        await _database
            .into(_database.courseOfferingStudents)
            .insert(
              CourseOfferingStudentsCompanion.insert(
                courseOffering: courseOfferingId,
                student: studentRowId,
              ),
              mode: .insertOrIgnore,
            );
      }
    }

    await (_database.update(
      _database.courseOfferings,
    )..where((o) => o.id.equals(courseOfferingId))).write(
      CourseOfferingsCompanion(rosterFetchedAt: Value(DateTime.now())),
    );
  });
}