Esempio n. 1
0
class LabelResource(Resource):

    def __init__(self):
        super().__init__()
        self.resourceName = 'label'

        self.ann_file_name = "annotation.json"

        self.coll_m = Collection()
        self.file_m = File()
        self.folder_m = Folder()
        self.item_m = Item()
        self.upload_m = Upload()
        self.asset_m = Assetstore()

        self.setupRoutes()

    def setupRoutes(self):
        self.route('GET', (), handler=self.getLabelList)
        self.route('GET', (':label_id',), self.getLabel)
        self.route('GET', ('meta',), self.getLabelMeta)
        self.route('GET', ('create',), self.createLabelFile)
        self.route('GET', ('by_name',), self.getLabelByName)
        self.route('POST', (), self.postLabel)

    # ############# PUBLIC METHODS ##################

    @access.public
    @autoDescribeRoute(
        Description('Get label list').param('assign_id', 'assignment folder id'))
    @rest.rawResponse
    @trace
    def getLabelList(self, assign_id):
        files = self.folder_m.fileList(assign_id, user=self.getCurrentUser(), data=False,
                                       includeMetadata=True, mimeFilter=['application/json'])
        files = list(files)
        cherrypy.response.headers["Content-Type"] = "application/json"
        return dumps(files)

    @access.public
    @autoDescribeRoute(
        Description('Create a new label file inside the label folder if it doesnt exist')
            .param('assign_id', 'the parent folder id')
            .param('name', 'image name for which we are creating this label file'))
    @rest.rawResponse
    @trace
    def createLabelFile(self, assign_id, name):
        p_folder = self.folder_m.load(id=assign_id,
                                      user=self.getCurrentUser(),
                                      level=AccessType.WRITE)

        label_folder = find_folder(p_folder=p_folder,
                                   name=name,
                                   user=self.getCurrentUser(),
                                   desc="Label Folder",
                                   create=True)

        file = find_file(p_folder=label_folder,
                         name=self.ann_file_name,
                         user=self.getCurrentUser(),
                         assetstore=self.asset_m.getCurrent(),
                         create=False)

        if not file:
            file = create_new_file(p_folder=label_folder,
                                   name=self.ann_file_name,
                                   user=self.getCurrentUser(),
                                   assetstore=self.asset_m.getCurrent())

            config_file = self.__find_config(assign_id)

            if not config_file:
                printFail("No config file found")
                return errorMessage("No config file found")
            else:
                res = copy_file(src_file=config_file,
                                dest_file=file,
                                user=self.getCurrentUser())
                return dumps({
                    "label_id": res['fileId']
                })

        return dumps({
            "label_id": file['_id']
        })

    @access.public
    @autoDescribeRoute(
        Description('Get labels by file_name')
            .param('name', 'label file name')
            .param('assign_id', 'the assignment id'))
    @rest.rawResponse
    @trace
    def getLabelByName(self, name, assign_id):
        p_folder = self.folder_m.load(assign_id,
                                      user=self.getCurrentUser(),
                                      level=AccessType.READ)

        label_folder = find_folder(p_folder=p_folder,
                                   name=name,
                                   user=self.getCurrentUser(),
                                   desc="Label Folder",
                                   create=True)

        # this file is created in <assign_folder>/<label_folder>/assignment.json
        file = find_file(p_folder=label_folder,
                         name=self.ann_file_name,
                         user=self.getCurrentUser(),
                         assetstore=self.asset_m.getCurrent(),
                         create=False)

        cherrypy.response.headers["Content-Type"] = "application/json"
        if file:
            return self.file_m.download(file)
        else:
            return dumps({})

    @access.public
    @autoDescribeRoute(
        Description('Get label file by id')
            .param('label_id', 'label file id'))
    @rest.rawResponse
    @trace
    def getLabel(self, label_id):
        file = self.file_m.load(label_id, level=AccessType.READ, user=self.getCurrentUser())
        printOk2(file)
        cherrypy.response.headers["Content-Type"] = "application/json"
        return self.file_m.download(file)

    @access.public
    @autoDescribeRoute(
        Description('Get label meta data by id')
            .param('label_id', 'label file id'))
    @trace
    def getLabelMeta(self, label_id):
        file = self.file_m.load(label_id, level=AccessType.READ, user=self.getCurrentUser())
        cherrypy.response.headers["Content-Type"] = "application/json"
        return dumps(file)

    @access.public
    @autoDescribeRoute(
        Description('Post to label file by id')
            .param('label_id', 'label file id')
            .param('labels', 'labels to be updated'))
    @rest.rawResponse
    @trace
    def postLabel(self, label_id, labels):
        file = self.file_m.load(label_id, level=AccessType.WRITE, user=self.getCurrentUser())
        cherrypy.response.headers["Content-Type"] = "application/json"
        params = {'labels': json.loads(labels)}
        data = json.dumps(params, indent=2, sort_keys=True)
        upload = writeData(self.getCurrentUser(), file, data)
        printOk2(file)
        printOk(upload)
        return dumps(upload)

    # ############# PRIVATE METHODS ##################

    def __create_new_file(self, folder, file_name):
        item = self.item_m.createItem(file_name,
                                      creator=self.getCurrentUser(),
                                      folder=folder,
                                      description='label file',
                                      reuseExisting=False)

        file = self.file_m.createFile(size=0,
                                      item=item,
                                      name=file_name,
                                      creator=self.getCurrentUser(),
                                      assetstore=self.asset_m.getCurrent(),
                                      mimeType="application/json")
        return file

    @staticmethod
    def __get_owner_id(folder):
        aclList = Folder().getFullAccessList(folder)
        for acl in aclList['users']:
            if acl['level'] == AccessType.ADMIN:
                return str(acl['id'])
        return None

    def __get_config_folder(self, label_folder_id):
        label_folder = Folder().load(label_folder_id,
                                     user=self.getCurrentUser(),
                                     level=AccessType.READ)
        ownerId = self.__get_owner_id(label_folder)
        config_folder = self.folder_m.load(label_folder['meta'][ownerId], level=AccessType.READ,
                                           user=self.getCurrentUser())
        return config_folder

    def __find_config(self, folder_id):
        folder = self.__get_config_folder(folder_id)
        printOk2("Config folder {}".format(folder))
        files = self.folder_m.fileList(folder, self.getCurrentUser(), data=False)
        for file_path, file in files:
            printOk(file)
            if file['name'] == "config.json":
                return file

    def __findFolder(self, p_folder, name, desc="", create=False):
        """
        Find folder by name. If not found create the folder
        :param p_folder: parent folder
        :param name: name of the folder you want to find inside the parent folder
        :return: folder doc
        """
        folder = list(self.folder_m.find({'folderId': p_folder['_id'], 'name': name}).limit(1))
        if not folder:
            # check if you are allowed to create, else return nothing
            if create:
                folder = self.folder_m.createFolder(parent=p_folder,
                                                    name=name,
                                                    creator=self.getCurrentUser(),
                                                    description=desc,
                                                    reuseExisting=True)
            else:
                return None

        return folder

    def __findFile(self, folder, file_name):
        item = list(self.item_m.find({'folderId': folder['_id'], 'name': file_name}).limit(1))
        if not item:
            return None

        item = item[0]
        file = list(self.file_m.find({'itemId': item['_id']}).limit(1))

        if not file:
            return None

        return file[0]
Esempio n. 2
0
class LabelImageResource(Resource):
    def __init__(self):
        super().__init__()
        self.resourceName = 'labelImage'

        self.coll_m = Collection()
        self.file_m = File()
        self.folder_m = Folder()
        self.item_m = Item()
        self.upload_m = Upload()
        self.asset_m = Assetstore()

        self.label_image_folder_name = "LabelImages"

        self.setupRoutes()

    def setupRoutes(self):
        self.route('GET', (), handler=self.getList)
        self.route('POST', (), handler=self.post)
        self.route('GET', ("download", ), handler=self.download)

    @access.public
    @autoDescribeRoute(Description('Get label Image list'))
    @rest.rawResponse
    @trace
    def getList(self):
        printOk2("get label image called")
        collection = list(
            self.coll_m.list(user=self.getCurrentUser(), offset=0, limit=1))[0]
        files = self.coll_m.fileList(collection,
                                     user=self.getCurrentUser(),
                                     data=False,
                                     includeMetadata=True,
                                     mimeFilter=['application/png'])
        files = list(files)
        cherrypy.response.headers["Content-Type"] = "application/png"
        return dumps(files)

    @staticmethod
    def getOwnerId(folder):
        aclList = Folder().getFullAccessList(folder)
        for acl in aclList['users']:
            if acl['level'] == AccessType.ADMIN:
                return str(acl['id'])
        return None

    def getConfigFolder(self, label_folder_id):
        label_folder = Folder().load(label_folder_id,
                                     user=self.getCurrentUser(),
                                     level=AccessType.READ)
        ownerId = self.getOwnerId(label_folder)
        config_folder = self.folder_m.load(label_folder['meta'][ownerId],
                                           level=AccessType.READ,
                                           user=self.getCurrentUser())
        return config_folder

    def findConfig(self, folder_id):
        folder = self.getConfigFolder(folder_id)
        printOk2("Config folder {}".format(folder))
        files = self.folder_m.fileList(folder,
                                       self.getCurrentUser(),
                                       data=False)
        for file_path, file in files:
            printOk(file)
            if file['name'] == "config.json":
                return file

    @access.public
    @autoDescribeRoute(
        Description(
            'Create a new label image file if it doesnt exist, else update').
        param('label_name', 'label name').param(
            'image_name', 'The original image that this belongs to').param(
                'assign_id',
                'the assignment folder id').param('image',
                                                  'image in string64'))
    @rest.rawResponse
    @trace
    def post(self, label_name, image_name, assign_id, image):
        p_folder = self.folder_m.load(assign_id,
                                      user=self.getCurrentUser(),
                                      level=AccessType.WRITE)

        label_folder = find_folder(p_folder=p_folder,
                                   name=image_name,
                                   user=self.getCurrentUser(),
                                   create=True)

        label_image_folder = find_folder(p_folder=label_folder,
                                         name=self.label_image_folder_name,
                                         user=self.getCurrentUser(),
                                         create=True)
        safe_label_name = label_name.replace("/", "_")
        file_name = ".".join([safe_label_name, 'png'])
        file = find_file(p_folder=label_image_folder,
                         name=file_name,
                         user=self.getCurrentUser(),
                         assetstore=self.asset_m.getCurrent(),
                         create=True)

        # remove data:image/png;base64,
        image = image.split(',')[1]
        image = base64.b64decode(image)
        # image = decode_base64(image)
        upload = writeBytes(self.getCurrentUser(), file, image)
        return dumps({"label_image_file": upload['fileId']})

    def __downloadFolder(self, folder):
        pass

    @access.public
    @autoDescribeRoute(
        Description(
            "download label images using image id. Returns a stream to the zip file. "
        ).param('assign_id', 'id of the assignment').param(
            'image_name',
            'name of the images whose label images you want to download'))
    @rest.rawResponse
    @trace
    def downloadAssignment(self, assign_id):
        assignment = self.folder_m.load(assign_id,
                                        user=self.getCurrentUser(),
                                        level=AccessType.WRITE)
        # find the label image folder

        return self.__downloadFolder(assignment)

    @access.public
    @autoDescribeRoute(
        Description(
            "download the full collection. Returns a stream to the zip file. "
        ).param('assign_id', 'id of the assignment').param(
            'image_name',
            'name of the images whose label images you want to download'))
    @rest.rawResponse
    @trace
    def downloadCollection(self):
        collection = list(
            self.coll_m.list(user=self.getCurrentUser(), offset=0, limit=1))[0]

        # find the label image folder

        return self.__downloadFolder(collection)

    @access.cookie
    @access.public(scope=TokenScope.DATA_READ)
    @autoDescribeRoute(
        Description(
            "download label images using image id. Returns a stream to the zip file. "
        ).param('assign_id', 'id of the assignment').param(
            'image_name',
            'name of the images whose label images you want to download').
        produces('application/zip'))
    @rest.rawResponse
    @trace
    def download(self, assign_id, image_name):

        assignment = self.folder_m.load(assign_id,
                                        user=self.getCurrentUser(),
                                        level=AccessType.READ)

        label_folder = find_folder(p_folder=assignment,
                                   name=image_name,
                                   user=self.getCurrentUser(),
                                   create=True)

        folder = find_folder(p_folder=label_folder,
                             name=self.label_image_folder_name,
                             user=self.getCurrentUser(),
                             create=True)

        printOk(folder)

        setResponseHeader('Content-Type', 'application/zip')
        setContentDisposition(label_folder['name'] + '.zip')
        user = self.getCurrentUser()

        def stream():
            zip = ziputil.ZipGenerator(folder['name'])
            for (path, file) in self.folder_m.fileList(folder,
                                                       user=user,
                                                       subpath=False):
                for data in zip.addFile(file, path):
                    yield data
            yield zip.footer()

        return stream
Esempio n. 3
0
class Folder(Resource):
    """API Endpoint for folders."""

    def __init__(self):
        super(Folder, self).__init__()
        self.resourceName = 'folder'
        self._model = FolderModel()
        self.route('DELETE', (':id',), self.deleteFolder)
        self.route('DELETE', (':id', 'contents'), self.deleteContents)
        self.route('GET', (), self.find)
        self.route('GET', (':id',), self.getFolder)
        self.route('GET', (':id', 'details'), self.getFolderDetails)
        self.route('GET', (':id', 'access'), self.getFolderAccess)
        self.route('GET', (':id', 'download'), self.downloadFolder)
        self.route('GET', (':id', 'rootpath'), self.rootpath)
        self.route('POST', (), self.createFolder)
        self.route('PUT', (':id',), self.updateFolder)
        self.route('PUT', (':id', 'access'), self.updateFolderAccess)
        self.route('POST', (':id', 'copy'), self.copyFolder)
        self.route('PUT', (':id', 'metadata'), self.setMetadata)
        self.route('DELETE', (':id', 'metadata'), self.deleteMetadata)

    @access.public(scope=TokenScope.DATA_READ)
    @filtermodel(model=FolderModel)
    @autoDescribeRoute(
        Description('Search for folders by certain properties.')
        .notes('You must pass either a "folderId" or "text" field '
               'to specify how you are searching for folders.  '
               'If you omit one of these parameters the request will fail and respond : '
               '"Invalid search mode."')
        .responseClass('Folder', array=True)
        .param('parentType', "Type of the folder's parent", required=False,
               enum=['folder', 'user', 'collection'])
        .param('parentId', "The ID of the folder's parent.", required=False)
        .param('text', 'Pass to perform a text search.', required=False)
        .param('name', 'Pass to lookup a folder by exact name match. Must '
               'pass parentType and parentId as well when using this.', required=False)
        .pagingParams(defaultSort='lowerName')
        .errorResponse()
        .errorResponse('Read access was denied on the parent resource.', 403)
    )
    def find(self, parentType, parentId, text, name, limit, offset, sort):
        """
        Get a list of folders with given search parameters. Currently accepted
        search modes are:

        1. Searching by parentId and parentType, with optional additional
           filtering by the name field (exact match) or using full text search
           within a single parent folder. Pass a "name" parameter or "text"
           parameter to invoke these additional filters.
        2. Searching with full text search across all folders in the system.
           Simply pass a "text" parameter for this mode.
        """
        user = self.getCurrentUser()

        if parentType and parentId:
            parent = ModelImporter.model(parentType).load(
                parentId, user=user, level=AccessType.READ, exc=True)

            filters = {}
            if text:
                filters['$text'] = {
                    '$search': text
                }
            if name:
                filters['name'] = name

            return self._model.childFolders(
                parentType=parentType, parent=parent, user=user,
                offset=offset, limit=limit, sort=sort, filters=filters)
        elif text:
            return self._model.textSearch(
                text, user=user, limit=limit, offset=offset, sort=sort)
        else:
            raise RestException('Invalid search mode.')

    @access.public(scope=TokenScope.DATA_READ)
    @autoDescribeRoute(
        Description('Get detailed information about a folder.')
        .modelParam('id', model=FolderModel, level=AccessType.READ)
        .errorResponse()
        .errorResponse('Read access was denied on the folder.', 403)
    )
    def getFolderDetails(self, folder):
        return {
            'nItems': self._model.countItems(folder),
            'nFolders': self._model.countFolders(
                folder, user=self.getCurrentUser(), level=AccessType.READ)
        }

    @access.public(scope=TokenScope.DATA_READ, cookie=True)
    @autoDescribeRoute(
        Description('Download an entire folder as a zip archive.')
        .modelParam('id', model=FolderModel, level=AccessType.READ)
        .jsonParam('mimeFilter', 'JSON list of MIME types to include.', required=False,
                   requireArray=True)
        .produces('application/zip')
        .errorResponse('ID was invalid.')
        .errorResponse('Read access was denied for the folder.', 403)
    )
    def downloadFolder(self, folder, mimeFilter):
        """
        Returns a generator function that will be used to stream out a zip
        file containing this folder's contents, filtered by permissions.
        """
        setResponseHeader('Content-Type', 'application/zip')
        setContentDisposition(folder['name'] + '.zip')
        user = self.getCurrentUser()

        def stream():
            zip = ziputil.ZipGenerator(folder['name'])
            for (path, file) in self._model.fileList(
                    folder, user=user, subpath=False, mimeFilter=mimeFilter):
                for data in zip.addFile(file, path):
                    yield data
            yield zip.footer()
        return stream

    @access.user(scope=TokenScope.DATA_WRITE)
    @filtermodel(model=FolderModel)
    @autoDescribeRoute(
        Description('Update a folder or move it into a new parent.')
        .responseClass('Folder')
        .modelParam('id', model=FolderModel, level=AccessType.WRITE)
        .param('name', 'Name of the folder.', required=False, strip=True)
        .param('description', 'Description for the folder.', required=False, strip=True)
        .param('parentType', "Type of the folder's parent", required=False,
               enum=['folder', 'user', 'collection'], strip=True)
        .param('parentId', 'Parent ID for the new parent of this folder.', required=False)
        .jsonParam('metadata', 'A JSON object containing the metadata keys to add',
                   paramType='form', requireObject=True, required=False)
        .errorResponse('ID was invalid.')
        .errorResponse('Write access was denied for the folder or its new parent object.', 403)
    )
    def updateFolder(self, folder, name, description, parentType, parentId, metadata):
        user = self.getCurrentUser()
        if name is not None:
            folder['name'] = name
        if description is not None:
            folder['description'] = description

        folder = self._model.updateFolder(folder)
        if metadata:
            folder = self._model.setMetadata(folder, metadata)

        if parentType and parentId:
            parent = ModelImporter.model(parentType).load(
                parentId, level=AccessType.WRITE, user=user, exc=True)
            if (parentType, parent['_id']) != (folder['parentCollection'], folder['parentId']):
                folder = self._model.move(folder, parent, parentType)

        return folder

    @access.user(scope=TokenScope.DATA_OWN)
    @filtermodel(model=FolderModel, addFields={'access'})
    @autoDescribeRoute(
        Description('Update the access control list for a folder.')
        .modelParam('id', model=FolderModel, level=AccessType.ADMIN)
        .jsonParam('access', 'The JSON-encoded access control list.', requireObject=True)
        .jsonParam('publicFlags', 'JSON list of public access flags.', requireArray=True,
                   required=False)
        .param('public', 'Whether the folder should be publicly visible.',
               dataType='boolean', required=False)
        .param('recurse', 'Whether the policies should be applied to all '
               'subfolders under this folder as well.', dataType='boolean',
               default=False, required=False)
        .param('progress', 'If recurse is set to True, this controls whether '
               'progress notifications will be sent.', dataType='boolean',
               default=False, required=False)
        .errorResponse('ID was invalid.')
        .errorResponse('Admin access was denied for the folder.', 403)
    )
    def updateFolderAccess(self, folder, access, publicFlags, public, recurse, progress):
        user = self.getCurrentUser()
        progress = progress and recurse  # Only enable progress in recursive case
        with ProgressContext(progress, user=user, title='Updating permissions',
                             message='Calculating progress...') as ctx:
            if progress:
                ctx.update(total=self._model.subtreeCount(
                    folder, includeItems=False, user=user, level=AccessType.ADMIN))
            return self._model.setAccessList(
                folder, access, save=True, recurse=recurse, user=user,
                progress=ctx, setPublic=public, publicFlags=publicFlags)

    @access.user(scope=TokenScope.DATA_WRITE)
    @filtermodel(model=FolderModel)
    @autoDescribeRoute(
        Description('Create a new folder.')
        .responseClass('Folder')
        .param('parentType', "Type of the folder's parent", required=False,
               enum=['folder', 'user', 'collection'], default='folder')
        .param('parentId', "The ID of the folder's parent.")
        .param('name', 'Name of the folder.', strip=True)
        .param('description', 'Description for the folder.', required=False,
               default='', strip=True)
        .param('reuseExisting', 'Return existing folder if it exists rather than '
               'creating a new one.', required=False,
               dataType='boolean', default=False)
        .param('public', 'Whether the folder should be publicly visible. By '
               'default, inherits the value from parent folder, or in the '
               'case of user or collection parentType, defaults to False.',
               required=False, dataType='boolean')
        .jsonParam('metadata', 'A JSON object containing the metadata keys to add',
                   paramType='form', requireObject=True, required=False)
        .errorResponse()
        .errorResponse('Write access was denied on the parent', 403)
    )
    def createFolder(self, public, parentType, parentId, name, description,
                     reuseExisting, metadata):
        user = self.getCurrentUser()
        parent = ModelImporter.model(parentType).load(
            id=parentId, user=user, level=AccessType.WRITE, exc=True)

        newFolder = self._model.createFolder(
            parent=parent, name=name, parentType=parentType, creator=user,
            description=description, public=public, reuseExisting=reuseExisting)
        if metadata:
            newFolder = self._model.setMetadata(newFolder, metadata)
        return newFolder

    @access.public(scope=TokenScope.DATA_READ)
    @filtermodel(model=FolderModel)
    @autoDescribeRoute(
        Description('Get a folder by ID.')
        .responseClass('Folder')
        .modelParam('id', model=FolderModel, level=AccessType.READ)
        .errorResponse('ID was invalid.')
        .errorResponse('Read access was denied for the folder.', 403)
    )
    def getFolder(self, folder):
        return folder

    @access.user(scope=TokenScope.DATA_OWN)
    @autoDescribeRoute(
        Description('Get the access control list for a folder.')
        .responseClass('Folder')
        .modelParam('id', model=FolderModel, level=AccessType.ADMIN)
        .errorResponse('ID was invalid.')
        .errorResponse('Admin access was denied for the folder.', 403)
    )
    def getFolderAccess(self, folder):
        return self._model.getFullAccessList(folder)

    @access.user(scope=TokenScope.DATA_OWN)
    @autoDescribeRoute(
        Description('Delete a folder by ID.')
        .modelParam('id', model=FolderModel, level=AccessType.ADMIN)
        .param('progress', 'Whether to record progress on this task.',
               required=False, dataType='boolean', default=False)
        .errorResponse('ID was invalid.')
        .errorResponse('Admin access was denied for the folder.', 403)
    )
    def deleteFolder(self, folder, progress):
        with ProgressContext(progress, user=self.getCurrentUser(),
                             title='Deleting folder %s' % folder['name'],
                             message='Calculating folder size...') as ctx:
            # Don't do the subtree count if we weren't asked for progress
            if progress:
                ctx.update(total=self._model.subtreeCount(folder))
            self._model.remove(folder, progress=ctx)
        return {'message': 'Deleted folder %s.' % folder['name']}

    @access.user(scope=TokenScope.DATA_WRITE)
    @filtermodel(model=FolderModel)
    @autoDescribeRoute(
        Description('Set metadata fields on an folder.')
        .responseClass('Folder')
        .notes('Set metadata fields to null in order to delete them.')
        .modelParam('id', model=FolderModel, level=AccessType.WRITE)
        .jsonParam('metadata', 'A JSON object containing the metadata keys to add',
                   paramType='body', requireObject=True)
        .param('allowNull', 'Whether "null" is allowed as a metadata value.', required=False,
               dataType='boolean', default=False)
        .errorResponse(('ID was invalid.',
                        'Invalid JSON passed in request body.',
                        'Metadata key name was invalid.'))
        .errorResponse('Write access was denied for the folder.', 403)
    )
    def setMetadata(self, folder, metadata, allowNull):
        return self._model.setMetadata(folder, metadata, allowNull=allowNull)

    @access.user(scope=TokenScope.DATA_WRITE)
    @filtermodel(model=FolderModel)
    @autoDescribeRoute(
        Description('Copy a folder.')
        .responseClass('Folder')
        .modelParam('id', 'The ID of the original folder.', model=FolderModel,
                    level=AccessType.READ)
        .param('parentType', "Type of the new folder's parent", required=False,
               enum=['folder', 'user', 'collection'])
        .param('parentId', 'The ID of the parent document.', required=False)
        .param('name', 'Name for the new folder.', required=False)
        .param('description', "Description for the new folder.", required=False)
        .param('public', "Whether the folder should be publicly visible. By "
               "default, inherits the value from parent folder, or in the case "
               "of user or collection parentType, defaults to False. If "
               "'original', use the value of the original folder.",
               required=False, enum=['true', 'false', 'original'])
        .param('progress', 'Whether to record progress on this task.',
               required=False, dataType='boolean', default=False)
        .errorResponse(('A parameter was invalid.',
                        'ID was invalid.'))
        .errorResponse('Read access was denied on the original folder.\n\n'
                       'Write access was denied on the parent.', 403)
    )
    def copyFolder(self, folder, parentType, parentId, name, description, public, progress):
        user = self.getCurrentUser()
        parentType = parentType or folder['parentCollection']
        if parentId:
            parent = ModelImporter.model(parentType).load(
                id=parentId, user=user, level=AccessType.WRITE, exc=True)
        else:
            parent = None

        with ProgressContext(progress, user=self.getCurrentUser(),
                             title='Copying folder %s' % folder['name'],
                             message='Calculating folder size...') as ctx:
            # Don't do the subtree count if we weren't asked for progress
            if progress:
                ctx.update(total=self._model.subtreeCount(folder))
            return self._model.copyFolder(
                folder, creator=user, name=name, parentType=parentType,
                parent=parent, description=description, public=public, progress=ctx)

    @access.user(scope=TokenScope.DATA_WRITE)
    @autoDescribeRoute(
        Description('Remove all contents from a folder.')
        .notes('Cleans out all the items and subfolders from under a folder, '
               'but does not remove the folder itself.')
        .modelParam('id', 'The ID of the folder to clean.', model=FolderModel,
                    level=AccessType.WRITE)
        .param('progress', 'Whether to record progress on this task.',
               required=False, dataType='boolean', default=False)
        .errorResponse('ID was invalid.')
        .errorResponse('Write access was denied on the folder.', 403)
    )
    def deleteContents(self, folder, progress):
        with ProgressContext(progress, user=self.getCurrentUser(),
                             title='Clearing folder %s' % folder['name'],
                             message='Calculating folder size...') as ctx:
            # Don't do the subtree count if we weren't asked for progress
            if progress:
                ctx.update(total=self._model.subtreeCount(folder) - 1)
            self._model.clean(folder, progress=ctx)
        return {'message': 'Cleaned folder %s.' % folder['name']}

    @access.user(scope=TokenScope.DATA_WRITE)
    @filtermodel(FolderModel)
    @autoDescribeRoute(
        Description('Delete metadata fields on a folder.')
        .responseClass('Folder')
        .modelParam('id', model=FolderModel, level=AccessType.WRITE)
        .jsonParam(
            'fields', 'A JSON list containing the metadata fields to delete',
            paramType='body', schema={
                'type': 'array',
                'items': {
                    'type': 'string'
                }
            }
        )
        .errorResponse(('ID was invalid.',
                        'Invalid JSON passed in request body.',
                        'Metadata key name was invalid.'))
        .errorResponse('Write access was denied for the folder.', 403)
    )
    def deleteMetadata(self, folder, fields):
        return self._model.deleteMetadata(folder, fields)

    @access.public(scope=TokenScope.DATA_READ)
    @autoDescribeRoute(
        Description('Get the path to the root of the folder\'s hierarchy.')
        .modelParam('id', model=FolderModel, level=AccessType.READ)
        .errorResponse('ID was invalid.')
        .errorResponse('Read access was denied for the folder.', 403)
    )
    def rootpath(self, folder, params):
        return self._model.parentsToRoot(folder, user=self.getCurrentUser())
Esempio n. 4
0
class Folder(Resource):
    """API Endpoint for folders."""
    def __init__(self):
        super(Folder, self).__init__()
        self.resourceName = 'folder'
        self._model = FolderModel()
        self.route('DELETE', (':id', ), self.deleteFolder)
        self.route('DELETE', (':id', 'contents'), self.deleteContents)
        self.route('GET', (), self.find)
        self.route('GET', (':id', ), self.getFolder)
        self.route('GET', (':id', 'details'), self.getFolderDetails)
        self.route('GET', (':id', 'access'), self.getFolderAccess)
        self.route('GET', (':id', 'download'), self.downloadFolder)
        self.route('GET', (':id', 'rootpath'), self.rootpath)
        self.route('GET', (':id', 'position'), self.findPosition)
        self.route('POST', (), self.createFolder)
        self.route('PUT', (':id', ), self.updateFolder)
        self.route('PUT', (':id', 'access'), self.updateFolderAccess)
        self.route('POST', (':id', 'copy'), self.copyFolder)
        self.route('PUT', (':id', 'metadata'), self.setMetadata)
        self.route('DELETE', (':id', 'metadata'), self.deleteMetadata)

    @access.public(scope=TokenScope.DATA_READ)
    @filtermodel(model=FolderModel)
    @autoDescribeRoute(
        Description('Search for folders by certain properties.').notes(
            'You must pass either a "folderId" or "text" field '
            'to specify how you are searching for folders.  '
            'If you omit one of these parameters the request will fail and respond : '
            '"Invalid search mode."').responseClass(
                'Folder', array=True).param(
                    'parentType',
                    "Type of the folder's parent",
                    required=False,
                    enum=['folder', 'user', 'collection']).param(
                        'parentId',
                        "The ID of the folder's parent.",
                        required=False).param('text',
                                              'Pass to perform a text search.',
                                              required=False).
        param('name', 'Pass to lookup a folder by exact name match. Must '
              'pass parentType and parentId as well when using this.',
              required=False).pagingParams(
                  defaultSort='lowerName').errorResponse().errorResponse(
                      'Read access was denied on the parent resource.', 403))
    def find(self, parentType, parentId, text, name, limit, offset, sort):
        """
        Get a list of folders with given search parameters. Currently accepted
        search modes are:

        1. Searching by parentId and parentType, with optional additional
           filtering by the name field (exact match) or using full text search
           within a single parent folder. Pass a "name" parameter or "text"
           parameter to invoke these additional filters.
        2. Searching with full text search across all folders in the system.
           Simply pass a "text" parameter for this mode.
        """
        return self._find(parentType, parentId, text, name, limit, offset,
                          sort)

    def _find(self,
              parentType,
              parentId,
              text,
              name,
              limit,
              offset,
              sort,
              filters=None):
        user = self.getCurrentUser()

        filters = (filters.copy() if filters else {})
        if parentType and parentId:
            parent = ModelImporter.model(parentType).load(
                parentId, user=user, level=AccessType.READ, exc=True)

            if text:
                filters['$text'] = {'$search': text}
            if name:
                filters['name'] = name

            return self._model.childFolders(parentType=parentType,
                                            parent=parent,
                                            user=user,
                                            offset=offset,
                                            limit=limit,
                                            sort=sort,
                                            filters=filters)
        elif text:
            return self._model.textSearch(text,
                                          user=user,
                                          limit=limit,
                                          offset=offset,
                                          sort=sort,
                                          filters=filters)
        else:
            raise RestException('Invalid search mode.')

    @access.public(scope=TokenScope.DATA_READ)
    @autoDescribeRoute(
        Description('Report the offset of a folder in a list or search.').
        notes(
            'You must pass either a "folderId" or "text" field '
            'to specify how you are searching for folders.  '
            'If you omit one of these parameters the request will fail and respond : '
            '"Invalid search mode."').modelParam(
                'id', model=FolderModel, level=AccessType.READ).param(
                    'parentType',
                    "Type of the folder's parent",
                    required=False,
                    enum=['folder', 'user', 'collection']).param(
                        'parentId',
                        "The ID of the folder's parent.",
                        required=False).param('text',
                                              'Pass to perform a text search.',
                                              required=False).
        param('name', 'Pass to lookup a folder by exact name match. Must '
              'pass parentType and parentId as well when using this.',
              required=False).param(
                  'sort',
                  'Field to sort the result set by.',
                  default='lowerName',
                  required=False,
                  strip=True).param(
                      'sortdir',
                      'Sort order: 1 for ascending, -1 for descending.',
                      required=False,
                      dataType='integer',
                      enum=[SortDir.ASCENDING, SortDir.DESCENDING],
                      default=SortDir.ASCENDING).errorResponse().errorResponse(
                          'Read access was denied on the parent resource.',
                          403))
    def findPosition(self, folder, parentType, parentId, text, name, params):
        limit, offset, sort = self.getPagingParameters(params, 'lowerName')
        if len(sort) != 1 or sort[0][0] not in folder:
            raise RestException('Invalid sort mode.')
        sortField, sortDir = sort[0]
        dir = '$lt' if sortDir == SortDir.ASCENDING else '$gt'
        filters = {
            '$or': [
                {
                    sortField: {
                        dir: folder.get(sortField)
                    }
                },
                # For folders that have the same sort value, sort by _id.
                # Mongo may fall back to this as the final sort, but this isn't
                # documented.
                {
                    sortField: folder.get(sortField),
                    '_id': {
                        dir: folder['_id']
                    }
                }
            ]
        }
        # limit and offset don't affect the results.
        cursor = self._find(parentType, parentId, text, name, 1, 0, sort,
                            filters)
        return cursor.count()

    @access.public(scope=TokenScope.DATA_READ)
    @autoDescribeRoute(
        Description('Get detailed information about a folder.').modelParam(
            'id', model=FolderModel,
            level=AccessType.READ).errorResponse().errorResponse(
                'Read access was denied on the folder.', 403))
    def getFolderDetails(self, folder):
        return {
            'nItems':
            self._model.countItems(folder),
            'nFolders':
            self._model.countFolders(folder,
                                     user=self.getCurrentUser(),
                                     level=AccessType.READ)
        }

    @access.public(scope=TokenScope.DATA_READ, cookie=True)
    @autoDescribeRoute(
        Description('Download an entire folder as a zip archive.').modelParam(
            'id', model=FolderModel, level=AccessType.READ).jsonParam(
                'mimeFilter',
                'JSON list of MIME types to include.',
                required=False,
                requireArray=True).produces('application/zip').errorResponse(
                    'ID was invalid.').errorResponse(
                        'Read access was denied for the folder.', 403))
    def downloadFolder(self, folder, mimeFilter):
        """
        Returns a generator function that will be used to stream out a zip
        file containing this folder's contents, filtered by permissions.
        """
        setResponseHeader('Content-Type', 'application/zip')
        setContentDisposition(folder['name'] + '.zip')
        user = self.getCurrentUser()

        def stream():
            zip = ziputil.ZipGenerator(folder['name'])
            for (path, file) in self._model.fileList(folder,
                                                     user=user,
                                                     subpath=False,
                                                     mimeFilter=mimeFilter):
                for data in zip.addFile(file, path):
                    yield data
            yield zip.footer()

        return stream

    @access.user(scope=TokenScope.DATA_WRITE)
    @filtermodel(model=FolderModel)
    @autoDescribeRoute(
        Description('Update a folder or move it into a new parent.'
                    ).responseClass('Folder').modelParam(
                        'id', model=FolderModel, level=AccessType.WRITE).param(
                            'name',
                            'Name of the folder.',
                            required=False,
                            strip=True).param('description',
                                              'Description for the folder.',
                                              required=False,
                                              strip=True).
        param('parentType',
              "Type of the folder's parent",
              required=False,
              enum=['folder', 'user', 'collection'],
              strip=True).param(
                  'parentId',
                  'Parent ID for the new parent of this folder.',
                  required=False).jsonParam(
                      'metadata',
                      'A JSON object containing the metadata keys to add',
                      paramType='form',
                      requireObject=True,
                      required=False).errorResponse('ID was invalid.').
        errorResponse(
            'Write access was denied for the folder or its new parent object.',
            403))
    def updateFolder(self, folder, name, description, parentType, parentId,
                     metadata):
        user = self.getCurrentUser()
        if name is not None:
            folder['name'] = name
        if description is not None:
            folder['description'] = description

        folder = self._model.updateFolder(folder)
        if metadata:
            folder = self._model.setMetadata(folder, metadata)

        if parentType and parentId:
            parent = ModelImporter.model(parentType).load(
                parentId, level=AccessType.WRITE, user=user, exc=True)
            if (parentType, parent['_id']) != (folder['parentCollection'],
                                               folder['parentId']):
                folder = self._model.move(folder, parent, parentType)

        return folder

    @access.user(scope=TokenScope.DATA_OWN)
    @filtermodel(model=FolderModel, addFields={'access'})
    @autoDescribeRoute(
        Description('Update the access control list for a folder.').modelParam(
            'id', model=FolderModel, level=AccessType.ADMIN).jsonParam(
                'access',
                'The JSON-encoded access control list.',
                requireObject=True).jsonParam(
                    'publicFlags',
                    'JSON list of public access flags.',
                    requireArray=True,
                    required=False).param(
                        'public',
                        'Whether the folder should be publicly visible.',
                        dataType='boolean',
                        required=False).param(
                            'recurse',
                            'Whether the policies should be applied to all '
                            'subfolders under this folder as well.',
                            dataType='boolean',
                            default=False,
                            required=False).
        param('progress', 'If recurse is set to True, this controls whether '
              'progress notifications will be sent.',
              dataType='boolean',
              default=False,
              required=False).errorResponse('ID was invalid.').errorResponse(
                  'Admin access was denied for the folder.', 403))
    def updateFolderAccess(self, folder, access, publicFlags, public, recurse,
                           progress):
        user = self.getCurrentUser()
        progress = progress and recurse  # Only enable progress in recursive case
        with ProgressContext(progress,
                             user=user,
                             title='Updating permissions',
                             message='Calculating progress...') as ctx:
            if progress:
                ctx.update(
                    total=self._model.subtreeCount(folder,
                                                   includeItems=False,
                                                   user=user,
                                                   level=AccessType.ADMIN))
            return self._model.setAccessList(folder,
                                             access,
                                             save=True,
                                             recurse=recurse,
                                             user=user,
                                             progress=ctx,
                                             setPublic=public,
                                             publicFlags=publicFlags)

    @access.user(scope=TokenScope.DATA_WRITE)
    @filtermodel(model=FolderModel)
    @autoDescribeRoute(
        Description('Create a new folder.').responseClass('Folder').param(
            'parentType',
            "Type of the folder's parent",
            required=False,
            enum=['folder', 'user', 'collection'],
            default='folder').param('parentId',
                                    "The ID of the folder's parent.").param(
                                        'name',
                                        'Name of the folder.',
                                        strip=True).param(
                                            'description',
                                            'Description for the folder.',
                                            required=False,
                                            default='',
                                            strip=True).
        param('reuseExisting',
              'Return existing folder if it exists rather than '
              'creating a new one.',
              required=False,
              dataType='boolean',
              default=False).param(
                  'public',
                  'Whether the folder should be publicly visible. By '
                  'default, inherits the value from parent folder, or in the '
                  'case of user or collection parentType, defaults to False.',
                  required=False,
                  dataType='boolean').jsonParam(
                      'metadata',
                      'A JSON object containing the metadata keys to add',
                      paramType='form',
                      requireObject=True,
                      required=False).errorResponse().errorResponse(
                          'Write access was denied on the parent', 403))
    def createFolder(self, public, parentType, parentId, name, description,
                     reuseExisting, metadata):
        user = self.getCurrentUser()
        parent = ModelImporter.model(parentType).load(id=parentId,
                                                      user=user,
                                                      level=AccessType.WRITE,
                                                      exc=True)

        newFolder = self._model.createFolder(parent=parent,
                                             name=name,
                                             parentType=parentType,
                                             creator=user,
                                             description=description,
                                             public=public,
                                             reuseExisting=reuseExisting)
        if metadata:
            newFolder = self._model.setMetadata(newFolder, metadata)
        return newFolder

    @access.public(scope=TokenScope.DATA_READ)
    @filtermodel(model=FolderModel)
    @autoDescribeRoute(
        Description('Get a folder by ID.').responseClass('Folder').modelParam(
            'id', model=FolderModel, level=AccessType.READ).errorResponse(
                'ID was invalid.').errorResponse(
                    'Read access was denied for the folder.', 403))
    def getFolder(self, folder):
        return folder

    @access.user(scope=TokenScope.DATA_OWN)
    @autoDescribeRoute(
        Description('Get the access control list for a folder.').responseClass(
            'Folder').modelParam(
                'id', model=FolderModel, level=AccessType.ADMIN).errorResponse(
                    'ID was invalid.').errorResponse(
                        'Admin access was denied for the folder.', 403))
    def getFolderAccess(self, folder):
        return self._model.getFullAccessList(folder)

    @access.user(scope=TokenScope.DATA_OWN)
    @autoDescribeRoute(
        Description('Delete a folder by ID.').modelParam(
            'id', model=FolderModel, level=AccessType.ADMIN).param(
                'progress',
                'Whether to record progress on this task.',
                required=False,
                dataType='boolean',
                default=False).errorResponse('ID was invalid.').errorResponse(
                    'Admin access was denied for the folder.', 403))
    def deleteFolder(self, folder, progress):
        with ProgressContext(progress,
                             user=self.getCurrentUser(),
                             title='Deleting folder %s' % folder['name'],
                             message='Calculating folder size...') as ctx:
            # Don't do the subtree count if we weren't asked for progress
            if progress:
                ctx.update(total=self._model.subtreeCount(folder))
            self._model.remove(folder, progress=ctx)
        return {'message': 'Deleted folder %s.' % folder['name']}

    @access.user(scope=TokenScope.DATA_WRITE)
    @filtermodel(model=FolderModel)
    @autoDescribeRoute(
        Description('Set metadata fields on an folder.').
        responseClass('Folder').notes(
            'Set metadata fields to null in order to delete them.').modelParam(
                'id', model=FolderModel, level=AccessType.WRITE).jsonParam(
                    'metadata',
                    'A JSON object containing the metadata keys to add',
                    paramType='body',
                    requireObject=True).param(
                        'allowNull',
                        'Whether "null" is allowed as a metadata value.',
                        required=False,
                        dataType='boolean',
                        default=False).errorResponse(
                            ('ID was invalid.',
                             'Invalid JSON passed in request body.',
                             'Metadata key name was invalid.')).errorResponse(
                                 'Write access was denied for the folder.',
                                 403))
    def setMetadata(self, folder, metadata, allowNull):
        return self._model.setMetadata(folder, metadata, allowNull=allowNull)

    @access.user(scope=TokenScope.DATA_WRITE)
    @filtermodel(model=FolderModel)
    @autoDescribeRoute(
        Description('Copy a folder.').responseClass('Folder').modelParam(
            'id',
            'The ID of the original folder.',
            model=FolderModel,
            level=AccessType.READ).param(
                'parentType',
                "Type of the new folder's parent",
                required=False,
                enum=['folder', 'user', 'collection'
                      ]).param('parentId',
                               'The ID of the parent document.',
                               required=False).param(
                                   'name',
                                   'Name for the new folder.',
                                   required=False).param(
                                       'description',
                                       'Description for the new folder.',
                                       required=False).
        param('public', 'Whether the folder should be publicly visible. By '
              'default, inherits the value from parent folder, or in the case '
              'of user or collection parentType, defaults to False. If '
              "'original', use the value of the original folder.",
              required=False,
              enum=['true', 'false', 'original']).param(
                  'progress',
                  'Whether to record progress on this task.',
                  required=False,
                  dataType='boolean',
                  default=False).errorResponse(
                      ('A parameter was invalid.',
                       'ID was invalid.')).errorResponse(
                           'Read access was denied on the original folder.\n\n'
                           'Write access was denied on the parent.', 403))
    def copyFolder(self, folder, parentType, parentId, name, description,
                   public, progress):
        user = self.getCurrentUser()
        parentType = parentType or folder['parentCollection']
        if parentId:
            parent = ModelImporter.model(parentType).load(
                id=parentId, user=user, level=AccessType.WRITE, exc=True)
        else:
            parent = None

        with ProgressContext(progress,
                             user=self.getCurrentUser(),
                             title='Copying folder %s' % folder['name'],
                             message='Calculating folder size...') as ctx:
            # Don't do the subtree count if we weren't asked for progress
            if progress:
                ctx.update(total=self._model.subtreeCount(folder))
            return self._model.copyFolder(folder,
                                          creator=user,
                                          name=name,
                                          parentType=parentType,
                                          parent=parent,
                                          description=description,
                                          public=public,
                                          progress=ctx)

    @access.user(scope=TokenScope.DATA_WRITE)
    @autoDescribeRoute(
        Description('Remove all contents from a folder.').notes(
            'Cleans out all the items and subfolders from under a folder, '
            'but does not remove the folder itself.').modelParam(
                'id',
                'The ID of the folder to clean.',
                model=FolderModel,
                level=AccessType.WRITE).param(
                    'progress',
                    'Whether to record progress on this task.',
                    required=False,
                    dataType='boolean',
                    default=False).errorResponse(
                        'ID was invalid.').errorResponse(
                            'Write access was denied on the folder.', 403))
    def deleteContents(self, folder, progress):
        with ProgressContext(progress,
                             user=self.getCurrentUser(),
                             title='Clearing folder %s' % folder['name'],
                             message='Calculating folder size...') as ctx:
            # Don't do the subtree count if we weren't asked for progress
            if progress:
                ctx.update(total=self._model.subtreeCount(folder) - 1)
            self._model.clean(folder, progress=ctx)
        return {'message': 'Cleaned folder %s.' % folder['name']}

    @access.user(scope=TokenScope.DATA_WRITE)
    @filtermodel(FolderModel)
    @autoDescribeRoute(
        Description('Delete metadata fields on a folder.').responseClass(
            'Folder').modelParam(
                'id', model=FolderModel, level=AccessType.WRITE).jsonParam(
                    'fields',
                    'A JSON list containing the metadata fields to delete',
                    paramType='body',
                    schema={
                        'type': 'array',
                        'items': {
                            'type': 'string'
                        }
                    }).errorResponse(
                        ('ID was invalid.',
                         'Invalid JSON passed in request body.',
                         'Metadata key name was invalid.')).errorResponse(
                             'Write access was denied for the folder.', 403))
    def deleteMetadata(self, folder, fields):
        return self._model.deleteMetadata(folder, fields)

    @access.public(scope=TokenScope.DATA_READ)
    @autoDescribeRoute(
        Description(
            "Get the path to the root of the folder's hierarchy.").modelParam(
                'id', model=FolderModel, level=AccessType.READ).errorResponse(
                    'ID was invalid.').errorResponse(
                        'Read access was denied for the folder.', 403))
    def rootpath(self, folder, params):
        return self._model.parentsToRoot(folder, user=self.getCurrentUser())
Esempio n. 5
0
class LabelResource(Resource):

    def __init__(self):
        super().__init__()
        self.resourceName = 'label'

        self.coll_m = Collection()
        self.file_m = File()
        self.folder_m = Folder()
        self.item_m = Item()
        self.upload_m = Upload()
        self.asset_m = Assetstore()

        self.setupRoutes()

    def setupRoutes(self):
        self.route('GET', (), handler=self.getLabelList)
        self.route('GET', (':label_id',), self.getLabel)
        self.route('GET', ('meta',), self.getLabelMeta)
        self.route('GET', ('create',), self.createLabelFile)
        self.route('GET', ('by_name',), self.getLabelByName)
        self.route('POST', (), self.postLabel)

    def createNewFile(self, folder, file_name):
        item = self.item_m.createItem(file_name,
                                      creator=self.getCurrentUser(),
                                      folder=folder,
                                      description='label file',
                                      reuseExisting=False)

        file = self.file_m.createFile(size=0,
                                      item=item,
                                      name=file_name,
                                      creator=self.getCurrentUser(),
                                      assetstore=self.asset_m.getCurrent(),
                                      mimeType="application/json")
        return file

    def copy(self, srcFile, destFile):
        upload = self.upload_m.createUploadToFile(destFile, self.getCurrentUser(), srcFile['size'])
        self.upload_m.handleChunk(upload=upload,
                                  chunk=RequestBodyStream(self.file_m.open(srcFile), size=destFile['size']),
                                  user=self.getCurrentUser())
        return upload

    @access.public
    @autoDescribeRoute(
        Description('Get label list'))
    @rest.rawResponse
    def getLabelList(self):
        printOk('getLabelsList() was called!')

        try:
            collection = list(self.coll_m.list(user=self.getCurrentUser(), offset=0, limit=1))[0]
            files = self.coll_m.fileList(collection, user=self.getCurrentUser(), data=False,
                                         includeMetadata=True, mimeFilter=['application/json'])
            files = list(files)
            cherrypy.response.headers["Content-Type"] = "application/json"
            return dumps(files)

        except:
            printFail(traceback.print_exc)

    @staticmethod
    def getOwnerId(folder):
        aclList = Folder().getFullAccessList(folder)
        for acl in aclList['users']:
            if acl['level'] == AccessType.ADMIN:
                return str(acl['id'])
        return None

    def getConfigFolder(self, label_folder_id):
        label_folder = Folder().load(label_folder_id,
                                     user=self.getCurrentUser(),
                                     level=AccessType.READ)
        ownerId = self.getOwnerId(label_folder)
        config_folder = self.folder_m.load(label_folder['meta'][ownerId], level=AccessType.READ,
                                           user=self.getCurrentUser())
        return config_folder

    def findConfig(self, folder_id):
        folder = self.getConfigFolder(folder_id)
        printOk2("Config folder {}".format(folder))
        files = self.folder_m.fileList(folder, self.getCurrentUser(), data=False)
        for file_path, file in files:
            printOk(file)
            if file['name'] == "config.json":
                return file

    def __findFile(self, folder, file_name):
        item = list(self.item_m.find({'folderId': folder['_id'], 'name': file_name}).limit(1))
        if not item:
            return None

        item = item[0]
        file = list(self.file_m.find({'itemId': item['_id']}).limit(1))

        if not file:
            return None

        return file[0]

    @access.public
    @autoDescribeRoute(
        Description('Create a new label file if it doesnt exist')
            .param('file_name', 'label file name').param('folder_id', 'the parent folder id'))
    @rest.rawResponse
    def createLabelFile(self, file_name, folder_id):
        try:
            folder = self.folder_m.load(folder_id, user=self.getCurrentUser(), level=AccessType.WRITE)
            file = self.__findFile(folder, file_name)
            if not file:
                file = self.createNewFile(folder, file_name)
                config_file = self.findConfig(folder_id)
                if not config_file:
                    printFail("No config file found")
                    return errorMessage("No config file found")
                else:
                    res = self.copy(config_file, file)
                    return dumps({
                        "label_id": res['fileId']
                    })

            return dumps({
                "label_id": file['_id']
            })
        except:
            printFail(traceback.print_exc)
            cherrypy.response.status = 500

    @access.public
    @autoDescribeRoute(
        Description('Get labels by file_name')
            .param('file_name', 'label file name').param('folder_id', 'the parent folder id'))
    @rest.rawResponse
    def getLabelByName(self, file_name, folder_id):
        try:
            folder = self.folder_m.load(folder_id, user=self.getCurrentUser(), level=AccessType.READ)
            file = self.__findFile(folder, file_name)
            cherrypy.response.headers["Content-Type"] = "application/json"
            if file:
                return self.file_m.download(file)
            else:
                return dumps({})
        except:
            printFail(traceback.print_exc)
            cherrypy.response.status = 500

    @access.public
    @autoDescribeRoute(
        Description('Get label by id')
            .param('label_id', 'label file id'))
    @rest.rawResponse
    def getLabel(self, label_id):
        try:
            file = self.file_m.load(label_id, level=AccessType.READ, user=self.getCurrentUser())
            printOk2(file)
            cherrypy.response.headers["Content-Type"] = "application/json"
            return self.file_m.download(file)
        except:
            # Unknown slug
            printFail(traceback.print_exc)
            cherrypy.response.status = 404

    @access.public
    @autoDescribeRoute(
        Description('Get label by id')
            .param('label_id', 'label file id'))
    def getLabelMeta(self, label_id):
        try:
            file = self.file_m.load(label_id, level=AccessType.READ, user=self.getCurrentUser())
            cherrypy.response.headers["Content-Type"] = "application/json"
            return dumps(file)
        except:
            # Unknown slug
            printFail(traceback.print_exc)
            cherrypy.response.status = 404

    @access.public
    @autoDescribeRoute(
        Description('Post label by id')
            .param('label_id', 'label file id'))
    @rest.rawResponse
    def postLabel(self, label_id, params):
        try:
            file = self.file_m.load(label_id, level=AccessType.WRITE, user=self.getCurrentUser())
            cherrypy.response.headers["Content-Type"] = "application/json"
            params['labels'] = json.loads(params['labels'])
            data = json.dumps(params, indent=2, sort_keys=True)
            upload = writeData(self.getCurrentUser(), file, data)
            printOk2(file)
            printOk(upload)
            return dumps(upload)

        except:
            # Unknown slug
            printFail(traceback.print_exc)
            cherrypy.response.status = 404

    @access.public
    @autoDescribeRoute(
        Description('Post label by id')
            .param('label_id', 'label file id'))
    @rest.rawResponse
    def strokeToOutline(self, strokes):
        pass