# coding: utf-8 import openpyxl, json, io, copy, re from openpyxl.styles import Font, PatternFill from openpyxl.utils import get_column_letter SKY='FFBDD7EE' wb=openpyxl.load_workbook('한국인터넷진흥원.xlsx'); ws=wb.active NC=27 # AA plan=json.load(io.open('_plan.json',encoding='utf-8')) planmap={p['leaf_row']:p for p in plan} # effective D..J maxr=ws.max_row eff={r:[ws.cell(r,c).value for c in range(4,11)] for r in range(3,maxr+1)} for mr in ws.merged_cells.ranges: if mr.min_col>=4 and mr.max_col<=10: v=ws.cell(mr.min_row,mr.min_col).value for rr in range(mr.min_row,mr.max_row+1): eff[rr][mr.min_col-4]=v # snapshot per source row: values B..AA, styles, rowheight def snap(r): vals={c:ws.cell(r,c).value for c in range(2,NC+1)} vals.update({4+i:eff[r][i] for i in range(7)}) # effective D..J stys={c:copy.copy(ws.cell(r,c)._style) for c in range(2,NC+1)} h=ws.row_dimensions[r].height return {'vals':vals,'stys':stys,'h':h} # build records recs=[] # each: {'vals':{c:v}, 'stys':{c:style}, 'h':, 'newcells':set(cols to sky)} for r in range(3,maxr+1): s=snap(r) rec={'vals':s['vals'],'stys':s['stys'],'h':s['h'],'newcells':set()} if r in planmap: p=planmap[r]; lc=p['leaf_col'] ch=p['children'] # self child = first child with self True self_child=next(c for c in ch if c['self']) # leaf row becomes self-child: add self name at lc+1 rec['vals'][lc+1]=self_child['name'] rec['newcells'].add(lc+1) # clear deeper cols beyond lc+1 on self row for cc in range(lc+2,11): rec['vals'][cc]=None recs.append(rec) # new children for c in ch: if c['self']: continue nr={'vals':{},'stys':{},'h':s['h'],'newcells':set()} # copy path D..lc from leaf (effective) for cc in range(4,11): nr['vals'][cc]=rec['vals'][cc] if cc<=lc else None nr['vals'][lc+1]=c['name'] # B,C nr['vals'][2]=None # renumber later nr['vals'][3]=ws.cell(r,3).value # site name # K..AA nr['vals'][11]=c['url'] nr['vals'][12]=c['L'] nr['vals'][13]=c['M'] # N default if c['ctype']=='URL': nr['vals'][14]='기타'; nr['vals'][15]=None # site: O none else: nr['vals'][14]='어문'; nr['vals'][15]='미부착' nr['vals'][16]=None; nr['vals'][17]=None for cc in range(18,NC+1): nr['vals'][cc]=None # styles: copy from leaf row per column for cc in range(2,NC+1): nr['stys'][cc]=copy.copy(s['stys'][cc]) nr['newcells']=set([lc+1,11,12,13,14,15]) recs.append(nr) else: recs.append(rec) # write out: clear data region first (rows 3..) values+styles+merges # remove existing merges in D..J (and any) for data rows for mr in list(ws.merged_cells.ranges): if mr.min_row>=3: ws.unmerge_cells(str(mr)) # clear old data rows fully old_max=ws.max_row for r in range(3,old_max+1): for c in range(2,NC+1): cell=ws.cell(r,c); cell.value=None; cell.hyperlink=None N=len(recs) # write records for i,rec in enumerate(recs): r=3+i ws.row_dimensions[r].height=rec['h'] if rec['h'] else 17 for c in range(2,NC+1): cell=ws.cell(r,c) cell.value=rec['vals'].get(c) cell._style=copy.copy(rec['stys'][c]) # B renumber ws.cell(r,2).value=i+1 # K hyperlinks (blue underline) linkfont=Font(name='맑은 고딕',size=10,color='FF0000FF',underline='single') for i,rec in enumerate(recs): r=3+i url=rec['vals'].get(11) kc=ws.cell(r,11) if url and isinstance(url,str) and url.startswith('http'): kc.hyperlink=url f=copy.copy(kc.font) kc.font=Font(name=f.name or '맑은 고딕',size=f.sz or 10,color='FF0000FF',underline='single') # sky-blue new/changed cells fill=PatternFill(fill_type='solid',fgColor=SKY,bgColor=SKY) for i,rec in enumerate(recs): r=3+i for c in rec['newcells']: ws.cell(r,c).fill=fill # re-merge D..J by hierarchical equal-value runs def colvals(c): return [rec['vals'].get(c) for rec in recs] for c in range(4,11): i=0 while i1: ws.merge_cells(start_row=3+i,start_column=c,end_row=3+(j-1),end_column=c) i=j # center valign merged category cells already inherited from style; ensure header freeze ok wb.save('한국인터넷진흥원.xlsx') print('done rows=%d (was %d)'%(N, old_max-2))