chore(rebar): Pier_T_rebar 작업 폴더 초기 구성 및 외부 의존 파일 통합
- 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>
This commit is contained in:
commit
964a841de1
3978 files changed
+58013356
No files matched your search
@@ -0,0 +1,108 @@
|
||||
"""Example run of the Ungok P1 pier through the running web app (HTTP, so progress shows in the browser).
|
||||
|
||||
Steps: upload the 3 DWG (copies of the RAG originals) -> check table candidates -> confirm the required
|
||||
sections -> run and approve G1..G5 -> make the review report draft.
|
||||
Confirmations and approvals are recorded under the name "예제실행(Agent)" so they are not mistaken for a
|
||||
person's review. Standard library only (urllib).
|
||||
usage: python run_example.py [pier name] [--base http://127.0.0.1:5000]
|
||||
"""
|
||||
import argparse
|
||||
import json
|
||||
import re
|
||||
import sys
|
||||
import time
|
||||
import urllib.parse
|
||||
import urllib.request
|
||||
import uuid
|
||||
from pathlib import Path
|
||||
|
||||
APP = Path(__file__).resolve().parents[1]
|
||||
ROOT = APP.parents[1]
|
||||
sys.path.insert(0, str(APP / "web"))
|
||||
import store # noqa: E402 (read-only use: section lists, job files)
|
||||
|
||||
BY = "예제실행(Agent)"
|
||||
|
||||
|
||||
class NoRedirect(urllib.request.HTTPRedirectHandler):
|
||||
def redirect_request(self, *a, **k):
|
||||
return None
|
||||
|
||||
|
||||
OPENER = urllib.request.build_opener(NoRedirect)
|
||||
|
||||
|
||||
def call(base, path, data=None, files=None):
|
||||
url = base + urllib.parse.quote(path)
|
||||
if files:
|
||||
b = uuid.uuid4().hex
|
||||
body = b""
|
||||
for name, (fname, content) in files:
|
||||
body += (f"--{b}\r\nContent-Disposition: form-data; name=\"{name}\"; filename=\"{fname}\"\r\n"
|
||||
f"Content-Type: application/octet-stream\r\n\r\n").encode("utf-8") + content + b"\r\n"
|
||||
body += f"--{b}--\r\n".encode()
|
||||
req = urllib.request.Request(url, body, {"Content-Type": f"multipart/form-data; boundary={b}"})
|
||||
elif data is not None:
|
||||
req = urllib.request.Request(url, urllib.parse.urlencode(data, doseq=True).encode("utf-8"))
|
||||
else:
|
||||
req = urllib.request.Request(url)
|
||||
try:
|
||||
with OPENER.open(req, timeout=600) as r:
|
||||
return r.status, r.read().decode("utf-8")
|
||||
except urllib.error.HTTPError as e: # 302 redirects after POST are expected
|
||||
return e.code, ""
|
||||
|
||||
|
||||
def wait_job(pier, gate, limit=1800):
|
||||
jid = (pier.gate_status(gate)["job"] or {}).get("id")
|
||||
t0 = time.time()
|
||||
while time.time() - t0 < limit:
|
||||
job = pier.gate_status(gate)["job"] or {}
|
||||
if job.get("id") == jid and job.get("state") in ("완료", "오류"):
|
||||
return job
|
||||
time.sleep(5)
|
||||
return None
|
||||
|
||||
|
||||
def main(name, base):
|
||||
pier = store.Pier(name)
|
||||
print(f"[교각] {name} ({base}/pier/{name})", flush=True)
|
||||
dwgs = sorted((ROOT / "RAG").glob("C0050205-02*-운곡교 교각도*.dwg"))
|
||||
st, _ = call(base, f"/pier/{name}/upload", files=[("dwg", (p.name, p.read_bytes())) for p in dwgs])
|
||||
print(f"[도면] {len(dwgs)}장 업로드 → 문자 {len(pier.texts())}개 색인 (HTTP {st})", flush=True)
|
||||
_, html = call(base, f"/pier/{name}/candidates")
|
||||
m = re.search(r"후보값 비교 \((\d+)건 · 다름 (\d+)건\)", html)
|
||||
print(f"[표 후보값] 비교 {m.group(1)}건, 다름 {m.group(2)}건" if m else "[표 후보값] 표를 찾지 못함", flush=True)
|
||||
need = sorted({(r["file"], r["key"]) for g in store.GATE_SECTIONS for r in pier.gate_requirements(g)})
|
||||
for f, k in need:
|
||||
call(base, f"/pier/{name}/section/{f}/{k}/confirm", {"by": BY})
|
||||
ok = all(r["confirmed"] for g in store.GATE_SECTIONS for r in pier.gate_requirements(g))
|
||||
print(f"[확정] 필요한 묶음 {len(need)}개 확정 ({'완료' if ok else '미완료'}, 확정자 {BY})", flush=True)
|
||||
for g in ("G1", "G2", "G3", "G4", "G5"):
|
||||
t0 = time.time()
|
||||
call(base, f"/pier/{name}/gate/{g}/run", {})
|
||||
job = wait_job(pier, g)
|
||||
rep = pier.gate_status(g)["report"] or {}
|
||||
if not job or job["state"] != "완료" or rep.get("verdict") != "PASS":
|
||||
print(f"[{g}] 중단: {job and job['state']} {rep.get('verdict')} {(job or {}).get('error', '')[-300:]}")
|
||||
return 1
|
||||
s = rep["summary"]
|
||||
call(base, f"/pier/{name}/gate/{g}/decide", {"by": BY, "decision": "승인", "note": "예제 실행"})
|
||||
print(f"[{g}] PASS {s['passed']}/{s['total']} (계획 FAIL {len(rep['planned_fails'])}) · "
|
||||
f"음성 {'/'.join('검출' if v['detected'] else '미검출' for v in rep['negative'].values())} · "
|
||||
f"재로드 {'일치' if rep['reload']['ok'] else '불일치'} · 승인 {pier.gate_status(g)['approved']} · "
|
||||
f"{time.time() - t0:.0f}초", flush=True)
|
||||
call(base, f"/pier/{name}/report", {"by": BY})
|
||||
chk = json.loads((pier.run_dir / "report_check.json").read_text(encoding="utf-8"))
|
||||
files = sorted(p.name for p in pier.run_dir.glob("보고서_*"))
|
||||
print(f"[보고서] {files} · 자체 점검 {'통과' if chk['ok'] else '미통과'}", flush=True)
|
||||
print(f"[모델] {pier.run_dir / 'G5.3dm'}")
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
ap = argparse.ArgumentParser()
|
||||
ap.add_argument("name", nargs="?", default="운곡교P1")
|
||||
ap.add_argument("--base", default="http://127.0.0.1:5000")
|
||||
a = ap.parse_args()
|
||||
sys.exit(main(a.name, a.base))
|
||||
Reference in new issue
Block a user