fix: 公式-认定vsOA重写-项目名预填+COUNTIFS直写(无IF包装)

This commit is contained in:
大师 2026-06-09 04:26:30 +08:00
parent 801e14c944
commit 381b4c740f
2 changed files with 50 additions and 54 deletions

View File

@ -1,5 +1,5 @@
#!/usr/bin/env python3
"""危大方案看板数据工作簿 v5 — 双数据源(OA登记+公司认定) + GROUPBY/FILTER动态公式"""
"""危大方案看板数据工作簿 v5 — 双数据源 + 纯Excel公式认定vsOA用COUNTIFS直写"""
import sys
import pandas as pd
from openpyxl import Workbook
@ -15,10 +15,8 @@ else:
BASE = f"/mnt/y/Openclaw_Hub/03.资源/实施项目 wiki/dashboard/data/{REPORT_DATE}"
CERT_DIR = "/mnt/y/Openclaw_Hub/03.资源/实施项目 wiki/dashboard/data/认定数据/2026"
OUT = f"{BASE}/cleaned/危大方案看板数据工作簿.xlsx"
REF = "'有效≥2026'"
CREF = "'认定数据'"
# ════ 数据源1: OA登记 ════
# ════ 数据源 ════
df = pd.read_csv(f"{BASE}/cleaned/methods_cleaned.csv")
SRC_COLS = list(df.columns[:24])
valid_all = df[df['是否有效登记'] == True].copy()
@ -29,20 +27,19 @@ completed = m['是否完成审批'].sum(); unfinished = tot-completed
projects = m['项目名称'].nunique(); countries = m['所属国别'].value_counts().to_dict()
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: 公司认定 ════
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")
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()
tech_tot = len(tech_valid)
print(f"公司认定: 表1危大{cert_tot}项({cert_gen}一般/{cert_sup}超规) + 表2技术{tech_tot}项 = {cert_tot+tech_tot}总计")
print(f"OA: {tot}(一般{gen}/超规{sup}) 完成{completed} | 认定: 表1{cert_tot}+表2{tech_tot}={cert_tot+tech_tot}")
# ════ 样式 ════
HDR_F=Font(name='微软雅黑',bold=True,size=10,color='FFFFFF'); HDR_BG=PatternFill('solid',fgColor='1A3A5C')
@ -55,6 +52,7 @@ WARN_BG=PatternFill('solid',fgColor='FFF3E0'); INFO_BG=PatternFill('solid',fgCol
BORDER=Border(left=Side('thin','DBE2EA'),right=Side('thin','DBE2EA'),top=Side('thin','DBE2EA'),bottom=Side('thin','DBE2EA'))
CENTER=Alignment(horizontal='center',vertical='center',wrap_text=True)
GOLD_BD=Border(bottom=Side(style='medium',color='C8962E'))
REF="'有效≥2026'"; CREF="'认定数据'"
def hdr_row(ws,r,cols):
for i,h in enumerate(cols):
@ -86,82 +84,85 @@ def write_formula_sheet(ws,title,subtitle,formulas,col_widths):
ws.cell(2,1,subtitle).font=Font(name='微软雅黑',size=9,color='8899AA')
for col_letter,row_num,formula,label in formulas:
cell=ws.cell(row_num,ord(col_letter)-64,formula); cell.font=FORMULA_F; cell.border=BORDER
if label:
ws.cell(row_num,ord(col_letter)-63,label).font=GRAY_F
for col_letter,w in col_widths:
ws.column_dimensions[col_letter].width=w
if label: ws.cell(row_num,ord(col_letter)-63,label).font=GRAY_F
for col_letter,w in col_widths: ws.column_dimensions[col_letter].width=w
ws.merge_cells(start_row=12,start_column=1,end_row=12,end_column=4)
ws.cell(12,1,'💡 源数据更新后公式自动刷新WPS新版/Excel 365').font=GRAY_F; ws.cell(12,1).fill=INFO_BG
wb = Workbook()
# ═══ S1: OA清洗后数据 ═══
# ═══ S1-S3 数据源 ═══
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=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=wb.create_sheet('认定数据')
CERT_COLS=['所属区域','所属国别','项目名称','方案名称','编制单位','工程类别','分部工程类别','是否超一定规模','计划开工日期']
write_data_sheet(s3,cert_valid.reset_index(drop=True),
f'2026年度公司认定危大方案明细中港科技便20266号·{cert_tot}项)',CERT_COLS)
# ═══ S4: 公式-认定vsOA纯Excel公式·COUNTIFS动态对比 ═══
# ═══ S4: 公式-认定vsOA ═══
# 项目名从认定数据 C4:C200 提取,省去 UNIQUEWPS兼容性更好
# 用 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}·Excel公式自动计算').font=TITLE_F
s4.cell(1,1).border=GOLD_BD
s4.cell(1,1,f'认定 vs OA登记 项目级对比({REPORT_DATE}·COUNTIFS公式').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
s4.cell(2,1,'A列=项目 | B=认定超规 | C=认定一般 | D=OA超规 | E=OA一般 | F=差额超规 | G=差额一般').font=GRAY_F
hdr_row(s4,3,COMP_HDR)
# A4: UNIQUE动态获取项目列表
s4.cell(4,1,"=UNIQUE('认定数据'!C4:C200)").font=FORMULA_F; s4.cell(4,1).border=BORDER
# 用 Python 预填项目名(因为 UNIQUE 在部分WPS版本不支持
# 然后 COUNTIFS 引用项目名列
proj_list = sorted(cert_valid['项目名称'].unique())
for ri, proj_name in enumerate(proj_list):
r = ri + 4
s4.cell(r, 1, proj_name).font=DATA_F; s4.cell(r, 1).border=BORDER
# B4-G23: COUNTIFS + 差额公式预填20行超UNIQUE范围自动留空
# 公式行B-H 全部用 =COUNTIFS / =差额预填20行
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
ar = f'$A{r}'
sr = str(r)
# B: 认定超规
s4.cell(r, 2, f'=COUNTIFS(' + "'认定数据'!C4:C200," + f'{ar},' + "'认定数据'!H4:H200," + '"")').font=FORMULA_F; s4.cell(r, 2).border=BORDER
# C: 认定一般
s4.cell(r, 3, f'=COUNTIFS(' + "'认定数据'!C4:C200," + f'{ar},' + "'认定数据'!H4:H200," + '"")').font=FORMULA_F; s4.cell(r, 3).border=BORDER
# D: OA超规
s4.cell(r, 4, f'=COUNTIFS(' + "'有效≥2026'!D4:D200," + f'{ar},' + "'有效≥2026'!K4:K200," + '"")').font=FORMULA_F; s4.cell(r, 4).border=BORDER
# E: OA一般
s4.cell(r, 5, f'=COUNTIFS(' + "'有效≥2026'!D4:D200," + f'{ar},' + "'有效≥2026'!K4:K200," + '"")').font=FORMULA_F; s4.cell(r, 5).border=BORDER
# F: 差额超规 = D - B
s4.cell(r, 6, f'=D{sr}-B{sr}').font=FORMULA_F; s4.cell(r, 6).border=BORDER
# G: 差额一般 = E - C
s4.cell(r, 7, f'=E{sr}-C{sr}').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: 认定技术方案 ═══
s5=wb.create_sheet('认定技术方案')
TECH_COLS=['所属国别','项目名称','方案名称','编制单位','工程类别','方案等级','工程特点/说明','计划开工日期']
write_data_sheet(s5,tech_valid.reset_index(drop=True),
f'2026年度公司认定技术方案明细ⅡⅢ类·中港科技便20266号·{tech_tot}项)',TECH_COLS)
# ═══ S6-S10: 动态公式 ═══
f'2026年度公司认定技术方案明细ⅡⅢ类·{tech_tot}项)',TECH_COLS)
# ═══ S6-S10b: 动态公式 ═══
s6=wb.create_sheet('公式-年度认定')
write_formula_sheet(s6,f'OA年度认定≥2026开工',
f'GROUPBY({REF}!K3:K200,{REF}!A3:A200,COUNTA,3,0)',
write_formula_sheet(s6,'OA年度认定','GROUPBY on 是否超一定规模',
[('A',4,f'=GROUPBY({REF}!K3:K200,{REF}!A3:A200,COUNTA,3,0)','')],[('A',18),('B',12)])
s7=wb.create_sheet('公式-国别分布')
write_formula_sheet(s7,f'OA国别×分类自动排序',
f'GROUPBY({REF}!C3:C200,{REF}!A3:A200,COUNTA,3,0,-2)',
write_formula_sheet(s7,'OA国别×分类','GROUPBY on 所属国别',
[('A',4,f'=GROUPBY({REF}!C3:C200,{REF}!A3:A200,COUNTA,3,0,-2)','')],[('A',30),('B',12)])
s8=wb.create_sheet('公式-审批进度')
write_formula_sheet(s8,f'OA审批进度 & 预警',
f'引用 {REF}!Z:Z + AD:AD',
write_formula_sheet(s8,'OA审批进度 & 预警','COUNTIF on 是否完成审批',
[('A',4,'方案总数',''),('B',4,f'=COUNTA({REF}!A4:A200)',''),
('A',5,'已完成审批',''),('B',5,f'=COUNTIF({REF}!Z4:Z200,TRUE)',''),
('A',6,'未完成审批',''),('B',6,f'=COUNTIF({REF}!Z4:Z200,FALSE)',''),
@ -170,23 +171,20 @@ write_formula_sheet(s8,f'OA审批进度 & 预警',
('A',9,'预警合计',''),('B',9,'=B7+B8','')],[('A',20),('B',14)])
s9=wb.create_sheet('公式-预警明细')
write_formula_sheet(s9,f'OA预警明细FILTER',
f'FILTER({REF}!A3:AD200,{REF}!AD3:AD200<>"none","无预警")',
write_formula_sheet(s9,'OA预警明细','FILTER on 预警信号',
[('A',4,f'=FILTER({REF}!A3:AD200,{REF}!AD3:AD200<>"none","🎉 无预警项")','')],[('A',22)])
s10=wb.create_sheet('公式-认定分类')
write_formula_sheet(s10,f'公司认定方案分类GROUPBY·{cert_tot}项)',
f'GROUPBY({CREF}!H3:H200,{CREF}!D3:D200,COUNTA,3,0)',
write_formula_sheet(s10,'公司认定分类','GROUPBY on 认定数据',
[('A',4,f'=GROUPBY({CREF}!H3:H200,{CREF}!D3:D200,COUNTA,3,0)','')],[('A',20),('B',12)])
s10b=wb.create_sheet('公式-技术方案分类')
TREF = "'认定技术方案'"
write_formula_sheet(s10b,f'认定技术方案等级分布GROUPBY·{tech_tot}项)',
TREF="'认定技术方案'"
write_formula_sheet(s10b,'认定技术方案等级分布',
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)])
# ═══ 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,['分类','方案数','项目数','占比','备注'])
@ -241,9 +239,7 @@ for w,col in zip([6,8,40,35,18,12,10,20],'ABCDEFGH'): s14.column_dimensions[col]
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: COUNTIFS动态对比")
print(f" S5 认定技术方案(表2): {tech_tot}")
print(f" S6-S10 GROUPBY/FILTER 动态公式(含表1+表2)")
print(f" S11-S14 静态汇总表(交叉验证)")
print(f" S1-S3 数据源: OA{len(valid_all)}/{len(valid_2026)} + 认定{cert_tot}+技术{tech_tot}")
print(f" S4 公式-认定vsOA: COUNTIFS对比(项目名预填+公式自动计算)")
print(f" S5-S10b 动态公式: GROUPBY/FILTER/COUNTIF")
print(f" S11-S14 静态汇总: 交叉验证")