feat(courses): add GET /api/admin/courses/:id/enrolled-users endpoint - #448
Merged
DeFiVC merged 2 commits intoAug 31, 2026
Merged
Conversation
Admin-only, paginated endpoint returning a course's enrolled users with per-user quiz progress: userId, displayName, stellarAddress, enrolledAt, completedAt, quizCount, averageScore. - Mirrors the existing getCourseReviews pagination/caching pattern in course.service.ts (count + paginated select, cache-aside via cacheGet/cacheSet, 30s TTL per the acceptance criteria). - Route added to admin-course.routes.ts alongside the other admin course routes, inheriting the file-level authGuard + adminGuard hooks. - quizCount/averageScore are computed from quiz_submissions joined to quizzes scoped to this course (quizzes.courseId), non-superseded submissions only, for just the userIds on the current page. - averageScore is an average of quiz_submissions.score, which is a raw correct-answer count (see QuizService.submitQuiz), not a percentage — documented on EnrolledUserSummary since quizzes aren't fixed-length in this codebase, so it isn't comparable across quizzes with different question counts. Computing a normalized percentage average would need each submission's quiz question count folded into the aggregation, which is a larger change than this issue asks for; noted as a possible follow-up rather than silently approximated here. Tested against real Postgres + Redis (docker): 4 new tests in src/test/course-enrolled-users.test.ts — quizCount/averageScore correct for both a user with a submission and one without, pagination + ordering (most-recently-enrolled first), cache-aside behavior (a row added after the first call isn't reflected until the cache entry expires), and NotFoundError for a nonexistent course. eslint clean on all touched files. `npm run typecheck` was not usable to verify this — it fails with pre-existing syntax errors in three unrelated files, confirmed present on main before this change (via git stash). Closes ChainLearnOfficial#340
|
@oluwagbemigaphilip Great news! 🎉 Based on an automated assessment of this PR, the linked Wave issue(s) no longer count against your application limits. You can now already apply to more issues while waiting for a review of this PR. Keep up the great work! 🚀 |
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 changed
Adds
GET /api/admin/courses/:id/enrolled-users— an admin-only, paginated endpoint returning a course's enrolled users with their quiz progress (userId,displayName,stellarAddress,enrolledAt,completedAt,quizCount,averageScore).Follows the existing
getCourseReviewspattern exactly: same pagination shape, same cache-aside (cacheGet/cacheSet) approach, 30s TTL per the issue's acceptance criteria. Route lives inadmin-course.routes.tsalongside the other admin course routes, inheriting the file-levelauthGuard+adminGuardhooks — no new auth wiring needed.A units note on
averageScorequiz_submissions.scoreis a raw correct-answer count (seeQuizService.submitQuiz), not a percentage. Since quizzes in this codebase aren't fixed-length, averaging raw scores isn't comparable across quizzes with different question counts. I documented this explicitly onEnrolledUserSummaryrather than silently computing a normalized percentage — that would need each submission's quiz question count folded into the aggregation, which felt like a bigger, separate decision than this issue asks for. Happy to add it if that's actually wanted.How verified
Tested against real Postgres + Redis (started via
docker run, schema pushed withdrizzle-kit push) — not just type-checked:src/test/course-enrolled-users.test.ts: quizCount/averageScore correct for both a user with a submission and one without; pagination + ordering (most-recently-enrolled first); cache-aside behavior (a row added after the first call isn't reflected until the cache entry expires);NotFoundErrorfor a nonexistent course.eslintclean on all touched files.npm run typecheckwas not usable to verify this — it fails with pre-existing syntax errors in three unrelated files (admin-users.service.ts,course.routes.ts,user.routes.ts). Confirmed viagit stashthat these are present onmainbefore this change, unrelated to it.Closes #340
Closes #341
Closes #342
Closes #357