def find(tagFolderId, user):
     folderModel = Folder()
     tagFolder = folderModel.load(tagFolderId,
                                  user=user,
                                  level=AccessType.READ)
     if not tagFolder:
         return None
     imageFolder = folderModel.load(tagFolder['parentId'],
                                    user=user,
                                    level=AccessType.READ)
     return DockerImageItem(imageFolder, tagFolder, user)
    def execute_simulation(self, folderId, targetTime):
        user, token = self.getCurrentUser(returnToken=True)
        folder_model = Folder()
        job_model = Job()

        folder = folder_model.load(folderId,
                                   user=user,
                                   level=AccessType.WRITE,
                                   exc=True)
        girder_config = GirderConfig(token=token['_id'], folder=folder['_id'])
        simulation_config = SimulationConfig()

        # TODO: This would be better stored as a dict, but it's easier once we change the
        #       config object format.
        simulation_config_file = StringIO()
        simulation_config.write(simulation_config_file)

        job = job_model.createJob(
            title='NLI Simulation',
            type='nli_simulation',
            kwargs={
                'girder_config': attr.asdict(girder_config),
                'simulation_config': simulation_config_file.getvalue(),
            },
        )

        job = run_simulation.delay(
            girder_config=girder_config,
            simulation_config=simulation_config,
            target_time=targetTime,
            job=job,
        )
        return job
Exemple #3
0
    def _validate_dataset(tale):
        try:
            jsonschema.validate(tale["dataSet"], dataSetSchema)
        except jsonschema.exceptions.ValidationError as exc:
            raise ValidationException(str(exc))

        creator = User().load(tale["creatorId"], force=True)
        for obj in tale["dataSet"]:
            if obj["_modelType"] == "folder":
                model = Folder()
            else:
                model = Item()
            model.load(obj["itemId"],
                       level=AccessType.READ,
                       user=creator,
                       fields={},
                       exc=True)
    def getAdjacentImages(self, currentImage):
        folderModel = Folder()
        folder = folderModel.load(currentImage['folderId'],
                                  user=self.getCurrentUser(),
                                  level=AccessType.READ)
        allImages = [
            item for item in folderModel.childItems(folder)
            if _isLargeImageItem(item)
        ]
        try:
            index = allImages.index(currentImage)
        except ValueError:
            raise RestException('Id is not an image', 404)

        return {
            'previous': allImages[index - 1],
            'next': allImages[(index + 1) % len(allImages)]
        }
    def getAdjacentImages(self, currentImage, currentFolder=None):
        folderModel = Folder()
        if currentFolder:
            folder = currentFolder
        else:
            folder = folderModel.load(
                currentImage['folderId'], user=self.getCurrentUser(), level=AccessType.READ)

        if folder.get('isVirtual'):
            children = folderModel.childItems(folder, includeVirtual=True)
        else:
            children = folderModel.childItems(folder)

        allImages = [item for item in children if _isLargeImageItem(item)]
        try:
            index = allImages.index(currentImage)
        except ValueError:
            raise RestException('Id is not an image', 404)

        return {
            'previous': allImages[index - 1],
            'next': allImages[(index + 1) % len(allImages)]
        }
Exemple #6
0
    def testAccessFlags(self):
        resp = self.request('/system/access_flag')
        self.assertStatusOk(resp)
        self.assertEqual(resp.json, {})

        registerAccessFlag('my_key', name='hello', description='a custom flag')

        resp = self.request('/system/access_flag')
        self.assertStatusOk(resp)
        self.assertEqual(
            resp.json, {
                'my_key': {
                    'name': 'hello',
                    'description': 'a custom flag',
                    'admin': False
                }
            })

        self.users[1] = User().load(self.users[1]['_id'], force=True)
        user = self.users[1]

        # Manage custom access flags on an access controlled resource
        self.assertFalse(User().hasAccessFlags(user, user, flags=['my_key']))

        # Admin should always have permission
        self.assertTrue(User().hasAccessFlags(user,
                                              self.users[0],
                                              flags=['my_key']))

        # Test the requireAccessFlags method
        with self.assertRaises(AccessException):
            User().requireAccessFlags(user, user=user, flags='my_key')

        User().requireAccessFlags(user, user=self.users[0], flags='my_key')

        acl = User().getFullAccessList(user)
        self.assertEqual(acl['users'][0]['flags'], [])

        # Test loadmodel requiredFlags argument via REST endpoint
        resp = self.request('/test_endpoints/loadmodel_with_flags/%s' %
                            user['_id'],
                            user=self.users[1])
        self.assertStatus(resp, 403)

        user = User().setAccessList(
            self.users[0],
            access={
                'users': [{
                    'id': self.users[1]['_id'],
                    'level': AccessType.ADMIN,
                    'flags': ['my_key', 'not a registered flag']
                }],
                'groups': [{
                    'id': self.group['_id'],
                    'level': AccessType.ADMIN,
                    'flags': ['my_key']
                }]
            },
            save=True)

        resp = self.request('/test_endpoints/loadmodel_with_flags/%s' %
                            user['_id'],
                            user=self.users[1])
        self.assertStatusOk(resp)
        self.assertEqual(resp.json, 'success')

        # Only registered flags should be stored
        acl = User().getFullAccessList(user)
        self.assertEqual(acl['users'][0]['flags'], ['my_key'])
        self.assertTrue(User().hasAccessFlags(user, user, flags=['my_key']))

        # Create an admin-only access flag
        registerAccessFlag('admin_flag', name='admin flag', admin=True)

        # Non-admin shouldn't be able to set it
        user = User().setAccessList(self.users[0],
                                    access={
                                        'users': [{
                                            'id': self.users[1]['_id'],
                                            'level': AccessType.ADMIN,
                                            'flags': ['admin_flag']
                                        }],
                                        'groups': []
                                    },
                                    save=True,
                                    user=self.users[1])

        acl = User().getFullAccessList(user)
        self.assertEqual(acl['users'][0]['flags'], [])

        # Admin user should be able to set it
        user = User().setAccessList(self.users[1],
                                    access={
                                        'users': [{
                                            'id': self.users[1]['_id'],
                                            'level': AccessType.ADMIN,
                                            'flags': ['admin_flag']
                                        }],
                                        'groups': [{
                                            'id': self.group['_id'],
                                            'level': AccessType.ADMIN,
                                            'flags': ['admin_flag']
                                        }]
                                    },
                                    save=True,
                                    user=self.users[0])

        acl = User().getFullAccessList(user)
        self.assertEqual(acl['users'][0]['flags'], ['admin_flag'])

        # An already-enabled admin-only flag should stay enabled for non-admin user
        user = User().setAccessList(self.users[1],
                                    access={
                                        'users': [{
                                            'id':
                                            self.users[1]['_id'],
                                            'level':
                                            AccessType.ADMIN,
                                            'flags': ['my_key', 'admin_flag']
                                        }],
                                        'groups': [{
                                            'id': self.group['_id'],
                                            'level': AccessType.ADMIN,
                                            'flags': ['admin_flag']
                                        }]
                                    },
                                    save=True,
                                    user=self.users[1])

        acl = User().getFullAccessList(user)
        self.assertEqual(set(acl['users'][0]['flags']),
                         {'my_key', 'admin_flag'})
        self.assertEqual(acl['groups'][0]['flags'], ['admin_flag'])

        # Test setting public flags on a collection and folder
        collectionModel = Collection()
        folderModel = Folder()
        itemModel = Item()
        collection = collectionModel.createCollection('coll',
                                                      creator=self.users[0],
                                                      public=True)
        folder = folderModel.createFolder(collection,
                                          'folder',
                                          parentType='collection',
                                          creator=self.users[0])

        # Add an item to the folder so we can test AclMixin flag behavior
        item = itemModel.createItem(folder=folder,
                                    name='test',
                                    creator=self.users[0])

        folder = folderModel.setUserAccess(folder,
                                           self.users[1],
                                           level=AccessType.ADMIN,
                                           save=True,
                                           currentUser=self.users[0])

        with self.assertRaises(AccessException):
            collectionModel.requireAccessFlags(collection,
                                               user=None,
                                               flags='my_key')

        # Test AclMixin flag behavior
        with self.assertRaises(AccessException):
            itemModel.requireAccessFlags(item, user=None, flags='my_key')

        self.assertFalse(
            itemModel.hasAccessFlags(item, user=None, flags='my_key'))

        collection = collectionModel.setAccessList(collection,
                                                   access=collection['access'],
                                                   save=True,
                                                   recurse=True,
                                                   user=self.users[0],
                                                   publicFlags=['my_key'])
        collectionModel.requireAccessFlags(collection,
                                           user=None,
                                           flags='my_key')

        # Make sure recursive setting of public flags worked
        folder = folderModel.load(folder['_id'], force=True)
        self.assertEqual(folder['publicFlags'], ['my_key'])

        itemModel.requireAccessFlags(item, user=None, flags='my_key')

        # Non-admin shouldn't be able to set admin-only public flags
        folder = folderModel.setPublicFlags(folder,
                                            flags=['admin_flag'],
                                            user=self.users[1],
                                            save=True)
        self.assertEqual(folder['publicFlags'], [])

        # Admin users should be able to set admin-only public flags
        folder = folderModel.setPublicFlags(folder,
                                            flags=['admin_flag'],
                                            user=self.users[0],
                                            save=True,
                                            append=True)
        self.assertEqual(folder['publicFlags'], ['admin_flag'])

        # Non-admin users can set admin-only public flags if they are already enabled
        folder = folderModel.setPublicFlags(folder,
                                            flags=['admin_flag', 'my_key'],
                                            user=self.users[1],
                                            save=True)
        self.assertEqual(set(folder['publicFlags']), {'admin_flag', 'my_key'})

        # Test "force" options
        folder = folderModel.setPublicFlags(folder,
                                            flags='admin_flag',
                                            force=True,
                                            save=True)
        self.assertEqual(folder['publicFlags'], ['admin_flag'])

        folder = folderModel.setAccessList(folder,
                                           access={
                                               'users': [{
                                                   'id':
                                                   self.users[1]['_id'],
                                                   'level':
                                                   AccessType.ADMIN,
                                                   'flags':
                                                   ['my_key', 'admin_flag']
                                               }],
                                               'groups': []
                                           },
                                           save=True,
                                           force=True)
        folderModel.requireAccessFlags(folder,
                                       user=self.users[1],
                                       flags='my_key')

        folder = folderModel.setUserAccess(folder,
                                           self.users[1],
                                           level=AccessType.READ,
                                           save=True,
                                           force=True,
                                           flags=[])
        self.assertFalse(
            folderModel.hasAccessFlags(folder, self.users[1], flags='my_key'))

        folder = folderModel.setGroupAccess(folder,
                                            self.group,
                                            level=AccessType.READ,
                                            save=True,
                                            force=True,
                                            flags='my_key')
        folderModel.requireAccessFlags(folder,
                                       user=self.users[1],
                                       flags='my_key')

        # Testing with flags=None should give sensible behavior
        folderModel.requireAccessFlags(folder, user=None, flags=None)

        # Test filtering results by access flags (both ACModel and AclMixin)
        for model, doc in ((folderModel, folder), (itemModel, item)):
            cursor = model.find({})
            self.assertGreater(len(list(cursor)), 0)

            cursor = model.find({})
            filtered = list(
                model.filterResultsByPermission(cursor,
                                                user=None,
                                                level=AccessType.READ,
                                                flags='my_key'))
            self.assertEqual(len(filtered), 0)

            cursor = model.find({})
            filtered = list(
                model.filterResultsByPermission(cursor,
                                                user=self.users[1],
                                                level=AccessType.READ,
                                                flags=('my_key',
                                                       'admin_flag')))
            self.assertEqual(len(filtered), 1)
            self.assertEqual(filtered[0]['_id'], doc['_id'])
Exemple #7
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]
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
Exemple #9
0
class AssignmentResource(Resource):
    def __init__(self):
        super().__init__()

        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.user_m = User()

        self.setupRoutes()

    def setupRoutes(self):
        self.route('GET', (), handler=self.list)
        self.route('GET', (':a_id',), handler=self.getAssignment)
        self.route('GET', ('admin_data',), handler=self.getAdminData)

    def __findName(self, folder):
        imageFolder = self.__findImageFolder(folder)
        if isinstance(imageFolder, dict):
            return imageFolder['name']

        return ""

    def __findImageFolder(self, folder):
        owner = self.__findOwner(folder)
        ret = ""
        if self.folder_m.getAccessLevel(folder, self.getCurrentUser()) == AccessType.ADMIN:
            # this folder was created by this user, and so it will contain the images
            # printFail(folder)
            ret = folder
        else:
            meta = folder['meta']
            # this is the label file, and so should only have one entry in the metadata
            assert len(meta) == 1
            # that one entry contains link to the image folder, key must be the creator of this folder
            assert str(owner['_id']) in meta, (str(owner['_id']), meta)

            ret = self.folder_m.load(meta[str(owner['_id'])],
                                     level=AccessType.READ,
                                     user=self.getCurrentUser())

        return ret

    def __findLabelFolder(self, folder):
        owner = self.__findOwner(folder)
        ret = []
        if self.folder_m.getAccessLevel(folder, self.getCurrentUser()) == AccessType.ADMIN:
            # this folder was created by this user, so it will contain images
            if 'meta' not in folder:
                return []

            meta = folder['meta']
            for k, v in meta.items():
                ret.append(self.folder_m.load(v, level=AccessType.READ, user=self.getCurrentUser()))
        else:
            # this folder was not created by this user, so it will contain labels
            ret = [folder]

        return ret

    def __findOwner(self, folder):
        aclList = Folder().getFullAccessList(folder)
        for acl in aclList['users']:
            if acl['level'] == AccessType.ADMIN:
                return self.user_m.load(str(acl['id']), level=AccessType.READ, user=self.getCurrentUser())
        return None

    @access.public
    @autoDescribeRoute(
        Description('Get list of all assignments that it owns or is a part of')
            .param('limit', 'Number of assignments to return')
            .param('offset', 'offset from 0th assignment to start looking for assignments'))
    @rest.rawResponse
    def list(self, limit, offset):
        try:
            printOk((limit, offset))
            ret = self.__list(int(limit), int(offset))
            cherrypy.response.headers["Content-Type"] = "application/json"
            return dumps(ret)

        except:
            printFail(traceback.print_exc)

    def __list(self, limit=0, offset=0):
        user = self.getCurrentUser()
        folders = self.folder_m.find({'parentId': user['_id'],
                                      'parentCollection': 'user'}, limit=limit, offset=offset)
        ret = []
        for folder in folders:
            if self.__findName(folder):
                ret.append({'name': self.__findName(folder),
                            'image_folder': self.__findImageFolder(folder),
                            'label_folders': self.__findLabelFolder(folder),
                            'owner': self.__findOwner(folder)})

        return ret

    def __getAssignment(self, _id):
        assignments = self.__list()
        for assignment in assignments:
            # printOk2(assignment)
            if str(assignment['image_folder']['_id']) == _id:
                return assignment

        return None

    def __getAnnotators(self, assignment):
        ret = []
        for label_folder in assignment['label_folders']:
            printOk(label_folder)
            ret.append({
                'user': self.user_m.load(label_folder['parentId'], user=self.getCurrentUser(), level=AccessType.READ),
                'expanded': False,
            })

        return ret

    @access.public
    @autoDescribeRoute(
        Description('Get assignment by id').param('a_id', 'folder id that controls the assignment'))
    @rest.rawResponse
    def getAssignment(self, a_id):
        try:
            assignment = self.__getAssignment(a_id)
            return dumps(assignment)
        except:
            printFail(traceback.print_exc)

    @access.public
    @autoDescribeRoute(
        Description('Get admin data associated with assignment id').param('a_id',
                                                                          'folder id that controls the assignment'))
    @rest.rawResponse
    def getAdminData(self, a_id):
        try:
            assignment = self.__getAssignment(a_id)
            if assignment['owner']['_id'] == self.getCurrentUser()['_id']:
                # then current user is this assignment's admin
                return dumps({'annotators': self.__getAnnotators(assignment)})

        except:
            printFail(traceback.print_exc)
Exemple #10
0
    def testAccessFlags(self):
        resp = self.request('/system/access_flag')
        self.assertStatusOk(resp)
        self.assertEqual(resp.json, {})

        registerAccessFlag('my_key', name='hello', description='a custom flag')

        resp = self.request('/system/access_flag')
        self.assertStatusOk(resp)
        self.assertEqual(resp.json, {
            'my_key': {
                'name': 'hello',
                'description': 'a custom flag',
                'admin': False
            }
        })

        self.users[1] = User().load(self.users[1]['_id'], force=True)
        user = self.users[1]

        # Manage custom access flags on an access controlled resource
        self.assertFalse(User().hasAccessFlags(user, user, flags=['my_key']))

        # Admin should always have permission
        self.assertTrue(User().hasAccessFlags(user, self.users[0], flags=['my_key']))

        # Test the requireAccessFlags method
        with self.assertRaises(AccessException):
            User().requireAccessFlags(user, user=user, flags='my_key')

        User().requireAccessFlags(user, user=self.users[0], flags='my_key')

        acl = User().getFullAccessList(user)
        self.assertEqual(acl['users'][0]['flags'], [])

        # Test loadmodel requiredFlags argument via REST endpoint
        resp = self.request(
            '/test_endpoints/loadmodel_with_flags/%s' % user['_id'], user=self.users[1])
        self.assertStatus(resp, 403)

        user = User().setAccessList(self.users[0], access={
            'users': [{
                'id': self.users[1]['_id'],
                'level': AccessType.ADMIN,
                'flags': ['my_key', 'not a registered flag']
            }],
            'groups': [{
                'id': self.group['_id'],
                'level': AccessType.ADMIN,
                'flags': ['my_key']
            }]
        }, save=True)

        resp = self.request(
            '/test_endpoints/loadmodel_with_flags/%s' % user['_id'], user=self.users[1])
        self.assertStatusOk(resp)
        self.assertEqual(resp.json, 'success')

        # Only registered flags should be stored
        acl = User().getFullAccessList(user)
        self.assertEqual(acl['users'][0]['flags'], ['my_key'])
        self.assertTrue(User().hasAccessFlags(user, user, flags=['my_key']))

        # Create an admin-only access flag
        registerAccessFlag('admin_flag', name='admin flag', admin=True)

        # Non-admin shouldn't be able to set it
        user = User().setAccessList(self.users[0], access={
            'users': [{
                'id': self.users[1]['_id'],
                'level': AccessType.ADMIN,
                'flags': ['admin_flag']
            }],
            'groups': []
        }, save=True, user=self.users[1])

        acl = User().getFullAccessList(user)
        self.assertEqual(acl['users'][0]['flags'], [])

        # Admin user should be able to set it
        user = User().setAccessList(self.users[1], access={
            'users': [{
                'id': self.users[1]['_id'],
                'level': AccessType.ADMIN,
                'flags': ['admin_flag']
            }],
            'groups': [{
                'id': self.group['_id'],
                'level': AccessType.ADMIN,
                'flags': ['admin_flag']
            }]
        }, save=True, user=self.users[0])

        acl = User().getFullAccessList(user)
        self.assertEqual(acl['users'][0]['flags'], ['admin_flag'])

        # An already-enabled admin-only flag should stay enabled for non-admin user
        user = User().setAccessList(self.users[1], access={
            'users': [{
                'id': self.users[1]['_id'],
                'level': AccessType.ADMIN,
                'flags': ['my_key', 'admin_flag']
            }],
            'groups': [{
                'id': self.group['_id'],
                'level': AccessType.ADMIN,
                'flags': ['admin_flag']
            }]
        }, save=True, user=self.users[1])

        acl = User().getFullAccessList(user)
        self.assertEqual(set(acl['users'][0]['flags']), {'my_key', 'admin_flag'})
        self.assertEqual(acl['groups'][0]['flags'], ['admin_flag'])

        # Test setting public flags on a collection and folder
        collectionModel = Collection()
        folderModel = Folder()
        itemModel = Item()
        collection = collectionModel.createCollection('coll', creator=self.users[0], public=True)
        folder = folderModel.createFolder(
            collection, 'folder', parentType='collection', creator=self.users[0])

        # Add an item to the folder so we can test AclMixin flag behavior
        item = itemModel.createItem(folder=folder, name='test', creator=self.users[0])

        folder = folderModel.setUserAccess(
            folder, self.users[1], level=AccessType.ADMIN, save=True, currentUser=self.users[0])

        with self.assertRaises(AccessException):
            collectionModel.requireAccessFlags(collection, user=None, flags='my_key')

        # Test AclMixin flag behavior
        with self.assertRaises(AccessException):
            itemModel.requireAccessFlags(item, user=None, flags='my_key')

        self.assertFalse(itemModel.hasAccessFlags(item, user=None, flags='my_key'))

        collection = collectionModel.setAccessList(
            collection, access=collection['access'], save=True, recurse=True, user=self.users[0],
            publicFlags=['my_key'])
        collectionModel.requireAccessFlags(collection, user=None, flags='my_key')

        # Make sure recursive setting of public flags worked
        folder = folderModel.load(folder['_id'], force=True)
        self.assertEqual(folder['publicFlags'], ['my_key'])

        itemModel.requireAccessFlags(item, user=None, flags='my_key')

        # Non-admin shouldn't be able to set admin-only public flags
        folder = folderModel.setPublicFlags(
            folder, flags=['admin_flag'], user=self.users[1], save=True)
        self.assertEqual(folder['publicFlags'], [])

        # Admin users should be able to set admin-only public flags
        folder = folderModel.setPublicFlags(
            folder, flags=['admin_flag'], user=self.users[0], save=True, append=True)
        self.assertEqual(folder['publicFlags'], ['admin_flag'])

        # Non-admin users can set admin-only public flags if they are already enabled
        folder = folderModel.setPublicFlags(
            folder, flags=['admin_flag', 'my_key'], user=self.users[1], save=True)
        self.assertEqual(set(folder['publicFlags']), {'admin_flag', 'my_key'})

        # Test "force" options
        folder = folderModel.setPublicFlags(folder, flags='admin_flag', force=True, save=True)
        self.assertEqual(folder['publicFlags'], ['admin_flag'])

        folder = folderModel.setAccessList(folder, access={
            'users': [{
                'id': self.users[1]['_id'],
                'level': AccessType.ADMIN,
                'flags': ['my_key', 'admin_flag']
            }],
            'groups': []
        }, save=True, force=True)
        folderModel.requireAccessFlags(folder, user=self.users[1], flags='my_key')

        folder = folderModel.setUserAccess(
            folder, self.users[1], level=AccessType.READ, save=True, force=True, flags=[])
        self.assertFalse(folderModel.hasAccessFlags(folder, self.users[1], flags='my_key'))

        folder = folderModel.setGroupAccess(
            folder, self.group, level=AccessType.READ, save=True, force=True, flags='my_key')
        folderModel.requireAccessFlags(folder, user=self.users[1], flags='my_key')

        # Testing with flags=None should give sensible behavior
        folderModel.requireAccessFlags(folder, user=None, flags=None)

        # Test filtering results by access flags (both ACModel and AclMixin)
        for model, doc in ((folderModel, folder), (itemModel, item)):
            cursor = model.find({})
            self.assertGreater(len(list(cursor)), 0)

            cursor = model.find({})
            filtered = list(model.filterResultsByPermission(
                cursor, user=None, level=AccessType.READ, flags='my_key'))
            self.assertEqual(len(filtered), 0)

            cursor = model.find({})
            filtered = list(model.filterResultsByPermission(
                cursor, user=self.users[1], level=AccessType.READ, flags=('my_key', 'admin_flag')))
            self.assertEqual(len(filtered), 1)
            self.assertEqual(filtered[0]['_id'], doc['_id'])
class AssignmentResource(Resource):
    def __init__(self):
        super().__init__()

        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.user_m = User()

        self.setupRoutes()

    def setupRoutes(self):
        self.route('GET', (), handler=self.list)
        self.route('GET', (':a_id',), handler=self.getAssignment)
        self.route('GET', ('admin_data',), handler=self.getAdminData)

    def __findName(self, folder):
        imageFolder = self.__findImageFolder(folder)
        if isinstance(imageFolder, dict):
            return imageFolder['name']

        return ""

    def __findImageFolder(self, folder):
        owner = self.__findOwner(folder)
        ret = ""
        if self.folder_m.getAccessLevel(folder, self.getCurrentUser()) == AccessType.ADMIN:
            # this folder was created by this user, and so it will contain the images
            # printFail(folder)
            ret = folder
        else:
            meta = folder['meta']
            # this is the label file, and so should only have one entry in the metadata
            assert len(meta) == 1
            # that one entry contains link to the image folder, key must be the creator of this folder
            assert str(owner['_id']) in meta, (str(owner['_id']), meta)

            ret = self.folder_m.load(meta[str(owner['_id'])],
                                     level=AccessType.READ,
                                     user=self.getCurrentUser())

        return ret

    def __findLabelFolder(self, folder):
        owner = self.__findOwner(folder)
        ret = []
        if self.folder_m.getAccessLevel(folder, self.getCurrentUser()) == AccessType.ADMIN:
            # this folder was created by this user, so it will contain images
            if 'meta' not in folder:
                return []

            meta = folder['meta']
            for k, v in meta.items():
                ret.append(self.folder_m.load(v, level=AccessType.READ, user=self.getCurrentUser()))
        else:
            # this folder was not created by this user, so it will contain labels
            ret = [folder]

        return ret

    def __findOwner(self, folder):
        aclList = Folder().getFullAccessList(folder)
        for acl in aclList['users']:
            if acl['level'] == AccessType.ADMIN:
                return self.user_m.load(str(acl['id']), level=AccessType.READ, user=self.getCurrentUser())
        return None

    @access.public
    @autoDescribeRoute(
        Description('Get list of all assignments that it owns or is a part of')
            .param('limit', 'Number of assignments to return')
            .param('offset', 'offset from 0th assignment to start looking for assignments'))
    @rest.rawResponse
    def list(self, limit, offset):
        try:
            printOk((limit, offset))
            ret = self.__list(int(limit), int(offset))
            cherrypy.response.headers["Content-Type"] = "application/json"
            return dumps(ret)

        except:
            printFail(traceback.print_exc)

    def __list(self, limit=0, offset=0):
        user = self.getCurrentUser()
        folders = self.folder_m.find({'parentId': user['_id'],
                                      'parentCollection': 'user'}, limit=limit, offset=offset)
        ret = []
        for folder in folders:
            if self.__findName(folder):
                ret.append({'name': self.__findName(folder),
                            'image_folder': self.__findImageFolder(folder),
                            'label_folders': self.__findLabelFolder(folder),
                            'owner': self.__findOwner(folder)})

        return ret

    def __getAssignment(self, _id):
        assignments = self.__list()
        for assignment in assignments:
            # printOk2(assignment)
            if str(assignment['image_folder']['_id']) == _id:
                return assignment

        return None

    def __getAnnotators(self, assignment):
        ret = []
        for label_folder in assignment['label_folders']:
            printOk(label_folder)
            ret.append({
                'user': self.user_m.load(label_folder['parentId'], user=self.getCurrentUser(), level=AccessType.READ),
                'expanded': False,
            })

        return ret

    @access.public
    @autoDescribeRoute(
        Description('Get assignment by id').param('a_id', 'folder id that controls the assignment'))
    @rest.rawResponse
    def getAssignment(self, a_id):
        try:
            assignment = self.__getAssignment(a_id)
            return dumps(assignment)
        except:
            printFail(traceback.print_exc)

    @access.public
    @autoDescribeRoute(
        Description('Get admin data associated with assignment id').param('a_id',
                                                                          'folder id that controls the assignment'))
    @rest.rawResponse
    def getAdminData(self, a_id):
        try:
            assignment = self.__getAssignment(a_id)
            if assignment['owner']['_id'] == self.getCurrentUser()['_id']:
                # then current user is this assignment's admin
                return dumps({'annotators': self.__getAnnotators(assignment)})

        except:
            printFail(traceback.print_exc)
Exemple #12
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
Exemple #13
0
class App(Resource):
    def __init__(self):
        super(App, self).__init__()
        self.resourceName = 'app'
        self._model = Folder()

        self.route('POST', (), self.initApp)
        self.route('GET', (), self.listApp)
        self.route('DELETE', (':app_id', ), self.deleteApp)
        self.route('POST', (':app_id', 'release'), self.createNewRelease)
        self.route('GET', (':app_id', 'release'), self.getAllReleases)
        self.route('GET', (':app_id', 'release', ':release_id_or_name'),
                   self.getReleaseByIdOrName)
        self.route('DELETE', (':app_id', 'release', ':release_id_or_name'),
                   self.deleteReleaseByIdOrName)
        self.route('GET', (':app_id', 'extension'), self.getExtensions)
        self.route('GET', (':app_id', 'extension', ':extension_name'),
                   self.getExtensionByName)
        self.route('POST', (':app_id', 'extension'),
                   self.createOrUpdateExtension)
        self.route('DELETE', (':app_id', 'extension', ':ext_id'),
                   self.deleteExtension)

    @autoDescribeRoute(
        Description('Create a new application.').responseClass('Folder').notes(
            'If collectionId is missing or collectionName does not match an existing '
            'collection, a fresh new collection will be created with the "collection_name" '
            'given in parameters. '
            'By default the name "Applications" will be given to the collection.'
        ).param('name', 'The name of the application.').param(
            'app_description', 'Application description.',
            required=False).param(
                'collection_id',
                'The ID of the collection which contain the application',
                required=False).param(
                    'collection_name',
                    'The Name of the collection which be created to contain'
                    ' the application',
                    required=False).param('collection_description',
                                          'Collection description.',
                                          required=False).
        param('public',
              'Whether the collection should be publicly visible.',
              required=False,
              dataType='boolean',
              default=True).errorResponse(
                  'Write permission denied on the application.', 403))
    @access.user(scope=TokenScope.DATA_WRITE)
    def initApp(self, name, app_description, collection_id, collection_name,
                collection_description, public):
        """
        Create the directory for start a new application. By default, without specifying
        a ``collection_id``, it will create a new collection name either ``collection_name``
        if provided, or **'Applications'**. If the collection 'Applications already exist it will
        get it.
        Return the new application (as a folder) that always contain a default
        sub-folder named 'nightly'.

        :param name: Name of the new application
        :param app_description: Description of the new application
        :param collection_id: Id of the collection within create the application
        :param collection_name: Name of the collection which will be created
        :param collection_description: Description of the new collection
        :param public: Whether the new collection should be publicly visible
        :return: The new application folder
        """
        creator = self.getCurrentUser()
        # Load or create the collection that contain the application
        if collection_id:
            collection = Collection().load(collection_id, force=True)
        elif collection_name:
            collection = list(Collection().find({'name': collection_name},
                                                user=creator))
            if not collection:
                collection = Collection().createCollection(
                    name=collection_name,
                    description=collection_description,
                    public=public,
                    creator=creator)
            else:
                collection = collection[0]
        else:
            collection = list(Collection().find({'name': 'Applications'},
                                                user=creator))
            if not collection:
                collection = Collection().createCollection(
                    name='Applications',
                    description=collection_description,
                    public=public,
                    creator=creator)
            else:
                collection = collection[0]
        # Create the application
        if not app_description:
            app_description = ''
        app = self._model.createFolder(parent=collection,
                                       name=name,
                                       description=app_description,
                                       parentType='Collection',
                                       public=public,
                                       creator=creator)
        # Create the 'nightly' release which will be the default folder when uploading an extension
        self._model.createFolder(
            parent=app,
            name=constants.NIGHTLY_RELEASE_NAME,
            description='Uploaded each night, always up-to-date',
            parentType='Folder',
            public=public,
            creator=creator)
        # Set a default template name for extensions in the application,
        # this can be changed in anytime.
        return self._model.setMetadata(
            app, {
                'extensionNameTemplate':
                '{app_revision}_{os}_{arch}_{baseName}_{revision}'
            })

    @autoDescribeRoute(
        Description('List existing application.').responseClass(
            'Folder', array=True).param('app_id',
                                        'The ID of the application.',
                                        required=False).param(
                                            'collection_id',
                                            'The ID of the collection.',
                                            required=False).param(
                                                'name',
                                                'The name of the application.',
                                                required=False).
        param('text',
              'Provide text search of the application.',
              required=False).pagingParams(
                  defaultSort='name').errorResponse().errorResponse(
                      'Read permission denied on the application.', 403))
    @access.user(scope=TokenScope.DATA_READ)
    def listApp(self, app_id, collection_id, name, text, limit, offset, sort):
        """
        List existing applications base on some optional parameters:

        :param app_id: Application ID
        :param collection_id: Collection ID
        :param name: Name of the application
        :param text: Provide text search on the name of the application
        :return: List of applications
        """
        user = self.getCurrentUser()

        if ObjectId.is_valid(app_id):
            return self._model.load(app_id, user=user)
        else:
            if collection_id:
                parent = Collection().load(collection_id,
                                           user=user,
                                           level=AccessType.READ,
                                           exc=True)
            else:
                parent = Collection().findOne(query={'name': 'Applications'},
                                              user=user,
                                              offset=offset)
            if parent:
                filters = {}
                if text:
                    filters['$text'] = {'$search': text}
                if name:
                    filters['name'] = name

                return list(
                    self._model.childFolders(parentType='collection',
                                             parent=parent,
                                             user=user,
                                             offset=offset,
                                             limit=limit,
                                             sort=sort,
                                             filters=filters))
            return []

    @access.user(scope=TokenScope.DATA_WRITE)
    @autoDescribeRoute(
        Description('Delete an Application by ID.').modelParam(
            'app_id', model=Folder, 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 application.', 403))
    def deleteApp(self, folder, progress):
        """
        Delete the application by ID.

        :param id: Id of the application
        :return: Confirmation message with the deleted application name
        """
        return _deleteFolder(folder, progress, self.getCurrentUser())

    @autoDescribeRoute(
        Description('Create a new release.').responseClass('Folder').notes(
            'The application\'s revision is stored as metadata of the new release.'
        ).param('name', 'The release\'s name.').param(
            'app_id', 'The application\'s ID which contain the release').param(
                'app_revision',
                'The application\'s revision which correspond to the release'
            ).param('description',
                    'The application\'s description.',
                    required=False).param(
                        'public',
                        'Whether the release should be publicly visible.',
                        required=False,
                        dataType='boolean',
                        default=True).errorResponse())
    @access.user(scope=TokenScope.DATA_WRITE)
    def createNewRelease(self, name, app_id, app_revision, description,
                         public):
        """
        Create a new release with the ``name`` within the application. The ``app_revision``
        will permit to automatically choose this release when uploading an extension
        with a matching `app_revision`` metadata.

        :param name: Name of the new release
        :param app_id: Application ID
        :param app_revision: Revision of the application corresponding to this release
        :param description: Description of the new release
        :param public: Whether the release should be publicly visible

        :return: The new release folder
        """
        creator = self.getCurrentUser()
        application = self._model.load(app_id, user=creator)
        if not description:
            description = ''
        release = self._model.createFolder(parent=application,
                                           name=name,
                                           description=description,
                                           parentType='Folder',
                                           public=public,
                                           creator=creator)

        return self._model.setMetadata(release, {'revision': app_revision})

    @autoDescribeRoute(
        Description('Get all the releases from an application.').responseClass(
            'Folder').param('app_id', 'The application\'s ID.').pagingParams(
                defaultSort='name').errorResponse(
                    'ID was invalid.').errorResponse(
                        'Read permission denied on the application.', 403))
    @access.user(scope=TokenScope.DATA_READ)
    def getAllReleases(self, app_id, limit, offset, sort):
        """
        Get a list of all the release of an application.

        :param app_id: Application ID
        :return: List of all release within the application
        """
        user = self.getCurrentUser()
        application = self._model.load(app_id, user=user)
        # It returns the nightly release too
        return list(
            self._model.childFolders(application,
                                     'Folder',
                                     user=user,
                                     limit=limit,
                                     offset=offset,
                                     sort=sort))

    @autoDescribeRoute(
        Description(
            'Get a particular releases by ID or name from an application.').
        responseClass('Folder').param(
            'app_id', 'The application\'s ID.').param(
                'release_id_or_name',
                'The release\'s ID or name.').errorResponse(
                    'ID or name was invalid.').errorResponse(
                        'Read permission denied on the application.', 403))
    @access.user(scope=TokenScope.DATA_READ)
    def getReleaseByIdOrName(self, app_id, release_id_or_name):
        """
        Get the release folder by ID or by name.

        :param app_id: Application ID
        :param release_id_or_name: Could be either the release ID or the release name
        :return: The release folder
        """
        user = self.getCurrentUser()
        application = self._model.load(app_id, user=user)

        if ObjectId.is_valid(release_id_or_name):
            return self._model.load(release_id_or_name, user=user)
        release_folder = list(
            self._model.childFolders(
                application,
                'Folder',
                filters={'lowerName': release_id_or_name.lower()}))
        if not release_folder:
            return None
        return release_folder[0]

    @access.user(scope=TokenScope.DATA_WRITE)
    @autoDescribeRoute(
        Description('Delete a release by ID or name.').modelParam(
            'app_id', model=Folder,
            level=AccessType.ADMIN).param('release_id_or_name',
                                          'The release\'s ID or name.').
        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 release.', 403))
    def deleteReleaseByIdOrName(self, folder, release_id_or_name, progress):
        """
        Delete a release by ID or name.

        :param app_id: Application ID
        :param release_id_or_name: Could be either the release ID or the release name
        :param progress: Whether to record progress on this task
        :return: Confirmation message with the deleted release name
        """
        user = self.getCurrentUser()

        if ObjectId.is_valid(release_id_or_name):
            release = self._model.load(release_id_or_name, user=user)
        else:
            release_folder = list(
                self._model.childFolders(
                    folder,
                    'Folder',
                    filters={'lowerName': release_id_or_name.lower()}))
            if not release_folder:
                raise Exception("Couldn't find release %s" %
                                release_id_or_name)
            release = release_folder[0]

        return _deleteFolder(release, progress, self.getCurrentUser())

    @autoDescribeRoute(
        Description('List or search available extensions.').responseClass(
            'Extension').param('app_id', 'The ID of the application.').param(
                'release_id', 'The release id.', required=False).param(
                    'extension_id', 'The extension id.', required=False).param(
                        'os',
                        'The target operating system of the package.',
                        required=False,
                        enum=['linux', 'win',
                              'macosx']).param('arch',
                                               'The os chip architecture.',
                                               required=False,
                                               enum=['i386', 'amd64']).
        param('app_revision', 'The revision of the package.',
              required=False).param(
                  'search',
                  'Text matched against extension name or description.',
                  required=False).pagingParams(
                      defaultSort='created').errorResponse())
    @access.cookie
    @access.public
    def getExtensions(self, app_id, release_id, extension_id, os, arch,
                      app_revision, search, limit, offset, sort):
        """
        Get a list of extension which is filtered by some optional parameters:

        :param app_id: Application ID
        :param release_id: Release ID
        :param extension_id: Extension ID
        :param os: The operation system used for the extension.
        :param arch: The architecture compatible with the extension.
        :param app_revision: The revision of the application
        :param search: Text search on the name of the extension
        :return: The list of extensions
        """
        filters = {
            '$and': [{
                'meta.app_id': {
                    '$eq': app_id
                }
            }, {
                'meta.os': {
                    '$exists': True
                }
            }, {
                'meta.arch': {
                    '$exists': True
                }
            }, {
                'meta.revision': {
                    '$exists': True
                }
            }]
        }
        if ObjectId.is_valid(extension_id):
            filters['_id'] = ObjectId(extension_id)
        if ObjectId.is_valid(release_id):
            filters['folderId'] = ObjectId(release_id)
        if os:
            filters['meta.os'] = os
        if arch:
            filters['meta.arch'] = arch
        if app_revision:
            filters['meta.revision'] = app_revision
        if search:
            # Provide a full text search on baseName
            filters['meta.baseName'] = search

        return list(ExtensionModel().find(query=filters,
                                          limit=limit,
                                          offset=offset,
                                          sort=sort))

    @autoDescribeRoute(
        Description('Get a particular extension by name from an application.').
        responseClass('Item').param('app_id', 'The application\'s ID.').param(
            'extension_name', 'The extension\'s name.').errorResponse(
                'ID or name was invalid.').errorResponse(
                    'Read permission denied on the application.', 403))
    @access.user(scope=TokenScope.DATA_READ)
    def getExtensionByName(self, app_id, extension_name):
        """
        Get the extension item by name.

        :param app_id: Application ID
        :param extension_name: The extension name
        :return: The extension item
        """
        user = self.getCurrentUser()
        application = self._model.load(app_id, user=user)

        release_folder = list(self._model.childFolders(application, 'Folder'))
        if not release_folder:
            raise Exception('The application has no release')
        for release in release_folder:
            extensions = list(
                self._model.childItems(
                    release, filters={'lowerName': extension_name.lower()}))
            if extensions:
                return extensions[0]
        return None

    @autoDescribeRoute(  # noqa: C901
        Description('Create or Update an extension package.').param(
            'app_id', 'The ID of the App.').param(
                'os',
                'The target operating system of the package.',
                enum=['linux', 'win',
                      'macosx']).param('arch',
                                       'The os chip architecture.',
                                       enum=['i386', 'amd64']).
        param('baseName',
              'The baseName of the package (ie installer baseName).').param(
                  'repository_type',
                  'The type of the repository (svn, git).').param(
                      'repository_url', 'The url of the repository.').param(
                          'revision',
                          'The svn or git revision of the extension.').param(
                              'app_revision',
                              'The revision of the application '
                              'that the extension was built against.').param(
                                  'packagetype',
                                  'Installer, data, etc.').param(
                                      'codebase',
                                      'The codebase baseName (Ex: Slicer4).').
        param('description', 'Text describing the extension.').param(
            'release',
            'Release identifier (Ex: 0.0.1, 0.0.2, 0.1).',
            required=False).param(
                'icon_url',
                'The url of the icon for the extension.',
                required=False).param(
                    'development_status',
                    'Arbitrary description of the status of the extension '
                    '(stable, active, etc).',
                    required=False).
        param(
            'category',
            'Category under which to place the extension. Subcategories should be '
            'delimited by character. If none is passed, will render under '
            'the Miscellaneous category..',
            required=False
        ).param(
            'enabled',
            'Boolean indicating if the extension should be automatically enabled '
            'after its installation.',
            required=False).param('homepage',
                                  'The url of the extension homepage.',
                                  required=False).
        param('screenshots',
              'Space-separate list of URLs of screenshots for the extension.',
              required=False).param('contributors',
                                    'List of contributors of the extension',
                                    required=False).errorResponse())
    @access.cookie
    @access.public
    def createOrUpdateExtension(self, app_id, os, arch, baseName,
                                repository_type, repository_url, revision,
                                app_revision, packagetype, codebase,
                                description, release, icon_url,
                                development_status, category, enabled,
                                homepage, screenshots, contributors):
        """
        Upload an extension package in the database, in a specific release with providing
        ``release_id``. Or by default in the **'Nightly'** folder.

        :param app_id: The ID of the application.
        :param os: The operation system used for the extension.
        :param arch: The architecture compatible with the extension.
        :param baseName: The base name of the extension.
        :param repository_type: The type of repository (github, gitlab, ...).
        :param repository_url: The Url of the repository.
        :param revision: The revision of the extension.
        :param app_revision: The revision of the application.
        :param packagetype: Type of the extension.
        :param codebase: The codebase baseName.
        :param description: The description of the extension.
        :return: The status of the upload.
        """
        creator = self.getCurrentUser()
        application = self._model.load(app_id, user=creator)
        release_folder = None
        # Find the release by metadata revision
        releases = self._model.childFolders(application,
                                            'Folder',
                                            user=creator)
        for folder in releases:
            if 'meta' in folder:
                if folder['meta']['revision'] == app_revision:
                    release_folder = folder
                    break
        if not release_folder:
            # Only the nightly folder in the list
            release_folder = list(
                self._model.childFolders(
                    application,
                    'Folder',
                    user=creator,
                    filters={'name': constants.NIGHTLY_RELEASE_NAME}))
            if not release_folder:
                raise Exception('The %s folder not found.' %
                                constants.NIGHTLY_RELEASE_NAME)
            release_folder = release_folder[0]

        params = {
            'app_id': app_id,
            'baseName': baseName,
            'os': os,
            'arch': arch,
            'repository_type': repository_type,
            'repository_url': repository_url,
            'revision': revision,
            'app_revision': app_revision,
            'packagetype': packagetype,
            'codebase': codebase,
            'description': description
        }
        if release:
            params['release'] = release
        if icon_url:
            params['icon_url'] = icon_url
        if development_status:
            params['development_status'] = development_status
        if category:
            params['category'] = category
        if enabled:
            params['enabled'] = enabled
        if homepage:
            params['homepage'] = homepage
        if screenshots:
            params['screenshots'] = screenshots
        if contributors:
            params['contributors'] = contributors

        name = application['meta']['extensionNameTemplate'].format(**params)
        filters = {'name': name}
        # Only one extensions should be in this list
        extensions = list(ExtensionModel().get(release_folder,
                                               filters=filters))
        if not len(extensions):
            # The extension doesn't exist yet:
            extension = ExtensionModel().createExtension(
                name, creator, release_folder, params)
        elif len(extensions) == 1:
            extension = extensions[0]
        else:
            raise Exception(
                'Too many extensions found for the same name :"%s"' % name)

        # Check the file inside the extension Item
        files = Item().childFiles(extension)
        if files.count() == 1:
            old_file = files.next()
            # catch the event of upload success and remove the file
            events.bind('model.file.finalizeUpload.after', 'application',
                        File().remove(old_file))
        elif not files.count():
            # Extension new or empty
            pass
        else:
            raise Exception("More than 1 binary file in the extension.")

        old_meta = {
            'baseName': extension['meta']['baseName'],
            'os': extension['meta']['os'],
            'arch': extension['meta']['arch'],
            'revision': extension['meta']['revision'],
            'app_revision': extension['meta']['app_revision']
        }
        identifier_meta = {
            'baseName': baseName,
            'os': os,
            'arch': arch,
            'revision': revision,
            'app_revision': app_revision
        }
        if identifier_meta == old_meta and len(extensions):
            # The revision is the same than these before, no need to upload
            extension = ExtensionModel().setMetadata(extension, params)
            events.unbind('model.file.finalizeUpload.after', 'application')

        # Ready to upload the binary file
        return extension

    @access.user(scope=TokenScope.DATA_WRITE)
    @autoDescribeRoute(
        Description('Delete an Extension by ID.').param(
            'app_id', 'The ID of the App.').modelParam(
                'ext_id', model=Item, level=AccessType.WRITE).errorResponse(
                    'ID was invalid.').errorResponse(
                        'Admin access was denied for the extension.', 403))
    def deleteExtension(self, app_id, item):
        """
        Delete the extension by ID.

        :param app_id: Application ID
        :param ext_id: Extension ID
        :return: Confirmation message with the name of the deleted extension
        """
        Item().remove(item)
        return {'message': 'Deleted extension %s.' % item['name']}