routes.py 8.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286
  1. """
  2. 数据指标API接口模块
  3. 该模块提供了数据指标的各种API接口,包括:
  4. - 指标新增、更新和查询
  5. - 指标列表展示
  6. - 指标图谱生成
  7. - 指标代码生成
  8. - 指标关系管理
  9. """
  10. from flask import request, jsonify
  11. import json
  12. from app.api.data_metric import bp
  13. from app.core.data_metric.metric_interface import (
  14. metric_list, handle_metric_relation, handle_data_metric,
  15. handle_meta_data_metric, handle_id_metric, metric_kinship_graph,
  16. metric_impact_graph, metric_all_graph, data_metric_edit
  17. )
  18. from app.core.llm import code_generate_metric
  19. from app.core.meta_data import translate_and_parse
  20. from app.models.result import success, failed
  21. from app.core.graph.graph_operations import MyEncoder, connect_graph
  22. @bp.route('/data/metric/relation', methods=['POST'])
  23. def data_metric_relation():
  24. """
  25. 处理数据指标血缘关系
  26. 请求参数:
  27. - id: 数据模型ID
  28. 返回:
  29. - 处理结果,包含血缘关系数据
  30. """
  31. try:
  32. # 传入请求参数
  33. receiver = request.get_json()
  34. model_ids = receiver['id'] # 给定一个数据模型的id
  35. response_data = handle_metric_relation(model_ids)
  36. res = success(response_data, "success")
  37. return json.dumps(res, ensure_ascii=False, cls=MyEncoder)
  38. except Exception as e:
  39. res = failed({}, {"error": f"{e}"})
  40. return json.dumps(res, ensure_ascii=False, cls=MyEncoder)
  41. @bp.route('/data/metric/add', methods=['POST'])
  42. def data_metric_add():
  43. """
  44. 新增数据指标
  45. 请求参数:
  46. - name: 指标名称
  47. - 其他指标相关属性
  48. 返回:
  49. - 处理结果,成功或失败
  50. """
  51. # 传入请求参数
  52. receiver = request.get_json()
  53. metric_name = receiver['name'] # 数据指标的name
  54. try:
  55. result_list = translate_and_parse(metric_name)
  56. id, id_list = handle_data_metric(metric_name, result_list, receiver)
  57. handle_meta_data_metric(id, id_list)
  58. res = success({}, "success")
  59. return json.dumps(res, ensure_ascii=False, cls=MyEncoder)
  60. except Exception as e:
  61. res = failed({}, {"error": f"{e}"})
  62. return json.dumps(res, ensure_ascii=False, cls=MyEncoder)
  63. @bp.route('/data/metric/code', methods=['POST'])
  64. def data_metric_code():
  65. """
  66. 生成指标计算的代码
  67. 请求参数:
  68. - content: 指标规则描述
  69. - relation: 映射关系
  70. 返回:
  71. - 生成的代码
  72. """
  73. # 传入请求参数
  74. receiver = request.get_json()
  75. content = receiver['content'] # 指标规则描述
  76. relation = receiver['relation'] # 映射关系
  77. try:
  78. id_list = [item["id"] for item in relation]
  79. cql = """
  80. MATCH (n:meta_node) WHERE id(n) IN $Id_list
  81. WITH n.en_name AS en_name, n.name AS name
  82. WITH collect({en_name: en_name, name: name}) AS res
  83. WITH reduce(acc = {}, item IN res | apoc.map.setKey(acc, item.en_name, item.name)) AS result
  84. RETURN result
  85. """
  86. id_relation = connect_graph.run(cql, Id_list=id_list).evaluate()
  87. result = code_generate_metric(content, id_relation)
  88. res = success(result, "success")
  89. return json.dumps(res, ensure_ascii=False, cls=MyEncoder)
  90. except Exception as e:
  91. res = failed({}, {"error": f"{e}"})
  92. return json.dumps(res, ensure_ascii=False, cls=MyEncoder)
  93. @bp.route('/data/metric/detail', methods=['POST'])
  94. def data_metric_detail():
  95. """
  96. 获取数据指标详情
  97. 请求参数:
  98. - id: 指标ID
  99. 返回:
  100. - 指标详情数据
  101. """
  102. try:
  103. # 传入请求参数
  104. receiver = request.get_json()
  105. id = int(receiver.get('id'))
  106. response_data = handle_id_metric(id)
  107. res = success(response_data, "success")
  108. return json.dumps(res, ensure_ascii=False, cls=MyEncoder)
  109. except Exception as e:
  110. res = failed({}, {"error": f"{e}"})
  111. return json.dumps(res, ensure_ascii=False, cls=MyEncoder)
  112. @bp.route('/data/metric/list', methods=['POST'])
  113. def data_metric_list():
  114. """
  115. 获取数据指标列表
  116. 请求参数:
  117. - current: 当前页码,默认1
  118. - size: 每页大小,默认10
  119. - en_name: 英文名称过滤
  120. - name: 名称过滤
  121. - category: 类别过滤
  122. - time: 时间过滤
  123. - tag: 标签过滤
  124. 返回:
  125. - 指标列表数据和分页信息
  126. """
  127. try:
  128. # 传入请求参数
  129. receiver = request.get_json()
  130. page = int(receiver.get('current', 1))
  131. page_size = int(receiver.get('size', 10))
  132. en_name_filter = receiver.get('en_name', None)
  133. name_filter = receiver.get('name', None)
  134. category = receiver.get('category', None)
  135. time = receiver.get('time', None)
  136. tag = receiver.get('tag', None)
  137. # 计算跳过的记录的数量
  138. skip_count = (page - 1) * page_size
  139. data, total = metric_list(skip_count, page_size, en_name_filter,
  140. name_filter, category, time, tag)
  141. response_data = {'records': data, 'total': total, 'size': page_size, 'current': page}
  142. res = success(response_data, "success")
  143. return json.dumps(res, ensure_ascii=False, cls=MyEncoder)
  144. except Exception as e:
  145. res = failed({}, {"error": f"{e}"})
  146. return json.dumps(res, ensure_ascii=False, cls=MyEncoder)
  147. @bp.route('/data/metric/graph/all', methods=['POST'])
  148. def data_metric_graph_all():
  149. """
  150. 获取数据指标图谱
  151. 请求参数:
  152. - id: 指标ID
  153. - type: 图谱类型,可选kinship/impact/all
  154. - meta: 是否返回元数据,true/false
  155. 返回:
  156. - 图谱数据
  157. """
  158. try:
  159. # 传入请求参数
  160. receiver = request.get_json()
  161. nodeid = receiver['id']
  162. type = receiver['type'] # kinship/impact/all
  163. meta = receiver['meta'] # true/false 是否返回元数据
  164. if type == 'kinship':
  165. result = metric_kinship_graph(nodeid, meta)
  166. elif type == 'impact':
  167. result = metric_impact_graph(nodeid, meta)
  168. else:
  169. result = metric_all_graph(nodeid, meta)
  170. return json.dumps(success(result, "success"), ensure_ascii=False, cls=MyEncoder)
  171. except Exception as e:
  172. return json.dumps(failed({}, str(e)), ensure_ascii=False, cls=MyEncoder)
  173. @bp.route('/data/metric/list/graph', methods=['POST'])
  174. def data_metric_list_graph():
  175. """
  176. 获取数据指标列表图谱
  177. 请求参数:
  178. - tag: 标签ID
  179. 返回:
  180. - 图谱数据,包含节点和连线
  181. """
  182. try:
  183. # 传入请求参数
  184. receiver = request.get_json()
  185. if not receiver or 'tag' not in receiver:
  186. raise ValueError("Missing 'tag' parameter in request body")
  187. nodeid = receiver['tag']
  188. # 构建查询条件
  189. params = {}
  190. where_clause = ""
  191. if nodeid is not None:
  192. where_clause = "MATCH (n)-[:label]->(la) WHERE id(la) = $nodeId"
  193. params['nodeId'] = nodeid
  194. query = f"""
  195. MATCH (n:data_metric)
  196. OPTIONAL MATCH (n)-[:child]->(child)
  197. {where_clause}
  198. WITH
  199. collect(DISTINCT {{id: toString(id(n)), text: n.name, type: split(labels(n)[0], '_')[1]}}) AS nodes,
  200. collect(DISTINCT {{id: toString(id(child)), text: child.name, type: split(labels(child)[0], '_')[1]}}) AS nodes2,
  201. collect(DISTINCT {{from: toString(id(n)), to: toString(id(child)), text: '下级'}}) AS lines
  202. RETURN nodes + nodes2 AS nodes, lines AS lines
  203. """
  204. # 修复:使用正确的session方式执行查询
  205. driver = connect_graph()
  206. if not driver:
  207. return json.dumps(failed({}, "无法连接到数据库"), ensure_ascii=False, cls=MyEncoder)
  208. with driver.session() as session:
  209. result = session.run(query, **params)
  210. res = {}
  211. for item in result:
  212. res = {
  213. "nodes": [record for record in item['nodes'] if record['id']],
  214. "lines": [record for record in item['lines'] if record['from'] and record['to']],
  215. }
  216. return json.dumps(success(res, "success"), ensure_ascii=False, cls=MyEncoder)
  217. except Exception as e:
  218. return json.dumps(failed({}, str(e)), ensure_ascii=False, cls=MyEncoder)
  219. @bp.route('/data/metric/update', methods=['POST'])
  220. def data_metric_update():
  221. """
  222. 更新数据指标
  223. 请求参数:
  224. - id: 指标ID
  225. - 其他需要更新的属性
  226. 返回:
  227. - 处理结果,成功或失败
  228. """
  229. try:
  230. # 传入请求参数
  231. receiver = request.get_json()
  232. data_metric_edit(receiver)
  233. res = success({}, "success")
  234. return json.dumps(res, ensure_ascii=False, cls=MyEncoder)
  235. except Exception as e:
  236. res = failed({}, {"error": f"{e}"})
  237. return json.dumps(res, ensure_ascii=False, cls=MyEncoder)