Spaces:
Sleeping
Sleeping
| """Exterior/interior part classification for assembly review. | |
| Classifies parts based on name-pattern matching against configurable | |
| exterior patterns. Everything that does not match is classified as interior. | |
| """ | |
| from __future__ import annotations | |
| import logging | |
| from dataclasses import dataclass | |
| from config.settings import SplitterSettings | |
| from src.loader.assembly_tree import AssemblyNode | |
| logger = logging.getLogger(__name__) | |
| class ClassificationResult: | |
| """Result of exterior/interior classification for a part.""" | |
| node_id: str | |
| name: str | |
| classification: str # "exterior" or "interior" | |
| score: int # 1 = matched exterior pattern, 0 = no match | |
| reasons: list[str] | |
| def classify_parts( | |
| root: AssemblyNode, | |
| settings: SplitterSettings | None = None, | |
| ) -> list[ClassificationResult]: | |
| """Classify all leaf parts as exterior or interior. | |
| Uses simple case-insensitive substring matching against the configured | |
| exterior_patterns. Any part whose name matches at least one pattern | |
| is classified as "exterior"; everything else is "interior". | |
| Args: | |
| root: Assembly tree root node. | |
| settings: Splitter settings with exterior_patterns. | |
| Returns: | |
| List of ClassificationResult for each leaf part. | |
| """ | |
| if settings is None: | |
| settings = SplitterSettings() | |
| patterns = [p.lower() for p in settings.exterior_patterns] | |
| results: list[ClassificationResult] = [] | |
| for leaf in root.iter_leaves(): | |
| if leaf.shape is None or leaf.shape.IsNull(): | |
| continue | |
| name_lower = leaf.name.lower() | |
| matched = [p for p in patterns if p in name_lower] | |
| if matched: | |
| classification = "exterior" | |
| score = 1 | |
| reasons = [f"name matches exterior pattern(s): {', '.join(matched)}"] | |
| else: | |
| classification = "interior" | |
| score = 0 | |
| reasons = ["no exterior pattern matched"] | |
| leaf.classification = classification | |
| result = ClassificationResult( | |
| node_id=leaf.id, | |
| name=leaf.name, | |
| classification=classification, | |
| score=score, | |
| reasons=reasons, | |
| ) | |
| results.append(result) | |
| logger.debug("Part '%s': -> %s (%s)", leaf.name, classification, "; ".join(reasons)) | |
| ext_count = sum(1 for r in results if r.classification == "exterior") | |
| int_count = sum(1 for r in results if r.classification == "interior") | |
| logger.info("Classification: %d exterior, %d interior", ext_count, int_count) | |
| return results | |