mirror of
https://github.com/mwisnowski/mtg_python_deckbuilder.git
synced 2025-12-17 08:00:13 +01:00
feat(web): Core Refactor Phase A — extract sampling and cache modules; add adaptive TTL + eviction heuristics, Redis PoC, and metrics wiring. Tests added for TTL, eviction, exports, splash-adaptive, card index, and service worker. Docs+roadmap updated.
This commit is contained in:
parent
c4a7fc48ea
commit
a029d430c5
49 changed files with 3889 additions and 701 deletions
94
code/scripts/snapshot_taxonomy.py
Normal file
94
code/scripts/snapshot_taxonomy.py
Normal file
|
|
@ -0,0 +1,94 @@
|
|||
"""Snapshot the current power bracket taxonomy to a dated JSON artifact.
|
||||
|
||||
Outputs a JSON file under logs/taxonomy_snapshots/ named
|
||||
taxonomy_<YYYYMMDD>_<HHMMSS>.json
|
||||
containing:
|
||||
{
|
||||
"generated_at": ISO8601,
|
||||
"hash": sha256 hex of canonical payload (excluding this top-level wrapper),
|
||||
"brackets": [ {level,name,short_desc,long_desc,limits} ... ]
|
||||
}
|
||||
|
||||
If a snapshot with identical hash already exists today, creation is skipped
|
||||
unless --force provided.
|
||||
|
||||
Usage (from repo root):
|
||||
python -m code.scripts.snapshot_taxonomy
|
||||
python -m code.scripts.snapshot_taxonomy --force
|
||||
|
||||
Intended to provide an auditable evolution trail for taxonomy adjustments
|
||||
before we implement taxonomy-aware sampling changes.
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
import argparse
|
||||
import json
|
||||
import hashlib
|
||||
from datetime import datetime
|
||||
from pathlib import Path
|
||||
from typing import Any, Dict
|
||||
|
||||
from code.deck_builder.phases.phase0_core import BRACKET_DEFINITIONS
|
||||
|
||||
SNAP_DIR = Path("logs/taxonomy_snapshots")
|
||||
SNAP_DIR.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
|
||||
def _canonical_brackets():
|
||||
return [
|
||||
{
|
||||
"level": b.level,
|
||||
"name": b.name,
|
||||
"short_desc": b.short_desc,
|
||||
"long_desc": b.long_desc,
|
||||
"limits": b.limits,
|
||||
}
|
||||
for b in sorted(BRACKET_DEFINITIONS, key=lambda x: x.level)
|
||||
]
|
||||
|
||||
|
||||
def compute_hash(brackets) -> str:
|
||||
# Canonical JSON with sorted keys for repeatable hash
|
||||
payload = json.dumps(brackets, sort_keys=True, separators=(",", ":"))
|
||||
return hashlib.sha256(payload.encode("utf-8")).hexdigest()
|
||||
|
||||
|
||||
def find_existing_hashes() -> Dict[str, Path]:
|
||||
existing = {}
|
||||
for p in SNAP_DIR.glob("taxonomy_*.json"):
|
||||
try:
|
||||
data = json.loads(p.read_text(encoding="utf-8"))
|
||||
h = data.get("hash")
|
||||
if h:
|
||||
existing[h] = p
|
||||
except Exception:
|
||||
continue
|
||||
return existing
|
||||
|
||||
|
||||
def main():
|
||||
ap = argparse.ArgumentParser()
|
||||
ap.add_argument("--force", action="store_true", help="Write new snapshot even if identical hash exists today")
|
||||
args = ap.parse_args()
|
||||
|
||||
brackets = _canonical_brackets()
|
||||
h = compute_hash(brackets)
|
||||
existing = find_existing_hashes()
|
||||
if h in existing and not args.force:
|
||||
print(f"Snapshot identical (hash={h[:12]}...) exists: {existing[h].name}; skipping.")
|
||||
return 0
|
||||
|
||||
ts = datetime.utcnow().strftime("%Y%m%d_%H%M%S")
|
||||
out = SNAP_DIR / f"taxonomy_{ts}.json"
|
||||
wrapper: Dict[str, Any] = {
|
||||
"generated_at": datetime.utcnow().isoformat() + "Z",
|
||||
"hash": h,
|
||||
"brackets": brackets,
|
||||
}
|
||||
out.write_text(json.dumps(wrapper, indent=2, sort_keys=True) + "\n", encoding="utf-8")
|
||||
print(f"Wrote taxonomy snapshot {out} (hash={h[:12]}...)")
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == "__main__": # pragma: no cover
|
||||
raise SystemExit(main())
|
||||
Loading…
Add table
Add a link
Reference in a new issue