Updated picture_raw view to show different sizes.
Albums shifted to semantic url names. Uploads are now thumbnailed when first viewed.
This commit is contained in:
parent
088310b652
commit
cf72709ddf
@ -39,7 +39,7 @@ def main(global_config, **settings):
|
|||||||
config.add_route('logout', '/logout')
|
config.add_route('logout', '/logout')
|
||||||
|
|
||||||
add_route(config, 'picture', '/picture')
|
add_route(config, 'picture', '/picture')
|
||||||
config.add_route('picture_raw', '/p/{id}')
|
config.add_route('picture_raw', '/p/{size}/{id}')
|
||||||
config.add_route('upload', '/upload')
|
config.add_route('upload', '/upload')
|
||||||
config.add_route('api_upload', '/v1/upload')
|
config.add_route('api_upload', '/v1/upload')
|
||||||
|
|
||||||
|
@ -78,7 +78,7 @@ class Album(Base):
|
|||||||
|
|
||||||
@name.setter
|
@name.setter
|
||||||
def name(self, name):
|
def name(self, name):
|
||||||
self.name = name
|
self._name = name
|
||||||
self.semantic = semantic_name(name)
|
self.semantic = semantic_name(name)
|
||||||
|
|
||||||
def __init__(self, name, description, user_id, is_public, id=None, is_fixture=False):
|
def __init__(self, name, description, user_id, is_public, id=None, is_fixture=False):
|
||||||
@ -105,6 +105,12 @@ class Album(Base):
|
|||||||
return None
|
return None
|
||||||
return DBSession.query(cls).filter(cls.name.ilike(name)).first()
|
return DBSession.query(cls).filter(cls.name.ilike(name)).first()
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def by_semantic(cls, semantic):
|
||||||
|
if not semantic:
|
||||||
|
return None
|
||||||
|
return DBSession.query(cls).filter(cls.semantic.ilike(semantic)).first()
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def menu_item(cls):
|
def menu_item(cls):
|
||||||
return uuid.UUID('dad46805-f577-4e5b-8073-9b788e0173fc')
|
return uuid.UUID('dad46805-f577-4e5b-8073-9b788e0173fc')
|
||||||
|
@ -33,9 +33,10 @@
|
|||||||
var UploadCtrlResolve = {};
|
var UploadCtrlResolve = {};
|
||||||
|
|
||||||
var PictureCtrlResolve = {
|
var PictureCtrlResolve = {
|
||||||
album: ['$route', 'Picture', function ($route, Picture) {
|
picture: ['$route', 'Picture', function ($route, Picture) {
|
||||||
var id = $route.current.params.id;
|
var id = $route.current.params.id,
|
||||||
return Picture.get({id: id}).$promise;
|
size = $route.current.params.s;
|
||||||
|
return Picture.get({id: id, s: size}).$promise;
|
||||||
}]
|
}]
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
<div class="carousel-inner">
|
<!--?<div class="carousel-inner">-->
|
||||||
<div class="item">
|
<div class="item">
|
||||||
<img ng-src="{{picture.imageUrl}}">
|
<img ng-src="{{picture.imageUrl}}">
|
||||||
</div>
|
</div>
|
||||||
</div>
|
<!--?</div>-->
|
@ -51,6 +51,7 @@
|
|||||||
<script src="/app/album/album.service.js"></script>
|
<script src="/app/album/album.service.js"></script>
|
||||||
|
|
||||||
<script src="/app/picture/picture.controller.js"></script>
|
<script src="/app/picture/picture.controller.js"></script>
|
||||||
|
<script src="/app/picture/picture.service.js"></script>
|
||||||
|
|
||||||
<script src="/script/angular_directive.js"></script>
|
<script src="/script/angular_directive.js"></script>
|
||||||
<script src="/script/angular_filter.js"></script>
|
<script src="/script/angular_filter.js"></script>
|
||||||
|
@ -2,8 +2,11 @@ import uuid
|
|||||||
import re
|
import re
|
||||||
|
|
||||||
import pkg_resources
|
import pkg_resources
|
||||||
|
|
||||||
from pyramid.response import FileResponse, Response
|
from pyramid.response import FileResponse, Response
|
||||||
|
|
||||||
from pyramid.security import authenticated_userid
|
from pyramid.security import authenticated_userid
|
||||||
|
|
||||||
from pyramid.view import view_config
|
from pyramid.view import view_config
|
||||||
import transaction
|
import transaction
|
||||||
|
|
||||||
@ -35,7 +38,13 @@ def save(request):
|
|||||||
@view_config(request_method='POST', route_name='api_album_id', renderer='json', permission='Albums')
|
@view_config(request_method='POST', route_name='api_album_id', renderer='json', permission='Albums')
|
||||||
@TryCatchFunction
|
@TryCatchFunction
|
||||||
def update(request):
|
def update(request):
|
||||||
item = Album.by_id(uuid.UUID(request.matchdict['id']))
|
p = re.compile('^[A-Fa-f0-9]{8}-[A-Fa-f0-9]{4}-[A-Fa-f0-9]{4}-[A-Fa-f0-9]{4}-[A-Fa-f0-9]{12}$')
|
||||||
|
id = request.matchdict['id']
|
||||||
|
if p.match(id):
|
||||||
|
item = Album.by_id(uuid.UUID(id))
|
||||||
|
else:
|
||||||
|
item = Album.by_semantic(id)
|
||||||
|
|
||||||
if item.is_fixture:
|
if item.is_fixture:
|
||||||
raise ValidationError("{0} is a fixture and cannot be edited or deleted.".format(item.name))
|
raise ValidationError("{0} is a fixture and cannot be edited or deleted.".format(item.name))
|
||||||
item.name = request.json_body['name']
|
item.name = request.json_body['name']
|
||||||
@ -61,7 +70,13 @@ def delete(request):
|
|||||||
|
|
||||||
@view_config(request_method='GET', route_name='api_album_id', renderer='json', permission='Albums')
|
@view_config(request_method='GET', route_name='api_album_id', renderer='json', permission='Albums')
|
||||||
def show_id(request):
|
def show_id(request):
|
||||||
return album_info(request.matchdict['id'])
|
p = re.compile('^[A-Fa-f0-9]{8}-[A-Fa-f0-9]{4}-[A-Fa-f0-9]{4}-[A-Fa-f0-9]{4}-[A-Fa-f0-9]{12}$')
|
||||||
|
id = request.matchdict['id']
|
||||||
|
if p.match(id):
|
||||||
|
item = Album.by_id(uuid.UUID(id))
|
||||||
|
else:
|
||||||
|
item = Album.by_semantic(id)
|
||||||
|
return album_info(item)
|
||||||
|
|
||||||
|
|
||||||
@view_config(request_method='GET', route_name='api_album', renderer='json', permission='Albums')
|
@view_config(request_method='GET', route_name='api_album', renderer='json', permission='Albums')
|
||||||
@ -77,8 +92,8 @@ def show_list(request):
|
|||||||
for item in list:
|
for item in list:
|
||||||
albums.append({'name': item.name, 'description': item.description, 'isPublic': item.is_public,
|
albums.append({'name': item.name, 'description': item.description, 'isPublic': item.is_public,
|
||||||
'user': item.user.name, 'isFixture': item.is_fixture, 'pictures': [],
|
'user': item.user.name, 'isFixture': item.is_fixture, 'pictures': [],
|
||||||
'viewUrl': request.route_url('album_id_view', id=item.id, ),
|
'viewUrl': request.route_url('album_id_view', id=item.semantic, ),
|
||||||
'editUrl': request.route_url('album_id', id=item.id)})
|
'editUrl': request.route_url('album_id', id=item.semantic)})
|
||||||
return albums
|
return albums
|
||||||
|
|
||||||
|
|
||||||
@ -90,11 +105,12 @@ def show_pics(request):
|
|||||||
if p.match(id):
|
if p.match(id):
|
||||||
album = Album.by_id(uuid.UUID(id))
|
album = Album.by_id(uuid.UUID(id))
|
||||||
else:
|
else:
|
||||||
album = Album.by_name(id)
|
album = Album.by_semantic(id)
|
||||||
info = album_info(album)
|
info = album_info(album)
|
||||||
for item in album.pictures:
|
for item in album.pictures:
|
||||||
info['pictures'].append({'name': item.name, 'url': request.route_url('picture_id', id=item.id),
|
info['pictures'].append({'name': item.name,
|
||||||
'imageUrl': request.route_url('picture_raw', id=item.id)})
|
'url': request.route_url('picture_id', id=item.id, _query={'s': '1600x1200'}),
|
||||||
|
'imageUrl': request.route_url('picture_raw', size='242x200', id=item.id)})
|
||||||
return info
|
return info
|
||||||
|
|
||||||
|
|
||||||
|
@ -4,10 +4,12 @@ import shutil
|
|||||||
import uuid
|
import uuid
|
||||||
|
|
||||||
from PIL import Image, ExifTags
|
from PIL import Image, ExifTags
|
||||||
|
from PIL.Image import LANCZOS
|
||||||
import pkg_resources
|
import pkg_resources
|
||||||
from pyramid.response import FileResponse
|
from pyramid.response import FileResponse, Response
|
||||||
from pyramid.security import authenticated_userid
|
from pyramid.security import authenticated_userid
|
||||||
from pyramid.view import view_config
|
from pyramid.view import view_config
|
||||||
|
from sqlalchemy.exc import IntegrityError
|
||||||
import transaction
|
import transaction
|
||||||
import numpy
|
import numpy
|
||||||
|
|
||||||
@ -16,6 +18,7 @@ from soter.models.master import Picture, Album
|
|||||||
|
|
||||||
|
|
||||||
@view_config(request_method='GET', route_name='upload', permission='Albums')
|
@view_config(request_method='GET', route_name='upload', permission='Albums')
|
||||||
|
@view_config(request_method='GET', route_name='picture_id', permission='Albums')
|
||||||
def html(request):
|
def html(request):
|
||||||
package, resource = 'soter:static/index.html'.split(':', 1)
|
package, resource = 'soter:static/index.html'.split(':', 1)
|
||||||
file = pkg_resources.resource_filename(package, resource)
|
file = pkg_resources.resource_filename(package, resource)
|
||||||
@ -26,23 +29,34 @@ def html(request):
|
|||||||
def upload(request):
|
def upload(request):
|
||||||
input_file = request.POST['file'].file
|
input_file = request.POST['file'].file
|
||||||
file_id = uuid.uuid4()
|
file_id = uuid.uuid4()
|
||||||
file_path = pkg_resources.resource_filename('soter', 'upload/' + str(file_id) + '.jpg')
|
file_path = pkg_resources.resource_filename('soter', 'upload/o/' + str(file_id) + '.jpg')
|
||||||
album = request.POST['album']
|
album = request.POST['album']
|
||||||
temp_file_path = file_path + '~'
|
temp_file_path = file_path + '~'
|
||||||
input_file.seek(0)
|
input_file.seek(0)
|
||||||
with open(temp_file_path, 'wb') as output_file:
|
with open(temp_file_path, 'wb') as output_file:
|
||||||
shutil.copyfileobj(input_file, output_file)
|
shutil.copyfileobj(input_file, output_file)
|
||||||
os.rename(temp_file_path, file_path)
|
pic = process_picture(temp_file_path, str(file_id), uuid.UUID(authenticated_userid(request)), file_id,
|
||||||
# Process picture here to get the hash and the exif tags to be added to the database
|
uuid.UUID(album))
|
||||||
pic = process_picture(file_path, str(file_id), uuid.UUID(authenticated_userid(request)), file_id, uuid.UUID(album))
|
|
||||||
DBSession.add(pic)
|
DBSession.add(pic)
|
||||||
transaction.commit()
|
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}
|
return {'location': file_path}
|
||||||
|
|
||||||
|
|
||||||
def process_picture(path, name, user_id, file_id, album_id):
|
def process_picture(path, name, user_id, file_id, album_id):
|
||||||
image = get_image(path)
|
image = get_image(path)
|
||||||
hash = hashlib.sha512(numpy.array(image)).digest()
|
img = numpy.array(image)
|
||||||
|
img = img[:, :, ::-1].copy()
|
||||||
|
|
||||||
|
hash = hashlib.sha512(numpy.array(img)).digest()
|
||||||
picture = Picture(name, hash, user_id, True, file_id)
|
picture = Picture(name, hash, user_id, True, file_id)
|
||||||
picture.albums.append(Album.by_id(album_id))
|
picture.albums.append(Album.by_id(album_id))
|
||||||
return picture
|
return picture
|
||||||
@ -51,37 +65,58 @@ def process_picture(path, name, user_id, file_id, album_id):
|
|||||||
def get_image(path):
|
def get_image(path):
|
||||||
image = Image.open(path)
|
image = Image.open(path)
|
||||||
orientation = [tag for tag in ExifTags.TAGS.keys() if ExifTags.TAGS[tag] == 'Orientation'][0]
|
orientation = [tag for tag in ExifTags.TAGS.keys() if ExifTags.TAGS[tag] == 'Orientation'][0]
|
||||||
exif = dict(image._getexif().items())
|
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:
|
if exif[orientation] == 3:
|
||||||
image = image.rotate(180, expand=True)
|
image = image.rotate(180, expand=True)
|
||||||
elif exif[orientation] == 6:
|
elif exif[orientation] == 6:
|
||||||
image = image.rotate(270, expand=True)
|
image = image.rotate(270, expand=True)
|
||||||
elif exif[orientation] == 8:
|
elif exif[orientation] == 8:
|
||||||
image = image.rotate(90, expand=True)
|
image = image.rotate(90, expand=True)
|
||||||
img = numpy.array(image)
|
return image
|
||||||
# CONVERT RGB TO BGR
|
|
||||||
return img[:, :, ::-1].copy()
|
|
||||||
|
|
||||||
|
|
||||||
@view_config(request_method='GET', route_name='picture_raw', permission='Albums')
|
@view_config(request_method='GET', route_name='picture_raw', permission='Albums')
|
||||||
def show_raw(request):
|
def show_raw(request):
|
||||||
package, resource = ('soter:upload/' + request.matchdict['id'] + '.jpg').split(':', 1)
|
size = request.matchdict['size']
|
||||||
|
id = request.matchdict['id']
|
||||||
|
package, resource = ('soter:upload/' + size + '/' + id + '.jpg').split(':', 1)
|
||||||
file = pkg_resources.resource_filename(package, resource)
|
file = pkg_resources.resource_filename(package, resource)
|
||||||
|
if not os.path.isfile(file):
|
||||||
|
create_thumbnail(id, size)
|
||||||
return FileResponse(file, request=request)
|
return FileResponse(file, request=request)
|
||||||
|
|
||||||
|
|
||||||
@view_config(request_method='GET', route_name='api_album_id', renderer='json', request_param='pictures',
|
def create_thumbnail(id, size):
|
||||||
permission='Albums')
|
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):
|
def show_id(request):
|
||||||
return picture_info(request.matchdict['id'], request)
|
return picture_info(request.matchdict['id'], request.GET['s'], request)
|
||||||
|
|
||||||
|
|
||||||
def picture_info(id, 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):
|
if not isinstance(id, Picture):
|
||||||
picture = Picture.by_id(id)
|
picture = Picture.by_id(id)
|
||||||
else:
|
else:
|
||||||
picture = id
|
picture = id
|
||||||
album = {'id': picture.id, 'name': picture.name, 'imageUrl': request.route_url('picture_raw', id=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}}
|
'user': {'id': picture.user_id, 'name': picture.user.name}}
|
||||||
return album
|
return album
|
||||||
|
Loading…
Reference in New Issue
Block a user