RathodHarish commited on
Commit
db18ade
·
verified ·
1 Parent(s): f037165

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +112 -66
app.py CHANGED
@@ -48,9 +48,9 @@ try:
48
  "summarization",
49
  model="t5-small",
50
  device=device,
51
- max_length=40,
52
  min_length=10,
53
- num_beams=1
54
  )
55
  logging.info(f"Hugging Face model preloaded on {'GPU' if device == 0 else 'CPU'}")
56
  except Exception as e:
@@ -164,12 +164,10 @@ def save_to_salesforce(df, reminders_df):
164
  current_date = datetime.now()
165
  next_30_days = current_date + timedelta(days=30)
166
  records = []
167
- # Cap the number of records to save
168
- max_records_to_save = 1000
169
- df_to_save = df.head(max_records_to_save)
170
- logging.info(f"Processing {len(df_to_save)} records for Salesforce (capped at {max_records_to_save})")
171
 
172
- for idx, row in df_to_save.iterrows():
173
  status = str(row['status']).lower()
174
  log_type = str(row['log_type']).lower()
175
  status_mapped = picklist_mapping['Status__c'].get(status, status_values[0] if status_values else 'Active')
@@ -202,7 +200,7 @@ def save_to_salesforce(df, reminders_df):
202
  records.append(record)
203
 
204
  if records:
205
- batch_size = 100 # Reduced batch size for faster inserts
206
  for i in range(0, len(records), batch_size):
207
  batch = records[i:i + batch_size]
208
  try:
@@ -222,12 +220,10 @@ def save_to_salesforce(df, reminders_df):
222
  def summarize_logs(df):
223
  start_time = time.time()
224
  try:
225
- # Sample data for summarization if large
226
- sample_df = df.sample(n=min(500, len(df)), random_state=42) if len(df) > 500 else df
227
- total_devices = sample_df["device_id"].nunique()
228
- most_used = sample_df.groupby("device_id")["usage_hours"].sum().idxmax() if not sample_df.empty else "N/A"
229
  prompt = f"Maintenance logs: {total_devices} devices. Most used: {most_used}."
230
- summary = summarizer(prompt, max_length=40, min_length=10, do_sample=False)[0]["summary_text"]
231
  logging.info(f"Summary generation took {time.time() - start_time:.2f} seconds")
232
  return summary
233
  except Exception as e:
@@ -278,12 +274,10 @@ def check_amc_reminders(df, current_date):
278
  def generate_dashboard_insights(df):
279
  start_time = time.time()
280
  try:
281
- # Sample data for insights if large
282
- sample_df = df.sample(n=min(500, len(df)), random_state=42) if len(df) > 500 else df
283
- total_devices = sample_df["device_id"].nunique()
284
- avg_usage = sample_df["usage_hours"].mean() if "usage_hours" in sample_df.columns else 0
285
  prompt = f"Insights: {total_devices} devices, avg usage {avg_usage:.2f} hours."
286
- insights = summarizer(prompt, max_length=40, min_length=10, do_sample=False)[0]["summary_text"]
287
  logging.info(f"Insights generation took {time.time() - start_time:.2f} seconds")
288
  return insights
289
  except Exception as e:
@@ -296,13 +290,13 @@ def create_usage_chart(df):
296
  if df.empty:
297
  return None
298
  usage_data = df.groupby("device_id")["usage_hours"].sum().reset_index()
299
- if len(usage_data) > 3: # Reduced to top 3
300
- usage_data = usage_data.nlargest(3, "usage_hours")
301
  fig = px.bar(
302
  usage_data,
303
  x="device_id",
304
  y="usage_hours",
305
- title="Usage Hours per Device (Top 3)",
306
  labels={"device_id": "Device ID", "usage_hours": "Usage Hours"}
307
  )
308
  fig.update_layout(title_font_size=16, margin=dict(l=20, r=20, t=40, b=20))
@@ -315,13 +309,13 @@ def create_usage_chart(df):
315
  def create_downtime_chart(df):
316
  try:
317
  downtime_data = df.groupby("device_id")["downtime"].sum().reset_index()
318
- if len(downtime_data) > 3: # Reduced to top 3
319
- downtime_data = downtime_data.nlargest(3, "downtime")
320
  fig = px.bar(
321
  downtime_data,
322
  x="device_id",
323
  y="downtime",
324
- title="Downtime per Device (Top 3)",
325
  labels={"device_id": "Device ID", "downtime": "Downtime (Hours)"}
326
  )
327
  fig.update_layout(title_font_size=16, margin=dict(l=20, r=20, t=40, b=20))
@@ -348,6 +342,30 @@ def create_daily_log_trends_chart(df):
348
  logging.error(f"Failed to create daily log trends chart: {str(e)}")
349
  return None
350
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
351
  # Create anomaly alerts chart
352
  def create_anomaly_alerts_chart(anomalies_df):
353
  try:
@@ -401,12 +419,32 @@ def generate_device_cards(df):
401
  logging.error(f"Failed to generate device cards: {str(e)}")
402
  return f'<p>Error generating device cards: {str(e)}</p>'
403
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
404
  # Generate PDF content
405
- def generate_pdf_content(summary, preview_df, anomalies, amc_reminders, insights, device_cards_html, daily_log_chart, anomaly_alerts_chart, downtime_chart, df):
406
  if not reportlab_available:
407
  return None
408
  try:
409
- pdf_path = f"status_report_{datetime.now().strftime('%Y%m%d_%H%M%S')}.pdf"
410
  doc = SimpleDocTemplate(pdf_path, pagesize=letter)
411
  styles = getSampleStyleSheet()
412
  story = []
@@ -414,38 +452,38 @@ def generate_pdf_content(summary, preview_df, anomalies, amc_reminders, insights
414
  def safe_paragraph(text, style):
415
  return Paragraph(str(text).replace('\n', '<br/>'), style) if text else Paragraph("", style)
416
 
417
- story.append(Paragraph("LabOps Status Report", styles['Title']))
418
  story.append(Paragraph(f"Generated on {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}", styles['Normal']))
419
  story.append(Spacer(1, 12))
420
 
 
 
 
 
 
 
421
  story.append(Paragraph("Summary Report", styles['Heading2']))
422
  story.append(safe_paragraph(summary, styles['Normal']))
423
  story.append(Spacer(1, 12))
424
 
425
  story.append(Paragraph("Log Preview", styles['Heading2']))
426
  if not preview_df.empty:
427
- # Limit PDF table to 100 rows to avoid excessive rendering time
428
- max_pdf_rows = 100
429
- pdf_preview_df = preview_df.head(max_pdf_rows)
430
- data = [pdf_preview_df.columns.tolist()] + pdf_preview_df.values.tolist()
431
  table = Table(data)
432
  table.setStyle(TableStyle([
433
  ('BACKGROUND', (0, 0), (-1, 0), colors.grey),
434
  ('TEXTCOLOR', (0, 0), (-1, 0), colors.whitesmoke),
435
  ('ALIGN', (0, 0), (-1, -1), 'CENTER'),
436
  ('FONTNAME', (0, 0), (-1, 0), 'Helvetica-Bold'),
437
- ('FONTSIZE', (0, 0), (-1, 0), 8),
438
- ('BOTTOMPADDING', (0, 0), (-1, 0), 10),
439
  ('BACKGROUND', (0, 1), (-1, -1), colors.beige),
440
  ('TEXTCOLOR', (0, 1), (-1, -1), colors.black),
441
  ('FONTNAME', (0, 1), (-1, -1), 'Helvetica'),
442
- ('FONTSIZE', (0, 1), (-1, -1), 6),
443
  ('GRID', (0, 0), (-1, -1), 1, colors.black)
444
  ]))
445
  story.append(table)
446
- if len(preview_df) > max_pdf_rows:
447
- story.append(Spacer(1, 12))
448
- story.append(safe_paragraph(f"Note: Log Preview in PDF limited to {max_pdf_rows} rows. See dashboard for full table.", styles['Normal']))
449
  else:
450
  story.append(safe_paragraph("No preview available.", styles['Normal']))
451
  story.append(Spacer(1, 12))
@@ -478,23 +516,21 @@ def generate_pdf_content(summary, preview_df, anomalies, amc_reminders, insights
478
  return None
479
 
480
  # Main processing function
481
- async def process_logs(file_obj, lab_site_filter, equipment_type_filter, date_range, last_modified_state):
482
  start_time = time.time()
483
  try:
484
  if not file_obj:
485
- return "No file uploaded.", pd.DataFrame(), None, '<p>No device cards available.</p>', None, None, None, "No anomalies detected.", "No AMC reminders.", "No insights generated.", None, last_modified_state
486
 
487
  file_path = file_obj.name
488
  current_modified_time = os.path.getmtime(file_path)
489
  if last_modified_state and current_modified_time == last_modified_state:
490
- return None, None, None, None, None, None, None, None, None, None, None, last_modified_state
491
 
492
  logging.info(f"Processing file: {file_path}")
493
  if not file_path.endswith(".csv"):
494
- return "Please upload a CSV file.", pd.DataFrame(), None, '<p>No device cards available.</p>', None, None, None, "", "", "", None, last_modified_state
495
 
496
- # Cap the CSV size to avoid excessive processing
497
- max_rows = 5000
498
  required_columns = ["device_id", "log_type", "status", "timestamp", "usage_hours", "downtime", "amc_date"]
499
  dtypes = {
500
  "device_id": "string",
@@ -504,19 +540,17 @@ async def process_logs(file_obj, lab_site_filter, equipment_type_filter, date_ra
504
  "downtime": "float32",
505
  "amc_date": "string"
506
  }
507
- df = pd.read_csv(file_path, dtype=dtypes, low_memory=False, nrows=max_rows)
508
- if len(df) == max_rows:
509
- logging.warning(f"CSV exceeds {max_rows} rows. Only the first {max_rows} rows will be processed.")
510
  missing_columns = [col for col in required_columns if col not in df.columns]
511
  if missing_columns:
512
- return f"Missing columns: {missing_columns}", pd.DataFrame(), None, '<p>No device cards available.</p>', None, None, None, None, None, None, None, last_modified_state
513
 
514
  df["timestamp"] = pd.to_datetime(df["timestamp"], errors='coerce')
515
  df["amc_date"] = pd.to_datetime(df["amc_date"], errors='coerce')
516
  if df["timestamp"].dt.tz is None:
517
  df["timestamp"] = df["timestamp"].dt.tz_localize('UTC').dt.tz_convert('Asia/Kolkata')
518
  if df.empty:
519
- return "No data available.", pd.DataFrame(), None, '<p>No device cards available.</p>', None, None, None, None, None, None, None, last_modified_state
520
 
521
  # Apply filters
522
  filtered_df = df.copy()
@@ -530,16 +564,22 @@ async def process_logs(file_obj, lab_site_filter, equipment_type_filter, date_ra
530
  start_date = today + pd.Timedelta(days=days_start)
531
  end_date = today + pd.Timedelta(days=days_end) + pd.Timedelta(days=1) - pd.Timedelta(seconds=1)
532
  filtered_df = filtered_df[(filtered_df['timestamp'] >= start_date) & (filtered_df['timestamp'] <= end_date)]
 
 
 
 
 
 
533
 
534
  if filtered_df.empty:
535
- return "No data after applying filters.", pd.DataFrame(), None, '<p>No device cards available.</p>', None, None, None, None, None, None, None, last_modified_state
536
 
537
- # Generate table for preview with all rows
538
- preview_df = filtered_df # Show all rows
539
  preview_html = preview_df.to_html(index=False, classes='table table-striped', border=0)
540
 
541
  # Run tasks concurrently
542
- with ThreadPoolExecutor(max_workers=8) as executor: # Increased workers
543
  future_summary = executor.submit(summarize_logs, filtered_df)
544
  future_anomalies = executor.submit(detect_anomalies, filtered_df)
545
  future_amc = executor.submit(check_amc_reminders, filtered_df, datetime.now())
@@ -547,6 +587,7 @@ async def process_logs(file_obj, lab_site_filter, equipment_type_filter, date_ra
547
  future_usage_chart = executor.submit(create_usage_chart, filtered_df)
548
  future_downtime_chart = executor.submit(create_downtime_chart, filtered_df)
549
  future_daily_log_chart = executor.submit(create_daily_log_trends_chart, filtered_df)
 
550
  future_device_cards = executor.submit(generate_device_cards, filtered_df)
551
  future_reports = executor.submit(create_salesforce_reports, filtered_df)
552
 
@@ -559,39 +600,41 @@ async def process_logs(file_obj, lab_site_filter, equipment_type_filter, date_ra
559
  usage_chart = future_usage_chart.result()
560
  downtime_chart = future_downtime_chart.result()
561
  daily_log_chart = future_daily_log_chart.result()
562
- anomaly_alerts_chart = create_anomaly_alerts_chart(anomalies_df)
 
563
  device_cards = future_device_cards.result()
564
 
565
  save_to_salesforce(filtered_df, reminders_df)
566
- pdf_file = generate_pdf_content(summary, preview_df, anomalies, amc_reminders, insights, device_cards, daily_log_chart, anomaly_alerts_chart, downtime_chart, filtered_df)
567
 
568
  elapsed_time = time.time() - start_time
569
  logging.info(f"Processing completed in {elapsed_time:.2f} seconds")
570
- if elapsed_time > 20:
571
- logging.warning(f"Processing time exceeded 20 seconds: {elapsed_time:.2f} seconds")
572
 
573
- return (summary, preview_html, usage_chart, device_cards, daily_log_chart, anomaly_alerts_chart, downtime_chart, anomalies, amc_reminders, insights, pdf_file, current_modified_time)
574
  except Exception as e:
575
  logging.error(f"Failed to process file: {str(e)}")
576
- return f"Error: {str(e)}", pd.DataFrame(), None, '<p>Error processing data.</p>', None, None, None, None, None, None, None, last_modified_state
577
 
578
  # Update filters
579
  def update_filters(file_obj):
580
  if not file_obj:
581
- return gr.update(choices=['All'], value='All'), gr.update(choices=['All'], value='All')
582
  try:
583
  with open(file_obj.name, 'rb') as f:
584
  csv_content = f.read().decode('utf-8')
585
- df = pd.read_csv(io.StringIO(csv_content), low_memory=False, nrows=5000)
586
  df['timestamp'] = pd.to_datetime(df['timestamp'], errors='coerce')
587
 
588
  lab_site_options = ['All'] + [site for site in df['lab_site'].dropna().astype(str).unique().tolist() if site.strip()] if 'lab_site' in df.columns else ['All']
589
  equipment_type_options = ['All'] + [equip for equip in df['equipment_type'].dropna().astype(str).unique().tolist() if equip.strip()] if 'equipment_type' in df.columns else ['All']
 
590
 
591
- return gr.update(choices=lab_site_options, value='All'), gr.update(choices=equipment_type_options, value='All')
592
  except Exception as e:
593
  logging.error(f"Failed to update filters: {str(e)}")
594
- return gr.update(choices=['All'], value='All'), gr.update(choices=['All'], value='All')
595
 
596
  # Gradio Interface
597
  try:
@@ -621,6 +664,7 @@ try:
621
  lab_site_filter = gr.Dropdown(label="Lab Site", choices=['All'], value='All', interactive=True)
622
  equipment_type_filter = gr.Dropdown(label="Equipment Type", choices=['All'], value='All', interactive=True)
623
  date_range_filter = gr.Slider(label="Date Range (Days from Today)", minimum=-365, maximum=0, step=1, value=[-30, 0])
 
624
  submit_button = gr.Button("Analyze", variant="primary")
625
 
626
  with gr.Column(scale=2):
@@ -643,6 +687,8 @@ try:
643
  downtime_chart_output = gr.Plot()
644
  with gr.Tab("Daily Log Trends"):
645
  daily_log_trends_output = gr.Plot()
 
 
646
  with gr.Tab("Anomaly Alerts"):
647
  anomaly_alerts_output = gr.Plot()
648
  with gr.Group(elem_classes="dashboard-section"):
@@ -656,19 +702,19 @@ try:
656
  insights_output = gr.Markdown()
657
  with gr.Group(elem_classes="dashboard-section"):
658
  gr.Markdown("### Export Report")
659
- pdf_output = gr.File(label="Download Status Report as PDF")
660
 
661
  file_input.change(
662
  fn=update_filters,
663
  inputs=[file_input],
664
- outputs=[lab_site_filter, equipment_type_filter],
665
  queue=False
666
  )
667
 
668
  submit_button.click(
669
  fn=process_logs,
670
- inputs=[file_input, lab_site_filter, equipment_type_filter, date_range_filter, last_modified_state],
671
- outputs=[summary_output, preview_output, usage_chart_output, device_cards_output, daily_log_trends_output, anomaly_alerts_output, downtime_chart_output, anomaly_output, amc_output, insights_output, pdf_output, last_modified_state]
672
  )
673
 
674
  logging.info("Gradio interface initialized successfully")
 
48
  "summarization",
49
  model="t5-small",
50
  device=device,
51
+ max_length=50,
52
  min_length=10,
53
+ num_beams=2
54
  )
55
  logging.info(f"Hugging Face model preloaded on {'GPU' if device == 0 else 'CPU'}")
56
  except Exception as e:
 
164
  current_date = datetime.now()
165
  next_30_days = current_date + timedelta(days=30)
166
  records = []
167
+ reminder_device_ids = set(reminders_df['device_id']) if not reminders_df.empty else set()
168
+ logging.info(f"Processing {len(df)} records for Salesforce")
 
 
169
 
170
+ for idx, row in df.iterrows():
171
  status = str(row['status']).lower()
172
  log_type = str(row['log_type']).lower()
173
  status_mapped = picklist_mapping['Status__c'].get(status, status_values[0] if status_values else 'Active')
 
200
  records.append(record)
201
 
202
  if records:
203
+ batch_size = 200 # Smaller batch size for faster processing
204
  for i in range(0, len(records), batch_size):
205
  batch = records[i:i + batch_size]
206
  try:
 
220
  def summarize_logs(df):
221
  start_time = time.time()
222
  try:
223
+ total_devices = df["device_id"].nunique()
224
+ most_used = df.groupby("device_id")["usage_hours"].sum().idxmax() if not df.empty else "N/A"
 
 
225
  prompt = f"Maintenance logs: {total_devices} devices. Most used: {most_used}."
226
+ summary = summarizer(prompt, max_length=50, min_length=10, do_sample=False)[0]["summary_text"]
227
  logging.info(f"Summary generation took {time.time() - start_time:.2f} seconds")
228
  return summary
229
  except Exception as e:
 
274
  def generate_dashboard_insights(df):
275
  start_time = time.time()
276
  try:
277
+ total_devices = df["device_id"].nunique()
278
+ avg_usage = df["usage_hours"].mean() if "usage_hours" in df.columns else 0
 
 
279
  prompt = f"Insights: {total_devices} devices, avg usage {avg_usage:.2f} hours."
280
+ insights = summarizer(prompt, max_length=50, min_length=10, do_sample=False)[0]["summary_text"]
281
  logging.info(f"Insights generation took {time.time() - start_time:.2f} seconds")
282
  return insights
283
  except Exception as e:
 
290
  if df.empty:
291
  return None
292
  usage_data = df.groupby("device_id")["usage_hours"].sum().reset_index()
293
+ if len(usage_data) > 5:
294
+ usage_data = usage_data.nlargest(5, "usage_hours")
295
  fig = px.bar(
296
  usage_data,
297
  x="device_id",
298
  y="usage_hours",
299
+ title="Usage Hours per Device",
300
  labels={"device_id": "Device ID", "usage_hours": "Usage Hours"}
301
  )
302
  fig.update_layout(title_font_size=16, margin=dict(l=20, r=20, t=40, b=20))
 
309
  def create_downtime_chart(df):
310
  try:
311
  downtime_data = df.groupby("device_id")["downtime"].sum().reset_index()
312
+ if len(downtime_data) > 5:
313
+ downtime_data = downtime_data.nlargest(5, "downtime")
314
  fig = px.bar(
315
  downtime_data,
316
  x="device_id",
317
  y="downtime",
318
+ title="Downtime per Device",
319
  labels={"device_id": "Device ID", "downtime": "Downtime (Hours)"}
320
  )
321
  fig.update_layout(title_font_size=16, margin=dict(l=20, r=20, t=40, b=20))
 
342
  logging.error(f"Failed to create daily log trends chart: {str(e)}")
343
  return None
344
 
345
+ # Create weekly uptime chart
346
+ def create_weekly_uptime_chart(df):
347
+ try:
348
+ df['week'] = df['timestamp'].dt.isocalendar().week
349
+ df['year'] = df['timestamp'].dt.year
350
+ weekly_data = df.groupby(['year', 'week']).agg({
351
+ 'usage_hours': 'sum',
352
+ 'downtime': 'sum'
353
+ }).reset_index()
354
+ weekly_data['uptime_percent'] = (weekly_data['usage_hours'] / (weekly_data['usage_hours'] + weekly_data['downtime'])) * 100
355
+ weekly_data['year_week'] = weekly_data['year'].astype(str) + '-W' + weekly_data['week'].astype(str)
356
+ fig = px.bar(
357
+ weekly_data,
358
+ x='year_week',
359
+ y='uptime_percent',
360
+ title="Weekly Uptime Percentage",
361
+ labels={"year_week": "Year-Week", "uptime_percent": "Uptime %"}
362
+ )
363
+ fig.update_layout(title_font_size=16, margin=dict(l=20, r=20, t=40, b=20))
364
+ return fig
365
+ except Exception as e:
366
+ logging.error(f"Failed to create weekly uptime chart: {str(e)}")
367
+ return None
368
+
369
  # Create anomaly alerts chart
370
  def create_anomaly_alerts_chart(anomalies_df):
371
  try:
 
419
  logging.error(f"Failed to generate device cards: {str(e)}")
420
  return f'<p>Error generating device cards: {str(e)}</p>'
421
 
422
+ # Generate monthly status
423
+ def generate_monthly_status(df, selected_month):
424
+ try:
425
+ total_devices = df['device_id'].nunique()
426
+ total_usage_hours = df['usage_hours'].sum()
427
+ total_downtime = df['downtime'].sum()
428
+ avg_usage = total_usage_hours / total_devices if total_devices > 0 else 0
429
+ avg_downtime = total_downtime / total_devices if total_devices > 0 else 0
430
+ return f"""
431
+ Monthly Status for {selected_month}:
432
+ - Total Devices: {total_devices}
433
+ - Total Usage Hours: {total_usage_hours:.2f}
434
+ - Total Downtime Hours: {total_downtime:.2f}
435
+ - Average Usage per Device: {avg_usage:.2f} hours
436
+ - Average Downtime per Device: {avg_downtime:.2f} hours
437
+ """
438
+ except Exception as e:
439
+ logging.error(f"Failed to generate monthly status: {str(e)}")
440
+ return f"Failed to generate monthly status: {str(e)}"
441
+
442
  # Generate PDF content
443
+ def generate_pdf_content(summary, preview_df, anomalies, amc_reminders, insights, device_cards_html, daily_log_chart, weekly_uptime_chart, anomaly_alerts_chart, downtime_chart, df, selected_month):
444
  if not reportlab_available:
445
  return None
446
  try:
447
+ pdf_path = f"monthly_status_report_{selected_month.replace(' ', '_')}_{datetime.now().strftime('%Y%m%d_%H%M%S')}.pdf"
448
  doc = SimpleDocTemplate(pdf_path, pagesize=letter)
449
  styles = getSampleStyleSheet()
450
  story = []
 
452
  def safe_paragraph(text, style):
453
  return Paragraph(str(text).replace('\n', '<br/>'), style) if text else Paragraph("", style)
454
 
455
+ story.append(Paragraph("LabOps Monthly Status Report", styles['Title']))
456
  story.append(Paragraph(f"Generated on {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}", styles['Normal']))
457
  story.append(Spacer(1, 12))
458
 
459
+ if selected_month != "All":
460
+ monthly_status = generate_monthly_status(df, selected_month)
461
+ story.append(Paragraph("Monthly Status Summary", styles['Heading2']))
462
+ story.append(safe_paragraph(monthly_status, styles['Normal']))
463
+ story.append(Spacer(1, 12))
464
+
465
  story.append(Paragraph("Summary Report", styles['Heading2']))
466
  story.append(safe_paragraph(summary, styles['Normal']))
467
  story.append(Spacer(1, 12))
468
 
469
  story.append(Paragraph("Log Preview", styles['Heading2']))
470
  if not preview_df.empty:
471
+ data = [preview_df.columns.tolist()] + preview_df.head(5).values.tolist()
 
 
 
472
  table = Table(data)
473
  table.setStyle(TableStyle([
474
  ('BACKGROUND', (0, 0), (-1, 0), colors.grey),
475
  ('TEXTCOLOR', (0, 0), (-1, 0), colors.whitesmoke),
476
  ('ALIGN', (0, 0), (-1, -1), 'CENTER'),
477
  ('FONTNAME', (0, 0), (-1, 0), 'Helvetica-Bold'),
478
+ ('FONTSIZE', (0, 0), (-1, 0), 12),
479
+ ('BOTTOMPADDING', (0, 0), (-1, 0), 12),
480
  ('BACKGROUND', (0, 1), (-1, -1), colors.beige),
481
  ('TEXTCOLOR', (0, 1), (-1, -1), colors.black),
482
  ('FONTNAME', (0, 1), (-1, -1), 'Helvetica'),
483
+ ('FONTSIZE', (0, 1), (-1, -1), 10),
484
  ('GRID', (0, 0), (-1, -1), 1, colors.black)
485
  ]))
486
  story.append(table)
 
 
 
487
  else:
488
  story.append(safe_paragraph("No preview available.", styles['Normal']))
489
  story.append(Spacer(1, 12))
 
516
  return None
517
 
518
  # Main processing function
519
+ async def process_logs(file_obj, lab_site_filter, equipment_type_filter, date_range, month_filter, last_modified_state):
520
  start_time = time.time()
521
  try:
522
  if not file_obj:
523
+ 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
524
 
525
  file_path = file_obj.name
526
  current_modified_time = os.path.getmtime(file_path)
527
  if last_modified_state and current_modified_time == last_modified_state:
528
+ return None, None, None, None, None, None, None, None, None, None, None, None, last_modified_state
529
 
530
  logging.info(f"Processing file: {file_path}")
531
  if not file_path.endswith(".csv"):
532
+ return "Please upload a CSV file.", pd.DataFrame(), None, '<p>No device cards available.</p>', None, None, None, None, "", "", "", None, last_modified_state
533
 
 
 
534
  required_columns = ["device_id", "log_type", "status", "timestamp", "usage_hours", "downtime", "amc_date"]
535
  dtypes = {
536
  "device_id": "string",
 
540
  "downtime": "float32",
541
  "amc_date": "string"
542
  }
543
+ df = pd.read_csv(file_path, dtype=dtypes)
 
 
544
  missing_columns = [col for col in required_columns if col not in df.columns]
545
  if missing_columns:
546
+ 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
547
 
548
  df["timestamp"] = pd.to_datetime(df["timestamp"], errors='coerce')
549
  df["amc_date"] = pd.to_datetime(df["amc_date"], errors='coerce')
550
  if df["timestamp"].dt.tz is None:
551
  df["timestamp"] = df["timestamp"].dt.tz_localize('UTC').dt.tz_convert('Asia/Kolkata')
552
  if df.empty:
553
+ return "No data available.", pd.DataFrame(), None, '<p>No device cards available.</p>', None, None, None, None, None, None, None, None, last_modified_state
554
 
555
  # Apply filters
556
  filtered_df = df.copy()
 
564
  start_date = today + pd.Timedelta(days=days_start)
565
  end_date = today + pd.Timedelta(days=days_end) + pd.Timedelta(days=1) - pd.Timedelta(seconds=1)
566
  filtered_df = filtered_df[(filtered_df['timestamp'] >= start_date) & (filtered_df['timestamp'] <= end_date)]
567
+ if month_filter and month_filter != "All":
568
+ selected_date = pd.to_datetime(month_filter, format="%B %Y")
569
+ filtered_df = filtered_df[
570
+ (filtered_df['timestamp'].dt.year == selected_date.year) &
571
+ (filtered_df['timestamp'].dt.month == selected_date.month)
572
+ ]
573
 
574
  if filtered_df.empty:
575
+ 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
576
 
577
+ # Generate table for preview
578
+ preview_df = filtered_df[['device_id', 'log_type', 'status', 'timestamp', 'usage_hours', 'downtime', 'amc_date']].head(5)
579
  preview_html = preview_df.to_html(index=False, classes='table table-striped', border=0)
580
 
581
  # Run tasks concurrently
582
+ with ThreadPoolExecutor(max_workers=6) as executor:
583
  future_summary = executor.submit(summarize_logs, filtered_df)
584
  future_anomalies = executor.submit(detect_anomalies, filtered_df)
585
  future_amc = executor.submit(check_amc_reminders, filtered_df, datetime.now())
 
587
  future_usage_chart = executor.submit(create_usage_chart, filtered_df)
588
  future_downtime_chart = executor.submit(create_downtime_chart, filtered_df)
589
  future_daily_log_chart = executor.submit(create_daily_log_trends_chart, filtered_df)
590
+ future_weekly_uptime_chart = executor.submit(create_weekly_uptime_chart, filtered_df)
591
  future_device_cards = executor.submit(generate_device_cards, filtered_df)
592
  future_reports = executor.submit(create_salesforce_reports, filtered_df)
593
 
 
600
  usage_chart = future_usage_chart.result()
601
  downtime_chart = future_downtime_chart.result()
602
  daily_log_chart = future_daily_log_chart.result()
603
+ weekly_uptime_chart = future_weekly_uptime_chart.result()
604
+ anomaly_alerts_chart = create_anomaly_alerts_chart(anomalies_df) # Use anomalies_df
605
  device_cards = future_device_cards.result()
606
 
607
  save_to_salesforce(filtered_df, reminders_df)
608
+ 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, filtered_df, month_filter)
609
 
610
  elapsed_time = time.time() - start_time
611
  logging.info(f"Processing completed in {elapsed_time:.2f} seconds")
612
+ if elapsed_time > 10:
613
+ logging.warning(f"Processing time exceeded 10 seconds: {elapsed_time:.2f} seconds")
614
 
615
+ 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)
616
  except Exception as e:
617
  logging.error(f"Failed to process file: {str(e)}")
618
+ return f"Error: {str(e)}", pd.DataFrame(), None, '<p>Error processing data.</p>', None, None, None, None, None, None, None, None, last_modified_state
619
 
620
  # Update filters
621
  def update_filters(file_obj):
622
  if not file_obj:
623
+ return gr.update(choices=['All'], value='All'), gr.update(choices=['All'], value='All'), gr.update(choices=['All'], value='All')
624
  try:
625
  with open(file_obj.name, 'rb') as f:
626
  csv_content = f.read().decode('utf-8')
627
+ df = pd.read_csv(io.StringIO(csv_content))
628
  df['timestamp'] = pd.to_datetime(df['timestamp'], errors='coerce')
629
 
630
  lab_site_options = ['All'] + [site for site in df['lab_site'].dropna().astype(str).unique().tolist() if site.strip()] if 'lab_site' in df.columns else ['All']
631
  equipment_type_options = ['All'] + [equip for equip in df['equipment_type'].dropna().astype(str).unique().tolist() if equip.strip()] if 'equipment_type' in df.columns else ['All']
632
+ month_options = ['All'] + sorted(df['timestamp'].dt.strftime('%B %Y').dropna().unique().tolist()) if 'timestamp' in df.columns else ['All']
633
 
634
+ return gr.update(choices=lab_site_options, value='All'), gr.update(choices=equipment_type_options, value='All'), gr.update(choices=month_options, value='All')
635
  except Exception as e:
636
  logging.error(f"Failed to update filters: {str(e)}")
637
+ return gr.update(choices=['All'], value='All'), gr.update(choices=['All'], value='All'), gr.update(choices=['All'], value='All')
638
 
639
  # Gradio Interface
640
  try:
 
664
  lab_site_filter = gr.Dropdown(label="Lab Site", choices=['All'], value='All', interactive=True)
665
  equipment_type_filter = gr.Dropdown(label="Equipment Type", choices=['All'], value='All', interactive=True)
666
  date_range_filter = gr.Slider(label="Date Range (Days from Today)", minimum=-365, maximum=0, step=1, value=[-30, 0])
667
+ month_filter = gr.Dropdown(label="Select Month for Report", choices=['All'], value='All', interactive=True)
668
  submit_button = gr.Button("Analyze", variant="primary")
669
 
670
  with gr.Column(scale=2):
 
687
  downtime_chart_output = gr.Plot()
688
  with gr.Tab("Daily Log Trends"):
689
  daily_log_trends_output = gr.Plot()
690
+ with gr.Tab("Weekly Uptime Percentage"):
691
+ weekly_uptime_output = gr.Plot()
692
  with gr.Tab("Anomaly Alerts"):
693
  anomaly_alerts_output = gr.Plot()
694
  with gr.Group(elem_classes="dashboard-section"):
 
702
  insights_output = gr.Markdown()
703
  with gr.Group(elem_classes="dashboard-section"):
704
  gr.Markdown("### Export Report")
705
+ pdf_output = gr.File(label="Download Monthly Status Report as PDF")
706
 
707
  file_input.change(
708
  fn=update_filters,
709
  inputs=[file_input],
710
+ outputs=[lab_site_filter, equipment_type_filter, month_filter],
711
  queue=False
712
  )
713
 
714
  submit_button.click(
715
  fn=process_logs,
716
+ inputs=[file_input, lab_site_filter, equipment_type_filter, date_range_filter, month_filter, last_modified_state],
717
+ 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]
718
  )
719
 
720
  logging.info("Gradio interface initialized successfully")