OpenMensa parser for STW Potsdam after canteen website redesign
This commit is contained in:
@@ -0,0 +1,3 @@
|
|||||||
|
.cache
|
||||||
|
.idea
|
||||||
|
.pytest_cache
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
|
||||||
|
RUN = pipenv run python -m stw_potsdam
|
||||||
|
|
||||||
|
run:
|
||||||
|
$(RUN)
|
||||||
|
|
||||||
|
debug:
|
||||||
|
FLASK_ENV=development $(RUN)
|
||||||
|
|
||||||
|
test:
|
||||||
|
pipenv run python -m pytest -v
|
||||||
|
|
||||||
|
test_debug:
|
||||||
|
pipenv run python -m pytest -v --trace
|
||||||
|
|
||||||
|
.PHONY: run debug test test_debug
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
[packages]
|
||||||
|
requests = "*"
|
||||||
|
pyopenmensa = "*"
|
||||||
|
flask = "*"
|
||||||
|
|
||||||
|
[dev-packages]
|
||||||
|
pytest = "*"
|
||||||
Generated
+192
@@ -0,0 +1,192 @@
|
|||||||
|
{
|
||||||
|
"_meta": {
|
||||||
|
"hash": {
|
||||||
|
"sha256": "c9813dd3ac9565de259cfecf7c479f2e6d0e4f756aeeea2a3168ae774a79b980"
|
||||||
|
},
|
||||||
|
"pipfile-spec": 6,
|
||||||
|
"requires": {},
|
||||||
|
"sources": [
|
||||||
|
{
|
||||||
|
"name": "pypi",
|
||||||
|
"url": "https://pypi.org/simple",
|
||||||
|
"verify_ssl": true
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"default": {
|
||||||
|
"certifi": {
|
||||||
|
"hashes": [
|
||||||
|
"sha256:376690d6f16d32f9d1fe8932551d80b23e9d393a8578c5633a2ed39a64861638",
|
||||||
|
"sha256:456048c7e371c089d0a77a5212fb37a2c2dce1e24146e3b7e0261736aaeaa22a"
|
||||||
|
],
|
||||||
|
"version": "==2018.8.24"
|
||||||
|
},
|
||||||
|
"chardet": {
|
||||||
|
"hashes": [
|
||||||
|
"sha256:84ab92ed1c4d4f16916e05906b6b75a6c0fb5db821cc65e70cbd64a3e2a5eaae",
|
||||||
|
"sha256:fc323ffcaeaed0e0a02bf4d117757b98aed530d9ed4531e3e15460124c106691"
|
||||||
|
],
|
||||||
|
"version": "==3.0.4"
|
||||||
|
},
|
||||||
|
"click": {
|
||||||
|
"hashes": [
|
||||||
|
"sha256:29f99fc6125fbc931b758dc053b3114e55c77a6e4c6c3a2674a2dc986016381d",
|
||||||
|
"sha256:f15516df478d5a56180fbf80e68f206010e6d160fc39fa508b65e035fd75130b"
|
||||||
|
],
|
||||||
|
"version": "==6.7"
|
||||||
|
},
|
||||||
|
"flask": {
|
||||||
|
"hashes": [
|
||||||
|
"sha256:2271c0070dbcb5275fad4a82e29f23ab92682dc45f9dfbc22c02ba9b9322ce48",
|
||||||
|
"sha256:a080b744b7e345ccfcbc77954861cb05b3c63786e93f2b3875e0913d44b43f05"
|
||||||
|
],
|
||||||
|
"index": "pypi",
|
||||||
|
"version": "==1.0.2"
|
||||||
|
},
|
||||||
|
"idna": {
|
||||||
|
"hashes": [
|
||||||
|
"sha256:156a6814fb5ac1fc6850fb002e0852d56c0c8d2531923a51032d1b70760e186e",
|
||||||
|
"sha256:684a38a6f903c1d71d6d5fac066b58d7768af4de2b832e426ec79c30daa94a16"
|
||||||
|
],
|
||||||
|
"version": "==2.7"
|
||||||
|
},
|
||||||
|
"itsdangerous": {
|
||||||
|
"hashes": [
|
||||||
|
"sha256:cbb3fcf8d3e33df861709ecaf89d9e6629cff0a217bc2848f1b41cd30d360519"
|
||||||
|
],
|
||||||
|
"version": "==0.24"
|
||||||
|
},
|
||||||
|
"jinja2": {
|
||||||
|
"hashes": [
|
||||||
|
"sha256:74c935a1b8bb9a3947c50a54766a969d4846290e1e788ea44c1392163723c3bd",
|
||||||
|
"sha256:f84be1bb0040caca4cea721fcbbbbd61f9be9464ca236387158b0feea01914a4"
|
||||||
|
],
|
||||||
|
"version": "==2.10"
|
||||||
|
},
|
||||||
|
"markupsafe": {
|
||||||
|
"hashes": [
|
||||||
|
"sha256:a6be69091dac236ea9c6bc7d012beab42010fa914c459791d627dad4910eb665"
|
||||||
|
],
|
||||||
|
"version": "==1.0"
|
||||||
|
},
|
||||||
|
"pyopenmensa": {
|
||||||
|
"hashes": [
|
||||||
|
"sha256:936100088ffc53699d2d893a1715210f4399a2881247342a6fb77947ebb0aaa0"
|
||||||
|
],
|
||||||
|
"index": "pypi",
|
||||||
|
"version": "==0.95.0"
|
||||||
|
},
|
||||||
|
"requests": {
|
||||||
|
"hashes": [
|
||||||
|
"sha256:63b52e3c866428a224f97cab011de738c36aec0185aa91cfacd418b5d58911d1",
|
||||||
|
"sha256:ec22d826a36ed72a7358ff3fe56cbd4ba69dd7a6718ffd450ff0e9df7a47ce6a"
|
||||||
|
],
|
||||||
|
"index": "pypi",
|
||||||
|
"version": "==2.19.1"
|
||||||
|
},
|
||||||
|
"urllib3": {
|
||||||
|
"hashes": [
|
||||||
|
"sha256:a68ac5e15e76e7e5dd2b8f94007233e01effe3e50e8daddf69acfd81cb686baf",
|
||||||
|
"sha256:b5725a0bd4ba422ab0e66e89e030c806576753ea3ee08554382c14e685d117b5"
|
||||||
|
],
|
||||||
|
"markers": "python_version != '3.2.*' and python_version != '3.3.*' and python_version != '3.0.*' and python_version != '3.1.*' and python_version < '4' and python_version >= '2.6'",
|
||||||
|
"version": "==1.23"
|
||||||
|
},
|
||||||
|
"werkzeug": {
|
||||||
|
"hashes": [
|
||||||
|
"sha256:c3fd7a7d41976d9f44db327260e263132466836cef6f91512889ed60ad26557c",
|
||||||
|
"sha256:d5da73735293558eb1651ee2fddc4d0dedcfa06538b8813a2e20011583c9e49b"
|
||||||
|
],
|
||||||
|
"version": "==0.14.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"develop": {
|
||||||
|
"atomicwrites": {
|
||||||
|
"hashes": [
|
||||||
|
"sha256:0312ad34fcad8fac3704d441f7b317e50af620823353ec657a53e981f92920c0",
|
||||||
|
"sha256:ec9ae8adaae229e4f8446952d204a3e4b5fdd2d099f9be3aaf556120135fb3ee"
|
||||||
|
],
|
||||||
|
"markers": "python_version >= '2.7' and python_version != '3.0.*' and python_version != '3.1.*' and python_version != '3.2.*' and python_version != '3.3.*'",
|
||||||
|
"version": "==1.2.1"
|
||||||
|
},
|
||||||
|
"attrs": {
|
||||||
|
"hashes": [
|
||||||
|
"sha256:10cbf6e27dbce8c30807caf056c8eb50917e0eaafe86347671b57254006c3e69",
|
||||||
|
"sha256:ca4be454458f9dec299268d472aaa5a11f67a4ff70093396e1ceae9c76cf4bbb"
|
||||||
|
],
|
||||||
|
"version": "==18.2.0"
|
||||||
|
},
|
||||||
|
"funcsigs": {
|
||||||
|
"hashes": [
|
||||||
|
"sha256:330cc27ccbf7f1e992e69fef78261dc7c6569012cf397db8d3de0234e6c937ca",
|
||||||
|
"sha256:a7bb0f2cf3a3fd1ab2732cb49eba4252c2af4240442415b4abce3b87022a8f50"
|
||||||
|
],
|
||||||
|
"markers": "python_version < '3.0'",
|
||||||
|
"version": "==1.0.2"
|
||||||
|
},
|
||||||
|
"more-itertools": {
|
||||||
|
"hashes": [
|
||||||
|
"sha256:c187a73da93e7a8acc0001572aebc7e3c69daf7bf6881a2cea10650bd4420092",
|
||||||
|
"sha256:c476b5d3a34e12d40130bc2f935028b5f636df8f372dc2c1c01dc19681b2039e",
|
||||||
|
"sha256:fcbfeaea0be121980e15bc97b3817b5202ca73d0eae185b4550cbfce2a3ebb3d"
|
||||||
|
],
|
||||||
|
"version": "==4.3.0"
|
||||||
|
},
|
||||||
|
"pathlib2": {
|
||||||
|
"hashes": [
|
||||||
|
"sha256:8eb170f8d0d61825e09a95b38be068299ddeda82f35e96c3301a8a5e7604cb83",
|
||||||
|
"sha256:d1aa2a11ba7b8f7b21ab852b1fb5afb277e1bb99d5dfc663380b5015c0d80c5a"
|
||||||
|
],
|
||||||
|
"markers": "python_version < '3.6'",
|
||||||
|
"version": "==2.3.2"
|
||||||
|
},
|
||||||
|
"pluggy": {
|
||||||
|
"hashes": [
|
||||||
|
"sha256:6e3836e39f4d36ae72840833db137f7b7d35105079aee6ec4a62d9f80d594dd1",
|
||||||
|
"sha256:95eb8364a4708392bae89035f45341871286a333f749c3141c20573d2b3876e1"
|
||||||
|
],
|
||||||
|
"markers": "python_version >= '2.7' and python_version != '3.0.*' and python_version != '3.1.*' and python_version != '3.2.*' and python_version != '3.3.*'",
|
||||||
|
"version": "==0.7.1"
|
||||||
|
},
|
||||||
|
"py": {
|
||||||
|
"hashes": [
|
||||||
|
"sha256:06a30435d058473046be836d3fc4f27167fd84c45b99704f2fb5509ef61f9af1",
|
||||||
|
"sha256:50402e9d1c9005d759426988a492e0edaadb7f4e68bcddfea586bc7432d009c6"
|
||||||
|
],
|
||||||
|
"markers": "python_version >= '2.7' and python_version != '3.0.*' and python_version != '3.1.*' and python_version != '3.2.*' and python_version != '3.3.*'",
|
||||||
|
"version": "==1.6.0"
|
||||||
|
},
|
||||||
|
"pytest": {
|
||||||
|
"hashes": [
|
||||||
|
"sha256:453cbbbe5ce6db38717d282b758b917de84802af4288910c12442984bde7b823",
|
||||||
|
"sha256:a8a07f84e680482eb51e244370aaf2caa6301ef265f37c2bdefb3dd3b663f99d"
|
||||||
|
],
|
||||||
|
"index": "pypi",
|
||||||
|
"version": "==3.8.0"
|
||||||
|
},
|
||||||
|
"scandir": {
|
||||||
|
"hashes": [
|
||||||
|
"sha256:04b8adb105f2ed313a7c2ef0f1cf7aff4871aa7a1883fa4d8c44b5551ab052d6",
|
||||||
|
"sha256:1444134990356c81d12f30e4b311379acfbbcd03e0bab591de2696a3b126d58e",
|
||||||
|
"sha256:1b5c314e39f596875e5a95dd81af03730b338c277c54a454226978d5ba95dbb6",
|
||||||
|
"sha256:346619f72eb0ddc4cf355ceffd225fa52506c92a2ff05318cfabd02a144e7c4e",
|
||||||
|
"sha256:44975e209c4827fc18a3486f257154d34ec6eaec0f90fef0cca1caa482db7064",
|
||||||
|
"sha256:61859fd7e40b8c71e609c202db5b0c1dbec0d5c7f1449dec2245575bdc866792",
|
||||||
|
"sha256:a5e232a0bf188362fa00123cc0bb842d363a292de7126126df5527b6a369586a",
|
||||||
|
"sha256:c14701409f311e7a9b7ec8e337f0815baf7ac95776cc78b419a1e6d49889a383",
|
||||||
|
"sha256:c7708f29d843fc2764310732e41f0ce27feadde453261859ec0fca7865dfc41b",
|
||||||
|
"sha256:c9009c527929f6e25604aec39b0a43c3f831d2947d89d6caaab22f057b7055c8",
|
||||||
|
"sha256:f5c71e29b4e2af7ccdc03a020c626ede51da471173b4a6ad1e904f2b2e04b4bd"
|
||||||
|
],
|
||||||
|
"markers": "python_version < '3.5'",
|
||||||
|
"version": "==1.9.0"
|
||||||
|
},
|
||||||
|
"six": {
|
||||||
|
"hashes": [
|
||||||
|
"sha256:70e8a77beed4562e7f14fe23a786b54f6296e34344c23bc42f07b15018ff98e9",
|
||||||
|
"sha256:832dc0e10feb1aa2c68dcc57dbb658f1c7e65b9b61af69048abc87a2db00a0eb"
|
||||||
|
],
|
||||||
|
"version": "==1.11.0"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
# OpenMensa STW Potsdam
|
||||||
|
|
||||||
|
## Development Quickstart
|
||||||
|
|
||||||
|
$ pipenv install --two --dev # Create venv
|
||||||
|
$ make test # Check setup by running tests
|
||||||
|
$ make debug # Start app instance with debugger
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
# -*- encoding: utf-8 -*-
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
# -*- encoding: utf-8 -*-
|
||||||
|
from views import app
|
||||||
|
|
||||||
|
app.run()
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
# -*- encoding: utf-8 -*-
|
||||||
|
|
||||||
|
from collections import namedtuple
|
||||||
|
|
||||||
|
Canteen = namedtuple('Canteen', ('name', 'street', 'city', 'id', 'chash'))
|
||||||
@@ -0,0 +1,33 @@
|
|||||||
|
# -*- encoding: utf-8 -*-
|
||||||
|
|
||||||
|
import json
|
||||||
|
import requests
|
||||||
|
from collections import namedtuple
|
||||||
|
|
||||||
|
MenuParams = namedtuple('MenuParams', ('canteen_id', 'chash'))
|
||||||
|
|
||||||
|
URL = 'https://www.studentenwerk-potsdam.de/essen/unsere-mensen-cafeterien/detailinfos/'
|
||||||
|
|
||||||
|
|
||||||
|
def _param_json(it):
|
||||||
|
return json.dumps(it, separators=(',', ':'))
|
||||||
|
|
||||||
|
|
||||||
|
def download_menu(menu_params):
|
||||||
|
context = {
|
||||||
|
'record': 'pages_66',
|
||||||
|
'path': 'tt_content.list.20.ddfmensa_ddfmensajson'
|
||||||
|
}
|
||||||
|
|
||||||
|
params = {
|
||||||
|
'tx_typoscriptrendering[context]': _param_json(context),
|
||||||
|
'tx_ddfmensa_ddfmensajson[mensa]': menu_params.canteen_id,
|
||||||
|
'cHash': menu_params.chash
|
||||||
|
}
|
||||||
|
|
||||||
|
body = {
|
||||||
|
'data': False
|
||||||
|
}
|
||||||
|
|
||||||
|
request = requests.post(URL, params=params, json=body)
|
||||||
|
return request.json()
|
||||||
@@ -0,0 +1,41 @@
|
|||||||
|
[neues_palais]
|
||||||
|
name = Mensa Am Neuen Palais
|
||||||
|
street = Am Neuen Palais 10, Haus 12
|
||||||
|
city = 14469 Potsdam
|
||||||
|
id = 1
|
||||||
|
cHash = 677e69a0122d1ccca57a6c30dd149cc8
|
||||||
|
|
||||||
|
[golm]
|
||||||
|
name = Mensa Golm
|
||||||
|
street = Karl-Liebknecht-Straße 24/25
|
||||||
|
city = 14476 Potsdam / OT Golm
|
||||||
|
id = 4
|
||||||
|
cHash = b14a1168346e0b7db67da056660d3a0e
|
||||||
|
|
||||||
|
[griebnitzsee]
|
||||||
|
name = Mensa Griebnitzsee
|
||||||
|
street = August-Bebel-Straße 69, Haus 6
|
||||||
|
city = 14482 Potsdam
|
||||||
|
id = 6
|
||||||
|
cHash = 0c7f1095dcc78ff74b6cd32cd231c75f
|
||||||
|
|
||||||
|
[kiepenheuerallee]
|
||||||
|
name = Mensa Kiepenheuerallee
|
||||||
|
street = Kiepenheuerallee 5
|
||||||
|
city = 14469 Potsdam
|
||||||
|
id = 7
|
||||||
|
cHash = 3900afbd3d9b776e947708b7dfd2378c
|
||||||
|
|
||||||
|
[wildau]
|
||||||
|
name = Mensa Wildau
|
||||||
|
street = Hochschulring 1
|
||||||
|
city = 15745 Wildau
|
||||||
|
id = 33
|
||||||
|
cHash = 2ba7ac17766ceec4a35dac29b593378f
|
||||||
|
|
||||||
|
[brandenburg]
|
||||||
|
name = Mensa Brandenburg an der Havel
|
||||||
|
street = Magdeburger Straße 50
|
||||||
|
city = 14770 Brandenburg an der Havel
|
||||||
|
id = 38
|
||||||
|
cHash = 0843fa029901bdfc0e76d8f31eee7f56
|
||||||
@@ -0,0 +1,29 @@
|
|||||||
|
# -*- encoding: utf-8 -*-
|
||||||
|
|
||||||
|
import ConfigParser
|
||||||
|
import io
|
||||||
|
import os
|
||||||
|
from functools import partial
|
||||||
|
from canteen import Canteen
|
||||||
|
|
||||||
|
|
||||||
|
def _get_config(filename):
|
||||||
|
config = ConfigParser.SafeConfigParser()
|
||||||
|
path = os.path.join('stw_potsdam', filename)
|
||||||
|
with io.open(path, encoding='utf-8') as f:
|
||||||
|
config.readfp(f)
|
||||||
|
return config
|
||||||
|
|
||||||
|
|
||||||
|
def _parse_canteen(config, canteen_name):
|
||||||
|
get = partial(config.get, canteen_name)
|
||||||
|
return Canteen(name=get('name'),
|
||||||
|
street=get('street'),
|
||||||
|
city=get('city'),
|
||||||
|
id=get('id'),
|
||||||
|
chash=get('cHash'))
|
||||||
|
|
||||||
|
|
||||||
|
def read_canteen_config():
|
||||||
|
config = _get_config('canteens.ini')
|
||||||
|
return {name: _parse_canteen(config, name) for name in config.sections()}
|
||||||
@@ -0,0 +1,65 @@
|
|||||||
|
# -*- encoding: utf-8 -*-
|
||||||
|
|
||||||
|
from pyopenmensa.feed import LazyBuilder
|
||||||
|
|
||||||
|
PRICE_ROLE_MAPPING = {
|
||||||
|
'student': 'preis_s',
|
||||||
|
'other': 'preis_g',
|
||||||
|
'employee': 'preis_m'
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def _active_days(menu):
|
||||||
|
for container in menu['wochentage']:
|
||||||
|
day = container['datum']
|
||||||
|
active = 'angebote' in day
|
||||||
|
if active:
|
||||||
|
yield day
|
||||||
|
|
||||||
|
|
||||||
|
def _notes(offer):
|
||||||
|
result = []
|
||||||
|
for label in offer['labels']:
|
||||||
|
result.append(label['name'].capitalize())
|
||||||
|
return result
|
||||||
|
|
||||||
|
|
||||||
|
def _prices(offer):
|
||||||
|
result = {}
|
||||||
|
for role, api_role in PRICE_ROLE_MAPPING.items():
|
||||||
|
if api_role not in offer:
|
||||||
|
continue
|
||||||
|
|
||||||
|
price = offer[api_role]
|
||||||
|
# When no price is set, this can be empty dict
|
||||||
|
if (isinstance(price, unicode) or isinstance(price, str)) and price.strip():
|
||||||
|
result[role] = str(price) # Convert unicode to str for PyOpenMensa -> misses type check
|
||||||
|
|
||||||
|
return result
|
||||||
|
|
||||||
|
|
||||||
|
def _process_day(builder, day):
|
||||||
|
for offer in day['angebote']:
|
||||||
|
builder.addMeal(date=day['data'],
|
||||||
|
category=offer['titel'],
|
||||||
|
name=offer['beschreibung'],
|
||||||
|
notes=_notes(offer),
|
||||||
|
prices=_prices(offer),
|
||||||
|
roles=None)
|
||||||
|
|
||||||
|
|
||||||
|
def _create_builder(canteen):
|
||||||
|
builder = LazyBuilder()
|
||||||
|
builder.name = canteen.name
|
||||||
|
builder.address = canteen.street
|
||||||
|
builder.city = canteen.city
|
||||||
|
return builder
|
||||||
|
|
||||||
|
|
||||||
|
def render(canteen, menu):
|
||||||
|
builder = _create_builder(canteen)
|
||||||
|
|
||||||
|
for day in _active_days(menu):
|
||||||
|
_process_day(builder, day)
|
||||||
|
|
||||||
|
return builder.toXMLFeed()
|
||||||
@@ -0,0 +1,56 @@
|
|||||||
|
# -*- encoding: utf-8 -*-
|
||||||
|
|
||||||
|
from flask import Flask, make_response
|
||||||
|
from werkzeug.contrib.cache import SimpleCache
|
||||||
|
|
||||||
|
import feed
|
||||||
|
from config import read_canteen_config
|
||||||
|
from canteen_api import MenuParams, download_menu
|
||||||
|
|
||||||
|
CACHE_TIMEOUT = 45 * 60
|
||||||
|
|
||||||
|
app = Flask(__name__)
|
||||||
|
|
||||||
|
cache = SimpleCache()
|
||||||
|
|
||||||
|
|
||||||
|
def canteen_not_found(config, canteen_name):
|
||||||
|
app.logger.warn('Canteen %s not found', canteen_name)
|
||||||
|
configured = ', '.join("'{}'".format(c) for c in config.keys())
|
||||||
|
message = "Canteen '{canteen}' not found, available: {configured}".format(canteen=canteen_name,
|
||||||
|
configured=configured)
|
||||||
|
return make_response(message, 404)
|
||||||
|
|
||||||
|
|
||||||
|
def get_menu_cached(canteen):
|
||||||
|
params = MenuParams(canteen_id=canteen.id, chash=canteen.chash)
|
||||||
|
menu = cache.get(params)
|
||||||
|
if menu:
|
||||||
|
app.logger.info('Using cached menu for %s', canteen)
|
||||||
|
return menu or get_menu(canteen, params)
|
||||||
|
|
||||||
|
|
||||||
|
def get_menu(canteen, params):
|
||||||
|
app.logger.info('Downloading menu for %s', canteen)
|
||||||
|
menu = download_menu(params)
|
||||||
|
cache.set(params, menu, timeout=CACHE_TIMEOUT)
|
||||||
|
return menu
|
||||||
|
|
||||||
|
|
||||||
|
def canteen_feed_xml(canteen, menu):
|
||||||
|
xml = feed.render(canteen, menu)
|
||||||
|
response = make_response(xml)
|
||||||
|
response.mimetype = 'text/xml'
|
||||||
|
return response
|
||||||
|
|
||||||
|
|
||||||
|
@app.route('/canteen/<canteen_name>')
|
||||||
|
def canteen_feed(canteen_name):
|
||||||
|
config = read_canteen_config()
|
||||||
|
|
||||||
|
if canteen_name not in config:
|
||||||
|
return canteen_not_found(config, canteen_name)
|
||||||
|
|
||||||
|
canteen = config[canteen_name]
|
||||||
|
menu = get_menu_cached(canteen)
|
||||||
|
return canteen_feed_xml(canteen, menu)
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,362 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<openmensa version="2.1" xmlns="http://openmensa.org/open-mensa-v2" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://openmensa.org/open-mensa-v2 http://openmensa.org/open-mensa-v2.xsd">
|
||||||
|
<canteen>
|
||||||
|
<name>Mensa Griebnitzsee</name>
|
||||||
|
<address>August-Bebel-Straße 69, Haus 6</address>
|
||||||
|
<city>14482 Potsdam</city>
|
||||||
|
<day date="2018-09-19">
|
||||||
|
<category name="Angebot 1">
|
||||||
|
<meal>
|
||||||
|
<name>Gefüllter Germknödel
|
||||||
|
mit Vanillesauce und Mohnzucker</name>
|
||||||
|
<note>Vegetarisch</note>
|
||||||
|
<price role="employee">3.10</price>
|
||||||
|
<price role="other">3.10</price>
|
||||||
|
<price role="student">1.40</price>
|
||||||
|
</meal>
|
||||||
|
</category>
|
||||||
|
<category name="Angebot 2">
|
||||||
|
<meal>
|
||||||
|
<name>Gebratene Rindfleischstreifen mit Gemüse und Kidneybohnen-Dip, dazu Pommes frites</name>
|
||||||
|
<note>Rindfleisch</note>
|
||||||
|
<price role="employee">3.90</price>
|
||||||
|
<price role="other">3.90</price>
|
||||||
|
<price role="student">2.00</price>
|
||||||
|
</meal>
|
||||||
|
</category>
|
||||||
|
<category name="Angebot 3">
|
||||||
|
<meal>
|
||||||
|
<name>Fangfrisches Seelachsfilet auf Farfalle mit Kirschtomaten, Rucola , dazu Salat</name>
|
||||||
|
<note>Fisch</note>
|
||||||
|
<price role="employee">4.50</price>
|
||||||
|
<price role="other">4.50</price>
|
||||||
|
<price role="student">2.50</price>
|
||||||
|
</meal>
|
||||||
|
</category>
|
||||||
|
<category name="Angebot 4">
|
||||||
|
<meal>
|
||||||
|
<name>Bunte Gemüsepfanne dazu Bulgur und Blattsalatvariation</name>
|
||||||
|
<note>Knoblauch</note>
|
||||||
|
<note>Vegan</note>
|
||||||
|
<price role="employee">4.50</price>
|
||||||
|
<price role="other">4.50</price>
|
||||||
|
<price role="student">2.50</price>
|
||||||
|
</meal>
|
||||||
|
</category>
|
||||||
|
</day>
|
||||||
|
<day date="2018-09-20">
|
||||||
|
<category name="Angebot 1">
|
||||||
|
<meal>
|
||||||
|
<name>Eierfrikassee mit buntem Gemüse, dazu Kartoffelpüree</name>
|
||||||
|
<note>Vegetarisch</note>
|
||||||
|
<price role="employee">3.10</price>
|
||||||
|
<price role="other">3.10</price>
|
||||||
|
<price role="student">1.40</price>
|
||||||
|
</meal>
|
||||||
|
</category>
|
||||||
|
<category name="Angebot 2">
|
||||||
|
<meal>
|
||||||
|
<name>Schweinsbraten mit süßem Senf und Bratkartoffeln oder bayrischer Kartoffelsalat, dazu Blattsalat</name>
|
||||||
|
<note>Schweinefleisch</note>
|
||||||
|
<price role="employee">3.90</price>
|
||||||
|
<price role="other">3.90</price>
|
||||||
|
<price role="student">2.00</price>
|
||||||
|
</meal>
|
||||||
|
</category>
|
||||||
|
<category name="Angebot 3">
|
||||||
|
<meal>
|
||||||
|
<name>Truthahnsteak Strindberg Art aus Mecklenburg mit jungen Erbsen
|
||||||
|
und Grillkartoffeln oder frischen Eierknöpfle</name>
|
||||||
|
<note>Gefluegel</note>
|
||||||
|
<price role="employee">4.50</price>
|
||||||
|
<price role="other">4.50</price>
|
||||||
|
<price role="student">2.50</price>
|
||||||
|
</meal>
|
||||||
|
</category>
|
||||||
|
<category name="Angebot 4">
|
||||||
|
<meal>
|
||||||
|
<name>Falafelbällchen mit Tomatenreis und Ajvar-Dip, dazu Salatvariation mit Oliven</name>
|
||||||
|
<note>Knoblauch</note>
|
||||||
|
<note>Vegan</note>
|
||||||
|
<price role="employee">4.50</price>
|
||||||
|
<price role="other">4.50</price>
|
||||||
|
<price role="student">2.50</price>
|
||||||
|
</meal>
|
||||||
|
</category>
|
||||||
|
</day>
|
||||||
|
<day date="2018-09-21">
|
||||||
|
<category name="Angebot 1">
|
||||||
|
<meal>
|
||||||
|
<name>Spaghetti Bolognese mit Reibekäse</name>
|
||||||
|
<note>Schweinefleisch</note>
|
||||||
|
<price role="employee">3.10</price>
|
||||||
|
<price role="other">3.10</price>
|
||||||
|
<price role="student">1.40</price>
|
||||||
|
</meal>
|
||||||
|
<meal>
|
||||||
|
<name>Spaghetti mit veganer Tomatensauce mit geschrotetem Buchweizen</name>
|
||||||
|
<note>Vegan</note>
|
||||||
|
<price role="employee">3.10</price>
|
||||||
|
<price role="other">3.10</price>
|
||||||
|
<price role="student">1.40</price>
|
||||||
|
</meal>
|
||||||
|
</category>
|
||||||
|
<category name="Angebot 2">
|
||||||
|
<meal>
|
||||||
|
<name>Burger mit Putenfleisch aus Mecklenburg mit Pommes frites</name>
|
||||||
|
<note>Gefluegel</note>
|
||||||
|
<price role="employee">3.90</price>
|
||||||
|
<price role="other">3.90</price>
|
||||||
|
<price role="student">2.00</price>
|
||||||
|
</meal>
|
||||||
|
</category>
|
||||||
|
<category name="Angebot 3">
|
||||||
|
<meal>
|
||||||
|
<name>Forelle Müllerin Art mit Petersilienkartoffeln und Gurkensalat</name>
|
||||||
|
<note>Fisch</note>
|
||||||
|
<price role="employee">4.50</price>
|
||||||
|
<price role="other">4.50</price>
|
||||||
|
<price role="student">2.50</price>
|
||||||
|
</meal>
|
||||||
|
</category>
|
||||||
|
<category name="Angebot 4">
|
||||||
|
<meal>
|
||||||
|
<name>Kartoffelrösti mit Tomaten und Mozzarella überbacken, dazu Wildkräutersalat</name>
|
||||||
|
<note>Knoblauch</note>
|
||||||
|
<note>Vegetarisch</note>
|
||||||
|
<price role="employee">4.50</price>
|
||||||
|
<price role="other">4.50</price>
|
||||||
|
<price role="student">2.50</price>
|
||||||
|
</meal>
|
||||||
|
</category>
|
||||||
|
</day>
|
||||||
|
<day date="2018-09-24">
|
||||||
|
<category name="Angebot 1">
|
||||||
|
<meal>
|
||||||
|
<name>Backkartoffel mit Kräuterquark</name>
|
||||||
|
<note>Vegetarisch</note>
|
||||||
|
<price role="employee">3.10</price>
|
||||||
|
<price role="other">3.10</price>
|
||||||
|
<price role="student">1.40</price>
|
||||||
|
</meal>
|
||||||
|
<meal>
|
||||||
|
<name>Backkartoffel mit Soja-Kräuterdip</name>
|
||||||
|
<note>Knoblauch</note>
|
||||||
|
<note>Vegan</note>
|
||||||
|
<price role="employee">3.10</price>
|
||||||
|
<price role="other">3.10</price>
|
||||||
|
<price role="student">1.40</price>
|
||||||
|
</meal>
|
||||||
|
</category>
|
||||||
|
<category name="Angebot 2">
|
||||||
|
<meal>
|
||||||
|
<name>Putenbratwurst aus Mecklenburg mit hausgemachter Currysauce, dazu Kartoffel-Gurkensalat</name>
|
||||||
|
<note>Gefluegel</note>
|
||||||
|
<price role="employee">3.90</price>
|
||||||
|
<price role="other">3.90</price>
|
||||||
|
<price role="student">2.00</price>
|
||||||
|
</meal>
|
||||||
|
<meal>
|
||||||
|
<name>Vegetarische Bratwurst mit hausgemachter Currysauce, dazu Kartoffel-Gurkensalat</name>
|
||||||
|
<note>Vegetarisch</note>
|
||||||
|
<price role="employee">3.90</price>
|
||||||
|
<price role="other">3.90</price>
|
||||||
|
<price role="student">2.00</price>
|
||||||
|
</meal>
|
||||||
|
</category>
|
||||||
|
<category name="Angebot 3">
|
||||||
|
<meal>
|
||||||
|
<name>Knusperfisch mit Kräutersauce,
|
||||||
|
dazu Saisongemüse und Wildmischreis
|
||||||
|
oder Herzoginkartoffeln</name>
|
||||||
|
<note>Fisch</note>
|
||||||
|
<price role="employee">4.50</price>
|
||||||
|
<price role="other">4.50</price>
|
||||||
|
<price role="student">2.50</price>
|
||||||
|
</meal>
|
||||||
|
</category>
|
||||||
|
<category name="Angebot 4">
|
||||||
|
<meal>
|
||||||
|
<name>Pan Haggerty - mit Cheddar überbackenes Kartoffelgericht, dazu Sellerie-Apfelsalat</name>
|
||||||
|
<note>Vegetarisch</note>
|
||||||
|
<price role="employee">4.50</price>
|
||||||
|
<price role="other">4.50</price>
|
||||||
|
<price role="student">2.50</price>
|
||||||
|
</meal>
|
||||||
|
</category>
|
||||||
|
</day>
|
||||||
|
<day date="2018-09-25">
|
||||||
|
<category name="Angebot 1">
|
||||||
|
<meal>
|
||||||
|
<name>Rührei mit Rahmspinat und Salzkartoffeln</name>
|
||||||
|
<note>Vegetarisch</note>
|
||||||
|
<price role="employee">3.10</price>
|
||||||
|
<price role="other">3.10</price>
|
||||||
|
<price role="student">1.40</price>
|
||||||
|
</meal>
|
||||||
|
</category>
|
||||||
|
<category name="Angebot 2">
|
||||||
|
<meal>
|
||||||
|
<name>Hirtenrolle mit Balkangemüse, dazu bunter Nudelreis-Salat</name>
|
||||||
|
<note>Knoblauch</note>
|
||||||
|
<note>Schweinefleisch</note>
|
||||||
|
<price role="employee">3.90</price>
|
||||||
|
<price role="other">3.90</price>
|
||||||
|
<price role="student">2.00</price>
|
||||||
|
</meal>
|
||||||
|
<meal>
|
||||||
|
<name>Vegetarische Bratwurst mit hausgemachter Currysauce, dazu Kartoffel-Gurkensalat</name>
|
||||||
|
<note>Vegetarisch</note>
|
||||||
|
<price role="employee">3.90</price>
|
||||||
|
<price role="other">3.90</price>
|
||||||
|
<price role="student">2.00</price>
|
||||||
|
</meal>
|
||||||
|
</category>
|
||||||
|
<category name="Angebot 3">
|
||||||
|
<meal>
|
||||||
|
<name>Fangfrisches Seelachsfilet mit Rahm-Wirsing, dazu Gnocchi oder Langkornreis</name>
|
||||||
|
<note>Fisch</note>
|
||||||
|
<price role="employee">4.50</price>
|
||||||
|
<price role="other">4.50</price>
|
||||||
|
<price role="student">2.50</price>
|
||||||
|
</meal>
|
||||||
|
</category>
|
||||||
|
</day>
|
||||||
|
<day date="2018-09-26">
|
||||||
|
<category name="Angebot 1">
|
||||||
|
<meal>
|
||||||
|
<name>Gebratene Jagdwurst mit Nudeln und Tomatensauce</name>
|
||||||
|
<note>Schweinefleisch</note>
|
||||||
|
<price role="employee">3.10</price>
|
||||||
|
<price role="other">3.10</price>
|
||||||
|
<price role="student">1.40</price>
|
||||||
|
</meal>
|
||||||
|
<meal>
|
||||||
|
<name>Tofusteak mit Nudeln und veganer Tomatensauce</name>
|
||||||
|
<note>Vegan</note>
|
||||||
|
<price role="employee">3.10</price>
|
||||||
|
<price role="other">3.10</price>
|
||||||
|
<price role="student">1.40</price>
|
||||||
|
</meal>
|
||||||
|
</category>
|
||||||
|
<category name="Angebot 2">
|
||||||
|
<meal>
|
||||||
|
<name>Geflügelfrikassee mit jungen Erbsen, dazu Langkornreis oder Salzkartoffeln</name>
|
||||||
|
<note>Alkohol</note>
|
||||||
|
<note>Gefluegel</note>
|
||||||
|
<price role="employee">3.90</price>
|
||||||
|
<price role="other">3.90</price>
|
||||||
|
<price role="student">2.00</price>
|
||||||
|
</meal>
|
||||||
|
</category>
|
||||||
|
<category name="Angebot 3">
|
||||||
|
<meal>
|
||||||
|
<name>Rinder- oder Schweinesteak mit Kräuterbutter und Pfefferbohnen, dazu Pommes frites oder gebratene Drillinge</name>
|
||||||
|
<note>Rindfleisch</note>
|
||||||
|
<note>Schweinefleisch</note>
|
||||||
|
<price role="employee">4.50</price>
|
||||||
|
<price role="other">4.50</price>
|
||||||
|
<price role="student">2.50</price>
|
||||||
|
</meal>
|
||||||
|
</category>
|
||||||
|
<category name="Angebot 4">
|
||||||
|
<meal>
|
||||||
|
<name>Beelitzer Dinkel-Nudeln und Tomaten-Sauce mit körnigem Frischkäse, dazu Wildkräutersalat</name>
|
||||||
|
<note>Knoblauch</note>
|
||||||
|
<note>Vegetarisch</note>
|
||||||
|
<price role="employee">4.50</price>
|
||||||
|
<price role="other">4.50</price>
|
||||||
|
<price role="student">2.50</price>
|
||||||
|
</meal>
|
||||||
|
</category>
|
||||||
|
</day>
|
||||||
|
<day date="2018-09-27">
|
||||||
|
<category name="Angebot 1">
|
||||||
|
<meal>
|
||||||
|
<name>Kartoffelsuppe mit Wiener Würstchen oder Geflügelwiener aus Mecklenburg, dazu Roggenbrot</name>
|
||||||
|
<note>Rindfleisch</note>
|
||||||
|
<note>Schweinefleisch</note>
|
||||||
|
<price role="employee">3.10</price>
|
||||||
|
<price role="other">3.10</price>
|
||||||
|
<price role="student">1.40</price>
|
||||||
|
</meal>
|
||||||
|
<meal>
|
||||||
|
<name>vegane Kartoffelsuppe mit Räuchertofu und Roggenbrot</name>
|
||||||
|
<note>Vegetarisch</note>
|
||||||
|
<price role="employee">3.10</price>
|
||||||
|
<price role="other">3.10</price>
|
||||||
|
<price role="student">1.40</price>
|
||||||
|
</meal>
|
||||||
|
</category>
|
||||||
|
<category name="Angebot 2">
|
||||||
|
<meal>
|
||||||
|
<name>Thaicurry mit Hähnchenfleisch, Paprika und Sprossen, dazu Basmatirei</name>
|
||||||
|
<note>Gefluegel</note>
|
||||||
|
<price role="employee">3.90</price>
|
||||||
|
<price role="other">3.90</price>
|
||||||
|
<price role="student">2.00</price>
|
||||||
|
</meal>
|
||||||
|
</category>
|
||||||
|
<category name="Angebot 3">
|
||||||
|
<meal>
|
||||||
|
<name>Dönerteller mit Kräuter-Sauce, dazu Pommes frites oder Fladenbrot</name>
|
||||||
|
<note>Rindfleisch</note>
|
||||||
|
<price role="employee">4.50</price>
|
||||||
|
<price role="other">4.50</price>
|
||||||
|
<price role="student">2.50</price>
|
||||||
|
</meal>
|
||||||
|
</category>
|
||||||
|
<category name="Angebot 4">
|
||||||
|
<meal>
|
||||||
|
<name>Makkaroni-Tomaten-Brokkoli-Auflauf, dazu italienische Salatmischung</name>
|
||||||
|
<note>Vegetarisch</note>
|
||||||
|
<price role="employee">4.50</price>
|
||||||
|
<price role="other">4.50</price>
|
||||||
|
<price role="student">2.50</price>
|
||||||
|
</meal>
|
||||||
|
</category>
|
||||||
|
</day>
|
||||||
|
<day date="2018-09-28">
|
||||||
|
<category name="Angebot 1">
|
||||||
|
<meal>
|
||||||
|
<name>Hausgemachte Eierkuchen
|
||||||
|
mit Zitronen-Vanillequark oder Apfelmus</name>
|
||||||
|
<note>Vegetarisch</note>
|
||||||
|
<price role="employee">3.10</price>
|
||||||
|
<price role="other">3.10</price>
|
||||||
|
<price role="student">1.40</price>
|
||||||
|
</meal>
|
||||||
|
</category>
|
||||||
|
<category name="Angebot 2">
|
||||||
|
<meal>
|
||||||
|
<name>Rauch-Matjestopf
|
||||||
|
mit Preiselbeeren, dazu Bratkartoffeln
|
||||||
|
und Gurkensalat mit Sauerrahm</name>
|
||||||
|
<note>Fisch</note>
|
||||||
|
<price role="employee">3.90</price>
|
||||||
|
<price role="other">3.90</price>
|
||||||
|
<price role="student">2.00</price>
|
||||||
|
</meal>
|
||||||
|
</category>
|
||||||
|
<category name="Angebot 3">
|
||||||
|
<meal>
|
||||||
|
<name>Paniertes Schnitzel mit Blumenkohl in Semmelbutter, dazu Petersilienkartoffeln</name>
|
||||||
|
<note>Schweinefleisch</note>
|
||||||
|
<price role="employee">4.50</price>
|
||||||
|
<price role="other">4.50</price>
|
||||||
|
<price role="student">2.50</price>
|
||||||
|
</meal>
|
||||||
|
</category>
|
||||||
|
<category name="Angebot 4">
|
||||||
|
<meal>
|
||||||
|
<name>Gemüsebratling mit Pesto und buntem Gemüse-Nudelsalat</name>
|
||||||
|
<note>Knoblauch</note>
|
||||||
|
<note>Vegan</note>
|
||||||
|
<price role="employee">4.50</price>
|
||||||
|
<price role="other">4.50</price>
|
||||||
|
<price role="student">2.50</price>
|
||||||
|
</meal>
|
||||||
|
</category>
|
||||||
|
</day>
|
||||||
|
</canteen>
|
||||||
|
</openmensa>
|
||||||
@@ -0,0 +1,36 @@
|
|||||||
|
# -*- encoding: utf-8 -*-
|
||||||
|
|
||||||
|
import io
|
||||||
|
import json
|
||||||
|
import os
|
||||||
|
|
||||||
|
from stw_potsdam import feed
|
||||||
|
from stw_potsdam.config import read_canteen_config
|
||||||
|
|
||||||
|
|
||||||
|
def _resource_path(filename):
|
||||||
|
return os.path.join('tests', 'resources', filename)
|
||||||
|
|
||||||
|
|
||||||
|
def _canteen():
|
||||||
|
return read_canteen_config()['griebnitzsee']
|
||||||
|
|
||||||
|
|
||||||
|
def _menu():
|
||||||
|
with open(_resource_path('input.json')) as f:
|
||||||
|
return json.load(f)
|
||||||
|
|
||||||
|
|
||||||
|
def _expected_feed():
|
||||||
|
with io.open(_resource_path('output.xml'), encoding='utf-8') as f:
|
||||||
|
return f.read()
|
||||||
|
|
||||||
|
|
||||||
|
def test_consistency():
|
||||||
|
canteen = _canteen()
|
||||||
|
menu = _menu()
|
||||||
|
|
||||||
|
actual = feed.render(canteen, menu)
|
||||||
|
|
||||||
|
expected = _expected_feed()
|
||||||
|
assert expected == actual
|
||||||
@@ -0,0 +1,30 @@
|
|||||||
|
# -*- encoding: utf-8 -*-
|
||||||
|
|
||||||
|
import os
|
||||||
|
import pytest
|
||||||
|
|
||||||
|
from stw_potsdam import feed
|
||||||
|
from stw_potsdam.config import read_canteen_config
|
||||||
|
from stw_potsdam.canteen_api import download_menu
|
||||||
|
from stw_potsdam.canteen_api import MenuParams
|
||||||
|
|
||||||
|
ENV_ENABLED = 'ENABLE_API_QUERY'
|
||||||
|
|
||||||
|
CANTEENS = read_canteen_config()
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture(params=CANTEENS.values(), ids=lambda canteen: canteen.name)
|
||||||
|
def canteen(request):
|
||||||
|
return request.param
|
||||||
|
|
||||||
|
|
||||||
|
requires_online_api = pytest.mark.skipif(
|
||||||
|
not bool(os.getenv(ENV_ENABLED)),
|
||||||
|
reason='Querying the online API is disabled. Turn on by setting env variable %s.' % ENV_ENABLED
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@requires_online_api
|
||||||
|
def test_retrieval(canteen):
|
||||||
|
menu = download_menu(MenuParams(canteen_id=canteen.id, chash=canteen.chash))
|
||||||
|
feed.render(canteen, menu)
|
||||||
Reference in New Issue
Block a user