124-webapp/main.py

196 lines
5.9 KiB
Python
Raw Normal View History

2025-09-11 16:01:32 +02:00
import os
import shutil
2025-10-07 13:02:29 +02:00
import csv
from pathlib import Path
2025-09-17 16:35:11 +02:00
from fastapi import FastAPI, UploadFile, File, Request, Form
2025-10-02 12:20:11 +02:00
from fastapi.responses import HTMLResponse
2025-09-11 16:01:32 +02:00
from fastapi.templating import Jinja2Templates
from fastapi.staticfiles import StaticFiles
from cost_calculator import allowed_file, analyze_pdf, get_rate_black, get_rate_color, UPLOAD_FOLDER
2025-09-17 16:35:11 +02:00
from mailer import send_order_sync
from dotenv import load_dotenv
load_dotenv()
2025-10-07 13:02:29 +02:00
# Get server hostname from environment
SERVER_HOSTNAME = os.environ.get("SERVER_HOSTNAME", "einszwovier.local")
BOOKSTACK_PORT = os.environ.get("BOOKSTACK_PORT", "6875")
OPENWEBUI_PORT = os.environ.get("OPENWEBUI_PORT", "8080")
PORTAINER_PORT = os.environ.get("PORTAINER_PORT", "9000")
# Courses CSV path
COURSES_CSV = Path("data/courses.csv")
2025-09-11 16:01:32 +02:00
app = FastAPI()
templates = Jinja2Templates(directory="templates")
os.makedirs(UPLOAD_FOLDER, exist_ok=True)
app.mount("/static", StaticFiles(directory="static"), name="static")
2025-10-02 12:20:11 +02:00
import asyncio
presence_state = {"present": False}
async def update_presence_periodically():
while True:
try:
# Here you can implement logic to update presence automatically
# For example, check some condition, a file, or even keep it False
print("Updating presence state…")
# Example: flip presence every 5 mins (just for demo)
# presence_state["present"] = not presence_state["present"]
except Exception as e:
print("Error updating presence:", e)
await asyncio.sleep(300) # 5 minutes
@app.on_event("startup")
async def startup_event():
asyncio.create_task(update_presence_periodically())
2025-09-11 16:01:32 +02:00
2025-10-02 12:20:11 +02:00
# ---- Presence State ----
presence_state = {"present": False}
@app.get("/presence")
def get_presence():
"""Return current presence state"""
return presence_state
@app.post("/presence")
def set_presence(present: bool = Form(...)):
"""Set presence state explicitly"""
presence_state["present"] = present
return {"status": "ok", "present": presence_state["present"]}
@app.post("/presence/toggle")
def toggle_presence():
"""Toggle presence state"""
presence_state["present"] = not presence_state["present"]
return {"status": "ok", "present": presence_state["present"]}
# ---- Existing Endpoints ----
2025-09-11 16:01:32 +02:00
@app.get("/", response_class=HTMLResponse)
async def welcome(request: Request):
2025-10-02 12:20:11 +02:00
return templates.TemplateResponse(
"landing.html",
{
"request": request,
"studio_open": presence_state["present"],
2025-10-07 13:02:29 +02:00
"opening_hours": "Di-Do 11:0016:00",
"server_hostname": SERVER_HOSTNAME,
"bookstack_port": BOOKSTACK_PORT,
"openwebui_port": OPENWEBUI_PORT,
"portainer_port": PORTAINER_PORT,
2025-10-02 12:20:11 +02:00
},
)
2025-09-11 16:01:32 +02:00
@app.get("/about", response_class=HTMLResponse)
async def about(request: Request):
2025-10-07 13:02:29 +02:00
# Load courses from CSV
courses = []
if COURSES_CSV.exists():
try:
with open(COURSES_CSV, 'r', encoding='utf-8') as f:
reader = csv.DictReader(f)
courses = list(reader)
except Exception as e:
print(f"Error loading courses: {e}")
return templates.TemplateResponse("about.html", {
"request": request,
2025-10-07 17:48:55 +02:00
"courses": courses,
"bookstack_url": f"http://{SERVER_HOSTNAME}:{BOOKSTACK_PORT}"
2025-10-07 13:02:29 +02:00
})
2025-09-11 16:01:32 +02:00
@app.get("/cost", response_class=HTMLResponse)
async def cost_dashboard(request: Request):
return templates.TemplateResponse(
"cost-calculator.html",
{
"request": request,
"rate_black": get_rate_black(),
2025-09-17 16:35:11 +02:00
"rate_color": get_rate_color(),
},
2025-09-11 16:01:32 +02:00
)
@app.post("/upload")
async def upload_file(request: Request, file: UploadFile = File(...)):
if not allowed_file(file.filename):
return templates.TemplateResponse(
"cost-calculator.html",
{"request": request, "error": "Unsupported file type. Only PDF allowed."},
)
path = os.path.join(UPLOAD_FOLDER, file.filename)
with open(path, "wb") as buffer:
shutil.copyfileobj(file.file, buffer)
result = analyze_pdf(path)
return templates.TemplateResponse(
"result.html",
{
"request": request,
"result": result,
"rate_black": get_rate_black(),
2025-09-17 16:35:11 +02:00
"rate_color": get_rate_color(),
2025-09-11 16:01:32 +02:00
},
)
2025-09-17 16:35:11 +02:00
@app.post("/send-order")
def send_order_endpoint(
request: Request,
filename: str = Form(...),
2025-10-02 12:20:11 +02:00
name: str = Form(...),
2025-09-17 16:35:11 +02:00
comment: str = Form(""),
):
path = os.path.join(UPLOAD_FOLDER, filename)
if not os.path.exists(path):
return templates.TemplateResponse(
"cost-calculator.html",
2025-10-02 12:20:11 +02:00
{"request": request, "error": "Datei nicht gefunden. Bitte erneut hochladen."},
2025-09-17 16:35:11 +02:00
)
analysis = analyze_pdf(path)
2025-10-07 13:02:29 +02:00
# Get Matrix room ID from environment
matrix_room = os.environ.get("MATRIX_ROOM", "!eFWbWEnYsgeIKqyfjw:einszwovier.local")
2025-09-17 16:35:11 +02:00
try:
2025-10-02 12:20:11 +02:00
send_order_sync(
pdf_path=path,
analysis=analysis,
2025-10-07 13:02:29 +02:00
room_id=matrix_room,
2025-10-02 12:20:11 +02:00
name=name,
comment=comment,
)
2025-09-17 16:35:11 +02:00
return templates.TemplateResponse(
"result.html",
{
"request": request,
"result": analysis,
"rate_black": get_rate_black(),
"rate_color": get_rate_color(),
2025-10-02 12:20:11 +02:00
"success": "✅ Dein Auftrag wurde erfolgreich gesendet!",
"name": name,
"comment": comment,
2025-09-17 16:35:11 +02:00
},
)
except Exception as e:
return templates.TemplateResponse(
"result.html",
{
"request": request,
"result": analysis,
"rate_black": get_rate_black(),
"rate_color": get_rate_color(),
2025-10-02 12:20:11 +02:00
"error": f"Fehler beim Senden des Auftrags: {e}",
"name": name,
"comment": comment,
2025-09-17 16:35:11 +02:00
},
2025-10-02 12:20:11 +02:00
)