예제 #1
0
 def __init__(self, pe_id=None):
     if pe_id is None:
         parser = argparse.ArgumentParser(
             description='A user defined script.')
         parser.add_argument('--idx',
                             nargs='?',
                             action='store',
                             help='Id of related pipeline element.')
         args = parser.parse_args()
     lostconfig = LOSTConfig()
     dbm = access.DBMan(lostconfig)
     db_fs = dbm.get_fs(name='lost_data')
     self.file_man = FileMan(fs_db=db_fs)
     self._dbm = dbm  #type: lost.db.access.DBMan
     if pe_id is None:
         pe = dbm.get_pipe_element(int(args.idx))
     else:
         pe = dbm.get_pipe_element(pe_id)
     super().__init__(pe, dbm)
     logfile_path = self.file_man.get_pipe_log_path(self._pipe.idx)
     self._log_stream = self.file_man.fs.open(logfile_path, 'a')
     self._logger = log.get_stream_logger(os.path.basename(pe.script.path),
                                          self._log_stream)
     if self.pipe_info.logfile_path is None or not self.pipe_info.logfile_path:
         self.pipe_info.logfile_path = self.get_rel_path(logfile_path)
     self._inp = inout.Input(self)
     self._outp = inout.ScriptOutput(self)
     self.rejected_execution = False
     # If pe_id is None we have a normal script
     # If pe_id is not None a JupyterNotebook uses this script
     if pe_id is None:
         return self._run()
예제 #2
0
 def __init__(self, pe_id=None):
     if pe_id is None:
         parser = argparse.ArgumentParser(description='A user defined script.')
         parser.add_argument('--idx', nargs='?', action='store',
                             help='Id of related pipeline element.')
         args = parser.parse_args()
     lostconfig = LOSTConfig()
     self.file_man = FileMan(lostconfig)
     dbm = access.DBMan(lostconfig)
     self._dbm = dbm #type: lost.db.access.DBMan
     if pe_id is None:
         pe = dbm.get_pipe_element(int(args.idx))
     else:
         pe = dbm.get_pipe_element(pe_id)
     super().__init__(pe, dbm)
     logfile_path = self.file_man.get_pipe_log_path(self._pipe.idx)
     self._logger = log.get_file_logger(os.path.basename(pe.script.path),
                                       logfile_path)
     if self.pipe_info.logfile_path is None or not self.pipe_info.logfile_path:
         self.pipe_info.logfile_path = self.get_rel_path(logfile_path)
     self._inp = inout.Input(self)
     self._outp = inout.ScriptOutput(self)
     self.rejected_execution = False
     # If pe_id is None we have a normal script
     # If pe_id is not None a JupyterNotebook uses this script
     if pe_id is None:
         try:
             self.main()
             self.i_am_done()
             self._dbm.close_session()
         except:
             err_msg = str(datetime.datetime.now()) + '\n'
             err_msg += traceback.format_exc()
             self.report_err(err_msg)
             self._dbm.close_session()
예제 #3
0
    def __init__(self, pipe_template_dir, dbm, forTest=False):
        '''Load json file.

        Args:
            pipe_template_dir: Path to pipeline directory
        '''
        self.forTest = forTest
        self.dbm = dbm
        self.file_man = FileMan(self.dbm.lostconfig)
        if pipe_template_dir.endswith('/'):
            pipe_template_dir = pipe_template_dir[:-1]
        self.src_pipe_template_path = pipe_template_dir
        self.dst_pipe_template_path = os.path.join(
            self.file_man.pipe_path,
            os.path.basename(self.src_pipe_template_path))
        self.json_files = glob(os.path.join(pipe_template_dir, '*.json'))
        self.pipes = []
        self.namespace = os.path.basename(
            self.src_pipe_template_path).strip('/')
        for json_path in self.json_files:
            with open(json_path) as jfile:
                pipe = json.load(jfile)
            pipe['namespace'] = self.namespace
            pipe['name'] = self._namespaced_name(
                os.path.splitext(os.path.basename(json_path))[0])
            self.pipes.append(pipe)
            # Set name to name of the script file
            for pe in pipe['elements']:
                if 'script' in pe:
                    pe['script']['name'] = self._namespaced_name(
                        pe['script']['path'])
        self.checker = PipeDefChecker(logging)
예제 #4
0
    def __init__(self, pe, dbm):
        '''Represents a file or folder in the filesystem.

        Args:
            pe (object): :class:`lost.db.model.PipeElement`
            dbm (object): Database Management object.
        '''
        super().__init__(pe, dbm)
        self.file_man = FileMan(fs_db=pe.datasource.fs)
예제 #5
0
    def post(self):
        dbm = access.DBMan(LOST_CONFIG)
        identity = get_jwt_identity()
        user = dbm.get_user_by_id(identity)
        if not user.has_role(roles.ANNOTATOR):
            dbm.close_session()
            return "You need to be {} in order to perform this request.".format(
                roles.ANNOTATOR), 401

        else:
            #TODO: Check if user is permitted to load this image
            data = json.loads(request.data)
            #flask.current_app.logger.info('mia -> getimage. Received data: {}'.format(data))
            if data['type'] == 'imageBased':
                db_img = dbm.get_image_anno(data['id'])
                fm = FileMan(fs_db=db_img.fs)
                img = load_img(db_img, fm, user)
            elif data['type'] == 'annoBased':
                db_anno = dbm.get_two_d_anno(two_d_anno_id=data['id'])
                db_img = dbm.get_image_anno(db_anno.img_anno_id)
                fm = FileMan(fs_db=db_img.fs)
                # image = fm.load_img(db_img.img_path)
                image = load_img(db_img, fm, user)

                # get annotation_task config
                draw_anno = False
                context = 0.0
                try:
                    draw_anno = data['drawAnno']
                except:
                    pass
                try:
                    context = float(data['addContext'])
                except:
                    pass

                df = db_img.to_df()
                df = df[df['anno_uid'] == db_anno.idx]
                # ds = lds.LOSTDataset(df, filesystem=fm.fs)
                # ds.df.img_path = ds.df.abs_path
                # ds.transform_bbox_style('x1y1x2y2', inplace=True)
                # ds.to_abs(inplace=True)
                # ds.df.anno_lbl = ""
                # image = lds.vis_sample(image, ds.df, radius=10)
                crops, _ = anno_helper.crop_boxes(df['anno_data'].values,
                                                  df['anno_dtype'].values,
                                                  image,
                                                  context=context,
                                                  draw_annotations=draw_anno)
                # img = image
                img = crops[0]
            else:
                raise Exception('Unknown mia image type')
            _, data = cv2.imencode('.jpg', img)
            data64 = base64.b64encode(data.tobytes())
            dbm.close_session()
            return u'data:img/jpg;base64,' + data64.decode('utf-8')
예제 #6
0
 def __init__(self, dbm, pipe, lostconfig):
     '''
     :type dbm: lost.db.access.DBMan
     :type pipe: lost.db.model.Pipe
     '''
     super().__init__(dbm=dbm, pipe=pipe)
     self.lostconfig = lostconfig  #type: lost.logic.config.LOSTConfig
     self.file_man = FileMan(self.lostconfig)
     # self.logger = lost.logic.log.get_file_logger(
     #     'Executor: {}'.format(self.lostconfig.env),
     #     self.file_man.app_log_path)
     self.logger = get_task_logger(__name__)
예제 #7
0
def celery_exec_script(pipe_element_id):
    try:
        # Collect context information for celery task
        logger = get_task_logger(__name__)
        lostconfig = LOSTConfig()
        dbm = DBMan(lostconfig)
        pipe_e = dbm.get_pipe_element(pipe_e_id=pipe_element_id)
        worker = CurrentWorker(dbm, lostconfig)
        if not worker.enough_resources(pipe_e.script):
            logger.warning(
                'Not enough resources! Rejected {} (PipeElement ID {})'.format(
                    pipe_e.script.path, pipe_e.idx))
            return
        pipe_e.state = state.PipeElement.IN_PROGRESS
        dbm.save_obj(pipe_e)
        file_man = FileMan(lostconfig)
        pipe = pipe_e.pipe

        cmd = gen_run_cmd("pudb3", pipe_e, lostconfig)
        debug_script_path = file_man.get_instance_path(pipe_e)
        debug_script_path = os.path.join(debug_script_path, 'debug.sh')
        with open(debug_script_path, 'w') as sfile:
            sfile.write(cmd)

        cmd = gen_run_cmd("python3", pipe_e, lostconfig)
        start_script_path = file_man.get_instance_path(pipe_e)
        start_script_path = os.path.join(start_script_path, 'start.sh')
        with open(start_script_path, 'w') as sfile:
            sfile.write(cmd)
        p = subprocess.Popen('bash {}'.format(start_script_path),
                             stdout=subprocess.PIPE,
                             stderr=subprocess.PIPE,
                             shell=True)
        logger.info("{} ({}): Started script\n{}".format(
            pipe.name, pipe.idx, cmd))
        worker.add_script(pipe_e, pipe_e.script)
        out, err = p.communicate()
        worker.remove_script(pipe_e, pipe_e.script)
        if p.returncode != 0:
            raise Exception(err.decode('utf-8'))
        logger.info('{} ({}): Executed script successful: {}'.format(
            pipe.name, pipe.idx, pipe_e.script.path))
        dbm.close_session()

    except:
        pipe = pipe_e.pipe
        logger.info('{} ({}): Exception occurred in script: {}'.format(
            pipe.name, pipe.idx, pipe_e.script.path))
        msg = traceback.format_exc()
        logger.error(msg)
        script_api.report_script_err(pipe_e, pipe, dbm, msg)
        dbm.close_session()
예제 #8
0
파일: mia.py 프로젝트: wanlipeng/lost
 def __init__(self,
              db_man,
              annos,
              user_id,
              anno_task_id,
              proposedLabel=True):
     self.mia_json = dict()
     self.db_man = db_man
     self.annos = annos
     self.user_id = user_id
     self.anno_task_id = anno_task_id
     self.file_man = FileMan(self.db_man.lostconfig)
     self.proposedLabel = proposedLabel
예제 #9
0
파일: endpoint.py 프로젝트: l3p-cv/lost
    def post(self):
        dbm = access.DBMan(LOST_CONFIG)
        identity = get_jwt_identity()
        user = dbm.get_user_by_id(identity)
        if not user.has_role(roles.ANNOTATOR):
            dbm.close_session()
            return "You need to be {} in order to perform this request.".format(
                roles.ANNOTATOR), 401

        else:
            data = json.loads(request.data)
            img = dbm.get_image_anno(data['imageId'])
            flask.current_app.logger.info('img.img_path: {}'.format(
                img.img_path))
            flask.current_app.logger.info('img.fs.name: {}'.format(
                img.fs.name))
            # fs_db = dbm.get_fs(img.fs_id)
            fs = FileMan(fs_db=img.fs)
            #img = PIL.Image.open('/home/lost/data/media/10_voc2012/2007_008547.jpg')
            # img = PIL.Image.open(img_path)
            if data['clahe']['active']:
                img = fs.load_img(img.img_path, color_type='gray')
            else:
                img = fs.load_img(img.img_path, color_type='color')

            flask.current_app.logger.info(
                'Triggered filter. Received data: {}'.format(data))

            # img_io = BytesIO()
            # img.save(img_io, 'PNG')
            # img_io.seek(0)
            # return send_file(img_io, mimetype='image/png')
            if data['rotate']['active']:
                if data['rotate']['angle'] == 90:
                    img = cv2.rotate(img, cv2.ROTATE_90_CLOCKWISE)
                elif data['rotate']['angle'] == -90:
                    img = cv2.rotate(img, cv2.ROTATE_90_COUNTERCLOCKWISE)
                elif data['rotate']['angle'] == 180:
                    img = cv2.rotate(img, cv2.ROTATE_180)
            if data['clahe']['active']:
                clahe = cv2.createCLAHE(data['clahe']['clipLimit'])
                img = clahe.apply(img)
                # img = img.rotate(data['rotate']['angle'], expand=True)
            # img = ImageOps.autocontrast(img)

            # data = BytesIO()
            # img.save(data, "PNG")
            _, data = cv2.imencode('.jpg', img)
            data64 = base64.b64encode(data.tobytes())
            dbm.close_session()
            return u'data:img/jpg;base64,' + data64.decode('utf-8')
예제 #10
0
파일: endpoint.py 프로젝트: kengggg/lost
    def post(self):
        dbm = access.DBMan(LOST_CONFIG)
        identity = get_jwt_identity()
        user = dbm.get_user_by_id(identity)
        if not user.has_role(roles.ANNOTATOR):
            dbm.close_session()
            return "You need to be {} in order to perform this request.".format(
                roles.ANNOTATOR), 401

        else:
            #TODO: Check if user is permitted to load this image
            data = json.loads(request.data)
            #flask.current_app.logger.info('mia -> getimage. Received data: {}'.format(data))
            if data['type'] == 'imageBased':
                db_img = dbm.get_image_anno(data['id'])
                fm = FileMan(fs_db=db_img.fs)
                img = load_img(db_img, fm, user)
            elif data['type'] == 'annoBased':
                db_anno = dbm.get_two_d_anno(two_d_anno_id=data['id'])
                db_img = dbm.get_image_anno(db_anno.img_anno_id)
                fm = FileMan(fs_db=db_img.fs)
                # image = fm.load_img(db_img.img_path)
                image = load_img(db_img, fm, user)

                # get annotation_task config
                config = mia.get_config(dbm, user.idx)
                draw_anno = False
                context = None
                try:
                    draw_anno = config['drawAnno']
                except:
                    pass
                try:
                    context = float(config['addContext'])
                except:
                    pass
                crops, _ = anno_helper.crop_boxes(
                    [db_anno.to_vec('anno.data')],
                    [db_anno.to_vec('anno.dtype')],
                    image,
                    context=context,
                    draw_annotations=draw_anno)
                img = crops[0]
            else:
                raise Exception('Unknown mia image type')
            _, data = cv2.imencode('.jpg', img)
            data64 = base64.b64encode(data.tobytes())
            dbm.close_session()
            return u'data:img/jpg;base64,' + data64.decode('utf-8')
예제 #11
0
def update_version_log():
    fm = FileMan(LOSTConfig())
    path = fm.get_version_log_path()
    if not os.path.exists(path):
        print('Patchsystem: Created version log file: {}'.format(path))
        versions = []
        versions.append(lost.__version__)
        with open(path, 'w') as json_file:
            json.dump(versions, json_file)
    else:
        with open(path) as json_file:  
            versions = json.load(json_file)
        if versions[-1] == lost.__version__:
            print('Patchsystem: No version change!')
        else:
            print('Patchsystem: We maybe need to patch!')
예제 #12
0
class Datasource(Element):
    def __init__(self, pe, dbm):
        '''Represents a file or folder in the filesystem.

        Args:
            pe (object): :class:`lost.db.model.PipeElement`
            dbm (object): Database Management object.
        '''
        super().__init__(pe, dbm)
        self.file_man = FileMan(fs_db=pe.datasource.fs)

    @property
    def path(self):
        '''str: Relative path to file or folder'''
        return self.file_man.get_abs_path(self.pe.datasource.selected_path)

    def get_fs(self):
        '''Get filesystem for this datasource'''
        # ds = self.pe.datasource
        # fs_args = ast.literal_eval(ds.fs.connection)
        # fs = fsspec.filesystem(ds.fs.fs_type, **fs_args)
        # fs.lost_fs = ds.fs
        return self.file_man.fs

    def get_fm(self):
        '''Get FileMan object'''
        return self.file_man
예제 #13
0
 def get(self, path):
     print(path)
     dbm = access.DBMan(LOST_CONFIG)
     identity = get_jwt_identity()
     user = dbm.get_user_by_id(identity)
     if not user.has_role(roles.ANNOTATOR):
         dbm.close_session()
         return "You are not authorized.", 401
     else:
         # raise Exception('data/logs/ -> Not Implemented!')
         fm = FileMan(LOST_CONFIG)
         with fm.fs.open(fm.get_abs_path(path), 'rb') as f:
             resp = make_response(f.read())
             resp.headers[
                 "Content-Disposition"] = "attachment; filename=log.csv"
             resp.headers["Content-Type"] = "text/csv"
         return resp
예제 #14
0
    def get_filesystem(self, name=None):
        '''Get default lost filesystem or a specific filesystem by name.

        Returns:
            fsspec.spec.AbstractFileSystem: See https://filesystem-spec.readthedocs.io/en/latest/api.html#fsspec.spec.AbstractFileSystem
        '''
        if name is None:
            return self.file_man.fs
        #TODO: Check if pipeline user is permitted to load fs
        fs_db = self._dbm.get_fs(name=name)
        fm = FileMan(fs_db=fs_db)
        return fm.fs
예제 #15
0
파일: endpoint.py 프로젝트: kengggg/lost
 def post(self):
     dbm = access.DBMan(LOST_CONFIG)
     identity = get_jwt_identity()
     user = dbm.get_user_by_id(identity)
     if not user.has_role(roles.DESIGNER):
         dbm.close_session()
         return "You need to be {} in order to perform this request.".format(
             roles.DESIGNER), 401
     else:
         data = json.loads(request.data)
         fs_db = dbm.get_fs(name=data['fs']['name'])
         fm = FileMan(fs_db=fs_db)
         commonprefix = os.path.commonprefix(
             [data['path'], fs_db.root_path])
         if commonprefix != fs_db.root_path:
             path = fs_db.root_path
         else:
             path = data['path']
         res = fm.ls(path, detail=True)
         dbm.close_session()
         return chonkyfy(res, path, fm)
예제 #16
0
파일: template.py 프로젝트: l3p-cv/lost
def get_template(db_man, template_id, user):
    ''' read out one template

    Args:
        db_man:
        template_id: id of the template
    Returns: JSON with all nescessary template info 
    '''
    #TODO: implement the following access methods
    template = db_man.get_pipe_template(template_id)
    if template is None:
        error_msg = "PipeTemplate with ID '" + str(
            template_id) + "' does not exist."
        try:
            raise PipeTemplateNotFoundError(error_msg)
        finally:
            return error_msg
    file_man = FileMan(db_man.lostconfig)
    available_raw_files = file_man.get_media_rel_path_tree()
    available_groups = db_man.get_groups()
    default_group = db_man.get_group_by_name(user.user_name)
    available_label_trees = db_man.get_all_label_trees(
        group_id=default_group.idx, add_global=True)
    available_scripts = db_man.get_all_scripts()
    available_fs = list(db_man.get_public_fs())
    for user_group in db_man.get_user_groups_by_user_id(user.idx):
        if user_group.group.is_user_default:
            group_id = user_group.group.idx
    available_fs += list(db_man.get_fs(group_id=group_id))

    try:
        template_serialize = TemplateSerialize(db_man, template,
                                               available_raw_files,
                                               available_label_trees,
                                               available_groups,
                                               available_scripts, available_fs)
    except TypeError:
        return "No JSON found in PipeTemplate."
    template_serialize.add_available_info()
    return template_serialize.template_json
예제 #17
0
파일: endpoint.py 프로젝트: kengggg/lost
    def post(self):
        dbm = access.DBMan(LOST_CONFIG)
        identity = get_jwt_identity()
        user = dbm.get_user_by_id(identity)
        # raise Exception('lostsession: {}'.format(dask_session.ds_man.session))
        if not user.has_role(roles.ANNOTATOR):
            dbm.close_session()
            return "You need to be {} in order to perform this request.".format(
                roles.ANNOTATOR), 401

        else:
            #TODO: Check if user is permitted to load this image
            #TODO: Read img from stream -> cv2.imdecode()
            data = json.loads(request.data)
            flask.current_app.logger.info(
                'sia -> getimage. Received data: {}'.format(data))
            db_img = dbm.get_image_anno(data['imgId'])
            if LOST_CONFIG.worker_management != 'dynamic':
                fm = FileMan(fs_db=db_img.fs)
                flask.current_app.logger.info(
                    'sia -> getimage. fs.name: {} fs.root_path: {}'.format(
                        db_img.fs.name, db_img.fs.root_path))
                img = fm.load_img(db_img.img_path)
            else:
                img = dask_session.ds_man.read_fs_img(user, db_img.fs,
                                                      db_img.img_path)
                flask.current_app.logger.info(
                    'dask_session read_fs_img type: {}'.format(type(img)))
                flask.current_app.logger.info(
                    'dask_session read_fs_img shape: {}'.format(img.shape))

            # img_path = fm.get_abs_path(db_img.img_path)
            # #raise Exception('sia -> getimage: {}'.format(img_path))
            # img = cv2.imread(img_path)
            _, data = cv2.imencode('.jpg', img)
            data64 = base64.b64encode(data.tobytes())
            dbm.close_session()
            return u'data:img/jpg;base64,' + data64.decode('utf-8')
예제 #18
0
파일: sia.py 프로젝트: yc10086/lost
    def __init__(self, db_man, data, user_id):
        """
        :type db_man: lost.db.access.DBMan
        """
        self.timestamp = datetime.now()
        self.db_man = db_man
        self.user_id = user_id
        self.at = get_sia_anno_task(db_man,
                                    user_id)  #type: lost.db.model.AnnoTask
        self.sia_history_file = FileMan(
            self.db_man.lostconfig).get_sia_history_path(self.at)
        self.iteration = db_man.get_pipe_element(
            pipe_e_id=self.at.pipe_element_id).iteration
        self.image_anno = self.db_man.get_image_annotation(data['imgId'])
        self.image_anno.timestamp = self.timestamp
        if self.image_anno.anno_time is None:
            self.image_anno.anno_time = 0.0
        self.image_anno.anno_time += (
            self.image_anno.timestamp -
            self.image_anno.timestamp_lock).total_seconds()
        self.b_boxes = list()
        self.points = list()
        self.lines = list()
        self.polygons = list()
        self.history_json = dict()
        self.history_json['annotations'] = dict()
        self.history_json['annotations']['new'] = list()
        self.history_json['annotations']['unchanged'] = list()
        self.history_json['annotations']['changed'] = list()
        self.history_json['annotations']['deleted'] = list()
        self._update_img_labels(data)
        self.image_anno.is_junk = data['isJunk']

        # store certain annotations
        if 'bBoxes' in data['annotations']:
            self.b_boxes = data['annotations']['bBoxes']
        else:
            self.b_boxes = None
        if 'points' in data['annotations']:
            self.points = data['annotations']['points']
        else:
            self.points = None
        if 'lines' in data['annotations']:
            self.lines = data['annotations']['lines']
        else:
            self.lines = None
        if 'polygons' in data['annotations']:
            self.polygons = data['annotations']['polygons']
        else:
            self.polygons = None
예제 #19
0
 def get(self, deid):
     dbm = access.DBMan(LOST_CONFIG)
     identity = get_jwt_identity()
     user = dbm.get_user_by_id(identity)
     if not user.has_role(roles.DESIGNER):
         dbm.close_session()
         return "You are not authorized.", 401
     else:
         # data = json.loads(request.data)
         de = dbm.get_data_export(deid)
         fs_db = de.fs
         fm = FileMan(fs_db=fs_db)
         with fm.fs.open(de.file_path, 'rb') as f:
             resp = make_response(f.read())
             resp.headers[
                 "Content-Disposition"] = "attachment; filename=annos.parquet"
             resp.headers["Content-Type"] = "blob"
         return resp
예제 #20
0
def _read_fs_img(fs, img_path):
    fm = FileMan(fs_db=fs)
    img = fm.load_img(img_path)
    return img
예제 #21
0
class Script(pe_base.Element):
    '''Superclass for a user defined Script.

    Custom scripts need to inherit from Script and implement the main method.

    Attributes:
        pe_id (int): Pipe element id. Assign the pe id of a pipline script
            in order to emulate this script in a jupyter notebook for example.
    '''
    def __init__(self, pe_id=None):
        if pe_id is None:
            parser = argparse.ArgumentParser(
                description='A user defined script.')
            parser.add_argument('--idx',
                                nargs='?',
                                action='store',
                                help='Id of related pipeline element.')
            args = parser.parse_args()
        lostconfig = LOSTConfig()
        dbm = access.DBMan(lostconfig)
        db_fs = dbm.get_fs(name='lost_data')
        self.file_man = FileMan(fs_db=db_fs)
        self._dbm = dbm  #type: lost.db.access.DBMan
        if pe_id is None:
            pe = dbm.get_pipe_element(int(args.idx))
        else:
            pe = dbm.get_pipe_element(pe_id)
        super().__init__(pe, dbm)
        logfile_path = self.file_man.get_pipe_log_path(self._pipe.idx)
        self._log_stream = self.file_man.fs.open(logfile_path, 'a')
        self._logger = log.get_stream_logger(os.path.basename(pe.script.path),
                                             self._log_stream)
        if self.pipe_info.logfile_path is None or not self.pipe_info.logfile_path:
            self.pipe_info.logfile_path = self.get_rel_path(logfile_path)
        self._inp = inout.Input(self)
        self._outp = inout.ScriptOutput(self)
        self.rejected_execution = False
        # If pe_id is None we have a normal script
        # If pe_id is not None a JupyterNotebook uses this script
        if pe_id is None:
            return self._run()

    def _run(self, ret_success=False):
        try:
            self.main()
            self.i_am_done()
            success = 'PipeElementID: {}, Successfully executed script: {}'.format(
                self._pipe_element.idx, self._pipe_element.script.name)
            self._dbm.close_session()
            if ret_success:
                return success
        except:
            err_msg = str(datetime.datetime.now()) + '\n'
            err_msg += traceback.format_exc()
            self.report_err(err_msg)
            self._dbm.close_session()

    def __str__(self):
        my_str = 'I am a Script.\nMy name is: {}\nPipeElementID: {}'.format(
            self._pipe_element.script.name, self._pipe_element.idx)
        return my_str

    def main(self):
        #raise NotImplementedError("You need to implement a main method to get your Script running.")
        pass

    @property
    def logger(self):
        ''':class:`logging.Logger`: A standard python logger for this script. 
        
        It will log to the pipline log file.
        '''
        return self._logger

    @property
    def inp(self):
        ''':class:`lost.pyapi.inout.Input`
        '''
        return self._inp  #type: inout.Input

    @property
    def outp(self):
        ''':class:`lost.pyapi.inout.ScriptOutput`
        '''
        return self._outp  #type: inout.ScriptOutput

    def get_rel_path(self, path):
        '''Get relativ path for current project

        Args:
            path (str): A absolute path

        Returns:
            str : Relative path
        '''
        return self.file_man.get_rel_path(path)

    def get_label_tree(self, name):
        '''Get a LabelTree by name.
        
        Args:
            name (str): Name of the desired LabelTree.
        
        Retruns:
            :class:`lost.logic.label.LabelTree` or None: 
                If a label tree with the given name exists 
                it will be returned. Otherwise None
                will be returned'''
        group_id = self._pipe.group_id
        root_list = self._dbm.get_all_label_trees(group_id, add_global=True)
        root = next(filter(lambda x: x.name == name, root_list), None)
        if root is None:
            return None
        else:
            return LabelTree(self._dbm, root_leaf=root)

    def create_label_tree(self, name, external_id=None):
        '''Create a new LabelTree
        
        Args:
            name (str): Name of the tree / name of the root leaf.
            external_id (str): An external id for the root leaf.
        
        Returns:
            :class:`lost.logic.label.LabelTree`:
                The created LabelTree.
        '''
        tree = LabelTree(self._dbm)
        tree.create_root(name, external_id=external_id)
        return tree

    def get_abs_path(self, path):
        '''Get absolute path in current file system.

        Args:
            path (str): A relative path.

        Returns:
            str: Absolute path
        '''
        return self.file_man.get_abs_path(path)

    def break_loop(self):
        '''Break next loop in pipeline.
        '''
        loop_e = self._pipe_man.get_next_loop(self._pipe_element)
        if loop_e is not None:
            loop_e.loop.break_loop = True
        self._dbm.add(loop_e)

    def loop_is_broken(self):
        '''Check if the current loop is broken'''
        loop_e = self._pipe_man.get_next_loop(self._pipe_element)
        if loop_e is not None:
            return loop_e.loop.break_loop
        else:
            self.logger.warning(
                'loop_is_broken method was used, but no loop seems to be in this pipeline!'
            )
            return False

    def get_arg(self, arg_name):
        '''Get argument value by name for this script.

        Args:
            arg_name (str): Name of the argument.

        Returns:
            Value of the given argument.
        '''
        if self._pipe_element.arguments:
            args = json.loads(self._pipe_element.arguments)
            # args = ast.literal_eval(self._pipe_element.arguments)
            my_arg = args[arg_name]['value']
            if my_arg in ['t', 'true', 'yes']:
                return True
            if my_arg in ['f', 'false', 'no']:
                return False
            if my_arg in ['-', '', '[]']:
                return None
            try:
                return ast.literal_eval(my_arg)
            except:
                return my_arg

        else:
            return None

    def get_filesystem(self, name=None):
        '''Get default lost filesystem or a specific filesystem by name.

        Returns:
            fsspec.spec.AbstractFileSystem: See https://filesystem-spec.readthedocs.io/en/latest/api.html#fsspec.spec.AbstractFileSystem
        '''
        if name is None:
            return self.file_man.fs
        #TODO: Check if pipeline user is permitted to load fs
        fs_db = self._dbm.get_fs(name=name)
        fm = FileMan(fs_db=fs_db)
        return fm.fs

    def get_path(self, file_name, context='instance', ptype='abs'):
        '''Get path for the filename in a specific context in filesystem.

        Args:
            file_name (str): Name or relative path for a file.
            context (str): Options: *instance*, *pipe*, *static*
            ptype (str): Type of this path. Can be relative or absolute
                Options: *abs*, *rel*

        Returns:
            str: Path to the file in the specified context.
        '''
        if context == 'instance':
            path = os.path.join(self.instance_context, file_name)
        elif context == 'pipe':
            path = os.path.join(self.pipe_context, file_name)
        elif context == 'static':
            path = os.path.join(self.static_context, file_name)
        else:
            raise Exception('Unknown context: {}'.format(context))
        if ptype == 'abs':
            return path
        elif ptype == 'rel':
            return self.get_rel_path(path)
        else:
            raise Exception('Unknown argument ptype: {}'.format(ptype))

    @property
    def iteration(self):
        '''int: Get the current iteration.

        Number of times this script has been executed.
        '''
        return self._pipe_element.iteration

    @property
    def instance_context(self):
        '''str: Get the path to store files that are only valid for this instance.
        '''
        abs_path = self.file_man.create_instance_path(self._pipe_element)
        rel_path = self.file_man.make_path_relative(abs_path)
        self._pipe_element.instance_context = rel_path
        self._dbm.add(self._pipe_element)
        return abs_path

    @property
    def pipe_context(self):
        '''str: Root path to store files that should be visible for all elements
        in the pipeline.
        '''
        return self.file_man.get_pipe_context_path(self._pipe_element)

    @property
    def static_context(self):
        '''str: Get the static path.

        Files that are stored at this path can be accessed by all instances of a
        script.
        '''
        #TODO: Check how to handle different filesystem!
        return os.path.join(self._lostconfig.app_path,
                            os.path.split(self._pipe_element.script.path)[0])

    @property
    def progress(self):
        '''float: Get current progress that is displayed in the progress bar of this script.

        Current progress in percent 0...100
        '''
        return self._pipe_element.progress

    def update_progress(self, value):
        '''Update the progress for this script.

        Args:
            value (float): Progress in percent 0...100
        '''
        self._pipe_element.progress = value
        self._dbm.commit()

    def reject_execution(self):
        '''Reject execution of this script and set it to PENDING again.

        Note:
            This method is useful if you want to execute this script only
            when some condition based on previous pipeline elements is 
            meet.
        '''
        self.rejected_execution = True

    def get_alien_element(self, pe_id):
        '''Get an pipeline element by id from somewhere in the LOST system.

        It is an alien element since it is most likely not part of the 
        pipeline instance this script belongs to.

        Args:
            pe_id (int): PipeElementID of the alien element.
        
        Returns:
            * :class:`lost.pyapi.script.Script`
            * :class:`lost.pyapi.pipe_elements.AnnoTask`
            * :class:`lost.pyapi.pipe_elements.Datasource`
            * :class:`lost.pyapi.pipe_elements.VisualOutput`
            * :class:`lost.pyapi.pipe_elements.DataExport`
            * :class:`lost.pyapi.pipe_elements.Loop`

        '''
        pe = self._dbm.get_pipe_element(pe_id)

        if pe.dtype == dtype.PipeElement.SCRIPT:
            return Script(pe_id=pe_id)
        elif pe.dtype == dtype.PipeElement.ANNO_TASK:
            return pipe_elements.AnnoTask(pe, self._dbm)
        elif pe.dtype == dtype.PipeElement.DATASOURCE:
            return pipe_elements.Datasource(pe, self._dbm)
        elif pe.dtype == dtype.PipeElement.VISUALIZATION:
            return pipe_elements.VisualOutput(pe, self._dbm)
        elif pe.dtype == dtype.PipeElement.DATA_EXPORT:
            return pipe_elements.DataExport(pe, self._dbm)
        elif pe.dtype == dtype.PipeElement.LOOP:
            return pipe_elements.Loop(pe, self._dbm)
        else:
            raise Exception('Unknown pipe element type!')

    def i_am_done(self):
        if self.rejected_execution:
            self._pipe_element.state = state.PipeElement.PENDING
            self._dbm.add(self._pipe)
            self._dbm.add(self._pipe_element)
            self._dbm.commit()
            return

        #Save all changes to database
        if self._pipe_element.is_debug_mode == False:
            self._pipe_element.state = state.PipeElement.FINISHED
            self._pipe_element.progress = 100.0
            self._pipe.state = state.Pipe.IN_PROGRESS
            self._dbm.add(self._pipe)
            self._dbm.add(self._pipe_element)
            self._dbm.commit()
        else:
            answer = input("Have you finished debugging? [y/n]: ")
            if answer[0].lower() == 'y':
                self._pipe_element.state = state.PipeElement.FINISHED
                self._pipe_element.progress = 100.0
                self._pipe.state = state.Pipe.IN_PROGRESS
                self._dbm.add(self._pipe)
                self._dbm.add(self._pipe_element)
            else:
                self.outp.clean_up()
            self._pipe_man.pipe.state = state.Pipe.IN_PROGRESS
            self._dbm.commit()
        self._log_stream.close()

    def report_err(self, msg):
        '''Report an error for this user script to portal

        Args:
            msg: The error message that should be reported.

        Note:
            You can call this method multiple times if you like. All messages
            will be concatenated an sent to the portal.
        '''
        self.logger.error(msg)
        report_script_err(self._pipe_element, self._pipe, self._dbm, msg)
예제 #22
0
 def __init__(self, dbm, pipe):
     self.pipe = pipe  #type: lost.db.model.Pipe
     self.dbm = dbm  #type: lost.db.access.DBMan
     self.fm = FileMan(self.dbm.lostconfig)
예제 #23
0
#!/usr/bin/env python3

import argparse
import lostconfig as config
from lost.logic.file_man import FileMan
import logging

logging.basicConfig(level=logging.INFO, format='(%(levelname)s): %(message)s')

if __name__ == "__main__":
    parser = argparse.ArgumentParser(description='Copy example data')
    parser.add_argument('src',
                        nargs='?',
                        action='store',
                        help='Source dir with example data to copy')
    args = parser.parse_args()

    lostconfig = config.LOSTConfig()
    fm = FileMan(lostconfig)
    fm.fs.put(args.src, fm.media_root_path, recursive=True)
    logging.info('Copyed images from {} to {}'.format(args.src,
                                                      fm.media_root_path))
예제 #24
0
class PipeImporter(object):
    def __init__(self, pipe_template_dir, dbm, forTest=False):
        '''Load json file.

        Args:
            pipe_template_dir: Path to pipeline directory
        '''
        self.forTest = forTest
        self.dbm = dbm
        self.file_man = FileMan(self.dbm.lostconfig)
        if pipe_template_dir.endswith('/'):
            pipe_template_dir = pipe_template_dir[:-1]
        self.src_pipe_template_path = pipe_template_dir
        self.dst_pipe_template_path = os.path.join(
            self.file_man.pipe_path,
            os.path.basename(self.src_pipe_template_path))
        self.json_files = glob(os.path.join(pipe_template_dir, '*.json'))
        self.pipes = []
        self.namespace = os.path.basename(
            self.src_pipe_template_path).strip('/')
        for json_path in self.json_files:
            with open(json_path) as jfile:
                pipe = json.load(jfile)
            pipe['namespace'] = self.namespace
            pipe['name'] = self._namespaced_name(
                os.path.splitext(os.path.basename(json_path))[0])
            self.pipes.append(pipe)
            # Set name to name of the script file
            for pe in pipe['elements']:
                if 'script' in pe:
                    pe['script']['name'] = self._namespaced_name(
                        pe['script']['path'])
        self.checker = PipeDefChecker(logging)

    def _namespaced_name(self, name):
        return '{}.{}'.format(self.namespace, name)

    def update_pipe_project(self):

        if os.path.exists(self.dst_pipe_template_path):
            logging.info('\n\n++++++++++++++++++++++\n\n')
            for pipe in self.pipes:
                if not self.checker.check(pipe):
                    logging.error('Pipeline was not updated!')
                    return False
            for pipe in self.pipes:
                self.update_pipe(pipe)
            dir_util.copy_tree(self.src_pipe_template_path,
                               self.dst_pipe_template_path)

            logging.info(
                "Copyed pipeline template dir from %s to %s" %
                (self.src_pipe_template_path, self.dst_pipe_template_path))
        else:
            logging.warning(('Cannot update. No such pipe project: *{}*. '
                             'Maybe you want to import a pipeline instead '
                             'of updating it.').format(self.namespace))

    def update_pipe(self, pipe):
        for db_pipe in self.dbm.get_all_pipeline_templates():
            db_json = json.loads(db_pipe.json_template)
            # update pipeline if already present in db
            if db_json['name'].lower() == pipe['name'].lower():
                # Do everything relative from pipeline definition file path.
                oldwd = os.getcwd()
                os.chdir(self.src_pipe_template_path)
                logging.info('Updated pipeline: {}'.format(db_json['name']))
                for pe_j in pipe['elements']:
                    if 'script' in pe_j:
                        element_j = pe_j['script']
                        script = parse_script(element_j)
                        db_script = self.dbm.get_script(
                            name=self._get_script_name(script))
                        script_arguments = get_default_script_arguments(
                            script.path)
                        script_envs = get_default_script_envs(script.path)
                        script_resources = get_default_script_resources(
                            script.path)
                        if 'arguments' in element_j:
                            for arg in element_j['arguments']:
                                if arg not in script_arguments:
                                    logging.error(
                                        "Invalid argument >> {} << in pipeline definition json"
                                        .format(arg))
                                    valid_args = ""
                                    for v_arg in script_arguments:
                                        valid_args += ">> {} <<\n".format(
                                            v_arg)
                                    logging.error(
                                        "Valid arguments are: \n{}".format(
                                            valid_args[:-1]))
                                    raise Exception(
                                        'Invalid arguments. Start Cleanup')
                        if db_script is None:
                            self.dbm.add(script)
                            self.dbm.commit()
                            script_out_path = os.path.join(
                                self.dst_pipe_template_path, script.path)
                            script.path = self.file_man.make_path_relative(
                                script_out_path)
                            script.arguments = json.dumps(script_arguments)
                            script.envs = json.dumps(script_envs)
                            script.resources = json.dumps(script_resources)
                            self.dbm.save_obj(script)
                            logging.info("Added script to database")
                        else:
                            script_out_path = os.path.join(
                                self.dst_pipe_template_path, script.path)
                            db_script.path = self.file_man.make_path_relative(
                                script_out_path)
                            db_script.arguments = json.dumps(script_arguments)
                            db_script.envs = json.dumps(script_envs)
                            db_script.description = script.description
                            db_script.resources = json.dumps(script_resources)
                            self.dbm.save_obj(db_script)
                            logging.info('Updated script: {}'.format(
                                db_script.name))
                    # self._fix_sia_config(pe_j)
                db_pipe.json_template = json.dumps(pipe)
                self.dbm.save_obj(db_pipe)
                os.chdir(oldwd)  # Change dir back to old working directory.
                return True
        # import pipe if not already present in database
        self.import_pipe(pipe)

    def _get_script_name(self, script):
        return self._namespaced_name(os.path.basename(script.path))

    def start_import(self):
        logging.info('\n\n++++++++++++++++++++++ \n\n')
        logging.info('Start pipe project import for: {}'.format(
            self.src_pipe_template_path))
        for pipe in self.pipes:
            if not self.checker.check(pipe):
                logging.error(
                    'Wrong pipeline definition! Did not import pipe project!')
                return False
        if os.path.exists(self.dst_pipe_template_path):
            logging.warning('Cannot import pipeline!')
            logging.warning('Pipe Template Dir already exist: {}'.format(
                self.dst_pipe_template_path))
            return
        dir_util.copy_tree(self.src_pipe_template_path,
                           self.dst_pipe_template_path)
        logging.info(
            "Copyed pipeline template dir from %s to %s" %
            (self.src_pipe_template_path, self.dst_pipe_template_path))
        for pipe in self.pipes:
            self.import_pipe(pipe)

    def import_pipe(self, pipe):
        try:
            logging.info('\n---\n')
            # Do everything relative from pipeline definition file path.
            oldwd = os.getcwd()
            os.chdir(self.src_pipe_template_path)
            for db_pipe in self.dbm.get_all_pipeline_templates():
                db_json = json.loads(db_pipe.json_template)
                if db_json['name'].lower() == pipe['name'].lower():
                    logging.warning("PipeTemplate in database.")
                    logging.warning("Name of this template is: %s" %
                                    (pipe['name'], ))
                    logging.warning("Will not import PipeTemplate.")
                    return db_pipe.idx
            for pe_j in pipe['elements']:
                if 'script' in pe_j:
                    element_j = pe_j['script']
                    script = parse_script(element_j)
                    db_script = self.dbm.get_script(
                        name=self._get_script_name(script))
                    script_arguments = get_default_script_arguments(
                        script.path)
                    script_envs = get_default_script_envs(script.path)
                    script_resources = get_default_script_resources(
                        script.path)
                    if 'arguments' in element_j:
                        for arg in element_j['arguments']:
                            if arg not in script_arguments:
                                logging.error(
                                    "Invalid argument >> {} << in pipeline definition json"
                                    .format(arg))
                                valid_args = ""
                                for v_arg in script_arguments:
                                    valid_args += ">> {} <<\n".format(v_arg)
                                logging.error(
                                    "Valid arguments are: \n{}".format(
                                        valid_args[:-1]))
                                raise Exception(
                                    'Invalid arguments. Start Cleanup')
                    if db_script is None:
                        self.dbm.add(script)
                        self.dbm.commit()
                        script_out_path = os.path.join(
                            self.dst_pipe_template_path, script.path)
                        script.path = self.file_man.make_path_relative(
                            script_out_path)
                        script.arguments = json.dumps(script_arguments)
                        script.envs = json.dumps(script_envs)
                        script.resources = json.dumps(script_resources)
                        self.dbm.save_obj(script)
                        logging.info("Added script to database\n")
                    else:
                        logging.warning(
                            "Script is already present in database.\n")
                        logging.warning((str(db_script.idx), db_script.name,
                                         db_script.path))
                # self._fix_sia_config(pe_j)
            pipe_temp = model.PipeTemplate(json_template=json.dumps(pipe),
                                           timestamp=datetime.now())
            self.dbm.save_obj(pipe_temp)
            logging.info("Added Pipeline: *** %s ***" % (pipe['name'], ))
            os.chdir(oldwd)  # Change dir back to old working directory.
            return pipe_temp.idx
        except Exception as e:
            logging.error(e, exc_info=True)
            if not self.forTest:
                self.remove_pipe_project()
            logging.error('Cleanup successful. Removed buggy pipeline.')

    def remove_pipe_project(self):
        '''Remove an imported pipeline project from lost system.

        Note:
            Pipeline folder in LOST filesystem and all related db
            entrys will be deleted.
        '''
        clean_filesystem = True
        for pipe in self.pipes:
            if not self.remove_pipeline(pipe):
                clean_filesystem = False
        if clean_filesystem:
            shutil.rmtree(self.dst_pipe_template_path)
            logging.info(
                'Removed pipeline project from lost filesystem {}'.format(
                    self.dst_pipe_template_path))
            logging.info(
                'Whole pipeline project {} was successfull removed'.format(
                    self.namespace))
        else:
            logging.info('''Pipeline project {} was not completely removed 
                since some pipes are still in use'''.format(self.namespace))

    def remove_pipeline(self, pipe):
        '''Remove all related db entrys of a pipeline from lost database.
        '''
        #TODO: Remove script
        for db_pipe in self.dbm.get_all_pipeline_templates():
            db_json = json.loads(db_pipe.json_template)
            if db_json['name'].lower() == pipe['name'].lower():
                t = self.dbm.get_pipe(pipe_template_id=db_pipe.idx)
                if t is None:
                    for pe_j in db_json['elements']:
                        if 'script' in pe_j:
                            script_man.remove_script(
                                self.dbm,
                                os.path.join(
                                    self.namespace,
                                    os.path.basename(pe_j['script']['path'])))
                    self.dbm.delete(db_pipe)
                else:
                    logging.warning(
                        "Cannot remove pipeline. It is already in use by task with ID: %s"
                        % (t.idx, ))
                    return False
                self.dbm.commit()
                logging.info("Removed pipeline successfull: {}".format(
                    pipe['name']))
                return True
        return True
예제 #25
0
class PipeInstance(object):
    '''Model a Pipeline instance within LOST.
    '''
    def __init__(self, dbm, pipe):
        self.pipe = pipe  #type: lost.db.model.Pipe
        self.dbm = dbm  #type: lost.db.access.DBMan
        self.fm = FileMan(self.dbm.lostconfig)

    def _delete_result_links(self, pe):
        for result_link in self.dbm.get_resultlinks_pe_n(pe_n_id=pe.idx):
            self.dbm.delete(result_link)
        self.dbm.commit()

    def _delete_results(self, pe):
        '''Delete results of pe and all related data.

        Args:
            pe (:class:`lost.db.model.PipeElement`): PipeElement where 
                results should be deleted for.
        
        Note:
            Only scripts contain their own Results
            for all other elements results are just 
            just looped through from Scripts. 
        '''
        pe = pe  #type: lost.db.model.PipeElement
        #Only scripts contain their own Results
        #all other elements just loop through results from Scripts.
        if pe.dtype == dtype.PipeElement.SCRIPT:
            for res in pe.result_out:
                #Delete annotations
                for img_anno in res.img_annos:
                    for twod_anno in img_anno.twod_annos:
                        self.dbm.delete(twod_anno.label)
                        self.dbm.delete(twod_anno)
                    self.dbm.delete(img_anno)
                #Delete visual outputs
                for vis_out in res.visual_outputs:
                    self.dbm.delete(vis_out)
                #Delete data exports
                for de in res.data_exports:
                    self.dbm.delete(de)
                self.dbm.delete(res)
            self.dbm.commit()

    def delete_pipeline(self):
        '''Delete this pipeline instance from LOST'''
        for pe in self.pipe.pe_list:
            self._delete_result_links(pe)
        for pe in self.pipe.pe_list:
            self._delete_results(pe)
        for pe in self.pipe.pe_list:
            self._delete_pe(pe)
        self.fm.rm_pipe_log_path(self.pipe)
        self.fm.rm_pipe_context_path(self.pipe)
        self.dbm.delete(self.pipe)
        self.dbm.commit()

    def _delete_pe(self, pe):
        '''Delete a PipeElement of this pipeline
        
        Args:
            pe (:class:`lost.db.model.PipeElement`): The pipeline element
                to delete.
        '''
        pe = pe  #type: lost.db.model.PipeElement
        if pe.dtype == dtype.PipeElement.SCRIPT:
            self.dbm.delete(pe.script)
            self.fm.rm_instance_path(pe)
        elif pe.dtype == dtype.PipeElement.ANNO_TASK:
            for req_leaf in pe.anno_task.req_label_leaves:
                self.dbm.delete(req_leaf)
            self.dbm.delete(pe.anno_task)
            self.dbm.commit()
        elif pe.dtype == dtype.PipeElement.DATASOURCE:
            self.dbm.delete(pe.datasource)
        elif pe.dtype == dtype.PipeElement.LOOP:
            self.dbm.delete(pe.loop)
        elif pe.dtype == dtype.PipeElement.DATA_EXPORT:
            pass
        elif pe.dtype == dtype.PipeElement.VISUALIZATION:
            pass
        else:
            raise Exception('Unknown dtype for PipeElement!')
        self.dbm.delete(pe)
        self.dbm.commit()
예제 #26
0
파일: mia.py 프로젝트: wanlipeng/lost
class TwoDSerialize(object):
    def __init__(self,
                 db_man,
                 annos,
                 user_id,
                 anno_task_id,
                 proposedLabel=True):
        self.mia_json = dict()
        self.db_man = db_man
        self.annos = annos
        self.user_id = user_id
        self.anno_task_id = anno_task_id
        self.file_man = FileMan(self.db_man.lostconfig)
        self.proposedLabel = proposedLabel

    def serialize(self):
        directory = self.file_man.get_mia_crop_path(self.anno_task_id)
        self.mia_json['images'] = list()
        self.mia_json['proposedLabel'] = None
        if self.proposedLabel:
            self.mia_json['proposedLabel'] = get_proposed_label(
                self.db_man, self.annos[0], self.user_id)
        for anno in self.annos:
            image_json = dict()
            image_json['id'] = anno.idx
            # get image_anno of two_d anno
            image_anno = self.db_man.get_image_annotation(
                img_anno_id=anno.img_anno_id)
            cropped_image_path = os.path.join(directory, str(
                anno.idx)) + '.png'
            relative_cropped_image_path = self.file_man.mia_crop_rel_path + \
            str(self.anno_task_id) + "/" + str(anno.idx) + ".png"
            if os.path.exists(cropped_image_path):
                image_json['path'] = relative_cropped_image_path
                self.mia_json['images'].append(image_json)
                continue
            else:
                # crop two_d_anno out of image_anno
                config = get_config(self.db_man, self.user_id)
                draw_anno = False
                context = None
                try:
                    draw_anno = config['drawAnno']
                except:
                    pass
                try:
                    context = float(config['addContext'])
                except:
                    pass
                self._crop_twod_anno(image_anno, anno, draw_anno, context,
                                     cropped_image_path)
                image_json['path'] = relative_cropped_image_path
                self.mia_json['images'].append(image_json)

    def _crop_twod_anno(self, img_anno, twod_anno, draw_anno, context,
                        out_path):
        '''Helper method to crop a bounding box for a TwoDAnnotation and store it on disc
        
        Args:
            img_anno (:class:`model.ImageAnno`): The ImageAnno where the twod_anno belongs to.
            twod_anno (:class:`model.TwoDAnno`): The 2D-anno to crop.
            draw_anno (bool): Indicates wether the annotation should be painted inside the crop.
            context (float): Value that indicates how much context should 
                be cropped around the 2D annotation.  
            out_path (str): Path to store the cropped image.
        '''
        image = skimage.io.imread(self.file_man.get_abs_path(
            img_anno.img_path))
        crops, _ = anno_helper.crop_boxes([twod_anno.to_vec('anno.data')],
                                          [twod_anno.to_vec('anno.dtype')],
                                          image,
                                          context=context,
                                          draw_annotations=draw_anno)
        cropped_image = crops[0]
        skimage.io.imsave(out_path, cropped_image)
예제 #27
0
class PipeEngine(pipe_model.PipeEngine):
    def __init__(self, dbm, pipe, lostconfig):
        '''
        :type dbm: lost.db.access.DBMan
        :type pipe: lost.db.model.Pipe
        '''
        super().__init__(dbm=dbm, pipe=pipe)
        self.lostconfig = lostconfig  #type: lost.logic.config.LOSTConfig
        self.file_man = FileMan(self.lostconfig)
        # self.logger = lost.logic.log.get_file_logger(
        #     'Executor: {}'.format(self.lostconfig.env),
        #     self.file_man.app_log_path)
        self.logger = get_task_logger(__name__)

    def process_annotask(self, pipe_e):
        anno_task = self.dbm.get_anno_task(pipe_element_id=pipe_e.idx)
        if anno_task.state == state.AnnoTask.IN_PROGRESS or \
           anno_task.state == state.AnnoTask.PAUSED:
            if not at_man.has_annotation(self.dbm, anno_task.idx):
                at_man.set_finished(self.dbm, anno_task.idx)
                self.logger.warning('No Annotations have been requested for AnnoTask {}'\
                    .format(anno_task.idx))
                self.logger.warning("%d: AnnoTask has been finished (ID: %d, Name: %s)"\
                                %(self.pipe.idx, anno_task.idx, anno_task.name))
            # if pipe_e.anno_task.dtype == dtype.AnnoTask.MIA:
            #     if anno_task.progress is None:
            #         anno_task.progress = 0.0
            #     if anno_task.progress >= 100.0:
            #          anno_task.state = state.AnnoTask.FINISHED
            #          self.dbm.add(anno_task)
            #          pipe_e.state = state.PipeElement.FINISHED
            #          self.dbm.add(pipe_e)
            #          self.dbm.commit()
            #          print("%d: AnnoTask has been finished (ID: %d, Name: %s)"\
            #                       %(self.pipe.idx, anno_task.idx, anno_task.name))
            #     else:
            #         return

        # state = finished will be set in annotation tool
        if anno_task.state == state.AnnoTask.PENDING:
            anno_task.state = state.AnnoTask.IN_PROGRESS
            self.dbm.save_obj(anno_task)
            self.logger.info("%d: AnnoTask IN_PROGRESS (ID: %d, Name: %s)"\
                         %(self.pipe.idx, anno_task.idx, anno_task.name))

    def __gen_run_cmd(self, program, pipe_e):
        # script = self.dbm.get_script(pipe_e.script_id)
        script_path = os.path.join(self.lostconfig.project_path,
                                   pipe_e.script.path)
        cmd = self.lostconfig.py3_init + " && "
        cmd += program + " " + script_path + " --idx " + str(pipe_e.idx)
        return cmd

    def make_debug_session(self, pipe_e):
        debug_path = self.file_man.create_debug_path(pipe_element=pipe_e)
        debug_file_path = os.path.join(debug_path, 'debug.sh')
        # init = self.lostconfig.py3_init + '\n'
        cmd = self.__gen_run_cmd('pudb3', pipe_e)
        # script_content = init + cmd
        script_content = cmd
        with open(debug_file_path, 'w') as dfile:
            dfile.write(script_content)
        script_path = os.path.join(self.lostconfig.project_path,
                                   pipe_e.script.path)
        dsession_str = "For DEBUG start: bash " + debug_file_path
        dsession_str += "<br>If you want to EDIT go to: " + script_path
        pipe_e.debug_session = dsession_str
        self.dbm.save_obj(pipe_e)
        self.logger.info('Created debug script: {}'.format(debug_file_path))
        self.logger.info(pipe_e.debug_session)

    def __release_loop_iteration(self, pipe_e):
        pipe_e.loop.iteration += 1
        self.logger.info('{}: Run loop with id {} in iteration {}'.format(
            self.pipe.idx, pipe_e.loop.idx, pipe_e.loop.iteration))
        loop_pes = self.get_loop_pes(pipe_e.loop.pe_jump, pipe_e)
        for pe in loop_pes:
            pe.iteration += 1
            pe.state = state.PipeElement.PENDING
            if pe.dtype == dtype.PipeElement.ANNO_TASK:
                pe.anno_task.state = state.AnnoTask.PENDING
            elif pe.dtype == dtype.PipeElement.SCRIPT:
                pe.progress = 0.0
            elif pe.dtype == dtype.PipeElement.LOOP:
                # Check for loop in loop case; Set iteration of all inner loops
                # to zero.
                if pe is not pipe_e:
                    pe.loop.iteration = 0
            self.set_to_visit(pe)
            self.dbm.add(pe)

    def process_loop(self, pipe_e):
        if pipe_e.loop.break_loop:
            pipe_e.state = state.PipeElement.FINISHED
            self.dbm.add(pipe_e)
            self.logger.info('{}: Break loop with id {}'.format(
                self.pipe.idx, pipe_e.loop.idx))
            return
        if pipe_e.loop.max_iteration is not None:
            if pipe_e.loop.iteration is None:
                pipe_e.loop.iteration = 0
            if pipe_e.loop.iteration < pipe_e.loop.max_iteration - 1:
                self.__release_loop_iteration(pipe_e)
            else:
                pipe_e.state = state.PipeElement.FINISHED
                self.logger.info('{}: Loop ({}) terminated. Max iterations = {}'\
                             .format(self.pipe.idx, pipe_e.loop.idx,
                                     pipe_e.loop.max_iteration))
        else:
            self.__release_loop_iteration(pipe_e)
        self.dbm.add(pipe_e)

    def select_env_for_script(self, pipe_e):
        '''Select an environment where the script should be executed'''
        w_man = WorkerMan(self.dbm, self.lostconfig)
        if pipe_e.script.envs is not None:
            script_envs = json.loads(pipe_e.script.envs)
            if len(script_envs) == 0:
                return 'celery'
        else:
            script_envs = list()
            return 'celery'  # Return default queue
        worker_envs = w_man.get_worker_envs()
        for script_env in script_envs:
            if script_env in worker_envs:
                return script_env
        self.logger.warning('No suitable env to execute script: {}'.format(
            pipe_e.script.path))
        return None

    def process_pipe_element(self):
        pipe_e = self.get_next_element()
        while (pipe_e is not None):
            # if pipe_e is None:
            #     return
            if pipe_e.dtype == dtype.PipeElement.SCRIPT:
                if pipe_e.state != state.PipeElement.SCRIPT_ERROR:
                    # if pipe_e.is_debug_mode:
                    #     pipe_e.state = state.PipeElement.IN_PROGRESS
                    #     self.dbm.save_obj(pipe_e)
                    #     self.make_debug_session(pipe_e)
                    # else:
                    if pipe_e.state == state.PipeElement.PENDING:
                        env = self.select_env_for_script(pipe_e)
                        if env is None:
                            return
                        celery_exec_script.apply_async(args=[pipe_e.idx],
                                                       queue=env)
            elif pipe_e.dtype == dtype.PipeElement.ANNO_TASK:
                if pipe_e.state == state.PipeElement.PENDING:
                    update_anno_task(self.dbm, pipe_e.anno_task.idx)
                    try:
                        email.send_annotask_available(self.dbm,
                                                      pipe_e.anno_task)
                    except:
                        msg = "Could not send Email. \n"
                        msg += traceback.format_exc()
                        self.logger.error(msg)
                    pipe_e.state = state.PipeElement.IN_PROGRESS
                    self.dbm.save_obj(pipe_e)
                self.process_annotask(pipe_e)
            elif pipe_e.dtype == dtype.PipeElement.DATASOURCE:
                pipe_e.state = state.PipeElement.FINISHED
                self.dbm.save_obj(pipe_e)
            elif pipe_e.dtype == dtype.PipeElement.VISUALIZATION:
                pipe_e.state = state.PipeElement.FINISHED
                self.dbm.save_obj(pipe_e)
            elif pipe_e.dtype == dtype.PipeElement.DATA_EXPORT:
                pipe_e.state = state.PipeElement.FINISHED
                self.dbm.save_obj(pipe_e)
            elif pipe_e.dtype == dtype.PipeElement.LOOP:
                self.process_loop(pipe_e)
                self.dbm.commit()
            pipe_e = self.get_next_element()

    def process_pipeline(self):
        try:
            p = self.pipe
            # print('Process pipe: {}'.format(self.pipe.name))
            if p.is_locked is None:
                p.is_locked = False
            if not p.is_locked:
                p.is_locked = True
                self.dbm.save_obj(p)
            else:
                return
            if p.state == state.Pipe.PENDING:
                p.state = state.Pipe.IN_PROGRESS
                self.dbm.save_obj(p)
                self.process_pipe_element()
            elif p.state == state.Pipe.IN_PROGRESS:
                self.process_pipe_element()
            elif p.state == state.Pipe.FINISHED:
                return
            elif p.state == state.Pipe.ERROR:
                self.__report_error(p)
            else:
                raise Exception("Unknown PipeState!")
            p.is_locked = False
            self.dbm.save_obj(p)
        except:
            p.is_locked = False
            self.dbm.save_obj(p)
            raise

    def get_next_element(self):
        pe_wait = None
        for candidate in self.get_to_visit():
            if candidate is None:
                if self.pipe_finished():
                    self.pipe.state = state.Pipe.FINISHED
                    self.pipe.timestamp_finished = datetime.now()
                    self.dbm.save_obj(self.pipe)
                    self.logger.info("%d: Task is finished (Name: %s)" %
                                     (self.pipe.idx, self.pipe.name))
                    try:
                        email.send_pipeline_finished(self.pipe)
                    except:
                        msg = "Could not send Email. \n"
                        msg += traceback.format_exc()
                        self.logger.error(msg)
                    return None
                else:
                    continue
            else:
                pe = self.check_candiate(candidate)
                if pe is None:
                    continue
                #If there is a loop under candidates, it should be executed as
                #last possible element. Since a loop will set all other elements
                #within the loop to pending when processed. So if the last element
                #before the loop has subsequent elements. These elements would never
                #be executed since the loop would set the last element in the loop
                #to pending.
                elif pe.dtype == dtype.PipeElement.LOOP:
                    pe_wait = pe
                    continue
                else:
                    self.set_visited(pe)
                    return pe
        return pe_wait

    def pipe_finished(self):
        for pe in self.get_final_pes():
            if pe.state != state.PipeElement.FINISHED:
                return False
        return True

    def check_candiate(self, candidate):
        # If all prev elements are finished return candidate
        for pe_prev in self.get_prev_pes(candidate):
            if pe_prev is not None:
                if pe_prev.state != state.PipeElement.FINISHED:
                    return None
                # if pe_prev.state == state.PipeElement.FINISHED:
                #     if candidate.state == state.PipeElement.PENDING:
                #         return candidate
                #     elif candidate.dtype == dtype.PipeElement.ANNOTATION_TASK and\
                #         candidate.state == state.PipeElement.IN_PROGRESS:
                #         return candidate
            else:
                # if pe_prev is None and candidate.state == PENDING
                if candidate.state == state.PipeElement.PENDING:
                    return candidate
        return candidate

    def __report_error(self, pipe):
        for pipe_element in self.dbm.get_script_errors(pipe.idx):
            # Send mail to inform user about script error.
            try:
                email.send_script_error(pipe, pipe_element)
                pipe_element.error_reported = True
                self.dbm.add(pipe_element)
                self.dbm.commit()
            except:
                pipe_element.error_reported = True
                pipe_element.error_msg += traceback.format_exc()
                self.dbm.add(pipe_element)
                self.dbm.commit()