analysis_report.py 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303
  1. # -*- coding: utf-8 -*-
  2. from plotly.subplots import make_subplots
  3. from flask import Flask,request
  4. import time
  5. import random
  6. import logging
  7. import traceback
  8. import os
  9. from common.database_dml import get_df_list_from_mongo,insert_data_into_mongo
  10. import plotly.express as px
  11. import plotly.graph_objects as go
  12. import pandas as pd
  13. import plotly.io as pio
  14. app = Flask('analysis_report——service')
  15. def put_analysis_report_to_html(args,df_clean,df_accuracy):
  16. col_time = args['col_time']
  17. col_x_env = args['col_x_env']
  18. col_x_pre = args['col_x_pre']
  19. label = args['label']
  20. label_pre = args['label_pre']
  21. farmId = args['farmId']
  22. df_overview = pd.DataFrame(
  23. {'数据开始时间': [df_clean[col_time].min()], '数据结束时间': [df_clean[col_time].max()], '数据总记录数': [df_clean.shape[0]]})
  24. overview_html = df_overview.to_html(classes='table table-bordered table-striped', index=False)
  25. # -------------------- 数据描述 --------------------
  26. describe_html = df_clean.describe().reset_index().rename(columns={'index': '统计量'}).to_html(
  27. classes='table table-bordered table-striped', index=False)
  28. # -------------------- 实测气象与实际功率散点图--------------------
  29. # 生成实际功率与辐照度的散点图
  30. fig_scatter = px.scatter(df_clean, x=col_x_env, y=label)
  31. # 自定义散点图布局
  32. fig_scatter.update_layout(
  33. template='seaborn',
  34. plot_bgcolor='rgba(255, 255, 255, 0.8)', # 背景色
  35. xaxis=dict(
  36. showgrid=True,
  37. gridcolor='rgba(200, 200, 200, 0.5)',
  38. title=col_x_env
  39. ),
  40. yaxis=dict(
  41. showgrid=True,
  42. gridcolor='rgba(200, 200, 200, 0.5)',
  43. title=label
  44. ),
  45. legend=dict(x=0.01, y=0.99, bgcolor='rgba(255, 255, 255, 0.7)', bordercolor='black', borderwidth=1)
  46. )
  47. # 将散点图保存为 HTML 片段
  48. scatter_html = pio.to_html(fig_scatter, full_html=False)
  49. # -------------------- 生成相关性热力图 --------------------
  50. # 计算相关矩阵
  51. correlation_matrix = df_clean.corr()
  52. # 生成热力图,带数值标签和新配色
  53. fig_heatmap = go.Figure(data=go.Heatmap(
  54. z=correlation_matrix.values,
  55. x=correlation_matrix.columns,
  56. y=correlation_matrix.columns,
  57. colorscale='RdBu', # 使用红蓝配色:正相关为蓝色,负相关为红色
  58. text=correlation_matrix.round(2).astype(str), # 将相关性值保留两位小数并转换为字符串
  59. texttemplate="%{text}", # 显示数值标签
  60. colorbar=dict(title='Correlation'),
  61. zmin=-1, zmax=1 # 设置颜色映射的范围
  62. ))
  63. # 自定义热力图布局
  64. fig_heatmap.update_layout(
  65. # title='Correlation Matrix Heatmap',
  66. xaxis=dict(tickangle=45),
  67. yaxis=dict(autorange='reversed'),
  68. template='seaborn'
  69. )
  70. # 将热力图保存为 HTML 片段
  71. corr_html = pio.to_html(fig_heatmap, full_html=False)
  72. # -------------------- 实测气象与预测气象趋势曲线 --------------------
  73. # 生成折线图(以 C_GLOBALR 和 NWP预测总辐射 为例)
  74. fig_line = px.line(df_clean, x=col_time, y=[col_x_env, col_x_pre], markers=True)
  75. # 自定义趋势图布局
  76. fig_line.update_layout(
  77. template='seaborn',
  78. # title=dict(text=f"{col_x_env}与{col_x_pre}趋势曲线",
  79. # x=0.5, font=dict(size=24, color='darkblue')),
  80. plot_bgcolor='rgba(255, 255, 255, 0.8)', # 改为白色背景
  81. xaxis=dict(
  82. showgrid=True,
  83. gridcolor='rgba(200, 200, 200, 0.5)', # 网格线颜色
  84. rangeslider=dict(visible=True), # 显示滚动条
  85. rangeselector=dict(visible=True) # 显示预设的时间范围选择器
  86. ),
  87. yaxis=dict(showgrid=True, gridcolor='rgba(200, 200, 200, 0.5)'),
  88. legend=dict(x=0.01, y=0.99, bgcolor='rgba(255, 255, 255, 0.7)', bordercolor='black', borderwidth=1)
  89. )
  90. # 将折线图保存为 HTML 片段
  91. env_pre_html = pio.to_html(fig_line, full_html=False)
  92. # -------------------- 实测气象与预测气象偏差密度曲线 --------------------
  93. df_clean['deviation'] = df_clean[col_x_pre] - df_clean[col_x_env]
  94. # 生成预测与实测辐照度偏差的密度曲线图
  95. # 生成偏差的密度图
  96. fig_density = px.histogram(df_clean, x='deviation', nbins=30, marginal='rug', opacity=0.75,
  97. histnorm='density')
  98. # 自定义密度曲线图布局
  99. fig_density.update_layout(
  100. template='seaborn',
  101. # # title=dict(text=f"{col_x_pre}与{col_x_env}偏差密度曲线",
  102. # x=0.5, font=dict(size=24, color='darkred')),
  103. plot_bgcolor='rgba(255, 255, 255, 0.8)',
  104. xaxis=dict(
  105. showgrid=True,
  106. gridcolor='rgba(200, 200, 200, 0.5)',
  107. title='偏差'
  108. ),
  109. yaxis=dict(
  110. showgrid=True,
  111. gridcolor='rgba(200, 200, 200, 0.5)',
  112. title='Density'
  113. ),
  114. legend=dict(x=0.01, y=0.99, bgcolor='rgba(255, 255, 255, 0.7)', bordercolor='black', borderwidth=1)
  115. )
  116. # 将密度曲线图保存为 HTML 片段
  117. density_html = pio.to_html(fig_density, full_html=False)
  118. # -------------------- 预测功率与实际功率曲线 --------------------
  119. # 生成折线图(以 C_GLOBALR 和 NWP预测总辐射 为例)
  120. fig_line = px.line(df_clean, x='dateTime', y=[label, label_pre], markers=True)
  121. # 自定义趋势图布局
  122. fig_line.update_layout(
  123. template='seaborn',
  124. # title=dict(text=f"{label_pre}与{label}曲线",
  125. # x=0.5, font=dict(size=24, color='darkblue')),
  126. plot_bgcolor='rgba(255, 255, 255, 0.8)', # 改为白色背景
  127. xaxis=dict(
  128. showgrid=True,
  129. gridcolor='rgba(200, 200, 200, 0.5)', # 网格线颜色
  130. rangeslider=dict(visible=True), # 显示滚动条
  131. rangeselector=dict(visible=True) # 显示预设的时间范围选择器
  132. ),
  133. yaxis=dict(showgrid=True, gridcolor='rgba(200, 200, 200, 0.5)'),
  134. legend=dict(x=0.01, y=0.99, bgcolor='rgba(255, 255, 255, 0.7)', bordercolor='black', borderwidth=1)
  135. )
  136. # 将折线图保存为 HTML 片段
  137. power_html = pio.to_html(fig_line, full_html=False)
  138. # -------------------- 准确率表展示--------------------
  139. acc_html = df_accuracy.to_html(classes='table table-bordered table-striped', index=False)
  140. # -------------------- 生成完整 HTML 页面 --------------------
  141. html_content = f"""
  142. <!DOCTYPE html>
  143. <html lang="en">
  144. <head>
  145. <meta charset="UTF-8">
  146. <meta name="viewport" content="width=device-width, initial-scale=1.0">
  147. <title>Data Analysis Report</title>
  148. <!-- 引入 Bootstrap CSS -->
  149. <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
  150. <style>
  151. body {{
  152. background-color: #f4f4f9;
  153. font-family: Arial, sans-serif;
  154. padding: 20px;
  155. }}
  156. .container {{
  157. background-color: #fff;
  158. padding: 20px;
  159. border-radius: 10px;
  160. box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
  161. margin-bottom: 30px;
  162. }}
  163. h1 {{
  164. text-align: center;
  165. color: #333;
  166. margin-bottom: 20px;
  167. }}
  168. .plot-container {{
  169. margin: 20px 0;
  170. max-height: 500px; /* 限制高度 */
  171. overflow-y: auto; /* 显示垂直滚动条 */
  172. }}
  173. .table-container {{
  174. margin-top: 30px;
  175. overflow-x: auto; /* 水平滚动条 */
  176. max-width: 100%; /* 限制宽度 */
  177. white-space: nowrap; /* 防止内容换行 */
  178. }}
  179. table {{
  180. width: 100%;
  181. font-size: 12px; /* 设置字体大小为12px */
  182. }}
  183. th, td {{
  184. text-align: center; /* 表头和单元格文字居中 */
  185. }}
  186. </style>
  187. </head>
  188. <body>
  189. <div class="container">
  190. <h1>分析报告</h1>
  191. <!-- Pandas DataFrame 表格 -->
  192. <div class="table-container">
  193. <h2>1. 数据总览</h2>
  194. {overview_html}
  195. </div>
  196. <!-- Pandas DataFrame 表格 -->
  197. <div class="table-container">
  198. <h2>2. 数据描述</h2>
  199. {describe_html}
  200. </div>
  201. <div class="plot-container">
  202. <h2>3. 数据清洗后实测气象与实际功率散点图</h2>
  203. {scatter_html}
  204. </div>
  205. <div class="plot-container">
  206. <h2>4. 相关性分析</h2>
  207. {corr_html}
  208. </div>
  209. <div class="plot-container">
  210. <h2>5. 实测气象与预测气象曲线趋势</h2>
  211. {env_pre_html}
  212. </div>
  213. <div class="plot-container">
  214. <h2>6. 预测气象与实测气象偏差曲线</h2>
  215. {density_html}
  216. </div>
  217. <div class="plot-container">
  218. <h2>7. 预测功率与实际功率曲线对比</h2>
  219. {power_html}
  220. </div>
  221. <!-- Pandas DataFrame 表格 -->
  222. <div class="table-container">
  223. <h2>8. 准确率对比</h2>
  224. {acc_html}
  225. </div>
  226. </div>
  227. </body>
  228. </html>
  229. """
  230. filename = f"{farmId}_{int(time.time() * 1000)}_{random.randint(1000, 9999)}.html"
  231. # 保存为 HTML
  232. directory = '/usr/share/nginx/html'
  233. if not os.path.exists(directory):
  234. os.makedirs(directory)
  235. file_path = os.path.join(directory, filename)
  236. path = f"http://ds3:10010/{filename}"
  237. # 将 HTML 内容写入文件
  238. with open(file_path, "w", encoding="utf-8") as f:
  239. f.write(html_content)
  240. print("HTML report generated successfully!")
  241. return path
  242. @app.route('/analysis_report', methods=['POST'])
  243. def analysis_report():
  244. start_time = time.time()
  245. result = {}
  246. success = 0
  247. path = ""
  248. print("Program starts execution!")
  249. try:
  250. args = request.values.to_dict()
  251. print('args',args)
  252. logger.info(args)
  253. #获取数据
  254. df_clean, df_accuracy = get_df_list_from_mongo(args)[0], get_df_list_from_mongo(args)[1]
  255. path = put_analysis_report_to_html(args, df_clean, df_accuracy)
  256. success = 1
  257. except Exception as e:
  258. my_exception = traceback.format_exc()
  259. my_exception.replace("\n","\t")
  260. result['msg'] = my_exception
  261. end_time = time.time()
  262. result['success'] = success
  263. result['args'] = args
  264. result['start_time'] = time.strftime('%Y-%m-%d %H:%M:%S', time.localtime(start_time))
  265. result['end_time'] = time.strftime('%Y-%m-%d %H:%M:%S', time.localtime(end_time))
  266. result['file_path'] = path
  267. print("Program execution ends!")
  268. return result
  269. if __name__=="__main__":
  270. print("Program starts execution!")
  271. logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(name)s - %(levelname)s - %(message)s')
  272. logger = logging.getLogger("analysis_report log")
  273. from waitress import serve
  274. serve(app, host="0.0.0.0", port=10092)
  275. print("server start!")