39 lines
1.3 KiB
Python
39 lines
1.3 KiB
Python
from __future__ import annotations
|
|
|
|
import re
|
|
from urllib.parse import parse_qs, urlencode, urlsplit, urlunsplit
|
|
|
|
from app.errors import AppError
|
|
|
|
CATEGORY_PATH_RE = re.compile(r"/cat/([^/?#]+)", re.IGNORECASE)
|
|
|
|
|
|
def parse_category_key(source_url: str) -> str:
|
|
parts = urlsplit(source_url.strip())
|
|
query = parse_qs(parts.query)
|
|
if query.get("cat"):
|
|
return str(query["cat"][0])
|
|
match = CATEGORY_PATH_RE.search(parts.path)
|
|
if match:
|
|
return match.group(1)
|
|
raise AppError(422, "SOURCE_URL_INVALID", "Quellpfad muss eine Geizhals-Kategorie enthalten (cat=…)")
|
|
|
|
|
|
def slugify(value: str) -> str:
|
|
text = value.strip().casefold().translate(str.maketrans("äöüß", "aous"))
|
|
text = re.sub(r"[^a-z0-9]+", "-", text).strip("-")
|
|
return text or "category"
|
|
|
|
|
|
def with_extended_filters(source_url: str) -> str:
|
|
parts = urlsplit(source_url.strip())
|
|
query = [(name, value) for name, value in _query_items(parts.query) if name != "promode"]
|
|
query.append(("promode", "true"))
|
|
return urlunsplit((parts.scheme or "https", parts.netloc, parts.path or "/", urlencode(query), parts.fragment))
|
|
|
|
|
|
def _query_items(query: str) -> list[tuple[str, str]]:
|
|
from urllib.parse import parse_qsl
|
|
|
|
return parse_qsl(query, keep_blank_values=True)
|