KliWinBa-WebTool_2.0 / ui /inputs_ui.py
Tobi-ewl's picture
Initial push
0399906 verified
import streamlit as st
import pandas as pd
import io
from logic.calculations import calculate_specific_heat_demand
def render_heating_load_input(ui):
with st.container():
st.markdown(ui["input"]["main_box"], unsafe_allow_html=True)
st.write("")
with st.expander(ui["input"]["heat_load_expander"], expanded=False):
kenne_heizlast = st.checkbox(
ui["input"]["know_heat_load"],
key="kenne_heizlast"
)
heizlast_user = st.number_input(
ui["input"]["heat_load"],
min_value=1.0,
max_value=110.0,
value=10.0,
step=0.5,
help=ui["input"]["heat_load_help"],
disabled=not kenne_heizlast,
key="heizlast_user"
)
def render_basic_inputs(ui):
col1, col2 = st.columns(2)
with col1:
nutzflaeche = st.number_input(
ui["input"]["area"],
min_value=20.0, max_value=10000.0,
value=200.0, step=10.0
)
baujahr = st.number_input(
ui["input"]["building_year"],
min_value=1900, max_value=2025, value=1980,
help=ui["input"]["building_year_help"]
)
with col2:
gesamtbedarf_input = st.text_input(
ui["input"]["total_demand"],
value="",
placeholder=ui["input"]["total_demand_placeholder"],
help=ui["input"]["total_demand_help"]
)
gesamtbedarf = None
if gesamtbedarf_input.strip() != "":
try:
gesamtbedarf = float(gesamtbedarf_input.replace(",", "."))
except ValueError:
st.warning("Bitte einen gültigen Zahlenwert für den Gesamtwärmebedarf eintragen.")
if gesamtbedarf is not None and gesamtbedarf > 0:
spezifisch_disabled = True
spezifisch_hint = ui["input"]["specific_demand_hint_ignored"]
else:
spezifisch_disabled = False
spezifisch_hint = ""
energiebedarf = st.number_input(
ui["input"]["specific_demand"],
min_value=0.0, max_value=500.0,
value=150.0, step=5.0,
disabled=spezifisch_disabled,
help=ui["input"]["specific_demand_help"]
)
if spezifisch_hint:
st.info(spezifisch_hint)
return nutzflaeche, baujahr, gesamtbedarf, energiebedarf
# This function needs calculate_heat_demand from calculations.py
def show_specific_heat_demand_message(total_demand, floor_area, specific_demand, ui):
value = calculate_specific_heat_demand(total_demand, floor_area, specific_demand)
st.write(ui["input"]["used_value"].format(
value=f"{value:.2f}".replace(".", ",")
))
return value
def render_advanced_settings(ui, scen_values):
with st.expander(ui["input"]["advanced_settings"], expanded=False):
zinssatz = float(scen_values['zinssatz'])
beobachtungszeitraum = int(scen_values['beobachtungszeitraum'])
emission_cost_per_t = float(scen_values['emission_cost'])
preisaenderungsfaktor_emission = float(scen_values['preisaenderungsfaktor_emission'])
# --- Two-column layout ---
col1, col2 = st.columns(2)
with col1:
zinssatz_prozent = (zinssatz - 1) * 100
zinssatz_prozent = st.number_input(
ui["input"]["interest_rate"],
value=zinssatz_prozent,
min_value=0.0, max_value=100.0,
step=0.1, format="%.1f"
)
zinssatz = 1 + zinssatz_prozent / 100
beobachtungszeitraum = st.number_input(
ui["input"]["obs_period"],
min_value=5, max_value=40,
value=beobachtungszeitraum,
help=ui["input"]["obs_period_help"]
)
with col2:
wachstumsrate_emission = (preisaenderungsfaktor_emission - 1) * 100
wachstumsrate_emission = st.number_input(
ui["input"]["emission_growth"],
value=wachstumsrate_emission,
min_value=0.0, max_value=100.0,
step=0.1, format="%.1f"
)
preisaenderungsfaktor_emission = 1 + wachstumsrate_emission / 100
emission_cost_per_t = st.number_input(
ui["input"]["emission_cost"],
value=emission_cost_per_t,
min_value=0.0, max_value=1000.0,
step=1.0, format="%.1f"
)
emission_cost = emission_cost_per_t / 1000
return zinssatz, beobachtungszeitraum, preisaenderungsfaktor_emission, emission_cost
def render_heating_system_editor(df, ui):
st.markdown("## 🔧 " + ui["input"]["custom_values_header"])
with st.expander(ui["input"]["operating_costs_expander"], expanded=False):
st.markdown(ui["input"]["operating_costs_info"], unsafe_allow_html=True)
# Init Session-State
if "user_values" not in st.session_state:
st.session_state.user_values = {}
if "show_reset_msg" not in st.session_state:
st.session_state.show_reset_msg = False
if "reset_count" not in st.session_state:
st.session_state.reset_count = 0
if "show_global_reset_msg" not in st.session_state:
st.session_state.show_global_reset_msg = False
enable_custom = st.checkbox(
ui["input"]["custom_values_checkbox"],
value=st.session_state.get("enable_custom_values", False),
key="enable_custom_values"
)
if not enable_custom:
return False, None
tech_names = df["Name"].tolist()
selected = st.selectbox("Technologie auswählen", tech_names)
idx = df[df["Name"] == selected].index[0]
row = df.loc[idx]
if "save_triggered" not in st.session_state:
st.session_state.save_triggered = False
form_key_suffix = f"_{selected}_{st.session_state.reset_count}"
# nur wenn alle Keys da sind → aktuelle UI-Werte verwenden
expected_keys = [f"inv{form_key_suffix}"]
if st.session_state.save_triggered and all(k in st.session_state for k in expected_keys):
values = {
"Investitionskosten": st.session_state[f"inv{form_key_suffix}"],
"Förderung": st.session_state[f"gr{form_key_suffix}"],
"Betriebsdauer": st.session_state[f"life{form_key_suffix}"],
"Effizienz": st.session_state[f"eff{form_key_suffix}"],
"Preisänderungsfaktor_Inv": st.session_state[f"cf{form_key_suffix}"],
"Betriebskosten": st.session_state[f"op{form_key_suffix}"],
"Preisänderungsfaktor_Bedarf": st.session_state[f"of{form_key_suffix}"],
"Fixkosten_O+M": st.session_state[f"fo{form_key_suffix}"],
"Emissionsänderungsfaktor": st.session_state[f"ef{form_key_suffix}"],
"Emissionen": st.session_state[f"em{form_key_suffix}"],
}
else:
values = st.session_state.user_values.get(idx, row)
# Key-Suffix eindeutig machen mit reset_count
form_key_suffix = f"_{selected}_{st.session_state.reset_count}"
with st.form("edit_selected_heating_system"):
st.markdown(f"### {row['Name']} ({row['Leistung']} kW)")
col1, col2 = st.columns(2)
with col1:
invest = st.number_input(ui["input"]["capex"], 0.0,
value=float(values["Investitionskosten"]),
step=100.0, format="%f", key=f"inv{form_key_suffix}")
grant = st.number_input(ui["input"]["grant"], 0.0,
value=float(values["Förderung"]),
step=1.0, format="%f", key=f"gr{form_key_suffix}",
help=ui["input"]["grant_help"])
lifetime = st.number_input(ui["input"]["lifetime"], 1,
value=int(values["Betriebsdauer"]),
key=f"life{form_key_suffix}")
eff = st.number_input(ui["input"]["efficiency"], 0.01,
value=float(values["Effizienz"]),
step=0.05, key=f"eff{form_key_suffix}")
opex = st.number_input(ui["input"]["opex"],
value=float(values["Betriebskosten"]),
step=0.001, key=f"op{form_key_suffix}", format="%.3f")
with col2:
capex_factor = st.number_input(ui["input"]["capex_growth"],
value=float(values["Preisänderungsfaktor_Inv"]),
step=0.01, key=f"cf{form_key_suffix}")
opex_factor = st.number_input(ui["input"]["opex_growth"],
value=float(values["Preisänderungsfaktor_Bedarf"]),
step=0.01, key=f"of{form_key_suffix}")
fix_om = st.number_input(ui["input"]["fixed_om"],
value=float(values["Fixkosten_O+M"]),
step=0.01, key=f"fo{form_key_suffix}")
emis_factor = st.number_input(ui["input"]["emission_change"],
value=float(values["Emissionsänderungsfaktor"]),
step=0.01, key=f"ef{form_key_suffix}")
emission = st.number_input(ui["input"]["emissions"],
value=float(values["Emissionen"]),
step=0.001, key=f"em{form_key_suffix}", format="%.3f")
col1, col2 = st.columns([1, 1])
with col1:
save_clicked = st.form_submit_button(ui["input"]["save_button"])
with col2:
reset_clicked = st.form_submit_button(ui["input"]["reset_button"])
if st.session_state.get("show_reset_msg"):
st.success(ui["input"]["reset_success"])
st.session_state.show_reset_msg = False
if reset_clicked:
if idx in st.session_state.user_values:
del st.session_state.user_values[idx]
st.session_state.reset_count += 1 # WICHTIG: neuer Key-Suffix wird erzwungen
st.session_state.show_reset_msg = True
st.rerun()
if save_clicked:
st.session_state.user_values[idx] = {
"Investitionskosten": st.session_state[f"inv{form_key_suffix}"],
"Förderung": st.session_state[f"gr{form_key_suffix}"],
"Betriebsdauer": st.session_state[f"life{form_key_suffix}"],
"Effizienz": st.session_state[f"eff{form_key_suffix}"],
"Preisänderungsfaktor_Inv": st.session_state[f"cf{form_key_suffix}"],
"Betriebskosten": st.session_state[f"op{form_key_suffix}"],
"Preisänderungsfaktor_Bedarf": st.session_state[f"of{form_key_suffix}"],
"Fixkosten_O+M": st.session_state[f"fo{form_key_suffix}"],
"Emissionsänderungsfaktor": st.session_state[f"ef{form_key_suffix}"],
"Emissionen": st.session_state[f"em{form_key_suffix}"],
}
st.session_state.save_triggered = True
st.success(ui["input"]["save_success"])
#st.markdown("---")
if st.button(ui["input"]["reset_settings"]):
st.session_state.user_values = {}
st.session_state.reset_count += 1
st.session_state.show_global_reset_msg = True
st.rerun()
if st.session_state.get("show_global_reset_msg"):
st.success(ui["input"]["all_reset_success"])
st.session_state.show_global_reset_msg = False
return enable_custom, st.session_state.user_values
def render_batch_heating_system_editor(df, ui):
st.markdown("### " + ui["batch"]["custom_values_header"])
enable_custom = st.checkbox(
ui["batch"]["custom_values_checkbox"], value=False, key="enable_batch_custom"
)
if enable_custom and not df.empty:
with st.expander(ui["batch"]["operating_costs_expander"], expanded=False):
st.markdown(ui["batch"]["operating_costs_info"], unsafe_allow_html=True)
user_values = {}
with st.form("batch_edit_heizsysteme_form"):
for idx, row in df.iterrows():
with st.expander(f"{row['Name']}", expanded=False): # <--- Kein key-Argument!
def safe_float(x, fallback=0.0):
try:
return float(x)
except Exception:
return fallback
grant = st.number_input(
ui["batch"]["grant"],
min_value=0.0,
value=safe_float(row.get("Förderung", 0.0)),
step=1.0,
key=f"bfoe_{idx}",
format="%f",
help=ui["batch"]["grant_help"]
)
opex = st.number_input(
ui["batch"]["opex"],
value=safe_float(row.get("Betriebskosten", 0.0)),
step=0.001,
key=f"bbk_{idx}",
format="%.3f"
)
opex_growth = st.number_input(
ui["batch"]["opex_growth"],
value=safe_float(row.get('Preisänderungsfaktor_Bedarf', 1.0), 1.0),
step=0.01,
key=f"bprbed_{idx}"
)
emissions = st.number_input(
ui["batch"]["emissions"],
value=safe_float(row.get("Emissionen", 0.0)),
step=0.001,
key=f"bem_{idx}",
format="%.3f"
)
user_values[idx] = {
"Förderung": grant,
"Betriebskosten": opex,
"Preisänderungsfaktor_Bedarf": opex_growth,
"Emissionen": emissions
}
submitted = st.form_submit_button(ui["batch"]["save_button"])
return submitted, user_values
else:
return False, None
def apply_user_values(df, user_values):
df_copy = df.copy()
for idx in df_copy.index:
if idx in user_values:
for key in user_values[idx]:
df_copy.loc[idx, key] = user_values[idx][key]
return df_copy
def render_batch_template_download(ui):
lang = ui.get("lang", "de")
# Dynamische Spaltennamen aus UI-Texten
column_names = ui["batch"]["columns"]
tabelle_vorlage = pd.DataFrame({
column_names[0]: ["1", "2"],
column_names[1]: [500, 200],
column_names[2]: [1970, 1991],
column_names[3]: ["", 31200],
column_names[4]: [120, ""],
column_names[5]: ["", ""]
})
filename = "Input-Vorlage_DE.csv" if lang == "de" else "Input-Template_EN.csv"
outbuf = io.StringIO()
tabelle_vorlage.to_csv(outbuf, sep=";", index=False)
vorlage_bytes = outbuf.getvalue().encode("utf-8")
st.markdown("#### " + ui["batch"]["csv_template_header"])
st.download_button(
label=ui["batch"]["download_label"],
data=vorlage_bytes,
file_name=filename,
mime="text/csv",
help=ui["batch"]["download_help"]
)
def apply_batch_user_values(df, preview_df, batch_user_vals):
preview_names = {preview_df.loc[x, "Name"]: x for x in preview_df.index}
for pidx in batch_user_vals:
sysname = preview_df.loc[pidx, "Name"]
df_idx = df[df["Name"] == sysname].index
if not df_idx.empty:
for key, val in batch_user_vals[pidx].items():
df.at[df_idx[0], key] = val
return df
# --- Minimal constraint checkboxes ---
def render_basic_constraints(title: str = "Platzhalter: Eignungs-Check"):
st.markdown(f"#### {title}")
enough_space = st.checkbox("Platzhalter: Genug Platz vorhanden", value=False)
gas_boiler_ok = st.checkbox("Platzhalter: Gas-Heizung vorhanden und weiter nutzbar", value=False)
return {
"enough_space": enough_space,
"gas_boiler_ok": gas_boiler_ok,
}