Skip to content

feat: add course roster repository methods - #327

Draft
rileychh wants to merge 1 commit into
mainfrom
add-course-roster-repository
Draft

feat: add course roster repository methods#327
rileychh wants to merge 1 commit into
mainfrom
add-course-roster-repository

Conversation

@rileychh

@rileychh rileychh commented Jun 2, 2026

Copy link
Copy Markdown
Member

Summary

Wires CourseRepository to the I-School Plus roster (classmates), implementing the previously-stubbed getStudents. This is the repository/data layer for course rosters — part of #226's "more query features (大綱、同學、教材、公告)" line.

  • watchStudents(int courseOfferingId)Stream<List<Student>>. Backed by a Drift .watch() join over CourseOfferingStudents/Students, ordered by student ID. Emits the cached roster immediately, then background-fetches when empty or stale. Follows the repo's standard watchX/refreshX pattern; refresh errors are absorbed (stale data preferred).
  • refreshStudents(int courseOfferingId) → imperative counterpart. Resolves the offering number → I-School Plus handle, fetches the roster, replaces the junction rows, and stamps the new CourseOfferings.rosterFetchedAt cache timestamp.
  • upsertStudent DB action keyed on the unique studentId (a null name never overwrites a known one).
  • CourseOfferings.rosterFetchedAt cache column (1-day TTL), following the documented parent-row cache-timestamp convention.

Behavior notes

  • Offerings absent from I-School Plus (internships, special entries with no number) resolve to an empty roster; the timestamp is recorded either way, so an empty/off-platform roster doesn't re-fetch on every stream emission.
  • Students with a null ID are skipped (can't key them without the unique studentId).
  • Demo mode works end-to-end — the mock course numbers in mock_course_service.dart match the mock roster handles.

Not in this PR

  • UI: the course-table detail sheet wiring (provider + i18n + sheet rendering) is intentionally left out — these methods aren't consumed yet. It'll follow in a separate PR so the data layer can be reviewed on its own.
  • Caching/index: querying courses-for-a-student (the reverse junction lookup) still needs the course_offering_student_student index per AGENTS.md; not added since this PR only does students-in-a-course.

Docs

  • AGENTS.md: CourseRepository now described as serving "related I-School Plus data"; dropped rosters from the repository-layer backlog gaps.

Wire CourseRepository to the I-School Plus roster: watchStudents and
refreshStudents fetch enrolled classmates, persist them to the Students
and CourseOfferingStudents tables, and cache via a new
CourseOfferings.rosterFetchedAt timestamp.
@rileychh-dokploy-riley-ntut-npc

rileychh-dokploy-riley-ntut-npc Bot commented Jun 2, 2026

Copy link
Copy Markdown

Dokploy Preview Deployment

Name Status Preview Updated (UTC)
API Docs ✅ Done Preview URL 2026-06-02T02:05:08.748Z

@github-actions

github-actions Bot commented Jun 2, 2026

Copy link
Copy Markdown

PR Preview Builds

Build Number: 1113
Commit: 7ec4389
Message: feat: add course roster repository methods

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR implements the repository/data-layer support for I-School Plus course rosters (classmates) by adding a cached roster watch/refresh flow in CourseRepository, plus the necessary database schema and helper DB action to persist rosters.

Changes:

  • Add CourseRepository.watchStudents(courseOfferingId) and refreshStudents(courseOfferingId) to stream cached rosters and refresh them from I-School Plus with a 1-day TTL.
  • Add CourseOfferings.rosterFetchedAt as the cache timestamp column for roster freshness tracking.
  • Add a DB helper upsertStudent keyed by unique studentId, preserving known names when incoming name is null.

Reviewed changes

Copilot reviewed 4 out of 5 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
lib/repositories/course_repository.dart Adds roster watch/refresh logic and DB writes for course offering rosters.
lib/database/schema.dart Adds rosterFetchedAt column to CourseOfferings to support roster caching TTL.
lib/database/database.g.dart Drift-generated updates reflecting the new rosterFetchedAt column.
lib/database/actions.dart Adds upsertStudent helper action keyed by Students.studentId.
AGENTS.md Updates repository responsibility documentation to include related I-School Plus data.
Files not reviewed (1)
  • lib/database/database.g.dart: Language not supported

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +941 to +947
if (students.isEmpty) {
try {
await refreshStudents(courseOfferingId);
} catch (_) {
// Absorb: yield empty below so UI exits loading state
}
}
Comment on lines +989 to +997
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]);
@rileychh

Copy link
Copy Markdown
Member Author

Drafting because NTUT's plan to migrate off i-School.

@rileychh
rileychh marked this pull request as draft July 13, 2026 17:04
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants