main
HuangHai 4 months ago
parent 26896588e1
commit 7ecf9718f2

@ -48,6 +48,10 @@ async def get_question_by_id(db: asyncpg.Connection, question_id: str):
_data = await db.fetch(select_sql, question_id)
return _data
# 根据 SQL 查询数据
async def get_data_by_sql(db: asyncpg.Connection, sql: str):
_data = await db.fetch(sql)
return _data
# 保存系统推荐
async def set_system_recommend_questions(db: asyncpg.Connection, question_id: str, flag: str):

@ -42,115 +42,4 @@ if __name__ == "__main__":
for i, (comment, sql) in enumerate(sql_snippets, 1):
vn.train(sql=comment.strip() + '\n' + sql.strip() + '\n')
# 自然语言提问
# 整体情况
# question = '''
# 查询:
# 1、发布时间是2024年度
# 2、每个行政区每个学校都上传了多少课程数量
# 3、格式: 行政区划名,学段,排名,学校名称,课程数量
# '''
# 指定行政区域
# question = '''
# 查询:
# 1、发布时间是2024年度
# 2、二道区每个学校都上传了多少课程数量
# 3、格式: 行政区划名,学段,排名,学校名称,发布年份,课程数量
# '''
# 指定学段
question = '''
查询:
1发布时间是2024年度
2每个学段每个科目上传课程数量按由多到少排序
3字段名: 学段,科目,排名,课程数量
'''
common_prompt = '''
返回的信息要求
1行政区划为NULL 或者是空字符的不参加统计
2目标数据库是Postgresql 16
'''
question = question + common_prompt
# 开始查询
print("开始查询...")
# 获取完整 SQL
sql = vn.generate_sql(question)
print("生成的查询 SQL:\n", sql)
# 执行SQL查询
with PostgreSQLUtil() as db:
_data = db.execute_query(sql)
# 获取字段名和数据示例
field_names = list(_data[0].keys()) if _data else []
sample_data = _data[:3] # 取前 3 行作为示例数据
# 1、生成柱状图
generate_bar_chart(
_data=_data,
title="学段+科目课程数量柱状图",
x_columns=['学段', '科目'], # 动态指定 X 轴列
y_columns=['课程数量'], # 动态指定 Y 轴列
output_file="d:/lesson_bar_chart.html"
)
# 2、生成饼状图
generate_pie_chart(
_data=_data,
title="学段+科目分布",
category_columns=['学段', '科目'], # 多列组合参数
value_column='课程数量',
output_file="d:/lesson_pie_chart.html"
)
# 3、生成excel
filename = "d:/导出信息.xlsx"
save_to_excel(_data, filename)
# 4、生成word报告
prompt = '''
请根据以下 JSON 数据整理出2000字左右的话描述当前数据情况要求
1以Markdown格式返回我将直接通过markdown格式生成Word
2标题统一为长春云校数据分析报告
3内容中不要提到JSON数据统一称数据
4尽量以条目列出这样更清晰
5数据
'''
prompt = prompt + json.dumps(_data, ensure_ascii=False)
# 初始化 OpenAI 客户端
client = OpenAI(
api_key=MODEL_API_KEY,
base_url=MODEL_API_URL,
)
# 调用 OpenAI API 生成总结(流式输出)
response = client.chat.completions.create(
model=MODEL_NAME,
messages=[
{"role": "system", "content": "你是一个数据分析助手,擅长从 JSON 数据中提取关键信息并生成详细的总结。"},
{"role": "user", "content": prompt}
],
max_tokens=3000, # 控制生成内容的长度
temperature=0.7, # 控制生成内容的创造性
stream=True # 启用流式输出
)
# 初始化变量用于存储流式输出的内容
summary = ""
# 处理流式输出
for chunk in response:
if chunk.choices[0].delta.content: # 检查是否有内容
chunk_content = chunk.choices[0].delta.content
print(chunk_content, end="", flush=True) # 实时打印到控制台
summary += chunk_content # 将内容拼接到 summary 中
# 保存markdown
with open("d:/report.md", "w", encoding="utf-8") as file:
file.write(summary)
# 最终 summary 为完整的 Markdown 内容
print("\n\n流式输出完成summary 已拼接为完整字符串。")
# 生成 Word 文档
markdown_to_docx(summary, output_file="d:/report.docx")

@ -1,14 +1,26 @@
from pyecharts.charts import Bar
from pyecharts.charts import Bar, Pie
from pyecharts import options as opts
from pyecharts.commons.utils import JsCode
import os
def generate_bar_chart(_data, title: str, x_columns: list, y_columns: list, output_file: str = "bar_chart.html"):
def generate_chart(
_data,
chart_type: str, # 图表类型:'bar' 或 'pie'
title: str, # 图表标题
category_columns: list, # 分类数据列X 轴或饼图标签)
value_column: str, # 数值数据列Y 轴或饼图值)
output_file: str = "chart.html" # 输出文件路径
):
"""
根据结果集生成柱状图并保存为 HTML 文件
统一生成柱形图或饼形图
参数:
result: 结果集通常是一个列表字典格式
x_columns (list): 作为 X 轴的列名列表
y_columns (list): 作为 Y 轴的列名列表
output_file (str): 输出 HTML 文件名默认 "bar_chart.html"
_data: 结果集通常是一个列表字典格式
chart_type (str): 图表类型'bar' 'pie'
title (str): 图表标题
category_columns (list): 分类数据列X 轴或饼图标签
value_column (str): 数值数据列Y 轴或饼图值
output_file (str): 输出 HTML 文件名默认 "chart.html"
"""
try:
# 如果结果集为空,直接返回
@ -16,131 +28,86 @@ def generate_bar_chart(_data, title: str, x_columns: list, y_columns: list, outp
raise ValueError("结果集为空,无法生成图表!")
# 检查列名是否存在
for col in x_columns + y_columns:
for col in category_columns + [value_column]:
if col not in _data[0]:
raise ValueError(f"列名 '{col}' 不存在!")
# 提取 X 轴数据(动态组合多列)
x_axis_data = [" - ".join(str(row[col]) for col in x_columns) for row in _data]
# 提取 Y 轴数据
y_axis_data = [row[y_columns[0]] for row in _data] # 目前只支持一个 Y 轴列
# 提取分类数据(动态组合多列)
categories = [" - ".join(str(row[col]) for col in category_columns) for row in _data]
# 提取数值数据
values = [row[value_column] for row in _data]
# 为每个柱状图设置不同颜色
# 颜色配置
colors = [
"#5470C6", "#91CC75", "#EE6666", "#73C0DE", "#3BA272", "#FC8452", "#9A60B4", "#EA7CCC"
] # 自定义颜色列表
]
color_js = JsCode(f'''
function(params) {{
var colorList = {colors};
return colorList[params.dataIndex % colorList.length];
}}
''')
# 生成柱形图
if chart_type == 'bar':
chart = Bar()
chart.add_xaxis(categories)
chart.add_yaxis(
series_name=value_column,
y_axis=values,
bar_width="30%",
itemstyle_opts=opts.ItemStyleOpts(color=color_js)
)
chart.set_global_opts(
title_opts=opts.TitleOpts(title=title),
xaxis_opts=opts.AxisOpts(
name=" - ".join(category_columns),
axislabel_opts=opts.LabelOpts(rotate=45),
boundary_gap=True,
splitline_opts=opts.SplitLineOpts(is_show=False)
),
yaxis_opts=opts.AxisOpts(name=value_column),
legend_opts=opts.LegendOpts(is_show=False)
)
# 创建柱状图并配置布局
bar = Bar()
bar.add_xaxis(x_axis_data)
bar.add_yaxis(
series_name=y_columns[0], # 使用 y_columns 的第一个元素作为系列名称
y_axis=y_axis_data,
bar_width="30%", # 控制柱宽
itemstyle_opts=opts.ItemStyleOpts(
color=JsCode(f'''
function(params) {{
var colorList = {colors};
return colorList[params.dataIndex % colorList.length];
}}
''')
# 生成饼形图
elif chart_type == 'pie':
data_pairs = list(zip(categories, values))
chart = Pie()
chart.add(
series_name="占比",
data_pair=data_pairs,
radius=["30%", "55%"],
label_opts=opts.LabelOpts(
formatter="{b}\n{d}%",
position="outside",
font_size=14
),
itemstyle_opts=opts.ItemStyleOpts(color=color_js)
)
chart.set_global_opts(
title_opts=opts.TitleOpts(
title=title,
subtitle="数据维度:" + "".join(category_columns)
),
legend_opts=opts.LegendOpts(is_show=False)
)
)
bar.set_global_opts(
title_opts=opts.TitleOpts(title=title), # 添加标题
xaxis_opts=opts.AxisOpts(
name=" - ".join(x_columns),
axislabel_opts=opts.LabelOpts(rotate=45),
boundary_gap=True, # 开启分类间隙
splitline_opts=opts.SplitLineOpts(is_show=False)
),
yaxis_opts=opts.AxisOpts(name=y_columns[0]), # 使用 y_columns 的第一个元素作为 Y 轴名称
legend_opts=opts.LegendOpts(is_show=False) # 隐藏图例
)
else:
raise ValueError("不支持的图表类型!")
# 确保目标目录存在
if not os.path.exists(os.path.dirname(output_file)):
os.makedirs(os.path.dirname(output_file))
os.makedirs(os.path.dirname(output_file), exist_ok=True)
# 保存为 HTML 文件
bar.render(output_file)
print(f"柱状图已保存为 {output_file}")
chart.render(output_file)
print(f"图表已保存为 {output_file}")
# 检查文件是否存在
if os.path.exists(output_file):
print("文件生成成功!")
else:
print("文件生成失败!")
except Exception as e:
print(f"生成柱状图时发生错误: {e}")
from pyecharts import options as opts
from pyecharts.charts import Pie
from pyecharts.commons.utils import JsCode
import os
def generate_pie_chart(
_data,
title: str,
category_columns: list, # 改为列表接收多列(如["学段","科目"]
value_column: str,
output_file: str = "pie_chart.html"
):
"""
支持多列组合的饼图生成器
参数说明
category_columns : 分类标签的多个字段名
value_column : 数值字段名
"""
try:
# 数据预处理:生成组合标签
combined_data = {}
for item in _data:
# 生成组合键(例:"小学 - 语文"
key = " - ".join(str(item[col]) for col in category_columns)
combined_data[key] = combined_data.get(key, 0) + item[value_column]
# 转换为饼图数据格式
data_pairs = list(combined_data.items())
# 颜色配置(与柱状图一致)
color_js = JsCode("""
function(params) {
var colors = ['#5470C6','#91CC75','#EE6666','#73C0DE'];
return colors[params.dataIndex % colors.length];
}
""")
# 构建饼图
pie = Pie()
pie.add(
series_name="占比",
data_pair=data_pairs,
radius=["30%", "55%"], # 环形布局优化
label_opts=opts.LabelOpts(
formatter="{b}\n{d}%", # 换行显示标签和比例
position="outside",
font_size=14
),
itemstyle_opts=opts.ItemStyleOpts(color=color_js)
)
# 全局配置优化
pie.set_global_opts(
title_opts=opts.TitleOpts(
title=title,
subtitle="数据维度:" + "".join(category_columns)
),
legend_opts=opts.LegendOpts(is_show=False) # 不显示图例
)
# 输出文件
os.makedirs(os.path.dirname(output_file), exist_ok=True)
pie.render(output_file)
print(f"组合饼图已生成: {output_file}")
except Exception as e:
print(f"生成失败: {str(e)}")
print(f"生成图表时发生错误: {e}")

@ -11,6 +11,7 @@ from starlette.responses import StreamingResponse
from Config import *
from Model.biModel import *
from Text2Sql.Util.EchartsUtil import generate_chart
from Text2Sql.Util.MarkdownToDocxUtil import markdown_to_docx
from Text2Sql.Util.SaveToExcel import save_to_excel
from Text2Sql.Util.VannaUtil import VannaUtil
@ -268,6 +269,43 @@ async def get_data_scheme_by_id(
return {"success": True, "data": column_names}
# 生成图表
@app.get("/questions/get_chart")
async def get_chart(
question_id: str = Query(None, description="问题IDGET请求"),
type_id: str = Query(1, description="图表类型IDGET请求,1:柱图2饼图"),
db: asyncpg.Connection = Depends(get_db)
):
sql = (await get_question_by_id(db, question_id))[0]['sql']
# 执行sql获取数据集
_data = await get_data_by_sql(db, sql)
# 图表文件名称
uuid_str = str(uuid.uuid4())
filename = f"static/{uuid_str}.html"
if type_id == 1: # 1、生成柱状图
generate_chart(
_data=_data,
chart_type='bar',
title="柱形统计图",
category_columns=["学段", "科目"],
value_column="课程数量",
output_file=filename
)
elif type_id == 2: # 2、生成饼状图
generate_chart(
_data=_data,
chart_type='pie',
title="饼形统计图",
category_columns=["学段", "科目"],
value_column="课程数量",
output_file=filename
)
# 返回静态文件URL
return {"success": True, "message": "图表文件生成成功", "url": filename}
# 启动 FastAPI
if __name__ == "__main__":
uvicorn.run("app:app", host="0.0.0.0", port=8000, workers=4)

Loading…
Cancel
Save