CarlyneB commited on
Commit
7b4d13c
·
1 Parent(s): a78bf83

[FEAT] Replace hardcoded tab intros and method disabled notes with ui_text.xlsx; fix slider negative min

Browse files
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/inputs_aggregated.csv")
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
- # Tooltips are loaded from data/tooltips.xlsx (two sheets: "Resources" and "Methods").
42
- # To add or edit a tooltip, open that file: no code change needed.
43
- # If the file is missing, tooltips are silently disabled (no hover text shown).
 
 
 
 
 
 
 
44
  try:
45
- tooltips_xl = pd.read_excel("data/tooltips.xlsx", sheet_name=None)
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">This section lets you define the <strong>total available '
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
- """.format(CO2_UNIT=CO2_UNIT),
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=conf["min"], max_value=conf["max"], step=step, format=fmt,
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, conf["min"], conf["max"]),
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=conf["min"], max_value=conf["max"], step=step, format=fmt,
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, conf["min"], conf["max"]),
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