routes.py 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452
  1. import sqlite3
  2. from flask import Blueprint, request, jsonify, current_app
  3. from .model import predict, train_and_save_model
  4. import pandas as pd
  5. from . import db # 从 app 包导入 db 实例
  6. from sqlalchemy.engine.reflection import Inspector
  7. from .database_models import Model, ModelParameters, Dataset
  8. import os
  9. from .utils import create_dynamic_table, allowed_file
  10. from sqlalchemy.orm import sessionmaker
  11. # 创建蓝图 (Blueprint),用于分离路由
  12. bp = Blueprint('routes', __name__)
  13. @bp.route('/upload-dataset', methods=['POST'])
  14. def upload_dataset():
  15. try:
  16. # 检查是否包含文件
  17. if 'file' not in request.files:
  18. return jsonify({'error': 'No file part'}), 400
  19. file = request.files['file']
  20. # 如果没有文件或者文件名为空
  21. if file.filename == '':
  22. return jsonify({'error': 'No selected file'}), 400
  23. # 检查文件类型是否允许
  24. if file and allowed_file(file.filename):
  25. # 获取数据集的元数据
  26. dataset_name = request.form.get('dataset_name')
  27. dataset_description = request.form.get('dataset_description', 'No description provided')
  28. dataset_type = request.form.get('dataset_type') # 新增字段:数据集类型
  29. # 校验 dataset_type 是否存在
  30. if not dataset_type:
  31. return jsonify({'error': 'Dataset type is required'}), 400
  32. # 创建 Dataset 实体并保存到数据库
  33. new_dataset = Dataset(
  34. DatasetName=dataset_name,
  35. DatasetDescription=dataset_description,
  36. RowCount=0, # 初步创建数据集时,行数先置为0
  37. Status='pending', # 状态默认为 'pending'
  38. DatasetType=dataset_type # 保存数据集类型
  39. )
  40. db.session.add(new_dataset)
  41. db.session.commit()
  42. # 获取数据集的 ID
  43. dataset_id = new_dataset.DatasetID
  44. # 保存文件时使用数据库的 DatasetID 作为文件名
  45. unique_filename = f"dataset_{dataset_id}.xlsx"
  46. upload_folder = current_app.config['UPLOAD_FOLDER']
  47. file_path = os.path.join(upload_folder, unique_filename)
  48. # 保存文件
  49. file.save(file_path)
  50. # 读取 Excel 文件内容
  51. dataset_df = pd.read_excel(file_path)
  52. # 更新数据集的行数
  53. row_count = len(dataset_df)
  54. new_dataset.RowCount = row_count
  55. new_dataset.Status = 'processed' # 状态更新为 processed
  56. db.session.commit()
  57. # 动态创建数据表
  58. columns = {}
  59. for col in dataset_df.columns:
  60. if dataset_df[col].dtype == 'int64':
  61. columns[col] = 'int'
  62. elif dataset_df[col].dtype == 'float64':
  63. columns[col] = 'float'
  64. else:
  65. columns[col] = 'str'
  66. # 创建新表格(动态表格)
  67. dynamic_table_class = create_dynamic_table(dataset_id, columns)
  68. # 创建新的数据库会话
  69. Session = sessionmaker(bind=db.engine)
  70. session = Session()
  71. # 将每一行数据插入到动态创建的表格中
  72. for _, row in dataset_df.iterrows():
  73. record_data = row.to_dict()
  74. # 将数据插入到新表格中
  75. session.execute(dynamic_table_class.__table__.insert(), [record_data])
  76. session.commit()
  77. session.close()
  78. return jsonify({
  79. 'message': f'Dataset {dataset_name} uploaded successfully!',
  80. 'dataset_id': new_dataset.DatasetID,
  81. 'filename': unique_filename
  82. }), 201
  83. else:
  84. return jsonify({'error': 'Invalid file type'}), 400
  85. except Exception as e:
  86. return jsonify({'error': str(e)}), 500
  87. @bp.route('/tables', methods=['GET'])
  88. def list_tables():
  89. engine = db.engine # 使用 db 实例的 engine
  90. inspector = Inspector.from_engine(engine) # 创建 Inspector 对象
  91. table_names = inspector.get_table_names() # 获取所有表名
  92. return jsonify(table_names) # 以 JSON 形式返回表名列表
  93. @bp.route('/models/<int:model_id>', methods=['GET'])
  94. def get_model(model_id):
  95. try:
  96. model = Model.query.filter_by(ModelID=model_id).first()
  97. if model:
  98. return jsonify({
  99. 'ModelID': model.ModelID,
  100. 'ModelName': model.ModelName,
  101. 'ModelType': model.ModelType,
  102. 'CreatedAt': model.CreatedAt.strftime('%Y-%m-%d %H:%M:%S'),
  103. 'Description': model.Description
  104. })
  105. else:
  106. return jsonify({'message': 'Model not found'}), 404
  107. except Exception as e:
  108. return jsonify({'error': 'Internal server error', 'message': str(e)}), 500
  109. @bp.route('/models', methods=['GET'])
  110. def get_all_models():
  111. try:
  112. models = Model.query.all() # 获取所有模型数据
  113. if models:
  114. result = [
  115. {
  116. 'ModelID': model.ModelID,
  117. 'ModelName': model.ModelName,
  118. 'ModelType': model.ModelType,
  119. 'CreatedAt': model.CreatedAt.strftime('%Y-%m-%d %H:%M:%S'),
  120. 'Description': model.Description
  121. }
  122. for model in models
  123. ]
  124. return jsonify(result)
  125. else:
  126. return jsonify({'message': 'No models found'}), 404
  127. except Exception as e:
  128. return jsonify({'error': 'Internal server error', 'message': str(e)}), 500
  129. @bp.route('/model-parameters', methods=['GET'])
  130. def get_all_model_parameters():
  131. try:
  132. parameters = ModelParameters.query.all() # 获取所有参数数据
  133. if parameters:
  134. result = [
  135. {
  136. 'ParamID': param.ParamID,
  137. 'ModelID': param.ModelID,
  138. 'ParamName': param.ParamName,
  139. 'ParamValue': param.ParamValue
  140. }
  141. for param in parameters
  142. ]
  143. return jsonify(result)
  144. else:
  145. return jsonify({'message': 'No parameters found'}), 404
  146. except Exception as e:
  147. return jsonify({'error': 'Internal server error', 'message': str(e)}), 500
  148. @bp.route('/models/<int:model_id>/parameters', methods=['GET'])
  149. def get_model_parameters(model_id):
  150. try:
  151. model = Model.query.filter_by(ModelID=model_id).first()
  152. if model:
  153. # 获取该模型的所有参数
  154. parameters = [
  155. {
  156. 'ParamID': param.ParamID,
  157. 'ParamName': param.ParamName,
  158. 'ParamValue': param.ParamValue
  159. }
  160. for param in model.parameters
  161. ]
  162. # 返回模型参数信息
  163. return jsonify({
  164. 'ModelID': model.ModelID,
  165. 'ModelName': model.ModelName,
  166. 'ModelType': model.ModelType,
  167. 'CreatedAt': model.CreatedAt.strftime('%Y-%m-%d %H:%M:%S'),
  168. 'Description': model.Description,
  169. 'Parameters': parameters
  170. })
  171. else:
  172. return jsonify({'message': 'Model not found'}), 404
  173. except Exception as e:
  174. return jsonify({'error': 'Internal server error', 'message': str(e)}), 500
  175. @bp.route('/predict', methods=['POST'])
  176. def predict_route():
  177. try:
  178. data = request.get_json()
  179. model_name = data.get('model_name', 'RF_filt') # 提取模型名称
  180. parameters = data.get('parameters', {}) # 提取所有参数
  181. input_data = pd.DataFrame([parameters]) # 转换参数为DataFrame
  182. predictions = predict(input_data, model_name) # 调用预测函数
  183. return jsonify({'predictions': predictions}), 200
  184. except Exception as e:
  185. return jsonify({'error': str(e)}), 400
  186. # 定义添加数据库记录的 API 接口
  187. @bp.route('/add_item', methods=['POST'])
  188. def add_item():
  189. """
  190. 接收 JSON 格式的请求体,包含表名和要插入的数据。
  191. 尝试将数据插入到指定的表中。
  192. :return:
  193. """
  194. db = get_db()
  195. try:
  196. # 确保请求体是JSON格式
  197. data = request.get_json()
  198. if not data:
  199. raise ValueError("No JSON data provided")
  200. table_name = data.get('table')
  201. item_data = data.get('item')
  202. if not table_name or not item_data:
  203. return jsonify({'error': 'Missing table name or item data'}), 400
  204. cur = db.cursor()
  205. # 动态构建 SQL 语句
  206. columns = ', '.join(item_data.keys())
  207. placeholders = ', '.join(['?'] * len(item_data))
  208. sql = f"INSERT INTO {table_name} ({columns}) VALUES ({placeholders})"
  209. cur.execute(sql, tuple(item_data.values()))
  210. db.commit()
  211. # 返回更详细的成功响应
  212. return jsonify({'success': True, 'message': 'Item added successfully'}), 201
  213. except ValueError as e:
  214. return jsonify({'error': str(e)}), 400
  215. except KeyError as e:
  216. return jsonify({'error': f'Missing data field: {e}'}), 400
  217. except sqlite3.IntegrityError as e:
  218. # 处理例如唯一性约束违反等数据库完整性错误
  219. return jsonify({'error': 'Database integrity error', 'details': str(e)}), 409
  220. except sqlite3.Error as e:
  221. # 处理其他数据库错误
  222. return jsonify({'error': 'Database error', 'details': str(e)}), 500
  223. finally:
  224. db.close()
  225. # 定义删除数据库记录的 API 接口
  226. @bp.route('/delete_item', methods=['POST'])
  227. def delete_item():
  228. data = request.get_json()
  229. table_name = data.get('table')
  230. condition = data.get('condition')
  231. # 检查表名和条件是否提供
  232. if not table_name or not condition:
  233. return jsonify({
  234. "success": False,
  235. "message": "缺少表名或条件参数"
  236. }), 400
  237. # 尝试从条件字符串中分离键和值
  238. try:
  239. key, value = condition.split('=')
  240. except ValueError:
  241. return jsonify({
  242. "success": False,
  243. "message": "条件格式错误,应为 'key=value'"
  244. }), 400
  245. db = get_db()
  246. cur = db.cursor()
  247. try:
  248. # 执行删除操作
  249. cur.execute(f"DELETE FROM {table_name} WHERE {key} = ?", (value,))
  250. db.commit()
  251. # 如果没有错误发生,返回成功响应
  252. return jsonify({
  253. "success": True,
  254. "message": "记录删除成功"
  255. }), 200
  256. except sqlite3.Error as e:
  257. # 发生错误,回滚事务
  258. db.rollback()
  259. # 返回失败响应,并包含错误信息
  260. return jsonify({
  261. "success": False,
  262. "message": f"删除失败: {e}"
  263. }), 400
  264. # 定义修改数据库记录的 API 接口
  265. @bp.route('/update_item', methods=['PUT'])
  266. def update_record():
  267. data = request.get_json()
  268. # 检查必要的数据是否提供
  269. if not data or 'table' not in data or 'item' not in data:
  270. return jsonify({
  271. "success": False,
  272. "message": "请求数据不完整"
  273. }), 400
  274. table_name = data['table']
  275. item = data['item']
  276. # 假设 item 的第一个元素是 ID
  277. if not item or next(iter(item.keys())) is None:
  278. return jsonify({
  279. "success": False,
  280. "message": "记录数据为空"
  281. }), 400
  282. # 获取 ID 和其他字段值
  283. id_key = next(iter(item.keys()))
  284. record_id = item[id_key]
  285. updates = {key: value for key, value in item.items() if key != id_key} # 排除 ID
  286. db = get_db()
  287. cur = db.cursor()
  288. try:
  289. record_id = int(record_id) # 确保 ID 是整数
  290. except ValueError:
  291. return jsonify({
  292. "success": False,
  293. "message": "ID 必须是整数"
  294. }), 400
  295. # 准备参数列表,包括更新的值和 ID
  296. parameters = list(updates.values()) + [record_id]
  297. # 执行更新操作
  298. set_clause = ','.join([f"{k} = ?" for k in updates.keys()])
  299. sql = f"UPDATE {table_name} SET {set_clause} WHERE {id_key} = ?"
  300. try:
  301. cur.execute(sql, parameters)
  302. db.commit()
  303. if cur.rowcount == 0:
  304. return jsonify({
  305. "success": False,
  306. "message": "未找到要更新的记录"
  307. }), 404
  308. return jsonify({
  309. "success": True,
  310. "message": "数据更新成功"
  311. }), 200
  312. except sqlite3.Error as e:
  313. db.rollback()
  314. return jsonify({
  315. "success": False,
  316. "message": f"更新失败: {e}"
  317. }), 400
  318. # 定义查询数据库记录的 API 接口
  319. @bp.route('/search/record', methods=['GET'])
  320. def sql_search():
  321. """
  322. 接收 JSON 格式的请求体,包含表名和要查询的 ID。
  323. 尝试查询指定 ID 的记录并返回结果。
  324. :return:
  325. """
  326. try:
  327. data = request.get_json()
  328. # 表名
  329. sql_table = data['table']
  330. # 要搜索的 ID
  331. Id = data['id']
  332. # 连接到数据库
  333. db = get_db()
  334. cur = db.cursor()
  335. # 构造查询语句
  336. sql = f"SELECT * FROM {sql_table} WHERE id = ?"
  337. # 执行查询
  338. cur.execute(sql, (Id,))
  339. # 获取查询结果
  340. rows = cur.fetchall()
  341. column_names = [desc[0] for desc in cur.description]
  342. # 检查是否有结果
  343. if not rows:
  344. return jsonify({'error': '未查找到对应数据。'}), 400
  345. # 构造响应数据
  346. results = []
  347. for row in rows:
  348. result = {column_names[i]: row[i] for i in range(len(row))}
  349. results.append(result)
  350. # 关闭游标和数据库连接
  351. cur.close()
  352. db.close()
  353. # 返回 JSON 响应
  354. return jsonify(results), 200
  355. except sqlite3.Error as e:
  356. # 如果发生数据库错误,返回错误信息
  357. return jsonify({'error': str(e)}), 400
  358. except KeyError as e:
  359. # 如果请求数据中缺少必要的键,返回错误信息
  360. return jsonify({'error': f'缺少必要的数据字段: {e}'}), 400
  361. # 定义提供数据库列表,用于展示表格的 API 接口
  362. @bp.route('/tables', methods=['POST'])
  363. def get_table():
  364. data = request.get_json()
  365. table_name = data.get('table')
  366. if not table_name:
  367. return jsonify({'error': '需要表名'}), 400
  368. db = get_db()
  369. try:
  370. cur = db.cursor()
  371. cur.execute(f"SELECT * FROM {table_name}")
  372. rows = cur.fetchall()
  373. if not rows:
  374. return jsonify({'error': '表为空或不存在'}), 400
  375. headers = [description[0] for description in cur.description]
  376. return jsonify(rows=rows, headers=headers), 200
  377. except sqlite3.Error as e:
  378. return jsonify({'error': str(e)}), 400
  379. finally:
  380. db.close()