feat: add locale formatting config and update notebook outputs

Add configurable locale/display formatting environment variables
(`PALLADIUM_CURRENCY_SYMBOL`, `PALLADIUM_THOUSANDS_SEP`,
`PALLADIUM_DECIMAL_SEP`) to support regional number formatting in the
Streamlit app. Update `.env.example` with documentation for these new
variables.

Also refresh `00_setup.ipynb` with current execution outputs reflecting
a live Athena connection with report templates, a selected client
(Global Guardian Insurance, ID=2), and resolved NameError in assumption
override cells.
This commit is contained in:
2026-06-10 11:54:28 -04:00
parent 253ff38118
commit ecd164ee6d
13 changed files with 839 additions and 111 deletions

View File

@@ -5,13 +5,19 @@ from __future__ import annotations
import streamlit as st
from app.components import charts
from app.pages._helpers import report_meta, safe
from app.locale import CURRENCY_SYMBOL, currency_fmt, fmt_currency
from app.utils import icon
from app.views._helpers import report_meta, safe
from core.export import build_report_data
from core.tei_client import AthenaAPIError, TEIClient
def render(client: TEIClient, tool: dict) -> None:
st.header("📊 Financial Summary")
st.markdown(
f"<h2>{icon('bar-chart-line')} Financial Summary</h2>",
unsafe_allow_html=True,
)
public_id = tool["id"]
report = report_meta(client, tool)
@@ -39,14 +45,14 @@ def render(client: TEIClient, tool: dict) -> None:
cpv = float(summary.get("total_costs_pv") or 0)
cols = st.columns(5)
cols[0].metric("NPV", f"${npv/1_000_000:,.1f}M")
cols[0].metric("NPV", f"{CURRENCY_SYMBOL}{npv/1_000_000:,.1f}M")
cols[1].metric("ROI", f"{roi:,.0f}%")
cols[2].metric(
"Payback",
f"{float(payback):.1f} months" if payback is not None else "N/A",
)
cols[3].metric("Benefits PV", f"${bpv/1_000_000:,.1f}M")
cols[4].metric("Costs PV", f"${cpv/1_000_000:,.1f}M")
cols[3].metric("Benefits PV", f"{CURRENCY_SYMBOL}{bpv/1_000_000:,.1f}M")
cols[4].metric("Costs PV", f"{CURRENCY_SYMBOL}{cpv/1_000_000:,.1f}M")
st.divider()
@@ -64,7 +70,18 @@ def render(client: TEIClient, tool: dict) -> None:
if yb:
charts.cashflow(yb, initial_cost=initial)
with st.expander("Cash flow table"):
st.dataframe(yb, use_container_width=True, hide_index=True)
_cur = currency_fmt()
st.dataframe(
yb,
column_config={
"year": st.column_config.NumberColumn("Year", format="%d"),
"benefits": st.column_config.NumberColumn("Benefits", format=_cur),
"costs": st.column_config.NumberColumn("Costs", format=_cur),
"net": st.column_config.NumberColumn("Net", format=_cur),
},
width="stretch",
hide_index=True,
)
else:
st.caption("No yearly breakdown in this summary.")
@@ -94,11 +111,26 @@ def render(client: TEIClient, tool: dict) -> None:
}
for k, v in envelope["scenarios"].items()
]
st.dataframe(rows, use_container_width=True, hide_index=True)
_cur = currency_fmt()
st.dataframe(
rows,
column_config={
"Scenario": st.column_config.TextColumn("Scenario"),
"Benefits PV": st.column_config.NumberColumn("Benefits PV", format=_cur),
"Costs PV": st.column_config.NumberColumn("Costs PV", format=_cur),
"NPV": st.column_config.NumberColumn("NPV", format=_cur),
"ROI %": st.column_config.NumberColumn("ROI %", format="%.1f%%"),
"Payback (months)": st.column_config.NumberColumn(
"Payback (months)", format="%.1f"
),
},
width="stretch",
hide_index=True,
)
# Export button
st.divider()
if st.button("📦 Build export envelope (JSON)"):
if st.button("Build export envelope (JSON)"):
envelope = safe(
build_report_data,
client,