server/posts: add featured post retrieval
This commit is contained in:
parent
cf00a3a2de
commit
1476c84a9d
9 changed files with 115 additions and 23 deletions
57
API.md
57
API.md
|
@ -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
|
||||
|
||||
|
|
|
@ -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):
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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')
|
||||
|
|
|
@ -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}
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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)):
|
||||
|
|
|
@ -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,
|
||||
}
|
||||
|
|
|
@ -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()
|
||||
|
|
Loading…
Reference in a new issue