- 공공기관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>
143 lines
4.9 KiB
Python
143 lines
4.9 KiB
Python
import openpyxl, json, sys, os, shutil, time
|
|
from copy import copy
|
|
from openpyxl.styles import PatternFill
|
|
from openpyxl.utils import get_column_letter
|
|
sys.stdout.reconfigure(encoding='utf-8')
|
|
P=r'작업파일/공공기관2/12.한국철도공사/한국철도공사.xlsx'
|
|
BASE='https://info.korail.com'
|
|
BLUE=PatternFill(fill_type='solid', fgColor='FFBDD7EE', bgColor='FFBDD7EE')
|
|
ts=json.load(open(r'작업파일/공공기관2/12.한국철도공사/_tabscan.json',encoding='utf-8'))
|
|
|
|
EXPAND=[6,8,15,30,33,34,37,38,39,40,45,46,47,74,78,79,80,82,86,92,93,94,102,104,105,106]
|
|
IMG=set('719 720 730 769 781 783 785 799 871 872 894 899 900 901 902 906 907 908 970 2950 2951 2952 2977 1461 1462 1463 1464 1641 1303 1541 1503 1504 2730 1505 2731 1507 1508 2732 1510 1511'.split())
|
|
def nfor(key):
|
|
return '어문,이미지' if str(key) in IMG else '어문'
|
|
|
|
mt0=os.path.getmtime(P)
|
|
shutil.copy2(P, P.replace('.xlsx', f'_backup_{int(mt0)}.xlsx'))
|
|
wb=openpyxl.load_workbook(P)
|
|
ws=wb.active
|
|
|
|
# merge-fill map cols 4-10
|
|
fill={}
|
|
for m in list(ws.merged_cells.ranges):
|
|
v=ws.cell(m.min_row,m.min_col).value
|
|
for rr in range(m.min_row,m.max_row+1):
|
|
for cc in range(m.min_col,m.max_col+1):
|
|
fill[(rr,cc)]=v
|
|
def eff(r,c):
|
|
return fill.get((r,c), ws.cell(r,c).value)
|
|
|
|
# capture template styles per column from a clean data row (row 4)
|
|
TPL={}
|
|
for c in range(2,28):
|
|
cell=ws.cell(4,c)
|
|
TPL[c]=(copy(cell.font),copy(cell.border),copy(cell.fill),copy(cell.alignment),copy(cell.number_format))
|
|
# K hyperlink template style (font/color)
|
|
KSTYLE=(copy(ws.cell(3,11).font),copy(ws.cell(3,11).border),copy(ws.cell(3,11).fill),copy(ws.cell(3,11).alignment))
|
|
|
|
Cval=ws.cell(3,3).value # 사이트명
|
|
|
|
# Build records: list of dicts col->value, plus 'blue' set of cols, plus 'hl' url
|
|
records=[]
|
|
for r in range(3, ws.max_row+1):
|
|
base={}
|
|
for c in range(3,28): # C..AA (B renumbered later)
|
|
if 4<=c<=10:
|
|
base[c]=eff(r,c)
|
|
else:
|
|
base[c]=ws.cell(r,c).value
|
|
hl=ws.cell(r,11).hyperlink.target if ws.cell(r,11).hyperlink else (base[11] if base[11] else None)
|
|
if r in EXPAND:
|
|
leafcol=6 # F
|
|
tabs=ts[str(r)]['tabs']
|
|
# self tab = active (first); reuse this row but add G label + url stays
|
|
for i,t in enumerate(tabs):
|
|
rec=dict(base)
|
|
key=t['href'].split('key=')[-1]
|
|
url=BASE+t['href']
|
|
rec[7]=t['label'] # G
|
|
rec[11]=url # K value
|
|
rec['_hl']=url
|
|
rec[12]='페이지' # L
|
|
rec[13]=1 # M
|
|
rec[14]=nfor(key) # N
|
|
rec[15]='미부착' # O
|
|
rec['_blue']={7,11,12,13,14,15}
|
|
if i>0:
|
|
# new row: clear D,E,F values (merge covers) - keep for run-length; keep eff values
|
|
rec[16]=None; rec[17]=None # P,Q clear for new
|
|
for cc in range(18,28): rec[cc]=None # R-AA clear
|
|
records.append(rec)
|
|
else:
|
|
rec=dict(base); rec['_hl']=hl; rec['_blue']=set()
|
|
records.append(rec)
|
|
|
|
# Now rewrite sheet: clear merges + data region
|
|
for m in list(ws.merged_cells.ranges):
|
|
ws.unmerge_cells(str(m))
|
|
maxr=ws.max_row
|
|
for r in range(3, maxr+1):
|
|
for c in range(1,28):
|
|
cell=ws.cell(r,c); cell.value=None; cell.hyperlink=None
|
|
|
|
# write records
|
|
import re
|
|
for idx,rec in enumerate(records):
|
|
r=3+idx
|
|
ws.cell(r,2).value=idx+1 # B renumber
|
|
# apply template style to all cols
|
|
for c in range(2,28):
|
|
f,b,fl,al,nf=TPL[c]
|
|
cell=ws.cell(r,c)
|
|
cell.font=copy(f); cell.border=copy(b); cell.fill=copy(fl); cell.alignment=copy(al); cell.number_format=nf
|
|
for c in range(3,28):
|
|
ws.cell(r,c).value=rec.get(c)
|
|
# K hyperlink
|
|
url=rec.get('_hl')
|
|
if url and isinstance(url,str) and url.startswith('http'):
|
|
kc=ws.cell(r,11)
|
|
kc.hyperlink=url
|
|
kf,kb,kfl,kal=KSTYLE
|
|
kc.font=copy(kf)
|
|
# blue highlights
|
|
for c in rec.get('_blue',()):
|
|
ws.cell(r,c).fill=copy(BLUE)
|
|
|
|
nrows=len(records)
|
|
last=3+nrows-1
|
|
|
|
# snapshot of effective values BEFORE merging (merged cells read back as None)
|
|
SNAP={}
|
|
for idx,rec in enumerate(records):
|
|
rr=3+idx
|
|
for c in range(4,11):
|
|
SNAP[(rr,c)]=rec.get(c)
|
|
# run-length merges for cols D(4)..J(10)
|
|
def val(r,c): return SNAP.get((r,c))
|
|
for c in range(4,11):
|
|
r=3
|
|
while r<=last:
|
|
v=val(r,c)
|
|
if v is None:
|
|
r+=1; continue
|
|
# ancestor tuple
|
|
def anctup(rr):
|
|
return tuple(val(rr,cc) for cc in range(4,c))
|
|
a0=anctup(r)
|
|
r2=r
|
|
while r2+1<=last and val(r2+1,c)==v and anctup(r2+1)==a0:
|
|
r2+=1
|
|
if r2>r:
|
|
ws.merge_cells(start_row=r,start_column=c,end_row=r2,end_column=c)
|
|
r=r2+1
|
|
|
|
# mtime guard + save
|
|
if os.path.getmtime(P)!=mt0:
|
|
print('ABORT mtime moved'); sys.exit(2)
|
|
try:
|
|
wb.save(P)
|
|
except PermissionError as e:
|
|
print('ABORT-PERM (close Excel)', e); sys.exit(3)
|
|
print('OK rows', nrows, '(was', maxr-2, ') new', nrows-(maxr-2))
|