nakas commited on
Commit
be24fe4
·
verified ·
1 Parent(s): 1552018

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +66 -103
app.py CHANGED
@@ -9,7 +9,6 @@ import subprocess
9
  import sys
10
  import matplotlib.pyplot as plt
11
  from matplotlib.gridspec import GridSpec
12
- import matplotlib.dates as mdates
13
  from windrose import WindroseAxes
14
  from datetime import datetime
15
 
@@ -37,13 +36,18 @@ def scrape_weather_data(site_id, hours=720):
37
 
38
  try:
39
  with sync_playwright() as p:
 
40
  browser = p.chromium.launch(
41
  headless=True,
42
  args=['--no-sandbox', '--disable-dev-shm-usage']
43
  )
 
 
44
  context = browser.new_context(
45
- user_agent='Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36'
46
  )
 
 
47
  page = context.new_page()
48
  response = page.goto(url)
49
  print(f"Response status: {response.status}")
@@ -55,21 +59,28 @@ def scrape_weather_data(site_id, hours=720):
55
  # Get all text content
56
  print("Extracting data...")
57
  content = page.evaluate('''() => {
58
- const rows = [];
59
- const tables = document.getElementsByTagName('table');
60
- for (const table of tables) {
61
- if (table.textContent.includes('Date/Time')) {
62
- const headerRow = Array.from(table.querySelectorAll('th'))
63
- .map(th => th.textContent.trim());
64
- const dataRows = Array.from(table.querySelectorAll('tbody tr'))
65
- .map(row => Array.from(row.querySelectorAll('td'))
66
- .map(td => td.textContent.trim()));
67
- return {headers: headerRow, rows: dataRows};
 
 
 
 
68
  }
69
- }
70
- return null;
 
 
71
  }''')
72
 
 
73
  browser.close()
74
  return content
75
 
@@ -77,6 +88,16 @@ def scrape_weather_data(site_id, hours=720):
77
  print(f"Error scraping data: {str(e)}")
78
  raise e
79
 
 
 
 
 
 
 
 
 
 
 
80
  def parse_weather_data(data):
81
  """Parse the weather data into a pandas DataFrame"""
82
  if not data or 'rows' not in data:
@@ -123,137 +144,80 @@ def parse_weather_data(data):
123
  df['wind_dir_deg'] = df['wind_dir'].apply(parse_direction)
124
 
125
  # Convert datetime
126
- current_year = datetime.now().year
127
- df['datetime'] = pd.to_datetime(df['datetime'].apply(lambda x: f"{x}, {current_year}"),
128
- format="%b %d, %I:%M %p, %Y")
129
  df['date'] = df['datetime'].dt.date
130
 
131
  return df
132
 
133
- def create_wind_rose(df, ax=None):
134
  """Create a wind rose plot"""
135
- if ax is None:
136
- fig = plt.figure(figsize=(8, 8))
137
- ax = WindroseAxes.from_ax(fig=fig)
138
-
139
- ws = df['wind_speed'].dropna().values
140
- wd = df['wind_dir_deg'].dropna().values
141
-
142
- if len(ws) > 0 and len(wd) > 0:
143
- # Define wind speed bins
144
- bins = np.array([0, 5, 10, 15, 20, 25, 30, 35])
145
- # Create color map
146
- colors = plt.cm.viridis(np.linspace(0, 1, len(bins)-1))
147
-
148
- # Create the wind rose
149
- ax.bar(wd, ws, nsector=36, normed=True, opening=0.8,
150
- bins=bins, colors=colors)
151
- ax.set_legend(title='Wind Speed (mph)', bbox_to_anchor=(1.2, 0.5))
152
- return True
153
- return False
154
-
155
- def create_daily_wind_roses(df):
156
- """Create wind roses for each day"""
157
- daily_groups = df.groupby(df['datetime'].dt.date)
158
- roses = []
159
-
160
- for date, group in daily_groups:
161
- if len(group) > 0: # Only create rose if we have data
162
- fig = plt.figure(figsize=(8, 8))
163
- ax = WindroseAxes.from_ax(fig=fig)
164
- if create_wind_rose(group, ax):
165
- ax.set_title(f'Wind Rose - {date.strftime("%Y-%m-%d")}')
166
- plt.tight_layout()
167
- roses.append(fig)
168
- plt.close(fig)
169
-
170
- return roses
171
 
172
  def create_plots(df):
173
  """Create all weather plots"""
174
- # Create main figure with subplots
175
- fig = plt.figure(figsize=(20, 25))
176
- gs = GridSpec(4, 2, figure=fig, height_ratios=[1, 1, 1, 1])
177
 
178
  # Temperature plot
179
  ax1 = fig.add_subplot(gs[0, :])
180
  ax1.plot(df['datetime'], df['temp'], label='Temperature', color='red')
181
  ax1.plot(df['datetime'], df['wind_chill'], label='Wind Chill', color='blue')
182
- ax1.set_title('Temperature and Wind Chill Over Time', pad=20)
183
  ax1.set_xlabel('Date')
184
  ax1.set_ylabel('Temperature (°F)')
185
  ax1.legend()
186
  ax1.grid(True)
187
- ax1.xaxis.set_major_formatter(mdates.DateFormatter('%Y-%m-%d %H:%M'))
188
- plt.setp(ax1.xaxis.get_majorticklabels(), rotation=45, ha='right')
189
 
190
  # Wind speed plot
191
  ax2 = fig.add_subplot(gs[1, :])
192
  ax2.plot(df['datetime'], df['wind_speed'], label='Wind Speed', color='blue')
193
  ax2.plot(df['datetime'], df['wind_gust'], label='Wind Gust', color='orange')
194
- ax2.set_title('Wind Speed and Gusts Over Time', pad=20)
195
  ax2.set_xlabel('Date')
196
  ax2.set_ylabel('Wind Speed (mph)')
197
  ax2.legend()
198
  ax2.grid(True)
199
- ax2.xaxis.set_major_formatter(mdates.DateFormatter('%Y-%m-%d %H:%M'))
200
- plt.setp(ax2.xaxis.get_majorticklabels(), rotation=45, ha='right')
201
 
202
  # Snow depth plot
203
  ax3 = fig.add_subplot(gs[2, 0])
204
  ax3.plot(df['datetime'], df['snow_depth'], color='blue', label='Snow Depth')
205
- ax3.set_title('Snow Depth Over Time', pad=20)
206
  ax3.set_xlabel('Date')
207
  ax3.set_ylabel('Snow Depth (inches)')
208
  ax3.grid(True)
209
- ax3.xaxis.set_major_formatter(mdates.DateFormatter('%Y-%m-%d %H:%M'))
210
- plt.setp(ax3.xaxis.get_majorticklabels(), rotation=45, ha='right')
211
 
212
  # Daily new snow bar plot
213
  ax4 = fig.add_subplot(gs[2, 1])
214
  daily_snow = df.groupby('date')['snowfall_24hr'].max()
215
- bars = ax4.bar(range(len(daily_snow)), daily_snow.values, color='blue', width=0.8)
216
-
217
- # Customize x-axis
218
- ax4.set_xticks(range(len(daily_snow)))
219
- ax4.set_xticklabels([d.strftime('%Y-%m-%d') for d in daily_snow.index],
220
- rotation=45, ha='right')
221
-
222
- # Add value labels on top of bars
223
- for bar in bars:
224
- height = bar.get_height()
225
- if height > 0: # Only label bars with snow
226
- ax4.text(bar.get_x() + bar.get_width()/2., height,
227
- f'{height:.1f}"',
228
- ha='center', va='bottom')
229
-
230
- ax4.set_title('Daily New Snow', pad=20)
231
  ax4.set_xlabel('Date')
232
  ax4.set_ylabel('New Snow (inches)')
233
- ax4.grid(True, axis='y')
234
-
235
- # Wind roses
236
- ax5 = fig.add_subplot(gs[3, 0], projection='windrose')
237
- create_wind_rose(df, ax5)
238
- ax5.set_title('Overall Wind Rose')
239
-
240
- # Latest day's wind rose
241
- latest_date = df['date'].iloc[0]
242
- latest_data = df[df['date'] == latest_date]
243
- ax6 = fig.add_subplot(gs[3, 1], projection='windrose')
244
- create_wind_rose(latest_data, ax6)
245
- ax6.set_title(f'Latest Day Wind Rose\n{latest_date}')
246
 
247
  plt.tight_layout()
248
 
249
- # Create individual daily wind roses
250
- daily_roses = create_daily_wind_roses(df)
 
 
 
251
 
252
- return fig, daily_roses
253
 
254
  def analyze_weather_data(site_id, hours):
255
  """Analyze weather data and create visualizations"""
256
  try:
 
257
  print(f"Scraping data for {site_id}...")
258
  raw_data = scrape_weather_data(site_id, hours)
259
  if not raw_data:
@@ -284,9 +248,9 @@ def analyze_weather_data(site_id, hours):
284
 
285
  # Create plots
286
  print("Creating plots...")
287
- main_plots, daily_roses = create_plots(df)
288
 
289
- return html_output, main_plots, daily_roses
290
 
291
  except Exception as e:
292
  print(f"Error in analysis: {str(e)}")
@@ -323,13 +287,12 @@ with gr.Blocks(title="Weather Station Data Analyzer") as demo:
323
 
324
  with gr.Row():
325
  weather_plots = gr.Plot(label="Weather Plots")
326
-
327
- gallery = gr.Gallery(label="Daily Wind Roses", columns=3, rows=2, height=400)
328
 
329
  analyze_btn.click(
330
  fn=analyze_weather_data,
331
  inputs=[site_id, hours],
332
- outputs=[stats_output, weather_plots, gallery]
333
  )
334
 
335
  if __name__ == "__main__":
 
9
  import sys
10
  import matplotlib.pyplot as plt
11
  from matplotlib.gridspec import GridSpec
 
12
  from windrose import WindroseAxes
13
  from datetime import datetime
14
 
 
36
 
37
  try:
38
  with sync_playwright() as p:
39
+ # Launch browser with minimal settings
40
  browser = p.chromium.launch(
41
  headless=True,
42
  args=['--no-sandbox', '--disable-dev-shm-usage']
43
  )
44
+
45
+ # Create context with desktop user agent
46
  context = browser.new_context(
47
+ user_agent='Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36'
48
  )
49
+
50
+ # Create new page and navigate
51
  page = context.new_page()
52
  response = page.goto(url)
53
  print(f"Response status: {response.status}")
 
59
  # Get all text content
60
  print("Extracting data...")
61
  content = page.evaluate('''() => {
62
+ const getTextContent = () => {
63
+ const rows = [];
64
+ const tables = document.getElementsByTagName('table');
65
+ for (const table of tables) {
66
+ if (table.textContent.includes('Date/Time')) {
67
+ const headerRow = Array.from(table.querySelectorAll('th'))
68
+ .map(th => th.textContent.trim());
69
+
70
+ const dataRows = Array.from(table.querySelectorAll('tbody tr'))
71
+ .map(row => Array.from(row.querySelectorAll('td'))
72
+ .map(td => td.textContent.trim()));
73
+
74
+ return {headers: headerRow, rows: dataRows};
75
+ }
76
  }
77
+ return null;
78
+ };
79
+
80
+ return getTextContent();
81
  }''')
82
 
83
+ print(f"Found {len(content['rows'] if content else [])} rows of data")
84
  browser.close()
85
  return content
86
 
 
88
  print(f"Error scraping data: {str(e)}")
89
  raise e
90
 
91
+ def parse_date(date_str):
92
+ """Parse date string to datetime"""
93
+ try:
94
+ # Handle format like "Feb 10, 10:00 am"
95
+ # Add current year to the date string
96
+ current_year = datetime.now().year
97
+ return pd.to_datetime(f"{date_str}, {current_year}", format="%b %d, %I:%M %p, %Y")
98
+ except:
99
+ return pd.NaT
100
+
101
  def parse_weather_data(data):
102
  """Parse the weather data into a pandas DataFrame"""
103
  if not data or 'rows' not in data:
 
144
  df['wind_dir_deg'] = df['wind_dir'].apply(parse_direction)
145
 
146
  # Convert datetime
147
+ df['datetime'] = df['datetime'].apply(parse_date)
 
 
148
  df['date'] = df['datetime'].dt.date
149
 
150
  return df
151
 
152
+ def create_wind_rose(df, ax):
153
  """Create a wind rose plot"""
154
+ if not isinstance(ax, WindroseAxes):
155
+ ax = WindroseAxes.from_ax(ax=ax)
156
+ ax.bar(df['wind_dir_deg'].dropna(), df['wind_speed'].dropna(),
157
+ bins=np.arange(0, 40, 5), normed=True, opening=0.8, edgecolor='white')
158
+ ax.set_legend(title='Wind Speed (mph)')
159
+ ax.set_title('Wind Rose')
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
160
 
161
  def create_plots(df):
162
  """Create all weather plots"""
163
+ # Create figure with subplots
164
+ fig = plt.figure(figsize=(20, 15))
165
+ gs = GridSpec(3, 2, figure=fig)
166
 
167
  # Temperature plot
168
  ax1 = fig.add_subplot(gs[0, :])
169
  ax1.plot(df['datetime'], df['temp'], label='Temperature', color='red')
170
  ax1.plot(df['datetime'], df['wind_chill'], label='Wind Chill', color='blue')
171
+ ax1.set_title('Temperature and Wind Chill Over Time')
172
  ax1.set_xlabel('Date')
173
  ax1.set_ylabel('Temperature (°F)')
174
  ax1.legend()
175
  ax1.grid(True)
176
+ plt.setp(ax1.xaxis.get_majorticklabels(), rotation=45)
 
177
 
178
  # Wind speed plot
179
  ax2 = fig.add_subplot(gs[1, :])
180
  ax2.plot(df['datetime'], df['wind_speed'], label='Wind Speed', color='blue')
181
  ax2.plot(df['datetime'], df['wind_gust'], label='Wind Gust', color='orange')
182
+ ax2.set_title('Wind Speed and Gusts Over Time')
183
  ax2.set_xlabel('Date')
184
  ax2.set_ylabel('Wind Speed (mph)')
185
  ax2.legend()
186
  ax2.grid(True)
187
+ plt.setp(ax2.xaxis.get_majorticklabels(), rotation=45)
 
188
 
189
  # Snow depth plot
190
  ax3 = fig.add_subplot(gs[2, 0])
191
  ax3.plot(df['datetime'], df['snow_depth'], color='blue', label='Snow Depth')
192
+ ax3.set_title('Snow Depth Over Time')
193
  ax3.set_xlabel('Date')
194
  ax3.set_ylabel('Snow Depth (inches)')
195
  ax3.grid(True)
196
+ plt.setp(ax3.xaxis.get_majorticklabels(), rotation=45)
 
197
 
198
  # Daily new snow bar plot
199
  ax4 = fig.add_subplot(gs[2, 1])
200
  daily_snow = df.groupby('date')['snowfall_24hr'].max()
201
+ ax4.bar(daily_snow.index, daily_snow.values, color='blue')
202
+ ax4.set_title('Daily New Snow')
 
 
 
 
 
 
 
 
 
 
 
 
 
 
203
  ax4.set_xlabel('Date')
204
  ax4.set_ylabel('New Snow (inches)')
205
+ plt.setp(ax4.xaxis.get_majorticklabels(), rotation=45)
 
 
 
 
 
 
 
 
 
 
 
 
206
 
207
  plt.tight_layout()
208
 
209
+ # Create separate wind rose figure
210
+ fig_rose = plt.figure(figsize=(10, 10))
211
+ ax_rose = WindroseAxes.from_ax(fig=fig_rose)
212
+ create_wind_rose(df, ax_rose)
213
+ plt.tight_layout()
214
 
215
+ return fig, fig_rose
216
 
217
  def analyze_weather_data(site_id, hours):
218
  """Analyze weather data and create visualizations"""
219
  try:
220
+ # Scrape and parse data
221
  print(f"Scraping data for {site_id}...")
222
  raw_data = scrape_weather_data(site_id, hours)
223
  if not raw_data:
 
248
 
249
  # Create plots
250
  print("Creating plots...")
251
+ main_plots, wind_rose = create_plots(df)
252
 
253
+ return html_output, main_plots, wind_rose
254
 
255
  except Exception as e:
256
  print(f"Error in analysis: {str(e)}")
 
287
 
288
  with gr.Row():
289
  weather_plots = gr.Plot(label="Weather Plots")
290
+ wind_rose = gr.Plot(label="Wind Rose")
 
291
 
292
  analyze_btn.click(
293
  fn=analyze_weather_data,
294
  inputs=[site_id, hours],
295
+ outputs=[stats_output, weather_plots, wind_rose]
296
  )
297
 
298
  if __name__ == "__main__":