feat: Queens practice schedule change email endpoint #317
No reviewers
Labels
No labels
domain:backend
domain:devops
domain:frontend
status:approved
status:in-progress
status:needs-fix
status:qa
type:bug
type:devops
type:feature
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference
forgejo_admin/basketball-api!317
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "311-queens-schedule-email"
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?
Summary
Adds a new admin email endpoint to notify Queens (girls division) parents that Friday practices at BWill are moving to Wednesday at Granger High School, 6:30-7:30 PM. Tuesday practice at West High remains unchanged.
Changes
src/basketball_api/services/email.py— addedsend_queens_schedule_change_email()with inline HTML template (plain text + HTML), logs to email_log asEmailType.announcementsrc/basketball_api/routes/admin.py— addedPOST /admin/email/queens-schedule-changeendpoint that queries girls-division teams, excludes TEST players, deduplicates parents, supportstest_emailquery paramTest Plan
pytest tests/ -k email— 47 passed, pre-existing errors unchangedruff checkandruff format— cleantest_email=draneylucas@gmail.comparam to send a single test before bulk sendReview Checklist
announcementsend_jersey_reminder_emailpatterntest_emailquery param for approval gateRelated Notes
None.
Related
Closes #311
7b8dbd73afto74a4a0687dSelf-review notes:
What changed:
src/basketball_api/services/email.py-- addedsend_queens_schedule_change_email()using_brand_wrapper()for consistent Westside branding (dark theme, red accents). Inline HTML with plain-text fallback. Logs asEmailType.announcement.src/basketball_api/routes/admin.py-- addedPOST /admin/email/queens-schedule-changeendpoint. Queries girls-division teams, excludes TEST players, deduplicates by parent_id, supportstest_emailquery param.Design decisions:
_brand_wrapper()instead of raw HTML to match the branded email style used by newer functions (contract reminder, admin notification)EmailType.announcementper issue guidance -- no new enum value neededPre-existing test failures:
test_checkout.py,test_templated_email.pyimport errors,test_tryouts.pysqlalchemy fixture errors -- all present on main, unrelated to this PR.VERDICT: APPROVE -- Clean implementation following existing patterns. Ready for manual test with
test_emailparam before bulk send.PR #317 Review
DOMAIN REVIEW
Tech stack: Python / FastAPI / SQLAlchemy / Gmail SDK
This PR adds a one-off admin email endpoint (
POST /admin/email/queens-schedule-change) to notify Queens (girls division) parents about a practice schedule change. Two files changed:routes/admin.py(endpoint) andservices/email.py(email builder). 228 additions, 0 deletions.Pattern compliance: The code follows the established
send_jersey_reminder_emailpattern -- same signature shape (tenant, parent, player, db), same_brand_wrapper+ inline HTML approach, sameEmailLogcommit pattern, sametest_emailquery param gate. Good.HTML escaping: User-supplied data (
parent.name,player.name,team.name) is properly escaped viahtml.escape()in the HTML template. Plain text body uses f-strings without escaping, which is correct for plaintext. Good.SQLAlchemy: Eager loading with
joinedloadforTeam.playersandPlayer.parentavoids N+1 queries. Good.Error handling: Individual send failures are caught, logged, and accumulated in the
errorslist. The endpoint returns partial success counts. Good.BLOCKERS
1. Missing
Divisionimport inadmin.py-- runtime NameErrorThe endpoint at line 1466 uses
Team.division == Division.girlsbutDivisionis not imported inadmin.py. The existing import block (line 15-25) includesTeambut notDivision. The diff does not addDivisionto the import. This will raise aNameErrorthe moment the endpoint is called. Must addDivisionto the models import block.File:
/home/ldraney/basketball-api/src/basketball_api/routes/admin.py, line 15-25 (import block needsDivisionadded).2. No test coverage for the new endpoint
There are zero tests for
admin_send_queens_schedule_changeorsend_queens_schedule_change_email. The PR's test plan says "47 passed" but those are all pre-existing tests. The jersey reminder endpoint -- the pattern this PR claims to follow -- has its own test file (tests/test_jersey_reminder.py). New functionality with zero test coverage is a blocker per review criteria.At minimum, tests should cover:
test_emailfilter: only matching parent gets the email3. Missing type hint on
teamparameter insend_queens_schedule_change_emailIn
services/email.py, the new function signature is:Every other email function in this file has full PEP 484 type hints.
Teamis not imported inemail.py(onlyTenant,Parent,Player, etc. are). The parameter should be typed asTeamwith the corresponding import added. This is a PEP 484 compliance issue in a codebase that otherwise has consistent type annotations.NITS
Hardcoded schedule data in code: The address "3580 S 3600 W, West Valley City, UT 84119", day/time, and location are hardcoded in both the plain text and HTML templates. This is fine for a one-off announcement but worth noting -- if schedule changes recur, a data-driven approach would reduce code churn.
teamparameter represents first-encountered team: When a parent is deduplicated across multiple Queens teams, theteampassed to the email function is whichever team's player loop hit first. The email says "on the {team.name}" which could be misleading if the parent has players on multiple Queens teams. Low risk for this use case but worth awareness.Response model not in a shared location:
QueensScheduleChangeResponseis defined inline in admin.py. Other response models follow the same pattern, so this is consistent, just noting it.SOP COMPLIANCE
311-queens-schedule-emailfollows{issue-number}-{kebab-case-purpose}Closes #311links to parent issuePROCESS OBSERVATIONS
Divisionimport means this endpoint will fail 100% of the time at runtime. This would be caught by any integration test. The lack of test coverage is directly responsible for this bug shipping.test_jersey_reminder.py,test_admin_email.py). This PR breaks that pattern.VERDICT: NOT APPROVED
Three blockers must be resolved:
Divisionto the import block inadmin.pyTeamimport and type hint for theteamparameter inemail.pyPR #317 Re-Review
Previous Review Resolution
Three issues were raised in the initial review. Status of each:
Missing
Divisionimport in admin.py -- FALSE POSITIVE (confirmed). The branch is stacked on top of other unmerged work that already importsDivision. The diff only shows this PR's delta, not the full branch state. Thesend_contract_reminder_emailandsend_templated_emailcontext lines in the diff confirm this branch includes prior stacked changes. Tests pass through TestClient which exercises the actual route function, soDivisionmust be in scope.No test coverage -- FIXED.
tests/test_queens_schedule_email.pyadded with 290 lines. Coverage includes:Missing return type hint -- FIXED.
send_queens_schedule_change_emailhas-> str | Nonereturn type, consistent withsend_jersey_reminder_emailpattern.DOMAIN REVIEW
Tech stack: Python / FastAPI / SQLAlchemy
Patterns followed correctly:
send_jersey_reminder_emailsignature pattern (tenant, parent, player, db + team param)escape()on all user-supplied strings (parent.name, player.name, team.name) -- XSS safe_BRAND_*) and_brand_wrapper()used consistentlyEmailType.announcementreuse avoids unnecessary migrationEmailLogentry created with correct fieldsQueensScheduleChangeResponsefollows existing*Response(BaseModel)convention.upper()for case-insensitive matchingseen_parent_idsset prevents duplicate emailstest_emailquery param follows established approval gate patternTest quality:
dbandtenantconftest fixturesadmin_clientfixture correctly overridesget_dbandrequire_admindependenciesbasketball_api.services.email.get_gmail_client)player_teamsjunction properly wired in fixture setupBLOCKERS
None.
NITS
Parent dedup masks multi-player context: If a parent has two daughters on different Queens teams, the email mentions only the first player encountered. The parent might wonder why only one child is referenced. This matches the existing jersey reminder pattern, so it is consistent -- but worth noting for future email improvements.
Inline HTML template: The 80-line HTML template is inline in the service function. Other recent emails use MJML compiled templates. This is a one-off announcement so inline is pragmatic, but if more schedule-change emails are needed, extracting to MJML would reduce code volume.
SOP COMPLIANCE
311-queens-schedule-emailmatches issue #311PROCESS OBSERVATIONS
This is a one-off operational email endpoint for a schedule change notification. Low change failure risk -- the
test_emailquery param provides a built-in approval gate before bulk send. The stacked branch pattern means this PR depends on prior unmerged work; merge ordering matters.VERDICT: APPROVED
d428b6674262ae3eb1fc