fix: materialize final_players queryset before double-iteration in get_random_player

When no position filters are applied, `final_players` is a lazy Peewee queryset
with `ORDER BY RANDOM() LIMIT n`. Iterating it twice (once to build player_ids,
once for the response loop) executes two separate DB queries with different random
seeds, causing dex_by_player to be built for a different player set than returned,
silently producing empty paperdex for all players.

Add `final_players = list(final_players)` before building player_ids to ensure
both iterations operate on the same materialized result. Also fix pre-existing
syntax error in import statement and minor ruff lint issues in the same file.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Cal Corum 2026-03-10 14:03:26 -05:00
parent 2c4ff01ff8
commit 4445acb7d0

View File

@ -12,7 +12,7 @@ from pandas import DataFrame
from playwright.async_api import async_playwright from playwright.async_api import async_playwright
from ..card_creation import get_batter_card_data, get_pitcher_card_data from ..card_creation import get_batter_card_data, get_pitcher_card_data
from ..db_engine import (, DoesNotExist from ..db_engine import (
db, db,
Player, Player,
model_to_dict, model_to_dict,
@ -74,7 +74,6 @@ def normalize_franchise(franchise: str) -> str:
return FRANCHISE_NORMALIZE.get(titled, titled) return FRANCHISE_NORMALIZE.get(titled, titled)
router = APIRouter(prefix="/api/v2/players", tags=["players"]) router = APIRouter(prefix="/api/v2/players", tags=["players"])
@ -145,7 +144,7 @@ async def get_players(
): ):
all_players = Player.select() all_players = Player.select()
if all_players.count() == 0: if all_players.count() == 0:
raise HTTPException(status_code=404, detail=f"There are no players to filter") raise HTTPException(status_code=404, detail="There are no players to filter")
if name is not None: if name is not None:
all_players = all_players.where(fn.Lower(Player.p_name) == name.lower()) all_players = all_players.where(fn.Lower(Player.p_name) == name.lower())
@ -477,6 +476,7 @@ async def get_random_player(
return Response(content=return_val, media_type="text/csv") return Response(content=return_val, media_type="text/csv")
else: else:
final_players = list(final_players)
return_val = {"count": len(final_players), "players": []} return_val = {"count": len(final_players), "players": []}
player_ids = [p.player_id for p in final_players] player_ids = [p.player_id for p in final_players]
dex_by_player = {} dex_by_player = {}
@ -684,9 +684,6 @@ async def get_batter_card(
) )
headers = {"Cache-Control": "public, max-age=86400"} headers = {"Cache-Control": "public, max-age=86400"}
filename = (
f"{this_player.description} {this_player.p_name} {card_type} {d}-v{variant}"
)
if ( if (
os.path.isfile( os.path.isfile(
f"storage/cards/cardset-{this_player.cardset.id}/{card_type}/{player_id}-{d}-v{variant}.png" f"storage/cards/cardset-{this_player.cardset.id}/{card_type}/{player_id}-{d}-v{variant}.png"