"""Import qualitative relations from a Chinese draft spreadsheet. Usage: python import_relations_xlsx.py python import_relations_xlsx.py "C:\\Users\\Administrator\\Downloads\\dota2.xlsx" Writes data/relations.json. Skips category phrases (辅助/幻想系/...). """ from __future__ import annotations import sys from pathlib import Path sys.path.insert(0, str(Path(__file__).resolve().parents[1])) import argparse import json from datetime import datetime, timezone from shared.paths import SHARED_DATA from shared.relations import ( DEFAULT_RELATIONS, build_name_index, load_relations, resolve_name, save_relations, set_counter, set_synergy, split_names, ) # Phrases that are roles/tags, not single heroes. SKIP_TOKENS = { "辅助", "推进", "幻想系", "幻想", "所有肉核", "肉核", "高爆发", "奶妈", "核心", } def _should_skip(token: str) -> bool: t = token.strip() if not t: return True if t in SKIP_TOKENS: return True if "系" in t and t.endswith("系"): return True if t.startswith("所有"): return True if "魔晶" in t or "a仗" in t.lower(): # item/facet interactions in hero columns — skip for edge seed return True return False def import_xlsx(path: Path) -> tuple[dict, list[str]]: import openpyxl wb = openpyxl.load_workbook(path, data_only=True) index = build_name_index() data = load_relations() data["meta"] = { "source": str(path), "imported_at": datetime.now(timezone.utc).isoformat(), "note": "qualitative counters/synergies; no winrate", } unresolved: list[str] = [] sheets = ["力量", "敏捷", "智力", "全才"] def resolve_list(cell, context: str) -> list[str]: keys = [] for tok in split_names(cell): if _should_skip(tok): unresolved.append(f"{context}: skip tag '{tok}'") continue key = resolve_name(tok, index) if key is None: unresolved.append(f"{context}: unresolved '{tok}'") continue keys.append(key) return keys for sheet in sheets: if sheet not in wb.sheetnames: continue ws = wb[sheet] rows = list(ws.iter_rows(values_only=True)) # find header header_i = None for i, row in enumerate(rows): if row and row[0] == "英雄": header_i = i break if header_i is None: continue for row in rows[header_i + 1 :]: if not row or not row[0]: continue hero_raw = str(row[0]).strip() hero = resolve_name(hero_raw, index) if hero is None: unresolved.append(f"{sheet}: hero unresolved '{hero_raw}'") continue # col1 被克制, col2 克制, col3 搭档 countered_by = resolve_list(row[1] if len(row) > 1 else None, f"{hero_raw}/被克") counters = resolve_list(row[2] if len(row) > 2 else None, f"{hero_raw}/克制") partners = resolve_list(row[3] if len(row) > 3 else None, f"{hero_raw}/搭档") for other in counters: set_counter(data, hero, other, reason="") for other in countered_by: # other counters hero set_counter(data, other, hero, reason="") for other in partners: set_synergy(data, hero, other, reason="") return data, unresolved def main() -> None: ap = argparse.ArgumentParser() ap.add_argument( "xlsx", nargs="?", default=str(Path.home() / "Downloads" / "dota2.xlsx"), help="path to dota2.xlsx", ) ap.add_argument("--out", default=str(DEFAULT_RELATIONS)) args = ap.parse_args() path = Path(args.xlsx) if not path.is_file(): raise SystemExit(f"file not found: {path}") data, unresolved = import_xlsx(path) out = save_relations(data, args.out) print(f"wrote {out}") print(f"counters={len(data['counters'])} synergies={len(data['synergies'])}") report = SHARED_DATA / "relations_import_report.json" report.write_text( json.dumps({"unresolved": unresolved, "count": len(unresolved)}, ensure_ascii=False, indent=2) + "\n", encoding="utf-8", ) print(f"unresolved notes: {len(unresolved)} → {report}") for line in unresolved[:40]: print(" ", line) if len(unresolved) > 40: print(f" ... +{len(unresolved) - 40} more") if __name__ == "__main__": main()