DB_JOB/작업파일/완료/광역_사이트맵/충청남도/9.서천군/_rebuild.py
hehihoho3 df16c98366 백업: DB수집 전체 스냅샷 (공공기관2 정리 전)
공공기관2 작업 중. _temp 몽타주(재생성가능)는 제외.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-18 18:15:40 +09:00

184 lines
6.4 KiB
Python

# -*- coding: utf-8 -*-
import openpyxl, json, sys, re
from copy import copy
from openpyxl.utils import get_column_letter
sys.stdout.reconfigure(encoding='utf-8')
SRC = r'충청남도_서천군.xlsx'
OUT = r'_충청남도_서천군_NEW.xlsx' # temp; swap after verify
plan = json.load(open('_plan.json', encoding='utf-8'))
plan_by_row = {g['row']: g for g in plan}
wb = openpyxl.load_workbook(SRC)
ws = wb.active
NCOL = 27
DATA_LO, DATA_HI = 3, 276 # real data (rows 277+ empty/junk)
# ---- merge key maps for D(4) and E(5) ----
def covering(col, r):
for m in ws.merged_cells.ranges:
if m.min_col <= col <= m.max_col and m.min_row <= r <= m.max_row:
return str(m)
return None
def keymap(col):
km = {}
for r in range(DATA_LO, DATA_HI+1):
c = covering(col, r)
km[r] = c if c else f'{get_column_letter(col)}{r}'
return km
DKEY = keymap(4)
EKEY = keymap(5)
def kind_fields(kind, total=None):
if kind == 'site':
return dict(L='사이트', M=None, N=None, O=None)
if kind == 'bbs':
return dict(L='게시판', M=(total if total is not None else 0), N='어문', O='미부착')
# page / filter
return dict(L='페이지', M=1, N='어문', O='미부착')
# ---- build out_rows ----
# each: {'tpl': src_row, 'over': {col_letter:value}, 'klink': href_or_None,
# 'dkey':, 'ekey':, 'fgrp':, 'ggrp':}
out = []
fgrp_id = 0
for r in range(DATA_LO, DATA_HI+1):
base = {'tpl': r, 'over': {}, 'klink': None, 'dkey': DKEY[r], 'ekey': EKEY[r],
'fgrp': None, 'ggrp': None}
out.append(base)
if r not in plan_by_row:
continue
g = plan_by_row[r]
fgrp_id += 1
if g.get('slave'):
leaves = g['leaves']
l1 = leaves[0]
f = kind_fields('filter') # boards -> 게시판
base['over'] = {'G': l1['btxt'], 'H': l1['ctxt'], 'K': l1['url'],
'L': '게시판', 'M': l1['total'], 'N': '어문', 'O': '미부착'}
base['klink'] = l1['url']
base['fgrp'] = ('F', fgrp_id)
base['ggrp'] = ('G', fgrp_id, l1['b'])
for lf in leaves[1:]:
nr = {'tpl': r, 'dkey': DKEY[r], 'ekey': EKEY[r],
'over': {'C': '서천군', 'F': None, 'G': lf['btxt'], 'H': lf['ctxt'],
'K': lf['url'], 'L': '게시판', 'M': lf['total'], 'N': '어문', 'O': '미부착'},
'klink': lf['url'], 'fgrp': ('F', fgrp_id), 'ggrp': ('G', fgrp_id, lf['b'])}
out.append(nr)
else:
tabs = g['tabs']
midx = next((i for i,t in enumerate(tabs) if t['norm']==g['norm']), None)
if midx is not None:
base['over']['G'] = tabs[midx]['label']
others = [t for i,t in enumerate(tabs) if i != midx]
else:
t0 = tabs[0]
kf = kind_fields(t0['kind'], t0.get('total'))
base['over'].update({'G': t0['label'], 'K': t0['href'], **kf})
base['klink'] = t0['href']
others = tabs[1:]
ffull = g['F']
base['fgrp'] = ('F', fgrp_id) if ffull is not None else None
for t in others:
kf = kind_fields(t['kind'], t.get('total'))
nr = {'tpl': r, 'dkey': DKEY[r], 'ekey': EKEY[r],
'over': {'C': '서천군', 'F': None, 'G': t['label'],
'K': t['href'], **kf},
'klink': t['href'],
'fgrp': (('F', fgrp_id) if ffull is not None else None), 'ggrp': None}
out.append(nr)
print('total out data rows:', len(out))
# ---- write into a NEW worksheet ----
nwb = openpyxl.Workbook()
nws = nwb.active
nws.title = ws.title
# column widths
for col, dim in ws.column_dimensions.items():
nws.column_dimensions[col].width = dim.width
nws.column_dimensions[col].hidden = dim.hidden
nws.sheet_format = copy(ws.sheet_format)
try: nws.freeze_panes = ws.freeze_panes
except Exception: pass
def copy_cell(srow, scol, drow, dcol, value='__keep__', link='__none__'):
s = ws.cell(srow, scol); d = nws.cell(drow, dcol)
if s.has_style:
d.font = copy(s.font)
d.fill = copy(s.fill)
d.border = copy(s.border)
d.alignment = copy(s.alignment)
d.protection = copy(s.protection)
d.number_format = s.number_format
d.value = s.value if value == '__keep__' else value
if link != '__none__':
if link:
d.hyperlink = link
else:
if s.hyperlink is not None:
d.hyperlink = copy(s.hyperlink)
return d
# header rows 1-2
for r in (1,2):
h = ws.row_dimensions[r].height
if h: nws.row_dimensions[r].height = h
for c in range(1, NCOL+1):
copy_cell(r, c, r, c)
# data rows
LETTER2COL = {get_column_letter(c): c for c in range(1, NCOL+1)}
for i, ro in enumerate(out):
drow = 3 + i
tpl = ro['tpl']
h = ws.row_dimensions[tpl].height
if h: nws.row_dimensions[drow].height = h
over = ro['over']
overcols = {LETTER2COL[k]: v for k, v in over.items()}
for c in range(1, NCOL+1):
if c == 11 and ro['klink'] is not None:
copy_cell(tpl, c, drow, c, value=over.get('K', ws.cell(tpl,c).value), link=ro['klink'])
elif c in overcols:
copy_cell(tpl, c, drow, c, value=overcols[c], link='' if c==11 else '__none__')
else:
copy_cell(tpl, c, drow, c)
# B renumber
nws.cell(drow, 2).value = i + 1
ro['drow'] = drow
# ---- header merges ----
for m in ws.merged_cells.ranges:
if m.max_row <= 2:
nws.merge_cells(str(m))
# ---- recompute D/E merges ----
def merge_runs(keyattr, col):
i = 0; n = len(out)
while i < n:
j = i
while j+1 < n and out[j+1][keyattr] == out[i][keyattr]:
j += 1
if j > i:
nws.merge_cells(start_row=out[i]['drow'], start_column=col,
end_row=out[j]['drow'], end_column=col)
i = j+1
merge_runs('dkey', 4)
merge_runs('ekey', 5)
# ---- F merges (per expanded group) ----
from collections import defaultdict
fg = defaultdict(list); gg = defaultdict(list)
for ro in out:
if ro.get('fgrp'): fg[ro['fgrp']].append(ro['drow'])
if ro.get('ggrp'): gg[ro['ggrp']].append(ro['drow'])
for k, rows in fg.items():
if len(rows) > 1:
nws.merge_cells(start_row=min(rows), start_column=6, end_row=max(rows), end_column=6)
for k, rows in gg.items():
if len(rows) > 1:
nws.merge_cells(start_row=min(rows), start_column=7, end_row=max(rows), end_column=7)
nwb.save(OUT)
print('SAVED', OUT, 'rows=', 2+len(out))