Commit Graph

175 Commits

Author SHA1 Message Date
Cal Corum
9165419ed0 fix: add missing indexes on FK columns in stratplay and stratgame (#74)
Closes #74

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-07 23:05:25 -05:00
Cal Corum
c23ca9a721 fix: batch standings updates to eliminate N+1 queries in recalculate (#75)
Replace per-game update_standings() calls with pre-fetched dicts and
in-memory accumulation, then a single bulk_update at the end.
Reduces ~1,100+ queries for a full season to ~5 queries.

Closes #75

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-07 23:03:11 -05:00
Cal Corum
7a5327f490 fix: replace integer comparisons on boolean fields with True/False (#69)
Closes #69

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-07 22:57:01 -05:00
Cal Corum
eb886a4690 fix: remove SQLite fallback code from db_engine.py (#70)
Removes DATABASE_TYPE conditional entirely. PostgreSQL is now the only
supported backend. Moves PooledPostgresqlDatabase import to top-level
and raises RuntimeError at startup if POSTGRES_PASSWORD is unset,
preventing silent misconnection with misleading errors.

Closes #70

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-07 22:55:44 -05:00
Cal Corum
6637f6e9eb fix: disable autoconnect and set pool timeout on PooledPostgresqlDatabase (#80)
- Set timeout=5 so pool exhaustion surfaces as an error instead of hanging forever
- Set autoconnect=False to require explicit connection acquisition
- Add HTTP middleware in main.py to open/close connections per request

Closes #80

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-07 22:54:27 -05:00
Cal Corum
ece25ec22c fix: enforce Literal validation on sort parameter in GET /api/v3/players (#66)
Closes #66

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-07 22:53:33 -05:00
Cal Corum
18394aa74e fix: align CustomCommandCreator.discord_id model with BIGINT column (#78)
Closes #78

Change CharField(max_length=20) to BigIntegerField to match the BIGINT
column created by the migration. Remove the str() workaround in
get_creator_by_discord_id() that was compensating for the type mismatch.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-07 22:48:55 -05:00
Cal Corum
d61bc31daa fix: remove token value from Bad Token log warnings (#79)
Closes #79

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-07 22:42:49 -05:00
cal
ca15dfe380 Merge pull request 'fix: replace row-by-row DELETE with bulk DELETE in career recalculation (#77)' (#92) from issue/77-replace-row-by-row-delete-with-bulk-delete-in-care into main 2026-04-08 03:24:58 +00:00
cal
0cc0cba6a9 Merge pull request 'fix: replace deprecated Pydantic .dict() with .model_dump() (#76)' (#90) from issue/76-replace-deprecated-pydantic-dict-with-model-dump into main 2026-04-08 03:23:30 +00:00
cal
41fe4f6ce2 Merge pull request 'fix: add type annotations to untyped query parameters (#73)' (#86) from issue/73-add-type-annotations-to-untyped-query-parameters into main 2026-04-08 03:22:17 +00:00
cal
14234385fe Merge pull request 'fix: add combined_season classmethod to PitchingStat (#65)' (#67) from ai/major-domo-database-65 into main 2026-04-08 03:22:15 +00:00
Cal Corum
b46d8d33ef fix: remove empty finally clauses in custom_commands and help_commands
After removing db.close() calls, 22 finally: blocks were left empty
(12 in custom_commands.py, 10 in help_commands.py), causing
IndentationError at import time. Removed the finally: clause entirely
since connection lifecycle is now handled by the middleware.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-07 21:30:36 -05:00
Cal Corum
cfa6da06b7 fix: replace manual db.close() calls with middleware-based connection management (#71)
Closes #71

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-07 21:30:36 -05:00
12a76c2bb5 Merge branch 'main' into fix/remove-hardcoded-webhook 2026-04-08 02:24:10 +00:00
cal
c95459fa5d Update app/routers_v3/stratgame.py
All checks were successful
Build Docker Image / build (pull_request) Successful in 4m51s
2026-04-06 14:58:36 +00:00
cal
d809590f0e Merge pull request 'fix: correct column references in season pitching stats SQL' (#105) from fix/pitching-stats-column-name into main
All checks were successful
Build Docker Image / build (push) Successful in 2m11s
2026-04-02 16:57:30 +00:00
cal
0d8e666a75 Merge pull request 'fix: let HTTPException pass through @handle_db_errors' (#104) from fix/handle-db-errors-passthrough-http into main
Some checks failed
Build Docker Image / build (push) Has been cancelled
2026-04-02 16:57:12 +00:00
Cal Corum
bd19b7d913 fix: correct column references in season pitching stats view
All checks were successful
Build Docker Image / build (pull_request) Successful in 2m4s
sp.on_first/on_second/on_third don't exist — the actual columns are
on_first_id/on_second_id/on_third_id. This caused failures when
updating season pitching stats after games.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-02 11:54:56 -05:00
Cal Corum
215085b326 fix: let HTTPException pass through @handle_db_errors unchanged
All checks were successful
Build Docker Image / build (pull_request) Successful in 2m34s
The decorator was catching all exceptions including intentional
HTTPException (401, 404, etc.) and re-wrapping them as 500 "Database
error". This masked auth failures and other deliberate HTTP errors.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-02 08:30:22 -05:00
Cal Corum
d92f571960 hotfix: remove output caps from GET /players endpoint
All checks were successful
Build Docker Image / build (pull_request) Successful in 2m29s
The MAX_LIMIT/DEFAULT_LIMIT caps added in 16f3f8d are too restrictive
for the /players endpoint — bot and website consumers need full player
lists without pagination. Reverts limit param to Optional[int] with no
ceiling while keeping caps on all other endpoints.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-01 20:14:35 -05:00
Cal Corum
67e87a893a Fix fieldingstats count computed after limit applied
All checks were successful
Build Docker Image / build (pull_request) Successful in 2m9s
Capture total_count before .limit() so the response count reflects
all matching rows, not just the capped page size. Resolves #100.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-01 17:40:02 -05:00
Cal Corum
16f3f8d8de Fix unbounded API queries causing Gunicorn worker timeouts
All checks were successful
Build Docker Image / build (pull_request) Successful in 2m32s
Add MAX_LIMIT=500 cap across all list endpoints, empty string
stripping middleware, and limit/offset to /transactions. Resolves #98.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-01 17:23:25 -05:00
Cal Corum
b35b68a88f Merge remote-tracking branch 'origin/main' into fix/remove-hardcoded-webhook
All checks were successful
Build Docker Image / build (pull_request) Successful in 2m58s
2026-03-28 02:27:14 -05:00
Cal Corum
d8c6ce2a5e fix: replace row-by-row DELETE with bulk DELETE in career recalculation (#77)
All checks were successful
Build Docker Image / build (pull_request) Successful in 2m4s
Closes #77

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-27 03:02:55 -05:00
Cal Corum
75a8fc8505 fix: replace deprecated Pydantic .dict() with .model_dump() (#76)
All checks were successful
Build Docker Image / build (pull_request) Successful in 2m26s
Closes #76

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-27 02:02:49 -05:00
Cal Corum
dcaf184ad3 fix: add type annotations to untyped query parameters (#73)
All checks were successful
Build Docker Image / build (pull_request) Successful in 2m11s
Closes #73

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-27 00:02:57 -05:00
Cal Corum
1bcde424c6 Address PR review feedback for DISCORD_WEBHOOK_URL env var
All checks were successful
Build Docker Image / build (pull_request) Successful in 2m32s
- Add DISCORD_WEBHOOK_URL to docker-compose.yml api service environment block
- Add empty placeholder entry in .env for discoverability
- Move DISCORD_WEBHOOK_URL constant to the env-var constants section at top of dependencies.py

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-26 23:23:26 -05:00
Cal Corum
3be4f71e22 fix: move hardcoded Discord webhook URL to environment variable
All checks were successful
Build Docker Image / build (pull_request) Successful in 3m42s
Replace inline webhook URL+token with DISCORD_WEBHOOK_URL env var.
Logs a warning and returns False gracefully if the var is unset.

The exposed webhook token should be rotated in Discord.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-26 23:15:21 -05:00
Cal Corum
c451e02c52 fix: remove hardcoded fallback password from PostgreSQL connection
All checks were successful
Build Docker Image / build (pull_request) Successful in 18m25s
Raise RuntimeError on startup if POSTGRES_PASSWORD env var is not set,
instead of silently falling back to a known password in source code.

Closes #C2 from postgres migration review.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-26 23:15:07 -05:00
Cal Corum
a21bb2a380 fix: add combined_season classmethod to PitchingStat (#65)
All checks were successful
Build Docker Image / build (pull_request) Successful in 2m17s
Closes #65

`PitchingStat.combined_season()` was referenced in the `get_pitstats`
handler but never defined, causing a 500 on `s_type=combined/total/all`.

Added `combined_season` as a `@staticmethod` matching the pattern of
`BattingStat.combined_season` — returns all rows for the given season
with no week filter (both regular and postseason).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-17 17:33:47 -05:00
Cal Corum
697152808b fix: validate sort_by parameter with Literal type in views.py (#36)
All checks were successful
Build Docker Image / build (pull_request) Successful in 4m13s
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-17 16:29:58 -05:00
Cal Corum
95ff5eeaf9 fix: replace print(req.scope) with logger.debug in /api/docs (#21)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-17 16:29:58 -05:00
Cal Corum
a0d5d49724 fix: address review feedback (#52)
Guard bulk ID queries against empty lists to prevent PostgreSQL
syntax error (WHERE id IN ()) when batch POST endpoints receive
empty request bodies.

Affected endpoints:
- POST /api/v3/transactions
- POST /api/v3/results
- POST /api/v3/schedules
- POST /api/v3/battingstats

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-17 16:29:58 -05:00
Cal Corum
b0fd1d89ea fix: eliminate N+1 queries in batch POST endpoints (#25)
Replace per-row Team/Player lookups with bulk IN-list queries before
the validation loop in post_transactions, post_results, post_schedules,
and post_batstats. A 50-move batch now uses 2 queries instead of 150.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-17 16:29:58 -05:00
Cal Corum
5ac9cce7f0 fix: replace bare except: with except Exception: (#29)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-17 16:29:21 -05:00
Cal Corum
0e132e602f fix: remove unused imports in standings.py and pitchingstats.py (#30)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-17 16:28:25 -05:00
Cal Corum
d92bb263f1 fix: invalidate cache after PlayerService write operations (#32)
Add finally blocks to update_player, patch_player, create_players, and
delete_player in PlayerService to call invalidate_related_cache() using
the existing cache_patterns. Matches the pattern already used in
TeamService.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-17 16:28:07 -05:00
Cal Corum
9558da6ace fix: remove empty WEEK_NUMS dict from db_engine.py (#34)
Dead code - module-level constant defined but never referenced.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-17 16:28:07 -05:00
Cal Corum
6d1b0ac747 perf: push limit/offset to DB in PlayerService.get_players (#37)
Apply .offset() and .limit() on the Peewee query before materializing
results, instead of fetching all rows into memory and slicing in Python.
Total count is obtained via query.count() before pagination is applied.
In-memory (mock) queries continue to use Python-level slicing.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-17 16:28:07 -05:00
Cal Corum
a351010c3c fix: calculate lob_2outs and rbipercent in SeasonPitchingStats (#28)
Both fields were hardcoded to 0.0 in the INSERT. Added SQL expressions
to the pitching_stats CTE to calculate them from stratplay data, using
the same logic as the batting stats endpoint.

- lob_2outs: count of runners stranded when pitcher recorded the 3rd out
- rbipercent: RBI allowed (excluding HR) per runner opportunity

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-17 16:28:07 -05:00
Cal Corum
9ec69f9f2c fix: standardize all collection POST routes to use trailing slash
All checks were successful
Build Docker Image / build (pull_request) Successful in 3m38s
aiohttp follows 307 redirects but converts POST to GET, silently
dropping the request body. Standardize all @router.post('') to
@router.post('/') so the canonical URL always has a trailing slash,
preventing 307 redirects when clients POST with trailing slashes.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-09 19:34:28 -05:00
Cal Corum
6af278d737 fix: case-insensitive team_abbrev filter in transactions endpoint
All checks were successful
Build Docker Image / build (pull_request) Successful in 2m7s
The team_abbrev filter uppercased input but compared against mixed-case
DB values (e.g. "MKEMiL"), causing affiliate roster transactions to be
silently dropped from results. Use fn.UPPER() on the DB column to match.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-08 17:35:12 -05:00
Cal Corum
4b288c1e67 fix: raise HTTPException in recalculate_standings on failure (#23)
All checks were successful
Build Docker Image / build (pull_request) Successful in 2m2s
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-03 03:44:52 +00:00
Cal Corum
8143913aa2 fix: assign order_by() return value in GET /api/v3/games (#24)
All checks were successful
Build Docker Image / build (pull_request) Successful in 4m41s
Peewee's order_by() returns a new queryset and does not sort in place.
Both branches were discarding the result, so the sort parameter had no
effect. Assign back to all_games so the query is actually ordered.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-02 19:03:21 -06:00
Cal Corum
86f8495284 feat: Add group_by=sbaplayer to batting, pitching, and fielding endpoints
All checks were successful
Build Docker Image / build (pull_request) Successful in 2m32s
Enables career-total aggregation by real-world player identity (SbaPlayer)
across all seasons. JOINs StratPlay → Player to access Player.sbaplayer FK,
groups by that FK, and excludes players with null sbaplayer. Also refactors
stratplay router from single file into package and adds integration tests.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-17 17:42:14 -06:00
Cal Corum
90893aa774 feat: Add sbaplayer_id filter to /plays batting, pitching, and fielding endpoints
Enables cross-season stat queries by MLB player identity (SbaPlayer) without
requiring callers to look up every season-specific Player ID first. Filters
via Player subquery since StratPlay has no direct FK to SbaPlayer.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-17 17:42:14 -06:00
Cal Corum
bcdde572e8 docs: Add subdirectory CLAUDE.md files for routers, stratplay, and services
Provide targeted context for each app subdirectory so Claude Code
understands local patterns without relying on the root CLAUDE.md.
Also simplifies root CLAUDE.md dev/prod environment sections.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-17 17:32:25 -06:00
Cal Corum
31b14ec709 fix: Replace fragile locals() pattern in PATCH endpoints with explicit field dicts
Some checks failed
Build Docker Image / build (pull_request) Failing after 15s
The teams PATCH endpoint included the `data` variable itself when
building the update dict via locals(), causing Peewee to fail with
"type object 'Team' has no attribute 'data'". The players endpoint
had the same pattern with a workaround that was order-dependent.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-11 15:54:39 -06:00
Cal Corum
126ce53951 fix: Sort games chronologically in standings recalculation for accurate streaks
Some checks failed
Build Docker Image / build (pull_request) Failing after 20s
The standings recalculate function was processing games in arbitrary database
order, causing win/loss streaks to be calculated incorrectly. Added explicit
ordering by week and game_num (ascending) to ensure games are processed
chronologically.

This fixes inconsistent streak values that were reported due to the streak
logic depending on processing games in the correct temporal sequence.

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-02-10 14:44:58 -06:00