Spaces:
Sleeping
Sleeping
Update app.py
Browse files
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 |
-
#
|
| 137 |
-
|
| 138 |
-
|
| 139 |
-
|
| 140 |
-
|
| 141 |
-
|
| 142 |
-
|
| 143 |
-
|
| 144 |
-
|
| 145 |
-
|
|
|
|
| 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
|
| 155 |
-
logger.debug("Generating enhanced risk bar graph
|
| 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(
|
| 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=
|
| 194 |
tickformat="%Y-%m-%d",
|
| 195 |
-
tickmode="
|
| 196 |
-
nticks=
|
| 197 |
gridcolor='lightgrey'
|
| 198 |
),
|
| 199 |
yaxis=dict(
|
| 200 |
-
range=[
|
| 201 |
gridcolor='lightgrey',
|
| 202 |
-
zeroline=
|
| 203 |
-
|
|
|
|
| 204 |
),
|
| 205 |
plot_bgcolor='white',
|
| 206 |
margin=dict(t=80, b=80, l=60, r=60)
|
| 207 |
)
|
| 208 |
|
| 209 |
-
|
| 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
|
| 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)
|
| 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.
|
| 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(
|
| 334 |
width,
|
| 335 |
label=trade,
|
| 336 |
color=colors[idx % len(colors)]
|
| 337 |
)
|
| 338 |
-
for bar in bars:
|
| 339 |
-
|
| 340 |
-
|
| 341 |
ax1.text(
|
| 342 |
-
|
| 343 |
-
height +
|
| 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
|
| 357 |
-
ax1.set_ylim(
|
|
|
|
| 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.
|
| 403 |
-
table.
|
| 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=
|
| 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.
|
| 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")
|