import requests import json import psycopg2 from datetime import datetime from requests.packages.urllib3.exceptions import InsecureRequestWarning requests.packages.urllib3.disable_warnings(InsecureRequestWarning) # Database connection details (keep these secure and configurable) DB_HOST = 'localhost' DB_DATABASE = 'nws' DB_USER = 'nws' DB_PASSWORD = 'nws' # API URL LIVING_ATLAS_URL = "https://services9.arcgis.com/RHVPKKiFTONKtxq3/ArcGIS/rest/services/USA_Wildfires_v1/FeatureServer/0/query?where=1%3D1&geometry=%7B%22spatialReference%22:%7B%22latestWkid%22:3857,%22wkid%22:102100%7D,%22xmin%22:-9222738.841522107,%22ymin%22:4457648.21239309,%22xmax%22:-9009938.154776277,%22ymax%22:4723649.070825376%7D&geometryType=esriGeometryEnvelope&spatialRelationship=intersects&inSR=3857&returnGeometry=true&returnQueryGeometry=true&outFields=IrwinID,IncidentName,POOState,ModifiedOnDateTime,FireDiscoveryDateTime,FireDiscoveryAge,IncidentTypeCategory,CalculatedAcres,DailyAcres,DiscoveryAcres,PercentContained,TotalIncidentPersonnel&f=json" S = requests.Session() S.verify = False # Be cautious about disabling SSL verification in production def livingatlas(url): try: result = S.get(url, timeout=10) # Added timeout to prevent indefinite hanging result.raise_for_status() # Raise HTTPError for bad responses (4xx or 5xx) data = result.json() # Use .json() directly, it handles json.loads() and error handling conn = None # Initialize conn outside the loop for broader scope cursor = None try: conn = psycopg2.connect(host=DB_HOST, database=DB_DATABASE, user=DB_USER, password=DB_PASSWORD) cursor = conn.cursor() for feature in data.get('features', []): # Safely access features attributes = feature.get('attributes', {}) geometry = feature.get('geometry', {}) incid = attributes.get('IrwinID') incname = attributes.get('IncidentName') state = attributes.get('POOState') modified_timestamp = attributes.get('ModifiedOnDateTime') discoverytime_timestamp = attributes.get('FireDiscoveryDateTime') discoveryage = attributes.get('FireDiscoveryAge') inctype = attributes.get('IncidentTypeCategory') calcacres = attributes.get('CalculatedAcres') dailyacres = attributes.get('DailyAcres') discoveryacres = attributes.get('DiscoveryAcres') contained = attributes.get('PercentContained') personnel = attributes.get('TotalIncidentPersonnel') lat = geometry.get('y') lon = geometry.get('x') discoverytime = datetime.fromtimestamp(discoverytime_timestamp/1000) if discoverytime_timestamp else None modified = datetime.fromtimestamp(modified_timestamp/1000) if modified_timestamp else None print(incid, incname, state, modified, discoverytime, discoveryage, inctype, calcacres, dailyacres, discoveryacres, contained, personnel, lat, lon) sql_insert = """ INSERT INTO fire ( incid, incname, state, modified, discovery, age, type, calcacres, dailyacres, discoveryacres, contained, personnel, lat, lon ) VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s) ON CONFLICT (incid) DO UPDATE SET incname = %s, state = %s, modified = %s, discovery = %s, age = %s, type = %s, calcacres = %s, dailyacres = %s, discoveryacres = %s, contained = %s, personnel = %s, lat = %s, lon = %s """ vals = ( incid, incname, state, modified, discoverytime, discoveryage, inctype, calcacres, dailyacres, discoveryacres, contained, personnel, lat, lon, incname, state, modified, discoverytime, discoveryage, inctype, calcacres, dailyacres, discoveryacres, contained, personnel, lat, lon ) cursor.execute(sql_insert, vals) conn.commit() # Commit after each successful insert/update except psycopg2.Error as db_error: if conn: conn.rollback() # Rollback transaction on error print(f"Database error: {db_error}") finally: if cursor: cursor.close() if conn: conn.close() except requests.exceptions.RequestException as req_error: # Catch broader request exceptions print(f"API Request error: {req_error}") except json.JSONDecodeError as json_error: print(f"JSON Decode error: {json_error}. Response text was: {result.text if 'result' in locals() else 'No response received'}") # More informative JSON error livingatlas(LIVING_ATLAS_URL) conn = None # Re-initialize conn for the geometry update outside the livingatlas function cursor = None try: conn = psycopg2.connect(host=DB_HOST, database=DB_DATABASE, user=DB_USER, password=DB_PASSWORD) cursor = conn.cursor() cursor.execute("UPDATE public.fire SET geom = ST_SetSRID(ST_MakePoint(lon, lat), 4326) WHERE (lat IS NOT NULL AND lon IS NOT NULL AND geom IS NULL)") conn.commit() except psycopg2.Error as db_error: if conn: conn.rollback() print(f"Database error during geometry update: {db_error}") finally: if cursor: cursor.close() if conn: conn.close() print("Script execution completed.")