refactor: 认定vsOA改为纯Excel公式(UNIQUE+COUNTIFS)-零Python静态值

This commit is contained in:
大师 2026-06-09 04:09:00 +08:00
parent 54e7df87ce
commit 801e14c944
2 changed files with 79 additions and 85 deletions

View File

@ -7,19 +7,18 @@ from openpyxl.styles import Font, PatternFill, Alignment, Border, Side
from openpyxl.utils import get_column_letter
from datetime import datetime
# 日期:命令行参数 or 默认今天
if len(sys.argv) > 1:
REPORT_DATE = sys.argv[1]
else:
REPORT_DATE = datetime.now().strftime('%Y-%m-%d')
BASE = f"/mnt/y/Openclaw_Hub/03.资源/实施项目 wiki/dashboard/data/{REPORT_DATE}"
CERT_DIR = "/mnt/y/Openclaw_Hub/03.资源/实施项目 wiki/dashboard/data/认定数据/2026" # 年度固定目录
CERT_DIR = "/mnt/y/Openclaw_Hub/03.资源/实施项目 wiki/dashboard/data/认定数据/2026"
OUT = f"{BASE}/cleaned/危大方案看板数据工作簿.xlsx"
REF = "'有效≥2026'" # OA公式引用
CREF = "'认定数据'" # 认定公式引用
REF = "'有效≥2026'"
CREF = "'认定数据'"
# ════ 数据源1: OA登记methods_cleaned.csv ════
# ════ 数据源1: OA登记 ════
df = pd.read_csv(f"{BASE}/cleaned/methods_cleaned.csv")
SRC_COLS = list(df.columns[:24])
valid_all = df[df['是否有效登记'] == True].copy()
@ -32,14 +31,13 @@ warn_df = m[m['预警信号']!='none'].sort_values('分部分项工程计划开
warn_total = len(warn_df); orange = (warn_df['预警信号']=='orange').sum(); yellow = warn_total-orange
print(f"OA登记: {tot}项(一般{gen}/超规{sup}) 完成{completed} 预警{orange}{yellow}")
# ════ 数据源2: 公司认定certified_schemes_detail.csv ════
# ════ 数据源2: 公司认定 ════
cert_raw = pd.read_csv(f"{CERT_DIR}/certified_schemes_detail.csv")
cert_raw['计划开工日期_p'] = pd.to_datetime(cert_raw['计划开工日期'].astype(str).str.replace('.','-'), errors='coerce')
cert_valid = cert_raw[cert_raw['计划开工日期_p'].dt.year >= 2026].copy()
cert_tot = len(cert_valid)
cert_sup = (cert_valid['是否超一定规模']=='').sum(); cert_gen = cert_tot-cert_sup
cert_comp = pd.read_csv(f"{CERT_DIR}/certified_schemes.csv")
# 表2: I II Ⅲ类技术方案
tech_raw = pd.read_csv(f"{CERT_DIR}/certified_tech_schemes_detail.csv")
tech_raw['计划开工日期_p'] = pd.to_datetime(tech_raw['计划开工日期'].astype(str).str.replace('.','-'), errors='coerce')
tech_valid = tech_raw[tech_raw['计划开工日期_p'].dt.year >= 2026].copy()
@ -97,76 +95,72 @@ def write_formula_sheet(ws,title,subtitle,formulas,col_widths):
wb = Workbook()
# ════════ S1: OA清洗后数据 ════════
# ═══ S1: OA清洗后数据 ═══
s1=wb.active; s1.title='清洗后数据'
write_data_sheet(s1,valid_all.reset_index(drop=True),
f'OA登记·清洗后数据有效登记·全部年份·{len(valid_all)}行)',
SRC_COLS+['方案状态_clean','是否完成审批','是否有效登记','开工年份','开工月份','预警信号'])
# ════════ S2: OA有效≥2026 ════════
# ═══ S2: OA有效≥2026 ═══
s2=wb.create_sheet('有效≥2026')
write_data_sheet(s2,valid_2026.reset_index(drop=True),
f'OA登记·有效≥2026年开工{len(valid_2026)}行)',
SRC_COLS+['方案状态_clean','是否完成审批','是否有效登记','开工年份','开工月份','预警信号'])
# ════════ S3: 认定数据 ════════
# ═══ S3: 认定数据 ═══
s3=wb.create_sheet('认定数据')
CERT_COLS=['所属区域','所属国别','项目名称','方案名称','编制单位','工程类别','分部工程类别','是否超一定规模','计划开工日期']
write_data_sheet(s3,cert_valid.reset_index(drop=True),
f'2026年度公司认定危大方案明细中港科技便20266号·{cert_tot}项)',CERT_COLS)
# ════════ S4: 认定vsOA ════════
s4=wb.create_sheet('认定vsOA')
COMP_COLS=['项目名称','认定_危大方案总数','认定_超规数','认定_一般数',
'平台_方案总数','平台_超规数','平台_一般数',
'差额_超规','差额_一般','差额_合计','匹配状态']
ncol_c=len(COMP_COLS)
# ═══ S4: 公式-认定vsOA纯Excel公式·COUNTIFS动态对比 ═══
s4=wb.create_sheet('公式-认定vsOA')
COMP_HDR=['项目名称','认定超规','认定一般','OA超规','OA一般','差额超规','差额一般']
ncol_c=len(COMP_HDR)
s4.merge_cells(start_row=1,start_column=1,end_row=1,end_column=ncol_c)
s4.cell(1,1,f'认定 vs OA登记 项目级对比({REPORT_DATE}').font=TITLE_F; s4.cell(1,1).border=GOLD_BD
hdr_row(s4,3,COMP_COLS)
for ri,(_,row) in enumerate(cert_comp.iterrows()):
for ci,col in enumerate(COMP_COLS):
v=row.get(col,'')
if pd.isna(v): v=''
cell=s4.cell(ri+4,ci+1,v); cell.font=DATA_F; cell.border=BORDER
# 差额高亮
if col.startswith('差额_') and isinstance(v,(int,float)) and v!=0:
cell.font=RED_F if v<0 else GREEN_F
if col=='匹配状态': cell.font=GREEN_F if '' in str(v) else DATA_F
s4.auto_filter.ref=f'A3:{get_column_letter(ncol_c)}{len(cert_comp)+3}'
for i,col in enumerate(COMP_COLS):
s4.column_dimensions[get_column_letter(i+1)].width=max(14,min(45,len(str(col))*2.5))
s4.cell(1,1,f'认定 vs OA登记 项目级对比({REPORT_DATE}·Excel公式自动计算').font=TITLE_F
s4.cell(1,1).border=GOLD_BD
s4.merge_cells(start_row=2,start_column=1,end_row=2,end_column=ncol_c)
s4.cell(2,1,'COUNTIFS(认定数据!C:C,项目,H:H,""/"") vs COUNTIFS(有效≥2026!D:D,项目,K:K,""/"")').font=GRAY_F
hdr_row(s4,3,COMP_HDR)
# ════════ S4b: 认定技术方案表2 ════════
s4b=wb.create_sheet('认定技术方案')
# A4: UNIQUE动态获取项目列表
s4.cell(4,1,"=UNIQUE('认定数据'!C4:C200)").font=FORMULA_F; s4.cell(4,1).border=BORDER
# B4-G23: COUNTIFS + 差额公式预填20行超UNIQUE范围自动留空
for r in range(4, 24):
ar = f'$A{r}' if r > 4 else 'A4'
pref = f'IF({ar}="","",'
s4.cell(r,2,f'={pref}COUNTIFS(' + "'认定数据'!$C$4:$C$200," + f'{ar},' + "'认定数据'!$H$4:$H$200," + '""))').font=FORMULA_F; s4.cell(r,2).border=BORDER
s4.cell(r,3,f'={pref}COUNTIFS(' + "'认定数据'!$C$4:$C$200," + f'{ar},' + "'认定数据'!$H$4:$H$200," + '""))').font=FORMULA_F; s4.cell(r,3).border=BORDER
s4.cell(r,4,f'={pref}COUNTIFS(' + "'有效≥2026'!$D$4:$D$200," + f'{ar},' + "'有效≥2026'!$K$4:$K$200," + '""))').font=FORMULA_F; s4.cell(r,4).border=BORDER
s4.cell(r,5,f'={pref}COUNTIFS(' + "'有效≥2026'!$D$4:$D$200," + f'{ar},' + "'有效≥2026'!$K$4:$K$200," + '""))').font=FORMULA_F; s4.cell(r,5).border=BORDER
s4.cell(r,6,f'={pref}D{r}-B{r})').font=FORMULA_F; s4.cell(r,6).border=BORDER
s4.cell(r,7,f'={pref}E{r}-C{r})').font=FORMULA_F; s4.cell(r,7).border=BORDER
s4.auto_filter.ref='A3:G23'
for w,col in zip([40,12,12,12,12,12,12],'ABCDEFG'): s4.column_dimensions[col].width=w
# ═══ S5: 认定技术方案表2 ═══
s5=wb.create_sheet('认定技术方案')
TECH_COLS=['所属国别','项目名称','方案名称','编制单位','工程类别','方案等级','工程特点/说明','计划开工日期']
write_data_sheet(s4b,tech_valid.reset_index(drop=True),
write_data_sheet(s5,tech_valid.reset_index(drop=True),
f'2026年度公司认定技术方案明细ⅡⅢ类·中港科技便20266号·{tech_tot}项)',TECH_COLS)
# ── 公式-技术方案分类 ──
s4bf=wb.create_sheet('公式-技术方案分类')
TREF = "'认定技术方案'"
write_formula_sheet(s4bf,f'认定技术方案等级分布GROUPBY·{tech_tot}项)',
f'GROUPBY({TREF}!F3:F200,{TREF}!C3:C200,COUNTA,3,0)',
[('A',4,f'=GROUPBY({TREF}!F3:F200,{TREF}!C3:C200,COUNTA,3,0)','')],[('A',20),('B',12)])
# ═══ S6-S10: 动态公式 ═══
# ════════ S5-S8: 动态公式 ════════
# ── S5: 公式-年度认定 ──
s5=wb.create_sheet('公式-年度认定')
write_formula_sheet(s5,f'OA年度认定≥2026开工',
s6=wb.create_sheet('公式-年度认定')
write_formula_sheet(s6,f'OA年度认定≥2026开工',
f'GROUPBY({REF}!K3:K200,{REF}!A3:A200,COUNTA,3,0)',
[('A',4,f'=GROUPBY({REF}!K3:K200,{REF}!A3:A200,COUNTA,3,0)','')],[('A',18),('B',12)])
# ── S6: 公式-国别分布 ──
s6=wb.create_sheet('公式-国别分布')
write_formula_sheet(s6,f'OA国别×分类自动排序',
s7=wb.create_sheet('公式-国别分布')
write_formula_sheet(s7,f'OA国别×分类自动排序',
f'GROUPBY({REF}!C3:C200,{REF}!A3:A200,COUNTA,3,0,-2)',
[('A',4,f'=GROUPBY({REF}!C3:C200,{REF}!A3:A200,COUNTA,3,0,-2)','')],[('A',30),('B',12)])
# ── S7: 公式-审批进度 ──
s7=wb.create_sheet('公式-审批进度')
write_formula_sheet(s7,f'OA审批进度 & 预警',
s8=wb.create_sheet('公式-审批进度')
write_formula_sheet(s8,f'OA审批进度 & 预警',
f'引用 {REF}!Z:Z + AD:AD',
[('A',4,'方案总数',''),('B',4,f'=COUNTA({REF}!A4:A200)',''),
('A',5,'已完成审批',''),('B',5,f'=COUNTIF({REF}!Z4:Z200,TRUE)',''),
@ -175,48 +169,49 @@ write_formula_sheet(s7,f'OA审批进度 & 预警',
('A',8,'黄色预警',''),('B',8,f'=COUNTIF({REF}!AD4:AD200,"yellow")',''),
('A',9,'预警合计',''),('B',9,'=B7+B8','')],[('A',20),('B',14)])
# ── S8: 公式-预警明细 ──
s8=wb.create_sheet('公式-预警明细')
write_formula_sheet(s8,f'OA预警明细FILTER',
s9=wb.create_sheet('公式-预警明细')
write_formula_sheet(s9,f'OA预警明细FILTER',
f'FILTER({REF}!A3:AD200,{REF}!AD3:AD200<>"none","无预警")',
[('A',4,f'=FILTER({REF}!A3:AD200,{REF}!AD3:AD200<>"none","🎉 无预警项")','')],[('A',22)])
# ── S9: 公式-认定分类 ──
s9=wb.create_sheet('公式-认定分类')
write_formula_sheet(s9,f'公司认定方案分类GROUPBY·{cert_tot}项)',
s10=wb.create_sheet('公式-认定分类')
write_formula_sheet(s10,f'公司认定方案分类GROUPBY·{cert_tot}项)',
f'GROUPBY({CREF}!H3:H200,{CREF}!D3:D200,COUNTA,3,0)',
[('A',4,f'=GROUPBY({CREF}!H3:H200,{CREF}!D3:D200,COUNTA,3,0)','')],[('A',20),('B',12)])
# ════════ S10-S13: 静态汇总表(可交叉验证) ════════
s10b=wb.create_sheet('公式-技术方案分类')
TREF = "'认定技术方案'"
write_formula_sheet(s10b,f'认定技术方案等级分布GROUPBY·{tech_tot}项)',
f'GROUPBY({TREF}!F3:F200,{TREF}!C3:C200,COUNTA,3,0)',
[('A',4,f'=GROUPBY({TREF}!F3:F200,{TREF}!C3:C200,COUNTA,3,0)','')],[('A',20),('B',12)])
# ── S10: 年度认定汇总 ──
s10=wb.create_sheet('年度认定汇总')
s10.merge_cells('A1:E1'); s10.cell(1,1,f'OA年度认定≥2026·静态').font=TITLE_F; s10.cell(1,1).border=GOLD_BD
hdr_row(s10,3,['分类','方案数','项目数','占比','备注'])
# ═══ S11-S14: 静态汇总(交叉验证) ═══
s11=wb.create_sheet('年度认定汇总')
s11.merge_cells('A1:E1'); s11.cell(1,1,f'OA年度认定≥2026·静态').font=TITLE_F; s11.cell(1,1).border=GOLD_BD
hdr_row(s11,3,['分类','方案数','项目数','占比','备注'])
for r,(lab,val,proj,pct,note) in enumerate([
('一般类',gen,valid_2026[valid_2026['是否超一定规模']!='']['项目名称'].nunique(),f'{gen/tot*100:.0f}%','非超一定规模'),
('超规类',sup,valid_2026[valid_2026['是否超一定规模']=='']['项目名称'].nunique(),f'{sup/tot*100:.0f}%','超一定规模'),
('合计',tot,projects,'100%',f'涵盖{len(countries)}')]):
fmts=[BOLD_F if '合计' in lab else DATA_F]*5
for c,(v,f) in enumerate(zip([lab,val,proj,pct,note],fmts)):
cell=s10.cell(r+4,c+1,v); cell.font=f; cell.border=BORDER; cell.alignment=CENTER
for w,col in zip([12,10,10,10,20],'ABCDE'): s10.column_dimensions[col].width=w
cell=s11.cell(r+4,c+1,v); cell.font=f; cell.border=BORDER; cell.alignment=CENTER
for w,col in zip([12,10,10,10,20],'ABCDE'): s11.column_dimensions[col].width=w
# ── S11: 国别×分类 ──
s11=wb.create_sheet('国别×分类')
s11.merge_cells('A1:D1'); s11.cell(1,1,'OA国别×分类·静态').font=TITLE_F; s11.cell(1,1).border=GOLD_BD
s12=wb.create_sheet('国别×分类')
s12.merge_cells('A1:D1'); s12.cell(1,1,'OA国别×分类·静态').font=TITLE_F; s12.cell(1,1).border=GOLD_BD
ct=m.groupby(['所属国别','是否超一定规模']).size().unstack(fill_value=0)
ct.columns=['一般类' if c=='' else '超规类' for c in ct.columns]; ct['合计']=ct.sum(1); ct.loc['合计']=ct.sum()
hdr_row(s11,3,['国别']+list(ct.columns))
hdr_row(s12,3,['国别']+list(ct.columns))
for r,(idx,row) in enumerate(ct.iterrows()):
for c,v in enumerate([idx]+[int(x) for x in row]):
cell=s11.cell(r+4,c+1,v); cell.font=BOLD_F if '合计' in str(idx) else DATA_F; cell.border=BORDER; cell.alignment=CENTER
s11.column_dimensions['A'].width=25
cell=s12.cell(r+4,c+1,v); cell.font=BOLD_F if '合计' in str(idx) else DATA_F; cell.border=BORDER; cell.alignment=CENTER
s12.column_dimensions['A'].width=25
# ── S12: 审批进度 ──
s12=wb.create_sheet('审批进度')
s12.merge_cells('A1:D1'); s12.cell(1,1,f'OA审批进度·静态{REPORT_DATE}').font=TITLE_F; s12.cell(1,1).border=GOLD_BD
hdr_row(s12,3,['指标','数值','占比','备注'])
s13=wb.create_sheet('审批进度')
s13.merge_cells('A1:D1'); s13.cell(1,1,f'OA审批进度·静态{REPORT_DATE}').font=TITLE_F; s13.cell(1,1).border=GOLD_BD
hdr_row(s13,3,['指标','数值','占比','备注'])
for r,(lab,val,pct,note,fmts) in enumerate([
('方案总数',tot,'100%','≥2026年开工',[BLUE_F]*3+[GRAY_F]),
('已完成审批',int(completed),f'{completed/tot*100:.0f}%','"已完成"',[GREEN_F]*3+[GRAY_F]),
@ -225,13 +220,12 @@ for r,(lab,val,pct,note,fmts) in enumerate([
('🟡 黄色预警',int(yellow),f'{yellow/tot*100:.0f}%','≤45天',[Font(name='微软雅黑',bold=True,size=10,color='F9A825')]*3+[GRAY_F]),
('预警合计',int(warn_total),f'{warn_total/tot*100:.0f}%',f'🟠{orange}+🟡{yellow}',[RED_F]*3+[GRAY_F])]):
for c,(v,f) in enumerate(zip([lab,val,pct,note],fmts)):
cell=s12.cell(r+4,c+1,v); cell.font=f; cell.border=BORDER; cell.alignment=CENTER
for w,col in zip([18,10,10,35],'ABCD'): s12.column_dimensions[col].width=w
cell=s13.cell(r+4,c+1,v); cell.font=f; cell.border=BORDER; cell.alignment=CENTER
for w,col in zip([18,10,10,35],'ABCD'): s13.column_dimensions[col].width=w
# ── S13: 预警明细 ──
s13=wb.create_sheet('预警明细')
s13.merge_cells('A1:H1'); s13.cell(1,1,f'OA预警明细·静态{warn_total}项)').font=TITLE_F; s13.cell(1,1).border=GOLD_BD
hdr_row(s13,3,['信号','距开工','项目名称','方案名称','方案状态','计划开工','超规/一般','国别'])
s14=wb.create_sheet('预警明细')
s14.merge_cells('A1:H1'); s14.cell(1,1,f'OA预警明细·静态{warn_total}项)').font=TITLE_F; s14.cell(1,1).border=GOLD_BD
hdr_row(s14,3,['信号','距开工','项目名称','方案名称','方案状态','计划开工','超规/一般','国别'])
today=pd.Timestamp(REPORT_DATE)
for r,(_,row) in enumerate(warn_df.iterrows()):
days=(pd.to_datetime(row['分部分项工程计划开工日期'])-today).days
@ -241,15 +235,15 @@ for r,(_,row) in enumerate(warn_df.iterrows()):
str(row['分部分项工程计划开工日期'])[:10],
'超规类' if row['是否超一定规模']=='' else '一般类',row['所属国别']]
for c,v in enumerate(vals):
cell=s13.cell(r+4,c+1,v); cell.font=RED_F if c==1 and days<=3 else DATA_F; cell.border=BORDER; cell.fill=WARN_BG
s13.auto_filter.ref=f'A3:H{warn_total+3}'
for w,col in zip([6,8,40,35,18,12,10,20],'ABCDEFGH'): s13.column_dimensions[col].width=w
cell=s14.cell(r+4,c+1,v); cell.font=RED_F if c==1 and days<=3 else DATA_F; cell.border=BORDER; cell.fill=WARN_BG
s14.auto_filter.ref=f'A3:H{warn_total+3}'
for w,col in zip([6,8,40,35,18,12,10,20],'ABCDEFGH'): s14.column_dimensions[col].width=w
wb.save(OUT)
print(f"\n{OUT}")
print(f" S1-S2 OA登记: {len(valid_all)}全量 + {len(valid_2026)}≥2026")
print(f" S3 认定危大方案(表1): {cert_tot}行({cert_gen}一般+{cert_sup}超规)")
print(f" S4 认定vsOA: {len(cert_comp)}项目")
print(f" S4b 认定技术方案(表2): {tech_tot}")
print(f" S5-S10 GROUPBY/FILTER 动态公式(含表1+表2)")
print(f" S4 公式-认定vsOA: COUNTIFS动态对比")
print(f" S5 认定技术方案(表2): {tech_tot}")
print(f" S6-S10 GROUPBY/FILTER 动态公式(含表1+表2)")
print(f" S11-S14 静态汇总表(交叉验证)")