import os import glob import threading from logging.config import dictConfig from flask import send_from_directory from flask_socketio import SocketIO from logging import config from pycs import app from pycs.database.Database import Database from pycs.frontend.endpoints.ListJobs import ListJobs from pycs.frontend.endpoints.ListLabelProviders import ListLabelProviders from pycs.frontend.endpoints.ListModels import ListModels from pycs.frontend.endpoints.ListProjects import ListProjects from pycs.frontend.endpoints.additional.FolderInformation import FolderInformation from pycs.frontend.endpoints.data.GetFile import GetFile from pycs.frontend.endpoints.data.GetPreviousAndNextFile import GetPreviousAndNextFile from pycs.frontend.endpoints.data.GetResizedFile import GetResizedFile from pycs.frontend.endpoints.data.RemoveFile import RemoveFile from pycs.frontend.endpoints.data.UploadFile import UploadFile from pycs.frontend.endpoints.jobs.RemoveJob import RemoveJob from pycs.frontend.endpoints.labels.CreateLabel import CreateLabel from pycs.frontend.endpoints.labels.EditLabelName import EditLabelName from pycs.frontend.endpoints.labels.EditLabelParent import EditLabelParent from pycs.frontend.endpoints.labels.ListLabels import ListLabels from pycs.frontend.endpoints.labels.RemoveLabel import RemoveLabel from pycs.frontend.endpoints.pipelines.FitModel import FitModel from pycs.frontend.endpoints.pipelines.PredictFile import PredictFile from pycs.frontend.endpoints.pipelines.PredictModel import PredictModel from pycs.frontend.endpoints.projects.CreateProject import CreateProject from pycs.frontend.endpoints.projects.EditProjectDescription import EditProjectDescription from pycs.frontend.endpoints.projects.EditProjectName import EditProjectName from pycs.frontend.endpoints.projects.ExecuteExternalStorage import ExecuteExternalStorage from pycs.frontend.endpoints.projects.ExecuteLabelProvider import ExecuteLabelProvider from pycs.frontend.endpoints.projects.GetProjectModel import GetProjectModel from pycs.frontend.endpoints.projects.ListCollections import ListCollections from pycs.frontend.endpoints.projects.ListFiles import ListFiles from pycs.frontend.endpoints.projects.RemoveProject import RemoveProject from pycs.frontend.endpoints.results.ConfirmResult import ConfirmResult from pycs.frontend.endpoints.results.CreateResult import CreateResult from pycs.frontend.endpoints.results.EditResultData import EditResultData from pycs.frontend.endpoints.results.EditResultLabel import EditResultLabel from pycs.frontend.endpoints.results.GetProjectResults import GetProjectResults from pycs.frontend.endpoints.results.GetResults import GetResults from pycs.frontend.endpoints.results.RemoveResult import RemoveResult from pycs.frontend.endpoints.results.ResetResults import ResetResults from pycs.frontend.notifications.NotificationManager import NotificationManager from pycs.frontend.util.JSONEncoder import JSONEncoder from pycs.jobs.JobRunner import JobRunner from pycs.util.PipelineCache import PipelineCache class WebServer: """ wrapper class for flask and socket.io which initializes most networking """ # pylint: disable=line-too-long def __init__(self, app, settings: dict): dictConfig(settings["logging"]) # initialize flask app instance self.app = app # initialize database self.db = Database() # start job runner self.logger.info('Starting job runner... ') self.jobs = JobRunner() # create pipeline cache self.logger.info('Creating pipeline cache') self.pipelines = PipelineCache(self.jobs) PRODUCTION = os.path.exists('webui/index.html') init_func = self.production_init if PRODUCTION else self.development_init kwargs, static_files = init_func() self.sio = SocketIO(self.app, **kwargs)#socketio.Server(**kwargs) # self.__app = socketio.WSGIApp(self.sio, self.app, static_files=static_files) self.host, self.port = settings['host'], settings['port'] # set json encoder so database objects are serialized correctly self.app.json_encoder = JSONEncoder self.start_runner() self.init_notifications() self.define_routes() self.logger.info("Server initialized") def start_runner(self): app.logger.info(f"Main Thread ID: {threading.get_ident()}") self.jobs.start() self.pipelines.start() def stop_runner(self): self.jobs.stop() self.pipelines.stop() def wait_for_runner(self): self.jobs.wait_for_empty_queue() self.pipelines.wait_for_empty_queue() @property def logger(self): return self.app.logger def init_notifications(self): # create notification manager self.notifications = n = NotificationManager(self.sio) self.jobs.on_create(n.create_job) self.jobs.on_start(n.edit_job) self.jobs.on_progress(n.edit_job) self.jobs.on_finish(n.edit_job) self.jobs.on_remove(n.remove_job) def development_init(self): self.logger.info('Initializing development build') # set access control header to allow requests from Vue.js development server @self.app.after_request def after_request(response): # pylint: disable=unused-variable response.headers['Access-Control-Allow-Origin'] = '*' return response return dict(cors_allowed_origins='*', async_mode='eventlet'), None def production_init(self): self.logger.info('Initializing production build') kwargs = dict(async_mode='eventlet') if len(settings['allowedOrigins']) > 0: origins = settings['allowedOrigins'] kwargs["cors_allowed_origins"] = origins # overwrite root path to serve index.html @self.app.route('/', methods=['GET']) def index(): # pylint: disable=unused-variable return send_from_directory(os.path.join(os.getcwd(), 'webui'), 'index.html') return kwargs, self.static_files @property def static_files(self) -> dict: # find static files and folders static_files = {} for file_path in glob.glob('webui/*'): file_path = file_path.replace('\\', '/') static_files[file_path[5:]] = file_path # separately add svg files and set their correct mime type for svg_path in glob.glob('webui/img/*.svg'): svg_path = svg_path.replace('\\', '/') static_files[svg_path[5:]] = {'content_type': 'image/svg+xml', 'filename': svg_path} return static_files def define_routes(self): # additional self.app.add_url_rule( '/folder', view_func=FolderInformation.as_view('folder_information') ) # jobs self.app.add_url_rule( '/jobs', view_func=ListJobs.as_view('list_jobs', self.jobs) ) self.app.add_url_rule( '/jobs//remove', view_func=RemoveJob.as_view('remove_job', self.jobs) ) # models self.app.add_url_rule( '/models', view_func=ListModels.as_view('list_models', self.db) ) self.app.add_url_rule( '/projects//model', view_func=GetProjectModel.as_view('get_project_model', self.db) ) # labels self.app.add_url_rule( '/label_providers', view_func=ListLabelProviders.as_view('label_providers', self.db) ) self.app.add_url_rule( '/projects//labels', view_func=ListLabels.as_view('list_labels', self.db) ) self.app.add_url_rule( '/projects//labels', view_func=CreateLabel.as_view('create_label', self.db, self.notifications) ) self.app.add_url_rule( '/projects//labels//remove', view_func=RemoveLabel.as_view('remove_label', self.db, self.notifications) ) self.app.add_url_rule( '/projects//labels//name', view_func=EditLabelName.as_view('edit_label_name', self.db, self.notifications) ) self.app.add_url_rule( '/projects//labels//parent', view_func=EditLabelParent.as_view('edit_label_parent', self.db, self.notifications) ) # collections self.app.add_url_rule( '/projects//collections', view_func=ListCollections.as_view('list_collections', self.db) ) self.app.add_url_rule( '/projects//data///', view_func=ListFiles.as_view('list_collection_files', self.db) ) # data self.app.add_url_rule( '/projects//data', view_func=UploadFile.as_view('upload_file', self.db, self.notifications) ) self.app.add_url_rule( '/projects//data//', view_func=ListFiles.as_view('list_files', self.db) ) self.app.add_url_rule( '/data//remove', view_func=RemoveFile.as_view('remove_file', self.db, self.notifications) ) self.app.add_url_rule( '/data/', view_func=GetFile.as_view('get_file', self.db) ) self.app.add_url_rule( '/data//', view_func=GetResizedFile.as_view('get_resized_file', self.db) ) self.app.add_url_rule( '/data//previous_next', view_func=GetPreviousAndNextFile.as_view('get_previous_and_next_file', self.db) ) # results self.app.add_url_rule( '/projects//results', view_func=GetProjectResults.as_view('get_project_results') ) self.app.add_url_rule( '/data//results', view_func=GetResults.as_view('get_results', self.db) ) self.app.add_url_rule( '/data//results', view_func=CreateResult.as_view('create_result', self.db, self.notifications) ) self.app.add_url_rule( '/data//reset', view_func=ResetResults.as_view('reset_results', self.db, self.notifications) ) self.app.add_url_rule( '/results//remove', view_func=RemoveResult.as_view('remove_result', self.db, self.notifications) ) self.app.add_url_rule( '/results//confirm', view_func=ConfirmResult.as_view('confirm_result', self.db, self.notifications) ) self.app.add_url_rule( '/results//label', view_func=EditResultLabel.as_view('edit_result_label', self.db, self.notifications) ) self.app.add_url_rule( '/results//data', view_func=EditResultData.as_view('edit_result_data', self.db, self.notifications) ) # projects self.app.add_url_rule( '/projects', view_func=ListProjects.as_view('list_projects', self.db) ) self.app.add_url_rule( '/projects', view_func=CreateProject.as_view('create_project', self.db, self.notifications, self.jobs) ) self.app.add_url_rule( '/projects//label_provider', view_func=ExecuteLabelProvider.as_view('execute_label_provider', self.db, self.notifications, self.jobs) ) self.app.add_url_rule( '/projects//external_storage', view_func=ExecuteExternalStorage.as_view('execute_external_storage', self.db, self.notifications, self.jobs) ) self.app.add_url_rule( '/projects//remove', view_func=RemoveProject.as_view('remove_project', self.db, self.notifications) ) self.app.add_url_rule( '/projects//name', view_func=EditProjectName.as_view('edit_project_name', self.db, self.notifications) ) self.app.add_url_rule( '/projects//description', view_func=EditProjectDescription.as_view('edit_project_description', self.db, self.notifications) ) # pipelines self.app.add_url_rule( '/projects//pipelines/fit', view_func=FitModel.as_view('fit_model', self.db, self.jobs, self.pipelines) ) self.app.add_url_rule( '/projects//pipelines/predict', view_func=PredictModel.as_view('predict_model', self.db, self.notifications, self.jobs, self.pipelines) ) self.app.add_url_rule( '/data//predict', view_func=PredictFile.as_view('predict_file', self.db, self.notifications, self.jobs, self.pipelines) ) def run(self): self.logger.info("Starting server...") return self.sio.run(self.app, host=self.host, port=self.port) # eventlet.wsgi.server(eventlet.listen((self.__host, self.__port)), self.__app)