diff --git a/soter/models/auth.py b/soter/models/auth.py index b6abbe1..a83f30f 100644 --- a/soter/models/auth.py +++ b/soter/models/auth.py @@ -37,7 +37,7 @@ class User(Base): _password = Column('password', Unicode(64)) locked_out = Column('locked_out', Boolean) - roles = relationship("Role", secondary=role_user) + roles = relationship("Role", secondary=role_user, backref='users') def _get_password(self): return self._password diff --git a/soter/models/master.py b/soter/models/master.py index a7494c9..7354e65 100644 --- a/soter/models/master.py +++ b/soter/models/master.py @@ -1,6 +1,8 @@ import uuid +import re -from sqlalchemy import Column, PickleType, Unicode, ForeignKey, Boolean, Table, UniqueConstraint +from sqlalchemy import Column, PickleType, Unicode, ForeignKey, Boolean, Table, UniqueConstraint, LargeBinary +from sqlalchemy.ext.hybrid import hybrid_property from sqlalchemy.orm import relationship @@ -8,6 +10,11 @@ from soter.models import Base, DBSession from soter.models.guidtype import GUID +def semantic_name(name): + p = re.compile('([!\*’\(\);:@&=\+\$,/\?#\[\] ])') + return p.sub('-', name) + + class DbSetting(Base): __tablename__ = 'settings' @@ -43,12 +50,21 @@ picture_tag = Table( UniqueConstraint('picture_id', 'tag_id') ) +picture_album = Table( + 'picture_albums', Base.metadata, + Column('id', GUID(), primary_key=True, default=uuid.uuid4), + Column('picture_id', GUID(), ForeignKey('pictures.id'), nullable=False), + Column('album_id', GUID(), ForeignKey('albums.id'), nullable=False), + UniqueConstraint('picture_id', 'album_id') +) + class Album(Base): __tablename__ = 'albums' id = Column('id', GUID(), primary_key=True, default=uuid.uuid4) - name = Column('name', Unicode(255), unique=True, nullable=False) + _name = Column('name', Unicode(255), unique=True, nullable=False) + semantic = Column('semantic', Unicode(255), unique=True, nullable=False) description = Column('description', Unicode(255)) user_id = Column('user_id', GUID(), ForeignKey('users.id'), nullable=False) is_fixture = Column('is_fixture', Boolean, nullable=False) @@ -56,7 +72,14 @@ class Album(Base): user = relationship('User', primaryjoin="User.id==Album.user_id", cascade=None) - pictures = relationship('Picture', backref='album') + @hybrid_property + def name(self): + return self._name + + @name.setter + def name(self, name): + self.name = name + self.semantic = semantic_name(name) def __init__(self, name, description, user_id, is_public, id=None, is_fixture=False): self.name = name @@ -95,20 +118,19 @@ class Picture(Base): __tablename__ = 'pictures' id = Column('id', GUID(), primary_key=True, default=uuid.uuid4) - name = Column('name', Unicode(255), unique=True) - url = Column('url', Unicode(255), unique=True) + name = Column('name', Unicode(255), nullable=False) + hash = Column('hash', LargeBinary(512), unique=True, nullable=False) user_id = Column('user_id', GUID(), ForeignKey('users.id'), nullable=False) - album_id = Column('album_id', GUID(), ForeignKey('albums.id'), nullable=False) is_public = Column('is_public', Boolean, nullable=False) tags = relationship("Tag", secondary=picture_tag) + albums = relationship("Album", secondary=picture_album, backref='pictures') user = relationship('User', primaryjoin="User.id==Picture.user_id", cascade=None) - def __init__(self, name, url, user_id, album_id, is_public, id=None, is_fixture=False): + def __init__(self, name, hash, user_id, is_public, id=None, is_fixture=False): self.name = name - self.url = url + self.hash = hash self.user_id = user_id - self.album_id = album_id self.is_public = is_public self.id = id self.is_fixture = is_fixture diff --git a/soter/views/picture.py b/soter/views/picture.py index 5057f59..03705e6 100644 --- a/soter/views/picture.py +++ b/soter/views/picture.py @@ -1,3 +1,4 @@ +import hashlib import os import shutil import uuid @@ -8,10 +9,10 @@ from pyramid.response import FileResponse from pyramid.security import authenticated_userid from pyramid.view import view_config import transaction - import numpy + from soter.models import DBSession -from soter.models.master import Picture +from soter.models.master import Picture, Album @view_config(request_method='GET', route_name='upload', permission='Albums') @@ -33,15 +34,18 @@ def upload(request): shutil.copyfileobj(input_file, output_file) os.rename(temp_file_path, file_path) # Process picture here to get the hash and the exif tags to be added to the database - pic = Picture(file_id, str(file_id), uuid.UUID(authenticated_userid(request)), uuid.UUID(album), True, file_id) + pic = process_picture(file_path, str(file_id), uuid.UUID(authenticated_userid(request)), file_id, uuid.UUID(album)) DBSession.add(pic) transaction.commit() return {'location': file_path} -def process_picture(path): +def process_picture(path, name, user_id, file_id, album_id): image = get_image(path) - Picture(file_id, str(file_id), uuid.UUID(authenticated_userid(request)), uuid.UUID(album), True, file_id) + hash = hashlib.sha512(numpy.array(image)).digest() + picture = Picture(name, hash, user_id, True, file_id) + picture.albums.append(Album.by_id(album_id)) + return picture def get_image(path):