feat: add local teams first-week schedule exception email #329
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!329
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "313-local-first-week-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
send_local_first_week_email()andPOST /admin/email/local-first-weekendpoint so local team parents (16U/17U Local Kings) are informed: no Monday practice April 6, first practice is Tuesday April 7 at West High 6-8 PM with parent-player meeting, Monday schedule resumes April 13 at BWill.Changes
src/basketball_api/services/email.py-- addedsend_local_first_week_email()function with branded HTML/plain text, practice schedule table, email_log entrysrc/basketball_api/routes/admin.py-- addedPOST /admin/email/local-first-weekendpoint that queries teams withLIKE '%Local%', excludes TEST players, supportstest_emailquery param, deduplicates parentstests/test_local_first_week_email.py-- 8 tests covering endpoint 200, local-only filtering, TEST exclusion, content verification, auth gate, error capture, subject line, and HTML key content assertionsTest Plan
pytest tests/test_local_first_week_email.py-- 5 pass, 4 errors (pre-existing DB fixture enum race condition affecting all test files equally, not introduced by this PR)ruff formatandruff checkpass cleantest_email=draneylucas@gmail.comquery param to preview before sending to all local parentsReview Checklist
Related Notes
Related
Closes #313
QA Review -- PR #329
Summary
Adds
send_local_first_week_email()andPOST /admin/email/local-first-weekendpoint for local team parents. Follows the exact pattern established bysend_welcome_practice_email()and the welcome-practice endpoint from PR #318.Code Quality
Team.name.ilike("%Local%")-- correct per issue spec (no hardcoded team IDs).EmailType.announcementas required. No new enum values.LocalFirstWeekResponsemirrorsWelcomePracticeResponse. Consistent.Email Content
Tests
get_gmail_clientand inspectsend_messagecall args directly -- good approach.teampreferenceCREATE TYPE collision ondrop_all/create_allcycle). Confirmed same failures ontest_welcome_practice_email.py. Not introduced by this PR.Nits
None. Clean implementation that follows established patterns exactly.
VERDICT: APPROVED
PR #329 Review
DOMAIN REVIEW
Tech stack: Python / FastAPI / SQLAlchemy / Pydantic. Gmail SDK for email delivery. pytest with unittest.mock for tests.
Python/FastAPI specifics:
html.escape()used on all user-supplied values interpolated into HTML (parent name, player name, team name, location). Correct XSS prevention.BaseModelfor response (LocalFirstWeekResponse). Clean.dbandrequire_admin. Correct pattern.SQLAlchemy patterns:
joinedload(Parent.players)used to avoid N+1 on parent->player relationship. Good.player.teamproperty (returns first team from M2M) used correctly -- matches existing codebase pattern atmodels.py:261.PracticeScheduleper team -- this is N+1 but acceptable for a low-volume admin endpoint. Not a blocker.ilike("%Local%")for team filtering is correct and case-insensitive. Good.Input validation:
test_emailis a query parameter filtered viaParent.email == test_email-- it's used as a DB filter, not interpolated into SQL. No injection risk.Error handling:
except Exception, logged, and returned inerrorslist. Endpoint stays 200 with partial results. Matches existing patterns.Important note on
PracticeScheduleimport: The endpoint queriesPracticeScheduleinadmin.pybut the diff does not show this import being added. This import must already exist onmainfrom the merged #318 PR. If it does not exist, this will be a runtimeNameError. Since the PR declares dependency on #318 (merged), this should be fine, but worth confirming at merge time.BLOCKERS
None found.
require_admindependency).NITS
Misleading test name:
test_email_html_contains_key_contentinTestLocalFirstWeekEndpointpatchessend_local_first_week_emailand only assertscall_kwargs is not None. The actual HTML content assertions live in the separateTestLocalFirstWeekEmailContentclass. Consider renaming totest_send_called_with_correct_argsor adding actual kwarg assertions (e.g., verify the parent, player, team objects passed).Hardcoded event dates: April 6, April 7, April 13, "West High Field House", "BWill" are all hardcoded in both the endpoint docstring and the email body. This is acceptable for a one-time event email but makes the function non-reusable. If future first-week emails are needed, consider parameterizing dates/locations.
breakafter first qualifying player: The inner loop breaks after the first matching player-team pair per parent (line:# One email per parent/break). This means if a parent has multiple players on different local teams, only the first player's info appears in the email. This might be intentional (dedup), but the parent would only see one child's team info. Theemailed_parentsset provides a second layer of dedup that is now redundant given thebreak. Consider documenting the intended behavior.Test fixture
phonefield: Theregistered_parentfixture setsphone="555-9876"even thoughParent.phoneis nullable. Not wrong, but inconsistent with not setting other optional fields. Minor.SOP COMPLIANCE
313-local-first-week-emailfollows{issue-number}-{kebab-case-purpose}conventionPROCESS OBSERVATIONS
VERDICT: APPROVED
eeca2142e3ba1dd5bfe5