Files
prop-data-guard/screenshots/gen_ppt.py
T
hiderfong 6d70520e79 feat: 全量功能模块开发与集成测试修复
- 新增后端模块:Alert、APIAsset、Compliance、Lineage、Masking、Risk、SchemaChange、Unstructured、Watermark
- 新增前端模块页面与API接口
- 新增Alembic迁移脚本(002-014)覆盖全量业务表
- 新增测试数据生成脚本与集成测试脚本
- 修复metadata模型JSON类型导入缺失导致启动失败的问题
- 修复前端Alert/APIAsset页面request模块路径错误
- 更新docker-compose与开发计划文档
2026-04-25 08:51:38 +08:00

419 lines
16 KiB
Python

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""Generate DataPointer White Paper PPT with screenshots."""
import os
from pptx import Presentation
from pptx.util import Inches, Pt
from pptx.dml.color import RGBColor
from pptx.enum.text import PP_ALIGN, MSO_ANCHOR
from pptx.enum.shapes import MSO_SHAPE
from io import BytesIO
from PIL import Image
# Config
SCREENSHOT_DIR = '/Users/nathan/Work/DataPointer/prop-data-guard/screenshots'
OUTPUT_PATH = '/Users/nathan/Work/DataPointer/DataPointer产品介绍白皮书.pptx'
SLIDE_WIDTH = Inches(13.333)
SLIDE_HEIGHT = Inches(7.5)
# Color theme
COLOR_PRIMARY = RGBColor(0x1A, 0x56, 0xDB) # Deep blue
COLOR_SECONDARY = RGBColor(0x10, 0xB9, 0x81) # Green
COLOR_DARK = RGBColor(0x1E, 0x29, 0x3B) # Dark slate
COLOR_TEXT = RGBColor(0x37, 0x41, 0x51) # Gray text
COLOR_LIGHT = RGBColor(0xF8, 0xFA, 0xFC) # Light bg
COLOR_ACCENT = RGBColor(0xF5, 0x9E, 0x0B) # Orange accent
def add_title_slide(prs, title, subtitle):
slide = prs.slides.add_slide(prs.slide_layouts[6]) # blank
slide.shapes._spTree.remove(slide.shapes._spTree[0]) if len(slide.shapes._spTree) > 0 else None
# Background shape
bg = slide.shapes.add_shape(MSO_SHAPE.RECTANGLE, 0, 0, SLIDE_WIDTH, SLIDE_HEIGHT)
bg.fill.solid()
bg.fill.fore_color.rgb = COLOR_DARK
bg.line.fill.background()
# Accent bar
bar = slide.shapes.add_shape(MSO_SHAPE.RECTANGLE, Inches(0.8), Inches(3.0), Inches(0.15), Inches(2.2))
bar.fill.solid()
bar.fill.fore_color.rgb = COLOR_PRIMARY
bar.line.fill.background()
# Title
title_box = slide.shapes.add_textbox(Inches(1.2), Inches(2.8), Inches(10), Inches(1.5))
tf = title_box.text_frame
tf.word_wrap = True
p = tf.paragraphs[0]
p.text = title
p.font.size = Pt(48)
p.font.bold = True
p.font.color.rgb = RGBColor(0xFF, 0xFF, 0xFF)
# Subtitle
sub_box = slide.shapes.add_textbox(Inches(1.2), Inches(4.4), Inches(10), Inches(0.8))
tf = sub_box.text_frame
p = tf.paragraphs[0]
p.text = subtitle
p.font.size = Pt(22)
p.font.color.rgb = RGBColor(0xA0, 0xAE, 0xC0)
# Bottom info
info_box = slide.shapes.add_textbox(Inches(1.2), Inches(6.5), Inches(10), Inches(0.5))
tf = info_box.text_frame
p = tf.paragraphs[0]
p.text = "Property Insurance Data Classification Platform | 2026"
p.font.size = Pt(12)
p.font.color.rgb = RGBColor(0x71, 0x80, 0x96)
return slide
def add_section_slide(prs, section_num, section_title):
slide = prs.slides.add_slide(prs.slide_layouts[6])
bg = slide.shapes.add_shape(MSO_SHAPE.RECTANGLE, 0, 0, SLIDE_WIDTH, SLIDE_HEIGHT)
bg.fill.solid()
bg.fill.fore_color.rgb = COLOR_PRIMARY
bg.line.fill.background()
num_box = slide.shapes.add_textbox(Inches(0.8), Inches(2.5), Inches(2), Inches(1.5))
tf = num_box.text_frame
p = tf.paragraphs[0]
p.text = f"0{section_num}" if section_num < 10 else str(section_num)
p.font.size = Pt(72)
p.font.bold = True
p.font.color.rgb = RGBColor(0xFF, 0xFF, 0xFF)
p.font.opacity = 0.3
title_box = slide.shapes.add_textbox(Inches(0.8), Inches(4.0), Inches(10), Inches(1.0))
tf = title_box.text_frame
p = tf.paragraphs[0]
p.text = section_title
p.font.size = Pt(40)
p.font.bold = True
p.font.color.rgb = RGBColor(0xFF, 0xFF, 0xFF)
return slide
def add_content_slide(prs, title, bullets, image_path=None):
slide = prs.slides.add_slide(prs.slide_layouts[6])
# Light background
bg = slide.shapes.add_shape(MSO_SHAPE.RECTANGLE, 0, 0, SLIDE_WIDTH, SLIDE_HEIGHT)
bg.fill.solid()
bg.fill.fore_color.rgb = COLOR_LIGHT
bg.line.fill.background()
# Send to back
spTree = slide.shapes._spTree
spTree.insert(2, bg._element)
# Top bar
top_bar = slide.shapes.add_shape(MSO_SHAPE.RECTANGLE, 0, 0, SLIDE_WIDTH, Inches(0.12))
top_bar.fill.solid()
top_bar.fill.fore_color.rgb = COLOR_PRIMARY
top_bar.line.fill.background()
# Title
title_box = slide.shapes.add_textbox(Inches(0.6), Inches(0.35), Inches(12), Inches(0.6))
tf = title_box.text_frame
p = tf.paragraphs[0]
p.text = title
p.font.size = Pt(28)
p.font.bold = True
p.font.color.rgb = COLOR_DARK
# Content
if image_path and os.path.exists(image_path):
# Image on right, text on left
content_box = slide.shapes.add_textbox(Inches(0.6), Inches(1.2), Inches(4.5), Inches(5.5))
tf = content_box.text_frame
tf.word_wrap = True
for i, bullet in enumerate(bullets):
if i == 0:
p = tf.paragraphs[0]
else:
p = tf.add_paragraph()
p.text = f"{bullet}"
p.font.size = Pt(16)
p.font.color.rgb = COLOR_TEXT
p.space_after = Pt(12)
# Add image
img = Image.open(image_path)
orig_w, orig_h = img.size
max_w = Inches(7.0)
max_h = Inches(5.5)
ratio = min(max_w / orig_w, max_h / orig_h)
img_w = orig_w * ratio
img_h = orig_h * ratio
left = SLIDE_WIDTH - img_w - Inches(0.6)
top = Inches(1.0) + (max_h - img_h) / 2
slide.shapes.add_picture(image_path, left, top, width=img_w, height=img_h)
else:
content_box = slide.shapes.add_textbox(Inches(0.6), Inches(1.2), Inches(12), Inches(5.5))
tf = content_box.text_frame
tf.word_wrap = True
for i, bullet in enumerate(bullets):
if i == 0:
p = tf.paragraphs[0]
else:
p = tf.add_paragraph()
p.text = f"{bullet}"
p.font.size = Pt(18)
p.font.color.rgb = COLOR_TEXT
p.space_after = Pt(14)
return slide
def add_image_slide(prs, title, image_path, caption=""):
slide = prs.slides.add_slide(prs.slide_layouts[6])
bg = slide.shapes.add_shape(MSO_SHAPE.RECTANGLE, 0, 0, SLIDE_WIDTH, SLIDE_HEIGHT)
bg.fill.solid()
bg.fill.fore_color.rgb = RGBColor(0xFF, 0xFF, 0xFF)
bg.line.fill.background()
spTree = slide.shapes._spTree
spTree.insert(2, bg._element)
top_bar = slide.shapes.add_shape(MSO_SHAPE.RECTANGLE, 0, 0, SLIDE_WIDTH, Inches(0.12))
top_bar.fill.solid()
top_bar.fill.fore_color.rgb = COLOR_PRIMARY
top_bar.line.fill.background()
title_box = slide.shapes.add_textbox(Inches(0.6), Inches(0.35), Inches(12), Inches(0.5))
tf = title_box.text_frame
p = tf.paragraphs[0]
p.text = title
p.font.size = Pt(26)
p.font.bold = True
p.font.color.rgb = COLOR_DARK
if os.path.exists(image_path):
img = Image.open(image_path)
orig_w, orig_h = img.size
max_w = Inches(11.5)
max_h = Inches(5.8)
ratio = min(max_w / orig_w, max_h / orig_h)
img_w = orig_w * ratio
img_h = orig_h * ratio
left = (SLIDE_WIDTH - img_w) / 2
top = Inches(1.0)
slide.shapes.add_picture(image_path, left, top, width=img_w, height=img_h)
if caption:
cap_box = slide.shapes.add_textbox(Inches(0.6), Inches(6.9), Inches(12), Inches(0.4))
tf = cap_box.text_frame
p = tf.paragraphs[0]
p.text = caption
p.font.size = Pt(12)
p.font.color.rgb = RGBColor(0x71, 0x80, 0x96)
p.alignment = PP_ALIGN.CENTER
return slide
def add_summary_slide(prs):
slide = prs.slides.add_slide(prs.slide_layouts[6])
bg = slide.shapes.add_shape(MSO_SHAPE.RECTANGLE, 0, 0, SLIDE_WIDTH, SLIDE_HEIGHT)
bg.fill.solid()
bg.fill.fore_color.rgb = COLOR_DARK
bg.line.fill.background()
title_box = slide.shapes.add_textbox(Inches(0.8), Inches(1.5), Inches(11.5), Inches(1.0))
tf = title_box.text_frame
p = tf.paragraphs[0]
p.text = "DataPointer 产品优势"
p.font.size = Pt(36)
p.font.bold = True
p.font.color.rgb = RGBColor(0xFF, 0xFF, 0xFF)
items = [
("全生命周期数据安全", "从数据采集、分类分级、风险评估到持续监控,覆盖数据安全全生命周期"),
("智能自动化引擎", "基于规则+AI的智能分类引擎,自动识别敏感数据,效率提升80%"),
("精细化权限管控", "RBAC 角色权限模型,支持数据隔离,满足多租户场景"),
("保险行业深度适配", "内置保险行业数据分类分级标准,覆盖客户、保单、理赔等核心领域"),
("可视化数据资产", "仪表盘+报表多维展示,数据资产状况一目了然"),
]
y = Inches(2.8)
for title, desc in items:
box = slide.shapes.add_textbox(Inches(0.8), y, Inches(11.5), Inches(0.8))
tf = box.text_frame
tf.word_wrap = True
p = tf.paragraphs[0]
p.text = f"{title}"
p.font.size = Pt(18)
p.font.bold = True
p.font.color.rgb = COLOR_SECONDARY
p = tf.add_paragraph()
p.text = f" {desc}"
p.font.size = Pt(14)
p.font.color.rgb = RGBColor(0xA0, 0xAE, 0xC0)
y += Inches(0.85)
return slide
def add_end_slide(prs):
slide = prs.slides.add_slide(prs.slide_layouts[6])
bg = slide.shapes.add_shape(MSO_SHAPE.RECTANGLE, 0, 0, SLIDE_WIDTH, SLIDE_HEIGHT)
bg.fill.solid()
bg.fill.fore_color.rgb = COLOR_PRIMARY
bg.line.fill.background()
title_box = slide.shapes.add_textbox(Inches(0), Inches(2.8), SLIDE_WIDTH, Inches(1.0))
tf = title_box.text_frame
p = tf.paragraphs[0]
p.text = "谢谢观看"
p.font.size = Pt(52)
p.font.bold = True
p.font.color.rgb = RGBColor(0xFF, 0xFF, 0xFF)
p.alignment = PP_ALIGN.CENTER
sub_box = slide.shapes.add_textbox(Inches(0), Inches(4.0), SLIDE_WIDTH, Inches(0.6))
tf = sub_box.text_frame
p = tf.paragraphs[0]
p.text = "DataPointer - 让数据安全触手可及"
p.font.size = Pt(20)
p.font.color.rgb = RGBColor(0xA0, 0xAE, 0xC0)
p.alignment = PP_ALIGN.CENTER
return slide
def main():
prs = Presentation()
prs.slide_width = SLIDE_WIDTH
prs.slide_height = SLIDE_HEIGHT
# 1. Cover
add_title_slide(prs, "DataPointer", "数据安全分级及风险管理平台产品介绍白皮书")
# 2. TOC
add_content_slide(prs, "目录", [
"产品概述与定位",
"系统架构与技术栈",
"核心功能模块详解",
"产品优势与价值",
"应用场景与最佳实践"
])
# 3. Section: Overview
add_section_slide(prs, 1, "产品概述")
add_content_slide(prs, "DataPointer 产品定位", [
"DataPointer 是面向保险行业的数据安全分级及风险管理平台",
"基于国家及行业数据分类分级标准,帮助企业实现数据资产的自动化识别与分级",
"支持从数据源接入、元数据采集、智能分类、人工复核到报表输出的完整工作流",
"内置保险行业专属分类标准,覆盖客户、保单、理赔、财务、渠道、监管等核心领域",
"提供多维度数据资产视图,助力企业满足数据安全合规要求"
])
# 4. Section: Architecture
add_section_slide(prs, 2, "系统架构")
add_content_slide(prs, "技术架构", [
"前端:Vue 3.4 + Vite + TypeScript + Element Plus 2.7 + ECharts 5.4",
"后端:FastAPI 0.111 + SQLAlchemy 2.0 + PostgreSQL 16 + Redis 7 + Celery 5.4",
"部署:容器化部署,支持 Docker Compose / Kubernetes",
"安全:JWT 认证 + RBAC 权限控制 + 数据隔离,满足等保要求",
"扩展:模块化设计,支持自定义分类标准与规则引擎扩展"
])
# 5. Section: Features
add_section_slide(prs, 3, "核心功能")
add_image_slide(prs, "安全登录与权限管控", os.path.join(SCREENSHOT_DIR, "01_login.png"),
"支持用户名密码登录,JWT Token 认证,会话安全")
add_image_slide(prs, "仪表盘总览", os.path.join(SCREENSHOT_DIR, "02_dashboard.png"),
"实时展示数据源、表、字段、分级结果等核心指标,支持敏感数据分布可视化")
add_content_slide(prs, "数据源管理", [
"支持多种数据库类型接入:MySQL、PostgreSQL、Oracle、SQL Server 等",
"自动扫描数据库元数据,采集表结构与字段信息",
"支持数据源连接测试与状态监控",
"批量导入导出数据源配置,提升运维效率"
], os.path.join(SCREENSHOT_DIR, "03_datasource.png"))
add_content_slide(prs, "数据资产管理", [
"自动生成数据资产目录,展示数据库、表、字段的层级关系",
"支持字段级元数据查看,包括数据类型、注释、采样数据等",
"资产变更追踪,记录元数据的增删改历史",
"与分类分级结果联动,直观展示各资产的安全等级"
], os.path.join(SCREENSHOT_DIR, "04_metadata.png"))
add_content_slide(prs, "分类分级标准", [
"内置保险行业五级分类标准:公开级(L1)、内部级(L2)、敏感级(L3)、重要级(L4)、核心级(L5)",
"支持自定义分类标准与分级规则,灵活适配不同业务场景",
"规则引擎支持正则匹配、关键字匹配、语义识别等多种检测方式",
"标准模板管理,支持一键导入导出行业标准模板"
], os.path.join(SCREENSHOT_DIR, "05_category.png"))
add_content_slide(prs, "项目管理", [
"支持创建多个分类分级项目,独立管理与进度跟踪",
"项目维度统计:自动识别、人工标注、已复核数量",
"支持批量触发自动分类任务,提升处理效率",
"项目经理可查看负责项目的全部进度与成果"
], os.path.join(SCREENSHOT_DIR, "06_project.png"))
add_content_slide(prs, "任务管理", [
"自动拆解项目为标注/复核任务,分配给标注员与审核员",
"任务状态追踪:待处理、处理中、已完成、已驳回",
"支持任务领取与批量处理,提升团队协作效率",
"数据隔离:普通用户仅能查看和处理分配给自己的任务"
], os.path.join(SCREENSHOT_DIR, "07_task.png"))
add_content_slide(prs, "分类分级结果", [
"展示所有字段的分类分级结果,支持多维筛选与搜索",
"结果包含:字段名称、所属表/库、分类标准、安全等级、识别方式",
"支持结果的批量导出,便于合规审计与报告生成",
"人工复核机制,确保分类分级的准确性与权威性"
], os.path.join(SCREENSHOT_DIR, "08_classification.png"))
add_content_slide(prs, "报表统计", [
"多维统计报表:按等级分布、类别分布、数据源分布等维度展示",
"支持 Word 报告一键生成,包含完整的数据资产与分级详情",
"可视化图表直观展示敏感数据分布与趋势",
"满足监管报送与内部审计的数据安全报告需求"
], os.path.join(SCREENSHOT_DIR, "09_report.png"))
add_content_slide(prs, "系统管理", [
"用户管理:支持用户增删改查、角色分配、部门归属",
"角色权限:基于 RBAC 模型,支持 admin、project_manager、labeler、reviewer、guest 等角色",
"数据隔离:严格的数据权限控制,确保跨部门数据安全",
"操作日志:完整记录用户操作行为,支持安全审计"
], os.path.join(SCREENSHOT_DIR, "10_system.png"))
# 6. Section: Advantages
add_section_slide(prs, 4, "产品优势")
add_summary_slide(prs)
# 7. Section: Scenarios
add_section_slide(prs, 5, "应用场景")
add_content_slide(prs, "典型应用场景", [
"数据安全合规:帮助企业满足《数据安全法》《个人信息保护法》等法规要求",
"等保测评:提供完整的数据分类分级证据链,支撑等级保护测评",
"数据资产盘点:全面梳理企业数据资产,建立统一的数据资产目录",
"敏感数据发现:自动识别客户身份证号、银行卡号、保单信息等敏感字段",
"数据分级保护:根据分级结果制定差异化的数据安全保护策略"
])
# 8. End
add_end_slide(prs)
prs.save(OUTPUT_PATH)
print(f"PPT saved to: {OUTPUT_PATH}")
if __name__ == "__main__":
main()