server/general: cosmetic fixes
This commit is contained in:
parent
508cb6e7ab
commit
f3bb6c28a1
12 changed files with 202 additions and 201 deletions
|
@ -2,8 +2,8 @@ import falcon
|
||||||
from szurubooru import errors
|
from szurubooru import errors
|
||||||
from szurubooru.func import net
|
from szurubooru.func import net
|
||||||
|
|
||||||
def _lower_first(input):
|
def _lower_first(source):
|
||||||
return input[0].lower() + input[1:]
|
return source[0].lower() + source[1:]
|
||||||
|
|
||||||
def _param_wrapper(func):
|
def _param_wrapper(func):
|
||||||
def wrapper(self, name, required=False, default=None, **kwargs):
|
def wrapper(self, name, required=False, default=None, **kwargs):
|
||||||
|
@ -11,10 +11,10 @@ def _param_wrapper(func):
|
||||||
value = self.input[name]
|
value = self.input[name]
|
||||||
try:
|
try:
|
||||||
value = func(self, value, **kwargs)
|
value = func(self, value, **kwargs)
|
||||||
except errors.InvalidParameterError as e:
|
except errors.InvalidParameterError as ex:
|
||||||
raise errors.InvalidParameterError(
|
raise errors.InvalidParameterError(
|
||||||
'Parameter %r is invalid: %s' % (
|
'Parameter %r is invalid: %s' % (
|
||||||
name, _lower_first(str(e))))
|
name, _lower_first(str(ex))))
|
||||||
return value
|
return value
|
||||||
if not required:
|
if not required:
|
||||||
return default
|
return default
|
||||||
|
|
|
@ -1,10 +1,4 @@
|
||||||
import sqlalchemy
|
from szurubooru.search import tokens
|
||||||
from szurubooru import db, errors
|
|
||||||
from szurubooru.func import util
|
|
||||||
from szurubooru.search import criteria, tokens
|
|
||||||
|
|
||||||
def wildcard_transformer(value):
|
|
||||||
return value.replace('*', '%')
|
|
||||||
|
|
||||||
class BaseSearchConfig(object):
|
class BaseSearchConfig(object):
|
||||||
SORT_ASC = tokens.SortToken.SORT_ASC
|
SORT_ASC = tokens.SortToken.SORT_ASC
|
||||||
|
@ -34,127 +28,3 @@ class BaseSearchConfig(object):
|
||||||
@property
|
@property
|
||||||
def sort_columns(self):
|
def sort_columns(self):
|
||||||
return {}
|
return {}
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def _apply_num_criterion_to_column(column, criterion):
|
|
||||||
'''
|
|
||||||
Decorate SQLAlchemy filter on given column using supplied criterion.
|
|
||||||
'''
|
|
||||||
try:
|
|
||||||
if isinstance(criterion, criteria.PlainCriterion):
|
|
||||||
expr = column == int(criterion.value)
|
|
||||||
elif isinstance(criterion, criteria.ArrayCriterion):
|
|
||||||
expr = column.in_(int(value) for value in criterion.values)
|
|
||||||
elif isinstance(criterion, criteria.RangedCriterion):
|
|
||||||
assert criterion.min_value != '' \
|
|
||||||
or criterion.max_value != ''
|
|
||||||
if criterion.min_value != '' and criterion.max_value != '':
|
|
||||||
expr = column.between(
|
|
||||||
int(criterion.min_value), int(criterion.max_value))
|
|
||||||
elif criterion.min_value != '':
|
|
||||||
expr = column >= int(criterion.min_value)
|
|
||||||
elif criterion.max_value != '':
|
|
||||||
expr = column <= int(criterion.max_value)
|
|
||||||
else:
|
|
||||||
assert False
|
|
||||||
except ValueError:
|
|
||||||
raise errors.SearchError(
|
|
||||||
'Criterion value %r must be a number.' % (criterion,))
|
|
||||||
return expr
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def _create_num_filter(column):
|
|
||||||
def wrapper(query, criterion, negated):
|
|
||||||
expr = BaseSearchConfig._apply_num_criterion_to_column(
|
|
||||||
column, criterion)
|
|
||||||
if negated:
|
|
||||||
expr = ~expr
|
|
||||||
return query.filter(expr)
|
|
||||||
return wrapper
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def _apply_str_criterion_to_column(
|
|
||||||
column, criterion, transformer=wildcard_transformer):
|
|
||||||
'''
|
|
||||||
Decorate SQLAlchemy filter on given column using supplied criterion.
|
|
||||||
'''
|
|
||||||
if isinstance(criterion, criteria.PlainCriterion):
|
|
||||||
expr = column.ilike(transformer(criterion.value))
|
|
||||||
elif isinstance(criterion, criteria.ArrayCriterion):
|
|
||||||
expr = sqlalchemy.sql.false()
|
|
||||||
for value in criterion.values:
|
|
||||||
expr = expr | column.ilike(transformer(value))
|
|
||||||
elif isinstance(criterion, criteria.RangedCriterion):
|
|
||||||
raise errors.SearchError(
|
|
||||||
'Composite token %r is invalid in this context.' % (criterion,))
|
|
||||||
else:
|
|
||||||
assert False
|
|
||||||
return expr
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def _create_str_filter(column, transformer=wildcard_transformer):
|
|
||||||
def wrapper(query, criterion, negated):
|
|
||||||
expr = BaseSearchConfig._apply_str_criterion_to_column(
|
|
||||||
column, criterion, transformer)
|
|
||||||
if negated:
|
|
||||||
expr = ~expr
|
|
||||||
return query.filter(expr)
|
|
||||||
return wrapper
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def _apply_date_criterion_to_column(column, criterion):
|
|
||||||
'''
|
|
||||||
Decorate SQLAlchemy filter on given column using supplied criterion.
|
|
||||||
Parse the datetime inside the criterion.
|
|
||||||
'''
|
|
||||||
if isinstance(criterion, criteria.PlainCriterion):
|
|
||||||
min_date, max_date = util.parse_time_range(criterion.value)
|
|
||||||
expr = column.between(min_date, max_date)
|
|
||||||
elif isinstance(criterion, criteria.ArrayCriterion):
|
|
||||||
expr = sqlalchemy.sql.false()
|
|
||||||
for value in criterion.values:
|
|
||||||
min_date, max_date = util.parse_time_range(value)
|
|
||||||
expr = expr | column.between(min_date, max_date)
|
|
||||||
elif isinstance(criterion, criteria.RangedCriterion):
|
|
||||||
assert criterion.min_value or criterion.max_value
|
|
||||||
if criterion.min_value and criterion.max_value:
|
|
||||||
min_date = util.parse_time_range(criterion.min_value)[0]
|
|
||||||
max_date = util.parse_time_range(criterion.max_value)[1]
|
|
||||||
expr = column.between(min_date, max_date)
|
|
||||||
elif criterion.min_value:
|
|
||||||
min_date = util.parse_time_range(criterion.min_value)[0]
|
|
||||||
expr = column >= min_date
|
|
||||||
elif criterion.max_value:
|
|
||||||
max_date = util.parse_time_range(criterion.max_value)[1]
|
|
||||||
expr = column <= max_date
|
|
||||||
else:
|
|
||||||
assert False
|
|
||||||
return expr
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def _create_date_filter(column):
|
|
||||||
def wrapper(query, criterion, negated):
|
|
||||||
expr = BaseSearchConfig._apply_date_criterion_to_column(
|
|
||||||
column, criterion)
|
|
||||||
if negated:
|
|
||||||
expr = ~expr
|
|
||||||
return query.filter(expr)
|
|
||||||
return wrapper
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def _create_subquery_filter(
|
|
||||||
left_id_column,
|
|
||||||
right_id_column,
|
|
||||||
filter_column,
|
|
||||||
filter_factory,
|
|
||||||
subquery_decorator=None):
|
|
||||||
filter_func = filter_factory(filter_column)
|
|
||||||
def wrapper(query, criterion, negated):
|
|
||||||
subquery = db.session.query(right_id_column.label('foreign_id'))
|
|
||||||
if subquery_decorator:
|
|
||||||
subquery = subquery_decorator(subquery)
|
|
||||||
subquery = subquery.options(sqlalchemy.orm.lazyload('*'))
|
|
||||||
subquery = filter_func(subquery, criterion, negated)
|
|
||||||
subquery = subquery.subquery('t')
|
|
||||||
return query.filter(left_id_column.in_(subquery))
|
|
||||||
return wrapper
|
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
from sqlalchemy.sql.expression import func
|
from sqlalchemy.sql.expression import func
|
||||||
from szurubooru import db
|
from szurubooru import db
|
||||||
|
from szurubooru.search.configs import util as search_util
|
||||||
from szurubooru.search.configs.base_search_config import BaseSearchConfig
|
from szurubooru.search.configs.base_search_config import BaseSearchConfig
|
||||||
|
|
||||||
class CommentSearchConfig(BaseSearchConfig):
|
class CommentSearchConfig(BaseSearchConfig):
|
||||||
|
@ -11,22 +12,22 @@ class CommentSearchConfig(BaseSearchConfig):
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def anonymous_filter(self):
|
def anonymous_filter(self):
|
||||||
return self._create_str_filter(db.Comment.text)
|
return search_util.create_str_filter(db.Comment.text)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def named_filters(self):
|
def named_filters(self):
|
||||||
return {
|
return {
|
||||||
'id': self._create_num_filter(db.Comment.comment_id),
|
'id': search_util.create_num_filter(db.Comment.comment_id),
|
||||||
'post': self._create_num_filter(db.Comment.post_id),
|
'post': search_util.create_num_filter(db.Comment.post_id),
|
||||||
'user': self._create_str_filter(db.User.name),
|
'user': search_util.create_str_filter(db.User.name),
|
||||||
'author': self._create_str_filter(db.User.name),
|
'author': search_util.create_str_filter(db.User.name),
|
||||||
'text': self._create_str_filter(db.Comment.text),
|
'text': search_util.create_str_filter(db.Comment.text),
|
||||||
'creation-date': self._create_date_filter(db.Comment.creation_time),
|
'creation-date': search_util.create_date_filter(db.Comment.creation_time),
|
||||||
'creation-time': self._create_date_filter(db.Comment.creation_time),
|
'creation-time': search_util.create_date_filter(db.Comment.creation_time),
|
||||||
'last-edit-date': self._create_date_filter(db.Comment.last_edit_time),
|
'last-edit-date': search_util.create_date_filter(db.Comment.last_edit_time),
|
||||||
'last-edit-time': self._create_date_filter(db.Comment.last_edit_time),
|
'last-edit-time': search_util.create_date_filter(db.Comment.last_edit_time),
|
||||||
'edit-date': self._create_date_filter(db.Comment.last_edit_time),
|
'edit-date': search_util.create_date_filter(db.Comment.last_edit_time),
|
||||||
'edit-time': self._create_date_filter(db.Comment.last_edit_time),
|
'edit-time': search_util.create_date_filter(db.Comment.last_edit_time),
|
||||||
}
|
}
|
||||||
|
|
||||||
@property
|
@property
|
||||||
|
|
|
@ -3,6 +3,7 @@ from sqlalchemy.sql.expression import func
|
||||||
from szurubooru import db, errors
|
from szurubooru import db, errors
|
||||||
from szurubooru.func import util
|
from szurubooru.func import util
|
||||||
from szurubooru.search import criteria, tokens
|
from szurubooru.search import criteria, tokens
|
||||||
|
from szurubooru.search.configs import util as search_util
|
||||||
from szurubooru.search.configs.base_search_config import BaseSearchConfig
|
from szurubooru.search.configs.base_search_config import BaseSearchConfig
|
||||||
|
|
||||||
def _enum_transformer(available_values, value):
|
def _enum_transformer(available_values, value):
|
||||||
|
@ -45,7 +46,7 @@ def _create_score_filter(score):
|
||||||
user_alias = aliased(db.User)
|
user_alias = aliased(db.User)
|
||||||
score_alias = aliased(db.PostScore)
|
score_alias = aliased(db.PostScore)
|
||||||
expr = score_alias.score == score
|
expr = score_alias.score == score
|
||||||
expr = expr & BaseSearchConfig._apply_str_criterion_to_column(
|
expr = expr & search_util.apply_str_criterion_to_column(
|
||||||
user_alias.name, criterion)
|
user_alias.name, criterion)
|
||||||
if negated:
|
if negated:
|
||||||
expr = ~expr
|
expr = ~expr
|
||||||
|
@ -106,69 +107,69 @@ class PostSearchConfig(BaseSearchConfig):
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def anonymous_filter(self):
|
def anonymous_filter(self):
|
||||||
return self._create_subquery_filter(
|
return search_util.create_subquery_filter(
|
||||||
db.Post.post_id,
|
db.Post.post_id,
|
||||||
db.PostTag.post_id,
|
db.PostTag.post_id,
|
||||||
db.TagName.name,
|
db.TagName.name,
|
||||||
self._create_str_filter,
|
search_util.create_str_filter,
|
||||||
lambda subquery: subquery.join(db.Tag).join(db.TagName))
|
lambda subquery: subquery.join(db.Tag).join(db.TagName))
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def named_filters(self):
|
def named_filters(self):
|
||||||
return util.unalias_dict({
|
return util.unalias_dict({
|
||||||
'id': self._create_num_filter(db.Post.post_id),
|
'id': search_util.create_num_filter(db.Post.post_id),
|
||||||
'tag': self._create_subquery_filter(
|
'tag': search_util.create_subquery_filter(
|
||||||
db.Post.post_id,
|
db.Post.post_id,
|
||||||
db.PostTag.post_id,
|
db.PostTag.post_id,
|
||||||
db.TagName.name,
|
db.TagName.name,
|
||||||
self._create_str_filter,
|
search_util.create_str_filter,
|
||||||
lambda subquery: subquery.join(db.Tag).join(db.TagName)),
|
lambda subquery: subquery.join(db.Tag).join(db.TagName)),
|
||||||
'score': self._create_num_filter(db.Post.score),
|
'score': search_util.create_num_filter(db.Post.score),
|
||||||
('uploader', 'upload', 'submit'):
|
('uploader', 'upload', 'submit'):
|
||||||
self._create_subquery_filter(
|
search_util.create_subquery_filter(
|
||||||
db.Post.user_id,
|
db.Post.user_id,
|
||||||
db.User.user_id,
|
db.User.user_id,
|
||||||
db.User.name,
|
db.User.name,
|
||||||
self._create_str_filter),
|
search_util.create_str_filter),
|
||||||
'comment': self._create_subquery_filter(
|
'comment': search_util.create_subquery_filter(
|
||||||
db.Post.post_id,
|
db.Post.post_id,
|
||||||
db.Comment.post_id,
|
db.Comment.post_id,
|
||||||
db.User.name,
|
db.User.name,
|
||||||
self._create_str_filter,
|
search_util.create_str_filter,
|
||||||
lambda subquery: subquery.join(db.User)),
|
lambda subquery: subquery.join(db.User)),
|
||||||
'fav': self._create_subquery_filter(
|
'fav': search_util.create_subquery_filter(
|
||||||
db.Post.post_id,
|
db.Post.post_id,
|
||||||
db.PostFavorite.post_id,
|
db.PostFavorite.post_id,
|
||||||
db.User.name,
|
db.User.name,
|
||||||
self._create_str_filter,
|
search_util.create_str_filter,
|
||||||
lambda subquery: subquery.join(db.User)),
|
lambda subquery: subquery.join(db.User)),
|
||||||
'liked': _create_score_filter(1),
|
'liked': _create_score_filter(1),
|
||||||
'disliked': _create_score_filter(-1),
|
'disliked': _create_score_filter(-1),
|
||||||
'tag-count': self._create_num_filter(db.Post.tag_count),
|
'tag-count': search_util.create_num_filter(db.Post.tag_count),
|
||||||
'comment-count': self._create_num_filter(db.Post.comment_count),
|
'comment-count': search_util.create_num_filter(db.Post.comment_count),
|
||||||
'fav-count': self._create_num_filter(db.Post.favorite_count),
|
'fav-count': search_util.create_num_filter(db.Post.favorite_count),
|
||||||
'note-count': self._create_num_filter(db.Post.note_count),
|
'note-count': search_util.create_num_filter(db.Post.note_count),
|
||||||
'feature-count': self._create_num_filter(db.Post.feature_count),
|
'feature-count': search_util.create_num_filter(db.Post.feature_count),
|
||||||
'type': self._create_str_filter(db.Post.type, _type_transformer),
|
'type': search_util.create_str_filter(db.Post.type, _type_transformer),
|
||||||
'file-size': self._create_num_filter(db.Post.file_size),
|
'file-size': search_util.create_num_filter(db.Post.file_size),
|
||||||
('image-width', 'width'):
|
('image-width', 'width'):
|
||||||
self._create_num_filter(db.Post.canvas_width),
|
search_util.create_num_filter(db.Post.canvas_width),
|
||||||
('image-height', 'height'):
|
('image-height', 'height'):
|
||||||
self._create_num_filter(db.Post.canvas_height),
|
search_util.create_num_filter(db.Post.canvas_height),
|
||||||
('image-area', 'area'):
|
('image-area', 'area'):
|
||||||
self._create_num_filter(db.Post.canvas_area),
|
search_util.create_num_filter(db.Post.canvas_area),
|
||||||
('creation-date', 'creation-time', 'date', 'time'):
|
('creation-date', 'creation-time', 'date', 'time'):
|
||||||
self._create_date_filter(db.Post.creation_time),
|
search_util.create_date_filter(db.Post.creation_time),
|
||||||
('last-edit-date', 'last-edit-time', 'edit-date', 'edit-time'):
|
('last-edit-date', 'last-edit-time', 'edit-date', 'edit-time'):
|
||||||
self._create_date_filter(db.Post.last_edit_time),
|
search_util.create_date_filter(db.Post.last_edit_time),
|
||||||
('comment-date', 'comment-time'):
|
('comment-date', 'comment-time'):
|
||||||
self._create_date_filter(db.Post.last_comment_edit_time),
|
search_util.create_date_filter(db.Post.last_comment_edit_time),
|
||||||
('fav-date', 'fav-time'):
|
('fav-date', 'fav-time'):
|
||||||
self._create_date_filter(db.Post.last_favorite_time),
|
search_util.create_date_filter(db.Post.last_favorite_time),
|
||||||
('feature-date', 'feature-time'):
|
('feature-date', 'feature-time'):
|
||||||
self._create_date_filter(db.Post.last_feature_time),
|
search_util.create_date_filter(db.Post.last_feature_time),
|
||||||
('safety', 'rating'):
|
('safety', 'rating'):
|
||||||
self._create_str_filter(db.Post.safety, _safety_transformer),
|
search_util.create_str_filter(db.Post.safety, _safety_transformer),
|
||||||
})
|
})
|
||||||
|
|
||||||
@property
|
@property
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
from szurubooru import db
|
from szurubooru import db
|
||||||
|
from szurubooru.search.configs import util as search_util
|
||||||
from szurubooru.search.configs.base_search_config import BaseSearchConfig
|
from szurubooru.search.configs.base_search_config import BaseSearchConfig
|
||||||
|
|
||||||
class SnapshotSearchConfig(BaseSearchConfig):
|
class SnapshotSearchConfig(BaseSearchConfig):
|
||||||
|
@ -11,10 +12,10 @@ class SnapshotSearchConfig(BaseSearchConfig):
|
||||||
@property
|
@property
|
||||||
def named_filters(self):
|
def named_filters(self):
|
||||||
return {
|
return {
|
||||||
'type': self._create_str_filter(db.Snapshot.resource_type),
|
'type': search_util.create_str_filter(db.Snapshot.resource_type),
|
||||||
'id': self._create_str_filter(db.Snapshot.resource_repr),
|
'id': search_util.create_str_filter(db.Snapshot.resource_repr),
|
||||||
'date': self._create_date_filter(db.Snapshot.creation_time),
|
'date': search_util.create_date_filter(db.Snapshot.creation_time),
|
||||||
'time': self._create_date_filter(db.Snapshot.creation_time),
|
'time': search_util.create_date_filter(db.Snapshot.creation_time),
|
||||||
'operation': self._create_str_filter(db.Snapshot.operation),
|
'operation': search_util.create_str_filter(db.Snapshot.operation),
|
||||||
'user': self._create_str_filter(db.User.name),
|
'user': search_util.create_str_filter(db.User.name),
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,6 +2,7 @@ from sqlalchemy.orm import subqueryload
|
||||||
from sqlalchemy.sql.expression import func
|
from sqlalchemy.sql.expression import func
|
||||||
from szurubooru import db
|
from szurubooru import db
|
||||||
from szurubooru.func import util
|
from szurubooru.func import util
|
||||||
|
from szurubooru.search.configs import util as search_util
|
||||||
from szurubooru.search.configs.base_search_config import BaseSearchConfig
|
from szurubooru.search.configs.base_search_config import BaseSearchConfig
|
||||||
|
|
||||||
class TagSearchConfig(BaseSearchConfig):
|
class TagSearchConfig(BaseSearchConfig):
|
||||||
|
@ -23,33 +24,35 @@ class TagSearchConfig(BaseSearchConfig):
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def anonymous_filter(self):
|
def anonymous_filter(self):
|
||||||
return self._create_subquery_filter(
|
return search_util.create_subquery_filter(
|
||||||
db.Tag.tag_id,
|
db.Tag.tag_id,
|
||||||
db.TagName.tag_id,
|
db.TagName.tag_id,
|
||||||
db.TagName.name,
|
db.TagName.name,
|
||||||
self._create_str_filter)
|
search_util.create_str_filter)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def named_filters(self):
|
def named_filters(self):
|
||||||
return util.unalias_dict({
|
return util.unalias_dict({
|
||||||
'name': self._create_subquery_filter(
|
'name': search_util.create_subquery_filter(
|
||||||
db.Tag.tag_id,
|
db.Tag.tag_id,
|
||||||
db.TagName.tag_id,
|
db.TagName.tag_id,
|
||||||
db.TagName.name,
|
db.TagName.name,
|
||||||
self._create_str_filter),
|
search_util.create_str_filter),
|
||||||
'category': self._create_subquery_filter(
|
'category': search_util.create_subquery_filter(
|
||||||
db.Tag.category_id,
|
db.Tag.category_id,
|
||||||
db.TagCategory.tag_category_id,
|
db.TagCategory.tag_category_id,
|
||||||
db.TagCategory.name,
|
db.TagCategory.name,
|
||||||
self._create_str_filter),
|
search_util.create_str_filter),
|
||||||
('creation-date', 'creation-time'):
|
('creation-date', 'creation-time'):
|
||||||
self._create_date_filter(db.Tag.creation_time),
|
search_util.create_date_filter(db.Tag.creation_time),
|
||||||
('last-edit-date', 'last-edit-time', 'edit-date', 'edit-time'):
|
('last-edit-date', 'last-edit-time', 'edit-date', 'edit-time'):
|
||||||
self._create_date_filter(db.Tag.last_edit_time),
|
search_util.create_date_filter(db.Tag.last_edit_time),
|
||||||
('usage-count', 'post-count', 'usages'):
|
('usage-count', 'post-count', 'usages'):
|
||||||
self._create_num_filter(db.Tag.post_count),
|
search_util.create_num_filter(db.Tag.post_count),
|
||||||
'suggestion-count': self._create_num_filter(db.Tag.suggestion_count),
|
'suggestion-count':
|
||||||
'implication-count': self._create_num_filter(db.Tag.implication_count),
|
search_util.create_num_filter(db.Tag.suggestion_count),
|
||||||
|
'implication-count':
|
||||||
|
search_util.create_num_filter(db.Tag.implication_count),
|
||||||
})
|
})
|
||||||
|
|
||||||
@property
|
@property
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
from sqlalchemy.sql.expression import func
|
from sqlalchemy.sql.expression import func
|
||||||
from szurubooru import db
|
from szurubooru import db
|
||||||
|
from szurubooru.search.configs import util as search_util
|
||||||
from szurubooru.search.configs.base_search_config import BaseSearchConfig
|
from szurubooru.search.configs.base_search_config import BaseSearchConfig
|
||||||
|
|
||||||
class UserSearchConfig(BaseSearchConfig):
|
class UserSearchConfig(BaseSearchConfig):
|
||||||
|
@ -13,18 +14,18 @@ class UserSearchConfig(BaseSearchConfig):
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def anonymous_filter(self):
|
def anonymous_filter(self):
|
||||||
return self._create_str_filter(db.User.name)
|
return search_util.create_str_filter(db.User.name)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def named_filters(self):
|
def named_filters(self):
|
||||||
return {
|
return {
|
||||||
'name': self._create_str_filter(db.User.name),
|
'name': search_util.create_str_filter(db.User.name),
|
||||||
'creation-date': self._create_date_filter(db.User.creation_time),
|
'creation-date': search_util.create_date_filter(db.User.creation_time),
|
||||||
'creation-time': self._create_date_filter(db.User.creation_time),
|
'creation-time': search_util.create_date_filter(db.User.creation_time),
|
||||||
'last-login-date': self._create_date_filter(db.User.last_login_time),
|
'last-login-date': search_util.create_date_filter(db.User.last_login_time),
|
||||||
'last-login-time': self._create_date_filter(db.User.last_login_time),
|
'last-login-time': search_util.create_date_filter(db.User.last_login_time),
|
||||||
'login-date': self._create_date_filter(db.User.last_login_time),
|
'login-date': search_util.create_date_filter(db.User.last_login_time),
|
||||||
'login-time': self._create_date_filter(db.User.last_login_time),
|
'login-time': search_util.create_date_filter(db.User.last_login_time),
|
||||||
}
|
}
|
||||||
|
|
||||||
@property
|
@property
|
||||||
|
|
124
server/szurubooru/search/configs/util.py
Normal file
124
server/szurubooru/search/configs/util.py
Normal file
|
@ -0,0 +1,124 @@
|
||||||
|
import sqlalchemy
|
||||||
|
from szurubooru import db, errors
|
||||||
|
from szurubooru.func import util
|
||||||
|
from szurubooru.search import criteria
|
||||||
|
|
||||||
|
def wildcard_transformer(value):
|
||||||
|
return value.replace('*', '%')
|
||||||
|
|
||||||
|
def apply_num_criterion_to_column(column, criterion):
|
||||||
|
'''
|
||||||
|
Decorate SQLAlchemy filter on given column using supplied criterion.
|
||||||
|
'''
|
||||||
|
try:
|
||||||
|
if isinstance(criterion, criteria.PlainCriterion):
|
||||||
|
expr = column == int(criterion.value)
|
||||||
|
elif isinstance(criterion, criteria.ArrayCriterion):
|
||||||
|
expr = column.in_(int(value) for value in criterion.values)
|
||||||
|
elif isinstance(criterion, criteria.RangedCriterion):
|
||||||
|
assert criterion.min_value != '' \
|
||||||
|
or criterion.max_value != ''
|
||||||
|
if criterion.min_value != '' and criterion.max_value != '':
|
||||||
|
expr = column.between(
|
||||||
|
int(criterion.min_value), int(criterion.max_value))
|
||||||
|
elif criterion.min_value != '':
|
||||||
|
expr = column >= int(criterion.min_value)
|
||||||
|
elif criterion.max_value != '':
|
||||||
|
expr = column <= int(criterion.max_value)
|
||||||
|
else:
|
||||||
|
assert False
|
||||||
|
except ValueError:
|
||||||
|
raise errors.SearchError(
|
||||||
|
'Criterion value %r must be a number.' % (criterion,))
|
||||||
|
return expr
|
||||||
|
|
||||||
|
def create_num_filter(column):
|
||||||
|
def wrapper(query, criterion, negated):
|
||||||
|
expr = apply_num_criterion_to_column(
|
||||||
|
column, criterion)
|
||||||
|
if negated:
|
||||||
|
expr = ~expr
|
||||||
|
return query.filter(expr)
|
||||||
|
return wrapper
|
||||||
|
|
||||||
|
def apply_str_criterion_to_column(
|
||||||
|
column, criterion, transformer=wildcard_transformer):
|
||||||
|
'''
|
||||||
|
Decorate SQLAlchemy filter on given column using supplied criterion.
|
||||||
|
'''
|
||||||
|
if isinstance(criterion, criteria.PlainCriterion):
|
||||||
|
expr = column.ilike(transformer(criterion.value))
|
||||||
|
elif isinstance(criterion, criteria.ArrayCriterion):
|
||||||
|
expr = sqlalchemy.sql.false()
|
||||||
|
for value in criterion.values:
|
||||||
|
expr = expr | column.ilike(transformer(value))
|
||||||
|
elif isinstance(criterion, criteria.RangedCriterion):
|
||||||
|
raise errors.SearchError(
|
||||||
|
'Composite token %r is invalid in this context.' % (criterion,))
|
||||||
|
else:
|
||||||
|
assert False
|
||||||
|
return expr
|
||||||
|
|
||||||
|
def create_str_filter(column, transformer=wildcard_transformer):
|
||||||
|
def wrapper(query, criterion, negated):
|
||||||
|
expr = apply_str_criterion_to_column(
|
||||||
|
column, criterion, transformer)
|
||||||
|
if negated:
|
||||||
|
expr = ~expr
|
||||||
|
return query.filter(expr)
|
||||||
|
return wrapper
|
||||||
|
|
||||||
|
def apply_date_criterion_to_column(column, criterion):
|
||||||
|
'''
|
||||||
|
Decorate SQLAlchemy filter on given column using supplied criterion.
|
||||||
|
Parse the datetime inside the criterion.
|
||||||
|
'''
|
||||||
|
if isinstance(criterion, criteria.PlainCriterion):
|
||||||
|
min_date, max_date = util.parse_time_range(criterion.value)
|
||||||
|
expr = column.between(min_date, max_date)
|
||||||
|
elif isinstance(criterion, criteria.ArrayCriterion):
|
||||||
|
expr = sqlalchemy.sql.false()
|
||||||
|
for value in criterion.values:
|
||||||
|
min_date, max_date = util.parse_time_range(value)
|
||||||
|
expr = expr | column.between(min_date, max_date)
|
||||||
|
elif isinstance(criterion, criteria.RangedCriterion):
|
||||||
|
assert criterion.min_value or criterion.max_value
|
||||||
|
if criterion.min_value and criterion.max_value:
|
||||||
|
min_date = util.parse_time_range(criterion.min_value)[0]
|
||||||
|
max_date = util.parse_time_range(criterion.max_value)[1]
|
||||||
|
expr = column.between(min_date, max_date)
|
||||||
|
elif criterion.min_value:
|
||||||
|
min_date = util.parse_time_range(criterion.min_value)[0]
|
||||||
|
expr = column >= min_date
|
||||||
|
elif criterion.max_value:
|
||||||
|
max_date = util.parse_time_range(criterion.max_value)[1]
|
||||||
|
expr = column <= max_date
|
||||||
|
else:
|
||||||
|
assert False
|
||||||
|
return expr
|
||||||
|
|
||||||
|
def create_date_filter(column):
|
||||||
|
def wrapper(query, criterion, negated):
|
||||||
|
expr = apply_date_criterion_to_column(
|
||||||
|
column, criterion)
|
||||||
|
if negated:
|
||||||
|
expr = ~expr
|
||||||
|
return query.filter(expr)
|
||||||
|
return wrapper
|
||||||
|
|
||||||
|
def create_subquery_filter(
|
||||||
|
left_id_column,
|
||||||
|
right_id_column,
|
||||||
|
filter_column,
|
||||||
|
filter_factory,
|
||||||
|
subquery_decorator=None):
|
||||||
|
filter_func = filter_factory(filter_column)
|
||||||
|
def wrapper(query, criterion, negated):
|
||||||
|
subquery = db.session.query(right_id_column.label('foreign_id'))
|
||||||
|
if subquery_decorator:
|
||||||
|
subquery = subquery_decorator(subquery)
|
||||||
|
subquery = subquery.options(sqlalchemy.orm.lazyload('*'))
|
||||||
|
subquery = filter_func(subquery, criterion, negated)
|
||||||
|
subquery = subquery.subquery('t')
|
||||||
|
return query.filter(left_id_column.in_(subquery))
|
||||||
|
return wrapper
|
Loading…
Reference in a new issue