MaheshP98 commited on
Commit
e2b7de3
·
verified ·
1 Parent(s): 91fa4de

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +173 -114
app.py CHANGED
@@ -1,144 +1,203 @@
1
  import gradio as gr
2
  import pandas as pd
3
- from utils.load_data import load_logs
4
- from utils.visualize import plot_usage
5
- from utils.report import generate_pdf
6
- from models.anomaly import detect_anomalies
7
- from utils.amc import upcoming_amc_devices
8
- import logging
9
  import os
 
 
 
 
10
 
11
  # Configure logging
12
- logging.basicConfig(level=logging.INFO, format="%(asctime)s - %(levelname)s - %(message)s")
13
- logger = logging.getLogger(__name__)
14
 
15
  def process_files(uploaded_files):
16
- """Process uploaded CSV files and generate dashboard outputs."""
17
- logger.info(f"Received uploaded files: {uploaded_files}")
18
- # Handle Gradio's File component output: may be a tuple/list of lists
 
19
  if not uploaded_files:
20
- logger.warning("No files uploaded.")
21
- return "Please upload at least one valid CSV file.", None, None, None, None
22
-
23
- # Flatten the structure: uploaded_files might be (['path'],) or ['path']
24
- if isinstance(uploaded_files, (tuple, list)) and len(uploaded_files) > 0:
25
- if isinstance(uploaded_files[0], list):
26
- valid_files = uploaded_files[0] # Extract the inner list
27
- else:
28
- valid_files = uploaded_files
29
- else:
30
- valid_files = []
31
 
32
- # Filter out None values and ensure we have valid files
33
- valid_files = [f for f in valid_files if f is not None]
34
  if not valid_files:
35
- logger.warning("No valid files after filtering.")
36
- return "Please upload at least one valid CSV file.", None, None, None, None
37
-
38
- logger.info(f"Processing {len(valid_files)} valid files: {valid_files}")
39
- try:
40
- # Load data
41
- df = load_logs(valid_files)
42
- logger.info(f"Loaded {len(df)} log records from uploaded files.")
43
 
44
- # Log table
45
- log_table = df.head().to_dict(orient="records")
46
 
47
- # Usage chart
48
- logger.info("Generating usage plot...")
49
- fig = plot_usage(df)
50
- logger.info("Usage plot generated successfully.")
51
-
52
- # Anomalies
53
- anomaly_table = "Anomaly detection failed."
54
  try:
55
- anomalies = detect_anomalies(df)
56
- anomaly_table = anomalies.to_dict(orient="records") if not anomalies.empty else "No anomalies detected."
 
 
57
  except Exception as e:
58
- logger.error(f"Anomaly detection failed: {e}")
 
59
 
60
- # AMC expiries
61
- amc_table = None
62
- try:
63
- if "amc_expiry" in df.columns:
64
- logger.info("Processing AMC expiries...")
65
- amc_df = upcoming_amc_devices(df)
66
- amc_table = amc_df.to_dict(orient="records") if not amc_df.empty else "No upcoming AMC expiries."
67
- else:
68
- amc_table = "Column `amc_expiry` not found in uploaded data."
69
- logger.warning("Missing `amc_expiry` column in data.")
70
- except Exception as e:
71
- logger.error(f"AMC processing failed: {e}")
72
- amc_table = f"Error processing AMC expiries: {e}"
73
 
74
- return log_table, fig, anomaly_table, amc_table, df
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
75
  except Exception as e:
76
- logger.error(f"Error processing files: {e}")
77
- return f"Error: {e}", None, None, None, None
78
 
79
- def generate_pdf_report(df):
80
- """Generate and return path to PDF report, with error message if applicable."""
81
- if df is None:
82
- logger.warning("No data available for PDF generation.")
83
- return None, "Please upload CSV files first."
84
- logger.info("Generating PDF report...")
85
  try:
86
- pdf_path = generate_pdf(df)
87
- return pdf_path, "PDF generated successfully."
 
 
 
 
88
  except Exception as e:
89
- logger.error(f"Failed to generate PDF: {e}")
90
- return None, f"Error generating PDF: {e}"
91
 
92
- with gr.Blocks(title="Multi-Device LabOps Dashboard") as demo:
93
- gr.Markdown("# 📊 Multi-Device LabOps Dashboard")
94
-
95
- with gr.Row():
96
- file_input = gr.File(file_count="multiple", file_types=[".csv"], label="Upload Device Logs (CSV)")
97
-
98
- with gr.Row():
99
- submit_btn = gr.Button("Process Files")
100
-
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
101
  with gr.Row():
102
- with gr.Column():
103
- gr.Markdown("## 📋 Uploaded Logs")
104
- log_output = gr.Dataframe()
105
- with gr.Column():
106
- gr.Markdown("## 📈 Daily Usage Chart")
107
- chart_output = gr.Plot()
108
-
109
  with gr.Row():
110
- with gr.Column():
111
- gr.Markdown("## 🚨 Detected Anomalies")
112
- anomaly_output = gr.Dataframe()
113
- with gr.Column():
114
- gr.Markdown("## 🛠 Upcoming AMC Devices")
115
- amc_output = gr.Dataframe()
116
-
117
  with gr.Row():
118
- pdf_btn = gr.Button("📄 Generate PDF Report")
119
- pdf_output = gr.File(label="Download PDF Report")
120
- pdf_message = gr.Textbox(label="PDF Generation Status")
121
 
122
- # State to store dataframe
123
- df_state = gr.State()
124
-
125
- # Connect inputs to outputs
126
- submit_btn.click(
127
  fn=process_files,
128
  inputs=[file_input],
129
- outputs=[log_output, chart_output, anomaly_output, amc_output, df_state]
130
- )
131
-
132
- pdf_btn.click(
133
- fn=generate_pdf_report,
134
- inputs=[df_state],
135
- outputs=[pdf_output, pdf_message]
136
  )
137
 
138
  if __name__ == "__main__":
139
- try:
140
- logger.info("Application starting...")
141
- demo.launch(server_name="0.0.0.0", server_port=7860)
142
- except Exception as e:
143
- logger.error(f"Application failed to start: {e}")
144
- raise
 
1
  import gradio as gr
2
  import pandas as pd
3
+ import matplotlib.pyplot as plt
4
+ from sklearn.ensemble import IsolationForest
5
+ from datetime import datetime, timedelta
 
 
 
6
  import os
7
+ import logging
8
+ from reportlab.lib.pagesizes import letter
9
+ from reportlab.pdfgen import canvas
10
+ import tempfile
11
 
12
  # Configure logging
13
+ logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
 
14
 
15
  def process_files(uploaded_files):
16
+ """
17
+ Process uploaded CSV files, generate usage plots, detect anomalies, and process AMC expiries.
18
+ Returns a dataframe, plot, PDF path, and status message.
19
+ """
20
  if not uploaded_files:
21
+ logging.warning("No files uploaded.")
22
+ return None, None, None, "Please upload at least one valid CSV file."
 
 
 
 
 
 
 
 
 
23
 
24
+ valid_files = [f for f in uploaded_files if f.name.endswith('.csv')]
 
25
  if not valid_files:
26
+ logging.warning("No valid CSV files uploaded.")
27
+ return None, None, None, "Please upload at least one valid CSV file."
 
 
 
 
 
 
28
 
29
+ logging.info(f"Processing {len(valid_files)} valid files: {[f.name for f in valid_files]}")
30
+ all_data = []
31
 
32
+ # Load and combine CSV files
33
+ for file in valid_files:
 
 
 
 
 
34
  try:
35
+ logging.info(f"Loading logs from {file.name}")
36
+ df = pd.read_csv(file.name)
37
+ logging.info(f"Loaded {len(df)} records from {file.name}")
38
+ all_data.append(df)
39
  except Exception as e:
40
+ logging.error(f"Failed to load {file.name}: {str(e)}")
41
+ return None, None, None, f"Error loading {file.name}: {str(e)}"
42
 
43
+ if not all_data:
44
+ logging.warning("No data loaded from uploaded files.")
45
+ return None, None, None, "No valid data found in uploaded files."
46
+
47
+ combined_df = pd.concat(all_data, ignore_index=True)
48
+ logging.info(f"Combined {len(combined_df)} total records.")
49
+ logging.info(f"CSV columns: {combined_df.columns.tolist()}")
 
 
 
 
 
 
50
 
51
+ # Generate usage plot
52
+ plot_path = generate_usage_plot(combined_df)
53
+
54
+ # Detect anomalies
55
+ anomaly_df = detect_anomalies(combined_df)
56
+
57
+ # Process AMC expiries
58
+ amc_message, amc_df = process_amc_expiries(combined_df)
59
+
60
+ # Generate PDF report
61
+ pdf_path = generate_pdf_report(combined_df, anomaly_df, amc_df)
62
+
63
+ # Prepare output dataframe (combine original data with anomalies)
64
+ output_df = combined_df.copy()
65
+ if anomaly_df is not None:
66
+ output_df['anomaly'] = anomaly_df['anomaly']
67
+
68
+ return output_df, plot_path, pdf_path, amc_message
69
+
70
+ def generate_usage_plot(df):
71
+ """
72
+ Generate a bar plot of usage_count by equipment and status.
73
+ Returns the path to the saved plot.
74
+ """
75
+ logging.info("Generating usage plot...")
76
+ try:
77
+ plt.figure(figsize=(10, 6))
78
+ for status in df['status'].unique():
79
+ subset = df[df['status'] == status]
80
+ plt.bar(subset['equipment'] + f" ({status})", subset['usage_count'], label=status)
81
+ plt.xlabel("Equipment (Status)")
82
+ plt.ylabel("Usage Count")
83
+ plt.title("Usage Count by Equipment and Status")
84
+ plt.legend()
85
+ plt.xticks(rotation=45)
86
+ plt.tight_layout()
87
+
88
+ # Save plot to temporary file
89
+ with tempfile.NamedTemporaryFile(delete=False, suffix='.png') as tmp:
90
+ plt.savefig(tmp.name, format='png')
91
+ plot_path = tmp.name
92
+ plt.close()
93
+ logging.info("Usage plot generated successfully.")
94
+ return plot_path
95
  except Exception as e:
96
+ logging.error(f"Failed to generate usage plot: {str(e)}")
97
+ return None
98
 
99
+ def detect_anomalies(df):
100
+ """
101
+ Detect anomalies in usage_count using Isolation Forest.
102
+ Returns a dataframe with an 'anomaly' column (-1 for anomalies, 1 for normal).
103
+ """
104
+ logging.info("Detecting anomalies...")
105
  try:
106
+ model = IsolationForest(contamination=0.1, random_state=42)
107
+ anomalies = model.fit_predict(df[['usage_count']].values)
108
+ anomaly_df = df.copy()
109
+ anomaly_df['anomaly'] = anomalies
110
+ logging.info(f"Detected {sum(anomalies == -1)} anomalies.")
111
+ return anomaly_df
112
  except Exception as e:
113
+ logging.error(f"Failed to detect anomalies: {str(e)}")
114
+ return None
115
 
116
+ def process_amc_expiries(df):
117
+ """
118
+ Identify devices with AMC expiries within 7 days from 2025-06-05.
119
+ Returns a message and a dataframe of devices with upcoming expiries.
120
+ """
121
+ logging.info("Processing AMC expiries...")
122
+ try:
123
+ current_date = datetime(2025, 6, 5)
124
+ threshold = current_date + timedelta(days=7)
125
+ df['amc_expiry'] = pd.to_datetime(df['amc_expiry'])
126
+ upcoming_expiries = df[df['amc_expiry'] <= threshold]
127
+ unique_devices = upcoming_expiries['equipment'].unique()
128
+ message = f"Found {len(unique_devices)} devices with upcoming AMC expiries: {', '.join(unique_devices)}"
129
+ logging.info(message)
130
+ return message, upcoming_expiries
131
+ except Exception as e:
132
+ logging.error(f"Failed to process AMC expiries: {str(e)}")
133
+ return f"Error processing AMC expiries: {str(e)}", None
134
+
135
+ def generate_pdf_report(original_df, anomaly_df, amc_df):
136
+ """
137
+ Generate a PDF report with data summary, anomalies, and AMC expiries.
138
+ Returns the path to the saved PDF.
139
+ """
140
+ logging.info("Generating PDF report...")
141
+ try:
142
+ with tempfile.NamedTemporaryFile(delete=False, suffix='.pdf') as tmp:
143
+ c = canvas.Canvas(tmp.name, pagesize=letter)
144
+ c.drawString(100, 750, "Equipment Log Analysis Report")
145
+ y = 700
146
+
147
+ # Summary
148
+ c.drawString(100, y, f"Total Records: {len(original_df)}")
149
+ c.drawString(100, y-20, f"Devices: {', '.join(original_df['equipment'].unique())}")
150
+ y -= 40
151
+
152
+ # Anomalies
153
+ if anomaly_df is not None:
154
+ num_anomalies = sum(anomaly_df['anomaly'] == -1)
155
+ c.drawString(100, y, f"Anomalies Detected: {num_anomalies}")
156
+ if num_anomalies > 0:
157
+ anomaly_equipment = anomaly_df[anomaly_df['anomaly'] == -1]['equipment'].unique()
158
+ c.drawString(100, y-20, f"Anomalous Devices: {', '.join(anomaly_equipment)}")
159
+ y -= 40
160
+ else:
161
+ c.drawString(100, y, "Anomaly detection failed.")
162
+ y -= 20
163
+
164
+ # AMC Expiries
165
+ if amc_df is not None:
166
+ c.drawString(100, y, f"Devices with Upcoming AMC Expiries: {len(amc_df['equipment'].unique())}")
167
+ for _, row in amc_df.iterrows():
168
+ c.drawString(100, y-20, f"{row['equipment']}: {row['amc_expiry'].strftime('%Y-%m-%d')}")
169
+ y -= 20
170
+ else:
171
+ c.drawString(100, y, "No AMC expiry data available.")
172
+ y -= 20
173
+
174
+ c.showPage()
175
+ c.save()
176
+ pdf_path = tmp.name
177
+ logging.info("PDF report generated successfully.")
178
+ return pdf_path
179
+ except Exception as e:
180
+ logging.error(f"Failed to generate PDF report: {str(e)}")
181
+ return None
182
+
183
+ # Gradio interface
184
+ with gr.Blocks() as demo:
185
+ gr.Markdown("# Equipment Log Analysis")
186
  with gr.Row():
187
+ file_input = gr.File(file_count="multiple", label="Upload CSV Files")
188
+ process_button = gr.Button("Process Files")
 
 
 
 
 
189
  with gr.Row():
190
+ output_df = gr.Dataframe(label="Processed Data")
191
+ output_plot = gr.Image(label="Usage Plot")
 
 
 
 
 
192
  with gr.Row():
193
+ output_message = gr.Textbox(label="AMC Expiry Status")
194
+ output_pdf = gr.File(label="Download PDF Report")
 
195
 
196
+ process_button.click(
 
 
 
 
197
  fn=process_files,
198
  inputs=[file_input],
199
+ outputs=[output_df, output_plot, output_pdf, output_message]
 
 
 
 
 
 
200
  )
201
 
202
  if __name__ == "__main__":
203
+ demo.launch(server_name="0.0.0.0", server_port=7860)