RathodHarish commited on
Commit
c9564d9
·
verified ·
1 Parent(s): 25f7d47

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +101 -605
app.py CHANGED
@@ -1,616 +1,112 @@
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
10
- from transformers import pipeline
11
- import torch
12
- from concurrent.futures import ThreadPoolExecutor
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')
22
-
23
- # Salesforce configuration
24
- try:
25
- sf = Salesforce(
26
- username=os.getenv('SF_USERNAME', 'multi-devicelabopsdashboard@sathkrutha.com'),
27
- password=os.getenv('SF_PASSWORD', 'Team@1234'),
28
- security_token=os.getenv('SF_SECURITY_TOKEN'),
29
- domain='login'
30
- )
31
- logging.info("Salesforce connection established")
32
- except Exception as e:
33
- logging.error(f"Failed to connect to Salesforce: {str(e)}")
34
- sf = None
35
-
36
- # Email configuration
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., harishkumarr@sathkrutha.com
40
- SMTP_PASSWORD = os.getenv('Harish@048') # App-specific password
41
- EMAIL_FROM = "harishkumarr@sathkrutha.com"
42
- EMAIL_TO = "sanjaybhargavneela@sathkrutha.com"
43
-
44
- # Try to import reportlab
45
- try:
46
- from reportlab.lib.pagesizes import letter
47
- from reportlab.platypus import SimpleDocTemplate, Paragraph, Spacer
48
- from reportlab.lib.styles import getSampleStyleSheet
49
- reportlab_available = True
50
- logging.info("reportlab module successfully imported")
51
- except ImportError:
52
- logging.warning("reportlab module not found. PDF generation disabled.")
53
- reportlab_available = False
54
-
55
- # Preload Hugging Face model
56
- logging.info("Preloading Hugging Face model...")
57
- try:
58
- device = 0 if torch.cuda.is_available() else -1
59
- summarizer = pipeline(
60
- "summarization",
61
- model="facebook/bart-large-cnn",
62
- device=device,
63
- max_length=50,
64
- min_length=10,
65
- num_beams=4
66
- )
67
- logging.info(f"Hugging Face model preloaded on {'GPU' if device == 0 else 'CPU'}")
68
- except Exception as e:
69
- logging.error(f"Failed to preload model: {str(e)}")
70
- raise e
71
-
72
- # Fetch valid picklist values from Salesforce
73
- def get_picklist_values(field_name):
74
- if sf is None:
75
- return []
76
- try:
77
- obj_desc = sf.SmartLog__c.describe()
78
- for field in obj_desc['fields']:
79
- if field['name'] == field_name:
80
- return [value['value'] for value in field['picklistValues'] if value['active']]
81
- return []
82
- except Exception as e:
83
- logging.error(f"Failed to fetch picklist values for {field_name}: {str(e)}")
84
- return []
85
-
86
- # Cache picklist values at startup
87
- status_values = get_picklist_values('Status__c') or ["Active", "Inactive", "Pending"]
88
- log_type_values = get_picklist_values('Log_Type__c') or ["Smart Log", "Cell Analysis", "UV Verification"]
89
- logging.info(f"Valid Status__c values: {status_values}")
90
- logging.info(f"Valid Log_Type__c values: {log_type_values}")
91
-
92
- # Map invalid picklist values to valid ones
93
- picklist_mapping = {
94
- 'Status__c': {
95
- 'normal': 'Active',
96
- 'error': 'Inactive',
97
- 'warning': 'Pending',
98
- 'ok': 'Active',
99
- 'failed': 'Inactive'
100
- },
101
- 'Log_Type__c': {
102
- 'maint': 'Smart Log',
103
- 'error': 'Cell Analysis',
104
- 'ops': 'UV Verification',
105
- 'maintenance': 'Smart Log',
106
- 'cell': 'Cell Analysis',
107
- 'uv': 'UV Verification'
108
- }
109
- }
110
-
111
- # Fetch folder ID for "LabOps Reports"
112
- def get_folder_id(folder_name):
113
- if sf is None:
114
- return None
115
- try:
116
- query = f"SELECT Id FROM Folder WHERE Name = '{folder_name}' AND Type = 'Report'"
117
- result = sf.query(query)
118
- if result['totalSize'] > 0:
119
- folder_id = result['records'][0]['Id']
120
- logging.info(f"Found folder ID for '{folder_name}': {folder_id}")
121
- return folder_id
122
- else:
123
- logging.error(f"Folder '{folder_name}' not found in Salesforce.")
124
- return None
125
- except Exception as e:
126
- logging.error(f"Failed to fetch folder ID for '{folder_name}': {str(e)}")
127
- return None
128
-
129
- # Cache the folder ID at startup
130
- LABOPS_REPORTS_FOLDER_ID = get_folder_id('LabOps Reports')
131
-
132
- # Send AMC reminder emails
133
- def send_amc_reminder_emails(reminders_df):
134
- if reminders_df.empty:
135
- logging.info("No AMC reminders to send via email.")
136
- return "No AMC reminder emails sent (no reminders found)."
137
-
138
- if not all([SMTP_USERNAME, SMTP_PASSWORD]):
139
- logging.error("SMTP credentials not configured. Please set SMTP_USERNAME and SMTP_PASSWORD environment variables.")
140
- return "Failed to send emails: SMTP credentials not configured."
141
-
142
- try:
143
- # Set up the SMTP server
144
- server = smtplib.SMTP(SMTP_SERVER, SMTP_PORT)
145
- server.starttls()
146
- server.login(SMTP_USERNAME, SMTP_PASSWORD)
147
-
148
- email_results = []
149
- for _, row in reminders_df.iterrows():
150
- device_id = row['device_id']
151
- amc_date = row['amc_date'].strftime('%Y-%m-%d')
152
-
153
- # Create the email
154
- msg = MIMEMultipart()
155
- msg['From'] = EMAIL_FROM
156
- msg['To'] = EMAIL_TO
157
- msg['Subject'] = f"AMC Reminder for Device {device_id}"
158
-
159
- body = f"""
160
- Dear Sanjay Bhargav Neela,
161
-
162
- This is a reminder that the Annual Maintenance Contract (AMC) for the following device is due:
163
-
164
- - Device ID: {device_id}
165
- - AMC Date: {amc_date}
166
-
167
- Please schedule the maintenance at your earliest convenience.
168
-
169
- Best regards,
170
- Harish Kumar
171
- LabOps Team
172
- """
173
- msg.attach(MIMEText(body, 'plain'))
174
-
175
- # Send the email
176
- server.sendmail(EMAIL_FROM, EMAIL_TO, msg.as_string())
177
- logging.info(f"AMC reminder email sent for Device ID {device_id} to {EMAIL_TO}")
178
- email_results.append(f"Sent AMC reminder for Device ID {device_id}")
179
-
180
- server.quit()
181
- return "\n".join(email_results) if email_results else "No emails sent."
182
- except Exception as e:
183
- logging.error(f"Failed to send AMC reminder emails: {str(e)}")
184
- return f"Failed to send AMC reminder emails: {str(e)}"
185
-
186
- # Create Salesforce reports (Usage and AMC Reminders)
187
- def create_salesforce_reports(df):
188
- if sf is None:
189
- return "Salesforce connection not available."
190
- if not LABOPS_REPORTS_FOLDER_ID:
191
- return "Cannot create reports: 'LabOps Reports' folder not found in Salesforce."
192
-
193
- try:
194
- # Usage Report (Summary Report)
195
- usage_report_metadata = {
196
- "reportMetadata": {
197
- "name": f"SmartLog_Usage_Report_{datetime.now().strftime('%Y%m%d_%H%M%S')}",
198
- "reportType": "SmartLog__c",
199
- "reportFormat": "SUMMARY",
200
- "reportFilters": [
201
- {"column": "Status__c", "operator": "equals", "value": "Active"},
202
- {"column": "Timestamp__c", "operator": "greaterOrEqual", "value": "THIS_MONTH"}
203
- ],
204
- "reportColumns": [
205
- {"field": "Device_Id__c"},
206
- {"field": "Log_Type__c"},
207
- {"field": "Status__c"},
208
- {"field": "Timestamp__c"},
209
- {"field": "Usage_Hours__c", "aggregateTypes": ["Sum"]},
210
- {"field": "Downtime__c", "aggregateTypes": ["Sum"]},
211
- {"field": "AMC_Date__c"}
212
- ],
213
- "groupingsDown": [
214
- {"field": "Device_Id__c", "sortOrder": "Asc", "dateGranularity": "None"}
215
- ],
216
- "folderId": LABOPS_REPORTS_FOLDER_ID
217
- }
218
- }
219
- logging.info(f"Creating Usage Report with metadata: {json.dumps(usage_report_metadata, indent=2)}")
220
- usage_result = sf.restful('analytics/reports', method='POST', json=usage_report_metadata)
221
- usage_report_id = usage_result['id']
222
- logging.info(f"Usage Report created: {usage_report_id}")
223
-
224
- # AMC Reminders Report (Tabular Report)
225
- amc_report_metadata = {
226
- "reportMetadata": {
227
- "name": f"SmartLog_AMC_Reminders_{datetime.now().strftime('%Y%m%d_%H%M%S')}",
228
- "reportType": "SmartLog__c",
229
- "reportFormat": "TABULAR",
230
- "reportFilters": [
231
- {"column": "Status__c", "operator": "equals", "value": "Active"},
232
- {"column": "AMC_Date__c", "operator": "greaterOrEqual", "value": "TODAY"},
233
- {"column": "AMC_Date__c", "operator": "lessOrEqual", "value": "NEXT_N_DAYS:30"}
234
- ],
235
- "reportColumns": [
236
- {"field": "Device_Id__c"},
237
- {"field": "AMC_Date__c"},
238
- {"field": "Status__c"}
239
- ],
240
- "folderId": LABOPS_REPORTS_FOLDER_ID
241
- }
242
- }
243
- logging.info(f"Creating AMC Reminders Report with metadata: {json.dumps(amc_report_metadata, indent=2)}")
244
- amc_result = sf.restful('analytics/reports', method='POST', json=amc_report_metadata)
245
- amc_report_id = amc_result['id']
246
- logging.info(f"AMC Reminders Report created: {amc_report_id}")
247
-
248
- return f"Usage Report ID: {usage_report_id}, AMC Reminders Report ID: {amc_report_id}"
249
- except Exception as e:
250
- logging.error(f"Failed to create Salesforce reports: {str(e)}")
251
- return f"Failed to create reports: {str(e)}"
252
-
253
- # Save results to Salesforce SmartLog__c
254
- def save_to_salesforce(df, summary, anomalies, amc_reminders, insights):
255
- if sf is None:
256
- return "Salesforce connection not available."
257
- try:
258
- records = []
259
- current_date = datetime.now()
260
- next_30_days = current_date + timedelta(days=30)
261
- for _, row in df.head(100).iterrows():
262
- # Validate and map picklist values
263
- status = str(row['status'])
264
- log_type = str(row['log_type'])
265
-
266
- # Map Status__c
267
- if status not in status_values:
268
- status = picklist_mapping['Status__c'].get(status.lower(), status_values[0] if status_values else None)
269
- if status is None:
270
- logging.warning(f"Skipping record with invalid Status__c: {row['status']}")
271
- continue
272
-
273
- # Map Log_Type__c
274
- if log_type not in log_type_values:
275
- log_type = picklist_mapping['Log_Type__c'].get(log_type.lower(), log_type_values[0] if log_type_values else None)
276
- if log_type is None:
277
- logging.warning(f"Skipping record with invalid Log_Type__c: {row['log_type']}")
278
- continue
279
-
280
- # Ensure AMC_Date__c is in correct format
281
- amc_date_str = row['amc_date'].strftime('%Y-%m-%d') if pd.notna(row['amc_date']) else None
282
- if amc_date_str:
283
- amc_date = datetime.strptime(amc_date_str, '%Y-%m-%d')
284
- # Log if this record qualifies for AMC Reminders
285
- if status == "Active" and current_date.date() <= amc_date.date() <= next_30_days.date():
286
- logging.info(f"Record qualifies for AMC Reminders: Device ID {row['device_id']}, AMC Date {amc_date_str}")
287
-
288
- record = {
289
- 'Device_Id__c': str(row['device_id'])[:50],
290
- 'Log_Type__c': log_type,
291
- 'Status__c': status,
292
- 'Timestamp__c': row['timestamp'].isoformat() if pd.notna(row['timestamp']) else None,
293
- 'Usage_Hours__c': float(row['usage_hours']) if pd.notna(row['usage_hours']) else 0.0,
294
- 'Downtime__c': float(row['downtime']) if pd.notna(row['downtime']) else 0.0,
295
- 'AMC_Date__c': amc_date_str
296
- }
297
- records.append(record)
298
-
299
- # Bulk insert to reduce API calls
300
- if records:
301
- sf.bulk.SmartLog__c.insert(records)
302
- logging.info(f"Saved {len(records)} records to Salesforce")
303
- return f"Saved {len(records)} records to Salesforce."
304
- except Exception as e:
305
- logging.error(f"Failed to save to Salesforce: {str(e)}")
306
- return f"Failed to save to Salesforce: {str(e)}"
307
-
308
- # Summarize logs
309
- def summarize_logs(df, progress=gr.Progress()):
310
- progress(0.1, "Generating summary report...")
311
- try:
312
- total_devices = df["device_id"].nunique()
313
- most_used = df.groupby("device_id")["usage_hours"].sum().idxmax() if not df.empty else "N/A"
314
- prompt = f"Maintenance logs: {total_devices} devices. Most used: {most_used}."
315
- summary = summarizer(prompt, max_length=50, min_length=10, do_sample=False)[0]["summary_text"]
316
- logging.info("Summary generated successfully")
317
- return summary
318
- except Exception as e:
319
- logging.error(f"Summary generation failed: {str(e)}")
320
- return f"Failed to generate summary: {str(e)}"
321
-
322
- # Anomaly detection
323
- def detect_anomalies(df, progress=gr.Progress()):
324
- progress(0.4, "Detecting anomalies...")
325
- try:
326
- if "usage_hours" not in df.columns or "downtime" not in df.columns:
327
- return "Anomaly detection requires 'usage_hours' and 'downtime' columns."
328
- if len(df) > 1000:
329
- df = df.sample(n=1000, random_state=42)
330
- features = df[["usage_hours", "downtime"]].fillna(0)
331
- iso_forest = IsolationForest(contamination=0.1, random_state=42, n_jobs=-1)
332
- df["anomaly"] = iso_forest.fit_predict(features)
333
- anomalies = df[df["anomaly"] == -1][["device_id", "usage_hours", "downtime", "timestamp"]]
334
- if anomalies.empty:
335
- return "No anomalies detected."
336
- anomaly_lines = ["Detected Anomalies:"]
337
- for _, row in anomalies.head(5).iterrows():
338
- anomaly_lines.append(
339
- f"- Device ID: {row['device_id']}, Usage Hours: {row['usage_hours']}, "
340
- f"Downtime: {row['downtime']}, Timestamp: {row['timestamp']}"
341
- )
342
- return "\n".join(anomaly_lines)
343
- except Exception as e:
344
- logging.error(f"Anomaly detection failed: {str(e)}")
345
- return f"Anomaly detection failed: {str(e)}"
346
-
347
- # AMC reminders (identify records for display and email)
348
- def check_amc_reminders(df, current_date, progress=gr.Progress()):
349
- progress(0.6, "Checking AMC reminders...")
350
- try:
351
- if "device_id" not in df.columns or "amc_date" not in df.columns:
352
- return "AMC reminders require 'device_id' and 'amc_date' columns.", pd.DataFrame()
353
- df["amc_date"] = pd.to_datetime(df["amc_date"], errors='coerce')
354
- current_date = pd.to_datetime(current_date)
355
- df["days_to_amc"] = (df["amc_date"] - current_date).dt.days
356
- reminders = df[(df["days_to_amc"] >= 0) & (df["days_to_amc"] <= 30)][["device_id", "amc_date"]]
357
- if reminders.empty:
358
- return "No AMC reminders due within the next 30 days.", reminders
359
- reminder_lines = ["Upcoming AMC Reminders:"]
360
- for _, row in reminders.head(5).iterrows():
361
- reminder_lines.append(f"- Device ID: {row['device_id']}, AMC Date: {row['amc_date']}")
362
- return "\n".join(reminder_lines), reminders
363
- except Exception as e:
364
- logging.error(f"AMC reminder generation failed: {str(e)}")
365
- return f"AMC reminder generation failed: {str(e)}", pd.DataFrame()
366
-
367
- # Dashboard insights
368
- def generate_dashboard_insights(df, progress=gr.Progress()):
369
- progress(0.8, "Generating dashboard insights...")
370
- try:
371
- total_devices = df["device_id"].nunique()
372
- avg_usage = df["usage_hours"].mean() if "usage_hours" in df.columns else 0
373
- prompt = f"Insights: {total_devices} devices, avg usage {avg_usage:.2f} hours."
374
- insights = summarizer(prompt, max_length=50, min_length=10, do_sample=False)[0]["summary_text"]
375
- return insights
376
- except Exception as e:
377
- logging.error(f"Dashboard insights generation failed: {str(e)}")
378
- return f"Dashboard insights generation failed: {str(e)}"
379
-
380
- # Create usage chart
381
- def create_usage_chart(df, progress=gr.Progress()):
382
- progress(0.9, "Creating usage chart...")
383
- try:
384
- usage_data = df.groupby("device_id")["usage_hours"].sum().reset_index()
385
- if len(usage_data) > 5:
386
- usage_data = usage_data.nlargest(5, "usage_hours")
387
- custom_colors = ['#FF6B6B', '#4ECDC4', '#45B7D1', '#96CEB4']
388
- fig = px.bar(
389
- usage_data,
390
- x="device_id",
391
- y="usage_hours",
392
- title="Usage Hours per Device",
393
- labels={"device_id": "Device ID", "usage_hours": "Usage Hours"},
394
- color="device_id",
395
- color_discrete_sequence=custom_colors
396
- )
397
- fig.update_layout(
398
- title_font_size=16,
399
- margin=dict(l=20, r=20, t=40, b=20),
400
- plot_bgcolor="white",
401
- paper_bgcolor="white",
402
- font=dict(size=12)
403
- )
404
- return fig
405
- except Exception as e:
406
- logging.error(f"Failed to create usage chart: {str(e)}")
407
- return None
408
-
409
- # Generate PDF content
410
- def generate_pdf_content(summary, preview, anomalies, amc_reminders, insights, email_status):
411
- if not reportlab_available:
412
- return None
413
- try:
414
- pdf_path = f"analysis_report_{datetime.now().strftime('%Y%m%d_%H%M%S')}.pdf"
415
- doc = SimpleDocTemplate(pdf_path, pagesize=letter)
416
- styles = getSampleStyleSheet()
417
- story = []
418
-
419
- def safe_paragraph(text, style):
420
- return Paragraph(str(text).replace('\n', '<br/>'), style) if text else Paragraph("", style)
421
-
422
- story.append(Paragraph("LabOps Log Analysis Report", styles['Title']))
423
- story.append(Paragraph(f"Generated on {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}", styles['Normal']))
424
- story.append(Spacer(1, 12))
425
-
426
- story.append(Paragraph("Summary Report", styles['Heading2']))
427
- story.append(safe_paragraph(summary or "No summary available.", styles['Normal']))
428
- story.append(Spacer(1, 12))
429
-
430
- story.append(Paragraph("Log Preview", styles['Heading2']))
431
- story.append(safe_paragraph(preview or "No preview available.", styles['Normal']))
432
- story.append(Spacer(1, 12))
433
-
434
- story.append(Paragraph("Anomaly Detection", styles['Heading2']))
435
- story.append(safe_paragraph(anomalies or "No anomalies detected.", styles['Normal']))
436
- story.append(Spacer(1, 12))
437
-
438
- story.append(Paragraph("AMC Reminders", styles['Heading2']))
439
- story.append(safe_paragraph(amc_reminders or "No AMC reminders.", styles['Normal']))
440
- story.append(Spacer(1, 12))
441
-
442
- story.append(Paragraph("Email Notification Status", styles['Heading2']))
443
- story.append(safe_paragraph(email_status or "No emails sent.", styles['Normal']))
444
- story.append(Spacer(1, 12))
445
-
446
- story.append(Paragraph("Dashboard Insights", styles['Heading2']))
447
- story.append(safe_paragraph(insights or "No insights generated.", styles['Normal']))
448
-
449
- doc.build(story)
450
- logging.info(f"PDF generated at {pdf_path}")
451
- return pdf_path
452
- except Exception as e:
453
- logging.error(f"Failed to generate PDF: {str(e)}")
454
- return None
455
-
456
- # Main Gradio function
457
- async def process_logs(file_obj, progress=gr.Progress()):
458
- try:
459
- progress(0, "Starting file processing...")
460
- if not file_obj:
461
- 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."
462
-
463
- file_name = file_obj.name
464
- logging.info(f"Processing file: {file_name}")
465
-
466
- if not file_name.endswith(".csv"):
467
- return "Please upload a CSV file.", "", None, "", "", "", None, "", "", ""
468
-
469
- required_columns = ["device_id", "log_type", "status", "timestamp", "usage_hours", "downtime", "amc_date"]
470
- dtypes = {
471
- "device_id": "string",
472
- "log_type": "string",
473
- "status": "string",
474
- "usage_hours": "float32",
475
- "downtime": "float32",
476
- "amc_date": "string"
477
  }
478
- df = pd.read_csv(file_obj, dtype=dtypes)
479
- missing_columns = [col for col in required_columns if col not in df.columns]
480
- if missing_columns:
481
- return f"Missing columns: {missing_columns}", None, None, None, None, None, None, None, None, None
482
- df["timestamp"] = pd.to_datetime(df["timestamp"], errors='coerce')
483
- df["amc_date"] = pd.to_datetime(df["amc_date"], errors='coerce')
484
- if df.empty:
485
- return "No data available.", None, None, None, None, None, None, None, None, None
486
-
487
- with ThreadPoolExecutor() as executor:
488
- future_summary = executor.submit(summarize_logs, df)
489
- future_anomalies = executor.submit(detect_anomalies, df)
490
- future_amc = executor.submit(check_amc_reminders, df, datetime.now())
491
- future_insights = executor.submit(generate_dashboard_insights, df)
492
- future_chart = executor.submit(create_usage_chart, df)
493
- future_reports = executor.submit(create_salesforce_reports, df)
494
-
495
- summary = f"Step 1: Summary Report\n{future_summary.result()}"
496
- anomalies = f"Anomaly Detection\n{future_anomalies.result()}"
497
- amc_reminders, reminders_df = future_amc.result() # Get both display text and DataFrame
498
- amc_reminders = f"AMC Reminders\n{amc_reminders}"
499
- insights = f"Dashboard Insights (AI)\n{future_insights.result()}"
500
- chart = future_chart.result()
501
- report_result = future_reports.result()
502
-
503
- preview_lines = ["Step 2: Log Preview (First 5 Rows)"]
504
- for idx, row in df.head(5).iterrows():
505
- preview_lines.append(
506
- f"Row {idx + 1}: Device ID: {row['device_id']}, "
507
- f"Log Type: {row['log_type']}, Status: {row['status']}, "
508
- f"Timestamp: {row['timestamp']}, Usage Hours: {row['usage_hours']}, "
509
- f"Downtime: {row['downtime']}, AMC Date: {row['amc_date']}"
510
- )
511
- preview = "\n".join(preview_lines)
512
-
513
- salesforce_result = save_to_salesforce(df, summary, anomalies, amc_reminders, insights)
514
- email_status = send_amc_reminder_emails(reminders_df)
515
- pdf_file = generate_pdf_content(summary, preview, anomalies, amc_reminders, insights, email_status)
516
-
517
- progress(1.0, "Done!")
518
- return summary, preview, chart, anomalies, amc_reminders, insights, pdf_file, salesforce_result, report_result, email_status
519
- except Exception as e:
520
- logging.error(f"Failed to process file: {str(e)}")
521
- return f"Error: {str(e)}", None, None, None, None, None, None, None, None, None
522
-
523
- # Gradio Interface
524
- try:
525
- logging.info("Initializing Gradio interface...")
526
- with gr.Blocks(css="""
527
- .dashboard-container {border: 1px solid #e0e0e0; padding: 10px; border-radius: 5px;}
528
- .dashboard-title {font-size: 24px; font-weight: bold; margin-bottom: 5px;}
529
- .dashboard-section {margin-bottom: 20px;}
530
- .dashboard-section h3 {font-size: 18px; margin-bottom: 2px;}
531
- .dashboard-section p {margin: 1px 0; line-height: 1.2;}
532
- .dashboard-section ul {margin: 2px 0; padding-left: 20px;}
533
- """) as iface:
534
- gr.Markdown("<h1>LabOps Log Analyzer Dashboard (Hugging Face AI)</h1>")
535
- gr.Markdown("Upload a CSV file to analyze, generate Salesforce reports, and send AMC reminder emails.")
536
-
537
- with gr.Row():
538
- with gr.Column(scale=1):
539
- file_input = gr.File(label="Upload Logs (CSV)", file_types=[".csv"])
540
- submit_button = gr.Button("Analyze", variant="primary")
541
-
542
- with gr.Column(scale=2):
543
- with gr.Group(elem_classes="dashboard-container"):
544
- gr.Markdown("<div class='dashboard-title'>Analysis Results</div>")
545
-
546
- with gr.Group(elem_classes="dashboard-section"):
547
- gr.Markdown("### Step 1: Summary Report")
548
- summary_output = gr.Markdown()
549
-
550
- with gr.Group(elem_classes="dashboard-section"):
551
- gr.Markdown("### Step 2: Log Preview")
552
- preview_output = gr.Markdown()
553
-
554
- with gr.Group(elem_classes="dashboard-section"):
555
- gr.Markdown("### Step 3: Usage Chart")
556
- chart_output = gr.Plot()
557
-
558
- with gr.Group(elem_classes="dashboard-section"):
559
- gr.Markdown("### Step 4: Anomaly Detection")
560
- anomaly_output = gr.Markdown()
561
-
562
- with gr.Group(elem_classes="dashboard-section"):
563
- gr.Markdown("### Step 5: AMC Reminders")
564
- amc_output = gr.Markdown()
565
-
566
- with gr.Group(elem_classes="dashboard-section"):
567
- gr.Markdown("### Step 6: Insights (AI)")
568
- insights_output = gr.Markdown()
569
-
570
- with gr.Group(elem_classes="dashboard-section"):
571
- gr.Markdown("### Step 7: Email Notification Status")
572
- email_output = gr.Markdown()
573
-
574
- with gr.Group(elem_classes="dashboard-section"):
575
- gr.Markdown("### Salesforce Integration")
576
- salesforce_output = gr.Markdown()
577
-
578
- with gr.Group(elem_classes="dashboard-section"):
579
- gr.Markdown("### Salesforce Reports")
580
- report_output = gr.Markdown()
581
-
582
- with gr.Group(elem_classes="dashboard-section"):
583
- gr.Markdown("### Download Report")
584
- pdf_output = gr.File(label="Download Analysis Report as PDF")
585
-
586
- submit_button.click(
587
- fn=process_logs,
588
- inputs=[file_input],
589
- outputs=[
590
- summary_output,
591
- preview_output,
592
- chart_output,
593
- anomaly_output,
594
- amc_output,
595
- insights_output,
596
- pdf_output,
597
- salesforce_output,
598
- report_output,
599
- email_output
600
- ]
601
- )
602
-
603
- logging.info("Gradio interface initialized successfully")
604
- except Exception as e:
605
- logging.error(f"Failed to initialize Gradio interface: {str(e)}")
606
- raise e
607
-
608
- if __name__ == "__main__":
609
- try:
610
- logging.info("Launching Gradio interface...")
611
- iface.launch(server_name="0.0.0.0", server_port=7860, debug=True, share=False)
612
- logging.info("Gradio interface launched successfully")
613
- except Exception as e:
614
- logging.error(f"Failed to launch Gradio interface: {str(e)}")
615
- print(f"Error launching app: {str(e)}")
616
- raise e
 
1
+
 
 
2
  import gradio as gr
3
  import pandas as pd
 
 
4
  import plotly.express as px
5
+ import numpy as np
6
+ import logging
7
  from sklearn.ensemble import IsolationForest
 
 
 
8
  from simple_salesforce import Salesforce
9
+ from transformers import pipeline
10
+ from datetime import datetime
11
  import os
12
+ from reportlab.platypus import SimpleDocTemplate, Paragraph, Spacer
13
+ from reportlab.lib.pagesizes import letter
14
+ from reportlab.lib.styles import getSampleStyleSheet
15
  import smtplib
16
  from email.mime.text import MIMEText
17
  from email.mime.multipart import MIMEMultipart
18
 
19
+ # Setup logging
20
+ logging.basicConfig(level=logging.INFO)
21
+
22
+ # Environment Variables
23
+ SF_USERNAME = os.getenv("SF_USERNAME")
24
+ SF_PASSWORD = os.getenv("SF_PASSWORD")
25
+ SF_SECURITY_TOKEN = os.getenv("SF_SECURITY_TOKEN")
26
+ SMTP_USERNAME = os.getenv('SMTP_USERNAME', 'harishkumarr@sathkrutha.com')
27
+ SMTP_PASSWORD = os.getenv('SMTP_PASSWORD', 'your_app_password_here')
28
+ EMAIL_FROM = SMTP_USERNAME
29
+ EMAIL_TO = os.getenv('EMAIL_TO', 'sanjaybhargavneela@sathkrutha.com')
30
+
31
+ # Hugging Face Pipeline
32
+ summarizer = pipeline("summarization")
33
+
34
+ # Connect to Salesforce
35
+ sf = Salesforce(username=SF_USERNAME, password=SF_PASSWORD, security_token=SF_SECURITY_TOKEN)
36
+
37
+ def detect_anomalies(df):
38
+ df['Usage_Hours__c'] = pd.to_numeric(df['Usage_Hours__c'], errors='coerce').fillna(0)
39
+ model = IsolationForest(contamination=0.1)
40
+ df['anomaly'] = model.fit_predict(df[['Usage_Hours__c']])
41
+ df['anomaly_label'] = df['anomaly'].apply(lambda x: 'Anomaly' if x == -1 else 'Normal')
42
+ return df
43
+
44
+ def summarize_logs(df):
45
+ joined_text = "\n".join([f"Device {row['Device_Id__c']} used for {row['Usage_Hours__c']} hrs with status {row['Status__c']}" for _, row in df.iterrows()])
46
+ summary = summarizer(joined_text, max_length=100, min_length=30, do_sample=False)[0]['summary_text']
47
+ return summary
48
+
49
+ def save_to_salesforce(df):
50
+ records = []
51
+ for _, row in df.iterrows():
52
+ record = {
53
+ "Device_Id__c": row.get("Device_Id__c"),
54
+ "Usage_Hours__c": float(row.get("Usage_Hours__c", 0)),
55
+ "Downtime__c": float(row.get("Downtime__c", 0)),
56
+ "Timestamp__c": row.get("Timestamp__c"),
57
+ "Status__c": row.get("Status__c"),
58
+ "Log_Type__c": row.get("Log_Type__c"),
59
+ "AMC_Date__c": row.get("AMC_Date__c"),
60
+ "Name": row.get("Device_Id__c") + "-" + datetime.now().strftime("%Y%m%d%H%M%S")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
61
  }
62
+ records.append(record)
63
+ result = sf.bulk.SmartLog__c.insert(records)
64
+ logging.info(f"Inserted {len(result)} records into Salesforce")
65
+ return result
66
+
67
+ def generate_pdf(summary):
68
+ filename = "/tmp/audit_summary.pdf"
69
+ doc = SimpleDocTemplate(filename, pagesize=letter)
70
+ styles = getSampleStyleSheet()
71
+ elements = [Paragraph("Smart Audit Log Summary", styles['Title']), Spacer(1, 12)]
72
+ elements.append(Paragraph(summary, styles['BodyText']))
73
+ doc.build(elements)
74
+ return filename
75
+
76
+ def send_email_alert(subject, body):
77
+ msg = MIMEMultipart()
78
+ msg['From'] = EMAIL_FROM
79
+ msg['To'] = EMAIL_TO
80
+ msg['Subject'] = subject
81
+ msg.attach(MIMEText(body, 'plain'))
82
+
83
+ with smtplib.SMTP_SSL('smtp.gmail.com', 465) as server:
84
+ server.login(SMTP_USERNAME, SMTP_PASSWORD)
85
+ server.send_message(msg)
86
+
87
+ def process_logs(file):
88
+ df = pd.read_csv(file)
89
+ df = detect_anomalies(df)
90
+ summary = summarize_logs(df)
91
+ pdf_path = generate_pdf(summary)
92
+ save_to_salesforce(df)
93
+
94
+ anomalies = df[df['anomaly_label'] == 'Anomaly']
95
+ if not anomalies.empty:
96
+ alert_msg = f"Anomalies Detected in Devices:\n" + "\n".join(anomalies['Device_Id__c'].values)
97
+ send_email_alert("Anomaly Alert", alert_msg)
98
+
99
+ fig = px.scatter(df, x="Device_Id__c", y="Usage_Hours__c", color="anomaly_label", title="Device Usage with Anomalies")
100
+ return df, summary, fig, pdf_path
101
+
102
+ with gr.Blocks() as app:
103
+ gr.Markdown("### LabOps Log Analyzer Dashboard")
104
+ file_input = gr.File(label="Upload LabOps CSV", file_types=[".csv"])
105
+ df_output = gr.Dataframe()
106
+ summary_output = gr.Textbox(label="Summary")
107
+ graph_output = gr.Plot()
108
+ pdf_download = gr.File(label="Download PDF Summary")
109
+
110
+ file_input.change(fn=process_logs, inputs=file_input, outputs=[df_output, summary_output, graph_output, pdf_download])
111
+
112
+ app.launch()