api_prepare.py 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343
  1. from django.contrib import auth
  2. from rest_framework.views import APIView
  3. from rest_framework.response import Response
  4. from rest_framework import status
  5. from rest_framework.authtoken.models import Token
  6. from rest_framework.authentication import BasicAuthentication, TokenAuthentication
  7. from .serializers import UserRegisterSerializer
  8. from django.middleware.csrf import get_token
  9. from django.contrib.auth import login
  10. from api.utils import *
  11. from api.models import File, Mission, Plan, Algorithm
  12. import json
  13. import logging
  14. logger = logging.getLogger('prepare')
  15. class UploadFileAPI(APIView):
  16. def get(self, request):
  17. user = request.user
  18. history = File.objects.getHistory(user=user)
  19. if history == FAILED:
  20. return failed(message="获取文件上传历史失败")
  21. else:
  22. return success(data=history)
  23. def post(self, request):
  24. user = request.user
  25. # 获取上传的文件对象
  26. nodeFileName = request.data.get('nodeFileName')
  27. edgeFileName = request.data.get('edgeFileName')
  28. nodeFile = request.data.get('nodes')
  29. edgeFile = request.data.get('edges')
  30. # 检查文件用途并进行响应处理
  31. file_usage = request.data.get('usage')
  32. if file_usage == 'input':
  33. pass
  34. else:
  35. return failed(message=filename + "上传失败 暂时只支持上传图处理原料文件")
  36. # 检查文件类型并进行相应处理
  37. file_type = request.data.get('type')
  38. for filename in [nodeFileName, edgeFileName]:
  39. if file_type == 'csv':
  40. if filename.split('.')[-1] != 'csv':
  41. return failed(message=filename + "上传失败 文件类型和文件后缀名不匹配")
  42. else:
  43. return failed(message=filename + "上传失败 暂不支持CSV之外文件")
  44. successUploadedFiles = []
  45. pre_file = None
  46. for filename, fileData in [[edgeFileName, edgeFile], [nodeFileName, nodeFile]]:
  47. # 处理数据库中记录
  48. file = File()
  49. file.name = filename
  50. file.user = request.user
  51. file.type = file_type
  52. file.usage = file_usage
  53. # 一定先edge再node
  54. if pre_file == None:
  55. file.content = 'edge'
  56. else:
  57. file.content = 'node'
  58. file.save()
  59. # 尝试保存文件
  60. result = file.storage(fileData)
  61. if result != OK:
  62. file.delete()
  63. # 第二个文件错误,同样删除第一个文件
  64. if pre_file:
  65. pre_file.delete()
  66. if result == FILE_ALREADY_EXIST:
  67. return failed(message=filename + "上传失败 已存在同名文件", data="已存在同名文件", code=400)
  68. elif result == FILE_FAILED_CREATE_DIR:
  69. return failed(message=filename + "上传失败 文件目录创建失败", data="文件目录创建失败", code=400)
  70. else:
  71. return failed(message=filename + "上传失败 因未知原因文件上传失败", data="因未知原因文件上传失败", code=400)
  72. # 第二个文件也成功时,将两个文件互相关联
  73. if pre_file:
  74. file.associate = pre_file
  75. pre_file.associate = file
  76. file.saveWithInfo()
  77. pre_file.saveWithInfo()
  78. else:
  79. pre_file = file
  80. # 两个文件均上传成功后,检测文件内容是否符合规范
  81. if file.checkIllegal() and pre_file.checkIllegal():
  82. # 确保正确后创建mission,并将信息返回
  83. mission = Mission()
  84. mission.nodeFile = file
  85. mission.edgeFile = pre_file
  86. mission.user = user
  87. mission.save()
  88. successUploadedFiles.append({
  89. "id": mission.id,
  90. "name": mission.name,
  91. # 防止近义词错误
  92. "state": mission.state,
  93. "status": mission.state,
  94. "content": "mission",
  95. })
  96. # 初步对文件内容进行分析,获取节点和边的基本数据
  97. for file in [file, pre_file]:
  98. successUploadedFiles.append({
  99. "id": file.id,
  100. "name": file.name,
  101. "content": file.content,
  102. "nodes": file.own_file_info.nodes,
  103. "sNodes": file.own_file_info.sNodes,
  104. "dNodes": file.own_file_info.dNodes,
  105. "iNodes": file.own_file_info.iNodes,
  106. "edges": file.own_file_info.edges,
  107. })
  108. return success(message="文件上传成功", data=successUploadedFiles, code=200)
  109. else:
  110. return failed(message="文件合法性检查失败")
  111. def delete(self, request):
  112. user = request.user
  113. file = File.objects.get(id=request.data.get('id'))
  114. result = file.deleteStorage()
  115. if result == OK:
  116. return success(message="删除文件成功")
  117. elif result == False:
  118. return failed(messgae="删除文件失败")
  119. class InputFileAPI(APIView):
  120. def post(self, request):
  121. user = request.user
  122. nodes = request.data.get('nodes')
  123. edges = request.data.get('edges')
  124. logger.info(nodes)
  125. logger.info(edges)
  126. # 保存节点文件
  127. # 当前设置输入的节点信息仅包含一个名称
  128. nodesFile = File.objects.create(type='csv', usage='input', user=user, content='node')
  129. try:
  130. nodesCsvList = []
  131. for node in nodes:
  132. nodeList = [int(node['id']), str(node['type'])]
  133. for key in [k for k in node if k not in ['id', 'type']]:
  134. nodeList.append(str(key) + ':' +str(node[key]))
  135. nodesCsvList.append(nodeList)
  136. logger.info(nodesCsvList)
  137. nodesFile.generate(nodesCsvList)
  138. except Exception:
  139. # 回退操作
  140. logger.error(f"从输入信息创建节点文件失败 Error:{Exception}")
  141. nodesFile.delete()
  142. # 保存边文件
  143. edgesFile = File.objects.create(type='csv', usage='input', user=user, content='edge')
  144. try:
  145. edgesCsvList = []
  146. for edge in edges:
  147. # 边中应至少有source和target两个参数
  148. edgeList = [int(edge['source']), int(edge['target'])]
  149. for key in [k for k in edge if k not in ['source', 'target']]:
  150. edgeList.append(str(key) + ':' + str(edge[key]))
  151. edgesCsvList.append(edgeList)
  152. logger.info(edgesCsvList)
  153. edgesFile.generate(edgesCsvList)
  154. except Exception:
  155. # 回退操作
  156. logger.error(f"从输入信息创建边文件失败 Error:{Exception}")
  157. nodesFile.delete()
  158. edgesFile.delete()
  159. # 节点和边均保存成功,可以互相关联并创建mission
  160. nodesFile.associate = edgesFile
  161. edgesFile.associate = nodesFile
  162. # 计算节点和边的信息,生成fileInfo
  163. nodesFile.saveWithInfo()
  164. edgesFile.saveWithInfo()
  165. # 构造mission并返回完整信息
  166. successUploadedFiles = []
  167. if nodesFile.checkIllegal() and edgesFile.checkIllegal():
  168. # 确保正确后创建mission,并将信息返回
  169. mission = Mission()
  170. mission.nodeFile = nodesFile
  171. mission.edgeFile = edgesFile
  172. mission.user = user
  173. mission.state = 'init'
  174. mission.save()
  175. successUploadedFiles.append({
  176. "id": mission.id,
  177. "name": mission.name,
  178. "content": "mission",
  179. })
  180. # 初步对文件内容进行分析,获取节点和边的基本数据
  181. for file in [nodesFile, edgesFile]:
  182. successUploadedFiles.append({
  183. "id": file.id,
  184. "name": file.name,
  185. "content": file.content,
  186. "nodes": file.own_file_info.nodes,
  187. "sNodes": file.own_file_info.sNodes,
  188. "dNodes": file.own_file_info.dNodes,
  189. "iNodes": file.own_file_info.iNodes,
  190. "edges": file.own_file_info.edges,
  191. })
  192. return success(message="文件上传成功", data=successUploadedFiles, code=200)
  193. else:
  194. logger.error("节点和边文件的合法性检测未通过")
  195. nodesFile.delete()
  196. edgesFile.delete()
  197. return failed(message="节点和边信息输入失败,节点或边信息未通过合法性检测")
  198. class PlanAPI(APIView):
  199. def get(self, request):
  200. user = request.user
  201. try:
  202. mission = Mission.objects.get(id=request.GET.get('mission'))
  203. except Mission.DoesNotExist:
  204. logger.error(f"处理规划所属任务不存在{request.GET.get('mission')}")
  205. return failed(message="未找到规划任务所属任务")
  206. plans = mission.own_plans.all()
  207. response = []
  208. for plan in plans:
  209. if plan.parent:
  210. response.append({
  211. 'id': plan.id,
  212. 'parent': plan.parent.id,
  213. # 使用名称检索算法
  214. 'algorithm': plan.algorithm.name,
  215. })
  216. else:
  217. response.append({
  218. 'id': plan.id,
  219. 'parent': None,
  220. # 使用名称检索算法
  221. 'algorithm': None,
  222. })
  223. return success(data=response)
  224. def post(self, request):
  225. user = request.user
  226. plans = request.data.get('plans')
  227. overWritePlans = False
  228. try:
  229. mission = Mission.objects.get(id=request.data.get('mission'))
  230. except Mission.DoesNotExist:
  231. logger.error("处理规划所属任务不存在")
  232. return failed(message="未找到规划任务所属任务")
  233. # 重复提交同一Mission的规划将覆盖
  234. if mission.own_plans.exists():
  235. overWritePlans = True
  236. for plan in mission.own_plans.all():
  237. plan.delete()
  238. # 全部放入矩阵
  239. planMat = [[{}, {}, {}] for i in range(10)]
  240. # 找出所有并行的源头
  241. origins = []
  242. for plan in plans:
  243. if plan['row'] == 0:
  244. origins.append(plan)
  245. planMat[plan['row']][plan['col']] = plan
  246. rootPlan = Plan(mission=mission, parent=None, algorithm=None, user=user)
  247. rootPlan.save()
  248. originPlans = []
  249. # 将新建的plan的id都要返回
  250. createdPlans = []
  251. # 添加第一代规划
  252. for pJson in origins:
  253. # p.algorithm应该用来新建plan,先占位
  254. # childPlan = Plan(mission=mission, parent=rootPlan, algorithm=p['algorithm'], user=user)
  255. try:
  256. algorithm = Algorithm.objects.get(name=pJson['algorithm'])
  257. except Algorithm.DoesNotExist:
  258. logger.error("传递算法不存在")
  259. return failed(message=str(pJson['id']) + "规划选定算法不存在")
  260. pModel = Plan(mission=mission, parent=rootPlan, algorithm=algorithm, user=user)
  261. pModel.save()
  262. createdPlans.append({
  263. 'id': pModel.id,
  264. 'col': pJson['col'],
  265. 'row': pJson['row'],
  266. })
  267. # p是json数据,headPlan是保存的PlanModel
  268. originPlans.append([pJson, pModel])
  269. # 添加后续规划
  270. while originPlans:
  271. pJson, pModel = originPlans.pop()
  272. for childPos in pJson['children']:
  273. childJson = planMat[childPos['row']][childPos['col']]
  274. try:
  275. algorithm = Algorithm.objects.get(name=childJson['algorithm'])
  276. except Algorithm.DoesNotExist:
  277. logger.error("传递算法不存在")
  278. return failed(message=str(pJson['id']) + "规划选定算法不存在")
  279. childModel = Plan(mission=mission, parent=pModel, algorithm=algorithm, user=user)
  280. childModel.save()
  281. # 将新建的plan加入列表
  282. createdPlans.append({
  283. 'id': childModel.id,
  284. 'col': childPos['col'],
  285. 'row': childPos['row'],
  286. 'parentId': pModel.id,
  287. })
  288. originPlans.append([childJson, childModel])
  289. if overWritePlans:
  290. return success(message="规划已覆盖", data=createdPlans)
  291. else:
  292. return success(message="规划已提交", data=createdPlans)
  293. class MissionsAPI(APIView):
  294. def get(self, request):
  295. user = request.user
  296. missions = Mission.objects.filter(user=user).all()
  297. response = []
  298. for mission in missions:
  299. nodesInfo = mission.nodeFile.own_file_info
  300. edgesInfo = mission.edgeFile.own_file_info
  301. response.append({
  302. 'id': mission.id,
  303. 'name': mission.name,
  304. 'createTime': mission.create_time,
  305. 'nodesInfo': {
  306. 'S': nodesInfo.sNodes,
  307. 'D': nodesInfo.dNodes,
  308. 'I': nodesInfo.iNodes,
  309. },
  310. 'edgesInfo': {
  311. 'num': edgesInfo.edges,
  312. },
  313. # 防止近义词错误
  314. 'status': mission.state,
  315. 'state': mission.state,
  316. })
  317. return success(data=response)