mirror of
https://github.com/mwisnowski/mtg_python_deckbuilder.git
synced 2025-09-22 04:50:46 +02:00
Add text file export along with csv, for easy import into moxfield or other deck building sites
This commit is contained in:
parent
760c36d75d
commit
d9b56d8e12
2 changed files with 98 additions and 1 deletions
|
@ -205,7 +205,15 @@ class DeckBuilder(CommanderSelectionMixin,
|
||||||
# Export
|
# Export
|
||||||
if hasattr(self, 'export_decklist_csv'):
|
if hasattr(self, 'export_decklist_csv'):
|
||||||
logger.info("Export decklist phase")
|
logger.info("Export decklist phase")
|
||||||
self.export_decklist_csv()
|
csv_path = self.export_decklist_csv()
|
||||||
|
# Also emit plaintext list (.txt) for quick copy/paste
|
||||||
|
try:
|
||||||
|
# Derive matching stem by replacing extension from csv_path
|
||||||
|
import os as _os
|
||||||
|
base, _ext = _os.path.splitext(_os.path.basename(csv_path))
|
||||||
|
self.export_decklist_text(filename=base + '.txt') # type: ignore[attr-defined]
|
||||||
|
except Exception:
|
||||||
|
logger.warning("Plaintext export failed (non-fatal)")
|
||||||
end_ts = datetime.datetime.now()
|
end_ts = datetime.datetime.now()
|
||||||
logger.info(f"=== Deck Build: COMPLETE in {(end_ts - start_ts).total_seconds():.2f}s ===")
|
logger.info(f"=== Deck Build: COMPLETE in {(end_ts - start_ts).total_seconds():.2f}s ===")
|
||||||
except KeyboardInterrupt:
|
except KeyboardInterrupt:
|
||||||
|
|
|
@ -190,8 +190,97 @@ class ReportingMixin:
|
||||||
w.writerow(data_row)
|
w.writerow(data_row)
|
||||||
|
|
||||||
self.output_func(f"Deck exported to {fname}")
|
self.output_func(f"Deck exported to {fname}")
|
||||||
|
# Auto-generate matching plaintext list (best-effort; ignore failures)
|
||||||
|
try: # pragma: no cover - sidecar convenience
|
||||||
|
stem = os.path.splitext(os.path.basename(fname))[0]
|
||||||
|
# Always overwrite sidecar to reflect latest deck state
|
||||||
|
self.export_decklist_text(directory=directory, filename=stem + '.txt') # type: ignore[attr-defined]
|
||||||
|
except Exception:
|
||||||
|
logger.warning("Plaintext sidecar export failed (non-fatal)")
|
||||||
return fname
|
return fname
|
||||||
|
|
||||||
|
def export_decklist_text(self, directory: str = 'deck_files', filename: str | None = None) -> str:
|
||||||
|
"""Export a simple plaintext list: one line per unique card -> "[Count] [Card Name]".
|
||||||
|
|
||||||
|
Naming mirrors CSV export (same stem, .txt extension). Sorting follows same
|
||||||
|
category precedence then alphabetical within category for consistency.
|
||||||
|
"""
|
||||||
|
os.makedirs(directory, exist_ok=True)
|
||||||
|
# Derive base filename logic (shared with CSV exporter) – intentionally duplicated to avoid refactor risk.
|
||||||
|
if filename is None:
|
||||||
|
cmdr = getattr(self, 'commander_name', '') or getattr(self, 'commander', '') or ''
|
||||||
|
cmdr_first = cmdr.split()[0] if cmdr else 'deck'
|
||||||
|
theme = getattr(self, 'primary_tag', None) or (self.selected_tags[0] if getattr(self, 'selected_tags', []) else None)
|
||||||
|
theme_first = str(theme).split()[0] if theme else 'notheme'
|
||||||
|
def _slug(s: str) -> str:
|
||||||
|
s2 = _re.sub(r'[^A-Za-z0-9_]+', '', s)
|
||||||
|
return s2 or 'x'
|
||||||
|
cmdr_slug = _slug(cmdr_first)
|
||||||
|
theme_slug = _slug(theme_first)
|
||||||
|
date_part = _dt.date.today().strftime('%Y%m%d')
|
||||||
|
filename = f"{cmdr_slug}_{theme_slug}_{date_part}.txt"
|
||||||
|
if not filename.lower().endswith('.txt'):
|
||||||
|
filename = filename + '.txt'
|
||||||
|
path = os.path.join(directory, filename)
|
||||||
|
|
||||||
|
# Sorting reproduction
|
||||||
|
precedence_order = [
|
||||||
|
'Commander', 'Battle', 'Planeswalker', 'Creature', 'Instant', 'Sorcery', 'Artifact', 'Enchantment', 'Land'
|
||||||
|
]
|
||||||
|
precedence_index = {k: i for i, k in enumerate(precedence_order)}
|
||||||
|
commander_name = getattr(self, 'commander_name', '') or getattr(self, 'commander', '') or ''
|
||||||
|
def classify(primary_type_line: str, card_name: str) -> str:
|
||||||
|
if commander_name and card_name == commander_name:
|
||||||
|
return 'Commander'
|
||||||
|
tl = (primary_type_line or '').lower()
|
||||||
|
if 'battle' in tl:
|
||||||
|
return 'Battle'
|
||||||
|
if 'planeswalker' in tl:
|
||||||
|
return 'Planeswalker'
|
||||||
|
if 'creature' in tl:
|
||||||
|
return 'Creature'
|
||||||
|
if 'instant' in tl:
|
||||||
|
return 'Instant'
|
||||||
|
if 'sorcery' in tl:
|
||||||
|
return 'Sorcery'
|
||||||
|
if 'artifact' in tl:
|
||||||
|
return 'Artifact'
|
||||||
|
if 'enchantment' in tl:
|
||||||
|
return 'Enchantment'
|
||||||
|
if 'land' in tl:
|
||||||
|
return 'Land'
|
||||||
|
return 'ZZZ'
|
||||||
|
|
||||||
|
# We may want enriched type lines from snapshot; build quick lookup
|
||||||
|
full_df = getattr(self, '_full_cards_df', None)
|
||||||
|
combined_df = getattr(self, '_combined_cards_df', None)
|
||||||
|
snapshot = full_df if full_df is not None else combined_df
|
||||||
|
row_lookup: Dict[str, any] = {}
|
||||||
|
if snapshot is not None and not snapshot.empty and 'name' in snapshot.columns:
|
||||||
|
for _, r in snapshot.iterrows():
|
||||||
|
nm = str(r.get('name'))
|
||||||
|
if nm not in row_lookup:
|
||||||
|
row_lookup[nm] = r
|
||||||
|
|
||||||
|
sortable: List[tuple] = []
|
||||||
|
for name, info in self.card_library.items():
|
||||||
|
base_type = info.get('Card Type') or info.get('Type','')
|
||||||
|
row = row_lookup.get(name)
|
||||||
|
if row is not None:
|
||||||
|
row_type = row.get('type', row.get('type_line', ''))
|
||||||
|
if row_type:
|
||||||
|
base_type = row_type
|
||||||
|
cat = classify(base_type, name)
|
||||||
|
prec = precedence_index.get(cat, 999)
|
||||||
|
sortable.append(((prec, name.lower()), name, info.get('Count',1)))
|
||||||
|
sortable.sort(key=lambda x: x[0])
|
||||||
|
|
||||||
|
with open(path, 'w', encoding='utf-8') as f:
|
||||||
|
for _, name, count in sortable:
|
||||||
|
f.write(f"{count} {name}\n")
|
||||||
|
self.output_func(f"Plaintext deck list exported to {path}")
|
||||||
|
return path
|
||||||
|
|
||||||
def print_card_library(self, table: bool = True): # noqa: C901
|
def print_card_library(self, table: bool = True): # noqa: C901
|
||||||
if table and PrettyTable is None:
|
if table and PrettyTable is None:
|
||||||
table = False
|
table = False
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue