buku/bukuserver/filters.py

236 lines
7.4 KiB
Python
Raw Normal View History

Feature/server (#289) * new: dev: version flag fix: dev: pylint error * chg: dev: sort route !cosmetic * new: dev: custom BaseModelView for buku bookmark * new: dev: formatted entry * new: dev: url render mode * new: dev: bookmark edit !wip * chg: dev: use existing form for bookmark * chg: dev: form name !refactor * new: dev: enable details views * new: dev: views module * new: dev: tag model view * chg: dev: only split page_size defined * chg: dev: use SelectMultipleField * fix: dev: Bookmark tags choices * chg dev: configure tags * chg: dev: tag edit form !wip * chg: dev: update bookmark model * chg: dev: remove unused function * new: dev: add flask wtf and admin * chg: dev: use SimpleNamespace instead namedtuple * new: dev: delete tags feature * new: dev: chatty parameter for delete_tag_at_index * fix: dev: skip confirmation when deleting tag * fix: dev: when update bookmark model * new: dev: update tag * chg: dev: use parse_tags method * new: dev: select2 field for tagsfield * chg: dev: remove unused code * fix: dev: syntax * fix: dev: update bookmark model * chg: dev: change api based on flask-api * fix: dev: new tags on tag_detail PUT * chg: dev: raise error when parsing failed * chg: dev: move server required package * new: dev: create_model * chg: dev: override abstract method model view class * chg: dev: delete model for bookmark * fix: dev: pylint ignore !cosmetic * new: dev: filter for tag * chg: dev: more filter for TagModel * new: dev: new filter for tag model * chg: dev: deduplicate filter * fix: dev: pylint !cosmetic * chg: dev: generalize tag, bookmark filter * chg: dev: add filters for bookmark * fix: dev: not equal filter * new: dev: url basic filter * chg: dev: configure bookmark model view * chg: dev: reorder bookmark view method * new: dev: tags number filter * chg: dev: bookmark url with unknown scheme * new: dev: network handle api * new: dev: modal edit/create for bookmark * chg: dev: link tag bookmark tag search * fix: dev: empty tag contain search * chg: dev: buku search option * new: dev: buku search to filter * chg: dev: front page search * chg: dev: move Statistic page to views module * fix: dev: bookmark search * new: dev: title filter * fix: dev: statistic label * fix: dev: link on statistic page * chg: dev: strip search value * fix: dev: bookmark entry fix * fix: dev: netloc modal on * fix: dev: pylint !cosmetic * chg: dev: remove duplicate package * chg: dev: move admin to root * fix: dev: link on statistic page * chg: dev: pin pyyaml package
2018-06-28 09:04:35 -05:00
from enum import Enum
from flask_admin.model import filters
class BookmarkField(Enum):
ID = 0
URL = 1
TITLE = 2
TAGS = 3
DESCRIPTION = 4
def equal_func(query, value, index):
return filter(lambda x: x[index] == value, query)
def not_equal_func(query, value, index):
return filter(lambda x: x[index] != value, query)
def greater_func(query, value, index):
return filter(lambda x: x[index] > value, query)
def smaller_func(query, value, index):
return filter(lambda x: x[index] > value, query)
def in_list_func(query, value, index):
return filter(lambda x: x[index] in value, query)
def not_in_list_func(query, value, index):
return filter(lambda x: x[index] not in value, query)
def top_x_func(query, value, index):
items = sorted(set(x[index] for x in query), reverse=True)
top_x = items[:value]
return filter(lambda x: x[index] in top_x, query)
def bottom_x_func(query, value, index):
items = sorted(set(x[index] for x in query), reverse=False)
top_x = items[:value]
return filter(lambda x: x[index] in top_x, query)
class FilterType(Enum):
EQUAL = {'func': equal_func, 'text':'equals'}
NOT_EQUAL = {'func': not_equal_func, 'text':'not equal'}
GREATER = {'func': greater_func, 'text':'greater than'}
SMALLER = {'func': smaller_func, 'text':'smaller than'}
IN_LIST = {'func': in_list_func, 'text':'in list'}
NOT_IN_LIST = {'func': not_in_list_func, 'text':'not in list'}
TOP_X = {'func': top_x_func, 'text': 'top x'}
BOTTOM_X = {'func': bottom_x_func, 'text': 'bottom x'}
class BaseFilter(filters.BaseFilter):
def operation(self):
return getattr(self, 'operation_text')
def apply(self, query, value):
return getattr(self, 'apply_func')(query, value, getattr(self, 'index'))
class TagBaseFilter(BaseFilter):
def __init__(self, name, operation_text=None, apply_func=None, filter_type=None, options=None, data_type=None):
if operation_text in ('in list', 'not in list'):
super().__init__(name, options, data_type='select2-tags')
else:
super().__init__(name, options, data_type)
if name == 'name':
self.index = 0
elif name == 'usage_count':
self.index = 1
else:
raise ValueError('name: {}'.format(name))
self.filter_type = None
if filter_type:
self.apply_func = filter_type.value['func']
self.operation_text = filter_type.value['text']
self.filter_type = filter_type
else:
self.apply_func = apply_func
self.operation_text = operation_text
def clean(self, value):
if self.filter_type in (FilterType.IN_LIST, FilterType.NOT_IN_LIST) and self.name == 'usage_count':
value = [int(v.strip()) for v in value.split(',') if v.strip()]
elif self.filter_type in (FilterType.IN_LIST, FilterType.NOT_IN_LIST):
value = [v.strip() for v in value.split(',') if v.strip()]
elif self.name == 'usage_count':
value = int(value)
if self.filter_type in (FilterType.TOP_X, FilterType.BOTTOM_X) and value < 1:
raise ValueError
if isinstance(value, str):
return value.strip()
return value
class BookmarkBukuFilter(BaseFilter):
def __init__(self, *args, **kwargs):
self.keys = {
'all_keywords': 'match all',
'deep': 'deep',
'regex': 'regex'
}
for key, value in kwargs.items():
if key in self.keys and value:
setattr(self, key, value)
else:
setattr(self, key, False)
list(map(lambda x: kwargs.pop(x), self.keys))
super().__init__('buku', *args, **kwargs)
def operation(self):
parts = []
for key, value in self.keys.items():
if getattr(self, key):
parts.append(value)
if not parts:
return 'search'
return 'search ' + ', '.join(parts)
def apply(self, query, value):
return query
class BookmarkBaseFilter(BaseFilter):
def __init__(self, name, operation_text=None, apply_func=None, filter_type=None, options=None, data_type=None):
if operation_text in ('in list', 'not in list'):
super().__init__(name, options, data_type='select2-tags')
else:
super().__init__(name, options, data_type)
bm_fields_dict = {x.name.lower(): x.value for x in BookmarkField}
if name in bm_fields_dict:
self.index = bm_fields_dict[name]
else:
raise ValueError('name: {}'.format(name))
self.filter_type = None
if filter_type:
self.apply_func = filter_type.value['func']
self.operation_text = filter_type.value['text']
else:
self.apply_func = apply_func
self.operation_text = operation_text
def clean(self, value):
if self.filter_type in (FilterType.IN_LIST, FilterType.NOT_IN_LIST) and self.name == BookmarkField.ID.name.lower():
value = [int(v.strip()) for v in value.split(',') if v.strip()]
elif self.filter_type in (FilterType.IN_LIST, FilterType.NOT_IN_LIST):
value = [v.strip() for v in value.split(',') if v.strip()]
elif self.name == BookmarkField.ID.name.lower():
value = int(value)
if self.filter_type in (FilterType.TOP_X, FilterType.BOTTOM_X) and value < 1:
raise ValueError
if isinstance(value, str):
return value.strip()
return value
class BookmarkTagNumberEqualFilter(BookmarkBaseFilter):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
def apply_func(query, value, index):
for item in query:
tags = [tag for tag in item[index].split(',') if tag]
if len(tags) == value:
yield item
self.apply_func = apply_func
def clean(self, value):
value = int(value)
if value < 0:
raise ValueError
return value
class BookmarkTagNumberGreaterFilter(BookmarkTagNumberEqualFilter):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
def apply_func(query, value, index):
for item in query:
tags = [tag for tag in item[index].split(',') if tag]
if len(tags) > value:
yield item
self.apply_func = apply_func
class BookmarkTagNumberNotEqualFilter(BookmarkTagNumberEqualFilter):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
def apply_func(query, value, index):
for item in query:
tags = [tag for tag in item[index].split(',') if tag]
if len(tags) != value:
yield item
self. apply_func = apply_func
class BookmarkTagNumberSmallerFilter(BookmarkBaseFilter):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
def apply_func(query, value, index):
for item in query:
tags = [tag for tag in item[index].split(',') if tag]
if len(tags) < value:
yield item
self.apply_func = apply_func
def clean(self, value):
value = int(value)
if value < 1:
raise ValueError
return value