From 04480235fbb15f4de65115c7d214ce6a547d0109 Mon Sep 17 00:00:00 2001 From: TeachMeTW Date: Fri, 11 Oct 2024 11:45:27 -0700 Subject: [PATCH] Loading components now visible --- app_sidebar_collapsible.py | 47 ++++++++++++++++++++----- pages/data.py | 2 +- pages/home.py | 72 ++++++++++++++++++++++++-------------- 3 files changed, 84 insertions(+), 37 deletions(-) diff --git a/app_sidebar_collapsible.py b/app_sidebar_collapsible.py index 71b51b7..b734a1b 100644 --- a/app_sidebar_collapsible.py +++ b/app_sidebar_collapsible.py @@ -241,17 +241,46 @@ def make_home_page(): return [ ] -def make_layout(): return html.Div([ - dcc.Location(id='url', refresh=False), - dcc.Store(id='store-trips', data={}), - dcc.Store(id='store-uuids', data={}), - dcc.Store(id='store-excluded-uuids', data={}), # list of UUIDs from excluded subgroups - dcc.Store(id='store-demographics', data={}), - dcc.Store(id='store-trajectories', data={}), - html.Div(id='page-content', children=make_home_page()), -]) +def make_layout(): + layout = html.Div([ + html.Div(id='home-page-load', style={'display': 'none'}), + dcc.Location(id='url', refresh=False), + dcc.Store(id='store-trips', data={}), + dcc.Store(id='store-uuids', data={}), + dcc.Store(id='store-excluded-uuids', data={}), + dcc.Store(id='store-demographics', data={}), + dcc.Store(id='store-trajectories', data={}), + html.Div(id='page-content', children=make_home_page()), + + ]) + logging.debug("Main layout component IDs: %s", get_all_component_ids(layout)) + return layout + app.layout = make_layout +def get_all_component_ids(component): + ids = [] + if hasattr(component, 'id') and component.id: + ids.append(component.id) + if hasattr(component, 'children'): + children = component.children + if isinstance(children, list): + for child in children: + ids.extend(get_all_component_ids(child)) + else: + ids.extend(get_all_component_ids(children)) + return ids + + + +app.validation_layout = html.Div([ + make_layout(), + *[page['layout'] for page in dash.page_registry.values()] +]) + +logging.debug("Validation layout component IDs: %s", get_all_component_ids(app.validation_layout)) + + # make the 'filters' menu collapsible @app.callback( Output("collapse-filters", "is_open"), diff --git a/pages/data.py b/pages/data.py index 29a5c86..bd21574 100644 --- a/pages/data.py +++ b/pages/data.py @@ -31,7 +31,7 @@ html.Div(id='tabs-content'), dcc.Store(id='selected-tab', data='tab-uuids-datatable'), # Store to hold selected tab dcc.Interval(id='interval-load-more', interval=20000, n_intervals=0), # default loading at 10s, can be lowered or hightened based on perf (usual process local is 3s) - dcc.Store(id='store-uuids', data=[]), # Store to hold the original UUIDs data + #dcc.Store(id='store-uuids', data=[]), # Store to hold the original UUIDs data dcc.Store(id='store-loaded-uuids', data={'data': [], 'loaded': False}), # Store to track loaded data # RadioItems for key list switch, wrapped in a div that can hide/show html.Div( diff --git a/pages/home.py b/pages/home.py index c0b75f9..420667a 100644 --- a/pages/home.py +++ b/pages/home.py @@ -5,7 +5,7 @@ """ from uuid import UUID -from dash import dcc, html, Input, Output, callback, register_page +from dash import dcc, html, Input, Output, callback, register_page, no_update import dash_bootstrap_components as dbc import plotly.express as px @@ -35,17 +35,17 @@ [ dcc.Markdown(intro), - # Cards + # Cards with loading spinners dbc.Row([ - dbc.Col(id='card-users'), - dbc.Col(id='card-active-users'), - dbc.Col(id='card-trips') + dbc.Col(dcc.Loading(children=[html.Div(id='card-users')], type='default')), + dbc.Col(dcc.Loading(children=[html.Div(id='card-active-users')], type='default')), + dbc.Col(dcc.Loading(children=[html.Div(id='card-trips')], type='default')) ]), - # Plots + # Plots with loading spinners dbc.Row([ - dcc.Graph(id="fig-sign-up-trend"), - dcc.Graph(id="fig-trips-trend"), + dbc.Col(dcc.Loading(children=[dcc.Graph(id="fig-sign-up-trend")], type='default')), + dbc.Col(dcc.Loading(children=[dcc.Graph(id="fig-trips-trend")], type='default')), ]) ] ) @@ -117,22 +117,32 @@ def generate_card(title_text, body_text, icon): ]) return card - @callback( Output('card-users', 'children'), Input('store-uuids', 'data'), + Input('url', 'pathname'), + Input('home-page-load', 'children') ) -def update_card_users(store_uuids): - number_of_users = store_uuids.get('length') if has_permission('overview_users') else 0 +def update_card_users(store_uuids, pathname, _): + if pathname != "/": + return no_update + if store_uuids is None or not has_permission('overview_users'): + number_of_users = 0 + else: + number_of_users = store_uuids.get('length', 0) card = generate_card("# Users", f"{number_of_users} users", "fa fa-users") return card - @callback( Output('card-active-users', 'children'), Input('store-uuids', 'data'), + Input('url', 'pathname'), # Added Input + Input('home-page-load', 'children') ) -def update_card_active_users(store_uuids): +def update_card_active_users(store_uuids, pathname, _): + if pathname != "/": + return no_update + uuid_df = pd.DataFrame(store_uuids.get('data')) number_of_active_users = 0 if not uuid_df.empty and has_permission('overview_active_users'): @@ -141,49 +151,57 @@ def update_card_active_users(store_uuids): card = generate_card("# Active users", f"{number_of_active_users} users", "fa fa-person-walking") return card - @callback( Output('card-trips', 'children'), Input('store-trips', 'data'), + Input('url', 'pathname') # Added Input ) -def update_card_trips(store_trips): +def update_card_trips(store_trips, pathname): + if pathname != "/": + return no_update + number_of_trips = store_trips.get('length') if has_permission('overview_trips') else 0 card = generate_card("# Confirmed trips", f"{number_of_trips} trips", "fa fa-angles-right") return card - def generate_barplot(data, x, y, title): fig = px.bar() - if data is not None: + if data is not None and not data.empty: fig = px.bar(data, x=x, y=y) fig.update_layout(title=title) return fig - @callback( Output('fig-sign-up-trend', 'figure'), Input('store-uuids', 'data'), + Input('url', 'pathname') # Added Input ) -def generate_plot_sign_up_trend(store_uuids): +def generate_plot_sign_up_trend(store_uuids, pathname): + if pathname != "/": + return no_update + df = pd.DataFrame(store_uuids.get("data")) trend_df = None if not df.empty and has_permission('overview_signup_trends'): trend_df = compute_sign_up_trend(df) - fig = generate_barplot(trend_df, x = 'date', y = 'count', title = "Sign-ups trend") + fig = generate_barplot(trend_df, x='date', y='count', title="Sign-ups trend") return fig - @callback( Output('fig-trips-trend', 'figure'), Input('store-trips', 'data'), - Input('date-picker', 'start_date'), # these are ISO strings - Input('date-picker', 'end_date'), # these are ISO strings + Input('date-picker', 'start_date'), # these are ISO strings + Input('date-picker', 'end_date'), # these are ISO strings + Input('url', 'pathname') # Added Input ) -def generate_plot_trips_trend(store_trips, start_date, end_date): +def generate_plot_trips_trend(store_trips, start_date, end_date, pathname): + if pathname != "/": + return no_update + df = pd.DataFrame(store_trips.get("data")) trend_df = None (start_date, end_date) = iso_to_date_only(start_date, end_date) if not df.empty and has_permission('overview_trips_trend'): - trend_df = compute_trips_trend(df, date_col = "trip_start_time_str") - fig = generate_barplot(trend_df, x = 'date', y = 'count', title = f"Trips trend({start_date} to {end_date})") - return fig + trend_df = compute_trips_trend(df, date_col="trip_start_time_str") + fig = generate_barplot(trend_df, x='date', y='count', title=f"Trips trend ({start_date} to {end_date})") + return fig \ No newline at end of file