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()
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'}
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
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()
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)
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()
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] }
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())
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()
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']
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()
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)
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()
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) } )
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
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
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()
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.')
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())