Skip to content

feat(learners): send a learner's next content delivery immediately - #802

Merged
payamnj merged 1 commit into
masterfrom
feat/send-next-delivery-now
Aug 15, 2026
Merged

feat(learners): send a learner's next content delivery immediately#802
payamnj merged 1 commit into
masterfrom
feat/send-next-delivery-now

Conversation

@payamnj

@payamnj payamnj commented Aug 15, 2026

Copy link
Copy Markdown
Contributor

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 same SCHEDULED → PROCESSING compare-and-set the database queue uses, then hands it to DeliverContentsJob.process_delivery. A concurrent job run therefore cannot send the same content twice. A schedule that is no longer SCHEDULED is left untouched and reported back; a schedule left PROCESSING because its content row had nothing to send is returned to SCHEDULED rather than hidden from the job forever.
  • DeliverContentsJob takes an optional queue, so constructing one for a single delivery does not claim a batch of work the running job should be handling. _block_delivery became block_delivery so 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. Returns 409 when the delivery is no longer scheduled, 404 across organizations, 500 when the send fails (retried or blocked by the job's own rules).
  • next_delivery on the enrollment detail response — the earliest still-scheduled delivery, or null.
  • 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.pynext_delivery present, earliest-first, and null.
  • 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

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>
@payamnj

payamnj commented Aug 15, 2026

Copy link
Copy Markdown
Contributor Author

@copilot can you please review this PR?

Copilot AI commented Aug 15, 2026

Copy link
Copy Markdown
Contributor

@copilot can you please review this PR?

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.

@payamnj
payamnj merged commit 8638c68 into master Aug 15, 2026
14 checks passed
@payamnj
payamnj deleted the feat/send-next-delivery-now branch August 15, 2026 13:13
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants