modular parser framework

This commit is contained in:
Hadrian Burkhardt
2026-05-21 08:21:49 +00:00
parent 1223791074
commit cf5348a0c8
35 changed files with 452 additions and 166 deletions
+1 -1
View File
@@ -5,7 +5,7 @@ import json
import os
import pytest
from stw_potsdam.config import read_canteen_config
from openmensa_parsers.config import read_canteen_config
def _resource_path(filename):
+91
View File
@@ -0,0 +1,91 @@
# -*- encoding: utf-8 -*-
import pytest
from openmensa_parsers.config import Canteen
from openmensa_parsers.parsers.base import BaseOpenMensaParser, FeedDefinition
from openmensa_parsers.parsers.registry import create_parser, get_parser_class
from openmensa_parsers.parsers.potsdam import PotsdamParser
from openmensa_parsers.views import app
from openmensa_parsers.xml_types.builder import Builder
from openmensa_parsers.xml_types.canteen_xml import CanteenMeta, CanteenXML
from openmensa_parsers.xml_types.times_xml import CanteenOpenTimespec, TimesXML
def _configured_canteen():
return Canteen(
key="demo",
name="Demo Canteen",
street="Demo Street",
city="Demo City",
id="1",
chash="demo",
)
def _canteen_xml():
meta = CanteenMeta(
name="Demo Canteen",
address="Demo Street, 12345 Demo City",
city="Demo City",
phone="+49 331 123456",
email="demo@example.test",
availability="public",
)
times = TimesXML({
day: CanteenOpenTimespec("geschlossen")
for day in TimesXML.VALID_DAYS
})
return CanteenXML(meta, times)
class DemoParser(BaseOpenMensaParser):
id = "demo"
feed = FeedDefinition(source="https://example.test")
def __init__(self):
self.fetched = False
self.parsed_raw_data = None
def fetch(self):
self.fetched = True
return {"source": "fixture"}
def parse(self, _config, raw_data):
self.parsed_raw_data = raw_data
return {"demo": _canteen_xml()}
def test_builder_uses_supplied_parser():
parser = DemoParser()
with app.test_request_context():
builder = Builder({"demo": _configured_canteen()}, parser=parser)
assert parser.fetched
assert parser.parsed_raw_data == {"source": "fixture"}
assert b"https://example.test" in builder.get_xml("demo")
def test_builder_accepts_source_data_fixture():
parser = DemoParser()
with app.test_request_context():
Builder(
{"demo": _configured_canteen()},
source_data={"source": "test-fixture"},
parser=parser,
)
assert not parser.fetched
assert parser.parsed_raw_data == {"source": "test-fixture"}
def test_parser_registry_loads_default_parser():
assert get_parser_class("potsdam") is PotsdamParser
assert isinstance(create_parser("potsdam"), PotsdamParser)
def test_parser_registry_rejects_unknown_parser():
with pytest.raises(KeyError, match="Unknown parser"):
create_parser("unknown")
+2 -2
View File
@@ -4,8 +4,8 @@ import json
import logging
import os
import pytest
from stw_potsdam.config import read_canteen_config
from stw_potsdam.views import canteen_xml_feed, app
from openmensa_parsers.config import read_canteen_config
from openmensa_parsers.views import canteen_xml_feed, app
# pragma pylint: disable=invalid-name,redefined-outer-name
-12
View File
@@ -1,12 +0,0 @@
# -*- encoding: utf-8 -*-
from stw_potsdam.swp_webspeiseplan_api import SWPWebspeiseplanAPI
# pytest fixtures are linked via parameter names of test methods
# pragma pylint: disable=unused-import,unused-argument,redefined-outer-name
from tests.stub_api import api_offline
def test_api_init_does_not_fetch(api_offline):
"""Creating the API client does not perform network requests."""
SWPWebspeiseplanAPI()
+2 -2
View File
@@ -1,7 +1,7 @@
# -*- encoding: utf-8 -*-
import pytest
from stw_potsdam import views
from openmensa_parsers import views
# pytest fixtures are linked via parameter names of test methods
# pragma pylint: disable=unused-import,redefined-outer-name,unused-argument
@@ -21,7 +21,7 @@ def test_health_check(client):
assert response.data == b"OK"
def test_index(client):
def test_index(client, api_online_one_shot):
response = client.get("/").json
canteen_url = response.get("griebnitzsee", None)
assert canteen_url, "Known canteen in index response"
+43
View File
@@ -0,0 +1,43 @@
# -*- encoding: utf-8 -*-
import httpretty
import pytest
from openmensa_parsers.webspeiseplan_api import WebspeiseplanAPI
# pytest fixtures are linked via parameter names of test methods
# pragma pylint: disable=unused-import,unused-argument,redefined-outer-name
from tests.stub_api import api_offline
def test_api_init_does_not_fetch(api_offline):
"""Creating the API client does not perform network requests."""
WebspeiseplanAPI("https://menus.example.test")
def test_api_init_requires_valid_base_url():
"""Creating the API client validates the base URL."""
with pytest.raises(ValueError):
WebspeiseplanAPI("menus.example.test")
def test_parse_token_uses_configured_base_url():
"""The API client uses the configured Webspeiseplan host."""
httpretty.enable(allow_net_connect=False)
try:
httpretty.register_uri(
httpretty.GET,
"https://menus.example.test",
body='<script src="/main.abc123.js"></script>',
)
httpretty.register_uri(
httpretty.GET,
"https://menus.example.test/main.abc123.js",
body='PROXY_TOKEN: "0123456789abcdef"',
)
token = WebspeiseplanAPI("https://menus.example.test/").parse_token()
finally:
httpretty.disable()
httpretty.reset()
assert token == "0123456789abcdef"
@@ -1,6 +1,6 @@
# -*- encoding: utf-8 -*-
from stw_potsdam.swp_webspeiseplan_parser import SWPWebspeiseplanParser
from openmensa_parsers.webspeiseplan_parser import WebspeiseplanParser
def _menu_item(name):
@@ -25,7 +25,7 @@ def _menu_item(name):
def _parse_meal(name):
parser = SWPWebspeiseplanParser()
parser = WebspeiseplanParser()
meals = parser.parse_meals(_menu_item(name), {1: {"name": "Salattheke"}})
return meals[0]["meal"]