- Code\rebar_pipeline, review_pipeline, pier-program을 정션에서 실제 폴더로 전환 - Code\rhino_mcp 및 CLAUDE_rhino배경.md 추가, .mcp.json은 PYTHONPATH로 새 위치 사용 - pier-program 내부 .git 제거(이력은 backup\pier-program_20261028.bundle에 보관) - worktree 가이드 반영(CLAUDE.local.md, HANDOFF.md) - D:\Rhino-MCP 경로 참조를 새 위치로 치환 Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
62 lines
2.4 KiB
Python
62 lines
2.4 KiB
Python
"""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 <pier> [--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)
|