Luncher/food_api/food_service.py

285 lines
11 KiB
Python
Executable File

#!/usr/bin/env python3
import datetime
from typing import List
from bs4 import BeautifulSoup
import tempfile
import sys
import os
import urllib.request
import platform
from datetime import date, timedelta
URL_SLADOVNICKA = "https://sladovnicka.unasplzenchutna.cz/cz/denni-nabidka"
URL_MOTLICI = "https://www.umotliku.cz"
URL_TECHTOWER = "https://www.equifarm.cz/restaurace-techtower"
DAY_NAMES = ['pondělí', 'úterý', 'středa',
'čtvrtek', 'pátek', 'sobota', 'neděle']
# Fráze v názvech jídel, které naznačují že se jedná o polévku
SOUP_NAMES = ['polévka', 'česnečka', 'česnekový krém', 'cibulačka', 'vývar']
class bcolors:
HEADER = '\033[95m'
OKBLUE = '\033[94m'
OKCYAN = '\033[96m'
OKGREEN = '\033[92m'
WARNING = '\033[93m'
FAIL = '\033[91m'
ENDC = '\033[0m'
BOLD = '\033[1m'
UNDERLINE = '\033[4m'
class Food:
name = None
amount = None
price = None
is_soup = False
def __init__(self, name, amount, price, is_soup=False) -> None:
self.name = name
self.amount = amount
self.price = price
self.is_soup = is_soup
def getOrDownloadHtml(prefix: str, url: str):
'''Vrátí HTML pro daný prefix pro aktuální den.
Pokud v tempu neexistuje, provede jeho stažení z předané URL a uložení.'''
filename = prefix + "_" + date.today().strftime("%Y_%m_%d") + ".html"
filepath = os.path.join(tempfile.gettempdir(), filename)
if not os.path.isfile(filepath):
urllib.request.urlretrieve(url, filepath)
file = open(filepath, "r", encoding='utf-8')
contents = file.read()
file.close()
return contents
def isNameOfDay(text: str):
'''Vrátí True, pokud předaný text představuje název dne v týdnu (např. "pondělí")'''
return text.strip().lower() in DAY_NAMES
def getDayNameOfDate(date: datetime.datetime):
'''Vrátí název dne v týdnu - např. pondělí, úterý, ...'''
return DAY_NAMES[date.weekday()]
def getStartOfWeekDate():
'''Vrátí datetime představující pondělí v aktuálním týdnu.'''
today = datetime.datetime.now()
return today - timedelta(days=today.weekday())
def isTextSoupName(text: str):
'''Vrátí True, pokud se předaný text jeví jako název polévky.
Používá se tam, kde nemáme lepší způsob detekce (TechTower).'''
for name in SOUP_NAMES:
if name in text.lower():
return True
return False
def printMenu(name: str, foodList: List[Food]):
'''Vytiskne jídelní lístek na obrazovku.'''
print(f"{bcolors.OKGREEN}{name}{bcolors.ENDC}\n---------------------------------------------------------------------------------")
maxLength = 0
for jidlo in foodList:
if len(jidlo.name) > maxLength:
maxLength = len(jidlo.name)
for jidlo in foodList:
barva = bcolors.HEADER if jidlo.is_soup else bcolors.WARNING
print(f"{barva}{jidlo.amount}\t{jidlo.name.ljust(maxLength)}\t{bcolors.ENDC}{bcolors.OKCYAN}{jidlo.price}{bcolors.ENDC}")
print('\n')
def getMenuSladovnicka(mock: bool = False) -> List[Food]:
if mock:
foodList: List[Food] = []
foodList.append(Food("Zelná polévka s klobásou", "0,25l", "35 Kč", True))
foodList.append(Food("Hovězí na česneku s bramborovým knedlíkem", "150g", "135 Kč"))
foodList.append(Food("Přírodní holandský řízek s bramborovou kaší, rajčatový salát", "250g", "135 Kč"))
foodList.append(Food("Bagel s vinnou klobásou, cibulový konfit, kysané zelí, slanina a hořčicová mayo, hranolky, curry omáčka", "350g", "135 Kč"))
return foodList
if getDayNameOfDate(date.today()).lower() == 'sobota' or getDayNameOfDate(date.today()).lower() == 'neděle':
return []
html = getOrDownloadHtml('sladovnicka', URL_SLADOVNICKA)
soup = BeautifulSoup(html, "html.parser")
div = soup.select_one("div.tab-pane.fade.in.active")
datumDen = div.find("h2").text
split = datumDen.split(".")
denMesic = split[0] + "." + split[1] + "."
# nazevDen = split[2]
# Windows má pro padding '#', POSIX systémy '-'
if platform.system() == 'Windows':
format = "%#d.%#m."
else:
format = "%-d.%-m."
dnesniDatum = date.today().strftime(format)
if denMesic != dnesniDatum:
print('Chyba: neočekávané datum na stránce Sladovnické (' +
denMesic + '), očekáváno ' + dnesniDatum, file=sys.stderr)
sys.exit(1)
tables = div.find_all("table", {"class": "simple"})
if len(tables) != 2:
print('Chyba: neočekávaný počet tabulek na stránce Sladovnické (' +
str(len(tables)) + '), očekávány 2', file=sys.stderr)
sys.exit(1)
foodList: List[Food] = []
polevkaValues = tables[0].find_all("td")
amount = polevkaValues[0].text.strip()
name = polevkaValues[1].text.strip()
price = polevkaValues[2].text.strip()
foodList.append(Food(name, amount, price, True))
foodTables = tables[1].find_all("tr")
for food in foodTables:
rows = food.find_all("td")
if (len(rows) != 3):
print("Neočekávaný počet řádek hlavního jídla Sladovnické (" +
str(len(rows)) + ", očekávány 3, přeskakuji...")
continue
amount = rows[0].text.strip()
name = rows[1].text.strip()
price = rows[2].text.strip()
foodList.append(Food(name, amount, price))
return foodList
def getMenuUMotliku(mock: bool = False) -> List[Food]:
if mock:
foodList: List[Food] = []
foodList.append(Food("Hovězí vývar s nudlemi", "0,33l", "35 Kč", True))
foodList.append(Food("Opečený párek, čočka, sázené vejce, okurka", "150g", "135 Kč"))
foodList.append(Food("Hovězí líčka na červeném víně, bramborová kaše", "150g", "145 Kč"))
foodList.append(Food("Tortilla s trhaným kuřecím masem, uzeným sýrem, dipem a kukuřicí, míchaný salát", "150g", "135 Kč"))
return foodList
if getDayNameOfDate(date.today()).lower() == 'sobota' or getDayNameOfDate(date.today()).lower() == 'neděle':
return []
html = getOrDownloadHtml('u_motliku', URL_MOTLICI)
soup = BeautifulSoup(html, "html.parser")
table = soup.find("table", {"class": "Xtable-striped"})
rows = table.find_all("tr")
if len(rows) < 4:
print('Chyba: neočekávaný celkový počet řádek tabulky (' +
str(len(rows)) + '), očekáváno 4 a více', file=sys.stderr)
sys.exit(1)
foodList: List[Food] = []
if rows[0].td.text.strip() == 'Polévka':
tds = rows[1].find_all("td")
if len(tds) != 3:
print('Chyba: neočekávaný počet <td> elementů v řádce polévky (' +
str(len(tds)) + '), očekáváno 3', file=sys.stderr)
sys.exit(1)
amount = tds[0].text.strip()
name = tds[1].text.strip()
price = tds[2].text.strip().replace(',-', '')
foodList.append(Food(name, amount, price, True))
rows = rows[2:]
if rows[0].td.text.strip() == 'Hlavní jídlo':
for i in range(1, len(rows)):
tds = rows[i].find_all("td")
if len(tds) != 3:
print("Neočekávaný počet <td> elementů (" + str(len(tds)
) + ") pro hlavní jídlo " + str(i) + ", přeskakuji")
continue
amount = tds[0].text.strip()
name = tds[1].text.strip()
price = tds[2].text.strip().replace(',-', '')
foodList.append(Food(name, amount, price))
return foodList
def getMenuTechTower(mock: bool = False) -> List[Food]:
if mock:
foodList: List[Food] = []
foodList.append(Food("Bavorská gulášová polévka s kroupami", "-", "40 Kč", True))
foodList.append(Food("Vepřové výpečky, kedlubnové zelí, bramborový knedlík", "-", "120 Kč"))
foodList.append(Food("Hambuger Black Angus s čedarem a slaninou, cibulové kroužky", "-", "220 Kč"))
return foodList
if getDayNameOfDate(date.today()).lower() == 'sobota' or getDayNameOfDate(date.today()).lower() == 'neděle':
return []
html = getOrDownloadHtml('techtower', URL_TECHTOWER)
soup = BeautifulSoup(html, "html.parser")
fonts = soup.find_all("font", {"class": ["wsw-41"]})
font = None
for f in fonts:
if (f.text.strip().startswith("Obědy")):
font = f
if font is None:
print('Chyba: nenalezen <font> pro obědy v HTML Techtower.', file=sys.stderr)
sys.exit(1)
siblings = font.parent.parent.find_next_siblings("p")
# dayNumber = date.today().strftime("%w")
currentDayName = getDayNameOfDate(datetime.datetime.now())
foodList: List[Food] = []
doParse = False
for i in range(0, len(siblings)):
text = siblings[i].text.strip().replace('\t', '').replace('\n', ' ')
if isNameOfDay(text):
if text == currentDayName:
# Našli jsme dnešní den, odtud začínáme parsovat jídla
doParse = True
elif doParse == True:
# Už parsujeme jídla, ale narazili jsme na následující den - končíme
break
elif doParse:
if len(text.strip()) == 0:
# Prázdná řádka - končíme (je za pátečním menu TechTower)
break
price = '? Kč'
if text.endswith(''):
split = text.rsplit(' ', 2)
price = " ".join(split[1:])
text = split[0]
foodList.append(Food(text, '-', price, isTextSoupName(text)))
return foodList
if __name__ == "__main__":
if len(sys.argv) > 1:
input = sys.argv[1].lower()
selectedDate = None
if input[0].isalpha():
matches = []
for day in DAY_NAMES:
if day.startswith(input):
matches.append(day)
if len(matches) == 1:
print("Match - den v týdnu - " + matches[0])
selectedDate = getStartOfWeekDate(
) + timedelta(DAY_NAMES.index(matches[0]))
elif len(matches) == 0:
# TODO zkusit v, z (včera, zítra)
if 'zítra'.startswith(input):
print("Match - zítra")
selectedDate = datetime.datetime.now() + timedelta(days=1)
elif 'včera'.startswith(input):
print("Match - včera")
selectedDate = datetime.datetime.now() + timedelta(days=-1)
elif 'dneska'.startswith(input):
print("Match - dnes")
selectedDate = datetime.datetime.now()
else:
print('Nejasný parametr "' + input +
'" - může znamenat jednu z možností: ' + ', '.join(matches), file=sys.stderr)
sys.exit(1)
else:
# TODO implementovat zadání datem
print('Zadání datem není aktuálně implementováno', file=sys.stderr)
sys.exit(1)
print("Datum: " + selectedDate.strftime('%d.%m.%Y'))
print("Den: " + getDayNameOfDate(selectedDate))
# printMenu('Sladovnická', getMenuSladovnicka())
# printMenu('U Motlíků', getMenuUMotliku())
# printMenu('TechTower', getMenuTechTower())