공공기관2 작업 중. _temp 몽타주(재생성가능)는 제외. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
118 lines
4.3 KiB
Python
118 lines
4.3 KiB
Python
import sys, io
|
|
sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding='utf-8')
|
|
import openpyxl
|
|
from openpyxl.utils import get_column_letter
|
|
PKG = r'D:\01.프로젝트\DB수집\작업파일\_제출패키지_2026-06-03\프리랜서1\충청남도_부여군.xlsx'
|
|
ws = openpyxl.load_workbook(PKG).active
|
|
|
|
# merge lookup
|
|
merges = list(ws.merged_cells.ranges)
|
|
def merge_of(r,c):
|
|
for m in merges:
|
|
if m.min_row<=r<=m.max_row and m.min_col<=c<=m.max_col:
|
|
return m
|
|
return None
|
|
def eff(r,c):
|
|
m=merge_of(r,c)
|
|
if m: return ws.cell(m.min_row,m.min_col).value
|
|
return ws.cell(r,c).value
|
|
|
|
data=[r for r in range(3,ws.max_row+1) if ws.cell(r,2).value not in (None,'')]
|
|
issues=[]
|
|
|
|
# 1) header merges
|
|
hdr={str(m) for m in merges}
|
|
for h in ('B1:R1','S1:W1','Y1:AA1'):
|
|
if h not in hdr: issues.append(f'헤더병합 누락 {h}')
|
|
|
|
# 2) G+ no merge (col>=7)
|
|
gplus=[str(m) for m in merges if m.min_col>=7 and m.min_row>=3]
|
|
if gplus:
|
|
issues.append(f'[규칙위반] G이상 병합 {len(gplus)}개: {gplus[:20]}')
|
|
|
|
# helper: maximal runs of equal non-None value over a row list for a column
|
|
def runs(rows, col):
|
|
out=[];
|
|
for r in rows:
|
|
v=eff(r,col)
|
|
if out and out[-1][2]==v and v not in (None,'') and r==out[-1][1]+1:
|
|
out[-1][1]=r
|
|
else:
|
|
out.append([r,r,v])
|
|
return out
|
|
|
|
def exact_merge(col,a,z):
|
|
m=merge_of(a,col)
|
|
return m is not None and m.min_col==col==m.max_col and m.min_row==a and m.max_row==z
|
|
|
|
# 3) D column
|
|
druns=runs(data,4)
|
|
nD=0
|
|
for a,z,v in druns:
|
|
if v in (None,''):
|
|
issues.append(f'D 빈값 행 r{a}'); continue
|
|
nD+=1
|
|
if z>a and not exact_merge(4,a,z):
|
|
m=merge_of(a,4)
|
|
issues.append(f'D런 {v!r} r{a}:{z} 병합불일치(현재 {m})')
|
|
if z==a:
|
|
m=merge_of(a,4)
|
|
if m and (m.max_row>m.min_row):
|
|
issues.append(f'D 단일행 r{a} {v!r} 인데 병합 {m}')
|
|
# split check: same D value appearing in non-adjacent runs
|
|
from collections import Counter
|
|
dval_runs=Counter(v for a,z,v in druns if v not in (None,''))
|
|
for v,cnt in dval_runs.items():
|
|
if cnt>1:
|
|
issues.append(f'[주의] D값 {v!r} 이 {cnt}개 비연속 런으로 분리됨')
|
|
|
|
# 4) E within each D-run
|
|
for a,z,dv in druns:
|
|
drows=[r for r in data if a<=r<=z]
|
|
for ea,ez,ev in runs(drows,5):
|
|
if ev in (None,''): continue
|
|
# E merge must be within D-run
|
|
if ez>ea and not exact_merge(5,ea,ez):
|
|
m=merge_of(ea,5)
|
|
issues.append(f'E런 {ev!r} r{ea}:{ez}(D={dv!r}) 병합불일치(현재 {m})')
|
|
m=merge_of(ea,5)
|
|
if m and (m.min_row<a or m.max_row>z):
|
|
issues.append(f'[규칙위반] E병합 {m}이 D그룹 r{a}:{z}({dv!r}) 경계 넘음')
|
|
if ez==ea and m and m.max_row>m.min_row:
|
|
issues.append(f'E 단일행 r{ea} {ev!r} 인데 병합 {m}')
|
|
|
|
# 5) F within each D+E sub-run
|
|
for a,z,dv in druns:
|
|
drows=[r for r in data if a<=r<=z]
|
|
for ea,ez,ev in runs(drows,5):
|
|
erows=[r for r in drows if ea<=r<=ez] if ev not in (None,'') else [r for r in drows if eff(r,5) in (None,'')]
|
|
# group F within this E sub-run
|
|
sub=[r for r in drows if ea<=r<=ez]
|
|
for fa,fz,fv in runs(sub,6):
|
|
if fv in (None,''): continue
|
|
if fz>fa and not exact_merge(6,fa,fz):
|
|
m=merge_of(fa,6)
|
|
issues.append(f'F런 {fv!r} r{fa}:{fz}(D={dv!r},E={ev!r}) 병합불일치(현재 {m})')
|
|
m=merge_of(fa,6)
|
|
if m and (m.min_row<ea or m.max_row>ez):
|
|
issues.append(f'[규칙위반] F병합 {m}이 D+E그룹 r{ea}:{ez} 경계 넘음')
|
|
|
|
# 6) merge overlaps
|
|
seen={}; overlap=0
|
|
for m in merges:
|
|
for r in range(m.min_row,m.max_row+1):
|
|
for c in range(m.min_col,m.max_col+1):
|
|
if (r,c) in seen: overlap+=1
|
|
seen[(r,c)]=1
|
|
if overlap: issues.append(f'병합 겹침 셀 {overlap}')
|
|
|
|
print(f'데이터행 {len(data)} 대분류(D런) {nD}개 총병합 {len(merges)}')
|
|
print(f'D 병합수={sum(1 for m in merges if m.min_col==4)} E={sum(1 for m in merges if m.min_col==5)} F={sum(1 for m in merges if m.min_col==6)} G+={len(gplus)}')
|
|
print('대분류 목록:', [v for a,z,v in druns])
|
|
print()
|
|
if issues:
|
|
print(f'⚠ 병합 이슈 {len(issues)}건:')
|
|
for x in issues: print(' ✗', x)
|
|
else:
|
|
print('✅ 병합 규칙 위반 없음 (D/E/F 경계·연속병합·G무병합·헤더병합 정상)')
|