feat(marketplace): add data model, migrations, and permissions#8476
Open
LWS49 wants to merge 15 commits into
Open
feat(marketplace): add data model, migrations, and permissions#8476LWS49 wants to merge 15 commits into
LWS49 wants to merge 15 commits into
Conversation
LWS49
commented
Jul 8, 2026
Collaborator
- add Listing and Adoption models under Course::Assessment::Marketplace namespace, with course_assessment_marketplace_ prefix on both tables
- Listing tracks published state and publisher, with a uniqueness constraint per assessment and an adoption_count helper
- Adoption links a listing to a destination course and duplicated assessment, one adoption per duplicated assessment
- wire has_one :marketplace_listing onto Course::Assessment
- add AssessmentMarketplaceAbilityComponent: admins can publish listings, course managers/owners can access, duplicate, and preview published listings
…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.
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"
- map student Level to grade-points via a safe parsed arithmetic formula - fold the Level term into weighted Total and per-student breakdown - add Configure Contributions controls: formula, level, show, clamp - persist a singleton LevelConfig per course (new table + migration) - surface a Level column in the weighted table, toggleable via column picker - warn when a contribution falls outside the range of level contributions - add toggle to clamp contributions within the range of level contributions
Expanding a student in the weighted gradebook now keeps that student's summary row pinned beneath the sticky header while their per-assessment breakdown is open, so the totals stay visible while scrolling the detail. The view becomes a single-open accordion (opening one student collapses any other), the focused row auto-scrolls to just under the header on expand (respecting prefers-reduced-motion), and focus stays on the expand/collapse toggle for keyboard users.
- Permit keepHighest in update_weights_params - Parse keepHighest from request and pass to bulk_update via keep_highest - Echo keepHighest in serialize_weight_updates response - Serialize keepHighest from contribution in index.json.jbuilder - Add controller tests: round-trip persist and index response - Add model tests: bulk_update persists keep_highest in equal mode
…guard Render level contribution in percent mode, and enforce that an enabled level contribution always has a parseable formula: - Model: validate on :formula (not the hidden :formula_ast) that an enabled config with a formula carries a non-blank ast — the error surfaces on the field the user edits, and an enabled config can no longer silently evaluate to nothing. - Dialog: parse the formula whenever level is enabled, so clearing it blocks Save and flags the field instead of round-tripping to a confusing 422. - Controller: wrap the tab-weight and level-config writes in one transaction so a rejected formula rolls back the tab weights instead of a partial save.
…'t add up +show honest weighted total in percent mode
…ation, inline grade entry) Foundation layer for external assessments: data model + migrations, gradebook read serialization, and inline grade entry (update_grade). Management UI (panel, add/edit/delete), weight integration, and CSV import are introduced in later PRs of the stack.
…ete, reorder, weight integration Adds the management surface on top of the read-only foundation: - Manage panel (launched from the gradebook + weighted toolbars) with add, edit, and delete prompts; drag-and-drop reordering of external columns. - Weighted-view integration: external rows in Configure Weights (name + single weight, no mode/expand), and flat external grouping in the column picker. - Store reducers + thunks for create/update/delete/reorder; API + types. - Backend: create/update/destroy/reorder actions with jbuilders; a per-course `position` column (append-on-create, reorder! rewrites order); routes.
…warnings External grade cells gain decimal-bounded inline editing with explicit accept/revert controls, a saving indicator, and a persistent save confirmation that echoes the student, assessment, and old → new grade so a row/column misclick is caught. Over-max and below-zero warning markers flag out-of-bound values. The weighted view annotates contributions that an active bound capped or floored. GradebookIndex surfaces an out-of-range summary alert above the table, and threads weightedViewEnabled into GradebookTable so the cell messaging matches the active view. m
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.
- levelFormula: make negative rounding consistent with ruby, fix error message when more than 2 arguments are used for min and max - WeightedGradebookTable: add cell tooltip for capping total at 100% - ConfigureWeightsPrompt: change capTotal to no longer be gated to when total weightage adds to over 100% - ImportWizard: successful import toast now shows how many grades got replaced/kept if import resolved from conflictPrompt - update to locales based on the above changes
…ort UI - unify manage and configure-weights into a GradebookSettingsDialog - decompose import wizard into ColumnMappingTable + useImportMapping hook - extract importValidation module with dedicated unit tests - rework ExternalAssessmentImportService header/identifier handling - refresh en/ko/zh locales for the new settings surface
35884a1 to
833d37f
Compare
- add Listing and Adoption models under Course::Assessment::Marketplace namespace, with course_assessment_marketplace_ prefix on both tables - Listing tracks published state and publisher, with a uniqueness constraint per assessment and an adoption_count helper - Adoption links a listing to a destination course and duplicated assessment, one adoption per duplicated assessment - wire has_one :marketplace_listing onto Course::Assessment - add AssessmentMarketplaceAbilityComponent: admins can publish listings, course managers/owners can access, duplicate, and preview published listings
833d37f to
9926c90
Compare
d70b9ff to
b7da646
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.