Update app.py
Browse files
app.py
CHANGED
@@ -48,6 +48,8 @@ class AirQualityMapper:
|
|
48 |
if not api_key or api_key.strip() == "":
|
49 |
return [], "β Please enter a valid AirNow API key"
|
50 |
|
|
|
|
|
51 |
try:
|
52 |
# Get data for major US cities and regions
|
53 |
# We'll use a comprehensive list of state capitals and major cities
|
@@ -72,8 +74,9 @@ class AirQualityMapper:
|
|
72 |
]
|
73 |
|
74 |
all_data = []
|
|
|
75 |
|
76 |
-
for zipcode, state in locations:
|
77 |
try:
|
78 |
# Current observations endpoint
|
79 |
url = f"{self.base_url}/aq/observation/zipCode/current/"
|
@@ -84,24 +87,35 @@ class AirQualityMapper:
|
|
84 |
"API_KEY": api_key
|
85 |
}
|
86 |
|
|
|
87 |
response = requests.get(url, params=params, timeout=10)
|
88 |
|
|
|
|
|
89 |
if response.status_code == 200:
|
90 |
data = response.json()
|
|
|
91 |
if data: # If data is not empty
|
92 |
for observation in data:
|
93 |
observation['source_state'] = state
|
94 |
observation['source_zipcode'] = zipcode
|
95 |
all_data.extend(data)
|
|
|
|
|
|
|
96 |
|
97 |
# Add delay to respect rate limits
|
98 |
time.sleep(0.5)
|
99 |
|
100 |
except requests.exceptions.RequestException as e:
|
|
|
101 |
continue # Skip this location and continue with others
|
102 |
|
|
|
|
|
|
|
103 |
if not all_data:
|
104 |
-
return [], "β οΈ No air quality data found. Please check your API key or try again later."
|
105 |
|
106 |
# Remove duplicates based on reporting area
|
107 |
seen_areas = set()
|
@@ -112,9 +126,10 @@ class AirQualityMapper:
|
|
112 |
seen_areas.add(area_key)
|
113 |
unique_data.append(item)
|
114 |
|
115 |
-
return unique_data, f"β
Successfully loaded {len(unique_data)} monitoring locations"
|
116 |
|
117 |
except Exception as e:
|
|
|
118 |
return [], f"β Error fetching data: {str(e)}"
|
119 |
|
120 |
def create_map(self, data: List[Dict]) -> str:
|
@@ -203,7 +218,7 @@ class AirQualityMapper:
|
|
203 |
font-size:14px; padding: 10px">
|
204 |
<h4>AQI Legend</h4>
|
205 |
<p><i class="fa fa-circle" style="color:green"></i> Good (0-50)</p>
|
206 |
-
<p><i class="fa fa-circle" style="color:
|
207 |
<p><i class="fa fa-circle" style="color:orange"></i> Unhealthy for Sensitive (101-150)</p>
|
208 |
<p><i class="fa fa-circle" style="color:red"></i> Unhealthy (151-200)</p>
|
209 |
<p><i class="fa fa-circle" style="color:purple"></i> Very Unhealthy (201-300)</p>
|
@@ -240,6 +255,12 @@ class AirQualityMapper:
|
|
240 |
# Initialize the mapper
|
241 |
mapper = AirQualityMapper()
|
242 |
|
|
|
|
|
|
|
|
|
|
|
|
|
243 |
def update_map(api_key: str):
|
244 |
"""Update the map with fresh air quality data"""
|
245 |
# Check for environment variable first, then use provided key
|
@@ -248,7 +269,7 @@ def update_map(api_key: str):
|
|
248 |
api_key = env_api_key
|
249 |
|
250 |
if not api_key.strip():
|
251 |
-
return "Please enter your AirNow API key above or set AIRNOW_API_KEY environment variable.", pd.DataFrame()
|
252 |
|
253 |
# Fetch data
|
254 |
data, status = mapper.fetch_airnow_data(api_key)
|
@@ -259,7 +280,7 @@ def update_map(api_key: str):
|
|
259 |
# Create data table
|
260 |
df = mapper.create_data_table(data)
|
261 |
|
262 |
-
return map_html, df
|
263 |
|
264 |
# Create Gradio interface
|
265 |
with gr.Blocks(title="AirNow Air Quality Sensor Map", theme=gr.themes.Soft()) as demo:
|
@@ -341,12 +362,9 @@ with gr.Blocks(title="AirNow Air Quality Sensor Map", theme=gr.themes.Soft()) as
|
|
341 |
load_button.click(
|
342 |
fn=update_map,
|
343 |
inputs=[api_key_input],
|
344 |
-
outputs=[map_output, data_table]
|
345 |
-
).then(
|
346 |
-
fn=lambda: "Map updated with latest air quality data! π",
|
347 |
-
outputs=[status_text]
|
348 |
)
|
349 |
|
350 |
# Launch the app
|
351 |
if __name__ == "__main__":
|
352 |
-
demo.launch(
|
|
|
48 |
if not api_key or api_key.strip() == "":
|
49 |
return [], "β Please enter a valid AirNow API key"
|
50 |
|
51 |
+
print(f"Using API key: {api_key[:8]}..." if len(api_key) > 8 else "API key too short")
|
52 |
+
|
53 |
try:
|
54 |
# Get data for major US cities and regions
|
55 |
# We'll use a comprehensive list of state capitals and major cities
|
|
|
74 |
]
|
75 |
|
76 |
all_data = []
|
77 |
+
successful_requests = 0
|
78 |
|
79 |
+
for zipcode, state in locations[:10]: # Start with first 10 locations for testing
|
80 |
try:
|
81 |
# Current observations endpoint
|
82 |
url = f"{self.base_url}/aq/observation/zipCode/current/"
|
|
|
87 |
"API_KEY": api_key
|
88 |
}
|
89 |
|
90 |
+
print(f"Fetching data for {zipcode} ({state})...")
|
91 |
response = requests.get(url, params=params, timeout=10)
|
92 |
|
93 |
+
print(f"Response status: {response.status_code}")
|
94 |
+
|
95 |
if response.status_code == 200:
|
96 |
data = response.json()
|
97 |
+
print(f"Received {len(data) if data else 0} records for {state}")
|
98 |
if data: # If data is not empty
|
99 |
for observation in data:
|
100 |
observation['source_state'] = state
|
101 |
observation['source_zipcode'] = zipcode
|
102 |
all_data.extend(data)
|
103 |
+
successful_requests += 1
|
104 |
+
else:
|
105 |
+
print(f"Error response for {zipcode}: {response.text[:200]}")
|
106 |
|
107 |
# Add delay to respect rate limits
|
108 |
time.sleep(0.5)
|
109 |
|
110 |
except requests.exceptions.RequestException as e:
|
111 |
+
print(f"Request error for {zipcode}: {str(e)}")
|
112 |
continue # Skip this location and continue with others
|
113 |
|
114 |
+
print(f"Total successful requests: {successful_requests}")
|
115 |
+
print(f"Total data points collected: {len(all_data)}")
|
116 |
+
|
117 |
if not all_data:
|
118 |
+
return [], f"β οΈ No air quality data found after {successful_requests} successful API calls. Please check your API key or try again later."
|
119 |
|
120 |
# Remove duplicates based on reporting area
|
121 |
seen_areas = set()
|
|
|
126 |
seen_areas.add(area_key)
|
127 |
unique_data.append(item)
|
128 |
|
129 |
+
return unique_data, f"β
Successfully loaded {len(unique_data)} monitoring locations from {successful_requests} API calls"
|
130 |
|
131 |
except Exception as e:
|
132 |
+
print(f"General error: {str(e)}")
|
133 |
return [], f"β Error fetching data: {str(e)}"
|
134 |
|
135 |
def create_map(self, data: List[Dict]) -> str:
|
|
|
218 |
font-size:14px; padding: 10px">
|
219 |
<h4>AQI Legend</h4>
|
220 |
<p><i class="fa fa-circle" style="color:green"></i> Good (0-50)</p>
|
221 |
+
<p><i class="fa fa-circle" style="color:orange"></i> Moderate (51-100)</p>
|
222 |
<p><i class="fa fa-circle" style="color:orange"></i> Unhealthy for Sensitive (101-150)</p>
|
223 |
<p><i class="fa fa-circle" style="color:red"></i> Unhealthy (151-200)</p>
|
224 |
<p><i class="fa fa-circle" style="color:purple"></i> Very Unhealthy (201-300)</p>
|
|
|
255 |
# Initialize the mapper
|
256 |
mapper = AirQualityMapper()
|
257 |
|
258 |
+
# Check environment variable on startup
|
259 |
+
env_api_key = os.getenv('AIRNOW_API_KEY')
|
260 |
+
print(f"Environment variable AIRNOW_API_KEY: {'SET' if env_api_key else 'NOT SET'}")
|
261 |
+
if env_api_key:
|
262 |
+
print(f"API key starts with: {env_api_key[:8]}...")
|
263 |
+
|
264 |
def update_map(api_key: str):
|
265 |
"""Update the map with fresh air quality data"""
|
266 |
# Check for environment variable first, then use provided key
|
|
|
269 |
api_key = env_api_key
|
270 |
|
271 |
if not api_key.strip():
|
272 |
+
return "Please enter your AirNow API key above or set AIRNOW_API_KEY environment variable.", pd.DataFrame(), "β No API key provided"
|
273 |
|
274 |
# Fetch data
|
275 |
data, status = mapper.fetch_airnow_data(api_key)
|
|
|
280 |
# Create data table
|
281 |
df = mapper.create_data_table(data)
|
282 |
|
283 |
+
return map_html, df, status
|
284 |
|
285 |
# Create Gradio interface
|
286 |
with gr.Blocks(title="AirNow Air Quality Sensor Map", theme=gr.themes.Soft()) as demo:
|
|
|
362 |
load_button.click(
|
363 |
fn=update_map,
|
364 |
inputs=[api_key_input],
|
365 |
+
outputs=[map_output, data_table, status_text]
|
|
|
|
|
|
|
366 |
)
|
367 |
|
368 |
# Launch the app
|
369 |
if __name__ == "__main__":
|
370 |
+
demo.launch()
|