elwig-backend: Allow multiple values in filters

This commit is contained in:
2024-09-17 18:55:21 +02:00
parent 55743033c2
commit 267ff6722a

View File

@ -24,25 +24,31 @@ class BadRequestError(Exception):
class Filter:
def __init__(self, name: str, value: int or str = None):
def __init__(self, name: str, values: list[int] or list[str] = None):
self.name = name
self.value = value
self.values = values
def is_int(self) -> bool:
return type(self.value) is int
return type(self.values[0]) is int
def is_str(self) -> bool:
return type(self.value) is str
return type(self.values[0]) is str
def is_single(self) -> bool:
return self.value is None
return self.values is None
def to_sql_list(self) -> str:
if self.is_int():
return ', '.join(str(v) for v in self.values)
else:
return ', '.join(f"'{v}'" for v in self.values)
def __repr__(self) -> str:
if self.is_single():
return self.name
elif self.name == 'kgnr':
return f'kgnr={self.value:05}'
return f'{self.name}={self.value}'
return f'kgnr={";".join(f"{v:05}" for v in self.values)}'
return f'{self.name}={";".join(str(v) for v in self.values)}'
def __str__(self) -> str:
return self.__repr__()
@ -54,7 +60,9 @@ class Filter:
def from_str(string: str) -> Filter:
f = string.split('=', 1)
if len(f) == 2:
return Filter(f[0], int(f[1]) if f[1].isdigit() else f[1])
ps = f[1].split(';')
is_digit = all(p.isdigit() for p in ps)
return Filter(f[0], [int(p) for p in ps] if is_digit else ps)
return Filter(f[0])
@ -78,11 +86,11 @@ def get_delivery_schedule_filter_clauses(filters: list[Filter]) -> list[str]:
clauses = []
for f in filters:
if f.name == 'year' and f.is_int():
clauses.append(f"s.year = {f.value}")
elif f.name == 'sortid' and f.is_str() and len(f.value) == 2 and f.value.isalpha() and f.value.isupper():
clauses.append(f"v.sortid = '{f.value}'")
elif f.name == 'date' and f.is_str() and re.match(r'[0-9]{4}-[0-9]{2}-[0-9]{2}', f.value) is not None:
clauses.append(f"s.date = '{f.value}'")
clauses.append(f"s.year IN ({f.to_sql_list()})")
elif f.name == 'sortid' and f.is_str() and all(len(v) == 2 and v.isalpha() and v.isupper() for v in f.values):
clauses.append(f"v.sortid IN ({f.to_sql_list()})")
elif f.name == 'date' and f.is_str() and all(re.match(r'[0-9]{4}-[0-9]{2}-[0-9]{2}', v) is not None for v in f.values):
clauses.append(f"s.date IN ({f.to_sql_list()})")
else:
raise BadRequestError(f"Invalid filter '{f}'")
return clauses
@ -279,27 +287,27 @@ class ElwigApi(BaseHTTPRequestHandler):
self.exec_collection(
"SELECT sortid, type, name, comment FROM wine_variety",
lambda r: f'{{"sortid":{jdmp(r[0])},"type":{jdmp(r[1])},"name":{jdmp(r[2])},"comment":{jdmp(r[3])}}}',
filters)
[], offset, limit)
elif path == '/wine/quality_levels':
self.exec_collection(
"SELECT qualid, name, min_kmw, predicate FROM wine_quality_level",
lambda r: f'{{"qualid":{jdmp(r[0])},"name":{jdmp(r[1]):22},"min_kmw":{jdmp(r[2])},"is_predicate":{jdmp(r[3], is_bool=True)}}}',
filters)
[], offset, limit)
elif path == '/wine/attributes':
self.exec_collection(
"SELECT attrid, name FROM wine_attribute",
lambda r: f'{{"attrid":{jdmp(r[0]):4},"name":{jdmp(r[1])}}}',
filters)
[], offset, limit)
elif path == '/wine/cultivations':
self.exec_collection(
"SELECT cultid, name, description FROM wine_cultivation",
lambda r: f'{{"cultid":{jdmp(r[0]):5},"name":{jdmp(r[1])},"description":{jdmp(r[2])}}}',
filters)
[], offset, limit)
elif path == '/modifiers':
self.exec_collection(
"SELECT year, modid, name, ordering FROM modifier",
lambda r: f'{{"year":{jdmp(r[0])},"modid":{jdmp(r[1]):5},"name":{jdmp(r[2]):18},"ordering":{jdmp(r[3])}}}',
filters)
[], offset, limit)
elif path == '/delivery_schedules':
self.do_GET_delivery_schedules(filters, offset, limit, order)
else: