prudhviLatha commited on
Commit
4439e5e
·
verified ·
1 Parent(s): a52d2e0

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +56 -60
app.py CHANGED
@@ -81,6 +81,7 @@ def process_data(file, trade_selection, filter_date):
81
  return "Error: Attendance column contains non-numeric values", None, None, None, None, None
82
 
83
  # Ensure Attendance is positive
 
84
  if (df['Attendance'] <= 0).any():
85
  logger.error("Attendance contains non-positive values")
86
  return "Error: Attendance must contain only positive values", None, None, None, None, None
@@ -133,16 +134,17 @@ def process_data(file, trade_selection, filter_date):
133
  # Parse forecast dates
134
  forecast_df['Date'] = pd.to_datetime(forecast_df['Date'])
135
 
136
- # Apply date filtering based on manual filter_date
137
- date_range_str = "All dates"
138
- if filter_date and filter_date.strip():
139
- try:
140
- selected_date = pd.to_datetime(filter_date.strip(), format='%d-%m-%Y')
141
- forecast_df = forecast_df[forecast_df['Date'].dt.date == selected_date.date()]
142
- date_range_str = selected_date.strftime('%Y-%m-%d')
143
- except ValueError:
144
- logger.error(f"Invalid date format: {filter_date}. Use DD-MM-YYYY (e.g., 01-04-2025)")
145
- return "Error: Invalid date format. Use DD-MM-YYYY (e.g., 01-04-2025)", None, None, None, None, None
 
146
 
147
  if forecast_df.empty:
148
  logger.warning("No data available for the selected date")
@@ -151,20 +153,18 @@ def process_data(file, trade_selection, filter_date):
151
  # Store the forecast data globally
152
  latest_forecast_df = forecast_df
153
 
154
- # Create bar graph for risk with enhanced layout and fixed overlapping labels
155
- logger.debug("Generating enhanced risk bar graph with fixed overlapping labels")
156
  risk_bar_fig = go.Figure()
157
- colors = px.colors.qualitative.Plotly
158
 
159
  for idx, trade in enumerate(trades_to_process):
160
  trade_forecast = forecast_df[forecast_df['Trade'] == trade]
161
  risk_bar_fig.add_trace(go.Bar(
162
  x=trade_forecast['Date'],
163
- y=trade_forecast['Risk'].clip(1, 100), # Minimum risk set to 1% instead of 0%
164
  name=trade,
165
  marker_color=colors[idx % len(colors)],
166
- width=0.3,
167
- offset=(idx - (len(trades_to_process) - 1) / 2) * 0.2,
168
  hovertemplate=(
169
  f"Trade: {trade}<br>" +
170
  "Date: %{x}<br>" +
@@ -176,7 +176,7 @@ def process_data(file, trade_selection, filter_date):
176
  title=f"Shortage Risk Bar Graph ({date_range_str}, Risk in %, {len(trades_to_process)} Trade{'s' if len(trades_to_process) != 1 else ''})",
177
  xaxis_title="Date",
178
  yaxis_title="Risk (%)",
179
- barmode='group',
180
  showlegend=True,
181
  hovermode="closest",
182
  legend=dict(
@@ -190,37 +190,26 @@ def process_data(file, trade_selection, filter_date):
190
  x=1
191
  ),
192
  xaxis=dict(
193
- tickangle=60,
194
  tickformat="%Y-%m-%d",
195
- tickmode="auto",
196
- nticks=5,
197
  gridcolor='lightgrey'
198
  ),
199
  yaxis=dict(
200
- range=[1, 100], # Adjusted to start from 1 for strictly positive risks
201
  gridcolor='lightgrey',
202
- zeroline=False, # Remove zeroline since risks start at 1
203
- gridwidth=1
 
204
  ),
205
  plot_bgcolor='white',
206
  margin=dict(t=80, b=80, l=60, r=60)
207
  )
208
 
209
- for idx, trade in enumerate(trades_to_process):
210
- trade_forecast = forecast_df[forecast_df['Trade'] == trade]
211
- for i, (date, risk) in enumerate(zip(trade_forecast['Date'], trade_forecast['Risk'].clip(1, 100))):
212
- y_offset = 5
213
- risk_bar_fig.add_annotation(
214
- x=date,
215
- y=risk,
216
- text=f"{risk}%",
217
- showarrow=False,
218
- yshift=y_offset,
219
- font=dict(size=10),
220
- align="center"
221
- )
222
-
223
  max_risk = forecast_df['Risk'].max()
 
224
  if max_risk > 100:
225
  risk_bar_fig.add_annotation(
226
  text=f"Note: Risk values capped at 100% (Max: {max_risk:.1f}%)",
@@ -230,7 +219,7 @@ def process_data(file, trade_selection, filter_date):
230
  font=dict(size=10)
231
  )
232
 
233
- logger.info("Enhanced risk bar graph with fixed overlapping labels generated")
234
 
235
  # Create line graph for attendance
236
  logger.debug("Generating attendance line graph")
@@ -239,9 +228,10 @@ def process_data(file, trade_selection, filter_date):
239
 
240
  for idx, trade in enumerate(trades_to_process):
241
  trade_forecast = forecast_df[forecast_df['Trade'] == trade]
 
242
  line_fig.add_trace(go.Scatter(
243
  x=trade_forecast['Date'],
244
- y=trade_forecast['Attendance'],
245
  mode='lines+markers',
246
  name=trade,
247
  line=dict(color=colors[idx % len(colors)]),
@@ -256,12 +246,13 @@ def process_data(file, trade_selection, filter_date):
256
  customdata=trade_forecast[['Risk', 'Alert']].values
257
  ))
258
 
 
259
  for alert_type in ['Warning', 'Critical']:
260
  alert_data = trade_forecast[trade_forecast['Alert'] == alert_type]
261
  if not alert_data.empty:
262
  line_fig.add_trace(go.Scatter(
263
  x=alert_data['Date'],
264
- y=alert_data['Attendance'],
265
  mode='markers',
266
  name=f"{trade} {alert_type}",
267
  marker=dict(
@@ -296,6 +287,7 @@ def process_data(file, trade_selection, filter_date):
296
  gridcolor='lightgrey'
297
  ),
298
  yaxis=dict(
 
299
  gridcolor='lightgrey'
300
  ),
301
  plot_bgcolor='white',
@@ -310,7 +302,7 @@ def process_data(file, trade_selection, filter_date):
310
  'Risk': 'mean'
311
  }).reset_index()
312
  summary_df['Attendance'] = summary_df['Attendance'].round(1)
313
- summary_df['Risk'] = summary_df['Risk'].round(1).clip(1) # Ensure minimum risk of 1%
314
  summary_df = summary_df.rename(columns={'Attendance': 'Average Attendance', 'Risk': 'Average Risk (%)'})
315
  logger.info(f"Summary statistics generated: {summary_df.to_dict()}")
316
 
@@ -319,9 +311,9 @@ def process_data(file, trade_selection, filter_date):
319
  fig = plt.figure(figsize=(12, 20))
320
  gs = fig.add_gridspec(5, 1, height_ratios=[3, 3, 0.2, 1.5, 0.5], hspace=0.8)
321
 
322
- # Plot bar graph for risk
323
  ax1 = fig.add_subplot(gs[0])
324
- width = 0.2
325
  unique_dates = forecast_df['Date'].unique()
326
  x_positions = range(len(unique_dates))
327
 
@@ -330,20 +322,20 @@ def process_data(file, trade_selection, filter_date):
330
  trade_x = [pos + idx * width for pos in x_positions]
331
  bars = ax1.bar(
332
  trade_x,
333
- trade_forecast['Risk'].clip(1, 100),
334
  width,
335
  label=trade,
336
  color=colors[idx % len(colors)]
337
  )
338
- for bar in bars:
339
- height = bar.get_height()
340
- y_offset = 5
341
  ax1.text(
342
- bar.get_x() + bar.get_width() / 2,
343
- height + y_offset,
344
  f'{height:.1f}%',
345
  ha='center',
346
- va='bottom',
347
  fontsize=8
348
  )
349
 
@@ -351,10 +343,12 @@ def process_data(file, trade_selection, filter_date):
351
  ax1.set_xlabel('Date', fontsize=12)
352
  ax1.set_ylabel('Risk (%)', fontsize=12)
353
  ax1.set_xticks([pos + (len(trades_to_process) - 1) * width / 2 for pos in x_positions])
354
- ax1.set_xticklabels([date.strftime('%Y-%m-%d') for date in unique_dates], rotation=45, fontsize=8)
 
355
  ax1.grid(True, axis='y', linestyle='--', alpha=0.7)
356
- ax1.legend(title="Trades", fontsize=10, loc='upper left')
357
- ax1.set_ylim(1, 100) # Adjusted to start from 1 for strictly positive risks
 
358
 
359
  # Plot line graph for attendance
360
  ax2 = fig.add_subplot(gs[1])
@@ -362,7 +356,7 @@ def process_data(file, trade_selection, filter_date):
362
  trade_forecast = forecast_df[forecast_df['Trade'] == trade]
363
  ax2.plot(
364
  trade_forecast['Date'],
365
- trade_forecast['Attendance'],
366
  label=trade,
367
  color=colors[idx % len(colors)],
368
  marker='o',
@@ -374,7 +368,7 @@ def process_data(file, trade_selection, filter_date):
374
  if not alert_data.empty:
375
  ax2.scatter(
376
  alert_data['Date'],
377
- alert_data['Attendance'],
378
  label=f"{trade} {alert_type}",
379
  color=alert_colors[alert_type],
380
  marker='D',
@@ -389,6 +383,7 @@ def process_data(file, trade_selection, filter_date):
389
  ax2.grid(True, linestyle='--', alpha=0.7)
390
  ax2.legend(title="Trades & Alerts", fontsize=10, loc='upper left', bbox_to_anchor=(0, -0.1), ncol=2)
391
  ax2.tick_params(axis='x', rotation=45, labelsize=8)
 
392
 
393
  # Empty spacer subplot for small gap
394
  ax_spacer = fig.add_subplot(gs[2])
@@ -399,9 +394,8 @@ def process_data(file, trade_selection, filter_date):
399
  ax3.axis('off')
400
  table_data = [summary_df.columns.tolist()] + summary_df.values.tolist()
401
  table = ax3.table(cellText=table_data, cellLoc='center', loc='center', colWidths=[0.3, 0.35, 0.35])
402
- table.auto_set_font_size(False)
403
- table.set_fontsize(12)
404
- table.scale(1.2, 1.2)
405
  ax3.set_title('Summary Statistics (Risk in %)', fontsize=14, pad=10)
406
 
407
  # Add legend for alerts
@@ -414,7 +408,7 @@ def process_data(file, trade_selection, filter_date):
414
  ]
415
  ax4.legend(handles=legend_elements, loc='center', ncol=3, title='Alert Types', fontsize=12, title_fontsize=12)
416
 
417
- plt.subplots_adjust(top=0.9, bottom=0.1, left=0.1, right=0.9, hspace=0.6)
418
  buf = io.BytesIO()
419
  plt.savefig(buf, format='pdf', dpi=300, bbox_inches='tight')
420
  plt.close()
@@ -460,8 +454,10 @@ def notify_contractor():
460
  logger.warning("No forecast data for notifications")
461
  return message, history_display
462
 
 
463
  risk_summary = latest_forecast_df.groupby('Trade')['Risk'].mean().round(1).to_dict()
464
 
 
465
  timestamp = datetime.now().strftime("%Y-%m-%d %I:%M:%S %p")
466
  notifications = []
467
  for trade, avg_risk in risk_summary.items():
@@ -513,7 +509,7 @@ with gr.Blocks() as demo:
513
  trade_dropdown = gr.Dropdown(label="Select Trade", choices=["All"])
514
 
515
  with gr.Row():
516
- filter_date = gr.Textbox(label="Enter Date (DD-MM-YYYY)", placeholder="e.g., 01-04-2025", value="")
517
 
518
  with gr.Row():
519
  submit_button = gr.Button("Generate Forecast")
 
81
  return "Error: Attendance column contains non-numeric values", None, None, None, None, None
82
 
83
  # Ensure Attendance is positive
84
+ df['Attendance'] = df['Attendance'].clip(lower=0)
85
  if (df['Attendance'] <= 0).any():
86
  logger.error("Attendance contains non-positive values")
87
  return "Error: Attendance must contain only positive values", None, None, None, None, None
 
134
  # Parse forecast dates
135
  forecast_df['Date'] = pd.to_datetime(forecast_df['Date'])
136
 
137
+ # Ensure only positive values for Risk and Attendance in forecast data
138
+ forecast_df['Risk'] = forecast_df['Risk'].clip(lower=0)
139
+ forecast_df['Attendance'] = forecast_df['Attendance'].clip(lower=0)
140
+
141
+ # Apply date filtering based on filter_date (daily view only)
142
+ if filter_date:
143
+ selected_date = pd.to_datetime(filter_date)
144
+ forecast_df = forecast_df[forecast_df['Date'].dt.date == selected_date.date()]
145
+ date_range_str = selected_date.strftime('%Y-%m-%d')
146
+ else:
147
+ date_range_str = "All dates"
148
 
149
  if forecast_df.empty:
150
  logger.warning("No data available for the selected date")
 
153
  # Store the forecast data globally
154
  latest_forecast_df = forecast_df
155
 
156
+ # Create bar graph for risk with enhanced layout
157
+ logger.debug("Generating enhanced risk bar graph")
158
  risk_bar_fig = go.Figure()
159
+ colors = px.colors.qualitative.Plotly # Consistent color palette for trades
160
 
161
  for idx, trade in enumerate(trades_to_process):
162
  trade_forecast = forecast_df[forecast_df['Trade'] == trade]
163
  risk_bar_fig.add_trace(go.Bar(
164
  x=trade_forecast['Date'],
165
+ y=trade_forecast['Risk'].clip(lower=0), # Cap risk values to zero or above
166
  name=trade,
167
  marker_color=colors[idx % len(colors)],
 
 
168
  hovertemplate=(
169
  f"Trade: {trade}<br>" +
170
  "Date: %{x}<br>" +
 
176
  title=f"Shortage Risk Bar Graph ({date_range_str}, Risk in %, {len(trades_to_process)} Trade{'s' if len(trades_to_process) != 1 else ''})",
177
  xaxis_title="Date",
178
  yaxis_title="Risk (%)",
179
+ barmode='group', # Group bars to avoid overlap
180
  showlegend=True,
181
  hovermode="closest",
182
  legend=dict(
 
190
  x=1
191
  ),
192
  xaxis=dict(
193
+ tickangle=45, # Rotate for better readability
194
  tickformat="%Y-%m-%d",
195
+ tickmode="linear", # Ensure even spacing
196
+ nticks=10, # Adjust number of ticks for clarity
197
  gridcolor='lightgrey'
198
  ),
199
  yaxis=dict(
200
+ range=[0, 100], # Set a reasonable range starting from 0
201
  gridcolor='lightgrey',
202
+ zeroline=True,
203
+ zerolinewidth=2,
204
+ zerolinecolor='black'
205
  ),
206
  plot_bgcolor='white',
207
  margin=dict(t=80, b=80, l=60, r=60)
208
  )
209
 
210
+ # Add annotation for capped values
 
 
 
 
 
 
 
 
 
 
 
 
 
211
  max_risk = forecast_df['Risk'].max()
212
+ min_risk = forecast_df['Risk'].min()
213
  if max_risk > 100:
214
  risk_bar_fig.add_annotation(
215
  text=f"Note: Risk values capped at 100% (Max: {max_risk:.1f}%)",
 
219
  font=dict(size=10)
220
  )
221
 
222
+ logger.info("Enhanced risk bar graph generated")
223
 
224
  # Create line graph for attendance
225
  logger.debug("Generating attendance line graph")
 
228
 
229
  for idx, trade in enumerate(trades_to_process):
230
  trade_forecast = forecast_df[forecast_df['Trade'] == trade]
231
+ # Add line for attendance
232
  line_fig.add_trace(go.Scatter(
233
  x=trade_forecast['Date'],
234
+ y=trade_forecast['Attendance'].clip(lower=0),
235
  mode='lines+markers',
236
  name=trade,
237
  line=dict(color=colors[idx % len(colors)]),
 
246
  customdata=trade_forecast[['Risk', 'Alert']].values
247
  ))
248
 
249
+ # Add markers for Warning and Critical alerts
250
  for alert_type in ['Warning', 'Critical']:
251
  alert_data = trade_forecast[trade_forecast['Alert'] == alert_type]
252
  if not alert_data.empty:
253
  line_fig.add_trace(go.Scatter(
254
  x=alert_data['Date'],
255
+ y=alert_data['Attendance'].clip(lower=0),
256
  mode='markers',
257
  name=f"{trade} {alert_type}",
258
  marker=dict(
 
287
  gridcolor='lightgrey'
288
  ),
289
  yaxis=dict(
290
+ range=[0, forecast_df['Attendance'].max() * 1.2], # Dynamic range starting from 0
291
  gridcolor='lightgrey'
292
  ),
293
  plot_bgcolor='white',
 
302
  'Risk': 'mean'
303
  }).reset_index()
304
  summary_df['Attendance'] = summary_df['Attendance'].round(1)
305
+ summary_df['Risk'] = summary_df['Risk'].round(1)
306
  summary_df = summary_df.rename(columns={'Attendance': 'Average Attendance', 'Risk': 'Average Risk (%)'})
307
  logger.info(f"Summary statistics generated: {summary_df.to_dict()}")
308
 
 
311
  fig = plt.figure(figsize=(12, 20))
312
  gs = fig.add_gridspec(5, 1, height_ratios=[3, 3, 0.2, 1.5, 0.5], hspace=0.8)
313
 
314
+ # Plot bar graph for risk with improved text alignment and spacing
315
  ax1 = fig.add_subplot(gs[0])
316
+ width = 0.35 # Adjusted width for even spacing
317
  unique_dates = forecast_df['Date'].unique()
318
  x_positions = range(len(unique_dates))
319
 
 
322
  trade_x = [pos + idx * width for pos in x_positions]
323
  bars = ax1.bar(
324
  trade_x,
325
+ trade_forecast['Risk'].clip(lower=0),
326
  width,
327
  label=trade,
328
  color=colors[idx % len(colors)]
329
  )
330
+ for bar, x, height in zip(bars, trade_x, trade_forecast['Risk'].clip(lower=0)):
331
+ # Dynamic vertical offset based on bar height with adjusted padding
332
+ offset = 5 if abs(height) < 10 else 10 # Adjusted for positive values only
333
  ax1.text(
334
+ x + width / 2, # Center horizontally
335
+ height + offset, # Dynamic vertical offset
336
  f'{height:.1f}%',
337
  ha='center',
338
+ va='bottom', # Align text appropriately for positive values
339
  fontsize=8
340
  )
341
 
 
343
  ax1.set_xlabel('Date', fontsize=12)
344
  ax1.set_ylabel('Risk (%)', fontsize=12)
345
  ax1.set_xticks([pos + (len(trades_to_process) - 1) * width / 2 for pos in x_positions])
346
+ ax1.set_xticklabels([date.strftime('%Y-%m-%d') for date in unique_dates], rotation=45, ha='right', fontsize=8)
347
+ ax1.set_yticks(range(0, 125, 25)) # Balanced y-axis scale with 25% increments starting from 0
348
  ax1.grid(True, axis='y', linestyle='--', alpha=0.7)
349
+ ax1.legend(title="Trades", fontsize=10, loc='upper right', bbox_to_anchor=(1.15, 1)) # Moved legend outside
350
+ ax1.set_ylim(0, 100) # Cap y-axis to positive values only
351
+ ax1.tick_params(axis='x', pad=10) # Increase padding to prevent overlap
352
 
353
  # Plot line graph for attendance
354
  ax2 = fig.add_subplot(gs[1])
 
356
  trade_forecast = forecast_df[forecast_df['Trade'] == trade]
357
  ax2.plot(
358
  trade_forecast['Date'],
359
+ trade_forecast['Attendance'].clip(lower=0),
360
  label=trade,
361
  color=colors[idx % len(colors)],
362
  marker='o',
 
368
  if not alert_data.empty:
369
  ax2.scatter(
370
  alert_data['Date'],
371
+ alert_data['Attendance'].clip(lower=0),
372
  label=f"{trade} {alert_type}",
373
  color=alert_colors[alert_type],
374
  marker='D',
 
383
  ax2.grid(True, linestyle='--', alpha=0.7)
384
  ax2.legend(title="Trades & Alerts", fontsize=10, loc='upper left', bbox_to_anchor=(0, -0.1), ncol=2)
385
  ax2.tick_params(axis='x', rotation=45, labelsize=8)
386
+ ax2.set_ylim(bottom=0) # Ensure y-axis starts at 0
387
 
388
  # Empty spacer subplot for small gap
389
  ax_spacer = fig.add_subplot(gs[2])
 
394
  ax3.axis('off')
395
  table_data = [summary_df.columns.tolist()] + summary_df.values.tolist()
396
  table = ax3.table(cellText=table_data, cellLoc='center', loc='center', colWidths=[0.3, 0.35, 0.35])
397
+ table.set_fontsize(12) # Set font size manually
398
+ table.scale(1.2, 1.2) # Keep the scaling
 
399
  ax3.set_title('Summary Statistics (Risk in %)', fontsize=14, pad=10)
400
 
401
  # Add legend for alerts
 
408
  ]
409
  ax4.legend(handles=legend_elements, loc='center', ncol=3, title='Alert Types', fontsize=12, title_fontsize=12)
410
 
411
+ plt.subplots_adjust(top=0.9, bottom=0.1, left=0.1, right=1.2, hspace=0.6) # Adjusted right margin for legend
412
  buf = io.BytesIO()
413
  plt.savefig(buf, format='pdf', dpi=300, bbox_inches='tight')
414
  plt.close()
 
454
  logger.warning("No forecast data for notifications")
455
  return message, history_display
456
 
457
+ # Calculate average risk per trade
458
  risk_summary = latest_forecast_df.groupby('Trade')['Risk'].mean().round(1).to_dict()
459
 
460
+ # Generate notifications for each trade
461
  timestamp = datetime.now().strftime("%Y-%m-%d %I:%M:%S %p")
462
  notifications = []
463
  for trade, avg_risk in risk_summary.items():
 
509
  trade_dropdown = gr.Dropdown(label="Select Trade", choices=["All"])
510
 
511
  with gr.Row():
512
+ filter_date = gr.DateTime(label="Site Calendar Date", value=None)
513
 
514
  with gr.Row():
515
  submit_button = gr.Button("Generate Forecast")