90 lines
5.7 KiB
Python
90 lines
5.7 KiB
Python
"""Independent arithmetic and serialized-output checks for the audited 12 samples."""
|
|
import json, math, hashlib
|
|
from pathlib import Path
|
|
from openpyxl import load_workbook, Workbook
|
|
from openpyxl.styles import Font, PatternFill, Alignment
|
|
from sample_support import ROOT, BASE
|
|
|
|
out=ROOT/'추출결과_12개교량_20261029'
|
|
summary=json.loads((out/'추출요약.json').read_text(encoding='utf-8'))
|
|
assert len(summary)==12
|
|
checked=0;piers=0;bearings=0;issues=[]
|
|
for s in summary:
|
|
path=Path(s['file']);data=json.loads(path.with_suffix('.json').read_text(encoding='utf-8'))
|
|
cells=data['cells'];wb=load_workbook(path)
|
|
for sheet,values in cells.items():
|
|
for addr,value in values.items():
|
|
actual=wb[sheet][addr].value
|
|
assert actual==value,(path.name,sheet,addr,actual,value)
|
|
if isinstance(value,(int,float)):assert math.isfinite(value)
|
|
checked+=1
|
|
for addr,p in cells['Pier'].items():
|
|
if not addr.startswith('B'):continue
|
|
r=addr[1:];piers+=1
|
|
assert cells['Pier']['K'+r]==(2 if cells['Pier']['C'+r]=='π형' else 1)
|
|
br=[a[1:] for a,v in cells['Bearing'].items() if a.startswith('B') and v==p]
|
|
ip=[a[1:] for a,v in cells['IP'].items() if a.startswith('B') and v==p]
|
|
assert len(ip)==3
|
|
def xy(sheet,r):return tuple(cells[sheet][c+r] for c in 'DE')
|
|
pts=[xy('Bearing',b) for b in br]
|
|
assert all(all(isinstance(x,(int,float)) for x in point) for point in pts)
|
|
maximum=max(math.dist(a,b) for a in pts for b in pts)*1000
|
|
assert cells['Pier']['I'+r]==(math.floor(round(maximum,6)/500)+1)*500
|
|
ips=[xy('IP',i) for i in ip]
|
|
assert len(set(ips))==3
|
|
length=math.dist(ips[0],ips[2])*1000
|
|
assert cells['Pier']['J'+r]==(math.floor(round(length,6)/500)+1)*500
|
|
for b in br:assert cells['Bearing']['H'+b] in ['전열','후열']
|
|
bearings+=s['bearings']
|
|
for rec in data['records']:
|
|
if rec['value'] is None and not(rec['sheet']=='IP' and rec['cell'].startswith('F')):
|
|
row=rec['cell'][1:];p=cells[rec['sheet']].get('B'+row)
|
|
issues.append([s['bridge'],p,rec['sheet'],rec['cell'],wb[rec['sheet']][rec['cell'][0]+'2'].value,rec['status'],' / '.join(rec['messages'])])
|
|
for e in rec['evidence']:assert e['doc'] in data['sources']
|
|
wb.close()
|
|
inventory=json.loads((BASE/'inventory.json').read_text(encoding='utf-8'))
|
|
assert len(inventory)==127
|
|
for src in inventory:
|
|
assert 'error' not in src,src
|
|
assert hashlib.sha256(Path(src['path']).read_bytes()).hexdigest()==src['sha256']
|
|
assert (piers,bearings)==(64,652)
|
|
|
|
book=Workbook();ws=book.active;ws.title='추출현황'
|
|
ws.append(['Sample','교량','교각 수','받침 수','미확정 셀 수','결과 파일'])
|
|
for s in summary:
|
|
ws.append([s['sample'],s['bridge'],s['piers'],s['bearings'],s['missing'],Path(s['file']).name])
|
|
ws.cell(ws.max_row,6).hyperlink=Path(s['file']).name
|
|
ws.cell(ws.max_row,6).style='Hyperlink'
|
|
ws.append(['합계','',piers,bearings,len(issues)])
|
|
review=book.create_sheet('미확정목록');review.append(['교량','교각','시트','셀','항목','상태','사유']);
|
|
for row in issues:review.append(row)
|
|
rules=book.create_sheet('적용기준')
|
|
for row in [
|
|
['항목','기준 / 범위'],
|
|
['교각형식','정면/평면 대표 형상 판독; T형 1개, π형 2개. 산외교 P9~P11 π형, 나머지 T형.'],
|
|
['두부보 EL / 받침 Z','ELEVATION 교좌면 EL 최소 / 교량받침 EL(몰탈상단).'],
|
|
['폭 / 길이','최대 받침 XY 거리 / IP1~IP3 XY 거리를 엄격히 초과하는 다음 500mm. 설계 확정 치수 아님.'],
|
|
['말뚝','길이 최소값 mm. 직접기초 확인 시 0, 길이 미확인/VAR은 빈칸.'],
|
|
['IP','도면 좌표 지시점 위→아래. 계문철교 원본 두 번째 P1 블록은 도면 위치에 따라 P2로 보정.'],
|
|
['기초 출처','종평면도. 목동교 좌/우 EL 및 기양천교 상충 EL은 병기 후 미확정.'],
|
|
['대호지교','종평면도 및 ELEVATION 산출근거도 부재: 관련 수치 빈칸.'],
|
|
['기존 엑셀 사용 범위','대상 교각/방향/지시점 대조용. 숫자는 CAD 문자에서 재추출. 이 배치 스크립트는 12개 샘플에 맞춘 판독 보정 포함.'],
|
|
['검증 범위','12개 XLSX 재읽기, 수치/단위/치수 산식/전후열/64교각·652받침/127원본 DWG 해시 검사.'],
|
|
['검증 한계','동일 작성자의 자기검증. 형상은 대표도면 표본 판독이며 모든 원도면의 독립 전수 육안 검증은 아님. 미확정값은 추정 대입하지 않음.'],
|
|
['연동','각 교량 XLSX 및 동일 이름 JSON/CSV 제공. 배포 wheel 재빌드 및 GUI 통합은 이번 배치에 포함하지 않음.']]:rules.append(row)
|
|
for sh in book:
|
|
sh.freeze_panes='A2';sh.auto_filter.ref=sh.dimensions
|
|
for c in sh[1]:c.font=Font(name='맑은 고딕',bold=True,color='FFFFFF');c.fill=PatternFill('solid',fgColor='203864')
|
|
for row in sh.iter_rows(min_row=2):
|
|
for c in row:c.font=Font(name='맑은 고딕',size=10);c.alignment=Alignment(vertical='top',wrap_text=True)
|
|
for col in 'ABCDEFG':sh.column_dimensions[col].width=18
|
|
sh.row_dimensions[1].height=28
|
|
ws.column_dimensions['F'].width=55;review.column_dimensions['G'].width=100
|
|
rules.column_dimensions['A'].width=27;rules.column_dimensions['B'].width=115
|
|
for r in range(2,rules.max_row+1):rules.row_dimensions[r].height=42
|
|
book.save(out/'00_추출현황_미확정목록.xlsx')
|
|
report=dict(xlsx_count=12,piers=piers,bearings=bearings,cells_checked=checked,source_dwg_hashes_checked=len(inventory),unresolved_cells=len(issues),checks='PASS',independent_review=False)
|
|
(out/'검증결과.json').write_text(json.dumps(report,ensure_ascii=False,indent=2),encoding='utf-8')
|
|
print(json.dumps(report,ensure_ascii=False))
|
|
|