A deckbuilder for the commander format of Magic: The Gathering
Find a file
2025-10-03 10:43:24 -07:00
.github/workflows ci: retire preview perf workflow 2025-09-30 16:20:05 -07:00
code feat: add supplemental theme catalog tooling, additional theme selection, and custom theme selection 2025-10-03 10:43:24 -07:00
config feat: add supplemental theme catalog tooling, additional theme selection, and custom theme selection 2025-10-03 10:43:24 -07:00
csv_files/testdata fix(ci): relax headless commander validation 2025-10-02 17:09:07 -07:00
docs feat: add supplemental theme catalog tooling, additional theme selection, and custom theme selection 2025-10-03 10:43:24 -07:00
logs/perf fix: prevent headless owned prompt loop 2025-09-28 18:30:45 -07:00
tests/e2e feat: complete include/exclude observability, fix validation bugs, and organize tests 2025-09-09 20:18:03 -07:00
.dockerignore Fixed an issue with files uploaded to dockerhub, causing it to include some csv and text files that don't need to be there 2025-08-21 11:50:27 -07:00
.env.example feat: add supplemental theme catalog tooling, additional theme selection, and custom theme selection 2025-10-03 10:43:24 -07:00
.gitattributes web: DRY Step 5 and alternatives (partial+macro), centralize start_ctx/owned_set, adopt builder_* 2025-09-02 11:39:14 -07:00
.gitignore feat(web): launch commander browser with deck builder CTA 2025-09-30 15:49:08 -07:00
_tmp_check_metrics.py 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. 2025-09-24 13:57:23 -07:00
_tmp_run_catalog.ps1 chore(changelog): document Phase D close-out (strict alias, min examples, new archetype tagging, governance workflows) 2025-09-19 11:58:05 -07:00
_tmp_run_orchestrator.py feat(editorial): Phase D synergy commander enrichment, augmentation, lint & docs\n\nAdds Phase D editorial tooling: synergy-based commander selection with 3/2/1 pattern, duplicate filtering, annotated synergy_commanders, promotion to minimum examples, and augmentation heuristics (e.g. Counters Matter/Proliferate injection). Includes new scripts (generate_theme_editorial_suggestions, lint, validate, catalog build/apply), updates orchestrator & web routes, expands CI workflow, and documents usage & non-determinism policies. Updates lint rules, type definitions, and docker configs. 2025-09-18 10:59:20 -07:00
CHANGELOG.md feat: add supplemental theme catalog tooling, additional theme selection, and custom theme selection 2025-10-03 10:43:24 -07:00
CONTRIBUTING_EDITORIAL.md feat(tagging+archetypes): add Pillowfort/Politics/Midrange/Toolbox tagging and unify archetype presence skip logic 2025-09-19 11:53:52 -07:00
docker-compose.yml feat: add supplemental theme catalog tooling, additional theme selection, and custom theme selection 2025-10-03 10:43:24 -07:00
DOCKER.md feat: add supplemental theme catalog tooling, additional theme selection, and custom theme selection 2025-10-03 10:43:24 -07:00
Dockerfile web: DRY Step 5 and alternatives (partial+macro), centralize start_ctx/owned_set, adopt builder_* 2025-09-02 11:39:14 -07:00
dockerhub-docker-compose.yml feat: add supplemental theme catalog tooling, additional theme selection, and custom theme selection 2025-10-03 10:43:24 -07:00
entrypoint.sh Finalize MDFC follow-ups, docs, and diagnostics tooling 2025-10-02 15:31:05 -07:00
LICENSE Removed references to an old card_info script I had originally thought to include. 2025-01-21 09:26:36 -08:00
mtg_deckbuilder.spec build(windows): fix PyInstaller packaging\n\n- Add spec file with hiddenimports and data dirs\n- Use spec in release workflow; fallback to --paths code\n- Insert ./code into sys.path when frozen to resolve local imports 2025-08-25 09:57:02 -07:00
mypy.ini Finished v2 of deck_builder, should be largely functional, but could use refinements. WIll continue to work on it, but largely satisfied with how it works. 2025-01-17 17:04:02 -08:00
pyproject.toml chore: prepare release v2.4.0 2025-10-02 17:22:58 -07:00
pytest.ini Web UI polish: thumbnail-hover preview, white thumbnail selection, Themes bullet list; global Scryfall image retry (thumbs+previews) with fallbacks and cache-bust; standardized data-card-name. Deck Summary alignment overhaul (count//name/owned grid, tabular numerals, inset highlight, tooltips, starts under header). Added diagnostics (health + logs pages, error pages, request-id propagation), global HTMX error toasts, and docs updates. Update DOCKER guide and add run-web scripts. Update CHANGELOG and release notes template. 2025-08-27 11:21:46 -07:00
README.md feat: add supplemental theme catalog tooling, additional theme selection, and custom theme selection 2025-10-03 10:43:24 -07:00
RELEASE_NOTES_TEMPLATE.md feat: add supplemental theme catalog tooling, additional theme selection, and custom theme selection 2025-10-03 10:43:24 -07:00
requirements.txt fix(ci): install httpx and run pytest via python -m 2025-09-27 16:01:44 -07:00
run-from-dockerhub.bat feat(owned-cards): add owned-only workflow, multi-file parsing, and recommendations export\n\n- Prompt to use only owned cards (gated by presence of lists)\n- Support .txt/.csv owned lists, multi-select; commander exempt\n- Owned-only filtering + add guard; recommendations CSV/TXT when incomplete\n- CSV Owned column when not owned-only\n- Docs and Docker updated (owned_cards + config mounts)\n- CI: Windows EXE on tag; Docker Hub tag fix (no major.minor)\n- Changelog added; RELEASE_NOTES.md ignored 2025-08-25 09:48:05 -07:00
run-from-dockerhub.sh feat(owned-cards): add owned-only workflow, multi-file parsing, and recommendations export\n\n- Prompt to use only owned cards (gated by presence of lists)\n- Support .txt/.csv owned lists, multi-select; commander exempt\n- Owned-only filtering + add guard; recommendations CSV/TXT when incomplete\n- CSV Owned column when not owned-only\n- Docs and Docker updated (owned_cards + config mounts)\n- CI: Windows EXE on tag; Docker Hub tag fix (no major.minor)\n- Changelog added; RELEASE_NOTES.md ignored 2025-08-25 09:48:05 -07:00
run-web-from-dockerhub.bat release: v2.0.1 — Web UI major upgrade, theming reset, diagnostics polish, docs refreshed 2025-08-28 16:44:58 -07:00
run-web-from-dockerhub.sh Web UI polish: thumbnail-hover preview, white thumbnail selection, Themes bullet list; global Scryfall image retry (thumbs+previews) with fallbacks and cache-bust; standardized data-card-name. Deck Summary alignment overhaul (count//name/owned grid, tabular numerals, inset highlight, tooltips, starts under header). Added diagnostics (health + logs pages, error pages, request-id propagation), global HTMX error toasts, and docs updates. Update DOCKER guide and add run-web scripts. Update CHANGELOG and release notes template. 2025-08-27 11:21:46 -07:00
WINDOWS_DOCKER_GUIDE.md release: v2.0.1 — Web UI major upgrade, theming reset, diagnostics polish, docs refreshed 2025-08-28 16:44:58 -07:00

🃏 MTG Python Deckbuilder

License: MIT Python 3.11+ Docker

A web-first Commander/EDH deckbuilder with a shared core for CLI, headless, and Docker workflows. It builds curated decks, enforces bracket policies, understands owned cards, and ships a modern FastAPI + HTMX UI.

  • Web UI priority: All homepage actions map to the sections below.
  • Shared logic: Web, CLI, and headless runs use the same builder engine and exports.
  • Deterministic outputs: Random modes respect seeds, include/exclude lists, and bracket rules.
  • Data-aware UX: Owned library, themes, commanders, diagnostics, and logs live side-by-side.

Table of contents


Quick start

Pick the path that fits your setup. All commands target Windows PowerShell.

docker compose up --build --no-deps -d web

The Web UI starts on http://localhost:8080. First boot seeds data, refreshes decks, and tags cards automatically (see env defaults in docker-compose.yml). Use docker compose stop web / docker compose start web to pause or resume.

Option 2 — Docker Hub image

docker run --rm -p 8080:8080 `
  -v "${PWD}/deck_files:/app/deck_files" `
  -v "${PWD}/logs:/app/logs" `
  -v "${PWD}/csv_files:/app/csv_files" `
  -v "${PWD}/config:/app/config" `
  -v "${PWD}/owned_cards:/app/owned_cards" `
  mwisnowski/mtg-python-deckbuilder:latest

Brings up the same web UI using the prebuilt image. All volumes persist on the host.

Option 3 — Run from source

python -m venv .venv
.\venv\Scripts\Activate.ps1
pip install -r requirements.txt
uvicorn code.web.app:app --host 127.0.0.1 --port 8080

CLI entry point: python code/main.py. Headless convenience runner: python code/headless_runner.py --config config/deck.json.

For deeper Docker notes (including headless runs, impersonating CLI via compose, and maintenance scripts) see DOCKER.md.


Homepage guide

Every tile on the homepage connects to a workflow. Use these sections as your tour.

Build a Deck

Start here for interactive deck creation.

  • Pick commander, themes (primary/secondary/tertiary), bracket, and optional deck name in the unified modal.
  • Add supplemental themes in the Additional Themes section (ENABLE_CUSTOM_THEMES): fuzzy suggestions, removable chips, and strict/permissive matching toggles respect THEME_MATCH_MODE and USER_THEME_LIMIT.
  • Locks, Replace, Compare, and Permalinks live in Step 5.
  • Exports (CSV, TXT, compliance JSON, summary JSON) land in deck_files/ and reuse your chosen deck name when set.
  • ALLOW_MUST_HAVES=1 (default) enables include/exclude enforcement.
  • WEB_AUTO_ENFORCE=1 re-runs bracket enforcement automatically after each build.

Run a JSON Config

Execute saved configs without manual input.

  • Place JSON configs under config/ (see config/deck.json for a template).
  • Launch via homepage button or by running the container with APP_MODE=cli and DECK_MODE=headless.
  • Respect include/exclude, owned, and theme overrides defined in the config file or env vars.
  • Supplemental themes: add "additional_themes": ["Theme A", "Theme B"] plus "theme_match_mode": "permissive"|"strict". Strict mode stops the build when a theme cannot be resolved; permissive keeps going and prints suggestions. Exported configs also include a camelCase alias ("userThemes") and the active catalog version ("themeCatalogVersion"); either field name is accepted on import.

Initial Setup

Refresh data and caches when formats shift.

  • Runs card downloads, CSV regeneration, tagging, and commander catalog rebuilds.
  • Controlled by SHOW_SETUP=1 (on by default in compose).
  • Force a rebuild manually:
    docker compose run --rm --entrypoint bash web -lc "python -m code.file_setup.setup"
    
  • Rebuild only the commander catalog:
    docker compose run --rm --entrypoint bash web -lc "python -m code.scripts.refresh_commander_catalog"
    

Owned Library

Keep track of cards you own.

  • Upload .txt or .csv lists; the app enriches and deduplicates entries.
  • Owned-only and Prefer-owned build modes live in the New Deck modal.
  • owned_cards/ is persisted via volume mounting (OWNED_CARDS_DIR=/app/owned_cards).
  • Enable virtualization for large libraries with WEB_VIRTUALIZE=1.

Browse Commanders

Explore the curated commander catalog.

  • Powered by csv_files/commander_cards.csv.
  • Toggle the tile with SHOW_COMMANDERS=1.
  • Refresh via Initial Setup or the commander catalog script above.
  • MDFC merges and compatibility snapshots are handled automatically; use --compat-snapshot on the refresh script to emit an unmerged snapshot.

Browse Themes

Investigate theme synergies and diagnostics.

  • ENABLE_THEMES=1 keeps the tile visible (default).
  • Extra tooling (/themes/metrics, uncapped synergies, editorial quality) is gated by WEB_THEME_PICKER_DIAGNOSTICS=1.
  • Rebuild the merged catalog as needed:
    docker compose run --rm --entrypoint bash web -lc "python -m code.scripts.build_theme_catalog"
    
  • Generate the normalized supplemental theme catalog (commander & card counts) for user-added themes:
    python -m code.scripts.generate_theme_catalog --output config/themes/theme_catalog.csv
    
    Add --logs-dir logs/generated to mirror the CSV for diffing, or --csv-dir to point at alternate datasets.
  • Advanced editorial knobs (EDITORIAL_*, SPLASH_ADAPTIVE, etc.) live in .env.example and are summarized in the env table below.

Finished Decks

Review, compare, and export previous builds.

  • Reads from the deck_files/ volume.
  • Compare view can diff two builds, copy summaries, and download text lists.
  • Locks, replace history, and compliance metadata persist per deck.

Random Build

Spin up surprise decks with deterministic fallbacks.

  • Enable backend endpoints with RANDOM_MODES=1 and the UI tile with RANDOM_UI=1.
  • Fine-tune with RANDOM_MAX_ATTEMPTS, RANDOM_TIMEOUT_MS, RANDOM_PRIMARY_THEME, RANDOM_SEED, and RANDOM_AUTO_FILL.
  • Random flows honor include/exclude lists, owned filters, and bracket enforcement.

Diagnostics

Peek at health & performance.

  • Enabled via SHOW_DIAGNOSTICS=1.
  • /diagnostics summarizes system status, feature flags, and theme metrics.
  • /healthz offers a lightweight probe ({status, version, uptime_seconds}).
  • Press v inside virtualized lists (when WEB_VIRTUALIZE=1) to view grid diagnostics.

View Logs

Tail and download logs without leaving the browser.

  • Enabled via SHOW_LOGS=1.
  • /logs shows recent entries, filtering, and copy-to-clipboard.
  • Raw files live under the mounted logs/ directory.
  • /status/logs?tail=N returns JSON payloads for automation.

CLI & headless flows

The CLI and headless runners share the builder core.

  • Launch menu-driven CLI: python code/main.py.
  • Run headless (non-interactive) builds: python code/headless_runner.py --config config/deck.json.
  • In Docker, set APP_MODE=cli (and optionally DECK_MODE=headless) to switch the container entrypoint to the CLI.
  • Config precedence is CLI prompts > environment variables > JSON config > defaults.

Data, exports, and volumes

Host path Container path Purpose
deck_files/ /app/deck_files CSV/TXT exports, compliance JSON, summary JSON
logs/ /app/logs Application logs, taxonomy snapshots
csv_files/ /app/csv_files Card datasets, commander catalog, tagging metadata
config/ /app/config JSON configs, bracket policies, themes, card lists
owned_cards/ /app/owned_cards Uploaded owned-card libraries

Exports follow a stable naming scheme and include a .summary.json sidecar containing deck metadata, resolved themes, and lock history.


Environment variables

Most defaults are defined in docker-compose.yml and documented in .env.example. Highlights:

Core modes & networking

Variable Default Purpose
APP_MODE web Switch between Web UI (web) and CLI (cli).
DECK_MODE (unset) headless auto-runs the builder in CLI mode.
DECK_CONFIG /app/config/deck.json Points the headless runner at a config file or folder.
HOST / PORT / WORKERS 0.0.0.0 / 8080 / 1 Uvicorn settings for the web server.

Homepage visibility & UX

Variable Default Purpose
SHOW_SETUP 1 Show the Initial Setup tile.
SHOW_LOGS 1 Enable the logs viewer tile and endpoints.
SHOW_DIAGNOSTICS 1 Unlock diagnostics views and overlays.
SHOW_COMMANDERS 1 Enable the commander browser.
ENABLE_THEMES 1 Keep the theme browser and selector active.
ENABLE_CUSTOM_THEMES 1 Surface the Additional Themes section in the New Deck modal.
WEB_VIRTUALIZE 1 Opt into virtualized lists for large datasets.
ALLOW_MUST_HAVES 1 Enforce include/exclude (must-have) lists.
THEME dark Default UI theme (system, light, or dark).

Random build tuning

Variable Default Purpose
RANDOM_MODES (unset) Expose random build endpoints.
RANDOM_UI (unset) Show the Random Build homepage tile.
RANDOM_MAX_ATTEMPTS 5 Retry budget when constraints are tight.
RANDOM_TIMEOUT_MS 5000 Per-attempt timeout in milliseconds.
RANDOM_REROLL_THROTTLE_MS 350 Minimum milliseconds between reroll requests (client-side guard).
RANDOM_STRUCTURED_LOGS 0 Emit structured JSON logs for random builds.
RANDOM_TELEMETRY 0 Enable lightweight timing/attempt metrics for diagnostics.
RANDOM_PRIMARY_THEME / RANDOM_SECONDARY_THEME / RANDOM_TERTIARY_THEME (blank) Override selected themes.
RANDOM_SEED (blank) Deterministic seed for reproducible builds.
RANDOM_AUTO_FILL 1 Allow auto-fill of missing theme slots.

Random rate limiting (optional)

Variable Default Purpose
RATE_LIMIT_ENABLED 0 Enable server-side rate limiting for random endpoints.
RATE_LIMIT_WINDOW_S 10 Rolling window size in seconds.
RATE_LIMIT_RANDOM 10 Max random attempts per window.
RATE_LIMIT_BUILD 10 Max full builds per window.
RATE_LIMIT_SUGGEST 30 Max suggestion calls per window.

Automation & performance

Variable Default Purpose
WEB_AUTO_SETUP 1 Auto-run setup when artifacts are missing or stale.
WEB_AUTO_REFRESH_DAYS 7 Refresh cards.csv if older than N days.
WEB_TAG_PARALLEL 1 Enable parallel tagging workers.
WEB_TAG_WORKERS 4 Worker count for tagging (compose default).
WEB_AUTO_ENFORCE 0 Auto-apply bracket enforcement after builds.
WEB_THEME_PICKER_DIAGNOSTICS 1 Enable theme diagnostics endpoints.

Paths & overrides

Variable Default Purpose
CSV_FILES_DIR /app/csv_files Alternate dataset location (useful for tests).
DECK_EXPORTS /app/deck_files Override where exports land.
OWNED_CARDS_DIR / CARD_LIBRARY_DIR /app/owned_cards Override owned library path.
CARD_INDEX_EXTRA_CSV (blank) Inject extra CSV data into the card index.

Supplemental themes

Variable Default Purpose
DECK_ADDITIONAL_THEMES (blank) Comma/semicolon separated list of supplemental themes to apply in headless builds.
THEME_MATCH_MODE permissive Controls fuzzy resolution strictness (strict blocks unresolved themes) and seeds the web UI default.
USER_THEME_LIMIT 8 Maximum number of user-supplied themes allowed in the web builder.

Refer to .env.example for advanced editorial, taxonomy, and experimentation knobs (EDITORIAL_*, SPLASH_ADAPTIVE, WEB_THEME_FILTER_PREWARM, etc.). Document any newly introduced variables in the README, DOCKER guide, compose files, and .env.example.


Project layout

code/                 FastAPI app, deckbuilding engine, CLI, scripts, and tests
├─ web/               Web UI (FastAPI + Jinja2 + HTMX)
├─ deck_builder/      Core builder logic and services
├─ tagging/           Tag pipelines and utilities
├─ locks/             Card locking utilities
├─ scripts/           Maintenance and editorial tools
├─ tests/             Pytest suite (web, CLI, random, tagging)
config/               JSON configs, bracket policies, themes, card lists
csv_files/            Card datasets, commander catalog, theme outputs
owned_cards/          User-supplied owned lists
logs/                 Application logs and taxonomy snapshots
deck_files/           Generated deck exports (CSV/TXT/JSON)

Development setup

  1. Create and activate the virtual environment:
    python -m venv .venv
    .\venv\Scripts\Activate.ps1
    
  2. Install dependencies:
    pip install -r requirements.txt -r requirements-dev.txt
    
  3. Run the web app locally:
    uvicorn code.web.app:app --host 127.0.0.1 --port 8080
    
  4. Run tests (prefer targeted files—no wildcards):
    C:/Users/Matt/mtg_python/mtg_python_deckbuilder/.venv/Scripts/python.exe -m pytest -q code/tests/test_random_determinism.py code/tests/test_permalinks_and_locks.py
    
    Use tasks.json entries such as pytest-fast-random or pytest-fast-locks for quick feedback.
  5. Linting and type checks follow pyproject.toml / mypy.ini defaults. Keep changes minimal and well-typed.

When adding features, favor the web UI first, keep public builder APIs stable, and update documentation (CHANGELOG → RELEASE_NOTES_TEMPLATE → DOCKER → README) in that order.


Troubleshooting

  • Blank page after start: Visit /healthz, check /logs, ensure SHOW_LOGS=1, and inspect host logs/ for stack traces.
  • Stale data: Run Initial Setup or delete csv_files/.tagging_complete.json to force reseeding.
  • Owned-only build fails: Confirm owned files were uploaded correctly and that owned_cards/ is mounted.
  • Random build stalls: Lower RANDOM_MAX_ATTEMPTS, increase RANDOM_TIMEOUT_MS, and verify selected themes exist via /themes/.
  • Commander list outdated: Rerun the commander refresh script or Initial Setup.

Contributing

Pull requests are welcome—follow the conventional commit style, keep diffs focused, add or update tests when behavior changes, and document new env vars or workflows. Review CONTRIBUTING_EDITORIAL.md for editorial tooling guidance.


License & attribution

Licensed under the MIT License. Card data and imagery are provided by Scryfall; please respect their API terms.


Further reading