6
0

WebServer.py 13 KB

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