server/tags: introduce tag category API

This commit is contained in:
rr- 2016-04-19 11:56:09 +02:00
parent 83784c5e76
commit 2fba374e65
14 changed files with 663 additions and 30 deletions

133
API.md
View File

@ -92,28 +92,151 @@ data.
## Listing tag categories ## Listing tag categories
- **Request**
Not implemented yet. `GET /tag-categories`
- **Output**
```json5
{
"tagCategories": [
<tag-category>,
<tag-category>,
<tag-category>
]
}
```
...where `<tag-category>` is a [tag category resource](#tag-category).
- **Errors**
- privileges are too low
- **Description**
Lists all tag categories. Doesn't support paging.
## Creating tag category ## Creating tag category
- **Request**
Not implemented yet. `POST /tag-categories`
- **Input**
```json5
{
"name": <name>,
"color": <color>
}
```
- **Output**
```json5
{
"tagCategory": <tag-category>
}
```
...where `<tag-category>` is a [tag category resource](#tag-category).
- **Errors**
- the name is used by an existing tag category (names are case insensitive)
- the name is invalid or missing
- the color is invalid or missing
- privileges are too low
- **Description**
Creates a new tag category using specified parameters. Name must match
`tag_category_name_regex` from server's configuration.
## Updating tag category ## Updating tag category
- **Request**
Not implemented yet. `PUT /tag-category/<name>`
- **Input**
```json5
{
"name": <name>, // optional
"color": <color>, // optional
}
```
- **Output**
```json5
{
"tagCategory": <tag-category>
}
```
...where `<tag-category>` is a [tag category resource](#tag-category).
- **Errors**
- the tag category does not exist
- the name is used by an existing tag category (names are case insensitive)
- the name is invalid
- the color is invalid
- privileges are too low
- **Description**
Updates an existing tag category using specified parameters. Name must
match `tag_category_name_regex` from server's configuration. All fields are
optional - update concerns only provided fields.
## Getting tag category ## Getting tag category
- **Request**
Not implemented yet. `GET /tag-category/<name>`
- **Output**
```json5
{
"tagCategory": <tag-category>
}
```
...where `<tag-category>` is a [tag category resource](#tag-category).
- **Errors**
- the tag category does not exist
- privileges are too low
- **Description**
Retrieves information about an existing tag category.
## Deleting tag category ## Deleting tag category
- **Request**
Not implemented yet. `DELETE /tag-category/<name>`
- **Output**
```json5
{}
```
- **Errors**
- the tag category does not exist
- the tag category is used by some tags
- privileges are too low
- **Description**
Deletes existing tag category. The tag category to be deleted must have no
usages.
## Listing tags ## Listing tags

View File

@ -37,6 +37,7 @@ limits:
max_comment_length: 5000 max_comment_length: 5000
tag_name_regex: ^:?[a-zA-Z0-9_-]+$ tag_name_regex: ^:?[a-zA-Z0-9_-]+$
tag_category_name_regex: ^.{1,}$
# changing ranks after deployment may require manual tweaks to the database. # changing ranks after deployment may require manual tweaks to the database.
ranks: ranks:
@ -103,6 +104,12 @@ privileges:
'tags:merge': mod 'tags:merge': mod
'tags:delete': mod 'tags:delete': mod
'tag_categories:create': mod
'tag_categories:edit:name': mod
'tag_categories:edit:color': mod
'tag_categories:list': anonymous
'tag_categories:delete': mod
'comments:create': regular_user 'comments:create': regular_user
'comments:delete:any': mod 'comments:delete:any': mod
'comments:delete:own': regular_user 'comments:delete:own': regular_user

View File

@ -3,4 +3,5 @@
from szurubooru.api.password_reset_api import PasswordResetApi from szurubooru.api.password_reset_api import PasswordResetApi
from szurubooru.api.user_api import UserListApi, UserDetailApi from szurubooru.api.user_api import UserListApi, UserDetailApi
from szurubooru.api.tag_api import TagListApi, TagDetailApi from szurubooru.api.tag_api import TagListApi, TagDetailApi
from szurubooru.api.tag_category_api import TagCategoryListApi, TagCategoryDetailApi
from szurubooru.api.context import Context, Request from szurubooru.api.context import Context, Request

View File

@ -0,0 +1,68 @@
from szurubooru.util import auth, tags, tag_categories
from szurubooru.api.base_api import BaseApi
def _serialize_category(category):
return {
'name': category.name,
'color': category.color,
}
class TagCategoryListApi(BaseApi):
def get(self, ctx):
auth.verify_privilege(ctx.user, 'tag_categories:list')
categories = tag_categories.get_all_categories()
return {
'tagCategories': [
_serialize_category(category) for category in categories],
}
def post(self, ctx):
auth.verify_privilege(ctx.user, 'tag_categories:create')
name = ctx.get_param_as_string('name', required=True)
color = ctx.get_param_as_string('color', required=True)
category = tag_categories.create_category(name, color)
ctx.session.add(category)
ctx.session.commit()
tags.export_to_json()
return {'tagCategory': _serialize_category(category)}
class TagCategoryDetailApi(BaseApi):
def get(self, ctx, category_name):
auth.verify_privilege(ctx.user, 'tag_categories:view')
category = tag_categories.get_category_by_name(category_name)
if not category:
raise tag_categories.TagCategoryNotFoundError(
'Tag category %r not found.' % category_name)
return {'tagCategory': _serialize_category(category)}
def put(self, ctx, category_name):
category = tag_categories.get_category_by_name(category_name)
if not category:
raise tag_categories.TagCategoryNotFoundError(
'Tag category %r not found.' % category_name)
if ctx.has_param('name'):
auth.verify_privilege(ctx.user, 'tag_categories:edit:name')
tag_categories.update_name(
category, ctx.get_param_as_string('name'))
if ctx.has_param('color'):
auth.verify_privilege(ctx.user, 'tag_categories:edit:color')
tag_categories.update_color(
category, ctx.get_param_as_string('color'))
ctx.session.commit()
tags.export_to_json()
return {'tagCategory': _serialize_category(category)}
def delete(self, ctx, category_name):
category = tag_categories.get_category_by_name(category_name)
if not category:
raise tag_categories.TagCategoryNotFoundError(
'Tag category %r not found.' % category_name)
if category.tag_count > 0:
raise tag_categories.TagCategoryIsInUseError(
'Tag category has some usages and cannot be deleted. ' +
'Please remove this category from relevant tags first..')
auth.verify_privilege(ctx.user, 'tag_categories:delete')
ctx.session.delete(category)
ctx.session.commit()
tags.export_to_json()
return {}

View File

@ -36,6 +36,8 @@ def create_app():
user_list_api = api.UserListApi() user_list_api = api.UserListApi()
user_detail_api = api.UserDetailApi() user_detail_api = api.UserDetailApi()
tag_category_list_api = api.TagCategoryListApi()
tag_category_detail_api = api.TagCategoryDetailApi()
tag_list_api = api.TagListApi() tag_list_api = api.TagListApi()
tag_detail_api = api.TagDetailApi() tag_detail_api = api.TagDetailApi()
password_reset_api = api.PasswordResetApi() password_reset_api = api.PasswordResetApi()
@ -49,6 +51,8 @@ def create_app():
app.add_route('/users/', user_list_api) app.add_route('/users/', user_list_api)
app.add_route('/user/{user_name}', user_detail_api) app.add_route('/user/{user_name}', user_detail_api)
app.add_route('/tag-categories/', tag_category_list_api)
app.add_route('/tag-category/{category_name}', tag_category_detail_api)
app.add_route('/tags/', tag_list_api) app.add_route('/tags/', tag_list_api)
app.add_route('/tag/{tag_name}', tag_detail_api) app.add_route('/tag/{tag_name}', tag_detail_api)
app.add_route('/password-reset/{user_name}', password_reset_api) app.add_route('/password-reset/{user_name}', password_reset_api)

View File

@ -1,5 +1,8 @@
from sqlalchemy import Column, Integer, String from sqlalchemy import Column, Integer, String, table
from sqlalchemy.orm import column_property
from sqlalchemy.sql.expression import func, select
from szurubooru.db.base import Base from szurubooru.db.base import Base
from szurubooru.db.tag import Tag
class TagCategory(Base): class TagCategory(Base):
__tablename__ = 'tag_category' __tablename__ = 'tag_category'
@ -8,5 +11,10 @@ class TagCategory(Base):
name = Column('name', String(32), nullable=False) name = Column('name', String(32), nullable=False)
color = Column('color', String(32), nullable=False, default='#000000') color = Column('color', String(32), nullable=False, default='#000000')
def __init__(self, name): def __init__(self, name=None):
self.name = name self.name = name
tag_count = column_property(
select([func.count('Tag.tag_id')]) \
.where(Tag.category_id == tag_category_id) \
.correlate(table('TagCategory')))

View File

@ -0,0 +1,96 @@
import os
import pytest
from szurubooru import api, config, db, errors
from szurubooru.util import misc, tag_categories
@pytest.fixture
def test_ctx(tmpdir, config_injector, context_factory, user_factory):
config_injector({
'data_dir': str(tmpdir),
'tag_category_name_regex': '^[^!]+$',
'ranks': ['anonymous', 'regular_user'],
'privileges': {'tag_categories:create': 'regular_user'},
})
ret = misc.dotdict()
ret.context_factory = context_factory
ret.user_factory = user_factory
ret.api = api.TagCategoryListApi()
return ret
def test_creating_category(test_ctx):
result = test_ctx.api.post(
test_ctx.context_factory(
input={'name': 'meta', 'color': 'black'},
user=test_ctx.user_factory(rank='regular_user')))
assert result == {
'tagCategory': {'name': 'meta', 'color': 'black'},
}
category = db.session.query(db.TagCategory).one()
assert category.name == 'meta'
assert category.color == 'black'
assert category.tag_count == 0
assert os.path.exists(os.path.join(config.config['data_dir'], 'tags.json'))
@pytest.mark.parametrize('input', [
{'name': None},
{'name': ''},
{'name': '!bad'},
{'color': None},
{'color': ''},
])
def test_invalid_inputs(test_ctx, input):
real_input = {
'name': 'okay',
'color': 'okay',
}
for key, value in input.items():
real_input[key] = value
with pytest.raises(errors.ValidationError):
test_ctx.api.post(
test_ctx.context_factory(
input=real_input,
user=test_ctx.user_factory(rank='regular_user')))
@pytest.mark.parametrize('field', ['name', 'color'])
def test_missing_mandatory_field(test_ctx, field):
input = {
'name': 'meta',
'color': 'black',
}
del input[field]
with pytest.raises(errors.ValidationError):
test_ctx.api.post(
test_ctx.context_factory(
input=input,
user=test_ctx.user_factory(rank='regular_user')))
def test_trying_to_use_existing_name(test_ctx):
result = test_ctx.api.post(
test_ctx.context_factory(
input={'name': 'meta', 'color': 'black'},
user=test_ctx.user_factory(rank='regular_user')))
with pytest.raises(tag_categories.TagCategoryAlreadyExistsError):
result = test_ctx.api.post(
test_ctx.context_factory(
input={'name': 'meta', 'color': 'black'},
user=test_ctx.user_factory(rank='regular_user')))
with pytest.raises(tag_categories.TagCategoryAlreadyExistsError):
result = test_ctx.api.post(
test_ctx.context_factory(
input={'name': 'META', 'color': 'black'},
user=test_ctx.user_factory(rank='regular_user')))
def test_trying_to_create_tag_with_invalid_color(test_ctx):
with pytest.raises(tag_categories.InvalidTagCategoryColorError):
test_ctx.api.post(
test_ctx.context_factory(
input={'name': 'meta', 'color': 'a' * 100},
user=test_ctx.user_factory(rank='regular_user')))
assert db.session.query(db.TagCategory).filter_by(name='meta').count() == 0
def test_trying_to_create_tag_without_privileges(test_ctx):
with pytest.raises(errors.AuthError):
test_ctx.api.post(
test_ctx.context_factory(
input={'name': 'meta', 'colro': 'black'},
user=test_ctx.user_factory(rank='anonymous')))

View File

@ -0,0 +1,69 @@
import pytest
import os
from datetime import datetime
from szurubooru import api, config, db, errors
from szurubooru.util import misc, tags, tag_categories
@pytest.fixture
def test_ctx(
tmpdir,
config_injector,
context_factory,
tag_factory,
tag_category_factory,
user_factory):
config_injector({
'data_dir': str(tmpdir),
'privileges': {
'tag_categories:delete': 'regular_user',
},
'ranks': ['anonymous', 'regular_user'],
})
ret = misc.dotdict()
ret.context_factory = context_factory
ret.user_factory = user_factory
ret.tag_factory = tag_factory
ret.tag_category_factory = tag_category_factory
ret.api = api.TagCategoryDetailApi()
return ret
def test_deleting(test_ctx):
db.session().add(test_ctx.tag_category_factory(name='category'))
db.session().commit()
result = test_ctx.api.delete(
test_ctx.context_factory(
user=test_ctx.user_factory(rank='regular_user')),
'category')
assert result == {}
assert db.session().query(db.TagCategory).count() == 0
assert os.path.exists(os.path.join(config.config['data_dir'], 'tags.json'))
def test_deleting_with_usages(test_ctx, tag_factory):
category = test_ctx.tag_category_factory(name='category')
db.session().add(category)
db.session().flush()
tag = test_ctx.tag_factory(names=['tag'], category=category)
db.session().add(tag)
db.session().commit()
with pytest.raises(tag_categories.TagCategoryIsInUseError):
test_ctx.api.delete(
test_ctx.context_factory(
user=test_ctx.user_factory(rank='regular_user')),
'category')
assert db.session().query(db.TagCategory).count() == 1
def test_deleting_non_existing(test_ctx):
with pytest.raises(tag_categories.TagCategoryNotFoundError):
test_ctx.api.delete(
test_ctx.context_factory(
user=test_ctx.user_factory(rank='regular_user')), 'bad')
def test_deleting_without_privileges(test_ctx):
db.session().add(test_ctx.tag_category_factory(name='category'))
db.session().commit()
with pytest.raises(errors.AuthError):
test_ctx.api.delete(
test_ctx.context_factory(
user=test_ctx.user_factory(rank='anonymous')),
'category')
assert db.session().query(db.TagCategory).count() == 1

View File

@ -0,0 +1,61 @@
import datetime
import pytest
from szurubooru import api, db, errors
from szurubooru.util import misc, tag_categories
@pytest.fixture
def test_ctx(
context_factory, config_injector, user_factory, tag_category_factory):
config_injector({
'privileges': {
'tag_categories:list': 'regular_user',
'tag_categories:view': 'regular_user',
},
'thumbnails': {'avatar_width': 200},
'ranks': ['anonymous', 'regular_user', 'mod', 'admin'],
'rank_names': {'regular_user': 'Peasant'},
})
ret = misc.dotdict()
ret.context_factory = context_factory
ret.user_factory = user_factory
ret.tag_category_factory = tag_category_factory
ret.list_api = api.TagCategoryListApi()
ret.detail_api = api.TagCategoryDetailApi()
return ret
def test_retrieving_multiple(test_ctx):
db.session().add_all([
test_ctx.tag_category_factory(name='c1'),
test_ctx.tag_category_factory(name='c2'),
])
result = test_ctx.list_api.get(
test_ctx.context_factory(
user=test_ctx.user_factory(rank='regular_user')))
assert [cat['name'] for cat in result['tagCategories']] == ['c1', 'c2']
def test_retrieving_single(test_ctx):
db.session().add(test_ctx.tag_category_factory(name='cat'))
result = test_ctx.detail_api.get(
test_ctx.context_factory(
user=test_ctx.user_factory(rank='regular_user')),
'cat')
assert result == {
'tagCategory': {
'name': 'cat',
'color': 'dummy',
}
}
def test_retrieving_non_existing(test_ctx):
with pytest.raises(tag_categories.TagCategoryNotFoundError):
test_ctx.detail_api.get(
test_ctx.context_factory(
user=test_ctx.user_factory(rank='regular_user')),
'-')
def test_retrieving_single_without_privileges(test_ctx):
with pytest.raises(errors.AuthError):
test_ctx.detail_api.get(
test_ctx.context_factory(
user=test_ctx.user_factory(rank='anonymous')),
'-')

View File

@ -0,0 +1,133 @@
import os
import pytest
from szurubooru import api, config, db, errors
from szurubooru.util import misc, tag_categories
@pytest.fixture
def test_ctx(
tmpdir,
config_injector,
context_factory,
user_factory,
tag_category_factory):
config_injector({
'data_dir': str(tmpdir),
'tag_category_name_regex': '^[^!]*$',
'ranks': ['anonymous', 'regular_user'],
'privileges': {
'tag_categories:edit:name': 'regular_user',
'tag_categories:edit:color': 'regular_user',
},
})
ret = misc.dotdict()
ret.context_factory = context_factory
ret.user_factory = user_factory
ret.tag_category_factory = tag_category_factory
ret.api = api.TagCategoryDetailApi()
return ret
def test_simple_updating(test_ctx):
category = test_ctx.tag_category_factory(name='name', color='black')
db.session().add(category)
db.session().commit()
result = test_ctx.api.put(
test_ctx.context_factory(
input={
'name': 'changed',
'color': 'white',
},
user=test_ctx.user_factory(rank='regular_user')),
'name')
assert result == {
'tagCategory': {
'name': 'changed',
'color': 'white',
}
}
assert tag_categories.get_category_by_name('name') is None
category = tag_categories.get_category_by_name('changed')
assert category is not None
assert category.name == 'changed'
assert category.color == 'white'
assert os.path.exists(os.path.join(config.config['data_dir'], 'tags.json'))
@pytest.mark.parametrize('input', [
{'name': None},
{'name': ''},
{'name': '!bad'},
{'color': None},
{'color': ''},
])
def test_invalid_inputs(test_ctx, input):
db.session().add(test_ctx.tag_category_factory(name='meta', color='black'))
db.session().commit()
with pytest.raises(tag_categories.InvalidTagCategoryNameError):
test_ctx.api.put(
test_ctx.context_factory(
input=input,
user=test_ctx.user_factory(rank='regular_user')),
'meta')
@pytest.mark.parametrize('field', ['name', 'color'])
def test_missing_optional_field(test_ctx, tmpdir, field):
db.session().add(test_ctx.tag_category_factory(name='name', color='black'))
db.session().commit()
input = {
'name': 'changed',
'color': 'white',
}
del input[field]
result = test_ctx.api.put(
test_ctx.context_factory(
input=input,
user=test_ctx.user_factory(rank='regular_user')),
'name')
assert result is not None
def test_trying_to_update_non_existing(test_ctx):
with pytest.raises(tag_categories.TagCategoryNotFoundError):
test_ctx.api.put(
test_ctx.context_factory(
input={'name': ['dummy']},
user=test_ctx.user_factory(rank='regular_user')),
'bad')
@pytest.mark.parametrize('new_name', ['cat', 'CAT'])
def test_reusing_own_name(test_ctx, new_name):
db.session().add(test_ctx.tag_category_factory(name='cat', color='black'))
db.session().commit()
result = test_ctx.api.put(
test_ctx.context_factory(
input={'name': new_name},
user=test_ctx.user_factory(rank='regular_user')),
'cat')
assert result['tagCategory']['name'] == new_name
category = tag_categories.get_category_by_name('cat')
assert category.name == new_name
@pytest.mark.parametrize('dup_name', ['cat1', 'CAT1'])
def test_trying_to_use_existing_name(test_ctx, dup_name):
db.session().add_all([
test_ctx.tag_category_factory(name='cat1', color='black'),
test_ctx.tag_category_factory(name='cat2', color='black')])
db.session().commit()
with pytest.raises(tag_categories.TagCategoryAlreadyExistsError):
test_ctx.api.put(
test_ctx.context_factory(
input={'name': dup_name},
user=test_ctx.user_factory(rank='regular_user')),
'cat2')
@pytest.mark.parametrize('input', [
{'name': 'whatever'},
{'color': 'whatever'},
])
def test_trying_to_update_tag_without_privileges(test_ctx, input):
db.session().add(test_ctx.tag_category_factory(name='dummy'))
db.session().commit()
with pytest.raises(errors.AuthError):
test_ctx.api.put(
test_ctx.context_factory(
input=input,
user=test_ctx.user_factory(rank='anonymous')),
'dummy')

View File

@ -38,16 +38,6 @@ def test_deleting(test_ctx):
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')) assert os.path.exists(os.path.join(config.config['data_dir'], 'tags.json'))
def test_deleting_without_privileges(test_ctx):
test_ctx.session.add(test_ctx.tag_factory(names=['tag']))
test_ctx.session.commit()
with pytest.raises(errors.AuthError):
test_ctx.api.delete(
test_ctx.context_factory(
user=test_ctx.user_factory(rank='anonymous')),
'tag')
assert test_ctx.session.query(db.Tag).count() == 1
def test_deleting_with_usages(test_ctx, post_factory): def test_deleting_with_usages(test_ctx, post_factory):
tag = test_ctx.tag_factory(names=['tag']) tag = test_ctx.tag_factory(names=['tag'])
post = post_factory() post = post_factory()
@ -66,3 +56,13 @@ def test_deleting_non_existing(test_ctx):
test_ctx.api.delete( test_ctx.api.delete(
test_ctx.context_factory( test_ctx.context_factory(
user=test_ctx.user_factory(rank='regular_user')), 'bad') user=test_ctx.user_factory(rank='regular_user')), 'bad')
def test_deleting_without_privileges(test_ctx):
test_ctx.session.add(test_ctx.tag_factory(names=['tag']))
test_ctx.session.commit()
with pytest.raises(errors.AuthError):
test_ctx.api.delete(
test_ctx.context_factory(
user=test_ctx.user_factory(rank='anonymous')),
'tag')
assert test_ctx.session.query(db.Tag).count() == 1

View File

@ -66,9 +66,19 @@ def user_factory():
return user return user
return factory return factory
@pytest.fixture
def tag_category_factory(session):
def factory(name='dummy', color='dummy'):
category = db.TagCategory()
category.name = name
category.color = color
return category
return factory
@pytest.fixture @pytest.fixture
def tag_factory(session): def tag_factory(session):
def factory(names=None, category_name='dummy'): def factory(names=None, category=None, category_name='dummy'):
if not category:
category = db.TagCategory(category_name) category = db.TagCategory(category_name)
session.add(category) session.add(category)
tag = db.Tag() tag = db.Tag()

View File

@ -0,0 +1,60 @@
import re
from szurubooru import config, db, errors
from szurubooru.util import misc
class TagCategoryNotFoundError(errors.NotFoundError): pass
class TagCategoryAlreadyExistsError(errors.ValidationError): pass
class TagCategoryIsInUseError(errors.ValidationError): pass
class InvalidTagCategoryNameError(errors.ValidationError): pass
class InvalidTagCategoryColorError(errors.ValidationError): pass
def _verify_name_validity(name):
name_regex = config.config['tag_category_name_regex']
if not re.match(name_regex, name):
raise InvalidTagCategoryNameError(
'Name must satisfy regex %r.' % name_regex)
def create_category(name, color):
category = db.TagCategory()
update_name(category, name)
update_color(category, color)
return category
def update_name(category, name):
if not name:
raise InvalidTagCategoryNameError('Name cannot be empty.')
expr = db.TagCategory.name.ilike(name)
if category.tag_category_id:
expr = expr & (db.TagCategory.tag_category_id != category.tag_category_id)
already_exists = db.session().query(db.TagCategory).filter(expr).count() > 0
if already_exists:
raise TagCategoryAlreadyExistsError(
'A category with this name already exists.')
if misc.value_exceeds_column_size(name, db.TagCategory.name):
raise InvalidTagCategoryNameError('Name is too long.')
_verify_name_validity(name)
category.name = name
def update_color(category, color):
if not color:
raise InvalidTagCategoryNameError('Color cannot be empty.')
if misc.value_exceeds_column_size(color, db.TagCategory.color):
raise InvalidTagCategoryColorError('Color is too long.')
category.color = color
def get_category_by_name(name):
return db.session.query(db.TagCategory) \
.filter(db.TagCategory.name.ilike(name)) \
.first()
def get_all_category_names():
return [row[0] for row in db.session.query(db.TagCategory.name).all()]
def get_all_categories():
return db.session.query(db.TagCategory).all()
def get_default_category():
return db.session().query(db.TagCategory) \
.order_by(db.TagCategory.tag_category_id.asc()) \
.limit(1) \
.one()

View File

@ -4,7 +4,7 @@ import os
import re import re
import sqlalchemy import sqlalchemy
from szurubooru import config, db, errors from szurubooru import config, db, errors
from szurubooru.util import misc from szurubooru.util import misc, tag_categories
class TagNotFoundError(errors.NotFoundError): pass class TagNotFoundError(errors.NotFoundError): pass
class TagAlreadyExistsError(errors.ValidationError): pass class TagAlreadyExistsError(errors.ValidationError): pass
@ -51,12 +51,6 @@ def get_tag_by_name(name):
.filter(db.TagName.name.ilike(name)) \ .filter(db.TagName.name.ilike(name)) \
.first() .first()
def get_default_category():
return db.session().query(db.TagCategory) \
.order_by(db.TagCategory.tag_category_id.asc()) \
.limit(1) \
.one()
def get_tags_by_names(names): def get_tags_by_names(names):
names = misc.icase_unique(names) names = misc.icase_unique(names)
if len(names) == 0: if len(names) == 0:
@ -81,7 +75,7 @@ def get_or_create_tags_by_names(names):
if not found: if not found:
new_tag = create_tag( new_tag = create_tag(
names=[name], names=[name],
category_name=get_default_category().name, category_name=tag_categories.get_default_category().name,
suggestions=[], suggestions=[],
implications=[]) implications=[])
db.session().add(new_tag) db.session().add(new_tag)
@ -103,8 +97,7 @@ def update_category_name(tag, category_name):
.filter(db.TagCategory.name == category_name) \ .filter(db.TagCategory.name == category_name) \
.first() .first()
if not category: if not category:
category_names = [ category_names = tag_categories.get_all_category_names()
name[0] for name in session.query(db.TagCategory.name).all()]
raise InvalidTagCategoryError( raise InvalidTagCategoryError(
'Category %r is invalid. Valid categories: %r.' % ( 'Category %r is invalid. Valid categories: %r.' % (
category_name, category_names)) category_name, category_names))