Spaces:
Sleeping
Sleeping
File size: 11,868 Bytes
21fef23 818baee 21fef23 818baee 21fef23 818baee 4613167 818baee 21fef23 4613167 1afdd36 818baee 1afdd36 818baee 1afdd36 4613167 818baee a086af7 818baee 1afdd36 21fef23 818baee 21fef23 818baee 21fef23 ea4a94a 21fef23 818baee 21fef23 818baee ea4a94a 21fef23 ce801b1 21fef23 ea4a94a c372bb1 4613167 a086af7 4613167 ea4a94a 1afdd36 ea4a94a a086af7 ea4a94a c372bb1 1afdd36 ea4a94a 1afdd36 ea4a94a a086af7 ea4a94a a086af7 ea4a94a 4613167 1afdd36 4613167 1afdd36 4613167 a086af7 4613167 a086af7 4613167 a086af7 4613167 21fef23 4613167 |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 |
import gradio as gr
import pandas as pd
import matplotlib.pyplot as plt
from datetime import datetime, timedelta
import random
# Simulate sample data for lab devices (mimicking Salesforce custom objects)
def generate_sample_data():
labs = ["Lab_A", "Lab_B", "Lab_C"]
equipment_types = ["Microscope", "Centrifuge", "UV_Sterilizer"]
devices = []
logs = []
# Generate devices
for i in range(10):
devices.append({
"Device_ID": f"Device_{i+1}",
"Lab": random.choice(labs),
"Equipment_Type": random.choice(equipment_types),
"Status": random.choice(["Operational", "Down", "Maintenance"])
})
# Generate logs for a broad date range (Jan 1, 2025 to Jun 30, 2025)
start_date = datetime(2025, 1, 1)
end_date = datetime(2025, 6, 30)
for device in devices:
current_date = start_date
while current_date <= end_date:
logs.append({
"Device_ID": device["Device_ID"],
"Log_Timestamp": current_date.strftime("%Y-%m-%d %H:%M:%S"),
"Usage_Count": random.randint(0, 50),
"Status": random.choice(["Operational", "Down", "Maintenance"]),
"AMC_Expiry": (current_date + timedelta(days=random.randint(10, 90))).strftime("%Y-%m-%d")
})
current_date += timedelta(days=1)
return pd.DataFrame(devices), pd.DataFrame(logs)
# Initialize sample data
devices_df, logs_df = generate_sample_data()
def process_dashboard_data(lab_filter, equipment_type_filter, start_date, end_date):
"""
Process device and log data based on filters and return dashboard components.
Args:
lab_filter (str): Selected lab (e.g., Lab_A or All).
equipment_type_filter (str): Selected equipment type (e.g., Microscope or All).
start_date (str): Start date in YYYY-MM-DD format.
end_date (str): End date in YYYY-MM-DD format.
Returns:
tuple: Device cards text, daily trend plot, uptime plot, anomaly alerts, report text, report file.
"""
try:
# Validate and parse date inputs
if start_date and end_date:
try:
start_date = start_date.strip() if start_date else ""
end_date = end_date.strip() if end_date else ""
start_date_dt = datetime.strptime(start_date, "%Y-%m-%d")
end_date_dt = datetime.strptime(end_date, "%Y-%m-%d")
if start_date_dt > end_date_dt:
return "Error: Start date must be before end date.", None, None, None, "", None
# Check if dates are within sample data range
data_start = datetime(2025, 1, 1)
data_end = datetime(2025, 6, 30)
if start_date_dt < data_start or end_date_dt > data_end:
return f"Error: Dates must be between 2025-01-01 and 2025-06-30. Received: Start={start_date}, End={end_date}", None, None, None, "", None
except ValueError:
return f"Error: Invalid date format. Use YYYY-MM-DD (e.g., 2025-05-01). Received: Start={start_date}, End={end_date}", None, None, None, "", None
else:
start_date_dt = datetime(2025, 1, 1)
end_date_dt = datetime(2025, 6, 30)
print(f"Input dates: Start={start_date}, End={end_date}") # Debug log
# Apply filters
filtered_devices = devices_df.copy()
if lab_filter != "All":
filtered_devices = filtered_devices[filtered_devices["Lab"] == lab_filter]
if equipment_type_filter != "All":
filtered_devices = filtered_devices[filtered_devices["Equipment_Type"] == equipment_type_filter]
filtered_logs = logs_df[logs_df["Device_ID"].isin(filtered_devices["Device_ID"])]
if start_date_dt and end_date_dt:
filtered_logs["Log_Date"] = pd.to_datetime(filtered_logs["Log_Timestamp"]).dt.date
start_date_str = start_date_dt.date()
end_date_str = end_date_dt.date()
filtered_logs = filtered_logs[
(filtered_logs["Log_Date"] >= start_date_str) &
(filtered_logs["Log_Date"] <= end_date_str)
]
print(f"Filtered logs count: {len(filtered_logs)}") # Debug log
# Device Cards
device_cards = "Device Cards:\n"
if filtered_devices.empty:
device_cards += "No devices match the selected filters.\n"
for _, device in filtered_devices.iterrows():
device_logs = filtered_logs[filtered_logs["Device_ID"] == device["Device_ID"]]
usage_count = device_logs["Usage_Count"].sum() if not device_logs.empty else 0
last_log = device_logs["Log_Timestamp"].max() if not device_logs.empty else "No logs"
device_cards += (
f"Device: {device['Device_ID']}, Lab: {device['Lab']}, Type: {device['Equipment_Type']}, "
f"Status: {device['Status']}, Usage Count: {usage_count}, Last Log: {last_log}\n"
)
# Daily Log Trends (Matplotlib Plot)
daily_trend_plot = None
if not filtered_logs.empty:
filtered_logs["Date"] = pd.to_datetime(filtered_logs["Log_Timestamp"]).dt.date
daily_trends = filtered_logs.groupby("Date")["Usage_Count"].sum().reset_index()
plt.figure(figsize=(8, 4))
plt.plot(daily_trends["Date"], daily_trends["Usage_Count"], marker="o", color="#1f77b4")
plt.title("Daily Log Trends")
plt.xlabel("Date")
plt.ylabel("Total Usage Count")
plt.xticks(rotation=45)
plt.tight_layout()
daily_trend_plot = plt.gcf()
else:
daily_trend_plot = "No data available for Daily Log Trends in the selected range."
# Weekly Uptime % (Matplotlib Plot)
uptime_plot = None
if not filtered_logs.empty:
filtered_logs["Week"] = pd.to_datetime(filtered_logs["Log_Timestamp"]).dt.isocalendar().week
uptime_data = filtered_logs.groupby("Week")["Status"].value_counts().unstack(fill_value=0)
uptime_data["Uptime_%"] = uptime_data.get("Operational", 0) / (
uptime_data.get("Operational", 0) + uptime_data.get("Down", 0) + uptime_data.get("Maintenance", 0)
) * 100
plt.figure(figsize=(8, 4))
plt.bar(uptime_data.index, uptime_data["Uptime_%"], color="#ff7f0e")
plt.title("Weekly Uptime %")
plt.xlabel("Week")
plt.ylabel("Uptime %")
plt.tight_layout()
uptime_plot = plt.gcf()
else:
uptime_plot = "No data available for Weekly Uptime % in the selected range."
# Anomaly Alerts (Usage spikes: >2x average usage)
anomaly_alerts = "Anomaly Alerts:\n"
if not filtered_logs.empty:
avg_usage = filtered_logs["Usage_Count"].mean()
anomalies = filtered_logs[filtered_logs["Usage_Count"] > 2 * avg_usage]
if anomalies.empty:
anomaly_alerts += "No anomalies detected.\n"
for _, log in anomalies.iterrows():
anomaly_alerts += (
f"Device: {log['Device_ID']}, Timestamp: {log['Log_Timestamp']}, "
f"Usage Spike: {log['Usage_Count']} (Avg: {avg_usage:.2f})\n"
)
else:
anomaly_alerts += "No data available for anomaly detection.\n"
# AMC Reminders (simulated)
report = "LabOps Dashboard Report:\n"
report += f"Generated: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}\n\n"
report += device_cards + "\n"
report += anomaly_alerts + "\n"
report += "AMC Reminders:\n"
if not filtered_logs.empty:
amc_expiring = filtered_logs[pd.to_datetime(filtered_logs["AMC_Expiry"]) <= (datetime.now() + timedelta(days=14))]
if amc_expiring.empty:
report += "No AMC expiries within the next 14 days.\n"
for _, log in amc_expiring.iterrows():
report += (
f"Device: {log['Device_ID']}, AMC Expiry: {log['AMC_Expiry']}\n"
)
else:
report += "No data available for AMC reminders.\n"
return device_cards, daily_trend_plot, uptime_plot, anomaly_alerts, report, "labops_report.txt"
except Exception as e:
return f"Error: {str(e)}", None, None, None, "", None
# Define Gradio interface
with gr.Blocks(title="LabOps Dashboard") as demo:
gr.Markdown("# LabOps Dashboard")
gr.Markdown("Monitor smart lab devices, view usage trends, uptime, anomalies, and export reports.")
gr.Markdown("**Note**: Use the calendar picker to select dates in YYYY-MM-DD format (e.g., 2025-05-01 to 2025-05-30). Dates must be between 2025-01-01 and 2025-06-30. If the calendar picker doesn't appear, enter dates manually.")
# Filters
gr.Markdown("## Filters")
lab_filter = gr.Dropdown(choices=["All"] + list(devices_df["Lab"].unique()), label="Lab Site")
equipment_type_filter = gr.Dropdown(choices=["All"] + list(devices_df["Equipment_Type"].unique()), label="Equipment Type")
start_date = gr.Textbox(
label="Start Date (YYYY-MM-DD)",
placeholder="Select or enter date (e.g., 2025-05-01)",
elem_id="start_date_picker"
)
end_date = gr.Textbox(
label="End Date (YYYY-MM-DD)",
placeholder="Select or enter date (e.g., 2025-05-30)",
elem_id="end_date_picker"
)
# Custom JavaScript to enable browser-native date picker with constraints
gr.HTML("""
<script>
document.addEventListener('DOMContentLoaded', function() {
const startPicker = document.getElementById('start_date_picker');
const endPicker = document.getElementById('end_date_picker');
if (startPicker && endPicker) {
startPicker.type = 'date';
endPicker.type = 'date';
startPicker.min = '2025-01-01';
startPicker.max = '2025-06-30';
endPicker.min = '2025-01-01';
endPicker.max = '2025-06-30';
} else {
console.error('Date picker elements not found');
}
});
</script>
""")
# Dashboard Components
gr.Markdown("## Device Cards")
device_cards_output = gr.Textbox(label="Device Status", lines=10, interactive=False)
gr.Markdown("## Daily Log Trends")
daily_trend_plot = gr.Plot(label="Daily Usage Trends")
gr.Markdown("## Weekly Uptime %")
uptime_plot = gr.Plot(label="Weekly Uptime")
gr.Markdown("## Anomaly Alerts")
anomaly_alerts_output = gr.Textbox(label="Anomaly Alerts", lines=5, interactive=False)
# Export Report
gr.Markdown("## Export Report")
report_output = gr.Textbox(label="Report Preview", lines=10, interactive=False)
download_button = gr.File(label="Download Report as Text")
# Update dashboard on filter change
def update_dashboard(lab, equipment, start_date, end_date):
device_cards, daily_trend, uptime, anomalies, report, report_file = process_dashboard_data(
lab, equipment, start_date, end_date
)
if report_file:
with open(report_file, "w") as f:
f.write(report)
return device_cards, daily_trend, uptime, anomalies, report, report_file
gr.Button("Update Dashboard").click(
fn=update_dashboard,
inputs=[lab_filter, equipment_type_filter, start_date, end_date],
outputs=[device_cards_output, daily_trend_plot, uptime_plot, anomaly_alerts_output, report_output, download_button]
)
# Launch the app
demo.launch() |