Your browser sucks, JavaScript disabled! Gunnar's console needs JavaScript and WebAssembly. Git over SSH on port 2222 does not.

nordisk/facett

py/facett_demo/panes/population_pane.py

"""POPULATION pane — the REAL facett table + stat-grid components, embedded as wasm.

This pane renders **no table and no chart of its own**. Every tabular and numeric
surface here is a facett component mounted through `facet-wrapped`:

  * the Iceberg **catalog** list          → `table`     (facett_table::Table)
  * the selected table's **schema**       → `table`
  * its **row preview**                   → `table`
  * the population **aggregate tables**   → `table`
  * the aggregate **bar series**          → `stat-grid` (facett_stat::StatGridView)

It previously used `pandas` + `plotly` + `st.dataframe`, which made it a THIRD
lookalike sitting beside the map and graph ones — facett owns `facett-table` (1483
LOC), `facett-stat`, `facett-plot`, `facett-grid`, `facett-icebergview`, and both korp
and dwarves depend on them. Rendering rows in Python duplicated all of that.

The rule: **if a facett component exists for it, mount the component.** Python here is
only the gRPC plumbing, the layout, and the controls (the selectbox).

Data sources (all korp-server RPCs): ``IcebergTables`` (catalog + schema + stats),
``TablePreview`` (server-formatted row preview), ``Population`` (aggregate tables +
chart series).
"""

from __future__ import annotations

import streamlit as st
from ..facet_host import facett_component, is_data_driven

from ..client import KorpClient, KorpUnavailable

#: The facett components this pane embeds (registry names in `facet-wrapped`).
TABLE_COMPONENT = "table"
STAT_COMPONENT = "stat-grid"


def _require(component: str) -> bool:
    """Refuse loudly if the component is unavailable — never fall back to a fixture."""
    if is_data_driven(component):
        return True
    st.error(
        f"`{component}` is not available as a data-driven facett component — the "
        "`facet-wrapped` wasm bundle is missing or stale. Rebuild it with "
        "`cargo xtask wrap-wasm`. Refusing to render this data in Python."
    )
    return False


def _table(title: str, columns: list[str], rows: list[list[str]], *, key: str, height: int = 320):
    """Mount the REAL facett table for a set of server-formatted rows."""
    if not rows:
        st.info(f"`{title}` returned no rows (honest empty).")
        return
    facett_component(
        TABLE_COMPONENT,
        data={"title": title, "columns": columns, "rows": rows},
        height=height,
        key=key,
    )


def render(client: KorpClient) -> None:
    st.subheader("📊 Population — the real facett table + stat components")
    st.caption(
        "Renderers: `facet-wrapped` → `table` (facett_table::Table) + `stat-grid` "
        "(facett_stat::StatGridView), both as wasm · "
        "Data: korp-server `IcebergTables` · `TablePreview` · `Population`"
    )

    try:
        tables = client.iceberg_tables()
    except KorpUnavailable as e:
        st.error(f"Iceberg catalog unavailable — server could not answer.\n\n`{e}`")
        return

    if not tables:
        st.warning("Warehouse has no tables (honest empty state).")
        return

    if not _require(TABLE_COMPONENT) or not _require(STAT_COMPONENT):
        return

    # ── the Iceberg catalog, in the facett table ───────────────────────────────
    st.markdown("#### 🧊 Iceberg tables")
    _table(
        "Iceberg catalog",
        ["namespace", "table", "rows", "snapshots", "columns"],
        [
            [t.namespace, t.name, str(t.rows), str(t.snapshots), str(len(t.columns))]
            for t in tables
        ],
        key="pop_catalog",
        height=360,
    )

    # ── headline magnitudes, in the facett stat grid ───────────────────────────
    total_rows = sum(t.rows for t in tables)
    facett_component(
        STAT_COMPONENT,
        data={
            "title": "warehouse",
            "rows": [
                {"label": "tables", "value": float(len(tables))},
                {"label": "rows (catalog)", "value": float(total_rows)},
            ],
        },
        height=150,
        key="pop_headline",
    )

    # ── schema + row preview of a selected table ───────────────────────────────
    names = [t.name for t in tables]
    sel = st.selectbox("Preview table", names, index=0, key="pop_table")
    chosen = next((t for t in tables if t.name == sel), None)

    if chosen is not None and chosen.columns:
        st.markdown(f"#### Schema — `{sel}`")
        _table(
            f"{sel} schema",
            ["column", "type"],
            [[c, ty] for c, ty in chosen.columns],
            key="pop_schema",
            height=260,
        )

    try:
        prev = client.table_preview(sel, limit=50)
        st.markdown(f"#### Rows — `{sel}`")
        st.caption(f"{prev.total_rows:,} rows total · {prev.snapshots} snapshot(s)")
        _table(f"{sel} preview", prev.columns, prev.rows, key="pop_preview", height=420)
    except KorpUnavailable as e:
        st.warning(f"Preview unavailable for `{sel}`: `{e}`")

    # ── population aggregates + diagrams ───────────────────────────────────────
    st.markdown("#### 📈 Population diagrams")
    try:
        pop = client.population()
    except KorpUnavailable as e:
        st.error(f"Population aggregates unavailable — server could not answer.\n\n`{e}`")
        return

    st.caption(f"Warehouse: `{pop.warehouse}` · {pop.total_rows:,} rows")

    # korp's ChartSeries bars are (key, value) — the stat grid takes them unreshaped.
    if pop.series:
        for i, s in enumerate(pop.series):
            st.markdown(f"**{s.label}**")
            if s.bars:
                facett_component(
                    STAT_COMPONENT,
                    data={
                        "title": s.label,
                        "bars": [{"key": k, "value": v} for k, v in s.bars],
                    },
                    height=min(680, 46 * len(s.bars) + 90),
                    key=f"pop_series_{i}",
                )
            else:
                st.info(f"`{s.label}` is an empty series (honest empty).")
    else:
        st.info("Server returned no chart series (honest empty).")

    for i, agg in enumerate(pop.aggregates):
        with st.expander(f"Aggregate — {agg.title}", expanded=(i == 0)):
            _table(agg.title, list(agg.columns), agg.rows, key=f"pop_agg_{i}", height=340)