From b31796da398ceda4678b3418dec9a1f5fa796b1d3222f026dbefe681be53c984 Mon Sep 17 00:00:00 2001 From: Hadrian Burkhardt Date: Fri, 1 May 2026 00:38:18 +0000 Subject: [PATCH] fixed salattheke prices --- stw_potsdam/swp_webspeiseplan_parser.py | 57 +++++++++++++++++++++-- tests/test_swp_webspeiseplan_parser.py | 60 +++++++++++++++++++++++++ 2 files changed, 113 insertions(+), 4 deletions(-) create mode 100644 tests/test_swp_webspeiseplan_parser.py diff --git a/stw_potsdam/swp_webspeiseplan_parser.py b/stw_potsdam/swp_webspeiseplan_parser.py index 95c1739..61a47a0 100644 --- a/stw_potsdam/swp_webspeiseplan_parser.py +++ b/stw_potsdam/swp_webspeiseplan_parser.py @@ -1,10 +1,14 @@ import logging +import re from datetime import datetime, date from stw_potsdam.xml_types.canteen_xml import CanteenMeta, CanteenXML from stw_potsdam.xml_types.times_xml import CanteenOpenTimespec, TimesXML from stw_potsdam.xml_types.meal_xml import MealXML +EURO_PRICE_PATTERN = re.compile(r"(\d+(?:[,.]\d{1,2})?)\s*€") + + class SWPWebspeiseplanParser: """Class method to parse SWP_Webspeiseplan.""" @@ -56,6 +60,42 @@ class SWPWebspeiseplanParser: canteen = CanteenXML(canteen_meta, canteen_times) return canteen + def _parse_price(self, value): + if value in (None, "", {}): + return None + return float(str(value).replace(",", ".")) + + def _parse_embedded_prices( + self, name: str, price: dict[str, float | None] + ) -> tuple[str, dict[str, float | None]]: + if any(price.values()): + return name, price + + matches = EURO_PRICE_PATTERN.findall(name) + if len(matches) < 2: + return name, price + + parsed = [self._parse_price(match) for match in matches] + if len(parsed) >= 3: + price = { + "student": parsed[0], + "employee": parsed[1], + "other": parsed[2], + } + elif "Stud" in name and ("Gäste" in name or "Gaeste" in name): + price = { + "student": parsed[0], + "employee": price["employee"], + "other": parsed[1], + } + else: + return name, price + + name = EURO_PRICE_PATTERN.sub("", name) + name = re.sub(r"\s*/\s*", " ", name) + name = re.sub(r"\s+", " ", name).strip() + return name, price + def parse_meals( self, menu_data, meal_categories ) -> list[tuple[date, str, MealXML]]: @@ -66,11 +106,20 @@ class SWPWebspeiseplanParser: info = meal_data["speiseplanAdvancedGericht"] additional_info = meal_data["zusatzinformationen"] price = { - "student": additional_info["mitarbeiterpreisDecimal2"], - "employee": additional_info["price3Decimal2"], - "other": additional_info["gaestepreisDecimal2"], + "student": self._parse_price( + additional_info["mitarbeiterpreisDecimal2"] + ), + "employee": self._parse_price( + additional_info["price3Decimal2"] + ), + "other": self._parse_price( + additional_info["gaestepreisDecimal2"] + ), } - meal = MealXML(name=info["gerichtname"], price=price) + name, price = self._parse_embedded_prices( + info["gerichtname"], price + ) + meal = MealXML(name=name, price=price) day = datetime.fromisoformat(info["datum"]).date() category = meal_categories[info["gerichtkategorieID"]]["name"] meals.append( diff --git a/tests/test_swp_webspeiseplan_parser.py b/tests/test_swp_webspeiseplan_parser.py new file mode 100644 index 0000000..4e4c295 --- /dev/null +++ b/tests/test_swp_webspeiseplan_parser.py @@ -0,0 +1,60 @@ +# -*- encoding: utf-8 -*- + +from stw_potsdam.swp_webspeiseplan_parser import SWPWebspeiseplanParser + + +def _menu_item(name): + return [ + { + "speiseplanGerichtData": [ + { + "speiseplanAdvancedGericht": { + "datum": "2026-05-01T00:00:00", + "gerichtkategorieID": 1, + "gerichtname": name, + }, + "zusatzinformationen": { + "mitarbeiterpreisDecimal2": 0, + "price3Decimal2": 0, + "gaestepreisDecimal2": 0, + }, + } + ] + } + ] + + +def _parse_meal(name): + parser = SWPWebspeiseplanParser() + meals = parser.parse_meals(_menu_item(name), {1: {"name": "Salattheke"}}) + return meals[0]["meal"] + + +def test_parse_salad_bar_three_embedded_prices(): + meal = _parse_meal( + "große Schale kleine Schale Relevo Schale 100g " + "1,10 €/ 1,70 €/ 1,90€" + ) + + assert meal.name == "große Schale kleine Schale Relevo Schale 100g" + assert meal.price == { + "student": 1.10, + "employee": 1.70, + "other": 1.90, + } + + +def test_parse_salad_bar_student_guest_embedded_prices(): + meal = _parse_meal( + "große Schale\nkleine Schale\nRelevo Schale\n" + "100g Stud. 1,00€/ Gäste 1,45€" + ) + + assert meal.name == ( + "große Schale kleine Schale Relevo Schale 100g Stud. Gäste" + ) + assert meal.price == { + "student": 1.00, + "employee": 0.0, + "other": 1.45, + }