The weekly email digest (#11) #1358
Loading…
Reference in a new issue
No description provided.
Delete branch "feat/11-weekly-digest"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Implements #11, the weekly email digest. Defaults below are the implementer's choices for the owner to confirm; each is a single constant in
webapp/backend/app/services/digest_service.py.What it does
users.digest_email_opt_in(boolean, not null, default false) andusers.digest_sent_at(timestamptz, null). Migrationb0011d1ae570, revisesa1257b0c1d2e, real downgrade (checked upgrade, downgrade -1, upgrade).PUT /api/me/notification-settings(digest: {email}; read back asdigestin the GET, null without mail).poll-weekly-digests, hourly. For each opted-in, non-system user with an address whose local time (users.timezone, else UTC) is Monday 08:00-08:59 and who has not been queued one in the last 6 days, it builds one email across all their live campaigns and queuessend_weekly_digest.digest_sent_atis stamped and committed right after the enqueue succeeds; a failed enqueue leaves the person unstamped so the next hourly poll retries. Nothing is stamped or sent when all three lists are empty, or when the instance has no mail.Defaults chosen (constants)
DIGEST_WEEKDAY = 0(Monday),DIGEST_HOUR = 8DIGEST_MIN_GAP = 6 daysDIGEST_LOOKAHEAD = DIGEST_LOOKBACK = 7 daysDIGEST_FALLBACK_ZONE = "UTC"3600.0secondsFor the owner to look at
outerjoinincollect()to drop.List-Unsubscribeheader): the footer links to the notification settings page as asked. A stop token for it would be a small follow-up (email_tokens,/email/stop).userstable has no is_active column).confirmed_timeof completed sessions as the date.BOT_CONTRACT_VERSIONuntouched.Wording for review
Setting (notification grid, Email column):
Email:
<title> (<campaign>) — Tue 10 Nov, 8:00 PM EST, you are inVerification
tests/test_weekly_digest.py tests/test_notifications.py tests/test_notifications_out.py tests/test_email_routes.py tests/test_email_tokens.py tests/test_recap_email.py tests/test_calendar_invites.py tests/test_asr_hotwords.py-> 215 passed.eslint0 errors;vitest run src/ui/NotifyGrid src/pages/Notifications.test.jsx src/pages/Profile.test.jsx-> 47 passed (first run had 3 timeouts at a host load average of 58; rerun with--testTimeout=90000all green).ruff format/ruff check(0.4.4, the CI paths),changelog.py check,check_version_sync.py: clean.🤖 Generated with Claude Code
Pushed 3 commits (afcfd620..00928fec).
CI fix.
test_enqueue_off_loopmatches publishing helpers by bare name.digest_taskshad a nested sync helper calledenqueue()that calls.delay(), so everyawait enqueue(...)in the app (the realapp.enqueue.enqueue) looked like a blocking publish: 31 hits on the branch, 0 on main. Renamed the helperpublish_digest, andqueue_due_digestsnow runs it viaoff_loop(#841). The check is unchanged.DG1.
users.digest_weekday(smallint, default 0, CHECK 0-6) added to the same migrationb0011d1ae570(upgrade/downgrade/upgrade verified).GET/PUT /api/me/notification-settingscarrydigest_weekday(422 outside 0-6). Settings → Notifications: a day select under the Weekly digest email choice, shown only while it is on. Hour stays 08:00 local.DG3. Reused the existing signed stop-token machinery (
what=digest) and the existing/email/stoppage,/api/email/stop/one-clickPOST (rate-limited, no session/CSRF) andList-Unsubscribe+List-Unsubscribe-Postheaders. Footer gains "Stop these emails". Bad or foreign tokens stay 404{"detail": ...}as for every other stop token.Tests: backend 156 passed (digest, off-loop, tokens, email routes, notifications, one-head); frontend 53 passed; ruff format/check, version sync, changelog check clean.
The render check found the two selects sharing one line at 390 px ("Eac", "Frid") and the stacked email select squeezed to "Each c" at 1280 px. The day now wraps onto its own line on narrow screens, and on a wide screen both keep room for their longest word. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>Render checks (orchestrator, journeys stack, Priya,
/profile/notifications):00928fec, merged with #1359): at 390 px the digest's two selects shared one line and clipped to "Eac" and "Frid". At 1280 px the email select clipped to "Each c".5bc722ae: at phone width the day takes its own line, and on a wide screen both selects keep room for their longest word.5bc722aewith "Each one" and Wednesday:heading-order(moderate), which is also on main ("Per campaign" is an h3 under the h1). Filed as #1360, not part of this PR.