feat(b2b_dashboard): filter learner-progress by course run - #79
Merged
Merged
Conversation
Adds an optional courserun_readable_id filter to the learner-progress endpoint, and a new course-runs endpoint listing a contract's course runs (reads mv_b2b_contract_courserun, already exposed by the b2b_learner_records tenant). Together these let the frontend's already-built module filter on the contract learner directory actually filter, and populate its dropdown. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
OpenAPI ChangesShow/hide changesUnexpected changes? Ensure your branch is up-to-date with |
Reject an explicit empty courserun_readable_id instead of silently treating it as "no filter" (a truthy check let "" fall through). Make course_runs() pagination deterministic by tie-breaking on the unique courserun_readable_id, since courserun_title can repeat across runs. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Picks up the minLength: 1 constraint added to courserun_readable_id. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
blarghmatey
approved these changes
Sep 30, 2026
This was referenced Sep 30, 2026
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.

What are the relevant tickets?
N/A
Description (What does it do?)
courserun_readable_idquery param toGET .../learner-progress, so the endpoint can narrow to one course run instead of silently dropping the param.GET .../organizations/{organization_id}/contracts/{contract_id}/course-runsendpoint listing the distinct course runs under a contract (id, title, start/end dates), for populating a course dropdown.openapi/specs/b2b_dashboard.yamlfor both changes.This unblocks the "Module" filter already built (and currently disabled) in mit-learn's
ContractLearnersPage.tsx— its own comment there says it sendscourserun_readable_id, which this API previously ignored rather than rejected.Implementation details
learner_queries.py:ProgressFiltersgainscourserun_readable_id: str | None;learner_progress()appends an exact-match predicate the same waysearchdoes today. A newcourse_runs()query readsmv_b2b_contract_courserun, scoped tosso_organization_id/contract_id— the same MV the separateb2b_learner_recordstenant already reads for its own/coursesendpoint (both tenants' configs default to the same physical schema,b2b_learner_records). No consent gating or anonymization floor on this query: it's course-run catalog metadata, not learner rows.learner_models.py: newCourseRunandCourseRunsResponsemodels, matching the field-description conventions of the existingLearnerProgress/LearnerProgressResponsemodels (manager-facing text, no field names in descriptions).routers/learners.py: newcourserun_readable_idquery param onlearner_progress, and a newcourse_runsroute, reusing the router's existingrequire_org_manager/require_contract_in_orgdependencies and the existingPaginationdependency.Screenshots (if appropriate):
N/A — API-only change, no UI in this repo.
How can this be tested?
uv run pytest tests/test_dashboard_learner_progress.py tests/test_dashboard_course_runs.py -q— 29 tests, including a new case assertingcourserun_readable_id = %sis bound into both the page and count queries when passed and omitted otherwise, and 7 new tests forcourse-runs(envelope shape, org/contract scoping, empty-contract case, null-start-date sort order, auth gates, field descriptions).uv run pytest tests/ -k "dashboard or openapi" -q— 39 passed.uv run ruff checkanduv run mypyclean on all touched files.uv run bin/generate-openapi-spec --check— confirmed the committed spec is current; the diff is additive only (new optional param, new endpoint/schemas), verified viagit diff openapi/specs/b2b_dashboard.yaml.mv_b2b_contract_courserun/mv_b2b_learner_enrollmentdata was available locally.Additional Context
Tracked as witan project
wp-add-course-module-filter-to-b2b-learner-director-bfe2ef. Follow-up inmit-learnto re-enableContractLearnersPage.tsx's commented-out module filter, and to make each row in the contract analytics "Course performance" table (enrollment-funnelendpoint) link to the learner directory pre-filtered by that row'scourserun_readable_id, is not part of this PR.