# -*- coding: utf-8 -*- import openpyxl, re from copy import copy from openpyxl.utils import get_column_letter from openpyxl.styles import PatternFill FN='국방과학연구소.xlsx' BLUE=PatternFill(fill_type='solid', fgColor='FFBDD7EE') U='https://www.add.re.kr/menu?menuId=' # parent menuId -> list of (label, url, L, M, N, O, S) def page(label, mid): return (label, U+mid, '페이지', 1, '어문', '미부착', None) TABS = { 'MENU02790':[page('국제협력','MENU02773'),page('방산수출','MENU02774'),page('수출지원 절차','MENU02775')], 'MENU02776':[page('민·군기술협력','MENU02777'),page('국방기술이전·사업화','MENU02778')], 'MENU02188':[page('故 김용철 님','MENU02192'),page('이충희 님','MENU02193')], 'MENU02191':[page('제1회 수상자','MENU02194'),page('제2회 수상자','MENU02195'),page('제3회 수상자','MENU02565'), page('제4회 수상자','MENU02803'),page('제5회 수상자','MENU02804'),page('제6회 수상자','MENU03106'), page('제7회 수상자','MENU03107'),page('제8회 수상자','MENU03108'),page('제9회 수상자','MENU03109'), page('제10회 수상자','MENU03110')], 'MENU02160':[page('전자정보원','MENU03095'),page('전자저널','MENU03096'),page('전자책','MENU03097'), page('WEB-DB','MENU03098'), ('외부접속안내','https://www.add.re.kr/resources/files/information.zip','사이트',None,None,None,'외부링크')], 'MENU02213':[page('정규직','MENU02214'),page('전문계약직','MENU02215')], 'MENU03037':[page('복리후생','MENU03038'),page('성과포상','MENU03039'),page('교육제도','MENU03040')], } DEL_MENU='MENU02024' # 인사말 duplicate -> delete FILL_F={'MENU02769':'ADD 신문고'} # fill empty F wb=openpyxl.load_workbook(FN) ws=wb.active MAXC=27 def midof(url): if not url: return None m=re.search(r'menuId=([A-Z0-9]+)', str(url)) return m.group(1) if m else None # effective D,E via merges eff={4:{},5:{}} for col in (4,5): for r in range(3, 123): eff[col][r]=ws.cell(r,col).value for mr in ws.merged_cells.ranges: if mr.min_col<=col<=mr.max_col and mr.min_row>=3: top=ws.cell(mr.min_row, col).value for r in range(mr.min_row, mr.max_row+1): eff[col][r]=top # build records recs=[] for r in range(3,123): rec={'src':r, 'new':False, 'mark':set()} rec['D']=eff[4][r]; rec['E']=eff[5][r]; rec['F']=ws.cell(r,6).value for c in range(7,MAXC+1): rec[c]=ws.cell(r,c).value rec['url']=ws.cell(r,11).value hl=ws.cell(r,11).hyperlink rec['hlink']=hl.target if hl else rec['url'] recs.append(rec) # helper find rec by menuId def findidx(mid): for i,rc in enumerate(recs): if midof(rc['url'])==mid: return i return None # Task3: delete 인사말 02024 (carry D to next handled by eff already on next rec) di=findidx(DEL_MENU) assert di is not None, 'del row not found' # ensure following rec keeps D=ADD소개 (it does via eff). just drop. del recs[di] # Task2: fill F for mid,val in FILL_F.items(): i=findidx(mid) assert i is not None recs[i]['F']=val; recs[i]['mark'].add(6) # Task1: tab expansion (process; indices stable because we rebuild list) out=[] for rc in recs: mid=midof(rc['url']) if mid in TABS: tabs=TABS[mid] # reuse parent row for first tab first=tabs[0] rc[7]=first[0] # G label rc['url']=first[1]; rc['hlink']=first[1]; rc[11]=first[1] rc[12]=first[2]; rc[13]=first[3]; rc[14]=first[4]; rc[15]=first[5]; rc[19]=first[6] rc['mark'].update([7,11,12,13,14,15] + ([19] if first[6] else [])) out.append(rc) # children for t in tabs[1:]: ch={'src':rc['src'], 'new':True, 'mark':set(range(4,16))} ch['D']=rc['D']; ch['E']=rc['E']; ch['F']=rc['F'] for c in range(7,MAXC+1): ch[c]=None ch[3]=ws.cell(3,3).value # site name (col C handled separately below) ch[7]=t[0]; ch['url']=t[1]; ch['hlink']=t[1]; ch[11]=t[1] ch[12]=t[2]; ch[13]=t[3]; ch[14]=t[4]; ch[15]=t[5]; ch[19]=t[6] if t[6]: ch['mark'].add(19) out.append(ch) else: out.append(rc) recs=out print('final rows:', len(recs)) # snapshot source styles BEFORE any overwrite stylesnap={} for r in range(3,123): for c in range(1,MAXC+1): stylesnap[(r,c)]=copy(ws.cell(r,c)._style) # ---- WRITE ---- # unmerge data merges (>=3) for mr in list(ws.merged_cells.ranges): if mr.min_row>=3: ws.unmerge_cells(str(mr)) SITE='국방과학연구소' n=len(recs) for i,rc in enumerate(recs): r=3+i src=rc['src'] # style: copy from snapshot of src row for all cols for c in range(1,MAXC+1): ws.cell(r,c)._style=copy(stylesnap[(src,c)]) # values ws.cell(r,1).value=None ws.cell(r,2).value=i+1 # B 순번 ws.cell(r,3).value=SITE # C ws.cell(r,4).value=rc['D'] ws.cell(r,5).value=rc['E'] ws.cell(r,6).value=rc['F'] for c in range(7,MAXC+1): ws.cell(r,c).value=rc.get(c) # K hyperlink kc=ws.cell(r,11) kc.value=rc['url'] kc.hyperlink=rc['hlink'] if rc['url'] else None # blue marks for c in rc['mark']: ws.cell(r,c).fill=BLUE ws.row_dimensions[r].height=15 # clear leftover rows for r in range(3+n, 197): for c in range(1,MAXC+1): cell=ws.cell(r,c) cell.value=None cell.hyperlink=None cell.fill=PatternFill(fill_type=None) # ---- RE-MERGE D,E,F ---- def remerge(col, scope_cols): r=3 while r < 3+n: v=ws.cell(r,col).value if v is None or v=='': r+=1; continue r2=r while r2+1 < 3+n: nv=ws.cell(r2+1,col).value # next row belongs to same group only if its scope matches AND value equals same=True # value: for D compare own; for E/F the body rows were written with eff value too if nv!=v: same=False for sc in scope_cols: if ws.cell(r2+1,sc).value!=ws.cell(r,sc).value: same=False; break if not same: break r2+=1 if r2>r: # blank body values BEFORE merging (merged body cells are read-only) for rr in range(r+1,r2+1): ws.cell(rr,col).value=None ws.merge_cells(start_row=r,start_column=col,end_row=r2,end_column=col) r=r2+1 # For E/F merge we need body rows to have the eff value. Currently children wrote eff D/E/F. # But parent/normal rows wrote raw F (=eff for them). Fill-down F across written rows for groups: # Actually children F were set = parent F (eff). Non-group rows F = own raw. Good. # But we must temporarily fill E down for rows where E blank? We wrote eff E on every rec, so fine. # deepest first so scope columns still hold their values when shallower merges run remerge(6, [4,5]) # F within same D,E remerge(5, [4]) # E within same D remerge(4, []) # D by contiguous equal wb.save(FN) print('saved')