Conversation
There was a problem hiding this comment.
Pull request overview
This PR introduces a staff-facing Sponsor Management UI under the sponsors app, plus supporting infrastructure for email testing and outbound-notification auditing/logging.
Changes:
- Add a new
/sponsors/manage/management UI (templates, forms, URLs) for handling sponsorships, packages, benefits, contacts, contracts, and notifications. - Persist outbound sponsorship notification sends to a new
SponsorshipNotificationLogmodel and display history in the management UI. - Improve local dev ergonomics by adding MailDev + SMTP-backed local email configuration; update sponsor contract email templates; bump
django-storages.
Reviewed changes
Copilot reviewed 43 out of 44 changed files in this pull request and generated 7 comments.
Show a summary per file
| File | Description |
|---|---|
uv.lock |
Bumps django-storages from 1.14.4 to 1.14.6 (lockfile update). |
pydotorg/settings/local.py |
Uses SMTP backend when EMAIL_HOST is set (for MailDev), otherwise console backend. |
docker-compose.yml |
Adds a maildev service and wires EMAIL_HOST/EMAIL_PORT into the web service. |
apps/sponsors/use_cases.py |
Logs each sent sponsorship notification to SponsorshipNotificationLog. |
apps/sponsors/urls.py |
Mounts the management UI under sponsors/manage/. |
apps/sponsors/templates/sponsors/manage/sponsorship_notify.html |
UI to preview/send a notification for a single sponsorship. |
apps/sponsors/templates/sponsors/manage/sponsorship_list.html |
Sponsorship list UI with filters, bulk actions, and live search. |
apps/sponsors/templates/sponsors/manage/sponsorship_edit.html |
Edit sponsorship details (package/fee/year). |
apps/sponsors/templates/sponsors/manage/sponsorship_detail.html |
Sponsorship detail/review UI incl. contracts, benefits, assets, communications. |
apps/sponsors/templates/sponsors/manage/sponsorship_approve_signed.html |
Approve sponsorship while uploading a signed contract document. |
apps/sponsors/templates/sponsors/manage/sponsorship_approve.html |
Approve sponsorship (creates draft contract) with quick date helpers. |
apps/sponsors/templates/sponsors/manage/sponsor_edit.html |
Create/edit sponsor company data. |
apps/sponsors/templates/sponsors/manage/package_list.html |
Package list UI grouped by year. |
apps/sponsors/templates/sponsors/manage/package_form.html |
Create/edit a sponsorship package. |
apps/sponsors/templates/sponsors/manage/package_confirm_delete.html |
Confirm-delete UI for packages. |
apps/sponsors/templates/sponsors/manage/notification_template_list.html |
List notification templates in the management UI. |
apps/sponsors/templates/sponsors/manage/notification_template_form.html |
Create/edit notification templates with variable copy helpers. |
apps/sponsors/templates/sponsors/manage/notification_template_confirm_delete.html |
Confirm-delete UI for notification templates. |
apps/sponsors/templates/sponsors/manage/notification_history.html |
Management UI listing sent notifications (log history). |
apps/sponsors/templates/sponsors/manage/guide.html |
End-user guide for PSF sponsorship team workflows. |
apps/sponsors/templates/sponsors/manage/dashboard.html |
Management dashboard summary for a selected year. |
apps/sponsors/templates/sponsors/manage/current_year_form.html |
UI to set the active sponsorship year. |
apps/sponsors/templates/sponsors/manage/contract_send.html |
UI for generating/sending contracts and internal review. |
apps/sponsors/templates/sponsors/manage/contract_execute.html |
UI to upload signed contract and execute/finalize sponsorship. |
apps/sponsors/templates/sponsors/manage/contact_form.html |
Create/edit sponsor contact roles and info. |
apps/sponsors/templates/sponsors/manage/clone_year.html |
Clone packages/benefits from one year to another. |
apps/sponsors/templates/sponsors/manage/bulk_notify.html |
UI to send notifications in bulk across sponsorships. |
apps/sponsors/templates/sponsors/manage/benefit_list.html |
Benefit list UI with filtering/pagination. |
apps/sponsors/templates/sponsors/manage/benefit_form.html |
Create/edit benefit + manage feature configurations and related data. |
apps/sponsors/templates/sponsors/manage/benefit_confirm_delete.html |
Confirm-delete UI for benefits. |
apps/sponsors/templates/sponsors/manage/benefit_config_form.html |
Create/edit benefit feature configuration objects. |
apps/sponsors/templates/sponsors/manage/_base.html |
Management UI shell + global JS table-sorting helper. |
apps/sponsors/templates/sponsors/email/sponsor_contract_subject.txt |
Replaces placeholder subject with a real contract subject template. |
apps/sponsors/templates/sponsors/email/sponsor_contract.txt |
Replaces placeholder contract email body with real copy. |
apps/sponsors/models/notifications.py |
Adds SponsorshipNotificationLog model to persist sent notifications. |
apps/sponsors/models/__init__.py |
Re-exports SponsorshipNotificationLog. |
apps/sponsors/migrations/0104_add_notification_log.py |
Migration creating the notification log table. |
apps/sponsors/management/commands/seed_sponsor_manage_data.py |
Dev-only command to seed realistic data for the management UI. |
apps/sponsors/manage/urls.py |
URL routing for the management UI. |
apps/sponsors/manage/forms.py |
Forms backing the management UI (benefits/packages/sponsorship ops/notifications/etc.). |
apps/sponsors/manage/__init__.py |
Documents that the manage UI is staff/group restricted. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
apps/sponsors/templates/sponsors/manage/sponsorship_detail.html
Outdated
Show resolved
Hide resolved
apps/sponsors/templates/sponsors/manage/sponsorship_detail.html
Outdated
Show resolved
Hide resolved
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 43 out of 44 changed files in this pull request and generated 2 comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
… CSV export on detail Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Complete rewrite covering: dashboard, sponsorship review workflow, 6-step composer wizard, contract lifecycle, benefits & packages with feature configs, notifications with templates and history, sponsors & contacts, bulk actions & export, and what's still in Django admin. Written in direct, human tone for new team member onboarding. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Dashboard shows cross-year "Expiring Soon" (90-day window with color-coded countdown) and "Recently Expired" sections with one-click "+ Renewal" buttons that launch the Composer with sponsor pre-selected and renewal flag set. Sponsorship list shows expiry tags in the Period column. Detail page shows Expiring Soon/Expired tags and a "+ Renewal" button for finalized sponsorships. Adds sponsors_manage templatetags, guide documentation, and 15 tests. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Adds a "Sync to Sponsorships" button on the benefit edit page that lets staff push updated benefit data (name, description, value, features) to all active sponsorships using that benefit. Shows eligible sponsorships with checkboxes, excludes rejected and expired. Includes guide documentation and 6 tests. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Regenerate only visible when sponsorship is unlocked - New contract revision = count of historical contracts (not always 0) - Regenerate/Cancel buttons use all:unset for consistent sizing - Add Assets download and Sponsor View buttons to detail page Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The generated PDF/DOCX are what was sent to the sponsor, not the final executed version. "Sent" is accurate, "Final Version" was misleading for contracts still awaiting signature. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Full CRUD for legal clauses under More > Legal Clauses. List view shows clause text preview, benefit count, and up/down move buttons for ordering. Edit page shows how many benefits reference the clause. Delete confirms with impact warning. Removes legal clause CRUD from the "admin-only" list in the guide. Includes 10 tests. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Composer contract editor now shows insert buttons for each managed legal clause below the Legal Clauses textarea. Clicking a clause button appends its text to the field. Also removes the legal clause line from the admin-only guide section. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Adds an asset browser under More > Assets that lists all sponsor and sponsorship assets with filters for asset type (text, image, file, response), related object (sponsor vs sponsorship), submission status (submitted vs missing), and internal name search. Uses batch-loaded lookups to avoid N+1 queries on generic relations. Removes "Detailed asset filtering" from the admin-only guide section. Includes 6 tests. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Assets grouped by sponsor company with collapsible sections and submitted/total badge (green/orange/red). Expired and rejected sponsorship assets excluded. Each row shows package name and sponsorship status. Companies with missing assets get a Send Reminder link. Refactored view into helper methods for linting. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Sponsor-level assets show "company-level" label instead of a Sponsor tag. Internal name no longer bold to avoid looking like a link. Sponsorship assets still link to their detail page with package and status tags. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Adds a Sponsors page (More > Sponsors) to browse and search all sponsor companies with contact count, sponsorship count, location, website, and quick links to edit or create a sponsorship via the Composer. Also adds the Assets section to the guide documenting the grouped asset browser, filters, and Send Reminder feature. Updates sponsor edit to redirect back to the sponsor directory. Includes 4 tests. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Adds a Revenue Report page (More > Revenue) with: - Summary cards: total/finalized/approved revenue, deal count + average - Revenue by Package: full-width horizontal bar chart - Year-over-Year comparison chart - Sponsorship detail table with fee, internal value, margin percentage Dashboard revenue card now links directly to the report. Sponsorship detail page gets a collapsible Financial Breakdown section showing fee vs internal value with a coverage bar, and value-by-program mini bar chart. Guide documented. 6 tests. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Replaces the revenue report with a full Finances page (More > Finances) featuring Chart.js interactive charts: - Revenue Trend: stacked bar (finalized vs approved) across all years - Status Breakdown: doughnut chart for selected year - Revenue by Package: horizontal bar chart - Deal Count & Avg Size: combo bar+line chart showing trends Renames revenue -> finances throughout (URL, nav, guide, dashboard link). Sponsorship detail keeps its financial breakdown section with fee vs internal value coverage bar and per-program mini charts. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 49 out of 51 changed files in this pull request and generated 5 comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
|
may be best to split on 6c06c38, merge that, and pull anything after into other PRs |
- Use json_script filter instead of |safe for chart data - Add rel="noopener noreferrer" to all target="_blank" links - Gate email date range on both start_date and end_date - Log notification persistence failures instead of silent pass - Fix test assertions to match actual view behavior Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 49 out of 51 changed files in this pull request and generated 4 comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| @register.simple_tag | ||
| def days_until(target_date, reference_date): | ||
| """Return the number of days between reference_date and target_date.""" | ||
| if not target_date or not reference_date: | ||
| return 0 | ||
| return (target_date - reference_date).days |
There was a problem hiding this comment.
This new days_until template tag isn’t covered by the existing templatetag test suite (apps/sponsors/tests/test_templatetags.py). Adding a small unit test (including edge cases like missing dates and negative deltas) would align with existing coverage and prevent subtle date math regressions.
| def _save_m2m(self): | ||
| """Persist reverse benefit associations after the package is saved.""" | ||
| super()._save_m2m() |
There was a problem hiding this comment.
This form overrides the private ModelForm._save_m2m() hook. Since it’s an internal API, it’s more brittle across Django upgrades. Prefer overriding save() (after super().save(commit=False) + save_m2m()) or implementing save_m2m() instead, and keep all M2M synchronization there.
| def _save_m2m(self): | |
| """Persist reverse benefit associations after the package is saved.""" | |
| super()._save_m2m() | |
| def save(self, commit=True): | |
| """Save the package and then persist benefit associations when committing.""" | |
| instance = super().save(commit=commit) | |
| if commit: | |
| self.save_m2m() | |
| return instance | |
| def save_m2m(self): | |
| """Persist reverse benefit associations after the package is saved.""" | |
| super().save_m2m() |
| <div class="manage-section-header" style="flex-wrap:wrap;gap:12px;"> | ||
| <h2>Sent Notifications <span class="badge">{{ paginator.count|default:logs|length }}</span></h2> | ||
| <form method="get" style="display:flex;gap:6px;align-items:center;"> | ||
| <input type="text" name="search" id="history-search" placeholder="Search subject, sponsor, recipient..." value="{{ filter_search }}" autocomplete="off" style="width:260px;padding:6px 10px;border:1px solid #ccc;border-radius:4px;font-size:13px;"> |
There was a problem hiding this comment.
{{ paginator.count|default:logs|length }} applies length to paginator.count (an int), which will render as 0/empty rather than the actual total. Use paginator.count (or page_obj.paginator.count) directly when paginated, and fall back to logs|length only when no paginator is present (e.g., via an {% if %} block or a precomputed total_count context var).
| # Persist notification log (best-effort, don't break sending) | ||
| try: | ||
| sent_by = None | ||
| if request and hasattr(request, "user") and getattr(request.user, "is_authenticated", False): | ||
| sent_by = request.user | ||
| SponsorshipNotificationLog.objects.create( | ||
| sponsorship=sponsorship, | ||
| subject=getattr(email, "subject", ""), | ||
| content=getattr(email, "body", ""), | ||
| recipients=", ".join(getattr(email, "to", [])), | ||
| contact_types=", ".join(contact_types), | ||
| sent_by=sent_by, | ||
| ) |
There was a problem hiding this comment.
New behavior persists SponsorshipNotificationLog entries when sending notifications, but the existing SendSponsorshipNotificationUseCaseTests don’t assert that logs are created (or that fields like subject/recipients/contact_types are recorded). Adding an assertion here would prevent regressions where the history UI silently stops recording sends.
1. Add distinct=True to Count annotations in SponsorListView to prevent cross-multiplication of contacts × sponsorships 2. Benefit form checks bound POST year data so changing year + package in a single submit works 3. SponsorshipEditForm same fix for year-scoped package filtering 4. Clear package M2M on cloned benefits when clone_packages unchecked 5. Return sent_count from notification use case; show warning when zero emails were actually sent instead of false success 6. Show error when PDF generation fails but DOCX succeeds so staff know the contract was not finalized Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
No description provided.