Skip to content

PR 6: refactor: per-area small refactors (CTS, Scheduler, Students, CMS)#179

Open
rlorenzo wants to merge 6 commits into
mainfrom
refactor/per-area-small
Open

PR 6: refactor: per-area small refactors (CTS, Scheduler, Students, CMS)#179
rlorenzo wants to merge 6 commits into
mainfrom
refactor/per-area-small

Conversation

@rlorenzo

@rlorenzo rlorenzo commented May 5, 2026

Copy link
Copy Markdown
Contributor

Summary

Per-area dedup driven by jscpd findings on the original code-anaylsis branch: 4 self-contained refactors across CTS, Scheduler, Students, and CMS (11 files). The Students refactor also uncovered and fixed two photo-gallery bugs (see below), so this PR carries behavior fixes and new regression tests in addition to the dedup.

(This was formerly "Part 6 of 6" of a stack; PRs #174 through #178 are all merged to main, and this branch is rebased off the latest main, so it now stands alone.)

Commits

  • refactor(cts): collapse hardcoded CourseStudents rows into a v-for: replace 3 near-identical <tr> blocks with a single v-for.
  • refactor(scheduler): share schedule filter LINQ via IScheduleEntity: introduce IScheduleEntity + ScheduleQueryExtensions so the by-clinician / by-rotation / by-week scheduling queries share their filter LINQ.
  • refactor(students): consolidate StudentGroupService photo + Ross helpers: extract BuildStudentPhotoListAsync, FormatStudentDisplayName, FormatGroupAssignment, ResolvePhotoUrl, and GetActiveRossIamIdsAsync so the by-class-level / by-group / by-course paths no longer hand-roll the same logic. This commit also includes:
    • Photo gallery bug fix. The gallery was returning zero students for every filter. Root cause: the queries projected into a StudentBaseRecord constructor and then applied OrderBy / Where, which EF Core cannot translate; the resulting runtime InvalidOperationException was swallowed by a catch and surfaced as an empty (but 200 OK) result. Fixed by moving ordering and filtering ahead of the projection and rethrowing with context instead of silently returning empty.
    • Trimmed GroupAssignment. The combined group label was built from untrimmed values while the individual EighthsGroup / TwentiethsGroup fields were trimmed; padded AAUD values no longer leak into the displayed "1A1 / 1AA" label.
    • EF.Parameter() on large Contains lists. The Ross IAM-id and enrolled-PIDM lookups now wrap their collections in EF.Parameter(...) so SQL Server translates them via OPENJSON (per the >10-item guideline) rather than inlining hundreds of literals.
    • Regression tests (test/Students/StudentGroupServiceQueryTests.cs): SQLite-backed tests that exercise all three query paths end to end, so the projection-translation failure cannot silently regress again.
  • refactor(cms): reduce Codecs to UU-only and cover with tests: collapse the UU encode/decode methods onto shared helpers (DecodeWithMap, EncodeWithMap), delete the unused XX variants and their maps (ColdFusion only ever uses the default UU encoding, so XX was dead in both the legacy app and here), and add CodecsTests. UUEncode has no caller yet but is kept for the CMS migration, where new files must store their AES key UU-encoded so legacy CF can decrypt them during the parallel-run period.
  • fix(students): avoid cross-context join in course Ross gallery: second photo-gallery bug, in the course path. Requesting Ross students for a course joined the Courses and AAUD DbContexts in a single query ("Cannot use multiple context instances within a single query execution"); before the projection fix above this surfaced as a silently empty gallery, after it as a 500. The Ross lookup now resolves against the RE-filtered enrolled-PIDM list already loaded from the Courses context, which also keeps withdrawn/waitlisted Ross students out of course galleries (previously the Ross-only lookup ignored enrollment status). Adds a regression test for the include-Ross course path, including a withdrawn Ross student that must stay excluded.
  • refactor(students): add AsNoTracking and EF.Parameter to Ross queries: review-feedback query hygiene; .AsNoTracking() on the read-only AAUD lookups and EF.Parameter(...) on the remaining list-based Contains filters in the Ross paths.

Test plan

  • CI green
  • CTS course students table renders correctly (5+ students)
  • Clinical Scheduler: by-clinician / by-rotation / by-week views still filter the same set
  • Students photo gallery: by-class-level / by-group / by-course flows match prior behavior; Ross students excluded/included as before (verified live: 155 V3 students with Ross excluded, 156 with Ross included; re-verified after the course Ross fix: V4 shows 153/154 across both tabs, and course galleries return 200 with includeRossStudents=true across multiple courses; StudentGroupServiceQueryTests cover all three paths plus the include-Ross course path in CI)
  • CMS Codecs: UU round-trip and canonical wire format ("Cat" / "#0V%T") now covered by automated tests (CodecsTests, run in CI); XX path removed

@coderabbitai

coderabbitai Bot commented May 5, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Adds IScheduleEntity and ApplyScheduleFilters; refactors Codecs to map-driven EncodeWithMap/DecodeWithMap and adds UU tests; consolidates StudentGroupService projection/photo/Ross IAM logic with relational tests; converts CourseStudents.vue logged-in rows to data-driven rendering.

Changes

Schedule Filtering Abstraction

Layer / File(s) Summary
IScheduleEntity contract and model implementations
web/Models/CTS/IScheduleEntity.cs, web/Models/CTS/InstructorSchedule.cs, web/Models/CTS/StudentSchedule.cs
IScheduleEntity interface exposes MothraId, RotationId, ServiceId, WeekId, DateStart/DateEnd, and Week; InstructorSchedule and StudentSchedule implement it.
ScheduleQueryExtensions with ApplyScheduleFilters
web/Areas/ClinicalScheduler/Services/ScheduleQueryExtensions.cs
ApplyScheduleFilters extension conditionally composes LINQ where clauses for class year (via Week.WeekGradYears), Mothra ID, rotation, service, week, and date-range overlap based on nullable inputs.
Service adoption of ApplyScheduleFilters
web/Areas/ClinicalScheduler/Services/InstructorScheduleService.cs, web/Areas/ClinicalScheduler/Services/StudentScheduleService.cs
InstructorScheduleService and StudentScheduleService replace inline per-parameter Where chains with a single ApplyScheduleFilters call applied after AsNoTracking().

Codec Parameterization and Testing

Layer / File(s) Summary
Codecs.cs parameterized encode/decode
web/Areas/CMS/Data/Codecs.cs
UUDecode and UUEncode public entry points; internal DecodeWithMap/EncodeWithMap accept lookup tables, use MapByte validation that throws FormatException on malformed input, and emit encoded characters via provided encMap/decMap.
CodecsTests comprehensive suite
test/CMS/CodecsTests.cs
xUnit tests validate encode→decode round-trips across representative lengths and all 0–255 bytes, pin canonical UU line for "Cat", assert empty-stream behaviour, null-argument ParamName checks, and malformed decode FormatException cases.

StudentGroupService Consolidation

Layer / File(s) Summary
StudentBaseRecord projection and helper consolidation
web/Areas/Students/Services/StudentGroupService.cs
Adds StudentBaseRecord projection; centralizes non-Ross photo construction via BuildStudentPhotoListAsync; centralizes Ross IAM lookup with GetActiveRossIamIdsAsync and uses EF.Parameter(...).Contains(...) for AAUD predicates; ResolvePhotoUrl now falls back to defaultPhotoUrl; FormatGroupAssignment rewritten; selective methods now throw contextual InvalidOperationExceptions on DB errors.

StudentGroupService Relational Tests

Layer / File(s) Summary
SQLite-backed query translation tests
test/Students/StudentGroupServiceQueryTests.cs
Adds tests and fixture using SQLite for AAUD translation and in-memory providers for other contexts; verifies class-level ordering, group filtering, course enrollment behavior, and Ross-inclusion logic; disposes contexts and shared connection.

CourseStudents Table Data-Driven Rendering

Layer / File(s) Summary
Data-driven table and select bindings
VueApp/src/CTS/pages/CourseStudents.vue
loggedInStudents array replaces hardcoded rows; table body uses v-for with dynamic Assess Competency route/aria-label and student name/time; Remove button aria-label made dynamic; select v-model repositioned.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Suggested reviewers

  • bsedwards
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 28.21% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed Title accurately summarizes the changeset: four self-contained refactors across CTS, Scheduler, Students, and CMS areas.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Description check ✅ Passed PR description directly addresses all four refactors (CTS, Scheduler, Students, CMS), cites specific commits, documents a bug fix with root cause, describes test coverage, and matches the changeset.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch refactor/per-area-small

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@rlorenzo rlorenzo force-pushed the refactor/per-area-small branch from 25b942b to d0b2e7f Compare May 5, 2026 07:39
@rlorenzo rlorenzo force-pushed the refactor/dead-code-and-shared-chrome branch from ebe32ec to 75abeb2 Compare May 5, 2026 07:39
@rlorenzo rlorenzo force-pushed the refactor/per-area-small branch from d0b2e7f to 0ea0117 Compare May 5, 2026 14:47
@rlorenzo rlorenzo force-pushed the refactor/dead-code-and-shared-chrome branch 5 times, most recently from d26c5d8 to 8cbfb69 Compare May 7, 2026 15:57
@rlorenzo rlorenzo force-pushed the refactor/per-area-small branch from 0ea0117 to 0fa6909 Compare May 7, 2026 15:57
@rlorenzo rlorenzo force-pushed the refactor/dead-code-and-shared-chrome branch 2 times, most recently from 0d62a4e to a0c55c4 Compare May 9, 2026 05:22
@rlorenzo rlorenzo force-pushed the refactor/per-area-small branch from 0fa6909 to 5cbd5f4 Compare May 9, 2026 05:35
@rlorenzo rlorenzo force-pushed the refactor/dead-code-and-shared-chrome branch from a0c55c4 to d8a3735 Compare May 9, 2026 17:27
@rlorenzo rlorenzo force-pushed the refactor/per-area-small branch from 5cbd5f4 to 8eb247a Compare May 9, 2026 17:27
@rlorenzo rlorenzo force-pushed the refactor/dead-code-and-shared-chrome branch from d8a3735 to ddd64e7 Compare May 11, 2026 16:29
@rlorenzo rlorenzo force-pushed the refactor/per-area-small branch from 8eb247a to a461dcd Compare May 11, 2026 16:30
@rlorenzo rlorenzo force-pushed the refactor/dead-code-and-shared-chrome branch from ddd64e7 to 66fe537 Compare May 12, 2026 02:33
@rlorenzo rlorenzo force-pushed the refactor/per-area-small branch from a461dcd to 6d0722d Compare May 12, 2026 03:18
@rlorenzo rlorenzo force-pushed the refactor/dead-code-and-shared-chrome branch from 66fe537 to d09da71 Compare May 21, 2026 06:36
Base automatically changed from refactor/dead-code-and-shared-chrome to main May 21, 2026 07:19
@rlorenzo rlorenzo force-pushed the refactor/per-area-small branch from 6d0722d to 9c30e16 Compare May 21, 2026 08:02
@codecov-commenter

codecov-commenter commented May 21, 2026

Copy link
Copy Markdown

Bundle Report

Changes will decrease total bundle size by 894 bytes (-0.04%) ⬇️. This is within the configured threshold ✅

Detailed changes
Bundle name Size Change
viper-frontend-esm 2.13MB -894 bytes (-0.04%) ⬇️

Affected Assets, Files, and Routes:

view changes for bundle: viper-frontend-esm

Assets Changed:

Asset Name Size Change Total Size Change (%)
assets/CourseStudents-*.js -894 bytes 1.94kB -31.53%

Files in assets/CourseStudents-*.js:

  • ./src/CTS/pages/CourseStudents.vue → Total Size: 145 bytes

@codecov-commenter

codecov-commenter commented May 21, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 71.11111% with 65 lines in your changes missing coverage. Please review.
✅ Project coverage is 44.21%. Comparing base (f45f6b7) to head (0bf9919).
⚠️ Report is 66 commits behind head on main.

Files with missing lines Patch % Lines
web/Areas/Students/Services/StudentGroupService.cs 79.35% 28 Missing and 4 partials ⚠️
...nicalScheduler/Services/ScheduleQueryExtensions.cs 0.00% 31 Missing ⚠️
...calScheduler/Services/InstructorScheduleService.cs 0.00% 1 Missing ⚠️
...inicalScheduler/Services/StudentScheduleService.cs 0.00% 1 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main     #179      +/-   ##
==========================================
+ Coverage   43.02%   44.21%   +1.18%     
==========================================
  Files         881      894      +13     
  Lines       51436    51604     +168     
  Branches     4812     4810       -2     
==========================================
+ Hits        22131    22815     +684     
+ Misses      28779    28225     -554     
- Partials      526      564      +38     
Flag Coverage Δ
backend 44.35% <71.11%> (+1.25%) ⬆️
frontend 41.32% <ø> (-0.16%) ⬇️

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

@rlorenzo rlorenzo force-pushed the refactor/per-area-small branch from 9c30e16 to 743f4e5 Compare May 21, 2026 16:07
@rlorenzo rlorenzo force-pushed the refactor/per-area-small branch from 743f4e5 to b8152a2 Compare June 1, 2026 20:07
@rlorenzo

rlorenzo commented Jun 1, 2026

Copy link
Copy Markdown
Contributor Author

@coderabbitai full review

@coderabbitai

coderabbitai Bot commented Jun 1, 2026

Copy link
Copy Markdown
Contributor
✅ Actions performed

Full review triggered.

@coderabbitai coderabbitai Bot 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.

Actionable comments posted: 5

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
web/Areas/Students/Services/StudentGroupService.cs (1)

407-433: 🧹 Nitpick | 🔵 Trivial | ⚡ Quick win

Add .AsNoTracking() to read-only query.

Same issue. Insert .AsNoTracking() before .OrderBy().

⚡ Suggested fix
                                sg != null ? sg.StudentgrpV3grp : null);

         // Apply group filtering if specified
         if (!string.IsNullOrEmpty(groupType) && !string.IsNullOrEmpty(groupId))
         {
             query = groupType.ToLower() switch
             {
                 "eighths" => query.Where(s => s.EighthsGroup == groupId),
                 "twentieths" => query.Where(s => s.TwentiethsGroup == groupId),
                 "teams" => query.Where(s => s.TeamNumber == groupId),
                 "v3specialty" => query.Where(s => s.V3SpecialtyGroup == groupId),
                 _ => query
             };
         }

-var students = await query.OrderBy(s => s.LastName).ThenBy(s => s.FirstName).ToListAsync();
+var students = await query.AsNoTracking().OrderBy(s => s.LastName).ThenBy(s => s.FirstName).ToListAsync();
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@web/Areas/Students/Services/StudentGroupService.cs` around lines 407 - 433,
The LINQ query building a sequence of StudentBaseRecord is read-only and should
use AsNoTracking to avoid EF change tracking overhead; update the code that
constructs "query" (the projection creating new StudentBaseRecord in
StudentGroupService) to call .AsNoTracking() on the IQueryable before applying
.OrderBy(...).ThenBy(...)/ToListAsync(), preserving any groupType filtering
logic (the switch) so call .AsNoTracking() on the final query instance just
prior to ordering and materialization.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@test/CMS/CodecsTests.cs`:
- Line 4: The namespace declared as Test.CMS doesn't match the project's
expected root namespace; update the namespace declaration to Viper.test.CMS
(replace the Test.CMS namespace token) so the class/test types in this file use
the Viper.test.CMS namespace and satisfy the ReSharper/CI checks.

In `@web/Areas/Students/Services/StudentGroupService.cs`:
- Around line 765-772: The query against _sisContext.StudentDesignations in
StudentGroupService should be executed as a read-only query; insert
.AsNoTracking() immediately after _sisContext.StudentDesignations (before the
existing .Where(...) calls) so the EF Core change tracker is not used for this
read-only projection (retain the rest of the chain:
.Where(...).Select(...).Where(...).Distinct().ToListAsync()).
- Line 738: Remove the null-forgiving operator from student.MailId in
StudentGroupService.cs and handle nullable MailId properly: either filter the
students iteration to only include those with MailId != null before mapping, or
assign MailId = student.MailId (no !) and ensure the consumer (e.g., the
StudentPhoto model or downstream code) accepts a nullable MailId; locate the
mapping where MailId = student.MailId! and replace it with a safe nullable-aware
approach consistent with how MailId is handled elsewhere (see other assignments
around lines with StudentPhoto.MailId).
- Around line 113-126: The LINQ query building StudentBaseRecord instances is
read-only but currently tracks entities; update the query used in
StudentGroupService (the variable named query that projects to
StudentBaseRecord) to call .AsNoTracking() before the OrderBy/ThenBy and
ToListAsync so EF Core doesn't enable change tracking for this read-only
projection; locate the projection creating new StudentBaseRecord and insert
.AsNoTracking() on that IQueryable prior to .OrderBy(s => s.LastName).ThenBy(s
=> s.FirstName).ToListAsync().
- Around line 341-354: The query projection creating StudentBaseRecord from
queryBase should be treated as read-only—modify the LINQ chain so that the
IQueryable returned by the Select (the variable named query) uses AsNoTracking()
before applying OrderBy/ThenBy; locate the Select(...) producing
StudentBaseRecord in StudentGroupService (the variable query) and insert
.AsNoTracking() on that query before calling OrderBy(s => s.LastName).ThenBy(s
=> s.FirstName).ToListAsync().

---

Outside diff comments:
In `@web/Areas/Students/Services/StudentGroupService.cs`:
- Around line 407-433: The LINQ query building a sequence of StudentBaseRecord
is read-only and should use AsNoTracking to avoid EF change tracking overhead;
update the code that constructs "query" (the projection creating new
StudentBaseRecord in StudentGroupService) to call .AsNoTracking() on the
IQueryable before applying .OrderBy(...).ThenBy(...)/ToListAsync(), preserving
any groupType filtering logic (the switch) so call .AsNoTracking() on the final
query instance just prior to ordering and materialization.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: d79bf74a-c6b5-4d4d-84dc-7f186950c9ab

📥 Commits

Reviewing files that changed from the base of the PR and between ed1f48b and b8152a2.

📒 Files selected for processing (10)
  • VueApp/src/CTS/pages/CourseStudents.vue
  • test/CMS/CodecsTests.cs
  • web/Areas/CMS/Data/Codecs.cs
  • web/Areas/ClinicalScheduler/Services/InstructorScheduleService.cs
  • web/Areas/ClinicalScheduler/Services/ScheduleQueryExtensions.cs
  • web/Areas/ClinicalScheduler/Services/StudentScheduleService.cs
  • web/Areas/Students/Services/StudentGroupService.cs
  • web/Models/CTS/IScheduleEntity.cs
  • web/Models/CTS/InstructorSchedule.cs
  • web/Models/CTS/StudentSchedule.cs

Comment thread test/CMS/CodecsTests.cs Outdated
Comment thread web/Areas/Students/Services/StudentGroupService.cs Outdated
Comment thread web/Areas/Students/Services/StudentGroupService.cs Outdated
Comment thread web/Areas/Students/Services/StudentGroupService.cs Outdated
Comment thread web/Areas/Students/Services/StudentGroupService.cs Outdated

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 10 out of 10 changed files in this pull request and generated no new comments.

@rlorenzo

Copy link
Copy Markdown
Contributor Author

@CodeRabbit review

@coderabbitai

coderabbitai Bot commented Jun 10, 2026

Copy link
Copy Markdown
Contributor
Action performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Pull request overview

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

Comment thread web/Areas/Students/Services/StudentGroupService.cs Outdated
Comment thread web/Areas/Students/Services/StudentGroupService.cs Outdated
@rlorenzo rlorenzo force-pushed the refactor/per-area-small branch from b25beef to 83489ac Compare June 10, 2026 16:45
@rlorenzo

Copy link
Copy Markdown
Contributor Author

@coderabbitai full review

@coderabbitai

coderabbitai Bot commented Jun 10, 2026

Copy link
Copy Markdown
Contributor
✅ Action performed

Full review finished.

@coderabbitai coderabbitai Bot 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.

Actionable comments posted: 4

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
VueApp/src/CTS/pages/CourseStudents.vue (1)

48-84: ⚠️ Potential issue | 🟠 Major | 🏗️ Heavy lift

Replace native table row/cell markup with Quasar table components in this changed block.

Line 48 introduces a v-for on native <tr>/<td>, which violates the Vue guideline requiring Quasar components instead of HTML elements. Refactor this table rendering to q-table (or Quasar table primitives) so this section is compliant.

As per coding guidelines, "**/*.vue: Use Quasar components (q-btn, q-dialog, q-banner, etc.) instead of HTML elements."

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@VueApp/src/CTS/pages/CourseStudents.vue` around lines 48 - 84, The native
<tr>/<td> loop rendering loggedInStudents must be replaced with Quasar table
primitives: use <q-table> with :rows="loggedInStudents" and a body slot (or use
QTr/QTd components) instead of v-for on <tr>; map columns for name/time and
render the action cells using the existing q-btn/q-icon inside QTd, keeping the
:key as row.id and preserving the AssessmentCompetency route construction and
aria-labels (refer to loggedInStudents, AssessmentCompetency route string, and
the q-btn/q-icon instances) so the table uses Quasar components throughout.

Source: Coding guidelines

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@test/Students/StudentGroupServiceQueryTests.cs`:
- Around line 200-236: Add a negative Ross fixture to the existing test
GetStudentsByCourseAsync_IncludeRoss_ReturnsRossStudentEnrolledInCourse: create
another SeedStudent + StudentDesignation pair (use a distinct pkey/pidm like
"P10"/"PIDM10" and matching IamId) and add a Roster entry for that pidm with
RosterCrn "12345" but set RosterEnrollStatus to a non-"RE" value (e.g. "WD" or
"DR"). Save to _sisContext and _coursesContext like the others, then keep the
existing call to _service.GetStudentsByCourseAsync(Term, "12345",
includeRossStudents: true) and add an assertion that this non-RE Ross student is
not present in result (e.g. assert no s with LastName matching the new seed or
that result.Count remains 2). Ensure you reference the existing SeedStudent,
StudentDesignation, Roster, _sisContext/_coursesContext and
GetStudentsByCourseAsync in the change.

In `@web/Areas/ClinicalScheduler/Services/ScheduleQueryExtensions.cs`:
- Around line 22-25: The current ScheduleQueryExtensions implementation uses
Week.WeekGradYears.Any(...) inside the Where, causing a correlated subquery;
instead preload the matching WeekIds from WeekGradYears (filter by GradYear and
Select WeekId/Distinct) and change the predicate to use
weekIds.Contains(s.WeekId). Add an overload on the
ApplyScheduleFilters/extension in ScheduleQueryExtensions that accepts an
IEnumerable<int> weekIds (or accept a nullable preloaded list) and, when
provided, apply query = query.Where(s => weekIds.Contains(s.WeekId)); keep the
original signature behavior when weekIds is null so callers that preload WeekIds
(from WeekGradYears where GradYear == classYear) can pass them in and avoid the
per-row Any().

In `@web/Areas/Students/Services/StudentGroupService.cs`:
- Around line 741-755: The GroupAssignment is being built from untrimmed
student.EighthsGroup and student.TwentiethsGroup which causes padded values to
appear even though the DTO fields are trimmed; fix by trimming those inputs
before calling FormatGroupAssignment (use student.EighthsGroup?.Trim() and
student.TwentiethsGroup?.Trim() when computing groupAssignment) so that
FormatGroupAssignment receives normalized values; ensure the existing
EighthsGroup and TwentiethsGroup assignments on the StudentPhoto DTO remain
trimmed as they are.
- Around line 447-459: The Ross-only lookup currently re-queries Rosters into
courseRosterPidms without the original RosterEnrollStatus == "RE" filter, which
can reintroduce withdrawn/waitlisted students; change the second query to reuse
the already-filtered enrolledPidms (the variable created for enrolled students)
instead of courseRosterPidms so the EF query in StudentGroupService (the
rossStudentsInCourse AAUD lookup) only considers actively enrolled PIDMs and
avoids re-adding non-enrolled Ross students.

---

Outside diff comments:
In `@VueApp/src/CTS/pages/CourseStudents.vue`:
- Around line 48-84: The native <tr>/<td> loop rendering loggedInStudents must
be replaced with Quasar table primitives: use <q-table> with
:rows="loggedInStudents" and a body slot (or use QTr/QTd components) instead of
v-for on <tr>; map columns for name/time and render the action cells using the
existing q-btn/q-icon inside QTd, keeping the :key as row.id and preserving the
AssessmentCompetency route construction and aria-labels (refer to
loggedInStudents, AssessmentCompetency route string, and the q-btn/q-icon
instances) so the table uses Quasar components throughout.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: 52013b18-6c8b-49ac-997d-893b713acd14

📥 Commits

Reviewing files that changed from the base of the PR and between 63047b9 and 829c1dd.

📒 Files selected for processing (11)
  • VueApp/src/CTS/pages/CourseStudents.vue
  • test/CMS/CodecsTests.cs
  • test/Students/StudentGroupServiceQueryTests.cs
  • web/Areas/CMS/Data/Codecs.cs
  • web/Areas/ClinicalScheduler/Services/InstructorScheduleService.cs
  • web/Areas/ClinicalScheduler/Services/ScheduleQueryExtensions.cs
  • web/Areas/ClinicalScheduler/Services/StudentScheduleService.cs
  • web/Areas/Students/Services/StudentGroupService.cs
  • web/Models/CTS/IScheduleEntity.cs
  • web/Models/CTS/InstructorSchedule.cs
  • web/Models/CTS/StudentSchedule.cs

Comment thread test/Students/StudentGroupServiceQueryTests.cs
Comment thread web/Areas/ClinicalScheduler/Services/ScheduleQueryExtensions.cs
Comment thread web/Areas/Students/Services/StudentGroupService.cs Outdated
Comment thread web/Areas/Students/Services/StudentGroupService.cs
@rlorenzo rlorenzo force-pushed the refactor/per-area-small branch from 829c1dd to 0366b1b Compare June 12, 2026 02:29
@rlorenzo

Copy link
Copy Markdown
Contributor Author

@coderabbitai review

@rlorenzo rlorenzo requested a review from Copilot June 12, 2026 03:01
@coderabbitai

coderabbitai Bot commented Jun 12, 2026

Copy link
Copy Markdown
Contributor
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 11 out of 11 changed files in this pull request and generated 3 comments.

Comment thread web/Areas/Students/Services/StudentGroupService.cs
Comment thread web/Areas/Students/Services/StudentGroupService.cs
Comment thread web/Areas/Students/Services/StudentGroupService.cs

@coderabbitai coderabbitai Bot 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.

Actionable comments posted: 2

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
web/Areas/Students/Services/StudentGroupService.cs (1)

459-463: ⚠️ Potential issue | 🟠 Major

Fix unparameterized Ross IAM membership Contains (use EF.Parameter)
rossStudentsInCourse.Contains(i.IdsIamId) is still a plain list membership check inside the EF query; parameterize it like the other Ross filters to keep SQL translation consistent.

🔧 Suggested fix
-                                                     where i.IdsIamId != null && rossStudentsInCourse.Contains(i.IdsIamId)
+                                                     where i.IdsIamId != null && EF.Parameter(rossStudentsInCourse).Contains(i.IdsIamId!)
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@web/Areas/Students/Services/StudentGroupService.cs` around lines 459 - 463,
The LINQ query uses rossStudentsInCourse.Contains(i.IdsIamId) unparameterized;
create an EF parameter for the collection and use that in the query. Before
building the query, create a parameter (e.g., var rossStudentsParam =
EF.Parameter(rossStudentsInCourse) or the project-specific EF.Parameter helper)
and replace rossStudentsInCourse.Contains(i.IdsIamId) with
rossStudentsParam.Contains(i.IdsIamId) inside the query that references
_aaudContext.Ids / People / Students (symbols: rossStudentsInCourse,
rossStudentsParam, i.IdsIamId, _aaudContext).

Source: Coding guidelines

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@test/Students/StudentGroupServiceQueryTests.cs`:
- Around line 108-151: SeedStudent currently always sets IdsMailid non-null so
the MailId ?? string.Empty branch in BuildStudentPhotoListAsync is untested;
modify the test fixtures by adding one student seeded via SeedStudent (or a new
overload/flag) with IdsMailid set to null (i.e., create an enrolled fixture
where IdsMailid is null) and then assert the returned photo list entry has
MailId == string.Empty; update assertions around BuildStudentPhotoListAsync to
include this null-mail regression case and ensure the new fixture is included in
the test ranges noted (also apply the same change for the other tests
referenced).

In `@web/Areas/Students/Services/StudentGroupService.cs`:
- Around line 718-754: BuildStudentPhotoListAsync currently manually projects
StudentBaseRecord -> StudentPhoto with a foreach; create a Mapperly mapping
(e.g., IStudentMapper or StudentMappers) that maps StudentBaseRecord to
StudentPhoto and refactor BuildStudentPhotoListAsync to use LINQ
students.Select(s => mapper.Map(s, photoUrls, defaultPhotoUrl)).ToList() instead
of the foreach; to enable the mapper to perform the same per-row logic, relocate
the helper methods ResolvePhotoUrl, FormatStudentDisplayName, and
FormatGroupAssignment so they are accessible to the mapper (make them
internal/static or move into the mapper partial), and ensure the mapper
signature accepts the photoUrls dictionary and defaultPhotoUrl so
HasPhoto/PhotoUrl, TeamNumber/V3SpecialtyGroup logic and trimming are applied
identically.

---

Outside diff comments:
In `@web/Areas/Students/Services/StudentGroupService.cs`:
- Around line 459-463: The LINQ query uses
rossStudentsInCourse.Contains(i.IdsIamId) unparameterized; create an EF
parameter for the collection and use that in the query. Before building the
query, create a parameter (e.g., var rossStudentsParam =
EF.Parameter(rossStudentsInCourse) or the project-specific EF.Parameter helper)
and replace rossStudentsInCourse.Contains(i.IdsIamId) with
rossStudentsParam.Contains(i.IdsIamId) inside the query that references
_aaudContext.Ids / People / Students (symbols: rossStudentsInCourse,
rossStudentsParam, i.IdsIamId, _aaudContext).
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: 536594cd-37e2-452f-8443-60ecb46085ec

📥 Commits

Reviewing files that changed from the base of the PR and between 829c1dd and 0366b1b.

📒 Files selected for processing (4)
  • test/CMS/CodecsTests.cs
  • test/Students/StudentGroupServiceQueryTests.cs
  • web/Areas/CMS/Data/Codecs.cs
  • web/Areas/Students/Services/StudentGroupService.cs

Comment thread test/Students/StudentGroupServiceQueryTests.cs
Comment thread web/Areas/Students/Services/StudentGroupService.cs

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 11 out of 11 changed files in this pull request and generated no new comments.

@rlorenzo rlorenzo requested a review from bsedwards June 12, 2026 08:06
@rlorenzo

Copy link
Copy Markdown
Contributor Author

@bsedwards Some code deduplication and photo gallery bugs

rlorenzo added 6 commits June 12, 2026 12:29
Photo-gallery queries order/filter on the source before projecting so EF Core can translate them, large Contains lists use EF.Parameter (OPENJSON), and the catch blocks rethrow with context instead of masking failures. Adds SQLite-backed regression tests.
The course photo gallery threw "Cannot use multiple context instances
within a single query execution" whenever Ross students were requested
and any were active for the term: the Ross-in-course lookup joined the
Courses and AAUD DbContexts in a single query, which EF cannot
translate. Before the gallery fix this surfaced as a silently empty
gallery (the exception was swallowed); after it, as a 500.

Resolve the Ross lookup against the RE-filtered enrolled PIDM list
already loaded from the Courses context, so only the AAUD context is
queried and withdrawn or waitlisted Ross students stay excluded. Add a
SQLite-backed regression test for the include-Ross course path, which
reproduces the failure on the old query and asserts a withdrawn Ross
student stays out.
@rlorenzo rlorenzo force-pushed the refactor/per-area-small branch from 485f63f to 0bf9919 Compare June 12, 2026 19:32
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.

3 participants