|
|
""" |
|
|
Data export module for HVAC Load Calculator. |
|
|
This module provides functionality for exporting calculation results. |
|
|
""" |
|
|
|
|
|
import streamlit as st |
|
|
import pandas as pd |
|
|
import numpy as np |
|
|
from typing import Dict, List, Any, Optional, Tuple |
|
|
import json |
|
|
import os |
|
|
import base64 |
|
|
import io |
|
|
from datetime import datetime |
|
|
import xlsxwriter |
|
|
|
|
|
|
|
|
class DataExport: |
|
|
"""Class for data export functionality.""" |
|
|
|
|
|
@staticmethod |
|
|
def export_to_csv(data: Dict[str, Any], file_path: str = None) -> Optional[str]: |
|
|
""" |
|
|
Export data to CSV format. |
|
|
|
|
|
Args: |
|
|
data: Dictionary with data to export |
|
|
file_path: Optional path to save the CSV file |
|
|
|
|
|
Returns: |
|
|
CSV string if file_path is None, otherwise None |
|
|
""" |
|
|
try: |
|
|
|
|
|
df = pd.DataFrame(data) |
|
|
|
|
|
|
|
|
csv_data = df.to_csv(index=False) |
|
|
|
|
|
|
|
|
if file_path: |
|
|
df.to_csv(file_path, index=False) |
|
|
return None |
|
|
|
|
|
|
|
|
return csv_data |
|
|
|
|
|
except Exception as e: |
|
|
st.error(f"Error exporting to CSV: {e}") |
|
|
return None |
|
|
|
|
|
@staticmethod |
|
|
def export_to_excel(data_dict: Dict[str, pd.DataFrame], file_path: str = None) -> Optional[bytes]: |
|
|
""" |
|
|
Export data to Excel format. |
|
|
|
|
|
Args: |
|
|
data_dict: Dictionary with sheet names and DataFrames |
|
|
file_path: Optional path to save the Excel file |
|
|
|
|
|
Returns: |
|
|
Excel bytes if file_path is None, otherwise None |
|
|
""" |
|
|
try: |
|
|
|
|
|
if file_path: |
|
|
writer = pd.ExcelWriter(file_path, engine='xlsxwriter') |
|
|
else: |
|
|
output = io.BytesIO() |
|
|
writer = pd.ExcelWriter(output, engine='xlsxwriter') |
|
|
|
|
|
|
|
|
for sheet_name, df in data_dict.items(): |
|
|
df.to_excel(writer, sheet_name=sheet_name, index=False) |
|
|
|
|
|
|
|
|
worksheet = writer.sheets[sheet_name] |
|
|
for i, col in enumerate(df.columns): |
|
|
max_width = max( |
|
|
df[col].astype(str).map(len).max(), |
|
|
len(col) |
|
|
) + 2 |
|
|
worksheet.set_column(i, i, max_width) |
|
|
|
|
|
|
|
|
writer.close() |
|
|
|
|
|
|
|
|
if not file_path: |
|
|
output.seek(0) |
|
|
return output.getvalue() |
|
|
|
|
|
return None |
|
|
|
|
|
except Exception as e: |
|
|
st.error(f"Error exporting to Excel: {e}") |
|
|
return None |
|
|
|
|
|
@staticmethod |
|
|
def export_scenario_to_json(scenario: Dict[str, Any], file_path: str = None) -> Optional[str]: |
|
|
""" |
|
|
Export scenario data to JSON format. |
|
|
|
|
|
Args: |
|
|
scenario: Dictionary with scenario data |
|
|
file_path: Optional path to save the JSON file |
|
|
|
|
|
Returns: |
|
|
JSON string if file_path is None, otherwise None |
|
|
""" |
|
|
try: |
|
|
|
|
|
json_data = json.dumps(scenario, indent=4) |
|
|
|
|
|
|
|
|
if file_path: |
|
|
with open(file_path, "w") as f: |
|
|
f.write(json_data) |
|
|
return None |
|
|
|
|
|
|
|
|
return json_data |
|
|
|
|
|
except Exception as e: |
|
|
st.error(f"Error exporting scenario to JSON: {e}") |
|
|
return None |
|
|
|
|
|
@staticmethod |
|
|
def get_download_link(data: Any, filename: str, text: str, mime_type: str = "text/csv") -> str: |
|
|
""" |
|
|
Generate a download link for data. |
|
|
|
|
|
Args: |
|
|
data: Data to download |
|
|
filename: Name of the file to download |
|
|
text: Text to display for the download link |
|
|
mime_type: MIME type of the file |
|
|
|
|
|
Returns: |
|
|
HTML string with download link |
|
|
""" |
|
|
if isinstance(data, str): |
|
|
b64 = base64.b64encode(data.encode()).decode() |
|
|
else: |
|
|
b64 = base64.b64encode(data).decode() |
|
|
|
|
|
href = f'<a href="data:{mime_type};base64,{b64}" download="{filename}">{text}</a>' |
|
|
return href |
|
|
|
|
|
@staticmethod |
|
|
def create_cooling_load_dataframes(results: Dict[str, Any]) -> Dict[str, pd.DataFrame]: |
|
|
""" |
|
|
Create DataFrames for cooling load results. |
|
|
|
|
|
Args: |
|
|
results: Dictionary with calculation results |
|
|
|
|
|
Returns: |
|
|
Dictionary with DataFrames for Excel export |
|
|
""" |
|
|
dataframes = {} |
|
|
|
|
|
|
|
|
summary_data = { |
|
|
"Metric": [ |
|
|
"Total Cooling Load", |
|
|
"Sensible Cooling Load", |
|
|
"Latent Cooling Load", |
|
|
"Cooling Load per Area" |
|
|
], |
|
|
"Value": [ |
|
|
results["cooling"]["total_load"], |
|
|
results["cooling"]["sensible_load"], |
|
|
results["cooling"]["latent_load"], |
|
|
results["cooling"]["load_per_area"] |
|
|
], |
|
|
"Unit": [ |
|
|
"kW", |
|
|
"kW", |
|
|
"kW", |
|
|
"W/m²" |
|
|
] |
|
|
} |
|
|
|
|
|
dataframes["Cooling Summary"] = pd.DataFrame(summary_data) |
|
|
|
|
|
|
|
|
component_data = { |
|
|
"Component": [ |
|
|
"Walls", |
|
|
"Roof", |
|
|
"Windows", |
|
|
"Doors", |
|
|
"People", |
|
|
"Lighting", |
|
|
"Equipment", |
|
|
"Infiltration", |
|
|
"Ventilation" |
|
|
], |
|
|
"Load (kW)": [ |
|
|
results["cooling"]["component_loads"]["walls"], |
|
|
results["cooling"]["component_loads"]["roof"], |
|
|
results["cooling"]["component_loads"]["windows"], |
|
|
results["cooling"]["component_loads"]["doors"], |
|
|
results["cooling"]["component_loads"]["people"], |
|
|
results["cooling"]["component_loads"]["lighting"], |
|
|
results["cooling"]["component_loads"]["equipment"], |
|
|
results["cooling"]["component_loads"]["infiltration"], |
|
|
results["cooling"]["component_loads"]["ventilation"] |
|
|
], |
|
|
"Percentage (%)": [ |
|
|
results["cooling"]["component_loads"]["walls"] / results["cooling"]["total_load"] * 100, |
|
|
results["cooling"]["component_loads"]["roof"] / results["cooling"]["total_load"] * 100, |
|
|
results["cooling"]["component_loads"]["windows"] / results["cooling"]["total_load"] * 100, |
|
|
results["cooling"]["component_loads"]["doors"] / results["cooling"]["total_load"] * 100, |
|
|
results["cooling"]["component_loads"]["people"] / results["cooling"]["total_load"] * 100, |
|
|
results["cooling"]["component_loads"]["lighting"] / results["cooling"]["total_load"] * 100, |
|
|
results["cooling"]["component_loads"]["equipment"] / results["cooling"]["total_load"] * 100, |
|
|
results["cooling"]["component_loads"]["infiltration"] / results["cooling"]["total_load"] * 100, |
|
|
results["cooling"]["component_loads"]["ventilation"] / results["cooling"]["total_load"] * 100 |
|
|
] |
|
|
} |
|
|
|
|
|
dataframes["Cooling Components"] = pd.DataFrame(component_data) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
wall_data = [] |
|
|
for wall in results["cooling"]["detailed_loads"]["walls"]: |
|
|
wall_data.append({ |
|
|
"Name": wall["name"], |
|
|
"Orientation": wall["orientation"], |
|
|
"Area (m²)": wall["area"], |
|
|
"U-Value (W/m²·K)": wall["u_value"], |
|
|
"CLTD (°C)": wall["cltd"], |
|
|
"Load (kW)": wall["load"] |
|
|
}) |
|
|
|
|
|
if wall_data: |
|
|
dataframes["Cooling Walls"] = pd.DataFrame(wall_data) |
|
|
|
|
|
|
|
|
roof_data = [] |
|
|
for roof in results["cooling"]["detailed_loads"]["roofs"]: |
|
|
roof_data.append({ |
|
|
"Name": roof["name"], |
|
|
"Orientation": roof["orientation"], |
|
|
"Area (m²)": roof["area"], |
|
|
"U-Value (W/m²·K)": roof["u_value"], |
|
|
"CLTD (°C)": roof["cltd"], |
|
|
"Load (kW)": roof["load"] |
|
|
}) |
|
|
|
|
|
if roof_data: |
|
|
dataframes["Cooling Roofs"] = pd.DataFrame(roof_data) |
|
|
|
|
|
|
|
|
window_data = [] |
|
|
for window in results["cooling"]["detailed_loads"]["windows"]: |
|
|
window_data.append({ |
|
|
"Name": window["name"], |
|
|
"Orientation": window["orientation"], |
|
|
"Area (m²)": window["area"], |
|
|
"U-Value (W/m²·K)": window["u_value"], |
|
|
"SHGC": window["shgc"], |
|
|
"SCL (W/m²)": window["scl"], |
|
|
"Load (kW)": window["load"] |
|
|
}) |
|
|
|
|
|
if window_data: |
|
|
dataframes["Cooling Windows"] = pd.DataFrame(window_data) |
|
|
|
|
|
|
|
|
door_data = [] |
|
|
for door in results["cooling"]["detailed_loads"]["doors"]: |
|
|
door_data.append({ |
|
|
"Name": door["name"], |
|
|
"Orientation": door["orientation"], |
|
|
"Area (m²)": door["area"], |
|
|
"U-Value (W/m²·K)": door["u_value"], |
|
|
"CLTD (°C)": door["cltd"], |
|
|
"Load (kW)": door["load"] |
|
|
}) |
|
|
|
|
|
if door_data: |
|
|
dataframes["Cooling Doors"] = pd.DataFrame(door_data) |
|
|
|
|
|
|
|
|
internal_data = [] |
|
|
for internal_load in results["cooling"]["detailed_loads"]["internal"]: |
|
|
internal_data.append({ |
|
|
"Type": internal_load["type"], |
|
|
"Name": internal_load["name"], |
|
|
"Quantity": internal_load["quantity"], |
|
|
"Heat Gain (W)": internal_load["heat_gain"], |
|
|
"CLF": internal_load["clf"], |
|
|
"Load (kW)": internal_load["load"] |
|
|
}) |
|
|
|
|
|
if internal_data: |
|
|
dataframes["Cooling Internal Loads"] = pd.DataFrame(internal_data) |
|
|
|
|
|
|
|
|
air_data = [ |
|
|
{ |
|
|
"Type": "Infiltration", |
|
|
"Air Flow (m³/s)": results["cooling"]["detailed_loads"]["infiltration"]["air_flow"], |
|
|
"Sensible Load (kW)": results["cooling"]["detailed_loads"]["infiltration"]["sensible_load"], |
|
|
"Latent Load (kW)": results["cooling"]["detailed_loads"]["infiltration"]["latent_load"], |
|
|
"Total Load (kW)": results["cooling"]["detailed_loads"]["infiltration"]["total_load"] |
|
|
}, |
|
|
{ |
|
|
"Type": "Ventilation", |
|
|
"Air Flow (m³/s)": results["cooling"]["detailed_loads"]["ventilation"]["air_flow"], |
|
|
"Sensible Load (kW)": results["cooling"]["detailed_loads"]["ventilation"]["sensible_load"], |
|
|
"Latent Load (kW)": results["cooling"]["detailed_loads"]["ventilation"]["latent_load"], |
|
|
"Total Load (kW)": results["cooling"]["detailed_loads"]["ventilation"]["total_load"] |
|
|
} |
|
|
] |
|
|
|
|
|
dataframes["Cooling Air Exchange"] = pd.DataFrame(air_data) |
|
|
|
|
|
return dataframes |
|
|
|
|
|
@staticmethod |
|
|
def create_heating_load_dataframes(results: Dict[str, Any]) -> Dict[str, pd.DataFrame]: |
|
|
""" |
|
|
Create DataFrames for heating load results. |
|
|
|
|
|
Args: |
|
|
results: Dictionary with calculation results |
|
|
|
|
|
Returns: |
|
|
Dictionary with DataFrames for Excel export |
|
|
""" |
|
|
dataframes = {} |
|
|
|
|
|
|
|
|
summary_data = { |
|
|
"Metric": [ |
|
|
"Total Heating Load", |
|
|
"Heating Load per Area", |
|
|
"Design Heat Loss", |
|
|
"Safety Factor" |
|
|
], |
|
|
"Value": [ |
|
|
results["heating"]["total_load"], |
|
|
results["heating"]["load_per_area"], |
|
|
results["heating"]["design_heat_loss"], |
|
|
results["heating"]["safety_factor"] |
|
|
], |
|
|
"Unit": [ |
|
|
"kW", |
|
|
"W/m²", |
|
|
"kW", |
|
|
"%" |
|
|
] |
|
|
} |
|
|
|
|
|
dataframes["Heating Summary"] = pd.DataFrame(summary_data) |
|
|
|
|
|
|
|
|
component_data = { |
|
|
"Component": [ |
|
|
"Walls", |
|
|
"Roof", |
|
|
"Floor", |
|
|
"Windows", |
|
|
"Doors", |
|
|
"Infiltration", |
|
|
"Ventilation" |
|
|
], |
|
|
"Load (kW)": [ |
|
|
results["heating"]["component_loads"]["walls"], |
|
|
results["heating"]["component_loads"]["roof"], |
|
|
results["heating"]["component_loads"]["floor"], |
|
|
results["heating"]["component_loads"]["windows"], |
|
|
results["heating"]["component_loads"]["doors"], |
|
|
results["heating"]["component_loads"]["infiltration"], |
|
|
results["heating"]["component_loads"]["ventilation"] |
|
|
], |
|
|
"Percentage (%)": [ |
|
|
results["heating"]["component_loads"]["walls"] / results["heating"]["total_load"] * 100, |
|
|
results["heating"]["component_loads"]["roof"] / results["heating"]["total_load"] * 100, |
|
|
results["heating"]["component_loads"]["floor"] / results["heating"]["total_load"] * 100, |
|
|
results["heating"]["component_loads"]["windows"] / results["heating"]["total_load"] * 100, |
|
|
results["heating"]["component_loads"]["doors"] / results["heating"]["total_load"] * 100, |
|
|
results["heating"]["component_loads"]["infiltration"] / results["heating"]["total_load"] * 100, |
|
|
results["heating"]["component_loads"]["ventilation"] / results["heating"]["total_load"] * 100 |
|
|
] |
|
|
} |
|
|
|
|
|
dataframes["Heating Components"] = pd.DataFrame(component_data) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
wall_data = [] |
|
|
for wall in results["heating"]["detailed_loads"]["walls"]: |
|
|
wall_data.append({ |
|
|
"Name": wall["name"], |
|
|
"Orientation": wall["orientation"], |
|
|
"Area (m²)": wall["area"], |
|
|
"U-Value (W/m²·K)": wall["u_value"], |
|
|
"Temperature Difference (°C)": wall["delta_t"], |
|
|
"Load (kW)": wall["load"] |
|
|
}) |
|
|
|
|
|
if wall_data: |
|
|
dataframes["Heating Walls"] = pd.DataFrame(wall_data) |
|
|
|
|
|
|
|
|
roof_data = [] |
|
|
for roof in results["heating"]["detailed_loads"]["roofs"]: |
|
|
roof_data.append({ |
|
|
"Name": roof["name"], |
|
|
"Orientation": roof["orientation"], |
|
|
"Area (m²)": roof["area"], |
|
|
"U-Value (W/m²·K)": roof["u_value"], |
|
|
"Temperature Difference (°C)": roof["delta_t"], |
|
|
"Load (kW)": roof["load"] |
|
|
}) |
|
|
|
|
|
if roof_data: |
|
|
dataframes["Heating Roofs"] = pd.DataFrame(roof_data) |
|
|
|
|
|
|
|
|
floor_data = [] |
|
|
for floor in results["heating"]["detailed_loads"]["floors"]: |
|
|
floor_data.append({ |
|
|
"Name": floor["name"], |
|
|
"Area (m²)": floor["area"], |
|
|
"U-Value (W/m²·K)": floor["u_value"], |
|
|
"Temperature Difference (°C)": floor["delta_t"], |
|
|
"Load (kW)": floor["load"] |
|
|
}) |
|
|
|
|
|
if floor_data: |
|
|
dataframes["Heating Floors"] = pd.DataFrame(floor_data) |
|
|
|
|
|
|
|
|
window_data = [] |
|
|
for window in results["heating"]["detailed_loads"]["windows"]: |
|
|
window_data.append({ |
|
|
"Name": window["name"], |
|
|
"Orientation": window["orientation"], |
|
|
"Area (m²)": window["area"], |
|
|
"U-Value (W/m²·K)": window["u_value"], |
|
|
"Temperature Difference (°C)": window["delta_t"], |
|
|
"Load (kW)": window["load"] |
|
|
}) |
|
|
|
|
|
if window_data: |
|
|
dataframes["Heating Windows"] = pd.DataFrame(window_data) |
|
|
|
|
|
|
|
|
door_data = [] |
|
|
for door in results["heating"]["detailed_loads"]["doors"]: |
|
|
door_data.append({ |
|
|
"Name": door["name"], |
|
|
"Orientation": door["orientation"], |
|
|
"Area (m²)": door["area"], |
|
|
"U-Value (W/m²·K)": door["u_value"], |
|
|
"Temperature Difference (°C)": door["delta_t"], |
|
|
"Load (kW)": door["load"] |
|
|
}) |
|
|
|
|
|
if door_data: |
|
|
dataframes["Heating Doors"] = pd.DataFrame(door_data) |
|
|
|
|
|
|
|
|
air_data = [ |
|
|
{ |
|
|
"Type": "Infiltration", |
|
|
"Air Flow (m³/s)": results["heating"]["detailed_loads"]["infiltration"]["air_flow"], |
|
|
"Temperature Difference (°C)": results["heating"]["detailed_loads"]["infiltration"]["delta_t"], |
|
|
"Load (kW)": results["heating"]["detailed_loads"]["infiltration"]["load"] |
|
|
}, |
|
|
{ |
|
|
"Type": "Ventilation", |
|
|
"Air Flow (m³/s)": results["heating"]["detailed_loads"]["ventilation"]["air_flow"], |
|
|
"Temperature Difference (°C)": results["heating"]["detailed_loads"]["ventilation"]["delta_t"], |
|
|
"Load (kW)": results["heating"]["detailed_loads"]["ventilation"]["load"] |
|
|
} |
|
|
] |
|
|
|
|
|
dataframes["Heating Air Exchange"] = pd.DataFrame(air_data) |
|
|
|
|
|
return dataframes |
|
|
|
|
|
@staticmethod |
|
|
def display_export_interface(session_state: Dict[str, Any]) -> None: |
|
|
""" |
|
|
Display export interface in Streamlit. |
|
|
|
|
|
Args: |
|
|
session_state: Streamlit session state containing calculation results |
|
|
""" |
|
|
st.header("Export Results") |
|
|
|
|
|
|
|
|
if "calculation_results" not in session_state or not session_state["calculation_results"]: |
|
|
st.warning("No calculation results available. Please run calculations first.") |
|
|
return |
|
|
|
|
|
|
|
|
tab1, tab2, tab3 = st.tabs(["CSV Export", "Excel Export", "Scenario Export"]) |
|
|
|
|
|
with tab1: |
|
|
DataExport._display_csv_export(session_state) |
|
|
|
|
|
with tab2: |
|
|
DataExport._display_excel_export(session_state) |
|
|
|
|
|
with tab3: |
|
|
DataExport._display_scenario_export(session_state) |
|
|
|
|
|
@staticmethod |
|
|
def _display_csv_export(session_state: Dict[str, Any]) -> None: |
|
|
""" |
|
|
Display CSV export interface. |
|
|
|
|
|
Args: |
|
|
session_state: Streamlit session state containing calculation results |
|
|
""" |
|
|
st.subheader("CSV Export") |
|
|
|
|
|
|
|
|
results = session_state["calculation_results"] |
|
|
|
|
|
|
|
|
tab1, tab2 = st.tabs(["Cooling Load CSV", "Heating Load CSV"]) |
|
|
|
|
|
with tab1: |
|
|
|
|
|
cooling_dfs = DataExport.create_cooling_load_dataframes(results) |
|
|
|
|
|
|
|
|
for sheet_name, df in cooling_dfs.items(): |
|
|
st.write(f"### {sheet_name}") |
|
|
st.dataframe(df) |
|
|
|
|
|
|
|
|
csv_data = DataExport.export_to_csv(df) |
|
|
if csv_data: |
|
|
filename = f"{sheet_name.replace(' ', '_').lower()}_{datetime.now().strftime('%Y%m%d_%H%M%S')}.csv" |
|
|
download_link = DataExport.get_download_link(csv_data, filename, f"Download {sheet_name} CSV") |
|
|
st.markdown(download_link, unsafe_allow_html=True) |
|
|
|
|
|
with tab2: |
|
|
|
|
|
heating_dfs = DataExport.create_heating_load_dataframes(results) |
|
|
|
|
|
|
|
|
for sheet_name, df in heating_dfs.items(): |
|
|
st.write(f"### {sheet_name}") |
|
|
st.dataframe(df) |
|
|
|
|
|
|
|
|
csv_data = DataExport.export_to_csv(df) |
|
|
if csv_data: |
|
|
filename = f"{sheet_name.replace(' ', '_').lower()}_{datetime.now().strftime('%Y%m%d_%H%M%S')}.csv" |
|
|
download_link = DataExport.get_download_link(csv_data, filename, f"Download {sheet_name} CSV") |
|
|
st.markdown(download_link, unsafe_allow_html=True) |
|
|
|
|
|
@staticmethod |
|
|
def _display_excel_export(session_state: Dict[str, Any]) -> None: |
|
|
""" |
|
|
Display Excel export interface. |
|
|
|
|
|
Args: |
|
|
session_state: Streamlit session state containing calculation results |
|
|
""" |
|
|
st.subheader("Excel Export") |
|
|
|
|
|
|
|
|
results = session_state["calculation_results"] |
|
|
|
|
|
|
|
|
tab1, tab2, tab3 = st.tabs(["Cooling Load Excel", "Heating Load Excel", "Combined Excel"]) |
|
|
|
|
|
with tab1: |
|
|
|
|
|
cooling_dfs = DataExport.create_cooling_load_dataframes(results) |
|
|
|
|
|
|
|
|
excel_data = DataExport.export_to_excel(cooling_dfs) |
|
|
if excel_data: |
|
|
filename = f"cooling_load_results_{datetime.now().strftime('%Y%m%d_%H%M%S')}.xlsx" |
|
|
download_link = DataExport.get_download_link( |
|
|
excel_data, |
|
|
filename, |
|
|
"Download Cooling Load Excel", |
|
|
"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet" |
|
|
) |
|
|
st.markdown(download_link, unsafe_allow_html=True) |
|
|
|
|
|
|
|
|
st.write("### Excel Preview") |
|
|
st.write("The Excel file will contain the following sheets:") |
|
|
for sheet_name in cooling_dfs.keys(): |
|
|
st.write(f"- {sheet_name}") |
|
|
|
|
|
with tab2: |
|
|
|
|
|
heating_dfs = DataExport.create_heating_load_dataframes(results) |
|
|
|
|
|
|
|
|
excel_data = DataExport.export_to_excel(heating_dfs) |
|
|
if excel_data: |
|
|
filename = f"heating_load_results_{datetime.now().strftime('%Y%m%d_%H%M%S')}.xlsx" |
|
|
download_link = DataExport.get_download_link( |
|
|
excel_data, |
|
|
filename, |
|
|
"Download Heating Load Excel", |
|
|
"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet" |
|
|
) |
|
|
st.markdown(download_link, unsafe_allow_html=True) |
|
|
|
|
|
|
|
|
st.write("### Excel Preview") |
|
|
st.write("The Excel file will contain the following sheets:") |
|
|
for sheet_name in heating_dfs.keys(): |
|
|
st.write(f"- {sheet_name}") |
|
|
|
|
|
with tab3: |
|
|
|
|
|
combined_dfs = {} |
|
|
|
|
|
|
|
|
if "building_info" in session_state: |
|
|
project_info = [ |
|
|
{"Parameter": "Project Name", "Value": session_state["building_info"].get("project_name", "")}, |
|
|
{"Parameter": "Building Name", "Value": session_state["building_info"].get("building_name", "")}, |
|
|
{"Parameter": "Location", "Value": session_state["building_info"].get("location", "")}, |
|
|
{"Parameter": "Climate Zone", "Value": session_state["building_info"].get("climate_zone", "")}, |
|
|
{"Parameter": "Building Type", "Value": session_state["building_info"].get("building_type", "")}, |
|
|
{"Parameter": "Floor Area", "Value": session_state["building_info"].get("floor_area", "")}, |
|
|
{"Parameter": "Number of Floors", "Value": session_state["building_info"].get("num_floors", "")}, |
|
|
{"Parameter": "Floor Height", "Value": session_state["building_info"].get("floor_height", "")}, |
|
|
{"Parameter": "Orientation", "Value": session_state["building_info"].get("orientation", "")}, |
|
|
{"Parameter": "Occupancy", "Value": session_state["building_info"].get("occupancy", "")}, |
|
|
{"Parameter": "Operating Hours", "Value": session_state["building_info"].get("operating_hours", "")}, |
|
|
{"Parameter": "Date", "Value": datetime.now().strftime("%Y-%m-%d")}, |
|
|
{"Parameter": "Time", "Value": datetime.now().strftime("%H:%M:%S")} |
|
|
] |
|
|
|
|
|
combined_dfs["Project Information"] = pd.DataFrame(project_info) |
|
|
|
|
|
|
|
|
cooling_dfs = DataExport.create_cooling_load_dataframes(results) |
|
|
for sheet_name, df in cooling_dfs.items(): |
|
|
combined_dfs[sheet_name] = df |
|
|
|
|
|
|
|
|
heating_dfs = DataExport.create_heating_load_dataframes(results) |
|
|
for sheet_name, df in heating_dfs.items(): |
|
|
combined_dfs[sheet_name] = df |
|
|
|
|
|
|
|
|
excel_data = DataExport.export_to_excel(combined_dfs) |
|
|
if excel_data: |
|
|
filename = f"hvac_load_results_{datetime.now().strftime('%Y%m%d_%H%M%S')}.xlsx" |
|
|
download_link = DataExport.get_download_link( |
|
|
excel_data, |
|
|
filename, |
|
|
"Download Combined Excel Report", |
|
|
"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet" |
|
|
) |
|
|
st.markdown(download_link, unsafe_allow_html=True) |
|
|
|
|
|
|
|
|
st.write("### Excel Preview") |
|
|
st.write("The Excel file will contain the following sheets:") |
|
|
for sheet_name in combined_dfs.keys(): |
|
|
st.write(f"- {sheet_name}") |
|
|
|
|
|
@staticmethod |
|
|
def _display_scenario_export(session_state: Dict[str, Any]) -> None: |
|
|
""" |
|
|
Display scenario export interface. |
|
|
|
|
|
Args: |
|
|
session_state: Streamlit session state containing calculation results |
|
|
""" |
|
|
st.subheader("Scenario Export") |
|
|
|
|
|
|
|
|
if "saved_scenarios" not in session_state or not session_state["saved_scenarios"]: |
|
|
st.info("No saved scenarios available for export. Save the current results as a scenario to enable export.") |
|
|
|
|
|
|
|
|
scenario_name = st.text_input("Scenario Name", value="Baseline") |
|
|
|
|
|
if st.button("Save Current Results as Scenario"): |
|
|
if "saved_scenarios" not in session_state: |
|
|
session_state["saved_scenarios"] = {} |
|
|
|
|
|
|
|
|
session_state["saved_scenarios"][scenario_name] = { |
|
|
"results": session_state["calculation_results"], |
|
|
"building_info": session_state["building_info"], |
|
|
"components": session_state["components"], |
|
|
"timestamp": datetime.now().strftime("%Y-%m-%d %H:%M:%S") |
|
|
} |
|
|
|
|
|
st.success(f"Scenario '{scenario_name}' saved successfully!") |
|
|
st.experimental_rerun() |
|
|
else: |
|
|
|
|
|
st.write("### Saved Scenarios") |
|
|
|
|
|
|
|
|
scenario_names = list(session_state["saved_scenarios"].keys()) |
|
|
selected_scenario = st.selectbox("Select Scenario to Export", scenario_names) |
|
|
|
|
|
if selected_scenario: |
|
|
|
|
|
scenario = session_state["saved_scenarios"][selected_scenario] |
|
|
|
|
|
|
|
|
st.write(f"**Scenario:** {selected_scenario}") |
|
|
st.write(f"**Timestamp:** {scenario['timestamp']}") |
|
|
|
|
|
|
|
|
json_data = DataExport.export_scenario_to_json(scenario) |
|
|
if json_data: |
|
|
filename = f"{selected_scenario.replace(' ', '_').lower()}_{datetime.now().strftime('%Y%m%d_%H%M%S')}.json" |
|
|
download_link = DataExport.get_download_link( |
|
|
json_data, |
|
|
filename, |
|
|
"Download Scenario JSON", |
|
|
"application/json" |
|
|
) |
|
|
st.markdown(download_link, unsafe_allow_html=True) |
|
|
|
|
|
|
|
|
if st.button("Export All Scenarios"): |
|
|
|
|
|
import zipfile |
|
|
from io import BytesIO |
|
|
|
|
|
zip_buffer = BytesIO() |
|
|
with zipfile.ZipFile(zip_buffer, "w", zipfile.ZIP_DEFLATED) as zip_file: |
|
|
for scenario_name, scenario in session_state["saved_scenarios"].items(): |
|
|
|
|
|
json_data = DataExport.export_scenario_to_json(scenario) |
|
|
if json_data: |
|
|
filename = f"{scenario_name.replace(' ', '_').lower()}.json" |
|
|
zip_file.writestr(filename, json_data) |
|
|
|
|
|
|
|
|
zip_buffer.seek(0) |
|
|
zip_data = zip_buffer.getvalue() |
|
|
|
|
|
filename = f"all_scenarios_{datetime.now().strftime('%Y%m%d_%H%M%S')}.zip" |
|
|
download_link = DataExport.get_download_link( |
|
|
zip_data, |
|
|
filename, |
|
|
"Download All Scenarios (ZIP)", |
|
|
"application/zip" |
|
|
) |
|
|
st.markdown(download_link, unsafe_allow_html=True) |
|
|
|
|
|
|
|
|
|
|
|
data_export = DataExport() |
|
|
|
|
|
|
|
|
if __name__ == "__main__": |
|
|
import streamlit as st |
|
|
|
|
|
|
|
|
if "calculation_results" not in st.session_state: |
|
|
st.session_state["calculation_results"] = { |
|
|
"cooling": { |
|
|
"total_load": 25.5, |
|
|
"sensible_load": 20.0, |
|
|
"latent_load": 5.5, |
|
|
"load_per_area": 85.0, |
|
|
"component_loads": { |
|
|
"walls": 5.0, |
|
|
"roof": 3.0, |
|
|
"windows": 8.0, |
|
|
"doors": 1.0, |
|
|
"people": 2.5, |
|
|
"lighting": 2.0, |
|
|
"equipment": 1.5, |
|
|
"infiltration": 1.0, |
|
|
"ventilation": 1.5 |
|
|
}, |
|
|
"detailed_loads": { |
|
|
"walls": [ |
|
|
{"name": "North Wall", "orientation": "NORTH", "area": 20.0, "u_value": 0.5, "cltd": 10.0, "load": 1.0} |
|
|
], |
|
|
"roofs": [ |
|
|
{"name": "Main Roof", "orientation": "HORIZONTAL", "area": 100.0, "u_value": 0.3, "cltd": 15.0, "load": 3.0} |
|
|
], |
|
|
"windows": [ |
|
|
{"name": "South Window", "orientation": "SOUTH", "area": 10.0, "u_value": 2.8, "shgc": 0.7, "scl": 800.0, "load": 8.0} |
|
|
], |
|
|
"doors": [ |
|
|
{"name": "Main Door", "orientation": "NORTH", "area": 2.0, "u_value": 2.0, "cltd": 10.0, "load": 1.0} |
|
|
], |
|
|
"internal": [ |
|
|
{"type": "People", "name": "Occupants", "quantity": 10, "heat_gain": 250, "clf": 1.0, "load": 2.5}, |
|
|
{"type": "Lighting", "name": "General Lighting", "quantity": 1000, "heat_gain": 2000, "clf": 1.0, "load": 2.0}, |
|
|
{"type": "Equipment", "name": "Office Equipment", "quantity": 5, "heat_gain": 300, "clf": 1.0, "load": 1.5} |
|
|
], |
|
|
"infiltration": { |
|
|
"air_flow": 0.05, |
|
|
"sensible_load": 0.8, |
|
|
"latent_load": 0.2, |
|
|
"total_load": 1.0 |
|
|
}, |
|
|
"ventilation": { |
|
|
"air_flow": 0.1, |
|
|
"sensible_load": 1.0, |
|
|
"latent_load": 0.5, |
|
|
"total_load": 1.5 |
|
|
} |
|
|
} |
|
|
}, |
|
|
"heating": { |
|
|
"total_load": 30.0, |
|
|
"load_per_area": 100.0, |
|
|
"design_heat_loss": 27.0, |
|
|
"safety_factor": 10.0, |
|
|
"component_loads": { |
|
|
"walls": 8.0, |
|
|
"roof": 5.0, |
|
|
"floor": 4.0, |
|
|
"windows": 7.0, |
|
|
"doors": 1.0, |
|
|
"infiltration": 2.0, |
|
|
"ventilation": 3.0 |
|
|
}, |
|
|
"detailed_loads": { |
|
|
"walls": [ |
|
|
{"name": "North Wall", "orientation": "NORTH", "area": 20.0, "u_value": 0.5, "delta_t": 25.0, "load": 8.0} |
|
|
], |
|
|
"roofs": [ |
|
|
{"name": "Main Roof", "orientation": "HORIZONTAL", "area": 100.0, "u_value": 0.3, "delta_t": 25.0, "load": 5.0} |
|
|
], |
|
|
"floors": [ |
|
|
{"name": "Ground Floor", "area": 100.0, "u_value": 0.4, "delta_t": 10.0, "load": 4.0} |
|
|
], |
|
|
"windows": [ |
|
|
{"name": "South Window", "orientation": "SOUTH", "area": 10.0, "u_value": 2.8, "delta_t": 25.0, "load": 7.0} |
|
|
], |
|
|
"doors": [ |
|
|
{"name": "Main Door", "orientation": "NORTH", "area": 2.0, "u_value": 2.0, "delta_t": 25.0, "load": 1.0} |
|
|
], |
|
|
"infiltration": { |
|
|
"air_flow": 0.05, |
|
|
"delta_t": 25.0, |
|
|
"load": 2.0 |
|
|
}, |
|
|
"ventilation": { |
|
|
"air_flow": 0.1, |
|
|
"delta_t": 25.0, |
|
|
"load": 3.0 |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
data_export.display_export_interface(st.session_state) |
|
|
|