Spaces:
Sleeping
Sleeping
Update app.py
Browse files
app.py
CHANGED
|
@@ -31,7 +31,9 @@ def summarize_logs(df):
|
|
| 31 |
try:
|
| 32 |
total_devices = df["device_id"].nunique()
|
| 33 |
total_usage = df["usage_hours"].sum() if "usage_hours" in df.columns else 0
|
| 34 |
-
|
|
|
|
|
|
|
| 35 |
except Exception as e:
|
| 36 |
logging.error(f"Summary generation failed: {str(e)}")
|
| 37 |
return "Failed to generate summary."
|
|
@@ -46,10 +48,10 @@ def detect_anomalies(df):
|
|
| 46 |
features = features.sample(n=50, random_state=42)
|
| 47 |
iso_forest = IsolationForest(contamination=0.1, random_state=42)
|
| 48 |
df["anomaly"] = iso_forest.fit_predict(features)
|
| 49 |
-
anomalies = df[df["anomaly"] == -1][["device_id", "usage_hours", "downtime", "timestamp"]]
|
| 50 |
if anomalies.empty:
|
| 51 |
return "No anomalies detected.", anomalies
|
| 52 |
-
return "\n".join([f"- Device ID: {row['device_id']}, Usage: {row['usage_hours']}, Downtime: {row['downtime']}, Timestamp: {row['timestamp']}" for _, row in anomalies.head(5).iterrows()]), anomalies
|
| 53 |
except Exception as e:
|
| 54 |
logging.error(f"Anomaly detection failed: {str(e)}")
|
| 55 |
return f"Anomaly detection failed: {str(e)}", pd.DataFrame()
|
|
@@ -62,10 +64,10 @@ def check_amc_reminders(df, current_date):
|
|
| 62 |
df["amc_date"] = pd.to_datetime(df["amc_date"], errors='coerce')
|
| 63 |
current_date = pd.to_datetime(current_date)
|
| 64 |
df["days_to_amc"] = (df["amc_date"] - current_date).dt.days
|
| 65 |
-
reminders = df[(df["days_to_amc"] >= 0) & (df["days_to_amc"] <= 30)][["device_id", "log_type", "status", "timestamp", "usage_hours", "downtime", "amc_date"]]
|
| 66 |
if reminders.empty:
|
| 67 |
return "No AMC reminders due within the next 30 days.", reminders
|
| 68 |
-
return "\n".join([f"- Device ID: {row['device_id']}, AMC Date: {row['amc_date']}" for _, row in reminders.head(5).iterrows()]), reminders
|
| 69 |
except Exception as e:
|
| 70 |
logging.error(f"AMC reminder generation failed: {str(e)}")
|
| 71 |
return f"AMC reminder generation failed: {str(e)}", pd.DataFrame()
|
|
@@ -75,7 +77,9 @@ def generate_dashboard_insights(df):
|
|
| 75 |
try:
|
| 76 |
total_devices = df["device_id"].nunique()
|
| 77 |
avg_usage = df["usage_hours"].mean() if "usage_hours" in df.columns else 0
|
| 78 |
-
|
|
|
|
|
|
|
| 79 |
except Exception as e:
|
| 80 |
logging.error(f"Dashboard insights generation failed: {str(e)}")
|
| 81 |
return "Failed to generate insights."
|
|
@@ -219,6 +223,8 @@ def generate_device_cards(df):
|
|
| 219 |
device_stats = df.groupby('device_id').agg({
|
| 220 |
'status': 'last',
|
| 221 |
'timestamp': 'max',
|
|
|
|
|
|
|
| 222 |
}).reset_index()
|
| 223 |
device_stats['count'] = df.groupby('device_id').size().reindex(device_stats['device_id']).values
|
| 224 |
device_stats['health'] = device_stats['status'].map({
|
|
@@ -230,10 +236,14 @@ def generate_device_cards(df):
|
|
| 230 |
for _, row in device_stats.iterrows():
|
| 231 |
health_color = {'Healthy': 'green', 'Unhealthy': 'red', 'Warning': 'orange', 'Unknown': 'gray'}.get(row['health'], 'gray')
|
| 232 |
timestamp_str = str(row['timestamp']) if pd.notna(row['timestamp']) else 'Unknown'
|
|
|
|
|
|
|
| 233 |
cards_html += f"""
|
| 234 |
<div style="border: 1px solid #e0e0e0; padding: 10px; border-radius: 5px; width: 200px;">
|
| 235 |
<h4>Device: {row['device_id']}</h4>
|
| 236 |
<p><b>Health:</b> <span style="color: {health_color}">{row['health']}</span></p>
|
|
|
|
|
|
|
| 237 |
<p><b>Usage Count:</b> {row['count']}</p>
|
| 238 |
<p><b>Last Log:</b> {timestamp_str}</p>
|
| 239 |
</div>
|
|
@@ -337,7 +347,9 @@ async def process_logs(file_obj, lab_site_filter, equipment_type_filter, date_ra
|
|
| 337 |
"status": "string",
|
| 338 |
"usage_hours": "float32",
|
| 339 |
"downtime": "float32",
|
| 340 |
-
"amc_date": "string"
|
|
|
|
|
|
|
| 341 |
}
|
| 342 |
df = pd.read_csv(file_path, dtype=dtypes)
|
| 343 |
missing_columns = [col for col in required_columns if col not in df.columns]
|
|
@@ -375,7 +387,7 @@ async def process_logs(file_obj, lab_site_filter, equipment_type_filter, date_ra
|
|
| 375 |
return "No data after applying filters.", "<p>No data after filters.</p>", None, '<p>No device cards available.</p>', None, None, None, None, "", "", "", None, df, current_modified_time
|
| 376 |
|
| 377 |
# Generate table for preview
|
| 378 |
-
preview_df = filtered_df[['device_id', 'log_type', 'status', 'timestamp', 'usage_hours', 'downtime', 'amc_date']].head(5)
|
| 379 |
preview_html = preview_df.to_html(index=False, classes='table table-striped', border=0)
|
| 380 |
|
| 381 |
# Run critical tasks concurrently
|
|
|
|
| 31 |
try:
|
| 32 |
total_devices = df["device_id"].nunique()
|
| 33 |
total_usage = df["usage_hours"].sum() if "usage_hours" in df.columns else 0
|
| 34 |
+
lab_sites = df["lab_site"].nunique() if "lab_site" in df.columns else 0
|
| 35 |
+
equipment_types = df["equipment_type"].nunique() if "equipment_type" in df.columns else 0
|
| 36 |
+
return f"{total_devices} devices processed with {total_usage:.2f} total usage hours across {lab_sites} lab sites and {equipment_types} equipment types."
|
| 37 |
except Exception as e:
|
| 38 |
logging.error(f"Summary generation failed: {str(e)}")
|
| 39 |
return "Failed to generate summary."
|
|
|
|
| 48 |
features = features.sample(n=50, random_state=42)
|
| 49 |
iso_forest = IsolationForest(contamination=0.1, random_state=42)
|
| 50 |
df["anomaly"] = iso_forest.fit_predict(features)
|
| 51 |
+
anomalies = df[df["anomaly"] == -1][["device_id", "usage_hours", "downtime", "timestamp", "lab_site", "equipment_type"]]
|
| 52 |
if anomalies.empty:
|
| 53 |
return "No anomalies detected.", anomalies
|
| 54 |
+
return "\n".join([f"- Device ID: {row['device_id']}, Usage: {row['usage_hours']}, Downtime: {row['downtime']}, Timestamp: {row['timestamp']}, Lab Site: {row['lab_site']}, Equipment Type: {row['equipment_type']}" for _, row in anomalies.head(5).iterrows()]), anomalies
|
| 55 |
except Exception as e:
|
| 56 |
logging.error(f"Anomaly detection failed: {str(e)}")
|
| 57 |
return f"Anomaly detection failed: {str(e)}", pd.DataFrame()
|
|
|
|
| 64 |
df["amc_date"] = pd.to_datetime(df["amc_date"], errors='coerce')
|
| 65 |
current_date = pd.to_datetime(current_date)
|
| 66 |
df["days_to_amc"] = (df["amc_date"] - current_date).dt.days
|
| 67 |
+
reminders = df[(df["days_to_amc"] >= 0) & (df["days_to_amc"] <= 30)][["device_id", "log_type", "status", "timestamp", "usage_hours", "downtime", "amc_date", "lab_site", "equipment_type"]]
|
| 68 |
if reminders.empty:
|
| 69 |
return "No AMC reminders due within the next 30 days.", reminders
|
| 70 |
+
return "\n".join([f"- Device ID: {row['device_id']}, AMC Date: {row['amc_date']}, Lab Site: {row['lab_site']}, Equipment Type: {row['equipment_type']}" for _, row in reminders.head(5).iterrows()]), reminders
|
| 71 |
except Exception as e:
|
| 72 |
logging.error(f"AMC reminder generation failed: {str(e)}")
|
| 73 |
return f"AMC reminder generation failed: {str(e)}", pd.DataFrame()
|
|
|
|
| 77 |
try:
|
| 78 |
total_devices = df["device_id"].nunique()
|
| 79 |
avg_usage = df["usage_hours"].mean() if "usage_hours" in df.columns else 0
|
| 80 |
+
lab_sites = df["lab_site"].unique().tolist() if "lab_site" in df.columns else []
|
| 81 |
+
equipment_types = df["equipment_type"].unique().tolist() if "equipment_type" in df.columns else []
|
| 82 |
+
return f"{total_devices} devices with average usage of {avg_usage:.2f} hours. Lab Sites: {', '.join(lab_sites)}. Equipment Types: {', '.join(equipment_types)}."
|
| 83 |
except Exception as e:
|
| 84 |
logging.error(f"Dashboard insights generation failed: {str(e)}")
|
| 85 |
return "Failed to generate insights."
|
|
|
|
| 223 |
device_stats = df.groupby('device_id').agg({
|
| 224 |
'status': 'last',
|
| 225 |
'timestamp': 'max',
|
| 226 |
+
'lab_site': 'last',
|
| 227 |
+
'equipment_type': 'last'
|
| 228 |
}).reset_index()
|
| 229 |
device_stats['count'] = df.groupby('device_id').size().reindex(device_stats['device_id']).values
|
| 230 |
device_stats['health'] = device_stats['status'].map({
|
|
|
|
| 236 |
for _, row in device_stats.iterrows():
|
| 237 |
health_color = {'Healthy': 'green', 'Unhealthy': 'red', 'Warning': 'orange', 'Unknown': 'gray'}.get(row['health'], 'gray')
|
| 238 |
timestamp_str = str(row['timestamp']) if pd.notna(row['timestamp']) else 'Unknown'
|
| 239 |
+
lab_site = row['lab_site'] if pd.notna(row['lab_site']) else 'Unknown'
|
| 240 |
+
equipment_type = row['equipment_type'] if pd.notna(row['equipment_type']) else 'Unknown'
|
| 241 |
cards_html += f"""
|
| 242 |
<div style="border: 1px solid #e0e0e0; padding: 10px; border-radius: 5px; width: 200px;">
|
| 243 |
<h4>Device: {row['device_id']}</h4>
|
| 244 |
<p><b>Health:</b> <span style="color: {health_color}">{row['health']}</span></p>
|
| 245 |
+
<p><b>Lab Site:</b> {lab_site}</p>
|
| 246 |
+
<p><b>Equipment Type:</b> {equipment_type}</p>
|
| 247 |
<p><b>Usage Count:</b> {row['count']}</p>
|
| 248 |
<p><b>Last Log:</b> {timestamp_str}</p>
|
| 249 |
</div>
|
|
|
|
| 347 |
"status": "string",
|
| 348 |
"usage_hours": "float32",
|
| 349 |
"downtime": "float32",
|
| 350 |
+
"amc_date": "string",
|
| 351 |
+
"lab_site": "string",
|
| 352 |
+
"equipment_type": "string"
|
| 353 |
}
|
| 354 |
df = pd.read_csv(file_path, dtype=dtypes)
|
| 355 |
missing_columns = [col for col in required_columns if col not in df.columns]
|
|
|
|
| 387 |
return "No data after applying filters.", "<p>No data after filters.</p>", None, '<p>No device cards available.</p>', None, None, None, None, "", "", "", None, df, current_modified_time
|
| 388 |
|
| 389 |
# Generate table for preview
|
| 390 |
+
preview_df = filtered_df[['device_id', 'log_type', 'status', 'timestamp', 'usage_hours', 'downtime', 'amc_date', 'lab_site', 'equipment_type']].head(5)
|
| 391 |
preview_html = preview_df.to_html(index=False, classes='table table-striped', border=0)
|
| 392 |
|
| 393 |
# Run critical tasks concurrently
|