RathodHarish commited on
Commit
ecc4ac2
·
verified ·
1 Parent(s): 39b0b7f

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +148 -36
app.py CHANGED
@@ -1,9 +1,9 @@
1
  """
2
- LabOps Log Analyzer Dashboard with CSV file upload, PDF generation, and Salesforce integration
3
  """
4
  import gradio as gr
5
  import pandas as pd
6
- from datetime import datetime
7
  import logging
8
  import plotly.express as px
9
  from sklearn.ensemble import IsolationForest
@@ -13,6 +13,9 @@ from concurrent.futures import ThreadPoolExecutor
13
  from simple_salesforce import Salesforce
14
  import os
15
  import json
 
 
 
16
 
17
  # Configure logging
18
  logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
@@ -30,6 +33,14 @@ except Exception as e:
30
  logging.error(f"Failed to connect to Salesforce: {str(e)}")
31
  sf = None
32
 
 
 
 
 
 
 
 
 
33
  # Try to import reportlab
34
  try:
35
  from reportlab.lib.pagesizes import letter
@@ -97,39 +108,120 @@ picklist_mapping = {
97
  }
98
  }
99
 
100
- # Create Salesforce report
101
- def create_salesforce_report(df):
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
102
  if sf is None:
103
  return "Salesforce connection not available."
104
  try:
105
- report_metadata = {
 
106
  "reportMetadata": {
107
  "name": f"SmartLog_Usage_Report_{datetime.now().strftime('%Y%m%d_%H%M%S')}",
108
  "reportType": {"type": "SmartLog__c"},
109
  "reportFormat": "SUMMARY",
110
  "reportBooleanFilter": "",
111
  "reportFilters": [
112
- {"column": "Status__c", "operator": "equals", "value": "Active"}
 
113
  ],
114
  "reportColumns": [
115
  {"column": "Device_Id__c"},
116
  {"column": "Log_Type__c"},
117
  {"column": "Status__c"},
118
  {"column": "Timestamp__c"},
119
- {"column": "Usage_Hours__c", "aggregate": "Sum"},
120
- {"column": "Downtime__c", "aggregate": "Sum"},
121
  {"column": "AMC_Date__c"}
122
  ],
123
  "groupingsDown": [{"name": "Device_Id__c", "sortOrder": "Asc"}],
124
  "folderName": "LabOps Reports"
125
  }
126
  }
127
- result = sf.restful('analytics/reports', method='POST', json=report_metadata)
128
- logging.info(f"Report created: {result['id']}")
129
- return result['id']
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
130
  except Exception as e:
131
- logging.error(f"Failed to create Salesforce report: {str(e)}")
132
- return None
133
 
134
  # Save results to Salesforce SmartLog__c
135
  def save_to_salesforce(df, summary, anomalies, amc_reminders, insights):
@@ -137,6 +229,8 @@ def save_to_salesforce(df, summary, anomalies, amc_reminders, insights):
137
  return "Salesforce connection not available."
138
  try:
139
  records = []
 
 
140
  for _, row in df.head(100).iterrows():
141
  # Validate and map picklist values
142
  status = str(row['status'])
@@ -156,6 +250,14 @@ def save_to_salesforce(df, summary, anomalies, amc_reminders, insights):
156
  logging.warning(f"Skipping record with invalid Log_Type__c: {row['log_type']}")
157
  continue
158
 
 
 
 
 
 
 
 
 
159
  record = {
160
  'Device_Id__c': str(row['device_id'])[:50],
161
  'Log_Type__c': log_type,
@@ -163,7 +265,7 @@ def save_to_salesforce(df, summary, anomalies, amc_reminders, insights):
163
  'Timestamp__c': row['timestamp'].isoformat() if pd.notna(row['timestamp']) else None,
164
  'Usage_Hours__c': float(row['usage_hours']) if pd.notna(row['usage_hours']) else 0.0,
165
  'Downtime__c': float(row['downtime']) if pd.notna(row['downtime']) else 0.0,
166
- 'AMC_Date__c': row['amc_date'].strftime('%Y-%m-%d') if pd.notna(row['amc_date']) else None
167
  }
168
  records.append(record)
169
 
@@ -215,25 +317,25 @@ def detect_anomalies(df, progress=gr.Progress()):
215
  logging.error(f"Anomaly detection failed: {str(e)}")
216
  return f"Anomaly detection failed: {str(e)}"
217
 
218
- # AMC reminders
219
  def check_amc_reminders(df, current_date, progress=gr.Progress()):
220
  progress(0.6, "Checking AMC reminders...")
221
  try:
222
  if "device_id" not in df.columns or "amc_date" not in df.columns:
223
- return "AMC reminders require 'device_id' and 'amc_date' columns."
224
  df["amc_date"] = pd.to_datetime(df["amc_date"], errors='coerce')
225
  current_date = pd.to_datetime(current_date)
226
- df["days_to_amc"] = (df["amc_date"] - current_date).dt.days
227
- reminders = df[(df["days_to_amc"] >= 0) & (df["days_to_amc"] <= 30)][["device_id", "amc_date"]]
228
  if reminders.empty:
229
- return "No AMC reminders due within the next 30 days."
230
  reminder_lines = ["Upcoming AMC Reminders:"]
231
  for _, row in reminders.head(5).iterrows():
232
  reminder_lines.append(f"- Device ID: {row['device_id']}, AMC Date: {row['amc_date']}")
233
- return "\n".join(reminder_lines)
234
  except Exception as e:
235
  logging.error(f"AMC reminder generation failed: {str(e)}")
236
- return f"AMC reminder generation failed: {str(e)}"
237
 
238
  # Dashboard insights
239
  def generate_dashboard_insights(df, progress=gr.Progress()):
@@ -278,7 +380,7 @@ def create_usage_chart(df, progress=gr.Progress()):
278
  return None
279
 
280
  # Generate PDF content
281
- def generate_pdf_content(summary, preview, anomalies, amc_reminders, insights):
282
  if not reportlab_available:
283
  return None
284
  try:
@@ -310,6 +412,10 @@ def generate_pdf_content(summary, preview, anomalies, amc_reminders, insights):
310
  story.append(safe_paragraph(amc_reminders or "No AMC reminders.", styles['Normal']))
311
  story.append(Spacer(1, 12))
312
 
 
 
 
 
313
  story.append(Paragraph("Dashboard Insights", styles['Heading2']))
314
  story.append(safe_paragraph(insights or "No insights generated.", styles['Normal']))
315
 
@@ -325,13 +431,13 @@ async def process_logs(file_obj, progress=gr.Progress()):
325
  try:
326
  progress(0, "Starting file processing...")
327
  if not file_obj:
328
- return "No file uploaded.", "No data to preview.", None, "No anomalies detected.", "No AMC reminders.", "No insights generated.", None, "No Salesforce data saved.", "No report created."
329
 
330
  file_name = file_obj.name
331
  logging.info(f"Processing file: {file_name}")
332
 
333
  if not file_name.endswith(".csv"):
334
- return "Please upload a CSV file.", "", None, "", "", "", None, "", ""
335
 
336
  required_columns = ["device_id", "log_type", "status", "timestamp", "usage_hours", "downtime", "amc_date"]
337
  dtypes = {
@@ -345,11 +451,11 @@ async def process_logs(file_obj, progress=gr.Progress()):
345
  df = pd.read_csv(file_obj, dtype=dtypes)
346
  missing_columns = [col for col in required_columns if col not in df.columns]
347
  if missing_columns:
348
- return f"Missing columns: {missing_columns}", None, None, None, None, None, None, None, None
349
  df["timestamp"] = pd.to_datetime(df["timestamp"], errors='coerce')
350
  df["amc_date"] = pd.to_datetime(df["amc_date"], errors='coerce')
351
  if df.empty:
352
- return "No data available.", None, None, None, None, None, None, None, None
353
 
354
  with ThreadPoolExecutor() as executor:
355
  future_summary = executor.submit(summarize_logs, df)
@@ -357,14 +463,15 @@ async def process_logs(file_obj, progress=gr.Progress()):
357
  future_amc = executor.submit(check_amc_reminders, df, datetime.now())
358
  future_insights = executor.submit(generate_dashboard_insights, df)
359
  future_chart = executor.submit(create_usage_chart, df)
360
- future_report = executor.submit(create_salesforce_report, df)
361
 
362
  summary = f"Step 1: Summary Report\n{future_summary.result()}"
363
  anomalies = f"Anomaly Detection\n{future_anomalies.result()}"
364
- amc_reminders = f"AMC Reminders\n{future_amc.result()}"
 
365
  insights = f"Dashboard Insights (AI)\n{future_insights.result()}"
366
  chart = future_chart.result()
367
- report_id = future_report.result()
368
 
369
  preview_lines = ["Step 2: Log Preview (First 5 Rows)"]
370
  for idx, row in df.head(5).iterrows():
@@ -377,14 +484,14 @@ async def process_logs(file_obj, progress=gr.Progress()):
377
  preview = "\n".join(preview_lines)
378
 
379
  salesforce_result = save_to_salesforce(df, summary, anomalies, amc_reminders, insights)
380
- pdf_file = generate_pdf_content(summary, preview, anomalies, amc_reminders, insights)
381
- report_result = f"Report created in Salesforce with ID: {report_id}" if report_id else "Failed to create report."
382
 
383
  progress(1.0, "Done!")
384
- return summary, preview, chart, anomalies, amc_reminders, insights, pdf_file, salesforce_result, report_result
385
  except Exception as e:
386
  logging.error(f"Failed to process file: {str(e)}")
387
- return f"Error: {str(e)}", None, None, None, None, None, None, None, None
388
 
389
  # Gradio Interface
390
  try:
@@ -398,7 +505,7 @@ try:
398
  .dashboard-section ul {margin: 2px 0; padding-left: 20px;}
399
  """) as iface:
400
  gr.Markdown("<h1>LabOps Log Analyzer Dashboard (Hugging Face AI)</h1>")
401
- gr.Markdown("Upload a CSV file to analyze and generate Salesforce reports.")
402
 
403
  with gr.Row():
404
  with gr.Column(scale=1):
@@ -433,12 +540,16 @@ try:
433
  gr.Markdown("### Step 6: Insights (AI)")
434
  insights_output = gr.Markdown()
435
 
 
 
 
 
436
  with gr.Group(elem_classes="dashboard-section"):
437
  gr.Markdown("### Salesforce Integration")
438
  salesforce_output = gr.Markdown()
439
 
440
  with gr.Group(elem_classes="dashboard-section"):
441
- gr.Markdown("### Salesforce Report")
442
  report_output = gr.Markdown()
443
 
444
  with gr.Group(elem_classes="dashboard-section"):
@@ -457,7 +568,8 @@ try:
457
  insights_output,
458
  pdf_output,
459
  salesforce_output,
460
- report_output
 
461
  ]
462
  )
463
 
 
1
  """
2
+ LabOps Log Analyzer Dashboard with CSV file upload, PDF generation, Salesforce integration, and AMC reminder email alerts
3
  """
4
  import gradio as gr
5
  import pandas as pd
6
+ from datetime import datetime, timedelta
7
  import logging
8
  import plotly.express as px
9
  from sklearn.ensemble import IsolationForest
 
13
  from simple_salesforce import Salesforce
14
  import os
15
  import json
16
+ import smtplib
17
+ from email.mime.text import MIMEText
18
+ from email.mime.multipart import MIMEMultipart
19
 
20
  # Configure logging
21
  logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
 
33
  logging.error(f"Failed to connect to Salesforce: {str(e)}")
34
  sf = None
35
 
36
+ # Email configuration (using environment variables)
37
+ SMTP_SERVER = os.getenv('SMTP_SERVER', 'smtp.gmail.com')
38
+ SMTP_PORT = int(os.getenv('SMTP_PORT', 587))
39
+ SMTP_USERNAME = os.getenv('harishkumarr@sathkrutha.com') # e.g., your-email@gmail.com
40
+ SMTP_PASSWORD = os.getenv('Harish@048') # App-specific password if using Gmail
41
+ EMAIL_FROM = os.getenv('EMAIL_FROM', SMTP_USERNAME)
42
+ EMAIL_TO = "harishkumarr@sathkrutha.com"
43
+
44
  # Try to import reportlab
45
  try:
46
  from reportlab.lib.pagesizes import letter
 
108
  }
109
  }
110
 
111
+ # Send AMC reminder emails
112
+ def send_amc_reminder_emails(reminders_df):
113
+ if reminders_df.empty:
114
+ logging.info("No AMC reminders to send via email.")
115
+ return "No AMC reminder emails sent (no reminders found)."
116
+
117
+ if not all([SMTP_USERNAME, SMTP_PASSWORD, EMAIL_FROM]):
118
+ logging.error("SMTP credentials not configured. Please set SMTP_USERNAME, SMTP_PASSWORD, and EMAIL_FROM environment variables.")
119
+ return "Failed to send emails: SMTP credentials not configured."
120
+
121
+ try:
122
+ # Set up the SMTP server
123
+ server = smtplib.SMTP(SMTP_SERVER, SMTP_PORT)
124
+ server.starttls()
125
+ server.login(SMTP_USERNAME, SMTP_PASSWORD)
126
+
127
+ email_results = []
128
+ for _, row in reminders_df.iterrows():
129
+ device_id = row['device_id']
130
+ amc_date = row['amc_date'].strftime('%Y-%m-%d')
131
+
132
+ # Create the email
133
+ msg = MIMEMultipart()
134
+ msg['From'] = EMAIL_FROM
135
+ msg['To'] = EMAIL_TO
136
+ msg['Subject'] = f"AMC Reminder for Device {device_id}"
137
+
138
+ body = f"""
139
+ Dear Harish Kumar,
140
+
141
+ This is a reminder that the Annual Maintenance Contract (AMC) for the following device is due:
142
+
143
+ - Device ID: {device_id}
144
+ - AMC Date: {amc_date}
145
+
146
+ Please schedule the maintenance at your earliest convenience.
147
+
148
+ Best regards,
149
+ LabOps Team
150
+ """
151
+ msg.attach(MIMEText(body, 'plain'))
152
+
153
+ # Send the email
154
+ server.sendmail(EMAIL_FROM, EMAIL_TO, msg.as_string())
155
+ logging.info(f"AMC reminder email sent for Device ID {device_id} to {EMAIL_TO}")
156
+ email_results.append(f"Sent AMC reminder for Device ID {device_id}")
157
+
158
+ server.quit()
159
+ return "\n".join(email_results) if email_results else "No emails sent."
160
+ except Exception as e:
161
+ logging.error(f"Failed to send AMC reminder emails: {str(e)}")
162
+ return f"Failed to send AMC reminder emails: {str(e)}"
163
+
164
+ # Create Salesforce reports (Usage and AMC Reminders)
165
+ def create_salesforce_reports(df):
166
  if sf is None:
167
  return "Salesforce connection not available."
168
  try:
169
+ # Usage Report
170
+ usage_report_metadata = {
171
  "reportMetadata": {
172
  "name": f"SmartLog_Usage_Report_{datetime.now().strftime('%Y%m%d_%H%M%S')}",
173
  "reportType": {"type": "SmartLog__c"},
174
  "reportFormat": "SUMMARY",
175
  "reportBooleanFilter": "",
176
  "reportFilters": [
177
+ {"column": "Status__c", "operator": "equals", "value": "Active"},
178
+ {"column": "Timestamp__c", "operator": "greaterOrEqual", "value": "THIS_MONTH"}
179
  ],
180
  "reportColumns": [
181
  {"column": "Device_Id__c"},
182
  {"column": "Log_Type__c"},
183
  {"column": "Status__c"},
184
  {"column": "Timestamp__c"},
185
+ {"column": "Usage_Hours__c", "aggregateTypes": ["Sum"]},
186
+ {"column": "Downtime__c", "aggregateTypes": ["Sum"]},
187
  {"column": "AMC_Date__c"}
188
  ],
189
  "groupingsDown": [{"name": "Device_Id__c", "sortOrder": "Asc"}],
190
  "folderName": "LabOps Reports"
191
  }
192
  }
193
+ usage_result = sf.restful('analytics/reports', method='POST', json=usage_report_metadata)
194
+ usage_report_id = usage_result['id']
195
+ logging.info(f"Usage Report created: {usage_report_id}")
196
+
197
+ # AMC Reminders Report
198
+ amc_report_metadata = {
199
+ "reportMetadata": {
200
+ "name": f"SmartLog_AMC_Reminders_{datetime.now().strftime('%Y%m%d_%H%M%S')}",
201
+ "reportType": {"type": "SmartLog__c"},
202
+ "reportFormat": "TABULAR",
203
+ "reportBooleanFilter": "",
204
+ "reportFilters": [
205
+ {"column": "Status__c", "operator": "equals", "value": "Active"},
206
+ {"column": "AMC_Date__c", "operator": "greaterOrEqual", "value": "TODAY"},
207
+ {"column": "AMC_Date__c", "operator": "lessOrEqual", "value": "NEXT_N_DAYS:30"}
208
+ ],
209
+ "reportColumns": [
210
+ {"column": "Device_Id__c"},
211
+ {"column": "AMC_Date__c"},
212
+ {"column": "Status__c"}
213
+ ],
214
+ "folderName": "LabOps Reports"
215
+ }
216
+ }
217
+ amc_result = sf.restful('analytics/reports', method='POST', json=amc_report_metadata)
218
+ amc_report_id = amc_result['id']
219
+ logging.info(f"AMC Reminders Report created: {amc_report_id}")
220
+
221
+ return f"Usage Report ID: {usage_report_id}, AMC Reminders Report ID: {amc_report_id}"
222
  except Exception as e:
223
+ logging.error(f"Failed to create Salesforce reports: {str(e)}")
224
+ return f"Failed to create reports: {str(e)}"
225
 
226
  # Save results to Salesforce SmartLog__c
227
  def save_to_salesforce(df, summary, anomalies, amc_reminders, insights):
 
229
  return "Salesforce connection not available."
230
  try:
231
  records = []
232
+ current_date = datetime.now()
233
+ next_30_days = current_date + timedelta(days=30)
234
  for _, row in df.head(100).iterrows():
235
  # Validate and map picklist values
236
  status = str(row['status'])
 
250
  logging.warning(f"Skipping record with invalid Log_Type__c: {row['log_type']}")
251
  continue
252
 
253
+ # Ensure AMC_Date__c is in correct format
254
+ amc_date_str = row['amc_date'].strftime('%Y-%m-%d') if pd.notna(row['amc_date']) else None
255
+ if amc_date_str:
256
+ amc_date = datetime.strptime(amc_date_str, '%Y-%m-%d')
257
+ # Log if this record qualifies for AMC Reminders
258
+ if status == "Active" and current_date.date() <= amc_date.date() <= next_30_days.date():
259
+ logging.info(f"Record qualifies for AMC Reminders: Device ID {row['device_id']}, AMC Date {amc_date_str}")
260
+
261
  record = {
262
  'Device_Id__c': str(row['device_id'])[:50],
263
  'Log_Type__c': log_type,
 
265
  'Timestamp__c': row['timestamp'].isoformat() if pd.notna(row['timestamp']) else None,
266
  'Usage_Hours__c': float(row['usage_hours']) if pd.notna(row['usage_hours']) else 0.0,
267
  'Downtime__c': float(row['downtime']) if pd.notna(row['downtime']) else 0.0,
268
+ 'AMC_Date__c': amc_date_str
269
  }
270
  records.append(record)
271
 
 
317
  logging.error(f"Anomaly detection failed: {str(e)}")
318
  return f"Anomaly detection failed: {str(e)}"
319
 
320
+ # AMC reminders (identify records for email and display)
321
  def check_amc_reminders(df, current_date, progress=gr.Progress()):
322
  progress(0.6, "Checking AMC reminders...")
323
  try:
324
  if "device_id" not in df.columns or "amc_date" not in df.columns:
325
+ return "AMC reminders require 'device_id' and 'amc_date' columns.", pd.DataFrame()
326
  df["amc_date"] = pd.to_datetime(df["amc_date"], errors='coerce')
327
  current_date = pd.to_datetime(current_date)
328
+ df["days_to_amc"] = (df["days_to_amc"] >= 0) & (df["days_to_amc"] <= 30)
329
+ reminders = df[df["days_to_amc"]][["device_id", "amc_date"]]
330
  if reminders.empty:
331
+ return "No AMC reminders due within the next 30 days.", reminders
332
  reminder_lines = ["Upcoming AMC Reminders:"]
333
  for _, row in reminders.head(5).iterrows():
334
  reminder_lines.append(f"- Device ID: {row['device_id']}, AMC Date: {row['amc_date']}")
335
+ return "\n".join(reminder_lines), reminders
336
  except Exception as e:
337
  logging.error(f"AMC reminder generation failed: {str(e)}")
338
+ return f"AMC reminder generation failed: {str(e)}", pd.DataFrame()
339
 
340
  # Dashboard insights
341
  def generate_dashboard_insights(df, progress=gr.Progress()):
 
380
  return None
381
 
382
  # Generate PDF content
383
+ def generate_pdf_content(summary, preview, anomalies, amc_reminders, insights, email_status):
384
  if not reportlab_available:
385
  return None
386
  try:
 
412
  story.append(safe_paragraph(amc_reminders or "No AMC reminders.", styles['Normal']))
413
  story.append(Spacer(1, 12))
414
 
415
+ story.append(Paragraph("Email Notification Status", styles['Heading2']))
416
+ story.append(safe_paragraph(email_status or "No emails sent.", styles['Normal']))
417
+ story.append(Spacer(1, 12))
418
+
419
  story.append(Paragraph("Dashboard Insights", styles['Heading2']))
420
  story.append(safe_paragraph(insights or "No insights generated.", styles['Normal']))
421
 
 
431
  try:
432
  progress(0, "Starting file processing...")
433
  if not file_obj:
434
+ return "No file uploaded.", "No data to preview.", None, "No anomalies detected.", "No AMC reminders.", "No insights generated.", None, "No Salesforce data saved.", "No report created.", "No emails sent."
435
 
436
  file_name = file_obj.name
437
  logging.info(f"Processing file: {file_name}")
438
 
439
  if not file_name.endswith(".csv"):
440
+ return "Please upload a CSV file.", "", None, "", "", "", None, "", "", ""
441
 
442
  required_columns = ["device_id", "log_type", "status", "timestamp", "usage_hours", "downtime", "amc_date"]
443
  dtypes = {
 
451
  df = pd.read_csv(file_obj, dtype=dtypes)
452
  missing_columns = [col for col in required_columns if col not in df.columns]
453
  if missing_columns:
454
+ return f"Missing columns: {missing_columns}", None, None, None, None, None, None, None, None, None
455
  df["timestamp"] = pd.to_datetime(df["timestamp"], errors='coerce')
456
  df["amc_date"] = pd.to_datetime(df["amc_date"], errors='coerce')
457
  if df.empty:
458
+ return "No data available.", None, None, None, None, None, None, None, None, None
459
 
460
  with ThreadPoolExecutor() as executor:
461
  future_summary = executor.submit(summarize_logs, df)
 
463
  future_amc = executor.submit(check_amc_reminders, df, datetime.now())
464
  future_insights = executor.submit(generate_dashboard_insights, df)
465
  future_chart = executor.submit(create_usage_chart, df)
466
+ future_reports = executor.submit(create_salesforce_reports, df)
467
 
468
  summary = f"Step 1: Summary Report\n{future_summary.result()}"
469
  anomalies = f"Anomaly Detection\n{future_anomalies.result()}"
470
+ amc_reminders, reminders_df = future_amc.result() # Get both display text and DataFrame
471
+ amc_reminders = f"AMC Reminders\n{amc_reminders}"
472
  insights = f"Dashboard Insights (AI)\n{future_insights.result()}"
473
  chart = future_chart.result()
474
+ report_result = future_reports.result()
475
 
476
  preview_lines = ["Step 2: Log Preview (First 5 Rows)"]
477
  for idx, row in df.head(5).iterrows():
 
484
  preview = "\n".join(preview_lines)
485
 
486
  salesforce_result = save_to_salesforce(df, summary, anomalies, amc_reminders, insights)
487
+ email_status = send_amc_reminder_emails(reminders_df)
488
+ pdf_file = generate_pdf_content(summary, preview, anomalies, amc_reminders, insights, email_status)
489
 
490
  progress(1.0, "Done!")
491
+ return summary, preview, chart, anomalies, amc_reminders, insights, pdf_file, salesforce_result, report_result, email_status
492
  except Exception as e:
493
  logging.error(f"Failed to process file: {str(e)}")
494
+ return f"Error: {str(e)}", None, None, None, None, None, None, None, None, None
495
 
496
  # Gradio Interface
497
  try:
 
505
  .dashboard-section ul {margin: 2px 0; padding-left: 20px;}
506
  """) as iface:
507
  gr.Markdown("<h1>LabOps Log Analyzer Dashboard (Hugging Face AI)</h1>")
508
+ gr.Markdown("Upload a CSV file to analyze, generate Salesforce reports, and send AMC reminder emails.")
509
 
510
  with gr.Row():
511
  with gr.Column(scale=1):
 
540
  gr.Markdown("### Step 6: Insights (AI)")
541
  insights_output = gr.Markdown()
542
 
543
+ with gr.Group(elem_classes="dashboard-section"):
544
+ gr.Markdown("### Step 7: Email Notification Status")
545
+ email_output = gr.Markdown()
546
+
547
  with gr.Group(elem_classes="dashboard-section"):
548
  gr.Markdown("### Salesforce Integration")
549
  salesforce_output = gr.Markdown()
550
 
551
  with gr.Group(elem_classes="dashboard-section"):
552
+ gr.Markdown("### Salesforce Reports")
553
  report_output = gr.Markdown()
554
 
555
  with gr.Group(elem_classes="dashboard-section"):
 
568
  insights_output,
569
  pdf_output,
570
  salesforce_output,
571
+ report_output,
572
+ email_output
573
  ]
574
  )
575