__init__.py 3.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111
  1. from flask import Flask, jsonify, request
  2. import os
  3. from flask import Flask
  4. from flask_cors import CORS
  5. from flask_sqlalchemy import SQLAlchemy
  6. from flask_migrate import Migrate
  7. import logging
  8. import pandas as pd
  9. from datetime import datetime
  10. import json
  11. from .utils import get_current_data, get_dataset_by_id, get_table_data ,get_table_metal_averages
  12. # 先创建SQLAlchemy实例,确保其他模块可以导入
  13. db = SQLAlchemy()
  14. # 导入配置和工具函数(在db定义之后)
  15. from . import config
  16. from .utils import get_current_data, get_dataset_by_id
  17. def create_app():
  18. app = Flask(__name__)
  19. # 配置应用
  20. app.config['SECRET_KEY'] = 'abcdef1234567890'
  21. app.config.from_object(config.Config)
  22. app.logger.setLevel(logging.DEBUG)
  23. # 初始化CORS
  24. CORS(app)
  25. # 初始化数据库
  26. db.init_app(app)
  27. # 初始化迁移工具
  28. migrate = Migrate(app, db)
  29. # 注册蓝图
  30. from . import routes, frontend
  31. app.register_blueprint(routes.bp)
  32. app.register_blueprint(frontend.bp)
  33. register_api_routes(app)
  34. return app
  35. def safe_data_conversion(df):
  36. """安全转换DataFrame为可序列化的字典列表,处理特殊数据类型"""
  37. # 处理日期时间类型
  38. for col in df.columns:
  39. if pd.api.types.is_datetime64_any_dtype(df[col]):
  40. df[col] = df[col].dt.strftime('%Y-%m-%d %H:%M:%S')
  41. # 处理空值
  42. df = df.fillna('')
  43. # 转换为字典列表
  44. data_list = df.to_dict(orient='records')
  45. # 处理可能的嵌套结构
  46. def serialize_item(item):
  47. if isinstance(item, dict):
  48. return {k: serialize_item(v) for k, v in item.items()}
  49. elif isinstance(item, list):
  50. return [serialize_item(v) for v in item]
  51. elif isinstance(item, datetime):
  52. return item.strftime('%Y-%m-%d %H:%M:%S')
  53. else:
  54. return item
  55. return [serialize_item(item) for item in data_list]
  56. def register_api_routes(app):
  57. # 新增:通用表查询接口
  58. @app.route('/api/table-data', methods=['GET'])
  59. def api_table_data():
  60. # 1. 获取前端传的表名
  61. table_name = request.args.get('table_name')
  62. if not table_name:
  63. return jsonify({"error": "请传入 table_name 参数(如 ?table_name=dataset_35)"}), 400
  64. try:
  65. # 2. 调用工具函数查询数据
  66. df = get_table_data(db.session, table_name)
  67. # 3. 安全转换数据(复用之前的 safe_data_conversion 函数)
  68. data = safe_data_conversion(df)
  69. return jsonify({"success": True, "data": data})
  70. except ValueError as e: # 非法表名
  71. return jsonify({"success": False, "error": str(e)}), 400
  72. except Exception as e: # 其他错误
  73. app.logger.error(f"查询表 {table_name} 失败: {str(e)}", exc_info=True)
  74. return jsonify({"success": False, "error": str(e)}), 500
  75. # 新增接口:查询金属平均值(/api/table-averages)
  76. @app.route('/api/table-averages', methods=['GET'])
  77. def api_table_averages():
  78. # 1. 获取参数
  79. table_name = request.args.get('table_name')
  80. if not table_name:
  81. return jsonify({"error": "缺少参数:table_name"}), 400
  82. try:
  83. # 2. 调用工具函数计算平均值(复用 db.session)
  84. averages = get_table_metal_averages(db.session, table_name)
  85. # 3. 返回结果
  86. return jsonify({"success": True, "averages": averages})
  87. except ValueError as e: # 表名校验失败(预期错误)
  88. return jsonify({"success": False, "error": str(e)}), 400
  89. except Exception as e: # 其他运行时错误(如数据库连接失败)
  90. app.logger.error(f"计算 {table_name} 平均值失败: {str(e)}", exc_info=True)
  91. return jsonify({"success": False, "error": str(e)}), 500