RathodHarish commited on
Commit
ce507e4
·
verified ·
1 Parent(s): c5bb90e

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +127 -163
app.py CHANGED
@@ -3,18 +3,18 @@ import pandas as pd
3
  from datetime import datetime
4
  import logging
5
  import plotly.express as px
6
- from sklearn.ensemble import IsolationForest
7
  from transformers import pipeline
8
- import torch
9
 
10
- # Configure logging
11
  logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
12
 
13
- # Preload Hugging Face summarization model
14
  logging.info("Preloading Hugging Face model...")
15
  try:
16
  device = 0 if torch.cuda.is_available() else -1
17
- summarizer = pipeline("summarization", model="sshleifer/distilbart-cnn-12-6", device=device)
18
  logging.info(f"Hugging Face model preloaded successfully on device: {'GPU' if device == 0 else 'CPU'}")
19
  except Exception as e:
20
  logging.error(f"Failed to preload model: {str(e)}")
@@ -26,57 +26,73 @@ def summarize_logs(df, progress=gr.Progress()):
26
  try:
27
  total_devices = df["device_id"].nunique()
28
  most_used = df.groupby("device_id")["usage_hours"].sum().idxmax() if not df.empty else "N/A"
 
29
  prompt = f"Maintenance logs: {total_devices} devices. Most used: {most_used}."
30
  summary = summarizer(prompt, max_length=50, min_length=10, do_sample=False)[0]["summary_text"]
 
31
  return summary
32
  except Exception as e:
33
  logging.error(f"Summary generation failed: {str(e)}")
34
  return "Failed to generate summary."
35
 
36
- # Anomaly Detection
37
  def detect_anomalies(df, progress=gr.Progress()):
38
  progress(0.4, "Detecting anomalies...")
39
  try:
40
  if "usage_hours" not in df.columns or "downtime" not in df.columns:
41
- return None, "Anomaly detection requires 'usage_hours' and 'downtime' columns."
 
 
42
  if len(df) > 5000:
43
  df = df.sample(n=5000, random_state=42)
44
  logging.info("Sampled data for anomaly detection to 5,000 rows")
 
45
  features = df[["usage_hours", "downtime"]].fillna(0)
46
  iso_forest = IsolationForest(contamination=0.1, random_state=42, n_jobs=-1)
47
  df["anomaly"] = iso_forest.fit_predict(features)
 
48
  anomalies = df[df["anomaly"] == -1][["device_id", "usage_hours", "downtime", "timestamp"]]
49
  if anomalies.empty:
50
- return None, "No anomalies detected."
51
- anomaly_lines = []
 
52
  for idx, row in anomalies.head(5).iterrows():
53
- anomaly_lines.append(f"Device ID: {row['device_id']}, Usage Hours: {row['usage_hours']}, Downtime: {row['downtime']}, Timestamp: {row['timestamp']}")
54
- return anomaly_lines, None
 
 
55
  except Exception as e:
56
  logging.error(f"Anomaly detection failed: {str(e)}")
57
- return None, f"Anomaly detection failed: {str(e)}"
58
 
59
- # AMC Reminders
60
  def check_amc_reminders(df, current_date, progress=gr.Progress()):
61
  progress(0.6, "Checking AMC reminders...")
62
  try:
63
  if "device_id" not in df.columns or "amc_date" not in df.columns:
64
- return None, "AMC reminders require 'device_id' and 'amc_date' columns."
 
 
65
  df["amc_date"] = pd.to_datetime(df["amc_date"], errors='coerce')
66
  current_date = pd.to_datetime(current_date)
 
67
  df["days_to_amc"] = (df["amc_date"] - current_date).dt.days
68
  reminders = df[(df["days_to_amc"] >= 0) & (df["days_to_amc"] <= 30)][["device_id", "amc_date"]]
 
69
  if reminders.empty:
70
- return None, "No AMC reminders due within the next 30 days."
71
- reminder_lines = []
 
72
  for idx, row in reminders.head(5).iterrows():
73
- reminder_lines.append(f"Device ID: {row['device_id']}, AMC Date: {row['amc_date']}")
74
- return reminder_lines, None
 
 
75
  except Exception as e:
76
  logging.error(f"AMC reminder generation failed: {str(e)}")
77
- return None, f"AMC reminder generation failed: {str(e)}"
78
 
79
- # Dashboard Insights
80
  def generate_dashboard_insights(df, progress=gr.Progress()):
81
  progress(0.8, "Generating dashboard insights...")
82
  try:
@@ -84,41 +100,57 @@ def generate_dashboard_insights(df, progress=gr.Progress()):
84
  avg_usage = df["usage_hours"].mean() if "usage_hours" in df.columns else 0
85
  prompt = f"Insights: {total_devices} devices, avg usage {avg_usage:.2f} hours."
86
  insights = summarizer(prompt, max_length=50, min_length=10, do_sample=False)[0]["summary_text"]
 
87
  return insights
88
  except Exception as e:
89
  logging.error(f"Dashboard insights generation failed: {str(e)}")
90
  return f"Dashboard insights generation failed: {str(e)}"
91
 
92
- # Create usage chart data for LaTeX table
93
- def create_usage_chart_data(df, progress=gr.Progress()):
94
- progress(0.9, "Creating usage chart data...")
95
  try:
96
  usage_data = df.groupby("device_id")["usage_hours"].sum().reset_index()
97
  if len(usage_data) > 5:
98
  usage_data = usage_data.nlargest(5, "usage_hours")
99
  logging.info("Limited chart data to top 5 devices")
100
- chart_lines = []
101
- for idx, row in usage_data.iterrows():
102
- chart_lines.append(f"{row['device_id']} & {row['usage_hours']:.2f} \\\\")
103
- return chart_lines
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
104
  except Exception as e:
105
- logging.error(f"Failed to create usage chart data: {str(e)}")
106
- return []
107
 
108
  # Main Gradio function
109
  async def process_logs(file_obj, row_limit=10000, progress=gr.Progress()):
110
  try:
111
  progress(0, "Starting file processing...")
112
  if file_obj is None:
113
- logging.warning("No file uploaded")
114
- return "No file uploaded."
115
 
116
  file_name = file_obj.name if hasattr(file_obj, 'name') else file_obj
117
  logging.info(f"Processing file: {file_name}")
118
 
119
  if not file_name.endswith(".csv"):
120
  logging.error("Unsupported file format")
121
- return "Unsupported file format. Please upload a CSV file."
122
 
123
  progress(0.05, "Loading CSV file...")
124
  try:
@@ -133,177 +165,109 @@ async def process_logs(file_obj, row_limit=10000, progress=gr.Progress()):
133
  logging.info(f"File loaded successfully with {len(df)} rows (limited to {row_limit} rows)")
134
  except Exception as e:
135
  logging.error(f"Failed to load CSV: {str(e)}")
136
- return f"Failed to load CSV: {str(e)}"
137
 
138
  progress(0.1, "Converting timestamps...")
139
  try:
140
  df["timestamp"] = pd.to_datetime(df["timestamp"], errors='coerce')
141
  except Exception as e:
142
  logging.error(f"Date conversion failed: {str(e)}")
143
- return f"Failed to convert timestamp to datetime: {str(e)}"
144
 
145
  if df.empty:
146
  logging.warning("No data in the file")
147
- return "No data available in the file."
148
 
149
  # Step 1: Summary Report
150
- summary = summarize_logs(df, progress)
 
151
 
152
  # Step 2: Log Preview
153
- preview_lines = []
154
  if not df.empty:
 
155
  for idx, row in df.head().iterrows():
156
- preview_lines.append(f"Row {idx + 1}: Device ID: {row['device_id']}, Timestamp: {row['timestamp']}, Usage Hours: {row['usage_hours']}, Downtime: {row['downtime']}, AMC Date: {row['amc_date']}")
 
157
  else:
158
- preview_lines = ["No data available."]
159
 
160
- # Step 3: Usage Chart (Textual Data)
161
- chart_data = create_usage_chart_data(df, progress)
162
 
163
  # Step 4: Anomaly Detection
164
- anomaly_lines, anomaly_error = detect_anomalies(df, progress)
165
- if anomaly_error:
166
- anomaly_lines = [anomaly_error]
167
 
168
  # Step 5: AMC Reminders
169
- reminder_lines, reminder_error = check_amc_reminders(df, datetime.now(), progress)
170
- if reminder_error:
171
- reminder_lines = [reminder_error]
172
 
173
  # Step 6: Dashboard Insights
174
- insights = generate_dashboard_insights(df, progress)
175
-
176
- # Define the LaTeX template
177
- latex_template = r"""
178
- % Defining document class and basic setup
179
- \documentclass[a4paper,10pt]{article}
180
- \usepackage[utf8]{inputenc}
181
- \usepackage[T1]{fontenc}
182
- \usepackage{geometry}
183
- \geometry{margin=1in}
184
- \usepackage{amsmath}
185
- \usepackage{amsfonts}
186
- \usepackage{graphicx}
187
- \usepackage{booktabs}
188
- \usepackage{enumitem}
189
- \usepackage{hyperref}
190
- \usepackage{xcolor}
191
- \usepackage{titlesec}
192
-
193
- % Setting up fonts and language support (using PDFLaTeX compatible fonts)
194
- \usepackage{mathptmx} % Times-compatible font for math and text
195
- \usepackage{helvet} % Helvetica for sans-serif
196
- \usepackage{courier} % Courier for monospace
197
-
198
- % Customizing section headers
199
- \titleformat{\section}{\large\bfseries}{\thesection}{1em}{}
200
- \titleformat{\subsection}{\normalsize\bfseries}{\thesubsection}{1em}{}
201
-
202
- % Reducing spacing in lists and sections
203
- \setlist{itemsep=2pt,parsep=2pt,topsep=4pt}
204
- \titlespacing*{\section}{0pt}{8pt}{4pt}
205
- \titlespacing*{\subsection}{0pt}{6pt}{2pt}
206
-
207
- % Document begins here
208
- \begin{document}
209
-
210
- % Title and date
211
- \title{LabOps Log Analyzer Report}
212
- \author{Automated Analysis}
213
- \date{June 05, 2025}
214
- \maketitle
215
-
216
- % Introduction
217
- \section*{Introduction}
218
- This report presents the analysis of lab equipment logs as of June 05, 2025. The analysis includes a summary, log preview, usage statistics, anomaly detection, AMC reminders, and AI-generated insights.
219
-
220
- % Step 1: Summary Report
221
- \section{Summary Report}
222
- {{SUMMARY_REPORT}}
223
-
224
- % Step 2: Log Preview
225
- \section{Log Preview (First 5 Rows)}
226
- \begin{itemize}
227
- {{LOG_PREVIEW}}
228
- \end{itemize}
229
-
230
- % Step 3: Usage Chart (Textual Description)
231
- \section{Usage Chart}
232
- The following table summarizes the usage hours for the top 5 devices:
233
- \begin{table}[h]
234
- \centering
235
- \begin{tabular}{lc}
236
- \toprule
237
- \textbf{Device ID} & \textbf{Usage Hours} \\
238
- \midrule
239
- {{USAGE_CHART_DATA}}
240
- \bottomrule
241
- \end{tabular}
242
- \caption{Usage Hours per Device}
243
- \end{table}
244
-
245
- % Step 4: Anomaly Detection
246
- \section{Anomaly Detection}
247
- \begin{itemize}
248
- {{ANOMALY_DETECTION}}
249
- \end{itemize}
250
-
251
- % Step 5: AMC Reminders
252
- \section{AMC Reminders}
253
- \begin{itemize}
254
- {{AMC_REMINDERS}}
255
- \end{itemize}
256
-
257
- % Step 6: Dashboard Insights
258
- \section{Dashboard Insights (AI)}
259
- {{DASHBOARD_INSIGHTS}}
260
-
261
- % End of document
262
- \end{document}
263
- """
264
-
265
- # Populate the LaTeX template with the output data
266
- latex_content = latex_template.replace("{{SUMMARY_REPORT}}", summary.replace("%", "\%"))
267
- latex_content = latex_content.replace("{{LOG_PREVIEW}}", "\n".join([f"\\item {line}" for line in preview_lines]).replace("%", "\%"))
268
- latex_content = latex_content.replace("{{USAGE_CHART_DATA}}", "\n".join(chart_data).replace("%", "\%"))
269
- latex_content = latex_content.replace("{{ANOMALY_DETECTION}}", "\n".join([f"\\item {line}" for line in anomaly_lines]).replace("%", "\%"))
270
- latex_content = latex_content.replace("{{AMC_REMINDERS}}", "\n".join([f"\\item {line}" for line in reminder_lines]).replace("%", "\%"))
271
- latex_content = latex_content.replace("{{DASHBOARD_INSIGHTS}}", insights.replace("%", "\%"))
272
-
273
- # Construct the xaiArtifact tag with proper indentation
274
- artifact_content = (
275
- '<xaiArtifact artifact_id="05686e4a-85a8-4140-8113-1e86048b96fc" '
276
- 'artifact_version_id="48e949a0-ea29-45d9-b47d-7f21069da965" '
277
- 'title="LabOps_Analysis_Report.tex" contentType="text/latex">\n'
278
- f'{latex_content}\n'
279
- '</xaiArtifact>'
280
- )
281
-
282
- return artifact_content
283
 
 
 
284
  except Exception as e:
285
  logging.error(f"Failed to process file: {str(e)}")
286
- return f"Failed to process file: {str(e)}"
287
 
288
- # Gradio Interface
289
  try:
290
  logging.info("Initializing Gradio Blocks interface...")
291
- with gr.Blocks() as iface:
 
 
 
 
 
 
 
 
292
  gr.Markdown("<h1>LabOps Log Analyzer Dashboard (Hugging Face AI)</h1>")
293
- gr.Markdown("Upload a CSV file to generate a PDF report.")
294
 
295
  with gr.Row():
296
  with gr.Column(scale=1):
297
  file_input = gr.File(label="Upload Logs (CSV)", file_types=[".csv"])
298
- submit_button = gr.Button("Generate PDF Report", variant="primary")
299
 
300
  with gr.Column(scale=2):
301
- output = gr.Markdown()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
302
 
303
  submit_button.click(
304
  fn=process_logs,
305
  inputs=[file_input],
306
- outputs=[output]
307
  )
308
 
309
  logging.info("Gradio interface initialized successfully")
 
3
  from datetime import datetime
4
  import logging
5
  import plotly.express as px
6
+ from sklearn.ensemble import IsolationForest # For anomaly detection
7
  from transformers import pipeline
8
+ import torch # For GPU availability check
9
 
10
+ # Configure logging for debugging
11
  logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
12
 
13
+ # Preload Hugging Face summarization model at startup
14
  logging.info("Preloading Hugging Face model...")
15
  try:
16
  device = 0 if torch.cuda.is_available() else -1
17
+ summarizer = pipeline("summarization", model="sshleifer/distilbart-cnn-12-6", device=device) # Lighter model
18
  logging.info(f"Hugging Face model preloaded successfully on device: {'GPU' if device == 0 else 'CPU'}")
19
  except Exception as e:
20
  logging.error(f"Failed to preload model: {str(e)}")
 
26
  try:
27
  total_devices = df["device_id"].nunique()
28
  most_used = df.groupby("device_id")["usage_hours"].sum().idxmax() if not df.empty else "N/A"
29
+
30
  prompt = f"Maintenance logs: {total_devices} devices. Most used: {most_used}."
31
  summary = summarizer(prompt, max_length=50, min_length=10, do_sample=False)[0]["summary_text"]
32
+ logging.info("Summary generated successfully")
33
  return summary
34
  except Exception as e:
35
  logging.error(f"Summary generation failed: {str(e)}")
36
  return "Failed to generate summary."
37
 
38
+ # Anomaly Detection using Isolation Forest with sampling for large datasets
39
  def detect_anomalies(df, progress=gr.Progress()):
40
  progress(0.4, "Detecting anomalies...")
41
  try:
42
  if "usage_hours" not in df.columns or "downtime" not in df.columns:
43
+ logging.warning("Required columns for anomaly detection not found")
44
+ return "Anomaly detection requires 'usage_hours' and 'downtime' columns."
45
+
46
  if len(df) > 5000:
47
  df = df.sample(n=5000, random_state=42)
48
  logging.info("Sampled data for anomaly detection to 5,000 rows")
49
+
50
  features = df[["usage_hours", "downtime"]].fillna(0)
51
  iso_forest = IsolationForest(contamination=0.1, random_state=42, n_jobs=-1)
52
  df["anomaly"] = iso_forest.fit_predict(features)
53
+
54
  anomalies = df[df["anomaly"] == -1][["device_id", "usage_hours", "downtime", "timestamp"]]
55
  if anomalies.empty:
56
+ return "No anomalies detected."
57
+
58
+ anomaly_lines = ["**Detected Anomalies:**"]
59
  for idx, row in anomalies.head(5).iterrows():
60
+ anomaly_lines.append(f"- Device ID: {row['device_id']}, Usage Hours: {row['usage_hours']}, Downtime: {row['downtime']}, Timestamp: {row['timestamp']}")
61
+ anomaly_list = "\n".join(anomaly_lines)
62
+ logging.info("Anomalies detected successfully")
63
+ return anomaly_list
64
  except Exception as e:
65
  logging.error(f"Anomaly detection failed: {str(e)}")
66
+ return f"Anomaly detection failed: {str(e)}"
67
 
68
+ # AMC Reminders based on device and AMC date
69
  def check_amc_reminders(df, current_date, progress=gr.Progress()):
70
  progress(0.6, "Checking AMC reminders...")
71
  try:
72
  if "device_id" not in df.columns or "amc_date" not in df.columns:
73
+ logging.warning("Required columns for AMC reminders not found")
74
+ return "AMC reminders require 'device_id' and 'amc_date' columns."
75
+
76
  df["amc_date"] = pd.to_datetime(df["amc_date"], errors='coerce')
77
  current_date = pd.to_datetime(current_date)
78
+
79
  df["days_to_amc"] = (df["amc_date"] - current_date).dt.days
80
  reminders = df[(df["days_to_amc"] >= 0) & (df["days_to_amc"] <= 30)][["device_id", "amc_date"]]
81
+
82
  if reminders.empty:
83
+ return "No AMC reminders due within the next 30 days."
84
+
85
+ reminder_lines = ["**Upcoming AMC Reminders:**"]
86
  for idx, row in reminders.head(5).iterrows():
87
+ reminder_lines.append(f"- Device ID: {row['device_id']}, AMC Date: {row['amc_date']}")
88
+ reminder_list = "\n".join(reminder_lines)
89
+ logging.info("AMC reminders generated successfully")
90
+ return reminder_list
91
  except Exception as e:
92
  logging.error(f"AMC reminder generation failed: {str(e)}")
93
+ return f"AMC reminder generation failed: {str(e)}"
94
 
95
+ # Dashboard Insights (AI-generated executive-level insights)
96
  def generate_dashboard_insights(df, progress=gr.Progress()):
97
  progress(0.8, "Generating dashboard insights...")
98
  try:
 
100
  avg_usage = df["usage_hours"].mean() if "usage_hours" in df.columns else 0
101
  prompt = f"Insights: {total_devices} devices, avg usage {avg_usage:.2f} hours."
102
  insights = summarizer(prompt, max_length=50, min_length=10, do_sample=False)[0]["summary_text"]
103
+ logging.info("Dashboard insights generated successfully")
104
  return insights
105
  except Exception as e:
106
  logging.error(f"Dashboard insights generation failed: {str(e)}")
107
  return f"Dashboard insights generation failed: {str(e)}"
108
 
109
+ # Create a bar chart for usage hours per device
110
+ def create_usage_chart(df, progress=gr.Progress()):
111
+ progress(0.9, "Creating usage chart...")
112
  try:
113
  usage_data = df.groupby("device_id")["usage_hours"].sum().reset_index()
114
  if len(usage_data) > 5:
115
  usage_data = usage_data.nlargest(5, "usage_hours")
116
  logging.info("Limited chart data to top 5 devices")
117
+
118
+ custom_colors = ['#FF6B6B', '#4ECDC4', '#45B7D1', '#96CEB4']
119
+ fig = px.bar(
120
+ usage_data,
121
+ x="device_id",
122
+ y="usage_hours",
123
+ title="Usage Hours per Device",
124
+ labels={"device_id": "Device ID", "usage_hours": "Usage Hours"},
125
+ color="device_id",
126
+ color_discrete_sequence=custom_colors
127
+ )
128
+ fig.update_layout(
129
+ title_font_size=16,
130
+ margin=dict(l=20, r=20, t=40, b=20),
131
+ plot_bgcolor="white",
132
+ paper_bgcolor="white",
133
+ font=dict(size=12)
134
+ )
135
+ return fig
136
  except Exception as e:
137
+ logging.error(f"Failed to create usage chart: {str(e)}")
138
+ return None
139
 
140
  # Main Gradio function
141
  async def process_logs(file_obj, row_limit=10000, progress=gr.Progress()):
142
  try:
143
  progress(0, "Starting file processing...")
144
  if file_obj is None:
145
+ logging.warning("No file uploaded, returning empty results")
146
+ return "No file uploaded.", "No data to preview.", None, "No anomalies detected.", "No AMC reminders.", "No insights generated."
147
 
148
  file_name = file_obj.name if hasattr(file_obj, 'name') else file_obj
149
  logging.info(f"Processing file: {file_name}")
150
 
151
  if not file_name.endswith(".csv"):
152
  logging.error("Unsupported file format")
153
+ return "Unsupported file format. Please upload a CSV file.", None, None, None, None, None
154
 
155
  progress(0.05, "Loading CSV file...")
156
  try:
 
165
  logging.info(f"File loaded successfully with {len(df)} rows (limited to {row_limit} rows)")
166
  except Exception as e:
167
  logging.error(f"Failed to load CSV: {str(e)}")
168
+ return f"Failed to load CSV: {str(e)}", None, None, None, None, None
169
 
170
  progress(0.1, "Converting timestamps...")
171
  try:
172
  df["timestamp"] = pd.to_datetime(df["timestamp"], errors='coerce')
173
  except Exception as e:
174
  logging.error(f"Date conversion failed: {str(e)}")
175
+ return f"Failed to convert timestamp to datetime: {str(e)}", None, None, None, None, None
176
 
177
  if df.empty:
178
  logging.warning("No data in the file")
179
+ return "No data available in the file.", "No data to preview.", None, "No anomalies detected.", "No AMC reminders.", "No insights generated."
180
 
181
  # Step 1: Summary Report
182
+ progress(0.2, "Generating summary...")
183
+ summary = f"**Step 1: Summary Report** \n{summarize_logs(df, progress)}"
184
 
185
  # Step 2: Log Preview
186
+ progress(0.3, "Previewing logs...")
187
  if not df.empty:
188
+ preview_lines = ["**Step 2: Log Preview (First 5 Rows)**"]
189
  for idx, row in df.head().iterrows():
190
+ preview_lines.append(f"**Row {idx + 1}:** Device ID: {row['device_id']}, Timestamp: {row['timestamp']}, Usage Hours: {row['usage_hours']}, Downtime: {row['downtime']}, AMC Date: {row['amc_date']}")
191
+ preview = "\n".join(preview_lines)
192
  else:
193
+ preview = "**Step 2: Log Preview** \nNo data available."
194
 
195
+ # Step 3: Usage Chart
196
+ chart = create_usage_chart(df, progress)
197
 
198
  # Step 4: Anomaly Detection
199
+ anomalies = f"**Step 3: Anomaly Detection** \n{detect_anomalies(df, progress)}"
 
 
200
 
201
  # Step 5: AMC Reminders
202
+ amc_reminders = f"**Step 4: AMC Reminders** \n{check_amc_reminders(df, datetime.now(), progress)}"
 
 
203
 
204
  # Step 6: Dashboard Insights
205
+ insights = f"**Step 5: Dashboard Insights (AI)** \n{generate_dashboard_insights(df, progress)}"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
206
 
207
+ progress(1.0, "Processing complete!")
208
+ return summary, preview, chart, anomalies, amc_reminders, insights
209
  except Exception as e:
210
  logging.error(f"Failed to process file: {str(e)}")
211
+ return f"Failed to process file: {str(e)}", None, None, None, None, None
212
 
213
+ # Gradio Interface with Step-by-Step Layout
214
  try:
215
  logging.info("Initializing Gradio Blocks interface...")
216
+ with gr.Blocks(css="""
217
+ .dashboard-container {border: 1px solid #e0e0e0; padding: 10/* Reduced padding */ 10px; border-radius: 5px; background-color: #f9f9f9;}
218
+ .dashboard-title {font-size: 24px; font-weight: bold; margin-bottom: 5px; /* Reduced margin */}
219
+ .dashboard-section {margin-bottom: 5px; /* Reduced margin */}
220
+ .dashboard-section h3 {font-size: 18px; margin-bottom: 2px; /* Reduced margin */}
221
+ .dashboard-section p {margin: 1px 0; line-height: 1.2; /* Tighter line spacing */}
222
+ .dashboard-section li {margin: 1px 0; line-height: 1.2; /* Tighter spacing for list items */}
223
+ .dashboard-section ul {margin: 2px 0; padding-left: 20px; /* Reduced margin/padding for lists */}
224
+ """) as iface:
225
  gr.Markdown("<h1>LabOps Log Analyzer Dashboard (Hugging Face AI)</h1>")
226
+ gr.Markdown("Upload a CSV file containing lab equipment logs to analyze usage.")
227
 
228
  with gr.Row():
229
  with gr.Column(scale=1):
230
  file_input = gr.File(label="Upload Logs (CSV)", file_types=[".csv"])
231
+ submit_button = gr.Button("Submit", variant="primary")
232
 
233
  with gr.Column(scale=2):
234
+ with gr.Group(elem_classes="dashboard-container"):
235
+ gr.Markdown("<div class='dashboard-title'>Analysis Results (Step-by-Step)</div>")
236
+
237
+ # Step 1: Summary Report
238
+ with gr.Group(elem_classes="dashboard-section"):
239
+ gr.Markdown("### Step 1: Summary Report")
240
+ summary_output = gr.Markdown()
241
+
242
+ # Step 2: Log Preview
243
+ with gr.Group(elem_classes="dashboard-section"):
244
+ gr.Markdown("### Step 2: Log Preview")
245
+ preview_output = gr.Markdown()
246
+
247
+ # Step 3: Usage Chart
248
+ with gr.Group(elem_classes="dashboard-section"):
249
+ gr.Markdown("### Step 3: Usage Chart")
250
+ chart_output = gr.Plot()
251
+
252
+ # Step 4: Anomaly Detection
253
+ with gr.Group(elem_classes="dashboard-section"):
254
+ gr.Markdown("### Step 4: Anomaly Detection")
255
+ anomaly_output = gr.Markdown()
256
+
257
+ # Step 5: AMC Reminders
258
+ with gr.Group(elem_classes="dashboard-section"):
259
+ gr.Markdown("### Step 5: AMC Reminders")
260
+ amc_output = gr.Markdown()
261
+
262
+ # Step 6: Dashboard Insights
263
+ with gr.Group(elem_classes="dashboard-section"):
264
+ gr.Markdown("### Step 6: Dashboard Insights (AI)")
265
+ insights_output = gr.Markdown()
266
 
267
  submit_button.click(
268
  fn=process_logs,
269
  inputs=[file_input],
270
+ outputs=[summary_output, preview_output, chart_output, anomaly_output, amc_output, insights_output]
271
  )
272
 
273
  logging.info("Gradio interface initialized successfully")