RathodHarish commited on
Commit
4b07003
·
verified ·
1 Parent(s): 74e3fee

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +111 -63
app.py CHANGED
@@ -3,15 +3,16 @@ import pandas as pd
3
  from datetime import datetime, timedelta
4
  import logging
5
  import plotly.express as px
 
6
  from sklearn.ensemble import IsolationForest
7
  from transformers import pipeline
8
  import torch
9
  from concurrent.futures import ThreadPoolExecutor
10
  from simple_salesforce import Salesforce
11
  import os
12
- import json
13
  import io
14
  import time
 
15
 
16
  # Configure logging
17
  logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
@@ -49,7 +50,7 @@ try:
49
  "summarization",
50
  model="t5-small",
51
  device=device,
52
- max_length=30, # Reduced for faster processing
53
  min_length=10,
54
  num_beams=2
55
  )
@@ -149,8 +150,8 @@ def summarize_logs(df):
149
  try:
150
  total_devices = df["device_id"].nunique()
151
  most_used = df.groupby("device_id")["usage_hours"].sum().idxmax() if not df.empty else "N/A"
152
- prompt = f"Maintenance logs: {total_devices} devices. Most used: {most_used}."
153
- summary = summarizer(prompt, max_length=30, min_length=10, do_sample=False)[0]["summary_text"]
154
  return summary
155
  except Exception as e:
156
  logging.error(f"Summary generation failed: {str(e)}")
@@ -162,8 +163,8 @@ def detect_anomalies(df):
162
  if "usage_hours" not in df.columns or "downtime" not in df.columns:
163
  return "Anomaly detection requires 'usage_hours' and 'downtime' columns.", pd.DataFrame()
164
  features = df[["usage_hours", "downtime"]].fillna(0)
165
- if len(features) > 200: # Reduced sample size
166
- features = features.sample(n=200, random_state=42)
167
  iso_forest = IsolationForest(contamination=0.1, random_state=42)
168
  df["anomaly"] = iso_forest.fit_predict(features)
169
  anomalies = df[df["anomaly"] == -1][["device_id", "usage_hours", "downtime", "timestamp"]]
@@ -196,17 +197,30 @@ def generate_dashboard_insights(df):
196
  total_devices = df["device_id"].nunique()
197
  avg_usage = df["usage_hours"].mean() if "usage_hours" in df.columns else 0
198
  prompt = f"Insights: {total_devices} devices, avg usage {avg_usage:.2f} hours."
199
- insights = summarizer(prompt, max_length=30, min_length=10, do_sample=False)[0]["summary_text"]
200
  return insights
201
  except Exception as e:
202
  logging.error(f"Dashboard insights generation failed: {str(e)}")
203
  return f"Dashboard insights generation failed: {str(e)}"
204
 
 
 
 
 
 
 
 
 
 
 
 
 
205
  # Create usage chart
206
  def create_usage_chart(df):
207
  try:
208
- if df.empty:
209
- return None
 
210
  usage_data = df.groupby("device_id")["usage_hours"].sum().reset_index()
211
  if len(usage_data) > 5:
212
  usage_data = usage_data.nlargest(5, "usage_hours")
@@ -221,11 +235,14 @@ def create_usage_chart(df):
221
  return fig
222
  except Exception as e:
223
  logging.error(f"Failed to create usage chart: {str(e)}")
224
- return None
225
 
226
  # Create downtime chart
227
  def create_downtime_chart(df):
228
  try:
 
 
 
229
  downtime_data = df.groupby("device_id")["downtime"].sum().reset_index()
230
  if len(downtime_data) > 5:
231
  downtime_data = downtime_data.nlargest(5, "downtime")
@@ -240,13 +257,18 @@ def create_downtime_chart(df):
240
  return fig
241
  except Exception as e:
242
  logging.error(f"Failed to create downtime chart: {str(e)}")
243
- return None
244
 
245
  # Create daily log trends chart
246
  def create_daily_log_trends_chart(df):
247
  try:
248
- df['date'] = df['timestamp'].dt.date
 
 
 
249
  daily_logs = df.groupby('date').size().reset_index(name='log_count')
 
 
250
  fig = px.line(
251
  daily_logs,
252
  x='date',
@@ -258,19 +280,24 @@ def create_daily_log_trends_chart(df):
258
  return fig
259
  except Exception as e:
260
  logging.error(f"Failed to create daily log trends chart: {str(e)}")
261
- return None
262
 
263
  # Create weekly uptime chart
264
  def create_weekly_uptime_chart(df):
265
  try:
266
- df['week'] = df['timestamp'].dt.isocalendar().week
267
- df['year'] = df['timestamp'].dt.year
 
 
 
268
  weekly_data = df.groupby(['year', 'week']).agg({
269
  'usage_hours': 'sum',
270
  'downtime': 'sum'
271
  }).reset_index()
272
  weekly_data['uptime_percent'] = (weekly_data['usage_hours'] / (weekly_data['usage_hours'] + weekly_data['downtime'])) * 100
273
  weekly_data['year_week'] = weekly_data['year'].astype(str) + '-W' + weekly_data['week'].astype(str)
 
 
274
  fig = px.bar(
275
  weekly_data,
276
  x='year_week',
@@ -282,15 +309,18 @@ def create_weekly_uptime_chart(df):
282
  return fig
283
  except Exception as e:
284
  logging.error(f"Failed to create weekly uptime chart: {str(e)}")
285
- return None
286
 
287
  # Create anomaly alerts chart
288
  def create_anomaly_alerts_chart(anomalies_df):
289
  try:
290
- if anomalies_df.empty:
291
- return None
292
- anomalies_df['date'] = anomalies_df['timestamp'].dt.date
 
293
  anomaly_counts = anomalies_df.groupby('date').size().reset_index(name='anomaly_count')
 
 
294
  fig = px.scatter(
295
  anomaly_counts,
296
  x='date',
@@ -302,7 +332,7 @@ def create_anomaly_alerts_chart(anomalies_df):
302
  return fig
303
  except Exception as e:
304
  logging.error(f"Failed to create anomaly alerts chart: {str(e)}")
305
- return None
306
 
307
  # Generate device cards
308
  def generate_device_cards(df):
@@ -408,41 +438,41 @@ def generate_pdf_content(summary, preview_df, anomalies, amc_reminders, insights
408
  return None
409
 
410
  # Main processing function
411
- async def process_logs(file_obj, lab_site_filter, equipment_type_filter, date_range, last_modified_state):
412
  start_time = time.time()
413
  try:
414
  if not file_obj:
415
- return "No file uploaded.", pd.DataFrame(), None, '<p>No device cards available.</p>', None, None, None, None, "No anomalies detected.", "No AMC reminders.", "No insights generated.", None, last_modified_state
416
 
417
  file_path = file_obj.name
418
  current_modified_time = os.path.getmtime(file_path)
419
- if last_modified_state and current_modified_time == last_modified_state:
420
- return None, None, None, None, None, None, None, None, None, None, None, None, last_modified_state
421
-
422
- logging.info(f"Processing file: {file_path}")
423
- if not file_path.endswith(".csv"):
424
- return "Please upload a CSV file.", pd.DataFrame(), None, '<p>No device cards available.</p>', None, None, None, None, "", "", "", None, last_modified_state
425
-
426
- required_columns = ["device_id", "log_type", "status", "timestamp", "usage_hours", "downtime", "amc_date"]
427
- dtypes = {
428
- "device_id": "string",
429
- "log_type": "string",
430
- "status": "string",
431
- "usage_hours": "float32",
432
- "downtime": "float32",
433
- "amc_date": "string"
434
- }
435
- df = pd.read_csv(file_path, dtype=dtypes)
436
- missing_columns = [col for col in required_columns if col not in df.columns]
437
- if missing_columns:
438
- return f"Missing columns: {missing_columns}", pd.DataFrame(), None, '<p>No device cards available.</p>', None, None, None, None, None, None, None, None, last_modified_state
439
-
440
- df["timestamp"] = pd.to_datetime(df["timestamp"], errors='coerce')
441
- df["amc_date"] = pd.to_datetime(df["amc_date"], errors='coerce')
442
- if df["timestamp"].dt.tz is None:
443
- df["timestamp"] = df["timestamp"].dt.tz_localize('UTC').dt.tz_convert('Asia/Kolkata')
444
- if df.empty:
445
- return "No data available.", pd.DataFrame(), None, '<p>No device cards available.</p>', None, None, None, None, None, None, None, None, last_modified_state
446
 
447
  # Apply filters
448
  filtered_df = df.copy()
@@ -458,23 +488,22 @@ async def process_logs(file_obj, lab_site_filter, equipment_type_filter, date_ra
458
  filtered_df = filtered_df[(filtered_df['timestamp'] >= start_date) & (filtered_df['timestamp'] <= end_date)]
459
 
460
  if filtered_df.empty:
461
- return "No data after applying filters.", pd.DataFrame(), None, '<p>No device cards available.</p>', None, None, None, None, None, None, None, None, last_modified_state
462
 
463
  # Generate table for preview
464
  preview_df = filtered_df[['device_id', 'log_type', 'status', 'timestamp', 'usage_hours', 'downtime', 'amc_date']].head(5)
465
  preview_html = preview_df.to_html(index=False, classes='table table-striped', border=0)
466
 
467
- # Run tasks concurrently
468
- with ThreadPoolExecutor(max_workers=6) as executor:
469
  future_summary = executor.submit(summarize_logs, filtered_df)
470
  future_anomalies = executor.submit(detect_anomalies, filtered_df)
471
  future_amc = executor.submit(check_amc_reminders, filtered_df, datetime.now())
472
  future_insights = executor.submit(generate_dashboard_insights, filtered_df)
473
  future_usage_chart = executor.submit(create_usage_chart, filtered_df)
474
  future_downtime_chart = executor.submit(create_downtime_chart, filtered_df)
475
- future_daily_log_chart = executor.submit(create_daily_log_trends_chart, filtered_df)
476
  future_weekly_uptime_chart = executor.submit(create_weekly_uptime_chart, filtered_df)
477
- future_anomaly_alerts_chart = executor.submit(create_anomaly_alerts_chart, future_anomalies.result()[1]) # Pass anomalies_df
478
  future_device_cards = executor.submit(generate_device_cards, filtered_df)
479
 
480
  summary = f"Step 1: Summary Report\n{future_summary.result()}"
@@ -487,21 +516,32 @@ async def process_logs(file_obj, lab_site_filter, equipment_type_filter, date_ra
487
  downtime_chart = future_downtime_chart.result()
488
  daily_log_chart = future_daily_log_chart.result()
489
  weekly_uptime_chart = future_weekly_uptime_chart.result()
490
- anomaly_alerts_chart = future_anomaly_alerts_chart.result()
491
  device_cards = future_device_cards.result()
492
 
 
 
 
493
  save_to_salesforce(filtered_df, reminders_df)
494
- pdf_file = generate_pdf_content(summary, preview_df, anomalies, amc_reminders, insights, device_cards, daily_log_chart, weekly_uptime_chart, anomaly_alerts_chart, downtime_chart)
495
 
496
  elapsed_time = time.time() - start_time
497
  logging.info(f"Processing completed in {elapsed_time:.2f} seconds")
498
- if elapsed_time > 10:
499
- logging.warning(f"Processing time exceeded 10 seconds: {elapsed_time:.2f} seconds")
500
 
501
- return (summary, preview_html, usage_chart, device_cards, daily_log_chart, weekly_uptime_chart, anomaly_alerts_chart, downtime_chart, anomalies, amc_reminders, insights, pdf_file, current_modified_time)
502
  except Exception as e:
503
  logging.error(f"Failed to process file: {str(e)}")
504
- return f"Error: {str(e)}", pd.DataFrame(), None, '<p>Error processing data.</p>', None, None, None, None, None, None, None, None, last_modified_state
 
 
 
 
 
 
 
 
 
 
505
 
506
  # Update filters
507
  def update_filters(file_obj, current_file_state):
@@ -537,10 +577,11 @@ try:
537
  .table tr:nth-child(even) {background-color: #f9f9f9;}
538
  """) as iface:
539
  gr.Markdown("<h1>LabOps Log Analyzer Dashboard (Hugging Face AI)</h1>")
540
- gr.Markdown("Upload a CSV file to analyze. Click 'Analyze' to refresh the dashboard with the latest data.")
541
 
542
  last_modified_state = gr.State(value=None)
543
  current_file_state = gr.State(value=None)
 
544
 
545
  with gr.Row():
546
  with gr.Column(scale=1):
@@ -551,6 +592,7 @@ try:
551
  equipment_type_filter = gr.Dropdown(label="Equipment Type", choices=['All'], value='All', interactive=True)
552
  date_range_filter = gr.Slider(label="Date Range (Days from Today)", minimum=-365, maximum=0, step=1, value=[-30, 0])
553
  submit_button = gr.Button("Analyze", variant="primary")
 
554
 
555
  with gr.Column(scale=2):
556
  with gr.Group(elem_classes="dashboard-container"):
@@ -598,8 +640,14 @@ try:
598
 
599
  submit_button.click(
600
  fn=process_logs,
601
- inputs=[file_input, lab_site_filter, equipment_type_filter, date_range_filter, last_modified_state],
602
- outputs=[summary_output, preview_output, usage_chart_output, device_cards_output, daily_log_trends_output, weekly_uptime_output, anomaly_alerts_output, downtime_chart_output, anomaly_output, amc_output, insights_output, pdf_output, last_modified_state]
 
 
 
 
 
 
603
  )
604
 
605
  logging.info("Gradio interface initialized successfully")
 
3
  from datetime import datetime, timedelta
4
  import logging
5
  import plotly.express as px
6
+ import plotly.graph_objects as go
7
  from sklearn.ensemble import IsolationForest
8
  from transformers import pipeline
9
  import torch
10
  from concurrent.futures import ThreadPoolExecutor
11
  from simple_salesforce import Salesforce
12
  import os
 
13
  import io
14
  import time
15
+ import asyncio
16
 
17
  # Configure logging
18
  logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
 
50
  "summarization",
51
  model="t5-small",
52
  device=device,
53
+ max_length=20, # Further reduced for speed
54
  min_length=10,
55
  num_beams=2
56
  )
 
150
  try:
151
  total_devices = df["device_id"].nunique()
152
  most_used = df.groupby("device_id")["usage_hours"].sum().idxmax() if not df.empty else "N/A"
153
+ prompt = f"Logs: {total_devices} devices. Most used: {most_used}."
154
+ summary = summarizer(prompt, max_length=20, min_length=10, do_sample=False)[0]["summary_text"]
155
  return summary
156
  except Exception as e:
157
  logging.error(f"Summary generation failed: {str(e)}")
 
163
  if "usage_hours" not in df.columns or "downtime" not in df.columns:
164
  return "Anomaly detection requires 'usage_hours' and 'downtime' columns.", pd.DataFrame()
165
  features = df[["usage_hours", "downtime"]].fillna(0)
166
+ if len(features) > 100: # Further reduced sample size
167
+ features = features.sample(n=100, random_state=42)
168
  iso_forest = IsolationForest(contamination=0.1, random_state=42)
169
  df["anomaly"] = iso_forest.fit_predict(features)
170
  anomalies = df[df["anomaly"] == -1][["device_id", "usage_hours", "downtime", "timestamp"]]
 
197
  total_devices = df["device_id"].nunique()
198
  avg_usage = df["usage_hours"].mean() if "usage_hours" in df.columns else 0
199
  prompt = f"Insights: {total_devices} devices, avg usage {avg_usage:.2f} hours."
200
+ insights = summarizer(prompt, max_length=20, min_length=10, do_sample=False)[0]["summary_text"]
201
  return insights
202
  except Exception as e:
203
  logging.error(f"Dashboard insights generation failed: {str(e)}")
204
  return f"Dashboard insights generation failed: {str(e)}"
205
 
206
+ # Placeholder chart for empty data
207
+ def create_placeholder_chart(title):
208
+ fig = go.Figure()
209
+ fig.add_annotation(
210
+ text="No data available for this chart",
211
+ xref="paper", yref="paper",
212
+ x=0.5, y=0.5, showarrow=False,
213
+ font=dict(size=16)
214
+ )
215
+ fig.update_layout(title=title, margin=dict(l=20, r=20, t=40, b=20))
216
+ return fig
217
+
218
  # Create usage chart
219
  def create_usage_chart(df):
220
  try:
221
+ if df.empty or "usage_hours" not in df.columns or "device_id" not in df.columns:
222
+ logging.warning("Insufficient data for usage chart")
223
+ return create_placeholder_chart("Usage Hours per Device")
224
  usage_data = df.groupby("device_id")["usage_hours"].sum().reset_index()
225
  if len(usage_data) > 5:
226
  usage_data = usage_data.nlargest(5, "usage_hours")
 
235
  return fig
236
  except Exception as e:
237
  logging.error(f"Failed to create usage chart: {str(e)}")
238
+ return create_placeholder_chart("Usage Hours per Device")
239
 
240
  # Create downtime chart
241
  def create_downtime_chart(df):
242
  try:
243
+ if df.empty or "downtime" not in df.columns or "device_id" not in df.columns:
244
+ logging.warning("Insufficient data for downtime chart")
245
+ return create_placeholder_chart("Downtime per Device")
246
  downtime_data = df.groupby("device_id")["downtime"].sum().reset_index()
247
  if len(downtime_data) > 5:
248
  downtime_data = downtime_data.nlargest(5, "downtime")
 
257
  return fig
258
  except Exception as e:
259
  logging.error(f"Failed to create downtime chart: {str(e)}")
260
+ return create_placeholder_chart("Downtime per Device")
261
 
262
  # Create daily log trends chart
263
  def create_daily_log_trends_chart(df):
264
  try:
265
+ if df.empty or "timestamp" not in df.columns:
266
+ logging.warning("Insufficient data for daily log trends chart")
267
+ return create_placeholder_chart("Daily Log Trends")
268
+ df['date'] = pd.to_datetime(df['timestamp']).dt.date
269
  daily_logs = df.groupby('date').size().reset_index(name='log_count')
270
+ if daily_logs.empty:
271
+ return create_placeholder_chart("Daily Log Trends")
272
  fig = px.line(
273
  daily_logs,
274
  x='date',
 
280
  return fig
281
  except Exception as e:
282
  logging.error(f"Failed to create daily log trends chart: {str(e)}")
283
+ return create_placeholder_chart("Daily Log Trends")
284
 
285
  # Create weekly uptime chart
286
  def create_weekly_uptime_chart(df):
287
  try:
288
+ if df.empty or "timestamp" not in df.columns or "usage_hours" not in df.columns or "downtime" not in df.columns:
289
+ logging.warning("Insufficient data for weekly uptime chart")
290
+ return create_placeholder_chart("Weekly Uptime Percentage")
291
+ df['week'] = pd.to_datetime(df['timestamp']).dt.isocalendar().week
292
+ df['year'] = pd.to_datetime(df['timestamp']).dt.year
293
  weekly_data = df.groupby(['year', 'week']).agg({
294
  'usage_hours': 'sum',
295
  'downtime': 'sum'
296
  }).reset_index()
297
  weekly_data['uptime_percent'] = (weekly_data['usage_hours'] / (weekly_data['usage_hours'] + weekly_data['downtime'])) * 100
298
  weekly_data['year_week'] = weekly_data['year'].astype(str) + '-W' + weekly_data['week'].astype(str)
299
+ if weekly_data.empty:
300
+ return create_placeholder_chart("Weekly Uptime Percentage")
301
  fig = px.bar(
302
  weekly_data,
303
  x='year_week',
 
309
  return fig
310
  except Exception as e:
311
  logging.error(f"Failed to create weekly uptime chart: {str(e)}")
312
+ return create_placeholder_chart("Weekly Uptime Percentage")
313
 
314
  # Create anomaly alerts chart
315
  def create_anomaly_alerts_chart(anomalies_df):
316
  try:
317
+ if anomalies_df is None or anomalies_df.empty or "timestamp" not in anomalies_df.columns:
318
+ logging.warning("Insufficient data for anomaly alerts chart")
319
+ return create_placeholder_chart("Anomaly Alerts Over Time")
320
+ anomalies_df['date'] = pd.to_datetime(anomalies_df['timestamp']).dt.date
321
  anomaly_counts = anomalies_df.groupby('date').size().reset_index(name='anomaly_count')
322
+ if anomaly_counts.empty:
323
+ return create_placeholder_chart("Anomaly Alerts Over Time")
324
  fig = px.scatter(
325
  anomaly_counts,
326
  x='date',
 
332
  return fig
333
  except Exception as e:
334
  logging.error(f"Failed to create anomaly alerts chart: {str(e)}")
335
+ return create_placeholder_chart("Anomaly Alerts Over Time")
336
 
337
  # Generate device cards
338
  def generate_device_cards(df):
 
438
  return None
439
 
440
  # Main processing function
441
+ async def process_logs(file_obj, lab_site_filter, equipment_type_filter, date_range, last_modified_state, cached_df_state):
442
  start_time = time.time()
443
  try:
444
  if not file_obj:
445
+ return "No file uploaded.", pd.DataFrame(), None, '<p>No device cards available.</p>', None, None, None, None, "No anomalies detected.", "No AMC reminders.", "No insights generated.", None, last_modified_state, cached_df_state
446
 
447
  file_path = file_obj.name
448
  current_modified_time = os.path.getmtime(file_path)
449
+ if last_modified_state and current_modified_time == last_modified_state and cached_df_state is not None:
450
+ df = cached_df_state
451
+ else:
452
+ logging.info(f"Processing file: {file_path}")
453
+ if not file_path.endswith(".csv"):
454
+ return "Please upload a CSV file.", pd.DataFrame(), None, '<p>No device cards available.</p>', None, None, None, None, "", "", "", None, last_modified_state, cached_df_state
455
+
456
+ required_columns = ["device_id", "log_type", "status", "timestamp", "usage_hours", "downtime", "amc_date"]
457
+ dtypes = {
458
+ "device_id": "string",
459
+ "log_type": "string",
460
+ "status": "string",
461
+ "usage_hours": "float32",
462
+ "downtime": "float32",
463
+ "amc_date": "string"
464
+ }
465
+ df = pd.read_csv(file_path, dtype=dtypes)
466
+ missing_columns = [col for col in required_columns if col not in df.columns]
467
+ if missing_columns:
468
+ return f"Missing columns: {missing_columns}", pd.DataFrame(), None, '<p>No device cards available.</p>', None, None, None, None, None, None, None, None, last_modified_state, cached_df_state
469
+
470
+ df["timestamp"] = pd.to_datetime(df["timestamp"], errors='coerce')
471
+ df["amc_date"] = pd.to_datetime(df["amc_date"], errors='coerce')
472
+ if df["timestamp"].dt.tz is None:
473
+ df["timestamp"] = df["timestamp"].dt.tz_localize('UTC').dt.tz_convert('Asia/Kolkata')
474
+ if df.empty:
475
+ return "No data available.", pd.DataFrame(), None, '<p>No device cards available.</p>', None, None, None, None, None, None, None, None, last_modified_state, cached_df_state
476
 
477
  # Apply filters
478
  filtered_df = df.copy()
 
488
  filtered_df = filtered_df[(filtered_df['timestamp'] >= start_date) & (filtered_df['timestamp'] <= end_date)]
489
 
490
  if filtered_df.empty:
491
+ return "No data after applying filters.", pd.DataFrame(), None, '<p>No device cards available.</p>', None, None, None, None, None, None, None, None, last_modified_state, df
492
 
493
  # Generate table for preview
494
  preview_df = filtered_df[['device_id', 'log_type', 'status', 'timestamp', 'usage_hours', 'downtime', 'amc_date']].head(5)
495
  preview_html = preview_df.to_html(index=False, classes='table table-striped', border=0)
496
 
497
+ # Run critical tasks concurrently
498
+ with ThreadPoolExecutor(max_workers=4) as executor: # Reduced workers
499
  future_summary = executor.submit(summarize_logs, filtered_df)
500
  future_anomalies = executor.submit(detect_anomalies, filtered_df)
501
  future_amc = executor.submit(check_amc_reminders, filtered_df, datetime.now())
502
  future_insights = executor.submit(generate_dashboard_insights, filtered_df)
503
  future_usage_chart = executor.submit(create_usage_chart, filtered_df)
504
  future_downtime_chart = executor.submit(create_downtime_chart, filtered_df)
505
+ future_daily_log_chart = executor.submit(create_daily_log_chart, filtered_df)
506
  future_weekly_uptime_chart = executor.submit(create_weekly_uptime_chart, filtered_df)
 
507
  future_device_cards = executor.submit(generate_device_cards, filtered_df)
508
 
509
  summary = f"Step 1: Summary Report\n{future_summary.result()}"
 
516
  downtime_chart = future_downtime_chart.result()
517
  daily_log_chart = future_daily_log_chart.result()
518
  weekly_uptime_chart = future_weekly_uptime_chart.result()
 
519
  device_cards = future_device_cards.result()
520
 
521
+ # Generate anomaly alerts chart after anomalies are computed
522
+ anomaly_alerts_chart = create_anomaly_alerts_chart(anomalies_df)
523
+
524
  save_to_salesforce(filtered_df, reminders_df)
 
525
 
526
  elapsed_time = time.time() - start_time
527
  logging.info(f"Processing completed in {elapsed_time:.2f} seconds")
528
+ if elapsed_time > 5: # Stricter threshold
529
+ logging.warning(f"Processing time exceeded 5 seconds: {elapsed_time:.2f} seconds")
530
 
531
+ return (summary, preview_html, usage_chart, device_cards, daily_log_chart, weekly_uptime_chart, anomaly_alerts_chart, downtime_chart, anomalies, amc_reminders, insights, None, current_modified_time, df)
532
  except Exception as e:
533
  logging.error(f"Failed to process file: {str(e)}")
534
+ return f"Error: {str(e)}", pd.DataFrame(), None, '<p>Error processing data.</p>', None, None, None, None, None, None, None, None, last_modified_state, cached_df_state
535
+
536
+ # Generate PDF separately
537
+ async def generate_pdf(summary, preview_html, usage_chart, device_cards, daily_log_chart, weekly_uptime_chart, anomaly_alerts_chart, downtime_chart, anomalies, amc_reminders, insights):
538
+ try:
539
+ preview_df = pd.read_html(preview_html)[0]
540
+ pdf_file = generate_pdf_content(summary, preview_df, anomalies, amc_reminders, insights, device_cards, daily_log_chart, weekly_uptime_chart, anomaly_alerts_chart, downtime_chart)
541
+ return pdf_file
542
+ except Exception as e:
543
+ logging.error(f"Failed to generate PDF: {str(e)}")
544
+ return None
545
 
546
  # Update filters
547
  def update_filters(file_obj, current_file_state):
 
577
  .table tr:nth-child(even) {background-color: #f9f9f9;}
578
  """) as iface:
579
  gr.Markdown("<h1>LabOps Log Analyzer Dashboard (Hugging Face AI)</h1>")
580
+ gr.Markdown("Upload a CSV file to analyze. Click 'Analyze' to refresh the dashboard. Use 'Export PDF' for report download.")
581
 
582
  last_modified_state = gr.State(value=None)
583
  current_file_state = gr.State(value=None)
584
+ cached_df_state = gr.State(value=None)
585
 
586
  with gr.Row():
587
  with gr.Column(scale=1):
 
592
  equipment_type_filter = gr.Dropdown(label="Equipment Type", choices=['All'], value='All', interactive=True)
593
  date_range_filter = gr.Slider(label="Date Range (Days from Today)", minimum=-365, maximum=0, step=1, value=[-30, 0])
594
  submit_button = gr.Button("Analyze", variant="primary")
595
+ pdf_button = gr.Button("Export PDF", variant="secondary")
596
 
597
  with gr.Column(scale=2):
598
  with gr.Group(elem_classes="dashboard-container"):
 
640
 
641
  submit_button.click(
642
  fn=process_logs,
643
+ inputs=[file_input, lab_site_filter, equipment_type_filter, date_range_filter, last_modified_state, cached_df_state],
644
+ outputs=[summary_output, preview_output, usage_chart_output, device_cards_output, daily_log_trends_output, weekly_uptime_output, anomaly_alerts_output, downtime_chart_output, anomaly_output, amc_output, insights_output, pdf_output, last_modified_state, cached_df_state]
645
+ )
646
+
647
+ pdf_button.click(
648
+ fn=generate_pdf,
649
+ inputs=[summary_output, preview_output, usage_chart_output, device_cards_output, daily_log_trends_output, weekly_uptime_output, anomaly_alerts_output, downtime_chart_output, anomaly_output, amc_output, insights_output],
650
+ outputs=[pdf_output]
651
  )
652
 
653
  logging.info("Gradio interface initialized successfully")