Initial commit: Waste Collection API
This commit is contained in:
0
app/routes/__init__.py
Normal file
0
app/routes/__init__.py
Normal file
56
app/routes/collections.py
Normal file
56
app/routes/collections.py
Normal file
@@ -0,0 +1,56 @@
|
||||
from fastapi import APIRouter, HTTPException
|
||||
|
||||
from ..schemas import (
|
||||
ICSRequest,
|
||||
AbfallIORequest,
|
||||
AWMRequest,
|
||||
StuttgartRequest,
|
||||
CollectionsResponse,
|
||||
)
|
||||
from ..collector import fetch_ics, fetch_abfall_io, fetch_awm_muenchen, fetch_stuttgart
|
||||
|
||||
router = APIRouter(prefix="/collections", tags=["collections"])
|
||||
|
||||
|
||||
@router.post("/ics", response_model=CollectionsResponse)
|
||||
def collections_ics(body: ICSRequest):
|
||||
try:
|
||||
collections = fetch_ics(body.url, body.count)
|
||||
except Exception as e:
|
||||
raise HTTPException(status_code=422, detail=str(e))
|
||||
if not collections:
|
||||
raise HTTPException(status_code=422, detail="No collections returned from this ICS URL")
|
||||
return CollectionsResponse(source="ics", collections=collections)
|
||||
|
||||
|
||||
@router.post("/abfall_io", response_model=CollectionsResponse)
|
||||
def collections_abfall_io(body: AbfallIORequest):
|
||||
try:
|
||||
collections = fetch_abfall_io(body.key, body.f_id_kommune, body.f_id_strasse, body.count)
|
||||
except Exception as e:
|
||||
raise HTTPException(status_code=422, detail=str(e))
|
||||
if not collections:
|
||||
raise HTTPException(status_code=422, detail="No collections returned. Check your IDs.")
|
||||
return CollectionsResponse(source="abfall_io", collections=collections)
|
||||
|
||||
|
||||
@router.post("/awm_muenchen_de", response_model=CollectionsResponse)
|
||||
def collections_awm_muenchen(body: AWMRequest):
|
||||
try:
|
||||
collections = fetch_awm_muenchen(body.street, body.house_number, body.count)
|
||||
except Exception as e:
|
||||
raise HTTPException(status_code=422, detail=str(e))
|
||||
if not collections:
|
||||
raise HTTPException(status_code=422, detail="No collections returned. Check street name.")
|
||||
return CollectionsResponse(source="awm_muenchen_de", collections=collections)
|
||||
|
||||
|
||||
@router.post("/stuttgart_de", response_model=CollectionsResponse)
|
||||
def collections_stuttgart(body: StuttgartRequest):
|
||||
try:
|
||||
collections = fetch_stuttgart(body.street, body.streetnr, body.count)
|
||||
except Exception as e:
|
||||
raise HTTPException(status_code=422, detail=str(e))
|
||||
if not collections:
|
||||
raise HTTPException(status_code=422, detail="No collections returned. Check street name and number.")
|
||||
return CollectionsResponse(source="stuttgart_de", collections=collections)
|
||||
44
app/routes/notifications.py
Normal file
44
app/routes/notifications.py
Normal file
@@ -0,0 +1,44 @@
|
||||
from fastapi import APIRouter, HTTPException
|
||||
|
||||
from ..schemas import (
|
||||
NotifyRequest,
|
||||
NotifyResponse,
|
||||
NotificationsResponse,
|
||||
DeleteResponse,
|
||||
)
|
||||
from ..core.scheduler import NotificationScheduler
|
||||
|
||||
router = APIRouter(prefix="/notifications", tags=["notifications"])
|
||||
|
||||
# singleton scheduler
|
||||
_scheduler = NotificationScheduler()
|
||||
|
||||
|
||||
@router.post("", response_model=NotifyResponse)
|
||||
def create_notification(body: NotifyRequest):
|
||||
entry = _scheduler.add(
|
||||
source_id=body.source_id,
|
||||
collection=body.collection.model_dump(),
|
||||
notify_at_days_before=body.notify_at_days_before,
|
||||
channels=body.channels,
|
||||
chat_id=body.chat_id,
|
||||
)
|
||||
return NotifyResponse(
|
||||
ok=True,
|
||||
notification_id=entry["id"],
|
||||
message=f"Reminder set for {entry['collection_date']}: {entry['collection_type']}",
|
||||
)
|
||||
|
||||
|
||||
@router.get("", response_model=NotificationsResponse)
|
||||
def list_notifications():
|
||||
notes = _scheduler.list_all()
|
||||
return NotificationsResponse(count=len(notes), notifications=notes)
|
||||
|
||||
|
||||
@router.delete("/{notification_id}", response_model=DeleteResponse)
|
||||
def delete_notification(notification_id: str):
|
||||
ok = _scheduler.delete(notification_id)
|
||||
if not ok:
|
||||
raise HTTPException(status_code=404, detail=f"Notification '{notification_id}' not found")
|
||||
return DeleteResponse(ok=True, deleted=notification_id)
|
||||
65
app/routes/sources.py
Normal file
65
app/routes/sources.py
Normal file
@@ -0,0 +1,65 @@
|
||||
import json
|
||||
from pathlib import Path
|
||||
|
||||
from fastapi import APIRouter, HTTPException
|
||||
from fastapi.responses import FileResponse
|
||||
|
||||
from ..schemas import (
|
||||
SourceListResponse,
|
||||
SourceDetailResponse,
|
||||
SourceParam,
|
||||
)
|
||||
from ..core.scheduler import SOURCE_DEFS
|
||||
|
||||
router = APIRouter(prefix="/sources", tags=["sources"])
|
||||
|
||||
SOURCES_JSON = (
|
||||
Path(__file__).resolve().parent.parent.parent.parent
|
||||
/ "waste_lib"
|
||||
/ "custom_components"
|
||||
/ "waste_collection_schedule"
|
||||
/ "sources.json"
|
||||
)
|
||||
|
||||
|
||||
@router.get("", response_model=SourceListResponse)
|
||||
def list_sources():
|
||||
return SourceListResponse(
|
||||
count=len(SOURCE_DEFS),
|
||||
sources=[{"id": k, "name": v["name"]} for k, v in SOURCE_DEFS.items()],
|
||||
)
|
||||
|
||||
|
||||
@router.get("/germany")
|
||||
def list_germany_sources():
|
||||
"""
|
||||
Return all 727 German municipalities from sources.json.
|
||||
Includes title, module type, and default_params (if any).
|
||||
"""
|
||||
with open(SOURCES_JSON) as f:
|
||||
data = json.load(f)
|
||||
return {"country": "Germany", "count": len(data["Germany"]), "sources": data["Germany"]}
|
||||
|
||||
|
||||
@router.get("/germany/search")
|
||||
def search_germany_sources(q: str):
|
||||
"""
|
||||
Search Germany municipalities by name (case-insensitive).
|
||||
"""
|
||||
with open(SOURCES_JSON) as f:
|
||||
data = json.load(f)
|
||||
q = q.lower()
|
||||
results = [s for s in data["Germany"] if q in s["title"].lower()]
|
||||
return {"query": q, "count": len(results), "sources": results[:20]}
|
||||
|
||||
|
||||
@router.get("/{source_id}", response_model=SourceDetailResponse)
|
||||
def get_source(source_id: str):
|
||||
if source_id not in SOURCE_DEFS:
|
||||
raise HTTPException(status_code=404, detail=f"Source '{source_id}' not found")
|
||||
s = SOURCE_DEFS[source_id]
|
||||
return SourceDetailResponse(
|
||||
id=source_id,
|
||||
name=s["name"],
|
||||
params=[SourceParam(**p) for p in s["params"]],
|
||||
)
|
||||
Reference in New Issue
Block a user