Skip to content

Commit

Permalink
Merge pull request #10 from Erik-Hoffmann/dev
Browse files Browse the repository at this point in the history
Dev
  • Loading branch information
Erik-Hoffmann authored Jul 22, 2023
2 parents f1771c5 + 4a6bc47 commit 446d9ac
Show file tree
Hide file tree
Showing 5 changed files with 125 additions and 65 deletions.
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -127,3 +127,6 @@ dmypy.json

# Pyre type checker
.pyre/

# Custom
deploy.sh
12 changes: 8 additions & 4 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -1,11 +1,15 @@
FROM python:3
FROM python:3.9-slim-bullseye

RUN python3 -m venv /opt/venv

WORKDIR /usr/src/app

COPY requirements.txt ./
RUN pip install --no-cache-dir -r requirements.txt
COPY requirements.txt .

RUN . /opt/venv/bin/activate && pip install --no-cache-dir -r requirements.txt

COPY . .

COPY ./src .

CMD [ "python", "./bot.py" ]
CMD . /opt/venv/bin/activate && python ./bot.py
5 changes: 3 additions & 2 deletions requirements.txt
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
requests==2.22.0
beautifulsoup4==4.8.2
discord.py==1.7.3
python-dotenv==0.20.0
python-dotenv==0.20.0
requests==2.25.1
pyquery==2.0.0
81 changes: 22 additions & 59 deletions src/bot.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,10 @@
import discord
from discord.ext import commands
from dotenv import load_dotenv
from datetime import date

from bs4 import BeautifulSoup
import requests
import re

from speiseplan import Speiseplan_extractor

load_dotenv()
TOKEN = os.getenv('DISCORD_TOKEN')
Expand All @@ -18,28 +17,6 @@

MENSA_HTWG = 'https://seezeit.com/essen/speiseplaene/mensa-htwg/'

# Utility
def validateIngr(sup):
return bool(re.match("\((\d\w?,?)+\)", sup))

def attr_lookup(attribute):
lookup = {
"Veg": "Vegetarisch",
"Vegan": "Vegan",
"Sch": "Schwein",
"R": "Rind/Kalb",
"G": "Geflügel",
"L": "Lamm",
"W": "Wild",
"F": "Fisch/Meeresfrüchte"
}
result = []
if attribute:
for attr in attribute:
if attr in lookup:
result.append(lookup[attr])
return ", ".join(result) if result else ""

# Commands
@bot.event
async def on_ready():
Expand Down Expand Up @@ -71,41 +48,27 @@ async def bug(ctx):

@bot.command(name='mensa', help='Responds with the current menu')
async def menu(ctx):
page_response = requests.get(MENSA_HTWG)
soup = BeautifulSoup(page_response.content, 'html.parser')
contents = soup.find('div', class_='tx-speiseplan')
date_tabs = contents.find_all('a', class_='tab')
current_tab = None
current_tab_class = None
attr_class='speiseplanTagKatIcon'
for tab in date_tabs:
current_tab = tab.text
if date.today().strftime("%d.%m.") in current_tab:
# if "16.05." in current_tab:
current_tab_class = tab.get('class')[1]
break


response = discord.Embed(
title="In der Mensa gibt es:",
url=MENSA_HTWG
)
if not current_tab_class is None:

day = contents.find('div', {"id":current_tab_class})
menus = day.find_all('div', class_='speiseplanTagKat')


for menu in menus:
category=menu.find('div', class_='category')
food=menu.find('div', class_='title_preise_1').find('div', class_='title')
for sup in food.select('sup'):
if not validateIngr(sup.text): sup.unwrap()
else : sup.decompose()
attribute=menu.find('div', class_='title_preise_2').find('div', class_=attr_class)['class']
response.add_field(name=f"{category.text} : {attr_lookup(attribute)}", value=f"{food.text}", inline=True)
else:
response.add_field(name="Heute wohl nix", value="Zu oder so :(\nVielleicht heitert dich ein Quiz auf?\nhttps://www.mensa.de/about/membership/online-iq-test/")
title="In der Mensa gibt es:",
url=Speiseplan_extractor.address
)
try:
extractor = Speiseplan_extractor()
data = extractor.get_tab_json()
if not data:
response.add_field(name="Heute wohl nix", value="Zu oder so :(\nVielleicht heitert dich ein Quiz auf?\nhttps://www.mensa.de/about/membership/online-iq-test/")
else:
for menu in data:
response.add_field(name=f"{menu.get('category')} {': ' + str(menu.get('tags')) if menu.get('tags') else ''}", value=menu.get('food'), inline=True)

except requests.ConnectionError as e:
response.add_field(name="Fehler!", value=f"Seite nicht erreichbar!\n{e}")
except IndexError as e:
response.add_field(name="Fehler!", value=f"Tab ID nicht gefunden!\n{e}")
except ValueError as e:
response.add_field(name="Fehler!", value=e)
except Exception as e:
response.add_field(name="Fehler", value=f"Unhandled Exception:\n```{e}```")

await ctx.send(embed=response)

Expand Down
89 changes: 89 additions & 0 deletions src/speiseplan.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
from requests import ConnectionError
from requests import get
from pyquery import PyQuery as pq
import re
from pprint import pprint

class Speiseplan_extractor:

address = "https://seezeit.com/essen/speiseplaene/mensa-htwg/"

def __init__(self):
self.speiseplan = self.get_menu()
self.tag_map = self.build_tag_map()

def get_menu(self):
req = get(self.address)
if not req.status_code == 200:
raise ConnectionError(req)
content = pq(req.content)
return content(".tx-speiseplan")

def get_aktiv_tab_id(self):
menu = self.speiseplan
return menu(".heute").attr("rel")

def get_tab_content(self, tab_idx):
menu = self.speiseplan
return menu(f"#tab{tab_idx}")

def get_tab_json(self, tab_idx=None):
menu = self.speiseplan
today_id = int(self.get_aktiv_tab_id())
if tab_idx is None:
idx = today_id
else:
idx = self.eval_index(today_id, tab_idx)
if not menu(f"#tab{idx}"):
raise IndexError("non existant tab id")
return self.extract_categories(self.get_tab_content(idx))

def eval_index(self, base_id: int, shift_id: str):
if re.match(r"^[+-]?\d$", shift_id):
try:
shift_val = int(shift_id[-1])
except Exception as err:
raise err
if shift_id.startswith('+'):
return base_id + shift_val
elif shift_id.startswith('-'):
return base_id - shift_val
else: return base_id + shift_val
raise ValueError(f"Invalid input: shift_id={shift_id}")

def extract_categories(self, tab):
return [self.elem_to_json(elem) for elem in tab(".speiseplanTagKat")]

def elem_to_json(self, elem):
cat_html = pq(elem)
cat_name = cat_html(".category").text()
cat_food = cat_html(".title").text()
tags_html = cat_html(".speiseplanTagKatIcon")
cat_tags = self.map_tags(tags_html)
return {"category": cat_name, "food": self.format_food(cat_food).rstrip(), "tags": cat_tags}

def format_food(self,food):
return re.sub(r"\(\d\d?\w?(,\d\d?\w?)*\)[ ]?",'', food)

def build_tag_map(self):
menu = self.speiseplan
tags = menu(".tabsIcons")(".tabIcon")
return {pq(elem).attr("class")[-1]: elem.text for elem in tags}

def map_tags(self, tags):
return list(
map(
lambda x : self.tag_map.get(x, None),
[pq(tag).attr("class")[-1] for tag in tags]
)
)




if __name__ == '__main__':
extractor = Speiseplan_extractor()
pprint(
extractor.get_tab_json()
)

0 comments on commit 446d9ac

Please sign in to comment.