Files changed (1) hide show
  1. app.py +191 -62
app.py CHANGED
@@ -90,11 +90,32 @@ def generate_pdf(record_data):
90
  logger.debug("Generating PDF...")
91
  pdf_file = BytesIO()
92
  c = canvas.Canvas(pdf_file, pagesize=letter)
93
- c.drawString(100, 750, f"Project Title: {record_data['project_title']}")
94
- c.drawString(100, 730, f"Estimated Duration: {record_data['estimated_duration']} days")
95
- c.drawString(100, 710, f"AI Plan Score: {record_data['ai_plan_score']}%")
96
- c.drawString(100, 690, f"Status: {record_data['status']}")
97
- c.drawString(100, 670, f"Risk Tags: {record_data['risk_tags']}")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
98
  c.save()
99
  pdf_file.seek(0)
100
  logger.debug("PDF generated successfully.")
@@ -114,8 +135,8 @@ def upload_pdf_to_salesforce(pdf_file, project_title, record_id=None):
114
  encoded_pdf_data = base64.b64encode(pdf_file.getvalue()).decode('utf-8')
115
  logger.debug(f"Uploading PDF for project: {project_title}, record ID: {record_id}")
116
  content_version_data = {
117
- "Title": f"{project_title} - Gantt Chart PDF",
118
- "PathOnClient": f"{project_title}_Gantt_Chart.pdf",
119
  "VersionData": encoded_pdf_data,
120
  }
121
 
@@ -194,14 +215,14 @@ def send_to_salesforce(project_title, gantt_chart_url, ai_plan_score, estimated_
194
  new_record_id = project_record['id']
195
  logger.info(f"Created new Salesforce record with ID: {new_record_id}")
196
  return new_record_id
197
-
198
  except Exception as e:
199
  logger.error(f"Error sending data to Salesforce: {str(e)}", exc_info=True)
200
  if hasattr(e, 'content') and e.content:
201
  logger.error(f"Salesforce API response: {e.content}")
202
  return None
203
 
204
- # Function to generate Gantt chart
205
  def generate_project_timeline(boq_file, weather, workforce, location, project_title):
206
  temp_dir = None
207
  try:
@@ -210,36 +231,101 @@ def generate_project_timeline(boq_file, weather, workforce, location, project_ti
210
  raise ValueError("No file uploaded")
211
 
212
  temp_dir = tempfile.mkdtemp()
213
- output_filename = f"gantt_chart_{project_title.replace(' ', '')}{id(boq_file)}.png"
214
  output_path = os.path.join(temp_dir, output_filename)
215
  logger.debug(f"Gantt chart will be saved to: {output_path}")
216
 
 
217
  if isinstance(boq_file, str):
218
  df = pd.read_csv(boq_file)
219
  else:
220
  df = pd.read_csv(boq_file.name)
221
 
222
- if "Task Name" not in df.columns or "Duration" not in df.columns:
223
- raise ValueError("CSV must contain 'Task Name' and 'Duration' columns")
224
-
225
- task_names = df["Task Name"].tolist()
226
- task_durations = df["Duration"].tolist()
227
- logger.debug(f"Tasks: {task_names}, Durations: {task_durations}")
228
-
229
- fig, ax = plt.subplots(figsize=(10, 5))
230
- ax.barh(task_names, task_durations, color="skyblue")
231
- ax.set_xlabel("Duration (days)")
232
- ax.set_title("Project Timeline Gantt Chart")
233
- fig.savefig(output_path, format="png", bbox_inches="tight")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
234
  plt.close(fig)
235
 
236
- risk_tags = [
237
- f"{task} - {'High' if weather == 'rainy' and duration > 5 else 'Low'} Risk (Weather)"
238
- for task, duration in zip(task_names, task_durations)
239
- ]
240
- risk_tags_str = "\n".join(risk_tags)
241
- logger.debug(f"Generated risk tags: {risk_tags_str}")
242
- logger.info("Gantt chart and risk tags generated successfully.")
243
  return output_path, risk_tags_str, temp_dir
244
  except Exception as e:
245
  logger.error(f"Error generating project timeline: {str(e)}", exc_info=True)
@@ -255,16 +341,22 @@ def gradio_interface(boq_file, weather, workforce, location, project_title):
255
  if not boq_file:
256
  return None, "Error: No BOQ file uploaded"
257
 
 
 
 
 
258
  boq_file_path = boq_file.name if hasattr(boq_file, 'name') else boq_file
259
  file_path, risk_tags, temp_dir = generate_project_timeline(boq_file_path, weather, workforce, location, project_title)
260
  if not file_path:
261
  return None, f"Error: Failed to generate timeline: {risk_tags}"
262
 
 
263
  df = pd.read_csv(boq_file_path)
264
  estimated_duration = sum(df["Duration"])
265
  ai_plan_score = min(100, max(0, 100 - (estimated_duration / 100)))
266
  logger.debug(f"Estimated duration: {estimated_duration}, AI plan score: {ai_plan_score}")
267
 
 
268
  record_id = send_to_salesforce(
269
  project_title=project_title,
270
  gantt_chart_url="",
@@ -277,17 +369,22 @@ def gradio_interface(boq_file, weather, workforce, location, project_title):
277
  )
278
 
279
  if not record_id:
280
- return None, f"Error: Failed to create Salesforce record - check logs for details\n\nRisk Tags:\n{risk_tags}"
281
 
 
282
  work_items_id = upload_file_to_salesforce(boq_file_path, "Boq_data.csv", record_id)
283
  if not work_items_id:
284
  logger.warning("Failed to upload BOQ file, but proceeding with record creation")
285
 
 
286
  record_data = {
287
  "project_title": project_title,
288
  "estimated_duration": estimated_duration,
289
  "ai_plan_score": ai_plan_score,
290
  "status": "Draft",
 
 
 
291
  "risk_tags": risk_tags,
292
  }
293
  pdf_file = generate_pdf(record_data)
@@ -300,6 +397,7 @@ def gradio_interface(boq_file, weather, workforce, location, project_title):
300
  if not pdf_content_id:
301
  logger.warning("Failed to upload PDF, but proceeding with record creation")
302
 
 
303
  update_result = send_to_salesforce(
304
  project_title=project_title,
305
  gantt_chart_url=pdf_url if pdf_url else "",
@@ -314,7 +412,8 @@ def gradio_interface(boq_file, weather, workforce, location, project_title):
314
  if not update_result:
315
  logger.warning("Failed to update record with PDF URL, but record was created")
316
 
317
- image_content_id = upload_file_to_salesforce(fileLf_path, f"{project_title}_Gantt_Chart.png", record_id)
 
318
  image_url = None
319
  if image_content_id:
320
  sf = get_salesforce_connection()
@@ -322,8 +421,22 @@ def gradio_interface(boq_file, weather, workforce, location, project_title):
322
  image_url = f"https://{sf.sf_instance}/sfc/servlet.shepherd/version/download/{image_content_id}"
323
  logger.debug(f"Generated image URL: {image_url}")
324
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
325
  logger.info("Gradio interface completed successfully.")
326
- return image_url if image_url else file_path, f"Successfully created Salesforce record ID: {record_id}\n\nRisk Tags:\n{risk_tags}"
327
  except Exception as e:
328
  logger.error(f"Error in Gradio interface: {str(e)}", exc_info=True)
329
  return None, f"Error in Gradio interface: {str(e)}"
@@ -336,20 +449,32 @@ def gradio_interface(boq_file, weather, workforce, location, project_title):
336
  demo = gr.Blocks(theme="default")
337
  with demo:
338
  gr.Markdown("## AI Civil Work Planner")
339
- gr.Markdown("Generate a project timeline (Gantt chart) and risk tags based on BOQ data and site parameters.")
340
 
341
  with gr.Row():
342
  with gr.Column():
343
- boq_file = gr.File(label="Upload BOQ Data (CSV format)")
344
- weather = gr.Dropdown(label="Weather", choices=["sunny", "rainy", "cloudy"], value="sunny")
345
- workforce = gr.Number(label="Workforce Size", value=10, precision=0)
346
- location = gr.Textbox(label="Location", placeholder="Enter project location")
347
- project_title = gr.Textbox(label="Project Title", placeholder="Enter project title")
348
- submit_btn = gr.Button("Generate Timeline")
 
 
 
 
 
 
 
 
 
349
 
350
  with gr.Column():
351
- output_image = gr.Image(label="Gantt Chart")
352
- risk_tags = gr.Textbox(label="Risk Tags and Salesforce Status")
 
 
 
353
 
354
  submit_btn.click(
355
  fn=gradio_interface,
@@ -361,21 +486,21 @@ with demo:
361
  app = FastAPI()
362
  app.add_middleware(
363
  CORSMiddleware,
364
- allow_origins=["https://aiplannerforcivilworktimel2-dev-ed.develop.lightning.force.com"],
365
  allow_credentials=True,
366
  allow_methods=["*"],
367
  allow_headers=["*"],
368
  )
369
 
370
- # Mount directory for temporary files (e.g., Gantt chart PNGs)
371
  app.mount("/static", StaticFiles(directory=tempfile.gettempdir()), name="static")
372
 
373
- # Health check endpoint to verify server status
374
  @app.get("/health")
375
  async def health_check():
376
  return {"status": "healthy"}
377
 
378
- # FastAPI endpoint for processing BOQ files and interacting with Salesforce
379
  @app.post("/api/gradio_interface")
380
  async def api_gradio_interface(
381
  boq_file: UploadFile = File(...),
@@ -400,7 +525,6 @@ async def api_gradio_interface(
400
  df = pd.read_csv(boq_file_path)
401
  estimated_duration = sum(df["Duration"])
402
  ai_plan_score = min(100, max(0, 100 - (estimated_duration / 100)))
403
- logger.debug(f"Estimated duration: {estimated_duration}, AI plan score: {ai_plan_score}")
404
 
405
  record_id = send_to_salesforce(
406
  project_title=project_title,
@@ -415,30 +539,27 @@ async def api_gradio_interface(
415
 
416
  if not record_id:
417
  return JSONResponse({
418
- "error": f"Failed to create Salesforce record - check logs for details",
419
- "text": f"Risk Tags:\n{risk_tags}"
420
  }, status_code=500)
421
 
422
  work_items_id = upload_file_to_salesforce(boq_file_path, "Boq_data.csv", record_id)
423
- if not work_items_id:
424
- logger.warning("Failed to upload BOQ file, but proceeding with record creation")
425
-
426
  record_data = {
427
  "project_title": project_title,
428
  "estimated_duration": estimated_duration,
429
  "ai_plan_score": ai_plan_score,
430
  "status": "Draft",
 
 
 
431
  "risk_tags": risk_tags,
432
  }
 
433
  pdf_file = generate_pdf(record_data)
434
- if not pdf_file:
435
- logger.warning("Failed to generate PDF, but proceeding with record creation")
436
-
437
  pdf_content_id, pdf_url = None, None
438
  if pdf_file:
439
  pdf_content_id, pdf_url = upload_pdf_to_salesforce(pdf_file, project_title, record_id)
440
- if not pdf_content_id:
441
- logger.warning("Failed to upload PDF, but proceeding with record creation")
442
 
443
  update_result = send_to_salesforce(
444
  project_title=project_title,
@@ -451,8 +572,6 @@ async def api_gradio_interface(
451
  weather_type=weather,
452
  work_items_id=work_items_id if work_items_id else ""
453
  )
454
- if not update_result:
455
- logger.warning("Failed to update record with PDF URL, but record was created")
456
 
457
  image_content_id = upload_file_to_salesforce(file_path, f"{project_title}_Gantt_Chart.png", record_id)
458
  image_url = None
@@ -460,12 +579,23 @@ async def api_gradio_interface(
460
  sf = get_salesforce_connection()
461
  if sf:
462
  image_url = f"https://{sf.sf_instance}/sfc/servlet.shepherd/version/download/{image_content_id}"
463
- logger.debug(f"Generated image URL: {image_url}")
464
 
465
- logger.info("API gradio interface completed successfully.")
 
 
 
 
 
 
 
 
 
 
 
 
466
  return JSONResponse({
467
  "image": image_url if image_url else f"/static/{os.path.basename(file_path)}",
468
- "text": f"Successfully created Salesforce record ID: {record_id}\n\nRisk Tags:\n{risk_tags}"
469
  })
470
  except Exception as e:
471
  logger.error(f"Error in API gradio interface: {str(e)}", exc_info=True)
@@ -473,7 +603,6 @@ async def api_gradio_interface(
473
  finally:
474
  if temp_dir and os.path.exists(temp_dir):
475
  shutil.rmtree(temp_dir)
476
- logger.debug(f"Cleaned up temporary directory: {temp_dir}")
477
 
478
  if __name__ == "__main__":
479
  # Run Gradio UI
 
90
  logger.debug("Generating PDF...")
91
  pdf_file = BytesIO()
92
  c = canvas.Canvas(pdf_file, pagesize=letter)
93
+
94
+ # Add project details
95
+ c.setFont("Helvetica-Bold", 14)
96
+ c.drawString(100, 750, "Project Summary Report")
97
+ c.setFont("Helvetica", 12)
98
+
99
+ y_position = 720
100
+ for key, value in record_data.items():
101
+ if key == "risk_tags":
102
+ continue # We'll handle risk tags separately
103
+
104
+ c.drawString(100, y_position, f"{key.replace('_', ' ').title()}: {value}")
105
+ y_position -= 20
106
+
107
+ # Add risk tags section
108
+ if "risk_tags" in record_data:
109
+ c.setFont("Helvetica-Bold", 12)
110
+ c.drawString(100, y_position - 20, "Risk Analysis:")
111
+ c.setFont("Helvetica", 10)
112
+
113
+ risk_tags = record_data["risk_tags"].split("\n")
114
+ for tag in risk_tags:
115
+ if tag.strip():
116
+ c.drawString(120, y_position - 40, tag)
117
+ y_position -= 15
118
+
119
  c.save()
120
  pdf_file.seek(0)
121
  logger.debug("PDF generated successfully.")
 
135
  encoded_pdf_data = base64.b64encode(pdf_file.getvalue()).decode('utf-8')
136
  logger.debug(f"Uploading PDF for project: {project_title}, record ID: {record_id}")
137
  content_version_data = {
138
+ "Title": f"{project_title} - Project Report",
139
+ "PathOnClient": f"{project_title}_Report.pdf",
140
  "VersionData": encoded_pdf_data,
141
  }
142
 
 
215
  new_record_id = project_record['id']
216
  logger.info(f"Created new Salesforce record with ID: {new_record_id}")
217
  return new_record_id
218
+
219
  except Exception as e:
220
  logger.error(f"Error sending data to Salesforce: {str(e)}", exc_info=True)
221
  if hasattr(e, 'content') and e.content:
222
  logger.error(f"Salesforce API response: {e.content}")
223
  return None
224
 
225
+ # Function to generate Gantt chart and risk analysis
226
  def generate_project_timeline(boq_file, weather, workforce, location, project_title):
227
  temp_dir = None
228
  try:
 
231
  raise ValueError("No file uploaded")
232
 
233
  temp_dir = tempfile.mkdtemp()
234
+ output_filename = f"gantt_chart_{project_title.replace(' ', '_')}.png"
235
  output_path = os.path.join(temp_dir, output_filename)
236
  logger.debug(f"Gantt chart will be saved to: {output_path}")
237
 
238
+ # Read the BOQ file
239
  if isinstance(boq_file, str):
240
  df = pd.read_csv(boq_file)
241
  else:
242
  df = pd.read_csv(boq_file.name)
243
 
244
+ # Validate required columns
245
+ required_columns = ["Task Name", "Duration"]
246
+ missing_columns = [col for col in required_columns if col not in df.columns]
247
+ if missing_columns:
248
+ raise ValueError(f"CSV is missing required columns: {', '.join(missing_columns)}")
249
+
250
+ # Generate detailed risk analysis
251
+ risk_analysis = []
252
+ for _, row in df.iterrows():
253
+ task = row["Task Name"]
254
+ duration = row["Duration"]
255
+
256
+ # Weather risk assessment
257
+ if weather.lower() == "rainy":
258
+ weather_impact = "High" if duration > 3 else "Medium"
259
+ weather_reason = "Prolonged rain exposure" if duration > 3 else "Some rain impact expected"
260
+ elif weather.lower() == "sunny":
261
+ weather_impact = "Low"
262
+ weather_reason = "Favorable working conditions"
263
+ else: # cloudy
264
+ weather_impact = "Low"
265
+ weather_reason = "Mild weather impact"
266
+
267
+ # Workforce risk assessment
268
+ if workforce < 10 and duration > 5:
269
+ workforce_impact = "High"
270
+ workforce_reason = "Insufficient workforce for task duration"
271
+ elif workforce < 15 and duration > 10:
272
+ workforce_impact = "Medium"
273
+ workforce_reason = "Workforce may be stretched for this duration"
274
+ else:
275
+ workforce_impact = "Low"
276
+ workforce_reason = "Adequate workforce available"
277
+
278
+ # Overall risk assessment
279
+ overall_risk = "High" if "High" in [weather_impact, workforce_impact] else "Medium" if "Medium" in [weather_impact, workforce_impact] else "Low"
280
+
281
+ risk_analysis.append(
282
+ f"Task: {task}\n"
283
+ f"- Duration: {duration} days\n"
284
+ f"- Weather Impact: {weather_impact} ({weather_reason})\n"
285
+ f"- Workforce Impact: {workforce_impact} ({workforce_reason})\n"
286
+ f"- Overall Risk: {overall_risk}\n"
287
+ )
288
+
289
+ risk_tags_str = "\n".join(risk_analysis)
290
+
291
+ # Generate Gantt chart
292
+ plt.style.use('ggplot')
293
+ fig, ax = plt.subplots(figsize=(12, 6))
294
+
295
+ # Color tasks based on risk level
296
+ colors = []
297
+ for _, row in df.iterrows():
298
+ duration = row["Duration"]
299
+ if weather.lower() == "rainy" and duration > 3:
300
+ colors.append('#ff6b6b') # red for high risk
301
+ elif workforce < 10 and duration > 5:
302
+ colors.append('#ff6b6b') # red for high risk
303
+ elif (weather.lower() == "rainy" and duration > 1) or (workforce < 15 and duration > 7):
304
+ colors.append('#ffd166') # yellow for medium risk
305
+ else:
306
+ colors.append('#06d6a0') # green for low risk
307
+
308
+ ax.barh(df["Task Name"], df["Duration"], color=colors, edgecolor='black')
309
+ ax.set_xlabel("Duration (days)", fontweight='bold')
310
+ ax.set_ylabel("Tasks", fontweight='bold')
311
+ ax.set_title(f"Project Timeline: {project_title}\nLocation: {location} | Weather: {weather}", fontweight='bold')
312
+
313
+ # Add risk legend
314
+ ax.text(0.95, 0.15,
315
+ "Risk Levels:\n"
316
+ "Green = Low Risk\n"
317
+ "Yellow = Medium Risk\n"
318
+ "Red = High Risk",
319
+ transform=ax.transAxes,
320
+ bbox=dict(facecolor='white', alpha=0.8),
321
+ verticalalignment='top',
322
+ horizontalalignment='right')
323
+
324
+ plt.tight_layout()
325
+ fig.savefig(output_path, format="png", bbox_inches="tight", dpi=100)
326
  plt.close(fig)
327
 
328
+ logger.info("Gantt chart and risk analysis generated successfully.")
 
 
 
 
 
 
329
  return output_path, risk_tags_str, temp_dir
330
  except Exception as e:
331
  logger.error(f"Error generating project timeline: {str(e)}", exc_info=True)
 
341
  if not boq_file:
342
  return None, "Error: No BOQ file uploaded"
343
 
344
+ # Validate workforce input
345
+ if workforce <= 0:
346
+ return None, "Error: Workforce size must be greater than 0"
347
+
348
  boq_file_path = boq_file.name if hasattr(boq_file, 'name') else boq_file
349
  file_path, risk_tags, temp_dir = generate_project_timeline(boq_file_path, weather, workforce, location, project_title)
350
  if not file_path:
351
  return None, f"Error: Failed to generate timeline: {risk_tags}"
352
 
353
+ # Calculate project metrics
354
  df = pd.read_csv(boq_file_path)
355
  estimated_duration = sum(df["Duration"])
356
  ai_plan_score = min(100, max(0, 100 - (estimated_duration / 100)))
357
  logger.debug(f"Estimated duration: {estimated_duration}, AI plan score: {ai_plan_score}")
358
 
359
+ # Create Salesforce record
360
  record_id = send_to_salesforce(
361
  project_title=project_title,
362
  gantt_chart_url="",
 
369
  )
370
 
371
  if not record_id:
372
+ return None, f"Error: Failed to create Salesforce record - check logs for details\n\n=== RISK ANALYSIS ===\n\n{risk_tags}"
373
 
374
+ # Upload BOQ file to Salesforce
375
  work_items_id = upload_file_to_salesforce(boq_file_path, "Boq_data.csv", record_id)
376
  if not work_items_id:
377
  logger.warning("Failed to upload BOQ file, but proceeding with record creation")
378
 
379
+ # Generate and upload PDF report
380
  record_data = {
381
  "project_title": project_title,
382
  "estimated_duration": estimated_duration,
383
  "ai_plan_score": ai_plan_score,
384
  "status": "Draft",
385
+ "location": location,
386
+ "weather": weather,
387
+ "workforce_size": workforce,
388
  "risk_tags": risk_tags,
389
  }
390
  pdf_file = generate_pdf(record_data)
 
397
  if not pdf_content_id:
398
  logger.warning("Failed to upload PDF, but proceeding with record creation")
399
 
400
+ # Update record with PDF URL
401
  update_result = send_to_salesforce(
402
  project_title=project_title,
403
  gantt_chart_url=pdf_url if pdf_url else "",
 
412
  if not update_result:
413
  logger.warning("Failed to update record with PDF URL, but record was created")
414
 
415
+ # Upload Gantt chart image
416
+ image_content_id = upload_file_to_salesforce(file_path, f"{project_title}_Gantt_Chart.png", record_id)
417
  image_url = None
418
  if image_content_id:
419
  sf = get_salesforce_connection()
 
421
  image_url = f"https://{sf.sf_instance}/sfc/servlet.shepherd/version/download/{image_content_id}"
422
  logger.debug(f"Generated image URL: {image_url}")
423
 
424
+ # Format output message
425
+ output_message = (
426
+ f"=== PROJECT SUMMARY ===\n\n"
427
+ f"Project: {project_title}\n"
428
+ f"Location: {location}\n"
429
+ f"Weather: {weather}\n"
430
+ f"Workforce Size: {workforce}\n"
431
+ f"Estimated Duration: {estimated_duration} days\n"
432
+ f"AI Plan Score: {ai_plan_score:.1f}%\n\n"
433
+ f"Salesforce Record ID: {record_id}\n\n"
434
+ f"=== RISK ANALYSIS ===\n\n"
435
+ f"{risk_tags}"
436
+ )
437
+
438
  logger.info("Gradio interface completed successfully.")
439
+ return image_url if image_url else file_path, output_message
440
  except Exception as e:
441
  logger.error(f"Error in Gradio interface: {str(e)}", exc_info=True)
442
  return None, f"Error in Gradio interface: {str(e)}"
 
449
  demo = gr.Blocks(theme="default")
450
  with demo:
451
  gr.Markdown("## AI Civil Work Planner")
452
+ gr.Markdown("Generate a project timeline (Gantt chart) and risk analysis based on BOQ data and site parameters.")
453
 
454
  with gr.Row():
455
  with gr.Column():
456
+ boq_file = gr.File(label="Upload BOQ Data (CSV format)", file_types=[".csv"])
457
+ weather = gr.Dropdown(label="Weather Condition",
458
+ choices=["Sunny", "Rainy", "Cloudy"],
459
+ value="Sunny")
460
+ workforce = gr.Number(label="Workforce Size",
461
+ value=10,
462
+ precision=0,
463
+ minimum=1,
464
+ maximum=100,
465
+ step=1)
466
+ location = gr.Textbox(label="Location",
467
+ placeholder="Enter project location")
468
+ project_title = gr.Textbox(label="Project Title",
469
+ placeholder="Enter project title")
470
+ submit_btn = gr.Button("Generate Project Plan", variant="primary")
471
 
472
  with gr.Column():
473
+ output_image = gr.Image(label="Gantt Chart",
474
+ type="filepath")
475
+ risk_tags = gr.Textbox(label="Project Summary and Risk Analysis",
476
+ lines=20,
477
+ max_lines=50)
478
 
479
  submit_btn.click(
480
  fn=gradio_interface,
 
486
  app = FastAPI()
487
  app.add_middleware(
488
  CORSMiddleware,
489
+ allow_origins=["*"],
490
  allow_credentials=True,
491
  allow_methods=["*"],
492
  allow_headers=["*"],
493
  )
494
 
495
+ # Mount directory for temporary files
496
  app.mount("/static", StaticFiles(directory=tempfile.gettempdir()), name="static")
497
 
498
+ # Health check endpoint
499
  @app.get("/health")
500
  async def health_check():
501
  return {"status": "healthy"}
502
 
503
+ # FastAPI endpoint for processing BOQ files
504
  @app.post("/api/gradio_interface")
505
  async def api_gradio_interface(
506
  boq_file: UploadFile = File(...),
 
525
  df = pd.read_csv(boq_file_path)
526
  estimated_duration = sum(df["Duration"])
527
  ai_plan_score = min(100, max(0, 100 - (estimated_duration / 100)))
 
528
 
529
  record_id = send_to_salesforce(
530
  project_title=project_title,
 
539
 
540
  if not record_id:
541
  return JSONResponse({
542
+ "error": "Failed to create Salesforce record",
543
+ "text": f"Risk Analysis:\n\n{risk_tags}"
544
  }, status_code=500)
545
 
546
  work_items_id = upload_file_to_salesforce(boq_file_path, "Boq_data.csv", record_id)
547
+
 
 
548
  record_data = {
549
  "project_title": project_title,
550
  "estimated_duration": estimated_duration,
551
  "ai_plan_score": ai_plan_score,
552
  "status": "Draft",
553
+ "location": location,
554
+ "weather": weather,
555
+ "workforce_size": workforce,
556
  "risk_tags": risk_tags,
557
  }
558
+
559
  pdf_file = generate_pdf(record_data)
 
 
 
560
  pdf_content_id, pdf_url = None, None
561
  if pdf_file:
562
  pdf_content_id, pdf_url = upload_pdf_to_salesforce(pdf_file, project_title, record_id)
 
 
563
 
564
  update_result = send_to_salesforce(
565
  project_title=project_title,
 
572
  weather_type=weather,
573
  work_items_id=work_items_id if work_items_id else ""
574
  )
 
 
575
 
576
  image_content_id = upload_file_to_salesforce(file_path, f"{project_title}_Gantt_Chart.png", record_id)
577
  image_url = None
 
579
  sf = get_salesforce_connection()
580
  if sf:
581
  image_url = f"https://{sf.sf_instance}/sfc/servlet.shepherd/version/download/{image_content_id}"
 
582
 
583
+ output_message = (
584
+ f"=== PROJECT SUMMARY ===\n\n"
585
+ f"Project: {project_title}\n"
586
+ f"Location: {location}\n"
587
+ f"Weather: {weather}\n"
588
+ f"Workforce Size: {workforce}\n"
589
+ f"Estimated Duration: {estimated_duration} days\n"
590
+ f"AI Plan Score: {ai_plan_score:.1f}%\n\n"
591
+ f"Salesforce Record ID: {record_id}\n\n"
592
+ f"=== RISK ANALYSIS ===\n\n"
593
+ f"{risk_tags}"
594
+ )
595
+
596
  return JSONResponse({
597
  "image": image_url if image_url else f"/static/{os.path.basename(file_path)}",
598
+ "text": output_message
599
  })
600
  except Exception as e:
601
  logger.error(f"Error in API gradio interface: {str(e)}", exc_info=True)
 
603
  finally:
604
  if temp_dir and os.path.exists(temp_dir):
605
  shutil.rmtree(temp_dir)
 
606
 
607
  if __name__ == "__main__":
608
  # Run Gradio UI