#!/usr/bin/env python3
"""HTML看板 → 可编辑PPTX v2 · 加饼图·修正三色卡·调间距"""
from pptx import Presentation
from pptx.util import Inches, Pt, Cm
from pptx.dml.color import RGBColor
from pptx.enum.text import PP_ALIGN
from pptx.enum.shapes import MSO_SHAPE
from pptx.chart.data import CategoryChartData
from pptx.enum.chart import XL_CHART_TYPE

# ── 品牌色 ──
BLUE   = RGBColor(0x1A,0x3A,0x5C)
GOLD   = RGBColor(0xC8,0x96,0x2E)
RED    = RGBColor(0xD9,0x4E,0x34)
GRAY   = RGBColor(0x88,0x99,0xAA)
BG     = RGBColor(0xEB,0xF0,0xF7)
WHITE  = RGBColor(0xFF,0xFF,0xFF)
BLACK  = RGBColor(0x33,0x33,0x33)
GREEN  = RGBColor(0x2E,0x7D,0x32)

prs = Presentation()
prs.slide_width  = Inches(13.333)
prs.slide_height = Inches(7.5)

def R(l,t,w,h,fill=None,line=None):
    s=prs.slides[-1].shapes.add_shape(MSO_SHAPE.RECTANGLE,l,t,w,h)
    s.line.fill.background()
    if fill: s.fill.solid(); s.fill.fore_color.rgb=fill
    if line: s.line.color.rgb=line; s.line.width=Pt(0.5)
    return s

def T(l,t,w,h,txt,sz=12,clr=BLACK,b=False,fn='Microsoft YaHei',al=PP_ALIGN.LEFT):
    tb=prs.slides[-1].shapes.add_textbox(l,t,w,h)
    tb.text_frame.word_wrap=True
    p=tb.text_frame.paragraphs[0]; p.text=txt
    p.font.size=Pt(sz); p.font.color.rgb=clr; p.font.bold=b
    p.font.name=fn; p.alignment=al
    return tb

def kpi(l,t,w,h,num,lbl,nc=BLUE,ns=44,ls=9):
    """KPI卡片方块"""
    R(l,t,w,h,fill=BG)
    T(l,t+Inches(0.06),w,Inches(0.5),str(num),sz=ns,clr=nc,b=True,al=PP_ALIGN.CENTER)
    T(l,t+Inches(0.55),w,Inches(0.18),lbl,sz=ls,clr=GRAY,al=PP_ALIGN.CENTER)

def warn_card(l,t,w,h,num,lbl,dot_color=RED,ns=28):
    """预警卡片：色球+数字+标签"""
    R(l,t,w,h,fill=BG)
    # 色球 (circle)
    s=prs.slides[-1].shapes.add_shape(MSO_SHAPE.OVAL,l+Inches(0.25),t+Inches(0.04),Inches(0.22),Inches(0.22))
    s.fill.solid(); s.fill.fore_color.rgb=dot_color; s.line.fill.background()
    T(l,t+Inches(0.26),w,Inches(0.28),str(num),sz=ns,clr=dot_color,b=True,al=PP_ALIGN.CENTER)
    T(l,t+Inches(0.55),w,Inches(0.18),lbl,sz=8,clr=GRAY,al=PP_ALIGN.CENTER)

def donut(l,t,w,h,vals,labels,colors):
    """原生饼图"""
    cd=CategoryChartData()
    cd.categories=labels
    cd.add_series('',vals)
    ch=prs.slides[-1].shapes.add_chart(XL_CHART_TYPE.DOUGHNUT,l,t,w,h,cd)
    plot=ch.chart.plots[0]
    for i,clr in enumerate(colors):
        plot.series[0].points[i].format.fill.solid()
        plot.series[0].points[i].format.fill.fore_color.rgb=clr
    ch.chart.has_legend=False
    ch.chart.has_title=False
    return ch

# ═══════════════════════════════════════
# SLIDE 1
# ═══════════════════════════════════════
s1=prs.slides.add_slide(prs.slide_layouts[6])

# Nav
R(Inches(0),Inches(0),Inches(13.333),Inches(0.42),fill=BLUE)
T(Inches(0.5),Inches(0.07),Inches(2),Inches(0.28),'★ 关键指标',sz=11,clr=GOLD,b=True)
T(Inches(2.5),Inches(0.07),Inches(2),Inches(0.28),'国别分布',sz=11,clr=GRAY)
T(Inches(4.5),Inches(0.07),Inches(2),Inches(0.28),'预警明细',sz=11,clr=GRAY)

# Title
R(Inches(0.5),Inches(0.58),Inches(0.06),Inches(0.28),fill=GOLD)
T(Inches(0.7),Inches(0.55),Inches(8),Inches(0.36),'危大方案编审进度看板',sz=22,clr=BLUE,b=True)
T(Inches(8.5),Inches(0.62),Inches(4.5),Inches(0.28),'中国港湾中东区域公司 技术部 · 2026年6月',sz=9,clr=GRAY,al=PP_ALIGN.RIGHT)

# === ROW 1: M1 + M2 ===
RY1=Inches(1.1); MW=Inches(5.9); MH=Inches(1.85)

# Module 1
R(Inches(0.5),RY1,MW,MH,fill=WHITE,line=RGBColor(0xDB,0xE2,0xEA))
R(Inches(0.5),RY1,MW,Inches(0.38),fill=BLUE)
T(Inches(0.7),RY1+Inches(0.04),Inches(3),Inches(0.3),'1. 年度认定',sz=13,clr=WHITE,b=True)
T(Inches(2.2),RY1+Inches(0.05),Inches(2.5),Inches(0.28),'中港科技便〔2026〕6号',sz=9,clr=GOLD)

kpi(Inches(0.7),RY1+Inches(0.58),Inches(1.1),Inches(1.0),'43','安全专项总数',ns=44)
donut(Inches(2.1),RY1+Inches(0.55),Inches(1.2),Inches(1.2),[27,16],['一般类','超规类'],[BLUE,GOLD])
T(Inches(3.5),RY1+Inches(0.7),Inches(1.5),Inches(0.25),'■ 一般类  27',sz=11,clr=BLUE)
T(Inches(3.5),RY1+Inches(1.0),Inches(1.5),Inches(0.25),'■ 超规类  16',sz=11,clr=GOLD)
T(Inches(3.5),RY1+Inches(1.3),Inches(1.5),Inches(0.25),'覆盖 7 个项目',sz=9,clr=GRAY)

# Module 2
MX2=Inches(6.93)
R(MX2,RY1,MW,MH,fill=WHITE,line=RGBColor(0xDB,0xE2,0xEA))
R(MX2,RY1,MW,Inches(0.38),fill=BLUE)
T(MX2+Inches(0.2),RY1+Inches(0.04),Inches(3),Inches(0.3),'2. OA有效登记',sz=13,clr=WHITE,b=True)
T(MX2+Inches(1.8),RY1+Inches(0.05),Inches(2),Inches(0.28),'排除已作废',sz=9,clr=GOLD)

kpi(MX2+Inches(0.2),RY1+Inches(0.58),Inches(1.1),Inches(1.0),'52','有效登记总数',ns=44)
donut(MX2+Inches(1.6),RY1+Inches(0.55),Inches(1.2),Inches(1.2),[30,22],['一般类','超规类'],[BLUE,GOLD])
T(MX2+Inches(3.0),RY1+Inches(0.7),Inches(1.5),Inches(0.25),'■ 一般类  30',sz=11,clr=BLUE)
T(MX2+Inches(3.0),RY1+Inches(1.0),Inches(1.5),Inches(0.25),'■ 超规类  22',sz=11,clr=GOLD)
T(MX2+Inches(3.0),RY1+Inches(1.3),Inches(1.5),Inches(0.25),'登记率 121%（52/43）',sz=9,clr=GRAY)

# === ROW 2: M3 + M4 ===
RY2=Inches(3.15)

# Module 3: 国别
R(Inches(0.5),RY2,MW,Inches(1.85),fill=WHITE,line=RGBColor(0xDB,0xE2,0xEA))
R(Inches(0.5),RY2,MW,Inches(0.36),fill=BLUE)
T(Inches(0.7),RY2+Inches(0.03),Inches(4),Inches(0.28),'3. 按国别分布 · 分层条形图',sz=11,clr=WHITE,b=True)

countries=[('阿拉伯联合酋长国',27,18,45),('沙特阿拉伯',3,3,6),('卡塔尔',0,1,1)]
y0=RY2+Inches(0.5)
for i,(name,gn,ch,tot) in enumerate(countries):
    y=y0+Inches(i*0.42)
    T(Inches(0.55),y,Inches(1.5),Inches(0.22),name,sz=10,clr=BLACK,al=PP_ALIGN.RIGHT)
    bw=max(gn*0.065,0.03) if gn>0 else 0
    if bw>0: R(Inches(2.15),y+Inches(0.02),Inches(bw),Inches(0.2),fill=BLUE)
    cw=max(ch*0.065,0.03) if ch>0 else 0
    if cw>0: R(Inches(2.15)+Inches(bw),y+Inches(0.02),Inches(cw),Inches(0.2),fill=GOLD)
    T(Inches(5.3),y,Inches(0.5),Inches(0.22),str(tot),sz=12,clr=BLUE,b=True)

T(Inches(0.55),RY2+Inches(1.58),Inches(2),Inches(0.18),'■ 一般类',sz=9,clr=BLUE)
T(Inches(1.3),RY2+Inches(1.58),Inches(2),Inches(0.18),'■ 超规类',sz=9,clr=GOLD)

# Module 4: 审批+预警
R(MX2,RY2,MW,Inches(1.85),fill=WHITE,line=RGBColor(0xDB,0xE2,0xEA))
R(MX2,RY2,MW,Inches(0.36),fill=BLUE)
T(MX2+Inches(0.2),RY2+Inches(0.03),Inches(4),Inches(0.28),'4. 审批进度 & 三色预警信号',sz=11,clr=WHITE,b=True)

# Row 1: 预警指标卡片 — 统一样式
CARD_W=Inches(0.85); CARD_H=Inches(0.7); CARD_GAP=Inches(0.08)
CY=RY2+Inches(0.48)

warn_data=[
    ('6', '预警总计', RED, 36, None),
    ('0', '红色', RED, 28, RED),
    ('2', '橙色', RED, 28, RGBColor(0xF0,0x80,0x00)),
    ('4', '黄色', GOLD, 28, GOLD),
    ('23', '未完成审批', GRAY, 28, None),
]
for idx,(num,lbl,clr,ns,dot) in enumerate(warn_data):
    cx=MX2+Inches(0.12)+Inches(idx*(CARD_W+CARD_GAP))
    if dot:
        warn_card(cx,CY,CARD_W,CARD_H,num,lbl,dot_color=dot,ns=ns)
    else:
        kpi(cx,CY,CARD_W,CARD_H,num,lbl,nc=clr,ns=ns,ls=8)

# Legend with colored rects
for j,(txt,clr) in enumerate([('在实施未审批',RED),('≤30天',RGBColor(0xF0,0x80,0x00)),('≤45天',GOLD)]):
    R(MX2+Inches(4.95),CY+Inches(0.05+j*0.18),Inches(0.12),Inches(0.12),fill=clr)
    T(MX2+Inches(5.1),CY+Inches(0.04+j*0.18),Inches(0.8),Inches(0.15),txt,sz=7,clr=GRAY)

# Row 2: 审批率 + 进度条
PY=RY2+Inches(1.3)
kpi(MX2+Inches(0.12),PY,Inches(0.95),Inches(0.5),'56%','审批完成率',nc=GREEN,ns=32,ls=8)
# Progress bar
R(MX2+Inches(1.2),PY+Inches(0.08),Inches(3.6),Inches(0.18),fill=BG)
R(MX2+Inches(1.2),PY+Inches(0.08),Inches(3.6*0.56),Inches(0.18),fill=BLUE)
T(MX2+Inches(1.2),PY+Inches(0.28),Inches(3.6),Inches(0.18),
  '已审批 29                          未审批 23 / 总计 52',sz=7,clr=GRAY)

# Unregistered detail (native table)
UDY=RY2+Inches(1.85)+Inches(0.12)
R(Inches(0.5),UDY,Inches(12.333),Inches(0.82),fill=WHITE,line=RGBColor(0xDB,0xE2,0xEA))
R(Inches(0.5),UDY,Inches(12.333),Inches(0.28),fill=BLUE)
T(Inches(0.7),UDY+Inches(0.02),Inches(10),Inches(0.24),'登记率未达100%的项目（认定→OA）',sz=10,clr=WHITE,b=True)

tbl=s1.shapes.add_table(2,4,Inches(0.5),UDY+Inches(0.28),Inches(12.333),Inches(0.48))
tbl.table.columns[0].width=Inches(7.5)
tbl.table.columns[1].width=Inches(1.5)
tbl.table.columns[2].width=Inches(1.5)
tbl.table.columns[3].width=Inches(1.833)
for i,(h,al) in enumerate([('项目名称',PP_ALIGN.LEFT),('认定',PP_ALIGN.CENTER),('OA',PP_ALIGN.CENTER),('缺口',PP_ALIGN.CENTER)]):
    c=tbl.table.cell(0,i); c.text=h
    c.fill.solid(); c.fill.fore_color.rgb=BG
    for p in c.text_frame.paragraphs: p.font.size=Pt(9); p.font.bold=True; p.font.color.rgb=BLUE; p.alignment=al
for i,val in enumerate(['阿联酋阿布扎比马斯努阿岛水工项目','2','0','2']):
    c=tbl.table.cell(1,i); c.text=val
    for p in c.text_frame.paragraphs:
        p.font.size=Pt(9)
        p.font.color.rgb=RED if val=='0' else BLUE
        p.font.bold=i>0
        p.alignment=PP_ALIGN.CENTER if i>0 else PP_ALIGN.LEFT

# Footer
R(Inches(0),Inches(7.18),Inches(13.333),Inches(0.32),fill=RGBColor(0xF5,0xF6,0xF8))
T(Inches(0.5),Inches(7.2),Inches(4),Inches(0.2),'中国港湾中东区域公司 技术部',sz=8,clr=GRAY)
T(Inches(11.5),Inches(7.2),Inches(1.5),Inches(0.2),'1 / 2',sz=8,clr=GRAY,al=PP_ALIGN.RIGHT)

# ═══════════════════════════════════════
# SLIDE 2: 预警明细
# ═══════════════════════════════════════
s2=prs.slides.add_slide(prs.slide_layouts[6])

R(Inches(0),Inches(0),Inches(13.333),Inches(0.42),fill=BLUE)
T(Inches(0.5),Inches(0.07),Inches(2),Inches(0.28),'关键指标',sz=11,clr=GRAY)
T(Inches(2.5),Inches(0.07),Inches(2),Inches(0.28),'国别分布',sz=11,clr=GRAY)
T(Inches(4.5),Inches(0.07),Inches(2),Inches(0.28),'★ 预警明细',sz=11,clr=GOLD,b=True)

R(Inches(0.5),Inches(0.58),Inches(0.06),Inches(0.28),fill=GOLD)
T(Inches(0.7),Inches(0.55),Inches(8),Inches(0.36),'预警信号明细清单',sz=22,clr=BLUE,b=True)
T(Inches(8.5),Inches(0.62),Inches(4.5),Inches(0.28),'共 6 项预警',sz=10,clr=GRAY,al=PP_ALIGN.RIGHT)

warnings=[
    ('🟠','超规类','阿联酋迪拜马克图姆国际机场地下结构工程项目','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天'),
    ('🟡','一般类','阿联酋迪拜马克图姆国际机场地下结构工程项目','T梁预制、运输和安装专项施工方案（4包）','已添加、未实施','2026-07-20','42天'),
]

# Native table: 7 rows × 7 cols (1 header + 6 data)
TY=Inches(1.1)
CW=[Inches(0.5),Inches(0.7),Inches(3.2),Inches(2.8),Inches(1.6),Inches(1.0),Inches(0.7)]
HDS=['信号','类型','项目名称','方案名称','当前状态','计划开工','距开工']
x_off=Inches(0.5)
tbl2=s2.shapes.add_table(7,7,x_off,TY,sum(CW),Inches(7*0.32))
for i,w in enumerate(CW): tbl2.table.columns[i].width=w
for i,h in enumerate(HDS):
    c=tbl2.table.cell(0,i); c.text=h
    c.fill.solid(); c.fill.fore_color.rgb=BLUE
    for p in c.text_frame.paragraphs: p.font.size=Pt(8); p.font.bold=True; p.font.color.rgb=WHITE
for j,(icon,typ,proj,scheme,status,pdate,days) in enumerate(warnings):
    row=j+1; vals=[icon,typ,proj,scheme,status,pdate,days]
    is_warn='未审批' in status
    bg_c=RGBColor(0xFF,0xF3,0xE0) if is_warn else (BG if j%2==0 else WHITE)
    for i,val in enumerate(vals):
        c=tbl2.table.cell(row,i); c.text=val
        c.fill.solid(); c.fill.fore_color.rgb=bg_c
        for p in c.text_frame.paragraphs:
            p.font.size=Pt({0:14,6:9}.get(i,8))
            p.font.color.rgb=RED if i==6 else BLACK
            p.font.bold=(i==6 or is_warn)
            p.alignment=PP_ALIGN.CENTER if i in (0,1,6) else PP_ALIGN.LEFT

# Rule
RY=TY+Inches(7*0.32+0.15)
R(x_off,RY,sum(CW),Inches(0.45),fill=BG)
R(x_off,RY,Inches(0.06),Inches(0.45),fill=GOLD)
T(x_off+Inches(0.2),RY+Inches(0.05),Inches(11),Inches(0.35),
  '📐 预警规则：🟠 橙色 ≤30天未审批 · 🟡 黄色 ≤45天未审批 · 🔴 红色 在实施未审批（本月0项）',sz=10,clr=BLACK)

R(Inches(0),Inches(7.18),Inches(13.333),Inches(0.32),fill=RGBColor(0xF5,0xF6,0xF8))
T(Inches(0.5),Inches(7.2),Inches(4),Inches(0.2),'中国港湾中东区域公司 技术部',sz=8,clr=GRAY)
T(Inches(11.5),Inches(7.2),Inches(1.5),Inches(0.2),'2 / 2',sz=8,clr=GRAY,al=PP_ALIGN.RIGHT)

out="/mnt/y/Openclaw_Hub/03.资源/实施项目 wiki/dashboard/data/2026-06-08/cleaned/危大方案编审进度看板.pptx"
prs.save(out)
print(f"✅ v2 已生成: {out}")
print(f"   + 原生饼图(一般/超规)")
print(f"   + 三色预警统一卡片样式")
print(f"   + 模块间距重新计算")
