feat(gradebook): weighted view with weights and exclusions#8436
Conversation
a8b79ab to
8d139e0
Compare
42f0dfc to
1a7691c
Compare
8d139e0 to
c1ea96f
Compare
1a7691c to
0c9f4e7
Compare
90115ff to
422b4af
Compare
0c9f4e7 to
c8af6a7
Compare
422b4af to
44284da
Compare
3d7baf6 to
f3cc59a
Compare
44284da to
2d24331
Compare
cbeb1a6 to
44de60d
Compare
5ad6ad2 to
eda6ef5
Compare
16ea8b5 to
1c5b845
Compare
fea40ba to
2fb3c20
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"
2fb3c20 to
a1a8b69
Compare
|
|
||
| def define_permissions | ||
| can :read_gradebook, Course, id: course.id if course_user&.staff? | ||
| can :read_gradebook, Course, id: course.id if course_user&.manager_or_owner? |
There was a problem hiding this comment.
I see you have a separate PR open to restrict gradebook access to manager/owner.
However this is already doing that, does that mean the other PR is redundant now?
There was a problem hiding this comment.
yep, will retire that PR accordingly.
There was a problem hiding this comment.
Pull request overview
Adds an opt-in weighted gradebook view (with per-tab weights, per-assessment custom weights, and exclusions) while keeping the existing “all assessments” gradebook unchanged unless enabled via a new course admin setting. The PR introduces new backend persistence + endpoints for weights/settings and a full frontend weighted-table UI with configuration dialog and supporting tests/translations.
Changes:
- Backend: add gradebook weighting persistence (tab + assessment contribution tables/models) and APIs (index JSON enrichment +
PATCH /gradebook/weights) with authorization gates. - Frontend: add a new Weighted total gradebook view (toggleable via tabs), configuration UI, and supporting shared UI components (e.g.,
SegmentedSwitch). - Admin: add a new Gradebook settings page + routes to enable/disable weighted view.
Reviewed changes
Copilot reviewed 63 out of 63 changed files in this pull request and generated 7 comments.
Show a summary per file
| File | Description |
|---|---|
| spec/models/course/settings/gradebook_component_spec.rb | Adds specs for the new weighted_view_enabled course setting. |
| spec/models/course/gradebook/contribution_spec.rb | Adds model specs for tab-level gradebook contributions and bulk update behavior. |
| spec/models/course/gradebook/assessment_contribution_spec.rb | Adds model specs for per-assessment gradebook contribution rows. |
| spec/models/course/gradebook_ability_spec.rb | Adds ability specs for reading gradebook and managing weights/settings. |
| spec/factories/course_gradebook_contributions.rb | Introduces a factory for tab-level gradebook contributions. |
| spec/factories/course_gradebook_assessment_contributions.rb | Introduces a factory for assessment-level gradebook contributions. |
| spec/controllers/course/gradebook_controller_spec.rb | Expands controller specs for authorization, weighted fields, and update_weights. |
| spec/controllers/course/admin/gradebook_settings_controller_spec.rb | Adds controller specs for the new gradebook settings admin endpoints. |
| db/schema.rb | Updates schema to include new gradebook contribution tables and FKs. |
| db/migrate/20260611000000_create_gradebook_contribution_tables.rb | Adds migration creating tab + assessment contribution tables. |
| config/routes.rb | Adds routes for gradebook settings and PATCH /gradebook/weights. |
| config/locales/zh/activerecord/errors.yml | Adds i18n for custom-weight mismatch validation. |
| config/locales/ko/activerecord/errors.yml | Adds i18n for custom-weight mismatch validation. |
| config/locales/en/activerecord/errors.yml | Adds i18n for custom-weight mismatch validation. |
| client/locales/zh.json | Adds client strings for weighted gradebook UI + settings. |
| client/locales/ko.json | Adds client strings for weighted gradebook UI + settings. |
| client/locales/en.json | Adds client strings for weighted gradebook UI + settings. |
| client/app/types/course/gradebook.ts | Extends gradebook payload types with weighting fields + update payload. |
| client/app/types/course/admin/gradebook.ts | Adds types for gradebook settings admin API payloads. |
| client/app/routers/course/admin.tsx | Adds admin route for the new Gradebook settings page. |
| client/app/lib/components/wrappers/mocks/I18nProvider.tsx | Adds a synchronous I18nProvider mock for deterministic tests. |
| client/app/lib/components/core/buttons/SegmentedSwitch.tsx | Introduces a new compact segmented switch control with keyboard support. |
| client/app/lib/components/core/buttons/test/SegmentedSwitch.test.tsx | Adds tests for SegmentedSwitch behavior and accessibility roles. |
| client/app/bundles/course/gradebook/types.ts | Re-exports UpdateWeightsPayload for gradebook bundle use. |
| client/app/bundles/course/gradebook/store.ts | Adds weighted-view flags and reducer support for updating weights/exclusions. |
| client/app/bundles/course/gradebook/selectors.ts | Adds selectors for weighted view enabled + manage-weights capability. |
| client/app/bundles/course/gradebook/pages/GradebookIndex/index.tsx | Adds view toggle (All vs Weighted), weighted hint, and weighted table rendering. |
| client/app/bundles/course/gradebook/operations.ts | Adds updateGradebookWeights operation calling the new API endpoint. |
| client/app/bundles/course/gradebook/computeWeighted.ts | Implements weighted subtotal/total math, breakdowns, and default weight resolution. |
| client/app/bundles/course/gradebook/components/WeightedViewHint.tsx | Adds one-time dismissible hint prompting managers to enable weighted view. |
| client/app/bundles/course/gradebook/components/WeightedGradebookTable.tsx | Adds the main weighted gradebook table UI (display mode, breakdowns, CSV, etc.). |
| client/app/bundles/course/gradebook/components/WeightedGradebookColumnTree.tsx | Adds column picker tree for the weighted table (student-info group only). |
| client/app/bundles/course/gradebook/components/ProjectedTotalHint.tsx | Adds one-time projected-total policy hint + shared tooltip translation. |
| client/app/bundles/course/gradebook/components/ConfigureWeightsPrompt.tsx | Adds UI to configure tab weights, modes, custom weights, and exclusions. |
| client/app/bundles/course/gradebook/tests/WeightedViewHint.test.tsx | Tests the weighted-view discovery hint and dismissal persistence. |
| client/app/bundles/course/gradebook/tests/WeightedGradebookColumnTree.test.tsx | Tests the weighted column picker tree behavior. |
| client/app/bundles/course/gradebook/tests/store.test.ts | Tests reducer behavior for updating weights, exclusions, and modes. |
| client/app/bundles/course/gradebook/tests/ProjectedTotalHint.test.tsx | Tests the projected-total hint and dismissal persistence. |
| client/app/bundles/course/gradebook/tests/GradebookIndex.test.tsx | Tests view toggle rendering and hint visibility rules. |
| client/app/bundles/course/gradebook/tests/ConfigureWeightsPrompt.test.tsx | Tests configure-weights dialog behavior (modes, sums, exclusions, save payload). |
| client/app/bundles/course/gradebook/tests/computeWeighted.test.ts | Tests weighted math, defaults, exclusions, and breakdown computations. |
| client/app/bundles/course/admin/pages/GradebookSettings/translations.ts | Adds translations for the admin gradebook settings page. |
| client/app/bundles/course/admin/pages/GradebookSettings/operations.ts | Adds admin API operations to fetch/update weighted-view setting. |
| client/app/bundles/course/admin/pages/GradebookSettings/index.tsx | Adds the admin Gradebook settings page container with preload + submit. |
| client/app/bundles/course/admin/pages/GradebookSettings/GradebookSettingsForm.tsx | Adds the admin form for toggling weighted view. |
| client/app/bundles/course/admin/pages/GradebookSettings/tests/GradebookSettings.test.tsx | Adds UI tests for the admin setting toggle behavior. |
| client/app/api/course/Gradebook.ts | Adds updateWeights client API method for PATCH /gradebook/weights. |
| client/app/api/course/Admin/index.ts | Registers the new admin gradebook API client. |
| client/app/api/course/Admin/Gradebook.ts | Implements the admin gradebook settings API client. |
| app/views/course/gradebook/index.json.jbuilder | Adds weighted-view flags + serialized weights/exclusions when enabled. |
| app/views/course/admin/gradebook_settings/edit.json.jbuilder | Renders gradebook settings JSON (weightedViewEnabled). |
| app/models/course/settings/gradebook_component.rb | Adds course setting accessors for weighted view enabled state. |
| app/models/course/gradebook/contribution.rb | Adds tab contribution model, enums, validations, and transactional bulk update. |
| app/models/course/gradebook/assessment_contribution.rb | Adds assessment contribution model with weight/excluded validations. |
| app/models/course/gradebook.rb | Adds table name prefix module for gradebook namespace models. |
| app/models/course/assessment/tab.rb | Adds association from tab to gradebook contribution. |
| app/models/course/assessment.rb | Adds association from assessment to gradebook assessment contribution. |
| app/models/course.rb | Adds association from course to gradebook contributions. |
| app/models/components/course/gradebook_ability_component.rb | Tightens/defines abilities for reading gradebook and managing weights/settings. |
| app/controllers/course/gradebook_controller.rb | Adds weighted fields to index and adds update_weights endpoint. |
| app/controllers/course/admin/gradebook_settings_controller.rb | Adds admin controller to edit/update gradebook settings. |
| app/controllers/components/course/gradebook_component.rb | Adds a settings sidebar item for gradebook settings when authorized. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
|
|
||
| def update_weights | ||
| authorize! :manage_gradebook_weights, current_course | ||
| updates = update_weights_params[:weights].map { |entry| parse_weight_entry(entry) } |
There was a problem hiding this comment.
Guarded: updates = (update_weights_params[:weights] || []).map { ... }. An omitted weights param is now a 200 no-op instead of NoMethodError -> 500.
| def self.validate_custom_assessment_weights_sum!(tab, entry, included_sum, included_any) | ||
| return unless included_any | ||
| return unless (included_sum * 100).round != (entry[:weight] * 100).round | ||
|
|
||
| tab.errors.add(:base, :custom_weights_mismatch) | ||
| raise ActiveRecord::RecordInvalid, tab | ||
| end |
There was a problem hiding this comment.
Left as-is. (included_sum * 100).round != (entry[:weight] * 100).round is an integer-cents comparison, which is robust against 2dp values in [0, 100]. And now that weights are rounded to 2dp at the parse boundary (see the parse_weight_entry change), the values reaching this gate are already clean 2dp. No float divergence left to guard against, so changing the model here would be a no-op cosmetic diff.
There was a problem hiding this comment.
Comments on frontend testing:
first alert: "Want to set how much each item contributes to the student's total grade? Enable weighted total view in gradebook settings."
Second alert can probably be removed entirely.
In the course settings, it should also be called "Weighted total" view to make things consistent everywhere.
Looks like 0/0 is resolving to NaN and it's propagating through the grade computation, you should probably coerce it to 0.
Nitpick: I don't feel the button group (thing that selects between Points and Percentage) style is consistent with what we have on CM currently. See below for reference
Though this is only UI change, so I'm OK to merge everything else first, then handle this at the end.
7665a14 to
b89ca5d
Compare
b89ca5d to
6dd61fc
Compare


Summary
Adds an opt-in weighted gradebook view on top of the base gradebook. Course staff enable it through a new Gradebook settings toggle, then configure per-tab weights in either equal mode (every included assessment counts equally toward the tab subtotal) or custom mode (explicit per-assessment weights that must sum to the tab total). Totals are additive: each tab subtotal is scaled by its weight and the gradebook Total is the literal sum of those subtotals. Staff can exclude individual assessments from totals . A per-student breakdown popover, a points/percentage display toggle, and a projected-total hint complete the view.
Design decisions
Regression prevention