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"), [ Input(ids.YEAR_DROPDOWN, "value"), 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( year: int, week: int, u_hits: bool, remove_single: bool, systems: list[str], span: int ) -> html.Div: try: print(f'year: {year}, week: {week}, span: {span}, systems: {systems}') if not all([year, week, span]): return html.Div("No data selected.") years = [year] start_week = week - span + 1 weeks = list(range(start_week, week + 1)) 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"), Output(ids.FEEDBACK_MESSAGE, "children"), Input(ids.MTBF_PAR_TABLE, "selected_rows"), Input(ids.SAVE_FEEDBACK_BUTTON_POPUP, "n_clicks"), Input(ids.CLOSE_FEEDBACK_BUTTON_POPUP, "n_clicks"), State(ids.FEEDBACK_MODAL, "is_open"), State(ids.MTBF_PAR_TABLE, "data"), State(ids.FEEDBACK_INPUT, "value"), ) def handle_feedback_modal(selected_rows, save_clicks, close_clicks, is_open, table_data, comment): ctx = callback_context if not ctx.triggered: return False, "" triggered_id = ctx.triggered[0]['prop_id'].split('.')[0] if triggered_id == ids.MTBF_PAR_TABLE and selected_rows: return True, "" if triggered_id == ids.SAVE_FEEDBACK_BUTTON_POPUP and selected_rows: selected_row_data = table_data[selected_rows[0]] air_value = selected_row_data[MTBFSchema.AIR] timestamp = datetime.now().strftime('%Y-%m-%d %H:%M:%S') if not comment: comment = "" feedback_data = f'{timestamp},dashboard_table,"AIR: {air_value} - {comment}"\n' file_path = "data/feedback.csv" try: is_new_file = not os.path.exists(file_path) or os.path.getsize(file_path) == 0 with open(file_path, "a") as f: if is_new_file: f.write("timestamp,category,comment\n") f.write(feedback_data) message = f"Feedback for AIR '{air_value}' has been saved successfully at {timestamp}." return False, message except Exception as e: return True, "An error occurred while saving feedback." if triggered_id == ids.CLOSE_FEEDBACK_BUTTON_POPUP: return False, "" return is_open, "" @app.callback( Output("feedback-category-label", "children"), Input(ids.MTBF_PAR_TABLE, "selected_rows"), State(ids.MTBF_PAR_TABLE, "data"), prevent_initial_call=True ) def update_feedback_air_label(selected_rows, data): if selected_rows: air_value = data[selected_rows[0]][MTBFSchema.AIR] return f"AIR: {air_value}" return "AIR: " modal = dbc.Modal( [ dbc.ModalHeader(dbc.ModalTitle("Submit Feedback")), dbc.ModalBody([ html.H6("AIR: ", id="feedback-category-label"), dcc.Input(id=ids.FEEDBACK_INPUT, type='text', placeholder='Enter feedback...', style={'width': '100%'}), ]), dbc.ModalFooter([ dbc.Button("Save", id=ids.SAVE_FEEDBACK_BUTTON_POPUP, className="ms-auto", n_clicks=0), dbc.Button("Close", id=ids.CLOSE_FEEDBACK_BUTTON_POPUP, className="ms-auto", n_clicks=0) ]), ], id=ids.FEEDBACK_MODAL, is_open=False, ) return html.Div([ html.Div(id=ids.DATA_TABLE), html.Div(id=ids.FEEDBACK_MESSAGE), modal ])