server/tags: add JSON export

This commit is contained in:
rr- 2016-04-16 22:57:02 +02:00
parent 61d2fb88ea
commit 9247e11596
7 changed files with 101 additions and 6 deletions

6
API.md
View file

@ -115,6 +115,12 @@ data.
Searches for tags. Searches for tags.
**Note**: independently, the server exports current tag list snapshots to
the data directory under `tags.json` name. Its purpose is to reduce the
trips frontend needs to make when doing autocompletion, and ease caching.
The data directory and its URL are controlled with `data_dir` and
`data_url` variables in server's configuration.
**Anonymous tokens** **Anonymous tokens**
Same as `name` token. Same as `name` token.

View file

@ -48,6 +48,7 @@ class TagListApi(BaseApi):
ctx.session, names, category, suggestions, implications) ctx.session, names, category, suggestions, implications)
ctx.session.add(tag) ctx.session.add(tag)
ctx.session.commit() ctx.session.commit()
tags.export_to_json(ctx.session)
return {'tag': _serialize_tag(tag)} return {'tag': _serialize_tag(tag)}
class TagDetailApi(BaseApi): class TagDetailApi(BaseApi):
@ -84,6 +85,7 @@ class TagDetailApi(BaseApi):
tag.last_edit_time = datetime.datetime.now() tag.last_edit_time = datetime.datetime.now()
ctx.session.commit() ctx.session.commit()
tags.export_to_json(ctx.session)
return {'tag': _serialize_tag(tag)} return {'tag': _serialize_tag(tag)}
def delete(self, ctx, tag_name): def delete(self, ctx, tag_name):
@ -98,4 +100,5 @@ class TagDetailApi(BaseApi):
auth.verify_privilege(ctx.user, 'tags:delete') auth.verify_privilege(ctx.user, 'tags:delete')
ctx.session.delete(tag) ctx.session.delete(tag)
ctx.session.commit() ctx.session.commit()
tags.export_to_json(ctx.session)
return {} return {}

View file

@ -1,6 +1,7 @@
import datetime import datetime
import os
import pytest import pytest
from szurubooru import api, db, errors from szurubooru import api, config, db, errors
from szurubooru.util import misc, tags from szurubooru.util import misc, tags
def get_tag(session, name): def get_tag(session, name):
@ -15,8 +16,14 @@ def assert_relations(relations, expected_tag_names):
@pytest.fixture @pytest.fixture
def test_ctx( def test_ctx(
session, config_injector, context_factory, user_factory, tag_factory): tmpdir,
session,
config_injector,
context_factory,
user_factory,
tag_factory):
config_injector({ config_injector({
'data_dir': str(tmpdir),
'tag_categories': ['meta', 'character', 'copyright'], 'tag_categories': ['meta', 'character', 'copyright'],
'tag_name_regex': '^[^!]*$', 'tag_name_regex': '^[^!]*$',
'ranks': ['anonymous', 'regular_user'], 'ranks': ['anonymous', 'regular_user'],
@ -58,6 +65,7 @@ def test_creating_simple_tags(test_ctx, fake_datetime):
assert tag.post_count == 0 assert tag.post_count == 0
assert_relations(tag.suggestions, []) assert_relations(tag.suggestions, [])
assert_relations(tag.implications, []) assert_relations(tag.implications, [])
assert os.path.exists(os.path.join(config.config['data_dir'], 'tags.json'))
def test_duplicating_names(test_ctx): def test_duplicating_names(test_ctx):
result = test_ctx.api.post( result = test_ctx.api.post(

View file

@ -1,12 +1,19 @@
import pytest import pytest
import os
from datetime import datetime from datetime import datetime
from szurubooru import api, db, errors from szurubooru import api, config, db, errors
from szurubooru.util import misc, tags from szurubooru.util import misc, tags
@pytest.fixture @pytest.fixture
def test_ctx( def test_ctx(
session, config_injector, context_factory, tag_factory, user_factory): tmpdir,
session,
config_injector,
context_factory,
tag_factory,
user_factory):
config_injector({ config_injector({
'data_dir': str(tmpdir),
'privileges': { 'privileges': {
'tags:delete': 'regular_user', 'tags:delete': 'regular_user',
}, },
@ -29,6 +36,7 @@ def test_removing_tags(test_ctx):
'tag') 'tag')
assert result == {} assert result == {}
assert test_ctx.session.query(db.Tag).count() == 0 assert test_ctx.session.query(db.Tag).count() == 0
assert os.path.exists(os.path.join(config.config['data_dir'], 'tags.json'))
def test_removing_tags_without_privileges(test_ctx): def test_removing_tags_without_privileges(test_ctx):
test_ctx.session.add(test_ctx.tag_factory(names=['tag'])) test_ctx.session.add(test_ctx.tag_factory(names=['tag']))

View file

@ -0,0 +1,42 @@
import datetime
import os
import json
from szurubooru import config, db
from szurubooru.util import tags
def test_export(tmpdir, session, config_injector, tag_factory):
config_injector({
'data_dir': str(tmpdir)
})
sug1 = tag_factory(names=['sug1'])
sug2 = tag_factory(names=['sug2'])
imp1 = tag_factory(names=['imp1'])
imp2 = tag_factory(names=['imp2'])
tag = tag_factory(names=['alias1', 'alias2'])
tag.post_count = 1
session.add_all([tag, sug1, sug2, imp1, imp2])
session.flush()
session.add_all([
db.TagSuggestion(tag.tag_id, sug1.tag_id),
db.TagSuggestion(tag.tag_id, sug2.tag_id),
db.TagImplication(tag.tag_id, imp1.tag_id),
db.TagImplication(tag.tag_id, imp2.tag_id),
])
session.flush()
tags.export_to_json(session)
export_path = os.path.join(config.config['data_dir'], 'tags.json')
assert os.path.exists(export_path)
with open(export_path, 'r') as handle:
assert json.loads(handle.read()) == [
{
'names': ['alias1', 'alias2'],
'usages': 1,
'suggestions': ['sug1', 'sug2'],
'implications': ['imp1', 'imp2'],
},
{'names': ['sug1'], 'usages': 0},
{'names': ['sug2'], 'usages': 0},
{'names': ['imp1'], 'usages': 0},
{'names': ['imp2'], 'usages': 0},
]

View file

@ -1,6 +1,7 @@
import datetime import datetime
import os
import pytest import pytest
from szurubooru import api, db, errors from szurubooru import api, config, db, errors
from szurubooru.util import misc, tags from szurubooru.util import misc, tags
def get_tag(session, name): def get_tag(session, name):
@ -15,8 +16,14 @@ def assert_relations(relations, expected_tag_names):
@pytest.fixture @pytest.fixture
def test_ctx( def test_ctx(
session, config_injector, context_factory, user_factory, tag_factory): tmpdir,
session,
config_injector,
context_factory,
user_factory,
tag_factory):
config_injector({ config_injector({
'data_dir': str(tmpdir),
'tag_categories': ['meta', 'character', 'copyright'], 'tag_categories': ['meta', 'character', 'copyright'],
'tag_name_regex': '^[^!]*$', 'tag_name_regex': '^[^!]*$',
'ranks': ['anonymous', 'regular_user'], 'ranks': ['anonymous', 'regular_user'],
@ -66,6 +73,7 @@ def test_simple_updating(test_ctx, fake_datetime):
assert tag.category == 'character' assert tag.category == 'character'
assert tag.suggestions == [] assert tag.suggestions == []
assert tag.implications == [] assert tag.implications == []
assert os.path.exists(os.path.join(config.config['data_dir'], 'tags.json'))
def test_trying_to_update_non_existing_tag(test_ctx): def test_trying_to_update_non_existing_tag(test_ctx):
with pytest.raises(tags.TagNotFoundError): with pytest.raises(tags.TagNotFoundError):

View file

@ -1,4 +1,6 @@
import datetime import datetime
import os
import json
import re import re
import sqlalchemy import sqlalchemy
from szurubooru import config, db, errors from szurubooru import config, db, errors
@ -25,6 +27,24 @@ def _lower_list(names):
def _check_name_intersection(names1, names2): def _check_name_intersection(names1, names2):
return len(set(_lower_list(names1)).intersection(_lower_list(names2))) > 0 return len(set(_lower_list(names1)).intersection(_lower_list(names2))) > 0
def export_to_json(session):
output = []
for tag in session.query(db.Tag).all():
item = {
'names': [tag_name.name for tag_name in tag.names],
'usages': tag.post_count
}
if len(tag.suggestions):
item['suggestions'] = \
[rel.child_tag.names[0].name for rel in tag.suggestions]
if len(tag.implications):
item['implications'] = \
[rel.child_tag.names[0].name for rel in tag.implications]
output.append(item)
export_path = os.path.join(config.config['data_dir'], 'tags.json')
with open(export_path, 'w') as handle:
handle.write(json.dumps(output, separators=(',', ':')))
def get_by_name(session, name): def get_by_name(session, name):
return session.query(db.Tag) \ return session.query(db.Tag) \
.join(db.TagName) \ .join(db.TagName) \