Compare commits

..

13 Commits

Author SHA1 Message Date
Cal Corum
0166c7dda4 fix: compute CSV after appending data row in get_one_player (#12)
return_val was assigned from DataFrame(data_list).to_csv() before the
player data row was appended to data_list, so the CSV response contained
only the header row. Moved the to_csv() call to after the append.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-05 03:32:09 +00:00
cal
d1728a804f Merge pull request 'fix: guard against None rating objects in pitcher sorting functions (#13)' (#50) from ai/paper-dynasty-database#13 into next-release
Reviewed-on: #50
2026-03-05 03:29:48 +00:00
Cal Corum
0948db0b49 fix: guard against None rating objects in pitcher sorting functions (#13)
Add None checks for vlval/vrval in get_total_ops inside sort_pitchers()
and sort_starters(). Returns float("inf") when ratings are missing so
pitchers without ratings sort to the end rather than raising AttributeError.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-05 03:25:28 +00:00
cal
1651ff1738 Merge pull request 'fix: document SQLite synchronous=0 pragma in db_engine.py (#20)' (#49) from ai/paper-dynasty-database#20 into next-release
Reviewed-on: #49
2026-03-05 03:23:50 +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
341296b573 Merge pull request 'fix: centralize logging config in main.py (#26)' (#46) from ai/paper-dynasty-database#26 into next-release
Reviewed-on: #46
2026-03-05 03:22:28 +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
e7c8b59201 Merge pull request 'fix: batch-fetch PitchingCardRatings instead of per-row queries (#19)' (#44) from ai/paper-dynasty-database#19 into next-release
Reviewed-on: #44
2026-03-05 03:19:58 +00:00
Cal Corum
ae8c20ea1c fix: batch-fetch PitchingCardRatings instead of per-row queries (#19)
Replace two get_or_none calls per row in sort_pitchers and sort_starters
with a single batched SELECT for all card IDs, reducing N*2 queries to 1.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-05 03:19:43 +00:00
cal
9096a4b976 Merge pull request 'fix: add type annotations to untyped path parameters (#27)' (#43) from ai/paper-dynasty-database#27 into next-release
Reviewed-on: #43
2026-03-05 03:18:49 +00:00
Cal Corum
5f86c8cb20 fix: add type annotations to untyped path parameters (#27)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-05 03:18:38 +00:00
cal
20f727a119 Merge pull request 'fix: remove duplicate ranking_max filter in get_teams (#21)' (#42) from ai/paper-dynasty-database#21 into next-release
Reviewed-on: #42
2026-03-05 03:17:50 +00:00
Cal Corum
86b4338b66 fix: remove duplicate ranking_max filter in get_teams (#21)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-03 16:01:52 -06:00
33 changed files with 86 additions and 229 deletions

View File

@ -30,20 +30,20 @@ if DATABASE_TYPE.lower() == "postgresql":
autorollback=True, # Automatically rollback failed transactions
)
else:
# Default SQLite configuration for local development
# SQLite configuration for local development only.
# Production always uses PostgreSQL (see DATABASE_TYPE env var).
#
# synchronous=0 (OFF): SQLite skips fsync() after every write, maximising
# throughput at the cost of durability — a hard crash could corrupt the DB.
# This is an acceptable trade-off in dev where data loss is tolerable and
# write speed matters. WAL journal mode reduces (but does not eliminate)
# the corruption window by keeping the main database file consistent while
# writes land in the WAL file first.
db = SqliteDatabase(
"storage/pd_master.db",
pragmas={"journal_mode": "wal", "cache_size": -1 * 64000, "synchronous": 0},
)
date = f"{datetime.now().year}-{datetime.now().month}-{datetime.now().day}"
log_level = logging.INFO if os.environ.get("LOG_LEVEL") == "INFO" else "WARN"
logging.basicConfig(
filename=f"logs/database/{date}.log",
format="%(asctime)s - database - %(levelname)s - %(message)s",
level=log_level,
)
# 2025, 2005
ranked_cardsets = [24, 25, 26, 27, 28, 29]
LIVE_CARDSET_ID = 27
@ -925,7 +925,13 @@ CardPosition.add_index(pos_index)
if not SKIP_TABLE_CREATION:
db.create_tables(
[BattingCard, BattingCardRatings, PitchingCard, PitchingCardRatings, CardPosition],
[
BattingCard,
BattingCardRatings,
PitchingCard,
PitchingCardRatings,
CardPosition,
],
safe=True,
)

View File

@ -5,21 +5,6 @@ import os
import requests
from fastapi.security import OAuth2PasswordBearer
date = f"{datetime.datetime.now().year}-{datetime.datetime.now().month}-{datetime.datetime.now().day}"
LOG_DATA = {
"filename": f"logs/database/{date}.log",
"format": "%(asctime)s - database - %(levelname)s - %(message)s",
"log_level": logging.INFO if os.environ.get("LOG_LEVEL") == "INFO" else "WARN",
}
logging.basicConfig(
filename=LOG_DATA["filename"],
format=LOG_DATA["format"],
level=LOG_DATA["log_level"],
)
oauth2_scheme = OAuth2PasswordBearer(tokenUrl="token")
master_debug = False
DB_URL = "https://pd.manticorum.com/api/"

View File

@ -1,7 +1,18 @@
import logging
import os
from datetime import datetime
from fastapi import FastAPI, Request
from fastapi.openapi.docs import get_swagger_ui_html
from fastapi.openapi.utils import get_openapi
_log_date = f"{datetime.now().year}-{datetime.now().month}-{datetime.now().day}"
logging.basicConfig(
filename=f"logs/database/{_log_date}.log",
format="%(asctime)s - database - %(levelname)s - %(message)s",
level=logging.INFO if os.environ.get("LOG_LEVEL") == "INFO" else logging.WARNING,
)
# from fastapi.staticfiles import StaticFiles
# from fastapi.templating import Jinja2Templates

View File

@ -2,13 +2,8 @@ from fastapi import APIRouter, Depends, HTTPException
import logging
from ..db_engine import Player
from ..dependencies import oauth2_scheme, valid_token, LOG_DATA, PRIVATE_IN_SCHEMA
from ..dependencies import oauth2_scheme, valid_token, PRIVATE_IN_SCHEMA
logging.basicConfig(
filename=LOG_DATA['filename'],
format=LOG_DATA['format'],
level=LOG_DATA['log_level']
)
router = APIRouter(
prefix='/api/v2/admin',

View File

@ -5,13 +5,8 @@ import pydantic
from pandas import DataFrame
from ..db_engine import Award, model_to_dict
from ..dependencies import oauth2_scheme, valid_token, LOG_DATA, PRIVATE_IN_SCHEMA
from ..dependencies import oauth2_scheme, valid_token, PRIVATE_IN_SCHEMA
logging.basicConfig(
filename=LOG_DATA['filename'],
format=LOG_DATA['format'],
level=LOG_DATA['log_level']
)
router = APIRouter(
prefix='/api/v2/awards',

View File

@ -7,13 +7,8 @@ import pydantic
from pandas import DataFrame
from ..db_engine import db, BattingStat, model_to_dict, fn, Card, Player, Current
from ..dependencies import oauth2_scheme, valid_token, LOG_DATA, PRIVATE_IN_SCHEMA
from ..dependencies import oauth2_scheme, valid_token, PRIVATE_IN_SCHEMA
logging.basicConfig(
filename=LOG_DATA['filename'],
format=LOG_DATA['format'],
level=LOG_DATA['log_level']
)
router = APIRouter(
prefix='/api/v2/batstats',

View File

@ -18,13 +18,8 @@ from ..db_engine import (
CardPosition,
)
from ..db_helpers import upsert_batting_card_ratings
from ..dependencies import oauth2_scheme, valid_token, LOG_DATA, PRIVATE_IN_SCHEMA
from ..dependencies import oauth2_scheme, valid_token, PRIVATE_IN_SCHEMA
logging.basicConfig(
filename=LOG_DATA["filename"],
format=LOG_DATA["format"],
level=LOG_DATA["log_level"],
)
router = APIRouter(prefix="/api/v2/battingcardratings", tags=["battingcardratings"])
RATINGS_FILE = "storage/batting-ratings.csv"

View File

@ -7,13 +7,8 @@ import pydantic
from ..db_engine import db, BattingCard, model_to_dict, fn, Player, MlbPlayer
from ..db_helpers import upsert_batting_cards
from ..dependencies import oauth2_scheme, valid_token, LOG_DATA
from ..dependencies import oauth2_scheme, valid_token
logging.basicConfig(
filename=LOG_DATA["filename"],
format=LOG_DATA["format"],
level=LOG_DATA["log_level"],
)
router = APIRouter(prefix="/api/v2/battingcards", tags=["battingcards"])

View File

@ -6,13 +6,8 @@ from pydantic import root_validator
from ..db_engine import db, CardPosition, model_to_dict, Player, fn
from ..db_helpers import upsert_card_positions
from ..dependencies import oauth2_scheme, valid_token, LOG_DATA
from ..dependencies import oauth2_scheme, valid_token
logging.basicConfig(
filename=LOG_DATA["filename"],
format=LOG_DATA["format"],
level=LOG_DATA["log_level"],
)
router = APIRouter(prefix="/api/v2/cardpositions", tags=["cardpositions"])

View File

@ -5,13 +5,8 @@ import pydantic
from pandas import DataFrame
from ..db_engine import db, Card, model_to_dict, Team, Player, Pack, Paperdex, CARDSETS
from ..dependencies import oauth2_scheme, valid_token, LOG_DATA
from ..dependencies import oauth2_scheme, valid_token
logging.basicConfig(
filename=LOG_DATA['filename'],
format=LOG_DATA['format'],
level=LOG_DATA['log_level']
)
router = APIRouter(
prefix='/api/v2/cards',

View File

@ -5,13 +5,8 @@ import pydantic
from pandas import DataFrame
from ..db_engine import Cardset, model_to_dict, fn, Event
from ..dependencies import oauth2_scheme, valid_token, LOG_DATA
from ..dependencies import oauth2_scheme, valid_token
logging.basicConfig(
filename=LOG_DATA['filename'],
format=LOG_DATA['format'],
level=LOG_DATA['log_level']
)
router = APIRouter(
prefix='/api/v2/cardsets',

View File

@ -5,13 +5,8 @@ import logging
import pydantic
from ..db_engine import Current, model_to_dict
from ..dependencies import oauth2_scheme, valid_token, LOG_DATA, PRIVATE_IN_SCHEMA
from ..dependencies import oauth2_scheme, valid_token, PRIVATE_IN_SCHEMA
logging.basicConfig(
filename=LOG_DATA['filename'],
format=LOG_DATA['format'],
level=LOG_DATA['log_level']
)
router = APIRouter(
prefix='/api/v2/current',

View File

@ -16,13 +16,8 @@ from ..db_engine import (
StratPlay,
)
from ..db_helpers import upsert_decisions
from ..dependencies import oauth2_scheme, valid_token, LOG_DATA
from ..dependencies import oauth2_scheme, valid_token
logging.basicConfig(
filename=LOG_DATA["filename"],
format=LOG_DATA["format"],
level=LOG_DATA["log_level"],
)
router = APIRouter(prefix="/api/v2/decisions", tags=["decisions"])

View File

@ -5,13 +5,8 @@ import pydantic
from pandas import DataFrame
from ..db_engine import Event, model_to_dict, fn
from ..dependencies import oauth2_scheme, valid_token, LOG_DATA
from ..dependencies import oauth2_scheme, valid_token
logging.basicConfig(
filename=LOG_DATA['filename'],
format=LOG_DATA['format'],
level=LOG_DATA['log_level']
)
router = APIRouter(
prefix='/api/v2/events',

View File

@ -5,13 +5,8 @@ import pydantic
from pandas import DataFrame
from ..db_engine import GameRewards, model_to_dict
from ..dependencies import oauth2_scheme, valid_token, LOG_DATA
from ..dependencies import oauth2_scheme, valid_token
logging.basicConfig(
filename=LOG_DATA['filename'],
format=LOG_DATA['format'],
level=LOG_DATA['log_level']
)
router = APIRouter(
prefix='/api/v2/gamerewards',

View File

@ -5,13 +5,8 @@ import pydantic
from ..db_engine import db, GauntletReward, model_to_dict, DatabaseError
from ..db_helpers import upsert_gauntlet_rewards
from ..dependencies import oauth2_scheme, valid_token, LOG_DATA
from ..dependencies import oauth2_scheme, valid_token
logging.basicConfig(
filename=LOG_DATA["filename"],
format=LOG_DATA["format"],
level=LOG_DATA["log_level"],
)
router = APIRouter(prefix="/api/v2/gauntletrewards", tags=["gauntletrewards"])

View File

@ -5,13 +5,8 @@ import logging
import pydantic
from ..db_engine import GauntletRun, model_to_dict, DatabaseError
from ..dependencies import oauth2_scheme, valid_token, LOG_DATA
from ..dependencies import oauth2_scheme, valid_token
logging.basicConfig(
filename=LOG_DATA['filename'],
format=LOG_DATA['format'],
level=LOG_DATA['log_level']
)
router = APIRouter(
prefix='/api/v2/gauntletruns',

View File

@ -16,13 +16,8 @@ from ..db_engine import (
query_to_csv,
)
from ..db_helpers import upsert_mlb_players
from ..dependencies import oauth2_scheme, valid_token, LOG_DATA
from ..dependencies import oauth2_scheme, valid_token
logging.basicConfig(
filename=LOG_DATA["filename"],
format=LOG_DATA["format"],
level=LOG_DATA["log_level"],
)
router = APIRouter(prefix="/api/v2/mlbplayers", tags=["mlbplayers"])

View File

@ -6,13 +6,8 @@ import pydantic
from pandas import DataFrame
from ..db_engine import Notification, model_to_dict, fn
from ..dependencies import oauth2_scheme, valid_token, LOG_DATA
from ..dependencies import oauth2_scheme, valid_token
logging.basicConfig(
filename=LOG_DATA['filename'],
format=LOG_DATA['format'],
level=LOG_DATA['log_level']
)
router = APIRouter(
prefix='/api/v2/notifs',

View File

@ -7,13 +7,8 @@ import pydantic
from pandas import DataFrame
from ..db_engine import db, Cardset, model_to_dict, Pack, Team, PackType
from ..dependencies import oauth2_scheme, valid_token, LOG_DATA
from ..dependencies import oauth2_scheme, valid_token
logging.basicConfig(
filename=LOG_DATA['filename'],
format=LOG_DATA['format'],
level=LOG_DATA['log_level']
)
router = APIRouter(
prefix='/api/v2/packs',
@ -109,7 +104,7 @@ async def get_packs(
@router.get('/{pack_id}')
async def get_one_pack(pack_id, csv: Optional[bool] = False):
async def get_one_pack(pack_id: int, csv: Optional[bool] = False):
try:
this_pack = Pack.get_by_id(pack_id)
except Exception:

View File

@ -5,13 +5,8 @@ import pydantic
from pandas import DataFrame
from ..db_engine import PackType, model_to_dict, fn
from ..dependencies import oauth2_scheme, valid_token, LOG_DATA
from ..dependencies import oauth2_scheme, valid_token
logging.basicConfig(
filename=LOG_DATA['filename'],
format=LOG_DATA['format'],
level=LOG_DATA['log_level']
)
router = APIRouter(
prefix='/api/v2/packtypes',

View File

@ -6,13 +6,8 @@ import pydantic
from pandas import DataFrame
from ..db_engine import Paperdex, model_to_dict, Player, Cardset, Team
from ..dependencies import oauth2_scheme, valid_token, LOG_DATA
from ..dependencies import oauth2_scheme, valid_token
logging.basicConfig(
filename=LOG_DATA['filename'],
format=LOG_DATA['format'],
level=LOG_DATA['log_level']
)
router = APIRouter(
prefix='/api/v2/paperdex',

View File

@ -19,13 +19,8 @@ from ..db_engine import (
CardPosition,
)
from ..db_helpers import upsert_pitching_card_ratings
from ..dependencies import oauth2_scheme, valid_token, LOG_DATA
from ..dependencies import oauth2_scheme, valid_token
logging.basicConfig(
filename=LOG_DATA["filename"],
format=LOG_DATA["format"],
level=LOG_DATA["log_level"],
)
router = APIRouter(prefix="/api/v2/pitchingcardratings", tags=["pitchingcardratings"])
RATINGS_FILE = "storage/pitching-ratings.csv"

View File

@ -7,13 +7,8 @@ import pydantic
from ..db_engine import db, PitchingCard, model_to_dict, Player, fn, MlbPlayer
from ..db_helpers import upsert_pitching_cards
from ..dependencies import oauth2_scheme, valid_token, LOG_DATA
from ..dependencies import oauth2_scheme, valid_token
logging.basicConfig(
filename=LOG_DATA["filename"],
format=LOG_DATA["format"],
level=LOG_DATA["log_level"],
)
router = APIRouter(prefix="/api/v2/pitchingcards", tags=["pitchingcards"])

View File

@ -6,13 +6,8 @@ import pydantic
from pandas import DataFrame
from ..db_engine import db, PitchingStat, model_to_dict, Card, Player, Current
from ..dependencies import oauth2_scheme, valid_token, LOG_DATA
from ..dependencies import oauth2_scheme, valid_token
logging.basicConfig(
filename=LOG_DATA['filename'],
format=LOG_DATA['format'],
level=LOG_DATA['log_level']
)
router = APIRouter(
prefix='/api/v2/pitstats',

View File

@ -28,7 +28,7 @@ from ..db_engine import (
MlbPlayer,
)
from ..db_helpers import upsert_players
from ..dependencies import oauth2_scheme, valid_token, LOG_DATA
from ..dependencies import oauth2_scheme, valid_token
# Franchise normalization: Convert city+team names to city-agnostic team names
# This enables cross-era player matching (e.g., 'Oakland Athletics' -> 'Athletics')
@ -73,11 +73,6 @@ def normalize_franchise(franchise: str) -> str:
return FRANCHISE_NORMALIZE.get(titled, titled)
logging.basicConfig(
filename=LOG_DATA["filename"],
format=LOG_DATA["format"],
level=LOG_DATA["log_level"],
)
router = APIRouter(prefix="/api/v2/players", tags=["players"])
@ -587,7 +582,7 @@ async def search_players(
@router.get("/{player_id}")
async def get_one_player(player_id, csv: Optional[bool] = False):
async def get_one_player(player_id: int, csv: Optional[bool] = False):
try:
this_player = Player.get_by_id(player_id)
except Exception:
@ -1119,7 +1114,7 @@ async def post_image_reset(
@router.delete("/{player_id}")
async def delete_player(player_id, token: str = Depends(oauth2_scheme)):
async def delete_player(player_id: int, token: str = Depends(oauth2_scheme)):
if not valid_token(token):
logging.warning(f"Bad Token: {token}")
raise HTTPException(

View File

@ -5,13 +5,8 @@ import pydantic
from pandas import DataFrame
from ..db_engine import Rarity, model_to_dict, fn
from ..dependencies import oauth2_scheme, valid_token, LOG_DATA
from ..dependencies import oauth2_scheme, valid_token
logging.basicConfig(
filename=LOG_DATA['filename'],
format=LOG_DATA['format'],
level=LOG_DATA['log_level']
)
router = APIRouter(
prefix='/api/v2/rarities',

View File

@ -5,13 +5,8 @@ import pydantic
from pandas import DataFrame
from ..db_engine import Result, model_to_dict, Team, DataError
from ..dependencies import oauth2_scheme, valid_token, LOG_DATA
from ..dependencies import oauth2_scheme, valid_token
logging.basicConfig(
filename=LOG_DATA['filename'],
format=LOG_DATA['format'],
level=LOG_DATA['log_level']
)
router = APIRouter(
prefix='/api/v2/results',

View File

@ -6,13 +6,8 @@ import pydantic
from pandas import DataFrame
from ..db_engine import Reward, model_to_dict, fn
from ..dependencies import oauth2_scheme, valid_token, LOG_DATA
from ..dependencies import oauth2_scheme, valid_token
logging.basicConfig(
filename=LOG_DATA['filename'],
format=LOG_DATA['format'],
level=LOG_DATA['log_level']
)
router = APIRouter(
prefix='/api/v2/rewards',

View File

@ -3,14 +3,9 @@ import logging
import pydantic
from ..db_engine import Player
from ..dependencies import oauth2_scheme, valid_token, LOG_DATA
from ..dependencies import oauth2_scheme, valid_token
from ..player_scouting import get_player_ids
logging.basicConfig(
filename=LOG_DATA['filename'],
format=LOG_DATA['format'],
level=LOG_DATA['log_level']
)
router = APIRouter(
prefix='/api/v2/scouting',

View File

@ -5,13 +5,8 @@ import pandas as pd
import pydantic
from ..db_engine import StratGame, model_to_dict, fn
from ..dependencies import oauth2_scheme, valid_token, LOG_DATA
from ..dependencies import oauth2_scheme, valid_token
logging.basicConfig(
filename=LOG_DATA['filename'],
format=LOG_DATA['format'],
level=LOG_DATA['log_level']
)
router = APIRouter(
prefix='/api/v2/games',

View File

@ -19,13 +19,8 @@ from ..db_engine import (
Decision,
)
from ..db_helpers import upsert_strat_plays
from ..dependencies import oauth2_scheme, valid_token, LOG_DATA
from ..dependencies import oauth2_scheme, valid_token
logging.basicConfig(
filename=LOG_DATA["filename"],
format=LOG_DATA["format"],
level=LOG_DATA["log_level"],
)
router = APIRouter(prefix="/api/v2/plays", tags=["plays"])

View File

@ -35,15 +35,9 @@ from ..db_engine import (
from ..dependencies import (
oauth2_scheme,
valid_token,
LOG_DATA,
PRIVATE_IN_SCHEMA,
)
logging.basicConfig(
filename=LOG_DATA["filename"],
format=LOG_DATA["format"],
level=LOG_DATA["log_level"],
)
router = APIRouter(prefix="/api/v2/teams", tags=["teams"])
@ -138,9 +132,6 @@ async def get_teams(
if ranking_max is not None:
all_teams = all_teams.where(Team.ranking <= ranking_max)
if ranking_max is not None:
all_teams = all_teams.where(Team.ranking <= ranking_max)
if has_guide is not None:
# Use boolean comparison (PostgreSQL-compatible)
if not has_guide:
@ -176,7 +167,7 @@ async def get_teams(
@router.get("/{team_id}")
async def get_one_team(team_id, inc_packs: bool = True, csv: Optional[bool] = False):
async def get_one_team(team_id: int, inc_packs: bool = True, csv: Optional[bool] = False):
try:
this_team = Team.get_by_id(team_id)
except Exception:
@ -282,7 +273,6 @@ def get_scouting_dfs(allowed_players, position: str):
)
)
def get_total_ops(df_data):
ops_vl = df_data["obp_vl"] + df_data["slg_vl"]
ops_vr = df_data["obp_vr"] + df_data["slg_vr"]
@ -590,16 +580,21 @@ def sort_pitchers(pitching_card_query) -> DataFrame | None:
pitcher_df = pd.DataFrame(all_s).set_index("player", drop=False)
logging.debug(f"pitcher_df: {pitcher_df}")
def get_total_ops(df_data):
vlval = PitchingCardRatings.get_or_none(
PitchingCardRatings.pitchingcard_id == df_data["id"],
PitchingCardRatings.vs_hand == "L",
)
vrval = PitchingCardRatings.get_or_none(
PitchingCardRatings.pitchingcard_id == df_data["id"],
PitchingCardRatings.vs_hand == "R",
card_ids = pitcher_df["id"].tolist()
ratings_map = {
(r.pitchingcard_id, r.vs_hand): r
for r in PitchingCardRatings.select().where(
(PitchingCardRatings.pitchingcard_id << card_ids)
& (PitchingCardRatings.vs_hand << ["L", "R"])
)
}
def get_total_ops(df_data):
vlval = ratings_map.get((df_data["id"], "L"))
vrval = ratings_map.get((df_data["id"], "R"))
if vlval is None or vrval is None:
return float("inf")
ops_vl = vlval.obp + vlval.slg
ops_vr = vrval.obp + vrval.slg
# TODO: should this be max??
@ -667,16 +662,21 @@ async def get_team_sp(
starter_df = pd.DataFrame(all_s).set_index("player", drop=False)
logging.debug(f"starter_df: {starter_df}")
def get_total_ops(df_data):
vlval = PitchingCardRatings.get_or_none(
PitchingCardRatings.pitchingcard_id == df_data["id"],
PitchingCardRatings.vs_hand == "L",
)
vrval = PitchingCardRatings.get_or_none(
PitchingCardRatings.pitchingcard_id == df_data["id"],
PitchingCardRatings.vs_hand == "R",
card_ids = starter_df["id"].tolist()
ratings_map = {
(r.pitchingcard_id, r.vs_hand): r
for r in PitchingCardRatings.select().where(
(PitchingCardRatings.pitchingcard_id << card_ids)
& (PitchingCardRatings.vs_hand << ["L", "R"])
)
}
def get_total_ops(df_data):
vlval = ratings_map.get((df_data["id"], "L"))
vrval = ratings_map.get((df_data["id"], "R"))
if vlval is None or vrval is None:
return float("inf")
ops_vl = vlval.obp + vlval.slg
ops_vr = vrval.obp + vrval.slg
return (ops_vr + ops_vl + min(ops_vl, ops_vr)) / 3