From 267ff6722a2b082d02b69f5b54208b9a96cbefa3 Mon Sep 17 00:00:00 2001 From: Lorenz Stechauner Date: Tue, 17 Sep 2024 18:55:21 +0200 Subject: [PATCH] elwig-backend: Allow multiple values in filters --- src/elwig-backend | 44 ++++++++++++++++++++++++++------------------ 1 file changed, 26 insertions(+), 18 deletions(-) diff --git a/src/elwig-backend b/src/elwig-backend index 78d06c5..7427e32 100755 --- a/src/elwig-backend +++ b/src/elwig-backend @@ -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: