feat: formula engine for evolution value computation (WP-09) (#74) #85

Merged
cal merged 1 commits from ai/paper-dynasty-database#74 into next-release 2026-03-16 16:10:44 +00:00
Collaborator

Closes #74

Summary

  • Adds app/services/formula_engine.py with three pure formula functions, a dispatch helper, and a tier classifier
  • Adds tests/test_formula_engine.py — 19 unit tests, all pass (no database required)

What was implemented

compute_batter_value(stats)PA + TB×2 where TB = 1B + 2×2B + 3×3B + 4×HR

compute_sp_value(stats)outs/3 + k

compute_rp_value(stats)outs/3 + k (same formula, different thresholds)

compute_value_for_track(card_type, stats) — dispatches to the correct formula by card_type ('batter'/'sp'/'rp'); raises ValueError on unknown type

tier_from_value(value, track) — classifies T0–T4 against track thresholds; accepts both dict (seed fixture) and attribute-style (Peewee model) track objects

Notes

  • Pitcher formulas use stats.k (not stats.so) to match the PlayerSeasonStats model introduced in WP-02, where k is the pitcher strikeouts field
  • All 10 required test scenarios covered plus edge cases (T2/T3 boundaries, attribute-style track objects)
  • No existing files modified

Files changed

  • app/services/__init__.py (new, empty)
  • app/services/formula_engine.py (new, 105 lines)
  • tests/test_formula_engine.py (new, 188 lines)
Closes #74 ## Summary - Adds `app/services/formula_engine.py` with three pure formula functions, a dispatch helper, and a tier classifier - Adds `tests/test_formula_engine.py` — 19 unit tests, all pass (no database required) ## What was implemented **`compute_batter_value(stats)`** — `PA + TB×2` where `TB = 1B + 2×2B + 3×3B + 4×HR` **`compute_sp_value(stats)`** — `outs/3 + k` **`compute_rp_value(stats)`** — `outs/3 + k` (same formula, different thresholds) **`compute_value_for_track(card_type, stats)`** — dispatches to the correct formula by `card_type` (`'batter'`/`'sp'`/`'rp'`); raises `ValueError` on unknown type **`tier_from_value(value, track)`** — classifies T0–T4 against track thresholds; accepts both dict (seed fixture) and attribute-style (Peewee model) track objects ## Notes - Pitcher formulas use `stats.k` (not `stats.so`) to match the `PlayerSeasonStats` model introduced in WP-02, where `k` is the pitcher strikeouts field - All 10 required test scenarios covered plus edge cases (T2/T3 boundaries, attribute-style track objects) - No existing files modified ## Files changed - `app/services/__init__.py` (new, empty) - `app/services/formula_engine.py` (new, 105 lines) - `tests/test_formula_engine.py` (new, 188 lines)
Claude added 1 commit 2026-03-13 00:34:55 +00:00
Closes #74

Adds app/services/formula_engine.py with three pure formula functions
(compute_batter_value, compute_sp_value, compute_rp_value), a dispatch
helper (compute_value_for_track), and a tier classifier (tier_from_value).
Tier boundaries and thresholds match the locked seed data from WP-03.

Note: pitcher formulas use stats.k (not stats.so) to match the
PlayerSeasonStats model field name introduced in WP-02.

19 unit tests in tests/test_formula_engine.py — all pass.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Claude added the
ai-reviewing
label 2026-03-13 00:45:47 +00:00
Author
Collaborator

AI Code Review

Files Reviewed

  • app/services/__init__.py (new, empty)
  • app/services/formula_engine.py (new, 105 lines)
  • tests/test_formula_engine.py (new, 188 lines)

Findings

Correctness

  • compute_batter_value: Singles derivation (hits - doubles - triples - hr) is correct. TB formula (singles + 2×2B + 3×3B + 4×HR) is correct. Returns float. ✓
  • compute_sp_value / compute_rp_value: outs / 3 + k — Python true division returns float correctly. Intentional shared formula acknowledged in PR body. ✓
  • compute_value_for_track: Dict dispatch with None check and ValueError on unknown type is clean and correct. ✓
  • tier_from_value: Cascade-if from T4 down is the correct pattern for inclusive-lower boundaries. Dict/attribute dual access is correct. ✓
  • 19 test count verified: 3 batter formula + 1 SP + 1 RP + 3 zero-stats + 4 dispatch + 7 tier boundary = 19. All assertions match the documented formulas. ✓
  • Scope: Exactly 3 new files, zero modifications to existing files — no scope creep. ✓

Security

  • Pure computation functions with no I/O, no DB access, no user input, no credentials. No issues.

Style & Conventions

  • BatterStats and PitcherStats Protocol classes are defined but not used as type annotations in the function signatures (functions are typed stats without annotation). They serve as inline documentation, which is fine, but the protocols don't enforce anything as written. Minor nit, not a blocker.

Suggestions

  • Tier tests only cover batter thresholds: tier_from_value is exercised against track_dict("batter") only. The SP (t1=10, t2=40, t3=120, t4=240) and RP (t1=3, t2=12, t3=35, t4=70) threshold sets have no tier boundary tests. The logic is identical so the risk is low, but a single SP and RP boundary test each would complete coverage.
  • Negative singles guard: If malformed stats are passed where doubles + triples + hr > hits, singles goes negative and TB is undervalued silently. Upstream data should be clean, but a max(0, singles) or an assertion would make the function more defensive. Suggestion only.

Verdict: APPROVED

Clean, focused implementation. All three formula functions are correct, the dispatch and tier helpers work as documented, and the 19-unit test suite covers the key boundaries. No security issues, no scope creep, no modifications to existing files.


Automated review by Claude PR Reviewer

## AI Code Review ### Files Reviewed - `app/services/__init__.py` (new, empty) - `app/services/formula_engine.py` (new, 105 lines) - `tests/test_formula_engine.py` (new, 188 lines) ### Findings #### Correctness - **`compute_batter_value`**: Singles derivation (`hits - doubles - triples - hr`) is correct. TB formula (`singles + 2×2B + 3×3B + 4×HR`) is correct. Returns float. ✓ - **`compute_sp_value` / `compute_rp_value`**: `outs / 3 + k` — Python true division returns float correctly. Intentional shared formula acknowledged in PR body. ✓ - **`compute_value_for_track`**: Dict dispatch with `None` check and `ValueError` on unknown type is clean and correct. ✓ - **`tier_from_value`**: Cascade-if from T4 down is the correct pattern for inclusive-lower boundaries. Dict/attribute dual access is correct. ✓ - **19 test count verified**: 3 batter formula + 1 SP + 1 RP + 3 zero-stats + 4 dispatch + 7 tier boundary = 19. All assertions match the documented formulas. ✓ - **Scope**: Exactly 3 new files, zero modifications to existing files — no scope creep. ✓ #### Security - Pure computation functions with no I/O, no DB access, no user input, no credentials. No issues. #### Style & Conventions - `BatterStats` and `PitcherStats` Protocol classes are defined but not used as type annotations in the function signatures (functions are typed `stats` without annotation). They serve as inline documentation, which is fine, but the protocols don't enforce anything as written. Minor nit, not a blocker. #### Suggestions - **Tier tests only cover batter thresholds**: `tier_from_value` is exercised against `track_dict("batter")` only. The SP (t1=10, t2=40, t3=120, t4=240) and RP (t1=3, t2=12, t3=35, t4=70) threshold sets have no tier boundary tests. The logic is identical so the risk is low, but a single SP and RP boundary test each would complete coverage. - **Negative singles guard**: If malformed stats are passed where `doubles + triples + hr > hits`, `singles` goes negative and TB is undervalued silently. Upstream data should be clean, but a `max(0, singles)` or an assertion would make the function more defensive. Suggestion only. ### Verdict: APPROVED Clean, focused implementation. All three formula functions are correct, the dispatch and tier helpers work as documented, and the 19-unit test suite covers the key boundaries. No security issues, no scope creep, no modifications to existing files. --- *Automated review by Claude PR Reviewer*
Claude added
ai-reviewed
and removed
ai-reviewing
labels 2026-03-13 00:48:09 +00:00
cal merged commit 44763a07ec into next-release 2026-03-16 16:10:44 +00:00
cal deleted branch ai/paper-dynasty-database#74 2026-03-16 16:10:44 +00:00
Sign in to join this conversation.
No description provided.