server/comments: add comment search

This commit is contained in:
rr- 2016-04-24 11:24:52 +02:00
parent b75cfff8f7
commit 0b47957bb9
8 changed files with 282 additions and 6 deletions

71
API.md
View file

@ -39,7 +39,7 @@
- [Getting featured post](#getting-featured-post)
- [Featuring post](#featuring-post)
- Comments
- ~~Listing comments~~
- [Listing comments](#listing-comments)
- [Creating comment](#creating-comment)
- [Updating comment](#updating-comment)
- [Getting comment](#getting-comment)
@ -673,6 +673,75 @@ data.
Features a post on the main page in web client.
## Listing comments
- **Request**
`GET /comments/?page=<page>&pageSize=<page-size>&query=<query>`
- **Output**
```json5
{
"query": <query>, // same as in input
"page": <page>, // same as in input
"pageSize": <page-size>,
"total": <total-count>,
"comments": [
<comment>,
<comment>,
<comment>
]
}
```
...where `<comment>` is a [comment resource](#comment) and `query` contains
standard [search query](#search).
- **Errors**
- privileges are too low
- **Description**
Searches for comments.
**Anonymous tokens**
Same as `text` token.
**Named tokens**
| `<value>` | Description |
| ---------------- | ---------------------------------------------- |
| `id` | specific comment ID |
| `post` | specific post ID |
| `user` | created by given user (accepts wildcards) |
| `text` | containing given text (accepts wildcards) |
| `creation-date` | created at given date |
| `creation-time` | alias of `creation-date` |
| `last-edit-date` | whose most recent edit date matches given date |
| `last-edit-time` | alias of `last-edit-date` |
| `edit-date` | alias of `last-edit-date` |
| `edit-time` | alias of `last-edit-date` |
**Sort style tokens**
| `<value>` | Description |
| ---------------- | ------------------------- |
| `random` | as random as it can get |
| `user` | author name, A to Z |
| `post` | post ID, newest to oldest |
| `creation-date` | newest to oldest |
| `creation-time` | alias of `creation-date` |
| `last-edit-date` | recently edited first |
| `last-edit-time` | alias of `last-edit-date` |
| `edit-date` | alias of `last-edit-date` |
| `edit-time` | alias of `last-edit-date` |
**Special tokens**
None.
## Creating comment
- **Request**

View file

@ -1,10 +1,20 @@
import datetime
from szurubooru import search
from szurubooru.api.base_api import BaseApi
from szurubooru.func import auth, comments, posts
class CommentListApi(BaseApi):
def __init__(self):
super().__init__()
self._search_executor = search.SearchExecutor(
search.CommentSearchConfig())
def get(self, ctx):
raise NotImplementedError()
auth.verify_privilege(ctx.user, 'comments:list')
return self._search_executor.execute_and_serialize(
ctx,
lambda comment: comments.serialize_comment(comment, ctx.user),
'comments')
def post(self, ctx):
auth.verify_privilege(ctx.user, 'comments:create')

View file

@ -4,3 +4,4 @@ from szurubooru.search.search_executor import SearchExecutor
from szurubooru.search.user_search_config import UserSearchConfig
from szurubooru.search.snapshot_search_config import SnapshotSearchConfig
from szurubooru.search.tag_search_config import TagSearchConfig
from szurubooru.search.comment_search_config import CommentSearchConfig

View file

@ -0,0 +1,43 @@
from sqlalchemy.sql.expression import func
from szurubooru import db
from szurubooru.search.base_search_config import BaseSearchConfig
class CommentSearchConfig(BaseSearchConfig):
def create_query(self):
return db.session.query(db.Comment).join(db.User)
def finalize_query(self, query):
return query.order_by(db.Comment.creation_time.desc())
@property
def anonymous_filter(self):
return self._create_str_filter(db.Comment.text)
@property
def named_filters(self):
return {
'id': self._create_num_filter(db.Comment.comment_id),
'post': self._create_num_filter(db.Comment.post_id),
'user': self._create_str_filter(db.User.name),
'text': self._create_str_filter(db.Comment.text),
'creation-date': self._create_date_filter(db.Comment.creation_time),
'creation-time': self._create_date_filter(db.Comment.creation_time),
'last-edit-date': self._create_date_filter(db.Comment.last_edit_time),
'last-edit-time': self._create_date_filter(db.Comment.last_edit_time),
'edit-date': self._create_date_filter(db.Comment.last_edit_time),
'edit-time': self._create_date_filter(db.Comment.last_edit_time),
}
@property
def sort_columns(self):
return {
'random': (func.random(), None),
'user': (db.User.name, self.SORT_ASC),
'post': (db.Comment.post_id, self.SORT_DESC),
'creation-date': (db.Comment.creation_time, self.SORT_DESC),
'creation-time': (db.Comment.creation_time, self.SORT_DESC),
'last-edit-date': (db.Comment.last_edit_time, self.SORT_DESC),
'last-edit-time': (db.Comment.last_edit_time, self.SORT_DESC),
'edit-date': (db.Comment.last_edit_time, self.SORT_DESC),
'edit-time': (db.Comment.last_edit_time, self.SORT_DESC),
}

View file

@ -14,7 +14,7 @@ class SnapshotSearchConfig(BaseSearchConfig):
'type': self._create_str_filter(db.Snapshot.resource_type),
'id': self._create_str_filter(db.Snapshot.resource_repr),
'date': self._create_date_filter(db.Snapshot.creation_time),
'time': self._create_str_filter(db.Snapshot.creation_time),
'time': self._create_date_filter(db.Snapshot.creation_time),
'operation': self._create_str_filter(db.Snapshot.operation),
'user': self._create_str_filter(db.User.name),
}

View file

@ -18,9 +18,31 @@ def test_ctx(context_factory, config_injector, user_factory, comment_factory):
ret.context_factory = context_factory
ret.user_factory = user_factory
ret.comment_factory = comment_factory
ret.list_api = api.CommentListApi()
ret.detail_api = api.CommentDetailApi()
return ret
def test_retrieving_multiple(test_ctx):
comment1 = test_ctx.comment_factory(text='text 1')
comment2 = test_ctx.comment_factory(text='text 2')
db.session.add_all([comment1, comment2])
result = test_ctx.list_api.get(
test_ctx.context_factory(
input={'query': '', 'page': 1},
user=test_ctx.user_factory(rank='regular_user')))
assert result['query'] == ''
assert result['page'] == 1
assert result['pageSize'] == 100
assert result['total'] == 2
assert [c['text'] for c in result['comments']] == ['text 1', 'text 2']
def test_trying_to_retrieve_multiple_without_privileges(test_ctx):
with pytest.raises(errors.AuthError):
test_ctx.list_api.get(
test_ctx.context_factory(
input={'query': '', 'page': 1},
user=test_ctx.user_factory(rank='anonymous')))
def test_retrieving_single(test_ctx):
comment = test_ctx.comment_factory(text='dummy text')
db.session.add(comment)

View file

@ -0,0 +1,131 @@
import datetime
import pytest
from szurubooru import db, errors, search
@pytest.fixture
def executor():
search_config = search.CommentSearchConfig()
return search.SearchExecutor(search_config)
@pytest.fixture
def verify_unpaged(executor):
def verify(input, expected_comment_text):
actual_count, actual_comments = executor.execute(
input, page=1, page_size=100)
print(actual_comments)
actual_comment_text = [c.text for c in actual_comments]
assert actual_count == len(expected_comment_text)
assert actual_comment_text == expected_comment_text
return verify
@pytest.mark.parametrize('input,expected_comment_text', [
('creation-time:2014', ['t2', 't1']),
('creation-date:2014', ['t2', 't1']),
])
def test_filter_by_creation_time(
verify_unpaged, comment_factory, input, expected_comment_text):
comment1 = comment_factory(text='t1')
comment2 = comment_factory(text='t2')
comment3 = comment_factory(text='t3')
comment1.creation_time = datetime.datetime(2014, 1, 1)
comment2.creation_time = datetime.datetime(2014, 6, 1)
comment3.creation_time = datetime.datetime(2015, 1, 1)
db.session.add_all([comment1, comment2, comment3])
verify_unpaged(input, expected_comment_text)
@pytest.mark.parametrize('input,expected_comment_text', [
('text:t1', ['t1']),
('text:t2', ['t2']),
('text:t1,t2', ['t1', 't2']),
('text:t*', ['t1', 't2']),
])
def test_filter_by_text(
verify_unpaged, comment_factory, input, expected_comment_text):
comment1 = comment_factory(text='t1')
comment2 = comment_factory(text='t2')
db.session.add_all([comment1, comment2])
verify_unpaged(input, expected_comment_text)
@pytest.mark.parametrize('input,expected_comment_text', [
('user:u1', ['t1']),
('user:u2', ['t2']),
('user:u1,u2', ['t2', 't1']),
])
def test_filter_by_user(
verify_unpaged, comment_factory, user_factory, input, expected_comment_text):
db.session.add(comment_factory(text='t2', user=user_factory(name='u2')))
db.session.add(comment_factory(text='t1', user=user_factory(name='u1')))
verify_unpaged(input, expected_comment_text)
@pytest.mark.parametrize('input,expected_comment_text', [
('post:1', ['t1']),
('post:2', ['t2']),
('post:1,2', ['t1', 't2']),
])
def test_filter_by_post(
verify_unpaged, comment_factory, post_factory, input, expected_comment_text):
db.session.add(comment_factory(text='t1', post=post_factory(id=1)))
db.session.add(comment_factory(text='t2', post=post_factory(id=2)))
verify_unpaged(input, expected_comment_text)
@pytest.mark.parametrize('input,expected_comment_text', [
('', ['t1', 't2']),
('t1', ['t1']),
('t2', ['t2']),
('t1,t2', ['t1', 't2']),
])
def test_anonymous(verify_unpaged, comment_factory, input, expected_comment_text):
db.session.add(comment_factory(text='t1'))
db.session.add(comment_factory(text='t2'))
verify_unpaged(input, expected_comment_text)
@pytest.mark.parametrize('input,expected_comment_text', [
('sort:user', ['t1', 't2']),
])
def test_sort_by_user(
verify_unpaged, comment_factory, user_factory, input, expected_comment_text):
db.session.add(comment_factory(text='t2', user=user_factory(name='u2')))
db.session.add(comment_factory(text='t1', user=user_factory(name='u1')))
verify_unpaged(input, expected_comment_text)
@pytest.mark.parametrize('input,expected_comment_text', [
('sort:post', ['t2', 't1']),
])
def test_sort_by_post(
verify_unpaged, comment_factory, post_factory, input, expected_comment_text):
db.session.add(comment_factory(text='t1', post=post_factory(id=1)))
db.session.add(comment_factory(text='t2', post=post_factory(id=2)))
verify_unpaged(input, expected_comment_text)
@pytest.mark.parametrize('input,expected_comment_text', [
('', ['t3', 't2', 't1']),
('sort:creation-date', ['t3', 't2', 't1']),
('sort:creation-time', ['t3', 't2', 't1']),
])
def test_sort_by_creation_time(
verify_unpaged, comment_factory, input, expected_comment_text):
comment1 = comment_factory(text='t1')
comment2 = comment_factory(text='t2')
comment3 = comment_factory(text='t3')
comment1.creation_time = datetime.datetime(1991, 1, 1)
comment2.creation_time = datetime.datetime(1991, 1, 2)
comment3.creation_time = datetime.datetime(1991, 1, 3)
db.session.add_all([comment3, comment1, comment2])
verify_unpaged(input, expected_comment_text)
@pytest.mark.parametrize('input,expected_comment_text', [
('sort:last-edit-date', ['t3', 't2', 't1']),
('sort:last-edit-time', ['t3', 't2', 't1']),
('sort:edit-date', ['t3', 't2', 't1']),
('sort:edit-time', ['t3', 't2', 't1']),
])
def test_sort_by_last_edit_time(
verify_unpaged, comment_factory, input, expected_comment_text):
comment1 = comment_factory(text='t1')
comment2 = comment_factory(text='t2')
comment3 = comment_factory(text='t3')
comment1.last_edit_time = datetime.datetime(1991, 1, 1)
comment2.last_edit_time = datetime.datetime(1991, 1, 2)
comment3.last_edit_time = datetime.datetime(1991, 1, 3)
db.session.add_all([comment3, comment1, comment2])
verify_unpaged(input, expected_comment_text)

View file

@ -102,13 +102,13 @@ def test_sort_by_name(verify_unpaged, tag_factory, input, expected_tag_names):
db.session.add(tag_factory(names=['t1']))
verify_unpaged(input, expected_tag_names)
@pytest.mark.parametrize('input,expected_user_names', [
@pytest.mark.parametrize('input,expected_tag_names', [
('', ['t1', 't2', 't3']),
('sort:creation-date', ['t3', 't2', 't1']),
('sort:creation-time', ['t3', 't2', 't1']),
])
def test_sort_by_creation_time(
verify_unpaged, tag_factory, input, expected_user_names):
verify_unpaged, tag_factory, input, expected_tag_names):
tag1 = tag_factory(names=['t1'])
tag2 = tag_factory(names=['t2'])
tag3 = tag_factory(names=['t3'])
@ -116,7 +116,7 @@ def test_sort_by_creation_time(
tag2.creation_time = datetime.datetime(1991, 1, 2)
tag3.creation_time = datetime.datetime(1991, 1, 3)
db.session.add_all([tag3, tag1, tag2])
verify_unpaged(input, expected_user_names)
verify_unpaged(input, expected_tag_names)
@pytest.mark.parametrize('input,expected_tag_names', [
('sort:suggestion-count', ['t1', 't2', 'sug1', 'sug2', 'sug3']),