diff --git a/attract/application/__init__.py b/attract/application/__init__.py index 7e084b0b..6e50cd2b 100644 --- a/attract/application/__init__.py +++ b/attract/application/__init__.py @@ -178,7 +178,7 @@ class ValidateCustomFields(Validator): def post_item(entry, data): - post_internal(entry, data) + return post_internal(entry, data) app = Eve(validator=ValidateCustomFields, auth=CustomTokenAuth) diff --git a/attract/application/file_server.py b/attract/application/file_server.py index 234b37b1..989f8a5e 100644 --- a/attract/application/file_server.py +++ b/attract/application/file_server.py @@ -1,19 +1,45 @@ import os +import hashlib from flask import Blueprint from flask import request from application import app +from application import post_item + +from datetime import datetime from PIL import Image +from bson import ObjectId + +RFC1123_DATE_FORMAT = '%a, %d %b %Y %H:%M:%S GMT' + + file_server = Blueprint('file_server', __name__, template_folder='templates', static_folder='static/storage') -@file_server.route('/file/thumbnail/') -def thumbnail(file_name=None): +def hashfile(afile, hasher, blocksize=65536): + buf = afile.read(blocksize) + while len(buf) > 0: + hasher.update(buf) + buf = afile.read(blocksize) + return hasher.hexdigest() + + +@file_server.route('/build_previews/') +def build_previews(file_name=None): + from pymongo import MongoClient + + # Get File + client = MongoClient() + db = client.eve + file_ = db.files.find({"path": "{0}".format(file_name)}) + file_ = file_[0] + user = file_['user'] + folder_name = file_name[:2] file_folder_path = os.path.join(app.config['FILE_STORAGE'], folder_name) @@ -22,49 +48,108 @@ def thumbnail(file_name=None): if not os.path.isfile(file_path): return "", 404 - format_ = "jpeg" - formats = ["jpeg", "png"] - size = "s" sizes = ["xs", "s", "m", "l", "xl"] - size_dict= { + size_dict = { "xs": (32, 32), "s": (64, 64), "m": (128, 128), "l": (640, 480), "xl": (1024, 768) } - if "format" in request.args: - if request.args['format'] in formats: - format_ = request.args['format'] - if "size" in request.args: - if request.args['size'] in sizes: - size = request.args['size'] - - # The Thumbnail already exist? - thumbnail_folder_path = os.path.join(file_folder_path, size) - thumbnail_file_path = os.path.join(thumbnail_folder_path, file_name) - if os.path.isfile(thumbnail_file_path): - file_static_path = os.path.join("", folder_name, size, file_name) - return file_server.send_static_file(file_static_path) - # Create thumbnail - if not os.path.exists(thumbnail_folder_path): - os.mkdir(thumbnail_folder_path) - if not os.path.isfile(thumbnail_file_path): - try: - im = Image.open(file_path) - except IOError: - return "", 500 - im.thumbnail(size_dict[size]) - try: - im.save(thumbnail_file_path) - except IOError: - raise - return "", 500 + # Generate + preview_list = [] + for size in sizes: + resized_file_name = "{0}_{1}".format(size, file_name) + resized_file_path = os.path.join( + app.config['FILE_STORAGE'], + resized_file_name) + + # Create thumbnail + if not os.path.isfile(resized_file_path): + try: + im = Image.open(file_path) + except IOError: + return "", 500 + im.thumbnail(size_dict[size]) + try: + im.save(resized_file_path) + except IOError: + return "", 500 + + # file_static_path = os.path.join("", folder_name, size, file_name) + picture_file_file = open(resized_file_path, 'rb') + hash_ = hashfile(picture_file_file, hashlib.md5()) + name = "{0}{1}".format(hash_, + os.path.splitext(file_name)[1]) + picture_file_file.close() + description = "Thumbnail {0} for file {1}".format( + size, file_name) + + prop = {} + prop['name'] = resized_file_name + prop['description'] = description + prop['user'] = user + prop['is_preview'] = True + prop['preview_name'] = "{0}_png".format(size) + # TODO set proper contentType and length + prop['contentType'] = 'image/png' + prop['length'] = 0 + prop['uploadDate'] = datetime.strftime( + datetime.now(), RFC1123_DATE_FORMAT) + prop['md5'] = hash_ + prop['filename'] = resized_file_name + prop['backend'] = 'attract' + prop['path'] = name + + entry = post_item ('files', prop) + if entry[0]['_status'] == 'ERR': + entry = db.files.find({"path": name}) + + entry = entry[0] + prop['_id'] = entry['_id'] + + new_folder_name = name[:2] + new_folder_path = os.path.join( + app.config['FILE_STORAGE'], + new_folder_name) + new_file_path = os.path.join( + new_folder_path, + name) + + if not os.path.exists(new_folder_path): + os.makedirs(new_folder_path) + + # Clean up temporary file + os.rename( + resized_file_path, + new_file_path) + + preview_list.append(str(prop['_id'])) + #print (new_file_path) + + # Add previews to file + previews = [] + try: + previews = file_['previews'] + except KeyError: + pass + + preview_list = preview_list + previews + + #print (previews) + #print (preview_list) + #print (file_['_id']) + + file_ = db.files.update( + {"_id": ObjectId(file_['_id'])}, + {"$set": {"previews": preview_list}} + ) + + #print (file_) + + return "", 200 - file_static_path = os.path.join("", folder_name, size, file_name) - return file_server.send_static_file(file_static_path) - return "", 500 @file_server.route('/file', methods=['POST']) @file_server.route('/file/') diff --git a/attract/settings.py b/attract/settings.py index 164b5006..ca4efe06 100644 --- a/attract/settings.py +++ b/attract/settings.py @@ -9,7 +9,7 @@ RESOURCE_METHODS = ['GET', 'POST', 'DELETE'] # individual items (defaults to read-only item access). ITEM_METHODS = ['GET', 'PUT', 'DELETE', 'PATCH'] -PAGINATION_LIMIT = 100 +PAGINATION_LIMIT = 999 # To be implemented on Eve 0.6 # RETURN_MEDIA_AS_URL = True @@ -144,6 +144,12 @@ files_schema = { 'type': 'string', 'required': True, }, + 'is_preview': { + 'type': 'boolean' + }, + 'preview_name': { + 'type': 'string' + }, 'user': { 'type': 'objectid', 'required': True, @@ -176,6 +182,18 @@ files_schema = { 'path': { 'type': 'string', 'required': True, + 'unique': True, + }, + 'previews': { + 'type': 'list', + 'schema': { + 'type': 'objectid', + 'data_relation': { + 'resource': 'files', + 'field': '_id', + 'embeddable': True + } + } } }