mirror of
https://github.com/mwisnowski/mtg_python_deckbuilder.git
synced 2025-12-16 23:50:12 +01:00
Finalize MDFC follow-ups, docs, and diagnostics tooling
document deck summary DFC badges, exporter annotations, and per-face metadata across README/DOCKER/release notes record completion of all MDFC roadmap follow-ups and add the authoring guide for multi-face CSV entries wire in optional DFC_PER_FACE_SNAPSHOT env support, exporter regression tests, and diagnostics updates noted in the changelog
This commit is contained in:
parent
6fefda714e
commit
88cf832bf2
46 changed files with 3292 additions and 86 deletions
|
|
@ -10,6 +10,8 @@ from deck_builder.builder import DeckBuilder
|
|||
from deck_builder import builder_constants as bc
|
||||
from file_setup.setup import initial_setup
|
||||
from tagging import tagger
|
||||
from exceptions import CommanderValidationError
|
||||
from commander_exclusions import lookup_commander_detail
|
||||
|
||||
def _is_stale(file1: str, file2: str) -> bool:
|
||||
"""Return True if file2 is missing or older than file1."""
|
||||
|
|
@ -67,6 +69,58 @@ def _headless_list_owned_files() -> List[str]:
|
|||
return sorted(entries)
|
||||
|
||||
|
||||
def _normalize_commander_name(value: Any) -> str:
|
||||
return str(value or "").strip().casefold()
|
||||
|
||||
|
||||
def _load_commander_name_lookup() -> set[str]:
|
||||
builder = DeckBuilder(
|
||||
headless=True,
|
||||
log_outputs=False,
|
||||
output_func=lambda *_: None,
|
||||
input_func=lambda *_: "",
|
||||
)
|
||||
df = builder.load_commander_data()
|
||||
names: set[str] = set()
|
||||
for column in ("name", "faceName"):
|
||||
if column not in df.columns:
|
||||
continue
|
||||
series = df[column].dropna().astype(str)
|
||||
for raw in series:
|
||||
normalized = _normalize_commander_name(raw)
|
||||
if normalized:
|
||||
names.add(normalized)
|
||||
return names
|
||||
|
||||
|
||||
def _validate_commander_available(command_name: str) -> None:
|
||||
normalized = _normalize_commander_name(command_name)
|
||||
if not normalized:
|
||||
return
|
||||
|
||||
available = _load_commander_name_lookup()
|
||||
if normalized in available:
|
||||
return
|
||||
|
||||
info = lookup_commander_detail(command_name)
|
||||
if info is not None:
|
||||
primary_face = str(info.get("primary_face") or info.get("name") or "").strip()
|
||||
eligible_faces = info.get("eligible_faces")
|
||||
face_hint = ", ".join(str(face) for face in eligible_faces) if isinstance(eligible_faces, list) else ""
|
||||
message = (
|
||||
f"Commander '{command_name}' is no longer available because only a secondary face met commander eligibility."
|
||||
)
|
||||
if primary_face and _normalize_commander_name(primary_face) != normalized:
|
||||
message += f" Try selecting the front face '{primary_face}' or choose a different commander."
|
||||
elif face_hint:
|
||||
message += f" The remaining eligible faces were: {face_hint}."
|
||||
else:
|
||||
message += " Choose a different commander whose front face is commander-legal."
|
||||
raise CommanderValidationError(message, details={"commander": command_name, "reason": info})
|
||||
|
||||
raise CommanderValidationError(f"Commander not found: {command_name}", details={"commander": command_name})
|
||||
|
||||
|
||||
@dataclass
|
||||
class RandomRunConfig:
|
||||
"""Runtime options for the headless random build flow."""
|
||||
|
|
@ -113,6 +167,11 @@ def run(
|
|||
seed: Optional[int | str] = None,
|
||||
) -> DeckBuilder:
|
||||
"""Run a scripted non-interactive deck build and return the DeckBuilder instance."""
|
||||
trimmed_commander = (command_name or "").strip()
|
||||
if trimmed_commander:
|
||||
_validate_commander_available(trimmed_commander)
|
||||
command_name = trimmed_commander
|
||||
|
||||
owned_prompt_inputs: List[str] = []
|
||||
owned_files_available = _headless_list_owned_files()
|
||||
if owned_files_available:
|
||||
|
|
@ -1460,7 +1519,11 @@ def _main() -> int:
|
|||
print("Error: commander is required. Provide --commander or a JSON config with a 'commander' field.")
|
||||
return 2
|
||||
|
||||
run(**resolved)
|
||||
try:
|
||||
run(**resolved)
|
||||
except CommanderValidationError as exc:
|
||||
print(str(exc))
|
||||
return 2
|
||||
return 0
|
||||
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue