Add after_route_name to boss battle export/seed pipeline

Exports now include after_route_name (resolved from the route FK),
and the seed loader resolves it back to an ID on import. Also adds
a draft bean for displaying encounter-less locations.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-02-08 15:51:23 +01:00
parent 47c8fa8e88
commit 758750b7b8
4 changed files with 38 additions and 1 deletions

View File

@@ -127,6 +127,7 @@ async def export_game_bosses(
.where(BossBattle.version_group_id == game.version_group_id)
.options(
selectinload(BossBattle.pokemon).selectinload(BossPokemon.pokemon),
selectinload(BossBattle.after_route),
)
.order_by(BossBattle.order)
)
@@ -143,6 +144,7 @@ async def export_game_bosses(
"badge_image_url": b.badge_image_url,
"level_cap": b.level_cap,
"order": b.order,
"after_route_name": b.after_route.name if b.after_route else None,
"location": b.location,
"section": b.section,
"sprite_url": b.sprite_url,

View File

@@ -211,10 +211,19 @@ async def upsert_bosses(
version_group_id: int,
bosses: list[dict],
dex_to_id: dict[int, int],
route_name_to_id: dict[str, int] | None = None,
) -> int:
"""Upsert boss battles for a version group, return count of bosses upserted."""
count = 0
for boss in bosses:
# Resolve after_route_name to an ID
after_route_id = None
after_route_name = boss.get("after_route_name")
if after_route_name and route_name_to_id:
after_route_id = route_name_to_id.get(after_route_name)
if after_route_id is None:
print(f" Warning: route '{after_route_name}' not found for boss '{boss['name']}'")
# Upsert the boss battle on (version_group_id, order) conflict
stmt = insert(BossBattle).values(
version_group_id=version_group_id,
@@ -225,6 +234,7 @@ async def upsert_bosses(
badge_image_url=boss.get("badge_image_url"),
level_cap=boss["level_cap"],
order=boss["order"],
after_route_id=after_route_id,
location=boss["location"],
section=boss.get("section"),
sprite_url=boss.get("sprite_url"),
@@ -237,6 +247,7 @@ async def upsert_bosses(
"badge_name": boss.get("badge_name"),
"badge_image_url": boss.get("badge_image_url"),
"level_cap": boss["level_cap"],
"after_route_id": after_route_id,
"location": boss["location"],
"section": boss.get("section"),
"sprite_url": boss.get("sprite_url"),

View File

@@ -68,6 +68,7 @@ async def seed():
# 4. Per version group: upsert routes once, then encounters per game
total_routes = 0
total_encounters = 0
route_maps_by_vg: dict[int, dict[str, int]] = {}
for vg_slug, vg_info in vg_data.items():
vg_id = vg_slug_to_id[vg_slug]
@@ -87,6 +88,7 @@ async def seed():
# Upsert routes once per version group
route_map = await upsert_routes(session, vg_id, routes_data)
route_maps_by_vg[vg_id] = route_map
total_routes += len(route_map)
print(f" {vg_slug}: {len(route_map)} routes")
@@ -147,7 +149,8 @@ async def seed():
if not bosses_data:
continue
boss_count = await upsert_bosses(session, vg_id, bosses_data, dex_to_id)
route_name_to_id = route_maps_by_vg.get(vg_id, {})
boss_count = await upsert_bosses(session, vg_id, bosses_data, dex_to_id, route_name_to_id)
total_bosses += boss_count
print(f" {vg_slug}: {boss_count} bosses")
@@ -416,6 +419,7 @@ async def _export_bosses(session: AsyncSession, vg_data: dict):
.where(BossBattle.version_group_id == vg.id)
.options(
selectinload(BossBattle.pokemon).selectinload(BossPokemon.pokemon),
selectinload(BossBattle.after_route),
)
.order_by(BossBattle.order)
)
@@ -434,6 +438,7 @@ async def _export_bosses(session: AsyncSession, vg_data: dict):
"badge_image_url": b.badge_image_url,
"level_cap": b.level_cap,
"order": b.order,
"after_route_name": b.after_route.name if b.after_route else None,
"location": b.location,
"section": b.section,
"sprite_url": b.sprite_url,