Files
Pier_v1/수량산출/tools/analyze.py
T

33 lines
4.7 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import json,re,hashlib,zipfile,warnings
from pathlib import Path
import openpyxl
warnings.filterwarnings('ignore',category=UserWarning)
root=Path(__file__).resolve().parent.parent
source=root/'교각수량산출.xlsx';w=openpyxl.load_workbook(source,data_only=False);v=openpyxl.load_workbook(source,data_only=True)
data={s.title:{c.coordinate:{'value':c.value,'cached':v[s.title][c.coordinate].value,'format':c.number_format} for row in s for c in row if c.value is not None} for s in w}
variables=[]
for r in range(18,47):
variables.append(dict(key=f'r{r}',row=r,name=w['3.2 관련변수'][f'G{r}'].value,symbol=w['3.2 관련변수'][f'L{r}'].value,unit='%' if r==28 else '개' if r in (39,42) else 'm'))
def merged_label(sheet,column,row):
cell=sheet.cell(row,column)
for merged in sheet.merged_cells.ranges:
if merged.min_row<=row<=merged.max_row and merged.min_col<=column<=merged.max_col:return sheet.cell(merged.min_row,merged.min_col).value
return cell.value
rows=[]
for r in list(range(6,32))+list(range(50,77)):
s=w['3.3 수량산출_P1'];b=merged_label(s,2,r);e=merged_label(s,5,r);g=merged_label(s,7,r)
rows.append(dict(row=r,cell=f'W{r}',name=' / '.join(str(x).replace('\n',' ') for x in [b,e,g] if x),unit=s[f'J{r}'].value,description=s[f'K{r}'].value))
meta=dict(sha256=hashlib.sha256(source.read_bytes()).hexdigest(),sheets=[dict(name=s.title,state=s.sheet_state,rows=s.max_row,cols=s.max_column,formulas=sum(c.data_type=='f' for row in s for c in row),merged=len(s.merged_cells.ranges),print_area=str(s.print_area),hidden_rows=[i for i,d in s.row_dimensions.items() if d.hidden],hidden_columns=[i for i,d in s.column_dimensions.items() if d.hidden]) for s in w],names={k:str(x.attr_text) for k,x in w.defined_names.items()},external_links=len(w._external_links),macros=any('vbaProject' in n for n in zipfile.ZipFile(source).namelist()))
(root/'template-data.ts').write_text('export type Cell={value:string|number|boolean|null;cached:string|number|boolean|null;format:string};\nexport const template:Record<string,Record<string,Cell>>='+json.dumps(data,ensure_ascii=False,separators=(',',':'),default=str)+';\nexport const variables='+json.dumps(variables,ensure_ascii=False)+';\nexport const quantityRows='+json.dumps(rows,ensure_ascii=False)+';\nexport const templateHash='+json.dumps(meta['sha256'])+';\n',encoding='utf8')
(root/'docs/template-analysis.json').write_text(json.dumps(meta,ensure_ascii=False,indent=2),encoding='utf8')
md=['# 원본 수량산출서 분석','',f"원본 SHA256: `{meta['sha256']}`",'', '## 시트 구성','']
for s in meta['sheets']:md.append(f"- {s['name']}: {s['rows']}행 × {s['cols']}열, 수식 {s['formulas']}개, 병합 {s['merged']}개, {s['state']}")
md+=['','## 최소 입력과 원본 수식 매핑','','|변수|기호|단위|입력셀(P1)|','|---|---|---|---|']
for x in variables:md.append(f"|{x['name']}|{x['symbol']}|{x['unit']}|3.2 관련변수!N{x['row']}|")
md+=['','## 수량항목','','|항목|필요 입력(직접 참조)|실제 수식/고정값|원본 위치|','|---|---|---|---|']
for r in rows:
f=data['3.3 수량산출_P1'][r['cell']]['value'];refs=sorted(set(re.findall(r'\$?BG\$?\d+',str(f))))
md.append(f"|{r['name']}|{', '.join(refs)}|`{f}`|3.3 수량산출_P1!{r['cell']}|")
md+=['','## 원본의 중요한 제약','','- 설명식과 실제 수식이 불일치하는 부분을 수정하지 않는다. W51은 표시 Hcol 설명과 달리 Zb-H1-H2-Zs+Zf를 사용한다. 원본 P1 값은 236.80068785698427㎡이다. 기둥 실제 높이와 일치한다고 간주하지 않는다.','- AO8은 풍화암 BG10을 사용하고 AO12는 연암 BG11을 사용한다. 그대로 재현한다.','- W50 설명에는 2(Ac1+Ac2+Ac3)이 있으나 실제 수식은 해당 부분에 계수 2가 없다. W58과 공유되는 곡률 ASIN 식을 그대로 보존한다.','- W63의 규격 표기는 T=300mm이지만 수식은 0.05m를 사용한다. 규격 텍스트와 계산 기준을 검토해야 한다.','- W67은 수식이 아닌 철근량 직접값, K68/K69/K74는 시험/시설 횟수 직접값이다. 자동 수량으로 가장하지 않는다.','- 실제 수식에 ROUND 계열은 없다. 원값으로 계산 후 원본 표시형식(상세 주로 소수3자리, 집계 주로2자리)만 적용한다.','- 13시트 모두 visible, 데이터 유효성 규칙 없음, 외부통합문서 링크와 매크로 없음. 이름 정의 3개는 원본 보존하며 계산 수식에서 사용되지 않는다.','- 토공 심도는 0~8m 구간만 있다. 8m 초과는 원본 적용 범위 밖이다.','- 단일 원형기둥·단일 직사각형기초·균일 말뚝의 곡률 두부보 산식이다. 다기둥/꺾인/비대칭 구조의 수량을 임의 배수로 확장하지 않는다.']
(root/'docs/ANALYSIS.md').write_text('\n'.join(md),encoding='utf8')