"""4개교_공통사항_종합.xlsx 에 '수정 내역' 계열 시트 5개를 추가하고 M-14(B9) 행을 갱신한다. 입력: Code/검토수정/결과/*.json (이번 수정 작업의 실행 결과), 검토파일 안의 모델 파일(해시 계산) 출력: 검토파일/4개교_공통사항_종합.xlsx (수정 전 파일은 *_백업_*.xlsx 로 보관) 같은 이름의 시트가 이미 있으면 중단한다(이중 적용 방지). """ import copy import datetime import hashlib import json import sys from pathlib import Path import openpyxl from openpyxl.styles import Alignment, Border, Font, PatternFill, Side ROOT = Path(r"D:\★Pier 자료\프로그램\Pier_T") REV = ROOT / "검토파일" RES = ROOT / "Code" / "검토수정" / "결과" XLSX = REV / "4개교_공통사항_종합.xlsx" NEW_SHEETS = ["수정 내역", "수정 증거(마크별)", "KDS 직경별 R", "미수정·보류", "수정 파일·해시"] def jload(name): return json.loads((RES / name).read_text(encoding="utf-8")) apply_ = jload("c1_apply.json") b9 = jload("c1_b9_ungok.json") kds = jload("c1_kds_scan.json") foot_u = jload("c1_footing_ungok3.json") foot_o = jload("c1_footing_other.json") wb = openpyxl.load_workbook(XLSX) if any(n in wb.sheetnames for n in NEW_SHEETS): sys.exit("이미 수정 내역 시트가 있다 - 중단") ref = wb["개별 항목"] HEAD_FILL = copy.copy(ref["A3"].fill) HEAD_FONT = copy.copy(ref["A3"].font) thin = Side(style="thin", color="FF999999") BORDER = Border(left=thin, right=thin, top=thin, bottom=thin) WRAP = Alignment(wrap_text=True, vertical="top") TITLE_FONT = copy.copy(ref["A1"].font) FILL_OK = PatternFill("solid", fgColor="FFE2EFDA") FILL_HOLD = PatternFill("solid", fgColor="FFFFF2CC") FILL_WARN = PatternFill("solid", fgColor="FFF8CBAD") def make_sheet(title, heading, note, headers, rows, widths, fills=None): ws = wb.create_sheet(title) ws["A1"] = heading ws["A1"].font = TITLE_FONT ws["A2"] = note ws["A2"].alignment = Alignment(wrap_text=False, vertical="top") for j, h in enumerate(headers, 1): c = ws.cell(row=3, column=j, value=h) c.fill = copy.copy(HEAD_FILL) c.font = copy.copy(HEAD_FONT) c.border = BORDER c.alignment = Alignment(wrap_text=True, vertical="center") for i, row in enumerate(rows, 4): for j, v in enumerate(row, 1): c = ws.cell(row=i, column=j, value=v) c.alignment = WRAP c.border = BORDER if fills and fills.get((i - 4, j - 1)): c.fill = copy.copy(fills[(i - 4, j - 1)]) for j, w in enumerate(widths, 1): ws.column_dimensions[openpyxl.utils.get_column_letter(j)].width = w ws.freeze_panes = "A4" ws.auto_filter.ref = "A3:%s%d" % (openpyxl.utils.get_column_letter(len(headers)), 3 + len(rows)) return ws # ---------------------------------------------------------------- 수정 내역 (요약) def c1_row(code, bridge, key, marks, rbefore, rafter, file): v = apply_[key] n = v["stat"]["replaced"] sb, sa = v["short_before"], v["short_after"] hold = [] if v["stat"].get("skipped_complex"): hold.append("형상 복잡 %d본(P3) 보류" % v["stat"]["skipped_complex"]) if v["stat"].get("skipped_pipe_fail"): hold.append("파이프 재생성 실패 %d본 보류" % v["stat"]["skipped_pipe_fail"]) if v["stat"].get("held_footing"): hold.append("기초 철근 %d본은 별도 규칙으로 처리" % v["stat"]["held_footing"]) ver = ("저장 직전 문서 재스캔: 기초 제외 부족 호 %d → %d개. 객체 수 %d → %d(동일). 새 파이프 부피 = π·r²·L 대비 ±0.2%% 이내(교체 전수)." % (sb["other"], sa.get("other", 0), v["objects_before"], v["objects_after"])) return [code, bridge, "굽힘 반지름(C-1)", marks, rbefore, rafter, n, "KDS 24 14 21 표 4.5-1 (내면지름 ≥ 3.5db, D16 이하 2db), 공칭 직경 기준. 사용자 승인 2026-10-23(창 선택). 공통 문제 C-1 대안 1(권장)", file, ver, "; ".join(hold)] rows = [ c1_row("R-1", "운곡교", "ungok", "S1·S2, C9~C16 (H19·H25)", "H19 R42.75 / H25 R56.25", "H19 R42.93 / H25 R56.95", "운곡교p1_수정_20261023.3dm (이후 수정3·수정6 에 포함)"), c1_row("R-2", "산외교", "sanoe", "C7~C10, C15, P5 (H22·H19)", "H22 R49.5 / H19 R42.75", "H22 R49.85 / H19 R42.93", "산외교P1 G5_수정_20261023.3dm (이후 수정2 에 포함)"), c1_row("R-3", "옥성교", "okseong", "S1·S2, C8~C14, C16, C18~C20 (H19·H22·H25)", "H19 R42.75 / H22 R49.5 / H25 R56.25", "H19 R42.93 / H22 R49.85 / H25 R56.95", "옥성교P1_G5_수정_20261023.3dm (이후 수정2 에 포함)"), c1_row("R-4", "Ramp-A교", "rampa", "C3, C7~C16 (H19·H25)", "H19 R42.75 / H25 R56.25", "H19 R42.93 / H25 R56.95", "ramp-A G5_수정_20261023.3dm"), ["R-5", "운곡교", "기초 철근 높이·피복 (사용자 규칙 2026-10-23)", "B5(S29 상면 X), B1(S25 상면 Y), B6·B8(S29 하면 X), B2·B4(S25 하면 Y)", "기초 하단 기준 z: B5 2127 / B1 2073 / B6·B8 178 / B2·B4 122 (B2 다리 끝 2072, B6 다리 끝 2128)", "B5 2100 / B1 2072.5 / B6·B8 150 / B2·B4 177 (B2 다리 끝 2072.5, B6 다리 끝 2100)", "이동 %d본 (B5 %d, B1 %d, B6 %d, B8 %d, B2 %d, B4 %d) + 굽힘 R 보정 %d본 (B1·B2·B3·B7·B10)" % ( sum(foot_u["stat"].get(m + "_done", 0) for m in ("B5", "B1", "B6", "B8", "B2", "B4")), foot_u["stat"]["B5_done"], foot_u["stat"]["B1_done"], foot_u["stat"]["B6_done"], foot_u["stat"]["B8_done"], foot_u["stat"]["B2_done"], foot_u["stat"]["B4_done"], sum(foot_u["stat"].get(m + "_done", 0) for m in ("B1", "B2", "B3", "B7", "B10"))), "사용자 지시 2026-10-23·26: 최외각 주철근(H29 B5·B6) 중심으로 피복 산정(상면 100 / 하면 150), 나머지 철근은 그 안쪽에 접촉 배치. " "교축·교직 직경이 다르면 계산서 확인·설계자 문의 필요 사항", "운곡교p1_수정3_20261023.3dm (이후 수정6 에 포함)", "저장 후 재측정 z 범위(기초 하단 기준): B1 1872.5~2072.5, B2 177~2072.5, B4 177, B5 1900~2100, B6 150~2100, B8 150. 객체 수 3,236 동일.", "B2 U자 다리 약 54.5mm 짧아짐(다리 끝을 B1 높이 2072.5 로 맞춤) → 재료표 b 1,950 과 다름. 설계자 확인"], ["R-6", "산외교", "기초 철근 높이·피복 (같은 규칙, 직경 동일: 상면 H16 / 하면 H22)", "B1·B4(H16 상면), B2·B3(H22 하면 바깥), B5·B6(H22 하면 안쪽)", "z: B1 2422.5 / B4 2377.5 / B2·B3 127.5 / B5·B6 172.5 (기초 하단 기준, 높이 2,500)", "B1 2400 / B4 2384 / B2·B3 150 / B5·B6 172 (B2 다리 끝 2400, B5 다리 끝 2400)", "이동 180본 (B1 31, B4 31, B3 28, B2 31, B6 28, B5 31) + 굽힘 R 보정 B2·B5 62본", "사용자 규칙(운곡교와 동일) + 도면 U자 다리 길이 = 하면선~상면선(2,250) 로 해석 [추론]", "산외교P1 G5_수정2_20261026.3dm", "저장 후 재측정 z: B1 2400, B4 2384, B2 150~2400, B3 150, B5 172~2400, B6 172. 객체 수 2,004 동일.", "B5 다리 22mm 짧아짐. B7(H13 세로철근 166~2384)은 주철근 사이에 놓여 있어 미수정 — 설계자 확인"], ["R-7", "옥성교", "기초 철근 높이·피복 (같은 규칙, 전부 H29)", "B5·B7(상면 안쪽), B6·B8(하면 안쪽). B1~B4 는 이미 2100 / 150 이라 그대로. 굽힘 R: B9~B12(H19)", "z: B5·B7 2077.5 / B6·B8 172.5 (B6 다리 끝 2122.5) — 바깥층과 22.5mm 차이라 H29 끼리 6.5mm 겹침", "B5·B7 2071 / B6·B8 179 (다리 끝 2100). 접촉 배치", "이동 150본 (B5 38, B7 37, B6 38, B8 37) + 굽힘 R 보정 B9 171, B10 5, B11 19, B12 19 = 214본", "사용자 규칙 2026-10-23·26 (직경 동일 → 도면 기준 최외각 B1~B4 유지)", "옥성교P1_G5_수정2_20261026.3dm", "저장 후 재측정 z: B5·B7 1096~2071, B6·B8 179~2100, B1·B3 1200~2100, B2·B4 150~1950. 객체 수 3,862 동일.", "B6·B8 다리 29mm 짧아짐. B12 는 도면 점보다 19mm 높음(엑셀 M-12)이라 이번 규칙과 별개로 미수정"], ["R-8", "운곡교", "기초 전단철근 B9 갈고리 (도면에 갈고리 R·연장 없음 = 도면 오류, 임시 수정)", "B9 (H16) 위쪽 180° 갈고리", "R20.4(c 64 를 반원 호 길이로 해석), 연장 0 / 길이 2,144.7 / 합계 1,057.3 m", "R24 (KDS H16 최소 중심선 R 23.85, 모델 H16 굽힘 24.0 과 동일), 연장 100 (임의) / 길이 2,256.1 / 합계 %.1f m / 갈고리 위쪽 피복 %.1f mm" % (b9["total_len_new_m"], b9["min_cover_above_hook"]), b9["stat"]["replaced"], "사용자 결정 2026-10-26: 주철근 H29 에 걸리는 B9(H16) 갈고리를 KDS 기준 R 로, 연장 C 는 임의 100. 도면에 값이 있는 경우는 도면 재료표 값 따름(a 1,995, 아래 90° b 96 유지). " "중간 시도 R65·150(수정4), R45·100(수정5)은 폐기", "운곡교p1_수정6_20261026.3dm", "저장된 수정6 파일을 다시 열어 확인(c1_b9_verify): 493본 모두 호 R24(90°)+R24(180°), 마지막 구간 100, 길이 2,256.1, 속성 b9_hook_fix 986개, 객체 수 3,236 동일.", "재료표 L 2,155 와 약 101mm 차이. 아래 90° 갈고리 연장 72 < 5db 80 (도면 b 96 에서 나온 값이라 유지). 설계자 확인 전 임시 값"], ["R-9", "4개교", "모델 안 수정 기록", "수정된 철근 객체·문서 속성", "수정 기록 없음", "객체 사용자 문자열 bend_r_fix / footing_fix / b9_hook_fix (날짜·내용) + 문서 문자열 수정일·수정내용", "-", "사용자 지시 2026-10-23: 모델에도 수정 날짜를 남길 것", "각 수정 모델 파일", "수정본 재로드 후 문자열 확인 (B9: 986개 객체에 b9_hook_fix, 수정일 2026-10-26)", "운곡교 수정_20261023 은 원본이 꺼 둔 기초 철근 레이어 38개를 켜서 저장(검토완료 모델과 표시 동일)"], ] fills = {} for i in range(len(rows)): fills[(i, 0)] = FILL_OK make_sheet("수정 내역", "4개교 모델 수정 내역 — 공통사항 (작성 2026-10-26)", "수정은 모두 새 파일에 저장했고 원본·검토완료 모델은 그대로 둠. 근거 데이터 = Code/검토수정/결과/*.json, 실행 스크립트 = Code/검토수정/스크립트/*.py", ["번호", "교량", "수정 항목", "대상 마크", "수정 전", "수정 후", "본수", "근거(규칙·결정)", "결과 파일", "검증(방법·결과)", "비고·남은 문제"], rows, [7, 9, 24, 34, 38, 38, 26, 48, 30, 48, 44], fills) # ---------------------------------------------------------------- 수정 증거(마크별) OLD_R = {"H19": 42.75, "H22": 49.5, "H25": 56.25} NEW_R = {"H19": 42.93, "H22": 49.85, "H25": 56.95} BR = {"ungok": "운곡교", "sanoe": "산외교", "okseong": "옥성교", "rampa": "Ramp-A교"} ev = [] for key, v in apply_.items(): if not isinstance(v, dict): continue for mk, x in v["per_mark"].items(): mark, dia = mk.split("/") ev.append([BR[key], mark, dia, "굽힘 반지름(C-1)", x["bars"], "R %.2f" % OLD_R[dia], "R %.2f" % NEW_R[dia], x["sum_dlen_mm"], "수정 전후 중심선 길이 차 합계(mm, 철근 끝점 고정이라 호만 커져 짧아짐). 저장 직전 재스캔 부족 호 0"]) foot_rows = [ ("운곡교", "B5", "S29", 32, "z 2127 (상면 X)", "z 2100", -27.0), ("운곡교", "B1", "S25", 38, "z 2073 (상면 Y)", "z 2072.5", -0.5), ("운곡교", "B6", "S29", 32, "z 178 (하면 X, 다리 끝 2128)", "z 150 (다리 끝 2100)", -28.0), ("운곡교", "B8", "S29", 29, "z 178", "z 150", -28.0), ("운곡교", "B2", "S25", 38, "z 122 (다리 끝 2072)", "z 177 (다리 끝 2072.5)", 55.0), ("운곡교", "B4", "S25", 35, "z 122", "z 177", 55.0), ("산외교", "B1", "H16", 31, "z 2422.5", "z 2400", -22.5), ("산외교", "B4", "H16", 31, "z 2377.5", "z 2384", 6.5), ("산외교", "B3", "H22", 28, "z 127.5", "z 150", 22.5), ("산외교", "B2", "H22", 31, "z 127.5 (다리 끝 2377.5)", "z 150 (다리 끝 2400)", 22.5), ("산외교", "B6", "H22", 28, "z 172.5", "z 172", -0.5), ("산외교", "B5", "H22", 31, "z 172.5 (다리 끝 2422.5)", "z 172 (다리 끝 2400)", -0.5), ("옥성교", "B5", "H29", 38, "z 2077.5", "z 2071", -6.5), ("옥성교", "B7", "H29", 37, "z 2077.5", "z 2071", -6.5), ("옥성교", "B6", "H29", 38, "z 172.5 (다리 끝 2122.5)", "z 179 (다리 끝 2100)", 6.5), ("옥성교", "B8", "H29", 37, "z 172.5 (다리 끝 2122.5)", "z 179 (다리 끝 2100)", 6.5), ] for b, m, d, n, bef, aft, dz in foot_rows: ev.append([b, m, d, "기초 철근 높이(기초 하단 기준 mm)", n, bef, aft, dz, "이동량 mm. 저장 후 재측정 z 범위가 목표와 일치"]) rf = [("운곡교", "B1", "S25", 38), ("운곡교", "B2", "S25", 38), ("운곡교", "B3", "S19", 38), ("운곡교", "B7", "S19", 32), ("운곡교", "B10", "S19", 9), ("산외교", "B2", "H22", 31), ("산외교", "B5", "H22", 31), ("옥성교", "B9", "H19", 171), ("옥성교", "B10", "H19", 5), ("옥성교", "B11", "H19", 19), ("옥성교", "B12", "H19", 19)] for b, m, d, n in rf: dia = "H" + d[1:] ev.append([b, m, d, "굽힘 반지름(C-1, 기초 철근)", n, "R %.2f" % OLD_R[dia], "R %.2f" % NEW_R[dia], None, "기초 철근 별도 확인 후 적용(사용자 규칙과 함께)"]) ev.append(["운곡교", "B9", "H16", "전단철근 갈고리", b9["stat"]["replaced"], "위 180° R20.4 / 연장 0 / 길이 2,144.7", "위 180° R24 / 연장 100 / 길이 %.1f" % b9["length_new"][0], round((b9["length_new"][0] - 2144.7) * b9["stat"]["replaced"], 1), "합계 길이 변화(mm). 갈고리 위쪽 피복 %.1f mm. 임시 값(설계자 확인 전)" % b9["min_cover_above_hook"]]) make_sheet("수정 증거(마크별)", "수정 증거 — 마크별 수정 전·후 값", "본수·길이 변화는 실행 결과 JSON(c1_apply·c1_footing_*·c1_b9_ungok)에서 가져옴. 높이 z 는 기초 하단 기준 mm", ["교량", "마크", "직경", "항목", "본수", "수정 전", "수정 후", "길이·이동 변화 합계(mm)", "확인 방법·비고"], ev, [10, 8, 8, 28, 8, 34, 34, 18, 52]) # ---------------------------------------------------------------- KDS 직경별 R NOM = [("D13", 12.7, 2.0), ("D16", 15.9, 2.0), ("D19", 19.1, 3.5), ("D22", 22.2, 3.5), ("D25", 25.4, 3.5), ("D29", 28.6, 3.5), ("D32", 31.8, 3.5), ("D35", 34.9, 3.5)] kr = [] for d, db, f in NOM: inner_d = f * db kr.append([d, db, "%.1fdb" % f if f != int(f) else "%ddb" % int(f), round(inner_d, 2), round(inner_d / 2, 2), round(inner_d / 2 + db / 2, 2), "KDS 24 14 21 표 4.5-1(4.5.2.1): 이형철근 D16 이하 2db, D19 이상 3.5db. 최대값 규정 없음. 갈고리 연장 5db 이상(4.5.4.2)", None, None, None]) r = len(kr) for key in ("ungok", "sanoe", "okseong", "rampa"): for d, x in kds[key]["per_dia"].items(): kr.append(["D" + d, None, None, None, None, x["req_center_R"], "모델 확인: " + BR[key] + " (최신 수정본)", x["min_R_found"], x["arcs"], x["arcs_below"]]) make_sheet("KDS 직경별 R", "KDS 24 14 21 최소 굽힘 반지름 — 직경별 기준값과 4개교 모델 확인 결과", "위 8행 = 기준값(공칭 직경). 아래 = 수정 후 모델 전수 스캔(중심선 호). 모델 기준 R 은 모델 파이프가 정수 직경이라 내면지름 ≥ 3.5×공칭이 되도록 올림한 값", ["직경", "공칭 db(mm)", "최소 내면지름 배수", "최소 내면지름(mm)", "최소 내면반지름(mm)", "최소 중심선 R(mm)", "출처·대상", "모델 최소 R", "호 개수", "기준 미달 호"], kr, [8, 12, 14, 16, 16, 16, 60, 12, 10, 12]) # ---------------------------------------------------------------- 미수정·보류 hold = [ ["운곡교", "P3 (H19 링)", "72본 / 호 144개", "굽힘 R 42.75 < 42.93", "호가 서로 이어진 형상이라 '호 반지름만 키우기' 방식으로 못 고침", "다른 방식 필요 (사용자 결정)", "R-1 보류"], ["옥성교", "C9·C13 (H25)", "18본 / 호 90개", "굽힘 R 56.25 < 56.95", "파이프 재생성 실패(형상 복잡)", "다른 방식 필요", "R-3 보류"], ["Ramp-A교", "C12 (H19)", "12본 / 호 60개", "굽힘 R 42.75 < 42.93", "파이프 재생성 실패(형상 복잡)", "다른 방식 필요", "R-4 보류"], ["운곡교", "B9 (H16)", "493본", "갈고리 R·연장이 도면에 없음(도면 오류)", "임시 수정(R24, 연장 100). 재료표 L 2,155 와 약 101mm 차이, 아래 90° 연장 72 < 5db 80", "설계자 확인", "R-8"], ["운곡교", "B2 (S25 U자)", "38본", "다리 끝을 B1 높이 2072.5 로 → 54.5mm 짧아짐", "산외·옥성은 다리 끝을 바깥 상면선(2400·2100)에 맞춤 — 해석 통일 필요", "사용자 결정(운곡교도 상면선 2100 으로 통일할지)", "R-5"], ["산외교", "B7 (H13 세로철근)", "98본", "양 끝 z 166 / 2384 가 주철근 사이에 놓임(바깥으로 걸리지 않음)", "운곡교 B9 와 같은 유형. 미수정", "설계자 확인", "R-6"], ["옥성교", "B12 (H19)", "19본", "도면 점보다 19mm 높음(엑셀 M-12)", "이번 기초 규칙과 별개 항목. 미수정", "사용자 결정", "R-7"], ["산외·옥성교", "기초 B 철근 전반", "-", "교축·교직 직경이 같아 이번에는 설계자 문의 없이 진행", "직경이 다른 교량이 나오면 계산서 확인 후 피복 작은 철근을 바깥으로 — 설계자 문의 사항 (OPEN #39)", "해당 시 문의", "규칙"], ["4개교", "엑셀 38건 중 위 항목 외", "-", "커플러·겹침 길이·+54 가산·개별 길이·조립용 철근 등", "수정 방향이 정해지지 않아(결정 칸 비어 있음) 미수정", "방향 결정 필요", "공통 문제 · 대안 시트"], ] hf = {(i, 5): FILL_HOLD for i in range(len(hold))} make_sheet("미수정·보류", "수정하지 않았거나 임시로 수정한 항목", "사유와 필요한 결정을 같이 적음", ["교량", "마크", "대상", "문제", "사유·현재 상태", "필요한 결정", "연결"], hold, [12, 20, 18, 44, 60, 30, 18], hf) # ---------------------------------------------------------------- 수정 파일·해시 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() files = [ ("운곡교", "철근 모델랑 운곡교P1_바이브코딩/운곡교p1_수정_20261023.3dm", "C-1 굽힘 R(기초 제외) + 철근 레이어 켬", "중간본"), ("운곡교", "철근 모델랑 운곡교P1_바이브코딩/운곡교p1_수정2_20261023.3dm", "폐기된 규칙(같은 방향 철근 같은 높이)", "폐기"), ("운곡교", "철근 모델랑 운곡교P1_바이브코딩/운곡교p1_수정3_20261023.3dm", "기초 철근 높이 규칙 + 기초 R 보정", "중간본"), ("운곡교", "철근 모델랑 운곡교P1_바이브코딩/운곡교p1_수정4_20261026.3dm", "B9 R65·연장 150 시도", "폐기"), ("운곡교", "철근 모델랑 운곡교P1_바이브코딩/운곡교p1_수정5_20261026.3dm", "B9 R45·연장 100 시도", "폐기"), ("운곡교", "철근 모델랑 운곡교P1_바이브코딩/운곡교p1_수정6_20261026.3dm", "B9 R24(KDS)·연장 100 — 전체 수정 포함", "최신"), ("산외교", "검토_산외교P1/산외교P1 G5_수정_20261023.3dm", "C-1 굽힘 R(기초 제외)", "중간본"), ("산외교", "검토_산외교P1/산외교P1 G5_수정2_20261026.3dm", "기초 철근 규칙 + R 보정 — 전체 수정 포함", "최신"), ("옥성교", "검토_옥성교P1/옥성교P1_G5_수정_20261023.3dm", "C-1 굽힘 R(기초 제외)", "중간본"), ("옥성교", "검토_옥성교P1/옥성교P1_G5_수정2_20261026.3dm", "기초 철근 규칙 + R 보정 — 전체 수정 포함", "최신"), ("Ramp-A교", "Ramp-A교P1/ramp-A G5_수정_20261023.3dm", "C-1 굽힘 R — 전체 수정 포함(말뚝 기초라 기초 철근 해당 없음)", "최신"), ("운곡교", "철근 모델랑 운곡교P1_바이브코딩/운곡교p1_검토완료.3dm", "검토 당시 모델 사본(비교용)", "원본 사본"), ("산외교", "검토_산외교P1/산외교P1 G5_검토완료.3dm", "검토 당시 모델 사본(비교용)", "원본 사본"), ("옥성교", "검토_옥성교P1/옥성교P1_G5_검토완료.3dm", "검토 당시 모델 사본(비교용)", "원본 사본"), ("Ramp-A교", "Ramp-A교P1/ramp-A G5_검토완료.3dm", "검토 당시 모델 사본(비교용)", "원본 사본"), ] fr = [] ff = {} for i, (b, rel, what, st) in enumerate(files): p = REV / rel if p.exists(): stt = p.stat() fr.append([b, "검토파일/" + rel, what, st, round(stt.st_size / 1e6, 1), datetime.datetime.fromtimestamp(stt.st_mtime).strftime("%Y-%m-%d %H:%M"), sha(p)]) else: fr.append([b, "검토파일/" + rel, what, st, None, None, "파일 없음"]) ff[(i, 3)] = FILL_OK if st == "최신" else (FILL_WARN if st == "폐기" else None) ff = {k: v for k, v in ff.items() if v} make_sheet("수정 파일·해시", "수정 결과 모델 파일 목록과 SHA-256 (증거용)", "해시는 이 엑셀을 만든 시점(2026-10-26)의 파일 기준. 파일이 바뀌면 해시가 달라짐. 최신 = 모든 수정이 들어 있는 파일", ["교량", "경로(Pier_T 기준)", "내용", "상태", "크기(MB)", "저장 시각", "SHA-256"], fr, [10, 64, 44, 10, 10, 18, 66], ff) # ---------------------------------------------------------------- M-14(B9) 행 갱신 NEW_NOTE = ("임시 수정 (2026-10-26): 도면 가공도에 갈고리 원 부분 R·연장이 없어(도면 오류) 설계자 확인 전까지 임시값으로 수정 — " "위 180° 갈고리 R24(KDS H16 최소 23.85), 연장 100(임의). 재료표 L 2,155 와 약 101mm 차이, 아래 90° 연장 72 < 5db 80. " "수정 파일: 운곡교p1_수정6_20261026.3dm. '수정 내역' 시트 R-8, OPEN #39") for name in ("교량별 항목 합본", "개별 항목"): ws = wb[name] for row in ws.iter_rows(min_row=4): if row[1].value == "M-14" and row[2].value == "B9": if name == "교량별 항목 합본": row[7].value = NEW_NOTE row[8].value = "기준↔모델 (도면 오류, 임시 수정)" else: row[6].value = NEW_NOTE row[5].value = "기준↔모델 (도면 오류, 임시 수정)" # ---------------------------------------------------------------- 안내 시트 a = wb["안내"] start = a.max_row + 1 desc = [("수정 내역", "4개교 모델 수정 요약 R-1~R-9 (수정 전·후 값, 근거, 검증, 결과 파일) — 2026-10-26 추가"), ("수정 증거(마크별)", "마크별 본수·수정 전후 값·길이 변화"), ("KDS 직경별 R", "KDS 24 14 21 직경별 최소 굽힘 반지름과 4개교 모델 전수 확인 결과"), ("미수정·보류", "수정하지 않았거나 임시로 수정한 항목, 사유, 필요한 결정"), ("수정 파일·해시", "수정 결과 모델 파일 목록과 SHA-256")] for i, (s, t) in enumerate(desc): for j, v in enumerate((s, t), 1): c = a.cell(row=start + i, column=j, value=v) src = a.cell(row=11, column=j) c.font = copy.copy(src.font) c.fill = copy.copy(src.fill) c.border = copy.copy(src.border) c.alignment = copy.copy(src.alignment) wb.save(XLSX) print("saved", XLSX, wb.sheetnames)