def process_and_create_file(project_id, name, length, mime_type): """Base function that handles the hashing of a file name and the creation of a record in the files collection. This function is use in the nodes module on assets_create. :type project_id: string :param bucket_name: The project id, uset to fetch the gcs bucket. :type name: string :param subdir: The the filename (currently used to build the path). :type length: int :param subdir: Filesize in bit (in case we start the upload from the js interface, we get the size for free, otherwise at the moment we hardcode it to 0) :type mime_type: string :param subdir: MIME type used do display/preview the file accordingly """ root, ext = os.path.splitext(name) # Hash name based on file name, user id and current timestamp hash_name = name + str(current_user.objectid) + str(round(time.time())) link = hashlib.sha1(hash_name).hexdigest() link = os.path.join(link[:2], link + ext) src_dir_path = os.path.join(app.config['UPLOAD_DIR'], str(current_user.objectid)) # Move the file in designated location destination_dir = os.path.join(app.config['SHARED_DIR'], link[:2]) if not os.path.isdir(destination_dir): os.makedirs(destination_dir) # (TODO) Check if filename already exsits src_file_path = os.path.join(src_dir_path, name) dst_file_path = os.path.join(destination_dir, link[3:]) # (TODO) Thread this operation shutil.move(src_file_path, dst_file_path) api = system_util.pillar_api() file_item = File({ 'name': link[3:], 'filename': name, 'user': current_user.objectid, 'backend': 'gcs', 'md5': '', 'content_type': mime_type, 'length': length, 'project': project_id }) file_item.create(api=api) return file_item
def attach_project_pictures(project, api): """Utility function that queries for file objects referenced in picture header and square. In eve we currently can't embed objects in nested properties, this is the reason why this exists. This function should be moved in the API, attached to a new Project object. """ if project.properties.picture_square: # Collect the picture square file object project.properties.picture_square = File.find( project.properties.picture_square, api=api) if project.properties.picture_header: # Collect the picture header file object project.properties.picture_header = File.find( project.properties.picture_header, api=api)
def mass_attach_project_pictures(projects: typing.Iterable[pillarsdk.Project], *, api, header=True, square=True): """Attach file object to all projects in the list. Queries for all picture files at once and sets the header and square images. """ my_log = log.getChild('mass_attach_project_pictures') if not (header or square): raise ValueError("at least one of header/square must be True") if not projects: return file_ids = set() if header: file_ids.update(p.picture_header for p in projects if p.picture_header) if square: file_ids.update(p.picture_square for p in projects if p.picture_square) if not file_ids: return fid_list = list(file_ids) my_log.debug('mass-fetching %d files %s', len(fid_list), fid_list) file_resp = File.all({'where': {'_id': {'$in': fid_list}}}, api=api) file_obs = {f['_id']: f for f in file_resp['_items']} to_find = len(file_ids) found = 0 missing = 0 for p in projects: if header and p.picture_header: try: p.picture_header = file_obs[p.picture_header] except KeyError: p.picture_header = None my_log.warning('File %r not found, but used as picture_header in project %s', p.picture_header, p['_id']) missing += 1 else: found += 1 if square and p.picture_square: try: p.picture_square = file_obs[p.picture_square] except KeyError: p.picture_square = None my_log.warning('File %s not found, but used as picture_square in project %s', p.picture_square, p['_id']) missing += 1 else: found += 1 my_log.debug('found %d of %d pictures, there were %d missing', found, to_find, missing)
def item_delete(item_id): """We run this when updating a preview picture (after succesfully uploading a new one). """ api = system_util.pillar_api() try: file_item = File.find(item_id, api=api) file_item.delete(api=api) message = 'File deleted' except ResourceNotFound: message = 'File not found' return jsonify(status='success', data=dict(message=message))
def create(): name = request.form['name'] size = request.form['size'] content_type = request.form['type'] root, ext = os.path.splitext(name) # Hash name based on file name, user id and current timestamp hash_name = name + str(current_user.objectid) + str(round(time.time())) link = hashlib.sha1(hash_name).hexdigest() link = os.path.join(link[:2], link + ext) src_dir_path = os.path.join(app.config['UPLOAD_DIR'], str(current_user.objectid)) # Move the file in designated location destination_dir = os.path.join(app.config['SHARED_DIR'], link[:2]) if not os.path.isdir(destination_dir): os.makedirs(destination_dir) # (TODO) Check if filename already exsits src_file_path = os.path.join(src_dir_path, name) dst_file_path = os.path.join(destination_dir, link[3:]) # (TODO) Thread this operation shutil.copy(src_file_path, dst_file_path) api = SystemUtility.attract_api() node_file = File({ 'name': link, 'filename': name, 'user': current_user.objectid, 'backend': 'cdnsun', 'md5': '', 'content_type': content_type, 'length': size }) node_file.create(api=api) thumbnail = node_file.thumbnail_file('s', api=api) return jsonify(status='success', data=dict(id=node_file._id, link=thumbnail.link))
def get_file(file_id, api=None): # TODO: remove this function and just use the Pillar SDK directly. if file_id is None: return None if api is None: api = system_util.pillar_api() try: return File.find(file_id, api=api) except ResourceNotFound: f = sys.exc_info()[2].tb_frame.f_back tb = traceback.format_stack(f=f, limit=2) log.warning('File %s not found, but requested from %s\n%s', file_id, request.url, ''.join(tb)) return None
def create(): """Endpoint hit by the automatic upload of a picture, currently used in the edit node form. Some sanity checks are already done in the fronted, but additional checks can be implemented here. """ name = request.form['name'] size = request.form['size'] content_type = request.form['type'] field_name = request.form['field_name'] project_id = request.form['project_id'] file_item = process_and_create_file(project_id, name, size, content_type) api = system_util.pillar_api() f = File.find(file_item['_id'], api=api) thumbnail_link = f.thumbnail('s', api=api) return jsonify(status='success', data=dict(id=file_item._id, link=thumbnail_link, field_name=field_name))
def __call__(self, field, **kwargs): html = super(CustomFileSelectWidget, self).__call__(field, **kwargs) file_format = self.file_format file_format_regex = '' if file_format and file_format == 'image': file_format_regex = '^image\/(gif|jpe?g|png|tif?f|tga)$' button = [u'<div class="form-upload-file">'] if field.data: api = system_util.pillar_api() try: # Load the existing file attached to the field file_item = File.find(field.data, api=api) except ResourceNotFound: pass else: filename = Markup.escape(file_item.filename) if file_item.content_type.split('/')[0] == 'image': # If a file of type image is available, display the preview button.append(u'<img class="preview-thumbnail" src="{0}" />'.format( file_item.thumbnail('s', api=api))) else: button.append(u'<p>{}</p>'.format(filename)) button.append(u'<ul class="form-upload-file-meta">') # File name button.append(u'<li class="name">{0}</li>'.format(filename)) # File size button.append(u'<li class="size">({0} MB)</li>'.format( round((file_item.length / 1024) * 0.001, 2))) # Image resolution (if image) button.append(u'<li class="dimensions">{0}x{1}</li>'.format( file_item.width, file_item.height)) # Delete button button.append(u'<li class="delete">' u'<a href="#" class="file_delete" ' u'data-field-name="{field_name}" ' u'data-file_id="{file_id}"> ' u'<i class="pi-trash"></i> Delete</a></li>'.format( field_name=field.name, file_id=field.data)) # Download button for original file button.append(u'<li class="original">' u'<a href="{}" class="file_original"> ' u'<i class="pi-download"></i>Original</a></li>' .format(file_item.link)) button.append(u'</ul>') upload_url = u'%s/storage/stream/{project_id}' % current_app.config[ 'PILLAR_SERVER_ENDPOINT'] button.append(u'<input class="fileupload" type="file" name="file" ' u'data-url="{url}" ' u'data-field-name="{name}" ' u'data-token="{token}" ' u'data-file-format="{file_format}">' u'<div class="form-upload-progress"> ' u'<div class="form-upload-progress-bar" role="progressbar" ' u'aria-valuenow="0" aria-valuemin="0" aria-valuemax="100" ' u'style="width: 0%;"> ' u'</div> ' u'</div>'.format(url=upload_url, name=field.name, token=Markup.escape(current_user.id), file_format=Markup.escape(file_format_regex))) button.append(u'</div>') return HTMLString(html + u''.join(button))
def test_pickling_node(self): from pillarsdk import Node, File base_link = 'https://storage.googleapis.com/57534c07c37a7195f/_%2F326760eb7d7b244afe52' picture = File({ '_id': '57534ccdc379cf1b24a7196d', '_created': '2016-06-04T23:49:01.000+0200', '_updated': '2017-09-07T15:55:37.000+0200', '_etag': '4bb3f525ea637c612fca882e5fcf334c056544fd', 'status': 'complete', 'name': '326763a705364fe0b0eb7d7b244afe52.png', 'backend': 'gcs', 'format': 'png', 'variations': [ {'width': 160, 'length': 4705, 'content_type': 'image/jpeg', 'height': 160, 'file_path': '326763a705364fe0b0eb7d7b244afe52-b.jpg', 'size': 'b', 'link': base_link + '-b.jpg'}, {'width': 269, 'length': 8508, 'content_type': 'image/jpeg', 'height': 269, 'file_path': '326763a705364fe0b0eb7d7b244afe52-h.jpg', 'size': 'h', 'link': base_link + '-h.jpg'}, {'width': 269, 'length': 8508, 'content_type': 'image/jpeg', 'height': 269, 'file_path': '326763a705364fe0b0eb7d7b244afe52-m.jpg', 'size': 'm', 'link': base_link + '-m.jpg'}, ], 'filename': '01d.png', 'project': '57534c07c379cf1b24a7195f', 'width': 269, 'length': 9681, 'user': '******', 'content_type': 'image/png', 'height': 269, 'file_path': '326763a705364fe0b0eb7d7b244afe52.png', 'md5': '', 'length_aggregate_in_bytes': 47050, 'link': base_link + '.png', 'link_expires': '2117-09-08T14:54:35.250+0200', } ) parent_node = Node({ '_id': '54134', 'name': 'Dadday', }) original = Node({ '_id': '123456', 'name': 'über cooole node', 'parent': parent_node, 'picture': picture, }) pickled = pickle.dumps(original) restored = pickle.loads(pickled) self.assertEqual(restored._id, '123456') self.assertEqual(restored.name, 'über cooole node') self.assertIsInstance(restored.parent, Node) self.assertEqual(restored.parent.name, 'Dadday') url = 'https://storage.googleapis.com/57534c07c37a7195f/_%2F326760eb7d7b244afe52-m.jpg' self.assertEqual(url, original.picture.thumbnail('m')) self.assertEqual(url, restored.picture.thumbnail('m'))
def index(): """Custom files entry point """ rfiles = [] backend = app.config['STORAGE_BACKEND'] api = SystemUtility.attract_api() user = current_user.objectid node_picture = File() for file_ in request.files: filestorage = request.files[file_] # Save file on AttractWeb Storage picture_path = os.path.join( app.config['UPLOAD_DIR'], filestorage.filename) filestorage.save(picture_path) picture_file_file = open(picture_path, 'rb') if backend == 'pillar': hash_ = hashfile(picture_file_file, hashlib.md5()) name = "{0}{1}".format(hash_, os.path.splitext(picture_path)[1]) picture_file_file.close() file_check = node_picture.all( {"where": "path=='{0}'".format(name)}, api=api) file_check = file_check['_items'] if len(file_check) == 0: prop = {} prop['name'] = filestorage.filename prop['description'] = "File {0}".format(filestorage.filename) prop['user'] = user prop['content_type'] = filestorage.content_type # TODO Fix length value prop['length'] = filestorage.content_length prop['md5'] = hash_ prop['filename'] = filestorage.filename prop['backend'] = backend if backend in ["pillar"]: prop['path'] = name node_picture.post(prop, api=api) prop['_id'] = node_picture['_id'] if backend == 'pillar': node_picture.post_file(picture_path, name, api=api) node_picture.build_previews(name, api=api) url = "{0}/file_storage/file/{1}".format( app.config['PILLAR_SERVER_ENDPOINT'], prop['path']) rfiles.append( { "id": prop['_id'], "name": prop['filename'], "size": prop['length'], "url": url, "thumbnailUrl": url, "deleteUrl": url, "deleteType": "DELETE" }) else: url = "{0}/file_storage/file/{1}".format( app.config['PILLAR_SERVER_ENDPOINT'], file_check[0]['path']) rfiles.append( { "id": file_check[0]['_id'], "name": file_check[0]['filename'], "size": file_check[0]['length'], "url": url, "thumbnailUrl": url, "deleteUrl": url, "deleteType": "DELETE" }) return jsonify(dict(files=rfiles))
def view(node_id): #import time #start = time.time() api = SystemUtility.attract_api() # Get node with embedded picture data try: node = Node.find(node_id + '/?embedded={"picture":1, "node_type":1}', api=api) except ResourceNotFound: return abort(404) except ForbiddenAccess: return abort(403) node_type_name = node.node_type.name # JsTree functionality. # This return a lightweight version of the node, to be used by JsTree in the # frontend. We have two possible cases: # - https://pillar/<node_id>/view?format=jstree (construct the whole expanded # tree starting from the node_id. Use only once) # - https://pillar/<node_id>/view?format=jstree&children=1 (deliver the # children of a node - use in the navigation of the tree) if request.args.get('format') and request.args.get('format') == 'jstree': if request.args.get('children') == '1': if node_type_name == 'storage': storage = StorageNode(node) # Check if we specify a path within the storage path = request.args.get('path') # Generate the storage listing listing = storage.browse(path) # Inject the current node id in the response, so that JsTree can # expose the storage_node property and use it for further queries listing['storage_node'] = node._id if 'children' in listing: for child in listing['children']: child['storage_node'] = node._id return jsonify(listing) else: return jsonify(jstree_build_children(node)) else: return jsonify(items=jstree_build_from_node(node)) # Continue to process the node (for HTML, HTML embeded and JSON responses) # Set the default name of the template path based on the node name template_path = os.path.join('nodes', 'custom', node_type_name) # Set the default action for a template. By default is view and we override # it only if we are working storage nodes, where an 'index' is also possible template_action = 'view' # XXX Code to detect a node of type asset, and aggregate file data if node_type_name == 'asset': node_file = File.find(node.properties.file, api=api) node_file_children = node_file.children(api=api) # Attach the file node to the asset node setattr(node, 'file', node_file) try: asset_type = node_file.content_type.split('/')[0] except AttributeError: asset_type = None if asset_type == 'video': # Process video type and select video template sources = [] if node_file_children: for f in node_file_children._items: sources.append(dict(type=f.content_type, src=f.link)) setattr(node, 'video_sources', json.dumps(sources)) setattr(node, 'file_children', node_file_children) template_path = os.path.join(template_path, asset_type) elif asset_type == 'image': # Process image type and select image template #setattr(node, 'file_children', node_file_children) template_path = os.path.join(template_path, asset_type) else: # Treat it as normal file (zip, blend, application, etc) template_path = os.path.join(template_path, 'file') # XXX The node is of type project elif node_type_name == 'project': if node.properties.picture_square: picture_square = File.find(node.properties.picture_square, api=api) node.properties.picture_square = picture_square if node.properties.picture_header: picture_header = File.find(node.properties.picture_header, api=api) node.properties.picture_header = picture_header if node.properties.nodes_latest: list_latest = [] for node_id in node.properties.nodes_latest: try: node_item = Node.find(node_id, { 'projection': '{"name":1, "user":1, "node_type":1}', 'embedded': '{"user":1}', }, api=api) list_latest.append(node_item) except ForbiddenAccess: list_latest.append(FakeNodeAsset()) node.properties.nodes_latest = list(reversed(list_latest)) if node.properties.nodes_featured: list_featured = [] for node_id in node.properties.nodes_featured: try: node_item = Node.find(node_id, { 'projection': '{"name":1, "user":1, "picture":1, "node_type":1}', 'embedded': '{"user":1}', }, api=api) picture = File.find(node_item.picture, api=api) node_item.picture = picture list_featured.append(node_item) except ForbiddenAccess: list_featured.append(FakeNodeAsset()) node.properties.nodes_featured = list(reversed(list_featured)) elif node_type_name == 'storage': storage = StorageNode(node) path = request.args.get('path') listing = storage.browse(path) node.name = listing['name'] listing['storage_node'] = node._id # If the item has children we are working with a group if 'children' in listing: for child in listing['children']: child['storage_node'] = node._id child['name'] = child['text'] child['content_type'] = os.path.dirname(child['type']) node.children = listing['children'] template_action = 'index' else: node.status = 'published' node.length = listing['size'] node.download_link = listing['signed_url'] # Get previews if node.picture: node.picture = File.find(node.picture._id, api=api) # Get Parent try: parent = Node.find(node['parent'], api=api) except KeyError: parent = None except ResourceNotFound: parent = None # Get children try: children = Node.all( { 'where': '{"parent": "%s"}' % node._id, 'embedded': '{"picture": 1, "node_type": 1}' }, api=api) children = children._items except ForbiddenAccess: return abort(403) for child in children: if child.picture: child.picture = File.find(child.picture._id, api=api) if request.args.get('format'): if request.args.get('format') == 'json': node = node.to_dict() node['url_edit'] = url_for('nodes.edit', node_id=node['_id']), if parent: parent = parent.to_dict() return_content = jsonify({ 'node': node, 'children': children.to_dict(), 'parent': parent }) else: embed_string = '' # Check if we want to embed the content via an AJAX call if request.args.get('embed'): if request.args.get('embed') == '1': # Define the prefix for the embedded template embed_string = '_embed' # Check if template exists on the filesystem template_path = '{0}/{1}{2}.html'.format(template_path, template_action, embed_string) template_path_full = os.path.join(app.config['TEMPLATES_PATH'], template_path) if not os.path.exists(template_path_full): return "Missing template" return_content = render_template(template_path, node=node, type_names=type_names(), parent=parent, children=children, config=app.config) #print(time.time() - start) return return_content
def view(node_id): #import time #start = time.time() api = SystemUtility.attract_api() # Get node with embedded picture data try: node = Node.find(node_id + '/?embedded={"picture":1, "node_type":1}', api=api) except ResourceNotFound: return abort(404) except ForbiddenAccess: return abort(403) node_type_name = node.node_type.name # JsTree functionality. # This return a lightweight version of the node, to be used by JsTree in the # frontend. We have two possible cases: # - https://pillar/<node_id>/view?format=jstree (construct the whole expanded # tree starting from the node_id. Use only once) # - https://pillar/<node_id>/view?format=jstree&children=1 (deliver the # children of a node - use in the navigation of the tree) if request.args.get('format') and request.args.get('format') == 'jstree': if request.args.get('children') == '1': if node_type_name == 'storage': storage = StorageNode(node) # Check if we specify a path within the storage path = request.args.get('path') # Generate the storage listing listing = storage.browse(path) # Inject the current node id in the response, so that JsTree can # expose the storage_node property and use it for further queries listing['storage_node'] = node._id if 'children' in listing: for child in listing['children']: child['storage_node'] = node._id return jsonify(listing) else: return jsonify(jstree_build_children(node)) else: return jsonify(items=jstree_build_from_node(node)) # Continue to process the node (for HTML, HTML embeded and JSON responses) # Set the default name of the template path based on the node name template_path = os.path.join('nodes', 'custom', node_type_name) # Set the default action for a template. By default is view and we override # it only if we are working storage nodes, where an 'index' is also possible template_action = 'view' # XXX Code to detect a node of type asset, and aggregate file data if node_type_name == 'asset': node_file = File.find(node.properties.file, api=api) node_file_children = node_file.children(api=api) # Attach the file node to the asset node setattr(node, 'file', node_file) try: asset_type = node_file.content_type.split('/')[0] except AttributeError: asset_type = None if asset_type == 'video': # Process video type and select video template sources = [] if node_file_children: for f in node_file_children._items: sources.append(dict( type=f.content_type, src=f.link)) setattr(node, 'video_sources', json.dumps(sources)) setattr(node, 'file_children', node_file_children) template_path = os.path.join(template_path, asset_type) elif asset_type == 'image': # Process image type and select image template #setattr(node, 'file_children', node_file_children) template_path = os.path.join(template_path, asset_type) else: # Treat it as normal file (zip, blend, application, etc) template_path = os.path.join(template_path, 'file') # XXX The node is of type project elif node_type_name == 'project': if node.properties.picture_square: picture_square = File.find(node.properties.picture_square, api=api) node.properties.picture_square = picture_square if node.properties.picture_header: picture_header = File.find(node.properties.picture_header, api=api) node.properties.picture_header = picture_header if node.properties.nodes_latest: list_latest = [] for node_id in node.properties.nodes_latest: try: node_item = Node.find(node_id, { 'projection': '{"name":1, "user":1, "node_type":1}', 'embedded': '{"user":1}', }, api=api) list_latest.append(node_item) except ForbiddenAccess: list_latest.append(FakeNodeAsset()) node.properties.nodes_latest = list(reversed(list_latest)) if node.properties.nodes_featured: list_featured = [] for node_id in node.properties.nodes_featured: try: node_item = Node.find(node_id, { 'projection': '{"name":1, "user":1, "picture":1, "node_type":1}', 'embedded': '{"user":1}', }, api=api) picture = File.find(node_item.picture, api=api) node_item.picture = picture list_featured.append(node_item) except ForbiddenAccess: list_featured.append(FakeNodeAsset()) node.properties.nodes_featured = list(reversed(list_featured)) elif node_type_name == 'storage': storage = StorageNode(node) path = request.args.get('path') listing = storage.browse(path) node.name = listing['name'] listing['storage_node'] = node._id # If the item has children we are working with a group if 'children' in listing: for child in listing['children']: child['storage_node'] = node._id child['name'] = child['text'] child['content_type'] = os.path.dirname(child['type']) node.children = listing['children'] template_action = 'index' else: node.status = 'published' node.length = listing['size'] node.download_link = listing['signed_url'] # Get previews if node.picture: node.picture = File.find(node.picture._id, api=api) # Get Parent try: parent = Node.find(node['parent'], api=api) except KeyError: parent = None except ResourceNotFound: parent = None # Get children try: children = Node.all({ 'where': '{"parent": "%s"}' % node._id, 'embedded': '{"picture": 1, "node_type": 1}'}, api=api) children = children._items except ForbiddenAccess: return abort(403) for child in children: if child.picture: child.picture = File.find(child.picture._id, api=api) if request.args.get('format'): if request.args.get('format') == 'json': node = node.to_dict() node['url_edit'] = url_for('nodes.edit', node_id=node['_id']), if parent: parent = parent.to_dict() return_content = jsonify({ 'node': node, 'children': children.to_dict(), 'parent': parent }) else: embed_string = '' # Check if we want to embed the content via an AJAX call if request.args.get('embed'): if request.args.get('embed') == '1': # Define the prefix for the embedded template embed_string = '_embed' # Check if template exists on the filesystem template_path = '{0}/{1}{2}.html'.format(template_path, template_action, embed_string) template_path_full = os.path.join(app.config['TEMPLATES_PATH'], template_path) if not os.path.exists(template_path_full): return "Missing template" return_content = render_template(template_path, node=node, type_names=type_names(), parent=parent, children=children, config=app.config) #print(time.time() - start) return return_content
def assets_create(): name = request.form['name'] parent_id = request.form.get('parent_id') # Detect filetype by extension (improve by detectin real file type) root, ext = os.path.splitext(name) if ext in ['.jpg', '.jpeg', '.png', '.tif', '.tiff']: filetype = 'image' elif ext in ['.blend', '.txt', '.zip']: filetype = 'file' elif ext in ['.mov', '.avi', '.mp4', '.m4v']: filetype = 'video' else: filetype = 'file' # Hash name based on file name, user id and current timestamp hash_name = name + str(current_user.objectid) + str(round(time.time())) link = hashlib.sha1(hash_name).hexdigest() link = os.path.join(link[:2], link + ext) api = SystemUtility.attract_api() node_type = NodeType.find_first({ 'where': '{"name" : "asset"}', }, api=api) # We will create the Node object later on, after creating the file object node_asset_props = dict( name=name, #description=a.description, #picture=picture, user=current_user.objectid, node_type=node_type._id, #parent=node_parent, properties=dict( content_type=filetype, #file=a.link[4:], status='processing')) src_dir_path = os.path.join(app.config['UPLOAD_DIR'], str(current_user.objectid)) # Move the file in designated location destination_dir = os.path.join(app.config['SHARED_DIR'], link[:2]) if not os.path.isdir(destination_dir): os.makedirs(destination_dir) # (TODO) Check if filename already exsits src_file_path = os.path.join(src_dir_path, name) dst_file_path = os.path.join(destination_dir, link[3:]) # (TODO) Thread this operation shutil.copy(src_file_path, dst_file_path) if filetype == 'file': mime_type = 'application' else: mime_type = filetype content_type = "{0}/{1}".format(mime_type, ext.replace(".", "")) node_file = File({ 'name': link, 'filename': name, 'user': current_user.objectid, 'backend': 'cdnsun', 'md5': '', 'content_type': content_type, 'length': 0 }) node_file.create(api=api) node_asset_props['properties']['file'] = node_file._id if parent_id: node_asset_props['parent'] = parent_id node_asset = Node(node_asset_props) node_asset.create(api=api) return jsonify( link=link, name=name, filetype=filetype, asset_id=node_asset._id)
def index(): """Custom files entry point """ rfiles = [] backend = app.config['STORAGE_BACKEND'] api = SystemUtility.attract_api() user = current_user.objectid node_picture = File() for file_ in request.files: filestorage = request.files[file_] # Save file on AttractWeb Storage picture_path = os.path.join(app.config['UPLOAD_DIR'], filestorage.filename) filestorage.save(picture_path) picture_file_file = open(picture_path, 'rb') if backend == 'pillar': hash_ = hashfile(picture_file_file, hashlib.md5()) name = "{0}{1}".format(hash_, os.path.splitext(picture_path)[1]) picture_file_file.close() file_check = node_picture.all({"where": "path=='{0}'".format(name)}, api=api) file_check = file_check['_items'] if len(file_check) == 0: prop = {} prop['name'] = filestorage.filename prop['description'] = "File {0}".format(filestorage.filename) prop['user'] = user prop['content_type'] = filestorage.content_type # TODO Fix length value prop['length'] = filestorage.content_length prop['md5'] = hash_ prop['filename'] = filestorage.filename prop['backend'] = backend if backend in ["pillar"]: prop['path'] = name node_picture.post(prop, api=api) prop['_id'] = node_picture['_id'] if backend == 'pillar': node_picture.post_file(picture_path, name, api=api) node_picture.build_previews(name, api=api) url = "{0}/file_storage/file/{1}".format( app.config['PILLAR_SERVER_ENDPOINT'], prop['path']) rfiles.append({ "id": prop['_id'], "name": prop['filename'], "size": prop['length'], "url": url, "thumbnailUrl": url, "deleteUrl": url, "deleteType": "DELETE" }) else: url = "{0}/file_storage/file/{1}".format( app.config['PILLAR_SERVER_ENDPOINT'], file_check[0]['path']) rfiles.append({ "id": file_check[0]['_id'], "name": file_check[0]['filename'], "size": file_check[0]['length'], "url": url, "thumbnailUrl": url, "deleteUrl": url, "deleteType": "DELETE" }) return jsonify(dict(files=rfiles))
def assets_create(): name = request.form['name'] parent_id = request.form.get('parent_id') # Detect filetype by extension (improve by detectin real file type) root, ext = os.path.splitext(name) if ext in ['.jpg', '.jpeg', '.png', '.tif', '.tiff']: filetype = 'image' elif ext in ['.blend', '.txt', '.zip']: filetype = 'file' elif ext in ['.mov', '.avi', '.mp4', '.m4v']: filetype = 'video' else: filetype = 'file' # Hash name based on file name, user id and current timestamp hash_name = name + str(current_user.objectid) + str(round(time.time())) link = hashlib.sha1(hash_name).hexdigest() link = os.path.join(link[:2], link + ext) api = SystemUtility.attract_api() node_type = NodeType.find_first({ 'where': '{"name" : "asset"}', }, api=api) # We will create the Node object later on, after creating the file object node_asset_props = dict( name=name, #description=a.description, #picture=picture, user=current_user.objectid, node_type=node_type._id, #parent=node_parent, properties=dict( content_type=filetype, #file=a.link[4:], status='processing')) src_dir_path = os.path.join(app.config['UPLOAD_DIR'], str(current_user.objectid)) # Move the file in designated location destination_dir = os.path.join(app.config['SHARED_DIR'], link[:2]) if not os.path.isdir(destination_dir): os.makedirs(destination_dir) # (TODO) Check if filename already exsits src_file_path = os.path.join(src_dir_path, name) dst_file_path = os.path.join(destination_dir, link[3:]) # (TODO) Thread this operation shutil.copy(src_file_path, dst_file_path) if filetype == 'file': mime_type = 'application' else: mime_type = filetype content_type = "{0}/{1}".format(mime_type, ext.replace(".", "")) node_file = File({ 'name': link, 'filename': name, 'user': current_user.objectid, 'backend': 'cdnsun', 'md5': '', 'content_type': content_type, 'length': 0 }) node_file.create(api=api) node_asset_props['properties']['file'] = node_file._id if parent_id: node_asset_props['parent'] = parent_id node_asset = Node(node_asset_props) node_asset.create(api=api) return jsonify(link=link, name=name, filetype=filetype, asset_id=node_asset._id)
def shots_index(): max_results = 100 api = system_util.pillar_api() node_type_name = "shot" node_type_list = NodeType.all({ 'where': '{"name" : "%s"}' % node_type_name, }, api=api) node_type = node_type_list._items[0] nodes = Node.all({ 'where': '{"node_type" : "%s"}' % node_type._id, 'max_results': max_results, 'embedded': '{"picture":1}', 'sort' : "order"}, api=api) # Get the task node type object id node_type_list = NodeType.all({ 'where': '{"name" : "task"}', }, api=api) node_type_task = node_type_list._items[0] nodes_datatables = [] for node in nodes._items: tasks = Node.all({ 'where': '{"node_type" : "%s", "parent" : "%s"}'\ % (node_type_task._id, node._id), 'sort' : "order"}, api=api) shot_status = None try: shot_status = node.properties.status except: # Notify about missing status property. This should be prominent. pass data = { 'DT_RowId': "row_{0}".format(node._id), 'DT_RowAttr': {'data-shot-status':shot_status}, '_id': node._id, 'order': node.order, 'picture': None, 'name': node.name, #'description': node.description, 'notes': node.properties.notes, 'timing': { 'cut_in': node.properties.cut_in, 'cut_out': node.properties.cut_out }, 'url_view': url_for('nodes.view', node_id=node._id), 'url_edit': url_for('nodes.edit', node_id=node._id, embed=1), 'tasks': { 'animation': None, 'lighting': None, 'fx_hair': None, 'fx_grass': None, 'fx_smoke': None }, } if node.picture: # This is an address on the Attract server, so it should be built # entirely here data['picture'] = "{0}/file_server/file/{1}".format( app.config['PILLAR_SERVER_ENDPOINT'], node.picture.path) # Get previews picture_node = File.find(node.picture['_id'] + \ '/?embedded={"previews":1}', api=api) if picture_node.previews: for preview in picture_node.previews: if preview.size == 'm': data['picture_thumbnail'] = app.config['PILLAR_SERVER_ENDPOINT'] + "/file_server/file/" + preview.path break else: data['picture_thumbnail'] = data['picture'] if node.order is None: data['order'] = 0 for task in tasks._items: # If there are tasks assigned to the shot we loop through them and # match them with the existing data indexes. if task.name in data['tasks']: data['tasks'][task.name] = { 'name': task.name, 'status': task.properties.status, 'url_view': url_for('nodes.view', node_id=task._id, embed=1), 'url_edit': url_for('nodes.edit', node_id=task._id, embed=1), 'is_conflicting': task.properties.is_conflicting, 'is_processing': task.properties.is_rendering, 'is_open': task.properties.is_open } nodes_datatables.append(data) return jsonify(data=nodes_datatables)
def shots_index(): max_results = 100 api = system_util.pillar_api() node_type_name = "shot" node_type_list = NodeType.all( { 'where': '{"name" : "%s"}' % node_type_name, }, api=api) node_type = node_type_list._items[0] nodes = Node.all( { 'where': '{"node_type" : "%s"}' % node_type._id, 'max_results': max_results, 'embedded': '{"picture":1}', 'sort': "order" }, api=api) # Get the task node type object id node_type_list = NodeType.all({ 'where': '{"name" : "task"}', }, api=api) node_type_task = node_type_list._items[0] nodes_datatables = [] for node in nodes._items: tasks = Node.all({ 'where': '{"node_type" : "%s", "parent" : "%s"}'\ % (node_type_task._id, node._id), 'sort' : "order"}, api=api) shot_status = None try: shot_status = node.properties.status except: # Notify about missing status property. This should be prominent. pass data = { 'DT_RowId': "row_{0}".format(node._id), 'DT_RowAttr': { 'data-shot-status': shot_status }, '_id': node._id, 'order': node.order, 'picture': None, 'name': node.name, #'description': node.description, 'notes': node.properties.notes, 'timing': { 'cut_in': node.properties.cut_in, 'cut_out': node.properties.cut_out }, 'url_view': url_for('nodes.view', node_id=node._id), 'url_edit': url_for('nodes.edit', node_id=node._id, embed=1), 'tasks': { 'animation': None, 'lighting': None, 'fx_hair': None, 'fx_grass': None, 'fx_smoke': None }, } if node.picture: # This is an address on the Attract server, so it should be built # entirely here data['picture'] = "{0}/file_server/file/{1}".format( app.config['PILLAR_SERVER_ENDPOINT'], node.picture.path) # Get previews picture_node = File.find(node.picture['_id'] + \ '/?embedded={"previews":1}', api=api) if picture_node.previews: for preview in picture_node.previews: if preview.size == 'm': data['picture_thumbnail'] = app.config[ 'PILLAR_SERVER_ENDPOINT'] + "/file_server/file/" + preview.path break else: data['picture_thumbnail'] = data['picture'] if node.order is None: data['order'] = 0 for task in tasks._items: # If there are tasks assigned to the shot we loop through them and # match them with the existing data indexes. if task.name in data['tasks']: data['tasks'][task.name] = { 'name': task.name, 'status': task.properties.status, 'url_view': url_for('nodes.view', node_id=task._id, embed=1), 'url_edit': url_for('nodes.edit', node_id=task._id, embed=1), 'is_conflicting': task.properties.is_conflicting, 'is_processing': task.properties.is_rendering, 'is_open': task.properties.is_open } nodes_datatables.append(data) return jsonify(data=nodes_datatables)
def __call__(self, field, **kwargs): html = super(CustomFileSelectWidget, self).__call__(field, **kwargs) file_format = self.file_format file_format_regex = '' if file_format and file_format == 'image': file_format_regex = '^image\/(gif|jpe?g|png|tif?f|tga)$' button = ['<div class="form-upload-file">'] if field.data: api = system_util.pillar_api() try: # Load the existing file attached to the field file_item = File.find(field.data, api=api) except ResourceNotFound: pass else: button.append('<div class="form-upload-file-meta-container">') filename = Markup.escape(file_item.filename) if file_item.content_type.split('/')[0] == 'image': # If a file of type image is available, display the preview button.append( '<img class="preview-thumbnail" src="{0}" />'.format( file_item.thumbnail('s', api=api))) button.append('<ul class="form-upload-file-meta">') # File name button.append('<li class="name">{0}</li>'.format(filename)) # File size button.append('<li class="size">({0} MB)</li>'.format( round((file_item.length / 1024) * 0.001, 2))) # Image resolution (if image) if file_item.content_type.split('/')[0] == 'image': button.append('<li class="dimensions">{0}x{1}</li>'.format( file_item.width, file_item.height)) button.append('</ul>') button.append('<ul class="form-upload-file-actions">') # Download button for original file button.append( '<li class="original">' '<a href="{}" class="file_original"> ' '<i class="pi-download"></i>Original</a></li>'.format( file_item.link)) # Delete button button.append( '<li class="delete">' '<a href="#" class="file_delete" ' 'data-field-name="{field_name}" ' 'data-file_id="{file_id}"> ' '<i class="pi-trash"></i> Delete</a></li>'.format( field_name=field.name, file_id=field.data)) button.append('</ul>') button.append('</div>') upload_url = '%sstorage/stream/{project_id}' % current_app.config[ 'PILLAR_SERVER_ENDPOINT'] button.append( '<input class="fileupload" type="file" name="file" ' 'data-url="{url}" ' 'data-field-name="{name}" ' 'data-field-slug="{slug}" ' 'data-token="{token}" ' 'data-file-format="{file_format}">' '<div class="form-upload-progress"> ' '<div class="form-upload-progress-bar" role="progressbar" ' 'aria-valuenow="0" aria-valuemin="0" aria-valuemax="100" ' 'style="width: 0%;"> ' '</div> ' '</div>'.format(url=upload_url, name=field.name, slug=field.name.replace('oid', 'slug'), token=Markup.escape(current_user.id), file_format=Markup.escape(file_format_regex))) button.append('</div>') return HTMLString(html + ''.join(button))