diff --git a/app/excel_download.py b/app/excel_download.py new file mode 100644 index 00000000..457c1080 --- /dev/null +++ b/app/excel_download.py @@ -0,0 +1,44 @@ +# -*- coding: utf-8 -*- +"""Utility functions to download a pd.DataFrame as Excel file with streamlit.""" +from io import BytesIO + +import pandas as pd +import streamlit as st + +st.cache_data() + + +def prepare_df_as_excel_stream(df: pd.DataFrame) -> bytes: + """Convert a Dataframe to excel bytes stream.""" + output = BytesIO() + writer = pd.ExcelWriter(output, engine="xlsxwriter") + df.to_excel(writer, index=True, sheet_name="Data") + writer.close() + return output.getvalue() + + +def prepare_and_download_df_as_excel(df: pd.DataFrame, filename: str): + """ + Prepare a bytes stream and add streamlit download button. + + https://stackoverflow.com/a/70120061 + + Parameters + ---------- + df : pd:dataFrame + filename : str + filename will be appended with ".xlsx" + + Returns + ------- + None + """ + excel_stream = prepare_df_as_excel_stream(df) + + st.download_button( + label="Download Table as Excel", + data=excel_stream, + file_name=f"{filename}.xlsx", + help="Click to download this Table as Excel File.", + ) + return None diff --git a/app/layout_elements.py b/app/layout_elements.py index 0ab1b323..959f06c9 100644 --- a/app/layout_elements.py +++ b/app/layout_elements.py @@ -3,6 +3,7 @@ import pandas as pd import streamlit as st +from app.excel_download import prepare_and_download_df_as_excel from app.plot_functions import create_bar_chart_costs from app.ptxboa_functions import change_index_names, config_number_columns @@ -85,6 +86,11 @@ def display_costs( with st.expander("**Data**"): column_config = config_number_columns(df_res, format=f"%.1f {output_unit}") st.dataframe(df_res, use_container_width=True, column_config=column_config) + fn = f"costs_per_{key}_{key_suffix}".strip("_") + if st.session_state["user_changes_df"] is not None: + fn = f"{fn}_{select_data}".lower().replace(" ", "_") + prepare_and_download_df_as_excel(df_res, filename=fn) + return None diff --git a/app/ptxboa_functions.py b/app/ptxboa_functions.py index 606697a4..8a137842 100644 --- a/app/ptxboa_functions.py +++ b/app/ptxboa_functions.py @@ -7,6 +7,7 @@ import pandas as pd import streamlit as st +from app.excel_download import prepare_and_download_df_as_excel from ptxboa.api import PtxboaAPI @@ -649,7 +650,7 @@ def display_and_edit_input_data( st.session_state[f"{key}_number"] = 0 editor_key = f"{key}_{st.session_state[f'{key}_number']}" - with st.form(key=f"{key}_form"): + with st.form(key=f"{key}_form", border=False): st.info( ( "You can edit data directly in the table. When done, click the " @@ -657,14 +658,6 @@ def display_and_edit_input_data( ) ) - df = st.data_editor( - df, - use_container_width=True, - key=editor_key, - num_rows="fixed", - disabled=[index], - column_config=column_config, - ) st.form_submit_button( "Apply Changes", type="primary", @@ -681,12 +674,28 @@ def display_and_edit_input_data( "editor_key": editor_key, }, ) + + df = st.data_editor( + df, + use_container_width=True, + key=editor_key, + num_rows="fixed", + disabled=[index], + column_config=column_config, + ) + else: st.dataframe( df, use_container_width=True, column_config=column_config, ) + + scenario = st.session_state["scenario"].lower() + scenario = scenario.replace(")", "").replace("(", "") + fn = f"{key}_{scenario}".replace(" ", "_") + prepare_and_download_df_as_excel(df, filename=fn) + return df diff --git a/app/tab_deep_dive_countries.py b/app/tab_deep_dive_countries.py index fc8fd6f8..ac858cb3 100644 --- a/app/tab_deep_dive_countries.py +++ b/app/tab_deep_dive_countries.py @@ -87,7 +87,7 @@ def content_deep_dive_countries( api, data_type="full load hours", scope=ddc, - key="input_data_editor_full_load_hours_ddc", + key=f"input_data_full_load_hours_{ddc.replace(' ', '_').lower()}", ) with c_0_1: diff --git a/app/tab_input_data.py b/app/tab_input_data.py index 73d1ef16..d062b399 100644 --- a/app/tab_input_data.py +++ b/app/tab_input_data.py @@ -65,7 +65,7 @@ def content_input_data(api: PtxboaAPI) -> None: api, data_type=data_selection, scope="world", - key=f"input_data_editor_{data_selection}", + key=f"input_data_{data_selection}", ) with c_0_1: st.markdown("**Regional Distribution**") @@ -82,21 +82,21 @@ def content_input_data(api: PtxboaAPI) -> None: api, data_type="electricity_generation", scope=None, - key="input_data_editor_electricity_generation", + key="input_data_electricity_generation", ) with st.expander("**Electrolysis and derivate production**"): display_and_edit_input_data( api, data_type="conversion_processes", scope=None, - key="input_data_editor_conversion_processes", + key="input_data_conversion_processes", ) with st.expander("**Transportation (ships and pipelines)**"): display_and_edit_input_data( api, data_type="transportation_processes", scope=None, - key="input_data_editor_transportation_processes", + key="input_data_transportation_processes", ) with st.expander( "**Transportation (compression, liquefication and reconversion)**" @@ -105,12 +105,12 @@ def content_input_data(api: PtxboaAPI) -> None: api, data_type="reconversion_processes", scope=None, - key="input_data_editor_reconversion_processes", + key="input_data_reconversion_processes", ) with st.expander("**Specific costs for materials and energy carriers**"): display_and_edit_input_data( api, data_type="specific_costs", scope=None, - key="input_data_editor_specific_costs", + key="input_data_specific_costs", ) diff --git a/requirements.txt b/requirements.txt index 3f3aaf08..1f814610 100644 --- a/requirements.txt +++ b/requirements.txt @@ -4,3 +4,4 @@ streamlit>=1.29 openpyxl~=3.1 plotly~=5.16 streamlit-antd-components +XlsxWriter