import gradio as gr import pandas as pd from utils.load_data import load_logs from utils.visualize import plot_usage from utils.report import generate_pdf from models.anomaly import detect_anomalies from utils.amc import upcoming_amc_devices import logging import os # Configure logging logging.basicConfig(level=logging.INFO, format="%(asctime)s - %(levelname)s - %(message)s") logger = logging.getLogger(__name__) def process_files(uploaded_files): """Process uploaded CSV files and generate dashboard outputs.""" logger.info(f"Received uploaded files: {uploaded_files}") # Handle Gradio's File component output: may be a tuple/list of lists if not uploaded_files: logger.warning("No files uploaded.") return "Please upload at least one valid CSV file.", None, None, None, None # Flatten the structure: uploaded_files might be (['path'],) or ['path'] if isinstance(uploaded_files, (tuple, list)) and len(uploaded_files) > 0: if isinstance(uploaded_files[0], list): valid_files = uploaded_files[0] # Extract the inner list else: valid_files = uploaded_files else: valid_files = [] # Filter out None values and ensure we have valid files valid_files = [f for f in valid_files if f is not None] if not valid_files: logger.warning("No valid files after filtering.") return "Please upload at least one valid CSV file.", None, None, None, None logger.info(f"Processing {len(valid_files)} valid files: {valid_files}") try: # Load data df = load_logs(valid_files) logger.info(f"Loaded {len(df)} log records from uploaded files.") # Log table log_table = df.head().to_dict(orient="records") # Usage chart logger.info("Generating usage plot...") fig = plot_usage(df) logger.info("Usage plot generated successfully.") # Anomalies anomaly_table = "Anomaly detection failed." try: anomalies = detect_anomalies(df) anomaly_table = anomalies.to_dict(orient="records") if not anomalies.empty else "No anomalies detected." except Exception as e: logger.error(f"Anomaly detection failed: {e}") # AMC expiries amc_table = None try: if "amc_expiry" in df.columns: logger.info("Processing AMC expiries...") amc_df = upcoming_amc_devices(df) amc_table = amc_df.to_dict(orient="records") if not amc_df.empty else "No upcoming AMC expiries." else: amc_table = "Column `amc_expiry` not found in uploaded data." logger.warning("Missing `amc_expiry` column in data.") except Exception as e: logger.error(f"AMC processing failed: {e}") amc_table = f"Error processing AMC expiries: {e}" return log_table, fig, anomaly_table, amc_table, df except Exception as e: logger.error(f"Error processing files: {e}") return f"Error: {e}", None, None, None, None def generate_pdf_report(df): """Generate and return path to PDF report, with error message if applicable.""" if df is None: logger.warning("No data available for PDF generation.") return None, "Please upload CSV files first." logger.info("Generating PDF report...") try: pdf_path = generate_pdf(df) return pdf_path, "PDF generated successfully." except Exception as e: logger.error(f"Failed to generate PDF: {e}") return None, f"Error generating PDF: {e}" with gr.Blocks(title="Multi-Device LabOps Dashboard") as demo: gr.Markdown("# 📊 Multi-Device LabOps Dashboard") with gr.Row(): file_input = gr.File(file_count="multiple", file_types=[".csv"], label="Upload Device Logs (CSV)") with gr.Row(): submit_btn = gr.Button("Process Files") with gr.Row(): with gr.Column(): gr.Markdown("## 📋 Uploaded Logs") log_output = gr.Dataframe() with gr.Column(): gr.Markdown("## 📈 Daily Usage Chart") chart_output = gr.Plot() with gr.Row(): with gr.Column(): gr.Markdown("## 🚨 Detected Anomalies") anomaly_output = gr.Dataframe() with gr.Column(): gr.Markdown("## 🛠 Upcoming AMC Devices") amc_output = gr.Dataframe() with gr.Row(): pdf_btn = gr.Button("📄 Generate PDF Report") pdf_output = gr.File(label="Download PDF Report") pdf_message = gr.Textbox(label="PDF Generation Status") # State to store dataframe df_state = gr.State() # Connect inputs to outputs submit_btn.click( fn=process_files, inputs=[file_input], outputs=[log_output, chart_output, anomaly_output, amc_output, df_state] ) pdf_btn.click( fn=generate_pdf_report, inputs=[df_state], outputs=[pdf_output, pdf_message] ) if __name__ == "__main__": try: logger.info("Application starting...") demo.launch(server_name="0.0.0.0", server_port=7860) except Exception as e: logger.error(f"Application failed to start: {e}") raise