From 9247e115968e206eea1105f6eba664c824065ffa Mon Sep 17 00:00:00 2001 From: rr- Date: Sat, 16 Apr 2016 22:57:02 +0200 Subject: [PATCH] server/tags: add JSON export --- API.md | 6 +++ server/szurubooru/api/tag_api.py | 3 ++ .../szurubooru/tests/api/test_tag_creating.py | 12 +++++- .../szurubooru/tests/api/test_tag_deleting.py | 12 +++++- .../szurubooru/tests/api/test_tag_export.py | 42 +++++++++++++++++++ .../szurubooru/tests/api/test_tag_updating.py | 12 +++++- server/szurubooru/util/tags.py | 20 +++++++++ 7 files changed, 101 insertions(+), 6 deletions(-) create mode 100644 server/szurubooru/tests/api/test_tag_export.py diff --git a/API.md b/API.md index 52631a0..5dc0381 100644 --- a/API.md +++ b/API.md @@ -115,6 +115,12 @@ data. 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** Same as `name` token. diff --git a/server/szurubooru/api/tag_api.py b/server/szurubooru/api/tag_api.py index c65f97d..680cd44 100644 --- a/server/szurubooru/api/tag_api.py +++ b/server/szurubooru/api/tag_api.py @@ -48,6 +48,7 @@ class TagListApi(BaseApi): ctx.session, names, category, suggestions, implications) ctx.session.add(tag) ctx.session.commit() + tags.export_to_json(ctx.session) return {'tag': _serialize_tag(tag)} class TagDetailApi(BaseApi): @@ -84,6 +85,7 @@ class TagDetailApi(BaseApi): tag.last_edit_time = datetime.datetime.now() ctx.session.commit() + tags.export_to_json(ctx.session) return {'tag': _serialize_tag(tag)} def delete(self, ctx, tag_name): @@ -98,4 +100,5 @@ class TagDetailApi(BaseApi): auth.verify_privilege(ctx.user, 'tags:delete') ctx.session.delete(tag) ctx.session.commit() + tags.export_to_json(ctx.session) return {} diff --git a/server/szurubooru/tests/api/test_tag_creating.py b/server/szurubooru/tests/api/test_tag_creating.py index e464ae1..2faf212 100644 --- a/server/szurubooru/tests/api/test_tag_creating.py +++ b/server/szurubooru/tests/api/test_tag_creating.py @@ -1,6 +1,7 @@ import datetime +import os import pytest -from szurubooru import api, db, errors +from szurubooru import api, config, db, errors from szurubooru.util import misc, tags def get_tag(session, name): @@ -15,8 +16,14 @@ def assert_relations(relations, expected_tag_names): @pytest.fixture def test_ctx( - session, config_injector, context_factory, user_factory, tag_factory): + tmpdir, + session, + config_injector, + context_factory, + user_factory, + tag_factory): config_injector({ + 'data_dir': str(tmpdir), 'tag_categories': ['meta', 'character', 'copyright'], 'tag_name_regex': '^[^!]*$', 'ranks': ['anonymous', 'regular_user'], @@ -58,6 +65,7 @@ def test_creating_simple_tags(test_ctx, fake_datetime): assert tag.post_count == 0 assert_relations(tag.suggestions, []) assert_relations(tag.implications, []) + assert os.path.exists(os.path.join(config.config['data_dir'], 'tags.json')) def test_duplicating_names(test_ctx): result = test_ctx.api.post( diff --git a/server/szurubooru/tests/api/test_tag_deleting.py b/server/szurubooru/tests/api/test_tag_deleting.py index 7c7fcb6..852cce4 100644 --- a/server/szurubooru/tests/api/test_tag_deleting.py +++ b/server/szurubooru/tests/api/test_tag_deleting.py @@ -1,12 +1,19 @@ import pytest +import os from datetime import datetime -from szurubooru import api, db, errors +from szurubooru import api, config, db, errors from szurubooru.util import misc, tags @pytest.fixture def test_ctx( - session, config_injector, context_factory, tag_factory, user_factory): + tmpdir, + session, + config_injector, + context_factory, + tag_factory, + user_factory): config_injector({ + 'data_dir': str(tmpdir), 'privileges': { 'tags:delete': 'regular_user', }, @@ -29,6 +36,7 @@ def test_removing_tags(test_ctx): 'tag') assert result == {} 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): test_ctx.session.add(test_ctx.tag_factory(names=['tag'])) diff --git a/server/szurubooru/tests/api/test_tag_export.py b/server/szurubooru/tests/api/test_tag_export.py new file mode 100644 index 0000000..a71d976 --- /dev/null +++ b/server/szurubooru/tests/api/test_tag_export.py @@ -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}, + ] diff --git a/server/szurubooru/tests/api/test_tag_updating.py b/server/szurubooru/tests/api/test_tag_updating.py index e7b0e64..f70b61c 100644 --- a/server/szurubooru/tests/api/test_tag_updating.py +++ b/server/szurubooru/tests/api/test_tag_updating.py @@ -1,6 +1,7 @@ import datetime +import os import pytest -from szurubooru import api, db, errors +from szurubooru import api, config, db, errors from szurubooru.util import misc, tags def get_tag(session, name): @@ -15,8 +16,14 @@ def assert_relations(relations, expected_tag_names): @pytest.fixture def test_ctx( - session, config_injector, context_factory, user_factory, tag_factory): + tmpdir, + session, + config_injector, + context_factory, + user_factory, + tag_factory): config_injector({ + 'data_dir': str(tmpdir), 'tag_categories': ['meta', 'character', 'copyright'], 'tag_name_regex': '^[^!]*$', 'ranks': ['anonymous', 'regular_user'], @@ -66,6 +73,7 @@ def test_simple_updating(test_ctx, fake_datetime): assert tag.category == 'character' assert tag.suggestions == [] 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): with pytest.raises(tags.TagNotFoundError): diff --git a/server/szurubooru/util/tags.py b/server/szurubooru/util/tags.py index 9ae2c48..c07726a 100644 --- a/server/szurubooru/util/tags.py +++ b/server/szurubooru/util/tags.py @@ -1,4 +1,6 @@ import datetime +import os +import json import re import sqlalchemy from szurubooru import config, db, errors @@ -25,6 +27,24 @@ def _lower_list(names): def _check_name_intersection(names1, names2): 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): return session.query(db.Tag) \ .join(db.TagName) \