RathodHarish commited on
Commit
1e2e9c7
·
verified ·
1 Parent(s): c7f5120

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +171 -157
app.py CHANGED
@@ -1,154 +1,70 @@
 
 
 
1
  import gradio as gr
2
  import pandas as pd
3
  from datetime import datetime
4
  import logging
5
  import plotly.express as px
6
- from sklearn.ensemble import IsolationForest # For anomaly detection
7
  from transformers import pipeline
8
- import torch # For GPU availability check
9
- from simple_salesforce import Salesforce # For Salesforce connection
 
 
 
 
 
 
 
 
 
 
10
 
11
  # Configure logging for debugging
12
  logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
13
 
14
- # Salesforce credentials (replace with your actual credentials or use environment variables)
15
- SALESFORCE_USERNAME = "your_username"
16
- SALESFORCE_PASSWORD = "your_password"
17
- SALESFORCE_SECURITY_TOKEN = "your_security_token"
18
- SALESFORCE_DOMAIN = "login" # Use "test" for sandbox, "login" for production
19
-
20
  # Preload Hugging Face summarization model at startup
21
  logging.info("Preloading Hugging Face model...")
22
  try:
23
  device = 0 if torch.cuda.is_available() else -1
24
- summarizer = pipeline("summarization", model="sshleifer/distilbart-cnn-12-6", device=device) # Lighter model
25
  logging.info(f"Hugging Face model preloaded successfully on device: {'GPU' if device == 0 else 'CPU'}")
26
  except Exception as e:
27
  logging.error(f"Failed to preload model: {str(e)}")
28
  raise e
29
 
30
- # Connect to Salesforce
31
- def connect_to_salesforce():
32
- try:
33
- sf = Salesforce(
34
- username=SALESFORCE_USERNAME,
35
- password=SALESFORCE_PASSWORD,
36
- security_token=SALESFORCE_SECURITY_TOKEN,
37
- domain=SALESFORCE_DOMAIN
38
- )
39
- logging.info("Successfully connected to Salesforce")
40
- return sf
41
- except Exception as e:
42
- logging.error(f"Failed to connect to Salesforce: {str(e)}")
43
- raise e
44
-
45
- # Fetch data from Salesforce
46
- def fetch_salesforce_data(sf, row_limit=10000, progress=gr.Progress()):
47
- progress(0.05, "Fetching data from Salesforce...")
48
- try:
49
- # Query Salesforce for LabEquipmentLog__c object
50
- query = """
51
- SELECT Device_ID__c, Log_Type__c, Status__c, Timestamp__c,
52
- Usage_Hours__c, Downtime__c, AMC_Date__c
53
- FROM LabEquipmentLog__c
54
- LIMIT {}
55
- """.format(row_limit)
56
- result = sf.query_all(query)
57
- records = result["records"]
58
-
59
- # Convert to DataFrame
60
- df = pd.DataFrame(records)
61
- df = df.rename(columns={
62
- "Device_ID__c": "device_id",
63
- "Log_Type__c": "log_type",
64
- "Status__c": "status",
65
- "Timestamp__c": "timestamp",
66
- "Usage_Hours__c": "usage_hours",
67
- "Downtime__c": "downtime",
68
- "AMC_Date__c": "amc_date"
69
- })
70
-
71
- # Ensure proper data types
72
- df["timestamp"] = pd.to_datetime(df["timestamp"], errors='coerce')
73
- df["amc_date"] = pd.to_datetime(df["amc_date"], errors='coerce')
74
- df["usage_hours"] = df["usage_hours"].astype("float32", errors='ignore')
75
- df["downtime"] = df["downtime"].astype("float32", errors='ignore')
76
- df["device_id"] = df["device_id"].astype("string")
77
-
78
- logging.info(f"Fetched {len(df)} records from Salesforce")
79
- return df
80
- except Exception as e:
81
- logging.error(f"Failed to fetch Salesforce data: {str(e)}")
82
- raise e
83
-
84
- # Load data from CSV file
85
- def load_csv_data(file_obj, row_limit=10000, progress=gr.Progress()):
86
- progress(0.05, "Loading CSV file...")
87
- try:
88
- file_name = file_obj.name if hasattr(file_obj, 'name') else file_obj
89
- logging.info(f"Processing CSV file: {file_name}")
90
-
91
- if not file_name.endswith(".csv"):
92
- logging.error("Unsupported file format")
93
- raise ValueError("Unsupported file format. Please upload a CSV file.")
94
-
95
- usecols = ["device_id", "log_type", "status", "timestamp", "usage_hours", "downtime", "amc_date"]
96
- dtypes = {
97
- "device_id": "string",
98
- "log_type": "string",
99
- "status": "string",
100
- "usage_hours": "float32",
101
- "downtime": "float32",
102
- "amc_date": "string"
103
- }
104
- df = pd.read_csv(file_name, usecols=usecols, dtype=dtypes, nrows=row_limit)
105
-
106
- # Convert timestamps
107
- df["timestamp"] = pd.to_datetime(df["timestamp"], errors='coerce')
108
- df["amc_date"] = pd.to_datetime(df["amc_date"], errors='coerce')
109
-
110
- logging.info(f"File loaded successfully with {len(df)} rows (limited to {row_limit} rows)")
111
- return df
112
- except Exception as e:
113
- logging.error(f"Failed to load CSV: {str(e)}")
114
- raise e
115
-
116
  # Format summary prompt and generate report
117
  def summarize_logs(df, progress=gr.Progress()):
118
  progress(0.1, "Generating summary report...")
119
  try:
120
  total_devices = df["device_id"].nunique()
121
  most_used = df.groupby("device_id")["usage_hours"].sum().idxmax() if not df.empty else "N/A"
122
-
123
  prompt = f"Maintenance logs: {total_devices} devices. Most used: {most_used}."
124
  summary = summarizer(prompt, max_length=50, min_length=10, do_sample=False)[0]["summary_text"]
125
  logging.info("Summary generated successfully")
126
  return summary
127
  except Exception as e:
128
  logging.error(f"Summary generation failed: {str(e)}")
129
- return "Failed to generate summary."
130
 
131
- # Anomaly Detection using Isolation Forest with sampling for large datasets
132
  def detect_anomalies(df, progress=gr.Progress()):
133
  progress(0.4, "Detecting anomalies...")
134
  try:
135
  if "usage_hours" not in df.columns or "downtime" not in df.columns:
136
  logging.warning("Required columns for anomaly detection not found")
137
  return "Anomaly detection requires 'usage_hours' and 'downtime' columns."
138
-
139
  if len(df) > 5000:
140
  df = df.sample(n=5000, random_state=42)
141
  logging.info("Sampled data for anomaly detection to 5,000 rows")
142
-
143
  features = df[["usage_hours", "downtime"]].fillna(0)
144
  iso_forest = IsolationForest(contamination=0.1, random_state=42, n_jobs=-1)
145
  df["anomaly"] = iso_forest.fit_predict(features)
146
-
147
  anomalies = df[df["anomaly"] == -1][["device_id", "usage_hours", "downtime", "timestamp"]]
148
  if anomalies.empty:
149
  return "No anomalies detected."
150
-
151
- anomaly_lines = ["**Detected Anomalies:**"]
152
  for idx, row in anomalies.head(5).iterrows():
153
  anomaly_lines.append(f"- Device ID: {row['device_id']}, Usage Hours: {row['usage_hours']}, Downtime: {row['downtime']}, Timestamp: {row['timestamp']}")
154
  anomaly_list = "\n".join(anomaly_lines)
@@ -165,17 +81,13 @@ def check_amc_reminders(df, current_date, progress=gr.Progress()):
165
  if "device_id" not in df.columns or "amc_date" not in df.columns:
166
  logging.warning("Required columns for AMC reminders not found")
167
  return "AMC reminders require 'device_id' and 'amc_date' columns."
168
-
169
  df["amc_date"] = pd.to_datetime(df["amc_date"], errors='coerce')
170
  current_date = pd.to_datetime(current_date)
171
-
172
  df["days_to_amc"] = (df["amc_date"] - current_date).dt.days
173
  reminders = df[(df["days_to_amc"] >= 0) & (df["days_to_amc"] <= 30)][["device_id", "amc_date"]]
174
-
175
  if reminders.empty:
176
  return "No AMC reminders due within the next 30 days."
177
-
178
- reminder_lines = ["**Upcoming AMC Reminders:**"]
179
  for idx, row in reminders.head(5).iterrows():
180
  reminder_lines.append(f"- Device ID: {row['device_id']}, AMC Date: {row['amc_date']}")
181
  reminder_list = "\n".join(reminder_lines)
@@ -185,7 +97,7 @@ def check_amc_reminders(df, current_date, progress=gr.Progress()):
185
  logging.error(f"AMC reminder generation failed: {str(e)}")
186
  return f"AMC reminder generation failed: {str(e)}"
187
 
188
- # Dashboard Insights (AI-generated executive-level insights)
189
  def generate_dashboard_insights(df, progress=gr.Progress()):
190
  progress(0.8, "Generating dashboard insights...")
191
  try:
@@ -207,7 +119,6 @@ def create_usage_chart(df, progress=gr.Progress()):
207
  if len(usage_data) > 5:
208
  usage_data = usage_data.nlargest(5, "usage_hours")
209
  logging.info("Limited chart data to top 5 devices")
210
-
211
  custom_colors = ['#FF6B6B', '#4ECDC4', '#45B7D1', '#96CEB4']
212
  fig = px.bar(
213
  usage_data,
@@ -230,113 +141,216 @@ def create_usage_chart(df, progress=gr.Progress()):
230
  logging.error(f"Failed to create usage chart: {str(e)}")
231
  return None
232
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
233
  # Main Gradio function
234
- async def process_logs(file_obj=None, progress=gr.Progress()):
235
  try:
236
- progress(0, "Starting data processing...")
 
 
 
 
 
 
237
 
238
- # Load data: prioritize CSV if uploaded, otherwise fetch from Salesforce
239
- if file_obj:
240
- df = load_csv_data(file_obj, row_limit=10000, progress=progress)
241
- else:
242
- progress(0.05, "No CSV uploaded, fetching from Salesforce...")
243
- sf = connect_to_salesforce()
244
- df = fetch_salesforce_data(sf, row_limit=10000, progress=progress)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
245
 
246
  if df.empty:
247
- logging.warning("No data available")
248
- return "No data available.", "No data to preview.", None, "No anomalies detected.", "No AMC reminders.", "No insights generated."
249
 
250
- # Step 1: Summary Report
251
- progress(0.2, "Generating summary...")
252
- summary = f"**Step 1: Summary Report** \n{summarize_logs(df, progress)}"
253
 
254
  # Step 2: Log Preview
255
- progress(0.3, "Previewing logs...")
256
  if not df.empty:
257
- preview_lines = ["**Step 2: Log Preview (First 5 Rows)**"]
258
- for idx, row in df.head().iterrows():
259
- preview_lines.append(f"**Row {idx + 1}:** Device ID: {row['device_id']}, Timestamp: {row['timestamp']}, Usage Hours: {row['usage_hours']}, Downtime: {row['downtime']}, AMC Date: {row['amc_date']}, Log Type: {row['log_type']}, Status: {row['status']}")
 
 
 
 
 
260
  preview = "\n".join(preview_lines)
261
  else:
262
- preview = "**Step 2: Log Preview** \nNo data available."
263
 
264
  # Step 3: Usage Chart
265
- chart = create_usage_chart(df, progress)
 
266
 
267
  # Step 4: Anomaly Detection
268
- anomalies = f"**Step 3: Anomaly Detection** \n{detect_anomalies(df, progress)}"
 
269
 
270
  # Step 5: AMC Reminders
271
- amc_reminders = f"**Step 4: AMC Reminders** \n{check_amc_reminders(df, datetime.now(), progress)}"
 
 
 
 
 
272
 
273
- # Step 6: Dashboard Insights
274
- insights = f"**Step 5: Dashboard Insights (AI)** \n{generate_dashboard_insights(df, progress)}"
 
 
 
 
 
275
 
276
- progress(1.0, "Processing complete!")
277
- return summary, preview, chart, anomalies, amc_reminders, insights
278
  except Exception as e:
279
- logging.error(f"Failed to process data: {str(e)}")
280
- return f"Failed to process data: {str(e)}", None, None, None, None, None
281
 
282
- # Gradio Interface with Step-by-Step Layout
283
  try:
284
- logging.info("Initializing Gradio Blocks interface...")
285
  with gr.Blocks(css="""
286
- .dashboard-container {border: 1px solid #e0e0e0; padding: 10px; border-radius: 5px; background-color: #f9f9f9;}
287
  .dashboard-title {font-size: 24px; font-weight: bold; margin-bottom: 5px;}
288
- .dashboard-section {margin-bottom: 5px;}
289
  .dashboard-section h3 {font-size: 18px; margin-bottom: 2px;}
290
  .dashboard-section p {margin: 1px 0; line-height: 1.2;}
291
- .dashboard-section li {margin: 1px 0; line-height: 1.2;}
292
  .dashboard-section ul {margin: 2px 0; padding-left: 20px;}
293
  """) as iface:
294
- gr.Markdown("<h1>LabOps Log Analyzer Dashboard (Salesforce + Hugging Face AI)</h1>")
295
- gr.Markdown("Upload a CSV file or fetch lab equipment logs from Salesforce to analyze usage.")
296
 
297
  with gr.Row():
298
  with gr.Column(scale=1):
299
  file_input = gr.File(label="Upload Logs (CSV)", file_types=[".csv"])
300
- submit_button = gr.Button("Analyze Data", variant="primary")
301
-
302
  with gr.Column(scale=2):
303
  with gr.Group(elem_classes="dashboard-container"):
304
  gr.Markdown("<div class='dashboard-title'>Analysis Results (Step-by-Step)</div>")
305
-
306
  # Step 1: Summary Report
307
  with gr.Group(elem_classes="dashboard-section"):
308
  gr.Markdown("### Step 1: Summary Report")
309
  summary_output = gr.Markdown()
310
-
311
  # Step 2: Log Preview
312
  with gr.Group(elem_classes="dashboard-section"):
313
  gr.Markdown("### Step 2: Log Preview")
314
  preview_output = gr.Markdown()
315
-
316
  # Step 3: Usage Chart
317
  with gr.Group(elem_classes="dashboard-section"):
318
  gr.Markdown("### Step 3: Usage Chart")
319
  chart_output = gr.Plot()
320
-
321
  # Step 4: Anomaly Detection
322
  with gr.Group(elem_classes="dashboard-section"):
323
  gr.Markdown("### Step 4: Anomaly Detection")
324
  anomaly_output = gr.Markdown()
325
-
326
  # Step 5: AMC Reminders
327
  with gr.Group(elem_classes="dashboard-section"):
328
  gr.Markdown("### Step 5: AMC Reminders")
329
  amc_output = gr.Markdown()
330
-
331
  # Step 6: Dashboard Insights
332
  with gr.Group(elem_classes="dashboard-section"):
333
- gr.Markdown("### Step 6: Dashboard Insights (AI)")
334
  insights_output = gr.Markdown()
 
 
 
 
 
335
 
336
  submit_button.click(
337
  fn=process_logs,
338
  inputs=[file_input],
339
- outputs=[summary_output, preview_output, chart_output, anomaly_output, amc_output, insights_output]
340
  )
341
 
342
  logging.info("Gradio interface initialized successfully")
@@ -352,4 +366,4 @@ if __name__ == "__main__":
352
  except Exception as e:
353
  logging.error(f"Failed to launch Gradio interface: {str(e)}")
354
  print(f"Error launching app: {str(e)}")
355
- raise e
 
1
+ """
2
+ LabOps Log Analyzer Dashboard with CSV file upload and optional PDF generation
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
10
  from transformers import pipeline
11
+ import torch
12
+
13
+ # Try to import reportlab for PDF generation
14
+ try:
15
+ from reportlab.lib.pagesizes import letter
16
+ from reportlab.platypus import SimpleDocTemplate, Paragraph, Spacer
17
+ from reportlab.lib.styles import getSampleStyleSheet
18
+ reportlab_available = True
19
+ logging.info("reportlab module successfully imported")
20
+ except ImportError:
21
+ logging.warning("reportlab module not found. PDF generation will be disabled. Install with: pip install reportlab")
22
+ reportlab_available = False
23
 
24
  # Configure logging for debugging
25
  logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
26
 
 
 
 
 
 
 
27
  # Preload Hugging Face summarization model at startup
28
  logging.info("Preloading Hugging Face model...")
29
  try:
30
  device = 0 if torch.cuda.is_available() else -1
31
+ summarizer = pipeline("summarization", model="sshleifer/distilbart-cnn-12-6", device=device)
32
  logging.info(f"Hugging Face model preloaded successfully on device: {'GPU' if device == 0 else 'CPU'}")
33
  except Exception as e:
34
  logging.error(f"Failed to preload model: {str(e)}")
35
  raise e
36
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
37
  # Format summary prompt and generate report
38
  def summarize_logs(df, progress=gr.Progress()):
39
  progress(0.1, "Generating summary report...")
40
  try:
41
  total_devices = df["device_id"].nunique()
42
  most_used = df.groupby("device_id")["usage_hours"].sum().idxmax() if not df.empty else "N/A"
 
43
  prompt = f"Maintenance logs: {total_devices} devices. Most used: {most_used}."
44
  summary = summarizer(prompt, max_length=50, min_length=10, do_sample=False)[0]["summary_text"]
45
  logging.info("Summary generated successfully")
46
  return summary
47
  except Exception as e:
48
  logging.error(f"Summary generation failed: {str(e)}")
49
+ return f"Failed to generate summary: {str(e)}"
50
 
51
+ # Anomaly Detection using Isolation Forest
52
  def detect_anomalies(df, progress=gr.Progress()):
53
  progress(0.4, "Detecting anomalies...")
54
  try:
55
  if "usage_hours" not in df.columns or "downtime" not in df.columns:
56
  logging.warning("Required columns for anomaly detection not found")
57
  return "Anomaly detection requires 'usage_hours' and 'downtime' columns."
 
58
  if len(df) > 5000:
59
  df = df.sample(n=5000, random_state=42)
60
  logging.info("Sampled data for anomaly detection to 5,000 rows")
 
61
  features = df[["usage_hours", "downtime"]].fillna(0)
62
  iso_forest = IsolationForest(contamination=0.1, random_state=42, n_jobs=-1)
63
  df["anomaly"] = iso_forest.fit_predict(features)
 
64
  anomalies = df[df["anomaly"] == -1][["device_id", "usage_hours", "downtime", "timestamp"]]
65
  if anomalies.empty:
66
  return "No anomalies detected."
67
+ anomaly_lines = ["Detected Anomalies:"]
 
68
  for idx, row in anomalies.head(5).iterrows():
69
  anomaly_lines.append(f"- Device ID: {row['device_id']}, Usage Hours: {row['usage_hours']}, Downtime: {row['downtime']}, Timestamp: {row['timestamp']}")
70
  anomaly_list = "\n".join(anomaly_lines)
 
81
  if "device_id" not in df.columns or "amc_date" not in df.columns:
82
  logging.warning("Required columns for AMC reminders not found")
83
  return "AMC reminders require 'device_id' and 'amc_date' columns."
 
84
  df["amc_date"] = pd.to_datetime(df["amc_date"], errors='coerce')
85
  current_date = pd.to_datetime(current_date)
 
86
  df["days_to_amc"] = (df["amc_date"] - current_date).dt.days
87
  reminders = df[(df["days_to_amc"] >= 0) & (df["days_to_amc"] <= 30)][["device_id", "amc_date"]]
 
88
  if reminders.empty:
89
  return "No AMC reminders due within the next 30 days."
90
+ reminder_lines = ["Upcoming AMC Reminders:"]
 
91
  for idx, row in reminders.head(5).iterrows():
92
  reminder_lines.append(f"- Device ID: {row['device_id']}, AMC Date: {row['amc_date']}")
93
  reminder_list = "\n".join(reminder_lines)
 
97
  logging.error(f"AMC reminder generation failed: {str(e)}")
98
  return f"AMC reminder generation failed: {str(e)}"
99
 
100
+ # Dashboard Insights (AI-generated)
101
  def generate_dashboard_insights(df, progress=gr.Progress()):
102
  progress(0.8, "Generating dashboard insights...")
103
  try:
 
119
  if len(usage_data) > 5:
120
  usage_data = usage_data.nlargest(5, "usage_hours")
121
  logging.info("Limited chart data to top 5 devices")
 
122
  custom_colors = ['#FF6B6B', '#4ECDC4', '#45B7D1', '#96CEB4']
123
  fig = px.bar(
124
  usage_data,
 
141
  logging.error(f"Failed to create usage chart: {str(e)}")
142
  return None
143
 
144
+ # Generate PDF content using reportlab if available
145
+ def generate_pdf_content(summary, preview, anomalies, amc_reminders, insights):
146
+ if not reportlab_available:
147
+ logging.warning("Skipping PDF generation: reportlab not installed")
148
+ return None
149
+ try:
150
+ pdf_path = "analysis_report.pdf"
151
+ doc = SimpleDocTemplate(pdf_path, pagesize=letter)
152
+ styles = getSampleStyleSheet()
153
+ story = []
154
+
155
+ # Title
156
+ story.append(Paragraph("LabOps Log Analysis Report", styles['Title']))
157
+ story.append(Paragraph(f"Generated on {datetime.now().strftime('%Y-%m-%d')}", styles['Normal']))
158
+ story.append(Spacer(1, 12))
159
+
160
+ # Summary Report
161
+ story.append(Paragraph("Summary Report", styles['Heading2']))
162
+ for line in summary.split('\n'):
163
+ story.append(Paragraph(line, styles['Normal']))
164
+ story.append(Spacer(1, 12))
165
+
166
+ # Log Preview
167
+ story.append(Paragraph("Log Preview", styles['Heading2']))
168
+ for line in preview.split('\n'):
169
+ story.append(Paragraph(line, styles['Normal']))
170
+ story.append(Spacer(1, 12))
171
+
172
+ # Anomaly Detection
173
+ story.append(Paragraph("Anomaly Detection", styles['Heading2']))
174
+ for line in anomalies.split('\n'):
175
+ story.append(Paragraph(line, styles['Normal']))
176
+ story.append(Spacer(1, 12))
177
+
178
+ # AMC Reminders
179
+ story.append(Paragraph("AMC Reminders", styles['Heading2']))
180
+ for line in amc_reminders.split('\n'):
181
+ story.append(Paragraph(line, styles['Normal']))
182
+ story.append(Spacer(1, 12))
183
+
184
+ # Dashboard Insights
185
+ story.append(Paragraph("Dashboard Insights", styles['Heading2']))
186
+ for line in insights.split('\n'):
187
+ story.append(Paragraph(line, styles['Normal']))
188
+
189
+ # Build PDF
190
+ doc.build(story)
191
+ logging.info(f"PDF generated successfully at {pdf_path}")
192
+ return pdf_path
193
+ except Exception as e:
194
+ logging.error(f"Failed to generate PDF: {str(e)}")
195
+ return None
196
+
197
  # Main Gradio function
198
+ async def process_logs(file_obj, progress=gr.Progress()):
199
  try:
200
+ progress(0, "Starting file processing...")
201
+ if not file_obj:
202
+ logging.warning("No file uploaded, returning empty results")
203
+ return "No file uploaded.", "No data to preview.", None, "No anomalies detected.", "No AMC reminders.", "No insights generated.", None
204
+
205
+ file_name = file_obj.name
206
+ logging.info(f"Processing file: {file_name}")
207
 
208
+ if not file_name.endswith(".csv"):
209
+ logging.error("Unsupported file format")
210
+ return "Unsupported file format. Please upload a CSV file.", "", None, "", "", "", None
211
+
212
+ try:
213
+ required_columns = ["device_id", "log_type", "status", "timestamp", "usage_hours", "downtime", "amc_date"]
214
+ dtypes = {
215
+ "device_id": "string",
216
+ "log_type": "string",
217
+ "status": "string",
218
+ "usage_hours": "float32",
219
+ "downtime": "float32",
220
+ "amc_date": "string"
221
+ }
222
+ df = pd.read_csv(file_obj, dtype=dtypes)
223
+ # Check for required columns
224
+ missing_columns = [col for col in required_columns if col not in df.columns]
225
+ if missing_columns:
226
+ logging.error(f"Missing columns: {missing_columns}")
227
+ return f"Missing required columns: {missing_columns}", None, None, None, None, None, None
228
+ logging.info(f"File loaded successfully with {len(df)} rows")
229
+ except Exception as e:
230
+ logging.error(f"Failed to load CSV: {str(e)}")
231
+ return f"Failed to load CSV: {str(e)}", None, None, None, None, None, None
232
+
233
+ try:
234
+ df["timestamp"] = pd.to_datetime(df["timestamp"], errors='coerce')
235
+ except Exception as e:
236
+ logging.error(f"Date conversion failed: {str(e)}")
237
+ return f"Failed to convert timestamp to datetime: {str(e)}", None, None, None, None, None, None
238
 
239
  if df.empty:
240
+ logging.warning("No data provided in the file")
241
+ return "No data available in provided.", None, None, None, None, None, None
242
 
243
+ # Step 1: Summary
244
+ progress(0.2, "Summary...")
245
+ summary = f"Step 1: Summary Report\n{summarize_logs(df)}"
246
 
247
  # Step 2: Log Preview
248
+ progress(0.3, "Previewing...")
249
  if not df.empty:
250
+ preview_lines = ["Step 2: Log Preview (First 5 Rows)"]
251
+ for idx, row in df.head(5).iterrows():
252
+ preview_lines.append(
253
+ f"Row {idx + 1}: Device ID: {row['device_id']}, "
254
+ f"Log Type: {row['log_type']}, Status: {row['status']}, "
255
+ f"Timestamp: {row['timestamp']}, Usage Hours: {row['usage_hours']}, "
256
+ f"Downtime: {row['downtime']}, AMC Date: {row['amc_date']}"
257
+ )
258
  preview = "\n".join(preview_lines)
259
  else:
260
+ preview = "Step 2: Log Preview\nNo data available."
261
 
262
  # Step 3: Usage Chart
263
+ progress(0.5, "Chart...")
264
+ chart = create_usage_chart(df)
265
 
266
  # Step 4: Anomaly Detection
267
+ progress(0.7, "Anomalies...")
268
+ anomalies = f"Anomaly Detection\n{detect_anomalies(df)}"
269
 
270
  # Step 5: AMC Reminders
271
+ progress(0.8, "AMC...")
272
+ amc_reminders = f"AMC Reminders\n{check_amc_reminders(df, datetime.now())}"
273
+
274
+ # Step 6: Insights
275
+ progress(0.9, "Insights...")
276
+ insights = f"Dashboard Insights (AI)\n{generate_dashboard_insights(df)}"
277
 
278
+ # Generate PDF if available
279
+ progress(0.95, "PDF...")
280
+ pdf_file = generate_pdf_content(summary, preview, anomalies, amc_reminders, insights) if reportlab_available else None
281
+ if pdf_file is None and reportlab_available:
282
+ logging.warning("PDF generation failed")
283
+ elif pdf_file is None:
284
+ logging.info("PDF skipped: no reportlab")
285
 
286
+ progress(1.0, "Done!")
287
+ return summary, preview, chart, anomalies, amc_reminders, insights, pdf_file
288
  except Exception as e:
289
+ logging.error(f"Failed to process file: {str(e)}")
290
+ return f"Error processing file: {str(e)}", None, None, None, None, None, None
291
 
292
+ # Gradio Interface
293
  try:
294
+ logging.info("Initializing Gradio interface...")
295
  with gr.Blocks(css="""
296
+ .dashboard-container {border: 1px solid #e0e0e0; padding: 10px; border-radius: 5px;}
297
  .dashboard-title {font-size: 24px; font-weight: bold; margin-bottom: 5px;}
298
+ .dashboard-section {margin-bottom: 20px;}
299
  .dashboard-section h3 {font-size: 18px; margin-bottom: 2px;}
300
  .dashboard-section p {margin: 1px 0; line-height: 1.2;}
 
301
  .dashboard-section ul {margin: 2px 0; padding-left: 20px;}
302
  """) as iface:
303
+ gr.Markdown("<h1>LabOps Log Analyzer Dashboard (Hugging Face AI)</h1>")
304
+ gr.Markdown("Upload a CSV file containing lab equipment logs to analyze.")
305
 
306
  with gr.Row():
307
  with gr.Column(scale=1):
308
  file_input = gr.File(label="Upload Logs (CSV)", file_types=[".csv"])
309
+ submit_button = gr.Button("Analyze", variant="primary")
310
+
311
  with gr.Column(scale=2):
312
  with gr.Group(elem_classes="dashboard-container"):
313
  gr.Markdown("<div class='dashboard-title'>Analysis Results (Step-by-Step)</div>")
314
+
315
  # Step 1: Summary Report
316
  with gr.Group(elem_classes="dashboard-section"):
317
  gr.Markdown("### Step 1: Summary Report")
318
  summary_output = gr.Markdown()
319
+
320
  # Step 2: Log Preview
321
  with gr.Group(elem_classes="dashboard-section"):
322
  gr.Markdown("### Step 2: Log Preview")
323
  preview_output = gr.Markdown()
324
+
325
  # Step 3: Usage Chart
326
  with gr.Group(elem_classes="dashboard-section"):
327
  gr.Markdown("### Step 3: Usage Chart")
328
  chart_output = gr.Plot()
329
+
330
  # Step 4: Anomaly Detection
331
  with gr.Group(elem_classes="dashboard-section"):
332
  gr.Markdown("### Step 4: Anomaly Detection")
333
  anomaly_output = gr.Markdown()
334
+
335
  # Step 5: AMC Reminders
336
  with gr.Group(elem_classes="dashboard-section"):
337
  gr.Markdown("### Step 5: AMC Reminders")
338
  amc_output = gr.Markdown()
339
+
340
  # Step 6: Dashboard Insights
341
  with gr.Group(elem_classes="dashboard-section"):
342
+ gr.Markdown("### Step 6: Insights (AI)")
343
  insights_output = gr.Markdown()
344
+
345
+ # PDF Download
346
+ with gr.Group(elem_classes="dashboard-section"):
347
+ gr.Markdown("### Download Report")
348
+ pdf_output = gr.File(label="Download Analysis Report as PDF")
349
 
350
  submit_button.click(
351
  fn=process_logs,
352
  inputs=[file_input],
353
+ outputs=[summary_output, preview_output, chart_output, anomaly_output, amc_output, insights_output, pdf_output]
354
  )
355
 
356
  logging.info("Gradio interface initialized successfully")
 
366
  except Exception as e:
367
  logging.error(f"Failed to launch Gradio interface: {str(e)}")
368
  print(f"Error launching app: {str(e)}")
369
+ raise e