server/posts: add post featuring
This commit is contained in:
parent
a30886cc70
commit
cf00a3a2de
13 changed files with 429 additions and 19 deletions
129
API.md
129
API.md
|
@ -27,6 +27,16 @@
|
|||
- [Deleting tag](#deleting-tag)
|
||||
- [Merging tags](#merging-tags)
|
||||
- [Listing tag siblings](#listing-tag-siblings)
|
||||
- Posts
|
||||
- ~~Listing posts~~
|
||||
- ~~Creating post~~
|
||||
- ~~Updating post~~
|
||||
- ~~Getting post~~
|
||||
- ~~Deleting post~~
|
||||
- ~~Scoring posts~~
|
||||
- ~~Adding posts to favorites~~
|
||||
- ~~Removing posts from favorites~~
|
||||
- [Featuring post](#featuring-post)
|
||||
- Users
|
||||
- [Listing users](#listing-users)
|
||||
- [Creating user](#creating-user)
|
||||
|
@ -44,6 +54,7 @@
|
|||
- [User](#user)
|
||||
- [Tag category](#tag-category)
|
||||
- [Tag](#tag)
|
||||
- [Post](#post)
|
||||
- [Snapshot](#snapshot)
|
||||
|
||||
4. [Search](#search)
|
||||
|
@ -590,6 +601,36 @@ data.
|
|||
list is truncated to the first 50 elements. Doesn't use paging.
|
||||
|
||||
|
||||
## Featuring post
|
||||
- **Request**
|
||||
|
||||
`POST /featured-post`
|
||||
|
||||
- **Output**
|
||||
|
||||
```json5
|
||||
{
|
||||
"post": <post>,
|
||||
"snapshots": [
|
||||
<snapshot>,
|
||||
<snapshot>,
|
||||
<snapshot>
|
||||
]
|
||||
}
|
||||
```
|
||||
...where `<post>` is a [post resource](#post), and `snapshots` contain its
|
||||
earlier versions.
|
||||
|
||||
- **Errors**
|
||||
|
||||
- privileges are too low
|
||||
- trying to feature a post that is currently featured
|
||||
|
||||
- **Description**
|
||||
|
||||
Features a post on the main page.
|
||||
|
||||
|
||||
## Listing users
|
||||
- **Request**
|
||||
|
||||
|
@ -1007,7 +1048,76 @@ A single tag. Tags are used to let users search for posts.
|
|||
- `<suggestions>`: a list of suggested tag names. Suggested tags are shown to
|
||||
the user by the web client on usage.
|
||||
- `<creation-time>`: time the tag was created, formatted as per RFC 3339.
|
||||
- `<creation-time>`: time the tag was edited, formatted as per RFC 3339.
|
||||
- `<last-edit-time>`: time the tag was edited, formatted as per RFC 3339.
|
||||
|
||||
## Post
|
||||
**Description**
|
||||
|
||||
One file together with its metadata posted to the site.
|
||||
|
||||
**Structure**
|
||||
|
||||
```json5
|
||||
{
|
||||
"id": <id>,
|
||||
"safety": <safety>,
|
||||
"type": <type>,
|
||||
"checksum": <checksum>,
|
||||
"source": <source>,
|
||||
"canvasWidth": <canvas-width>,
|
||||
"canvasHeight": <canvas-height>,
|
||||
"flags": <flags>,
|
||||
"tags": <tags>,
|
||||
"relations": <relations>,
|
||||
"creationTime": <creation-time>,
|
||||
"lastEditTime": <last-edit-time>,
|
||||
"user": <user>,
|
||||
"score": <score>,
|
||||
"favoritedBy": <favorited-by>,
|
||||
"featureCount": <feature-count>,
|
||||
"lastFeatureTime": <last-feature-time>,
|
||||
}
|
||||
```
|
||||
|
||||
**Field meaning**
|
||||
|
||||
- `<id>`: the post identifier.
|
||||
- `<safety>`: whether the post is safe for work.
|
||||
|
||||
Available values:
|
||||
|
||||
- `"safe"`
|
||||
- `"sketchy"`
|
||||
- `"unsafe"`
|
||||
|
||||
- `<type>`: the type of the post.
|
||||
|
||||
Available values:
|
||||
|
||||
- `"image"` - plain image.
|
||||
- `"animation"` - animated image (GIF).
|
||||
- `"video"` - WEBM video.
|
||||
- `"flash"` - Flash animation / game.
|
||||
- `"youtube"` - Youtube embed.
|
||||
|
||||
- `<checksum>`: the file checksum. Used in snapshots to signify changes of the
|
||||
post content.
|
||||
- `<source>`: where the post was grabbed form, supplied by the user.
|
||||
- `<canvas-width>` and `<canvas-height>`: the original width and height of the
|
||||
post content.
|
||||
- `<flags>`: various flags such as whether the post is looped, represented as
|
||||
array of plain strings.
|
||||
- `<tags>`: list of tag names the post is tagged with.
|
||||
- `<relations>`: a list of related post IDs. Links to related posts are shown
|
||||
to the user by the web client.
|
||||
- `<creation-time>`: time the tag was created, formatted as per RFC 3339.
|
||||
- `<last-edit-time>`: time the tag was edited, formatted as per RFC 3339.
|
||||
- `<user>`: who created the post, serialized as [user resource](#user).
|
||||
- `<score>`: the score (+1/-1 rating) of the given post.
|
||||
- `<favorited-by>`: list of users, serialized as [user resources](#user).
|
||||
- `<feature-count>`: how many times has the post been featured.
|
||||
- `<last-feature-time>`: the last time the post was featured, formatted as per
|
||||
RFC 3339.
|
||||
|
||||
## Snapshot
|
||||
**Description**
|
||||
|
@ -1078,6 +1188,23 @@ A snapshot is a version of a database resource.
|
|||
}
|
||||
```
|
||||
|
||||
- Post snapshot data (`<resource-type> = "post"`)
|
||||
|
||||
*Example*
|
||||
|
||||
```json5
|
||||
{
|
||||
"source": "http://example.com/",
|
||||
"safety": "safe",
|
||||
"checksum": "deadbeef",
|
||||
"tags": ["tag1", "tag2"],
|
||||
"relations": [1, 2],
|
||||
"notes": [{"polygon": [[1,1],[200,1],[200,200],[1,200]], "text": "..."}],
|
||||
"flags": ["loop"],
|
||||
"featured": false
|
||||
}
|
||||
```
|
||||
|
||||
- `<earlier-data>`: `<data>` field from the last snapshot of the same resource.
|
||||
This allows the client to create visual diffs for any given snapshot without
|
||||
the need to know any other snapshots for a given resource.
|
||||
|
|
|
@ -2,8 +2,15 @@
|
|||
|
||||
from szurubooru.api.password_reset_api import PasswordResetApi
|
||||
from szurubooru.api.user_api import UserListApi, UserDetailApi
|
||||
from szurubooru.api.tag_api import TagListApi, TagDetailApi, TagMergeApi, TagSiblingsApi
|
||||
from szurubooru.api.tag_category_api import TagCategoryListApi, TagCategoryDetailApi
|
||||
from szurubooru.api.tag_api import (
|
||||
TagListApi,
|
||||
TagDetailApi,
|
||||
TagMergeApi,
|
||||
TagSiblingsApi)
|
||||
from szurubooru.api.tag_category_api import (
|
||||
TagCategoryListApi,
|
||||
TagCategoryDetailApi)
|
||||
from szurubooru.api.post_api import PostFeatureApi
|
||||
from szurubooru.api.snapshot_api import SnapshotListApi
|
||||
from szurubooru.api.info_api import InfoApi
|
||||
from szurubooru.api.context import Context, Request
|
||||
|
|
58
server/szurubooru/api/post_api.py
Normal file
58
server/szurubooru/api/post_api.py
Normal file
|
@ -0,0 +1,58 @@
|
|||
from szurubooru.api.base_api import BaseApi
|
||||
from szurubooru.api.user_api import serialize_user
|
||||
from szurubooru.func import auth, posts, snapshots
|
||||
|
||||
def serialize_post(post, authenticated_user):
|
||||
ret = {
|
||||
'id': post.post_id,
|
||||
'creationTime': post.creation_time,
|
||||
'lastEditTime': post.last_edit_time,
|
||||
'safety': post.safety,
|
||||
'type': post.type,
|
||||
'checksum': post.checksum,
|
||||
'source': post.source,
|
||||
'fileSize': post.file_size,
|
||||
'canvasWidth': post.canvas_width,
|
||||
'canvasHeight': post.canvas_height,
|
||||
'flags': post.flags,
|
||||
'tags': [tag.first_name for tag in post.tags],
|
||||
'relations': [rel.post_id for rel in post.relations],
|
||||
'notes': sorted([{
|
||||
'path': note.path,
|
||||
'text': note.text,
|
||||
} for note in post.notes]),
|
||||
'user': serialize_user(post.user, authenticated_user),
|
||||
'score': post.score,
|
||||
'featureCount': post.feature_count,
|
||||
'lastFeatureTime': post.last_feature_time,
|
||||
'favoritedBy': [serialize_user(rel, authenticated_user) \
|
||||
for rel in post.favorited_by],
|
||||
}
|
||||
|
||||
# TODO: fetch own score if needed
|
||||
|
||||
return ret
|
||||
|
||||
def serialize_post_with_details(post, authenticated_user):
|
||||
return {
|
||||
'post': serialize_post(post, authenticated_user),
|
||||
'snapshots': snapshots.get_serialized_history(post),
|
||||
}
|
||||
|
||||
class PostFeatureApi(BaseApi):
|
||||
def post(self, ctx):
|
||||
auth.verify_privilege(ctx.user, 'posts:feature')
|
||||
post_id = ctx.get_param_as_int('id', required=True)
|
||||
post = posts.get_post_by_id(post_id)
|
||||
if not post:
|
||||
raise posts.PostNotFoundError('Post %r not found.' % post_id)
|
||||
featured_post = posts.get_featured_post()
|
||||
if featured_post and featured_post.post_id == post.post_id:
|
||||
raise posts.PostAlreadyFeaturedError(
|
||||
'Post %r is already featured.' % post_id)
|
||||
posts.feature_post(post, ctx.user)
|
||||
if featured_post:
|
||||
snapshots.modify(featured_post, ctx.user)
|
||||
snapshots.modify(post, ctx.user)
|
||||
ctx.session.commit()
|
||||
return serialize_post_with_details(post, ctx.user)
|
|
@ -3,7 +3,10 @@ from szurubooru import config, search
|
|||
from szurubooru.api.base_api import BaseApi
|
||||
from szurubooru.func import auth, users
|
||||
|
||||
def _serialize_user(authenticated_user, user):
|
||||
def serialize_user(user, authenticated_user):
|
||||
if not user:
|
||||
return {}
|
||||
|
||||
ret = {
|
||||
'name': user.name,
|
||||
'rank': user.rank,
|
||||
|
@ -36,7 +39,7 @@ class UserListApi(BaseApi):
|
|||
def get(self, ctx):
|
||||
auth.verify_privilege(ctx.user, 'users:list')
|
||||
return self._search_executor.execute_and_serialize(
|
||||
ctx, lambda user: _serialize_user(ctx.user, user), 'users')
|
||||
ctx, lambda user: serialize_user(user, ctx.user), 'users')
|
||||
|
||||
def post(self, ctx):
|
||||
auth.verify_privilege(ctx.user, 'users:create')
|
||||
|
@ -58,7 +61,7 @@ class UserListApi(BaseApi):
|
|||
|
||||
ctx.session.add(user)
|
||||
ctx.session.commit()
|
||||
return {'user': _serialize_user(ctx.user, user)}
|
||||
return {'user': serialize_user(user, ctx.user)}
|
||||
|
||||
class UserDetailApi(BaseApi):
|
||||
def get(self, ctx, user_name):
|
||||
|
@ -66,7 +69,7 @@ class UserDetailApi(BaseApi):
|
|||
user = users.get_user_by_name(user_name)
|
||||
if not user:
|
||||
raise users.UserNotFoundError('User %r not found.' % user_name)
|
||||
return {'user': _serialize_user(ctx.user, user)}
|
||||
return {'user': serialize_user(user, ctx.user)}
|
||||
|
||||
def put(self, ctx, user_name):
|
||||
user = users.get_user_by_name(user_name)
|
||||
|
@ -102,7 +105,7 @@ class UserDetailApi(BaseApi):
|
|||
ctx.get_file('avatar'))
|
||||
|
||||
ctx.session.commit()
|
||||
return {'user': _serialize_user(ctx.user, user)}
|
||||
return {'user': serialize_user(user, ctx.user)}
|
||||
|
||||
def delete(self, ctx, user_name):
|
||||
user = users.get_user_by_name(user_name)
|
||||
|
|
|
@ -55,6 +55,7 @@ def create_app():
|
|||
tag_detail_api = api.TagDetailApi()
|
||||
tag_merge_api = api.TagMergeApi()
|
||||
tag_siblings_api = api.TagSiblingsApi()
|
||||
post_feature_api = api.PostFeatureApi()
|
||||
password_reset_api = api.PasswordResetApi()
|
||||
snapshot_list_api = api.SnapshotListApi()
|
||||
info_api = api.InfoApi()
|
||||
|
@ -77,5 +78,6 @@ def create_app():
|
|||
app.add_route('/password-reset/{user_name}', password_reset_api)
|
||||
app.add_route('/snapshots/', snapshot_list_api)
|
||||
app.add_route('/info/', info_api)
|
||||
app.add_route('/featured-post/', post_feature_api)
|
||||
|
||||
return app
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
from sqlalchemy import Column, Integer, DateTime, String, Text, PickleType, ForeignKey
|
||||
from sqlalchemy.orm import relationship, column_property
|
||||
from sqlalchemy.orm import relationship, column_property, object_session
|
||||
from sqlalchemy.sql.expression import func, select
|
||||
from szurubooru.db.base import Base
|
||||
|
||||
|
@ -87,9 +87,9 @@ class Post(Base):
|
|||
checksum = Column('checksum', String(64), nullable=False)
|
||||
source = Column('source', String(200))
|
||||
file_size = Column('file_size', Integer)
|
||||
image_width = Column('image_width', Integer)
|
||||
image_height = Column('image_height', Integer)
|
||||
flags = Column('flags', Integer, nullable=False, default=0)
|
||||
canvas_width = Column('image_width', Integer)
|
||||
canvas_height = Column('image_height', Integer)
|
||||
flags = Column('flags', PickleType, default=None)
|
||||
|
||||
user = relationship('User')
|
||||
tags = relationship('Tag', backref='posts', secondary='post_tag')
|
||||
|
@ -102,7 +102,7 @@ class Post(Base):
|
|||
'PostFeature', cascade='all, delete-orphan', lazy='joined')
|
||||
scores = relationship(
|
||||
'PostScore', cascade='all, delete-orphan', lazy='joined')
|
||||
favorites = relationship(
|
||||
favorited_by = relationship(
|
||||
'PostFavorite', cascade='all, delete-orphan', lazy='joined')
|
||||
notes = relationship(
|
||||
'PostNote', cascade='all, delete-orphan', lazy='joined')
|
||||
|
@ -112,8 +112,16 @@ class Post(Base):
|
|||
.where(PostTag.post_id == post_id) \
|
||||
.correlate_except(PostTag))
|
||||
|
||||
@property
|
||||
def is_featured(self):
|
||||
featured_post = object_session(self) \
|
||||
.query(PostFeature) \
|
||||
.order_by(PostFeature.time.desc()) \
|
||||
.first()
|
||||
return featured_post and featured_post.post_id == self.post_id
|
||||
|
||||
# TODO: wire these
|
||||
fav_count = Column('auto_fav_count', Integer, nullable=False, default=0)
|
||||
favorite_count = Column('auto_fav_count', Integer, nullable=False, default=0)
|
||||
score = Column('auto_score', Integer, nullable=False, default=0)
|
||||
feature_count = Column('auto_feature_count', Integer, nullable=False, default=0)
|
||||
comment_count = Column('auto_comment_count', Integer, nullable=False, default=0)
|
||||
|
|
|
@ -1,5 +1,28 @@
|
|||
import datetime
|
||||
import sqlalchemy
|
||||
from szurubooru import db
|
||||
from szurubooru import db, errors
|
||||
|
||||
class PostNotFoundError(errors.NotFoundError): pass
|
||||
class PostAlreadyFeaturedError(errors.ValidationError): pass
|
||||
|
||||
def get_post_count():
|
||||
return db.session.query(sqlalchemy.func.count(db.Post.post_id)).one()[0]
|
||||
|
||||
def get_post_by_id(post_id):
|
||||
return db.session.query(db.Post) \
|
||||
.filter(db.Post.post_id == post_id) \
|
||||
.one_or_none()
|
||||
|
||||
def get_featured_post():
|
||||
post_feature = db.session \
|
||||
.query(db.PostFeature) \
|
||||
.order_by(db.PostFeature.time.desc()) \
|
||||
.first()
|
||||
return post_feature.post if post_feature else None
|
||||
|
||||
def feature_post(post, user):
|
||||
post_feature = db.PostFeature()
|
||||
post_feature.time = datetime.datetime.now()
|
||||
post_feature.post = post
|
||||
post_feature.user = user
|
||||
db.session.add(post_feature)
|
||||
|
|
|
@ -3,13 +3,27 @@ from sqlalchemy.inspection import inspect
|
|||
from szurubooru import db
|
||||
|
||||
def get_tag_snapshot(tag):
|
||||
ret = {
|
||||
return {
|
||||
'names': [tag_name.name for tag_name in tag.names],
|
||||
'category': tag.category.name,
|
||||
'suggestions': sorted(rel.first_name for rel in tag.suggestions),
|
||||
'implications': sorted(rel.first_name for rel in tag.implications),
|
||||
}
|
||||
return ret
|
||||
|
||||
def get_post_snapshot(post):
|
||||
return {
|
||||
'source': post.source,
|
||||
'safety': post.safety,
|
||||
'checksum': post.checksum,
|
||||
'tags': sorted([tag.first_name for tag in post.tags]),
|
||||
'relations': sorted([rel.post_id for rel in post.relations]),
|
||||
'notes': sorted([{
|
||||
'polygon': note.polygon,
|
||||
'text': note.text,
|
||||
} for note in post.notes]),
|
||||
'flags': post.flags,
|
||||
'featured': post.is_featured,
|
||||
}
|
||||
|
||||
def get_tag_category_snapshot(category):
|
||||
return {
|
||||
|
@ -25,6 +39,9 @@ serializers = {
|
|||
'tag_category': (
|
||||
get_tag_category_snapshot,
|
||||
lambda category: category.name),
|
||||
'post': (
|
||||
get_post_snapshot,
|
||||
lambda post: post.post_id),
|
||||
}
|
||||
|
||||
def get_resource_info(entity):
|
||||
|
|
|
@ -0,0 +1,22 @@
|
|||
'''
|
||||
Change flags column type
|
||||
|
||||
Revision ID: 84bd402f15f0
|
||||
Created at: 2016-04-22 20:48:32.386159
|
||||
'''
|
||||
|
||||
import sqlalchemy as sa
|
||||
from alembic import op
|
||||
|
||||
revision = '84bd402f15f0'
|
||||
down_revision = '9587de88a84b'
|
||||
branch_labels = None
|
||||
depends_on = None
|
||||
|
||||
def upgrade():
|
||||
op.drop_column('post', 'flags')
|
||||
op.add_column('post', sa.Column('flags', sa.PickleType(), nullable=True))
|
||||
|
||||
def downgrade():
|
||||
op.drop_column('post', 'flags')
|
||||
op.add_column('post', sa.Column('flags', sa.Integer(), autoincrement=False, nullable=False))
|
82
server/szurubooru/tests/api/test_post_featuring.py
Normal file
82
server/szurubooru/tests/api/test_post_featuring.py
Normal file
|
@ -0,0 +1,82 @@
|
|||
import datetime
|
||||
import pytest
|
||||
from szurubooru import api, db, errors
|
||||
from szurubooru.func import util, posts
|
||||
|
||||
@pytest.fixture
|
||||
def test_ctx(context_factory, config_injector, user_factory, post_factory):
|
||||
config_injector({
|
||||
'privileges': {'posts:feature': 'regular_user'},
|
||||
'ranks': ['anonymous', 'regular_user'],
|
||||
})
|
||||
ret = util.dotdict()
|
||||
ret.context_factory = context_factory
|
||||
ret.user_factory = user_factory
|
||||
ret.post_factory = post_factory
|
||||
ret.api = api.PostFeatureApi()
|
||||
return ret
|
||||
|
||||
def test_featuring(test_ctx):
|
||||
db.session.add(test_ctx.post_factory(id=1))
|
||||
db.session.commit()
|
||||
assert posts.get_featured_post() is None
|
||||
assert not posts.get_post_by_id(1).is_featured
|
||||
result = test_ctx.api.post(
|
||||
test_ctx.context_factory(
|
||||
input={'id': 1},
|
||||
user=test_ctx.user_factory(rank='regular_user')))
|
||||
assert posts.get_featured_post() is not None
|
||||
assert posts.get_featured_post().post_id == 1
|
||||
assert posts.get_post_by_id(1).is_featured
|
||||
assert 'post' in result
|
||||
assert 'snapshots' in result
|
||||
assert 'id' in result['post']
|
||||
|
||||
def test_trying_to_feature_the_same_post_twice(test_ctx):
|
||||
db.session.add(test_ctx.post_factory(id=1))
|
||||
db.session.commit()
|
||||
test_ctx.api.post(
|
||||
test_ctx.context_factory(
|
||||
input={'id': 1},
|
||||
user=test_ctx.user_factory(rank='regular_user')))
|
||||
with pytest.raises(posts.PostAlreadyFeaturedError):
|
||||
test_ctx.api.post(
|
||||
test_ctx.context_factory(
|
||||
input={'id': 1},
|
||||
user=test_ctx.user_factory(rank='regular_user')))
|
||||
|
||||
def test_featuring_one_post_after_another(test_ctx, fake_datetime):
|
||||
db.session.add(test_ctx.post_factory(id=1))
|
||||
db.session.add(test_ctx.post_factory(id=2))
|
||||
db.session.commit()
|
||||
assert posts.get_featured_post() is None
|
||||
assert not posts.get_post_by_id(1).is_featured
|
||||
assert not posts.get_post_by_id(2).is_featured
|
||||
with fake_datetime('1997'):
|
||||
result = test_ctx.api.post(
|
||||
test_ctx.context_factory(
|
||||
input={'id': 1},
|
||||
user=test_ctx.user_factory(rank='regular_user')))
|
||||
with fake_datetime('1998'):
|
||||
result = test_ctx.api.post(
|
||||
test_ctx.context_factory(
|
||||
input={'id': 2},
|
||||
user=test_ctx.user_factory(rank='regular_user')))
|
||||
assert posts.get_featured_post() is not None
|
||||
assert posts.get_featured_post().post_id == 2
|
||||
assert not posts.get_post_by_id(1).is_featured
|
||||
assert posts.get_post_by_id(2).is_featured
|
||||
|
||||
def test_trying_to_feature_non_existing(test_ctx):
|
||||
with pytest.raises(posts.PostNotFoundError):
|
||||
test_ctx.api.post(
|
||||
test_ctx.context_factory(
|
||||
input={'id': 1},
|
||||
user=test_ctx.user_factory(rank='regular_user')))
|
||||
|
||||
def test_trying_to_feature_without_privileges(test_ctx):
|
||||
with pytest.raises(errors.AuthError):
|
||||
test_ctx.api.post(
|
||||
test_ctx.context_factory(
|
||||
input={'id': 1},
|
||||
user=test_ctx.user_factory(rank='anonymous')))
|
|
@ -125,14 +125,16 @@ def tag_factory(session):
|
|||
@pytest.fixture
|
||||
def post_factory():
|
||||
def factory(
|
||||
id=None,
|
||||
safety=db.Post.SAFETY_SAFE,
|
||||
type=db.Post.TYPE_IMAGE,
|
||||
checksum='...'):
|
||||
post = db.Post()
|
||||
post.post_id = id
|
||||
post.safety = safety
|
||||
post.type = type
|
||||
post.checksum = checksum
|
||||
post.flags = 0
|
||||
post.flags = []
|
||||
post.creation_time = datetime.datetime(1996, 1, 1)
|
||||
return post
|
||||
return factory
|
||||
|
|
|
@ -68,7 +68,7 @@ def test_cascade_deletions(post_factory, user_factory, tag_factory):
|
|||
post.relations.append(related_post1)
|
||||
post.relations.append(related_post2)
|
||||
post.scores.append(score)
|
||||
post.favorites.append(favorite)
|
||||
post.favorited_by.append(favorite)
|
||||
post.features.append(feature)
|
||||
post.notes.append(note)
|
||||
db.session.flush()
|
||||
|
|
|
@ -3,6 +3,65 @@ import pytest
|
|||
from szurubooru import db
|
||||
from szurubooru.func import snapshots
|
||||
|
||||
def test_serializing_post(post_factory, user_factory, tag_factory):
|
||||
user = user_factory(name='dummy-user')
|
||||
tag1 = tag_factory(names=['dummy-tag1'])
|
||||
tag2 = tag_factory(names=['dummy-tag2'])
|
||||
post = post_factory(id=1)
|
||||
related_post1 = post_factory(id=2)
|
||||
related_post2 = post_factory(id=3)
|
||||
db.session.add_all([user, tag1, tag2, post, related_post1, related_post2])
|
||||
db.session.flush()
|
||||
|
||||
score = db.PostScore()
|
||||
score.post = post
|
||||
score.user = user
|
||||
score.time = datetime.datetime(1997, 1, 1)
|
||||
score.score = 1
|
||||
favorite = db.PostFavorite()
|
||||
favorite.post = post
|
||||
favorite.user = user
|
||||
favorite.time = datetime.datetime(1997, 1, 1)
|
||||
feature = db.PostFeature()
|
||||
feature.post = post
|
||||
feature.user = user
|
||||
feature.time = datetime.datetime(1997, 1, 1)
|
||||
note = db.PostNote()
|
||||
note.post = post
|
||||
note.polygon = [(1, 1), (200, 1), (200, 200), (1, 200)]
|
||||
note.text = 'some text'
|
||||
db.session.add_all([score])
|
||||
db.session.flush()
|
||||
|
||||
post.user = user
|
||||
post.checksum = 'deadbeef'
|
||||
post.source = 'example.com'
|
||||
post.tags.append(tag1)
|
||||
post.tags.append(tag2)
|
||||
post.relations.append(related_post1)
|
||||
post.relations.append(related_post2)
|
||||
post.scores.append(score)
|
||||
post.favorited_by.append(favorite)
|
||||
post.features.append(feature)
|
||||
post.notes.append(note)
|
||||
|
||||
assert snapshots.get_post_snapshot(post) == {
|
||||
'checksum': 'deadbeef',
|
||||
'featured': True,
|
||||
'flags': [],
|
||||
'notes': [
|
||||
{
|
||||
'polygon': [(1, 1), (200, 1), (200, 200), (1, 200)],
|
||||
'text': 'some text',
|
||||
}
|
||||
],
|
||||
'relations': [2, 3],
|
||||
'safety': 'safe',
|
||||
'source': 'example.com',
|
||||
'tags': ['dummy-tag1', 'dummy-tag2'],
|
||||
}
|
||||
|
||||
|
||||
def test_serializing_tag(tag_factory):
|
||||
tag = tag_factory(names=['main_name', 'alias'], category_name='dummy')
|
||||
assert snapshots.get_tag_snapshot(tag) == {
|
||||
|
|
Loading…
Reference in a new issue