Files
Aislo/resources/tester/scratch/verify_chapter_risk_tables.py
T

155 lines
6.9 KiB
Python

import os, sys, re, json, glob, fitz
sys.stdout.reconfigure(encoding='utf-8')
pdf_path = 'resources/knowledge/original/원가계산/건설공사_표준품셈/2026년_건설공사_표준품셈.pdf'
doc = fitz.open(pdf_path)
def verify_file(fpath):
fname = os.path.basename(fpath)
rel_path = os.path.relpath(fpath)
print(f"\n========================================================")
print(f" Verifying: {fname} ({rel_path})")
print(f"========================================================")
with open(fpath, 'r', encoding='utf-8') as f:
lines = f.readlines()
current_sec_num = 'UNKNOWN'
current_sec_title = 'UNKNOWN'
in_table = False
headers = []
table_rows = []
start_line = 0
file_risk_tables = []
for idx, line in enumerate(lines):
line_s = line.strip()
sec_m = re.search(r'(\d+-\d+(?:-\d+)?)(?:\s+([^\n(]+))?', line_s)
if sec_m and not line_s.startswith('|'):
current_sec_num = sec_m.group(1).strip()
current_sec_title = sec_m.group(0).strip()
if line_s.startswith('|') and not line_s.startswith('|---'):
cols = [c.strip() for c in line_s.split('|')[1:-1]]
if not in_table:
headers = cols
in_table = True
start_line = idx + 1
table_rows = []
continue
if in_table:
table_rows.append((idx + 1, cols))
else:
if in_table and (line_s == '' or not line_s.startswith('|')):
header_text = " ".join(headers)
col0_vals = [r[1][0] for r in table_rows if len(r[1]) > 0]
# Check Risk criteria:
is_risk = False
reasons = []
# C1: Efficiency / coefficient / factor
if any(k in header_text for k in ['효율', '작업효율', '계수', '할증', '보정', '손실률', '환산', '증감']) or any(k in current_sec_title for k in ['효율', '계수', '할증']):
is_risk = True
reasons.append('효율/계수/할증')
# C2: Multiple rows with remarks column
if any('비고' in h or '비 고' in h for h in headers) and len(table_rows) >= 2:
is_risk = True
reasons.append('비고 열 다중행')
# C3: Condition table (no workers)
if any(k in header_text for k in ['조건', '지형', '토질', '수심', '암질', '두께', '경사', '규격', '차단', '구분']) and not any(k in header_text for k in ['직종', '인부', '기술자', '기능사']):
if len(table_rows) >= 2:
is_risk = True
reasons.append('조건 분기 표')
# C4: Multi-level / empty col0
if any(v == '' for v in col0_vals) or any('→' in v for v in col0_vals):
is_risk = True
reasons.append('Col 0 빈칸/병합/연결')
if is_risk:
file_risk_tables.append({
'sec_num': current_sec_num,
'sec_title': current_sec_title,
'start_line': start_line,
'rows': len(table_rows),
'headers': headers,
'table_rows': table_rows,
'reasons': reasons
})
in_table = False
headers = []
table_rows = []
print(f"Total Risk Tables in {fname}: {len(file_risk_tables)}")
# Check each risk table in PDF
findings = []
for rt in file_risk_tables:
sec = rt['sec_num']
# Find in PDF
matched_pages = []
for p in range(len(doc)):
if p < 45: continue # skip TOC
t = doc[p].get_text()
if sec in t:
matched_pages.append(p)
# Compare with PDF tables
print(f"\n [위험 표] L{rt['start_line']} | {rt['sec_title']} ({', '.join(rt['reasons'])})")
print(f" MD Headers: {rt['headers']}")
print(f" MD Rows ({rt['rows']}): {[r[1][0] if len(r[1])>0 else '' for r in rt['table_rows'][:4]]} ...")
print(f" PDF Matched Pages: {[p+1 for p in matched_pages]}")
# Check table extraction on matched pages
pdf_table_found = False
for p in matched_pages:
page = doc[p]
tabs = page.find_tables()
for t_idx, tab in enumerate(tabs):
df = tab.extract()
if not df or len(df) < 2: continue
# Match table header or contents
df_flat = " ".join([c for row in df for c in row if c])
# Check if headers overlap
overlap = sum(1 for h in rt['headers'] if h and h in df_flat)
if overlap >= max(1, len(rt['headers']) // 2):
pdf_table_found = True
# Check row count and Col 0 structures
pdf_r_cnt = len(df) - 1
md_r_cnt = rt['rows']
# Inspect Col 0 in PDF vs MD
pdf_col0 = [r[0] if r and len(r)>0 and r[0] else '' for r in df[1:]]
md_col0 = [r[1][0] if len(r[1])>0 else '' for r in rt['table_rows']]
# Detect potential mismatch
# Multi-line in pdf col0 vs split rows in md
pdf_col0_lines = sum(len(c.split('\n')) for c in pdf_col0 if c)
# Print comparison snippet
print(f" -> Matched PDF Table on Page {p+1}: PDF rows={pdf_r_cnt} vs MD rows={md_r_cnt}")
# If mismatch in row counts or Col 0 text
if pdf_r_cnt != md_r_cnt:
print(f" [!] ROW COUNT DIFF: PDF {pdf_r_cnt} vs MD {md_r_cnt}")
print(f" PDF Col 0: {pdf_col0[:3]}")
print(f" MD Col 0: {md_col0[:3]}")
findings.append({
'sec': sec,
'line': rt['start_line'],
'page': p + 1,
'type': 'Row count mismatch',
'detail': f"PDF rows={pdf_r_cnt}, MD rows={md_r_cnt}"
})
break
if pdf_table_found:
break
return len(file_risk_tables), findings
if __name__ == '__main__':
target = sys.argv[1] if len(sys.argv) > 1 else 'resources/knowledge/original/원가계산/건설공사_표준품셈/02_토목부문/제1장_도로포장공사.md'
verify_file(target)