4. 审批进度 & 三色预警信号
-
+
-
+
🔴 在实施未审批🟠 ≤30天🟡 ≤45天
@@ -133,10 +133,9 @@ tr:nth-child(even) td{background:var(--bg)}
关键指标国别分布预警明细
-
+
| 信号 | 类型 | 项目名称 | 方案名称 | 当前状态 | 计划开工 | 距开工 |
| 🟠 | 超规类 | 阿联酋迪拜马克图姆国际机场地下结构工程项目 | BHS处理中心/GSE隧道现浇板专项施工方案(4包) | 已添加、未实施 | 2026-06-10 | 2天 |
-| 🟠 | 一般类 | 阿联酋迪拜马克图姆国际机场地下结构工程项目 | 钢筋加工厂桥式起重机安装专项施工方案(2包) | 已审批、未实施 | 2026-06-30 | 22天 |
| 🟡 | 超规类 | 阿联酋阿布扎比汽车基地房建项目 | 模板支立工程专项方案 | 未审批、未实施 | 2026-07-10 | 32天 |
| 🟡 | 超规类 | 阿联酋阿布扎比汽车基地房建项目 | 深基坑开挖方案 | 未审批、未实施 | 2026-07-15 | 37天 |
| 🟡 | 超规类 | 阿联酋迪拜马克图姆国际机场地下结构工程项目 | 处理中心现浇倒T梁专项施工方案(4包) | 审批中、未实施 | 2026-07-20 | 42天 |
diff --git a/data/2026-06-08/cleaned/危大方案看板数据工作簿.xlsx b/data/2026-06-08/cleaned/危大方案看板数据工作簿.xlsx
index 2ff5bb2..6e28dbf 100644
Binary files a/data/2026-06-08/cleaned/危大方案看板数据工作簿.xlsx and b/data/2026-06-08/cleaned/危大方案看板数据工作簿.xlsx differ
diff --git a/data/认定数据/2026/certified_schemes.csv b/data/认定数据/2026/certified_schemes.csv
new file mode 100644
index 0000000..da740a7
--- /dev/null
+++ b/data/认定数据/2026/certified_schemes.csv
@@ -0,0 +1,8 @@
+项目名称,认定_危大方案总数,认定_超规数,所属国别,平台_匹配项目,平台_方案总数,平台_超规数,差额,匹配状态
+沙特利雅得德拉伊耶门二期多功能场馆及办公楼房建项目,5,2,沙特,沙特利雅得德拉伊耶门二期多功能场馆及办公楼房建项目,3,1,-2,✅
+沙特吉赞基础下游工业城3区1巷独栋别墅一期项目,1,0,沙特,沙特吉赞基础下游工业城3区1巷独栋别墅一期项目,3,2,2,✅
+沙特达曼港第一和第二集装箱码头升级改造工程项目,1,1,沙特,沙特达曼港第一和第二集装箱码头升级改造工程,3,2,2,✅
+阿联酋沙迦卡尔巴摩托艇码头项目,2,0,阿联酋,阿联酋沙迦卡尔巴摩托艇港开发项目,3,1,1,✅
+阿联酋迪拜马克图姆国际机场地下结构工程项目,31,12,阿联酋,阿联酋迪拜马克图姆国际机场地下结构工程项目,35,13,4,✅
+阿联酋阿布扎比哈里发港EGA泊位翻新项目,1,0,阿联酋,阿联酋阿布扎比哈里发港EGA泊位翻新工程项目,1,0,0,✅
+阿联酋阿布扎比马斯努阿岛水工项目,2,1,阿联酋,阿联酋阿布扎比马斯努阿岛水工项目,0,0,-2,✅
diff --git a/data/认定数据/2026/certified_schemes_detail.csv b/data/认定数据/2026/certified_schemes_detail.csv
new file mode 100644
index 0000000..ce5b34a
--- /dev/null
+++ b/data/认定数据/2026/certified_schemes_detail.csv
@@ -0,0 +1,44 @@
+所属区域,所属国别,项目名称,方案名称,编制单位,工程类别,分部工程类别,是否超一定规模,计划开工日期
+中东,沙特,沙特利雅得德拉伊耶门二期多功能场馆及办公楼房建项目,预制看台安装方案,中交第二公路工程局有限公司,房屋建设和市政基础设施工程,起重吊装及起重机械安装拆卸工程,否,2026-07-01
+中东,沙特,沙特利雅得德拉伊耶门二期多功能场馆及办公楼房建项目,钢结构施工方案,中建钢构承包有限公司,房屋建设和市政基础设施工程,起重吊装及起重机械安装拆卸工程,是,2026-05-23
+中东,沙特,沙特利雅得德拉伊耶门二期多功能场馆及办公楼房建项目,幕墙施工方案,中建装饰绿创科技有限公司,房屋建设和市政基础设施工程,其它,否,2026-08-22
+中东,沙特,沙特利雅得德拉伊耶门二期多功能场馆及办公楼房建项目,高支模专项施工方案,中交第二公路工程局有限公司,房屋建设和市政基础设施工程,模板工程及支撑体系,是,2026-03-20
+中东,沙特,沙特利雅得德拉伊耶门二期多功能场馆及办公楼房建项目,650t履带吊安拆施工方案,中建钢构承包有限公司,房屋建设和市政基础设施工程,起重吊装及起重机械安装拆卸工程,否,2026-07-25
+中东,沙特,沙特吉赞基础下游工业城3区1巷独栋别墅一期项目,别墅主体结构安装专项施工方案,中交四航局二公司,房屋建设和市政基础设施工程,起重吊装及起重机械安装拆卸工程,否,2026-04-01
+中东,阿联酋,阿联酋迪拜马克图姆国际机场地下结构工程项目,处理中心专项施工方案(4包),中交四航局二公司(4包),民航工程,模板工程及支撑体系,否,2026-03-10
+中东,阿联酋,阿联酋迪拜马克图姆国际机场地下结构工程项目,GSE&BHS隧道施工专项施工方案(4包),中交四航局二公司(4包),民航工程,模板工程及支撑体系,否,2026-03-28
+中东,阿联酋,阿联酋迪拜马克图姆国际机场地下结构工程项目,T梁预制、运输和安装专项施工方案(4包),中交四航局二公司(4包),民航工程,起重吊装及安装拆卸工程,否,2026-05-13
+中东,阿联酋,阿联酋迪拜马克图姆国际机场地下结构工程项目,临时用电施工组织设计(4包),中交四航局二公司(4包),电力工程,其他,否,2026-02-28
+中东,阿联酋,阿联酋迪拜马克图姆国际机场地下结构工程项目,BHS处理中心/GSE隧道现浇板专项施工方案(4包),中交四航局二公司(4包),民航工程,模板工程及支撑体系,是,2026-05-15
+中东,阿联酋,阿联酋迪拜马克图姆国际机场地下结构工程项目,处理中心现浇倒T梁专项施工方案(4包),中交四航局二公司(4包),民航工程,模板工程及支撑体系,是,2026-04-15
+中东,阿联酋,阿联酋迪拜马克图姆国际机场地下结构工程项目,塔吊施工监管方案(4包),中交四航局二公司(4包),民航工程,起重吊装及安装拆卸工程,否,2026-03-25
+中东,阿联酋,阿联酋迪拜马克图姆国际机场地下结构工程项目,龙门吊施工监管方案(4包),中交四航局二公司(4包),民航工程,起重吊装及安装拆卸工程,是,2026-03-20
+中东,阿联酋,阿联酋迪拜马克图姆国际机场地下结构工程项目,混凝土拌和站施工监管方案(4包),中交四航局二公司(4包),民航工程,起重吊装及安装拆卸工程,否,2026-03-18
+中东,阿联酋,阿联酋迪拜马克图姆国际机场地下结构工程项目,履带吊安装和拆卸专项施工方案(4包),中交四航局二公司(4包),民航工程,起重吊装及安装拆卸工程,是,2026-03-25
+中东,阿联酋,阿联酋迪拜马克图姆国际机场地下结构工程项目,APM隧道现浇段顶板模板及支撑体系专项施工方案(2包),中交一航局三公司(2包),民航工程,模板工程和支撑体系,是,2026-11-20
+中东,阿联酋,阿联酋迪拜马克图姆国际机场地下结构工程项目,GSE隧道现浇段顶板模板及支撑体系专项施工方案(2包),中交一航局三公司(2包),民航工程,模板工程和支撑体系,是,2026-04-20
+中东,阿联酋,阿联酋迪拜马克图姆国际机场地下结构工程项目,APM隧道预制梁安装专项施工方案(2包),中交一航局三公司(2包),民航工程,起重吊装及安装拆卸工程,否,2026-11-15
+中东,阿联酋,阿联酋迪拜马克图姆国际机场地下结构工程项目,GSE隧道预制梁安装专项施工方案(2包),中交一航局三公司(2包),民航工程,起重吊装及安装拆卸工程,否,2026-04-30
+中东,阿联酋,阿联酋迪拜马克图姆国际机场地下结构工程项目,混凝土拌合站钢结构料仓大棚安装专项施工方案(2包),中交一航局三公司(2包),民航工程,起重吊装及安装拆卸工程,否,2026-03-25
+中东,阿联酋,阿联酋迪拜马克图姆国际机场地下结构工程项目,钢筋加工厂钢结构大棚专项施工方案(2包),中交一航局三公司(2包),民航工程,起重吊装及安装拆卸工程,否,2026-03-25
+中东,阿联酋,阿联酋迪拜马克图姆国际机场地下结构工程项目,预制场龙门吊安装专项施工方案,中交一航局三公司(2包),民航工程,起重吊装及安装拆卸工程,是,2026.3.25
+中东,阿联酋,阿联酋迪拜马克图姆国际机场地下结构工程项目,预制场龙门吊拆卸专项施工方案,中交一航局三公司(2包),民航工程,起重吊装及安装拆卸工程,否,2027-06-25
+中东,阿联酋,阿联酋迪拜马克图姆国际机场地下结构工程项目,履带吊安装专项施工方案,中交一航局三公司(2包),民航工程,起重吊装及安装拆卸工程,是,2026-04-20
+中东,阿联酋,阿联酋迪拜马克图姆国际机场地下结构工程项目,履带吊拆除专项施工方案,中交一航局三公司(2包),民航工程,起重吊装及安装拆卸工程,否,2027-06-25
+中东,阿联酋,阿联酋迪拜马克图姆国际机场地下结构工程项目,临时用电施工组织设计(2包),中交一航局三公司(2包),电力工程,电力通用_其他,否,2026-03-10
+中东,阿联酋,阿联酋迪拜马克图姆国际机场地下结构工程项目,隧道顶板专项施工方案(3包),中交二航局一公司(3包),民航工程,模板工程及支撑体系,是,2026-04-25
+中东,阿联酋,阿联酋迪拜马克图姆国际机场地下结构工程项目,预制构件安装专项施工方案(3包),中交二航局一公司(3包),民航工程,起重吊装及安装拆卸工程,是,2026-04-30
+中东,阿联酋,阿联酋迪拜马克图姆国际机场地下结构工程项目,搅拌站建设专项施工方案(3包),中交二航局一公司(3包),民航工程,起重吊装及安装拆卸工程,否,2026-03-10
+中东,阿联酋,阿联酋迪拜马克图姆国际机场地下结构工程项目,场站建设施工方案(3包),中交二航局一公司(3包),民航工程,起重吊装及安装拆卸工程,否,2026-03-10
+中东,阿联酋,阿联酋迪拜马克图姆国际机场地下结构工程项目,塔吊安装专项施工方案(3包),中交二航局一公司(3包),民航工程,起重吊装及安装拆卸工程,否,2026-03-10
+中东,阿联酋,阿联酋迪拜马克图姆国际机场地下结构工程项目,塔吊拆除专项施工方案(3包),中交二航局一公司(3包),民航工程,起重吊装及安装拆卸工程,否,2027-06-15
+中东,阿联酋,阿联酋迪拜马克图姆国际机场地下结构工程项目,门机安装专项施工方案(3包),中交二航局一公司(3包),民航工程,起重吊装及安装拆卸工程,是,2026-03-10
+中东,阿联酋,阿联酋迪拜马克图姆国际机场地下结构工程项目,门机拆除专项施工方案(3包),中交二航局一公司(3包),民航工程,起重吊装及安装拆卸工程,否,2027-06-15
+中东,阿联酋,阿联酋迪拜马克图姆国际机场地下结构工程项目,履带吊安拆专项施工方案(3包),中交二航局一公司(3包),民航工程,起重吊装及安装拆卸工程,是,2026-03-10
+中东,阿联酋,阿联酋迪拜马克图姆国际机场地下结构工程项目,临时用电专项施工方案(3包),中交二航局一公司(3包),电力工程,电力通用_其他,否,2026-03-10
+中东,沙特,沙特达曼港第一和第二集装箱码头升级改造工程项目,厂棚D钢结构安装专项施工方案,中交二航局一公司,房屋建设和市政基础设施工程,其它,是,2026-03-05
+中东,阿联酋,阿联酋阿布扎比马斯努阿岛水工项目,钢管桩打设专项施工方案,中交一航局三公司,水运工程,桥涵工程,否,2026-09-10
+中东,阿联酋,阿联酋阿布扎比马斯努阿岛水工项目,方块吊装专项施工方案,中交一航局三公司,水运工程,起重吊装工程,是,2026-06-01
+中东,阿联酋,阿联酋沙迦卡尔巴摩托艇码头项目,卡尔巴钢板桩围堰专项施工方案,中交一航局三公司,水运工程,大型临时工程,否,2026-05-01
+中东,阿联酋,阿联酋沙迦卡尔巴摩托艇码头项目,卡尔巴方块安装起重吊装专项施工方案,中交一航局三公司,水运工程,起重吊装工程,否,2026-08-15
+中东,阿联酋,阿联酋阿布扎比哈里发港EGA泊位翻新项目,临时用电施工组织设计,中交一航局三公司(2包),水运工程,临时用电工程,否,2026-02-20
diff --git a/src/clean_certified.py b/src/clean_certified.py
index a027cfc..043bdbc 100644
--- a/src/clean_certified.py
+++ b/src/clean_certified.py
@@ -16,7 +16,7 @@ import difflib
# ============================================================
RAW_DIR = Path("/mnt/y/WorkingEmail/OA收文_Incoming")
CERT_SRC_DIR = RAW_DIR / "2026-03-16_关于公布公司2026年度技术方案编制计划的通知"
-OUT_DIR = CERT_SRC_DIR / "cleaned"
+OUT_DIR = Path("/mnt/y/Openclaw_Hub/03.资源/实施项目 wiki/dashboard/data/认定数据/2026") # 年度固定输出
OUT_DIR.mkdir(parents=True, exist_ok=True)
CERT_FILES = [
diff --git a/src/gen_workbook.py b/src/gen_workbook.py
index ffc2802..fca5452 100644
--- a/src/gen_workbook.py
+++ b/src/gen_workbook.py
@@ -1,161 +1,226 @@
#!/usr/bin/env python3
-"""危大方案看板数据工作簿 v3 — 数据源完整镜像源表结构 + 汇总表"""
+"""危大方案看板数据工作簿 v5 — 双数据源(OA登记+公司认定) + GROUPBY/FILTER动态公式"""
import pandas as pd
from openpyxl import Workbook
from openpyxl.styles import Font, PatternFill, Alignment, Border, Side
from openpyxl.utils import get_column_letter
BASE = "/mnt/y/Openclaw_Hub/03.资源/实施项目 wiki/dashboard/data/2026-06-08"
+CERT_DIR = "/mnt/y/Openclaw_Hub/03.资源/实施项目 wiki/dashboard/data/认定数据/2026" # 年度固定目录·一年一次
OUT = f"{BASE}/cleaned/危大方案看板数据工作簿.xlsx"
+REPORT_DATE = "2026-06-08"
+REF = "'有效≥2026'" # OA公式引用
+CREF = "'认定数据'" # 认定公式引用
-# ── Read raw source ──
-raw = pd.read_excel("/mnt/y/Openclaw_Hub/03.资源/实施项目 wiki/dashboard/raw/2026-06-08/技术方案统计表.xlsx", header=1)
-raw.columns = [str(c).strip() for c in raw.columns]
-print(f"Source rows: {len(raw)}, cols: {list(raw.columns)[:5]}...")
+# ════ 数据源1: OA登记(methods_cleaned.csv) ════
+df = pd.read_csv(f"{BASE}/cleaned/methods_cleaned.csv")
+SRC_COLS = list(df.columns[:24])
+valid_all = df[df['是否有效登记'] == True].copy()
+valid_2026 = valid_all[valid_all['开工年份'] >= 2026].copy()
+m = valid_2026
+tot = len(m); gen = (m['是否超一定规模']!='是').sum(); sup = tot-gen
+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}黄")
-# ── Read cleaned data for filtering ──
-cl = pd.read_parquet(f"{BASE}/cleaned/methods_cleaned.parquet")
-valid_idx = cl[cl['是否有效登记'] == True].index.tolist()
-cl['开工年份'] = pd.to_datetime(cl['分部分项工程计划开工日期'], errors='coerce').dt.year
-yr_idx = cl[(cl['是否有效登记'] == True) & (cl['开工年份'] >= 2026)].index.tolist()
+# ════ 数据源2: 公司认定(certified_schemes_detail.csv) ════
+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")
+print(f"公司认定: {cert_tot}项(一般{cert_gen}/超规{cert_sup}) 项目{cert_comp['项目名称'].nunique()}")
-# Merge back to raw columns (align by index)
-raw_clean = raw.loc[raw.index.isin(valid_idx)].copy()
-raw_2026 = raw.loc[raw.index.isin(yr_idx)].copy()
+# ════ 样式 ════
+HDR_F=Font(name='微软雅黑',bold=True,size=10,color='FFFFFF'); HDR_BG=PatternFill('solid',fgColor='1A3A5C')
+TITLE_F=Font(name='微软雅黑',bold=True,size=14,color='1A3A5C'); DATA_F=Font(name='微软雅黑',size=10)
+BOLD_F=Font(name='微软雅黑',bold=True,size=10); GRAY_F=Font(name='微软雅黑',size=9,color='8899AA')
+GREEN_F=Font(name='微软雅黑',bold=True,size=10,color='2E7D32'); RED_F=Font(name='微软雅黑',bold=True,size=10,color='D94E34')
+BLUE_F=Font(name='微软雅黑',bold=True,size=10,color='1A3A5C'); ORANGE_F=Font(name='微软雅黑',bold=True,size=10,color='E65100')
+FORMULA_F=Font(name='Consolas',size=10,color='1A3A5C')
+WARN_BG=PatternFill('solid',fgColor='FFF3E0'); INFO_BG=PatternFill('solid',fgColor='F0F4FA')
+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'))
-print(f"Valid rows: {len(raw_clean)}, >=2026: {len(raw_2026)}")
+def hdr_row(ws,r,cols):
+ for i,h in enumerate(cols):
+ c=ws.cell(r,i+1,h); c.font=HDR_F; c.fill=HDR_BG; c.border=BORDER; c.alignment=CENTER
-# ── For summary sheets, use cleaned data ──
-m = cl[(cl['是否有效登记'] == True) & (cl['开工年份'] >= 2026)].copy()
-m['简化状态'] = m['方案状态_clean'].apply(lambda s: '已完成' if '已完成' in str(s) else '未完成')
-m['是否超规'] = m['是否超一定规模'].astype(str).apply(lambda x: '超规类' if x == '是' else '一般类')
-today = pd.Timestamp('2026-06-08')
-m['距开工天'] = (pd.to_datetime(m['分部分项工程计划开工日期']) - today).dt.days.astype(int)
+def write_data_sheet(ws,df_out,title,cols):
+ ncol=len(cols)
+ ws.merge_cells(start_row=1,start_column=1,end_row=1,end_column=ncol)
+ ws.cell(1,1,title).font=TITLE_F; ws.cell(1,1).border=GOLD_BD
+ hdr_row(ws,3,cols)
+ for ri,(_,row) in enumerate(df_out.iterrows()):
+ rr=ri+4
+ for ci,col in enumerate(cols):
+ v=row.get(col,'')
+ if pd.isna(v): v=''
+ elif isinstance(v,(pd.Timestamp,)): v=str(v)[:10]
+ elif isinstance(v,(float,)) and v==int(v): v=int(v)
+ ws.cell(rr,ci+1,v).font=DATA_F; ws.cell(rr,ci+1,v).border=BORDER
+ ws.auto_filter.ref=f'A3:{get_column_letter(ncol)}{len(df_out)+3}'
+ ws.freeze_panes='A4'
+ for i,col in enumerate(cols):
+ ws.column_dimensions[get_column_letter(i+1)].width=max(10,min(40,len(str(col))*2.2))
-# Warning
-def warn_lev(d, s):
- s = str(s); d = int(d)
- if '未实施' not in s and '审批中' not in s: return ''
- if d <= 30: return '🟠'
- if d <= 45: return '🟡'
- return ''
-m['预警'] = m.apply(lambda r: warn_lev(r['距开工天'], r['方案状态_clean']), axis=1)
-
-# ── Styles ──
-HDR_F = Font(name='微软雅黑', bold=True, size=10, color='FFFFFF')
-HDR_BG = PatternFill('solid', fgColor='1A3A5C')
-TITLE_F = Font(name='微软雅黑', bold=True, size=14, color='1A3A5C')
-SUB_F = Font(name='微软雅黑', bold=True, size=12, color='1A3A5C')
-GRAY_F = Font(name='微软雅黑', size=9, color='8899AA')
-DATA_F = Font(name='微软雅黑', size=10)
-BOLD_F = Font(name='微软雅黑', bold=True, size=10)
-RED_F = Font(name='微软雅黑', bold=True, size=10, color='D94E34')
-GREEN_F = Font(name='微软雅黑', bold=True, size=10, color='2E7D32')
-BLUE_F = Font(name='微软雅黑', bold=True, size=10, color='1A3A5C')
-WARN_BG = PatternFill('solid', fgColor='FFF3E0')
-BORDER = Border(left=Side('thin', 'DBE2EA'), right=Side('thin', 'DBE2EA'),
- top=Side('thin', 'DBE2EA'), bottom=Side('thin', 'DBE2EA'))
-CENTER = Alignment(horizontal='center', vertical='center')
-GOLD_BD = Border(bottom=Side(style='medium', color='C8962E'))
-
-def hdr_row(ws, r, cols):
- for i, h in enumerate(cols):
- c = ws.cell(r, i+1, h); c.font = HDR_F; c.fill = HDR_BG; c.border = BORDER; c.alignment = CENTER
-
-def write_data_sheet(ws, df, title):
- """Write a full data sheet from dataframe"""
- ws.merge_cells(start_row=1, start_column=1, end_row=1, end_column=len(df.columns))
- ws.cell(1, 1, title).font = TITLE_F
- ws.cell(1, 1).border = GOLD_BD
-
- cols = list(df.columns)
- hdr_row(ws, 3, cols)
- for r, (_, row) in enumerate(df.iterrows()):
- for c, col in enumerate(cols):
- v = row[col]
- if pd.isna(v): v = ''
- elif isinstance(v, (pd.Timestamp,)): v = str(v)[:10]
- cell = ws.cell(r+4, c+1, v); cell.font = DATA_F; cell.border = BORDER
- ws.auto_filter.ref = f'A3:{get_column_letter(len(cols))}{len(df)+3}'
- ws.freeze_panes = 'A4'
- for i in range(len(cols)):
- ws.column_dimensions[get_column_letter(i+1)].width = max(12, min(35, len(str(cols[i]))*2))
+def write_formula_sheet(ws,title,subtitle,formulas,col_widths):
+ ws.merge_cells(start_row=1,start_column=1,end_row=1,end_column=4)
+ ws.cell(1,1,title).font=TITLE_F; ws.cell(1,1).border=GOLD_BD
+ if subtitle:
+ ws.merge_cells(start_row=2,start_column=1,end_row=2,end_column=4)
+ 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
+ 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()
-# ════ Sheet 1: 源表清洗后(完整列) ════
-s1 = wb.active; s1.title = '源表清洗后'
-write_data_sheet(s1, raw_clean, '技术方案统计表 · 清洗后(有效登记 全部年份)')
+# ════════ 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','是否完成审批','是否有效登记','开工年份','开工月份','预警信号'])
-# ════ Sheet 2: 有效2026+ ════
-s2 = wb.create_sheet('有效≥2026')
-write_data_sheet(s2, raw_2026, '技术方案统计表 · 有效登记 ≥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','是否完成审批','是否有效登记','开工年份','开工月份','预警信号'])
-# ════ Sheet 3: 年度认定汇总 ════
-s3 = wb.create_sheet('年度认定汇总')
-s3.merge_cells('A1:D1'); s3.cell(1, 1, '年度认定(≥2026开工)').font = TITLE_F; s3.cell(1, 1).border = GOLD_BD
-hdr_row(s3, 3, ['分类', '方案数', '项目数', '占比'])
-tot = len(m)
-for r, (cat, sub) in enumerate([('一般类', m[m['是否超规'] == '一般类']), ('超规类', m[m['是否超规'] == '超规类'])]):
- cnt = len(sub); proj = sub['项目名称'].nunique()
- for c, (v, f) in enumerate(zip([cat, cnt, proj, f'{cnt/tot*100:.0f}%'], [DATA_F, DATA_F, DATA_F, DATA_F])):
- cell = s3.cell(r+4, c+1, v); cell.font = f; cell.border = BORDER
-for c, (v, f) in enumerate(zip(['合计', tot, m['项目名称'].nunique(), '100%'], [BOLD_F]*4)):
- cell = s3.cell(6, c+1, v); cell.font = f; cell.border = BORDER
-for w, c in zip([12, 10, 10, 10], 'ABCD'): s3.column_dimensions[c].width = w
+# ════════ 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)
-# ════ Sheet 4: 国别×分类 ════
-s4 = wb.create_sheet('国别×分类')
-s4.merge_cells('A1:D1'); s4.cell(1, 1, '国别×分类分布').font = TITLE_F; s4.cell(1, 1).border = GOLD_BD
-ct = m.groupby(['所属国别', '是否超规']).size().unstack(fill_value=0)
-ct['合计'] = ct.sum(1); ct.loc['合计'] = ct.sum()
-hdr_row(s4, 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 = s4.cell(r+4, c+1, v); cell.font = DATA_F; cell.border = BORDER
-s4.column_dimensions['A'].width = 25
+# ════════ S4: 认定vsOA ════════
+s4=wb.create_sheet('认定vsOA')
+COMP_COLS=['项目名称','认定_危大方案总数','认定_超规数','所属国别','平台_方案总数','平台_超规数','差额','匹配状态']
+ncol_c=len(COMP_COLS)
+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[col]
+ if pd.isna(v): v=''
+ cell=s4.cell(ri+4,ci+1,v); cell.font=DATA_F; cell.border=BORDER
+ if col=='差额' 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))
-# ════ Sheet 5: 审批进度 & 预警 ════
-s5 = wb.create_sheet('审批进度')
-s5.merge_cells('A1:D1'); s5.cell(1, 1, '审批进度 & 三色预警').font = TITLE_F; s5.cell(1, 1).border = GOLD_BD
-hdr_row(s5, 3, ['指标', '数值', '占比', '备注'])
-completed = (m['简化状态'] == '已完成').sum(); unfinished = tot - completed
-rn = (m['预警'] != '').sum(); orn = (m['预警'] == '🟠').sum(); ye = (m['预警'] == '🟡').sum()
-rows = [
- ('方案总数', tot, '100%', '≥2026年开工·排除已作废'),
- ('已完成审批', completed, f'{completed/tot*100:.0f}%', ''),
- ('未完成审批', unfinished, f'{unfinished/tot*100:.0f}%', '含审批中+未审批'),
- ('🟠 橙色预警', orn, f'{orn/tot*100:.0f}%', '≤30天未审批'),
- ('🟡 黄色预警', ye, f'{ye/tot*100:.0f}%', '≤45天未审批'),
- ('预警合计', rn, f'{rn/tot*100:.0f}%', f'🟠{orn}项+🟡{ye}项'),
-]
-for r, (lab, val, pct, note) in enumerate(rows):
- fmts = [DATA_F, DATA_F, DATA_F, GRAY_F]
- if '完成' in lab and '未' not in lab: fmts = [GREEN_F, BOLD_F, BOLD_F, GRAY_F]
- if '预警' in lab: fmts = [RED_F, BOLD_F, BOLD_F, GRAY_F]
- if '总数' in lab: fmts = [BLUE_F, BOLD_F, BOLD_F, GRAY_F]
- for c, (v, f) in enumerate(zip([lab, val, pct, note], fmts)):
- cell = s5.cell(r+4, c+1, v); cell.font = f; cell.border = BORDER
-for w, c in zip([18, 10, 10, 35], 'ABCD'): s5.column_dimensions[c].width = w
+# ════════ S5-S8: 动态公式 ════════
-# ════ Sheet 6: 预警明细 ════
-s6 = wb.create_sheet('预警明细')
-s6.merge_cells('A1:G1'); s6.cell(1, 1, f'三色预警明细(共{rn}项)').font = TITLE_F; s6.cell(1, 1).border = GOLD_BD
-hdr_row(s6, 3, ['信号', '类型', '项目名称', '方案名称', '状态', '计划开工', '距开工'])
-warned = m[m['预警'] != ''].sort_values('距开工天')
-for r, (_, row) in enumerate(warned.iterrows()):
- is_w = '未审批' in str(row['方案状态_clean'])
- bg = WARN_BG if is_w else None
- vals = [row['预警'], row['是否超规'], row['项目名称'], row['方案名称'],
- row['方案状态_clean'], str(row['分部分项工程计划开工日期'])[:10], f"{int(row['距开工天'])}天"]
- for c, v in enumerate(vals):
- cell = s6.cell(r+4, c+1, v); cell.font = RED_F if c == 6 else DATA_F; cell.border = BORDER
- if bg: cell.fill = bg
-s6.auto_filter.ref = f'A3:G{len(warned)+3}'
-for w, c in zip([6, 8, 40, 35, 18, 12, 8], 'ABCDEFG'): s6.column_dimensions[c].width = w
+# ── S5: 公式-年度认定 ──
+s5=wb.create_sheet('公式-年度认定')
+write_formula_sheet(s5,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国别×分类(自动排序)',
+ 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审批进度 & 预警',
+ 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)',''),
+ ('A',6,'未完成审批',''),('B',6,f'=COUNTIF({REF}!Z4:Z200,FALSE)',''),
+ ('A',7,'橙色预警',''),('B',7,f'=COUNTIF({REF}!AD4:AD200,"orange")',''),
+ ('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)',
+ 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}项)',
+ 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: 静态汇总表(可交叉验证) ════════
+
+# ── 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,['分类','方案数','项目数','占比','备注'])
+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
+
+# ── 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
+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))
+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
+
+# ── 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,['指标','数值','占比','备注'])
+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]),
+ ('未完成审批',int(unfinished),f'{unfinished/tot*100:.0f}%','审批中+未审批',[DATA_F]*3+[GRAY_F]),
+ ('🟠 橙色预警',int(orange),f'{orange/tot*100:.0f}%','≤30天',[ORANGE_F]*3+[GRAY_F]),
+ ('🟡 黄色预警',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
+
+# ── 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,['信号','距开工','项目名称','方案名称','方案状态','计划开工','超规/一般','国别'])
+today=pd.Timestamp(REPORT_DATE)
+for r,(_,row) in enumerate(warn_df.iterrows()):
+ days=(pd.to_datetime(row['分部分项工程计划开工日期'])-today).days
+ vals=[{'orange':'🟠','yellow':'🟡','red':'🔴'}.get(row['预警信号'],''),f'{int(days)}天',
+ row['项目名称'],row['方案名称'],
+ row['方案状态_clean'] if pd.notna(row.get('方案状态_clean')) else row['方案状态'],
+ 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
wb.save(OUT)
-print(f"✅ {OUT}")
-print(f" Sheet1: 源表清洗后 ({len(raw_clean)}行 × {len(raw_clean.columns)}列)")
-print(f" Sheet2: 有效≥2026 ({len(raw_2026)}行)")
-print(f" Sheet3-6: 汇总表 + 预警明细")
+print(f"\n✅ {OUT}")
+print(f" S1-S2 OA登记: {len(valid_all)}全量 + {len(valid_2026)}≥2026")
+print(f" S3 公司认定: {cert_tot}行({cert_gen}一般+{cert_sup}超规)")
+print(f" S4 认定vsOA: {len(cert_comp)}项目")
+print(f" S5-S9 GROUPBY/FILTER 动态公式")
+print(f" S10-S13 静态汇总表(交叉验证)")