server/posts: add featured post retrieval

This commit is contained in:
rr- 2016-04-23 08:07:21 +02:00
parent cf00a3a2de
commit 1476c84a9d
9 changed files with 115 additions and 23 deletions

57
API.md
View file

@ -36,6 +36,7 @@
- ~~Scoring posts~~
- ~~Adding posts to favorites~~
- ~~Removing posts from favorites~~
- [Getting featured post](#getting-featured-post)
- [Featuring post](#featuring-post)
- Users
- [Listing users](#listing-users)
@ -48,6 +49,8 @@
- [Password reset - step 2: confirmation](#password-reset---step-2-confirmation)
- Snapshots
- [Listing snapshots](#listing-snapshots)
- Global info
- [Getting global info](#getting-global-info)
3. [Resources](#resources)
@ -601,6 +604,37 @@ data.
list is truncated to the first 50 elements. Doesn't use paging.
## Getting featured post
- **Request**
`GET /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
- **Description**
Retrieves the post that is currently featured on the main page in web
client. If no post is featured, `<post>` is null and `snapshots` array is
empty.
## Featuring post
- **Request**
@ -628,7 +662,7 @@ data.
- **Description**
Features a post on the main page.
Features a post on the main page in web client.
## Listing users
@ -957,6 +991,27 @@ data.
None.
## Getting global info
- **Request**
`GET /info`
- **Output**
```json5
{
"postCount": <post-count>,
"diskUsage": <disk-usage>, // in bytes
"featuredPost": <featured-post>
}
```
- **Description**
Retrieves simple statistics. `<featured-post>` is null if there is no
featured post yet.
# Resources

View file

@ -1,6 +1,7 @@
import datetime
import os
from szurubooru import config
from szurubooru.api.post_api import serialize_post
from szurubooru.api.base_api import BaseApi
from szurubooru.func import posts
@ -10,10 +11,12 @@ class InfoApi(BaseApi):
self._cache_time = None
self._cache_result = None
def get(self, _ctx):
def get(self, ctx):
featured_post = posts.get_featured_post()
return {
'postCount': posts.get_post_count(),
'diskUsage': self._get_disk_usage()
'diskUsage': self._get_disk_usage(),
'featuredPost': serialize_post(featured_post, ctx.user),
}
def _get_disk_usage(self):

View file

@ -3,6 +3,9 @@ from szurubooru.api.user_api import serialize_user
from szurubooru.func import auth, posts, snapshots
def serialize_post(post, authenticated_user):
if not post:
return None
ret = {
'id': post.post_id,
'creationTime': post.creation_time,
@ -56,3 +59,7 @@ class PostFeatureApi(BaseApi):
snapshots.modify(post, ctx.user)
ctx.session.commit()
return serialize_post_with_details(post, ctx.user)
def get(self, ctx):
post = posts.get_featured_post()
return serialize_post_with_details(post, ctx.user)

View file

@ -2,7 +2,7 @@ from szurubooru import search
from szurubooru.api.base_api import BaseApi
from szurubooru.func import auth, snapshots
def _serialize_snapshot(snapshot):
def serialize_snapshot(snapshot):
earlier_snapshot = snapshots.get_previous_snapshot(snapshot)
return snapshots.serialize_snapshot(snapshot, earlier_snapshot)
@ -14,4 +14,4 @@ class SnapshotListApi(BaseApi):
def get(self, ctx):
auth.verify_privilege(ctx.user, 'snapshots:list')
return self._search_executor.execute_and_serialize(
ctx, _serialize_snapshot, 'snapshots')
ctx, serialize_snapshot, 'snapshots')

View file

@ -3,7 +3,7 @@ from szurubooru import search
from szurubooru.api.base_api import BaseApi
from szurubooru.func import auth, tags, snapshots
def _serialize_tag(tag):
def serialize_tag(tag):
return {
'names': [tag_name.name for tag_name in tag.names],
'category': tag.category.name,
@ -15,9 +15,9 @@ def _serialize_tag(tag):
'lastEditTime': tag.last_edit_time,
}
def _serialize_tag_with_details(tag):
def serialize_tag_with_details(tag):
return {
'tag': _serialize_tag(tag),
'tag': serialize_tag(tag),
'snapshots': snapshots.get_serialized_history(tag),
}
@ -29,7 +29,7 @@ class TagListApi(BaseApi):
def get(self, ctx):
auth.verify_privilege(ctx.user, 'tags:list')
return self._search_executor.execute_and_serialize(
ctx, _serialize_tag, 'tags')
ctx, serialize_tag, 'tags')
def post(self, ctx):
auth.verify_privilege(ctx.user, 'tags:create')
@ -47,7 +47,7 @@ class TagListApi(BaseApi):
snapshots.create(tag, ctx.user)
ctx.session.commit()
tags.export_to_json()
return _serialize_tag_with_details(tag)
return serialize_tag_with_details(tag)
class TagDetailApi(BaseApi):
def get(self, ctx, tag_name):
@ -55,7 +55,7 @@ class TagDetailApi(BaseApi):
tag = tags.get_tag_by_name(tag_name)
if not tag:
raise tags.TagNotFoundError('Tag %r not found.' % tag_name)
return _serialize_tag_with_details(tag)
return serialize_tag_with_details(tag)
def put(self, ctx, tag_name):
tag = tags.get_tag_by_name(tag_name)
@ -83,7 +83,7 @@ class TagDetailApi(BaseApi):
snapshots.modify(tag, ctx.user)
ctx.session.commit()
tags.export_to_json()
return _serialize_tag_with_details(tag)
return serialize_tag_with_details(tag)
def delete(self, ctx, tag_name):
tag = tags.get_tag_by_name(tag_name)
@ -121,7 +121,7 @@ class TagMergeApi(BaseApi):
tags.merge_tags(source_tag, target_tag)
ctx.session.commit()
tags.export_to_json()
return _serialize_tag_with_details(target_tag)
return serialize_tag_with_details(target_tag)
class TagSiblingsApi(BaseApi):
def get(self, ctx, tag_name):
@ -133,7 +133,7 @@ class TagSiblingsApi(BaseApi):
serialized_siblings = []
for sibling, occurrences in result:
serialized_siblings.append({
'tag': _serialize_tag(sibling),
'tag': serialize_tag(sibling),
'occurrences': occurrences
})
return {'siblings': serialized_siblings}

View file

@ -1,15 +1,15 @@
from szurubooru.api.base_api import BaseApi
from szurubooru.func import auth, tags, tag_categories, snapshots
def _serialize_category(category):
def serialize_category(category):
return {
'name': category.name,
'color': category.color,
}
def _serialize_category_with_details(category):
def serialize_category_with_details(category):
return {
'tagCategory': _serialize_category(category),
'tagCategory': serialize_category(category),
'snapshots': snapshots.get_serialized_history(category),
}
@ -19,7 +19,7 @@ class TagCategoryListApi(BaseApi):
categories = tag_categories.get_all_categories()
return {
'tagCategories': [
_serialize_category(category) for category in categories],
serialize_category(category) for category in categories],
}
def post(self, ctx):
@ -32,7 +32,7 @@ class TagCategoryListApi(BaseApi):
snapshots.create(category, ctx.user)
ctx.session.commit()
tags.export_to_json()
return _serialize_category_with_details(category)
return serialize_category_with_details(category)
class TagCategoryDetailApi(BaseApi):
def get(self, ctx, category_name):
@ -41,7 +41,7 @@ class TagCategoryDetailApi(BaseApi):
if not category:
raise tag_categories.TagCategoryNotFoundError(
'Tag category %r not found.' % category_name)
return _serialize_category_with_details(category)
return serialize_category_with_details(category)
def put(self, ctx, category_name):
category = tag_categories.get_category_by_name(category_name)
@ -60,7 +60,7 @@ class TagCategoryDetailApi(BaseApi):
snapshots.modify(category, ctx.user)
ctx.session.commit()
tags.export_to_json()
return _serialize_category_with_details(category)
return serialize_category_with_details(category)
def delete(self, ctx, category_name):
category = tag_categories.get_category_by_name(category_name)

View file

@ -91,6 +91,8 @@ def serialize_snapshot(snapshot, earlier_snapshot):
}
def get_serialized_history(entity):
if not entity:
return []
ret = []
earlier_snapshot = None
for snapshot in reversed(get_snapshots(entity)):

View file

@ -11,15 +11,18 @@ def test_info_api(
assert info_api.get(context_factory()) == {
'postCount': 2,
'diskUsage': 3,
'featuredPost': None,
}
directory.join('test2.txt').write('abc')
with fake_datetime('13:59'):
assert info_api.get(context_factory()) == {
'postCount': 2,
'diskUsage': 3, # still 3 - it's cached
'featuredPost': None,
}
with fake_datetime('14:01'):
assert info_api.get(context_factory()) == {
'postCount': 2,
'diskUsage': 6, # cache expired
'featuredPost': None,
}

View file

@ -6,7 +6,10 @@ 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'},
'privileges': {
'posts:feature': 'regular_user',
'posts:view': 'regular_user',
},
'ranks': ['anonymous', 'regular_user'],
})
ret = util.dotdict()
@ -16,10 +19,16 @@ def test_ctx(context_factory, config_injector, user_factory, post_factory):
ret.api = api.PostFeatureApi()
return ret
def test_no_featured_post(test_ctx):
assert posts.get_featured_post() is None
result = test_ctx.api.get(
test_ctx.context_factory(
user=test_ctx.user_factory(rank='regular_user')))
assert result == {'post': None, 'snapshots': []}
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(
@ -31,6 +40,11 @@ def test_featuring(test_ctx):
assert 'post' in result
assert 'snapshots' in result
assert 'id' in result['post']
result = test_ctx.api.get(
test_ctx.context_factory(
user=test_ctx.user_factory(rank='regular_user')))
assert 'post' 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))
@ -80,3 +94,11 @@ def test_trying_to_feature_without_privileges(test_ctx):
test_ctx.context_factory(
input={'id': 1},
user=test_ctx.user_factory(rank='anonymous')))
def test_getting_featured_post_without_privileges_to_view(test_ctx):
try:
test_ctx.api.get(
test_ctx.context_factory(
user=test_ctx.user_factory(rank='anonymous')))
except:
pytest.fail()