fix: model groups
→ Model groups being a pain → Accessing some objects too shallow
This commit is contained in:
@@ -53,17 +53,30 @@ def automate_function(
|
||||
processor.process_elements(model_root)
|
||||
|
||||
# Report compliance issues
|
||||
compliance_summary = logger.get_summary()
|
||||
for missing_property, elements in compliance_summary.items():
|
||||
automate_context.attach_warning_to_objects(
|
||||
category="Missing Revit Material Property",
|
||||
object_ids=elements,
|
||||
message=(
|
||||
f"Missing {missing_property} on the object, preventing mass calculation. "
|
||||
f"Update Revit object to contain the necessary properties if element is critical."
|
||||
),
|
||||
logger_warnings = logger.get_warnings_summary()
|
||||
if logger_warnings:
|
||||
for missing_property, elements in logger_warnings.items():
|
||||
automate_context.attach_warning_to_objects(
|
||||
category="Missing Required Revit Properties",
|
||||
object_ids=elements,
|
||||
message=(
|
||||
f"Property '{missing_property}' is missing, which prevents carbon "
|
||||
f"calculations. If this element is critical to your analysis, please "
|
||||
f"update its Revit properties."
|
||||
),
|
||||
)
|
||||
|
||||
logger_successes = logger.get_successful_summary()
|
||||
if logger_successes:
|
||||
automate_context.attach_success_to_objects(
|
||||
category="Successfully Processed",
|
||||
object_ids=logger_successes,
|
||||
message="Carbon calculations completed successfully for this element.",
|
||||
)
|
||||
|
||||
# TODO: Create new version
|
||||
# automate_context.create_new_version_in_project(model_root, "dev", "")
|
||||
|
||||
automate_context.mark_run_success("Processing completed successfully.")
|
||||
|
||||
except Exception as e:
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
from abc import ABC, abstractmethod
|
||||
from typing import Dict
|
||||
from typing import Dict, List
|
||||
|
||||
|
||||
class Logger(ABC):
|
||||
@@ -14,6 +14,16 @@ class Logger(ABC):
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
def get_summary(self) -> Dict:
|
||||
"""Get summary of logged messages"""
|
||||
def log_success(self, object_id: str, **kwargs) -> None:
|
||||
"""Log a success message"""
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
def get_warnings_summary(self) -> Dict:
|
||||
"""Get summary of logged warning messages"""
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
def get_successful_summary(self) -> List:
|
||||
"""Get list of successfully processed elements"""
|
||||
pass
|
||||
|
||||
@@ -8,6 +8,7 @@ from src.interfaces.logger import Logger # Import the interface
|
||||
class ComplianceLogger(Logger): # Explicitly implement Logger interface
|
||||
def __init__(self):
|
||||
self.missing_properties: DefaultDict[str, set] = defaultdict(set)
|
||||
self.successful_elements: set = set()
|
||||
self._structlog = structlog.get_logger()
|
||||
|
||||
def log_error(self, message: str, **kwargs) -> None:
|
||||
@@ -32,8 +33,17 @@ class ComplianceLogger(Logger): # Explicitly implement Logger interface
|
||||
if object_id and missing_property:
|
||||
self.missing_properties[missing_property].add(object_id)
|
||||
|
||||
def get_summary(self) -> Dict[str, list]:
|
||||
def log_success(self, object_id: str) -> None:
|
||||
"""Log a successful element processing"""
|
||||
self._structlog.info(f"Successfully processed element", object_id=object_id)
|
||||
self.successful_elements.add(object_id)
|
||||
|
||||
def get_warnings_summary(self) -> Dict[str, list]:
|
||||
"""Get summary of logged messages"""
|
||||
return {
|
||||
prop: list(elements) for prop, elements in self.missing_properties.items()
|
||||
}
|
||||
|
||||
def get_successful_summary(self) -> list:
|
||||
"""Get list of successfully processed elements"""
|
||||
return list(self.successful_elements)
|
||||
|
||||
+53
-49
@@ -4,10 +4,12 @@ from src.interfaces.material_processor import MaterialProcessor
|
||||
from src.interfaces.compliance_checker import ComplianceChecker
|
||||
from src.interfaces.logger import Logger
|
||||
from src.utils.constants import (
|
||||
APPLICATION_ID,
|
||||
ELEMENTS,
|
||||
NAME,
|
||||
PROPERTIES,
|
||||
MATERIAL_QUANTITIES,
|
||||
SPECKLE_TYPE,
|
||||
ID,
|
||||
VOLUME,
|
||||
STRUCTURAL_ASSET,
|
||||
@@ -51,68 +53,70 @@ class RevitModelProcessor(ModelProcessor):
|
||||
|
||||
def _process_type_group(self, type_group: Any, level_name: str) -> None:
|
||||
"""Process a group of elements of the same type"""
|
||||
revit_objects = getattr(type_group, ELEMENTS, None)
|
||||
if not revit_objects:
|
||||
groups = getattr(type_group, ELEMENTS, None)
|
||||
if not groups:
|
||||
type_name = getattr(type_group, NAME, "Unknown")
|
||||
raise ValueError(f"Invalid type structure: missing elements in {type_name}")
|
||||
|
||||
type_name = getattr(type_group, NAME)
|
||||
for revit_object in revit_objects:
|
||||
self.process_element(level_name, type_name, revit_object)
|
||||
|
||||
for group in groups:
|
||||
revit_objects = getattr(group, ELEMENTS, None)
|
||||
if not revit_objects:
|
||||
raise ValueError(
|
||||
f"Invalid type structure: missing elements in "
|
||||
f"{getattr(group, NAME, None)}"
|
||||
)
|
||||
for revit_object in revit_objects:
|
||||
self.process_element(level_name, type_name, revit_object)
|
||||
|
||||
def process_element(self, level: str, type_name: str, revit_object: Any) -> None:
|
||||
"""Process a single element following original logic exactly"""
|
||||
|
||||
# TODO: We can probably straight up skip Line and Arc. Logging it as a warning is dumb
|
||||
|
||||
element_id = getattr(revit_object, ID, None)
|
||||
if not element_id:
|
||||
return
|
||||
|
||||
# First check elements
|
||||
elements = getattr(revit_object, ELEMENTS, None)
|
||||
if not elements:
|
||||
# Check Material Quantities
|
||||
properties = getattr(revit_object, PROPERTIES, None)
|
||||
if not properties:
|
||||
self._logger.log_warning(
|
||||
f"Missing elements", object_id=element_id, missing_property=ELEMENTS
|
||||
f"Missing Material Quantities",
|
||||
object_id=element_id,
|
||||
missing_property=PROPERTIES,
|
||||
)
|
||||
return
|
||||
material_quantities = properties.get(MATERIAL_QUANTITIES, None)
|
||||
if not material_quantities:
|
||||
self._logger.log_warning(
|
||||
f"Missing Material Quantities",
|
||||
object_id=element_id,
|
||||
missing_property=MATERIAL_QUANTITIES,
|
||||
)
|
||||
return
|
||||
|
||||
# Process each element
|
||||
for element in elements:
|
||||
# Check properties
|
||||
properties = getattr(element, PROPERTIES, None)
|
||||
if not properties:
|
||||
self._logger.log_warning(
|
||||
f"Missing properties",
|
||||
object_id=element_id,
|
||||
missing_property=PROPERTIES,
|
||||
)
|
||||
return
|
||||
|
||||
# Check Material Quantities
|
||||
material_quantities = properties.get(MATERIAL_QUANTITIES, None)
|
||||
if not material_quantities:
|
||||
self._logger.log_warning(
|
||||
f"Missing Material Quantities",
|
||||
object_id=element_id,
|
||||
missing_property=MATERIAL_QUANTITIES,
|
||||
)
|
||||
return
|
||||
|
||||
# Process each material
|
||||
for material_name, material_data in material_quantities.items():
|
||||
# Check required material properties
|
||||
for required_prop in [VOLUME, STRUCTURAL_ASSET, DENSITY]:
|
||||
if required_prop not in material_data:
|
||||
self._logger.log_warning(
|
||||
f"Missing {required_prop}",
|
||||
object_id=element_id,
|
||||
missing_property=required_prop,
|
||||
)
|
||||
return
|
||||
|
||||
try:
|
||||
self._material_processor.process_material(
|
||||
material_data, level, type_name
|
||||
)
|
||||
except Exception as e:
|
||||
self._logger.log_error(
|
||||
f"Failed to process element {element_id}", error=str(e)
|
||||
# Process each material
|
||||
# TODO: Project 2427 is an interesting one with compound materials
|
||||
# TODO: Checkout object fefcc95c2f0ecd28a49ecdd7764e2d79. Worth skipping if volume = 0?
|
||||
for material_name, material_data in material_quantities.items():
|
||||
# Check required material properties
|
||||
for required_prop in [VOLUME, STRUCTURAL_ASSET, DENSITY]:
|
||||
if required_prop not in material_data:
|
||||
self._logger.log_warning(
|
||||
f"Missing {required_prop}",
|
||||
object_id=element_id,
|
||||
missing_property=required_prop,
|
||||
)
|
||||
return
|
||||
|
||||
try:
|
||||
self._material_processor.process_material(
|
||||
material_data, level, type_name
|
||||
)
|
||||
self._logger.log_success(element_id)
|
||||
except Exception as e:
|
||||
self._logger.log_error(
|
||||
f"Failed to process element {element_id}", error=str(e)
|
||||
)
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
REQUIRED_PROPERTIES = ["volume", "density", "structuralAsset"]
|
||||
|
||||
# Keys
|
||||
APPLICATION_ID = "applicationId"
|
||||
DENSITY = "density"
|
||||
ELEMENTS = "elements"
|
||||
ID = "id"
|
||||
@@ -9,6 +10,7 @@ MATERIAL_QUANTITIES = "Material Quantities"
|
||||
NAME = "name"
|
||||
PROPERTIES = "properties"
|
||||
SOURCE_APPLICATION = "sourceApplication"
|
||||
SPECKLE_TYPE = "speckle_type"
|
||||
STRUCTURAL_ASSET = "structuralAsset"
|
||||
UNITS = "units"
|
||||
VALUE = "value"
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
# pytest: skip-file
|
||||
|
||||
from src.processors.material import RevitMaterialProcessor
|
||||
from src.processors.compliance import RevitComplianceChecker
|
||||
from src.processors.model import RevitModelProcessor
|
||||
@@ -73,7 +75,7 @@ try:
|
||||
processor.process_elements(model_root)
|
||||
|
||||
# Report compliance issues
|
||||
compliance_summary = logger.get_summary()
|
||||
compliance_summary = logger.get_warnings_summary()
|
||||
|
||||
print("Processing completed successfully.")
|
||||
|
||||
|
||||
Reference in New Issue
Block a user