bluenevus commited on
Commit
563d7e0
·
1 Parent(s): 880474a

Update app.py via AI Editor

Browse files
Files changed (1) hide show
  1. app.py +109 -103
app.py CHANGED
@@ -12,14 +12,11 @@ import uuid
12
  import flask
13
  import logging
14
 
15
- # Load environment variables
16
  load_dotenv()
17
 
18
- # Logging setup
19
  logging.basicConfig(level=logging.INFO)
20
  logger = logging.getLogger("weather_dash")
21
 
22
- # Session and threading management
23
  session_locks = {}
24
  session_data_store = {}
25
  SESSION_COOKIE = "weather_dash_session_id"
@@ -49,14 +46,17 @@ def get_data_from_session(session_id, key):
49
  session_data = get_session_data(session_id)
50
  return session_data.get(key, None)
51
 
52
- # AccuWeather API key
53
  API_KEY = os.getenv('ACCUWEATHER_API_KEY')
54
-
55
- # Base URL for AccuWeather API
56
  BASE_URL = "http://dataservice.accuweather.com"
57
 
58
- # Health-related index keywords
59
- HEALTH_INDEX_KEYWORDS = ["Air Quality", "Asthma", "Flu", "Allergy", "Respiratory", "Pollen"]
 
 
 
 
 
 
60
 
61
  def get_location_key(lat, lon):
62
  url = f"{BASE_URL}/locations/v1/cities/geoposition/search"
@@ -94,66 +94,66 @@ def get_current_conditions(location_key):
94
  logger.error(f"Error in get_current_conditions: {e}")
95
  return None
96
 
97
- def get_forecast(location_key):
98
  if location_key is None:
99
  return None
100
  url = f"{BASE_URL}/forecasts/v1/daily/5day/{location_key}"
101
  params = {
102
  "apikey": API_KEY,
103
- "metric": "false", # Fahrenheit
104
  }
105
  try:
106
  response = requests.get(url, params=params)
107
  response.raise_for_status()
108
  return response.json()
109
  except requests.RequestException as e:
110
- logger.error(f"Error in get_forecast: {e}")
111
  return None
112
 
113
- def get_hourly_forecast(location_key):
114
  if location_key is None:
115
  return None
116
- url = f"{BASE_URL}/forecasts/v1/hourly/12hour/{location_key}"
117
  params = {
118
  "apikey": API_KEY,
119
- "metric": "false", # Fahrenheit
120
  }
121
  try:
122
  response = requests.get(url, params=params)
123
  response.raise_for_status()
124
  return response.json()
125
  except requests.RequestException as e:
126
- logger.error(f"Error in get_hourly_forecast: {e}")
127
  return None
128
 
129
- def get_indices(location_key):
130
  if location_key is None:
131
  return None
132
- url = f"{BASE_URL}/indices/v1/daily/5day/{location_key}"
133
  params = {
134
  "apikey": API_KEY,
 
135
  }
136
  try:
137
  response = requests.get(url, params=params)
138
  response.raise_for_status()
139
  return response.json()
140
  except requests.RequestException as e:
141
- logger.error(f"Error in get_indices: {e}")
142
  return None
143
 
144
- def get_alerts(location_key):
145
  if location_key is None:
146
  return None
147
- url = f"{BASE_URL}/alerts/v1/{location_key}"
148
- params = {
149
- "apikey": API_KEY,
150
- }
151
  try:
152
  response = requests.get(url, params=params)
153
  response.raise_for_status()
 
154
  return response.json()
155
  except requests.RequestException as e:
156
- logger.error(f"Error in get_alerts: {e}")
157
  return None
158
 
159
  def create_current_weather_card(current):
@@ -168,40 +168,65 @@ def create_current_weather_card(current):
168
  ])
169
  ], className="mb-4")
170
 
171
- def create_hourly_forecast_card(hourly):
172
- if not hourly:
173
  return dbc.Card([
174
  dbc.CardBody([
175
- html.H4("12-Hour Hourly Forecast", className="card-title"),
176
- html.P("No hourly forecast available")
177
  ])
178
  ], className="mb-4")
179
- hours = [datetime.strptime(hr['DateTime'], "%Y-%m-%dT%H:%M:%S%z").strftime("%I %p") for hr in hourly]
180
- temps = [hr['Temperature']['Value'] for hr in hourly]
181
- conditions = [hr['IconPhrase'] for hr in hourly]
182
- fig = go.Figure()
183
- fig.add_trace(go.Scatter(x=hours, y=temps, name="Temperature", line=dict(color="orange")))
184
- fig.update_layout(
185
- title="Next 12 Hours Forecast",
186
- xaxis_title="Time",
187
- yaxis_title="Temperature (°F)",
188
- legend_title="Legend",
189
- height=350,
190
- margin=dict(l=40, r=40, t=40, b=40)
191
- )
192
  return dbc.Card([
193
  dbc.CardBody([
194
- html.H4("12-Hour Hourly Forecast", className="card-title"),
195
- dcc.Graph(figure=fig)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
196
  ])
197
  ], className="mb-4")
198
 
199
- def create_forecast_card(forecast):
 
 
 
 
 
 
 
200
  daily_forecasts = forecast['DailyForecasts']
201
- fig = go.Figure()
202
  dates = [datetime.strptime(day['Date'], "%Y-%m-%dT%H:%M:%S%z").strftime("%m-%d") for day in daily_forecasts]
203
  max_temps = [day['Temperature']['Maximum']['Value'] for day in daily_forecasts]
204
  min_temps = [day['Temperature']['Minimum']['Value'] for day in daily_forecasts]
 
205
  fig.add_trace(go.Scatter(x=dates, y=max_temps, name="Max Temp", line=dict(color="red")))
206
  fig.add_trace(go.Scatter(x=dates, y=min_temps, name="Min Temp", line=dict(color="blue")))
207
  fig.update_layout(
@@ -218,51 +243,30 @@ def create_forecast_card(forecast):
218
  ])
219
  ], className="mb-4")
220
 
221
- def create_indices_card(indices):
222
- if not indices:
223
- return dbc.Card([
224
- dbc.CardBody([
225
- html.H4("Health Indices", className="card-title"),
226
- html.P("No health indices available.")
227
- ])
228
- ], className="mb-4")
229
- health_indices = []
230
- for idx in indices:
231
- for keyword in HEALTH_INDEX_KEYWORDS:
232
- if keyword.lower() in idx['Name'].lower():
233
- health_indices.append(idx)
234
- break
235
- if not health_indices:
236
- return dbc.Card([
237
- dbc.CardBody([
238
- html.H4("Health Indices", className="card-title"),
239
- html.P("No health-related indices available.")
240
- ])
241
- ], className="mb-4")
242
- return dbc.Card([
243
- dbc.CardBody([
244
- html.H4("Health Indices", className="card-title"),
245
- html.Div([
246
- html.P(f"{index['Name']}: {index['Category']}")
247
- for index in health_indices[:5]
248
- ])
249
- ])
250
- ], className="mb-4")
251
-
252
- def create_alert_card(alerts):
253
- if not alerts:
254
- return html.Div()
255
  return dbc.Card([
256
  dbc.CardBody([
257
- html.H4("Weather Alerts", className="card-title text-danger"),
258
- html.Div([
259
- html.P(alert['Text'])
260
- for alert in alerts
261
- ])
262
  ])
263
  ], className="mb-4")
264
 
265
- # App layout
266
  app = dash.Dash(__name__, external_stylesheets=[dbc.themes.BOOTSTRAP])
267
  server = app.server
268
 
@@ -281,7 +285,7 @@ app.layout = dbc.Container([
281
  dbc.CardBody([
282
  html.H5("Navigation", className="card-title"),
283
  dbc.Nav([
284
- dbc.NavLink("Health Indices", href="#", id="nav-health"),
285
  dbc.NavLink("Current Weather", href="#", id="nav-current"),
286
  ], vertical=True, pills=True)
287
  ])
@@ -302,8 +306,8 @@ app.layout = dbc.Container([
302
  type="default",
303
  children=[
304
  html.Div(id="hourly-forecast-output"),
305
- html.Div(id="forecast-output"),
306
- html.Div(id="alert-output")
307
  ],
308
  style={"width": "100%"}
309
  )
@@ -350,8 +354,8 @@ def set_session_cookie(response):
350
  Output("indices-output", "children"),
351
  Output("current-weather-output", "children"),
352
  Output("hourly-forecast-output", "children"),
 
353
  Output("forecast-output", "children"),
354
- Output("alert-output", "children"),
355
  ],
356
  [Input("location-store", "data")],
357
  [State("session-store", "data")]
@@ -366,7 +370,7 @@ def update_weather(location, session_data):
366
  return [dbc.Spinner(color="primary"), "", "", "", ""]
367
 
368
  lat, lon = location["latitude"], location["longitude"]
369
- results = {"indices": "", "current": "", "hourly": "", "forecast": "", "alerts": ""}
370
  def fetch_weather_data():
371
  try:
372
  location_key = get_data_from_session(session_id, "location_key")
@@ -377,27 +381,29 @@ def update_weather(location, session_data):
377
  raise ValueError("Failed to get location key")
378
 
379
  current = get_current_conditions(location_key)
380
- forecast = get_forecast(location_key)
381
- hourly = get_hourly_forecast(location_key)
382
- indices = get_indices(location_key)
383
- alerts = get_alerts(location_key)
384
-
385
- if current is None or forecast is None or indices is None:
 
 
386
  raise ValueError("Failed to fetch weather data")
387
 
388
- results["indices"] = create_indices_card(indices)
389
  results["current"] = create_current_weather_card(current)
390
- results["hourly"] = create_hourly_forecast_card(hourly)
391
- results["forecast"] = create_forecast_card(forecast)
392
- results["alerts"] = create_alert_card(alerts)
393
  save_session_data(session_id, "weather_results", results)
394
  except Exception as e:
395
  logger.error(f"Session {session_id} error: {str(e)}")
396
  results["indices"] = ""
397
  results["current"] = ""
398
  results["hourly"] = ""
399
- results["forecast"] = ""
400
- results["alerts"] = dbc.Card([
401
  dbc.CardBody([
402
  html.P(f"Error fetching weather data: {str(e)}", className="text-danger")
403
  ])
@@ -412,8 +418,8 @@ def update_weather(location, session_data):
412
  weather_results.get("indices", ""),
413
  weather_results.get("current", ""),
414
  weather_results.get("hourly", ""),
 
415
  weather_results.get("forecast", ""),
416
- weather_results.get("alerts", ""),
417
  ]
418
  else:
419
  return [dbc.Spinner(color="primary"), "", "", "", ""]
 
12
  import flask
13
  import logging
14
 
 
15
  load_dotenv()
16
 
 
17
  logging.basicConfig(level=logging.INFO)
18
  logger = logging.getLogger("weather_dash")
19
 
 
20
  session_locks = {}
21
  session_data_store = {}
22
  SESSION_COOKIE = "weather_dash_session_id"
 
46
  session_data = get_session_data(session_id)
47
  return session_data.get(key, None)
48
 
 
49
  API_KEY = os.getenv('ACCUWEATHER_API_KEY')
 
 
50
  BASE_URL = "http://dataservice.accuweather.com"
51
 
52
+ # Index IDs from AccuWeather's documentation (example IDs, replace with actual as needed)
53
+ INDEX_IDS = {
54
+ "Health": 31, # e.g., Flu Index
55
+ "Environmental": 34, # e.g., Air Quality Index
56
+ "Pollen": 11, # e.g., Tree Pollen
57
+ "Mosquito": 50, # e.g., Mosquito Index
58
+ "Pests": 53 # e.g., Pest Activity
59
+ }
60
 
61
  def get_location_key(lat, lon):
62
  url = f"{BASE_URL}/locations/v1/cities/geoposition/search"
 
94
  logger.error(f"Error in get_current_conditions: {e}")
95
  return None
96
 
97
+ def get_forecast_5day(location_key):
98
  if location_key is None:
99
  return None
100
  url = f"{BASE_URL}/forecasts/v1/daily/5day/{location_key}"
101
  params = {
102
  "apikey": API_KEY,
103
+ "metric": "false",
104
  }
105
  try:
106
  response = requests.get(url, params=params)
107
  response.raise_for_status()
108
  return response.json()
109
  except requests.RequestException as e:
110
+ logger.error(f"Error in get_forecast_5day: {e}")
111
  return None
112
 
113
+ def get_forecast_1day(location_key):
114
  if location_key is None:
115
  return None
116
+ url = f"{BASE_URL}/forecasts/v1/daily/1day/{location_key}"
117
  params = {
118
  "apikey": API_KEY,
119
+ "metric": "false",
120
  }
121
  try:
122
  response = requests.get(url, params=params)
123
  response.raise_for_status()
124
  return response.json()
125
  except requests.RequestException as e:
126
+ logger.error(f"Error in get_forecast_1day: {e}")
127
  return None
128
 
129
+ def get_hourly_forecast_1hour(location_key):
130
  if location_key is None:
131
  return None
132
+ url = f"{BASE_URL}/forecasts/v1/hourly/1hour/{location_key}"
133
  params = {
134
  "apikey": API_KEY,
135
+ "metric": "false",
136
  }
137
  try:
138
  response = requests.get(url, params=params)
139
  response.raise_for_status()
140
  return response.json()
141
  except requests.RequestException as e:
142
+ logger.error(f"Error in get_hourly_forecast_1hour: {e}")
143
  return None
144
 
145
+ def get_indices_1day(location_key, index_id):
146
  if location_key is None:
147
  return None
148
+ url = f"{BASE_URL}/indices/v1/daily/1day/{location_key}/{index_id}"
149
+ params = {"apikey": API_KEY}
 
 
150
  try:
151
  response = requests.get(url, params=params)
152
  response.raise_for_status()
153
+ # returns a list with one dict per day (only 1 for 1day endpoint)
154
  return response.json()
155
  except requests.RequestException as e:
156
+ logger.error(f"Error in get_indices_1day {index_id}: {e}")
157
  return None
158
 
159
  def create_current_weather_card(current):
 
168
  ])
169
  ], className="mb-4")
170
 
171
+ def create_hourly_1hour_card(hourly):
172
+ if not hourly or not isinstance(hourly, list):
173
  return dbc.Card([
174
  dbc.CardBody([
175
+ html.H4("Next Hour Forecast", className="card-title"),
176
+ html.P("No 1-hour forecast available")
177
  ])
178
  ], className="mb-4")
179
+ hr = hourly[0]
180
+ dt = datetime.strptime(hr['DateTime'], "%Y-%m-%dT%H:%M:%S%z").strftime("%I:%M %p")
181
+ temp = hr['Temperature']['Value']
182
+ phrase = hr['IconPhrase']
 
 
 
 
 
 
 
 
 
183
  return dbc.Card([
184
  dbc.CardBody([
185
+ html.H4("Next Hour Forecast", className="card-title"),
186
+ html.P(f"Time: {dt}"),
187
+ html.P(f"Temperature: {temp}°F"),
188
+ html.P(f"Condition: {phrase}")
189
+ ])
190
+ ], className="mb-4")
191
+
192
+ def create_forecast_1day_card(forecast):
193
+ if not forecast or 'DailyForecasts' not in forecast or not forecast['DailyForecasts']:
194
+ return dbc.Card([
195
+ dbc.CardBody([
196
+ html.H4("1-Day Forecast", className="card-title"),
197
+ html.P("No 1-day forecast available.")
198
+ ])
199
+ ], className="mb-4")
200
+ day = forecast['DailyForecasts'][0]
201
+ date = datetime.strptime(day['Date'], "%Y-%m-%dT%H:%M:%S%z").strftime("%A, %m-%d")
202
+ max_temp = day['Temperature']['Maximum']['Value']
203
+ min_temp = day['Temperature']['Minimum']['Value']
204
+ day_phrase = day['Day']['IconPhrase']
205
+ night_phrase = day['Night']['IconPhrase']
206
+ return dbc.Card([
207
+ dbc.CardBody([
208
+ html.H4("1-Day Forecast", className="card-title"),
209
+ html.P(f"Date: {date}"),
210
+ html.P(f"High: {max_temp}°F"),
211
+ html.P(f"Low: {min_temp}°F"),
212
+ html.P(f"Day: {day_phrase}"),
213
+ html.P(f"Night: {night_phrase}")
214
  ])
215
  ], className="mb-4")
216
 
217
+ def create_forecast_5day_card(forecast):
218
+ if not forecast or 'DailyForecasts' not in forecast or not forecast['DailyForecasts']:
219
+ return dbc.Card([
220
+ dbc.CardBody([
221
+ html.H4("5-Day Forecast", className="card-title"),
222
+ html.P("No 5-day forecast available.")
223
+ ])
224
+ ], className="mb-4")
225
  daily_forecasts = forecast['DailyForecasts']
 
226
  dates = [datetime.strptime(day['Date'], "%Y-%m-%dT%H:%M:%S%z").strftime("%m-%d") for day in daily_forecasts]
227
  max_temps = [day['Temperature']['Maximum']['Value'] for day in daily_forecasts]
228
  min_temps = [day['Temperature']['Minimum']['Value'] for day in daily_forecasts]
229
+ fig = go.Figure()
230
  fig.add_trace(go.Scatter(x=dates, y=max_temps, name="Max Temp", line=dict(color="red")))
231
  fig.add_trace(go.Scatter(x=dates, y=min_temps, name="Min Temp", line=dict(color="blue")))
232
  fig.update_layout(
 
243
  ])
244
  ], className="mb-4")
245
 
246
+ def create_indices_card(indices_dict):
247
+ cards = []
248
+ for idx_name, idx_data in indices_dict.items():
249
+ if not idx_data or not isinstance(idx_data, list) or 'Category' not in idx_data[0]:
250
+ cards.append(html.Div([
251
+ html.H5(f"{idx_name} Index"),
252
+ html.P("No data available.")
253
+ ], style={"marginBottom": "10px"}))
254
+ else:
255
+ info = idx_data[0]
256
+ cards.append(html.Div([
257
+ html.H5(f"{idx_name} Index"),
258
+ html.P(f"{info.get('Name', '')}: {info.get('Category', '')}"),
259
+ html.P(f"Value: {info.get('Value', 'N/A')}"),
260
+ html.P(f"Category Value: {info.get('CategoryValue', 'N/A')}"),
261
+ html.P(f"Text: {info.get('Text', '')}")
262
+ ], style={"marginBottom": "10px"}))
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
263
  return dbc.Card([
264
  dbc.CardBody([
265
+ html.H4("Today's Health & Environmental Indices", className="card-title"),
266
+ *cards
 
 
 
267
  ])
268
  ], className="mb-4")
269
 
 
270
  app = dash.Dash(__name__, external_stylesheets=[dbc.themes.BOOTSTRAP])
271
  server = app.server
272
 
 
285
  dbc.CardBody([
286
  html.H5("Navigation", className="card-title"),
287
  dbc.Nav([
288
+ dbc.NavLink("Health & Env. Indices", href="#", id="nav-health"),
289
  dbc.NavLink("Current Weather", href="#", id="nav-current"),
290
  ], vertical=True, pills=True)
291
  ])
 
306
  type="default",
307
  children=[
308
  html.Div(id="hourly-forecast-output"),
309
+ html.Div(id="forecast-1day-output"),
310
+ html.Div(id="forecast-output")
311
  ],
312
  style={"width": "100%"}
313
  )
 
354
  Output("indices-output", "children"),
355
  Output("current-weather-output", "children"),
356
  Output("hourly-forecast-output", "children"),
357
+ Output("forecast-1day-output", "children"),
358
  Output("forecast-output", "children"),
 
359
  ],
360
  [Input("location-store", "data")],
361
  [State("session-store", "data")]
 
370
  return [dbc.Spinner(color="primary"), "", "", "", ""]
371
 
372
  lat, lon = location["latitude"], location["longitude"]
373
+ results = {"indices": "", "current": "", "hourly": "", "forecast_1day": "", "forecast": ""}
374
  def fetch_weather_data():
375
  try:
376
  location_key = get_data_from_session(session_id, "location_key")
 
381
  raise ValueError("Failed to get location key")
382
 
383
  current = get_current_conditions(location_key)
384
+ forecast_5day = get_forecast_5day(location_key)
385
+ forecast_1day = get_forecast_1day(location_key)
386
+ hourly_1hour = get_hourly_forecast_1hour(location_key)
387
+ indices_dict = {}
388
+ for idx_name, idx_id in INDEX_IDS.items():
389
+ indices_dict[idx_name] = get_indices_1day(location_key, idx_id)
390
+
391
+ if current is None or forecast_5day is None or forecast_1day is None:
392
  raise ValueError("Failed to fetch weather data")
393
 
394
+ results["indices"] = create_indices_card(indices_dict)
395
  results["current"] = create_current_weather_card(current)
396
+ results["hourly"] = create_hourly_1hour_card(hourly_1hour)
397
+ results["forecast_1day"] = create_forecast_1day_card(forecast_1day)
398
+ results["forecast"] = create_forecast_5day_card(forecast_5day)
399
  save_session_data(session_id, "weather_results", results)
400
  except Exception as e:
401
  logger.error(f"Session {session_id} error: {str(e)}")
402
  results["indices"] = ""
403
  results["current"] = ""
404
  results["hourly"] = ""
405
+ results["forecast_1day"] = ""
406
+ results["forecast"] = dbc.Card([
407
  dbc.CardBody([
408
  html.P(f"Error fetching weather data: {str(e)}", className="text-danger")
409
  ])
 
418
  weather_results.get("indices", ""),
419
  weather_results.get("current", ""),
420
  weather_results.get("hourly", ""),
421
+ weather_results.get("forecast_1day", ""),
422
  weather_results.get("forecast", ""),
 
423
  ]
424
  else:
425
  return [dbc.Spinner(color="primary"), "", "", "", ""]