import zipfile,hashlib,xml.etree.ElementTree as E,json,re,warnings from pathlib import Path warnings.filterwarnings('ignore') import openpyxl root=Path(__file__).resolve().parent.parent source=root/'교각수량산출.xlsx';output=root/'outputs/verification/운곡교P1_모델연동.xlsx' ns={'s':'http://schemas.openxmlformats.org/spreadsheetml/2006/main'} with zipfile.ZipFile(source) as a,zipfile.ZipFile(output) as b: required=[p for p in a.namelist() if any(t in p for t in ('xl/media/','xl/drawings/','xl/styles.xml','xl/theme/','xl/printerSettings/'))] changed=[p for p in required if a.read(p)!=b.read(p)] assert not changed,changed sheet='xl/worksheets/sheet8.xml';sa=E.fromstring(a.read(sheet));sb=E.fromstring(b.read(sheet)) formula=lambda s:{c.attrib['r']:E.tostring(c.find('s:f',ns),encoding='unicode') for c in s.findall('.//s:c',ns) if c.find('s:f',ns)is not None} original_formulas=formula(sa);output_formulas=formula(sb) changed_formulas=[c for c in original_formulas if original_formulas[c]!=output_formulas.get(c)] assert set(changed_formulas).issubset({'W50','W58'}),changed_formulas for p in [p for p in a.namelist() if re.fullmatch(r'xl/worksheets/sheet\d+.xml',p)]: ra=E.fromstring(a.read(p));rb=E.fromstring(b.read(p)) for tag in ['mergeCells','pageSetup','pageMargins','printOptions','rowBreaks','colBreaks','cols']: x=ra.find('s:'+tag,ns);y=rb.find('s:'+tag,ns) assert (E.tostring(x)if x is not None else None)==(E.tostring(y)if y is not None else None),(p,tag) w=openpyxl.load_workbook(output,data_only=False);v=openpyxl.load_workbook(output,data_only=True) errors=[(s.title,c.coordinate,c.value) for s in v for row in s for c in row if c.data_type=='e'] assert not errors,errors assert all(w['3.3 수량산출_P2'][f'W{r}'].value is None for r in list(range(6,32))+list(range(50,77))) report={'sheets':len(w.sheetnames),'protected_parts_byte_identical':len(required),'active_pier_formulas_total':len(formula(sa)),'active_pier_formulas_unchanged':len(formula(sa))-len(changed_formulas),'straight_limit_formulas':changed_formulas,'merges_print_settings_column_widths_preserved':True,'cached_excel_errors':errors,'unused_pier_quantities_blank':True,'template_sha256':hashlib.sha256(source.read_bytes()).hexdigest(),'model_linked_values':{f'N{r}':v['3.2 관련변수'][f'N{r}'].value for r in [18,19,24,25,31,32,33,34,35,36,38,39,42]},'native_excel_recalculation':'not_verified: Excel COM rejected calls after initialization; artifact recalculation and original cached-result comparison used'} (root/'docs/package-verification.json').write_text(json.dumps(report,ensure_ascii=False,indent=2),encoding='utf8') print(json.dumps(report,ensure_ascii=False))