예제 #1
0
파일: assets.py 프로젝트: GetmeUK/hangar51
def set_expires():
    """Set the expiry date for an asset"""

    # Validate the parameters
    form = SetExpiresForm(request.values)
    if not form.validate():
        return fail('Invalid request', issues=form.errors)
    form_data = form.data

    # Get the asset
    asset = Asset.one(And(Q.account == g.account, Q.uid == form_data['uid']))

    # Update the assets `expires` value
    if 'expires' in form_data:
        # Set `expires`
        asset.expires = form_data['expires']
        asset.update('expires', 'modified')

    else:
        # Unset `expires`
        Asset.get_collection().update({'_id': asset._id},
                                      {'$unset': {
                                          'expires': ''
                                      }})
        asset.update('modified')

    return success()
예제 #2
0
def test_get(client, test_local_account, test_local_assets):
    account = test_local_account

    # Find a file and image asset to get
    file_asset = Asset.one(Q.name == 'file')
    image_asset = Asset.one(Q.name == 'image')

    # Get the details for a file
    response = client.get(url_for('api.get'),
                          data=dict(api_key=account.api_key,
                                    uid=file_asset.uid))
    assert response.json['status'] == 'success'

    # Check that the asset information returned is correct
    payload = response.json['payload']
    assert payload.get('created') is not None
    assert payload['ext'] == 'zip'
    assert payload['meta']['filename'] == 'file.zip'
    assert payload.get('modified') is not None
    assert payload['name'] == 'file'
    assert payload['type'] == 'file'
    assert payload.get('uid') is not None
    assert payload['store_key'] == 'file.' + payload['uid'] + '.zip'

    # Get the details for an image
    response = client.get(url_for('api.get'),
                          data=dict(api_key=account.api_key,
                                    uid=image_asset.uid))
    assert response.json['status'] == 'success'

    # Check that the asset information returned is correct
    payload = response.json['payload']

    assert payload.get('created') is not None
    assert payload['ext'] == 'jpg'
    assert payload['meta']['filename'] == 'image.jpg'
    assert payload['meta']['image'] == {'size': [720, 960], 'mode': 'RGB'}
    assert payload.get('modified') is not None
    assert payload['name'] == 'image'
    assert payload['type'] == 'image'
    assert payload.get('uid') is not None
    assert payload['store_key'] == 'image.' + payload['uid'] + '.jpg'
    assert len(payload['variations']) == 1

    variation = payload['variations'][0]
    assert variation['name'] == 'test'
    assert variation['ext'] == 'webp'
    key = 'image.{uid}.test.{version}.webp'.format(
        uid=payload['uid'], version=variation['version'])
    assert variation['store_key'] == key
    assert variation.get('version') is not None
    assert variation['meta']['image'] == {'size': [75, 100], 'mode': 'RGBA'}
예제 #3
0
def test_purge_expired_assets(celery_app, test_images):
    # Set the expiry date for all assets to an hour ago
    expires = datetime.now(timezone.utc) - timedelta(seconds=3600)
    expires = time.mktime(expires.timetuple())
    assets = Asset.many()
    for asset in assets:
        asset.expires = expires
        asset.update('modified', 'expires')

    # Call the `purge_expired_assets` task
    task = celery_app.tasks['purge_expired_assets']
    task.apply()

    # Check all the assets where purged
    assert Asset.count() == 0
예제 #4
0
파일: app.py 프로젝트: GetmeUK/hangar51
    def run(self):

        # Confirm the drop
        if not self.confirm('Enter the following string to confirm drop', \
                    'hangar51'):
            return

        # Delete all accounts, assets and files
        accounts = Account.many()
        for account in accounts:
            account.purge()

        # Drop the collections
        Asset.get_collection().drop()
        Account.get_collection().drop()
예제 #5
0
    def run(self, name):

        # Validate the parameters
        form = ViewAccountForm(name=name)
        if not form.validate():
            self.err(**form.errors)
            return

        # Find the account to view
        account = Account.one(Q.name == form.data['name'])

        # Output details of the account
        output = [("About '{0}':".format(account.name), 'underline_bold_blue')]

        pairs = [('created', account.created), ('modified', account.modified),
                 ('assets', Asset.count(Q.account == account)),
                 ('api_key', account.api_key),
                 ('backend', account.backend.get('backend', 'unknown'))]

        for key in sorted(account.backend.keys()):
            if key == 'backend':
                continue
            pairs.append(('> ' + key, account.backend[key]))

        # Find the longest key so we pad/align values
        width = sorted([len(p[0]) for p in pairs])[-1] + 2

        for pair in pairs:
            pair_str = '- {key:-<{width}} {value}'.format(key=pair[0].ljust(
                width, '-'),
                                                          value=pair[1],
                                                          width=width)
            output.append((pair_str, 'blue'))

        self.out(*output)
예제 #6
0
    def purge(self):
        """Deletes the account along with all related assets and files."""
        from models.assets import Asset

        # Purge all assets
        for asset in Asset.many(Q.account == self):
            asset.account = self
            asset.purge()

        # Delete self
        self.delete()
예제 #7
0
def test_generate_variations(client, test_backends, test_images):
    # Define a set of variations for the image
    variations = {
        'test1': [['fit', [200, 200]], ['crop', [0, 0.5, 0.5, 0]],
                  ['rotate', 90], ['output', {
                      'format': 'jpg',
                      'quality': 50
                  }]],
        'test2': [['fit', [100, 100]], ['rotate', 90],
                  ['crop', [0, 0.5, 0.5, 0]], ['rotate', 180],
                  ['output', {
                      'format': 'webp',
                      'quality': 50
                  }]]
    }

    # Test each backend
    for account in test_backends:
        asset = Asset.one(And(Q.account == account, Q.name == 'image'))
        response = client.post(url_for('api.generate_variations'),
                               data=dict(api_key=account.api_key,
                                         uid=asset.uid,
                                         variations=json.dumps(variations),
                                         on_delivery='wait'))
        assert response.json['status'] == 'success'

        # Check the response is correct
        payload = response.json['payload']
        assert len(payload.keys()) == 2

        # Test variation 1
        assert 'test1' in payload
        assert payload['test1']['ext'] == 'jpg'
        assert payload['test1']['name'] == 'test1'
        key = 'image.{uid}.test1.{version}.jpg'.format(
            uid=asset.uid, version=payload['test1']['version'])
        assert payload['test1']['store_key'] == key
        assert payload['test1']['meta']['image'] == {
            'mode': 'RGB',
            'size': [200, 150]
        }

        # Test variation 2
        assert 'test2' in payload
        assert payload['test2']['ext'] == 'webp'
        assert payload['test2']['name'] == 'test2'
        key = 'image.{uid}.test2.{version}.webp'.format(
            uid=asset.uid, version=payload['test2']['version'])
        assert payload['test2']['store_key'] == key
        assert payload['test2']['meta']['image'] == {
            'mode': 'RGBA',
            'size': [100, 75]
        }
예제 #8
0
파일: assets.py 프로젝트: GetmeUK/hangar51
def get():
    """Get the details for an asset"""

    # Validate the parameters
    form = GetForm(request.values)
    if not form.validate():
        return fail('Invalid request', issues=form.errors)
    form_data = form.data

    # Get the asset
    asset = Asset.one(And(Q.account == g.account, Q.uid == form_data['uid']))

    return success(asset.to_json_type())
예제 #9
0
    def purge_expired_assets():
        """Purge assets which have expired"""

        # Get any asset that has expired
        now = time.mktime(datetime.now(timezone.utc).timetuple())
        assets = Asset.many(And(
            Exists(Q.expires, True),
            Q.expires <= now
            ))

        # Purge each asset
        for asset in assets:
            asset.purge()
예제 #10
0
def test_download(client, test_local_account, test_local_assets):
    account = test_local_account

    # Find an asset to download
    file_asset = Asset.one(Q.name == 'file')

    # Download the file
    response = client.get(url_for('api.download'),
                          data=dict(api_key=account.api_key,
                                    uid=file_asset.uid))
    assert response.content_type == 'application/zip'
    content_disposition = 'attachment; filename=' + file_asset.store_key
    assert response.headers['Content-Disposition'] == content_disposition
    assert len(response.data) == file_asset.meta['length']
예제 #11
0
파일: assets.py 프로젝트: GetmeUK/hangar51
def generate_variations():
    """Generate one or more variations for of an image asset"""

    # Validate the parameters
    form = GenerateVariationsForm(request.values)
    if not form.validate():
        return fail('Invalid request', issues=form.errors)
    form_data = form.data

    # Find the asset
    asset = Asset.one(And(Q.account == g.account, Q.uid == form_data['uid']))

    # Check the asset is an image
    if asset.type != 'image':
        return fail('Variations can only be generated for images')

    # Parse the variation data
    variations = json.loads(form_data['variations'])

    # Has the user specified how they want the results delivered?
    on_delivery = form_data['on_delivery'] or 'wait'
    if on_delivery == 'wait':
        # Caller is waiting for a response so generate the variations now

        # Retrieve the original file
        backend = g.account.get_backend_instance()
        f = backend.retrieve(asset.store_key)
        im = Image.open(f)

        # Generate the variations
        new_variations = {}
        for name, ops in variations.items():
            new_variations[name] = asset.add_variation(f, im, name, ops)
            new_variations[name] = new_variations[name].to_json_type()

        # Update the assets modified timestamp
        asset.update('modified')

        return success(new_variations)

    else:
        # Caller doesn't want to wait for a response so generate the variations
        # in the background.
        current_app.celery.send_task('generate_variations', [
            g.account._id, asset.uid, variations, form_data['webhook'].strip()
        ])

        return success()
예제 #12
0
파일: s3.py 프로젝트: GetmeUK/hangar51
    def store(self, f, key):
        """Store a file"""

        # Guess the content type
        content_type = Asset.guess_content_type(key)

        # Set the file to be cached to a year from now
        cache_control = 'max-age=%d, public' % (365 * 24 * 60 * 60)

        # Store the object
        obj = self.s3.Object(self.bucket.name, key)
        if content_type:
            obj.put(Body=f,
                    ContentType=content_type,
                    CacheControl=cache_control)
        else:
            obj.put(Body=f, CacheControl=cache_control)
예제 #13
0
def test_local_assets(client, test_local_account):
    """Create a set of test assets"""
    account = test_local_account

    # Upload all test assets
    filepath = 'tests/data/assets/uploads'
    for filename in os.listdir(filepath):

        # Load the file to upload
        with open(os.path.join(filepath, filename), 'rb') as f:
            file_stream = io.BytesIO(f.read())

        # Upload the file
        response = client.post(url_for('api.upload'),
                               data=dict(
                                   api_key=account.api_key,
                                   asset=(file_stream, filename),
                               ))

        # Generate a variation for the `image.jpg` asset
        if filename == 'image.jpg':
            variations = {
                'test': [['fit', [100, 100]],
                         ['output', {
                             'format': 'webp',
                             'quality': 50
                         }]]
            }

            client.post(url_for('api.generate_variations'),
                        data=dict(api_key=account.api_key,
                                  uid=response.json['payload']['uid'],
                                  variations=json.dumps(variations)))

    assets = Asset.many()
    yield assets

    # Purge the assets
    for asset in assets:

        # Reload the asset to make sure it still exists before we attempt to
        # purge it.
        asset = asset.by_id(asset._id)
        if asset:
            asset.purge()
예제 #14
0
    def generate_variations(account_id, asset_uid, variations, webhook=''):
        """Generate a set of variations for an image asset"""

        # Find the account
        account = Account.by_id(account_id)
        if not account:
            return

        # Find the asset
        asset = Asset.one(And(Q.account == account, Q.uid == asset_uid))
        if not asset:
            return

        # Check the asset hasn't expired
        if asset.expired:
            return

        # Retrieve the original file
        backend = account.get_backend_instance()
        f = backend.retrieve(asset.store_key)
        im = Image.open(f)

        # Generate the variations
        new_variations = {}
        for name, ops in variations.items():
            variation = asset.add_variation(im, name, ops)
            new_variations[name] = variation.to_json_type()

        # Update the assets modified timestamp
        asset.update('modified')

        # If a webhook has been provide call it with details of the new
        # variations.
        if webhook:
            requests.get(
                webhook,
                data={
                    'account': account.name,
                    'asset': asset.uid,
                    'variations': json.dumps(variations)
                    }
                )
예제 #15
0
def test_set_expires(client, test_local_account):
    account = test_local_account

    # Load a file to upload
    with open('tests/data/assets/uploads/file.zip', 'rb') as f:
        file_stream = io.BytesIO(f.read())

    # Create an asset
    response = client.post(url_for('api.upload'),
                           data=dict(api_key=account.api_key,
                                     asset=(file_stream, 'file.zip'),
                                     name='files/test'))

    # Get the asset we uploaded
    asset = Asset.one(
        And(Q.account == account, Q.uid == response.json['payload']['uid']))

    # Set an expiry date 1 hour from now
    expires = datetime.now(timezone.utc) + timedelta(seconds=3600)
    expires = time.mktime(expires.timetuple())

    response = client.post(url_for('api.set_expires'),
                           data=dict(api_key=account.api_key,
                                     uid=asset.uid,
                                     expires=str(expires)))
    assert response.json['status'] == 'success'

    # Reload the asset and check the expires has been correctly set
    asset.reload()
    assert asset.expires == expires

    # Unset the expiry date
    response = client.post(url_for('api.set_expires'),
                           data=dict(api_key=account.api_key, uid=asset.uid))
    assert response.json['status'] == 'success'

    # Reload the asset and check the expires has been correctly set
    asset.reload()
    assert asset.expires == None
예제 #16
0
파일: assets.py 프로젝트: GetmeUK/hangar51
def download():
    """Download an asset"""

    # Validate the parameters
    form = DownloadForm(request.values)
    if not form.validate():
        return fail('Invalid request', issues=form.errors)
    form_data = form.data

    # Get the asset
    asset = Asset.one(And(Q.account == g.account, Q.uid == form_data['uid']))

    # Retrieve the original file
    backend = g.account.get_backend_instance()
    f = backend.retrieve(asset.store_key)

    # Build the file response to return
    response = make_response(f.read())
    response.headers['Content-Type'] = asset.content_type
    response.headers['Content-Disposition'] = \
        'attachment; filename={0}'.format(asset.store_key)

    return response
예제 #17
0
def test_images(app, client, test_backends):
    """Create a test image asset for all backends"""
    for account in test_backends:

        # Load the file to upload
        with open('tests/data/assets/uploads/image.jpg', 'rb') as f:
            file_stream = io.BytesIO(f.read())

        # Upload the file
        response = client.post(url_for('api.upload'),
                               data=dict(api_key=account.api_key,
                                         asset=(file_stream, 'image.jpg')))

    assets = Asset.many()
    yield assets

    # Purge the assets
    for asset in assets:

        # Reload the asset to make sure it still exists before we attempt to
        # purge it.
        asset = asset.by_id(asset._id)
        if asset:
            asset.purge()
예제 #18
0
 def validate_uid(form, field):
     """Validate that the asset exists"""
     asset = Asset.one(And(Q.account == g.account, Q.uid == field.data))
     if not asset or asset.expired:
         raise ValidationError('Asset not found.')
예제 #19
0
파일: assets.py 프로젝트: GetmeUK/hangar51
def upload():
    """Upload an asset"""

    # Check a file has been provided
    fs = request.files.get('asset')
    if not fs:
        return fail('No `asset` sent.')

    # Validate the parameters
    form = UploadForm(request.values)
    if not form.validate():
        return fail('Invalid request', issues=form.errors)

    # Prep the asset name for
    form_data = form.data

    # Name
    name = form_data['name']
    if not name:
        name = os.path.splitext(fs.filename)[0]
    name = slugify_name(name)

    # Extension
    ext = os.path.splitext(fs.filename)[1].lower()[1:]

    # If there's no extension associated with then see if we can guess it using
    # the imghdr module
    if not ext:
        fs.stream.seek(0)
        ext = imghdr.what(fs.filename, fs.stream.read()) or ''

    # If the file is a recognized image format then attempt to read it as an
    # image otherwise leave it as a file.
    asset_file = fs.stream
    asset_meta = {}
    asset_type = Asset.get_type(ext)
    if asset_type is 'image':
        try:
            asset_file, asset_meta = prep_image(asset_file)
        except IOError as e:
            return fail('File appears to be an image but it cannot be read.')

    # Add basic file information to the asset meta
    asset_meta.update({
        'filename': fs.filename,
        'length': get_file_length(asset_file)
    })

    # Create the asset
    asset = Asset(account=g.account._id,
                  name=name,
                  ext=ext,
                  meta=asset_meta,
                  type=asset_type,
                  variations=[])

    if form_data['expires']:
        asset.expires = form_data['expires']

    # Generate a unique Id for the asset
    asset.uid = generate_uid(6)
    while Asset.count(And(Q.account == g.account, Q.uid == asset.uid)) > 0:
        asset.uid = generate_uid(6)

    # Store the original file
    asset.store_key = Asset.get_store_key(asset)
    backend = g.account.get_backend_instance()
    backend.store(asset_file, asset.store_key)

    # Save the asset
    asset.insert()

    return success(asset.to_json_type())