Spaces:
Sleeping
Sleeping
Update app.py
Browse files
app.py
CHANGED
|
@@ -3,15 +3,16 @@ import pandas as pd
|
|
| 3 |
from datetime import datetime, timedelta
|
| 4 |
import logging
|
| 5 |
import plotly.express as px
|
|
|
|
| 6 |
from sklearn.ensemble import IsolationForest
|
| 7 |
from transformers import pipeline
|
| 8 |
import torch
|
| 9 |
from concurrent.futures import ThreadPoolExecutor
|
| 10 |
from simple_salesforce import Salesforce
|
| 11 |
import os
|
| 12 |
-
import json
|
| 13 |
import io
|
| 14 |
import time
|
|
|
|
| 15 |
|
| 16 |
# Configure logging
|
| 17 |
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
|
|
@@ -49,7 +50,7 @@ try:
|
|
| 49 |
"summarization",
|
| 50 |
model="t5-small",
|
| 51 |
device=device,
|
| 52 |
-
max_length=
|
| 53 |
min_length=10,
|
| 54 |
num_beams=2
|
| 55 |
)
|
|
@@ -149,8 +150,8 @@ def summarize_logs(df):
|
|
| 149 |
try:
|
| 150 |
total_devices = df["device_id"].nunique()
|
| 151 |
most_used = df.groupby("device_id")["usage_hours"].sum().idxmax() if not df.empty else "N/A"
|
| 152 |
-
prompt = f"
|
| 153 |
-
summary = summarizer(prompt, max_length=
|
| 154 |
return summary
|
| 155 |
except Exception as e:
|
| 156 |
logging.error(f"Summary generation failed: {str(e)}")
|
|
@@ -162,8 +163,8 @@ def detect_anomalies(df):
|
|
| 162 |
if "usage_hours" not in df.columns or "downtime" not in df.columns:
|
| 163 |
return "Anomaly detection requires 'usage_hours' and 'downtime' columns.", pd.DataFrame()
|
| 164 |
features = df[["usage_hours", "downtime"]].fillna(0)
|
| 165 |
-
if len(features) >
|
| 166 |
-
features = features.sample(n=
|
| 167 |
iso_forest = IsolationForest(contamination=0.1, random_state=42)
|
| 168 |
df["anomaly"] = iso_forest.fit_predict(features)
|
| 169 |
anomalies = df[df["anomaly"] == -1][["device_id", "usage_hours", "downtime", "timestamp"]]
|
|
@@ -196,17 +197,30 @@ def generate_dashboard_insights(df):
|
|
| 196 |
total_devices = df["device_id"].nunique()
|
| 197 |
avg_usage = df["usage_hours"].mean() if "usage_hours" in df.columns else 0
|
| 198 |
prompt = f"Insights: {total_devices} devices, avg usage {avg_usage:.2f} hours."
|
| 199 |
-
insights = summarizer(prompt, max_length=
|
| 200 |
return insights
|
| 201 |
except Exception as e:
|
| 202 |
logging.error(f"Dashboard insights generation failed: {str(e)}")
|
| 203 |
return f"Dashboard insights generation failed: {str(e)}"
|
| 204 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 205 |
# Create usage chart
|
| 206 |
def create_usage_chart(df):
|
| 207 |
try:
|
| 208 |
-
if df.empty:
|
| 209 |
-
|
|
|
|
| 210 |
usage_data = df.groupby("device_id")["usage_hours"].sum().reset_index()
|
| 211 |
if len(usage_data) > 5:
|
| 212 |
usage_data = usage_data.nlargest(5, "usage_hours")
|
|
@@ -221,11 +235,14 @@ def create_usage_chart(df):
|
|
| 221 |
return fig
|
| 222 |
except Exception as e:
|
| 223 |
logging.error(f"Failed to create usage chart: {str(e)}")
|
| 224 |
-
return
|
| 225 |
|
| 226 |
# Create downtime chart
|
| 227 |
def create_downtime_chart(df):
|
| 228 |
try:
|
|
|
|
|
|
|
|
|
|
| 229 |
downtime_data = df.groupby("device_id")["downtime"].sum().reset_index()
|
| 230 |
if len(downtime_data) > 5:
|
| 231 |
downtime_data = downtime_data.nlargest(5, "downtime")
|
|
@@ -240,13 +257,18 @@ def create_downtime_chart(df):
|
|
| 240 |
return fig
|
| 241 |
except Exception as e:
|
| 242 |
logging.error(f"Failed to create downtime chart: {str(e)}")
|
| 243 |
-
return
|
| 244 |
|
| 245 |
# Create daily log trends chart
|
| 246 |
def create_daily_log_trends_chart(df):
|
| 247 |
try:
|
| 248 |
-
df
|
|
|
|
|
|
|
|
|
|
| 249 |
daily_logs = df.groupby('date').size().reset_index(name='log_count')
|
|
|
|
|
|
|
| 250 |
fig = px.line(
|
| 251 |
daily_logs,
|
| 252 |
x='date',
|
|
@@ -258,19 +280,24 @@ def create_daily_log_trends_chart(df):
|
|
| 258 |
return fig
|
| 259 |
except Exception as e:
|
| 260 |
logging.error(f"Failed to create daily log trends chart: {str(e)}")
|
| 261 |
-
return
|
| 262 |
|
| 263 |
# Create weekly uptime chart
|
| 264 |
def create_weekly_uptime_chart(df):
|
| 265 |
try:
|
| 266 |
-
df
|
| 267 |
-
|
|
|
|
|
|
|
|
|
|
| 268 |
weekly_data = df.groupby(['year', 'week']).agg({
|
| 269 |
'usage_hours': 'sum',
|
| 270 |
'downtime': 'sum'
|
| 271 |
}).reset_index()
|
| 272 |
weekly_data['uptime_percent'] = (weekly_data['usage_hours'] / (weekly_data['usage_hours'] + weekly_data['downtime'])) * 100
|
| 273 |
weekly_data['year_week'] = weekly_data['year'].astype(str) + '-W' + weekly_data['week'].astype(str)
|
|
|
|
|
|
|
| 274 |
fig = px.bar(
|
| 275 |
weekly_data,
|
| 276 |
x='year_week',
|
|
@@ -282,15 +309,18 @@ def create_weekly_uptime_chart(df):
|
|
| 282 |
return fig
|
| 283 |
except Exception as e:
|
| 284 |
logging.error(f"Failed to create weekly uptime chart: {str(e)}")
|
| 285 |
-
return
|
| 286 |
|
| 287 |
# Create anomaly alerts chart
|
| 288 |
def create_anomaly_alerts_chart(anomalies_df):
|
| 289 |
try:
|
| 290 |
-
if anomalies_df.empty:
|
| 291 |
-
|
| 292 |
-
|
|
|
|
| 293 |
anomaly_counts = anomalies_df.groupby('date').size().reset_index(name='anomaly_count')
|
|
|
|
|
|
|
| 294 |
fig = px.scatter(
|
| 295 |
anomaly_counts,
|
| 296 |
x='date',
|
|
@@ -302,7 +332,7 @@ def create_anomaly_alerts_chart(anomalies_df):
|
|
| 302 |
return fig
|
| 303 |
except Exception as e:
|
| 304 |
logging.error(f"Failed to create anomaly alerts chart: {str(e)}")
|
| 305 |
-
return
|
| 306 |
|
| 307 |
# Generate device cards
|
| 308 |
def generate_device_cards(df):
|
|
@@ -408,41 +438,41 @@ def generate_pdf_content(summary, preview_df, anomalies, amc_reminders, insights
|
|
| 408 |
return None
|
| 409 |
|
| 410 |
# Main processing function
|
| 411 |
-
async def process_logs(file_obj, lab_site_filter, equipment_type_filter, date_range, last_modified_state):
|
| 412 |
start_time = time.time()
|
| 413 |
try:
|
| 414 |
if not file_obj:
|
| 415 |
-
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
|
| 416 |
|
| 417 |
file_path = file_obj.name
|
| 418 |
current_modified_time = os.path.getmtime(file_path)
|
| 419 |
-
if last_modified_state and current_modified_time == last_modified_state:
|
| 420 |
-
|
| 421 |
-
|
| 422 |
-
|
| 423 |
-
|
| 424 |
-
|
| 425 |
-
|
| 426 |
-
|
| 427 |
-
|
| 428 |
-
|
| 429 |
-
|
| 430 |
-
|
| 431 |
-
|
| 432 |
-
|
| 433 |
-
|
| 434 |
-
|
| 435 |
-
|
| 436 |
-
|
| 437 |
-
|
| 438 |
-
|
| 439 |
-
|
| 440 |
-
|
| 441 |
-
|
| 442 |
-
|
| 443 |
-
|
| 444 |
-
|
| 445 |
-
|
| 446 |
|
| 447 |
# Apply filters
|
| 448 |
filtered_df = df.copy()
|
|
@@ -458,23 +488,22 @@ async def process_logs(file_obj, lab_site_filter, equipment_type_filter, date_ra
|
|
| 458 |
filtered_df = filtered_df[(filtered_df['timestamp'] >= start_date) & (filtered_df['timestamp'] <= end_date)]
|
| 459 |
|
| 460 |
if filtered_df.empty:
|
| 461 |
-
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
|
| 462 |
|
| 463 |
# Generate table for preview
|
| 464 |
preview_df = filtered_df[['device_id', 'log_type', 'status', 'timestamp', 'usage_hours', 'downtime', 'amc_date']].head(5)
|
| 465 |
preview_html = preview_df.to_html(index=False, classes='table table-striped', border=0)
|
| 466 |
|
| 467 |
-
# Run tasks concurrently
|
| 468 |
-
with ThreadPoolExecutor(max_workers=
|
| 469 |
future_summary = executor.submit(summarize_logs, filtered_df)
|
| 470 |
future_anomalies = executor.submit(detect_anomalies, filtered_df)
|
| 471 |
future_amc = executor.submit(check_amc_reminders, filtered_df, datetime.now())
|
| 472 |
future_insights = executor.submit(generate_dashboard_insights, filtered_df)
|
| 473 |
future_usage_chart = executor.submit(create_usage_chart, filtered_df)
|
| 474 |
future_downtime_chart = executor.submit(create_downtime_chart, filtered_df)
|
| 475 |
-
future_daily_log_chart = executor.submit(
|
| 476 |
future_weekly_uptime_chart = executor.submit(create_weekly_uptime_chart, filtered_df)
|
| 477 |
-
future_anomaly_alerts_chart = executor.submit(create_anomaly_alerts_chart, future_anomalies.result()[1]) # Pass anomalies_df
|
| 478 |
future_device_cards = executor.submit(generate_device_cards, filtered_df)
|
| 479 |
|
| 480 |
summary = f"Step 1: Summary Report\n{future_summary.result()}"
|
|
@@ -487,21 +516,32 @@ async def process_logs(file_obj, lab_site_filter, equipment_type_filter, date_ra
|
|
| 487 |
downtime_chart = future_downtime_chart.result()
|
| 488 |
daily_log_chart = future_daily_log_chart.result()
|
| 489 |
weekly_uptime_chart = future_weekly_uptime_chart.result()
|
| 490 |
-
anomaly_alerts_chart = future_anomaly_alerts_chart.result()
|
| 491 |
device_cards = future_device_cards.result()
|
| 492 |
|
|
|
|
|
|
|
|
|
|
| 493 |
save_to_salesforce(filtered_df, reminders_df)
|
| 494 |
-
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)
|
| 495 |
|
| 496 |
elapsed_time = time.time() - start_time
|
| 497 |
logging.info(f"Processing completed in {elapsed_time:.2f} seconds")
|
| 498 |
-
if elapsed_time >
|
| 499 |
-
logging.warning(f"Processing time exceeded
|
| 500 |
|
| 501 |
-
return (summary, preview_html, usage_chart, device_cards, daily_log_chart, weekly_uptime_chart, anomaly_alerts_chart, downtime_chart, anomalies, amc_reminders, insights,
|
| 502 |
except Exception as e:
|
| 503 |
logging.error(f"Failed to process file: {str(e)}")
|
| 504 |
-
return f"Error: {str(e)}", pd.DataFrame(), None, '<p>Error processing data.</p>', None, None, None, None, None, None, None, None, last_modified_state
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 505 |
|
| 506 |
# Update filters
|
| 507 |
def update_filters(file_obj, current_file_state):
|
|
@@ -537,10 +577,11 @@ try:
|
|
| 537 |
.table tr:nth-child(even) {background-color: #f9f9f9;}
|
| 538 |
""") as iface:
|
| 539 |
gr.Markdown("<h1>LabOps Log Analyzer Dashboard (Hugging Face AI)</h1>")
|
| 540 |
-
gr.Markdown("Upload a CSV file to analyze. Click 'Analyze' to refresh the dashboard
|
| 541 |
|
| 542 |
last_modified_state = gr.State(value=None)
|
| 543 |
current_file_state = gr.State(value=None)
|
|
|
|
| 544 |
|
| 545 |
with gr.Row():
|
| 546 |
with gr.Column(scale=1):
|
|
@@ -551,6 +592,7 @@ try:
|
|
| 551 |
equipment_type_filter = gr.Dropdown(label="Equipment Type", choices=['All'], value='All', interactive=True)
|
| 552 |
date_range_filter = gr.Slider(label="Date Range (Days from Today)", minimum=-365, maximum=0, step=1, value=[-30, 0])
|
| 553 |
submit_button = gr.Button("Analyze", variant="primary")
|
|
|
|
| 554 |
|
| 555 |
with gr.Column(scale=2):
|
| 556 |
with gr.Group(elem_classes="dashboard-container"):
|
|
@@ -598,8 +640,14 @@ try:
|
|
| 598 |
|
| 599 |
submit_button.click(
|
| 600 |
fn=process_logs,
|
| 601 |
-
inputs=[file_input, lab_site_filter, equipment_type_filter, date_range_filter, last_modified_state],
|
| 602 |
-
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]
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 603 |
)
|
| 604 |
|
| 605 |
logging.info("Gradio interface initialized successfully")
|
|
|
|
| 3 |
from datetime import datetime, timedelta
|
| 4 |
import logging
|
| 5 |
import plotly.express as px
|
| 6 |
+
import plotly.graph_objects as go
|
| 7 |
from sklearn.ensemble import IsolationForest
|
| 8 |
from transformers import pipeline
|
| 9 |
import torch
|
| 10 |
from concurrent.futures import ThreadPoolExecutor
|
| 11 |
from simple_salesforce import Salesforce
|
| 12 |
import os
|
|
|
|
| 13 |
import io
|
| 14 |
import time
|
| 15 |
+
import asyncio
|
| 16 |
|
| 17 |
# Configure logging
|
| 18 |
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
|
|
|
|
| 50 |
"summarization",
|
| 51 |
model="t5-small",
|
| 52 |
device=device,
|
| 53 |
+
max_length=20, # Further reduced for speed
|
| 54 |
min_length=10,
|
| 55 |
num_beams=2
|
| 56 |
)
|
|
|
|
| 150 |
try:
|
| 151 |
total_devices = df["device_id"].nunique()
|
| 152 |
most_used = df.groupby("device_id")["usage_hours"].sum().idxmax() if not df.empty else "N/A"
|
| 153 |
+
prompt = f"Logs: {total_devices} devices. Most used: {most_used}."
|
| 154 |
+
summary = summarizer(prompt, max_length=20, min_length=10, do_sample=False)[0]["summary_text"]
|
| 155 |
return summary
|
| 156 |
except Exception as e:
|
| 157 |
logging.error(f"Summary generation failed: {str(e)}")
|
|
|
|
| 163 |
if "usage_hours" not in df.columns or "downtime" not in df.columns:
|
| 164 |
return "Anomaly detection requires 'usage_hours' and 'downtime' columns.", pd.DataFrame()
|
| 165 |
features = df[["usage_hours", "downtime"]].fillna(0)
|
| 166 |
+
if len(features) > 100: # Further reduced sample size
|
| 167 |
+
features = features.sample(n=100, random_state=42)
|
| 168 |
iso_forest = IsolationForest(contamination=0.1, random_state=42)
|
| 169 |
df["anomaly"] = iso_forest.fit_predict(features)
|
| 170 |
anomalies = df[df["anomaly"] == -1][["device_id", "usage_hours", "downtime", "timestamp"]]
|
|
|
|
| 197 |
total_devices = df["device_id"].nunique()
|
| 198 |
avg_usage = df["usage_hours"].mean() if "usage_hours" in df.columns else 0
|
| 199 |
prompt = f"Insights: {total_devices} devices, avg usage {avg_usage:.2f} hours."
|
| 200 |
+
insights = summarizer(prompt, max_length=20, min_length=10, do_sample=False)[0]["summary_text"]
|
| 201 |
return insights
|
| 202 |
except Exception as e:
|
| 203 |
logging.error(f"Dashboard insights generation failed: {str(e)}")
|
| 204 |
return f"Dashboard insights generation failed: {str(e)}"
|
| 205 |
|
| 206 |
+
# Placeholder chart for empty data
|
| 207 |
+
def create_placeholder_chart(title):
|
| 208 |
+
fig = go.Figure()
|
| 209 |
+
fig.add_annotation(
|
| 210 |
+
text="No data available for this chart",
|
| 211 |
+
xref="paper", yref="paper",
|
| 212 |
+
x=0.5, y=0.5, showarrow=False,
|
| 213 |
+
font=dict(size=16)
|
| 214 |
+
)
|
| 215 |
+
fig.update_layout(title=title, margin=dict(l=20, r=20, t=40, b=20))
|
| 216 |
+
return fig
|
| 217 |
+
|
| 218 |
# Create usage chart
|
| 219 |
def create_usage_chart(df):
|
| 220 |
try:
|
| 221 |
+
if df.empty or "usage_hours" not in df.columns or "device_id" not in df.columns:
|
| 222 |
+
logging.warning("Insufficient data for usage chart")
|
| 223 |
+
return create_placeholder_chart("Usage Hours per Device")
|
| 224 |
usage_data = df.groupby("device_id")["usage_hours"].sum().reset_index()
|
| 225 |
if len(usage_data) > 5:
|
| 226 |
usage_data = usage_data.nlargest(5, "usage_hours")
|
|
|
|
| 235 |
return fig
|
| 236 |
except Exception as e:
|
| 237 |
logging.error(f"Failed to create usage chart: {str(e)}")
|
| 238 |
+
return create_placeholder_chart("Usage Hours per Device")
|
| 239 |
|
| 240 |
# Create downtime chart
|
| 241 |
def create_downtime_chart(df):
|
| 242 |
try:
|
| 243 |
+
if df.empty or "downtime" not in df.columns or "device_id" not in df.columns:
|
| 244 |
+
logging.warning("Insufficient data for downtime chart")
|
| 245 |
+
return create_placeholder_chart("Downtime per Device")
|
| 246 |
downtime_data = df.groupby("device_id")["downtime"].sum().reset_index()
|
| 247 |
if len(downtime_data) > 5:
|
| 248 |
downtime_data = downtime_data.nlargest(5, "downtime")
|
|
|
|
| 257 |
return fig
|
| 258 |
except Exception as e:
|
| 259 |
logging.error(f"Failed to create downtime chart: {str(e)}")
|
| 260 |
+
return create_placeholder_chart("Downtime per Device")
|
| 261 |
|
| 262 |
# Create daily log trends chart
|
| 263 |
def create_daily_log_trends_chart(df):
|
| 264 |
try:
|
| 265 |
+
if df.empty or "timestamp" not in df.columns:
|
| 266 |
+
logging.warning("Insufficient data for daily log trends chart")
|
| 267 |
+
return create_placeholder_chart("Daily Log Trends")
|
| 268 |
+
df['date'] = pd.to_datetime(df['timestamp']).dt.date
|
| 269 |
daily_logs = df.groupby('date').size().reset_index(name='log_count')
|
| 270 |
+
if daily_logs.empty:
|
| 271 |
+
return create_placeholder_chart("Daily Log Trends")
|
| 272 |
fig = px.line(
|
| 273 |
daily_logs,
|
| 274 |
x='date',
|
|
|
|
| 280 |
return fig
|
| 281 |
except Exception as e:
|
| 282 |
logging.error(f"Failed to create daily log trends chart: {str(e)}")
|
| 283 |
+
return create_placeholder_chart("Daily Log Trends")
|
| 284 |
|
| 285 |
# Create weekly uptime chart
|
| 286 |
def create_weekly_uptime_chart(df):
|
| 287 |
try:
|
| 288 |
+
if df.empty or "timestamp" not in df.columns or "usage_hours" not in df.columns or "downtime" not in df.columns:
|
| 289 |
+
logging.warning("Insufficient data for weekly uptime chart")
|
| 290 |
+
return create_placeholder_chart("Weekly Uptime Percentage")
|
| 291 |
+
df['week'] = pd.to_datetime(df['timestamp']).dt.isocalendar().week
|
| 292 |
+
df['year'] = pd.to_datetime(df['timestamp']).dt.year
|
| 293 |
weekly_data = df.groupby(['year', 'week']).agg({
|
| 294 |
'usage_hours': 'sum',
|
| 295 |
'downtime': 'sum'
|
| 296 |
}).reset_index()
|
| 297 |
weekly_data['uptime_percent'] = (weekly_data['usage_hours'] / (weekly_data['usage_hours'] + weekly_data['downtime'])) * 100
|
| 298 |
weekly_data['year_week'] = weekly_data['year'].astype(str) + '-W' + weekly_data['week'].astype(str)
|
| 299 |
+
if weekly_data.empty:
|
| 300 |
+
return create_placeholder_chart("Weekly Uptime Percentage")
|
| 301 |
fig = px.bar(
|
| 302 |
weekly_data,
|
| 303 |
x='year_week',
|
|
|
|
| 309 |
return fig
|
| 310 |
except Exception as e:
|
| 311 |
logging.error(f"Failed to create weekly uptime chart: {str(e)}")
|
| 312 |
+
return create_placeholder_chart("Weekly Uptime Percentage")
|
| 313 |
|
| 314 |
# Create anomaly alerts chart
|
| 315 |
def create_anomaly_alerts_chart(anomalies_df):
|
| 316 |
try:
|
| 317 |
+
if anomalies_df is None or anomalies_df.empty or "timestamp" not in anomalies_df.columns:
|
| 318 |
+
logging.warning("Insufficient data for anomaly alerts chart")
|
| 319 |
+
return create_placeholder_chart("Anomaly Alerts Over Time")
|
| 320 |
+
anomalies_df['date'] = pd.to_datetime(anomalies_df['timestamp']).dt.date
|
| 321 |
anomaly_counts = anomalies_df.groupby('date').size().reset_index(name='anomaly_count')
|
| 322 |
+
if anomaly_counts.empty:
|
| 323 |
+
return create_placeholder_chart("Anomaly Alerts Over Time")
|
| 324 |
fig = px.scatter(
|
| 325 |
anomaly_counts,
|
| 326 |
x='date',
|
|
|
|
| 332 |
return fig
|
| 333 |
except Exception as e:
|
| 334 |
logging.error(f"Failed to create anomaly alerts chart: {str(e)}")
|
| 335 |
+
return create_placeholder_chart("Anomaly Alerts Over Time")
|
| 336 |
|
| 337 |
# Generate device cards
|
| 338 |
def generate_device_cards(df):
|
|
|
|
| 438 |
return None
|
| 439 |
|
| 440 |
# Main processing function
|
| 441 |
+
async def process_logs(file_obj, lab_site_filter, equipment_type_filter, date_range, last_modified_state, cached_df_state):
|
| 442 |
start_time = time.time()
|
| 443 |
try:
|
| 444 |
if not file_obj:
|
| 445 |
+
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, cached_df_state
|
| 446 |
|
| 447 |
file_path = file_obj.name
|
| 448 |
current_modified_time = os.path.getmtime(file_path)
|
| 449 |
+
if last_modified_state and current_modified_time == last_modified_state and cached_df_state is not None:
|
| 450 |
+
df = cached_df_state
|
| 451 |
+
else:
|
| 452 |
+
logging.info(f"Processing file: {file_path}")
|
| 453 |
+
if not file_path.endswith(".csv"):
|
| 454 |
+
return "Please upload a CSV file.", pd.DataFrame(), None, '<p>No device cards available.</p>', None, None, None, None, "", "", "", None, last_modified_state, cached_df_state
|
| 455 |
+
|
| 456 |
+
required_columns = ["device_id", "log_type", "status", "timestamp", "usage_hours", "downtime", "amc_date"]
|
| 457 |
+
dtypes = {
|
| 458 |
+
"device_id": "string",
|
| 459 |
+
"log_type": "string",
|
| 460 |
+
"status": "string",
|
| 461 |
+
"usage_hours": "float32",
|
| 462 |
+
"downtime": "float32",
|
| 463 |
+
"amc_date": "string"
|
| 464 |
+
}
|
| 465 |
+
df = pd.read_csv(file_path, dtype=dtypes)
|
| 466 |
+
missing_columns = [col for col in required_columns if col not in df.columns]
|
| 467 |
+
if missing_columns:
|
| 468 |
+
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, cached_df_state
|
| 469 |
+
|
| 470 |
+
df["timestamp"] = pd.to_datetime(df["timestamp"], errors='coerce')
|
| 471 |
+
df["amc_date"] = pd.to_datetime(df["amc_date"], errors='coerce')
|
| 472 |
+
if df["timestamp"].dt.tz is None:
|
| 473 |
+
df["timestamp"] = df["timestamp"].dt.tz_localize('UTC').dt.tz_convert('Asia/Kolkata')
|
| 474 |
+
if df.empty:
|
| 475 |
+
return "No data available.", pd.DataFrame(), None, '<p>No device cards available.</p>', None, None, None, None, None, None, None, None, last_modified_state, cached_df_state
|
| 476 |
|
| 477 |
# Apply filters
|
| 478 |
filtered_df = df.copy()
|
|
|
|
| 488 |
filtered_df = filtered_df[(filtered_df['timestamp'] >= start_date) & (filtered_df['timestamp'] <= end_date)]
|
| 489 |
|
| 490 |
if filtered_df.empty:
|
| 491 |
+
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, df
|
| 492 |
|
| 493 |
# Generate table for preview
|
| 494 |
preview_df = filtered_df[['device_id', 'log_type', 'status', 'timestamp', 'usage_hours', 'downtime', 'amc_date']].head(5)
|
| 495 |
preview_html = preview_df.to_html(index=False, classes='table table-striped', border=0)
|
| 496 |
|
| 497 |
+
# Run critical tasks concurrently
|
| 498 |
+
with ThreadPoolExecutor(max_workers=4) as executor: # Reduced workers
|
| 499 |
future_summary = executor.submit(summarize_logs, filtered_df)
|
| 500 |
future_anomalies = executor.submit(detect_anomalies, filtered_df)
|
| 501 |
future_amc = executor.submit(check_amc_reminders, filtered_df, datetime.now())
|
| 502 |
future_insights = executor.submit(generate_dashboard_insights, filtered_df)
|
| 503 |
future_usage_chart = executor.submit(create_usage_chart, filtered_df)
|
| 504 |
future_downtime_chart = executor.submit(create_downtime_chart, filtered_df)
|
| 505 |
+
future_daily_log_chart = executor.submit(create_daily_log_chart, filtered_df)
|
| 506 |
future_weekly_uptime_chart = executor.submit(create_weekly_uptime_chart, filtered_df)
|
|
|
|
| 507 |
future_device_cards = executor.submit(generate_device_cards, filtered_df)
|
| 508 |
|
| 509 |
summary = f"Step 1: Summary Report\n{future_summary.result()}"
|
|
|
|
| 516 |
downtime_chart = future_downtime_chart.result()
|
| 517 |
daily_log_chart = future_daily_log_chart.result()
|
| 518 |
weekly_uptime_chart = future_weekly_uptime_chart.result()
|
|
|
|
| 519 |
device_cards = future_device_cards.result()
|
| 520 |
|
| 521 |
+
# Generate anomaly alerts chart after anomalies are computed
|
| 522 |
+
anomaly_alerts_chart = create_anomaly_alerts_chart(anomalies_df)
|
| 523 |
+
|
| 524 |
save_to_salesforce(filtered_df, reminders_df)
|
|
|
|
| 525 |
|
| 526 |
elapsed_time = time.time() - start_time
|
| 527 |
logging.info(f"Processing completed in {elapsed_time:.2f} seconds")
|
| 528 |
+
if elapsed_time > 5: # Stricter threshold
|
| 529 |
+
logging.warning(f"Processing time exceeded 5 seconds: {elapsed_time:.2f} seconds")
|
| 530 |
|
| 531 |
+
return (summary, preview_html, usage_chart, device_cards, daily_log_chart, weekly_uptime_chart, anomaly_alerts_chart, downtime_chart, anomalies, amc_reminders, insights, None, current_modified_time, df)
|
| 532 |
except Exception as e:
|
| 533 |
logging.error(f"Failed to process file: {str(e)}")
|
| 534 |
+
return f"Error: {str(e)}", pd.DataFrame(), None, '<p>Error processing data.</p>', None, None, None, None, None, None, None, None, last_modified_state, cached_df_state
|
| 535 |
+
|
| 536 |
+
# Generate PDF separately
|
| 537 |
+
async def generate_pdf(summary, preview_html, usage_chart, device_cards, daily_log_chart, weekly_uptime_chart, anomaly_alerts_chart, downtime_chart, anomalies, amc_reminders, insights):
|
| 538 |
+
try:
|
| 539 |
+
preview_df = pd.read_html(preview_html)[0]
|
| 540 |
+
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)
|
| 541 |
+
return pdf_file
|
| 542 |
+
except Exception as e:
|
| 543 |
+
logging.error(f"Failed to generate PDF: {str(e)}")
|
| 544 |
+
return None
|
| 545 |
|
| 546 |
# Update filters
|
| 547 |
def update_filters(file_obj, current_file_state):
|
|
|
|
| 577 |
.table tr:nth-child(even) {background-color: #f9f9f9;}
|
| 578 |
""") as iface:
|
| 579 |
gr.Markdown("<h1>LabOps Log Analyzer Dashboard (Hugging Face AI)</h1>")
|
| 580 |
+
gr.Markdown("Upload a CSV file to analyze. Click 'Analyze' to refresh the dashboard. Use 'Export PDF' for report download.")
|
| 581 |
|
| 582 |
last_modified_state = gr.State(value=None)
|
| 583 |
current_file_state = gr.State(value=None)
|
| 584 |
+
cached_df_state = gr.State(value=None)
|
| 585 |
|
| 586 |
with gr.Row():
|
| 587 |
with gr.Column(scale=1):
|
|
|
|
| 592 |
equipment_type_filter = gr.Dropdown(label="Equipment Type", choices=['All'], value='All', interactive=True)
|
| 593 |
date_range_filter = gr.Slider(label="Date Range (Days from Today)", minimum=-365, maximum=0, step=1, value=[-30, 0])
|
| 594 |
submit_button = gr.Button("Analyze", variant="primary")
|
| 595 |
+
pdf_button = gr.Button("Export PDF", variant="secondary")
|
| 596 |
|
| 597 |
with gr.Column(scale=2):
|
| 598 |
with gr.Group(elem_classes="dashboard-container"):
|
|
|
|
| 640 |
|
| 641 |
submit_button.click(
|
| 642 |
fn=process_logs,
|
| 643 |
+
inputs=[file_input, lab_site_filter, equipment_type_filter, date_range_filter, last_modified_state, cached_df_state],
|
| 644 |
+
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, cached_df_state]
|
| 645 |
+
)
|
| 646 |
+
|
| 647 |
+
pdf_button.click(
|
| 648 |
+
fn=generate_pdf,
|
| 649 |
+
inputs=[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],
|
| 650 |
+
outputs=[pdf_output]
|
| 651 |
)
|
| 652 |
|
| 653 |
logging.info("Gradio interface initialized successfully")
|