api_prepare.py 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464
  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. missionName = request.data.get('missionName')
  27. missionDescription = request.data.get('missionDescription')
  28. nodeFileName = request.data.get('nodeFileName')
  29. edgeFileName = request.data.get('edgeFileName')
  30. nodeFile = request.data.get('nodes')
  31. edgeFile = request.data.get('edges')
  32. # 检查文件用途并进行响应处理
  33. file_usage = request.data.get('usage')
  34. if file_usage == 'input':
  35. pass
  36. else:
  37. return failed(message=filename + "上传失败 暂时只支持上传图处理原料文件")
  38. # 检查文件类型并进行相应处理
  39. file_type = request.data.get('type')
  40. for filename in [nodeFileName, edgeFileName]:
  41. if file_type == 'csv':
  42. if filename.split('.')[-1] != 'csv':
  43. return failed(message=filename + "上传失败 文件类型和文件后缀名不匹配")
  44. else:
  45. return failed(message=filename + "上传失败 暂不支持CSV之外文件")
  46. successUploadedFiles = []
  47. pre_file = None
  48. for filename, fileData in [[edgeFileName, edgeFile], [nodeFileName, nodeFile]]:
  49. # 处理数据库中记录
  50. file = File()
  51. file.name = filename
  52. file.user = request.user
  53. file.type = file_type
  54. file.usage = file_usage
  55. # 一定先edge再node
  56. if pre_file == None:
  57. file.content = 'edge'
  58. else:
  59. file.content = 'node'
  60. file.save()
  61. # 尝试保存文件
  62. result = file.storage(fileData)
  63. if result != OK:
  64. file.delete()
  65. # 第二个文件错误,同样删除第一个文件
  66. if pre_file:
  67. pre_file.delete()
  68. if result == FILE_ALREADY_EXIST:
  69. return failed(message=filename + "上传失败 已存在同名文件", data="已存在同名文件", code=400)
  70. elif result == FILE_FAILED_CREATE_DIR:
  71. return failed(message=filename + "上传失败 文件目录创建失败", data="文件目录创建失败", code=400)
  72. else:
  73. return failed(message=filename + "上传失败 因未知原因文件上传失败", data="因未知原因文件上传失败", code=400)
  74. # 第二个文件也成功时,将两个文件互相关联
  75. if pre_file:
  76. file.associate = pre_file
  77. pre_file.associate = file
  78. file.saveWithInfo()
  79. pre_file.saveWithInfo()
  80. else:
  81. pre_file = file
  82. # 两个文件均上传成功后,检测文件内容是否符合规范
  83. if file.checkIllegal() and pre_file.checkIllegal():
  84. # 确保正确后创建mission,并将信息返回
  85. mission = Mission()
  86. mission.nodeFile = file
  87. mission.edgeFile = pre_file
  88. mission.user = user
  89. if missionName:
  90. mission.name = missionName
  91. if missionDescription:
  92. mission.description = missionDescription
  93. mission.save()
  94. successUploadedFiles.append({
  95. "id": mission.id,
  96. "name": mission.name,
  97. # 防止近义词错误
  98. "state": mission.state,
  99. "status": mission.state,
  100. "content": "mission",
  101. })
  102. # 初步对文件内容进行分析,获取节点和边的基本数据
  103. for file in [file, pre_file]:
  104. successUploadedFiles.append({
  105. "id": file.id,
  106. "name": file.name,
  107. "content": file.content,
  108. "nodes": file.own_file_info.nodes,
  109. "sNodes": file.own_file_info.sNodes,
  110. "dNodes": file.own_file_info.dNodes,
  111. "iNodes": file.own_file_info.iNodes,
  112. "edges": file.own_file_info.edges,
  113. })
  114. return success(message="文件上传成功", data=successUploadedFiles, code=200)
  115. else:
  116. return failed(message="文件合法性检查失败")
  117. def delete(self, request):
  118. user = request.user
  119. file = File.objects.get(id=request.data.get('id'))
  120. result = file.deleteStorage()
  121. if result == OK:
  122. return success(message="删除文件成功")
  123. elif result == False:
  124. return failed(messgae="删除文件失败")
  125. class InputFileAPI(APIView):
  126. def post(self, request):
  127. user = request.user
  128. missionName = request.data.get('missionName')
  129. missionDescription = request.data.get('missionDescription')
  130. nodes = request.data.get('nodes')
  131. edges = request.data.get('edges')
  132. logger.info(nodes)
  133. logger.info(edges)
  134. # 保存节点文件
  135. # 当前设置输入的节点信息仅包含一个名称
  136. nodesFile = File.objects.create(type='csv', usage='input', user=user, content='node')
  137. try:
  138. nodesCsvList = []
  139. for node in nodes:
  140. nodeList = [int(node['id']), str(node['type'])]
  141. for key in [k for k in node if k not in ['id', 'type']]:
  142. # 检测是否有值,前端为输入节点名称时会有空值
  143. if node[key]:
  144. nodeList.append(str(key) + ':' +str(node[key]))
  145. nodesCsvList.append(nodeList)
  146. logger.info(nodesCsvList)
  147. nodesFile.generate(nodesCsvList)
  148. except Exception:
  149. # 回退操作
  150. logger.error(f"从输入信息创建节点文件失败 Error:{Exception}")
  151. nodesFile.delete()
  152. # 保存边文件
  153. edgesFile = File.objects.create(type='csv', usage='input', user=user, content='edge')
  154. try:
  155. edgesCsvList = []
  156. for edge in edges:
  157. # 边中应至少有source和target两个参数
  158. edgeList = [int(edge['source']), int(edge['target'])]
  159. for key in [k for k in edge if k not in ['source', 'target']]:
  160. edgeList.append(str(key) + ':' + str(edge[key]))
  161. edgesCsvList.append(edgeList)
  162. logger.info(edgesCsvList)
  163. edgesFile.generate(edgesCsvList)
  164. except Exception:
  165. # 回退操作
  166. logger.error(f"从输入信息创建边文件失败 Error:{Exception}")
  167. nodesFile.delete()
  168. edgesFile.delete()
  169. # 节点和边均保存成功,可以互相关联并创建mission
  170. nodesFile.associate = edgesFile
  171. edgesFile.associate = nodesFile
  172. # 计算节点和边的信息,生成fileInfo
  173. nodesFile.saveWithInfo()
  174. edgesFile.saveWithInfo()
  175. # 构造mission并返回完整信息
  176. successUploadedFiles = []
  177. if nodesFile.checkIllegal() and edgesFile.checkIllegal():
  178. # 确保正确后创建mission,并将信息返回
  179. mission = Mission()
  180. mission.nodeFile = nodesFile
  181. mission.edgeFile = edgesFile
  182. mission.user = user
  183. mission.state = 'init'
  184. if missionName != '':
  185. mission.name = missionName
  186. if missionDescription != '':
  187. mission.description = missionDescription
  188. mission.save()
  189. successUploadedFiles.append({
  190. "id": mission.id,
  191. "name": mission.name,
  192. "content": "mission",
  193. })
  194. # 初步对文件内容进行分析,获取节点和边的基本数据
  195. for file in [nodesFile, edgesFile]:
  196. successUploadedFiles.append({
  197. "id": file.id,
  198. "name": file.name,
  199. "content": file.content,
  200. "nodes": file.own_file_info.nodes,
  201. "sNodes": file.own_file_info.sNodes,
  202. "dNodes": file.own_file_info.dNodes,
  203. "iNodes": file.own_file_info.iNodes,
  204. "edges": file.own_file_info.edges,
  205. })
  206. return success(message="文件上传成功", data=successUploadedFiles, code=200)
  207. else:
  208. logger.error("节点和边文件的合法性检测未通过")
  209. nodesFile.delete()
  210. edgesFile.delete()
  211. return failed(message="节点和边信息输入失败,节点或边信息未通过合法性检测")
  212. class DownloadFileAPI(APIView):
  213. def get(self, request):
  214. user = request.user
  215. fileId = request.GET.get('fileId')
  216. try:
  217. file = File.objects.get(id=fileId)
  218. except File.DoesNotExist:
  219. return failed(message="文件不存在")
  220. if file.user != user:
  221. return failed(message="非本用户上传文件")
  222. if file.encrypted:
  223. return failed(message="请先解密后下载")
  224. response = file.download()
  225. if response:
  226. return response
  227. else:
  228. return failed(message="下载文件失败")
  229. class EncryptFileAPI(APIView):
  230. def get(self, request):
  231. missionId = request.GET.get('missionId')
  232. try:
  233. mission = Mission.objects.get(id=missionId)
  234. except Mission.DoesNotExist:
  235. logger.error(f"解密任务{missionId}失败,未找到该任务")
  236. response = []
  237. if mission.nodeFile.encrypted:
  238. response.append({
  239. 'id': mission.nodeFile.id,
  240. 'content': 'node',
  241. 'name': mission.nodeFile.name,
  242. })
  243. if mission.edgeFile.encrypted:
  244. response.append({
  245. 'id': mission.edgeFile.id,
  246. 'content': 'edge',
  247. 'name': mission.nodeFile.name,
  248. })
  249. return success(data=response)
  250. def post(self, request):
  251. user = request.user
  252. password = request.data.get('password')
  253. fileId = request.data.get('fileId')
  254. action = request.data.get('action')
  255. try:
  256. file = File.objects.get(id=fileId)
  257. except File.DoesNotExist:
  258. return failed(message="文件不存在")
  259. if file.user != user:
  260. return failed(message="非本用户上传文件")
  261. if action == 'encrypt':
  262. # 加密
  263. if not password:
  264. return failed(message="未提供加密密钥")
  265. if file.encrypt(password):
  266. return success(message="加密成功")
  267. else:
  268. return failed(message="加密失败")
  269. elif action == 'decrypt':
  270. # 解密
  271. if not password:
  272. return failed(message="未提供解密密钥")
  273. if file.decrypted(password):
  274. return success(message="解密成功")
  275. else:
  276. return failed(message="密码错误")
  277. elif action == 'verify':
  278. # 验证
  279. if not password:
  280. return failed(message="未提供验证密钥")
  281. if file.verify(password):
  282. # 使用直接赋值方式更新 Session
  283. encrypt_keys = request.session.get('encrypt-keys', {})
  284. encrypt_keys[str(file.id)] = password
  285. request.session['encrypt-keys'] = encrypt_keys # 覆盖赋值
  286. request.session.modified = True # 强制标记为已修改
  287. return success(message="验证成功")
  288. else:
  289. return failed(message="验证失败")
  290. # 不满足以上条件
  291. else:
  292. return failed(message="不支持的文件加、解密行为")
  293. class PlanAPI(APIView):
  294. def get(self, request):
  295. user = request.user
  296. try:
  297. mission = Mission.objects.get(id=request.GET.get('mission'))
  298. except Mission.DoesNotExist:
  299. logger.error(f"处理规划所属任务不存在{request.GET.get('mission')}")
  300. return failed(message="未找到规划任务所属任务")
  301. # 检查mission是否有加密文件,如有加密,则检查sessin中是否有解密密钥
  302. for file in [mission.nodeFile, mission.edgeFile]:
  303. if file.encrypted:
  304. # 如果文件被加密,查找是否有解密密钥,并验证密钥正确性
  305. if not file.verify(request.session.get('encrypt-keys', {})[str(file.id)]):
  306. return failed(message="存在加密文件,且未通过解密验证")
  307. plans = mission.own_plans.all()
  308. response = []
  309. for plan in plans:
  310. if plan.parent:
  311. response.append({
  312. 'id': plan.id,
  313. 'parent': plan.parent.id,
  314. # 使用名称检索算法
  315. 'algorithm': plan.algorithm.name,
  316. })
  317. else:
  318. response.append({
  319. 'id': plan.id,
  320. 'parent': None,
  321. # 使用名称检索算法
  322. 'algorithm': None,
  323. })
  324. return success(data=response)
  325. def post(self, request):
  326. user = request.user
  327. plans = request.data.get('plans')
  328. overWritePlans = False
  329. try:
  330. mission = Mission.objects.get(id=request.data.get('mission'))
  331. except Mission.DoesNotExist:
  332. logger.error("处理规划所属任务不存在")
  333. return failed(message="未找到规划任务所属任务")
  334. # 重复提交同一Mission的规划将覆盖
  335. if mission.own_plans.exists():
  336. overWritePlans = True
  337. for plan in mission.own_plans.all():
  338. plan.delete()
  339. # 全部放入矩阵
  340. planMat = [[{}, {}, {}] for i in range(10)]
  341. # 找出所有并行的源头
  342. origins = []
  343. for plan in plans:
  344. if plan['row'] == 0:
  345. origins.append(plan)
  346. planMat[plan['row']][plan['col']] = plan
  347. rootPlan = Plan(mission=mission, parent=None, algorithm=None, user=user)
  348. rootPlan.save()
  349. originPlans = []
  350. # 将新建的plan的id都要返回
  351. createdPlans = []
  352. # 添加第一代规划
  353. for pJson in origins:
  354. # p.algorithm应该用来新建plan,先占位
  355. # childPlan = Plan(mission=mission, parent=rootPlan, algorithm=p['algorithm'], user=user)
  356. try:
  357. algorithm = Algorithm.objects.get(name=pJson['algorithm'])
  358. except Algorithm.DoesNotExist:
  359. logger.error("传递算法不存在")
  360. return failed(message=str(pJson['id']) + "规划选定算法不存在")
  361. pModel = Plan(mission=mission, parent=rootPlan, algorithm=algorithm, user=user)
  362. pModel.save()
  363. createdPlans.append({
  364. 'id': pModel.id,
  365. 'col': pJson['col'],
  366. 'row': pJson['row'],
  367. })
  368. # p是json数据,headPlan是保存的PlanModel
  369. originPlans.append([pJson, pModel])
  370. # 添加后续规划
  371. while originPlans:
  372. pJson, pModel = originPlans.pop()
  373. for childPos in pJson['children']:
  374. childJson = planMat[childPos['row']][childPos['col']]
  375. try:
  376. algorithm = Algorithm.objects.get(name=childJson['algorithm'])
  377. except Algorithm.DoesNotExist:
  378. logger.error("传递算法不存在")
  379. return failed(message=str(pJson['id']) + "规划选定算法不存在")
  380. childModel = Plan(mission=mission, parent=pModel, algorithm=algorithm, user=user)
  381. childModel.save()
  382. # 将新建的plan加入列表
  383. createdPlans.append({
  384. 'id': childModel.id,
  385. 'col': childPos['col'],
  386. 'row': childPos['row'],
  387. 'parentId': pModel.id,
  388. })
  389. originPlans.append([childJson, childModel])
  390. if overWritePlans:
  391. return success(message="规划已覆盖", data=createdPlans)
  392. else:
  393. return success(message="规划已提交", data=createdPlans)
  394. class MissionsAPI(APIView):
  395. def get(self, request):
  396. user = request.user
  397. missions = Mission.objects.filter(user=user).all()
  398. response = []
  399. for mission in missions:
  400. nodesInfo = mission.nodeFile.own_file_info
  401. edgesInfo = mission.edgeFile.own_file_info
  402. encrpted = mission.nodeFile.encrypted or mission.edgeFile.encrypted
  403. response.append({
  404. 'id': mission.id,
  405. 'name': mission.name,
  406. 'description': mission.description,
  407. 'createTime': mission.create_time,
  408. 'encrypted': encrpted,
  409. 'nodesInfo': {
  410. 'S': nodesInfo.sNodes,
  411. 'D': nodesInfo.dNodes,
  412. 'I': nodesInfo.iNodes,
  413. },
  414. 'edgesInfo': {
  415. 'num': edgesInfo.edges,
  416. },
  417. # 防止近义词错误
  418. 'status': mission.state,
  419. 'state': mission.state,
  420. })
  421. return success(data=response)
  422. class MissionStatusAPI(APIView):
  423. def get(self, request):
  424. user = request.user
  425. missionId = int(request.GET.get('missionId'))
  426. try:
  427. mission = Mission.objects.get(id=missionId)
  428. except Mission.DoesNotExist:
  429. logger.error(f"获取任务状态失败,任务{missionId}不存在")
  430. return failed(message="获取任务状态失败,任务不存在")
  431. return success(data={'status': mission.state})