import pandas as pd import streamlit as st from config.config import CRRA_COLORS, CRRA_FONT, PLOTLY_TEMPLATE def apply_chart_style(fig, height=None): """Apply the standard CRRA theme (font, colours, margins) to a Plotly figure. Args: fig: Plotly figure object. height (int | None): optional fixed height in pixels. Returns: fig: the same figure with updated layout. """ fig.update_layout( template=PLOTLY_TEMPLATE, font=dict(family=CRRA_FONT, size=12, color=CRRA_COLORS["black"]), margin=dict(t=40, b=40, l=40, r=20), legend=dict( orientation="v", yanchor="middle", y=0.5, xanchor="left", x=1.02, ), ) if height: fig.update_layout(height=height) return fig def register_chart_data(name: str, df: pd.DataFrame): """Store a chart's underlying DataFrame in the session-level registry for bulk export. Args: name (str): unique chart label used as the dict key and export sheet name. df (pd.DataFrame): data behind the chart. Returns: None """ st.session_state.setdefault("chart_data_registry", {}) st.session_state["chart_data_registry"][name] = df.copy() def show_bar(): """Render the CRRA four-colour decorative bar below page headings. Returns: None """ st.markdown( """
""", unsafe_allow_html=True, ) def smart_display_format(val): """Format a numeric value showing all significant digits, no rounding. Uses repr() which gives the shortest decimal string that round-trips exactly to the same float — avoids floating-point noise like 462961.099999999976717 when the value is actually 462961.1. Falls back to fixed-decimal notation when repr() produces scientific notation (very small numbers such as 1e-11). Args: val (float): numeric value to format. Returns: str: formatted string, or "" for near-zero values. """ if abs(val) < 1e-14: return "" s = repr(val) if "e" not in s.lower(): return s # Scientific notation → convert to fixed decimal (e.g. 1e-11 → 0.00000000001) return f"{val:.15f}".rstrip("0").rstrip(".") def format_efficiency_summary(df: pd.DataFrame, df_default: pd.DataFrame): """Build a styled Pandas Styler for the resource-efficiency coefficient summary table. Cells whose value differs from the default median are highlighted in red. Args: df (pd.DataFrame): current coefficient values (methods × resources). df_default (pd.DataFrame): default median values for the same shape. Returns: pandas.io.formats.style.Styler: styled dataframe ready for st.dataframe(). """ df_numeric = df.copy() df_display = df_numeric.map(smart_display_format) def highlight_changes(data): styled = pd.DataFrame('', index=data.index, columns=data.columns) for m in data.index: for r in data.columns: try: current = df_numeric.at[m, r] default = df_default.at[m, r] if abs(current - default) > 1e-6: styled.at[m, r] = 'background-color: #ff5f4d' except: continue return styled return ( df_display .style .apply(highlight_changes, axis=None) )