feat(learners): send a learner's next content delivery immediately - #802
Merged
Conversation
The enrollment dialog now shows the next scheduled content delivery under the course title, with an admin-only "send now" link beside it. Sending runs that one delivery as if the job had just picked it up: the email goes out, the schedule is marked delivered, and the follow-up work happens too - the next content is scheduled, or the enrollment graduates. Moving the schedule's time forward would not be equivalent, since the delivery job runs on a cron and the content would still wait for its next tick. The schedule is claimed with the same SCHEDULED -> PROCESSING compare-and-set the database queue uses, so a concurrent job run cannot send the same content twice, and DeliverContentsJob takes an optional queue so constructing one here does not claim work the job should handle. Implemented with the help of Claude Code. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Contributor
Author
|
@copilot can you please review this PR? |
Contributor
Reviewed the PR changes and didn’t find any high-confidence blocking issues. I also checked the latest PR Checks workflow run and there were no failed jobs. |
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
The enrollment dialog on the Learners page now shows the learner's next scheduled content delivery under the course title — when it is due and which content it is — with a send now link beside it for organization admins.
Sending runs that one delivery as if the delivery job had just picked it up: the email goes out, the schedule is marked delivered, and the follow-up work happens too — the next content is scheduled, or the enrollment graduates if that was the last one.
Bringing the schedule's time forward was never equivalent, because the delivery job runs on a cron and the content would still wait for its next tick.
How
services/manual_delivery_service.py— claims the schedule with the sameSCHEDULED → PROCESSINGcompare-and-set the database queue uses, then hands it toDeliverContentsJob.process_delivery. A concurrent job run therefore cannot send the same content twice. A schedule that is no longerSCHEDULEDis left untouched and reported back; a schedule leftPROCESSINGbecause its content row had nothing to send is returned toSCHEDULEDrather than hidden from the job forever.DeliverContentsJobtakes an optional queue, so constructing one for a single delivery does not claim a batch of work the running job should be handling._block_deliverybecameblock_deliveryso the service can reuse it.POST /api/platform/organizations/<id>/enrollments/<id>/delivery-schedules/<id>/send/— admin-only (accessible_for(roles={"admin"})). Sending an email to a learner and advancing their enrollment is a heavier action than the read access the other enrollment views grant. Returns409when the delivery is no longer scheduled,404across organizations,500when the send fails (retried or blocked by the job's own rules).next_deliveryon the enrollment detail response — the earliest still-scheduled delivery, ornull.NextDelivery.jsx— the line under the course title, with the send-now link, a sending state, and inline error text. Reloads the enrollment after a successful send so the timeline picks up the new event.Testing
tests/platform/api/test_views/test_send_delivery_now_view.py— 13 tests: role gating, delivery, graduation, next-content scheduling,409/404/500, cross-organization and cross-enrollment isolation.tests/services/test_manual_delivery_service.py— 9 tests, including that constructing the job does not steal due work from a job run.tests/platform/api/test_views/test_enrollment_api.py—next_deliverypresent, earliest-first, andnull.frontend/src/test/platform/NextDelivery.test.jsx— 7 tests covering render, role gating, the POST, and both error paths.Full suites pass: 1131 backend tests (85% coverage), 386 frontend tests. Ruff, mypy and eslint clean. Verified through the test suites rather than a running browser session.
Implemented with the help of Claude Code.
🤖 Generated with Claude Code