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)