Files
eventlens/backend/app/main.py
T
2026-04-18 14:23:24 +02:00

227 lines
6.2 KiB
Python

from pathlib import Path
from datetime import datetime
from fastapi import Depends, FastAPI, HTTPException
from fastapi.responses import FileResponse
from fastapi.staticfiles import StaticFiles
from sqlalchemy.orm import Session
from app.config import settings
from app.database import Base, engine, get_db
from app.models import TrackedEvent, WatchItem, WatchSource
from app.scheduler import start_scheduler
from app.schemas import (
NotificationLogRead,
ProviderStatusRead,
PurchaseUpdate,
SyncResult,
TrackedEventRead,
WatchItemCreate,
WatchItemRead,
WatchItemUpdate,
WatchSourceCreate,
WatchSourceRead,
WatchSourceUpdate,
)
from app.services import (
list_events,
list_notifications,
list_provider_statuses,
list_watch_sources,
list_watch_items,
run_sync,
)
app = FastAPI(
title="eventlens",
version="0.1.0",
description="Beobachtet Kuenstler und Events in Hamburg oder Deutschland.",
)
frontend_dir = Path(__file__).resolve().parent / "frontend"
static_dir = frontend_dir / "static"
app.mount("/static", StaticFiles(directory=static_dir), name="static")
@app.on_event("startup")
def startup():
Base.metadata.create_all(bind=engine)
start_scheduler()
@app.get("/")
def root():
return FileResponse(frontend_dir / "index.html")
@app.get("/api")
def api_info():
return {
"service": settings.app_name,
"status": "running",
"docs": "/docs",
}
@app.get("/health")
def health():
return {"status": "ok"}
@app.get("/watch-items", response_model=list[WatchItemRead])
def get_watch_items(db: Session = Depends(get_db)):
return list_watch_items(db)
@app.post("/watch-items", response_model=WatchItemRead, status_code=201)
def create_watch_item(payload: WatchItemCreate, db: Session = Depends(get_db)):
watch_item = WatchItem(
name=payload.name,
watch_type=payload.watch_type,
region_scope=payload.region_scope,
notes=payload.notes,
)
db.add(watch_item)
db.commit()
db.refresh(watch_item)
return watch_item
@app.patch("/watch-items/{watch_item_id}", response_model=WatchItemRead)
def update_watch_item(
watch_item_id: int,
payload: WatchItemUpdate,
db: Session = Depends(get_db),
):
watch_item = db.get(WatchItem, watch_item_id)
if watch_item is None:
raise HTTPException(status_code=404, detail="Watch item nicht gefunden.")
updates = payload.model_dump(exclude_unset=True)
for field_name, value in updates.items():
setattr(watch_item, field_name, value)
watch_item.updated_at = datetime.utcnow()
db.commit()
db.refresh(watch_item)
return watch_item
@app.delete("/watch-items/{watch_item_id}", status_code=204)
def delete_watch_item(watch_item_id: int, db: Session = Depends(get_db)):
watch_item = db.get(WatchItem, watch_item_id)
if watch_item is None:
raise HTTPException(status_code=404, detail="Watch item nicht gefunden.")
db.delete(watch_item)
db.commit()
@app.get("/watch-sources", response_model=list[WatchSourceRead])
def get_watch_sources(watch_item_id: int | None = None, db: Session = Depends(get_db)):
return list_watch_sources(db, watch_item_id)
@app.post(
"/watch-items/{watch_item_id}/sources",
response_model=WatchSourceRead,
status_code=201,
)
def create_watch_source(
watch_item_id: int,
payload: WatchSourceCreate,
db: Session = Depends(get_db),
):
watch_item = db.get(WatchItem, watch_item_id)
if watch_item is None:
raise HTTPException(status_code=404, detail="Watch item nicht gefunden.")
source = WatchSource(
watch_item=watch_item,
label=payload.label,
url=payload.url,
parser_type=payload.parser_type,
)
db.add(source)
db.commit()
db.refresh(source)
return source
@app.patch("/watch-sources/{source_id}", response_model=WatchSourceRead)
def update_watch_source(
source_id: int,
payload: WatchSourceUpdate,
db: Session = Depends(get_db),
):
source = db.get(WatchSource, source_id)
if source is None:
raise HTTPException(status_code=404, detail="Quelle nicht gefunden.")
updates = payload.model_dump(exclude_unset=True)
for field_name, value in updates.items():
setattr(source, field_name, value)
source.updated_at = datetime.utcnow()
db.commit()
db.refresh(source)
return source
@app.delete("/watch-sources/{source_id}", status_code=204)
def delete_watch_source(source_id: int, db: Session = Depends(get_db)):
source = db.get(WatchSource, source_id)
if source is None:
raise HTTPException(status_code=404, detail="Quelle nicht gefunden.")
db.delete(source)
db.commit()
@app.get("/events", response_model=list[TrackedEventRead])
def get_events(db: Session = Depends(get_db)):
return list_events(db)
@app.patch("/events/{event_id}/purchase", response_model=TrackedEventRead)
def update_purchase_status(
event_id: int,
payload: PurchaseUpdate,
db: Session = Depends(get_db),
):
tracked_event = db.get(TrackedEvent, event_id)
if tracked_event is None:
raise HTTPException(status_code=404, detail="Event nicht gefunden.")
tracked_event.is_ticket_purchased = payload.is_ticket_purchased
tracked_event.purchased_at = datetime.utcnow() if payload.is_ticket_purchased else None
tracked_event.reminder_notified_at = None
db.commit()
db.refresh(tracked_event)
return tracked_event
@app.delete("/events/{event_id}", status_code=204)
def delete_event(event_id: int, db: Session = Depends(get_db)):
tracked_event = db.get(TrackedEvent, event_id)
if tracked_event is None:
raise HTTPException(status_code=404, detail="Event nicht gefunden.")
db.delete(tracked_event)
db.commit()
@app.get("/notifications", response_model=list[NotificationLogRead])
def get_notifications(db: Session = Depends(get_db)):
return list_notifications(db)
@app.get("/provider-statuses", response_model=list[ProviderStatusRead])
def get_provider_statuses(db: Session = Depends(get_db)):
return list_provider_statuses(db)
@app.post("/sync", response_model=SyncResult)
def trigger_sync(db: Session = Depends(get_db)):
return run_sync(db)