import sys,io,re,json,copy import openpyxl from openpyxl.utils import get_column_letter sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding='utf-8') SRC='충청남도_아산시.xlsx'; OUT='_rebuilt.xlsx' wb=openpyxl.load_workbook(SRC); ws=wb.active plan=json.load(open('_plan_out.json',encoding='utf-8')) existing={int(k):v for k,v in plan['existing'].items()} # row -> [row,L,M] groups={g['row']:g for g in plan['groups']} INFLATED={'어문,이미지,영상','어문,이미지,영상,오디오','어문,영상'} # effective (merge-filled) value getter mergemap={} for mr in ws.merged_cells.ranges: for rr in range(mr.min_row,mr.max_row+1): for cc in range(mr.min_col,mr.max_col+1): mergemap[(rr,cc)]=(mr.min_row,mr.min_col) def eff(r,c): t=mergemap.get((r,c),(r,c)); return ws.cell(*t).value def raw(r,c): return ws.cell(r,c).value # capture reference styles per column from row 5 (data row) REFROW=5 refstyle={} for c in range(2,28): cell=ws.cell(REFROW,c) refstyle[c]=dict(font=copy.copy(cell.font),border=copy.copy(cell.border), fill=copy.copy(cell.fill),alignment=copy.copy(cell.alignment), number_format=cell.number_format) # K hyperlink ref style kcell=ws.cell(5,11); kstyle=dict(font=copy.copy(kcell.font)) # ---- build records ---- # each record: dict col(2..27)->value ; D-J=4..10, K=11, L=12, M=13, N=14, O=15, P=16, Q=17, R=18, S=19, T..AA=20..27 records=[] def base_cols(r): """effective D..J for row r""" return {c:eff(r,c) for c in range(4,11)} # rows 3-15 keep as-is (effective D-J + raw K-AA) for r in range(3,16): if not raw(r,11): continue rec={} for c in range(4,11): rec[c]=eff(r,c) for c in range(11,28): rec[c]=raw(r,c) records.append(rec) # rows 16-270 for r in range(16,271): if not raw(r,11): continue if r in groups: g=groups[r]; col=g['col'] parent={c:eff(r,c) for c in range(4,col+1)} # D..col (group label at col) for k in g['kids']: rec={} for c in range(4,col+1): rec[c]=parent[c] rec[col+1]=k['label'] rec[11]=k['url']; rec[12]=k['L']; rec[13]=k['M'] rec[14]=k['N']; rec[15]=k['O']; rec[19]=k['S'] or None records.append(rec) else: L=existing[r][1]; M=existing[r][2] rec={} for c in range(4,11): rec[c]=eff(r,c) rec[11]=raw(r,11); rec[12]=L; rec[13]=(M if M!='' else None) # N curN=raw(r,14) if L=='사이트': rec[14]=None elif L=='페이지': rec[14]='어문' if curN in INFLATED else curN else: rec[14]=curN # O,P,Q,R,S,T-AA rec[15]=(None if L=='사이트' else raw(r,15)) for c in [16,17,18,19,20,21,22,23,24,25,26,27]: rec[c]=raw(r,c) records.append(rec) print('total records:',len(records)) # ---- clear data region & unmerge data merges ---- hdr_keep={'B1:R1','S1:W1','Y1:AA1'} for mr in list(ws.merged_cells.ranges): if str(mr) not in hdr_keep: ws.unmerge_cells(str(mr)) maxr=ws.max_row for r in range(3,maxr+1): for c in range(2,28): ws.cell(r,c).value=None ws.cell(r,c).hyperlink=None # ---- write records ---- def apply_style(cell,c): s=refstyle[c] cell.font=copy.copy(s['font']); cell.border=copy.copy(s['border']) cell.fill=copy.copy(s['fill']); cell.alignment=copy.copy(s['alignment']) cell.number_format=s['number_format'] for i,rec in enumerate(records): R=3+i ws.cell(R,2).value=i+1 # B 순번 apply_style(ws.cell(R,2),2) for c in range(4,28): v=rec.get(c) cell=ws.cell(R,c) cell.value=v apply_style(cell,c) # K hyperlink kv=rec.get(11) if kv and isinstance(kv,str) and kv.startswith('http'): ws.cell(R,11).hyperlink=kv ws.cell(R,11).font=copy.copy(kstyle['font']) # C 사이트명 always 아산시 ws.cell(R,3).value='아산시'; apply_style(ws.cell(R,3),3) last=3+len(records)-1 # ---- hierarchical re-merge D..I (4..9) ---- # capture full (uncleared) category values FIRST so merge keys stay stable catvals={} for i in range(len(records)): R=3+i for c in range(4,11): catvals[(R,c)]=ws.cell(R,c).value for c in range(4,10): r=3 while r<=last: key=tuple(catvals[(r,cc)] for cc in range(4,c+1)) v=catvals[(r,c)] if v in (None,''): r+=1; continue r2=r while r2+1<=last: key2=tuple(catvals[(r2+1,cc)] for cc in range(4,c+1)) if key2==key: r2+=1 else: break if r2>r: for rr in range(r+1,r2+1): ws.cell(rr,c).value=None ws.merge_cells(start_row=r,start_column=c,end_row=r2,end_column=c) r=r2+1 wb.save(OUT) print('saved',OUT,'rows 3..',last)