DB_JOB/_스크립트/_fix_emerge_all.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

117 lines
4.4 KiB
Python

# -*- coding: utf-8 -*-
"""E열(대분류) 병합 누락 보정 — 재크롤링 없이 기존 엑셀의 D/E/F 병합만 바로잡는다.
원인: phase1이 D를 먼저 병합 → 블록 2행부터 D=None → E 병합(같은 D 안에서만)이
D블록 첫 행에서 끊겨 첫 행 E가 단독으로 남음.
해결: 현재 병합에서 값 복원 → D/E/F 병합 해제 → 전 행에 값 채움 →
F → E → D 순서로 재병합(컬럼을 병합하면 그 컬럼 2행부터 None이 되므로 순서가 중요).
데이터(D/E/F 텍스트)는 그대로. 헤더 병합(B1:R1 등)은 건드리지 않음.
파일별 *_backup_emerge전.xlsx 백업.
"""
import sys, os, glob, shutil, warnings, openpyxl
from openpyxl.styles import Alignment
from openpyxl.utils import get_column_letter
sys.stdout.reconfigure(encoding='utf-8')
warnings.filterwarnings('ignore')
START = 3
COLS = (4, 5, 6) # D, E, F
CENTER = Alignment(horizontal='center', vertical='center', wrap_text=False)
def filled_values(ws, col):
"""현재 병합 상태에서 각 행의 실제 값을 복원(병합 top값으로 채움)."""
end = ws.max_row
vals = {r: ws.cell(r, col).value for r in range(START, end + 1)}
for mr in ws.merged_cells.ranges:
if mr.min_col == col == mr.max_col and mr.min_row >= START:
top = ws.cell(mr.min_row, col).value
for r in range(mr.min_row, mr.max_row + 1):
vals[r] = top
return vals, end
def merge_runs(ws, col, end, group_keys, fills):
"""fills[col][r] 기준으로 연속 동일 구간 병합. group_keys: 상위 컬럼 튜플."""
letter = get_column_letter(col)
runs = []
def key(r):
return tuple(fills[g][r] for g in group_keys)
cur_val = fills[col][START]
cur_grp = key(START)
run_start = START
for r in range(START + 1, end + 1):
v, g = fills[col][r], key(r)
if v == cur_val and g == cur_grp:
continue
if cur_val not in (None, '') and r - 1 > run_start:
runs.append((run_start, r - 1))
cur_val, cur_grp, run_start = v, g, r
if cur_val not in (None, '') and end > run_start:
runs.append((run_start, end))
for s, e in runs:
ws.merge_cells(f'{letter}{s}:{letter}{e}')
ws.cell(s, col).alignment = CENTER
return len(runs)
def fix(xlsx):
wb = openpyxl.load_workbook(xlsx)
ws = wb.active
# 1) 값 복원
fills, end = {}, None
for c in COLS:
fills[c], end = filled_values(ws, c)
# 2) D/E/F 데이터 병합만 해제 (단일컬럼·row>=START)
to_unmerge = [str(mr) for mr in ws.merged_cells.ranges
if mr.min_col == mr.max_col and mr.min_col in COLS and mr.min_row >= START]
for rng in to_unmerge:
ws.unmerge_cells(rng)
# 3) 전 행에 값 다시 기입
for c in COLS:
for r in range(START, end + 1):
ws.cell(r, c).value = fills[c][r]
# 4) F → E → D 순서 재병합
n_f = merge_runs(ws, 6, end, (4, 5), fills)
n_e = merge_runs(ws, 5, end, (4,), fills)
n_d = merge_runs(ws, 4, end, (), fills)
return wb, (n_d, n_e, n_f, len(to_unmerge))
def main():
sel = [a for a in sys.argv[1:] if not a.startswith('-')]
files = []
for region in ['충청남도', '충청북도']:
for d in sorted(glob.glob(region + r'\*')):
if not os.path.isdir(d):
continue
base = os.path.basename(d)
nm = base.split('.', 1)[1] if '.' in base else base
prov = os.path.basename(os.path.dirname(d))
f = os.path.join(d, f'{prov}_{nm}.xlsx')
if os.path.exists(f) and (not sel or nm in sel):
files.append((nm, f))
print(f'대상 {len(files)}개: {[n for n, _ in files]}')
ok = []
for nm, f in files:
try:
bak = f.replace('.xlsx', '') + '_backup_emerge전.xlsx'
if not os.path.exists(bak):
shutil.copy2(f, bak)
wb, (nd, ne, nf, un) = fix(f)
wb.save(f)
print(f'{nm:<6} 병합해제 {un:>3} → 재병합 D:{nd} E:{ne} F:{nf}')
ok.append(nm)
except PermissionError:
print(f' ⚠️ {nm:<6} 파일 잠김(열려있음) → 건너뜀')
except Exception as e:
import traceback; traceback.print_exc()
print(f' !! {nm:<6} 오류: {e}')
print(f'\n완료 {len(ok)}/{len(files)}')
if __name__ == '__main__':
main()