Spaces:
Sleeping
Sleeping
[FEAT] Replace hardcoded tab intros and method disabled notes with ui_text.xlsx; fix slider negative min
Browse files- config/config.py +26 -6
- data/tooltips.xlsx +0 -0
- data/ui_text.xlsx +0 -0
- tabs/tab1_inputs.py +2 -4
- tabs/tab2_constraints.py +7 -8
- tabs/tab3_coefficients.py +8 -12
- tabs/tab5_sensitivity.py +2 -5
config/config.py
CHANGED
|
@@ -6,7 +6,7 @@ CO2_UNIT = "MtCO₂"
|
|
| 6 |
with open("data/method_groups.json") as _f:
|
| 7 |
method_groups: dict[str, list[str]] = json.load(_f)
|
| 8 |
|
| 9 |
-
csv_taxonomy = pd.read_csv("data/
|
| 10 |
|
| 11 |
# resource_groups: group → [resources], order follows first appearance in CSV
|
| 12 |
resource_groups: dict[str, list[str]] = {}
|
|
@@ -38,13 +38,21 @@ try:
|
|
| 38 |
except FileNotFoundError:
|
| 39 |
secondary_resources: set[tuple[str, str]] = set()
|
| 40 |
|
| 41 |
-
#
|
| 42 |
-
#
|
| 43 |
-
#
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 44 |
try:
|
| 45 |
-
tooltips_xl = pd.read_excel("data/
|
| 46 |
df_res = tooltips_xl.get("Resources", pd.DataFrame(columns=["Resource", "Tooltip"]))
|
| 47 |
-
df_met = tooltips_xl.get("Methods", pd.DataFrame(columns=["Method", "Tooltip"]))
|
|
|
|
| 48 |
resource_tooltips: dict[str, str] = {
|
| 49 |
row["Resource"]: row["Tooltip"]
|
| 50 |
for _, row in df_res.iterrows()
|
|
@@ -55,9 +63,21 @@ try:
|
|
| 55 |
for _, row in df_met.iterrows()
|
| 56 |
if pd.notna(row.get("Tooltip")) and str(row.get("Tooltip", "")).strip()
|
| 57 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 58 |
except FileNotFoundError:
|
| 59 |
resource_tooltips: dict[str, str] = {}
|
| 60 |
method_tooltips: dict[str, str] = {}
|
|
|
|
|
|
|
| 61 |
|
| 62 |
# -- Theme colors and fonts for CRRA plots (used in plotly templates) ----------------------------------------------
|
| 63 |
|
|
|
|
| 6 |
with open("data/method_groups.json") as _f:
|
| 7 |
method_groups: dict[str, list[str]] = json.load(_f)
|
| 8 |
|
| 9 |
+
csv_taxonomy = pd.read_csv("data/inputs.csv")
|
| 10 |
|
| 11 |
# resource_groups: group → [resources], order follows first appearance in CSV
|
| 12 |
resource_groups: dict[str, list[str]] = {}
|
|
|
|
| 38 |
except FileNotFoundError:
|
| 39 |
secondary_resources: set[tuple[str, str]] = set()
|
| 40 |
|
| 41 |
+
# All editable copy (tooltips, per-method "why is this disabled?" notes, and
|
| 42 |
+
# each tab's OBJECTIVE intro paragraph) lives in data/ui_text.xlsx, three sheets:
|
| 43 |
+
# "Resources" — Resource | Tooltip
|
| 44 |
+
# "Methods" — Method | Tooltip | Note (Note = shown in Tab 2's
|
| 45 |
+
# "Why is this method disabled?" panel for that method)
|
| 46 |
+
# "Section Intros" — Tab | Text (Text is the OBJECTIVE paragraph for that
|
| 47 |
+
# tab; Tab is the module name, e.g. "tab2_constraints")
|
| 48 |
+
# To add or edit any of this text, open that file: no code change needed.
|
| 49 |
+
# If the file (or a given row) is missing, that piece of text is silently
|
| 50 |
+
# skipped (no hover text / no note / no intro paragraph shown).
|
| 51 |
try:
|
| 52 |
+
tooltips_xl = pd.read_excel("data/ui_text.xlsx", sheet_name=None)
|
| 53 |
df_res = tooltips_xl.get("Resources", pd.DataFrame(columns=["Resource", "Tooltip"]))
|
| 54 |
+
df_met = tooltips_xl.get("Methods", pd.DataFrame(columns=["Method", "Tooltip", "Note"]))
|
| 55 |
+
df_sec = tooltips_xl.get("Section Intros", pd.DataFrame(columns=["Tab", "Text"]))
|
| 56 |
resource_tooltips: dict[str, str] = {
|
| 57 |
row["Resource"]: row["Tooltip"]
|
| 58 |
for _, row in df_res.iterrows()
|
|
|
|
| 63 |
for _, row in df_met.iterrows()
|
| 64 |
if pd.notna(row.get("Tooltip")) and str(row.get("Tooltip", "")).strip()
|
| 65 |
}
|
| 66 |
+
method_notes: dict[str, str] = {
|
| 67 |
+
row["Method"]: row["Note"]
|
| 68 |
+
for _, row in df_met.iterrows()
|
| 69 |
+
if pd.notna(row.get("Note")) and str(row.get("Note", "")).strip()
|
| 70 |
+
}
|
| 71 |
+
section_intros: dict[str, str] = {
|
| 72 |
+
row["Tab"]: row["Text"]
|
| 73 |
+
for _, row in df_sec.iterrows()
|
| 74 |
+
if pd.notna(row.get("Text")) and str(row.get("Text", "")).strip()
|
| 75 |
+
}
|
| 76 |
except FileNotFoundError:
|
| 77 |
resource_tooltips: dict[str, str] = {}
|
| 78 |
method_tooltips: dict[str, str] = {}
|
| 79 |
+
method_notes: dict[str, str] = {}
|
| 80 |
+
section_intros: dict[str, str] = {}
|
| 81 |
|
| 82 |
# -- Theme colors and fonts for CRRA plots (used in plotly templates) ----------------------------------------------
|
| 83 |
|
data/tooltips.xlsx
DELETED
|
Binary file (11.3 kB)
|
|
|
data/ui_text.xlsx
ADDED
|
Binary file (10.2 kB). View file
|
|
|
tabs/tab1_inputs.py
CHANGED
|
@@ -3,7 +3,7 @@ import streamlit as st
|
|
| 3 |
|
| 4 |
from config.config import (resource_groups, resource_order, resource_tooltips,
|
| 5 |
resource_unit_map, method_groups, CO2_UNIT, secondary_resources,
|
| 6 |
-
hidden_resource_names)
|
| 7 |
from utils.data_utils import (strip_unit_suffix, get_resource_to_group, get_resource_to_unit,
|
| 8 |
get_method_group, standard_resource_names, to_excel_bytes, parse_custom_excel,
|
| 9 |
resource_caps_to_excel_bytes)
|
|
@@ -748,9 +748,7 @@ def render_tab(cost_sliders):
|
|
| 748 |
|
| 749 |
st.markdown('<p class="objective_title">OBJECTIVE</p>', unsafe_allow_html=True)
|
| 750 |
st.markdown(
|
| 751 |
-
'<p class="text_with_border">
|
| 752 |
-
'quantities of each resource</strong> for the optimisation model.<br>'
|
| 753 |
-
'Availability is per <strong>year</strong> (rate) for the specific year the portfolio is being estimated, for example 2030, or 2050</p>',
|
| 754 |
unsafe_allow_html=True,
|
| 755 |
)
|
| 756 |
|
|
|
|
| 3 |
|
| 4 |
from config.config import (resource_groups, resource_order, resource_tooltips,
|
| 5 |
resource_unit_map, method_groups, CO2_UNIT, secondary_resources,
|
| 6 |
+
hidden_resource_names, section_intros)
|
| 7 |
from utils.data_utils import (strip_unit_suffix, get_resource_to_group, get_resource_to_unit,
|
| 8 |
get_method_group, standard_resource_names, to_excel_bytes, parse_custom_excel,
|
| 9 |
resource_caps_to_excel_bytes)
|
|
|
|
| 748 |
|
| 749 |
st.markdown('<p class="objective_title">OBJECTIVE</p>', unsafe_allow_html=True)
|
| 750 |
st.markdown(
|
| 751 |
+
f'<p class="text_with_border">{section_intros.get("tab1_inputs", "")}</p>',
|
|
|
|
|
|
|
| 752 |
unsafe_allow_html=True,
|
| 753 |
)
|
| 754 |
|
tabs/tab2_constraints.py
CHANGED
|
@@ -1,7 +1,7 @@
|
|
| 1 |
import pandas as pd
|
| 2 |
import streamlit as st
|
| 3 |
|
| 4 |
-
from config.config import CO2_UNIT, method_groups, method_order, method_tooltips, secondary_resources, hidden_resource_names
|
| 5 |
from utils.ui_helpers import show_bar
|
| 6 |
from utils.data_utils import strip_unit_suffix, get_other_method_variants, make_variant_name, default_constraint
|
| 7 |
|
|
@@ -172,6 +172,9 @@ def _render_resource_status(m, cost_sliders_norm):
|
|
| 172 |
required_rows.append(row)
|
| 173 |
|
| 174 |
with st.expander("Why is this method disabled?", expanded=False):
|
|
|
|
|
|
|
|
|
|
| 175 |
if required_rows:
|
| 176 |
st.markdown("**Required resources**")
|
| 177 |
for _, r, hint in required_rows:
|
|
@@ -545,16 +548,12 @@ def render_tab(cost_sliders):
|
|
| 545 |
)
|
| 546 |
|
| 547 |
st.markdown(
|
| 548 |
-
"""
|
| 549 |
<div class="text_with_border">
|
| 550 |
-
<p>
|
| 551 |
-
This section lets you <strong>control which CDR methods are included or excluded from the optimisation</strong> according to the specific context of the studied country.<br>
|
| 552 |
-
For example, you can switch off marine CDR methods for those country with no access to the seas. Some countries may also have legal or tacit bans on specific CDR methods.<br><br>
|
| 553 |
-
For the included methods, you can also set a <strong>maximum deployment cap</strong> per method, by <strong>% of its total potential</strong> or by an <strong>absolute value</strong> in {CO2_UNIT}/yr. Use this parameter in situations where the resources are largely available in the country but other parameters will hinder the deployment, for example low maturity CDR methods, slow deployment of CO2 transport infrastructure, documented low adoption rates...
|
| 554 |
-
</p>
|
| 555 |
</div>
|
| 556 |
<br>
|
| 557 |
-
"""
|
| 558 |
unsafe_allow_html=True,
|
| 559 |
)
|
| 560 |
|
|
|
|
| 1 |
import pandas as pd
|
| 2 |
import streamlit as st
|
| 3 |
|
| 4 |
+
from config.config import CO2_UNIT, method_groups, method_order, method_tooltips, method_notes, secondary_resources, hidden_resource_names, section_intros
|
| 5 |
from utils.ui_helpers import show_bar
|
| 6 |
from utils.data_utils import strip_unit_suffix, get_other_method_variants, make_variant_name, default_constraint
|
| 7 |
|
|
|
|
| 172 |
required_rows.append(row)
|
| 173 |
|
| 174 |
with st.expander("Why is this method disabled?", expanded=False):
|
| 175 |
+
note = method_notes.get(m)
|
| 176 |
+
if note:
|
| 177 |
+
st.info(note, icon=":material/lightbulb:")
|
| 178 |
if required_rows:
|
| 179 |
st.markdown("**Required resources**")
|
| 180 |
for _, r, hint in required_rows:
|
|
|
|
| 548 |
)
|
| 549 |
|
| 550 |
st.markdown(
|
| 551 |
+
f"""
|
| 552 |
<div class="text_with_border">
|
| 553 |
+
<p>{section_intros.get("tab2_constraints", "").format(CO2_UNIT=CO2_UNIT)}</p>
|
|
|
|
|
|
|
|
|
|
|
|
|
| 554 |
</div>
|
| 555 |
<br>
|
| 556 |
+
""",
|
| 557 |
unsafe_allow_html=True,
|
| 558 |
)
|
| 559 |
|
tabs/tab3_coefficients.py
CHANGED
|
@@ -3,7 +3,7 @@ import streamlit as st
|
|
| 3 |
|
| 4 |
from config.config import (method_groups, method_order, resource_groups,
|
| 5 |
resource_order, secondary_resources, resource_unit_map, CO2_UNIT,
|
| 6 |
-
hidden_resource_names)
|
| 7 |
from utils.ui_helpers import format_efficiency_summary, show_bar
|
| 8 |
from utils.data_utils import (get_resource_to_group, normalize_costs,
|
| 9 |
get_slider_params, is_method_enabled, is_custom_secondary)
|
|
@@ -155,11 +155,12 @@ def render_standard_resource_row(method, r, conf, display_label, enabled_methods
|
|
| 155 |
# already stops here instead of leaving a live-but-meaningless widget).
|
| 156 |
return
|
| 157 |
|
|
|
|
| 158 |
col1, col2 = st.columns([3, 1])
|
| 159 |
with col1:
|
| 160 |
slider_val = st.slider(
|
| 161 |
label=display_label,
|
| 162 |
-
min_value=
|
| 163 |
key=slider_key,
|
| 164 |
on_change=make_sync_s_to_m(slider_key, manual_key)
|
| 165 |
)
|
|
@@ -168,7 +169,7 @@ def render_standard_resource_row(method, r, conf, display_label, enabled_methods
|
|
| 168 |
label="label", label_visibility="collapsed",
|
| 169 |
key=manual_key, help="Manual override (optional, any value)",
|
| 170 |
format=fmt, value=None, placeholder=str(round(slider_val, 6)),
|
| 171 |
-
on_change=make_sync_m_to_s(slider_key, manual_key,
|
| 172 |
)
|
| 173 |
|
| 174 |
manual_value = st.session_state.get(manual_key)
|
|
@@ -238,11 +239,12 @@ def render_custom_resource_row(method, r, conf, enabled_methods, custom_resource
|
|
| 238 |
# effect on the result.
|
| 239 |
return
|
| 240 |
|
|
|
|
| 241 |
col1, col2 = st.columns([3, 1])
|
| 242 |
with col1:
|
| 243 |
slider_val = st.slider(
|
| 244 |
label=r,
|
| 245 |
-
min_value=
|
| 246 |
key=slider_key,
|
| 247 |
on_change=make_sync_s_to_m(slider_key, manual_key),
|
| 248 |
)
|
|
@@ -251,7 +253,7 @@ def render_custom_resource_row(method, r, conf, enabled_methods, custom_resource
|
|
| 251 |
label="label", label_visibility="collapsed",
|
| 252 |
key=manual_key, help="Manual override (optional, any value)",
|
| 253 |
format=fmt, value=None, placeholder=str(round(slider_val, 6)),
|
| 254 |
-
on_change=make_sync_m_to_s(slider_key, manual_key,
|
| 255 |
)
|
| 256 |
|
| 257 |
st.session_state.costs.setdefault(method, {})
|
|
@@ -426,13 +428,7 @@ def render_tab(cost_sliders):
|
|
| 426 |
|
| 427 |
st.markdown('<p class="objective_title">OBJECTIVE</p>', unsafe_allow_html=True)
|
| 428 |
st.markdown(
|
| 429 |
-
'<div class="text_with_border"><p>'
|
| 430 |
-
"This section lets you adjust the resource usage coefficients for each CDR method "
|
| 431 |
-
"to best fit your country conditions.<br>"
|
| 432 |
-
"<li>Primary resources are always active and initialized with default values.</li>"
|
| 433 |
-
"<li>Secondary resources are initialized at zero and toggled off by default.</li>"
|
| 434 |
-
"<li>Toggling on a secondary resource restores its default value.</li>"
|
| 435 |
-
"</p></div>",
|
| 436 |
unsafe_allow_html=True,
|
| 437 |
)
|
| 438 |
with st.container(key="warning_resources"):
|
|
|
|
| 3 |
|
| 4 |
from config.config import (method_groups, method_order, resource_groups,
|
| 5 |
resource_order, secondary_resources, resource_unit_map, CO2_UNIT,
|
| 6 |
+
hidden_resource_names, section_intros)
|
| 7 |
from utils.ui_helpers import format_efficiency_summary, show_bar
|
| 8 |
from utils.data_utils import (get_resource_to_group, normalize_costs,
|
| 9 |
get_slider_params, is_method_enabled, is_custom_secondary)
|
|
|
|
| 155 |
# already stops here instead of leaving a live-but-meaningless widget).
|
| 156 |
return
|
| 157 |
|
| 158 |
+
slider_min = max(0.0, conf["min"])
|
| 159 |
col1, col2 = st.columns([3, 1])
|
| 160 |
with col1:
|
| 161 |
slider_val = st.slider(
|
| 162 |
label=display_label,
|
| 163 |
+
min_value=slider_min, max_value=conf["max"], step=step, format=fmt,
|
| 164 |
key=slider_key,
|
| 165 |
on_change=make_sync_s_to_m(slider_key, manual_key)
|
| 166 |
)
|
|
|
|
| 169 |
label="label", label_visibility="collapsed",
|
| 170 |
key=manual_key, help="Manual override (optional, any value)",
|
| 171 |
format=fmt, value=None, placeholder=str(round(slider_val, 6)),
|
| 172 |
+
on_change=make_sync_m_to_s(slider_key, manual_key, slider_min, conf["max"]),
|
| 173 |
)
|
| 174 |
|
| 175 |
manual_value = st.session_state.get(manual_key)
|
|
|
|
| 239 |
# effect on the result.
|
| 240 |
return
|
| 241 |
|
| 242 |
+
slider_min = max(0.0, conf["min"])
|
| 243 |
col1, col2 = st.columns([3, 1])
|
| 244 |
with col1:
|
| 245 |
slider_val = st.slider(
|
| 246 |
label=r,
|
| 247 |
+
min_value=slider_min, max_value=conf["max"], step=step, format=fmt,
|
| 248 |
key=slider_key,
|
| 249 |
on_change=make_sync_s_to_m(slider_key, manual_key),
|
| 250 |
)
|
|
|
|
| 253 |
label="label", label_visibility="collapsed",
|
| 254 |
key=manual_key, help="Manual override (optional, any value)",
|
| 255 |
format=fmt, value=None, placeholder=str(round(slider_val, 6)),
|
| 256 |
+
on_change=make_sync_m_to_s(slider_key, manual_key, slider_min, conf["max"]),
|
| 257 |
)
|
| 258 |
|
| 259 |
st.session_state.costs.setdefault(method, {})
|
|
|
|
| 428 |
|
| 429 |
st.markdown('<p class="objective_title">OBJECTIVE</p>', unsafe_allow_html=True)
|
| 430 |
st.markdown(
|
| 431 |
+
f'<div class="text_with_border"><p>{section_intros.get("tab3_coefficients", "")}</p></div>',
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 432 |
unsafe_allow_html=True,
|
| 433 |
)
|
| 434 |
with st.container(key="warning_resources"):
|
tabs/tab5_sensitivity.py
CHANGED
|
@@ -2,7 +2,7 @@ import copy
|
|
| 2 |
import pandas as pd
|
| 3 |
import streamlit as st
|
| 4 |
import plotly.express as px
|
| 5 |
-
from config.config import CO2_UNIT, hidden_resource_names, method_order, resource_order, resource_unit_map
|
| 6 |
from utils.data_utils import (get_resource_to_unit, get_resource_to_group, run_expanded_optimization,
|
| 7 |
is_other_method, get_other_method_variants, make_variant_name,
|
| 8 |
default_constraint)
|
|
@@ -328,10 +328,7 @@ def render_tab():
|
|
| 328 |
|
| 329 |
st.markdown('<p class="objective_title">OBJECTIVE</p>', unsafe_allow_html=True)
|
| 330 |
st.markdown(
|
| 331 |
-
'<div class="text_with_border">'
|
| 332 |
-
"<p>Explore how varying <strong>one or multiple resources' availability</strong> "
|
| 333 |
-
"or <strong>methods' maximum allocation</strong> affects total CO₂ removed.</p>"
|
| 334 |
-
"</div>",
|
| 335 |
unsafe_allow_html=True,
|
| 336 |
)
|
| 337 |
|
|
|
|
| 2 |
import pandas as pd
|
| 3 |
import streamlit as st
|
| 4 |
import plotly.express as px
|
| 5 |
+
from config.config import CO2_UNIT, hidden_resource_names, method_order, resource_order, resource_unit_map, section_intros
|
| 6 |
from utils.data_utils import (get_resource_to_unit, get_resource_to_group, run_expanded_optimization,
|
| 7 |
is_other_method, get_other_method_variants, make_variant_name,
|
| 8 |
default_constraint)
|
|
|
|
| 328 |
|
| 329 |
st.markdown('<p class="objective_title">OBJECTIVE</p>', unsafe_allow_html=True)
|
| 330 |
st.markdown(
|
| 331 |
+
f'<div class="text_with_border"><p>{section_intros.get("tab5_sensitivity", "")}</p></div>',
|
|
|
|
|
|
|
|
|
|
| 332 |
unsafe_allow_html=True,
|
| 333 |
)
|
| 334 |
|