공공기관2 작업 중. _temp 몽타주(재생성가능)는 제외. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
147 lines
5.5 KiB
Python
147 lines
5.5 KiB
Python
# -*- coding: utf-8 -*-
|
||
"""주택금융 정보목록 3보드에 분류 카테고리 자식행(게시판) 국소삽입.
|
||
전체 unmerge→삽입→fill-down→재병합(D~J)→헤더재병합→B재부번→K링크. mtime가드+백업."""
|
||
import sys, io, os, glob
|
||
sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding='utf-8')
|
||
from copy import copy
|
||
import openpyxl
|
||
from openpyxl.styles import Font
|
||
|
||
BASE = r"D:\01.프로젝트\DB수집\작업파일\공공기관2"
|
||
fp = [x for x in glob.glob(os.path.join(BASE, "5.*", "*.xlsx")) if "_backup" not in x and "~$" not in x][0]
|
||
COLS = list(range(4, 11)) # D..J
|
||
HEADER_MERGES = ['B1:R1', 'S1:W1', 'Y1:AA1']
|
||
|
||
# (보드URL키, 부모라벨, [(카테고리, srCategoryId, M)])
|
||
BOARDS = [
|
||
("sub06_04_01", "표준정보목록",
|
||
[("감사","24",8),("윤리","25",9),("기획","26",12),("재정","27",4),("법무","28",3),("노무","29",4),
|
||
("인사","30",3),("총무","31",23),("회계","32",16),("정보화","33",11),("홍보","34",5),("대외","35",3)], None),
|
||
("sub06_04_02", "고유정보목록",
|
||
[("주택구입","36",21),("주택보증","37",20),("주택연금","38",7),("유동화증권","39",19),("신용회복","40",5),("재무","41",7),("기타","42",20)], None),
|
||
("sub06_04_03", "즐겨찾는목록",
|
||
[("감사","24",5),("윤리","25",5),("기획","26",5),("재정","27",4),("법무","28",3),("노무","29",4),
|
||
("인사","30",3),("총무","31",5),("회계","32",5),("정보화","33",5),("홍보","34",5),("대외","35",3)], "즐겨찾는목록 카테고리 건수=페이지표시 기준(확인필요)"),
|
||
]
|
||
|
||
mt0 = os.path.getmtime(fp)
|
||
wb = openpyxl.load_workbook(fp)
|
||
ws = wb.active
|
||
|
||
# effective 경로 사전(부모 깊이 파악용)
|
||
def eff_paths():
|
||
last = {c: None for c in COLS}; res = {}
|
||
for r in range(3, ws.max_row+1):
|
||
for c in COLS:
|
||
v = ws.cell(r,c).value
|
||
if v not in (None,''):
|
||
last[c]=v
|
||
for cc in COLS:
|
||
if cc>c: last[cc]=None
|
||
res[r] = dict(last)
|
||
return res
|
||
paths = eff_paths()
|
||
|
||
def find_parent(key, label):
|
||
for r in range(3, ws.max_row+1):
|
||
k = str(ws.cell(r,11).value or '')
|
||
if key in k and 'srCategoryId' not in k:
|
||
if label in paths[r].values():
|
||
return r
|
||
return None
|
||
|
||
# 삽입 계획 (bottom-up)
|
||
plans = []
|
||
for key, label, cats, note in BOARDS:
|
||
pr = find_parent(key, label)
|
||
if not pr:
|
||
print(f"[{key}/{label}] 부모행 없음 — 스킵"); continue
|
||
ppath = paths[pr]
|
||
lc = max([c for c in COLS if ppath.get(c)], default=4)
|
||
childcol = min(lc+1, 10)
|
||
base_url = str(ws.cell(pr,11).value).split('?')[0]
|
||
plans.append((pr, childcol, base_url, cats, note, label))
|
||
print(f"[{label}] 부모 r{pr} childcol={childcol} +{len(cats)}")
|
||
plans.sort(key=lambda x: -x[0])
|
||
|
||
# 0) 전체 unmerge (insert 전에 — insert_rows의 merge손상 방지)
|
||
for m in [str(x) for x in ws.merged_cells.ranges]:
|
||
ws.unmerge_cells(m)
|
||
|
||
# 1) 삽입 (bottom-up)
|
||
for pr, childcol, base_url, cats, note, label in plans:
|
||
n = len(cats)
|
||
ws.insert_rows(pr+1, n)
|
||
for i,(nm,cid,m) in enumerate(cats):
|
||
rr = pr+1+i
|
||
for c in range(2, 28):
|
||
src = ws.cell(pr,c)
|
||
if src.has_style:
|
||
d=ws.cell(rr,c)
|
||
d.font=copy(src.font); d.fill=copy(src.fill); d.border=copy(src.border)
|
||
d.alignment=copy(src.alignment); d.number_format=src.number_format
|
||
ws.cell(rr,3).value = ws.cell(pr,3).value
|
||
ws.cell(rr,childcol).value = nm
|
||
ws.cell(rr,11).value = f"{base_url}?mode=list&srCategoryId={cid}"
|
||
ws.cell(rr,12).value = '게시판'
|
||
ws.cell(rr,13).value = m
|
||
ws.cell(rr,14).value = '어문'
|
||
ws.cell(rr,15).value = '미부착'
|
||
if note: ws.cell(rr,19).value = note
|
||
if label == '즐겨찾는목록':
|
||
ws.cell(pr,12).value = '게시판'
|
||
|
||
# endr
|
||
endr=2
|
||
for r in range(3,ws.max_row+1):
|
||
if ws.cell(r,2).value is not None or any(ws.cell(r,c).value for c in COLS) or ws.cell(r,11).value:
|
||
endr=r
|
||
|
||
# 3) fill-down
|
||
last={c:None for c in COLS}
|
||
for r in range(3,endr+1):
|
||
for c in COLS:
|
||
v=ws.cell(r,c).value
|
||
if v not in (None,''):
|
||
last[c]=v
|
||
for cc in COLS:
|
||
if cc>c: last[cc]=None
|
||
for c in COLS:
|
||
if ws.cell(r,c).value in (None,'') and last[c] not in (None,''):
|
||
ws.cell(r,c).value=last[c]
|
||
|
||
# 4) 재병합 (동일값+상위동일, 깊은열부터)
|
||
def upper_same(r1,r2,c):
|
||
return all(ws.cell(r1,cc).value==ws.cell(r2,cc).value for cc in COLS if cc<c)
|
||
for c in sorted(COLS, reverse=True):
|
||
r=3
|
||
while r<=endr:
|
||
v=ws.cell(r,c).value
|
||
if v in (None,''): r+=1; continue
|
||
r2=r
|
||
while r2+1<=endr and ws.cell(r2+1,c).value==v and upper_same(r,r2+1,c):
|
||
r2+=1
|
||
if r2>r:
|
||
ws.merge_cells(start_row=r,start_column=c,end_row=r2,end_column=c)
|
||
r=r2+1
|
||
|
||
# 5) 헤더 재병합
|
||
for m in HEADER_MERGES:
|
||
ws.merge_cells(m)
|
||
|
||
# 6) B재부번 + K링크
|
||
bn=0
|
||
for r in range(3,endr+1):
|
||
if any(ws.cell(r,c).value for c in COLS) or ws.cell(r,11).value:
|
||
bn+=1; ws.cell(r,2).value=bn
|
||
k=ws.cell(r,11); u=k.value
|
||
if u and isinstance(u,str) and u.startswith('http'):
|
||
k.hyperlink=u
|
||
old=k.font
|
||
k.font=Font(name=old.name or '맑은 고딕', size=old.size or 11, color='0000FF', underline='single')
|
||
|
||
if abs(os.path.getmtime(fp)-mt0)>0.5:
|
||
print("⚠️ABORT: mtime 변경(동시편집). 저장안함."); sys.exit(1)
|
||
wb.save(fp)
|
||
print(f"저장완료. 데이터행 {bn}")
|