Esempio n. 1
0
def getSkin(lang="en-US"):
    """
    Function to return the context for the current instance.

    :param language: ISO language string, optional
    :type language: None
    :returns: context dict
    """
    contextCollection = CollectionModel().findOne({'name': 'Context'})
    skinFolder = FolderModel().findOne({
        'name': 'Skin',
        'parentCollection': 'collection',
        'parentId': contextCollection.get('_id')
    }) if contextCollection else None
    defaultSkin = {
        'name': '',
        'colors': {
            'primary': '#000000',
            'secondary': '#FFFFFF'
        },
        'about': ''
    }
    skin = skinFolder.get(
        'meta', defaultSkin) if skinFolder is not None else defaultSkin
    for s in ['name', 'about']:
        lookup = jsonld_expander.getByLanguage(
            skin.get(s, ""),
            lang if lang and lang not in ["@context.@language", ""] else None)
        skin[s] = lookup if lookup and lookup not in [
            None,
            [{}],
        ] else jsonld_expander.getByLanguage(skin[s], None)
        skin[s] = jsonld_expander.fileObjectToStr(skin[s][0]) if isinstance(
            skin[s], list) and len(skin[s]) else skin[s]
    return (skin)
Esempio n. 2
0
 def getSkin(self):
     skinFolder = Folder().findOne({
         'name':
         'Skin',
         'parentCollection':
         'collection',
         'parentId':
         Collection().findOne({
             'name': 'Context'
         }).get('_id')
     })
     blankSkin = {
         'name': '',
         'colors': {
             'primary': '#000000',
             'secondary': '#FFFFFF'
         },
         'about': ''
     }
     skin = skinFolder.get('meta', blankSkin)
     ab = getByLanguage(skin.get('about'))
     dSkin = {
         "about":
         ab if isinstance(ab, str) else ab.get('@value', ab.get('@id', ''))
         if isinstance(ab, dict) else '',
         "colors":
         skin.get('colors', blankSkin.get('colors')),
         "name":
         getByLanguage(skin.get('name'))
     }
     return (dSkin)
    def testFileProcessHandler(self):
        admin, user = self.users

        # Create a collection, folder, and item
        collection = Collection().createCollection('collection1', admin, public=True)
        folder = Folder().createFolder(collection, 'folder1', parentType='collection', public=True)
        item = Item().createItem('item1', admin, folder)

        # Upload non-DICOM files
        self._uploadNonDicomFiles(item, admin)
        nonDicomItem = Item().load(item['_id'], force=True)
        self.assertIsNone(nonDicomItem.get('dicom'))

        # Upload DICOM files
        self._uploadDicomFiles(item, admin)

        # Check if the 'dicomItem' is well processed
        dicomItem = Item().load(item['_id'], force=True)
        self.assertIn('dicom', dicomItem)
        self.assertHasKeys(dicomItem['dicom'], ['meta', 'files'])

        # Check if the files list contain the good keys and all the file are well sorted
        for i in range(0, 4):
            self.assertTrue('_id' in dicomItem['dicom']['files'][i])
            self.assertTrue('name' in dicomItem['dicom']['files'][i])
            self.assertEqual(dicomItem['dicom']['files'][i]['name'], 'dicomFile{}.dcm'.format(i))
            self.assertTrue('SeriesNumber' in dicomItem['dicom']['files'][i]['dicom'])
            self.assertTrue('InstanceNumber' in dicomItem['dicom']['files'][i]['dicom'])
            self.assertTrue('SliceLocation' in dicomItem['dicom']['files'][i]['dicom'])

        # Check the common metadata
        self.assertIsNotNone(dicomItem['dicom']['meta'])
 def testDicomWithBinaryValues(self):
     # One of the test files in the pydicom module will throw an IOError
     # when parsing metadata.  We should work around that and still be able
     # to import the file
     samplePath = os.path.join(os.path.dirname(os.path.abspath(
         pydicom.__file__)), 'data', 'test_files', 'OBXXXX1A.dcm')
     admin, user = self.users
     # Create a collection, folder, and item
     collection = Collection().createCollection('collection5', admin, public=True)
     folder = Folder().createFolder(collection, 'folder5', parentType='collection', public=True)
     item = Item().createItem('item5', admin, folder)
     # Upload this dicom file
     with open(samplePath, 'rb') as fp, _EventHelper('dicom_viewer.upload.success') as helper:
         dcmFile = Upload().uploadFromFile(
             obj=fp,
             size=os.path.getsize(samplePath),
             name=os.path.basename(samplePath),
             parentType='item',
             parent=item,
             mimeType='application/dicom',
             user=user
         )
         self.assertIsNotNone(dcmFile)
         # Wait for handler success event
         handled = helper.wait()
         self.assertTrue(handled)
     # Check if the 'dicomItem' is well processed
     dicomItem = Item().load(item['_id'], force=True)
     self.assertIn('dicom', dicomItem)
     self.assertHasKeys(dicomItem['dicom'], ['meta', 'files'])
    def testSearchForDicomItem(self):
        admin, user = self.users

        # Create a collection, folder, and item
        collection = Collection().createCollection('collection3', admin, public=True)
        folder = Folder().createFolder(collection, 'folder3', parentType='collection', public=True)
        item = Item().createItem('item3', admin, folder)

        # Upload files
        self._uploadDicomFiles(item, admin)

        # Search for DICOM item with 'brain research' as common key/value
        resp = self.request(path='/resource/search', params={
            'q': 'brain research',
            'mode': 'dicom',
            'types': json.dumps(['item'])
        })
        self.assertStatusOk(resp)
        self.assertEqual(len(resp.json['item']), 1)
        self.assertEqual(resp.json['item'][0]['name'], 'item3')

        # Search for DICOM item with substring 'in resea' as common key/value
        resp = self.request(path='/resource/search', params={
            'q': 'in resea',
            'mode': 'dicom',
            'types': json.dumps(['item'])
        })
        self.assertStatusOk(resp)
        self.assertEqual(len(resp.json['item']), 1)
        self.assertEqual(resp.json['item'][0]['name'], 'item3')
    def testMakeDicomItem(self):
        admin, user = self.users

        # create a collection, folder, and item
        collection = Collection().createCollection('collection2', admin, public=True)
        folder = Folder().createFolder(collection, 'folder2', parentType='collection', public=True)
        item = Item().createItem('item2', admin, folder)

        # Upload files
        self._uploadDicomFiles(item, admin)

        # Check the endpoint 'parseDicom' for an admin user
        dicomItem = Item().load(item['_id'], force=True)
        dicomItem = self._purgeDicomItem(dicomItem)
        path = '/item/%s/parseDicom' % dicomItem.get('_id')
        resp = self.request(path=path, method='POST', user=admin)
        self.assertStatusOk(resp)
        dicomItem = Item().load(item['_id'], force=True)
        self.assertIn('dicom', dicomItem)
        self.assertHasKeys(dicomItem['dicom'], ['meta', 'files'])

        # Check the endpoint 'parseDicom' for an non admin user
        dicomItem = Item().load(item['_id'], force=True)
        dicomItem = self._purgeDicomItem(dicomItem)
        path = '/item/%s/parseDicom' % dicomItem.get('_id')
        resp = self.request(path=path, method='POST', user=user)
        self.assertStatus(resp, 403)
Esempio n. 7
0
 def setUp(self):
     base.TestCase.setUp(self)
     admin = {
         'email': '*****@*****.**',
         'login': '******',
         'firstName': 'Admin',
         'lastName': 'Last',
         'password': '******',
         'admin': True
     }
     self.admin = User().createUser(**admin)
     user = {
         'email': '*****@*****.**',
         'login': '******',
         'firstName': 'First',
         'lastName': 'Last',
         'password': '******',
         'admin': False
     }
     self.user = User().createUser(**user)
     coll = {
         'name': 'Test Collection',
         'description': 'The description',
         'public': True,
         'creator': self.admin
     }
     self.collection = Collection().createCollection(**coll)
     Folder().createFolder(parent=self.collection,
                           parentType='collection',
                           name='Public',
                           public=True,
                           creator=self.admin)
Esempio n. 8
0
 def _recalculateSizes(self, progress):
     fixes = 0
     models = [Collection(), User()]
     steps = sum(model.find().count() for model in models)
     progress.update(total=steps, current=0)
     for model in models:
         for doc in model.find():
             progress.update(increment=1)
             _, f = model.updateSize(doc)
             fixes += f
     return fixes
Esempio n. 9
0
    def load(self, info):
        getPlugin('jobs').load(info)

        name = 'thumbnails'
        info['apiRoot'].thumbnail = rest.Thumbnail()

        for model in (Item(), Collection(), Folder(), User()):
            model.exposeFields(level=AccessType.READ, fields='_thumbnails')
            events.bind('model.%s.remove' % model.name, name, removeThumbnails)

        events.bind('model.file.remove', name, removeThumbnailLink)
        events.bind('data.process', name, _onUpload)
Esempio n. 10
0
def lookUpPath(path, user=None, filter=True, force=False):
    """
    Look up a resource in the data hierarchy by path.

    :param path: path of the resource
    :param user: user with correct privileges to access path
    :param filter: Whether the returned model should be filtered.
    :type filter: bool
    :param force: if True, don't validate the access.
    :type force: bool
    """
    path = path.lstrip('/')
    pathArray = split(path)
    model = pathArray[0]

    if model == 'user':
        username = pathArray[1]
        parent = User().findOne({'login': username})

        if parent is None:
            raise ResourcePathNotFound('User not found: %s' % username)

    elif model == 'collection':
        collectionName = pathArray[1]
        parent = Collection().findOne({'name': collectionName})

        if parent is None:
            raise ResourcePathNotFound('Collection not found: %s' %
                                       collectionName)

    else:
        raise ValidationException('Invalid path format')

    try:
        document = parent
        if not force:
            ModelImporter.model(model).requireAccess(document, user)
        for token in pathArray[2:]:
            document, model = lookUpToken(token, model, document)
            if not force:
                ModelImporter.model(model).requireAccess(document, user)
    except (ValidationException, AccessException):
        # We should not distinguish the response between access and validation errors so that
        # adversarial users cannot discover the existence of data they don't have access to by
        # looking up a path.
        raise ResourcePathNotFound('Path not found: %s' % path)

    if filter:
        document = ModelImporter.model(model).filter(document, user)

    return {'model': model, 'document': document}
Esempio n. 11
0
    def create(self, applet, user):
        """
        Create an Assignment for a given Applet, returning an existing
        Assignment if one or more exists.

        :param applet: The ID of the Applet for which to find Assignments.
        :type applet: str
        :param user: User
        :type user: User
        :returns: New Assignments
        """
        # validate Applet ID
        try:
            applet = Applet().load(id=applet, force=True)
        except:
            raise ValidationException(
                message='Invalid Applet ID',
                field='applet'
            )
        assignmentsCollection = Collection().findOne({'name': 'Assignments'})
        # for some reason below didn't work/help.. so I added Assignments manually.
        if not assignmentsCollection:
            Collection().createCollection('Assignments')
        try:
            assignment = Folder().createFolder(
                parent=assignmentsCollection, parentType='collection',
                name=Applet().preferredName(applet), creator=user,
                reuseExisting=True, public=False)
        except:
            assignmentsCollection = Folder().createFolder(
                parent=user, parentType='user', name='Assignments',
                creator=user, reuseExisting=True, public=False)
            assignment = Folder().createFolder(
                parent=assignmentsCollection, parentType='folder',
                name=Applet().preferredName(applet), creator=user,
                reuseExisting=True, public=False)
        return(assignment)
Esempio n. 12
0
    def __init__(self):
        super(Collection, self).__init__()
        self.resourceName = 'collection'
        self._model = CollectionModel()

        self.route('DELETE', (':id',), self.deleteCollection)
        self.route('GET', (), self.find)
        self.route('GET', (':id',), self.getCollection)
        self.route('GET', (':id', 'details'), self.getCollectionDetails)
        self.route('GET', ('details',), self.getCollectionsDetails)
        self.route('GET', (':id', 'download'), self.downloadCollection)
        self.route('GET', (':id', 'access'), self.getCollectionAccess)
        self.route('POST', (), self.createCollection)
        self.route('PUT', (':id',), self.updateCollection)
        self.route('PUT', (':id', 'access'), self.updateCollectionAccess)
        self.route('PUT', (':id', 'metadata'), self.setMetadata)
        self.route('DELETE', (':id', 'metadata'), self.deleteMetadata)
Esempio n. 13
0
    def _getAssetsFolder(self, folderName):
        """
        Get or create a public folder, in the private "Homepage Assets" collection.

        This makes the folder effectively "unlisted" as it can't be browsed to by normal users, but
        its content can still be downloaded directly.

        :param folderName: The name of the folder to get or create.
        :return: The new folder document.
        """
        collection = Collection().createCollection(constants.COLLECTION_NAME,
                                                   public=False,
                                                   reuseExisting=True)
        folder = Folder().createFolder(collection,
                                       folderName,
                                       parentType='collection',
                                       public=True,
                                       reuseExisting=True)
        return folder
Esempio n. 14
0
    def findAssignments(self, applet):
        """
        Find all Assignments for a given Applet.

        :param applet: The ID of the Applet for which to find Assignments.
        :type applet: str
        :returns: list of Assignments
        """
        # validate Applet ID
        try:
            Applet().load(id=applet, force=True)
        except:
            raise ValidationException(
                message='Invalid Applet ID',
                field='applet'
            )
        allAssignments = [
            {
                '_id': a['_id'],
                '_modelType': 'collection'
            } for a in Collection().find({'name': 'Assignments'})
        ] + [
            {
                '_id': a['_id'],
                '_modelType': 'folder'
            } for a in Folder().find({'name': 'Assignments'})
        ]
        foundAssignments = Folder().find(
            {   '$and': [
                    {'$or': [
                        {'meta.applet.@id': str(applet)},
                        {'meta.applet.url': str(applet)}
                    ]},
                    {'$or': [
                        {'parentId': parent['_id']} for parent in allAssignments
                    ]}
                ]
            }
        )
        return(list(foundAssignments))
Esempio n. 15
0
class Collection(Resource):
    """API Endpoint for collections."""

    def __init__(self):
        super(Collection, self).__init__()
        self.resourceName = 'collection'
        self._model = CollectionModel()

        self.route('DELETE', (':id',), self.deleteCollection)
        self.route('GET', (), self.find)
        self.route('GET', (':id',), self.getCollection)
        self.route('GET', (':id', 'details'), self.getCollectionDetails)
        self.route('GET', ('details',), self.getCollectionsDetails)
        self.route('GET', (':id', 'download'), self.downloadCollection)
        self.route('GET', (':id', 'access'), self.getCollectionAccess)
        self.route('POST', (), self.createCollection)
        self.route('PUT', (':id',), self.updateCollection)
        self.route('PUT', (':id', 'access'), self.updateCollectionAccess)
        self.route('PUT', (':id', 'metadata'), self.setMetadata)
        self.route('DELETE', (':id', 'metadata'), self.deleteMetadata)

    @access.public(scope=TokenScope.DATA_READ)
    @filtermodel(model=CollectionModel)
    @autoDescribeRoute(
        Description('List or search for collections.')
        .responseClass('Collection', array=True)
        .param('name', 'Name of the collection', required=False)
        .param(
            'text',
            'Pass this to perform a text search for collections.',
            required=False
        )
        .pagingParams(defaultSort='name')
    )
    def find(self, name, text, limit, offset, sort):
        user = self.getCurrentUser()

        collections = self._model.textSearch(
            text,
            user=user,
            limit=limit,
            offset=offset
        ) if text is not None else self._model.list(
            user=user,
            offset=offset,
            limit=limit,
            sort=sort
        )

        return ([
            collection for collection in collections if name==collection[
                'name'
            ]
        ]) if name is not None else collections 

    @access.user(scope=TokenScope.DATA_WRITE)
    @filtermodel(model=CollectionModel)
    @autoDescribeRoute(
        Description('Create a new collection.')
        .responseClass('Collection')
        .param('name', 'Name for the collection. Must be unique.')
        .param('description', 'Collection description.', required=False)
        .param('public', 'Whether the collection should be publicly visible.',
               required=False, dataType='boolean', default=False)
        .errorResponse()
        .errorResponse('You are not authorized to create collections.', 403)
    )
    def createCollection(self, name, description, public):
        user = self.getCurrentUser()

        if not self._model.hasCreatePrivilege(user):
            raise AccessException('You are not authorized to create collections.')

        return self._model.createCollection(
            name=name, description=description, public=public, creator=user)

    @access.public(scope=TokenScope.DATA_READ)
    @filtermodel(model=CollectionModel)
    @autoDescribeRoute(
        Description('Get a collection by ID.')
        .responseClass('Collection')
        .modelParam('id', model=CollectionModel, level=AccessType.READ)
        .errorResponse('ID was invalid.')
        .errorResponse('Read permission denied on the collection.', 403)
    )
    def getCollection(self, collection):
        return collection

    @access.public(scope=TokenScope.DATA_READ)
    @autoDescribeRoute(
        Description('Get detailed information of accessible collections.')
    )
    def getCollectionsDetails(self):
        count = self._model.findWithPermissions(user=self.getCurrentUser()).count()
        return {
            'nCollections': count
        }

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

    @access.public(scope=TokenScope.DATA_READ, cookie=True)
    @autoDescribeRoute(
        Description('Download an entire collection as a zip archive.')
        .modelParam('id', model=CollectionModel, level=AccessType.READ)
        .jsonParam('mimeFilter', 'JSON list of MIME types to include.', requireArray=True,
                   required=False)
        .produces('application/zip')
        .errorResponse('ID was invalid.')
        .errorResponse('Read access was denied for the collection.', 403)
    )
    def downloadCollection(self, collection, mimeFilter):
        setResponseHeader('Content-Type', 'application/zip')
        setContentDisposition(collection['name'] + '.zip')

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

    @access.user(scope=TokenScope.DATA_OWN)
    @autoDescribeRoute(
        Description('Get the access control list for a collection.')
        .modelParam('id', model=CollectionModel, level=AccessType.ADMIN)
        .errorResponse('ID was invalid.')
        .errorResponse('Admin permission denied on the collection.', 403)
    )
    def getCollectionAccess(self, collection):
        return self._model.getFullAccessList(collection)

    @access.user(scope=TokenScope.DATA_OWN)
    @filtermodel(model=CollectionModel, addFields={'access'})
    @autoDescribeRoute(
        Description('Set the access control list for a collection.')
        .modelParam('id', model=CollectionModel, level=AccessType.ADMIN)
        .jsonParam('access', 'The access control list as JSON.', requireObject=True)
        .jsonParam('publicFlags', 'List of public access flags to set on the collection.',
                   required=False, requireArray=True)
        .param('public', 'Whether the collection should be publicly visible.',
               dataType='boolean', required=False)
        .param('recurse', 'Whether the policies should be applied to all '
               'folders under this collection 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 permission denied on the collection.', 403)
    )
    def updateCollectionAccess(self, collection, access, public, recurse, progress, publicFlags):
        user = self.getCurrentUser()
        progress = progress and recurse

        with ProgressContext(progress, user=user, title='Updating permissions',
                             message='Calculating progress...') as ctx:
            if progress:
                ctx.update(total=self._model.subtreeCount(
                    collection, includeItems=False, user=user, level=AccessType.ADMIN))
            return self._model.setAccessList(
                collection, access, save=True, user=user, recurse=recurse,
                progress=ctx, setPublic=public, publicFlags=publicFlags)

    @access.user(scope=TokenScope.DATA_READ)
    @filtermodel(model=CollectionModel)
    @autoDescribeRoute(
        Description('Edit a collection by ID.')
        .responseClass('Collection')
        .modelParam('id', model=CollectionModel, level=AccessType.WRITE)
        .param('name', 'Unique name for the collection.', required=False, strip=True)
        .param('description', 'Collection description.', required=False, strip=True)
        .errorResponse('ID was invalid.')
        .errorResponse('Write permission denied on the collection.', 403)
    )
    def updateCollection(self, collection, name, description):
        if name is not None:
            collection['name'] = name
        if description is not None:
            collection['description'] = description

        return self._model.updateCollection(collection)

    @access.user(scope=TokenScope.DATA_OWN)
    @autoDescribeRoute(
        Description('Delete a collection by ID.')
        .modelParam('id', model=CollectionModel, level=AccessType.ADMIN)
        .errorResponse('ID was invalid.')
        .errorResponse('Admin permission denied on the collection.', 403)
    )
    def deleteCollection(self, collection):
        self._model.remove(collection)
        return {'message': 'Deleted collection %s.' % collection['name']}

    @access.user(scope=TokenScope.DATA_WRITE)
    @filtermodel(model=CollectionModel)
    @autoDescribeRoute(
        Description('Set metadata fields on a collection.')
        .responseClass('Collection')
        .notes('Set metadata fields to null in order to delete them.')
        .modelParam('id', model=CollectionModel, 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 collection.', 403)
    )
    def setMetadata(self, collection, metadata, allowNull):
        return self._model.setMetadata(collection, metadata, allowNull=allowNull)

    @access.user(scope=TokenScope.DATA_WRITE)
    @filtermodel(CollectionModel)
    @autoDescribeRoute(
        Description('Delete metadata fields on a collection.')
        .responseClass('Collection')
        .modelParam('id', model=CollectionModel, 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 collection.', 403)
    )
    def deleteMetadata(self, collection, fields):
        return self._model.deleteMetadata(collection, fields)
    def testDownload(self):
        collection = Collection().createCollection('collection1', public=True)
        folder = Folder().createFolder(collection,
                                       'folder1',
                                       parentType='collection',
                                       public=True)
        item = Item().createItem('item1', self.admin, folder)

        # Path to test files
        file1Path = os.path.join(self.filesDir, 'txt1.txt')
        file2Path = os.path.join(self.filesDir, 'txt2.txt')

        # Upload files to item
        with open(file1Path, 'rb') as fp:
            file1 = Upload().uploadFromFile(fp,
                                            os.path.getsize(file1Path),
                                            'txt1.txt',
                                            parentType='item',
                                            parent=item,
                                            user=self.admin)

        with open(file2Path, 'rb') as fp:
            file2 = Upload().uploadFromFile(fp,
                                            os.path.getsize(file2Path),
                                            'txt2.txt',
                                            mimeType='image/jpeg',
                                            parentType='item',
                                            parent=item,
                                            user=self.admin)

        # Download item and its files several times and ensure downloads are recorded
        # Each file is downloaded 10 times
        for _ in range(0, 5):
            self._downloadItem(item['_id'])
            self._downloadFile(file1['_id'])
            self._downloadFile(file2['_id'])

        # Download each file 1 time by downloading parent folder
        self._downloadFolder(folder['_id'])

        # Download each file over 2 requests
        self._downloadFileInTwoChunks(file1['_id'])
        self._downloadFileInTwoChunks(file2['_id'])

        # Download each file partially, adding 1 to start and 4 to requested
        self._downloadPartialFile(file1['_id'])
        self._downloadPartialFile(file2['_id'])

        # Download entire collection
        # Each file is downloaded 1 additional time
        path = '/collection/%s/download' % collection['_id']
        resp = self.request(path, user=self.admin, isJson=False)

        # Iterate through generator to trigger download events
        for data in resp.body:
            data

        # Download collection filtered by mime type
        # file2 is downloaded one additional time
        path = '/collection/%s/download' % collection['_id']
        resp = self.request(path,
                            user=self.admin,
                            isJson=False,
                            method='GET',
                            params={
                                'id': collection['_id'],
                                'mimeFilter': json.dumps(['image/jpeg'])
                            })

        # iterate through generator to trigger download events
        for data in resp.body:
            data

        self._checkDownloadsCount(file1['_id'], 14, 18, 13)
        self._checkDownloadsCount(file2['_id'], 15, 19, 14)