"""DataTools — Data Cleaning Mastery Suite. Launch: streamlit run src/gui/app.py This module is the navigation manager for the full GUI: it registers every tool page with ``st.navigation`` so the sidebar can render section headers ("Data Review", "Data Cleaners", "Transformations", "Automations") instead of the flat numbered list Streamlit's auto-page-discovery would produce. The Home page itself is registered as a callable defined below so the entry script remains the single file users invoke. """ from __future__ import annotations import sys from pathlib import Path import streamlit as st # Ensure project root is on sys.path so `src.core` imports work _project_root = Path(__file__).resolve().parent.parent.parent if str(_project_root) not in sys.path: sys.path.insert(0, str(_project_root)) # --------------------------------------------------------------------------- # Home page (rendered when the user selects the default nav entry) # --------------------------------------------------------------------------- # # The renderer lives in ``src/gui/_home.py`` so the ``back_to_home_link`` # helper on tool pages can ``import _home_page`` and pass it into # ``st.switch_page`` without re-executing this entry script's # navigation setup (which would crash because tool pages have a # different "main script" context and the relative ``pages/…`` paths # below would no longer resolve). from src.gui._home import _home_page # noqa: E402 # --------------------------------------------------------------------------- # Navigation registration # --------------------------------------------------------------------------- # # ``st.navigation`` overrides Streamlit's auto-discovery of the # ``pages/`` directory, so every page we want in the sidebar must be # listed here. The dict key becomes the section header rendered above # the entries; an empty-string key suppresses the header so the Home # entry sits at the top without a label above it. from src.gui.tools_registry import TOOLS, section_label, tool_name # noqa: E402 from src.i18n import t as _t # noqa: E402 def _page_for(tool_id: str, *, page_slug: str, icon: str, title: str) -> "st.Page": return st.Page( f"pages/{page_slug}.py", title=title, icon=icon, url_path=tool_id, ) def _build_navigation() -> dict[str, list]: by_section: dict[str, list] = { "cleaners": [], "transformations": [], "automations": [], } # Resolve the tool name through ``tool_name`` (i18n lookup) instead # of using the registry's English ``tool.name`` field, otherwise the # sidebar stays in English when the user switches to Spanish. for tool in TOOLS: by_section[tool.section].append( _page_for( tool.tool_id, page_slug=tool.page_slug, icon=tool.icon, title=tool_name(tool.tool_id), ) ) home = st.Page( _home_page, title=_t("nav.home_page_title") or "Home", icon=":material/home:", default=True, url_path="home", ) # Activate + Close are both reached from the sticky-footer Help # popover (Activate / Manage-license link and Close button). They # are registered with ``st.navigation`` so ``/activate`` and # ``/close`` remain URL-routable — pages not listed in the dict # 404 even by direct hit — but their sidebar entries are hidden # via CSS in ``hide_streamlit_chrome``. We group them under a # single section header that is also hidden by that same CSS rule # so no orphan "Account" / "Close" label is left in the sidebar. activate = st.Page( "pages/_Activate.py", title=_t("nav.activate_title") or "Activate", icon=":material/key:", url_path="activate", ) close = st.Page( "pages/99_Close.py", title=_t("nav.close_title") or "Close", icon=":material/close:", url_path="close", ) # Activate + Close are placed in the unlabeled section alongside # Home (key ``""`` — Streamlit renders no header for it). The CSS # in ``hide_streamlit_chrome`` then hides just their two links by # ``href``, leaving Home visible and no orphan section header / # drilldown marker in the sidebar. return { "": [home, activate, close], section_label("cleaners"): by_section["cleaners"], section_label("transformations"): by_section["transformations"], section_label("automations"): by_section["automations"], } pg = st.navigation(_build_navigation()) pg.run()