mabuseif commited on
Commit
383fa1b
·
verified ·
1 Parent(s): cf5289d

Update app/materials_library.py

Browse files
Files changed (1) hide show
  1. app/materials_library.py +144 -130
app/materials_library.py CHANGED
@@ -56,7 +56,7 @@ DEFAULT_MATERIALS = {
56
  "density": 1920.0,
57
  "specific_heat": 840.0,
58
  "default_thickness": 0.1,
59
- "embodied_carbon": 240.0,
60
  "cost": 180.0,
61
  "absorptivity": 0.65,
62
  "emissivity": 0.90,
@@ -345,72 +345,25 @@ def categorize_thermal_mass(thermal_mass: float) -> str:
345
  else:
346
  return "High"
347
 
 
 
 
 
 
 
 
 
 
348
  def display_materials_page():
349
  """
350
  Display the material library page.
351
  This is the main function called by main.py when the Material Library page is selected.
352
  """
353
  # Initialize session state flags
354
- if "material_form_action" not in st.session_state:
355
- st.session_state.material_form_action = None
356
- if "fenestration_form_action" not in st.session_state:
357
- st.session_state.fenestration_form_action = None
358
- if "table_action" not in st.session_state:
359
- st.session_state.table_action = None
360
-
361
- # Process table actions before rendering UI
362
- if st.session_state.table_action:
363
- action = st.session_state.table_action
364
- action_type = action.get("type")
365
- name = action.get("name")
366
- is_material = action.get("is_material", True)
367
-
368
- if action_type == "preview":
369
- editor_key = "material_editor" if is_material else "fenestration_editor"
370
- data = (st.session_state.project_data["materials" if is_material else "fenestrations"]
371
- ["library" if action["is_library"] else "project"][name])
372
- st.session_state[editor_key] = {
373
- **data,
374
- "name": name,
375
- "edit_mode": False,
376
- "original_name": name,
377
- "is_library": action["is_library"]
378
- }
379
- st.session_state.active_tab = "Materials" if is_material else "Fenestrations"
380
- st.session_state.table_action = None
381
-
382
- elif action_type == "copy":
383
- library_data = st.session_state.project_data["materials" if is_material else "fenestrations"]["library"]
384
- project_data = st.session_state.project_data["materials" if is_material else "fenestrations"]["project"]
385
- new_name = f"{name}_Project"
386
- counter = 1
387
- while new_name in project_data or new_name in library_data:
388
- new_name = f"{name}_Project_{counter}"
389
- counter += 1
390
- project_data[new_name] = library_data[name].copy()
391
- logger.info(f"Copied library {'material' if is_material else 'fenestration'} '{name}' as '{new_name}' to project")
392
- st.session_state.table_action = None
393
-
394
- elif action_type == "edit":
395
- st.session_state.material_editor = {
396
- **st.session_state.project_data["materials"]["project"][name],
397
- "name": name,
398
- "edit_mode": True,
399
- "original_name": name,
400
- "is_library": False
401
- }
402
- st.session_state.active_tab = "Materials" if is_material else "Fenestrations"
403
- st.session_state.table_action = None
404
-
405
- elif action_type == "delete":
406
- check_fn = check_material_in_use if is_material else check_fenestration_in_use
407
- project_data = st.session_state.project_data["materials" if is_material else "fenestrations"]["project"]
408
- if check_fn(name):
409
- st.error(f"Cannot delete {'material' if is_material else 'fenestration'} '{name}' because it is in use.")
410
- else:
411
- del project_data[name]
412
- logger.info(f"Deleted {'material' if is_material else 'fenestration'} '{name}' from project")
413
- st.session_state.table_action = None
414
 
415
  st.title("Material Library")
416
 
@@ -435,24 +388,17 @@ def display_materials_page():
435
  with col1:
436
  if st.button("Back to Climate Data", key="back_to_climate"):
437
  st.session_state.current_page = "Climate Data"
438
- st.session_state.material_form_action = None
439
- st.session_state.fenestration_form_action = None
440
- st.session_state.table_action = None
441
  st.rerun()
442
 
443
  with col2:
444
  if st.button("Continue to Construction", key="continue_to_construction"):
445
  st.session_state.current_page = "Construction"
446
- st.session_state.material_form_action = None
447
- st.session_state.fenestration_form_action = None
448
- st.session_state.table_action = None
449
  st.rerun()
450
 
451
- # Trigger rerun if form actions are pending
452
- if st.session_state.material_form_action or st.session_state.fenestration_form_action or st.session_state.table_action:
453
- st.session_state.material_form_action = None
454
- st.session_state.fenestration_form_action = None
455
- st.session_state.table_action = None
456
  st.rerun()
457
 
458
  def display_materials_tab():
@@ -494,22 +440,41 @@ def display_materials_tab():
494
  cols[0].write(name)
495
  cols[1].write(thermal_mass_category)
496
  cols[2].write(f"{u_value:.3f}")
497
- unique_id = str(uuid.uuid4())
498
- if cols[3].button("Preview", key=f"preview_lib_mat_{name}_{unique_id}"):
499
- st.session_state.table_action = {
500
- "type": "preview",
501
- "name": name,
502
- "is_material": True,
503
- "is_library": True
504
- }
505
- unique_id = str(uuid.uuid4())
506
- if cols[4].button("Copy", key=f"copy_lib_mat_{name}_{unique_id}"):
507
- st.session_state.table_action = {
508
- "type": "copy",
509
  "name": name,
510
- "is_material": True,
 
 
 
 
 
 
 
 
 
 
 
511
  "is_library": True
512
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
513
  else:
514
  st.info("No materials found in the selected category.")
515
 
@@ -541,22 +506,40 @@ def display_materials_tab():
541
  cols[0].write(name)
542
  cols[1].write(thermal_mass_category)
543
  cols[2].write(f"{u_value:.3f}")
544
- unique_id = str(uuid.uuid4())
545
- if cols[3].button("Edit", key=f"edit_proj_mat_{name}_{unique_id}"):
546
- st.session_state.table_action = {
547
- "type": "edit",
 
 
 
548
  "name": name,
549
- "is_material": True,
550
- "is_library": False
551
- }
552
- unique_id = str(uuid.uuid4())
553
- if cols[4].button("Delete", key=f"delete_proj_mat_{name}_{unique_id}"):
554
- st.session_state.table_action = {
555
- "type": "delete",
556
- "name": name,
557
- "is_material": True,
 
 
 
558
  "is_library": False
559
  }
 
 
 
 
 
 
 
 
 
 
 
 
560
  else:
561
  st.info("No project materials in the selected category.")
562
 
@@ -631,22 +614,38 @@ def display_fenestrations_tab():
631
  cols[0].write(name)
632
  cols[1].write(fenestration["type"])
633
  cols[2].write(f"{fenestration['u_value']:.2f}")
634
- unique_id = str(uuid.uuid4())
635
- if cols[3].button("Preview", key=f"preview_lib_fen_{name}_{unique_id}"):
636
- st.session_state.table_action = {
637
- "type": "preview",
 
 
 
638
  "name": name,
639
- "is_material": False,
640
- "is_library": True
641
- }
642
- unique_id = str(uuid.uuid4())
643
- if cols[4].button("Copy", key=f"copy_lib_fen_{name}_{unique_id}"):
644
- st.session_state.table_action = {
645
- "type": "copy",
646
- "name": name,
647
- "is_material": False,
648
  "is_library": True
649
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
650
  else:
651
  st.info("No fenestrations found in the selected type.")
652
 
@@ -675,22 +674,37 @@ def display_fenestrations_tab():
675
  cols[0].write(name)
676
  cols[1].write(fenestration["type"])
677
  cols[2].write(f"{fenestration['u_value']:.2f}")
678
- unique_id = str(uuid.uuid4())
679
- if cols[3].button("Edit", key=f"edit_proj_fen_{name}_{unique_id}"):
680
- st.session_state.table_action = {
681
- "type": "edit",
682
- "name": name,
683
- "is_material": False,
684
- "is_library": False
685
- }
686
- unique_id = str(uuid.uuid4())
687
- if cols[4].button("Delete", key=f"delete_proj_fen_{name}_{unique_id}"):
688
- st.session_state.table_action = {
689
- "type": "delete",
690
  "name": name,
691
- "is_material": False,
 
 
 
 
 
 
 
 
692
  "is_library": False
693
  }
 
 
 
 
 
 
 
 
 
 
 
 
694
  else:
695
  st.info("No project fenestrations in the selected type.")
696
 
@@ -967,14 +981,14 @@ def display_material_editor():
967
  st.success(f"Material '{name}' added to your project.")
968
  logger.info(f"Added new material '{name}' to project")
969
 
970
- # Reset editor and flag action
971
  reset_material_editor()
972
- st.session_state.material_form_action = "save"
973
 
974
  # Handle clear button
975
  if clear_button:
976
  reset_material_editor()
977
- st.session_state.material_form_action = "clear"
978
 
979
  def display_fenestration_editor():
980
  """Display the fenestration editor form."""
@@ -1121,14 +1135,14 @@ def display_fenestration_editor():
1121
  st.success(f"Fenestration '{name}' added to your project.")
1122
  logger.info(f"Added new fenestration '{name}' to project")
1123
 
1124
- # Reset editor and flag action
1125
  reset_fenestration_editor()
1126
- st.session_state.fenestration_form_action = "save"
1127
 
1128
  # Handle clear button
1129
  if clear_button:
1130
  reset_fenestration_editor()
1131
- st.session_state.fenestration_form_action = "clear"
1132
 
1133
  def validate_material(
1134
  name: str, category: str, thermal_conductivity: float, density: float,
 
56
  "density": 1920.0,
57
  "specific_heat": 840.0,
58
  "default_thickness": 0.1,
59
+ "embodied_carbon": 240,
60
  "cost": 180.0,
61
  "absorptivity": 0.65,
62
  "emissivity": 0.90,
 
345
  else:
346
  return "High"
347
 
348
+ def get_stable_button_key(prefix: str, name: str, action: str) -> str:
349
+ """Generate a stable button key based on prefix, name, and action."""
350
+ # Create a stable hash-based key that won't change across reruns
351
+ import hashlib
352
+ key_string = f"{prefix}_{name}_{action}"
353
+ # Use first 8 characters of hash for shorter keys
354
+ hash_key = hashlib.md5(key_string.encode()).hexdigest()[:8]
355
+ return f"{prefix}_{action}_{hash_key}"
356
+
357
  def display_materials_page():
358
  """
359
  Display the material library page.
360
  This is the main function called by main.py when the Material Library page is selected.
361
  """
362
  # Initialize session state flags
363
+ if "material_saved" not in st.session_state:
364
+ st.session_state.material_saved = False
365
+ if "fenestration_saved" not in st.session_state:
366
+ st.session_state.fenestration_saved = False
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
367
 
368
  st.title("Material Library")
369
 
 
388
  with col1:
389
  if st.button("Back to Climate Data", key="back_to_climate"):
390
  st.session_state.current_page = "Climate Data"
 
 
 
391
  st.rerun()
392
 
393
  with col2:
394
  if st.button("Continue to Construction", key="continue_to_construction"):
395
  st.session_state.current_page = "Construction"
 
 
 
396
  st.rerun()
397
 
398
+ # Handle material/fenestration saved flags - trigger rerun only once
399
+ if st.session_state.material_saved or st.session_state.fenestration_saved:
400
+ st.session_state.material_saved = False
401
+ st.session_state.fenestration_saved = False
 
402
  st.rerun()
403
 
404
  def display_materials_tab():
 
440
  cols[0].write(name)
441
  cols[1].write(thermal_mass_category)
442
  cols[2].write(f"{u_value:.3f}")
443
+
444
+ # Use stable keys for buttons
445
+ preview_key = get_stable_button_key("lib_mat", name, "preview")
446
+ copy_key = get_stable_button_key("lib_mat", name, "copy")
447
+
448
+ if cols[3].button("Preview", key=preview_key):
449
+ st.session_state.material_editor = {
 
 
 
 
 
450
  "name": name,
451
+ "category": material["category"],
452
+ "thermal_conductivity": material["thermal_conductivity"],
453
+ "density": material["density"],
454
+ "specific_heat": material["specific_heat"],
455
+ "default_thickness": material["default_thickness"],
456
+ "embodied_carbon": material["embodied_carbon"],
457
+ "cost": material["cost"],
458
+ "absorptivity": material["absorptivity"],
459
+ "emissivity": material["emissivity"],
460
+ "colour": material["colour"],
461
+ "edit_mode": False,
462
+ "original_name": name,
463
  "is_library": True
464
  }
465
+ st.session_state.active_tab = "Materials"
466
+ st.success(f"Previewing material '{name}'")
467
+
468
+ if cols[4].button("Copy", key=copy_key):
469
+ new_name = f"{name}_Project"
470
+ counter = 1
471
+ while new_name in st.session_state.project_data["materials"]["project"] or new_name in library_materials:
472
+ new_name = f"{name}_Project_{counter}"
473
+ counter += 1
474
+ st.session_state.project_data["materials"]["project"][new_name] = material.copy()
475
+ st.success(f"Material '{new_name}' copied to project.")
476
+ logger.info(f"Copied library material '{name}' as '{new_name}' to project")
477
+ st.rerun()
478
  else:
479
  st.info("No materials found in the selected category.")
480
 
 
506
  cols[0].write(name)
507
  cols[1].write(thermal_mass_category)
508
  cols[2].write(f"{u_value:.3f}")
509
+
510
+ # Use stable keys for buttons
511
+ edit_key = get_stable_button_key("proj_mat", name, "edit")
512
+ delete_key = get_stable_button_key("proj_mat", name, "delete")
513
+
514
+ if cols[3].button("Edit", key=edit_key):
515
+ st.session_state.material_editor = {
516
  "name": name,
517
+ "category": material["category"],
518
+ "thermal_conductivity": material["thermal_conductivity"],
519
+ "density": material["density"],
520
+ "specific_heat": material["specific_heat"],
521
+ "default_thickness": material["default_thickness"],
522
+ "embodied_carbon": material["embodied_carbon"],
523
+ "cost": material["cost"],
524
+ "absorptivity": material["absorptivity"],
525
+ "emissivity": material["emissivity"],
526
+ "colour": material["colour"],
527
+ "edit_mode": True,
528
+ "original_name": name,
529
  "is_library": False
530
  }
531
+ st.session_state.active_tab = "Materials"
532
+ st.success(f"Editing material '{name}'")
533
+
534
+ if cols[4].button("Delete", key=delete_key):
535
+ is_in_use = check_material_in_use(name)
536
+ if is_in_use:
537
+ st.error(f"Cannot delete material '{name}' because it is in use in constructions.")
538
+ else:
539
+ del st.session_state.project_data["materials"]["project"][name]
540
+ st.success(f"Material '{name}' deleted from project.")
541
+ logger.info(f"Deleted material '{name}' from project")
542
+ st.rerun()
543
  else:
544
  st.info("No project materials in the selected category.")
545
 
 
614
  cols[0].write(name)
615
  cols[1].write(fenestration["type"])
616
  cols[2].write(f"{fenestration['u_value']:.2f}")
617
+
618
+ # Use stable keys for buttons
619
+ preview_key = get_stable_button_key("lib_fen", name, "preview")
620
+ copy_key = get_stable_button_key("lib_fen", name, "copy")
621
+
622
+ if cols[3].button("Preview", key=preview_key):
623
+ st.session_state.fenestration_editor = {
624
  "name": name,
625
+ "type": fenestration["type"],
626
+ "u_value": fenestration["u_value"],
627
+ "shgc": fenestration["shgc"],
628
+ "visible_transmittance": fenestration["visible_transmittance"],
629
+ "thickness": fenestration["thickness"],
630
+ "embodied_carbon": fenestration["embodied_carbon"],
631
+ "cost": fenestration["cost"],
632
+ "edit_mode": False,
633
+ "original_name": name,
634
  "is_library": True
635
  }
636
+ st.session_state.active_tab = "Fenestrations"
637
+ st.success(f"Previewing fenestration '{name}'")
638
+
639
+ if cols[4].button("Copy", key=copy_key):
640
+ new_name = f"{name}_Project"
641
+ counter = 1
642
+ while new_name in st.session_state.project_data["fenestrations"]["project"] or new_name in library_fenestrations:
643
+ new_name = f"{name}_Project_{counter}"
644
+ counter += 1
645
+ st.session_state.project_data["fenestrations"]["project"][new_name] = fenestration.copy()
646
+ st.success(f"Fenestration '{new_name}' copied to project.")
647
+ logger.info(f"Copied library fenestration '{name}' as '{new_name}' to project")
648
+ st.rerun()
649
  else:
650
  st.info("No fenestrations found in the selected type.")
651
 
 
674
  cols[0].write(name)
675
  cols[1].write(fenestration["type"])
676
  cols[2].write(f"{fenestration['u_value']:.2f}")
677
+
678
+ # Use stable keys for buttons
679
+ edit_key = get_stable_button_key("proj_fen", name, "edit")
680
+ delete_key = get_stable_button_key("proj_fen", name, "delete")
681
+
682
+ if cols[3].button("Edit", key=edit_key):
683
+ st.session_state.fenestration_editor = {
 
 
 
 
 
684
  "name": name,
685
+ "type": fenestration["type"],
686
+ "u_value": fenestration["u_value"],
687
+ "shgc": fenestration["shgc"],
688
+ "visible_transmittance": fenestration["visible_transmittance"],
689
+ "thickness": fenestration["thickness"],
690
+ "embodied_carbon": fenestration["embodied_carbon"],
691
+ "cost": fenestration["cost"],
692
+ "edit_mode": True,
693
+ "original_name": name,
694
  "is_library": False
695
  }
696
+ st.session_state.active_tab = "Fenestrations"
697
+ st.success(f"Editing fenestration '{name}'")
698
+
699
+ if cols[4].button("Delete", key=delete_key):
700
+ is_in_use = check_fenestration_in_use(name)
701
+ if is_in_use:
702
+ st.error(f"Cannot delete fenestration '{name}' because it is in use in components.")
703
+ else:
704
+ del st.session_state.project_data["fenestrations"]["project"][name]
705
+ st.success(f"Fenestration '{name}' deleted from project.")
706
+ logger.info(f"Deleted fenestration '{name}' from project")
707
+ st.rerun()
708
  else:
709
  st.info("No project fenestrations in the selected type.")
710
 
 
981
  st.success(f"Material '{name}' added to your project.")
982
  logger.info(f"Added new material '{name}' to project")
983
 
984
+ # Reset editor and flag for rerun
985
  reset_material_editor()
986
+ st.session_state.material_saved = True
987
 
988
  # Handle clear button
989
  if clear_button:
990
  reset_material_editor()
991
+ st.session_state.material_saved = True
992
 
993
  def display_fenestration_editor():
994
  """Display the fenestration editor form."""
 
1135
  st.success(f"Fenestration '{name}' added to your project.")
1136
  logger.info(f"Added new fenestration '{name}' to project")
1137
 
1138
+ # Reset editor and flag for rerun
1139
  reset_fenestration_editor()
1140
+ st.session_state.fenestration_saved = True
1141
 
1142
  # Handle clear button
1143
  if clear_button:
1144
  reset_fenestration_editor()
1145
+ st.session_state.fenestration_saved = True
1146
 
1147
  def validate_material(
1148
  name: str, category: str, thermal_conductivity: float, density: float,