- 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>
166 lines
8.1 KiB
Python
166 lines
8.1 KiB
Python
"""운곡교 '대조표_C1-C16_도면모델_분류_V2.xlsx' 형식(분류 안내 / ① 도면↔모델 불일치(+캡처) / ② 전체 대조표 / ③ 미검토 / ④ 측정 근거)으로
|
||
교량별 검토 엑셀을 만든다. 사용: python -I make_review_xlsx.py <cfg.json> <out.xlsx>
|
||
cfg: bridge, title, purpose, legend_counts(자동), items[{code,mark,content,cmp,diff,drawing_side,action,kind,image}],
|
||
rows[{no,mark,item,table,detail,model,verdict,note,kind}], unchecked[{item,content,note,status}], basis[{item,content,note}]
|
||
도면 내부 불일치는 이 엑셀에 넣지 않는다(별도 보관 파일).
|
||
"""
|
||
import sys, json, os
|
||
sys.path.insert(0, r"C:\Users\User\AppData\Local\Temp\claude\D--Rhino-MCP-rhino-mcp-main\f46b47bc-9105-4079-9b62-fcbee31b26ae\scratchpad\pylib")
|
||
from openpyxl import Workbook
|
||
from openpyxl.styles import Font, PatternFill, Alignment, Border, Side
|
||
from openpyxl.drawing.image import Image as XImage
|
||
from openpyxl.utils import get_column_letter
|
||
from PIL import Image as PImage
|
||
|
||
cfg = json.load(open(sys.argv[1], encoding="utf-8"))
|
||
OUT = sys.argv[2]
|
||
thin = Side(style="thin", color="999999")
|
||
BR = Border(left=thin, right=thin, top=thin, bottom=thin)
|
||
HF = PatternFill("solid", fgColor="D9D9D9")
|
||
FILL = {"도면↔모델": PatternFill("solid", fgColor="F8CBAD"), "기준↔모델": PatternFill("solid", fgColor="E6C84A"),
|
||
"도면 내부": PatternFill("solid", fgColor="FFF2CC"), "일치": PatternFill("solid", fgColor="E2F0D9"),
|
||
"미확인": PatternFill("solid", fgColor="D9D9D9")}
|
||
TOPW = Alignment(wrap_text=True, vertical="top")
|
||
CEN = Alignment(wrap_text=True, vertical="center", horizontal="center")
|
||
|
||
wb = Workbook()
|
||
# ------------------------------------------------ 분류 안내
|
||
ws = wb.active
|
||
ws.title = "분류 안내"
|
||
ws.append([cfg["title"]])
|
||
ws["A1"].font = Font(bold=True, size=14)
|
||
ws.append([cfg["purpose"]])
|
||
ws["A2"].alignment = TOPW
|
||
ws.merge_cells("A2:E2")
|
||
ws.row_dimensions[2].height = 64
|
||
ws.append([])
|
||
ws.append(["색", "구분", "뜻", "항목 수", "볼 시트"])
|
||
for c in range(1, 6):
|
||
ws.cell(row=4, column=c).font = Font(bold=True)
|
||
ws.cell(row=4, column=c).fill = HF
|
||
ws.cell(row=4, column=c).border = BR
|
||
cnt = {}
|
||
for r in cfg["rows"]:
|
||
cnt[r["kind"]] = cnt.get(r["kind"], 0) + 1
|
||
legend = [("주황", "도면↔모델", "도면에는 정보가 있거나 없는데 모델이 다르게 만들어졌거나 가정한 것 (모델 확인·수정 대상)", "① 도면↔모델 불일치"),
|
||
("진한 노랑", "기준↔모델", "설계기준(KDS 24 14 21)에 모델이 맞는지 (도면에는 정보 없음)", "① 도면↔모델 불일치"),
|
||
("초록", "일치", "도면(재료표·철근상세)과 모델이 일치", "② 전체 대조표"),
|
||
("회색", "미확인", "도면에 정보가 없어 확인하지 못한 것", "② 전체 대조표 · ③")]
|
||
for col, kind, mean, sheet in legend:
|
||
n = len([i for i in cfg["items"] if i["kind"] == kind]) if kind in ("도면↔모델", "기준↔모델") else cnt.get(kind, 0)
|
||
ws.append([col, kind, mean, str(n), sheet])
|
||
r = ws.max_row
|
||
ws.cell(row=r, column=2).fill = FILL[kind]
|
||
for c in range(1, 6):
|
||
ws.cell(row=r, column=c).border = BR
|
||
ws.cell(row=r, column=c).alignment = TOPW
|
||
ws.row_dimensions[r].height = 40
|
||
ws.append([])
|
||
ws.append(["※ 도면 내부 불일치(도면끼리 다른 것)는 이 파일에 넣지 않고 '도면내부_오류_보관.md' 에 따로 보관 — 요청 시 보고."])
|
||
ws.append(["※ ② 전체 대조표의 구분 열은 마크 행 수 기준, ① 의 항목 수는 코드(M-n) 수 기준."])
|
||
for i, w in enumerate([12, 14, 80, 10, 26], 1):
|
||
ws.column_dimensions[get_column_letter(i)].width = w
|
||
|
||
# ------------------------------------------------ ① 도면↔모델 불일치
|
||
w1 = wb.create_sheet("① 도면↔모델 불일치")
|
||
w1.append(["① 도면 ↔ 모델 불일치 (최우선 확인) — 주황 / 진한 노랑 / 회색"])
|
||
w1["A1"].font = Font(bold=True, size=13)
|
||
w1.append([cfg["sheet1_desc"]])
|
||
w1["A2"].alignment = TOPW
|
||
w1.merge_cells("A2:I2")
|
||
w1.row_dimensions[2].height = 48
|
||
w1.append([])
|
||
heads = ["번호", "마크", "내용", "비교", "차이 크기", "도면 쪽 상태", "확인·조치 필요 사항", "구분", "캡처 (왼쪽·위 도면 / 오른쪽·아래 모델)"]
|
||
w1.append(heads)
|
||
for c in range(1, 10):
|
||
cell = w1.cell(row=4, column=c)
|
||
cell.font = Font(bold=True)
|
||
cell.fill = HF
|
||
cell.border = BR
|
||
cell.alignment = CEN
|
||
w1.row_dimensions[4].height = 30
|
||
MAXH = 520
|
||
for it in cfg["items"]:
|
||
w1.append([it["code"], it["mark"], it["content"], it["cmp"], it["diff"], it["drawing_side"], it["action"], it["kind"], None])
|
||
r = w1.max_row
|
||
for c in range(1, 9):
|
||
cell = w1.cell(row=r, column=c)
|
||
cell.border = BR
|
||
cell.alignment = TOPW
|
||
w1.cell(row=r, column=8).fill = FILL[it["kind"]]
|
||
w1.cell(row=r, column=1).font = Font(bold=True)
|
||
if it.get("image") and os.path.exists(it["image"]):
|
||
im = PImage.open(it["image"])
|
||
h = MAXH
|
||
w = int(im.width * h / im.height)
|
||
xi = XImage(it["image"])
|
||
xi.width, xi.height = w, h
|
||
w1.add_image(xi, "I%d" % r)
|
||
w1.row_dimensions[r].height = h * 0.75 + 6
|
||
w1.column_dimensions["I"].width = max(w1.column_dimensions["I"].width or 0, w / 7.0)
|
||
w1.cell(row=r, column=9).border = BR
|
||
for i, w in enumerate([8, 12, 62, 24, 26, 34, 52, 12], 1):
|
||
w1.column_dimensions[get_column_letter(i)].width = w
|
||
w1.freeze_panes = "A5"
|
||
|
||
# ------------------------------------------------ ② 전체 대조표
|
||
w2 = wb.create_sheet("② 전체 대조표")
|
||
w2.append(["② 도면(재료표·철근상세) · 라이노 모델 대조표 — 전 마크"])
|
||
w2["A1"].font = Font(bold=True, size=13)
|
||
w2.append(["구분 열 색: 초록=일치 / 주황=도면↔모델 / 진한 노랑=기준↔모델 / 회색=미확인. 모델 치수는 중심선 꼭짓점 사이 거리(각진 치수), 길이는 모델 철근의 설계길이(user string)."])
|
||
w2.append([])
|
||
h2 = ["번호", "마크", "검토 항목", "도면 재료표 (L × N = 총길이)", "도면 철근상세 (치수)", "라이노 모델", "판정", "비고 / 근거", "구분"]
|
||
w2.append(h2)
|
||
for c in range(1, 10):
|
||
cell = w2.cell(row=4, column=c)
|
||
cell.font = Font(bold=True)
|
||
cell.fill = HF
|
||
cell.border = BR
|
||
cell.alignment = CEN
|
||
for r_ in cfg["rows"]:
|
||
w2.append([r_["no"], r_["mark"], r_["item"], r_["table"], r_["detail"], r_["model"], r_["verdict"], r_["note"], r_["kind"]])
|
||
r = w2.max_row
|
||
for c in range(1, 10):
|
||
cell = w2.cell(row=r, column=c)
|
||
cell.border = BR
|
||
cell.alignment = TOPW
|
||
w2.cell(row=r, column=9).fill = FILL[r_["kind"]]
|
||
n = max(len(str(r_["note"])) / 40.0, len(str(r_["model"])) / 34.0, len(str(r_["detail"])) / 30.0, 1)
|
||
w2.row_dimensions[r].height = max(32, 17 * (int(n) + 1))
|
||
for i, w in enumerate([6, 9, 26, 30, 32, 38, 14, 52, 12], 1):
|
||
w2.column_dimensions[get_column_letter(i)].width = w
|
||
w2.freeze_panes = "C5"
|
||
|
||
# ------------------------------------------------ ③ 미검토
|
||
w3 = wb.create_sheet("③ 미검토·확인 불가")
|
||
w3.append(["항목", "내용", "비고", "상태"])
|
||
for c in range(1, 5):
|
||
w3.cell(row=1, column=c).font = Font(bold=True)
|
||
w3.cell(row=1, column=c).fill = HF
|
||
for u in cfg["unchecked"]:
|
||
w3.append([u["item"], u["content"], u["note"], u["status"]])
|
||
for c in range(1, 5):
|
||
w3.cell(row=w3.max_row, column=c).alignment = TOPW
|
||
w3.row_dimensions[w3.max_row].height = 50
|
||
for i, w in enumerate([22, 80, 40, 12], 1):
|
||
w3.column_dimensions[get_column_letter(i)].width = w
|
||
|
||
# ------------------------------------------------ ④ 측정 근거
|
||
w4 = wb.create_sheet("④ 측정 근거")
|
||
w4.append(["항목", "측정/내용", "비고"])
|
||
for c in range(1, 4):
|
||
w4.cell(row=1, column=c).font = Font(bold=True)
|
||
w4.cell(row=1, column=c).fill = HF
|
||
for b in cfg["basis"]:
|
||
w4.append([b["item"], b["content"], b.get("note", "")])
|
||
for c in range(1, 4):
|
||
w4.cell(row=w4.max_row, column=c).alignment = TOPW
|
||
w4.row_dimensions[w4.max_row].height = 48
|
||
for i, w in enumerate([30, 95, 52], 1):
|
||
w4.column_dimensions[get_column_letter(i)].width = w
|
||
try:
|
||
wb.save(OUT)
|
||
print("saved", OUT)
|
||
except PermissionError:
|
||
print("열려 있어 저장 실패:", OUT)
|