타 AI 검토내역(tmp/기술문서DB_검토내역_260814.md) 전수 검증 후 동의분 적용. 파이프라인: - fix_fences.py units[:20] / collect_law.py 별표 limit=80·변경이력 [:40] 상한 제거 - gen_md.py 기준목록 출력을 운영본(04_참조_법령기준_목록.md) 경로로 정정 - README 구경로(docs/raw/law) 3곳 현행화, ruff format 일괄 적용 - verify_structure.py 신설: XML↔md 부칙·목·별표·CHANGELOG 행수 회귀검사 신규 발견 결함 2건 정정 (검토내역에 없던 것): - 행정규칙 XML은 부칙내용이 부칙 직속(부칙단위 래퍼 없음) — 변환기가 미처리해 행정규칙 md 22건에 부칙 전무. patch_law_md 확장 후 부칙 138건 복원 - patch_law_md 부칙 dedup 키가 괄호형 타법개정 헤더 미매칭 — 재실행 시 기존 부칙 1,724건 중복 추가되는 비멱등 결함. 정규식 보강 재수집 (law.go.kr): - 대기환경보전법 시행규칙 별표 PDF 78건 추가 확보 (80→158, 전수) - CHANGELOG 절단 3건 재생성 (고용보험법 시행령 42·국가계약법 시행령 41· 대기환경 45회 — API 연혁과 전건 일치 확인) 기술문서·데이터: - _최종검증로그: 55문서 스냅샷 범위 명시 (현재 65문서, 신설 10건 미검증 주지) - 제비율.md frontmatter에 조달청 제비율(2026.04.13)·국민연금법 출처 보강 - 원가_입력변수_사전: 장비대금 지급보증을 공사종류별 표로 정정, csv→json 표기 - labor_const JSON: 원문 노임 118건 전건 일치 확인 후 해시만 재동기화 (원문 변경분 = 신뢰도 플래그 컬럼뿐, 단가 무변) 검증: verify_structure 결함 0건, patch_law_md 재실행 +0 (멱등), 원가 JSON 해시 56건 중 잔여 불일치 1건(coef_2026 — 사용자 협의 대상) Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
95 lines
3.1 KiB
Python
95 lines
3.1 KiB
Python
# -*- coding: utf-8 -*-
|
||
"""기존 법령 md의 코드펜스(``` 부칙·개정문)를 인용블록으로 전환.
|
||
|
||
- 부칙: 같은 폴더 XML에서 절단 없이 다시 읽어 인용블록으로 교체(2500자 절단 복구)
|
||
- 개정문·제개정이유(CHANGELOG): 코드펜스 → 인용블록
|
||
본문·이미지 로컬화 결과는 건드리지 않는다(부칙/개정문 섹션만 치환).
|
||
"""
|
||
|
||
import re, sys
|
||
import xml.etree.ElementTree as ET
|
||
from pathlib import Path
|
||
|
||
ROOT = Path(__file__).resolve().parent.parent
|
||
FENCE = "```"
|
||
|
||
|
||
def clean(t):
|
||
t = (t or "").replace(" ", " ")
|
||
return re.sub(r"[ \t]+", " ", t).strip()
|
||
|
||
|
||
def to_quote(text):
|
||
return "\n".join(f"> {ln}" if ln.strip() else ">" for ln in text.split("\n"))
|
||
|
||
|
||
def buchik_from_xml(xml_path):
|
||
"""XML에서 부칙 인용블록 md(절단 없음) 생성. '## 부칙' 섹션 문자열 반환."""
|
||
root = ET.parse(xml_path).getroot()
|
||
bc = root.find("부칙")
|
||
if bc is None:
|
||
return None
|
||
units = bc.findall("부칙단위")
|
||
if not units:
|
||
return None
|
||
out = ["## 부칙", ""]
|
||
# 상한 없음 — 부칙 전건 출력 (상한을 두면 부칙 절단 결함 재발, 2026-08-15)
|
||
for b in units:
|
||
body = clean(b.findtext("부칙내용") or "")
|
||
out.append(to_quote(body))
|
||
out.append("")
|
||
return "\n".join(out).rstrip() + "\n"
|
||
|
||
|
||
def fix_law_md(md_path, xml_path):
|
||
"""현행/교본시점 md: '## 부칙' ~ 다음 '## ' 사이를 XML 부칙으로 교체."""
|
||
text = md_path.read_text(encoding="utf-8")
|
||
if "## 부칙" not in text:
|
||
return 0
|
||
new_buchik = buchik_from_xml(xml_path)
|
||
if not new_buchik:
|
||
return 0
|
||
# '## 부칙' 부터 다음 '## ' 헤딩 전까지 잘라 교체
|
||
m = re.search(r"^## 부칙\s*$", text, re.M)
|
||
if not m:
|
||
return 0
|
||
start = m.start()
|
||
nxt = re.search(r"^## (?!부칙)", text[m.end() :], re.M)
|
||
end = m.end() + nxt.start() if nxt else len(text)
|
||
new = text[:start] + new_buchik + ("\n" + text[end:].lstrip("\n") if text[end:].strip() else "")
|
||
if new != text:
|
||
md_path.write_text(new, encoding="utf-8")
|
||
return 1
|
||
return 0
|
||
|
||
|
||
def fix_changelog(md_path):
|
||
"""CHANGELOG: 개정문/제개정이유 코드펜스 → 인용블록."""
|
||
text = md_path.read_text(encoding="utf-8")
|
||
if FENCE not in text:
|
||
return 0
|
||
|
||
def repl(m):
|
||
inner = m.group(1).strip()
|
||
return to_quote(inner)
|
||
|
||
new = re.sub(FENCE + r"\w*\n(.*?)\n?" + FENCE, repl, text, flags=re.S)
|
||
if new != text:
|
||
md_path.write_text(new, encoding="utf-8")
|
||
return 1
|
||
return 0
|
||
|
||
|
||
if __name__ == "__main__":
|
||
n_law = n_cl = 0
|
||
for md in ROOT.rglob("*.md"):
|
||
if "임도기술교본" in str(md) or "_pipeline" in str(md):
|
||
continue
|
||
if md.name.startswith(("현행_", "교본시점_")):
|
||
xml = md.with_suffix(".xml")
|
||
if xml.exists():
|
||
n_law += fix_law_md(md, xml)
|
||
elif md.name == "CHANGELOG.md":
|
||
n_cl += fix_changelog(md)
|
||
print(f"부칙 인용블록 전환 {n_law}개 / CHANGELOG {n_cl}개")
|