Spaces:
Sleeping
Sleeping
Update app/materials_library.py
Browse files- app/materials_library.py +74 -71
app/materials_library.py
CHANGED
@@ -2,8 +2,8 @@
|
|
2 |
BuildSustain - Material Library Module
|
3 |
|
4 |
This module handles the material library functionality of the BuildSustain application,
|
5 |
-
allowing users to manage building materials and fenestrations (windows,
|
6 |
-
It provides both predefined library materials and the ability to create
|
7 |
|
8 |
Developed by: Dr Majed Abuseif, Deakin University
|
9 |
漏 2025
|
@@ -17,8 +17,6 @@ import logging
|
|
17 |
import uuid
|
18 |
from typing import Dict, List, Any, Optional, Tuple, Union
|
19 |
from enum import Enum
|
20 |
-
|
21 |
-
# Import default data and constants from centralized module
|
22 |
from app.m_c_data import SAMPLE_MATERIALS, SAMPLE_FENESTRATIONS, DEFAULT_MATERIAL_PROPERTIES, DEFAULT_WINDOW_PROPERTIES
|
23 |
|
24 |
# Configure logging
|
@@ -27,27 +25,22 @@ logger = logging.getLogger(__name__)
|
|
27 |
|
28 |
# Define constants
|
29 |
MATERIAL_CATEGORIES = [
|
30 |
-
"Masonry",
|
31 |
"Insulation",
|
32 |
-
"
|
33 |
-
"Wood",
|
34 |
"Finishing",
|
35 |
-
"
|
36 |
]
|
37 |
|
38 |
FENESTRATION_TYPES = [
|
39 |
-
"Window",
|
40 |
-
"
|
41 |
-
"Custom"
|
42 |
]
|
43 |
|
44 |
class MaterialCategory(Enum):
|
45 |
-
MASONRY = "Masonry"
|
46 |
INSULATION = "Insulation"
|
47 |
-
|
48 |
-
WOOD = "Wood"
|
49 |
FINISHING = "Finishing"
|
50 |
-
|
51 |
|
52 |
class Material:
|
53 |
def __init__(self, name: str, category: MaterialCategory, conductivity: float, density: float,
|
@@ -65,12 +58,19 @@ class Material:
|
|
65 |
self.emissivity = emissivity
|
66 |
self.is_library = is_library
|
67 |
|
68 |
-
def get_thermal_mass(self):
|
69 |
-
|
70 |
-
|
71 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
72 |
|
73 |
-
def get_u_value(self):
|
|
|
74 |
return self.conductivity / self.default_thickness if self.default_thickness > 0 else 0.0
|
75 |
|
76 |
class GlazingMaterial:
|
@@ -100,7 +100,8 @@ class MaterialLibrary:
|
|
100 |
"Embodied Carbon (kgCO鈧俥/kg)": m.embodied_carbon,
|
101 |
"Absorptivity": m.absorptivity,
|
102 |
"Price (USD/m虏)": m.price,
|
103 |
-
"Emissivity": m.emissivity
|
|
|
104 |
}
|
105 |
for m in project_materials.values()
|
106 |
]
|
@@ -226,7 +227,7 @@ def display_materials_page():
|
|
226 |
try:
|
227 |
material_library.library_materials[k] = Material(
|
228 |
name=k,
|
229 |
-
category=MaterialCategory(m["category"]),
|
230 |
conductivity=m["thermal_properties"]["conductivity"],
|
231 |
density=m["thermal_properties"]["density"],
|
232 |
specific_heat=m["thermal_properties"]["specific_heat"],
|
@@ -243,18 +244,17 @@ def display_materials_page():
|
|
243 |
|
244 |
material_library.library_glazing_materials = {}
|
245 |
for k, f in st.session_state.project_data["fenestrations"]["library"].items():
|
246 |
-
|
247 |
-
|
248 |
-
|
249 |
-
|
250 |
-
|
251 |
-
|
252 |
-
|
253 |
-
|
254 |
-
|
255 |
-
|
256 |
-
|
257 |
-
continue
|
258 |
|
259 |
with tab1:
|
260 |
display_materials_tab(material_library)
|
@@ -308,11 +308,11 @@ def display_materials_tab(material_library: MaterialLibrary):
|
|
308 |
cols[1].write("**Thermal Mass**")
|
309 |
cols[2].write("**U-Value (W/m虏路K)**")
|
310 |
cols[3].write("**Preview**")
|
311 |
-
cols[4].write("**
|
312 |
for material in library_materials:
|
313 |
cols = st.columns([2, 1, 1, 1, 1])
|
314 |
cols[0].write(material.name)
|
315 |
-
cols[1].write(
|
316 |
cols[2].write(f"{material.get_u_value():.3f}")
|
317 |
if cols[3].button("Preview", key=f"preview_lib_mat_{material.name}"):
|
318 |
if st.session_state.get("rerun_trigger") != f"preview_mat_{material.name}":
|
@@ -344,7 +344,7 @@ def display_materials_tab(material_library: MaterialLibrary):
|
|
344 |
"emissivity": material.emissivity
|
345 |
}
|
346 |
st.session_state.active_tab = "Materials"
|
347 |
-
if cols[4].button("
|
348 |
new_name = f"{material.name}_Project"
|
349 |
counter = 1
|
350 |
while new_name in st.session_state.project_data["materials"]["project"] or new_name in st.session_state.project_data["materials"]["library"]:
|
@@ -382,9 +382,9 @@ def display_materials_tab(material_library: MaterialLibrary):
|
|
382 |
for material in st.session_state.project_data["materials"]["project"].values():
|
383 |
cols = st.columns([2, 1, 1, 1, 1])
|
384 |
cols[0].write(material.name)
|
385 |
-
cols[1].write(
|
386 |
cols[2].write(f"{material.get_u_value():.3f}")
|
387 |
-
if cols[3].button("Edit", key=f"
|
388 |
if st.session_state.get("rerun_trigger") != f"edit_mat_{material.name}":
|
389 |
st.session_state.rerun_trigger = f"edit_mat_{material.name}"
|
390 |
st.session_state.material_editor = {
|
@@ -415,7 +415,7 @@ def display_materials_tab(material_library: MaterialLibrary):
|
|
415 |
"emissivity": material.emissivity
|
416 |
}
|
417 |
st.session_state.active_tab = "Materials"
|
418 |
-
if cols[4].button("Delete", key=f"
|
419 |
success, message = material_library.delete_project_material(
|
420 |
material.name, st.session_state.project_data["materials"]["project"], st.session_state.get("components", {})
|
421 |
)
|
@@ -459,7 +459,7 @@ def display_materials_tab(material_library: MaterialLibrary):
|
|
459 |
original_name = editor_state.get("original_name", "")
|
460 |
name = st.text_input(
|
461 |
"Material Name",
|
462 |
-
value=form_state.get("name", editor_state.get("name", "")
|
463 |
help="Unique material identifier",
|
464 |
key="material_name_input"
|
465 |
)
|
@@ -559,7 +559,7 @@ def display_materials_tab(material_library: MaterialLibrary):
|
|
559 |
try:
|
560 |
new_material = Material(
|
561 |
name=name,
|
562 |
-
category=MaterialCategory(category),
|
563 |
conductivity=conductivity,
|
564 |
density=density,
|
565 |
specific_heat=specific_heat,
|
@@ -589,18 +589,18 @@ def display_materials_tab(material_library: MaterialLibrary):
|
|
589 |
"embodied_carbon": 0.5,
|
590 |
"absorptivity": DEFAULT_MATERIAL_PROPERTIES["absorptivity"],
|
591 |
"price": 50.0,
|
592 |
-
"emissivity":
|
593 |
-
}
|
594 |
-
st.session_state.material_action = {"action": None, "
|
595 |
st.session_state.rerun_trigger = None
|
596 |
st.session_state.materials_rerun_pending = True
|
597 |
else:
|
598 |
st.error(f"Failed to save material: {message}")
|
599 |
except Exception as e:
|
600 |
-
st.error(
|
601 |
|
602 |
def display_fenestrations_tab(material_library: MaterialLibrary):
|
603 |
-
"""Display the fenestrations tab
|
604 |
col1, col2 = st.columns([3, 2])
|
605 |
with col1:
|
606 |
st.subheader("Fenestrations")
|
@@ -613,41 +613,44 @@ def display_fenestrations_tab(material_library: MaterialLibrary):
|
|
613 |
if st.session_state["fenestration_filter"] == "None":
|
614 |
library_fenestrations = []
|
615 |
elif fenestration_filter != "All":
|
616 |
-
|
617 |
-
|
|
|
|
|
|
|
618 |
cols[0].write("**Name**")
|
619 |
cols[1].write("**SHGC**")
|
620 |
cols[2].write("**U-Value (W/m虏路K)**")
|
621 |
cols[3].write("**Preview**")
|
622 |
-
cols[4].write("**
|
623 |
for fenestration in library_fenestrations:
|
624 |
cols = st.columns([2, 1, 1, 1, 1])
|
625 |
cols[0].write(fenestration.name)
|
626 |
-
cols[1].write(f"{
|
627 |
-
cols[2].write(f"{
|
628 |
-
if cols[3].button("Preview", key=f"
|
629 |
-
if st.session_state.get("rerun_trigger") != f"preview_fen_{fenestration.name}":
|
630 |
st.session_state.rerun_trigger = f"preview_fen_{fenestration.name}"
|
631 |
st.session_state.fenestration_editor = {
|
632 |
-
"name":
|
633 |
-
"shgc":
|
634 |
"u_value": fenestration.u_value,
|
635 |
-
"
|
636 |
"is_edit": False,
|
637 |
-
"
|
638 |
-
}
|
639 |
st.session_state.fenestration_form_state = {
|
640 |
"name": fenestration.name,
|
641 |
-
"shgc":
|
642 |
"u_value": fenestration.u_value,
|
643 |
-
"
|
644 |
-
}
|
645 |
st.session_state.active_tab = "Fenestrations"
|
646 |
-
if cols[4].button("
|
647 |
-
new_name = f"{
|
648 |
counter = 1
|
649 |
while new_name in st.session_state.project_data["fenestrations"]["project"] or new_name in st.session_state.project_data["fenestrations"]["library"]:
|
650 |
-
new_name = f"{
|
651 |
counter += 1
|
652 |
new_fenestration = GlazingMaterial(
|
653 |
name=new_name,
|
@@ -657,7 +660,7 @@ def display_fenestrations_tab(material_library: MaterialLibrary):
|
|
657 |
is_library=False
|
658 |
)
|
659 |
st.session_state.project_data["fenestrations"]["project"][new_name] = new_fenestration
|
660 |
-
st.success(f"Fenestration '{new_name}'
|
661 |
st.session_state.materials_rerun_pending = True
|
662 |
|
663 |
st.subheader("Project Fenestrations")
|
@@ -674,7 +677,7 @@ def display_fenestrations_tab(material_library: MaterialLibrary):
|
|
674 |
cols[0].write(fenestration.name)
|
675 |
cols[1].write(f"{fenestration.shgc:.2f}")
|
676 |
cols[2].write(f"{fenestration.u_value:.3f}")
|
677 |
-
if cols[3].button("Edit", key=f"
|
678 |
if st.session_state.get("rerun_trigger") != f"edit_fen_{fenestration.name}":
|
679 |
st.session_state.rerun_trigger = f"edit_fen_{fenestration.name}"
|
680 |
st.session_state.fenestration_editor = {
|
@@ -693,13 +696,13 @@ def display_fenestrations_tab(material_library: MaterialLibrary):
|
|
693 |
"h_o": fenestration.h_o
|
694 |
}
|
695 |
st.session_state.active_tab = "Fenestrations"
|
696 |
-
if cols[4].button("Delete", key=f"
|
697 |
if any(comp.get("fenestration_material") and comp["fenestration_material"].name == fenestration.name
|
698 |
for comp_list in st.session_state.get("components", {}).values() for comp in comp_list):
|
699 |
st.error(f"Fenestration '{fenestration.name}' is used in components and cannot be deleted.")
|
700 |
else:
|
701 |
del st.session_state.project_data["fenestrations"]["project"][fenestration.name]
|
702 |
-
st.success(f"Fenestration '{fenestration.name}' deleted!")
|
703 |
st.session_state.materials_rerun_pending = True
|
704 |
else:
|
705 |
st.write("No project fenestrations added.")
|
@@ -810,10 +813,10 @@ def display_fenestrations_tab(material_library: MaterialLibrary):
|
|
810 |
)
|
811 |
if is_edit and editor_state.get("edit_source") == "project" and name == original_name:
|
812 |
st.session_state.project_data["fenestrations"]["project"][original_name] = new_fenestration
|
813 |
-
st.success(f"Fenestration '{name}' updated!")
|
814 |
else:
|
815 |
st.session_state.project_data["fenestrations"]["project"][name] = new_fenestration
|
816 |
-
st.success(f"Fenestration '{name}' added!")
|
817 |
st.session_state.fenestration_editor = {}
|
818 |
st.session_state.fenestration_form_state = {
|
819 |
"name": "",
|
|
|
2 |
BuildSustain - Material Library Module
|
3 |
|
4 |
This module handles the material library functionality of the BuildSustain application,
|
5 |
+
allowing users to manage building materials and fenestrations (windows/skylights, doors).
|
6 |
+
It provides both predefined library materials and the ability to create project-specific materials.
|
7 |
|
8 |
Developed by: Dr Majed Abuseif, Deakin University
|
9 |
漏 2025
|
|
|
17 |
import uuid
|
18 |
from typing import Dict, List, Any, Optional, Tuple, Union
|
19 |
from enum import Enum
|
|
|
|
|
20 |
from app.m_c_data import SAMPLE_MATERIALS, SAMPLE_FENESTRATIONS, DEFAULT_MATERIAL_PROPERTIES, DEFAULT_WINDOW_PROPERTIES
|
21 |
|
22 |
# Configure logging
|
|
|
25 |
|
26 |
# Define constants
|
27 |
MATERIAL_CATEGORIES = [
|
|
|
28 |
"Insulation",
|
29 |
+
"Structural",
|
|
|
30 |
"Finishing",
|
31 |
+
"Sub-Structural"
|
32 |
]
|
33 |
|
34 |
FENESTRATION_TYPES = [
|
35 |
+
"Window/Skylight",
|
36 |
+
"Door"
|
|
|
37 |
]
|
38 |
|
39 |
class MaterialCategory(Enum):
|
|
|
40 |
INSULATION = "Insulation"
|
41 |
+
STRUCTURAL = "Structural"
|
|
|
42 |
FINISHING = "Finishing"
|
43 |
+
SUB_STRUCTURAL = "Sub-Structural"
|
44 |
|
45 |
class Material:
|
46 |
def __init__(self, name: str, category: MaterialCategory, conductivity: float, density: float,
|
|
|
58 |
self.emissivity = emissivity
|
59 |
self.is_library = is_library
|
60 |
|
61 |
+
def get_thermal_mass(self) -> str:
|
62 |
+
# Calculate areal heat capacity: 蟻 * cp * d (J/m虏路K)
|
63 |
+
thermal_mass = self.density * self.specific_heat * self.default_thickness
|
64 |
+
# Categorize based on thresholds
|
65 |
+
if thermal_mass < 30000:
|
66 |
+
return "Low"
|
67 |
+
elif 30000 <= thermal_mass <= 90000:
|
68 |
+
return "Medium"
|
69 |
+
else:
|
70 |
+
return "High"
|
71 |
|
72 |
+
def get_u_value(self) -> float:
|
73 |
+
# Calculate U-value: 位 / d (W/m虏路K)
|
74 |
return self.conductivity / self.default_thickness if self.default_thickness > 0 else 0.0
|
75 |
|
76 |
class GlazingMaterial:
|
|
|
100 |
"Embodied Carbon (kgCO鈧俥/kg)": m.embodied_carbon,
|
101 |
"Absorptivity": m.absorptivity,
|
102 |
"Price (USD/m虏)": m.price,
|
103 |
+
"Emissivity": m.emissivity,
|
104 |
+
"Thermal Mass Category": m.get_thermal_mass()
|
105 |
}
|
106 |
for m in project_materials.values()
|
107 |
]
|
|
|
227 |
try:
|
228 |
material_library.library_materials[k] = Material(
|
229 |
name=k,
|
230 |
+
category=MaterialCategory(m["category"].upper().replace("-", "_")),
|
231 |
conductivity=m["thermal_properties"]["conductivity"],
|
232 |
density=m["thermal_properties"]["density"],
|
233 |
specific_heat=m["thermal_properties"]["specific_heat"],
|
|
|
244 |
|
245 |
material_library.library_glazing_materials = {}
|
246 |
for k, f in st.session_state.project_data["fenestrations"]["library"].items():
|
247 |
+
try:
|
248 |
+
material_library.library_glazing_materials[k] = GlazingMaterial(
|
249 |
+
name=k,
|
250 |
+
shgc=f["performance"]["shgc"],
|
251 |
+
u_value=f["performance"]["u_value"],
|
252 |
+
h_o=f.get("h_o", DEFAULT_WINDOW_PROPERTIES["h_o"]),
|
253 |
+
is_library=True
|
254 |
+
)
|
255 |
+
except KeyError as e:
|
256 |
+
logger.error(f"Error processing fenestration {k}: Missing key {e}")
|
257 |
+
continue
|
|
|
258 |
|
259 |
with tab1:
|
260 |
display_materials_tab(material_library)
|
|
|
308 |
cols[1].write("**Thermal Mass**")
|
309 |
cols[2].write("**U-Value (W/m虏路K)**")
|
310 |
cols[3].write("**Preview**")
|
311 |
+
cols[4].write("**Clone**")
|
312 |
for material in library_materials:
|
313 |
cols = st.columns([2, 1, 1, 1, 1])
|
314 |
cols[0].write(material.name)
|
315 |
+
cols[1].write(material.get_thermal_mass())
|
316 |
cols[2].write(f"{material.get_u_value():.3f}")
|
317 |
if cols[3].button("Preview", key=f"preview_lib_mat_{material.name}"):
|
318 |
if st.session_state.get("rerun_trigger") != f"preview_mat_{material.name}":
|
|
|
344 |
"emissivity": material.emissivity
|
345 |
}
|
346 |
st.session_state.active_tab = "Materials"
|
347 |
+
if cols[4].button("Clone", key=f"copy_lib_mat_{material.name}"):
|
348 |
new_name = f"{material.name}_Project"
|
349 |
counter = 1
|
350 |
while new_name in st.session_state.project_data["materials"]["project"] or new_name in st.session_state.project_data["materials"]["library"]:
|
|
|
382 |
for material in st.session_state.project_data["materials"]["project"].values():
|
383 |
cols = st.columns([2, 1, 1, 1, 1])
|
384 |
cols[0].write(material.name)
|
385 |
+
cols[1].write(material.get_thermal_mass())
|
386 |
cols[2].write(f"{material.get_u_value():.3f}")
|
387 |
+
if cols[3].button("Edit", key=f"edit_mat_{material.name}"):
|
388 |
if st.session_state.get("rerun_trigger") != f"edit_mat_{material.name}":
|
389 |
st.session_state.rerun_trigger = f"edit_mat_{material.name}"
|
390 |
st.session_state.material_editor = {
|
|
|
415 |
"emissivity": material.emissivity
|
416 |
}
|
417 |
st.session_state.active_tab = "Materials"
|
418 |
+
if cols[4].button("Delete", key=f"delete_mat_{material.name}"):
|
419 |
success, message = material_library.delete_project_material(
|
420 |
material.name, st.session_state.project_data["materials"]["project"], st.session_state.get("components", {})
|
421 |
)
|
|
|
459 |
original_name = editor_state.get("original_name", "")
|
460 |
name = st.text_input(
|
461 |
"Material Name",
|
462 |
+
value=form_state.get("name", editor_state.get("name", ""),
|
463 |
help="Unique material identifier",
|
464 |
key="material_name_input"
|
465 |
)
|
|
|
559 |
try:
|
560 |
new_material = Material(
|
561 |
name=name,
|
562 |
+
category=MaterialCategory(category.upper().replace("-", "_")),
|
563 |
conductivity=conductivity,
|
564 |
density=density,
|
565 |
specific_heat=specific_heat,
|
|
|
589 |
"embodied_carbon": 0.5,
|
590 |
"absorptivity": DEFAULT_MATERIAL_PROPERTIES["absorptivity"],
|
591 |
"price": 50.0,
|
592 |
+
"emissivity": DEFAULT_PROPERTIES["emissivity"]
|
593 |
+
},
|
594 |
+
st.session_state.material_action = {"action": None, "material_action_id": None}
|
595 |
st.session_state.rerun_trigger = None
|
596 |
st.session_state.materials_rerun_pending = True
|
597 |
else:
|
598 |
st.error(f"Failed to save material: {message}")
|
599 |
except Exception as e:
|
600 |
+
st.error("Error saving material: {str(e)}")
|
601 |
|
602 |
def display_fenestrations_tab(material_library: MaterialLibrary):
|
603 |
+
"""Display the fenestrations tab with two-column layout."""
|
604 |
col1, col2 = st.columns([3, 2])
|
605 |
with col1:
|
606 |
st.subheader("Fenestrations")
|
|
|
613 |
if st.session_state["fenestration_filter"] == "None":
|
614 |
library_fenestrations = []
|
615 |
elif fenestration_filter != "All":
|
616 |
+
# Map fenestration types for filtering
|
617 |
+
fen_type = "window" if fenestration_filter == "Window/Skylight" else "door"
|
618 |
+
library_fenestrations = [f for f in library_fenestrations if
|
619 |
+
st.session_state.project_data["fenestrations"]["library"][f.name]["type"] == fen_type]
|
620 |
+
cols = st.columns([2, cols, 1, 1, 1, 1])
|
621 |
cols[0].write("**Name**")
|
622 |
cols[1].write("**SHGC**")
|
623 |
cols[2].write("**U-Value (W/m虏路K)**")
|
624 |
cols[3].write("**Preview**")
|
625 |
+
cols[4].write("**Clone**")
|
626 |
for fenestration in library_fenestrations:
|
627 |
cols = st.columns([2, 1, 1, 1, 1])
|
628 |
cols[0].write(fenestration.name)
|
629 |
+
cols[1].write(f"{fenestration_shgc:.2f}")
|
630 |
+
cols[2].write(f"{fenestration_data.u_value:.3f}")
|
631 |
+
if cols[3].button("Preview", key=f"preview_fen_btn_{fenestration.name}"):
|
632 |
+
if st.session_state.get("rerun_trigger") != f"preview_fen_{fenestration.name}"):
|
633 |
st.session_state.rerun_trigger = f"preview_fen_{fenestration.name}"
|
634 |
st.session_state.fenestration_editor = {
|
635 |
+
"name": fenestration_name,
|
636 |
+
"shgc": fenestration_shgc,
|
637 |
"u_value": fenestration.u_value,
|
638 |
+
"shgc": fenestration_h_o,
|
639 |
"is_edit": False,
|
640 |
+
"edit": "library"
|
641 |
+
},
|
642 |
st.session_state.fenestration_form_state = {
|
643 |
"name": fenestration.name,
|
644 |
+
"shgc": fenestration_shgc,
|
645 |
"u_value": fenestration.u_value,
|
646 |
+
"shgc": fenestration_h_o
|
647 |
+
},
|
648 |
st.session_state.active_tab = "Fenestrations"
|
649 |
+
if cols[4].button("Clone", key=f"copy_fen_btn_{fenestration.name}"):
|
650 |
+
new_name = f"{fenestration_name}_Project"
|
651 |
counter = 1
|
652 |
while new_name in st.session_state.project_data["fenestrations"]["project"] or new_name in st.session_state.project_data["fenestrations"]["library"]:
|
653 |
+
new_name = f"{fenestration_name}_Project_{counter}"
|
654 |
counter += 1
|
655 |
new_fenestration = GlazingMaterial(
|
656 |
name=new_name,
|
|
|
660 |
is_library=False
|
661 |
)
|
662 |
st.session_state.project_data["fenestrations"]["project"][new_name] = new_fenestration
|
663 |
+
st.success(f"Fenestration '{new_name}' cloned successfully!")
|
664 |
st.session_state.materials_rerun_pending = True
|
665 |
|
666 |
st.subheader("Project Fenestrations")
|
|
|
677 |
cols[0].write(fenestration.name)
|
678 |
cols[1].write(f"{fenestration.shgc:.2f}")
|
679 |
cols[2].write(f"{fenestration.u_value:.3f}")
|
680 |
+
if cols[3].button("Edit", key=f"edit_fen_{fenestration.name}"):
|
681 |
if st.session_state.get("rerun_trigger") != f"edit_fen_{fenestration.name}":
|
682 |
st.session_state.rerun_trigger = f"edit_fen_{fenestration.name}"
|
683 |
st.session_state.fenestration_editor = {
|
|
|
696 |
"h_o": fenestration.h_o
|
697 |
}
|
698 |
st.session_state.active_tab = "Fenestrations"
|
699 |
+
if cols[4].button("Delete", key=f"delete_fen_{fenestration.name}"):
|
700 |
if any(comp.get("fenestration_material") and comp["fenestration_material"].name == fenestration.name
|
701 |
for comp_list in st.session_state.get("components", {}).values() for comp in comp_list):
|
702 |
st.error(f"Fenestration '{fenestration.name}' is used in components and cannot be deleted.")
|
703 |
else:
|
704 |
del st.session_state.project_data["fenestrations"]["project"][fenestration.name]
|
705 |
+
st.success(f"Fenestration '{fenestration.name}' deleted successfully!")
|
706 |
st.session_state.materials_rerun_pending = True
|
707 |
else:
|
708 |
st.write("No project fenestrations added.")
|
|
|
813 |
)
|
814 |
if is_edit and editor_state.get("edit_source") == "project" and name == original_name:
|
815 |
st.session_state.project_data["fenestrations"]["project"][original_name] = new_fenestration
|
816 |
+
st.success(f"Fenestration '{name}' updated successfully!")
|
817 |
else:
|
818 |
st.session_state.project_data["fenestrations"]["project"][name] = new_fenestration
|
819 |
+
st.success(f"Fenestration '{name}' added successfully!")
|
820 |
st.session_state.fenestration_editor = {}
|
821 |
st.session_state.fenestration_form_state = {
|
822 |
"name": "",
|