RathodHarish commited on
Commit
adaebc7
·
verified ·
1 Parent(s): ddf8bcf

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +31 -58
app.py CHANGED
@@ -469,6 +469,10 @@ def generate_device_cards(df):
469
  counts = df_clean.groupby('device_id').size().reset_index(name='count')
470
  device_stats = device_stats.merge(counts, on='device_id')
471
 
 
 
 
 
472
  device_stats['health'] = device_stats['status'].map({
473
  'Active': 'Healthy',
474
  'Inactive': 'Unhealthy',
@@ -595,34 +599,24 @@ def generate_pdf_content(summary, preview, anomalies, amc_reminders, insights, d
595
  logging.error(f"Failed to generate PDF: {str(e)}", exc_info=True)
596
  return None
597
 
598
- # Validate inputs before generating PDF
599
- def validate_and_generate_pdf(summary, preview, anomalies, amc_reminders, insights, device_cards, daily_log_chart, weekly_uptime_chart, anomaly_alerts_chart, downtime_chart, df, month_filter):
600
- if not all([summary, preview, anomalies, amc_reminders, insights, device_cards, df is not None]):
601
- logging.error("One or more required inputs for PDF generation are missing.")
602
- return None, "Please click 'Analyze' to process the data before generating a PDF."
603
- pdf_path = generate_pdf_content(summary, preview, anomalies, amc_reminders, insights, device_cards, daily_log_chart, weekly_uptime_chart, anomaly_alerts_chart, downtime_chart, df, month_filter)
604
- if pdf_path is None:
605
- return None, "Failed to generate PDF. Check logs for details."
606
- return pdf_path, "PDF generated successfully."
607
-
608
  # Main Gradio function
609
  async def process_logs(file_obj, lab_site_filter, equipment_type_filter, date_range, month_filter, last_modified_state):
610
  try:
611
  start_time = datetime.now()
612
 
613
  if not file_obj:
614
- return "No file uploaded.", "No data to preview.", None, '<p>No device cards available.</p>', None, None, None, None, "No anomalies detected.", "No AMC reminders.", "No insights generated.", None, last_modified_state, None, None, None, None, None, None, False, "Please upload a CSV file to analyze."
615
 
616
  file_path = file_obj.name
617
  current_modified_time = os.path.getmtime(file_path)
618
 
619
  if last_modified_state and current_modified_time == last_modified_state:
620
- return None, None, None, None, None, None, None, None, None, None, None, None, last_modified_state, None, None, None, None, None, None, False, "No changes detected in the file."
621
 
622
  logging.info(f"Processing file: {file_path}, last modified: {current_modified_time}")
623
 
624
  if not file_path.endswith(".csv"):
625
- return "Please upload a CSV file.", "", None, '<p>No device cards available.</p>', None, None, None, None, "", "", "", None, last_modified_state, None, None, None, None, None, None, False, "Invalid file format. Please upload a CSV file."
626
 
627
  required_columns = ["device_id", "log_type", "status", "timestamp", "usage_hours", "downtime", "amc_date"]
628
  dtypes = {
@@ -634,9 +628,13 @@ async def process_logs(file_obj, lab_site_filter, equipment_type_filter, date_ra
634
  "amc_date": "string"
635
  }
636
  df = pd.read_csv(file_path, dtype=dtypes)
 
 
 
 
637
  missing_columns = [col for col in required_columns if col not in df.columns]
638
  if missing_columns:
639
- return f"Missing columns: {missing_columns}", None, None, '<p>No device cards available.</p>', None, None, None, None, None, None, None, None, last_modified_state, None, None, None, None, None, None, False, f"Missing required columns: {missing_columns}"
640
 
641
  df["timestamp"] = pd.to_datetime(df["timestamp"], errors='coerce')
642
  df["amc_date"] = pd.to_datetime(df["amc_date"], errors='coerce')
@@ -644,7 +642,7 @@ async def process_logs(file_obj, lab_site_filter, equipment_type_filter, date_ra
644
  logging.info("Localizing naive timestamps to IST")
645
  df["timestamp"] = df["timestamp"].dt.tz_localize('UTC').dt.tz_convert('Asia/Kolkata')
646
  if df.empty:
647
- return "No data available.", None, None, '<p>No device cards available.</p>', None, None, None, None, None, None, None, None, last_modified_state, None, None, None, None, None, None, False, "No data available in the uploaded file."
648
 
649
  logging.info(f"DataFrame before filtering:\n{df.head().to_string()}")
650
 
@@ -679,7 +677,7 @@ async def process_logs(file_obj, lab_site_filter, equipment_type_filter, date_ra
679
 
680
  if filtered_df.empty:
681
  logging.warning("Filtered DataFrame is empty after applying filters.")
682
- return "No data after applying filters.", None, None, '<p>No device cards available.</p>', None, None, None, None, None, None, None, None, last_modified_state, None, None, None, None, None, None, False, "No data available after applying filters."
683
 
684
  logging.info(f"Filtered DataFrame:\n{filtered_df.head().to_string()}")
685
 
@@ -788,24 +786,31 @@ async def process_logs(file_obj, lab_site_filter, equipment_type_filter, date_ra
788
  )
789
  preview_text = "\n".join(preview_lines)
790
 
791
- with ThreadPoolExecutor() as executor:
792
- executor.submit(save_to_salesforce, filtered_df, reminders_df, summary, anomalies, amc_reminders, insights)
793
- executor.submit(create_salesforce_reports, filtered_df)
794
-
795
  pdf_file = None
 
 
 
 
 
 
 
 
 
 
 
 
 
796
 
797
  elapsed_time = (datetime.now() - start_time).total_seconds()
798
  logging.info(f"Processing completed in {elapsed_time:.2f} seconds")
799
  if elapsed_time > 10:
800
  logging.warning(f"Processing time exceeded 10 seconds: {elapsed_time:.2f} seconds")
801
 
802
- # Log state variable assignments for debugging
803
- logging.info(f"Setting state variables: summary={summary}, preview={preview_text}, anomalies={anomalies}, amc_reminders={amc_reminders}, insights={insights}, device_cards={device_cards[:50]}..., df={'set' if df is not None else 'None'}")
804
-
805
- 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, summary, preview_text, anomalies, amc_reminders, insights, device_cards, filtered_df, True, "Analysis completed successfully. You can now generate the PDF report.")
806
  except Exception as e:
807
  logging.error(f"Failed to process file: {str(e)}")
808
- return f"Error: {str(e)}", None, None, '<p>Error processing data.</p>', None, None, None, None, None, None, None, None, last_modified_state, None, None, None, None, None, None, False, f"Failed to process file: {str(e)}"
809
 
810
  # Update filter options
811
  def update_filters(file_obj):
@@ -863,7 +868,7 @@ try:
863
  .dashboard-section ul {margin: 2px 0; padding-left: 20px;}
864
  """) as iface:
865
  gr.Markdown("<h1>LabOps Log Analyzer Dashboard</h1>")
866
- gr.Markdown("Upload a CSV file to analyze. Click 'Analyze' to refresh the dashboard with the latest data.")
867
 
868
  last_modified_state = gr.State(value=None)
869
  summary_state = gr.State()
@@ -873,7 +878,6 @@ try:
873
  insights_state = gr.State()
874
  device_cards_state = gr.State()
875
  df_state = gr.State()
876
- is_analyzed_state = gr.State(value=False)
877
 
878
  with gr.Row():
879
  with gr.Column(scale=1):
@@ -957,7 +961,6 @@ try:
957
 
958
  with gr.Group(elem_classes="dashboard-section"):
959
  gr.Markdown("### Export Report")
960
- pdf_button = gr.Button("Generate PDF Report", interactive=False) # Disabled by default
961
  pdf_output = gr.File(label="Download Monthly Status Report as PDF")
962
 
963
  file_input.change(
@@ -998,40 +1001,10 @@ try:
998
  insights_state,
999
  device_cards_state,
1000
  df_state,
1001
- is_analyzed_state,
1002
  status_message
1003
  ]
1004
  )
1005
 
1006
- def update_pdf_button_interactivity(is_analyzed):
1007
- return gr.update(interactive=is_analyzed)
1008
-
1009
- is_analyzed_state.change(
1010
- fn=update_pdf_button_interactivity,
1011
- inputs=[is_analyzed_state],
1012
- outputs=[pdf_button],
1013
- queue=False
1014
- )
1015
-
1016
- pdf_button.click(
1017
- fn=validate_and_generate_pdf,
1018
- inputs=[
1019
- summary_state,
1020
- preview_state,
1021
- anomalies_state,
1022
- amc_reminders_state,
1023
- insights_state,
1024
- device_cards_state,
1025
- daily_log_trends_output,
1026
- weekly_uptime_output,
1027
- anomaly_alerts_output,
1028
- downtime_chart_output,
1029
- df_state,
1030
- month_filter
1031
- ],
1032
- outputs=[pdf_output, status_message]
1033
- )
1034
-
1035
  logging.info("Gradio interface initialized successfully")
1036
  except Exception as e:
1037
  logging.error(f"Failed to initialize Gradio interface: {str(e)}")
 
469
  counts = df_clean.groupby('device_id').size().reset_index(name='count')
470
  device_stats = device_stats.merge(counts, on='device_id')
471
 
472
+ # Limit to top 10 devices by count
473
+ device_stats = device_stats.nlargest(10, 'count')
474
+ logging.info(f"Limited device cards to top {len(device_stats)} devices by usage count.")
475
+
476
  device_stats['health'] = device_stats['status'].map({
477
  'Active': 'Healthy',
478
  'Inactive': 'Unhealthy',
 
599
  logging.error(f"Failed to generate PDF: {str(e)}", exc_info=True)
600
  return None
601
 
 
 
 
 
 
 
 
 
 
 
602
  # Main Gradio function
603
  async def process_logs(file_obj, lab_site_filter, equipment_type_filter, date_range, month_filter, last_modified_state):
604
  try:
605
  start_time = datetime.now()
606
 
607
  if not file_obj:
608
+ return "No file uploaded.", "No data to preview.", None, '<p>No device cards available.</p>', None, None, None, None, "No anomalies detected.", "No AMC reminders.", "No insights generated.", None, last_modified_state, None, None, None, None, None, None, "Please upload a CSV file to analyze."
609
 
610
  file_path = file_obj.name
611
  current_modified_time = os.path.getmtime(file_path)
612
 
613
  if last_modified_state and current_modified_time == last_modified_state:
614
+ return None, None, None, None, None, None, None, None, None, None, None, None, last_modified_state, None, None, None, None, None, None, "No changes detected in the file."
615
 
616
  logging.info(f"Processing file: {file_path}, last modified: {current_modified_time}")
617
 
618
  if not file_path.endswith(".csv"):
619
+ return "Please upload a CSV file.", "", None, '<p>No device cards available.</p>', None, None, None, None, "", "", "", None, last_modified_state, None, None, None, None, None, None, "Invalid file format. Please upload a CSV file."
620
 
621
  required_columns = ["device_id", "log_type", "status", "timestamp", "usage_hours", "downtime", "amc_date"]
622
  dtypes = {
 
628
  "amc_date": "string"
629
  }
630
  df = pd.read_csv(file_path, dtype=dtypes)
631
+ # Downsample early if dataset is too large
632
+ if len(df) > 10000:
633
+ df = df.sample(n=10000, random_state=42)
634
+ logging.info(f"Downsampled DataFrame to 10,000 rows immediately after loading.")
635
  missing_columns = [col for col in required_columns if col not in df.columns]
636
  if missing_columns:
637
+ return f"Missing columns: {missing_columns}", None, None, '<p>No device cards available.</p>', None, None, None, None, None, None, None, None, last_modified_state, None, None, None, None, None, None, f"Missing required columns: {missing_columns}"
638
 
639
  df["timestamp"] = pd.to_datetime(df["timestamp"], errors='coerce')
640
  df["amc_date"] = pd.to_datetime(df["amc_date"], errors='coerce')
 
642
  logging.info("Localizing naive timestamps to IST")
643
  df["timestamp"] = df["timestamp"].dt.tz_localize('UTC').dt.tz_convert('Asia/Kolkata')
644
  if df.empty:
645
+ return "No data available.", None, None, '<p>No device cards available.</p>', None, None, None, None, None, None, None, None, last_modified_state, None, None, None, None, None, None, "No data available in the uploaded file."
646
 
647
  logging.info(f"DataFrame before filtering:\n{df.head().to_string()}")
648
 
 
677
 
678
  if filtered_df.empty:
679
  logging.warning("Filtered DataFrame is empty after applying filters.")
680
+ return "No data after applying filters.", None, None, '<p>No device cards available.</p>', None, None, None, None, None, None, None, None, last_modified_state, None, None, None, None, None, None, "No data available after applying filters."
681
 
682
  logging.info(f"Filtered DataFrame:\n{filtered_df.head().to_string()}")
683
 
 
786
  )
787
  preview_text = "\n".join(preview_lines)
788
 
789
+ # Auto-generate PDF after analysis
 
 
 
790
  pdf_file = None
791
+ status_msg = "Analysis completed successfully."
792
+ if all([summary, preview_text, anomalies, amc_reminders, insights, device_cards, filtered_df is not None]):
793
+ pdf_file = generate_pdf_content(
794
+ summary, preview_text, anomalies, amc_reminders, insights, device_cards,
795
+ daily_log_chart, weekly_uptime_chart, anomaly_alerts_chart, downtime_chart,
796
+ filtered_df, month_filter
797
+ )
798
+ if pdf_file:
799
+ status_msg = "Analysis completed successfully. PDF report generated and available for download."
800
+ else:
801
+ status_msg = "Analysis completed successfully, but failed to generate PDF. Check logs for details."
802
+ else:
803
+ status_msg = "Analysis completed, but some data is missing for PDF generation."
804
 
805
  elapsed_time = (datetime.now() - start_time).total_seconds()
806
  logging.info(f"Processing completed in {elapsed_time:.2f} seconds")
807
  if elapsed_time > 10:
808
  logging.warning(f"Processing time exceeded 10 seconds: {elapsed_time:.2f} seconds")
809
 
810
+ 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, summary, preview_text, anomalies, amc_reminders, insights, device_cards, filtered_df, status_msg)
 
 
 
811
  except Exception as e:
812
  logging.error(f"Failed to process file: {str(e)}")
813
+ return f"Error: {str(e)}", None, None, '<p>Error processing data.</p>', None, None, None, None, None, None, None, None, last_modified_state, None, None, None, None, None, None, f"Failed to process file: {str(e)}"
814
 
815
  # Update filter options
816
  def update_filters(file_obj):
 
868
  .dashboard-section ul {margin: 2px 0; padding-left: 20px;}
869
  """) as iface:
870
  gr.Markdown("<h1>LabOps Log Analyzer Dashboard</h1>")
871
+ gr.Markdown("Upload a CSV file to analyze. Click 'Analyze' to refresh the dashboard with the latest data. A PDF report will be generated automatically.")
872
 
873
  last_modified_state = gr.State(value=None)
874
  summary_state = gr.State()
 
878
  insights_state = gr.State()
879
  device_cards_state = gr.State()
880
  df_state = gr.State()
 
881
 
882
  with gr.Row():
883
  with gr.Column(scale=1):
 
961
 
962
  with gr.Group(elem_classes="dashboard-section"):
963
  gr.Markdown("### Export Report")
 
964
  pdf_output = gr.File(label="Download Monthly Status Report as PDF")
965
 
966
  file_input.change(
 
1001
  insights_state,
1002
  device_cards_state,
1003
  df_state,
 
1004
  status_message
1005
  ]
1006
  )
1007
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1008
  logging.info("Gradio interface initialized successfully")
1009
  except Exception as e:
1010
  logging.error(f"Failed to initialize Gradio interface: {str(e)}")