WebServer.py 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331
  1. import eventlet
  2. import os
  3. import socketio
  4. from glob import glob
  5. from os import getcwd
  6. from os import path
  7. from os.path import exists
  8. from flask import Flask
  9. from flask import send_from_directory
  10. from pycs import app
  11. from pycs.database.Model import Model
  12. from pycs.database.LabelProvider import LabelProvider
  13. from pycs.frontend.endpoints.ListJobs import ListJobs
  14. from pycs.frontend.endpoints.ListLabelProviders import ListLabelProviders
  15. from pycs.frontend.endpoints.ListModels import ListModels
  16. from pycs.frontend.endpoints.ListProjects import ListProjects
  17. from pycs.frontend.endpoints.additional.FolderInformation import FolderInformation
  18. from pycs.frontend.endpoints.data.GetCroppedFile import GetCroppedFile
  19. from pycs.frontend.endpoints.data.GetFile import GetFile
  20. from pycs.frontend.endpoints.data.GetPreviousAndNextFile import GetPreviousAndNextFile
  21. from pycs.frontend.endpoints.data.GetResizedFile import GetResizedFile
  22. from pycs.frontend.endpoints.data.RemoveFile import RemoveFile
  23. from pycs.frontend.endpoints.data.UploadFile import UploadFile
  24. from pycs.frontend.endpoints.jobs.RemoveJob import RemoveJob
  25. from pycs.frontend.endpoints.labels.CreateLabel import CreateLabel
  26. from pycs.frontend.endpoints.labels.EditLabelName import EditLabelName
  27. from pycs.frontend.endpoints.labels.EditLabelParent import EditLabelParent
  28. from pycs.frontend.endpoints.labels.ListLabelTree import ListLabelTree
  29. from pycs.frontend.endpoints.labels.ListLabels import ListLabels
  30. from pycs.frontend.endpoints.labels.RemoveLabel import RemoveLabel
  31. from pycs.frontend.endpoints.pipelines.FitModel import FitModel
  32. from pycs.frontend.endpoints.pipelines.PredictFile import PredictFile
  33. from pycs.frontend.endpoints.pipelines.PredictModel import PredictModel
  34. from pycs.frontend.endpoints.projects.CreateProject import CreateProject
  35. from pycs.frontend.endpoints.projects.EditProjectDescription import EditProjectDescription
  36. from pycs.frontend.endpoints.projects.EditProjectName import EditProjectName
  37. from pycs.frontend.endpoints.projects.ExecuteExternalStorage import ExecuteExternalStorage
  38. from pycs.frontend.endpoints.projects.ExecuteLabelProvider import ExecuteLabelProvider
  39. from pycs.frontend.endpoints.projects.GetProjectModel import GetProjectModel
  40. from pycs.frontend.endpoints.projects.ListProjectCollections import ListProjectCollections
  41. from pycs.frontend.endpoints.projects.ListProjectFiles import ListProjectFiles
  42. from pycs.frontend.endpoints.projects.RemoveProject import RemoveProject
  43. from pycs.frontend.endpoints.results.ConfirmResult import ConfirmResult
  44. from pycs.frontend.endpoints.results.CreateResult import CreateResult
  45. from pycs.frontend.endpoints.results.EditResultData import EditResultData
  46. from pycs.frontend.endpoints.results.EditResultLabel import EditResultLabel
  47. from pycs.frontend.endpoints.results.GetProjectResults import GetProjectResults
  48. from pycs.frontend.endpoints.results.GetResults import GetResults
  49. from pycs.frontend.endpoints.results.RemoveResult import RemoveResult
  50. from pycs.frontend.endpoints.results.ResetResults import ResetResults
  51. from pycs.frontend.notifications.NotificationManager import NotificationManager
  52. from pycs.frontend.util.JSONEncoder import JSONEncoder
  53. from pycs.jobs.JobRunner import JobRunner
  54. from pycs.util.PipelineCache import PipelineCache
  55. class WebServer:
  56. """
  57. wrapper class for flask and socket.io which initializes most networking
  58. """
  59. # pylint: disable=line-too-long
  60. # pylint: disable=too-many-statements
  61. def __init__(self, app, settings: dict):
  62. PRODUCTION = os.path.exists('webui/index.html')
  63. # initialize web server
  64. if PRODUCTION:
  65. print('production build')
  66. # find static files and folders
  67. static_files = {}
  68. for file_path in glob('webui/*'):
  69. file_path = file_path.replace('\\', '/')
  70. static_files[file_path[5:]] = file_path
  71. # separately add svg files and set their correct mime type
  72. for svg_path in glob('webui/img/*.svg'):
  73. svg_path = svg_path.replace('\\', '/')
  74. static_files[svg_path[5:]] = {'content_type': 'image/svg+xml', 'filename': svg_path}
  75. # create service objects
  76. if len(settings['allowedOrigins']) > 0:
  77. origins = settings['allowedOrigins']
  78. self.__sio = socketio.Server(cors_allowed_origins=origins, async_mode='eventlet')
  79. else:
  80. self.__sio = socketio.Server(async_mode='eventlet')
  81. self.__app = socketio.WSGIApp(self.__sio, app, static_files=static_files)
  82. # overwrite root path to serve index.html
  83. @app.route('/', methods=['GET'])
  84. def index():
  85. # pylint: disable=unused-variable
  86. return send_from_directory(path.join(getcwd(), 'webui'), 'index.html')
  87. else:
  88. print('development build')
  89. # create service objects
  90. self.__sio = socketio.Server(cors_allowed_origins='*', async_mode='eventlet')
  91. self.app = app
  92. self.wsgi_app = socketio.WSGIApp(self.__sio, app)
  93. # set access control header to allow requests from Vue.js development server
  94. @self.app.after_request
  95. def after_request(response):
  96. # pylint: disable=unused-variable
  97. response.headers['Access-Control-Allow-Origin'] = '*'
  98. return response
  99. # set json encoder so database objects are serialized correctly
  100. self.app.json_encoder = JSONEncoder
  101. self.host = settings.host
  102. self.port = settings.port
  103. # create notification manager
  104. jobs = JobRunner()
  105. pipelines = PipelineCache(jobs)
  106. notifications = NotificationManager(self.__sio)
  107. jobs.on_create(notifications.create_job)
  108. jobs.on_start(notifications.edit_job)
  109. jobs.on_progress(notifications.edit_job)
  110. jobs.on_finish(notifications.edit_job)
  111. jobs.on_remove(notifications.remove_job)
  112. self.define_routes(jobs, notifications, pipelines)
  113. Model.discover("models/")
  114. LabelProvider.discover("labels/")
  115. def define_routes(self, jobs, notifications, pipelines):
  116. # additional
  117. self.app.add_url_rule(
  118. '/folder',
  119. view_func=FolderInformation.as_view('folder_information')
  120. )
  121. # jobs
  122. self.app.add_url_rule(
  123. '/jobs',
  124. view_func=ListJobs.as_view('list_jobs', jobs)
  125. )
  126. self.app.add_url_rule(
  127. '/jobs/<int:job_id>/remove',
  128. view_func=RemoveJob.as_view('remove_job', jobs)
  129. )
  130. # models
  131. self.app.add_url_rule(
  132. '/models',
  133. view_func=ListModels.as_view('list_models')
  134. )
  135. self.app.add_url_rule(
  136. '/projects/<int:project_id>/model',
  137. view_func=GetProjectModel.as_view('get_project_model')
  138. )
  139. # labels
  140. self.app.add_url_rule(
  141. '/label_providers',
  142. view_func=ListLabelProviders.as_view('label_providers')
  143. )
  144. self.app.add_url_rule(
  145. '/projects/<int:project_id>/labels',
  146. view_func=ListLabels.as_view('list_labels')
  147. )
  148. self.app.add_url_rule(
  149. '/projects/<int:project_id>/labels/tree',
  150. view_func=ListLabelTree.as_view('list_label_tree')
  151. )
  152. self.app.add_url_rule(
  153. '/projects/<int:project_id>/labels',
  154. view_func=CreateLabel.as_view('create_label', notifications)
  155. )
  156. self.app.add_url_rule(
  157. '/projects/<int:project_id>/labels/<int:label_id>/remove',
  158. view_func=RemoveLabel.as_view('remove_label', notifications)
  159. )
  160. self.app.add_url_rule(
  161. '/projects/<int:project_id>/labels/<int:label_id>/name',
  162. view_func=EditLabelName.as_view('edit_label_name', notifications)
  163. )
  164. self.app.add_url_rule(
  165. '/projects/<int:project_id>/labels/<int:label_id>/parent',
  166. view_func=EditLabelParent.as_view('edit_label_parent', notifications)
  167. )
  168. # collections
  169. self.app.add_url_rule(
  170. '/projects/<int:project_id>/collections',
  171. view_func=ListProjectCollections.as_view('list_collections')
  172. )
  173. self.app.add_url_rule(
  174. '/projects/<int:project_id>/data/<int:collection_id>/<int:start>/<int:length>',
  175. view_func=ListProjectFiles.as_view('list_collection_files')
  176. )
  177. # data
  178. self.app.add_url_rule(
  179. '/projects/<int:project_id>/data',
  180. view_func=UploadFile.as_view('upload_file', notifications)
  181. )
  182. self.app.add_url_rule(
  183. '/projects/<int:project_id>/data/<int:start>/<int:length>',
  184. view_func=ListProjectFiles.as_view('list_files')
  185. )
  186. self.app.add_url_rule(
  187. '/data/<int:file_id>/remove',
  188. view_func=RemoveFile.as_view('remove_file', notifications)
  189. )
  190. self.app.add_url_rule(
  191. '/data/<int:file_id>',
  192. view_func=GetFile.as_view('get_file')
  193. )
  194. self.app.add_url_rule(
  195. '/data/<int:file_id>/<resolution>',
  196. view_func=GetResizedFile.as_view('get_resized_file')
  197. )
  198. self.app.add_url_rule(
  199. '/data/<int:file_id>/<resolution>/<crop_box>',
  200. view_func=GetCroppedFile.as_view('crop_result', database)
  201. )
  202. self.app.add_url_rule(
  203. '/data/<int:file_id>/previous_next',
  204. view_func=GetPreviousAndNextFile.as_view('get_previous_and_next_file')
  205. )
  206. # results
  207. self.app.add_url_rule(
  208. '/projects/<int:project_id>/results',
  209. view_func=GetProjectResults.as_view('get_project_results')
  210. )
  211. self.app.add_url_rule(
  212. '/data/<int:file_id>/results',
  213. view_func=GetResults.as_view('get_results')
  214. )
  215. self.app.add_url_rule(
  216. '/data/<int:file_id>/results',
  217. view_func=CreateResult.as_view('create_result', notifications)
  218. )
  219. self.app.add_url_rule(
  220. '/data/<int:file_id>/reset',
  221. view_func=ResetResults.as_view('reset_results', notifications)
  222. )
  223. self.app.add_url_rule(
  224. '/results/<int:result_id>/remove',
  225. view_func=RemoveResult.as_view('remove_result', notifications)
  226. )
  227. self.app.add_url_rule(
  228. '/results/<int:result_id>/confirm',
  229. view_func=ConfirmResult.as_view('confirm_result', notifications)
  230. )
  231. self.app.add_url_rule(
  232. '/results/<int:result_id>/crop',
  233. view_func=ResultAsCrop.as_view('crop_result')
  234. )
  235. self.app.add_url_rule(
  236. '/results/<int:result_id>/crop/<int:max_width>',
  237. view_func=ResultAsCrop.as_view('crop_result_resized_by_width')
  238. )
  239. self.app.add_url_rule(
  240. '/results/<int:result_id>/crop/<int:max_width>x<int:max_height>',
  241. view_func=ResultAsCrop.as_view('crop_result_resized')
  242. )
  243. self.app.add_url_rule(
  244. '/results/<int:result_id>/label',
  245. view_func=EditResultLabel.as_view('edit_result_label', notifications)
  246. )
  247. self.app.add_url_rule(
  248. '/results/<int:result_id>/data',
  249. view_func=EditResultData.as_view('edit_result_data', notifications)
  250. )
  251. # projects
  252. self.app.add_url_rule(
  253. '/projects',
  254. view_func=ListProjects.as_view('list_projects')
  255. )
  256. self.app.add_url_rule(
  257. '/projects',
  258. view_func=CreateProject.as_view('create_project', notifications, jobs)
  259. )
  260. self.app.add_url_rule(
  261. '/projects/<int:project_id>/label_provider',
  262. view_func=ExecuteLabelProvider.as_view('execute_label_provider', notifications, jobs)
  263. )
  264. self.app.add_url_rule(
  265. '/projects/<int:project_id>/external_storage',
  266. view_func=ExecuteExternalStorage.as_view('execute_external_storage', notifications, jobs)
  267. )
  268. self.app.add_url_rule(
  269. '/projects/<int:project_id>/remove',
  270. view_func=RemoveProject.as_view('remove_project', notifications)
  271. )
  272. self.app.add_url_rule(
  273. '/projects/<int:project_id>/name',
  274. view_func=EditProjectName.as_view('edit_project_name', notifications)
  275. )
  276. self.app.add_url_rule(
  277. '/projects/<int:project_id>/description',
  278. view_func=EditProjectDescription.as_view('edit_project_description', notifications)
  279. )
  280. # pipelines
  281. self.app.add_url_rule(
  282. '/projects/<int:project_id>/pipelines/fit',
  283. view_func=FitModel.as_view('fit_model', jobs, pipelines)
  284. )
  285. self.app.add_url_rule(
  286. '/projects/<int:project_id>/pipelines/predict',
  287. view_func=PredictModel.as_view('predict_model', notifications, jobs,
  288. pipelines)
  289. )
  290. self.app.add_url_rule(
  291. '/data/<int:file_id>/predict',
  292. view_func=PredictFile.as_view('predict_file', notifications, jobs, pipelines)
  293. )
  294. def run(self):
  295. # finally start web server
  296. eventlet.wsgi.server(eventlet.listen((self.host, self.port)), self.wsgi_app)