feat: Improve dashboard functionality and stability

This commit introduces several improvements to the dashboard:

- Refactored the component structure for consistency, defining callbacks within the `render` function.
- Added robust error handling to data loading and callbacks to prevent crashes.
- Implemented linking for SO and AIR columns in the source table.
- Added and improved filtering and display options for tables.
- Left-aligned columns in tables for better readability.
- Cleaned up unused component files.
This commit is contained in:
2025-09-18 05:20:13 +02:00
parent db30fa9b4e
commit 01da618733
11 changed files with 225 additions and 218 deletions

View File

@ -7,15 +7,6 @@ import dash_bootstrap_components as dbc
from ..data.loader_gz import MTBFSchema
from . import ids
import pandas as pd
from dash import Dash, dcc, html, dash_table, Input, Output, State, callback_context
from datetime import datetime
import os
import dash_bootstrap_components as dbc
from ..data.loader_gz import MTBFSchema
from . import ids
def render(app: Dash, data: pd.DataFrame) -> html.Div:
@app.callback(
Output(ids.DATA_TABLE, "children"),
@ -24,46 +15,68 @@ def render(app: Dash, data: pd.DataFrame) -> html.Div:
Input(ids.WEEK_DROPDOWN, "value"),
Input(ids.PU_HITS_SELECTOR, "value"),
Input(ids.SINGLE_HITTER_FILTER, "value"),
Input(ids.SYSTEM_FILTER, "value"),
Input("span", "value"),
],
)
def update_data_table(
years: list[str], weeks: list[str], u_hits: bool, remove_single: bool
year: int, week: int, u_hits: bool, remove_single: bool, systems: list[str], span: int
) -> html.Div:
filtered_data = data.query(
"year in @years and week in @weeks"
)
if filtered_data.shape[0] == 0:
return html.Div("No data selected.")
try:
print(f'year: {year}, week: {week}, span: {span}, systems: {systems}')
if not all([year, week, span]):
return html.Div("No data selected.")
hits_col = MTBFSchema.U_HITS if u_hits else MTBFSchema.P_HITS
# Count 'Y' values
hits_data = filtered_data[filtered_data[hits_col] == 'Y']
table_data = hits_data.groupby(MTBFSchema.AIR).agg(
count=(hits_col, 'size'),
air_issue_description=(MTBFSchema.AIR_ISSUE_DESCRIPTION, lambda x: ', '.join(x.dropna().astype(str).unique())),
close_notes=(MTBFSchema.CLOSE_NOTES, lambda x: ', '.join(x.dropna().astype(str).unique()))
).reset_index()
if remove_single:
table_data = table_data[table_data['count'] > 1]
years = [year]
table_data = table_data.sort_values('count', ascending=False)
# Reorder columns
table_data = table_data[[MTBFSchema.AIR, 'air_issue_description', 'close_notes', 'count']]
start_week = week - span + 1
weeks = list(range(start_week, week + 1))
return dash_table.DataTable(
id=ids.MTBF_PAR_TABLE, # Using this ID for feedback callbacks
data=table_data.to_dict("records"),
columns=[{"name": i, "id": i} for i in table_data.columns],
page_size=10,
row_selectable='single',
selected_rows=[],
filter_action='native',
sort_action='native'
)
filtered_data = data.query(
"year in @years and Week_Number in @weeks and System in @systems"
)
if filtered_data.shape[0] == 0:
return html.Div("No data selected.")
hits_col = MTBFSchema.U_HITS if u_hits else MTBFSchema.P_HITS
# Count 'Y' values
hits_data = filtered_data[filtered_data[hits_col] == 'Y']
table_data = hits_data.groupby([MTBFSchema.AIR, MTBFSchema.SYSTEM]).agg(
count=(hits_col, 'size'),
air_issue_description=(MTBFSchema.AIR_ISSUE_DESCRIPTION, lambda x: ', '.join(x.dropna().astype(str).unique())),
close_notes=(MTBFSchema.CLOSE_NOTES, lambda x: ', '.join(x.dropna().astype(str).unique()))
).reset_index()
if remove_single:
table_data = table_data[table_data['count'] > 1]
table_data['air_issue_description'] = table_data['air_issue_description'].apply(lambda s: s[:80] + '...' if len(s) > 80 else s)
table_data = table_data.sort_values('count', ascending=False)
# Reorder columns
table_data = table_data[[MTBFSchema.AIR, MTBFSchema.SYSTEM, 'air_issue_description', 'close_notes', 'count']]
return dash_table.DataTable(
id=ids.MTBF_PAR_TABLE, # Using this ID for feedback callbacks
data=table_data.to_dict("records"),
columns=[{"name": i, "id": i} for i in table_data.columns],
page_size=10,
row_selectable='single',
selected_rows=[],
filter_action='native',
sort_action='native',
style_cell_conditional=[
{
'if': {'column_id': c},
'textAlign': 'left'
} for c in ['Close_notes', 'count']
]
)
except Exception as e:
return html.Div(f"An error occurred in data_table: {e}")
@app.callback(
Output(ids.FEEDBACK_MODAL, "is_open"),