You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
❌ This issue is not open for contribution. Visit Contributing guidelines to learn about the contributing process and how to find suitable issues.
Overview
The Courses list page (CoursesRootPage.vue) has three columns — Progress, Learners, and
Mastery — that are hardcoded as '—' placeholders. This task replaces those placeholders
with real data: a course Status indicator, a Recipients display, and a Learner Progress tally.
Complexity: Medium Target branch: develop
Context
CoursesRootPage.vue renders a KTable whose tableRows computed property returns
hardcoded '—' for the Progress (col 1), Learners (col 2), and Mastery (col 3) columns:
The parallel Lessons list page (LessonsRootPage.vue) shows the target pattern:
Progress → StatusSummary component fed a {completed, started, notStarted, helpNeeded} tally from getLessonStatusTally (classSummary Vuex getter)
Recipients → group names / "Entire class" from getRecipientNamesForLesson
The CourseSessionViewset (kolibri/core/courses/viewsets.py) already exposes assignments (group collection IDs) and learner_ids (ad-hoc learners) per session via
its consolidate() method. The _compute_course_state() helper in the same file already
derives unit_phase and active_unit_id from UnitTestAssignment records but is not
yet included in the list API response.
The Change
Two column headers are renamed; the rest stay the same:
Learners → Recipients
Mastery → Learner Progress
(Progress → Status, Visible to learners, and Options columns are unchanged)
Backend (kolibri/core/courses/viewsets.py)
Extend CourseSessionViewset.consolidate() to add two new fields per session item:
unit_phase + active_unit_number + active_unit_title — call the existing _compute_course_state() helper in a batched loop over all session items; look up the active unit's ContentNode to get its title and position number.
test_learner_progress — a dict {completed: N, started: N, notStarted: N, total: M} where total is the count of assigned learners (derived from learner_ids + group memberships already computed in consolidate()), and the three counts are derived from MasteryLog records for the active unit's synthetic content ID:
completed: learners with a complete MasteryLog (complete=True)
started: learners with an in-progress MasteryLog (complete=False)
notStarted: remaining assigned learners with no MasteryLog
helpNeeded is not included
Return null when unit_phase is PreTestPending (no test activated yet).
Frontend (CoursesRootPage.vue)
Update tableHeaders to rename the two columns above.
In tableRows, replace '—' with real values:
Status: render phase label + unit number/title (e.g. "Pre-test running · Unit 1", "Completed", "Not started") with icon, using existing coursesStrings translatable strings and following the visual pattern in CourseSummaryPage.
Recipients: use the existing getRecipientNamesForCourseSession getter and render with the Recipients component (same as CourseSummaryPage).
Learner Progress: feed test_learner_progress into a StatusSummary component tally with {completed, started, notStarted}. Show '—' when test_learner_progress is null. Follow the StatusSummary pattern from LessonsRootPage and the tally shape from PR Add unit page #14614 (useUnitDetail.js).
How to Get There
Start the dev server: pnpm devserver
Log in as a coach or admin
Navigate to Coach → [Class] → Courses
The Courses list table is immediately visible — the Status, Recipients, and Learner Progress columns currently show — for all rows
To see non-placeholder data after the fix, assign a course to learners and activate a pre- or post-test from the Course Summary page
Out of Scope
The stacked mastery bar (red / yellow / green learner distribution) from the early design spec — deferred as a future enhancement
helpNeeded bucket in the Learner Progress tally
Any changes to the Course Summary page (CourseSummaryPage.vue)
The Recipients filter dropdown behavior (already works using assignments data)
Acceptance Criteria
General
The "Learners" column header is renamed to "Recipients"
The "Mastery" column header is renamed to "Learner Progress"
Status column: each row shows the current course phase with an icon and label:
"Not started" (grey dot) when no test has been activated
"Pre-test running · Unit N" (clock icon) when a pre-test is open
"Post-test running · Unit N" (clock icon) when a post-test is open
"Completed" (star icon) when all units are done
Recipients column: each row shows group names, individual learner names, or "Entire class" — matching the behavior of the Recipients column on the Lessons list page
Learner Progress column: each row shows a StatusSummary tally using {completed, started, notStarted} counts for the active unit's test:
"N have not started" when no learner has begun the test
"N of M have started" when some learners are in progress
"Completed by N of M" when some learners have finished
Shows — when no test has been activated (unit_phase is PreTestPending)
All three columns update correctly when navigating between classes with different course states
The existing Status filter (visible / not visible) and Recipients filter still work correctly after the changes
Accessibility and i18n
New column headers use existing coursesStrings / coachStrings translatable strings where available; any new strings are added with message and context fields
Status and Learner Progress text is not hardcoded in English
Testing
Backend/API tests cover the new test_learner_progress and unit phase fields returned by CourseSessionViewset for the key states: not started, pre-test active, post-test active, and completed
Frontend tests are minimal — focus on verifying that the correct data reaches the template for a small number of representative cases; avoid testing rendering details that duplicate backend coverage
Any getByText assertions use string function references (e.g. entireClassLabel$()) rather than hardcoded English strings
PR Add unit page #14614 — establishes the {completed, started, notStarted} tally shape and StatusSummary usage pattern to follow
CoursesRootPage.vue — file under change (table rows and headers)
kolibri/core/courses/viewsets.py — CourseSessionViewset.consolidate() and _compute_course_state() to extend
LessonsRootPage.vue — parallel recipients + progress implementation to follow
views/common/status/StatusSummary.vue — component to reuse for Learner Progress
views/common/Recipients.vue — component to reuse for Recipients column
composables/useUnitDetail.js (PR Add unit page #14614) — tally computation pattern
AI usage
Drafted with Claude Code. Explored the codebase to identify the hardcoded placeholders,
compared with the lessons/quizzes list implementations, reviewed PR #14614 for the tally
pattern, and iterated on scope through conversation. Reviewed and edited for accuracy.
❌ This issue is not open for contribution. Visit Contributing guidelines to learn about the contributing process and how to find suitable issues.
Overview
The Courses list page (
CoursesRootPage.vue) has three columns — Progress, Learners, andMastery — that are hardcoded as
'—'placeholders. This task replaces those placeholderswith real data: a course Status indicator, a Recipients display, and a Learner Progress tally.
Complexity: Medium
Target branch: develop
Context
CoursesRootPage.vuerenders aKTablewhosetableRowscomputed property returnshardcoded
'—'for the Progress (col 1), Learners (col 2), and Mastery (col 3) columns:The parallel Lessons list page (
LessonsRootPage.vue) shows the target pattern:StatusSummarycomponent fed a{completed, started, notStarted, helpNeeded}tally fromgetLessonStatusTally(classSummary Vuex getter)getRecipientNamesForLessonThe
CourseSessionViewset(kolibri/core/courses/viewsets.py) already exposesassignments(group collection IDs) andlearner_ids(ad-hoc learners) per session viaits
consolidate()method. The_compute_course_state()helper in the same file alreadyderives
unit_phaseandactive_unit_idfromUnitTestAssignmentrecords but is notyet included in the list API response.
The Change
Two column headers are renamed; the rest stay the same:
Backend (
kolibri/core/courses/viewsets.py)Extend
CourseSessionViewset.consolidate()to add two new fields per session item:unit_phase+active_unit_number+active_unit_title— call the existing_compute_course_state()helper in a batched loop over all session items; look up the active unit's ContentNode to get its title and position number.test_learner_progress— a dict{completed: N, started: N, notStarted: N, total: M}wheretotalis the count of assigned learners (derived fromlearner_ids+ group memberships already computed inconsolidate()), and the three counts are derived fromMasteryLogrecords for the active unit's synthetic content ID:completed: learners with a complete MasteryLog (complete=True)started: learners with an in-progress MasteryLog (complete=False)notStarted: remaining assigned learners with no MasteryLoghelpNeededis not includedReturn
nullwhenunit_phaseisPreTestPending(no test activated yet).Frontend (
CoursesRootPage.vue)tableHeadersto rename the two columns above.tableRows, replace'—'with real values:coursesStringstranslatable strings and following the visual pattern inCourseSummaryPage.getRecipientNamesForCourseSessiongetter and render with theRecipientscomponent (same asCourseSummaryPage).test_learner_progressinto aStatusSummarycomponent tally with{completed, started, notStarted}. Show'—'whentest_learner_progressis null. Follow theStatusSummarypattern fromLessonsRootPageand the tally shape from PR Add unit page #14614 (useUnitDetail.js).How to Get There
pnpm devserver—for all rowsOut of Scope
helpNeededbucket in the Learner Progress tallyCourseSummaryPage.vue)assignmentsdata)Acceptance Criteria
General
StatusSummarytally using{completed, started, notStarted}counts for the active unit's test:—when no test has been activated (unit_phaseisPreTestPending)Accessibility and i18n
coursesStrings/coachStringstranslatable strings where available; any new strings are added with message and context fieldsTesting
test_learner_progressand unit phase fields returned byCourseSessionViewsetfor the key states: not started, pre-test active, post-test active, and completedgetByTextassertions use string function references (e.g.entireClassLabel$()) rather than hardcoded English stringsReferences
{completed, started, notStarted}tally shape andStatusSummaryusage pattern to followCoursesRootPage.vue— file under change (table rows and headers)kolibri/core/courses/viewsets.py—CourseSessionViewset.consolidate()and_compute_course_state()to extendLessonsRootPage.vue— parallel recipients + progress implementation to followviews/common/status/StatusSummary.vue— component to reuse for Learner Progressviews/common/Recipients.vue— component to reuse for Recipients columncomposables/useUnitDetail.js(PR Add unit page #14614) — tally computation patternAI usage
Drafted with Claude Code. Explored the codebase to identify the hardcoded placeholders,
compared with the lessons/quizzes list implementations, reviewed PR #14614 for the tally
pattern, and iterated on scope through conversation. Reviewed and edited for accuracy.