- 공공기관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>
77 lines
2.7 KiB
Python
77 lines
2.7 KiB
Python
# -*- coding: utf-8 -*-
|
|
import openpyxl, io, os, glob
|
|
from collections import defaultdict
|
|
ROOT=r"D:\01.프로젝트\DB수집"
|
|
out=io.open(r"D:\01.프로젝트\DB수집\작업파일\_dupname_sweep_out.txt","w",encoding="utf-8")
|
|
def w(*a): out.write(" ".join(str(x) for x in a)+"\n")
|
|
|
|
SKIP_DIR=("_backup","backup","_temp","제출폴더","_스크립트","_nshots","_naudit","_shots",".git")
|
|
SKIP_NAME=("backup","conflicted","_작업현황","작업현황","_취합_예시","자료_취합","예시","~$")
|
|
|
|
def is_target(p):
|
|
pl=p.replace("/","\\")
|
|
for s in SKIP_DIR:
|
|
if "\\"+s in pl or "\\"+s+"\\" in pl: return False
|
|
base=os.path.basename(p)
|
|
for s in SKIP_NAME:
|
|
if s in base: return False
|
|
return base.endswith(".xlsx")
|
|
|
|
files=[]
|
|
for dp,dn,fn in os.walk(ROOT):
|
|
# prune skip dirs
|
|
dn[:]=[d for d in dn if d not in SKIP_DIR and "backup" not in d.lower()]
|
|
for f in fn:
|
|
p=os.path.join(dp,f)
|
|
if is_target(p): files.append(p)
|
|
|
|
w("scanned candidate files:",len(files));w("")
|
|
|
|
def eff_leaf_dups(ws):
|
|
eff={}
|
|
for r in range(3,ws.max_row+1):
|
|
for c in range(4,11):
|
|
eff[(r,c)]=ws.cell(r,c).value
|
|
for mr in ws.merged_cells.ranges:
|
|
if mr.min_col>=4 and mr.max_col<=10:
|
|
v=ws.cell(mr.min_row,mr.min_col).value
|
|
for r in range(mr.min_row,mr.max_row+1):
|
|
for c in range(mr.min_col,mr.max_col+1):
|
|
eff[(r,c)]=v
|
|
byleaf=defaultdict(list)
|
|
for r in range(3,ws.max_row+1):
|
|
# skip empty rows
|
|
path=[str(eff[(r,c)]).strip() for c in range(4,11) if eff[(r,c)] not in (None,"")]
|
|
if not path: continue
|
|
byleaf[path[-1]].append(r)
|
|
return {k:v for k,v in byleaf.items() if len(v)>1}
|
|
|
|
summary=[]
|
|
for p in sorted(files):
|
|
try:
|
|
wb=openpyxl.load_workbook(p,read_only=False,data_only=True)
|
|
except Exception as e:
|
|
w("LOADERR",p,e); continue
|
|
ws=wb.active
|
|
if ws.max_row<3:
|
|
wb.close(); continue
|
|
dups=eff_leaf_dups(ws)
|
|
rel=os.path.relpath(p,ROOT)
|
|
ngroups=len(dups); nduprows=sum(len(v) for v in dups.values())
|
|
summary.append((ngroups,nduprows,rel,dups,ws.max_row))
|
|
wb.close()
|
|
|
|
summary.sort(reverse=True)
|
|
w("=== FILES WITH DUPLICATE MENU NAMES (sorted by #groups) ===")
|
|
for ng,nr,rel,dups,mx in summary:
|
|
if ng==0: continue
|
|
w(f"\n[{ng} groups / {nr} rows] {rel} (max_row={mx})")
|
|
for nm,rs in sorted(dups.items(), key=lambda x:-len(x[1])):
|
|
w(f" '{nm}' x{len(rs)} rows={rs}")
|
|
clean=[s for s in summary if s[0]==0]
|
|
w("\n=== FILES WITH NO DUPLICATE MENU NAMES ===",len(clean))
|
|
for ng,nr,rel,dups,mx in clean:
|
|
w(" OK",rel)
|
|
out.close()
|
|
print("done; files:",len(files),"with dups:",sum(1 for s in summary if s[0]>0))
|