141 lines
6.2 KiB
Python
141 lines
6.2 KiB
Python
import sys, os, json, re
|
||
import fitz # PyMuPDF
|
||
sys.stdout.reconfigure(encoding='utf-8')
|
||
|
||
pdf_path = "resources/knowledge/original/원가계산/건설공사_표준품셈/2026년_건설공사_표준품셈.pdf"
|
||
doc = fitz.open(pdf_path)
|
||
|
||
# MD file mapping for chapters
|
||
md_map = {
|
||
"공통_제1장": "resources/knowledge/original/원가계산/건설공사_표준품셈/01_공통부문/제1장_적용기준.md",
|
||
"공통_제3장": "resources/knowledge/original/원가계산/건설공사_표준품셈/01_공통부문/제3장_토공사.md",
|
||
"공통_제4장": "resources/knowledge/original/원가계산/건설공사_표준품셈/01_공통부문/제4장_조경공사.md",
|
||
"공통_제6장": "resources/knowledge/original/원가계산/건설공사_표준품셈/01_공통부문/제6장_철근콘크리트공사.md",
|
||
"토목_제1장": "resources/knowledge/original/원가계산/건설공사_표준품셈/02_토목부문/제1장_도로포장공사.md",
|
||
"토목_제6장": "resources/knowledge/original/원가계산/건설공사_표준품셈/02_토목부문/제6장_관부설및접합공사.md",
|
||
"건축_제9장": "resources/knowledge/original/원가계산/건설공사_표준품셈/03_건축부문/제9장_미장공사.md",
|
||
"유지관리_제1장": "resources/knowledge/original/원가계산/건설공사_표준품셈/05_유지관리부문/제1장_공통.md",
|
||
"유지관리_제3장": "resources/knowledge/original/원가계산/건설공사_표준품셈/05_유지관리부문/제3장_건축.md",
|
||
}
|
||
|
||
# Read MD contents into memory
|
||
md_contents = {}
|
||
for k, p in md_map.items():
|
||
if os.path.exists(p):
|
||
with open(p, 'r', encoding='utf-8') as f:
|
||
md_contents[k] = f.read()
|
||
else:
|
||
print(f"MD missing: {p}")
|
||
|
||
with open('scratch/located_const_sections.json', 'r', encoding='utf-8') as f:
|
||
located_items = json.load(f)
|
||
|
||
print(f"Loaded located items: {len(located_items)}")
|
||
|
||
results = []
|
||
|
||
for item in located_items:
|
||
k = item['key']
|
||
num = item['number']
|
||
name = item['name']
|
||
pn = item['path_name']
|
||
pdf_pages = item.get('pdf_pages', [])
|
||
|
||
# Exclude chapter 8 (already audited by codex)
|
||
if "공통부문 › 건설기계" in pn:
|
||
continue
|
||
|
||
print(f"\n==================================================")
|
||
print(f"Auditing: {k} | {num} {name} | {pn}")
|
||
print(f"PDF Pages: {pdf_pages}")
|
||
|
||
# Determine MD chapter
|
||
md_key = None
|
||
if "공통부문 › 적용기준" in pn:
|
||
md_key = "공통_제1장"
|
||
elif "공통부문 › 토공사" in pn:
|
||
md_key = "공통_제3장"
|
||
elif "공통부문 › 조경공사" in pn:
|
||
md_key = "공통_제4장"
|
||
elif "공통부문 › 철근콘크리트공사" in pn:
|
||
md_key = "공통_제6장"
|
||
elif "토목부문 › 도로포장공사" in pn:
|
||
md_key = "토목_제1장"
|
||
elif "토목부문 › 관부설" in pn:
|
||
md_key = "토목_제6장"
|
||
elif "건축부문 › 미장공사" in pn:
|
||
md_key = "건축_제9장"
|
||
elif "유지관리부문 › 공 통" in pn:
|
||
md_key = "유지관리_제1장"
|
||
elif "유지관리부문 › 건 축" in pn:
|
||
md_key = "유지관리_제3장"
|
||
|
||
md_text = md_contents.get(md_key, "")
|
||
|
||
# Extract MD section chunk
|
||
# Usually in MD, sections start with "### 1-2-2" or "## 1-2-2" or "1-2-2."
|
||
escaped_num = re.escape(num)
|
||
sec_pattern = re.compile(rf"(^|\n)(#+\s+{escaped_num}[\.\s].*?)(?=\n#+\s+\d+-\d+-\d+[\.\s]|\Z)", re.DOTALL)
|
||
m = sec_pattern.search(md_text)
|
||
if not m:
|
||
# try without leading hash
|
||
sec_pattern2 = re.compile(rf"(^|\n)({escaped_num}[\.\s].*?)(?=\n\d+-\d+-\d+[\.\s]|\Z)", re.DOTALL)
|
||
m = sec_pattern2.search(md_text)
|
||
|
||
md_sec_text = m.group(2) if m else ""
|
||
if not md_sec_text:
|
||
print(f"⚠ Warning: Could not isolate section in MD: {num}")
|
||
else:
|
||
print(f"MD Section found (len={len(md_sec_text)} chars)")
|
||
|
||
# Extract PDF page text
|
||
pdf_text = ""
|
||
# Use the relevant page (typically the 2nd match if first was toc/index, or the page > 50)
|
||
actual_pages = [p for p in pdf_pages if p > 50]
|
||
for p in actual_pages:
|
||
pdf_text += f"\n--- PDF Page {p} ---\n" + doc[p - 1].get_text()
|
||
|
||
# Check 8 branches
|
||
audit = {
|
||
"work_item_key": k,
|
||
"section_number": num,
|
||
"section_name": name,
|
||
"path_name": pn,
|
||
"md_file": md_map.get(md_key),
|
||
"pdf_pages": actual_pages,
|
||
"branch1_basis_unit": {"status": "ok", "notes": []},
|
||
"branch2_multilevel_header": {"status": "ok", "notes": []},
|
||
"branch3_notes_boundary": {"status": "ok", "notes": []},
|
||
"branch4_page_break_row_merge": {"status": "ok", "notes": []},
|
||
"branch5_cell_values": {"status": "ok", "notes": []},
|
||
"branch6_missing_sections": {"status": "ok", "notes": []},
|
||
"branch7_notes_formula_boundary": {"status": "ok", "notes": []},
|
||
"branch8_hidden_materials_ref": {"status": "ok", "notes": []}
|
||
}
|
||
|
||
# 1. Check if section exists in MD
|
||
if not md_sec_text:
|
||
audit["branch6_missing_sections"]["status"] = "missing_in_md"
|
||
audit["branch6_missing_sections"]["notes"].append(f"Section {num} not found in MD {md_key}")
|
||
else:
|
||
# Check notes [주]
|
||
pdf_notes = re.findall(r"\[주\]|\[\s*주\s*\]|[①-⑩]", pdf_text)
|
||
md_notes = re.findall(r"\[주\]|\[\s*주\s*\]|[①-⑩]", md_sec_text)
|
||
if len(pdf_notes) > 0 and len(md_notes) == 0:
|
||
audit["branch3_notes_boundary"]["status"] = "suspicious_notes_missing"
|
||
audit["branch3_notes_boundary"]["notes"].append(f"PDF has {len(pdf_notes)} note markers, MD has 0")
|
||
|
||
# Check units in PDF vs MD
|
||
units_in_pdf = re.findall(r"\((?:단위\s*:\s*)?([^\)]+당|[^\)]+)\)", pdf_text)
|
||
# Check table presence
|
||
if "|" in pdf_text and "|" not in md_sec_text:
|
||
audit["branch4_page_break_row_merge"]["status"] = "table_flattened_to_text"
|
||
audit["branch4_page_break_row_merge"]["notes"].append("Table in PDF not rendered as markdown table")
|
||
|
||
results.append(audit)
|
||
|
||
with open('scratch/audit_const_linked_results.json', 'w', encoding='utf-8') as f:
|
||
json.dump(results, f, ensure_ascii=False, indent=2)
|
||
|
||
print(f"\nAudit completed for {len(results)} linked non-machine sections.")
|