soter/soter/views/picture.py

139 lines
4.9 KiB
Python

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