from fastapi import FastAPI, HTTPException, Query from starlette.responses import JSONResponse from obspy.clients.fdsn import Client from obspy.core.utcdatetime import UTCDateTime import logging from datetime import date # Configure logging logging.basicConfig(level=logging.INFO) # Initialize the FastAPI app app = FastAPI( title="Dynamic Earthquake Catalog API", description="A robust API to fetch earthquake data from the IRIS FDSN web service using URL parameters.", version="2.1.0", ) # Initialize the IRIS FDSN client try: client = Client("IRIS") logging.info("Successfully initialized IRIS FDSN client.") except Exception as e: client = None logging.error(f"Could not initialize IRIS FDSN client on startup: {e}") @app.get("/") def greet_json(): """ A simple 'Hello World' endpoint. """ return {"Hello": "World!", "message": "Go to /docs to try the dynamic earthquake endpoint. cwadayi \n https://cwadayi-python-app.hf.space/earthquakes?start_date=2024-07-01&end_date=2024-07-07&min_magnitude=5.0"} @app.get("/earthquakes") async def get_earthquake_catalog( start_date: date = Query(default="2024-01-01", description="Start date in YYYY-MM-DD format"), end_date: date = Query(default="2024-01-07", description="End date in YYYY-MM-DD format"), min_magnitude: float = Query(default=4.5, description="Minimum earthquake magnitude (e.g., 5.5)", gt=0, le=10) ): """ Fetches earthquake data based on date and magnitude parameters provided in the URL. """ if not client: raise HTTPException(status_code=503, detail="The IRIS FDSN client is not available.") if start_date > end_date: raise HTTPException(status_code=400, detail="The start_date cannot be after the end_date.") try: starttime = UTCDateTime(start_date) endtime = UTCDateTime(end_date) logging.info(f"Fetching events from {starttime} to {endtime} with min magnitude {min_magnitude}.") catalog = client.get_events( starttime=starttime, endtime=endtime, minmagnitude=min_magnitude, ) logging.info(f"Found {len(catalog)} events. Now processing.") earthquake_list = [] for event in catalog: try: origin = event.preferred_origin() magnitude = event.preferred_magnitude() if not origin or not magnitude: continue # --- Key Change Start --- # Safely get the description using getattr. This avoids errors if the 'descriptions' # attribute doesn't exist, by providing a default empty list []. place = "N/A" descriptions = getattr(event, 'descriptions', []) if descriptions: place = descriptions[0].text # --- Key Change End --- earthquake_list.append({ "time": origin.time.isoformat(), "latitude": origin.latitude, "longitude": origin.longitude, "depth_km": origin.depth / 1000.0 if origin.depth is not None else None, "magnitude": magnitude.mag, "magnitude_type": magnitude.magnitude_type, "place": place, }) except Exception as e: logging.warning(f"Skipping event due to unexpected processing error: {e}") continue logging.info(f"Successfully processed {len(earthquake_list)} events.") return JSONResponse(content={"earthquakes": earthquake_list}) except Exception as e: logging.error(f"A major error occurred during the request: {e}") raise HTTPException(status_code=500, detail=f"An internal error occurred: {e}")