Skip to content

feat(gradebook): CSV grade import for external assessments#8465

Open
LWS49 wants to merge 2 commits into
lws49/feat-ext-assessments-pr3-validationfrom
lws49/feat-ext-assessments-pr4-import
Open

feat(gradebook): CSV grade import for external assessments#8465
LWS49 wants to merge 2 commits into
lws49/feat-ext-assessments-pr3-validationfrom
lws49/feat-ext-assessments-pr4-import

Conversation

@LWS49

@LWS49 LWS49 commented Jun 28, 2026

Copy link
Copy Markdown
Collaborator

Summary

Adds CSV grade import for external assessments, reached through "Import CSV" in the manage panel. A three-step wizard defines the components, confirms the required headers and file, then previews the resolved rows before writing. Students are matched by External ID (Coursemology's term) or email, keyed by column position; headers are accepted in any order with duplicate-header reporting and near-miss ("did you mean") suggestions. The Verify step pairs each CSV identifier with its resolved student name, flags out-of-range cells as a non-blocking advisory, and blocks only on unresolved identifiers or non-numeric cells. Grade conflicts are shown as a per-student change matrix (old to new, unchanged cells dropped), and writes are batched with student emails preloaded to avoid N+1s.

Design decisions

  • The wizard is a thin renderer of backend-decided state: out-of-range detection, structured conflict rows, header suggestions, and reassigned-identifier detection are computed in the import service and emitted in the preview payload - this keeps validation in one place and the wizard presentation-only.
  • Import weightage is zeroed server-side unless the weighted view is enabled, placed in the controller rather than the service - the service stays a pure writer, matching where the manual-add path enforces the same rule; the hidden frontend field was previously the only guard, so a stale request could write a nonzero weight into a weighting-off course.
  • Conflicts are shown as a per-student change matrix with unchanged cells dropped - the prior flat list re-counted cells identical at two decimals; the matrix shows only real changes (old to new) grouped by the student they affect.
  • Identifier-first display (the CSV value plus a resolved-name column) replaces name-only rows - under an identifier-mode header, showing only the resolved name read as a mismatch; pairing the two confirms the match.

Regression prevention

Covers (backend): header order tolerance, duplicate-header reporting, near-miss header suggestion, identifier resolution and reassigned-identifier detection, out-of-range computation, structured conflict rows, the weightage-zeroing guard, the email-preload N+1 fix, and batched writes (import service and imports controller specs). Covers (frontend): the full wizard (define / template / upload / verify, did-you-mean, loading), the conflict change-matrix table (struck old value, arrow, bold new value, blank sides shown as a dash, new-fill vs existing fallback), and the template builder. Adds the imports controller route; no schema change.

@LWS49 LWS49 force-pushed the lws49/feat-ext-assessments-pr3-validation branch from 38c0e8e to c75dd03 Compare June 28, 2026 14:42
@LWS49 LWS49 force-pushed the lws49/feat-ext-assessments-pr4-import branch from 2d5ebeb to 6f62578 Compare June 28, 2026 14:52
@LWS49 LWS49 force-pushed the lws49/feat-ext-assessments-pr3-validation branch from c75dd03 to 32c59d0 Compare June 29, 2026 04:43
@LWS49 LWS49 force-pushed the lws49/feat-ext-assessments-pr4-import branch from 6f62578 to 245d005 Compare June 29, 2026 04:43
@LWS49 LWS49 force-pushed the lws49/feat-ext-assessments-pr3-validation branch 2 times, most recently from bd90952 to 79fe3b4 Compare July 2, 2026 08:44
@LWS49 LWS49 force-pushed the lws49/feat-ext-assessments-pr4-import branch 2 times, most recently from f8c3eba to 4b9e5f7 Compare July 2, 2026 09:16
@LWS49 LWS49 force-pushed the lws49/feat-ext-assessments-pr3-validation branch from 79fe3b4 to b9d4ef3 Compare July 2, 2026 09:16
@LWS49 LWS49 force-pushed the lws49/feat-ext-assessments-pr4-import branch from 4b9e5f7 to 3e6bb82 Compare July 2, 2026 09:30
@LWS49 LWS49 force-pushed the lws49/feat-ext-assessments-pr3-validation branch from b9d4ef3 to fc1a3fb Compare July 2, 2026 10:11
@LWS49 LWS49 force-pushed the lws49/feat-ext-assessments-pr4-import branch from 3e6bb82 to d1cd29d Compare July 2, 2026 10:11
@LWS49 LWS49 force-pushed the lws49/feat-ext-assessments-pr3-validation branch from fc1a3fb to 4498638 Compare July 2, 2026 11:21
@LWS49 LWS49 force-pushed the lws49/feat-ext-assessments-pr4-import branch from d1cd29d to afbf1ce Compare July 2, 2026 11:23
@LWS49 LWS49 force-pushed the lws49/feat-ext-assessments-pr3-validation branch from 4498638 to 03634d3 Compare July 2, 2026 11:37
@LWS49 LWS49 force-pushed the lws49/feat-ext-assessments-pr4-import branch 2 times, most recently from 7d3da88 to 54b509f Compare July 2, 2026 12:15
@LWS49 LWS49 force-pushed the lws49/feat-ext-assessments-pr3-validation branch 2 times, most recently from d36dee7 to dee2b98 Compare July 2, 2026 14:47
@LWS49 LWS49 force-pushed the lws49/feat-ext-assessments-pr4-import branch from 54b509f to 292581d Compare July 2, 2026 14:48
@LWS49 LWS49 force-pushed the lws49/feat-ext-assessments-pr3-validation branch from dee2b98 to 88b2a31 Compare July 2, 2026 15:05
@LWS49 LWS49 force-pushed the lws49/feat-ext-assessments-pr4-import branch from 292581d to 1f3b07c Compare July 2, 2026 15:09
@LWS49 LWS49 force-pushed the lws49/feat-ext-assessments-pr3-validation branch from 88b2a31 to 173bc6f Compare July 2, 2026 15:28
Introduce the full external-assessment CSV import feature as a single
slice on top of grade validation (pr3).

Backend:
- Course::Gradebook::ExternalAssessmentImportService — header-order
  tolerance, duplicate-header/identifier guards, reassigned-identifier
  detection, out-of-range flagging, and batched (bulk insert + upsert)
  grade writes.
- external_assessment_imports controller (preview + create/commit),
  create/preview jbuilders, and import routes.

Frontend:
- ImportExternalAssessmentsWizard (upload → define → verify), with the
  ExternalGradeConflict prompt/table change-matrix and buildTemplate.
- previewImport/commitImport operations + import API endpoints + types.
- ManageExternalAssessmentsPanel gains the Import CSV entry point.
@LWS49 LWS49 force-pushed the lws49/feat-ext-assessments-pr4-import branch from 1f3b07c to cb204b2 Compare July 2, 2026 15:29
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.

1 participant