Spaces:
Sleeping
Sleeping
Update app.py
Browse files
app.py
CHANGED
|
@@ -2,8 +2,9 @@ import streamlit as st
|
|
| 2 |
import streamlit.components.v1 as components
|
| 3 |
import pandas as pd
|
| 4 |
import matplotlib.pyplot as plt
|
|
|
|
| 5 |
import os
|
| 6 |
-
from datetime import datetime
|
| 7 |
import json
|
| 8 |
import requests
|
| 9 |
import base64
|
|
@@ -68,7 +69,7 @@ if 'weather_data' not in st.session_state:
|
|
| 68 |
def fetch_weather_data(project_location, date):
|
| 69 |
if not WEATHER_API_KEY:
|
| 70 |
logger.error("WEATHER_API_KEY not set")
|
| 71 |
-
return None, "Weather API key not set. Please provide a valid API key."
|
| 72 |
try:
|
| 73 |
params = {
|
| 74 |
"q": project_location,
|
|
@@ -79,7 +80,7 @@ def fetch_weather_data(project_location, date):
|
|
| 79 |
response.raise_for_status()
|
| 80 |
data = response.json()
|
| 81 |
|
| 82 |
-
# Find the closest forecast to the
|
| 83 |
target_date = datetime.strptime(date, "%Y-%m-%d")
|
| 84 |
closest_forecast = None
|
| 85 |
min_time_diff = float('inf')
|
|
@@ -92,31 +93,30 @@ def fetch_weather_data(project_location, date):
|
|
| 92 |
closest_forecast = forecast
|
| 93 |
|
| 94 |
if not closest_forecast:
|
| 95 |
-
return None, "No forecast available for the specified date."
|
| 96 |
|
| 97 |
# Map weather conditions to impact score
|
| 98 |
-
weather_main =
|
|
|
|
| 99 |
if 'clear' in weather_main:
|
| 100 |
impact_score = 10
|
| 101 |
elif 'clouds' in weather_main:
|
| 102 |
-
impact_score = 30 if
|
| 103 |
elif 'rain' in weather_main:
|
| 104 |
-
impact_score = 70 if
|
| 105 |
elif 'storm' in weather_main or 'thunderstorm' in weather_main:
|
| 106 |
impact_score = 90
|
| 107 |
-
else:
|
| 108 |
-
impact_score = 50 # Default for other conditions (e.g., fog, snow)
|
| 109 |
|
| 110 |
weather_condition = get_weather_condition(impact_score)
|
| 111 |
return {
|
| 112 |
"weather_impact_score": impact_score,
|
| 113 |
"weather_condition": weather_condition,
|
| 114 |
-
"temperature":
|
| 115 |
-
"humidity":
|
| 116 |
}, None
|
| 117 |
except Exception as e:
|
| 118 |
logger.error(f"Failed to fetch weather data: {str(e)}")
|
| 119 |
-
return None, f"Failed to fetch weather data for {project_location}: {str(e)}"
|
| 120 |
|
| 121 |
# Function to format high_risk_phases with flag and alert
|
| 122 |
def format_high_risk_phases(high_risk_phases):
|
|
@@ -127,8 +127,57 @@ def format_high_risk_phases(high_risk_phases):
|
|
| 127 |
formatted.append(f"{flag} {phase['phase']}: {phase['task']} (Risk: {phase['risk']:.1f}%) {alert}")
|
| 128 |
return formatted
|
| 129 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 130 |
# Function to generate PDF
|
| 131 |
-
def generate_pdf(input_data, prediction, heatmap_fig):
|
| 132 |
buffer = BytesIO()
|
| 133 |
doc = SimpleDocTemplate(buffer, pagesize=letter)
|
| 134 |
styles = getSampleStyleSheet()
|
|
@@ -186,17 +235,33 @@ def generate_pdf(input_data, prediction, heatmap_fig):
|
|
| 186 |
heatmap_fig.savefig(img_buffer, format='png', bbox_inches='tight')
|
| 187 |
img_buffer.seek(0)
|
| 188 |
story.append(Image(img_buffer, width=6*inch, height=2*inch))
|
| 189 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 190 |
|
| 191 |
doc.build(story)
|
| 192 |
buffer.seek(0)
|
| 193 |
return buffer
|
| 194 |
|
| 195 |
-
# Function to save data to Salesforce, including PDF
|
| 196 |
def save_to_salesforce(input_data, prediction, pdf_buffer):
|
| 197 |
if sf is None:
|
| 198 |
return "Salesforce connection not established."
|
| 199 |
try:
|
|
|
|
|
|
|
|
|
|
| 200 |
# Prepare data for Delay_Predictor__c object
|
| 201 |
sf_data = {
|
| 202 |
"Project_Name__c": input_data["project_name"],
|
|
@@ -214,7 +279,8 @@ def save_to_salesforce(input_data, prediction, pdf_buffer):
|
|
| 214 |
"Project_Location__c": input_data["project_location"],
|
| 215 |
"Delay_Probability__c": prediction["delay_probability"],
|
| 216 |
"AI_Insights__c": prediction["ai_insights"],
|
| 217 |
-
"High_Risk_Phases__c": "; ".join(format_high_risk_phases(prediction["high_risk_phases"]))
|
|
|
|
| 218 |
}
|
| 219 |
logger.info(f"Attempting to save to Salesforce Delay_Predictor__c: {sf_data}")
|
| 220 |
|
|
@@ -324,8 +390,8 @@ if submit_button:
|
|
| 324 |
if project_location and weather_forecast_date:
|
| 325 |
weather_data, weather_error = fetch_weather_data(project_location, input_data["weather_forecast_date"])
|
| 326 |
if weather_error:
|
| 327 |
-
st.error(weather_error)
|
| 328 |
-
logger.error(weather_error)
|
| 329 |
input_data["weather_impact_score"] = 50 # Fallback value
|
| 330 |
input_data["weather_condition"] = "Unknown"
|
| 331 |
else:
|
|
@@ -399,7 +465,13 @@ if submit_button:
|
|
| 399 |
ax.set_title("Delay Risk Heatmap")
|
| 400 |
plt.tight_layout()
|
| 401 |
|
| 402 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 403 |
plt.close(fig)
|
| 404 |
st.download_button(
|
| 405 |
label="Download Prediction Report (PDF)",
|
|
|
|
| 2 |
import streamlit.components.v1 as components
|
| 3 |
import pandas as pd
|
| 4 |
import matplotlib.pyplot as plt
|
| 5 |
+
import plotly.figure_factory as ff
|
| 6 |
import os
|
| 7 |
+
from datetime import datetime, timedelta
|
| 8 |
import json
|
| 9 |
import requests
|
| 10 |
import base64
|
|
|
|
| 69 |
def fetch_weather_data(project_location, date):
|
| 70 |
if not WEATHER_API_KEY:
|
| 71 |
logger.error("WEATHER_API_KEY not set")
|
| 72 |
+
return None, {"error": "Weather API key not set. Please provide a valid API key."}
|
| 73 |
try:
|
| 74 |
params = {
|
| 75 |
"q": project_location,
|
|
|
|
| 80 |
response.raise_for_status()
|
| 81 |
data = response.json()
|
| 82 |
|
| 83 |
+
# Find the closest forecast to the target date
|
| 84 |
target_date = datetime.strptime(date, "%Y-%m-%d")
|
| 85 |
closest_forecast = None
|
| 86 |
min_time_diff = float('inf')
|
|
|
|
| 93 |
closest_forecast = forecast
|
| 94 |
|
| 95 |
if not closest_forecast:
|
| 96 |
+
return None, {"error": "No forecast available for the specified date."}
|
| 97 |
|
| 98 |
# Map weather conditions to impact score
|
| 99 |
+
weather_main = forecast['weather'][0]['main'].lower()
|
| 100 |
+
impact_score = 50 # Default
|
| 101 |
if 'clear' in weather_main:
|
| 102 |
impact_score = 10
|
| 103 |
elif 'clouds' in weather_main:
|
| 104 |
+
impact_score = 30 if forecast['clouds']['all'] < 50 else 50
|
| 105 |
elif 'rain' in weather_main:
|
| 106 |
+
impact_score = 70 if forecast['rain'].get('3h', 0) < 2.5 else 85
|
| 107 |
elif 'storm' in weather_main or 'thunderstorm' in weather_main:
|
| 108 |
impact_score = 90
|
|
|
|
|
|
|
| 109 |
|
| 110 |
weather_condition = get_weather_condition(impact_score)
|
| 111 |
return {
|
| 112 |
"weather_impact_score": impact_score,
|
| 113 |
"weather_condition": weather_condition,
|
| 114 |
+
"temperature": forecast['main']['temp'],
|
| 115 |
+
"humidity": forecast['main']['humidity']
|
| 116 |
}, None
|
| 117 |
except Exception as e:
|
| 118 |
logger.error(f"Failed to fetch weather data: {str(e)}")
|
| 119 |
+
return None, {"error": f"Failed to fetch weather data for {project_location}: {str(e)}"}
|
| 120 |
|
| 121 |
# Function to format high_risk_phases with flag and alert
|
| 122 |
def format_high_risk_phases(high_risk_phases):
|
|
|
|
| 127 |
formatted.append(f"{flag} {phase['phase']}: {phase['task']} (Risk: {phase['risk']:.1f}%) {alert}")
|
| 128 |
return formatted
|
| 129 |
|
| 130 |
+
# Function to generate Gantt chart
|
| 131 |
+
def generate_gantt_chart(input_data, prediction):
|
| 132 |
+
try:
|
| 133 |
+
phase = input_data["phase"]
|
| 134 |
+
task = input_data["task"]
|
| 135 |
+
expected_duration = input_data["task_expected_duration"]
|
| 136 |
+
actual_duration = input_data["task_actual_duration"]
|
| 137 |
+
forecast_date = datetime.strptime(input_data["weather_forecast_date"], "%Y-%m-%d")
|
| 138 |
+
delay_risk = prediction["delay_probability"]
|
| 139 |
+
|
| 140 |
+
# Calculate start and end dates
|
| 141 |
+
start_date = forecast_date - timedelta(days=max(expected_duration, actual_duration))
|
| 142 |
+
expected_end = start_date + timedelta(days=expected_duration)
|
| 143 |
+
actual_end = start_date + timedelta(days=actual_duration) if actual_duration > 0 else expected_end
|
| 144 |
+
|
| 145 |
+
# Prepare Gantt chart data
|
| 146 |
+
df = [
|
| 147 |
+
dict(Task=f"{phase}: {task} (Expected)", Start=start_date.strftime("%Y-%m-%d"), Finish=expected_end.strftime("%Y-%m-%d"), Resource="Expected", Risk=delay_risk),
|
| 148 |
+
dict(Task=f"{phase}: {task} (Actual)", Start=start_date.strftime("%Y-%m-%d"), Finish=actual_end.strftime("%Y-%m-%d"), Resource="Actual", Risk=delay_risk)
|
| 149 |
+
]
|
| 150 |
+
|
| 151 |
+
# Color based on delay risk
|
| 152 |
+
colors = {
|
| 153 |
+
"Expected": "rgb(0, 255, 0)" if delay_risk <= 50 else "rgb(255, 255, 0)" if delay_risk <= 75 else "rgb(255, 0, 0)",
|
| 154 |
+
"Actual": "rgb(0, 200, 0)" if delay_risk <= 50 else "rgb(200, 200, 0)" if delay_risk <= 75 else "rgb(200, 0, 0)"
|
| 155 |
+
}
|
| 156 |
+
|
| 157 |
+
# Create Gantt chart
|
| 158 |
+
fig = ff.create_gantt(
|
| 159 |
+
df,
|
| 160 |
+
colors=colors,
|
| 161 |
+
index_col="Resource",
|
| 162 |
+
title=f"Gantt Chart for {phase}: {task}",
|
| 163 |
+
show_colorbar=True,
|
| 164 |
+
bar_width=0.4,
|
| 165 |
+
showgrid_x=True,
|
| 166 |
+
showgrid_y=True
|
| 167 |
+
)
|
| 168 |
+
fig.update_layout(
|
| 169 |
+
xaxis_title="Timeline",
|
| 170 |
+
yaxis_title="Task",
|
| 171 |
+
height=300,
|
| 172 |
+
margin=dict(l=150)
|
| 173 |
+
)
|
| 174 |
+
return fig
|
| 175 |
+
except Exception as e:
|
| 176 |
+
logger.error(f"Failed to generate Gantt chart: {str(e)}")
|
| 177 |
+
return None
|
| 178 |
+
|
| 179 |
# Function to generate PDF
|
| 180 |
+
def generate_pdf(input_data, prediction, heatmap_fig, gantt_fig):
|
| 181 |
buffer = BytesIO()
|
| 182 |
doc = SimpleDocTemplate(buffer, pagesize=letter)
|
| 183 |
styles = getSampleStyleSheet()
|
|
|
|
| 235 |
heatmap_fig.savefig(img_buffer, format='png', bbox_inches='tight')
|
| 236 |
img_buffer.seek(0)
|
| 237 |
story.append(Image(img_buffer, width=6*inch, height=2*inch))
|
| 238 |
+
story.append(Spacer(1, 12))
|
| 239 |
+
|
| 240 |
+
# Gantt Chart
|
| 241 |
+
if gantt_fig:
|
| 242 |
+
story.append(Paragraph("Gantt Chart", styles['Heading2']))
|
| 243 |
+
gantt_buffer = BytesIO()
|
| 244 |
+
try:
|
| 245 |
+
gantt_fig.write_image(gantt_buffer, format='PNG')
|
| 246 |
+
gantt_buffer.seek(0)
|
| 247 |
+
story.append(Image(gantt_buffer, width=6*inch, height=3*inch))
|
| 248 |
+
except Exception as e:
|
| 249 |
+
logger.error(f"Failed to include Gantt chart in PDF: {str(e)}")
|
| 250 |
+
story.append(Paragraph("Gantt Chart unavailable due to rendering issues.", styles['Normal']))
|
| 251 |
+
story.append(Spacer(1, 12))
|
| 252 |
|
| 253 |
doc.build(story)
|
| 254 |
buffer.seek(0)
|
| 255 |
return buffer
|
| 256 |
|
| 257 |
+
# Function to save data to Salesforce, including PDF and Status__c
|
| 258 |
def save_to_salesforce(input_data, prediction, pdf_buffer):
|
| 259 |
if sf is None:
|
| 260 |
return "Salesforce connection not established."
|
| 261 |
try:
|
| 262 |
+
# Determine Status__c based on delay probability
|
| 263 |
+
status = "Flagged" if prediction["delay_probability"] > 75 else "Running"
|
| 264 |
+
|
| 265 |
# Prepare data for Delay_Predictor__c object
|
| 266 |
sf_data = {
|
| 267 |
"Project_Name__c": input_data["project_name"],
|
|
|
|
| 279 |
"Project_Location__c": input_data["project_location"],
|
| 280 |
"Delay_Probability__c": prediction["delay_probability"],
|
| 281 |
"AI_Insights__c": prediction["ai_insights"],
|
| 282 |
+
"High_Risk_Phases__c": "; ".join(format_high_risk_phases(prediction["high_risk_phases"])),
|
| 283 |
+
"Status__c": status
|
| 284 |
}
|
| 285 |
logger.info(f"Attempting to save to Salesforce Delay_Predictor__c: {sf_data}")
|
| 286 |
|
|
|
|
| 390 |
if project_location and weather_forecast_date:
|
| 391 |
weather_data, weather_error = fetch_weather_data(project_location, input_data["weather_forecast_date"])
|
| 392 |
if weather_error:
|
| 393 |
+
st.error(weather_error.get("error", "Unknown weather error"))
|
| 394 |
+
logger.error(weather_error.get("error", "Unknown weather error"))
|
| 395 |
input_data["weather_impact_score"] = 50 # Fallback value
|
| 396 |
input_data["weather_condition"] = "Unknown"
|
| 397 |
else:
|
|
|
|
| 465 |
ax.set_title("Delay Risk Heatmap")
|
| 466 |
plt.tight_layout()
|
| 467 |
|
| 468 |
+
# Generate Gantt chart
|
| 469 |
+
gantt_fig = generate_gantt_chart(input_data, prediction)
|
| 470 |
+
if gantt_fig:
|
| 471 |
+
st.plotly_chart(gantt_fig, use_container_width=True)
|
| 472 |
+
logger.info("Gantt chart rendered")
|
| 473 |
+
|
| 474 |
+
pdf_buffer = generate_pdf(input_data, prediction, fig, gantt_fig)
|
| 475 |
plt.close(fig)
|
| 476 |
st.download_button(
|
| 477 |
label="Download Prediction Report (PDF)",
|