"""Apply the grade-A anchor candidates of a pier, one file at a time (2026-10-19), through the app's own route so that the P7 rule, the anchor log and the re-confirmation rule apply: first every differing A candidate of expected, then the differing A candidates of params whose drawing text was NOT used for expected (the same text may not feed both files). params / expected are backed up first. usage: python tools/apply_anchor_a.py [--dry]""" import shutil import sys import time from pathlib import Path APP = Path(__file__).resolve().parents[1] sys.path.insert(0, str(APP / "web")) sys.path.insert(0, str(APP)) import app as webapp # noqa: E402 import store # noqa: E402 from engine import verify # noqa: E402 name = sys.argv[1] dry = "--dry" in sys.argv p = store.Pier(name) c = webapp.app.test_client() def a_rows(): rows, _m, _meta = p.anchor_rows() return [r for r in rows if r["grade"] == "A" and not r["same"] and not r["self_source"]] def count(): a = p.run_autocheck(force=True) req = {f"{f}:{k}" for f, k in webapp.required(p)} return verify.counts(a["sections"], req), a before, a0 = count() print(name, "before:", {verify.ST_NAME[k]: v for k, v in before.items()}) ex = [r for r in a_rows() if r["file"] == "expected"] used = {(r["text"], r["x"], r["y"]) for r in ex} print(f"expected side: {len(ex)} A candidates;", end=" ") pa = [r for r in a_rows() if r["file"] == "params" and (r["text"], r["x"], r["y"]) not in used] print(f"params side (texts not used for expected): {len(pa)} of {len([r for r in a_rows() if r['file'] == 'params'])}") if dry: sys.exit(0) bk = p.dir / "backup" / f"before_anchor_A_{time.strftime('%Y%m%d_%H%M%S')}" bk.mkdir(parents=True) for f in ("params.json", "expected.json", "confirm.json"): if (p.dir / f).exists(): shutil.copy2(p.dir / f, bk / f) for label, rows in (("expected", ex), ("params", pa)): if not rows: continue r = c.post(f"/pier/{name}/anchors/apply", data={"field": [x["field"] for x in rows]}, follow_redirects=True) txt = r.get_data(as_text=True) msg = "반영했습니다" in txt print(f" apply {label}: {'ok' if msg else 'REFUSED'} ({len(rows)} fields)") if not msg: sys.exit(1) after, a1 = count() print(name, "after: ", {verify.ST_NAME[k]: v for k, v in after.items()}) bad = sorted(b for b, r in a1["sections"].items() if r["st"] == "b") print(" still 불일치:", bad) print(" backup:", bk)