diff --git a/app.py b/app.py new file mode 100644 index 0000000..e944251 --- /dev/null +++ b/app.py @@ -0,0 +1,17 @@ +from flask import Flask, abort + +import parsers +import utils + +app = Flask(__name__) + +parsers.define_parsers() + + +@app.route('/mensa//') +def mensa(parser_name, mensa_name): + try: + return utils.get_parser(parser_name).parse(mensa_name) + except KeyError: + abort(404) + diff --git a/parsers/__init__.py b/parsers/__init__.py new file mode 100644 index 0000000..ca3a2c6 --- /dev/null +++ b/parsers/__init__.py @@ -0,0 +1,5 @@ +from parsers import tuebingen + + +def define_parsers(): + tuebingen.define_parsers() diff --git a/parsers/tuebingen.json b/parsers/tuebingen.json new file mode 100644 index 0000000..5625a0f --- /dev/null +++ b/parsers/tuebingen.json @@ -0,0 +1,76 @@ +{ + "name": "tuebingen-hohenheim", + "base_url": "https://www.my-stuwe.de/wp-json/mealplans/v1/canteens/", + "canteens": [ + { + "id": "mensa-shedhalle", + "suffix": "611", + "name": "Mensa Shedhalle", + "street": "Schlachthausstraße 13", + "city": "72074 Tübingen" + }, + { + "id": "mensa-morgenstelle", + "suffix": "621", + "name": "Mensa Morgenstelle", + "street": "Auf der Morgenstelle 26", + "city": "72076 Tübingen" + }, + { + "id": "mensa-prinz_karl", + "suffix": "623", + "name": "Mensa Prinz Karl", + "street": "Hafengasse 6", + "city": "72070 Tübingen" + }, + { + "id": "mensa-hohenheim", + "suffix": "661", + "name": "Mensa Hohenheim", + "street": "Garbenstraße 29", + "city": "70599 Stuttgart" + }, + { + "id": "mensa-reutlingen", + "suffix": "631", + "name": "Mensa Reutlingen", + "street": "Alteburgstraße 150 Gebäude 7", + "city": "72762 Reutlingen" + }, + { + "id": "mensa-nuertingen", + "suffix": "665", + "name": "Mensa Nürtingen", + "street": "Neckarsteige 6-10", + "city": "72622 Nürtingen" + }, + { + "id": "mensa-rottenburg", + "suffix": "655", + "name": "Mensa Rottenburg", + "street": "Schadenweiler Hof 1", + "city": "72108 Rottenburg" + }, + { + "id": "mensa-albstadt", + "suffix": "645", + "name": "Mensa Albstadt", + "street": "Jakobstraße 1", + "city": "72458 Albstadt" + }, + { + "id": "mensa-sigmaringen", + "suffix": "640", + "name": "Mensa Sigmaringen", + "street": "Anton-Günther-Straße 51", + "city": " 72488 Sigmaringen" + }, + { + "id": "cafeteria-morgenstelle", + "suffix": "724", + "name": "Cafeteria Morgenstelle", + "street": "Auf der Morgenstelle 26", + "city": "72076 Tübingen" + } + ] +} \ No newline at end of file diff --git a/parsers/tuebingen.py b/parsers/tuebingen.py new file mode 100644 index 0000000..734e94d --- /dev/null +++ b/parsers/tuebingen.py @@ -0,0 +1,93 @@ +import json +import re +from urllib.request import urlopen, Request + +from pyopenmensa.feed import LazyBuilder + +from utils import Parser + +allergens = { + 'Gl-a': 'Weizen', + 'Gl-b': 'Roggen', + 'Gl-c': 'Gerste', + 'Gl-d': 'Hafer', + 'Gl-e': 'Dinkel', + 'Nu-a': 'Mandeln', + 'Ei': 'Eier', + 'Er': 'Erdnüsse', + 'So': 'Soja', + 'Sn': 'Senf', + 'Kr': 'Krebstiere (Krusten- und Schalentiere)', + 'Fi': 'Fisch', + 'ML': 'Milch/Laktose', + 'Se': 'Sellerie', + 'Sf': 'Schwefeldioxid und Sulfite', + 'Sa': 'Sesam', + 'Lu': 'Lupine', + 'We': 'Weichtiere', + 'ALK': 'Alkohol' +} + +additives = { + '1': 'Farbstoff', + '2': 'Konservierungsstoff', + '3': 'Nitritpökelsalz', + '4': 'Antioxidationsmittel', + '5': 'Geschmacksverstärker', + '6': 'geschwefelt', + '7': 'geschwärzt', + '8': 'gewachst', + '9': 'Süßungsmittel', + '10': 'enthält eine Phenylalaninquelle', + '11': 'Phosphat' +} + +legend = { + 'F': 'Fisch', 'G': 'Geflügel', 'K': 'Kalb', + 'L': 'Lamm', 'R': 'Rind', 'S': 'Schwein', + 'W': 'Wild', 'top': 'Empfehlung', + 'V': 'vegetarisch', 'vegan': 'vegan', +} + +multiple_whitespaces_regex = re.compile('\\s{2,}') + + +def parse_url(url, today=False): + canteen = LazyBuilder() + with urlopen(Request(url, None, {'User-Agent': 'Mozilla/5.0'})) as response: + data = json.loads(response.read()) + if len(data) == 0: + return canteen.toXMLFeed() + canteen_data = data[list(data)[0]] + menus = canteen_data['menus'] + canteen.name = canteen_data['canteen'] + canteen.setLegendData(legend) + + for menu in menus: + canteen.addMeal(menu['menuDate'], menu['menuLine'], + build_menu_name(menu['menu']) if len(menu['menu']) != 0 else menu['menuLine'], + build_allergens_additives(menu['allergens'], menu['additives']), + {'student': menu['studentPrice'], 'pupil': menu['pupilPrice'], 'other': menu['guestPrice']}) + return canteen.toXMLFeed() + + +def build_menu_name(menu): + output = ', '.join(menu) + output = multiple_whitespaces_regex.sub(' ', output) + output = output.replace(' [', '(').replace(']', ')').replace('(vegan)', '') + return output + + +def build_allergens_additives(raw_allergens, raw_additives): + output = [allergens.get(element, element) for element in raw_allergens] + \ + [additives.get(element, element) for element in raw_additives] + return output + + +def define_parsers(): + with open('parsers/tuebingen.json') as canteen_file: + canteen_json = json.load(canteen_file) + + parser = Parser(canteen_json['name'], handler=parse_url, shared_prefix=canteen_json['base_url']) + for canteen in canteen_json['canteens']: + parser.define(canteen['id'], canteen['suffix']) diff --git a/utils.py b/utils.py new file mode 100644 index 0000000..7eb5f36 --- /dev/null +++ b/utils.py @@ -0,0 +1,21 @@ +parser_list = {} + + +class Parser: + sources = {} + + def __init__(self, name, handler, shared_prefix: str): + self.name = name + self.handler = handler + self.shared_prefix = shared_prefix + parser_list[name] = self + + def define(self, name: str, suffix: str): + self.sources[name] = suffix + + def parse(self, source: str): + return self.handler(self.shared_prefix + self.sources[source]) + + +def get_parser(parser_name: str) -> Parser: + return parser_list[parser_name]