WebServer.py 13 KB

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