diff --git a/.github/workflows/analyze-and-test.yml b/.github/workflows/analyze-and-test.yml index 3501f98..cd7bf67 100644 --- a/.github/workflows/analyze-and-test.yml +++ b/.github/workflows/analyze-and-test.yml @@ -59,3 +59,14 @@ jobs: uses: psf/black@stable with: options: "--check" + run-tests: + name: Tests pass + runs-on: [ubuntu-latest] + steps: + - uses: actions/checkout@v3 + with: + set-safe-directory: true + - name: Setup environment + uses: ./.github/actions/setup-env + - name: Run unit tests + run: pytest diff --git a/.github/workflows/build-and-release.yml b/.github/workflows/build-and-release.yml index 5167ed7..3a30b4e 100644 --- a/.github/workflows/build-and-release.yml +++ b/.github/workflows/build-and-release.yml @@ -18,11 +18,20 @@ jobs: set-safe-directory: true - name: Setup environment uses: ./.github/actions/setup-env - - name: Export version information - run: | - echo "Version ${{ github.ref_name }}" - echo "VERSION = '${{ github.ref_name }}'" > src/jmenu/version.py - name: Build executable - run: python3 -m build + run: | + rm -rf dist + python3 -m build + - name: Install sanity test + run: pip install dist/*.whl + - name: Import sanity test + shell: python + run: | + try: + import jmenu.main + print(jmenu.main.get_version()) + exit(0) + except ModuleNotFoundError: + exit(1) - name: Publish package uses: pypa/gh-action-pypi-publish@release/v1 diff --git a/.gitignore b/.gitignore index aac3a05..df92b11 100644 --- a/.gitignore +++ b/.gitignore @@ -7,3 +7,4 @@ build jmenu.spec .ruff_cache *.egg-info +.pytest_cache diff --git a/pyproject.toml b/pyproject.toml index c1b4ba8..002497c 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -25,3 +25,9 @@ build-backend = "setuptools.build_meta" [project.scripts] jmenu = "jmenu.main:main" + +[tool.pytest.ini_options] +pythonpath = "src" +addopts = [ + "--import-mode=importlib" +] diff --git a/requirements.txt b/requirements.txt index 5ab31a3..a373302 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,9 +1,13 @@ build==1.0.0 certifi==2023.7.22 charset-normalizer==3.2.0 +exceptiongroup==1.1.3 idna==3.4 +iniconfig==2.0.0 packaging==23.1 +pluggy==1.3.0 pyproject_hooks==1.0.0 +pytest==7.4.2 requests==2.31.0 tomli==2.0.1 urllib3==2.0.4 diff --git a/src/jmenu/api.py b/src/jmenu/api.py new file mode 100644 index 0000000..b982973 --- /dev/null +++ b/src/jmenu/api.py @@ -0,0 +1,98 @@ +"""api.py + +Contains functions used to wrangle the JAMIX API. +This file can be imported and exposes the following functions: + + * fetch_restaurant + * parse_items_from_response + * get_menu_items + +The following constants are also exposed: + + * API_URL +""" + + +import requests +from datetime import datetime +from .classes import Restaurant, MenuItem, SKIPPED_ITEMS + +API_URL = "https://fi.jamix.cloud/apps/menuservice/rest/haku/menu" + + +def fetch_restaurant(rest: Restaurant, fetch_date: datetime) -> list[dict]: + """Return the JSON response containing all menu information for [Restaurant] + + Parameters + ---------- + rest : Restaurant + dataclass containing relevant restaurant information, see classes.RESTAURANTS + fetch_date : datetime + datetime object used to fetch the date specified menu + + Returns + ------- + list[dict] + parsed response json + """ + response = requests.get( + f"{API_URL}/{rest.client_id}/{rest.kitchen_id}?lang=fi&date={fetch_date.strftime('%Y%m%d')}", + timeout=5, + ) + return response.json() + + +def get_menu_items(rest: Restaurant, fetch_date: datetime) -> list[MenuItem]: + """Returns a list of restaurant [MenuItems] + + Parameters + ---------- + rest : Restaurant + dataclass containing relevant restaurant information, see classes.RESTAURANTS + fetch_date : datetime + datetime object used to fetch the date specified menu + + Returns + ------- + list[MenuItem] + list of restaurant menu items, see classes.MenuItem + """ + data = fetch_restaurant(rest, fetch_date) + items = parse_items_from_response(data, rest.relevant_menus) + return items + + +def parse_items_from_response( + data: list[dict], relevant_menus: list[str] = [] +) -> list[MenuItem]: + """Returns a list of [MenuItems] parsed from JSON data + + Parameters + ---------- + data : list[dict] + parsed JSON response from the jamix API, see api._fetch_restaurant + relevant_menus : list[str] + list of menu names to consider when parsing + defaults to empty list + + Returns + ------- + list[MenuItem] + list of restaurant menu items, see classes.MenuItem""" + menus = [] + for kitchen in data: + for m_type in kitchen["menuTypes"]: + if len(relevant_menus) == 0 or m_type["menuTypeName"] in relevant_menus: + menus.extend(m_type["menus"]) + if len(menus) == 0: + return [] + items = [] + for menu in menus: + day = menu["days"][0] + mealopts = day["mealoptions"] + sorted(mealopts, key=lambda x: x["orderNumber"]) + for opt in mealopts: + for item in opt["menuItems"]: + if item["name"] not in SKIPPED_ITEMS: + items.append(MenuItem(item["name"], item["diets"])) + return items diff --git a/src/jmenu/classes.py b/src/jmenu/classes.py new file mode 100644 index 0000000..14094b6 --- /dev/null +++ b/src/jmenu/classes.py @@ -0,0 +1,102 @@ +"""Classes.py + +Contains dataclasses jmenu uses to manage data. +This file can be imported and exposes the following classes: + + * MenuItem + * Restaurant + * Marker + +The following collections are use-case specific to the University of Oulu: + + * MARKERS + * RESTAURANTS + * SKIPPED_ITEMS +""" + +from collections import namedtuple + + +_MenuItem = namedtuple("MenuItem", ["name", "diets"]) + + +class MenuItem(_MenuItem): + """Dataclass for single menu items and their properties + + Attributes + --- + name : str + name of the dish + diets : str + list of allergen markers + """ + + +_Restaurant = namedtuple( + "Restaurant", ["name", "client_id", "kitchen_id", "menu_type", "relevant_menus"] +) + + +class Restaurant(_Restaurant): + """Dataclass for relevant restaurant information + + Attributes + --- + name : str + name of the restaurant + client_id : str + internal jamix identifier used for restaurant providers + kitchen_id : str + internal jamix identifier used to assign menu content + menu_type : str + internal jamix identifier used to classify menus based on content + relevant_menus : str + menu names used for filtering out desserts etc. + """ + + +_Marker = namedtuple("Marker", ["letters", "explanation"]) + + +class Marker(_Marker): + """Dataclass for allergen information markings + + Attributes + --- + letters : str + allergen markings + explanation : str + extended information about the marker + """ + + +SKIPPED_ITEMS = [ + "proteiinilisäke", + "Täysjyväriisi", + "Lämmin kasvislisäke", + "Höyryperunat", + "Tumma pasta", + "Meillä tehty perunamuusi", +] + +RESTAURANTS = [ + Restaurant("Foobar", 93077, 49, 84, ["Foobar Salad and soup", "Foobar Rohee"]), + Restaurant("Foodoo", 93077, 48, 89, ["Foodoo Salad and soup", "Foodoo Reilu"]), + Restaurant("Kastari", 95663, 5, 2, ["Ruokalista"]), + Restaurant("Kylymä", 93077, 48, 92, ["Kylymä Rohee"]), + Restaurant("Mara", 93077, 49, 111, ["Salad and soup", "Ravintola Mara"]), + Restaurant("Napa", 93077, 48, 79, ["Napa Rohee"]), +] + +MARKERS = [ + Marker("G", "Gluteeniton"), + Marker("M", "Maidoton"), + Marker("L", "Laktoositon"), + Marker("SO", "Sisältää soijaa"), + Marker("SE", "Sisältää selleriä"), + Marker("MU", "Munaton"), + Marker("[S], *", "Kelan korkeakouluruokailunsuosituksen mukainen"), + Marker("SIN", "Sisältää sinappia"), + Marker("<3", "Sydänmerkki"), + Marker("VEG", "Vegaani"), +] diff --git a/src/jmenu/main.py b/src/jmenu/main.py index 5230e93..38a3cd8 100644 --- a/src/jmenu/main.py +++ b/src/jmenu/main.py @@ -1,34 +1,61 @@ -from .version import VERSION -from .restaurants import ( - RESTAURANTS, - MARKINGS, - API_URL, - SKIPPED_ITEMS, - Restaurant, -) +"""main.py + +This file contains the logic for executing jmenu from the command line. +This file can be imported and exposes the following functions: + * main + * get_version +""" + +from .classes import RESTAURANTS, MARKERS, MenuItem +from .api import get_menu_items from datetime import datetime, timedelta -import requests import argparse from time import time +from sys import exit +from importlib.metadata import version, PackageNotFoundError + +class _ArgsNamespace: + """Dataclass for managing command line arguments + + Attributes + --- + explain : bool + print allergen marker info + allergens : list[str] + highlight the provided allergen markers + tomorrow: bool + fetch the menus for tomorrow + """ -class ArgsNamespace: explain: bool allergens: list[str] tomorrow: bool def main(): - args = get_args() + """Fetch and print restaurant menus + + Returns + --- + success : int + returns 1 if any errors were encountered + returns 0 otherwise + """ + args = _get_args() if args.explain: - print_explanations() + _print_explanations() exit(0) start = time() - print_menu(args) + errors = _print_menu(args) print("Process took {:.2f} seconds.".format(time() - start)) + if errors: + exit(1) + else: + exit(0) -def get_args(): +def _get_args(): parser = argparse.ArgumentParser( description="Display University of Oulu restaurant menus for the day" ) @@ -37,7 +64,7 @@ def get_args(): "--version", action="version", help="display version information", - version=VERSION, + version=get_version(), ) parser.add_argument( "-e", @@ -64,89 +91,64 @@ def get_args(): nargs="+", help='list of allergens, for ex. "g veg"', ) - return parser.parse_args(namespace=ArgsNamespace()) + return parser.parse_args(namespace=_ArgsNamespace()) -def get_restaurant_menu_items(rest: Restaurant, fetch_date: datetime) -> list[dict]: - response = requests.get( - f"{API_URL}/{rest.client_id}/{rest.kitchen_id}?lang=fi&date={fetch_date.strftime('%Y%m%d')}" - ) - data = response.json() - menus = get_menus(data, rest) - if len(menus) == 0: - return [] - items = get_menu_items(menus, rest) - return items - - -def get_menu_items(menus: dict, rest: Restaurant) -> list[dict]: - items = [] - for menu in menus: - day = menu["days"][0] - mealopts = day["mealoptions"] - sorted(mealopts, key=lambda x: x["orderNumber"]) - for opt in mealopts: - for item in opt["menuItems"]: - if item["name"] not in SKIPPED_ITEMS: - items.append(item) - return items - - -def get_menus(data: dict, rest: Restaurant) -> list[dict]: - menus = [] - for kitchen in data: - for m_type in kitchen["menuTypes"]: - if m_type["menuTypeName"] in rest.relevant_menus: - menus.extend(m_type["menus"]) - return menus - - -def print_menu(args: ArgsNamespace): +def _print_menu(args: _ArgsNamespace): + errors = [] fetch_date = datetime.now() if args.tomorrow: fetch_date += timedelta(days=1) + allergens = [] if args.allergens: allergens = [x.lower() for x in args.allergens] - print_header(fetch_date) + _print_header(fetch_date) for res in RESTAURANTS: try: - items = get_restaurant_menu_items(res, fetch_date) - sorted(items, key=lambda x: x["orderNumber"]) + items = get_menu_items(res, fetch_date) if len(items) == 0: print(res.name.ljust(8), "--") else: print(res.name) if not allergens: - for item in items: - print("\t", item["name"], item["diets"]) + print(*[f"\t {item.name} {item.diets}" for item in items], sep="\n") else: - print_highlight(items, allergens) + _print_highlight(items, allergens) - except Exception: + except Exception as e: + errors.append(e) print("Couldn't fetch menu for", res.name) + return errors -def print_explanations(): - for mark in MARKINGS: +def _print_explanations(): + for mark in MARKERS: print(mark.letters, "\t", mark.explanation) -def print_highlight(items: list[str], allergens: list[str]): +def _print_highlight(items: list[MenuItem], allergens: list[str]): for item in items: - diets = [x.strip().lower() for x in item["diets"].split(",")] + diets = [diets.strip().lower() for diets in item.diets.split(",")] if all(marker in diets for marker in allergens): - print("\033[92m", "\t", item["name"], item["diets"], "\033[0m") + print("\033[92m", "\t", item.name, item.diets, "\033[0m") else: - print("\t", item["name"], item["diets"]) + print("\t", item.name, item.diets) -def print_header(fetch_date: datetime): +def _print_header(fetch_date: datetime): print("-" * 79) print("Menu for", fetch_date.strftime("%d.%m")) print("-" * 79) +def get_version() -> str: + try: + return version("jmenu") + except PackageNotFoundError: + return "development build" + + if __name__ == "__main__": main() diff --git a/src/jmenu/restaurants.py b/src/jmenu/restaurants.py deleted file mode 100644 index 6eb6867..0000000 --- a/src/jmenu/restaurants.py +++ /dev/null @@ -1,39 +0,0 @@ -from collections import namedtuple - -Restaurant = namedtuple( - "Restaurant", ["name", "client_id", "kitchen_id", "menu_type", "relevant_menus"] -) -Marking = namedtuple("Marking", ["letters", "explanation"]) - -API_URL = "https://fi.jamix.cloud/apps/menuservice/rest/haku/menu" - -SKIPPED_ITEMS = [ - "proteiinilisäke", - "Täysjyväriisi", - "Lämmin kasvislisäke", - "Höyryperunat", - "Tumma pasta", - "Meillä tehty perunamuusi", -] - -RESTAURANTS = [ - Restaurant("Foobar", 93077, 49, 84, ["Foobar Salad and soup", "Foobar Rohee"]), - Restaurant("Foodoo", 93077, 48, 89, ["Foodoo Salad and soup", "Foodoo Reilu"]), - Restaurant("Kastari", 95663, 5, 2, ["Ruokalista"]), - Restaurant("Kylymä", 93077, 48, 92, ["Kylymä Rohee"]), - Restaurant("Mara", 93077, 49, 111, ["Salad and soup", "Ravintola Mara"]), - Restaurant("Napa", 93077, 48, 79, ["Napa Rohee"]), -] - -MARKINGS = [ - Marking("G", "Gluteeniton"), - Marking("M", "Maidoton"), - Marking("L", "Laktoositon"), - Marking("SO", "Sisältää soijaa"), - Marking("SE", "Sisältää selleriä"), - Marking("MU", "Munaton"), - Marking("[S], *", "Kelan korkeakouluruokailunsuosituksen mukainen"), - Marking("SIN", "Sisältää sinappia"), - Marking("<3", "Sydänmerkki"), - Marking("VEG", "Vegaani"), -] diff --git a/src/jmenu/version.py b/src/jmenu/version.py deleted file mode 100644 index 970baa1..0000000 --- a/src/jmenu/version.py +++ /dev/null @@ -1 +0,0 @@ -VERSION = "development build" diff --git a/tests/test_api.py b/tests/test_api.py new file mode 100644 index 0000000..0229b89 --- /dev/null +++ b/tests/test_api.py @@ -0,0 +1,480 @@ +from jmenu.api import parse_items_from_response +from jmenu.classes import RESTAURANTS + +# ruff: noqa + + +def test_parsing_with_defaults(): + items = parse_items_from_response(response) + assert items[0].name is not None + assert len(items) == 20 # base menu contains 20 items + + +def test_parsing_with_rest_values(): + rest = list(filter(lambda x: x.name == "Mara", RESTAURANTS)).pop() + assert rest is not None + items = parse_items_from_response(response, rest.relevant_menus) + assert len(items) == 6 # Mara relevant menu should result in 6 items + + +response = [ + { + "kitchenName": "Ravintola Mara", + "kitchenId": 49, + "address": "", + "city": "", + "email": "", + "phone": "", + "info": "", + "menuTypes": [ + { + "menuTypeId": 31, + "menuTypeName": "Jälkiruoka", + "menus": [ + { + "menuName": "Mara jälkiruoka (37-42)", + "menuAdditionalName": "BI 11.09.2023-20.10.2023", + "menuId": 4115, + "days": [ + { + "date": 20230913, + "weekday": 3, + "mealoptions": [ + { + "name": "JÄLKIRUOKA", + "orderNumber": 50, + "id": 8, + "menuItems": [ + { + "name": "Kookospannacotta", + "orderNumber": 1, + "portionSize": 100, + "diets": "", + "ingredients": "Valkoinen leivontasuklaa (Sokeri, kaakaovoi, täysMAITOjauhe, LAKTOOSI, VOIrasva, emulgointiaine (auringonkukkalesitiini, E476), vanilja-aromi. Saattaa sisältää pieniä määriä gluteenia sisältäviä viljoja, soijaa, kananmunaa, pähkinöitä ja maapähkinää.) (maito), Vispikerma (KERMA, stabilointiaine (karrageeni)) (Maito), Kookoskerma (Kookospähkinäuute (90 %), vesi), Tomusokeri (Sokeri ja perunatärkkelys 2%), Mansikka (mansikka (100 %)), Mustikka (pensasmustikka), Liivate (Liivatelehti. Ihmisravinnoksi tarkoitettu gelatiini. Valmistettu sian nahasta.), Valkolupiininverso (suomalainen valkolupiinin verso) (lupiini), Vaniljatanko (Vaniljatanko.)", + } + ], + } + ], + } + ], + } + ], + }, + { + "menuTypeId": 51, + "menuTypeName": "Salad and soup", + "menus": [ + { + "menuName": "Mara Salad & Soup (37-42)", + "menuAdditionalName": "BI 11.09.2023-20.10.2023", + "menuId": 4114, + "days": [ + { + "date": 20230913, + "weekday": 3, + "mealoptions": [ + { + "name": "LOUNAS KEITTO", + "orderNumber": 24, + "id": 6, + "menuItems": [ + { + "name": "Punajuurisosekeitto", + "orderNumber": 1, + "portionSize": 402, + "diets": "G, L, Mu, *", + "ingredients": "Vesi, Punajuuri (karbokuorittu punajuuri (100%)), Peruna (peruna, hapettumisenestoaine (NATRIUMDISULFIITTI)) (Rikkidioksidi), Ruokakerma, Sipuli (sipuli (Puola)), Punajuurikuutio mausteliemessä (Punajuuri, vesi, sokeri, happo (etikkahappo), säilöntäaine (kaliumsorbaatti), neilikka-aromi.), Vähäsuolainen kasvisliemijauhe (Suola, maltodekstriini, sipuli 10 %, luontainen aromi, palsternakka 5,2 %, porkkana 4,6 %, purjo 0,3 %. Kasvikset 20 %.), Suola (Ruokasuola ja paakkuuntumisenestoaine (E 536). Valmistettu vakuumisuolasta lisäämällä jodia kaliumjodidina.), Timjami (Timjami.)", + }, + { + "name": "Creme fraiche", + "orderNumber": 2, + "portionSize": 30, + "diets": "G, L, Mu", + "ingredients": "Creme fraiche (Pastöroitu KERMA, sakeuttamisaineet (E412. E1442), hapate.) (Maito)", + }, + { + "name": "proteiinilisäke", + "orderNumber": 3, + "portionSize": 60, + "diets": "", + "ingredients": "***", + }, + ], + } + ], + } + ], + } + ], + }, + { + "menuTypeId": 82, + "menuTypeName": "Foobar Salad and soup", + "menus": [ + { + "menuName": "Foobar Salad & Soup (37-42)", + "menuAdditionalName": "BI 11.09.2023-20.10.2023", + "menuId": 4117, + "days": [ + { + "date": 20230913, + "weekday": 3, + "mealoptions": [ + { + "name": "LOUNAS KEITTO", + "orderNumber": 24, + "id": 6, + "menuItems": [ + { + "name": "Punajuurisosekeitto", + "orderNumber": 1, + "portionSize": 402, + "diets": "G, L, Mu, *", + "ingredients": "Vesi, Punajuuri (karbokuorittu punajuuri (100%)), Peruna (peruna, hapettumisenestoaine (NATRIUMDISULFIITTI)) (Rikkidioksidi), Ruokakerma, Sipuli (sipuli (Puola)), Punajuurikuutio mausteliemessä (Punajuuri, vesi, sokeri, happo (etikkahappo), säilöntäaine (kaliumsorbaatti), neilikka-aromi.), Vähäsuolainen kasvisliemijauhe (Suola, maltodekstriini, sipuli 10 %, luontainen aromi, palsternakka 5,2 %, porkkana 4,6 %, purjo 0,3 %. Kasvikset 20 %.), Suola (Ruokasuola ja paakkuuntumisenestoaine (E 536). Valmistettu vakuumisuolasta lisäämällä jodia kaliumjodidina.), Timjami (Timjami.)", + }, + { + "name": "Creme fraiche", + "orderNumber": 2, + "portionSize": 30, + "diets": "G, L, Mu", + "ingredients": "Creme fraiche (Pastöroitu KERMA, sakeuttamisaineet (E412. E1442), hapate.) (Maito)", + }, + { + "name": "proteiinilisäke", + "orderNumber": 3, + "portionSize": 60, + "diets": "", + "ingredients": "***", + }, + ], + } + ], + } + ], + } + ], + }, + { + "menuTypeId": 83, + "menuTypeName": "Foobar Jälkiruoka", + "menus": [ + { + "menuName": "Foobar jälkiruoka (37-42)", + "menuAdditionalName": "BI 11.09.-20.10.2023", + "menuId": 4118, + "days": [ + { + "date": 20230913, + "weekday": 3, + "mealoptions": [ + { + "name": "JÄLKIRUOKA", + "orderNumber": 50, + "id": 8, + "menuItems": [ + { + "name": "Kookospannacotta", + "orderNumber": 1, + "portionSize": 100, + "diets": "", + "ingredients": "Valkoinen leivontasuklaa (Sokeri, kaakaovoi, täysMAITOjauhe, LAKTOOSI, VOIrasva, emulgointiaine (auringonkukkalesitiini, E476), vanilja-aromi. Saattaa sisältää pieniä määriä gluteenia sisältäviä viljoja, soijaa, kananmunaa, pähkinöitä ja maapähkinää.) (maito), Vispikerma (KERMA, stabilointiaine (karrageeni)) (Maito), Kookoskerma (Kookospähkinäuute (90 %), vesi), Tomusokeri (Sokeri ja perunatärkkelys 2%), Mansikka (mansikka (100 %)), Mustikka (pensasmustikka), Liivate (Liivatelehti. Ihmisravinnoksi tarkoitettu gelatiini. Valmistettu sian nahasta.), Valkolupiininverso (suomalainen valkolupiinin verso) (lupiini), Vaniljatanko (Vaniljatanko.)", + } + ], + } + ], + } + ], + } + ], + }, + { + "menuTypeId": 84, + "menuTypeName": "Foobar Rohee", + "menus": [ + { + "menuName": "Ravintola Foobar Rohee (37-42)", + "menuAdditionalName": "BI 11.09.2023-20.10.2023", + "menuId": 4116, + "days": [ + { + "date": 20230913, + "weekday": 3, + "mealoptions": [ + { + "name": "KASVIS OP", + "orderNumber": 7, + "id": 106, + "menuItems": [ + { + "name": "Pinaatti-linssikiusaus", + "orderNumber": 1, + "portionSize": 380, + "diets": "M, Mu, *, VEG", + "ingredients": "Peruna (Peruna 100%), Kaurajuoma, Vihreä linssi (Liotetut linssit, vesi, suola. Saattaa sisältää pieniä määriä VEHNÄÄ.) (Gluteeni, vehnä), Munakoiso, Kesäkurpitsa (Kesäkurpitsa), Pinaatti, Vaalea kastikeaines (Maissitärkkelys, muunnettu perunatärkkelys, maltodekstriini, suola, kasvisrasvat (palmurasva, karitevoi), emulgointiaine (E 451), hiivauute, muunnettu maissitärkkelys, aromi, väriaine (E 150c), kurkuma, riisijauho.), Vähäsuolainen kasvisliemijauhe (Suola, maltodekstriini, sipuli 10 %, luontainen aromi, palsternakka 5,2 %, porkkana 4,6 %, purjo 0,3 %. Kasvikset 20 %.), Sokeri (sokeri), Suola (Ruokasuola ja paakkuuntumisenestoaine (E 536). Valmistettu vakuumisuolasta lisäämällä jodia kaliumjodidina.), Välimeren yrttimausteseos (Punainen paprika (25%), yrtit (25%)(basilika, oregano, kynteli, timjami), korianteri, valkosipuli, sipuli, maustepippuri.)", + } + ], + }, + { + "name": "LOUNAS I", + "orderNumber": 11, + "id": 3, + "menuItems": [ + { + "name": "Broilerpyörykkä", + "orderNumber": 1, + "portionSize": 120, + "diets": "M, Mu, *", + "ingredients": "Broilerpyörykkä (broilerinliha (50 %), vesi, SOIJAPROTEIINI, koneellisesti eroteltu siipikarjanliha (Suomi), korppujauho (VEHNÄ), mausteet (sipuli, valkopippuri, paprika, mustapippuri, valkosipuli, korianteri, maustepippuri), perunajauho, stabilointiaine auringonkukkalesitiini, dekstroosi, happamuudensäätöaineet (kaliumkloridi, E 300), jodioitu suola, hiivauute, stabilointiaine E 461, aromi. Broilerinlihan alkuperä Suomi) (Gluteeni, Soijapapu, vehnä)", + }, + { + "name": "Vihreä thai-kastike", + "orderNumber": 2, + "portionSize": 40, + "diets": "G, M, Mu, VEG", + "ingredients": "Vesi, Kookosmaito (kookospähkinäuute (75 %), vesi, emulgointiaine (E471), stabilointiaine (E466).), Vihreä currytahna (Sitruunaruoho, vihreä chili 15 %, vesi, sipuli, valkosipuli, galangal, suola, korianteri, ryppylime, kurkuma.), Vaalea kastikeaines (Maissitärkkelys, muunnettu perunatärkkelys, maltodekstriini, suola, kasvisrasvat (palmurasva, karitevoi), emulgointiaine (E 451), hiivauute, muunnettu maissitärkkelys, aromi, väriaine (E 150c), kurkuma, riisijauho.), Fariinisokeri (Sokeri ja ruokosokerisiirappi), Valkosipulimurska (Valkosipuli 80%, vesi, säilöntäaine: kaliumsorbaatti, stabilointiaine: sitruunahappo.), Maissitärkkelys, Vähäsuolainen kasvisliemijauhe (Suola, maltodekstriini, sipuli 10 %, luontainen aromi, palsternakka 5,2 %, porkkana 4,6 %, purjo 0,3 %. Kasvikset 20 %.), Suola (Ruokasuola ja paakkuuntumisenestoaine (E 536). Valmistettu vakuumisuolasta lisäämällä jodia kaliumjodidina.), Rypsiöljy (rapsiöljy)", + }, + ], + }, + { + "name": "LOUNAS II", + "orderNumber": 13, + "id": 4, + "menuItems": [ + { + "name": "Pekoninen porsaanlihapata", + "orderNumber": 1, + "portionSize": 290, + "diets": "G, L, M, Mu, *", + "ingredients": "Vesi, Possunliha kuutio (suomalainen porsaanliha, jodioitu suola.LAKTOOSITON. GLUTEENITON. RUNSASPROTEIININEN.100 g:aan tuotetta käytetty 123 g lihaa.), Porkkana (porkkana), Tomaattisose (tomaatti (100 %, Portugali)), Pekonirouhe (viljaporsaanliha, vesi, jodioitu suola, dekstroosi, stabilointiaine E 450, hapettumisenestoaine E 301, säilöntäaine natriumnitriitti. Viljaporsaanlihan alkuperä Suomi), Sipuli (sipuli (Puola)), Maissitärkkelys, Sokeri (sokeri), Vähäsuolainen lihaliemijauhe (Maltodekstriini, suola, luontainen aromi, naudanlihauute 8,9 %, sipuli.), Rypsiöljy (rapsiöljy), Suola (Ruokasuola ja paakkuuntumisenestoaine (E 536). Valmistettu vakuumisuolasta lisäämällä jodia kaliumjodidina.)", + } + ], + }, + { + "name": "LÄMPIMÄT LISUKKEET", + "orderNumber": 71, + "id": 50, + "menuItems": [ + { + "name": "Höyryperunat", + "orderNumber": 1, + "portionSize": 20, + "diets": "G, M, Mu, *, VEG", + "ingredients": "Peruna (Peruna)", + }, + { + "name": "Tumma pasta", + "orderNumber": 2, + "portionSize": 15, + "diets": "L, M, Mu, *, VEG", + "ingredients": "Vesi, Täysjyväpasta (TäysjyvädurumVEHNÄ) (Gluteeni, vehnä)", + }, + { + "name": "Täysjyväriisi", + "orderNumber": 3, + "portionSize": 15, + "diets": "G, M, Mu, *, VEG", + "ingredients": "Vesi, Tummariisi (parboil-käsitelty pitkäjyväinen tumma riisi), Rypsiöljy (rapsiöljy), Suola (Ruokasuola ja paakkuuntumisenestoaine (E 536). Valmistettu vakuumisuolasta lisäämällä jodia kaliumjodidina.)", + }, + { + "name": "Lämmin kasvislisäke", + "orderNumber": 4, + "portionSize": 70, + "diets": "G, VEG", + "ingredients": "Lämmin kasvislisäke", + }, + ], + }, + ], + } + ], + } + ], + }, + { + "menuTypeId": 85, + "menuTypeName": "Foobar Fusion", + "menus": [ + { + "menuName": "Foobar Fusion (37-42)", + "menuAdditionalName": "BI 11.09.2023-20.10.2023", + "menuId": 4120, + "days": [ + { + "date": 20230913, + "weekday": 3, + "mealoptions": [ + { + "name": "FUSION", + "orderNumber": 30, + "id": 14, + "menuItems": [ + { + "name": "Tuplajuustoburger", + "orderNumber": 1, + "portionSize": 335, + "diets": "L", + "ingredients": "Naudan hampurilaispihvi (naudanliha (kasvatettu ja teurastettu: useat EU-maat), suola, pippuri), Hampurilaissämpylä (VEHNÄJAUHO, vesi, sokeri, rypsiöljy, hiiva, SEESAMINSIEMEN, jodioitu suola, emulgointiaine (E472e), säilöntäaine (E282), stabilointiaine (E516), hapettumisenestoaine (E300).) (Gluteeni, Seesaminsiemen, vehnä), Tomaatti (Tomaatti), Cheddarjuusto (CHEDDARJUUSTO (80%, EU ja muu kuin EU), vesi, VOI, sulatesuolat (E331, E339), luontainen JUUSTOaromi, suola, säilöntäaine (E200), värit (E160a, E160c), stabilointiaine (E322 auringonkukkalesitiini).) (Maito), Punasipuli (punasipuli (100 %)), Kevytmajoneesi (vesi, rapsiöljy (EU), muunnettu perunatärkkelys, KANANMUNA, sokeri, SINAPPI (vesi, SINAPINSIEMEN, sokeri, etikka, suola, mausteet), suola, etikka, stabilointiaine (E412), säilöntäaine (E202), happamuudensäätöaineet (E270, E330), väri (E160a)) (Muna, Sinappi), Chilimajoneesi (vesi, rapsiöljy (EU), sokeri, etikka, tomaattisose, muunnettu maissitärkkelys, KANANMUNANKELTUAINEN, jodioitu suola, mausteet (chili, juustokumina, valkosipuli, oregano, cayenne), sakeuttamisaineet (E415, E412), säilöntäaine (E202), paprikauute) (Muna), Jääsalaatti (Jääsalaatti), Vesi, Sokeri (sokeri), Punaviinietikka (punaviinietikka, säilöntäaine (kaliumvetySULFIITTI)) (Rikkidioksidi), Suola (Ruokasuola ja paakkuuntumisenestoaine (E 536). Valmistettu vakuumisuolasta lisäämällä jodia kaliumjodidina.), Mustapippuri (Mustapippuri (Tellicherry Garbled Special Extra Bold).)", + }, + { + "name": "Ranskalaiset perunat", + "orderNumber": 2, + "portionSize": 0, + "diets": "G, M, Mu, VEG", + "ingredients": "Ranskalaiset perunat (peruna 96% (EU), auringonkukkaöljy)", + }, + ], + }, + { + "name": "FUSION MEAL", + "orderNumber": 31, + "id": 44, + "menuItems": [ + { + "name": "Vegaaninen Tofu-Guocamoleburger", + "orderNumber": 1, + "portionSize": 319, + "diets": "L, M, *, VEG", + "ingredients": "Tofu (vesi, SOIJAPAPU 47 %, happamuudensäätöaine (E511)) (Soijapapu), Kaura-spelttihampurilaissämpylä (VEHNÄjauho, vesi, esiliotetut jyvät 11 % (RUIShapantaikina, SPELTIN jyvät), KAURAhiutale 5,9 %, sokeri, rypsiöljy, hiiva, VEHNÄGLUTEENI, VEHNÄrouhe, lesejauhoseos (VEHNÄ), suola, juurisikurikuitu, emulgointiaine (E472e), säilöntäaineet (E282, E202), hapettumisenestoaine (E300). Saattaa sisältää pieniä määriä seesaminsiemeniä.) (Gluteeni, kaura, ruis, Seesaminsiemen, speltti, vehnä), Tomaatti (Tomaatti), Guacamole (Avocado (70%), vesi, punainen paprika, sokeri, suola, valkosipuli, sipuli, hapettumisenestoaineet (E300), sakeuttamisaineet (E401), stabiliointiaineet (E415), jalapeno chili jauhe, happamuudensäätöaineet (E330).), Punasipuli (punasipuli (100 %)), Soijajogurtti (SOIJApohja (vesi, kuorittu SOIJApapu 7,9%), kookosrasva, rapsiöljy, dekstroosi, maissitärkkelys, happamuudensäätöaine (kalsiumsitraatti), sakeuttamisaine (pektiini), suola, hapate. Soijapapujen alkuperä Kanada ja USA.) (Soijapapu), Jääsalaatti (Jääsalaatti), Balsamietikka (viinietikka (Italia), rypälemehutiiviste, väri (E150d). Sisältää SULFIITTEJA.) (Rikkidioksidi), Sokeri (sokeri), Punaviinietikka (punaviinietikka, säilöntäaine (kaliumvetySULFIITTI)) (Rikkidioksidi), Suola (Ruokasuola ja paakkuuntumisenestoaine (E 536). Valmistettu vakuumisuolasta lisäämällä jodia kaliumjodidina.), Rypsiöljy (rapsiöljy)", + }, + { + "name": "Ranskalaiset perunat", + "orderNumber": 2, + "portionSize": 0, + "diets": "G, M, Mu, VEG", + "ingredients": "Ranskalaiset perunat (peruna 96% (EU), auringonkukkaöljy)", + }, + ], + }, + { + "name": "FUSION BURGER", + "orderNumber": 32, + "id": 45, + "menuItems": [ + { + "name": "Pohjosen Burger", + "orderNumber": 1, + "portionSize": 324, + "diets": "L", + "ingredients": "Naudan hampurilaispihvi (naudanliha (kasvatettu ja teurastettu: useat EU-maat), suola, pippuri), Kaura-spelttihampurilaissämpylä (VEHNÄjauho, vesi, esiliotetut jyvät 11 % (RUIShapantaikina, SPELTIN jyvät), KAURAhiutale 5,9 %, sokeri, rypsiöljy, hiiva, VEHNÄGLUTEENI, VEHNÄrouhe, lesejauhoseos (VEHNÄ), suola, juurisikurikuitu, emulgointiaine (E472e), säilöntäaineet (E282, E202), hapettumisenestoaine (E300). Saattaa sisältää pieniä määriä seesaminsiemeniä.) (Gluteeni, kaura, ruis, Seesaminsiemen, speltti, vehnä), Punasipuli (punasipuli (100 %)), Kevytmajoneesi (vesi, rapsiöljy (EU), muunnettu perunatärkkelys, KANANMUNA, sokeri, SINAPPI (vesi, SINAPINSIEMEN, sokeri, etikka, suola, mausteet), suola, etikka, stabilointiaine (E412), säilöntäaine (E202), happamuudensäätöaineet (E270, E330), väri (E160a)) (Muna, Sinappi), Tomaatti (Tomaatti), Savujuustocreme (JUUSTO mm. savuJUUSTO [MAITO, hapate, suola, happamuudensäätöaine (E509), säilöntäaine (E252)], vesi, Laktoositon VOI, sulatesuolat (E452, E339), modifioitu tapiokatärkkelys (E1442), säilöntäaine (E200).) (Maito), Vesi, Savupororouhe (poronliha(60%), saksanhirvenliha(20%), vesi, suola(2,6%), stabilointiaine E451, glukoosi, hapettumisenestoaine E301, luontainen savuaromi, säilöntäaine E250, lihapitoisuus 80%. lihan alkuperämaa: poro; suomi/ ruotsi, saksanhirvenliha; uusiseelanti.), Jääsalaatti (Jääsalaatti), Sokeri (sokeri), Punaviinietikka (punaviinietikka, säilöntäaine (kaliumvetySULFIITTI)) (Rikkidioksidi), Suola (Ruokasuola ja paakkuuntumisenestoaine (E 536). Valmistettu vakuumisuolasta lisäämällä jodia kaliumjodidina.), Mustapippurirouhe (Mustapippuri.)", + }, + { + "name": "Ranskalaiset perunat", + "orderNumber": 2, + "portionSize": 200, + "diets": "G, M, Mu, VEG", + "ingredients": "Ranskalaiset perunat (peruna 96% (EU), auringonkukkaöljy)", + }, + ], + }, + ], + } + ], + } + ], + }, + { + "menuTypeId": 111, + "menuTypeName": "Ravintola Mara", + "menus": [ + { + "menuName": "Mara Roheelounas (37-42)", + "menuAdditionalName": "BI 11.09.2023-20.10.2023", + "menuId": 4111, + "days": [ + { + "date": 20230913, + "weekday": 3, + "mealoptions": [ + { + "name": "KASVIS OP", + "orderNumber": 7, + "id": 106, + "menuItems": [ + { + "name": "Pinaatti-linssikiusaus", + "orderNumber": 1, + "portionSize": 380, + "diets": "M, Mu, *, VEG", + "ingredients": "Peruna (Peruna 100%), Kaurajuoma, Vihreä linssi (Liotetut linssit, vesi, suola. Saattaa sisältää pieniä määriä VEHNÄÄ.) (Gluteeni, vehnä), Munakoiso, Kesäkurpitsa (Kesäkurpitsa), Pinaatti, Vaalea kastikeaines (Maissitärkkelys, muunnettu perunatärkkelys, maltodekstriini, suola, kasvisrasvat (palmurasva, karitevoi), emulgointiaine (E 451), hiivauute, muunnettu maissitärkkelys, aromi, väriaine (E 150c), kurkuma, riisijauho.), Vähäsuolainen kasvisliemijauhe (Suola, maltodekstriini, sipuli 10 %, luontainen aromi, palsternakka 5,2 %, porkkana 4,6 %, purjo 0,3 %. Kasvikset 20 %.), Sokeri (sokeri), Suola (Ruokasuola ja paakkuuntumisenestoaine (E 536). Valmistettu vakuumisuolasta lisäämällä jodia kaliumjodidina.), Välimeren yrttimausteseos (Punainen paprika (25%), yrtit (25%)(basilika, oregano, kynteli, timjami), korianteri, valkosipuli, sipuli, maustepippuri.)", + } + ], + }, + { + "name": "LOUNAS I", + "orderNumber": 11, + "id": 3, + "menuItems": [ + { + "name": "Rapeat kalapalat", + "orderNumber": 1, + "portionSize": 150, + "diets": "M, Mu, *", + "ingredients": "Rapeat kalapalat (SEITI (64 %)(Pollachius virens, pyydetty troolilla, verkolla, nuotalla sekä koukuilla ja siimoilla Koillis-Atlantilta FAO-alue 27), VEHNÄjauho, rapsiöljy, SEMOLINAVEHNÄ, vesi, perunatärkkelys, maissitärkkelys, suola, VEHNÄGLUTEENI, hiiva, nostatusaineet (E450, E500), sokeri, VEHNÄtärkkelys, pippuriuute) (Gluteeni, Kala, vehnä)", + }, + { + "name": "Lime-tilli-kermaviilikastike", + "orderNumber": 2, + "portionSize": 30, + "diets": "G, L, Mu", + "ingredients": "Kermaviili (pastöroitu KERMA, hapate) (Maito), Sinappi (Vesi, glukoosi-fruktoosisiirappi, SINAPINSIEMEN (18 %), suola, happamuudensäätöaine (E260), mausteet (sipuli, valkopippuri), säilöntäaine (E211), väri (E150d), paprika-aromi.) (Sinappi), Sokeri (sokeri), Limemehu (Limetäysmehu (99,5%), limeöljy, hapettumisenestoaine (kaliumdiSULFIITTI).) (Rikkidioksidi), Suola (Ruokasuola ja paakkuuntumisenestoaine (E 536). Valmistettu vakuumisuolasta lisäämällä jodia kaliumjodidina.), Tilli (Tilli.), Valkopippuri (Valkopippuri.)", + }, + ], + }, + { + "name": "LOUNAS II", + "orderNumber": 13, + "id": 4, + "menuItems": [ + { + "name": "Pekoninen porsaanlihapata", + "orderNumber": 1, + "portionSize": 300, + "diets": "G, L, M, Mu, *", + "ingredients": "Vesi, Possunliha kuutio (suomalainen porsaanliha, jodioitu suola.LAKTOOSITON. GLUTEENITON. RUNSASPROTEIININEN.100 g:aan tuotetta käytetty 123 g lihaa.), Porkkana (porkkana), Tomaattisose (tomaatti (100 %, Portugali)), Pekonirouhe (viljaporsaanliha, vesi, jodioitu suola, dekstroosi, stabilointiaine E 450, hapettumisenestoaine E 301, säilöntäaine natriumnitriitti. Viljaporsaanlihan alkuperä Suomi), Sipuli (sipuli (Puola)), Maissitärkkelys, Sokeri (sokeri), Vähäsuolainen lihaliemijauhe (Maltodekstriini, suola, luontainen aromi, naudanlihauute 8,9 %, sipuli.), Rypsiöljy (rapsiöljy), Suola (Ruokasuola ja paakkuuntumisenestoaine (E 536). Valmistettu vakuumisuolasta lisäämällä jodia kaliumjodidina.)", + } + ], + }, + { + "name": "LÄMPIMÄT LISUKKEET", + "orderNumber": 71, + "id": 50, + "menuItems": [ + { + "name": "Höyryperunat", + "orderNumber": 1, + "portionSize": 20, + "diets": "G, M, Mu, *, VEG", + "ingredients": "Peruna (Peruna)", + }, + { + "name": "Tumma pasta", + "orderNumber": 2, + "portionSize": 15, + "diets": "L, M, Mu, *, VEG", + "ingredients": "Vesi, Täysjyväpasta (TäysjyvädurumVEHNÄ) (Gluteeni, vehnä)", + }, + { + "name": "Täysjyväriisi", + "orderNumber": 3, + "portionSize": 15, + "diets": "G, M, Mu, *, VEG", + "ingredients": "Vesi, Tummariisi (parboil-käsitelty pitkäjyväinen tumma riisi), Rypsiöljy (rapsiöljy), Suola (Ruokasuola ja paakkuuntumisenestoaine (E 536). Valmistettu vakuumisuolasta lisäämällä jodia kaliumjodidina.)", + }, + { + "name": "Lämmin kasvislisäke", + "orderNumber": 4, + "portionSize": 70, + "diets": "G, VEG", + "ingredients": "Lämmin kasvislisäke", + }, + ], + }, + ], + } + ], + } + ], + }, + ], + } +] diff --git a/tests/test_classes.py b/tests/test_classes.py new file mode 100644 index 0000000..a36bf79 --- /dev/null +++ b/tests/test_classes.py @@ -0,0 +1,41 @@ +from jmenu.classes import Marker, Restaurant, MenuItem, MARKERS, RESTAURANTS + + +def test_rest(): + rest = Restaurant("test", 1, 2, 3, ["test"]) + assert rest is not None + assert rest.name == "test" + assert rest.client_id == 1 + assert rest.kitchen_id == 2 + assert rest.menu_type == 3 + assert rest.relevant_menus == ["test"] + + +def test_marker(): + mark = Marker("t", "test") + assert mark is not None + assert mark.letters == "t" + assert mark.explanation == "test" + + +def test_menu_item(): + item = MenuItem("test", "t") + assert item is not None + assert item.diets == "t" + assert item.name == "test" + + +def test_restaurants(): + assert RESTAURANTS is not None + assert len(RESTAURANTS) == 6 + for rest in RESTAURANTS: + assert rest is not None + assert rest.name is not None + + +def test_markers(): + assert MARKERS is not None + assert len(MARKERS) == 10 + for mark in MARKERS: + assert mark is not None + assert mark.letters is not None diff --git a/tests/test_response.py b/tests/test_response.py new file mode 100644 index 0000000..e69de29