web_service.py 5.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149
  1. #!/usr/bin/env python3
  2. import subprocess
  3. import uuid
  4. from flask import Flask, request, jsonify, Response, redirect
  5. from concurrent.futures import ProcessPoolExecutor
  6. import sqlite3
  7. import os
  8. import time
  9. app = Flask(__name__)
  10. PATH_DB = "db.sqlite"
  11. PATH_RESULTS = "web_service/results/"
  12. SQL_INSERT_REQUEST = "INSERT INTO requests ('id', 'username', 'depth', 'width', 'int_cycles', 'ext_cycles', 'status') VALUES ('{request_id}', '{username}', '{depth}', '{width}', '{int_cycles}', '{ext_cycles}', '{status}');"
  13. SQL_SELECT_USERNAME = "SELECT username FROM users WHERE api_key='{api_key}'"
  14. SQL_SELECT_REQUEST_STATUS = "SELECT status FROM requests WHERE id='{request_id}'"
  15. SQL_UPDATE_STATUS = "UPDATE requests SET status='{status}' WHERE id='{request_id}'"
  16. CMD_DEVSTONE_COMPARATIVE = "python3 devstone_comparative.py -w {width} -d {depth} -n {num_rep} -i {int_cycles} -e {ext_cycles} -o {out_file}"
  17. executor = ProcessPoolExecutor()
  18. def get_username_from_api_key(api_key):
  19. conn = sqlite3.connect(PATH_DB)
  20. c = conn.cursor()
  21. sql = SQL_SELECT_USERNAME.format(api_key=api_key)
  22. res = c.execute(sql).fetchall()
  23. conn.close()
  24. return res[0][0] if res else None
  25. def get_request_status(request_id):
  26. conn = sqlite3.connect(PATH_DB)
  27. c = conn.cursor()
  28. sql = SQL_SELECT_REQUEST_STATUS.format(request_id=request_id)
  29. res = c.execute(sql).fetchall()
  30. conn.close()
  31. return res[0][0] if res else None
  32. def run_devstone_task(request_id, username, model_types, depth, width, int_cycles, ext_cycles, num_rep):
  33. # TODO: here it would be the devstone comparative script call
  34. out_file = PATH_RESULTS + "%s.csv" % request_id
  35. cmd = CMD_DEVSTONE_COMPARATIVE.format(depth=depth, width=width, int_cycles=int_cycles, ext_cycles=ext_cycles, num_rep=num_rep, out_file=out_file)
  36. result = subprocess.run(cmd.split(), stdout=subprocess.PIPE)
  37. with open("cmd_outputs.log", "a") as out_file:
  38. out_file.write("(%s): %s" % (username, cmd))
  39. out_file.write(str(result.stdout))
  40. conn = sqlite3.connect(PATH_DB)
  41. sql = SQL_UPDATE_STATUS.format(request_id=request_id, status="finished")
  42. c = conn.cursor()
  43. c.execute(sql)
  44. conn.commit()
  45. conn.close()
  46. @app.route('/')
  47. def hello():
  48. return 'Hey there! Ready to execute some DEVStone models?'
  49. @app.route('/run_devstone')
  50. def run_devstone():
  51. try:
  52. api_key = request.args["api_key"]
  53. except Exception as e:
  54. return jsonify({"status": "denied", "reason": "No api_key was specified."})
  55. try:
  56. depth = int(request.args["depth"])
  57. width = int(request.args["width"])
  58. int_cycles = int(request.args["int_cycles"]) if "int_cycles" in request.args else 0
  59. ext_cycles = int(request.args["ext_cycles"]) if "ext_cycles" in request.args else 0
  60. model_types = request.args["model_types"] if "model_types" in request.args else "LI,HI,HO,HOmod"
  61. num_rep = int(request.args["num_rep"]) if "num_rep" in request.args else 10
  62. except Exception as e:
  63. return jsonify({"status": "denied", "reason": "Invalid params."})
  64. # Check that api key exists
  65. username = get_username_from_api_key(api_key)
  66. if username is None:
  67. return jsonify({"status": "denied", "reason": "Invalid api key."})
  68. request_id = uuid.uuid4().hex
  69. # Insert task into DB
  70. conn = sqlite3.connect(PATH_DB)
  71. c = conn.cursor()
  72. sql = SQL_INSERT_REQUEST.format(request_id=request_id, username=username, depth=depth, width=width,
  73. int_cycles=int_cycles, ext_cycles=ext_cycles, status="pending")
  74. c.execute(sql)
  75. conn.commit()
  76. conn.close()
  77. # Execute task
  78. executor.submit(run_devstone_task, request_id, username, model_types, depth, width, int_cycles, ext_cycles, num_rep)
  79. # Return response
  80. status_url = request.host_url + "status?request_id=" + request_id
  81. return jsonify({"status": "submitted", "request_id": request_id, "status_url": status_url})
  82. @app.route('/status')
  83. def status():
  84. try:
  85. request_id = request.args["request_id"]
  86. except Exception as e:
  87. return jsonify({"status": "denied", "reason": "No request_id was specified."})
  88. status = get_request_status(request_id)
  89. res = {"status": status, "request_id": request_id}
  90. if status == "finished":
  91. download_url = request.host_url + "download?request_id=" + request_id
  92. res["download_link"] = download_url
  93. return jsonify(res)
  94. @app.route('/download')
  95. def download():
  96. try:
  97. request_id = request.args["request_id"]
  98. except Exception as e:
  99. return jsonify({"status": "denied", "reason": "No request_id was specified."})
  100. status = get_request_status(request_id)
  101. if status != "finished":
  102. status_url = request.host_url + "status?request_id=" + request_id
  103. return redirect(status_url)
  104. with open(PATH_RESULTS + "%s.csv" % request_id) as f:
  105. csv = f.read()
  106. return Response(
  107. csv,
  108. mimetype="text/csv",
  109. headers={"Content-disposition":
  110. "attachment; filename=%s.csv" % request_id})
  111. if __name__ == '__main__':
  112. if not os.path.exists('results'):
  113. os.makedirs('results')
  114. app.run(debug=True, host="0.0.0.0", port=8080)