fix: 公式-认定vsOA重写-项目名预填+COUNTIFS直写(无IF包装)
This commit is contained in:
parent
801e14c944
commit
381b4c740f
Binary file not shown.
@ -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年度公司认定危大方案明细(中港科技便〔2026〕6号·{cert_tot}项)',CERT_COLS)
|
||||
|
||||
# ═══ S4: 公式-认定vsOA(纯Excel公式·COUNTIFS动态对比) ═══
|
||||
# ═══ S4: 公式-认定vsOA ═══
|
||||
# 项目名从认定数据 C4:C200 提取,省去 UNIQUE(WPS兼容性更好)
|
||||
# 用 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年度公司认定技术方案明细(ⅠⅡⅢ类·中港科技便〔2026〕6号·{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 静态汇总: 交叉验证")
|
||||
Loading…
x
Reference in New Issue
Block a user