"""App configuration for dashboard.""" from typing import Union from dash import html, get_asset_url import dash_bootstrap_components as dbc import vizro.models as vm from chart_groups import ALL_CHART_GROUP, CHART_GROUPS, ChartGroup, IncompletePage from custom_components import FlexContainer, Markdown from vizro import Vizro def make_chart_card(page: Union[vm.Page, IncompletePage]) -> vm.Card: """Makes a card with svg icon, linked to the right page if page is complete. Args: page: page to make card for Returns: card with svg icon, linked to the right page if page is complete. """ # There's one SVG per chart title, so that e.g. pages distribution-butterfly and deviation-butterfly, which both # have title "Butterfly", correspond to butterfly.svg. # Incomplete pages have page.path = "" so won't be linked to here. svg_name = page.title.lower().replace(" ", "-") return vm.Card( text=f""" ![](assets/images/charts/{svg_name}.svg#chart-icon) #### {page.title} """, href=page.path, ) def make_homepage_container(chart_group: ChartGroup) -> vm.Container: """Makes a container with cards for each completed and incomplete chart in chart_group. Args: chart_group: group of charts to make container for. Returns: container with cards for each chart in chart_group. """ # Pages are sorted in title's alphabetical order and deduplicated so that e.g. pages distribution-butterfly and # deviation-butterfly, which both have title "Butterfly", correspond to a single card. return vm.Container( title=chart_group.name, layout=vm.Layout(grid=[[0, 1, 1]], col_gap="40px"), components=[ Markdown(text=chart_group.intro_text, classname="intro-text"), FlexContainer( components=[ make_chart_card(page) for page in sorted( _remove_duplicates(chart_group.pages + chart_group.incomplete_pages), key=lambda page: page.title, ) ], ), ], ) def _remove_duplicates(pages: list[Union[vm.Page, IncompletePage]]) -> list[Union[vm.Page, IncompletePage]]: # Deduplicate pages that have the same title. Using reversed means that the page that is kept is the first one # in the dashboard. This will be the one that the card on the homepage links to. return list({page.title: page for page in reversed(pages)}.values()) def make_navlink(chart_group: ChartGroup) -> vm.NavLink: """Makes a navlink with icon and links to every complete page within chart_group. Args: chart_group: chart_group to make a navlink for. Returns: navlink for chart_group. """ # Pages are sorted in alphabetical order within each chart group. return vm.NavLink( label=chart_group.name, pages={chart_group.name: [page.id for page in sorted(chart_group.pages, key=lambda page: page.title)]}, icon=chart_group.icon, ) homepage = vm.Page( title="Overview", components=[ vm.Tabs(tabs=[make_homepage_container(chart_group) for chart_group in [ALL_CHART_GROUP, *CHART_GROUPS]]), ], ) # TODO: consider whether each chart group should have its own individual homepage, # e.g. at http://localhost:8050/deviation/. This could just repeat the content of the tab from the homepage and would # work nicely with the hierarchical navigation. dashboard = vm.Dashboard( # ALL_CHART_GROUP.pages has duplicated pages, e.g. both distribution-butterfly and deviation-butterfly. title="Visual vocabulary", pages=[homepage, *ALL_CHART_GROUP.pages], navigation=vm.Navigation( nav_selector=vm.NavBar( items=[ vm.NavLink(label="Overview", pages=[homepage.id], icon="Home"), ] + [make_navlink(chart_group) for chart_group in CHART_GROUPS] ) ), ) app = Vizro().build(dashboard) app.dash.layout.children.append( html.Div( [ html.Div( [ "Made using ", html.Img(src=get_asset_url("logo.svg"), id="banner", alt="Vizro logo"), dbc.NavLink("vizro", href="https://github.com/mckinsey/vizro", target="_blank", external_link=True), ], className="anchor-div", ), ], className="anchor-container", ) ) server = app.dash.server if __name__ == "__main__": app.run(port=8051)