mirror of
https://github.com/traberph/mensa-check.git
synced 2026-10-06 18:24:45 +00:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
2ac9405f1c | ||
|
|
835a6d8b48 | ||
|
|
cf695f1518 | ||
|
|
1597a99360 | ||
|
|
28aa4d2962 | ||
|
|
11ad5a9320 | ||
|
|
5118db82e2 | ||
|
|
e4c05f310f | ||
|
|
d6f282757e | ||
|
|
94ffe174c8 | ||
|
|
c6f8c9f912 | ||
|
|
88f942eb60 | ||
|
|
a8aefb0e62 | ||
|
|
81aa6ec59b | ||
|
|
896f13a448 | ||
|
|
076ad18f10 | ||
|
|
da365ac881 | ||
|
|
9064802962 | ||
|
|
4019969841 | ||
|
|
72266fe2e8 |
@@ -0,0 +1,31 @@
|
|||||||
|
name: Ruff - Code Quality Check
|
||||||
|
|
||||||
|
on:
|
||||||
|
pull_request:
|
||||||
|
branches:
|
||||||
|
- main
|
||||||
|
workflow_dispatch:
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
build:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Checkout code
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: Setup python
|
||||||
|
uses: actions/setup-python@v4
|
||||||
|
with:
|
||||||
|
python-version: '3.12'
|
||||||
|
|
||||||
|
- name: Install dependencies
|
||||||
|
run: |
|
||||||
|
python -m pip install --upgrade pip
|
||||||
|
pip install -r requirements.txt
|
||||||
|
|
||||||
|
- name: Run Ruff
|
||||||
|
run: |
|
||||||
|
pip install ruff
|
||||||
|
ruff format --check
|
||||||
|
ruff check
|
||||||
+3
-1
@@ -1 +1,3 @@
|
|||||||
.venv
|
.venv
|
||||||
|
venv
|
||||||
|
__pycache__
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
[lint]
|
||||||
|
extend-select = ["I"]
|
||||||
Vendored
+4
@@ -0,0 +1,4 @@
|
|||||||
|
{
|
||||||
|
"editor.formatOnSave": true,
|
||||||
|
"editor.defaultFormatter": "charliermarsh.ruff"
|
||||||
|
}
|
||||||
+3
-4
@@ -1,7 +1,6 @@
|
|||||||
FROM python:3
|
FROM python:3.12-alpine
|
||||||
WORKDIR /app
|
WORKDIR /app
|
||||||
COPY requirements.txt ./
|
COPY requirements.txt .
|
||||||
RUN pip install --no-cache-dir -r requirements.txt
|
RUN pip install -r requirements.txt --no-cache-dir
|
||||||
|
|
||||||
COPY src/ .
|
COPY src/ .
|
||||||
CMD [ "fastapi", "run", "api.py" ]
|
CMD [ "fastapi", "run", "api.py" ]
|
||||||
@@ -1,17 +1,17 @@
|
|||||||
# Spätzle check
|
# Spätzle check
|
||||||
|
|
||||||
Me and my colleges have been eating in the Mensa at our university for a while now. If spätzle are on the menu it was always a gamble if they are good or really bad. Until somebody told us the problem of the bad spätzle is that they are vegan (no hate against vegan products but some products just shouldn't be vegan).
|
Me and my colleagues have been eating in the Mensa at our university for a while now. If spätzle are on the menu it was always a gamble if they are good or really bad. Until somebody told us the problem of the bad spätzle is that they are vegan (no hate against vegan products but some products just shouldn't be vegan).
|
||||||
|
|
||||||
|
|
||||||
Because of the lack of eggs the spätzle are not sticking together and are just a big pile of mush.
|
Because of the lack of eggs the spätzle are not sticking together and are just a big pile of mush.
|
||||||
|
|
||||||
Using the allergen information on the mensa website we can check if the spätzle are vegan or not and therefore can decide if one can eat them or not.
|
Using the allergen information on the mensa website we can check if the spätzle are vegan or not and therefore can decide if one can eat them or not.
|
||||||
|
|
||||||
This project serves a simple [website](spaetzle.traberph.de) that checks the spätzle for you.
|
This project serves a simple [website](https://spaetzle.traberph.de) that checks the spätzle for you.
|
||||||
|
|
||||||
## How to run
|
## How to run
|
||||||
Simply run it with docker-compose
|
Simply run it with docker compose
|
||||||
|
|
||||||
```
|
```
|
||||||
docker-compose up
|
docker compose up
|
||||||
```
|
```
|
||||||
|
|||||||
+3
-3
@@ -1,5 +1,5 @@
|
|||||||
fastapi[standard]==0.114.0
|
fastapi[standard]==0.115.0
|
||||||
Jinja2==3.1.4
|
Jinja2==3.1.4
|
||||||
requests==2.32.3
|
requests==2.32.3
|
||||||
cachetools==5.5.0
|
cachetools==5.5.0
|
||||||
beautifulsoup4==4.12.3
|
beautifulsoup4==4.12.3
|
||||||
+57
-34
@@ -1,55 +1,78 @@
|
|||||||
from fastapi import FastAPI
|
import logging
|
||||||
from fastapi import FastAPI, Request
|
import re
|
||||||
from fastapi.responses import HTMLResponse
|
|
||||||
from fastapi.templating import Jinja2Templates
|
|
||||||
from cachetools import cached, TTLCache
|
|
||||||
from bs4 import BeautifulSoup
|
|
||||||
import requests
|
import requests
|
||||||
|
from bs4 import BeautifulSoup
|
||||||
|
from cachetools import TTLCache, cached
|
||||||
|
from fastapi import FastAPI, Request
|
||||||
|
from fastapi.responses import FileResponse, HTMLResponse
|
||||||
|
from fastapi.templating import Jinja2Templates
|
||||||
|
|
||||||
|
logger = logging.getLogger("uvicorn.error")
|
||||||
cache = TTLCache(maxsize=1, ttl=3600)
|
cache = TTLCache(maxsize=1, ttl=3600)
|
||||||
app = FastAPI()
|
app = FastAPI()
|
||||||
templates = Jinja2Templates(directory="template")
|
templates = Jinja2Templates(directory="template")
|
||||||
|
regex = re.compile(r"spätzle <sup>([^/]*)</sup>", re.IGNORECASE)
|
||||||
|
|
||||||
|
|
||||||
@cached(cache)
|
@cached(cache)
|
||||||
def checkMensa():
|
def checkMensa():
|
||||||
print("Checking Mensa")
|
logger.info("Checking Mensa")
|
||||||
|
|
||||||
url = 'https://seezeit.com/essen/speiseplaene/mensa-giessberg/'
|
|
||||||
r = requests.get(url)
|
|
||||||
soup = BeautifulSoup(r.text, 'html.parser')
|
|
||||||
|
|
||||||
today = soup.find('a', class_='heute')['rel'][0]
|
try:
|
||||||
today_menu = soup.find('div', id=f'tab{today}')
|
url = "https://seezeit.com/essen/speiseplaene/mensa-giessberg/"
|
||||||
meals = today_menu.find_all('div', class_='title')
|
r = requests.get(url)
|
||||||
|
soup = BeautifulSoup(r.text, "html.parser")
|
||||||
|
|
||||||
spaetzle = None
|
today = soup.find("a", class_="heute")["rel"][0]
|
||||||
for m in meals:
|
today_menu = soup.find("div", id=f"tab{today}")
|
||||||
if "Spätzle" in m.text:
|
meals = today_menu.find_all("div", class_="title")
|
||||||
spaetzle = m
|
|
||||||
break
|
|
||||||
|
|
||||||
if spaetzle is not None:
|
spaetzle = None
|
||||||
ingredients = spaetzle.find('sup').text
|
for m in meals:
|
||||||
if '28' in ingredients:
|
if "spätzle" in m.text.lower():
|
||||||
answer = "Spätzle with egg today"
|
spaetzle = m
|
||||||
color = "green"
|
break
|
||||||
emoji = "✅"
|
|
||||||
|
if spaetzle is not None:
|
||||||
|
ingredients = re.search(regex, str(spaetzle)).group(1)
|
||||||
|
if "28" in ingredients:
|
||||||
|
answer = "Spätzle with egg today"
|
||||||
|
color = "green"
|
||||||
|
emoji = "✅"
|
||||||
|
|
||||||
|
else:
|
||||||
|
answer = "WARNING: Spätzle without egg today"
|
||||||
|
color = "red"
|
||||||
|
emoji = "❌"
|
||||||
else:
|
else:
|
||||||
answer = "WARNING: Spätzle without egg today"
|
answer = "No Spätzle today "
|
||||||
color = "red"
|
color = "grey"
|
||||||
emoji = "❌"
|
emoji = "🤷"
|
||||||
else:
|
except Exception:
|
||||||
answer = "No Spätzle today "
|
answer = "No menu today"
|
||||||
color = "grey"
|
color = "grey"
|
||||||
emoji = "🤷🏻♂️"
|
emoji = "⛱️"
|
||||||
|
finally:
|
||||||
|
logger.info(f"Answer: {answer}, Color: {color}, Emoji: {emoji}")
|
||||||
|
return answer, color, emoji
|
||||||
|
|
||||||
return answer, color, emoji
|
|
||||||
|
|
||||||
@app.get("/", response_class=HTMLResponse)
|
@app.get("/", response_class=HTMLResponse)
|
||||||
async def root(request: Request):
|
async def root(request: Request):
|
||||||
answer, color, emoji = checkMensa()
|
answer, color, emoji = checkMensa()
|
||||||
return templates.TemplateResponse(
|
return templates.TemplateResponse(
|
||||||
request=request, name="main.html", context={"text": answer, "color": color , "emoji": emoji}
|
request=request,
|
||||||
|
name="main.html",
|
||||||
|
context={"text": answer, "color": color, "emoji": emoji},
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@app.get("/manifest.json")
|
||||||
|
async def manifest(request: Request):
|
||||||
|
return FileResponse(path="assets/manifest.json")
|
||||||
|
|
||||||
|
|
||||||
|
@app.get("/icons/512.png")
|
||||||
|
async def icon(request: Request):
|
||||||
|
return FileResponse(path="assets/logo.png")
|
||||||
|
|||||||
Binary file not shown.
|
After Width: | Height: | Size: 127 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 125 KiB |
@@ -0,0 +1,12 @@
|
|||||||
|
{
|
||||||
|
"name": "Spätzle Check",
|
||||||
|
"start_url": "/",
|
||||||
|
"icons": [
|
||||||
|
{
|
||||||
|
"src": "icons/512.png",
|
||||||
|
"type": "image/png",
|
||||||
|
"sizes": "512x512"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"display": "standalone"
|
||||||
|
}
|
||||||
@@ -6,6 +6,7 @@
|
|||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||||||
|
<link rel="manifest" href="/manifest.json">
|
||||||
|
|
||||||
<title>{{text}} - {{emoji}}</title>
|
<title>{{text}} - {{emoji}}</title>
|
||||||
<style>
|
<style>
|
||||||
|
|||||||
Reference in New Issue
Block a user