LarsHoliday / api.py
phhttps
feat: enhance scraper reliability, observability and scheduling
5dc68a0
from fastapi import FastAPI, Query
from fastapi.middleware.cors import CORSMiddleware
from fastapi.responses import FileResponse
from typing import Dict, List
from pydantic import BaseModel
from holland_agent import HollandVacationAgent
from observability import observability_tracker
from report_generator import ReportGenerator
import os
import uvicorn
class ExportRequest(BaseModel):
deals: List[Dict]
search_params: Dict
app = FastAPI(title="Lars Holiday Deal API")
report_gen = ReportGenerator()
agent = HollandVacationAgent()
# Allow requests
app.add_middleware(
CORSMiddleware,
allow_origins=["*"],
allow_credentials=True,
allow_methods=["*"],
allow_headers=["*"],
)
def get_env_robust(key_name: str):
# Try exact match
val = os.getenv(key_name)
if val: return val
# Try lowercase/uppercase
for k, v in os.environ.items():
if k.upper().strip() == key_name.upper():
return v
return None
@app.get("/health")
async def health_check():
return {
"status": "online",
"keys_found": {
"OPENWEATHER_API_KEY": bool(get_env_robust("OPENWEATHER_API_KEY")),
"FIRECRAWL_API_KEY": bool(get_env_robust("FIRECRAWL_API_KEY")),
"AGENT_BROWSER_SESSION": bool(get_env_robust("AGENT_BROWSER_SESSION")),
},
"available_vars": [k for k in os.environ.keys() if "API" in k or "KEY" in k],
"python_version": os.sys.version,
"observability": observability_tracker.snapshot(),
}
@app.get("/")
async def serve_frontend():
return FileResponse("frontend_dashboard.html")
@app.get("/search")
async def search_deals(
cities: str = Query(..., description="Comma-separated list of cities"),
checkin: str = Query(..., description="Check-in date (YYYY-MM-DD)"),
checkout: str = Query(..., description="Check-out date (YYYY-MM-DD)"),
adults: int = 4,
children: int = 0,
pets: int = 1,
budget: int = 250,
budget_type: str = "night"
):
print(f"\n--- [API Request] Suche gestartet: {cities} ---")
city_list = [c.strip() for c in cities.split(",")]
# Run the existing agent logic
print(f"--- [API Request] Agent findet Deals für {len(city_list)} Städte...")
results = await agent.find_best_deals(
cities=city_list,
checkin=checkin,
checkout=checkout,
group_size=adults,
children=children,
pets=pets,
budget=budget,
budget_type=budget_type,
)
print(
f"--- [API Request] Agent fertig. Run-ID={results.get('run_id')} | "
f"{results.get('total_deals_found')} Deals gefunden."
)
# Process all deal lists for image fallbacks
for key in ["top_10_deals", "top_airbnb_deals", "top_booking_deals"]:
deals = results.get(key, [])
for deal in deals:
if not deal.get("image_url"):
deal["image_url"] = "https://via.placeholder.com/800x450.png?text=KEIN+BILD+GEFUNDEN"
results[key] = deals
return results
@app.post("/export-pdf")
async def export_pdf(request: ExportRequest):
print(f"--- [API Request] PDF Export gestartet für {len(request.deals)} Deals ---")
# Create temp filename
import tempfile
with tempfile.NamedTemporaryFile(delete=False, suffix=".pdf") as tmp:
filename = tmp.name
success = report_gen.generate_report(
deals=request.deals,
search_params=request.search_params,
filename=filename
)
if success:
return FileResponse(
filename,
media_type="application/pdf",
filename="UrlaubsDeals.pdf",
background=None # FileResponse handles cleanup if we use a background task but let's keep it simple
)
return {"error": "PDF generation failed"}
if __name__ == "__main__":
uvicorn.run("api:app", host="0.0.0.0", port=8000, reload=True)