Home now appears in the sidebar as "File Analysis" under a labeled "Analysis" section together with Reconcile Two Files — both pages are data-analysis workflows (importing/profiling files vs. matching across files), so grouping them clarifies the sidebar's mental model. - tools_registry: new ``analysis`` Section; reconcile moves out of automations into it. - i18n: ``nav.section_analysis`` + ``nav.file_analysis_title`` added to en.json and es.json. - app.py: home dropped from the unlabeled section and surfaced at the top of the Analysis group; ``default=True`` preserved so first-visit routing is unchanged. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
155 lines
5.9 KiB
Python
155 lines
5.9 KiB
Python
"""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))
|
|
|
|
|
|
# Quiet the loguru stderr sink. The CLIs (``src/cli*.py``) reconfigure
|
|
# it per-script to ``WARNING``; the GUI's ``python -m src.gui``
|
|
# entrypoint never did, so internal ``logger.debug`` /
|
|
# ``logger.warning`` breadcrumbs bled into the terminal where users
|
|
# launched the app from. Drop the default ``DEBUG``-level stderr sink
|
|
# and add a clean ``ERROR``-only one so only genuinely actionable
|
|
# problems surface — diagnostic warnings still land in the audit log
|
|
# via its own sink. Runs once, before any module-level ``logger`` use
|
|
# in the GUI tree.
|
|
from loguru import logger as _logger # noqa: E402
|
|
_logger.remove()
|
|
_logger.add(sys.stderr, level="ERROR", format="{level: <8} | {message}")
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# 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] = {
|
|
"analysis": [],
|
|
"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 is now surfaced under the new "Analysis" section as
|
|
# "File Analysis" — the home page's content (importing files,
|
|
# running the analyzer, browsing findings) is itself a data-analysis
|
|
# workflow, so grouping it next to Reconcile keeps the sidebar's
|
|
# mental model coherent. ``default=True`` still points at this
|
|
# page so first-visit lands here regardless of section placement.
|
|
home = st.Page(
|
|
_home_page,
|
|
title=_t("nav.file_analysis_title") or "File Analysis",
|
|
icon=":material/insert_chart_outlined:",
|
|
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",
|
|
)
|
|
logs = st.Page(
|
|
"pages/_Logs.py",
|
|
title="Logs",
|
|
icon=":material/description:",
|
|
url_path="logs",
|
|
)
|
|
close = st.Page(
|
|
"pages/99_Close.py",
|
|
title=_t("nav.close_title") or "Close",
|
|
icon=":material/close:",
|
|
url_path="close",
|
|
)
|
|
|
|
# Activate / Logs / Close stay in the unlabeled section (key ``""``)
|
|
# so the CSS in ``hide_streamlit_chrome`` keeps hiding them by
|
|
# ``href``. Home moved out of that bucket into "Analysis" — the
|
|
# unlabeled section now contains ONLY hidden pages, so no orphan
|
|
# entry appears above the "Analysis" header in the sidebar.
|
|
return {
|
|
"": [activate, logs, close],
|
|
section_label("analysis"): [home, *by_section["analysis"]],
|
|
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()
|