Hacktoberfest 2026:メンテナが10月に向けて印を付けた、オープンで初心者向けの issue。 Hacktoberfest の issue を見る

Course-launch batch schedule: one confirmed cohort launch-and-schedule enqueues eligible announcements

オープン
#468 コメント 6 件 リアクション 0 件 担当者 0 名 GitHub で見る

まだ誰も着手していません。

評価

難易度
4/5
見積もり時間
3〜5日
初心者へのやさしさ
35/100
issue の種類
機能追加
明瞭さ
明確に書かれている
活発さ
活発
技術スタック
django, python

調査の方向性

Start with courses/services/launch_batch.py and its focused test courses/tests/test_course_launch_batch_schedule.py, then inspect templates/studio/course_launch.html and the OpenAPI/CLI entry points. Run the focused Django and CLI tests first. Done means preview makes no changes, confirmed execution enqueues each eligible revision once, and Studio/API/CLI expose identical skip reasons; the issue is already marked implemented and QA-accepted.

索引モデルが issue の本文から書いたものです。

説明

admin courses data-migration email enhancement integration operations P1

Course-launch batch schedule (dependent follow-up to #467)

Raw reporter intent preserved: user requires cohort launch to GENERATE AND SCHEDULE automatically (linked to module/project STARTs); sending stays explicit/scheduled. Core #467 generate() creates only unarmed draft proposed times (armed: 0, no dispatch) requiring per-row approve+schedule. This issue adds the missing one-action batch scheduling without changing core semantics.

Groomed spec (PM, 2026-10-17)

Normative links
  • #467 (prerequisite core increment): templates/publications, Course defaults, cohort overrides, native starts, unarmed generation, per-row approve/schedule/send, Relay courses simulation, Telegram quarantine, dual manage+send authority, CLI auth/parity.
  • Specs 04/05/06 + open-decisions §10/§11 (Relay), §19 pattern (no secret retention); playbooks 02-course-launch-social-system.md (module/project announcements), 01-course-platform-setup.md (Monday 23:00 UTC, copy-previous outline).
  • Ownership: website canonical scheduling (C-LAUNCH-1); no CMP change; no new scheduler (reuse community_base.jobs leased dispatch_after_commit).
Scope (website repo only, additive)
  1. One explicit confirmed privileged batch action (launch_and_schedule or equivalent name): Studio + Admin API + CLI parity, same send capability (dual manage+send authority), confirmed: true required, Idempotency-Key required.
  2. Two-step UX: preview/dry-run first (eligible count + per-row skip reasons, no mutations), then confirmed execute. Studio shows batch section separately from per-row approve/schedule; CLI offers --dry-run (default preview) + --confirmed execute.
  3. Eligibility: only generated rows with immutable template version, known future anchor (starts_at present, proposed anchor+offset > now), status draft/approved (not settled/dispatching/submitted/ambiguous, not cancelled), live cohort + current curriculum. Everything else skipped with explicit reason (missing_start, past_due, settled, cancelled, blocked_destination, stale_revision).
  4. Effect per eligible row: approve current copy (set approved_revision = revision after preview render passes) + enqueue one durable scheduled job at proposed time via the exact single-schedule dispatch path (same idempotency key scheme announcement:<id>:<revision>, same revision fencing). Returns per-row enqueued/skipped{reason} + batch summary counts. Rejected naive/missing/past never enqueued; no catch-up.
  5. Safety preserved: simulation default (non-prod or flag-off → simulated, never live); Relay sender courses only; Telegram uncertain-ack quarantine (no auto-retry); no secret/destination persistence beyond setting key; audit redacted; preference enforcement unchanged (enrolled + email_course_updates).
  6. Idempotency/revision: same-key replay returns prior batch result without duplicate jobs; per-row stale revision → skip with reason (never overwrite another operator's edit); assignment/template change mid-batch fences via existing reconcile (disarm) path.
  7. Docs/tests: runbook section (batch vs per-row, eligibility table, dry-run example), OpenAPI + registry entries, focused Django tests, CLI tests, desktop+mobile screenshots of batch preview + result (reason codes visible).
Non-goals
  • No change to #467 core semantics: generate() stays preview-only/unarmed; per-row approve/schedule/send/cancel/reschedule unchanged; no renaming drafts as auto-scheduled.
  • No batch immediate-send (send now stays per-row explicit only); no past-due catch-up; no resurrecting cancelled/settled rows via batch.
  • No new scheduler infra, worker, or cron; no production sender config/deploy; no live/real-user sends; no CMP/import changes; no RBAC redesign (existing dual-authority + global scope).
  • No mass-assignment or existence leaks; support role still excluded from bodies/recipients.
Dependencies
  • Depends on #467 (must land first or coordinator sequences branches; this work must not mutate the frozen v5/v6 core diff). Blocked while #467 gates (broad run + Argon final + PM accept) are pending.
Acceptance criteria
  • Batch preview (dry-run) lists eligible + skipped-with-reason per cohort with zero mutations and zero jobs; confirmed execute with Idempotency-Key enqueues exactly the eligible future rows once each (job per row, proposed time = anchor+offset).
  • Missing/past/settled/cancelled/blocked rows are skipped with explicit reasons; past rows never dispatched; cancelled rows stay cancelled (explicit per-row recovery only).
  • Same-key replay returns identical batch result with no duplicate jobs; stale-revision rows skip (409-style reason) without overwriting.
  • Permission: dual manage+send (or site_admin) can execute; course-only or email-only principals get 401/403 denial with no existence leak and no partial enqueue (atomic per-row, reported).
  • Simulation: dev/flag-off run enqueues simulated jobs only (no live Relay/Telegram IO); live path unchanged and still requires authorized release/config (human step).
  • Studio/API/CLI parity: Studio batch section + API action + CLI command produce identical results; --dry-run default, --confirmed required to execute; --json/help/exit codes consistent with existing CLI.
  • Screenshots: desktop+mobile batch preview + post-execute states showing enqueued/skipped reasons, no error/debug/broken layout.
  • No .env/tokens/DB/screenshots committed; scratch under .tmp/; migration-drift clean (no schema change expected; if any, nullable additive + null-preserving backfill rule from #467 applies).
Django/integration scenarios
  • Live cohort, all starts future → preview N eligible/0 skipped → confirm → N jobs, each row approved+scheduled, single idempotency keys, replay → same result, zero duplicates.
  • Mixed cohort (one missing start, one past start, one cancelled, one dispatched) → preview 1 eligible + 4 skipped with reasons → confirm → 1 job only.
  • Dual-role principal succeeds; single-role principals denied with zero side effects.
Browser scenarios (desktop + mobile)
  • Studio batch preview → confirm → result list (enqueued + skipped reasons); denied principal sees safe denial; empty cohort (no versions/starts) shows guidance, not silent no-op.
Repository/operations scenarios
  • Focused Django + CLI tests for batch contract; Playwright smoke/core as selector demands; versioned verification plan with base/head + digests; no live sends, secrets, or cloud mutations.
Roles
  • Engineer: launch-web (after #467 lands / coordinator sequences). Tester: independent (Argon or next). PM acceptance: after tester passes; overall #467+#468 goal closes only when both shipped.

QA Review (Argon QA launch-qa2, 2026-10-17 — ACCEPT)

Verdict
  • Status: ACCEPT (QA2-468-FINAL-ACCEPT)
  • Reviewer: Independent Final QA (launch-qa2, session b51faf34-e16a-4107-bf14-38f70c545c4c, engine Argon)
  • Frozen Source Bundle: /home/alexey/git/dtc-website-wt-course-launch-integrated/.tmp/launch-integrated19/ (19 changed paths over HEAD 054f233d7e8192f832064603c109fdd3f8b02a15)
    • source-manifest.json SHA-256: 8aff8b24354cbd18f2d82a3465605b5fa3548724faa745404bd415de49b1fb6e
    • tracked.patch SHA-256: 216a9d516e1d26e6e36db6e2de2024e9cc2c2c163bd765a32a5caff229c73e23
    • courses/services/launch_batch.py SHA-256: 172cd9afa30a4a7e827367b43a2d671026becc7b72c8a2505ead358278e60c64
    • courses/tests/test_course_launch_batch_schedule.py SHA-256: fca3a933192bb27b15dddd3905cf9afac64400ab99c0081c50ba2c634ab4295c
    • templates/studio/course_launch.html SHA-256: 3126690b23c9e4ac6039a3d001c9d1488e23c88828e581b33b0f5c942bbe2743
Verification Plan & 7-Component Full Report Validation
  • Author verification-plan.json Digests:
    • Raw file sha256sum: 8b87b6e264aca497c09de15181197c0630aea856062727b9a00ace8df500604d
    • Canonical sha256_json(plan) (report.plan_sha256 at ci/verification.py:879): 88b49abb90bf640c0cd64eccb120103ffe7ccc02ce5154c14da16d0a49a09b60
  • Tester verification-plan.json Digests:
    • Raw file sha256sum: 564f363a407ee9584ba038c435b9aaea35f27aa8561805a8c8699224f72515b0
    • Canonical sha256_json(plan): bae325f0b5587984b60713df03a5a4e27369d091fb3656b86f9fabe51a6114df
  • Shared Plan Digests (Identical Across Author & Tester Plans):
    • source_tree.manifest_sha256: 21ff298bb27c21e141ceed93b28d1ac4eb596c1deb01ce83add00a91a126c621
    • render.sha256: 7af0b74118152b92e9234ad0631e2e47062f1d3b8650ca909a2b623a574c6d6b
    • graph_sha256: bb924cbe4e46a273f1cea46f5e85f1b9b0711e7ff8b1100b0162bc306de1186b
    • verification_state_sha256: 897e2b7b5ef6ff645bec50901772143e103e6a839cc34ab1e67253bb247a34fb
  • Completed Report (/home/alexey/git/dtc-website-wt-course-launch-integrated/.tmp/verification-468-final19b/):
    • verification-report.json SHA-256: 1c753cde5e9e05c6c25761f94ebacb35315d162bf2d31c6fda7610edc28d994d (Verdict: success; validate-report + validate-evidence-directory + all 7 validate-evidence checks PASS)
    • verification-report.md SHA-256: f731e453913b8443d60a9e21c4673a43a9c3726dfe331767a3e71acc06d604a3
    • Explanation of invalid_evidence: [{"component": "screenshots", "reason": "render_inputs_changed"}]: In ci/verification.py:899-905, report["invalid_evidence"] is a mandatory historical plan-selection diagnostic derived from [component for component, item in plan["components"].items() if item["disposition"] == "rerun" and item["reason"] in INVALID_EVIDENCE_REASONS]. Because screenshots is always planned with disposition: "rerun" and reason: "render_inputs_changed" when render inputs change (and "render_inputs_changed" in INVALID_EVIDENCE_REASONS), validate_report strictly enforces that invalid_evidence contains screenshots:render_inputs_changed even after the fresh screenshots rerun (evidence_id: f7af1e6b7496964f395cf82ff34a0b62741f8c0b1f2fb01ed8259df297682582, result: "success") passes in buckets.rerun.
    • Container Ambient Environment Verification: container-environment.json (0a099d7b17b7c90044c908e065e8e20e1fddbfcc77e3a9b3330502c2071da55d) matches component_environment_fingerprint("container") (environment.sha256 = e0764a1d065452d57f9347633766dfe0464f35a8193cb9e365e1d928bb77f9f5) and plan.components.container.environment.sha256 from the normal runner (scripts/ci.py) without any forced fingerprint override (13 container assertions passed, status: "pass").
  • 7 Component Evidence Envelopes (all result=success, failed=0):
    • selector-evidence.json: f149d6c3afc297247495ccf375651479d54bb0dfda02c791d57dea47e4876624 (changed_paths=19, direct_nodes=9, downstream_nodes=10)
    • quality-evidence.json: 90e19ca53dce7d4943c664c9b31b0e85cc66cccbaedf37d4c8ffae2c6792ca45 (tests=765, passed=760, skipped=5, failed=0)
    • django-evidence.json: 805ce02a97c90ba00f6646c9a9b5fa38b792d2b0a065d4962a7dafac51432cc6 (tests=4358, passed=4352, skipped=6, failed=0)
    • playwright-evidence.json: 5cf56448ef09f1b1beb88074cf2c0bfadec39fba97c273881dfeeeeaf2434334 (attempted=352, passed=352, failed=0, rerun=0, quarantined=0)
    • container-evidence.json: 9690c151db030bf7d681a6f84406136322836e923333935adf49a2d117bc8255 (assertions=13, failed=0)
    • screenshots-evidence.json (producer_role=tester): 0c0a06b4edda64c9e046fea489aa817b796ca710a81b3df4719ab2e6308d89c4 (captures=8, assertions=8, failed=0, screenshots.json 3166e1ab17c4b5c9937d088b8c9f1c4aee5b9b906394bdd57f26ed04ba8b5290)
    • evidence_validation-evidence.json: bdefae542ad4777406921b6a3083b17566bb9104d642558cbd57b3bff31d665d (tests=765, passed=760, skipped=5, failed=0)
Independent Functional, Security & Adversarial Verification
  1. Two-Step Preview & Confirmed Batch Execution:
    • preview_cohort_launch_schedule is strictly read-only (0 CourseLaunchAnnouncement, 0 JobIntent, 0 ManagementIdempotencyRecord, and 0 ManagementAuditLog writes) and returns a Django-signed preview_token binding cohort_id, template_state_sha256, and each eligible row's key, revision, anchor_starts_at, and proposed_scheduled_for.
    • confirm_cohort_launch_schedule requires dual courses.manage_course_launch + courses.send_course_launch_announcement authority (checked before idempotency reservation or row mutation), confirmed=True, a valid signed preview_token, and an Idempotency-Key. Legacy client-supplied preview maps (expected_revisions / preview_revisions / eligible_keys) are rejected with 400.
  2. Lock Ordering, Template State Fencing & Prospective Null-Revision Guard:
    • Confirmed execution acquires locks in canonical order Course -> existing CourseLaunchTemplateAssignment -> Cohort -> existing CohortAnnouncementPlan -> CourseLaunchAnnouncement (by PK), raising 409 Conflict cleanly inside the atomic transaction when parent rows disappear or template_state_sha256 (effective assignment, published template version, channel config, destination key, or resolved destination) drifts between preview and confirm.
    • Prospective rows (revision is None in preview_token) require current revision == 1, approved_revision is None, raw subject_template / body_template matching the published template version, context_overrides == {}, and cohort.announcement_context == default_cohort_announcement_context(cohort); any post-preview edit skips the row as stale_revision.
  3. Eligibility, Durable JobIntent Scheduling & Idempotent Replay:
    • Only future-anchored (anchor + offset > now) draft/approved rows on active non-self-paced cohorts with valid destinations are approved and enqueued via _enqueue_scheduled_dispatch (idempotency_key = announcement:<id>:<revision>, scheduled_for = recomputed anchor + offset).
    • Rows with missing/past starts, manual edits, cancelled/settled/already-scheduled states, or blocked destinations are safely skipped with explicit reasons (missing_start, past_due, stale_revision, cancelled, settled, already_scheduled, blocked_destination, not_in_preview) without re-arming or catch-up.
    • Same-key idempotent replay returns the stored response (replayed: True) without creating duplicate JobIntent rows. Non-production / flag-off runs remain strictly simulated (simulated_only).
  4. Studio / Admin API / CLI Parity:
    • Verified across 78 Django course-launch tests (32 in test_course_launch_batch_schedule.py + 46 existing course-launch regressions) and 2 live-server HTTP CLI Playwright tests (playwright_tests/test_course_launch_cli.py), exercising both --dry-run preview and --confirmed --preview-token <TOKEN> --idempotency-key <KEY> execution.
Visual & Screenshot Evidence (Inspected via Image Viewer)
  • 8 Graph-Required Screenshots (/home/alexey/git/dtc-website-wt-course-launch-qa2/.tmp/verification/evidence/screenshots/):
    • courses-courses-desktop-1440x900.png: 5f5a9a3e4429b4fb40ede2c4f439b4ae3cdcc8e53d8fc0a6445116b2f12dd470
    • courses-courses-mobile-390x844.png: 19822e0448354cf6d7c53e1e9f4730cce88fb6670a63307ee1fa0b13c7c0042c
    • home-home-desktop-1440x900.png: 1bd927557c21aa4428854564341d32f18cea1bcc119608aa6bb380da7e0878d3
    • home-home-mobile-390x844.png: 8f64b0818a23a3593ea3510c52100ccbbf634fdb17fb630df79e92c3686a5f59
    • studio-studio-sign-in-desktop-1440x900.png: 84253046e0b9865295302aec4670292e631c0ee0af8edc671233ef9f513f4b27
    • studio-studio-sign-in-mobile-390x844.png: 1a10b294003b658f444baa72fe327d0cd3127e291bbfad3fe00f69e2a5677440
    • verification-missing-not-found-desktop-1440x900.png: d22f14f0225528d6abe09ab2d20e6159d3aa93013752c6d8858170093ad06314
    • verification-missing-not-found-mobile-390x844.png: 9a22572bab68bf58f92bd06a025090ad70a0cc2ab0f5901f72db46439e4048ca
  • 4 Studio Batch Preview & Post-Confirm Screenshots (/home/alexey/git/dtc-website-wt-course-launch-qa2/.tmp/screenshots/issue-468/, manifest.json 86d41e9990b7d8fe8b26fc2877966e29998d64b787be66661e5d91d6f48b1503):
    • batch-preview-desktop-1440x900.png: 3e5739975166c6ec5e44b6d8c8a600504b50c8ba7b17e99d8ea205d96b7a7e66
    • batch-preview-mobile-390x844.png: 06c81c9fc65b5094f0925d4e27c24d1a101f64d0a2282041f26f2579d3421c0b
    • batch-confirmed-desktop-1440x900.png: 666414f273591fe7d0e63709ce943cb5232a818c71c1bd4f5240577131996a56
    • batch-confirmed-mobile-390x844.png: 8850c331ea99d23b0ab8854029eb064bcdab627bd926610d92a00ec8cf563828
Rollout Boundary
  • Code and verification evidence are complete and accepted on the frozen 19-file bundle over 054f233d7e8192f832064603c109fdd3f8b02a15. No live announcements or cloud deployments were triggered during QA; production deployment, live sender credentials, and COURSE_ANNOUNCEMENTS_ENABLED activation (website/settings/announcements.py:12) remain explicit human-operated release steps.
主要言語
Python
スター
0
フォーク
0
PR マージ指標
30日以内にマージされた PR はありません

環境構築

  • Dockerfile または Docker Compose ファイルあり
  • プルリクエストのテンプレートなし
  • コントリビューションガイドなし

はじめの一歩

  1. issue を最後まで読み、次にプロジェクトのコントリビューションガイドを読みます。
  2. 着手することを issue にコメントします — 二人が同じ作業をするのを防げます。
  3. リポジトリをフォークし、ブランチを切って変更します。
  4. issue 番号を参照したプルリクエストを送ります。

DataTalksClub/website のほかの issue

DataTalksClub/website の issue をすべて見る

似ている issue

Python の issue をもっと見る

新しい issue をメールで受け取る

初心者向けの GitHub issue を短くまとめたダイジェスト。