"""Ramp-A교 P3-2(ㄷ자철근 형상 오류) 수정을 엑셀에 반영한다 (2026-10-26). - 교량별 항목 합본 / 개별 항목: Ramp-A M-8 (P3-2) 행 추가 - 수정 내역 R-16, 수정 증거(마크별) 행, 수정 파일·해시(수정4 최신, 수정3 중간본) - 공통 문제 상세(이미지): 맨 아래 새 행에 수정 전(F열)·수정 후(M열) 이미지와 수정 내용(I열) 같은 R-16 이 이미 있으면 중단(이중 적용 방지). """ import copy import datetime import hashlib import json import sys from pathlib import Path import openpyxl from openpyxl.drawing.image import Image as XLImage from openpyxl.styles import Alignment, PatternFill ROOT = Path(r"D:\★Pier 자료\프로그램\Pier_T") REV = ROOT / "검토파일" IMG = REV / "수정후_캡처" XLSX = REV / "4개교_공통사항_종합.xlsx" fx = json.loads((ROOT / "Code" / "검토수정" / "결과" / "c1_p32_fix.json").read_text(encoding="utf-8")) smp = fx["samples"][0] wb = openpyxl.load_workbook(XLSX) ws = wb["수정 내역"] if any(r[0].value == "R-16" for r in ws.iter_rows(min_row=4)): sys.exit("R-16 이 이미 있음 - 중단") GREEN = PatternFill("solid", fgColor="FFE2EFDA") def clone(src, dst): dst.font = copy.copy(src.font) dst.fill = copy.copy(src.fill) dst.border = copy.copy(src.border) dst.alignment = copy.copy(src.alignment) dst.number_format = src.number_format def append_like(sheet, values, like_row): r = sheet.max_row + 1 for j, v in enumerate(values, 1): c = sheet.cell(row=r, column=j, value=v) clone(sheet.cell(row=like_row, column=j), c) sheet.auto_filter.ref = "A3:%s%d" % (openpyxl.utils.get_column_letter(sheet.max_column), r) return r CONTENT = ("Ramp-A교 P3-2(H16, 30본)는 도면(4) '철근망 최하단 저부철근 상세도'에서 바닥 수평 + 다리 수직 ㄷ자(다리 200 은 P1-2 에 용접)인데, " "모델은 다리 위쪽 184 만 수직이고 그 아래 459 가 약 13.4° 안쪽으로 기울며 모서리가 R135 두 호(13.44° + 76.56°)로 이어진 깔때기 형상임") DIFF = "다리 약 13.4° 기울어짐, 바닥 폭이 다리 위쪽 간격보다 좁음, 중심선 길이 3,276.3 (재료표 L 3,342)" FIXED = ("수정 완료 (2026-10-26): 바닥 수평 + 다리 수직 ㄷ자로 다시 만듦 — 바닥 폭 2,050, 다리 높이 646 (W + 2h = 재료표 L 3,342), 모서리 R24 " "(도면에 반지름 없어 KDS H16 최소 적용), 중심선 길이 3,321.4. 파일: ramp-A G5_수정4_20261026.3dm. '수정 내역' R-16") # 합본 wg = wb["교량별 항목 합본"] like = next(r[0].row for r in wg.iter_rows(min_row=4) if r[8].value == "도면↔모델") r = append_like(wg, ["Ramp-A교", "M-8", "P3-2", CONTENT, "도면(4) 저부철근 상세도 ↔ 모델 형상", DIFF, "바닥 수평 + 다리 수직 ㄷ자, 다리 200 이 P1-2 에 용접 표기 / 재료표 P3-2 H16 L 3.342 N 30", FIXED, "도면↔모델 (수정 완료)", "개별"], like) # 개별 항목 wi = wb["개별 항목"] like_i = next(r[0].row for r in wi.iter_rows(min_row=4) if r[5].value == "도면↔모델") append_like(wi, ["Ramp-A교", "M-8", "P3-2", CONTENT, DIFF, "도면↔모델 (수정 완료)", FIXED], like_i) # 수정 내역 / 증거 / 파일 append_like(ws, ["R-16", "Ramp-A교", "ㄷ자철근 형상 오류 (P3-2)", "P3-2 (H16)", "다리 위 184 수직 + 아래 459 약 13.4° 기울음 + 모서리 R135 두 호 / 바닥 폭 약 1,554+호 / 중심선 길이 %.1f" % smp["old_len"], "바닥 수평 + 다리 수직 ㄷ자: 바닥 폭 %.0f, 다리 높이 %.0f, 모서리 R24 / 중심선 길이 %.1f" % (smp["W"], smp["h"], smp["new_len"]), fx["stat"]["done"], "사용자 지적 2026-10-26: P3-2 는 그냥 ㄷ자 철근. 도면 저부철근 상세도 형상 + 재료표 L 3,342 (직선 치수 합 W + 2h), 모서리 R 은 도면에 없어 KDS H16 최소", "ramp-A G5_수정4_20261026.3dm", "저장 직전 문서 확인: 30본 모두 재작성, 객체 수 %d → %d 동일, 파이프 부피 ±0.5%% 이내. 수정 전후 Rhino 캡처를 '공통 문제 상세(이미지)' 시트 맨 아래에 추가" % (fx["objects_before"], fx["objects_after"]), "다리 위치·평면 각도는 모델 값 유지. 다리 높이는 L 에서 역산(646)이라 다리 200 용접 구간 포함 여부는 설계자 확인"], 4) for row in ws.iter_rows(min_row=4): if row[0].value == "R-16": row[0].fill = GREEN ev = wb["수정 증거(마크별)"] append_like(ev, ["Ramp-A교", "P3-2", "H16", "ㄷ자 형상 재작성", fx["stat"]["done"], "다리 일부 13.4° 기울음 / 길이 %.1f" % smp["old_len"], "ㄷ자 W %.0f · h %.0f · R24 / 길이 %.1f" % (smp["W"], smp["h"], smp["new_len"]), round((smp["new_len"] - smp["old_len"]) * fx["stat"]["done"], 1), "도면 저부철근 상세도 + 재료표 L 3,342"], 4) fs = wb["수정 파일·해시"] def sha(p): h = hashlib.sha256() with open(p, "rb") as f: for blk in iter(lambda: f.read(1 << 20), b""): h.update(blk) return h.hexdigest() for row in fs.iter_rows(min_row=4): if str(row[1].value).endswith("ramp-A G5_수정3_20261026.3dm") and row[3].value == "최신": row[3].value = "중간본" row[3].fill = PatternFill(fill_type=None) p = REV / "Ramp-A교P1" / "ramp-A G5_수정4_20261026.3dm" st = p.stat() rr = append_like(fs, ["Ramp-A교", "검토파일/Ramp-A교P1/ramp-A G5_수정4_20261026.3dm", "P3-2 ㄷ자 재작성 — 전체 수정 포함", "최신", round(st.st_size / 1e6, 1), datetime.datetime.fromtimestamp(st.st_mtime).strftime("%Y-%m-%d %H:%M"), sha(p)], 4) fs.cell(row=rr, column=4).fill = GREEN # 공통 문제 상세(이미지): 새 행 wd = wb["공통 문제 상세(이미지)"] nr = wd.max_row + 1 wd.cell(row=nr, column=1, value="M-8") wd.cell(row=nr, column=2, value="[개별] Ramp-A교 P3-2 ㄷ자철근 — 모델이 깔때기 형상 (도면은 바닥 수평 + 다리 수직 ㄷ자)") wd.cell(row=nr, column=7, value="모델 생성 과정에서 다리 일부를 기울이고 모서리를 R135 두 호로 이은 것으로 추정 (코드 미확인). 도면 저부철근 상세도는 단순 ㄷ자") wd.cell(row=nr, column=8, value="도면 형상(바닥 수평 + 다리 수직)과 재료표 L 3,342 로 다시 작도, 모서리 R 은 KDS H16 최소") wd.cell(row=nr, column=9, value=FIXED) for col in (1, 2, 7, 8, 9): c = wd.cell(row=nr, column=col) clone(wd.cell(row=nr - 3 if nr - 3 > 3 else 4, column=col), c) c.alignment = Alignment(wrap_text=True, vertical="top") wd.cell(row=nr, column=9).fill = copy.copy(GREEN) wd.cell(row=nr, column=3, value="(해당 없음 — Ramp-A교 P3-2)") wd.cell(row=nr, column=4, value="(해당 없음 — Ramp-A교 P3-2)") wd.cell(row=nr, column=5, value="(해당 없음 — Ramp-A교 P3-2)") wd.cell(row=nr, column=10, value="(해당 없음)") wd.cell(row=nr, column=11, value="(해당 없음)") wd.cell(row=nr, column=12, value="(해당 없음)") wd.row_dimensions[nr].height = 360 for fname, anchor in (("RampA_P3-2_수정전.png", "F%d" % nr), ("RampA_P3-2_수정후.png", "M%d" % nr)): im = XLImage(str(IMG / fname)) w0, h0 = im.width, im.height im.width, im.height = 560, int(560 * h0 / w0) wd.add_image(im, anchor) wd.cell(row=nr, column=6).alignment = Alignment(wrap_text=True, vertical="top") wb.save(XLSX) print("saved; new row", nr)