Source code for cata_log_hub.providers.kaufland

# SPDX-License-Identifier: AGPL-3.0-or-later
#
# Cata-Log - the central hub for digital flyers
# Copyright (C) 2026 David Aderbauer & The Cata-Log Contributors
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
# published by the Free Software Foundation, either version 3 of the
# License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <https://www.gnu.org/licenses/>.

from datetime import datetime, timedelta
from typing import override

from pydantic import Field

from cata_log_hub.exceptions import CatalogUnavailableWarning, PagesExhausted
from cata_log_hub.utils.page_numbers import PageNumber

from .base import Preview, Provider
from .regions import Germany


[docs] class KauflandWoche(Provider): """Provider class for Kaufland Wochenangebote catalog.""" uid = "kaufland-de" name = "Kaufland" description = "Kaufland Angebote"
[docs] class Configuration(Provider.Configuration): filial_id: str = Field( default="0", description="""ID der Kaufland-Filiale. Öffne filiale.kaufland.de und wähle deine Filiale aus. Öffne den Web-Inspektor und suche im Webspeicher nach einem Cookie namens 'x-aem-variant. Der Wert des Cookies beginnt mit DE. Die Zahl danach ist die Filial-ID. """, )
url = "https://filiale.kaufland.de/prospekte.html" region = Germany first_page_number = 0 overview_url_template = "https://endpoints.leaflets.schwarz/v4/overview/?region_id={filial_id}&client_locale=kaufland/{language_code_lower}-{language_code_upper}" subcategory_name = "KDZ1" flyer_name = "Prospekt" flyer_index = 0
[docs] @override def _get_catalog_data(self) -> None: overview_response = self._client.get( self.overview_url_template.format( filial_id=self._configuration.filial_id, language_code_lower=self.region.language_code.lower(), language_code_upper=self.region.language_code.upper(), ) ) try: category_data = overview_response.json()["categories"][0] except IndexError as index_error: raise CatalogUnavailableWarning from index_error overview_subcategory = next( ( subcategory for subcategory in category_data["subcategories"] if self.subcategory_name in subcategory["name"] ), None, ) if overview_subcategory is None: raise CatalogUnavailableWarning flyers = [ flyer for flyer in overview_subcategory["flyers"] if self.flyer_name in flyer["name"] ] try: flyer_data = flyers[self.flyer_index] except IndexError as index_error: raise CatalogUnavailableWarning from index_error flyer_json_url = flyer_data["flyerJson"] self.flyer_json = self._client.get(flyer_json_url).json()
[docs] @override def _get_page(self, page_number: PageNumber) -> bytes: try: url = self.flyer_json["flyer"]["pages"][int(page_number)]["zoom"] except IndexError as error: raise PagesExhausted from error return self._client.get(url).content
[docs] @override def _get_valid_since(self) -> datetime: return datetime.strptime( self.flyer_json["flyer"]["offerStartDate"], "%Y-%m-%d" ).replace(tzinfo=self.region.timezone)
[docs] @override def _get_valid_until(self) -> datetime: return datetime.strptime( self.flyer_json["flyer"]["offerEndDate"], "%Y-%m-%d" ).replace(tzinfo=self.region.timezone) + timedelta(days=1)
[docs] class KauflandWochePreview(Preview, KauflandWoche): """Provider class for Kaufland Wochenangebote preview catalog for next week.""" uid = KauflandWoche.uid + "-preview" name = KauflandWoche.name + "-Vorschau" description = KauflandWoche.description + " im nächsten Katalog"
[docs] @override def _get_preview_timedelta(self) -> timedelta: return timedelta(days=7)
flyer_index = 1
[docs] class KauflandSonder(KauflandWoche): """Provider class for Kaufland Sonderangebote catalog.""" uid = "kaufland-sonder-de" name = "Kaufland Sonderprospekt" description = "Kaufland Sonderangebote" flyer_index = 0 subcategory_name = "KDZ2"