feat: add parent_phone, school, height to CoachPlayerBrief #290
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!290
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "283-coach-dashboard-parent-contact"
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
parent_phone,school, andheightfields toCoachPlayerBriefso coaches can see parent contact info and player details in their dashboard roster. Eagerly loads thePlayer.parentrelationship to avoid N+1 queries.Changes
src/basketball_api/routes/coaches_api.py— Added three optional fields toCoachPlayerBriefmodel, populated them in_build_team_response, and chainedjoinedload(Player.parent)on both endpoint queriestests/test_coaches_api.py— Updatedteam_with_playersfixture with phone/school/height data, added two new tests verifying field presence and null handlingTest Plan
test_includes_parent_phone_school_heightverifies populated fieldstest_null_fields_when_missingverifies null when player lacks school/heightruff formatandruff checkcleanReview Checklist
school, DB storescurrent_schoolRelated Notes
Related
QA Review -- PR #290
Diff Analysis
Findings
No issues found.
parent_phone,school,height) with= Nonedefaults. Backward compatible for existing API consumers.schoolcorrectly maps fromcurrent_schoolDB field, matching frontend contract expectation (Spike #278 Mismatch 4).p.parent.phone if p.parent else Noneguards against orphaned players.joinedload(Player.parent)to prevent N+1 queries. Correct approach.ruff formatandruff checkclean per PR body.Acceptance Criteria Check
parent_phone: Optional[str]on CoachPlayerBriefschool: Optional[str]populated fromcurrent_schoolheight: Optional[str]on CoachPlayerBriefVERDICT: APPROVED
PR #290 Review
DOMAIN REVIEW
Tech stack: Python / FastAPI / SQLAlchemy / Pydantic
Model correctness -- All three new fields map correctly to existing DB columns:
parent_phone-- sourced fromPlayer.parent.phone(Parent model,String(50), nullable)school-- sourced fromPlayer.current_school(Player model,String(200), nullable)height-- sourced fromPlayer.height(Player model,String(20), nullable)The field name mapping
school(API) tocurrent_school(DB) is an intentional frontend-contract decision, documented in the PR checklist. This is fine.SQLAlchemy eager loading -- The chained
.joinedload(Team.players).joinedload(Player.parent)is applied to both the/meand/{coach_id}queries. Note thatTeam.playersalready haslazy="selectin"on the relationship definition, so the explicitjoinedload(Team.players)overrides that to a JOIN for those queries. The chained.joinedload(Player.parent)correctly prevents N+1 on the parent lookup. Both endpoints are updated consistently -- no DRY divergence.Null safety --
p.parent.phone if p.parent else Nonecorrectly handles the case where a player has no parent record (thoughparent_idis non-nullable in the model, defensive coding is appropriate).p.current_schoolandp.heightare nullable columns and map directly to optional Pydantic fields.Pydantic model --
CoachPlayerBriefadds threestr | None = Nonefields withfrom_attributes = True. DefaultNoneensures backward compatibility if the model is ever constructed without these fields.Import --
Playerimport added to support the type reference injoinedload(Player.parent). Correct and necessary.Test coverage -- Two new tests:
test_includes_parent_phone_school_height-- verifies populated values (happy path)test_null_fields_when_missing-- verifies null for school/height when not set on the playerThe fixture is updated with
phone="555-1234"on the parent,heightandcurrent_schoolon only the first player (Jordan), leaving the second player (Alex) without those values. This properly exercises both code paths.One observation: both new tests only exercise the
/coaches/meendpoint. The/{coach_id}endpoint uses the same_build_team_responsehelper and the same eager-loading pattern, so the coverage is effectively shared. This is acceptable given the shared helper.BLOCKERS
None.
_build_team_responseNITS
Test: no coverage of
/{coach_id}with new fields -- While the shared helper means the logic is tested, a single assertion on the profile endpoint response would provide full confidence that the eager loading works on both query paths. Low risk, non-blocking.Playerimport on main branch -- The diff addsPlayerto the import line. Onmain,coaches_api.pyimports onlyCoach, Team. ThePlayerimport is necessary for thejoinedloadchain. Clean.SOP COMPLIANCE
283-coach-dashboard-parent-contactfollows{issue-number}-{kebab-case-purpose}Closes #283, spike #278PROCESS OBSERVATIONS
Player.parentshould follow this same pattern.VERDICT: APPROVED