feat: refresh theme catalog metadata

This commit is contained in:
matt 2025-09-27 15:19:32 -07:00
parent ca2ef70c23
commit 33570399f0
5 changed files with 19290 additions and 1134 deletions

5
.gitignore vendored
View file

@ -11,6 +11,11 @@ __pycache__/
csv_files/
dist/
logs/
!logs/
logs/*
!logs/perf/
logs/perf/*
!logs/perf/theme_preview_warm_baseline.json
deck_files/
csv_files/
config/themes/catalog/

View file

@ -27,6 +27,7 @@ from collections import Counter
from dataclasses import dataclass, field
from pathlib import Path
from typing import Any, Dict, List, Optional, Set, Tuple
import re
try: # Optional
import yaml # type: ignore
@ -64,6 +65,20 @@ except ModuleNotFoundError:
should_keep_theme,
)
try:
from scripts.export_themes_to_yaml import slugify as slugify_theme # type: ignore
except Exception:
_SLUG_RE = re.compile(r'[^a-z0-9-]')
def slugify_theme(name: str) -> str:
s = name.strip().lower()
s = s.replace('+', 'plus')
s = s.replace('/', '-')
s = re.sub(r'[\s_]+', '-', s)
s = _SLUG_RE.sub('', s)
s = re.sub(r'-{2,}', '-', s)
return s.strip('-')
ROOT = Path(__file__).resolve().parents[2]
CODE_ROOT = ROOT / 'code'
if str(CODE_ROOT) not in sys.path:
@ -729,7 +744,8 @@ def build_catalog(limit: int, verbose: bool) -> Dict[str, Any]:
else:
primary, secondary = _primary_secondary(theme, analytics['frequencies'])
entry = {'theme': theme, 'synergies': merged}
slug = getattr(y, 'id', None) or slugify_theme(theme)
entry = {'id': slug, 'theme': theme, 'synergies': merged}
if primary:
entry['primary_color'] = primary
if secondary:

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,767 @@
{{
"event": "preview_perf_warm_baseline", "avg_ms": 47.75,
"collected_at": "2025-09-23T18:45:00Z", "count": 735,
"mode": "warm-single-pass", "event": "preview_perf_warm_baseline",
"count": 735, "mode": "all",
"p50_ms": 30.27, "p50_ms": 30.27,
"p90_ms": 45.44, "p90_ms": 45.44,
"p95_ms": 58.77, "p95_ms": 58.77,
"avg_ms": 47.75, "slugs": [
"notes": "Captured from warm pass benchmarking after theme fast path rebuild.", "0-1-counters",
"slugs_sample": [ "0-2-counters",
"plus1-plus1-counters", "1-1-counters",
"aggro", "adamant",
"eldrazi-kindred", "adapt",
"vampire-kindred", "addendum",
"wizard-kindred" "advisor-kindred",
] "aetherborn-kindred",
} "affinity",
"afflict",
"afterlife",
"aftermath",
"age-counters",
"aggro",
"airbending",
"alien-kindred",
"alliance",
"ally-kindred",
"amass",
"amplify",
"angel-kindred",
"annihilator",
"antelope-kindred",
"ape-kindred",
"archer-kindred",
"archon-kindred",
"aristocrats",
"armadillo-kindred",
"army-kindred",
"artifact-tokens",
"artifacts-matter",
"artificer-kindred",
"ascend",
"assassin-kindred",
"assembly-worker-kindred",
"assist",
"astartes-kindred",
"atog-kindred",
"auras",
"aurochs-kindred",
"avatar-kindred",
"awaken",
"azra-kindred",
"backgrounds-matter",
"backup",
"badger-kindred",
"banding",
"barbarian-kindred",
"bard-kindred",
"bargain",
"basic-landcycling",
"basilisk-kindred",
"bat-kindred",
"battalion",
"battle-cry",
"battles-matter",
"bear-kindred",
"beast-kindred",
"beaver-kindred",
"beeble-kindred",
"behold",
"beholder-kindred",
"berserker-kindred",
"bestow",
"big-mana",
"bird-kindred",
"blaze-counters",
"blink",
"blitz",
"blood-token",
"bloodrush",
"bloodthirst",
"boar-kindred",
"board-wipes",
"boast",
"bolster",
"bounty-counters",
"bracket-extraturn",
"bracket-gamechanger",
"bracket-masslanddenial",
"bracket-tutornonland",
"brushwagg-kindred",
"burn",
"bushido",
"buyback",
"c-tan-kindred",
"camarid-kindred",
"camel-kindred",
"cantrips",
"capybara-kindred",
"card-draw",
"card-selection",
"carrier-kindred",
"cascade",
"cases-matter",
"casualty",
"cat-kindred",
"celebration",
"centaur-kindred",
"champion",
"changeling",
"channel",
"charge-counters",
"child-kindred",
"chimera-kindred",
"choose-a-background",
"chroma",
"cipher",
"citizen-kindred",
"clash",
"cleave",
"cleric-kindred",
"cloak",
"clones",
"clown-kindred",
"clue-token",
"cockatrice-kindred",
"cohort",
"collect-evidence",
"combat-matters",
"combat-tricks",
"compleated",
"conditional-draw",
"conjure",
"connive",
"conspire",
"constellation",
"construct-kindred",
"control",
"converge",
"convert",
"convoke",
"corpse-counters",
"corrupted",
"cost-reduction",
"cost-scaling",
"council-s-dilemma",
"counters-matter",
"counterspells",
"coven",
"coward-kindred",
"coyote-kindred",
"crab-kindred",
"craft",
"creature-tokens",
"crew",
"crocodile-kindred",
"cumulative-upkeep",
"custodes-kindred",
"cyberman-kindred",
"cycling",
"cyclops-kindred",
"dalek-kindred",
"dash",
"dauthi-kindred",
"daybound",
"deathtouch",
"defender",
"defense-counters",
"delay-counters",
"delirium",
"delve",
"demigod-kindred",
"demon-kindred",
"demonstrate",
"depletion-counters",
"descend",
"deserter-kindred",
"detain",
"detective-kindred",
"dethrone",
"devil-kindred",
"devoid",
"devotion-counters",
"devour",
"dinosaur-kindred",
"discard-matters",
"discover",
"disguise",
"disturb",
"divinity-counters",
"djinn-kindred",
"doctor-kindred",
"doctor-s-companion",
"dog-kindred",
"domain",
"doom-counters",
"double-strike",
"dragon-kindred",
"drake-kindred",
"dreadnought-kindred",
"dredge",
"drone-kindred",
"druid-kindred",
"dryad-kindred",
"dwarf-kindred",
"earthbend",
"echo",
"eerie",
"efreet-kindred",
"egg-kindred",
"elder-kindred",
"eldrazi-kindred",
"elemental-kindred",
"elephant-kindred",
"elf-kindred",
"elk-kindred",
"embalm",
"emerge",
"employee-kindred",
"enchant",
"enchantment-tokens",
"enchantments-matter",
"encore",
"endure",
"energy",
"energy-counters",
"enlist",
"enrage",
"enter-the-battlefield",
"entwine",
"eon-counters",
"epic",
"equip",
"equipment",
"equipment-matters",
"escalate",
"escape",
"eternalize",
"evoke",
"evolve",
"exalted",
"exert",
"exhaust",
"exile-matters",
"experience-counters",
"exploit",
"explore",
"extort",
"eye-kindred",
"fabricate",
"fade-counters",
"fading",
"faerie-kindred",
"fateful-hour",
"fateseal",
"fathomless-descent",
"fear",
"ferocious",
"ferret-kindred",
"fight",
"finality-counters",
"firebending",
"first-strike",
"fish-kindred",
"flagbearer-kindred",
"flanking",
"flash",
"flashback",
"flood-counters",
"flurry",
"flying",
"food",
"food-token",
"for-mirrodin",
"forage",
"forecast",
"forestcycling",
"forestwalk",
"foretell",
"formidable",
"fox-kindred",
"fractal-kindred",
"freerunning",
"frog-kindred",
"fungus-counters",
"fungus-kindred",
"fuse-counters",
"gargoyle-kindred",
"germ-kindred",
"giant-kindred",
"gift",
"gith-kindred",
"glimmer-kindred",
"gnoll-kindred",
"gnome-kindred",
"goad",
"goat-kindred",
"goblin-kindred",
"god-kindred",
"gold-token",
"golem-kindred",
"gorgon-kindred",
"graft",
"grandeur",
"gravestorm",
"graveyard-matters",
"gremlin-kindred",
"griffin-kindred",
"group-hug",
"growth-counters",
"guest-kindred",
"hag-kindred",
"halfling-kindred",
"hamster-kindred",
"harmonize",
"harpy-kindred",
"haste",
"hatching-counters",
"hatchling-counters",
"haunt",
"healing-counters",
"hellbent",
"hellion-kindred",
"hero-kindred",
"heroic",
"hexproof",
"hexproof-from",
"hideaway",
"hippo-kindred",
"hippogriff-kindred",
"historics-matter",
"hit-counters",
"homarid-kindred",
"homunculus-kindred",
"horror-kindred",
"horse-kindred",
"horsemanship",
"hour-counters",
"human-kindred",
"hydra-kindred",
"hyena-kindred",
"ice-counters",
"illusion-kindred",
"imp-kindred",
"impending",
"imprint",
"improvise",
"impulse",
"incarnation-kindred",
"incubate",
"incubator-token",
"indestructible",
"infect",
"infection-counters",
"ingest",
"inkling-kindred",
"insect-kindred",
"inspired",
"interaction",
"intimidate",
"investigate",
"islandcycling",
"islandwalk",
"jackal-kindred",
"jellyfish-kindred",
"job-select",
"join-forces",
"judgment-counters",
"juggernaut-kindred",
"jump",
"jump-start",
"junk-token",
"junk-tokens",
"kavu-kindred",
"ki-counters",
"kicker",
"kinship",
"kirin-kindred",
"kithkin-kindred",
"knight-kindred",
"kobold-kindred",
"kor-kindred",
"kraken-kindred",
"lamia-kindred",
"lammasu-kindred",
"land-types-matter",
"landcycling",
"landfall",
"lands-matter",
"landwalk",
"learn",
"leave-the-battlefield",
"leech-kindred",
"legends-matter",
"level-counters",
"level-up",
"leviathan-kindred",
"lhurgoyf-kindred",
"licid-kindred",
"lieutenant",
"life-matters",
"life-to-draw",
"lifegain",
"lifegain-triggers",
"lifelink",
"lifeloss",
"lifeloss-triggers",
"little-fellas",
"living-metal",
"living-weapon",
"lizard-kindred",
"loot",
"lore-counters",
"loyalty-counters",
"madness",
"magecraft",
"mana-dork",
"mana-rock",
"manifest",
"manifest-dread",
"manticore-kindred",
"map-token",
"max-speed",
"mayhem",
"megamorph",
"meld",
"melee",
"menace",
"mentor",
"mercenary-kindred",
"merfolk-kindred",
"metalcraft",
"metathran-kindred",
"midrange",
"mill",
"minion-kindred",
"minotaur-kindred",
"miracle",
"mite-kindred",
"mobilize",
"modal",
"modular",
"mole-kindred",
"monarch",
"monger-kindred",
"mongoose-kindred",
"monk-kindred",
"monkey-kindred",
"monstrosity",
"moonfolk-kindred",
"morbid",
"more-than-meets-the-eye",
"morph",
"mount-kindred",
"mountaincycling",
"mountainwalk",
"mouse-kindred",
"multikicker",
"multiple-copies",
"mutant-kindred",
"mutate",
"myr-kindred",
"myriad",
"mystic-kindred",
"nautilus-kindred",
"necron-kindred",
"net-counters",
"nightbound",
"nightmare-kindred",
"nightstalker-kindred",
"ninja-kindred",
"ninjutsu",
"noble-kindred",
"nomad-kindred",
"nymph-kindred",
"octopus-kindred",
"offering",
"offspring",
"ogre-kindred",
"oil-counters",
"omen-counters",
"ooze-kindred",
"open-an-attraction",
"orb-kindred",
"orc-kindred",
"ore-counters",
"orgg-kindred",
"otter-kindred",
"ouphe-kindred",
"outlast",
"outlaw-kindred",
"overload",
"ox-kindred",
"oyster-kindred",
"pack-tactics",
"pangolin-kindred",
"paradox",
"parley",
"partner",
"partner-with",
"peasant-kindred",
"pegasus-kindred",
"performer-kindred",
"persist",
"pest-kindred",
"phasing",
"phoenix-kindred",
"phyrexian-kindred",
"pillowfort",
"pilot-kindred",
"pingers",
"pirate-kindred",
"plague-counters",
"plainscycling",
"plainswalk",
"planeswalkers",
"plant-kindred",
"plot",
"plus0-plus1-counters",
"plus1-plus0-counters",
"plus1-plus1-counters",
"plus2-plus2-counters",
"poison-counters",
"politics",
"populate",
"porcupine-kindred",
"possum-kindred",
"powerstone-token",
"praetor-kindred",
"primarch-kindred",
"processor-kindred",
"proliferate",
"protection",
"prototype",
"provoke",
"prowess",
"prowl",
"quest-counters",
"rabbit-kindred",
"raccoon-kindred",
"rad-counters",
"radiance",
"raid",
"rally",
"ramp",
"rampage",
"ranger-kindred",
"rat-kindred",
"ravenous",
"reach",
"read-ahead",
"reanimate",
"rebel-kindred",
"rebound",
"reconfigure",
"recover",
"reflection-kindred",
"reinforce",
"removal",
"renew",
"renown",
"replacement-draw",
"replicate",
"resource-engine",
"retrace",
"revolt",
"rhino-kindred",
"rigger-kindred",
"riot",
"ripple",
"robot-kindred",
"rogue-kindred",
"role-token",
"roll-to-visit-your-attractions",
"rooms-matter",
"sacrifice-matters",
"sacrifice-to-draw",
"saddle",
"sagas-matter",
"salamander-kindred",
"samurai-kindred",
"sand-kindred",
"saproling-kindred",
"satyr-kindred",
"scarecrow-kindred",
"scavenge",
"scientist-kindred",
"scion-kindred",
"scorpion-kindred",
"scout-kindred",
"scream-counters",
"scry",
"sculpture-kindred",
"secret-council",
"serf-kindred",
"serpent-kindred",
"servo-kindred",
"shade-kindred",
"shadow",
"shaman-kindred",
"shapeshifter-kindred",
"shark-kindred",
"sheep-kindred",
"shield-counters",
"shrines-matter",
"shroud",
"siren-kindred",
"skeleton-kindred",
"skulk",
"skunk-kindred",
"slime-counters",
"slith-kindred",
"sliver-kindred",
"sloth-kindred",
"slug-kindred",
"snail-kindred",
"snake-kindred",
"soldier-kindred",
"soltari-kindred",
"soul-counters",
"soulbond",
"soulshift",
"spawn-kindred",
"spectacle",
"specter-kindred",
"spell-copy",
"spell-mastery",
"spells-matter",
"spellshaper-kindred",
"spellslinger",
"sphinx-kindred",
"spider-kindred",
"spike-kindred",
"spirit-kindred",
"splice",
"split-second",
"sponge-kindred",
"spore-counters",
"spree",
"squad",
"squid-kindred",
"squirrel-kindred",
"starfish-kindred",
"start-your-engines",
"stash-counters",
"station",
"stax",
"storage-counters",
"storm",
"strive",
"stun-counters",
"super-friends",
"superfriends",
"support",
"surge",
"surrakar-kindred",
"surveil",
"survival",
"survivor-kindred",
"suspect",
"suspend",
"swampcycling",
"swampwalk",
"sweep",
"synth-kindred",
"tempting-offer",
"tentacle-kindred",
"thalakos-kindred",
"theft",
"thopter-kindred",
"threshold",
"thrull-kindred",
"tide-counters",
"tiefling-kindred",
"time-counters",
"time-travel",
"token-creation",
"token-modification",
"tokens-matter",
"toolbox",
"topdeck",
"toughness-matters",
"toxic",
"toy-kindred",
"training",
"trample",
"transform",
"transmute",
"treasure",
"treasure-token",
"treefolk-kindred",
"tribute",
"trilobite-kindred",
"troll-kindred",
"turtle-kindred",
"tyranid-kindred",
"umbra-armor",
"unconditional-draw",
"undaunted",
"undergrowth",
"undying",
"unearth",
"unicorn-kindred",
"unleash",
"valiant",
"vampire-kindred",
"vanishing",
"varmint-kindred",
"vedalken-kindred",
"vehicles",
"venture-into-the-dungeon",
"verse-counters",
"vigilance",
"void",
"void-counters",
"voltron",
"wall-kindred",
"ward",
"warlock-kindred",
"warp",
"warrior-kindred",
"waterbending",
"weasel-kindred",
"weird-kindred",
"werewolf-kindred",
"whale-kindred",
"wheels",
"will-of-the-council",
"will-of-the-planeswalkers",
"wind-counters",
"wish-counters",
"wither",
"wizard-kindred",
"wizardcycling",
"wolf-kindred",
"wolverine-kindred",
"wombat-kindred",
"worm-kindred",
"wraith-kindred",
"wurm-kindred",
"x-spells",
"yeti-kindred",
"zombie-kindred",
"zubera-kindred"
],
"source_pass": 2,
"total_slugs": 735,
"warm_baseline": true
}