import datetime import hashlib import os import shutil import uuid from PIL import Image, ExifTags from PIL.Image import LANCZOS import pkg_resources from pyramid.response import FileResponse, Response from pyramid.security import authenticated_userid from pyramid.view import view_config from sqlalchemy.exc import IntegrityError import transaction import numpy from soter.models import DBSession from soter.models.master import Picture, Album, Property, PictureProperty @view_config(request_method='GET', route_name='upload', permission='Albums') @view_config(request_method='GET', route_name='picture_id', permission='Albums') def html(request): package, resource = 'soter:static/index.html'.split(':', 1) file = pkg_resources.resource_filename(package, resource) return FileResponse(file, request=request) @view_config(request_method='POST', route_name='api_upload', renderer='json', permission='Albums') def upload(request): input_file = request.POST['file'].file name = request.POST['name'] last_modified_date = request.POST.get('lastModifiedDate', None) if 'album_id' in request.POST: album = Album.by_id(request.POST['album_id']) elif 'album' in request.POST: album = Album.by_name(request.POST['album']) else: album = Album.by_id(Album.no_album()) return upload_file(input_file, name, last_modified_date, album, uuid.UUID(authenticated_userid(request))) def upload_file(file, name, last_modified_date, album, user_id): last_modified_date_prop = Property.by_name('last_modified_date') upload_date_prop = Property.by_name('upload_date') file_id = uuid.uuid4() file_path = pkg_resources.resource_filename('soter', 'upload/o/' + str(file_id) + '.jpg') temp_file_path = file_path + '~' file.seek(0) with open(temp_file_path, 'wb') as output_file: shutil.copyfileobj(file, output_file) pic_hash = get_hash(temp_file_path) pic = Picture(name, pic_hash, user_id, True, file_id) if album is not None: pic.albums.append(album) if last_modified_date is None: last_modified_date = datetime.datetime.utcnow() pic.properties.append(PictureProperty(property=last_modified_date_prop, data=last_modified_date)) pic.properties.append(PictureProperty(property=upload_date_prop, data=datetime.datetime.utcnow())) DBSession.add(pic) try: transaction.commit() except IntegrityError as ex: transaction.abort() os.remove(temp_file_path) response = Response("Duplicate file") response.status_int = 500 return response else: os.rename(temp_file_path, file_path) return {'location': file_path} def get_hash(path): image = get_image(path) img = numpy.array(image) img = img[:, :, ::-1].copy() return hashlib.sha512(numpy.array(img)).digest() def get_image(path): image = Image.open(path) orientation = [tag for tag in ExifTags.TAGS.keys() if ExifTags.TAGS[tag] == 'Orientation'][0] exif = getattr(image, "_getexif", None) if exif is None: return image exif = image._getexif() if exif is None: return image exif = dict(exif.items()) if exif[orientation] == 3: image = image.rotate(180, expand=True) elif exif[orientation] == 6: image = image.rotate(270, expand=True) elif exif[orientation] == 8: image = image.rotate(90, expand=True) return image @view_config(request_method='GET', route_name='picture_raw', permission='Albums') def show_raw(request): size = request.matchdict['size'] id = request.matchdict['id'] package, resource = ('soter:upload/' + size + '/' + id + '.jpg').split(':', 1) file = pkg_resources.resource_filename(package, resource) if not os.path.isfile(file): create_thumbnail(id, size) return FileResponse(file, request=request) def create_thumbnail(id, size): file_path = pkg_resources.resource_filename('soter', 'upload/' + size + '/' + id + '.jpg') size = tuple([int(i) for i in size.split('x')]) file = pkg_resources.resource_filename('soter', 'upload/o/' + id + '.jpg') image = get_image(file) image.thumbnail(size, resample=LANCZOS) image.save(file_path) @view_config(request_method='GET', route_name='api_picture_id', renderer='json', request_param='s', permission='Albums') def show_id(request): return picture_info(request.matchdict['id'], request.GET['s'], request) @view_config(request_method='GET', route_name='api_picture_id', renderer='json', permission='Albums') def show_id_org(request): return picture_info(request.matchdict['id'], 'o', request) def picture_info(id, size, request): if not isinstance(id, Picture): picture = Picture.by_id(id) else: picture = id album = {'id': picture.id, 'name': picture.name, 'imageUrl': request.route_url('picture_raw', id=picture.id, size=size), 'user': {'id': picture.user_id, 'name': picture.user.name}} return album