Exemplo n.º 1
0
class AIController:

    #TODO: relay routings if AIController is on a different machine

    def __init__(self, config, app):
        self.config = config
        self.app = app
        self.middleware = AIMiddleware(config)
        self.login_check = None
        self._initBottle()


    def loginCheck(self, project=None, admin=False, superuser=False, canCreateProjects=False, extend_session=False):
        return self.login_check(project, admin, superuser, canCreateProjects, extend_session)


    def addLoginCheckFun(self, loginCheckFun):
        self.login_check = loginCheckFun


    def _initBottle(self):

        @self.app.get('/<project>/listModelStates')
        def list_model_states(project):
            '''
                Returns a list of saved AI model states'
                metadata for a given project.
            '''
            if not self.loginCheck(project=project, admin=True):
                abort(401, 'forbidden')
            
            return {'modelStates': self.middleware.listModelStates(project) }

        
        #TODO: deprecated; replace with workflow:
        @self.app.post('/<project>/startTraining')
        def start_training(project):
            '''
                Manually requests the AIController to train the model.
                This still only works if there is no training process ongoing.
                Otherwise the request is aborted.
            '''
            if self.loginCheck(project=project, admin=True):
                try:
                    params = request.json
                    if 'minNumAnnoPerImage' in params:
                        minNumAnnoPerImage = int(params['minNumAnnoPerImage'])
                    else:
                        minNumAnnoPerImage = 0      #TODO
                    if 'maxNum_train' in params:
                        maxNumImages_train = int(params['maxNum_train'])
                    else:
                        maxNumImages_train = -1     #TODO

                    status = self.middleware.start_training(project=project,
                                        minTimestamp='lastState', 
                                        minNumAnnoPerImage=minNumAnnoPerImage,
                                        maxNumImages=maxNumImages_train,
                                        maxNumWorkers=1)
                except Exception as e:
                    status = str(e)
                return { 'status' : status }

            else:
                abort(401, 'unauthorized')

        
        #TODO: deprecated; replace with workflow:
        @self.app.post('/<project>/startInference')
        def start_inference(project):
            '''
                Manually requests the AIController to issue an inference job.
            '''
            if self.loginCheck(project=project, admin=True):
                try:
                    params = request.json
                    if 'maxNum_inference' in params:
                        maxNumImages_inference = int(params['maxNum_inference'])
                    else:
                        maxNumImages_inference = -1                                 #TODO
                    status = self.middleware.start_inference(
                                            project=project,
                                            forceUnlabeled=False,      #TODO 
                                            maxNumImages=maxNumImages_inference,
                                            maxNumWorkers=-1)           #TODO
                except Exception as e:
                    status = str(e)
                return { 'status' : status }
            
            else:
                abort(401, 'unauthorized')


        #TODO: deprecated; replace with workflow:
        @self.app.post('/<project>/start')
        def start_model(project):
            '''
                Manually launches one of the model processes (train, inference, both, etc.),
                depending on the provided flags.
            '''
            if not self.loginCheck(project=project, admin=True):
                abort(401, 'forbidden')
            try:
                params = request.json
                doTrain = 'train' in params and params['train'] is True
                doInference = 'inference' in params and params['inference'] is True

                if 'minNumAnnoPerImage' in params:
                    minNumAnnoPerImage = int(params['minNumAnnoPerImage'])
                else:
                    minNumAnnoPerImage = 0    #TODO
                if 'maxNum_train' in params:
                    maxNumImages_train = int(params['maxNum_train'])
                else:
                    maxNumImages_train = -1    #TODO
                if 'maxNum_inference' in params:
                    maxNumImages_inference = int(params['maxNum_inference'])
                else:
                    maxNumImages_inference = -1    #TODO

                if doTrain:
                    if doInference:
                        status = self.middleware.start_train_and_inference(
                                project=project,
                                minTimestamp='lastState',
                                minNumAnnoPerImage=minNumAnnoPerImage,
                                maxNumWorkers_train=1,          #TODO
                                forceUnlabeled_inference=False,
                                maxNumImages_inference=maxNumImages_inference,
                                maxNumWorkers_inference=-1)     #TODO
                    else:
                        #TODO: expand to other tasks and requests
                        if self.middleware.task_ongoing(project, ('AIController.start_training',
                                                                'AIWorker.call_train', 'AIWorker.call_average_model_states')):
                            raise Exception('A training process is already ongoing for project "{}".'.format(project))
                        
                        status = self.middleware.start_training(
                                project=project,
                                numEpochs=1,
                                minTimestamp='lastState',
                                minNumAnnoPerImage=minNumAnnoPerImage,
                                maxNumImages=maxNumImages_train,
                                maxNumWorkers=1)                #TODO
                else:
                    status = self.middleware.start_inference(
                                project=project,
                                forceUnlabeled=False, 
                                maxNumImages=maxNumImages_inference, 
                                maxNumWorkers=-1)               #TODO

                return { 'status' : status }
            except Exception as e:
                abort(400, 'bad request')



        @self.app.post('/<project>/launchWorkflow')
        def launch_workflow(project):
            '''
                New way of submitting jobs. This starts entire workflows, which
                can be a chain of multiple training and inference jobs in a row.
            '''
            if not self.loginCheck(project=project, admin=True):
                abort(401, 'forbidden')
            try:
                username = html.escape(request.get_cookie('username'))
                params = request.json
                taskID = self.middleware.launch_task(project, params['workflow'], username)

                return { 'status': 0,
                        'task_id': taskID}

            except Exception as e:
                return { 'status': 1,
                        'message': str(e) }


        
        @self.app.post('/<project>/abortWorkflow')
        def abort_workflow(project):
            if not self.loginCheck(project=project, admin=True):
                abort(401, 'forbidden')
            try:
                username = html.escape(request.get_cookie('username'))
                params = request.json
                taskID = params['taskID']
                self.middleware.revoke_task(project, taskID, username)

                return { 'status': 0}

            except Exception as e:
                return { 'status': 1,
                        'message': str(e) }


        
        @self.app.get('/<project>/status')
        def check_status(project):
            '''
                Queries the middleware for any ongoing training worker processes
                and returns the status of each in a dict.
            '''
            if self.loginCheck(project=project):
                try:
                    queryProject = 'project' in request.query
                    queryTasks = 'tasks' in request.query
                    queryWorkers = 'workers' in request.query
                    status = self.middleware.check_status(
                        project,
                        queryProject, queryTasks, queryWorkers)
                except Exception as e:
                    status = str(e)
                return { 'status' : status }

            else:
                abort(401, 'unauthorized')


        #TODO: REPLACED WITH GENERIC FN OF ProjectAdministration
        # @self.app.get('/<project>/getAImodelSettings')
        # def get_ai_model_info(project):
        #     '''
        #         Returns the model class and settings for the AI model
        #         and the AL criterion.
        #     '''
        #     if not self.login_check(project=project, admin=True):
        #         abort(401, 'unauthorized')
            
        #     return { 'settings': self.middleware.getProjectModelSettings(project) }

        @self.app.get('/<project>/getSavedWorkflows')
        def get_saved_workflows(project):
            '''
                Returns all the model workflows saved for this project,
                also made by other users.
            '''
            if not self.loginCheck(project, admin=True):
                abort(401, 'unauthorized')
            
            try:
                workflows = self.middleware.getSavedWorkflows(project)
                return { 'workflows': workflows }
            except Exception as e:
                return { 'status': str(e) }

        
        @self.app.post('/<project>/saveWorkflow')
        def save_workflow(project):
            '''
                Receives a workflow definition through JSON, verifies it
                by parsing, and stores it in the database if valid. If
                the flag "set_default" is given and set to True, the pro-
                vided workflow will be set as the default, to be executed
                automatically.
            '''
            if not self.loginCheck(project, admin=True):
                abort(401, 'unauthorized')
            
            try:
                username = html.escape(request.get_cookie('username'))
                workflow = request.json['workflow']
                workflowName = request.json['workflow_name']
                try:
                    # for updating existing workflows
                    workflowID = request.json['workflow_id']
                except:
                    workflowID = None
                try:
                    setDefault = request.json['set_default']
                except:
                    setDefault = False
                
                status = self.middleware.saveWorkflow(project, username, workflow, workflowID, workflowName, setDefault)
                return { 'response': status }

            except Exception as e:
                return { 'response': {'status':1, 'message':str(e)} }

    
        @self.app.get('/<project>/getAvailableAImodels')
        def get_available_ai_models(project):
            '''
                Returns all available AI models (class, name) that are
                installed in this instance of AIDE.
            '''
            if not self.loginCheck(project=project, admin=True):
                abort(401, 'unauthorized')
            
            return self.middleware.getAvailableAImodels()


        @self.app.post('/<project>/verifyAImodelOptions')
        def verify_model_options(project):
            '''
                Receives JSON-encoded options and verifies their
                correctness with the AI model (either specified through
                the JSON arguments, or taken from the default project
                option). If the AI model does not support verification
                (as is the case in legacy models), a warning is returned.
            '''
            if not self.loginCheck(project=project, admin=True):
                abort(401, 'unauthorized')
            
            try:
                modelOptions = request.json['options']
                try:
                    modelLibrary = request.json['ai_model_library']
                except:
                    modelLibrary = None
                status = self.middleware.verifyAImodelOptions(project, modelOptions, modelLibrary)
                return {'status': status}
            except Exception as e:
                return {'status': 1, 'message': str(e)}


        
        @self.app.post('/<project>/saveAImodelSettings')
        def save_model_settings(project):
            if not self.loginCheck(project=project, admin=True):
                abort(401, 'unauthorized')
            
            try:
                settings = request.json['settings']
                response = self.middleware.updateAImodelSettings(project, settings)
                return {'status': 0, 'message': response}
            except Exception as e:
                return {'status': 1, 'message': str(e)}
Exemplo n.º 2
0
class AIController:

    def __init__(self, config, app):
        if config.getProperty('Project', 'demoMode', type=bool, fallback=False):
            raise Exception('AIController cannot be launched in demo mode.')

        self.config = config
        self.app = app

        self.middleware = AIMiddleware(config)

        self.login_check = None

        self._init_params()
        self._initBottle()


    def _init_params(self):
        self.minNumAnnoPerImage = self.config.getProperty(self, 'minNumAnnoPerImage', type=int, fallback=0)
        self.maxNumImages_train = self.config.getProperty(self, 'maxNumImages_train', type=int)
        self.maxNumWorkers_train = self.config.getProperty(self, 'maxNumWorkers_train', type=int, fallback=-1)
        self.maxNumWorkers_inference = self.config.getProperty(self, 'maxNumWorkers_inference', type=int, fallback=-1)
        self.maxNumImages_inference = self.config.getProperty(self, 'maxNumImages_inference', type=int)


    def loginCheck(self, needBeAdmin=False):
        return True if self.login_check is None else self.login_check(needBeAdmin)


    def addLoginCheckFun(self, loginCheckFun):
        self.login_check = loginCheckFun


    def _initBottle(self):
        
        @self.app.post('/startTraining')
        def start_training():
            '''
                Manually requests the AIController to train the model.
                This still only works if there is no training process ongoing.
                Otherwise the request is aborted.
            '''
            if self.loginCheck(True):
                try:
                    params = request.json
                    if 'minNumAnnoPerImage' in params:
                        minNumAnnoPerImage = int(params['minNumAnnoPerImage'])
                    else:
                        minNumAnnoPerImage = self.minNumAnnoPerImage
                    if 'maxNum_train' in params:
                        maxNumImages_train = int(params['maxNum_train'])
                    else:
                        maxNumImages_train = self.maxNumImages_train

                    status = self.middleware.start_training(minTimestamp='lastState', 
                                        minNumAnnoPerImage=minNumAnnoPerImage,
                                        maxNumImages=maxNumImages_train,
                                        maxNumWorkers=self.maxNumWorkers_train)
                except Exception as e:
                    status = str(e)
                return { 'status' : status }

            else:
                abort(401, 'unauthorized')

        
        @self.app.post('/startInference')
        def start_inference():
            '''
                Manually requests the AIController to issue an inference job.
            '''
            if self.loginCheck(True):
                try:
                    params = request.json
                    if 'maxNum_inference' in params:
                        maxNumImages_inference = int(params['maxNum_inference'])
                    else:
                        maxNumImages_inference = self.maxNumImages_inference
                    status = self.middleware.start_inference(forceUnlabeled=False,      #TODO 
                                            maxNumImages=maxNumImages_inference,
                                            maxNumWorkers=self.maxNumWorkers_inference)
                except Exception as e:
                    status = str(e)
                return { 'status' : status }
            
            else:
                abort(401, 'unauthorized')


        @self.app.post('/start')
        def start_model():
            '''
                Manually launches one of the model processes (train, inference, both, etc.),
                depending on the provided flags.
            '''
            if self.loginCheck(True):
                try:
                    params = request.json
                    doTrain = 'train' in params and params['train'] is True
                    doInference = 'inference' in params and params['inference'] is True

                    if 'minNumAnnoPerImage' in params:
                        minNumAnnoPerImage = int(params['minNumAnnoPerImage'])
                    else:
                        minNumAnnoPerImage = self.minNumAnnoPerImage
                    if 'maxNum_train' in params:
                        maxNumImages_train = int(params['maxNum_train'])
                    else:
                        maxNumImages_train = self.maxNumImages_train
                    if 'maxNum_inference' in params:
                        maxNumImages_inference = int(params['maxNum_inference'])
                    else:
                        maxNumImages_inference = self.maxNumImages_inference

                    if doTrain:
                        if doInference:
                            status = self.middleware.start_train_and_inference(minTimestamp='lastState',
                                    minNumAnnoPerImage=minNumAnnoPerImage,
                                    maxNumWorkers_train=self.maxNumWorkers_train,
                                    forceUnlabeled_inference=False, maxNumImages_inference=maxNumImages_inference, maxNumWorkers_inference=self.maxNumWorkers_inference)
                        else:
                            status = self.middleware.start_training(minTimestamp='lastState',
                                    minNumAnnoPerImage=minNumAnnoPerImage,
                                    maxNumImages=maxNumImages_train,
                                    maxNumWorkers=self.maxNumWorkers_train)
                    else:
                        status = self.middleware.start_inference(forceUnlabeled=False, 
                                    maxNumImages=maxNumImages_inference, 
                                    maxNumWorkers=self.maxNumWorkers_inference)

                    return { 'status' : status }
                except:
                    abort(400, 'bad request')


        @self.app.get('/status')
        def check_status():
            '''
                Queries the middleware for any ongoing training worker processes
                and returns the status of each in a dict.
            '''
            if self.loginCheck(False):
                try:
                    queryProject = 'project' in request.query
                    queryTasks = 'tasks' in request.query
                    queryWorkers = 'workers' in request.query
                    status = self.middleware.check_status(queryProject, queryTasks, queryWorkers)
                except Exception as e:
                    status = str(e)
                return { 'status' : status }

            else:
                abort(401, 'unauthorized')