From ec4cba94a9943e0e29aeee037ff3069db1824cc9 Mon Sep 17 00:00:00 2001 From: rr- Date: Fri, 15 Apr 2016 20:40:12 +0200 Subject: [PATCH] server/db: introduce tags --- server/szurubooru/db/__init__.py | 3 +- server/szurubooru/db/tag.py | 59 ++++++++++++++ server/szurubooru/db/user.py | 22 +++--- .../00cb3a2734db_create_tags_tables.py | 55 +++++++++++++ server/szurubooru/tests/conftest.py | 10 +++ server/szurubooru/tests/db/test_tag.py | 79 +++++++++++++++++++ server/szurubooru/tests/db/test_user.py | 21 +++++ server/szurubooru/util/__init__.py | 1 - 8 files changed, 236 insertions(+), 14 deletions(-) create mode 100644 server/szurubooru/db/tag.py create mode 100644 server/szurubooru/migrations/versions/00cb3a2734db_create_tags_tables.py create mode 100644 server/szurubooru/tests/db/test_tag.py create mode 100644 server/szurubooru/tests/db/test_user.py diff --git a/server/szurubooru/db/__init__.py b/server/szurubooru/db/__init__.py index 634c179..0c65170 100644 --- a/server/szurubooru/db/__init__.py +++ b/server/szurubooru/db/__init__.py @@ -1,4 +1,3 @@ -''' Database layer. ''' - from szurubooru.db.base import Base from szurubooru.db.user import User +from szurubooru.db.tag import Tag, TagName, TagSuggestion, TagImplication diff --git a/server/szurubooru/db/tag.py b/server/szurubooru/db/tag.py new file mode 100644 index 0000000..30f1067 --- /dev/null +++ b/server/szurubooru/db/tag.py @@ -0,0 +1,59 @@ +from sqlalchemy import Column, Integer, DateTime, String, ForeignKey +from sqlalchemy.orm import relationship +from szurubooru.db.base import Base + +class TagSuggestion(Base): + __tablename__ = 'tag_suggestion' + parent_id = Column('parent_id', Integer, ForeignKey('tag.id'), primary_key=True) + child_id = Column('child_id', Integer, ForeignKey('tag.id'), primary_key=True) + def __init__(self, parent_id, child_id): + self.parent_id = parent_id + self.child_id = child_id + +class TagImplication(Base): + __tablename__ = 'tag_implication' + parent_id = Column('parent_id', Integer, ForeignKey('tag.id'), primary_key=True) + child_id = Column('child_id', Integer, ForeignKey('tag.id'), primary_key=True) + def __init__(self, parent_id, child_id): + self.parent_id = parent_id + self.child_id = child_id + +class Tag(Base): + __tablename__ = 'tag' + tag_id = Column('id', Integer, primary_key=True) + category = Column('category', String(32), nullable=False) + creation_time = Column('creation_time', DateTime, nullable=False) + last_edit_time = Column('last_edit_time', DateTime) + post_count = Column('post_count', Integer, nullable=False, default=0) + names = relationship('TagName', cascade='all, delete-orphan') + + suggestions = relationship( + TagSuggestion, + backref='parent_tag', + primaryjoin=tag_id == TagSuggestion.parent_id, + cascade='all, delete-orphan') + suggested_by = relationship( + TagSuggestion, + backref='child_tag', + primaryjoin=tag_id == TagSuggestion.child_id, + cascade='all, delete-orphan') + + implications = relationship( + TagImplication, + backref='parent_tag', + primaryjoin=tag_id == TagImplication.parent_id, + cascade='all, delete-orphan') + implied_by = relationship( + TagImplication, + backref='child_tag', + primaryjoin=tag_id == TagImplication.child_id, + cascade='all, delete-orphan') + +class TagName(Base): + __tablename__ = 'tag_name' + tag_name_id = Column('tag_name_id', Integer, primary_key=True) + tag_id = Column('tag_id', Integer, ForeignKey('tag.id')) + name = Column('name', String(50), nullable=False, unique=True) + + def __init__(self, name): + self.name = name diff --git a/server/szurubooru/db/user.py b/server/szurubooru/db/user.py index a8bb004..67d58ac 100644 --- a/server/szurubooru/db/user.py +++ b/server/szurubooru/db/user.py @@ -1,4 +1,4 @@ -import sqlalchemy as sa +from sqlalchemy import Column, Integer, String, DateTime from szurubooru.db.base import Base class User(Base): @@ -7,13 +7,13 @@ class User(Base): AVATAR_GRAVATAR = 'gravatar' AVATAR_MANUAL = 'manual' - user_id = sa.Column('id', sa.Integer, primary_key=True) - name = sa.Column('name', sa.String(50), nullable=False, unique=True) - password_hash = sa.Column('password_hash', sa.String(64), nullable=False) - password_salt = sa.Column('password_salt', sa.String(32)) - email = sa.Column('email', sa.String(200), nullable=True) - rank = sa.Column('rank', sa.String(32), nullable=False) - creation_time = sa.Column('creation_time', sa.DateTime, nullable=False) - last_login_time = sa.Column('last_login_time', sa.DateTime) - avatar_style = sa.Column( - 'avatar_style', sa.String(32), nullable=False, default=AVATAR_GRAVATAR) + user_id = Column('id', Integer, primary_key=True) + name = Column('name', String(50), nullable=False, unique=True) + password_hash = Column('password_hash', String(64), nullable=False) + password_salt = Column('password_salt', String(32)) + email = Column('email', String(200), nullable=True) + rank = Column('rank', String(32), nullable=False) + creation_time = Column('creation_time', DateTime, nullable=False) + last_login_time = Column('last_login_time', DateTime) + avatar_style = Column( + 'avatar_style', String(32), nullable=False, default=AVATAR_GRAVATAR) diff --git a/server/szurubooru/migrations/versions/00cb3a2734db_create_tags_tables.py b/server/szurubooru/migrations/versions/00cb3a2734db_create_tags_tables.py new file mode 100644 index 0000000..4a1d8c0 --- /dev/null +++ b/server/szurubooru/migrations/versions/00cb3a2734db_create_tags_tables.py @@ -0,0 +1,55 @@ +''' +Create tags tables + +Revision ID: 00cb3a2734db +Created at: 2016-04-15 23:15:36.255429 +''' + +import sqlalchemy as sa +from alembic import op + +revision = '00cb3a2734db' +down_revision = 'e5c1216a8503' +branch_labels = None +depends_on = None + +def upgrade(): + op.create_table( + 'tag', + sa.Column('id', sa.Integer(), nullable=False), + sa.Column('category', sa.String(length=32), nullable=False), + sa.Column('creation_time', sa.DateTime(), nullable=False), + sa.Column('last_edit_time', sa.DateTime(), nullable=True), + sa.Column('post_count', sa.Integer(), nullable=False), + sa.PrimaryKeyConstraint('id')) + + op.create_table( + 'tag_name', + sa.Column('tag_name_id', sa.Integer(), nullable=False), + sa.Column('tag_id', sa.Integer(), nullable=True), + sa.Column('name', sa.String(length=50), nullable=False), + sa.ForeignKeyConstraint(['tag_id'], ['tag.id']), + sa.PrimaryKeyConstraint('tag_name_id'), + sa.UniqueConstraint('name')) + + op.create_table( + 'tag_implication', + sa.Column('parent_id', sa.Integer(), nullable=False), + sa.Column('child_id', sa.Integer(), nullable=False), + sa.ForeignKeyConstraint(['parent_id'], ['tag.id']), + sa.ForeignKeyConstraint(['child_id'], ['tag.id']), + sa.PrimaryKeyConstraint('parent_id', 'child_id')) + + op.create_table( + 'tag_suggestion', + sa.Column('parent_id', sa.Integer(), nullable=False), + sa.Column('child_id', sa.Integer(), nullable=False), + sa.ForeignKeyConstraint(['parent_id'], ['tag.id']), + sa.ForeignKeyConstraint(['child_id'], ['tag.id']), + sa.PrimaryKeyConstraint('parent_id', 'child_id')) + +def downgrade(): + op.drop_table('tag_suggestion') + op.drop_table('tag_name') + op.drop_table('tag_implication') + op.drop_table('tag') diff --git a/server/szurubooru/tests/conftest.py b/server/szurubooru/tests/conftest.py index 804bf70..5eb3e54 100644 --- a/server/szurubooru/tests/conftest.py +++ b/server/szurubooru/tests/conftest.py @@ -48,3 +48,13 @@ def user_factory(): user.avatar_style = db.User.AVATAR_GRAVATAR return user return factory + +@pytest.fixture +def tag_factory(): + def factory(names=None, category='dummy'): + tag = db.Tag() + tag.names = [db.TagName(name) for name in (names or ['dummy'])] + tag.category = category + tag.creation_time = datetime(1996, 1, 1) + return tag + return factory diff --git a/server/szurubooru/tests/db/test_tag.py b/server/szurubooru/tests/db/test_tag.py new file mode 100644 index 0000000..83423a0 --- /dev/null +++ b/server/szurubooru/tests/db/test_tag.py @@ -0,0 +1,79 @@ +from datetime import datetime +from szurubooru import db + +def test_saving_tag(session, tag_factory): + suggested_tag1 = tag_factory(names=['suggested1']) + suggested_tag2 = tag_factory(names=['suggested2']) + implied_tag1 = tag_factory(names=['implied1']) + implied_tag2 = tag_factory(names=['implied2']) + tag = db.Tag() + tag.names = [db.TagName('alias1'), db.TagName('alias2')] + tag.suggestions = [] + tag.implications = [] + tag.category = 'category' + tag.creation_time = datetime(1997, 1, 1) + tag.last_edit_time = datetime(1998, 1, 1) + tag.post_count = 1 + session.add_all([ + tag, suggested_tag1, suggested_tag2, implied_tag1, implied_tag2]) + session.commit() + + assert tag.tag_id is not None + assert suggested_tag1.tag_id is not None + assert suggested_tag2.tag_id is not None + assert implied_tag1.tag_id is not None + assert implied_tag2.tag_id is not None + tag.suggestions.append(db.TagSuggestion(tag.tag_id, suggested_tag1.tag_id)) + tag.suggestions.append(db.TagSuggestion(tag.tag_id, suggested_tag2.tag_id)) + tag.implications.append(db.TagImplication(tag.tag_id, implied_tag1.tag_id)) + tag.implications.append(db.TagImplication(tag.tag_id, implied_tag2.tag_id)) + session.commit() + + tag = session.query(db.Tag) \ + .join(db.TagName) \ + .filter(db.TagName.name=='alias1') \ + .one() + assert [tag_name.name for tag_name in tag.names] == ['alias1', 'alias2'] + assert tag.category == 'category' + assert tag.creation_time == datetime(1997, 1, 1) + assert tag.last_edit_time == datetime(1998, 1, 1) + assert tag.post_count == 1 + assert [relation.child_tag.names[0].name for relation in tag.suggestions] \ + == ['suggested1', 'suggested2'] + assert [relation.child_tag.names[0].name for relation in tag.implications] \ + == ['implied1', 'implied2'] + +def test_cascade_deletions(session, tag_factory): + suggested_tag1 = tag_factory(names=['suggested1']) + suggested_tag2 = tag_factory(names=['suggested2']) + implied_tag1 = tag_factory(names=['implied1']) + implied_tag2 = tag_factory(names=['implied2']) + tag = db.Tag() + tag.names = [db.TagName('alias1'), db.TagName('alias2')] + tag.suggestions = [] + tag.implications = [] + tag.category = 'category' + tag.creation_time = datetime(1997, 1, 1) + tag.last_edit_time = datetime(1998, 1, 1) + tag.post_count = 1 + session.add_all([ + tag, suggested_tag1, suggested_tag2, implied_tag1, implied_tag2]) + session.commit() + + assert tag.tag_id is not None + assert suggested_tag1.tag_id is not None + assert suggested_tag2.tag_id is not None + assert implied_tag1.tag_id is not None + assert implied_tag2.tag_id is not None + tag.suggestions.append(db.TagSuggestion(tag.tag_id, suggested_tag1.tag_id)) + tag.suggestions.append(db.TagSuggestion(tag.tag_id, suggested_tag2.tag_id)) + tag.implications.append(db.TagImplication(tag.tag_id, implied_tag1.tag_id)) + tag.implications.append(db.TagImplication(tag.tag_id, implied_tag2.tag_id)) + session.commit() + + session.delete(tag) + session.commit() + assert session.query(db.Tag).count() == 4 + assert session.query(db.TagName).count() == 4 + assert session.query(db.TagImplication).count() == 0 + assert session.query(db.TagSuggestion).count() == 0 diff --git a/server/szurubooru/tests/db/test_user.py b/server/szurubooru/tests/db/test_user.py new file mode 100644 index 0000000..1cabc73 --- /dev/null +++ b/server/szurubooru/tests/db/test_user.py @@ -0,0 +1,21 @@ +from datetime import datetime +from szurubooru import db + +def test_saving_user(session): + user = db.User() + user.name = 'name' + user.password_salt = 'salt' + user.password_hash = 'hash' + user.email = 'email' + user.rank = 'rank' + user.creation_time = datetime(1997, 1, 1) + user.avatar_style = db.User.AVATAR_GRAVATAR + session.add(user) + user = session.query(db.User).one() + assert user.name == 'name' + assert user.password_salt == 'salt' + assert user.password_hash == 'hash' + assert user.email == 'email' + assert user.rank == 'rank' + assert user.creation_time == datetime(1997, 1, 1) + assert user.avatar_style == db.User.AVATAR_GRAVATAR diff --git a/server/szurubooru/util/__init__.py b/server/szurubooru/util/__init__.py index e0cd4cb..e69de29 100644 --- a/server/szurubooru/util/__init__.py +++ b/server/szurubooru/util/__init__.py @@ -1 +0,0 @@ -''' Cool functions. '''