Spaces:
Sleeping
Sleeping
| 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, | |
| } | |