Name the Pokemon demo, and land a seed rename on its own row
`seed.py` matches a seed file to its scenario by title, so renaming one inserted a second public scenario and stranded the first. The stranded row stays public forever and has to be deleted by hand on every deployment, which is what happened to "Road to the Champion". A seed file now lists its old titles under `previous_titles`, and the rename updates the existing row. The cover art is a PNG data URI. `app/images.py` accepts raster formats only, because SVG can carry script and the bytes are served from the app's own origin, so an SVG stores but yields an empty `image_url`. `tools/make_pokeball.py` draws the ball with `zlib` alone. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01PacdRuPXSkQQy4ZYdH32hF
This commit is contained in:
co-authored by
Claude Opus 5
parent
ab546d6f3c
commit
ae39514d1e
+37
-11
@@ -28,8 +28,10 @@ logger = logging.getLogger(__name__)
|
||||
|
||||
SEED_DIR = Path(__file__).resolve().parent / "seed_data"
|
||||
|
||||
_SCALARS = ("description", "prompt", "memory", "authors_note", "ai_instructions", "tags",
|
||||
"image", "icon")
|
||||
# `title` is in this list so that a rename found through `previous_titles` is
|
||||
# both detected by `_matches` and written by `_apply_scalars`.
|
||||
_SCALARS = ("title", "description", "prompt", "memory", "authors_note", "ai_instructions",
|
||||
"tags", "image", "icon")
|
||||
_CARD_FIELDS = ("type", "name", "keys", "entry", "notes")
|
||||
_SCRIPT_FIELDS = ("name", "library_js", "input_js", "context_js", "output_js")
|
||||
|
||||
@@ -55,15 +57,7 @@ def seed_public_scenarios(engine: Engine) -> None:
|
||||
if not title:
|
||||
continue
|
||||
|
||||
existing = (
|
||||
db.query(models.Scenario)
|
||||
.filter(
|
||||
models.Scenario.title == title,
|
||||
models.Scenario.user_id.is_(None),
|
||||
models.Scenario.is_public.is_(True),
|
||||
)
|
||||
.first()
|
||||
)
|
||||
existing = _find_seeded(db, title) or _find_renamed(db, data)
|
||||
if existing is None:
|
||||
_insert_scenario(db, data)
|
||||
changed += 1
|
||||
@@ -90,6 +84,38 @@ def _script_tuple(source, get) -> tuple:
|
||||
return tuple(get(source, f) for f in _SCRIPT_FIELDS)
|
||||
|
||||
|
||||
def _find_seeded(db, title: str) -> models.Scenario | None:
|
||||
"""Returns the seeded scenario with this exact title, if there is one."""
|
||||
return (
|
||||
db.query(models.Scenario)
|
||||
.filter(
|
||||
models.Scenario.title == title,
|
||||
models.Scenario.user_id.is_(None),
|
||||
models.Scenario.is_public.is_(True),
|
||||
)
|
||||
.first()
|
||||
)
|
||||
|
||||
|
||||
def _find_renamed(db, data: dict) -> models.Scenario | None:
|
||||
"""Returns the row a renamed seed file used to own, so the rename lands on it.
|
||||
|
||||
A seed is matched by title, so renaming one inserts a second scenario and
|
||||
strands the first. The stranded row stays public forever and has to be
|
||||
deleted by hand on every deployment. List the old title under
|
||||
`previous_titles` in the seed file and the rename updates the existing row
|
||||
instead, which also keeps the adventures already started from it pointing at
|
||||
a scenario that still exists.
|
||||
|
||||
Drop a `previous_titles` entry once every deployment has booted past it.
|
||||
"""
|
||||
for old in data.get("previous_titles") or []:
|
||||
found = _find_seeded(db, str(old))
|
||||
if found is not None:
|
||||
return found
|
||||
return None
|
||||
|
||||
|
||||
def _matches(scenario: models.Scenario, data: dict) -> bool:
|
||||
"""True when the DB scenario already equals the seed file, so we can skip
|
||||
the write and avoid churning rows on every boot."""
|
||||
|
||||
Reference in New Issue
Block a user