From 91d82ac475b3aeb20d11e61718bbbb921a0463ce Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=A4=A7=E5=B8=88?= Date: Tue, 9 Jun 2026 03:09:03 +0800 Subject: [PATCH] =?UTF-8?q?fix:=20=E5=B7=A5=E4=BD=9C=E7=B0=BFv3=20?= =?UTF-8?q?=E2=80=94=20=E6=95=B0=E6=8D=AE=E6=BA=90=E5=AE=8C=E6=95=B4?= =?UTF-8?q?=E9=95=9C=E5=83=8F=E6=BA=90=E8=A1=A825=E5=88=97+=E6=95=B0?= =?UTF-8?q?=E5=AD=97=E5=AF=B9=E9=BD=90HTML?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../cleaned/危大方案看板数据工作簿.xlsx | Bin 14372 -> 31953 bytes src/gen_workbook.py | 266 +++++++++--------- 2 files changed, 138 insertions(+), 128 deletions(-) diff --git a/data/2026-06-08/cleaned/危大方案看板数据工作簿.xlsx b/data/2026-06-08/cleaned/危大方案看板数据工作簿.xlsx index a82b1c17e63eb8ab8777dbbe451cd426abcf8468..2ff5bb282fe22dc29df42670b77fab4eac6d72b2 100644 GIT binary patch literal 31953 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ⅈ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%0A%|- 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 delta 10755 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].copy() +# ── 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]}...") + +# ── 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() + +# 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() + +print(f"Valid rows: {len(raw_clean)}, >=2026: {len(raw_2026)}") + +# ── 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 '一般类') -m['开工年份'] = m['开工年份'].astype(int) -today=pd.Timestamp('2026-06-08') -m['距开工天'] = (pd.to_datetime(m['分部分项工程计划开工日期'])-today).dt.days.astype(int) +today = pd.Timestamp('2026-06-08') +m['距开工天'] = (pd.to_datetime(m['分部分项工程计划开工日期']) - today).dt.days.astype(int) + +# 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') -GOLD_LINE=Border(bottom=Side(style='medium',color='C8962E')) -WARN_BG=PatternFill('solid',fgColor='FFF3E0') -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') -BORDER=Border(left=Side('thin','DBE2EA'),right=Side('thin','DBE2EA'),top=Side('thin','DBE2EA'),bottom=Side('thin','DBE2EA')) -CENTER=Alignment(horizontal='center',vertical='center') +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 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 data_row(ws,r,vals,fmts=None): - for i,v in enumerate(vals): - c=ws.cell(r,i+1,v); c.border=BORDER; c.font=fmts[i] if fmts else DATA_F +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 -wb=Workbook() + 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)) -# ════ Sheet 0: 透视表说明 ════ -s0=wb.active; s0.title='透视表说明' -s0.merge_cells('A1:D1'); s0.cell(1,1,'🗂️ 如何创建 Excel 原生透视表').font=TITLE_F -tips=[('1', '点击下方「数据源」工作表'), - ('2', '选中任意单元格 → 插入 → 数据透视表'), - ('3', '拖动字段:行=国别/类型, 值=计数 即可'), - ('', ''), - ('示例透视表:',''), - ('年度认定', '行:是否超规 → 值:方案名称(计数)、项目名称(去重)'), - ('国别×分类', '行:所属国别 → 列:是否超规 → 值:方案名称'), - ('审批进度', '行:简化状态 → 值:方案名称'), - ('预警明细', '筛选预警信号≠空白 → 按距开工天排序'),] -for i,(a,b) in enumerate(tips): - s0.cell(i+3,1,a).font=BOLD_F; s0.cell(i+3,2,b).font=DATA_F -s0.column_dimensions['A'].width=15; s0.column_dimensions['B'].width=55 +wb = Workbook() -# ════ Sheet 1: 数据源 ════ -s1=wb.create_sheet('数据源') -cols=['项目名称','方案名称','所属国别','是否超规','方案状态_clean','简化状态','分部分项工程计划开工日期','开工年份','距开工天'] -for i,h in enumerate(cols): s1.cell(1,i+1,h); s1.cell(1,i+1).font=HDR_F; s1.cell(1,i+1).fill=HDR_BG; s1.cell(1,i+1).border=BORDER; s1.cell(1,i+1).alignment=CENTER -for r,(_,row) in enumerate(m[cols].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=s1.cell(r+2,c+1,v); cell.font=DATA_F; cell.border=BORDER -s1.auto_filter.ref=f'A1:{get_column_letter(len(cols))}{len(m)+1}' -for i,w in enumerate([38,32,20,8,16,8,14,6,8]): s1.column_dimensions[get_column_letter(i+1)].width=w -s1.freeze_panes='A2' +# ════ Sheet 1: 源表清洗后(完整列) ════ +s1 = wb.active; s1.title = '源表清洗后' +write_data_sheet(s1, raw_clean, '技术方案统计表 · 清洗后(有效登记 全部年份)') -# ════ Sheet 2: 年度认定汇总 ════ -s2=wb.create_sheet('年度认定汇总') -s2.merge_cells('A1:D1'); s2.cell(1,1,'年度认定(≥2026开工)').font=TITLE_F; s2.cell(1,1).border=GOLD_LINE -s2.cell(3,1,'分类'); s2.cell(3,2,'方案数'); s2.cell(3,3,'项目数'); s2.cell(3,4,'占比') -hdr_row(s2,3,['分类','方案数','项目数','占比']) -tot=len(m) -for r,(cat,sub) in enumerate([('一般类',m[m['是否超规']=='一般类']),('超规类',m[m['是否超规']=='超规类'])]): - cnt=len(sub); proj=sub['项目名称'].nunique() - data_row(s2,r+4,[cat,cnt,proj,f'{cnt/tot*100:.0f}%']) -data_row(s2,6,['合计',tot,m['项目名称'].nunique(),'100%'],[BOLD_F]*4) -s2.column_dimensions['A'].width=12 -for c in 'BCD': s2.column_dimensions[c].width=10 +# ════ Sheet 2: 有效2026+ ════ +s2 = wb.create_sheet('有效≥2026') +write_data_sheet(s2, raw_2026, '技术方案统计表 · 有效登记 ≥2026年开工') -# ════ Sheet 3: 国别×分类 ════ -s3=wb.create_sheet('国别×分类') -s3.merge_cells('A1:D1'); s3.cell(1,1,'国别×分类分布').font=TITLE_F; s3.cell(1,1).border=GOLD_LINE -ct=m.groupby(['所属国别','是否超规']).size().unstack(fill_value=0) -ct['合计']=ct.sum(1); ct.loc['合计']=ct.sum() -hdr_row(s3,3,['国别']+list(ct.columns)) -for r,(idx,row) in enumerate(ct.iterrows()): - data_row(s3,r+4,[idx]+[int(v) for v in row]) -s3.column_dimensions['A'].width=25 +# ════ 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 -# ════ Sheet 4: 审批进度 ════ -s4=wb.create_sheet('审批进度') -s4.merge_cells('A1:D1'); s4.cell(1,1,'审批进度 & 三色预警').font=TITLE_F; s4.cell(1,1).border=GOLD_LINE -hdr_row(s4,3,['指标','数值','占比','备注']) -completed=(m['简化状态']=='已完成').sum(); unfinished=tot-completed -# 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['w']=m.apply(lambda r:warn_lev(r['距开工天'],r['方案状态_clean']),axis=1) -rn=(m['w']!='').sum(); orn=(m['w']=='🟠').sum(); ye=(m['w']=='🟡').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}%','🟠2项+🟡4项'),] -for r,(lab,val,pct,note) in enumerate(rows): - fmts=[DATA_F,DATA_F,DATA_F,GRAY_F] - if '完成' 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] - data_row(s4,r+4,[lab,val,pct,note],fmts) -s4.column_dimensions['A'].width=15; s4.column_dimensions['B'].width=10 -s4.column_dimensions['C'].width=10; s4.column_dimensions['D'].width=35 +# ════ 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 -# ════ Sheet 5: 预警明细 ════ -s5=wb.create_sheet('预警明细') -s5.merge_cells('A1:G1'); s5.cell(1,1,'三色预警明细(共6项)').font=TITLE_F; s5.cell(1,1).border=GOLD_LINE -hdr_row(s5,3,['信号','类型','项目名称','方案名称','状态','计划开工','距开工']) -warned=m[m['w']!=''].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['w'],row['是否超规'],row['项目名称'],row['方案名称'],row['方案状态_clean'], - str(row['分部分项工程计划开工日期'])[:10],f"{int(row['距开工天'])}天"] - fmts=[DATA_F]*7; fmts[6]=RED_F - for c,(v,f) in enumerate(zip(vals,fmts)): - cell=s5.cell(r+4,c+1,v); cell.font=f; cell.border=BORDER - if bg: cell.fill=bg -s5.auto_filter.ref=f'A3:G{len(warned)+3}' -s5.column_dimensions['A'].width=6; s5.column_dimensions['B'].width=8 -s5.column_dimensions['C'].width=40; s5.column_dimensions['D'].width=35 -s5.column_dimensions['E'].width=18; s5.column_dimensions['F'].width=12 -s5.column_dimensions['G'].width=8 +# ════ 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 + +# ════ 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 wb.save(OUT) print(f"✅ {OUT}") -print(f" 📊 数据源(52行) → 汇总表4个 + 预警明细 + 透视表创建说明") +print(f" Sheet1: 源表清洗后 ({len(raw_clean)}行 × {len(raw_clean.columns)}列)") +print(f" Sheet2: 有效≥2026 ({len(raw_2026)}行)") +print(f" Sheet3-6: 汇总表 + 预警明细")