import json import datetime from typing import Dict, List, Optional
class DataReportSkill: """ 数据报告撰写Skill:自动生成图文并茂的HTML数据报告 支持:自定义数据、分析目标、报告风格,自动生成图表+分析文案 """
# 报告风格配置(内嵌CSS类,基于Tailwind CSS)
STYLE_TEMPLATES = {
"商务正式": {
"primary_color": "bg-blue-900 text-white",
"card_bg": "bg-white shadow-lg rounded-lg p-6",
"font": "font-sans",
"title_size": "text-3xl font-bold"
},
"简洁清爽": {
"primary_color": "bg-teal-600 text-white",
"card_bg": "bg-gray-50 border border-gray-200 rounded-lg p-5",
"font": "font-sans",
"title_size": "text-2xl font-semibold"
},
"可视化聚焦": {
"primary_color": "bg-purple-700 text-white",
"card_bg": "bg-white shadow-md rounded-xl p-6",
"font": "font-mono",
"title_size": "text-3xl font-bold text-purple-800"
}
}
def __init__(
self,
report_title: str,
analyze_data: Dict,
analyze_target: str,
report_style: str = "商务正式",
author: str = "数据分析师"
):
"""
初始化报告Skill
:param report_title: 报告标题
:param analyze_data: 分析数据(字典格式,支持一维/二维数据)
:param analyze_target: 分析目标(如:季度销售复盘、用户增长分析、业务效率评估)
:param report_style: 报告风格(商务正式/简洁清爽/可视化聚焦)
:param author: 报告作者
"""
self.title = report_title
self.data = analyze_data
self.target = analyze_target
self.style = self.STYLE_TEMPLATES.get(report_style, self.STYLE_TEMPLATES["商务正式"])
self.author = author
self.date = datetime.datetime.now().strftime("%Y年%m月%d日")
self.chart_id = f"chart_{int(datetime.datetime.now().timestamp())}"
def _auto_select_chart_type(self) -> str:
"""自动根据数据结构选择图表类型:饼图(占比)/折线图(趋势)/柱状图(对比)"""
data_values = list(self.data.values())
# 饼图:数据总和为100%(占比类数据)
if all(0 <= v <= 100 for v in data_values) and abs(sum(data_values) - 100) <= 1:
return "pie"
# 折线图:时间序列/趋势数据(键为日期/月份)
if any(key in ["1月","2月","Q1","周一","2024"] for key in self.data.keys()):
return "line"
# 默认柱状图:分类对比数据
return "bar"
def _generate_analysis_text(self) -> List[str]:
"""自动生成数据分析文案:核心结论、数据洞察、优化建议"""
if not self.data:
return ["无有效数据可分析"]
max_key = max(self.data, key=self.data.get)
min_key = min(self.data, key=self.data.get)
max_val = self.data[max_key]
min_val = self.data[min_key]
total = sum(self.data.values())
# 核心结论
conclusion = f"【核心结论】本次{self.target}中,{max_key}表现最优,数值为{max_val};{min_key}表现最弱,数值为{min_val}。"
# 数据洞察
insight = f"【数据洞察】整体数据总量为{total},数据分布呈现{max_key}主导的特征,符合{self.target}的业务预期。"
# 优化建议
suggestion = f"【优化建议】针对{min_key}的短板,建议结合业务场景制定提升策略,缩小与头部指标{max_key}的差距。"
return [conclusion, insight, suggestion]
def _generate_chart_config(self) -> str:
"""生成Chart.js图表配置(内嵌HTML)"""
chart_type = self._auto_select_chart_type()
labels = json.dumps(list(self.data.keys()))
values = json.dumps(list(self.data.values()))
return f"""
const ctx = document.getElementById('{self.chart_id}').getContext('2d');
new Chart(ctx, {{
type: '{chart_type}',
data: {{
labels: {labels},
datasets: [{{
label: '{self.target}',
data: {values},
backgroundColor: [
'rgba(54, 162, 235, 0.6)',
'rgba(255, 99, 132, 0.6)',
'rgba(255, 206, 86, 0.6)',
'rgba(75, 192, 192, 0.6)',
'rgba(153, 102, 255, 0.6)'
],
borderWidth: 1
}}]
}},
options: {{
responsive: true,
maintainAspectRatio: false,
plugins: {{
legend: {{ position: 'top' }},
title: {{ display: true, text: '{self.target}数据可视化' }}
}}
}}
}});
"""
def generate_html_report(self, save_path: str = "data_report.html") -> str:
"""
生成最终HTML报告并保存
:param save_path: HTML文件保存路径
:return: 保存路径
"""
# 自动生成分析文案
analysis_paragraphs = self._generate_analysis_text()
# 图表配置
chart_script = self._generate_chart_config()
# 风格样式
style = self.style
# HTML完整模板(内嵌CDN,无外部依赖)
html_template = f"""
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>{self.title}</title>
<!-- Tailwind CSS 样式 -->
<script src="https://cdn.tailwindcss.com"></script>
<!-- Chart.js 图表库 -->
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
</head>
<body class="{style['font']} bg-gray-50 min-h-screen">
<!-- 报告头部 -->
<header class="{style['primary_color']} py-8 px-6 mb-8">
<div class="max-w-6xl mx-auto">
<h1 class="{style['title_size']}">{self.title}</h1>
<p class="mt-2 opacity-90">分析目标:{self.target} | 作者:{self.author} | 生成时间:{self.date}</p>
</div>
</header>
<!-- 报告主体 -->
<main class="max-w-6xl mx-auto px-4 pb-12">
<!-- 数据可视化模块 -->
<section class="{style['card_bg']} mb-8">
<h2 class="text-xl font-bold mb-4 text-gray-800">📊 数据可视化图表</h2>
<div class="h-96">
<canvas id="{self.chart_id}"></canvas>
</div>
</section>
<!-- 数据分析模块 -->
<section class="{style['card_bg']} mb-8">
<h2 class="text-xl font-bold mb-4 text-gray-800">📝 深度数据分析</h2>
<div class="space-y-4 text-gray-700 leading-relaxed">
{"".join([f'<p>{p}</p>' for p in analysis_paragraphs])}
</div>
</section>
<!-- 原始数据模块 -->
<section class="{style['card_bg']}">
<h2 class="text-xl font-bold mb-4 text-gray-800">📋 原始数据明细</h2>
<div class="overflow-x-auto">
<table class="w-full border-collapse">
<thead>
<tr class="bg-gray-100">
<th class="border p-3 text-left">指标</th>
<th class="border p-3 text-left">数值</th>
</tr>
</thead>
<tbody>
{"".join([f'<tr><td class="border p-3">{k}</td><td class="border p-3">{v}</td></tr>' for k, v in self.data.items()])}
</tbody>
</table>
</div>
</section>
</main>
<!-- 图表渲染脚本 -->
<script>
document.addEventListener('DOMContentLoaded', function() {{
{chart_script}
}});
</script>
</body>
</html>
"""
# 保存HTML文件
with open(save_path, "w", encoding="utf-8") as f:
f.write(html_template)
print(f"✅ 报告生成成功!保存路径:{save_path}")
return save_path
Scan to join WeChat group