from enum import Enum
from flask_admin.model import filters
from bukuserver import _l, _key
[docs]
class BookmarkField(Enum):
ID = 0
URL = 1
TITLE = 2
TAGS = 3
DESCRIPTION = 4
[docs]
def equal_func(query, value, index):
return filter(lambda x: x[index] == value, query)
[docs]
def not_equal_func(query, value, index):
return filter(lambda x: x[index] != value, query)
[docs]
def contains_func(query, value, index):
return filter(lambda x: value in x[index], query)
[docs]
def not_contains_func(query, value, index):
return filter(lambda x: value not in x[index], query)
[docs]
def greater_func(query, value, index):
return filter(lambda x: x[index] > value, query)
[docs]
def smaller_func(query, value, index):
return filter(lambda x: x[index] < value, query)
[docs]
def in_list_func(query, value, index):
return filter(lambda x: x[index] in value, query)
[docs]
def not_in_list_func(query, value, index):
return filter(lambda x: x[index] not in value, query)
[docs]
def top_x_func(query, value, index):
items = sorted(set(x[index] for x in query), reverse=True)
top_x = set(items[:value])
return filter((lambda x: x[index] in top_x), query)
[docs]
def bottom_x_func(query, value, index):
items = sorted(set(x[index] for x in query), reverse=False)
top_x = set(items[:value])
return filter((lambda x: x[index] in top_x), query)
[docs]
class FilterType(Enum):
EQUAL = {'func': equal_func, 'text': _l('equals')}
NOT_EQUAL = {'func': not_equal_func, 'text': _l('not equals')}
CONTAINS = {'func': contains_func, 'text': _l('contains')}
NOT_CONTAINS = {'func': not_contains_func, 'text': _l('not contains')}
GREATER = {'func': greater_func, 'text': _l('greater than')}
SMALLER = {'func': smaller_func, 'text': _l('smaller than')}
IN_LIST = {'func': in_list_func, 'text': _l('in list')}
NOT_IN_LIST = {'func': not_in_list_func, 'text': _l('not in list')}
TOP_X = {'func': top_x_func, 'text': _l('top X')}
BOTTOM_X = {'func': bottom_x_func, 'text': _l('bottom X')}
[docs]
class BaseFilter(filters.BaseFilter):
[docs]
def operation(self):
return getattr(self, 'operation_text')
[docs]
def apply(self, query, value):
return getattr(self, 'apply_func')(query, value, getattr(self, 'index'))
[docs]
class TagBaseFilter(BaseFilter):
def __init__(
self,
name,
operation_text=None,
apply_func=None,
filter_type=None,
options=None,
data_type=None):
try:
self.index = ['name', 'usage_count'].index(name)
except ValueError as e:
raise ValueError(f'name: {name}') from e
self.filter_type = filter_type
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
if _key(self.operation_text) in ('in list', 'not in list'):
super().__init__(name, options, data_type='select2-tags')
else:
super().__init__(name, options, data_type)
[docs]
def clean(self, value):
on_list = self.filter_type in (FilterType.IN_LIST, FilterType.NOT_IN_LIST)
if on_list and self.name == 'usage_count':
value = [int(v.strip()) for v in value.split(',') if v.strip()]
elif on_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
[docs]
class BookmarkOrderFilter(BaseFilter):
DIR_LIST = [('asc', _l('natural')), ('desc', _l('reversed'))]
FIELDS = ['index', 'url', 'netloc', 'title', 'description', 'tags', '-#', '#']
_NAMES = {'-#': 'with tag first', '#': 'with tag last'}
def __init__(self, field, *args, **kwargs):
self.field = field
super().__init__('order', *args, options=(None if field in self._NAMES else self.DIR_LIST), **kwargs)
[docs]
def operation(self):
key = self._NAMES.get(self.field, f'by {self.field}')
return _l(key)
[docs]
def apply(self, query, value):
return query
[docs]
@staticmethod
def value(filters, values):
return [(filters[idx].field + value if filters[idx].field in BookmarkOrderFilter._NAMES else
('-' if value == 'desc' else '+') + filters[idx].field)
for idx, key, value in values if key == 'order']
[docs]
class BookmarkBukuFilter(BaseFilter):
KEYS = {
'markers': 'markers',
'all_keywords': 'match all',
'deep': 'deep',
'regex': 'regex',
}
def __init__(self, *args, **kwargs):
self.params = {key: kwargs.pop(key, False) for key in self.KEYS}
super().__init__('buku', *args, **kwargs)
[docs]
def operation(self):
parts = ', '.join(v for k, v in self.KEYS.items() if self.params[k])
key = 'search' + (parts and ' ' + parts)
return _l(key)
[docs]
def apply(self, query, value):
return query
[docs]
class BookmarkBaseFilter(BaseFilter):
def __init__(
self,
name,
operation_text=None,
apply_func=None,
filter_type=None,
options=None,
data_type=None):
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(f'name: {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
if _key(self.operation_text) in ('in list', 'not in list'):
super().__init__(name, options, data_type='select2-tags')
else:
super().__init__(name, options, data_type)
[docs]
def clean(self, value):
on_list = _key(self.operation_text) in ('in list', 'not in list')
if on_list and self.name == BookmarkField.ID.name.lower():
value = [int(v.strip()) for v in value.split(',') if v.strip()]
elif on_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
[docs]
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
[docs]
def clean(self, value):
value = int(value)
if value < 0:
raise ValueError
return value
[docs]
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
[docs]
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
[docs]
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
[docs]
def clean(self, value):
value = int(value)
if value < 1:
raise ValueError
return value