############ Imports ##############
import vizro.plotly.express as px
import vizro.models as vm
from vizro.models.types import capture
from vizro import Vizro
import pandas as pd
from vizro.managers import data_manager
import dash_mantine_components as dmc
from dash import html
from vizro.figures import kpi_card
###### Function definitions ######
def load_equipment_health_data(equipment_id: str) -> list:
"""Load 30 days of historical health data for equipment chart from consolidated CSV file."""
# Load the consolidated history data
history_df = pd.read_csv("equipment_history_consolidated.csv")
# Filter data for the specific equipment
equipment_data = history_df[history_df['equipment_id'] == equipment_id]
# Transform data for better visualization
chart_data = []
for _, row in equipment_data.iterrows():
data_point = {
"date": row["date"],
"health_score": row["health_score"],
# Scale predicted failure risk to percentage (0-100) for visibility
"predicted_failure": row["predicted_failure"] * 100
}
chart_data.append(data_point)
return chart_data
@capture("figure")
def equipment_risk_cards(data_frame: pd.DataFrame) -> html.Div:
"""Creates collapsible cards for equipment at risk with detailed information."""
# Filter for equipment at risk (predicted failure <= 7 days)
at_risk_df = data_frame[data_frame["Predicted_Failure_Days"] <= 7].copy()
cards = []
for idx, row in at_risk_df.iterrows():
risk_level = row["Risk_Level"]
# Set badge color based on maintenance schedule
maintenance_color = "#689f38" if row["Maintenance_Scheduled"] == "Yes" else "#ff5267"
maintenance_text = "Maintenance Scheduled" if row["Maintenance_Scheduled"] == "Yes" else "No Maintenance Scheduled"
# Create the card content
card = dmc.Card(
children=[
dmc.Group(
[
dmc.Text(
f"{row['Equipment_Name']} ({row['Equipment_ID']})",
size="lg",
fw=500,
),
dmc.Badge(
maintenance_text,
color=maintenance_color,
variant="filled",
),
],
justify="space-between",
),
dmc.Space(h="md"),
dmc.Accordion(
children=[
dmc.AccordionItem(
children=[
dmc.AccordionControl(
f"{risk_level} Risk ({row['Predicted_Failure_Days']} days) - Part: {row['Part_Name']} - {row['Failure_Reason']}"
),
dmc.AccordionPanel(
[
dmc.Space(h="sm"),
# Two-column layout: equipment details on left, chart on right
dmc.Grid(
[
dmc.GridCol(
[
dmc.Group(
[
dmc.Text(
"Equipment Type:", fw=500, size="sm"
),
dmc.Text(
row["Equipment_Type"],
size="sm",
c="dimmed",
),
]
),
dmc.Space(h="xs"),
dmc.Group(
[
dmc.Text(
"Location:", fw=500, size="sm"
),
dmc.Text(
row["Location"],
size="sm",
c="dimmed",
),
]
),
dmc.Space(h="xs"),
dmc.Group(
[
dmc.Text(
"Health Score:", fw=500, size="sm"
),
dmc.Text(
f"{row['Health_Score']}/100",
size="sm",
c="dimmed",
),
]
),
dmc.Space(h="xs"),
dmc.Group(
[
dmc.Text(
"Maintenance Status:",
fw=500,
size="sm",
),
dmc.Text(
"Scheduled" if row["Maintenance_Scheduled"] == "Yes" else "Not Scheduled",
size="sm",
c="dimmed",
),
]
),
dmc.Space(h="xs"),
dmc.Group(
[
dmc.Text(
"Recommended Window:",
fw=500,
size="sm",
),
dmc.Text(
f"{row['Recommended_Window_Start']} to {row['Recommended_Window_End']}",
size="sm",
c="dimmed",
),
]
),
dmc.Space(h="xs"),
dmc.Group(
[
dmc.Text(
"Priority:", fw=500, size="sm"
),
dmc.Badge(
row["Maintenance_Priority"],
variant="light",
),
]
),
dmc.Space(h="sm"),
dmc.Text("Sensor Readings:", fw=500, size="sm"),
dmc.Group(
[
dmc.Text(
f"Temperature: {row['Temperature']}°C",
size="xs",
c="dimmed",
),
dmc.Text(
f"Vibration: {row['Vibration']} Hz",
size="xs",
c="dimmed",
),
dmc.Text(
f"Pressure: {row['Pressure']} PSI",
size="xs",
c="dimmed",
),
]
),
],
span=6
),
dmc.GridCol(
[
dmc.Text("30-Day Health Trend", fw=500, size="sm", style={"marginBottom": "10px"}),
dmc.CompositeChart(
h=250,
data=load_equipment_health_data(row["Equipment_ID"]),
dataKey="date",
withLegend=True,
series=[
{
"name": "health_score",
"color": "#08bdba",
"type": "area"
},
{
"name": "predicted_failure",
"color": "#ff9222",
"type": "line"
},
]
)
],
span=6
)
]
)
]
),
],
value=f"item-{idx}",
)
]
),
],
withBorder=True,
shadow="sm",
style={"marginBottom": "16px"},
)
cards.append(card)
if not cards:
return html.Div(
[
dmc.Alert(
title="No Equipment at Risk",
children="All equipment is operating within normal parameters.",
color="#689f38",
variant="light",
)
]
)
return html.Div(cards)
###### Data Manager Settings - Dynamic Data #####
def load_equipment_data():
"""Load equipment health data from CSV file."""
return pd.read_csv("equipment_health_data.csv")
def load_operational_kpi():
"""Load operational equipment count."""
equipment_df = pd.read_csv("equipment_health_data.csv")
operational_count = len(equipment_df[equipment_df['Status'] == 'Operational'])
return pd.DataFrame({'count': [operational_count]})
def load_at_risk_kpi():
"""Load at-risk equipment count."""
equipment_df = pd.read_csv("equipment_health_data.csv")
at_risk_count = len(equipment_df[equipment_df['Predicted_Failure_Days'] <= 7])
return pd.DataFrame({'count': [at_risk_count]})
# Register dynamic data functions with data manager
data_manager["equipment_health_data"] = load_equipment_data
data_manager["operational_kpi"] = load_operational_kpi
data_manager["at_risk_kpi"] = load_at_risk_kpi
########### Model code ############
model = vm.Dashboard(
pages=[
vm.Page(
components=[
vm.Container(
layout=vm.Grid(grid=[[0, 1, 2]]),
components=[
vm.Text(
id="analysis_status",
text="Equipment Health Analysis Dashboard\n\nShowing current equipment status and risk assessments."
),
vm.Figure(
id="operational_kpi",
figure=kpi_card(
data_frame="operational_kpi",
value_column="count",
title="Operational Equipment",
value_format="{value} / 15",
icon="Circle",
agg_func="sum"
)
),
vm.Figure(
id="at_risk_kpi",
figure=kpi_card(
data_frame="at_risk_kpi",
value_column="count",
title="Equipment at Risk (≤7 days)",
value_format="{value} equipment",
icon="Warning",
agg_func="sum"
)
),
],
),
vm.Figure(
id="equipment_at_risk_details",
figure=equipment_risk_cards(data_frame="equipment_health_data"),
),
],
title="Equipment Health",
controls=[
vm.Filter(
column="Maintenance_Scheduled",
targets=["equipment_at_risk_details"],
selector=vm.Dropdown(
title="Maintenance Schedule Status"
),
),
vm.Filter(
column="Location",
targets=["equipment_at_risk_details"],
selector=vm.Checklist(
title="Location"
),
),
vm.Filter(
column="Equipment_Type",
targets=["equipment_at_risk_details"],
selector=vm.Checklist(
title="Equipment Type"
),
),
],
layout=vm.Flex(direction="column"),
)
],
theme="vizro_dark",
title="Equipment Health Monitoring - Mineral Refinement Factory",
)
Vizro().build(model).run()