feat(gradebook): gradebook overhaul - weights, external assessments, CSV import#8441
Open
LWS49 wants to merge 3 commits into
Open
feat(gradebook): gradebook overhaul - weights, external assessments, CSV import#8441LWS49 wants to merge 3 commits into
LWS49 wants to merge 3 commits into
Conversation
2220631 to
ad01bc8
Compare
…links & sorting
Introduce the course gradebook: a frozen-column table of students × assessments
with a column picker, CSV export, and per-grade links into submissions.
Gradebook table
- Page with TanStack-backed table: pinned checkbox + Name columns, sticky
header and Max Marks rows, frozen-column border seams that survive sticky
scroll compositing.
- Column picker (assessments grouped by tab/category) and CSV export of the
selected columns; empty-state hint when no data columns are chosen.
- External ID column, shown when any student has one.
- Grade cells link to the student's submission; a dismissible GradeLinkHint
banner explains the affordance (persisted per-user via useDismissibleOnce).
- "Search students" global search.
- Default sort with name ascending, null/undefined at bottom of sort regardless of order
Shared table builder
- getColumnCanGlobalFilter is gated on column visibility ("search what you
see"): hiding a column via the picker removes it from search, and the
nullable-first-row type sniff is bypassed. Affects all TanStack tables.
Backend
- Gradebook controller, ability and course component; index JSON serializes
students, assessments, submissions (with submissionId) and gamification.
- Submission grade query also selects the submission id for grade links.
- Remove the redundant ScoreAssessmentSummary download from Statistics.
4c71a02 to
1b32632
Compare
Add weighted view built on top of gradebook: - add tables course_gradebook_contributions and course_gradebook_assessment_contributions - weighted table with equal/custom weight modes and per-assessment weight inputs, with a sum gate on custom weights - points / percentage display toggle - inline per-student assessment breakdown (row expand) - projected-total hint - gradebook_excluded column, serialization, and update-weights API echo - per-assessment include/exclude in the configure-weights modal, seeding custom weights from included assessments only - excluded assessments shown in the breakdown with no contribution - add SegmentedSelect component for stylized selection that is not "on-off"
7d9fdb4 to
ade1ddf
Compare
ad01bc8 to
82c7316
Compare
- add ExternalAssessment/Grade models, CRUD + grade-set endpoints gated to teaching staff, scoped to the course - extend gradebook contributions to weight an external contributor (XOR validation), serialized as a synthetic negative-id grouping - CSV import wizard (define/upload/verify/conflict) with dry-run preview, upsert commit, and conflict/mismatch detection - FE store, operations, and inline add/rename/delete/grade/max editing for external columns in the gradebook table - surface external assessments in gradebook index JSON and route weight entries to external contributions - add controller, model, service, and component specs
c799dd5 to
2de895b
Compare
521efed to
2efbb02
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
This PR ships the complete gradebook overhaul: a new gradebook page with column picker and CSV export; a weighted view with configurable per-tab weights, keep-highest rules, and projected totals; an externally-graded assessments system (add/rename/delete columns, per-student inline grade editing); and a CSV bulk import wizard that lets professors define component metadata, download a template, upload filled marks, verify a preview, resolve grade conflicts, and commit in one flow.
Externals are surfaced as a synthetic "External Assessments" category in the gradebook API response: the backend assembles them from
course_external_assessmentsand serializes them with negative IDs (distinguishing from real assessment IDs), avoiding any schema change to the existing categories/tabs structure. Weight contributions are stored in a newcourse_gradebook_contributionstable that accepts both realassessment_identries and anexternal_assessment_idslot; the weighted total is a canvas-style additive sum (no normalization cap, bonus allowed).The CSV import uses a two-gate flow:
preview(dry-run — resolves every identifier against the current roster, returns a sample + conflict list, writes nothing) followed bycommit(re-validates inside one transaction, authoritative). Re-importing into an existing component upserts grades only — max marks and weightage are never touched. The binding key is the resolvedcourse_user_id; the raw identifier string is snapshotted intoimported_identifierfor audit and reassigned-ID mismatch detection.Design decisions
Synthetic negative-ID serialization for externals - The gradebook index view assembles external assessments into a virtual category with negative IDs rather than polluting the real assessment tree. This lets the frontend treat externals uniformly in the column picker, weighted table, and CSV export without a schema migration on existing categories/tabs.
Component metadata in the dialog, not the CSV - The import wizard collects name, weightage, and max marks in a dialog form; the CSV carries only
Identifier+ one grade column per component. This avoids professors having to format a structured header row with embedded metadata and keeps the CSV human-editable with a plain template.preview+committwo-gate instead of a single upload -commitre-validates entirely server-side in one transaction and never trusts a client-sent conflict list. The client posts the same payload both times; the preview result is purely advisory. This prevents a race between preview and commit (roster changes, concurrent imports) from silently committing bad data.Conflict UI is a design copy, not a shared component -
ExternalGradeConflictPrompt/Tablemirror the visual design ofExternalIdConflictPrompt/Table(user-invitations) but are independent components acting on grades. Sharing the component would couple two unrelated domains through a generic prop interface neither actually owns.Regression prevention
Covers: gradebook load and column picker toggle, CSV export output shape, weighted total calculation (equal/custom/keep-highest), external assessment CRUD, per-student inline grade edit, import service (preview dry-run, fresh import, upsert keep/replace, blank cell handling, conflict detection, identifier mismatch, determinacy), import controller (authorization, malformed grades, duplicate components, email mode, conflicts in response), template CSV escaping, wizard step flow (no conflicts, unresolved error, conflict prompt keep + replace paths).
Manual testing confirmed: all 8 scenarios ran including both conflict resolution paths (Keep Existing + Replace), email-mode import, and the error path with an unresolved student ID.
Existing gradebook controller, external assessments controller, and contribution model specs updated to cover the new behaviour. No changes to existing assessment or submission write paths.