Skip to content
This repository has been archived by the owner on Nov 20, 2022. It is now read-only.

Commit

Permalink
Remaining locations which are supported by current implementation (#8)
Browse files Browse the repository at this point in the history
* correct some prices and add some missing ones

* solve multi row dishes problem (see comment in changes)

* allow location_id as parameter and add missing locations which have
same interface and works with this implementation

* adapt the cli for the new locations

* adapt parse.sh for the new locations

* adapt README for the new locations

* prices , -> .

* update tests „self service“

* switch to just "individual"
  • Loading branch information
raabf authored and srehwald committed Dec 4, 2017
1 parent add3b31 commit 6da5768
Show file tree
Hide file tree
Showing 6 changed files with 116 additions and 36 deletions.
26 changes: 21 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,27 @@
[![Build Status](https://travis-ci.org/srehwald/eat-api.svg?branch=master)](https://travis-ci.org/srehwald/eat-api)

Simple static API for the canteens of the [Studentenwerk München](http://www.studentenwerk-muenchen.de) as well as some other locations. By now, the following locations are supported:
- Mensa Garching (mensa-garching)
- Mensa Arcisstraße (mensa-arcisstrasse)
- StuBistro Großhadern (stubistro-grosshadern)
- FMI Bistro Garching (fmi-bistro)
- IPP Bistro Garching (ipp-bistro)

- Mensa Arcisstraße (mensa-arcisstr), Arcisstraße 17, München
- Mensa Garching (mensa-garching), Lichtenbergstraße 2, Garching
- Mensa Leopoldstraße (mensa-leopoldstr), Leopoldstraße 13a, München
- Mensa Lothstraße (mensa-lothstr), Lothstraße 13d, München
- Mensa Martinsried (mensa-martinsried), Großhaderner Straße 6, Planegg-Martinsried
- Mensa Pasing (mensa-pasing), Am Stadtpark 20, München
- Mensa Weihenstephan (mensa-weihenstephan), Maximus-von-Imhof-Forum 5, Freising
- StuBistro Arcisstraße (stubistro-arcisstr), Arcisstraße 12, München
- StuBistro Goethestraße (stubistro-goethestr), Goethestraße 70, München
- StuBistro Großhadern (stubistro-grosshadern), Butenandtstraße 13, Gebäude F, München
- StuBistro Rosenheim (stubistro-rosenheim), Hochschulstraße 1, Rosenheim
- StuBistro Schellingstraße (stubistro-schellingstr), Schellingstraße 3, München
- StuCafé Adalbertstraße (stucafe-adalbertstr), Adalbertstraße 5, München
- StuCafé Akademie Weihenstephan (stucafe-akademie-weihenstephan), Alte Akademie 1, Freising
- StuCafé Boltzmannstraße (stucafe-boltzmannstr), Boltzmannstraße 15, Garching
- StuCafé in der Mensa Garching (stucafe-garching), Lichtenbergstraße 2, Garching
- StuCafé Karlstraße (stucafe-karlstr), Karlstraße 6, München
- StuCafé Pasing (stucafe-pasing), Am Stadtpark 20, München
- FMI Bistro Garching (fmi-bistro), Boltzmannstraße 3, 85748 Garching
- IPP Bistro Garching (ipp-bistro), Boltzmannstraße 2, 85748 Garching

## Usage

Expand Down
24 changes: 16 additions & 8 deletions scripts/parse.sh
Original file line number Diff line number Diff line change
@@ -1,8 +1,16 @@
#!/bin/sh
mkdir dist
python src/main.py mensa-garching -j ./dist/mensa-garching
python src/main.py mensa-arcisstrasse -j ./dist/mensa-arcisstrasse
python src/main.py stubistro-grosshadern -j ./dist/stubistro-grosshadern
python src/main.py fmi-bistro -j ./dist/fmi-bistro
python src/main.py ipp-bistro -j ./dist/ipp-bistro
tree dist/
#!/bin/bash

# --parents prevents error exit if folder already exists
mkdir --parents dist

loc_list=( "mensa-arcisstr" "mensa-arcisstrasse" "mensa-garching" "mensa-leopoldstr" "mensa-lothstr" \
"mensa-martinsried" "mensa-pasing" "mensa-weihenstephan" "stubistro-arcisstr" "stubistro-goethestr" \
"stubistro-großhadern" "stubistro-grosshadern" "stubistro-rosenheim" "stubistro-schellingstr" "stucafe-adalbertstr" \
"stucafe-akademie-weihenstephan" "stucafe-boltzmannstr" "stucafe-garching" "stucafe-karlstr" "stucafe-pasing" \
"ipp-bistro" "fmi-bistro" )

for loc in "${loc_list[@]}"; do
python src/main.py "$loc" --jsonify "./dist/$loc"
done

tree dist/
6 changes: 4 additions & 2 deletions src/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,13 @@

import argparse

import menu_parser


def parse_cli_args():
parser = argparse.ArgumentParser()
parser.add_argument('location', choices=['mensa-garching', 'mensa-arcisstrasse', 'stubistro-grosshadern',
'fmi-bistro', 'ipp-bistro'],
parser.add_argument('location', choices=(
['fmi-bistro', 'ipp-bistro'] + list(menu_parser.StudentenwerkMenuParser.location_id_mapping.keys())),
help='the location you want to eat at')
parser.add_argument('-d', '--date', help='date (DD.MM.YYYY) of the day of which you want to get the menu')
parser.add_argument('-j', '--jsonify',
Expand Down
2 changes: 1 addition & 1 deletion src/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ def get_menu_parsing_strategy(location):
parser = None

# set parsing strategy based on location
if location in ["mensa-garching", "mensa-arcisstrasse", "stubistro-grosshadern"]:
if isinstance(location, int) or location in menu_parser.StudentenwerkMenuParser.location_id_mapping.keys():
parser = menu_parser.StudentenwerkMenuParser()
elif location == "fmi-bistro":
parser = menu_parser.FMIBistroMenuParser()
Expand Down
88 changes: 71 additions & 17 deletions src/menu_parser.py
Original file line number Diff line number Diff line change
@@ -1,13 +1,15 @@
# -*- coding: utf-8 -*-

import requests
import re
import unicodedata
import sys
import tempfile
import unicodedata
from datetime import datetime
from lxml import html
from subprocess import call

import requests
from lxml import html

import util
from entities import Dish, Menu

Expand All @@ -24,23 +26,64 @@ class StudentenwerkMenuParser(MenuParser):
"Aktionsessen 5": 2.8, "Aktionsessen 6": 3.0, "Aktionsessen 7": 3.2, "Aktionsessen 8": 3.5, "Aktionsessen 9": 4,
"Aktionsessen 10": 4.5, "Biogericht 1": 1.55, "Biogericht 2": 1.9, "Biogericht 3": 2.4, "Biogericht 4": 2.6,
"Biogericht 5": 2.8, "Biogericht 6": 3.0, "Biogericht 7": 3.2, "Biogericht 8": 3.5, "Biogericht 9": 4,
"Biogericht 10": 4.5, "Self-Service": "Self-Service", "Self-Service Grüne Mensa": "Self-Service Grüne Mensa",
"Baustellenteller": "Baustellenteller (2.40€ - 3.45€)", "Fast Lane": "Fast Lane (3.50€ - 5.20€)"
"Biogericht 10": 4.5, "Self-Service": "0.68€ / 100g", "Self-Service Grüne Mensa": "0.33€ / 100g",
"Baustellenteller": "Baustellenteller (> 2.40€)", "Fast Lane": "Fast Lane (> 3.50€)",
"Länder-Mensa": "0.75€ / 100g", "Mensa Spezial Pasta": "0.60€ / 100g",
"Mensa Spezial": "individual", # 0.85€ / 100g (one-course dishes have individual prices)
}
links = {
"mensa-garching": 'http://www.studentenwerk-muenchen.de/mensa/speiseplan/speiseplan_422_-de.html',
"mensa-arcisstrasse": "http://www.studentenwerk-muenchen.de/mensa/speiseplan/speiseplan_421_-de.html",
"stubistro-grosshadern": "http://www.studentenwerk-muenchen.de/mensa/speiseplan/speiseplan_414_-de.html"

# Some of the locations do not use the general Studentenwerk system and do not have a location id.
# It differs how they publish their menus — probably everyone needs an own parser.
# For documentation they are in the list but commented out.
location_id_mapping = {
"mensa-arcisstr": 421,
"mensa-arcisstrasse": 421, # backwards compatibility
"mensa-garching": 422,
"mensa-leopoldstr": 411,
"mensa-lothstr": 431,
"mensa-martinsried": 412,
"mensa-pasing": 432,
"mensa-weihenstephan": 423,
"stubistro-arcisstr": 450,
# "stubistro-benediktbeuern": ,
"stubistro-goethestr": 418,
"stubistro-großhadern": 414,
"stubistro-grosshadern": 414,
"stubistro-rosenheim": 441,
"stubistro-schellingstr": 416,
# "stubistro-schillerstr": ,
"stucafe-adalbertstr": 512,
"stucafe-akademie-weihenstephan": 526,
# "stucafe-audimax" ,
"stucafe-boltzmannstr": 527,
"stucafe-garching": 524,
# "stucafe-heßstr": ,
"stucafe-karlstr": 532,
# "stucafe-leopoldstr": ,
# "stucafe-olympiapark": ,
"stucafe-pasing": 534,
# "stucafe-weihenstephan": ,
}

base_url = "http://www.studentenwerk-muenchen.de/mensa/speiseplan/speiseplan_{}_-de.html"

def parse(self, location):
page_link = self.links.get(location, "")
if page_link != "":
page = requests.get(page_link)
tree = html.fromstring(page.content)
return self.get_menus(tree)
else:
return None
"""`location` can be either the numeric location id or its string alias as defined in `location_id_mapping`"""
try:
location_id = int(location)
except ValueError:
try:
location_id = self.location_id_mapping[location]
except KeyError:
print("Location {} not found. Choose one of {}.".format(
location, ', '.join(self.location_id_mapping.keys())), file=sys.stderr)
return None

page_link = self.base_url.format(location_id)

page = requests.get(page_link)
tree = html.fromstring(page.content)
return self.get_menus(tree)

def get_menus(self, page):
# initialize empty dictionary
Expand Down Expand Up @@ -89,9 +132,20 @@ def __parse_dishes(menu_html):
# create dictionary out of dish name and dish type
dishes_dict = {dish_name: dish_type for dish_name, dish_type in zip(dish_names, dish_types)}
# create Dish objects with correct prices; if price is not available, -1 is used instead
dishes = [Dish(name, StudentenwerkMenuParser.prices.get(dishes_dict[name], "N/A")) for name in dishes_dict]

dishes = []
for name in dishes_dict:
if not dishes_dict[name]:
# some dishes are multi-row. That means that for the same type the dish is written in multiple rows.
# From the second row on the type is then just empty. In that case, we just use the price of the
# previous dish.
dishes.append(Dish(name, dishes[-1].price))
else:
dishes.append(Dish(name, StudentenwerkMenuParser.prices.get(dishes_dict[name], "N/A")))

return dishes


class FMIBistroMenuParser(MenuParser):
url = "http://www.wilhelm-gastronomie.de/tum-garching"
allergens = ["Gluten", "Laktose", "Milcheiweiß", "Hühnerei", "Soja", "Nüsse", "Erdnuss", "Sellerie", "Fisch",
Expand Down
6 changes: 3 additions & 3 deletions src/test/test_menu_parser.py
Original file line number Diff line number Diff line change
Expand Up @@ -48,9 +48,9 @@ class StudentenwerkMenuParserTest(unittest.TestCase):
dish1_4_arcisstrasse = Dish("Pasta Pomodori", 1.9)
dish1_5_arcisstrasse = Dish("Gebackene Calamari-Ringe mit Zitronen-Knoblauch-Dip", 2.6)
dish1_6_arcisstrasse = Dish("Seelachsfilet (MSC) im Sesammantel mit Zitronen-Knoblauch-Dip", 2.6)
dish1_7_arcisstrasse = Dish("Pasta Pomodori (2)", "Self-Service")
dish1_8_arcisstrasse = Dish("Kartoffelgulasch mit Paprika (2)", "Self-Service")
dish1_9_arcisstrasse = Dish("Pasta mit Sojabolognese", "Self-Service")
dish1_7_arcisstrasse = Dish("Pasta Pomodori (2)", "0.68€ / 100g")
dish1_8_arcisstrasse = Dish("Kartoffelgulasch mit Paprika (2)", "0.68€ / 100g")
dish1_9_arcisstrasse = Dish("Pasta mit Sojabolognese", "0.68€ / 100g")
menu1_arcisstrasse = Menu(menu1_date, [dish1_1_arcisstrasse, dish1_2_arcisstrasse, dish1_3_arcisstrasse,
dish1_4_arcisstrasse, dish1_5_arcisstrasse, dish1_6_arcisstrasse,
dish1_7_arcisstrasse, dish1_8_arcisstrasse, dish1_9_arcisstrasse])
Expand Down

0 comments on commit 6da5768

Please sign in to comment.