Spaces:
Sleeping
Sleeping
Update app.py
Browse files
app.py
CHANGED
@@ -2,95 +2,124 @@ import dash
|
|
2 |
from dash import dcc, html, Input, Output
|
3 |
import plotly.graph_objs as go
|
4 |
import requests
|
|
|
5 |
from datetime import datetime
|
6 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
7 |
|
8 |
# Initialize Dash app
|
9 |
app = dash.Dash(__name__)
|
10 |
app.title = "Weather Forecast Dashboard"
|
11 |
|
12 |
-
#
|
13 |
-
API_KEY = "53d50455f91b6bc3c920959e2954576d"
|
|
|
|
|
|
|
|
|
14 |
|
15 |
# Layout
|
16 |
app.layout = html.Div([
|
17 |
html.Div([
|
18 |
html.H1("Weather Forecast Dashboard", style={'textAlign': 'center', 'color': '#FFFFFF'}),
|
19 |
-
html.P("
|
20 |
style={'textAlign': 'center', 'color': '#D3D9D4'})
|
21 |
], style={'backgroundColor': '#212A31', 'padding': '20px'}),
|
22 |
|
23 |
html.Div([
|
24 |
-
|
25 |
-
|
26 |
-
html.Button('Get Weather', id='submit-btn', n_clicks=0,
|
27 |
-
style={'backgroundColor': '#748D92', 'color': '#212A31', 'padding': '5px 15px'}),
|
28 |
-
dcc.Dropdown(id='forecast-days', options=[
|
29 |
-
{'label': '5 Days', 'value': 5},
|
30 |
-
{'label': '10 Days', 'value': 10}
|
31 |
-
], value=5, style={'width': '50%', 'margin': '10px'})
|
32 |
], style={'backgroundColor': '#2E3944', 'padding': '20px', 'borderRadius': '10px'}),
|
33 |
-
|
34 |
-
html.Div(id='weather-summary', style={'color': '#FFFFFF', 'padding': '20px'}),
|
35 |
-
dcc.Graph(id='temperature-graph'),
|
36 |
-
dcc.Graph(id='precipitation-graph'),
|
37 |
-
dcc.Graph(id='wind-graph'),
|
38 |
], style={'backgroundColor': '#1A2329', 'fontFamily': 'Arial', 'minHeight': '100vh'})
|
39 |
|
40 |
# Callback to update dashboard
|
41 |
@app.callback(
|
42 |
-
|
43 |
-
|
44 |
-
Output('precipitation-graph', 'figure'),
|
45 |
-
Output('wind-graph', 'figure')],
|
46 |
-
[Input('submit-btn', 'n_clicks')],
|
47 |
-
[dash.dependencies.State('city-input', 'value'),
|
48 |
-
dash.dependencies.State('forecast-days', 'value')]
|
49 |
)
|
50 |
-
def update_dashboard(
|
51 |
-
|
52 |
-
|
53 |
-
|
54 |
-
|
55 |
-
|
56 |
-
|
57 |
-
|
58 |
-
|
59 |
-
|
60 |
-
|
61 |
-
|
62 |
-
|
63 |
-
|
64 |
-
|
65 |
-
html.
|
66 |
-
|
67 |
-
|
68 |
-
|
69 |
-
|
70 |
-
|
71 |
-
|
72 |
-
|
73 |
-
|
74 |
-
|
75 |
-
|
76 |
-
|
77 |
-
|
78 |
-
|
79 |
-
|
80 |
-
|
81 |
-
|
82 |
-
|
83 |
-
|
84 |
-
|
85 |
-
|
86 |
-
|
87 |
-
|
88 |
-
|
89 |
-
|
90 |
-
|
91 |
-
|
92 |
-
|
93 |
-
|
|
|
|
|
|
|
94 |
|
95 |
# Run server on port 7860 for Hugging Face Spaces
|
96 |
if __name__ == '__main__':
|
|
|
2 |
from dash import dcc, html, Input, Output
|
3 |
import plotly.graph_objs as go
|
4 |
import requests
|
5 |
+
import os
|
6 |
from datetime import datetime
|
7 |
+
|
8 |
+
# Assuming weather_model.py exists; if not, we'll inline it
|
9 |
+
try:
|
10 |
+
from weather_model import get_weather_data
|
11 |
+
except ImportError:
|
12 |
+
def get_weather_data(city, days, api_key):
|
13 |
+
try:
|
14 |
+
geo_url = f"http://api.openweathermap.org/geo/1.0/direct?q={city}&limit=1&appid={api_key}"
|
15 |
+
geo_response = requests.get(geo_url).json()
|
16 |
+
if not geo_response:
|
17 |
+
return None
|
18 |
+
lat, lon = geo_response[0]['lat'], geo_response[0]['lon']
|
19 |
+
|
20 |
+
url = f"http://api.openweathermap.org/data/2.5/onecall?lat={lat}&lon={lon}&exclude=minutely,hourly&units=metric&appid={api_key}"
|
21 |
+
response = requests.get(url).json()
|
22 |
+
|
23 |
+
current = {
|
24 |
+
'temp': response['current']['temp'],
|
25 |
+
'weather': response['current']['weather'][0]['description'],
|
26 |
+
'humidity': response['current']['humidity'],
|
27 |
+
'wind_speed': response['current']['wind_speed']
|
28 |
+
}
|
29 |
+
|
30 |
+
forecast = []
|
31 |
+
for i in range(min(days, len(response['daily']))):
|
32 |
+
day = response['daily'][i]
|
33 |
+
forecast.append({
|
34 |
+
'date': datetime.fromtimestamp(day['dt']).strftime('%Y-%m-%d'),
|
35 |
+
'temp': day['temp']['day'],
|
36 |
+
'precipitation': day.get('rain', 0) + day.get('snow', 0),
|
37 |
+
'wind_speed': day['wind_speed'],
|
38 |
+
'weather': day['weather'][0]['main']
|
39 |
+
})
|
40 |
+
|
41 |
+
return {'current': current, 'forecast': forecast}
|
42 |
+
except Exception as e:
|
43 |
+
print(f"Error fetching weather data: {e}")
|
44 |
+
return None
|
45 |
|
46 |
# Initialize Dash app
|
47 |
app = dash.Dash(__name__)
|
48 |
app.title = "Weather Forecast Dashboard"
|
49 |
|
50 |
+
# Use environment variable for API key
|
51 |
+
API_KEY = os.getenv("OPENWEATHERMAP_API_KEY", "53d50455f91b6bc3c920959e2954576d")
|
52 |
+
|
53 |
+
# Fixed locations
|
54 |
+
LOCATIONS = ["Hyderabad", "Jogulamba Gadwal", "Bangalore"]
|
55 |
+
DAYS = 5 # Fixed 5-day forecast
|
56 |
|
57 |
# Layout
|
58 |
app.layout = html.Div([
|
59 |
html.Div([
|
60 |
html.H1("Weather Forecast Dashboard", style={'textAlign': 'center', 'color': '#FFFFFF'}),
|
61 |
+
html.P("Current weather for Hyderabad, Gadwal, and Karnataka (Bangalore).",
|
62 |
style={'textAlign': 'center', 'color': '#D3D9D4'})
|
63 |
], style={'backgroundColor': '#212A31', 'padding': '20px'}),
|
64 |
|
65 |
html.Div([
|
66 |
+
html.H2("Weather Overview", style={'color': '#FFFFFF', 'padding': '10px'}),
|
67 |
+
html.Div(id='weather-display', style={'display': 'flex', 'flexWrap': 'wrap', 'justifyContent': 'space-around'})
|
|
|
|
|
|
|
|
|
|
|
|
|
68 |
], style={'backgroundColor': '#2E3944', 'padding': '20px', 'borderRadius': '10px'}),
|
|
|
|
|
|
|
|
|
|
|
69 |
], style={'backgroundColor': '#1A2329', 'fontFamily': 'Arial', 'minHeight': '100vh'})
|
70 |
|
71 |
# Callback to update dashboard
|
72 |
@app.callback(
|
73 |
+
Output('weather-display', 'children'),
|
74 |
+
Input('weather-display', 'id') # Dummy input to trigger on load
|
|
|
|
|
|
|
|
|
|
|
75 |
)
|
76 |
+
def update_dashboard(_):
|
77 |
+
weather_components = []
|
78 |
+
|
79 |
+
for city in LOCATIONS:
|
80 |
+
weather_data = get_weather_data(city, DAYS, API_KEY)
|
81 |
+
if not weather_data:
|
82 |
+
weather_components.append(
|
83 |
+
html.Div(f"Error: Could not fetch data for {city}", style={'color': '#FF5555', 'padding': '10px'})
|
84 |
+
)
|
85 |
+
continue
|
86 |
+
|
87 |
+
current = weather_data['current']
|
88 |
+
forecast = weather_data['forecast']
|
89 |
+
|
90 |
+
# Text Summary
|
91 |
+
summary = html.Div([
|
92 |
+
html.H3(f"{city}", style={'color': '#FFFFFF'}),
|
93 |
+
html.P(f"Current Temp: {current['temp']}°C", style={'color': '#D3D9D4'}),
|
94 |
+
html.P(f"Condition: {current['weather']}", style={'color': '#D3D9D4'}),
|
95 |
+
html.P(f"Humidity: {current['humidity']}%", style={'color': '#D3D9D4'}),
|
96 |
+
html.P(f"Wind Speed: {current['wind_speed']} km/h", style={'color': '#D3D9D4'}),
|
97 |
+
], style={'width': '30%', 'padding': '10px'})
|
98 |
+
|
99 |
+
# Donut Charts
|
100 |
+
temp_values = [f['temp'] for f in forecast]
|
101 |
+
temp_fig = go.Figure(data=[go.Pie(labels=[f['date'] for f in forecast], values=temp_values, hole=.3)])
|
102 |
+
temp_fig.update_layout(title=f"{city} - Temperature (°C)", template='plotly_dark')
|
103 |
+
|
104 |
+
precip_values = [f['precipitation'] for f in forecast]
|
105 |
+
precip_fig = go.Figure(data=[go.Pie(labels=[f['date'] for f in forecast], values=precip_values, hole=.3)])
|
106 |
+
precip_fig.update_layout(title=f"{city} - Precipitation (mm)", template='plotly_dark')
|
107 |
+
|
108 |
+
wind_values = [f['wind_speed'] for f in forecast]
|
109 |
+
wind_fig = go.Figure(data=[go.Pie(labels=[f['date'] for f in forecast], values=wind_values, hole=.3)])
|
110 |
+
wind_fig.update_layout(title=f"{city} - Wind Speed (km/h)", template='plotly_dark')
|
111 |
+
|
112 |
+
# Combine into a city section
|
113 |
+
city_section = html.Div([
|
114 |
+
summary,
|
115 |
+
dcc.Graph(figure=temp_fig, style={'width': '20%', 'display': 'inline-block'}),
|
116 |
+
dcc.Graph(figure=precip_fig, style={'width': '20%', 'display': 'inline-block'}),
|
117 |
+
dcc.Graph(figure=wind_fig, style={'width': '20%', 'display': 'inline-block'}),
|
118 |
+
], style={'margin': '20px', 'backgroundColor': '#2E3944', 'borderRadius': '10px', 'padding': '10px'})
|
119 |
+
|
120 |
+
weather_components.append(city_section)
|
121 |
+
|
122 |
+
return weather_components
|
123 |
|
124 |
# Run server on port 7860 for Hugging Face Spaces
|
125 |
if __name__ == '__main__':
|