- 공공기관2/3 작업본 + 오늘 제출 17곳 D~J 카테고리 셀병합 정상화 - 한국지역난방공사 옵션2(고아셀 F98 수정)+전행 높이17 - 제출_프리랜서2_2026-06-21.zip 생성(17개 xlsx, 2,468행) Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
94 lines
3.6 KiB
Python
94 lines
3.6 KiB
Python
# -*- coding: utf-8 -*-
|
|
import openpyxl, copy, sys, os, shutil, datetime
|
|
sys.stdout.reconfigure(encoding='utf-8')
|
|
FN='한국청소년활동진흥원.xlsx'; BLUE='FFBDD7EE'; NCOL=27
|
|
wb=openpyxl.load_workbook(FN); ws=wb.active; MAXR=ws.max_row
|
|
merged=list(ws.merged_cells.ranges)
|
|
def eff(r,c):
|
|
v=ws.cell(r,c).value
|
|
if v is not None: return v
|
|
for rng in merged:
|
|
if rng.min_row<=r<=rng.max_row and rng.min_col<=c<=rng.max_col:
|
|
return ws.cell(rng.min_row,rng.min_col).value
|
|
return None
|
|
def snap(r): return [copy.copy(ws.cell(r,c)._style) for c in range(1,NCOL+1)]
|
|
|
|
def daebun(url):
|
|
u=str(url or '')
|
|
if '/management/' in u: return '정보공개'
|
|
if 'booking.kywa' in u or '/business/business8' in u: return '국립수련시설'
|
|
if '/business/' in u or '/leader/' in u: return '사업소개'
|
|
if '/pressinfo/' in u: return '알림마당'
|
|
if '/community/' in u: return '국민참여'
|
|
if '/about/' in u or '/membership/' in u: return 'KYWA소개'
|
|
return None # fallback -> caller keeps current D
|
|
|
|
records=[]
|
|
for r in range(3,MAXR+1):
|
|
rec={'vals':{},'style':snap(r),'blue':set()}
|
|
for c in range(1,NCOL+1):
|
|
rec['vals'][c]= eff(r,c) if 4<=c<=10 else ws.cell(r,c).value
|
|
url=ws.cell(r,11).value
|
|
cd,ce,cf,cg=eff(r,4),eff(r,5),eff(r,6),eff(r,7)
|
|
nd=daebun(url) or cd
|
|
if cd==nd: # already 대분류 (정보공개 r3-9)
|
|
ne,nf,ng=ce,cf,cg
|
|
else: # demote current D->E, E->F, F->G
|
|
ne,nf,ng=cd,ce,cf
|
|
new=[nd,ne,nf,ng,None,None,None] # D E F G H I J
|
|
old=[cd,ce,cf,cg]
|
|
for i,c in enumerate(range(4,11)):
|
|
rec['vals'][c]=new[i]
|
|
# blue where D/E/F/G changed
|
|
for i,c in enumerate(range(4,8)):
|
|
if (new[i] or '')!=(old[i] or ''): rec['blue'].add(c)
|
|
records.append(rec)
|
|
|
|
# rewrite
|
|
title=ws.title
|
|
hdr={}
|
|
for rr in (1,2):
|
|
for c in range(1,NCOL+1):
|
|
cell=ws.cell(rr,c); hdr[(rr,c)]=(cell.value,copy.copy(cell._style))
|
|
hdr_merges=[str(m) for m in merged if m.min_row<=2]
|
|
colw={k:v.width for k,v in ws.column_dimensions.items()}
|
|
del wb[title]; ns=wb.create_sheet(title)
|
|
for (rr,c),(v,st) in hdr.items():
|
|
cc=ns.cell(rr,c); cc.value=v; cc._style=st
|
|
for m in hdr_merges: ns.merge_cells(m)
|
|
for col,w in colw.items():
|
|
if w: ns.column_dimensions[col].width=w
|
|
from openpyxl.styles import PatternFill, Font
|
|
bluefill=PatternFill(fill_type='solid',fgColor=BLUE,bgColor=BLUE)
|
|
linkfont=Font(name='맑은 고딕',size=10,color='0000FF',underline='single')
|
|
for i,rec in enumerate(records):
|
|
R=3+i
|
|
for c in range(1,NCOL+1):
|
|
cell=ns.cell(R,c); cell._style=rec['style'][c-1]; cell.value=rec['vals'][c]
|
|
ns.cell(R,2).value=i+1
|
|
kv=rec['vals'][11]
|
|
if kv and isinstance(kv,str) and kv.startswith('http'):
|
|
kc=ns.cell(R,11); kc.hyperlink=kv; kc.font=linkfont
|
|
for c in rec['blue']: ns.cell(R,c).fill=bluefill
|
|
# re-merge D..J boundary-aware
|
|
D0=3; DN=3+len(records)-1
|
|
def val(R,c): return ns.cell(R,c).value
|
|
for c in range(4,11):
|
|
R=D0
|
|
while R<=DN:
|
|
v=val(R,c)
|
|
if v in (None,''): R+=1; continue
|
|
r2=R
|
|
while r2+1<=DN and val(r2+1,c)==v and all(val(r2+1,pc)==val(R,pc) for pc in range(4,c)):
|
|
r2+=1
|
|
if r2>R:
|
|
for rr in range(R+1,r2+1): ns.cell(rr,c).value=None
|
|
ns.merge_cells(start_row=R,start_column=c,end_row=r2,end_column=c)
|
|
R=r2+1
|
|
ns.freeze_panes='A3'
|
|
os.makedirs('_backup',exist_ok=True)
|
|
ts=datetime.datetime.fromtimestamp(os.path.getmtime(FN)).strftime('%Y%m%d_%H%M%S')
|
|
shutil.copy2(FN,os.path.join('_backup',f'_GNB대분류재구성전_{ts}.xlsx'))
|
|
wb.save(FN)
|
|
print('SAVED rows',DN)
|