Commit Graph

54 Commits

Author SHA1 Message Date
Cal Corum
a7d02aeb10 style: remove redundant parentheses on boost_delta_json declaration
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-30 11:07:19 -05:00
Cal Corum
4a1251a734 feat: add Refractor Phase 2 foundation — boost functions, schema, tests
Pure functions for computing boosted card ratings when a player
reaches a new Refractor tier. Batter boost applies fixed +0.5 to
four offensive columns per tier; pitcher boost uses a 1.5 TB-budget
priority algorithm. Both preserve the 108-sum invariant.

- Create refractor_boost.py with apply_batter_boost, apply_pitcher_boost,
  and compute_variant_hash (Decimal arithmetic, zero-floor truncation)
- Add RefractorBoostAudit model, Card.variant, BattingCard/PitchingCard
  image_url, RefractorCardState.variant fields to db_engine.py
- Add migration SQL for refractor_card_state.variant column and
  refractor_boost_audit table (JSONB, UNIQUE constraint, transactional)
- 26 unit tests covering 108-sum invariant, deltas, truncation, TB
  accounting, determinism, x-check protection, and variant hash behavior

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-29 13:39:03 -05:00
Cal Corum
0b5d0b474b feat: add GET /api/v2/refractor/cards list endpoint (#172)
Closes #172

- New GET /api/v2/refractor/cards endpoint in refractor router with
  team_id (required), card_type, tier, season, progress, limit, offset filters
- season filter uses EXISTS subquery against batting/pitching_season_stats
- progress=close filter uses CASE expression to compare current_value
  against next tier threshold (>= 80%)
- LEFT JOIN on Player so deleted players return player_name: null
- Sorting: current_tier DESC, current_value DESC
- count reflects total matching rows before pagination
- Extended _build_card_state_response() with progress_pct (computed) and
  optional player_name; single-card endpoint gains progress_pct automatically
- Added non-unique team_id index on refractor_card_state in db_engine.py
- Migration: 2026-03-25_add_refractor_card_state_team_index.sql
- Removed pre-existing unused RefractorTrack import in evaluate_game (ruff)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-25 01:35:18 -05:00
Cal Corum
b7dec3f231 refactor: rename evolution system to refractor
Complete rename of the card progression system from "Evolution" to
"Refractor" across all code, routes, models, services, seeds, and tests.

- Route prefix: /api/v2/evolution → /api/v2/refractor
- Model classes: EvolutionTrack → RefractorTrack, etc.
- 12 files renamed, 8 files content-edited
- New migration to rename DB tables
- 117 tests pass, no logic changes

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-23 13:31:55 -05:00
Cal Corum
c935c50a96 feat: add ProcessedGame ledger for full idempotency in update_season_stats() (#105)
Closes #105

Replace the last_game FK guard in update_season_stats() with an atomic
INSERT into a new processed_game ledger table. The old guard only blocked
same-game immediate replay; it was silently bypassed if game G+1 was
processed first (last_game already overwritten). The ledger is keyed on
game_id so any re-delivery — including out-of-order — is caught reliably.

Changes:
- app/db_engine.py: add ProcessedGame model (game FK PK + processed_at)
- app/services/season_stats.py: replace last_game check with
  ProcessedGame.get_or_create(); import ProcessedGame; update docstrings
- migrations/2026-03-18_add_processed_game.sql: CREATE TABLE IF NOT EXISTS
  processed_game with FK to stratgame ON DELETE CASCADE
- tests/conftest.py: add ProcessedGame to imports and _TEST_MODELS list
- tests/test_season_stats_update.py: add test_out_of_order_replay_prevented;
  update test_double_count_prevention docstring

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-18 01:05:31 -05:00
Cal Corum
926c18af70 feat(WP-01): add evolution Peewee models
Add EvolutionTrack, EvolutionCardState, EvolutionTierBoost, and
EvolutionCosmetic models to db_engine.py with composite unique indexes
and create_tables blocks. Also includes PlayerSeasonStats (WP-02).

Add ruff.toml to suppress pre-existing F403/F405 from intentional
`from peewee import *` wildcard import pattern in db_engine.py.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-17 19:32:03 -05:00
Cal Corum
bd8e4578cc refactor: split PlayerSeasonStats into BattingSeasonStats and PitchingSeasonStats
Some checks failed
Build Docker Image / build (push) Has been cancelled
Separate batting and pitching into distinct tables with descriptive column
names. Eliminates naming collisions (so/k ambiguity) and column mismatches
between the ORM model and raw SQL. Each table now covers all aggregatable
fields from its source (BattingStat/PitchingStat) including sac, ibb, gidp,
earned_runs, runs_allowed, wild_pitches, balks, and games_started.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-17 09:43:22 -05:00
Cal Corum
4ed62dea2c refactor: rename PlayerSeasonStats so to so_batter and k to so_pitcher
All checks were successful
Build Docker Image / build (push) Successful in 8m41s
The single-letter `k` field was ambiguous and too short for comfortable use.
Rename to `so_pitcher` for clarity, and `so` to `so_batter` to distinguish
batting strikeouts from pitching strikeouts in the same model.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-17 09:31:52 -05:00
Cal Corum
8dfc5ef371 fix: remove evolution models from WP-02 PR (#82)
Evolution models (EvolutionTrack, EvolutionCardState, EvolutionTierBoost,
EvolutionCosmetic), their re-export module, and tests were included in
this PR without disclosure. Removed to keep this PR scoped to
PlayerSeasonStats (WP-02) only per review feedback.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-12 17:02:00 -05:00
Cal Corum
4bfd878486 feat: add PlayerSeasonStats Peewee model (#67)
Closes #67

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-12 16:35:02 -05:00
Cal Corum
44b6222ad5 fix: refactor Roster from 26 FK columns to RosterSlot junction table (#29)
- Remove card_1..card_26 FK columns from Roster ORM model
- Add RosterSlot model with (roster, slot, card) and a unique index on (roster, slot)
- Activate get_cards() helper on Roster using the new junction table
- Register RosterSlot in create_tables for SQLite dev environments
- Add migrations/migrate_roster_junction_table.py to backfill existing data

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-05 15:34:39 -06:00
Cal Corum
5e182bedac feat: add scout_opportunities and scout_claims tables and API endpoints (#44)
Support the Discord bot's new scouting feature where players can scout
cards from other teams' opened packs. Stores opportunities with expiry
timestamps and tracks which teams claim which cards.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-05 03:45:38 +00:00
Cal Corum
870c753bf7 fix: document SQLite synchronous=0 pragma in db_engine.py (#20)
Add explanatory comment clarifying that synchronous=OFF is a dev-only
trade-off (production uses PostgreSQL), and describing the crash-corruption
risk and how WAL mode partially mitigates it.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-05 03:23:30 +00:00
Cal Corum
053fcbab05 fix: centralize logging config in main.py — remove basicConfig from 32 files (#26)
Moved logging.basicConfig() to app/main.py as the single source of truth.
Removed duplicate (no-op) calls from app/db_engine.py, app/dependencies.py,
and all 30 router files in app/routers_v2/. Removed the now-unused LOG_DATA
dict and date/log_level locals from dependencies.py and db_engine.py.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-05 03:22:02 +00:00
Cal Corum
b8a6c6bd2c Fix KeyError: 'human' in gauntlet-9 CARDSETS
Added missing 'human' key to gauntlet-9 cardset configuration.
This was causing 500 errors when players tried to start gauntlet
games because the legal-check endpoint couldn't validate cards.

Error: KeyError: 'human' at app/routers_v2/cards.py:242
when checking CARDSETS[rarity_name]['human']

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-02-04 08:47:21 -06:00
Cal Corum
40c512c665 Add PostgreSQL compatibility fixes for query ordering
- Add explicit ORDER BY id to all queries for consistent results across SQLite and PostgreSQL
- PostgreSQL does not guarantee row order without ORDER BY, unlike SQLite
- Skip table creation when DATABASE_TYPE=postgresql (production tables already exist)
- Fix datetime handling in notifications (PostgreSQL native datetime vs SQLite timestamp)
- Fix grouped query count() calls that don't work in PostgreSQL
- Update .gitignore to include storage/templates/ directory

This completes the PostgreSQL migration compatibility layer while maintaining
backwards compatibility with SQLite for local development.

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-02-03 10:39:14 -06:00
Cal Corum
985a6ed2b0 Add default ORDER BY id to BaseModel.select() for PostgreSQL compatibility
PostgreSQL does not guarantee row order without ORDER BY, unlike SQLite
which implicitly returned rows by rowid. This caused bugs where queries
returned results in unexpected order (e.g., get_team_by_owner returning
gauntlet team instead of main team).

Override select() in BaseModel to add default ordering by id. Explicit
.order_by() calls will override this default.

Also mark legacy db_engine.py as deprecated.
2026-01-31 16:06:44 -06:00
Cal Corum
8c039dedf8 Fix DateTimeField defaults for PostgreSQL compatibility
Paperdex and GauntletRun models used int timestamps as defaults which
worked in SQLite but fail in PostgreSQL. Changed to datetime.now.
2026-01-31 15:56:33 -06:00
Cal Corum
3dce457463 Fix flashback cardset legality and rewards timestamp handling
- Add 2018 Promos (14) and 2022 Promos (4) to flashback mode legal cardsets
- Convert Unix timestamps to datetime in rewards POST/PATCH for PostgreSQL

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-30 15:08:21 -06:00
Cal Corum
92fc101e38 Fix PostgreSQL compatibility for GROUP BY queries and aggregations
- Fix NULL handling for FK checks in stratplays.py: use x.field_id instead
  of x.field to avoid triggering FK lookups on potentially missing rows
- Cast boolean is_start to integer for SUM() - PostgreSQL cannot sum booleans
- Add missing GROUP BY clause to Decision aggregate query
- Add Case import for boolean-to-integer casting
- Update migration script with boolean/datetime column mappings
- Exclude legacy battingstat/pitchingstat tables from migration
- Add comprehensive POSTGRES_MIGRATION_GUIDE.md documentation

Tested: /plays/batting and /plays/pitching endpoints work with group_by=player
2026-01-26 21:59:25 -06:00
Cal Corum
0cba52cea5 PostgreSQL migration: Complete code preparation phase
- Add db_helpers.py with cross-database upsert functions for SQLite/PostgreSQL
- Replace 12 on_conflict_replace() calls with PostgreSQL-compatible upserts
- Add unique indexes: StratPlay(game, play_num), Decision(game, pitcher)
- Add max_length to Team model fields (abbrev, sname, lname)
- Fix boolean comparison in teams.py (== 0/1 to == False/True)
- Create migrate_to_postgres.py with ID-preserving migration logic
- Create audit_sqlite.py for pre-migration data integrity checks
- Add PROJECT_PLAN.json for migration tracking
- Add .secrets/ to .gitignore for credentials

Audit results: 658,963 records across 29 tables, 2,390 orphaned stats (expected)

Based on Major Domo migration lessons learned (33 issues resolved there)
2026-01-25 23:05:54 -06:00
Cal Corum
fbe8623eb4 Merge branch 'main' into postgres-migration 2026-01-25 22:53:35 -06:00
Cal Corum
a34e553b25 2005 Live Series Updates 2025-11-08 18:25:08 -06:00
Cal Corum
f6e8aa7108 CLAUDE: Add PostgreSQL support and table names to models
- Add environment-based PostgreSQL configuration to db_engine.py
- Add table_name to all 30 models (Meta class)
- Update db_migrations.py to auto-select migrator based on DB type
- Add comprehensive PostgreSQL migration plan document
2025-11-07 10:23:14 -06:00
Cal Corum
2791e1964b Gauntlet 8 updates 2025-05-29 22:19:19 -05:00
Cal Corum
acb65ef47c Gauntlet 7 cardsets 2024-12-23 16:13:05 -06:00
Cal Corum
21fe5c419a Flip image libraries due to white bar bug 2024-11-04 23:17:07 -06:00
Cal Corum
1e4569dfbf Clean up root_validators
Remove root_path from FastAPI
Update Season 8 Cardsets
Force pydantic 1.x
2024-11-03 01:46:40 -05:00
Cal Corum
17416cd50f Log mlbplayer dupes
Update cardsets for season 8
2024-11-02 23:44:32 -05:00
Cal Corum
889db717d1 Update for Swagger URL 2024-07-07 22:18:51 -05:00
Cal Corum
d0c3caaf87 Add support for buying Promo pack 2024-05-13 17:03:48 -05:00
Cal Corum
f0e3b38938 Cardset updates 2024-05-13 15:37:08 -05:00
Cal Corum
b9a64e8333 2024 Updates 2024-04-21 17:48:51 -05:00
Cal Corum
62bcfd5e45 Cardset updates 2024-03-08 00:26:00 -06:00
Cal Corum
563f46c30c Update human legal-check 2024-03-01 20:01:56 -06:00
Cal Corum
ac14e8517e Add high-inning preference for AI lineups 2024-02-24 23:34:36 -06:00
Cal Corum
1f917b8a6b Mark '18 Promo as ranked legal 2024-02-24 21:36:56 -06:00
Cal Corum
e217375974 Update db_engine.py
Update ranked cardsets
2023-12-04 13:40:10 -06:00
Cal Corum
dbbe257654 Update db_engine.py
Move 2023 from primary to secondary in the gauntlet
2023-11-29 10:34:41 -06:00
Cal Corum
eb0babc78c Major League Campaign update
Prep for flashback campaign
2023-11-05 20:04:30 -06:00
Cal Corum
ed296a0b13 Update Major League cardsets 2023-10-29 14:10:27 -05:00
Cal Corum
e8a5c6f503 Added gauntlet cardset data 2023-10-23 00:58:47 -05:00
Cal Corum
714345c589 Decision endpoint functional 2023-10-22 00:08:11 -05:00
Cal Corum
edde7a1b82 StratPlays added 2023-10-21 19:54:31 -05:00
Cal Corum
0a59d3e98d Moved AI lineup creation to db 2023-10-21 15:31:36 -05:00
Cal Corum
96be768ec9 Pre-Season 6 Updates 2023-10-19 23:16:47 -05:00
Cal Corum
485a046855 First pass of real card gen 2023-10-11 00:58:30 -05:00
Cal Corum
07c1b51d0f Bug fixes for unique indeces 2023-10-03 12:06:48 -05:00
Cal Corum
8a0d094227 Scouting tables added to db 2023-09-22 01:29:35 -05:00
Cal Corum
995735d878 Added cardpositions 2023-09-16 18:36:15 -05:00