feat: add evaluated_only filter to GET /api/v2/refractor/cards (#174) #175
No reviewers
Labels
No Label
ai-changes-requested
ai-failed
ai-merged
ai-pr-opened
ai-reviewed
ai-reviewing
ai-reviewing
ai-working
bug
enhancement
evolution
performance
phase-0
phase-1a
phase-1b
phase-1c
phase-1d
security
tech-debt
todo
No Milestone
No project
No Assignees
2 Participants
Notifications
Due Date
No due date set.
Dependencies
No dependencies set.
Reference: cal/paper-dynasty-database#175
Loading…
Reference in New Issue
Block a user
No description provided.
Delete Branch "issue/174-get-api-v2-refractor-cards-add-evaluated-only-filt"
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?
Closes #174
Summary
evaluated_only: bool = Query(default=True)parameter tolist_card_states()inrouters_v2/refractor.pyTrue(default), appliesWHERE last_evaluated_at IS NOT NULL— excludes placeholder rows created at pack-open time that have never been through the evaluatorFalse, returns all rows including zero-value unevaluated cards (admin/pipeline use case)last_evaluated_at IS NOT NULLrather thancurrent_value > 0to correctly include cards with legitimately zero stats after evaluationFiles changed
app/routers_v2/refractor.py— new param + filter predicate + updated docstringtests/test_refractor_state_api.py— new testtest_list_cards_evaluated_only_false_includes_unevaluated(T3-8)Test
New SQLite-backed test creates one unevaluated card (
last_evaluated_at=None) and one evaluated card (last_evaluated_at=timestamp) for the same team, then asserts:evaluated_only=falsereturns both cards (count=2)No migration required — pure query-layer change.
AI Code Review
Files Reviewed
app/routers_v2/refractor.py(modified)tests/test_refractor_state_api.py(modified)Findings
Correctness
evaluated_only: bool = Query(default=True)is added tolist_card_states()at the correct position in the parameter list (beforelimit/offset)..is_null(False)is the correct way to expressIS NOT NULL. ✅total = query.count(), so the responsecountcorrectly reflects total matching evaluated rows before pagination. ✅Trueis a behaviour change for existing callers (they now get only evaluated cards by default), but the PR body clearly documents this as intentional and motivates it with the performance context ("2753 rows, ~14 evaluated"). No issues.Security
evaluated_onlyis a plain boolean — no injection risk.Style & Conventions
bool(notOptional[bool]) is consistent withlimit: intandoffset: intin the same signature. ✅.where()call. ✅Test Coverage
last_evaluated_at=None) and one evaluated row (last_evaluated_at=timestamp) for the same team. ✅evaluated_onlyparam) and once withevaluated_only=false— and asserts count and exact player names in both cases. ✅Suggestions
T3-8, but based on prior PRsT3-8was already used fortest_get_card_state_last_evaluated_at_null. The numeric label is comment-only and doesn't affect test execution, but consider renaming toT3-9to avoid confusion if the T3-x numbering is referenced elsewhere.Verdict: APPROVED
Clean, focused PR. Correct Peewee idiom, filter placed correctly before the count, both the default and opt-out paths are tested with specific assertions. No migration needed. The T3-8 label collision is a trivial nit.
Automated review by Claude PR Reviewer
AI Code Review
Files Reviewed
app/routers_v2/refractor.py(modified)tests/test_refractor_state_api.py(modified)Findings
Correctness
query = query.where(RefractorCardState.last_evaluated_at.is_null(False))is applied beforetotal = query.count(), so the count reflects the filtered result rather than the full table — this is the right behavior.is_null(False)is the correct Peewee idiom forIS NOT NULL. Usingis_null(True)would invert the logic, so the boolean argument is load-bearing and correctly set.default=Truesemantics are accurate: new callers get evaluated-only results without needing to pass the parameter, which matches the stated use case (bot performance at 2753 rows, ~14 evaluated).Breaking Change Risk: default=True
GET /api/v2/refractor/cardsthat expects unevaluated placeholder rows in its results will now silently receive a filtered subset.default=Trueis the correct default and any result set that previously included unevaluated rows was arguably wrong. If any admin/pipeline caller depends on the full unfiltered set, it must now passevaluated_only=false.default=Trueis the right call.Security
is_null()generates parameterized SQL.Style & Conventions
Test Coverage
evaluated_only=falsereturns both.evaluated_only=true(explicitly passed, not defaulted) behaves the same as the default. This is a minor omission since the parameter plumbing is trivially correct, but noted for completeness.Suggestions
?evaluated_only=trueexplicit-true assertion alongside the default call in T3-8 to ensure the parameter round-trips correctly through FastAPI's bool parsing (FastAPI acceptstrue/falseas strings for bool query params — worth one extra assert).Verdict: APPROVED
The implementation is correct, the filter logic is sound Peewee ORM, the default is well-justified by the production data shape, and the test coverage is solid. No issues warrant blocking this merge.
Automated review by Claude PR Reviewer
Approved via pd-pr