---
name: "data-type-classifier"
description: "Classify construction data by type (structured, unstructured, semi-structured). Analyze data sources and recommend appropriate storage/processing methods"
homepage: "https://datadrivenconstruction.io"
metadata: {"openclaw": {"emoji": "🏷️", "os": ["win32"], "homepage": "https://datadrivenconstruction.io", "requires": {"bins": ["python3"], "anyBins": ["tesseract", "ifcopenshell"]}}}
---
# Data Type Classifier

## Overview

Based on DDC methodology (Chapter 2.1), this skill classifies construction data by type, analyzes data sources, and recommends appropriate storage, processing, and integration methods.

**Book Reference:** "Типы данных в строительстве" / "Data Types in Construction"

## Quick Start

```python
from dataclasses import dataclass, field
from enum import Enum
from typing import List, Dict, Optional, Any, Tuple
from datetime import datetime
import json
import re
import mimetypes

class DataStructure(Enum):
    """Data structure classification"""
    STRUCTURED = "structured"           # Tables, databases, spreadsheets
    SEMI_STRUCTURED = "semi_structured" # JSON, XML, IFC
    UNSTRUCTURED = "unstructured"       # Documents, images, videos
    GEOMETRIC = "geometric"             # CAD, BIM geometry
    TEMPORAL = "temporal"               # Time-series, schedules
    SPATIAL = "spatial"                 # GIS, coordinates

class DataFormat(Enum):
    """Common construction data formats"""
    # Structured
    CSV = "csv"
    EXCEL = "excel"
    SQL = "sql"
    PARQUET = "parquet"

    # Semi-structured
    JSON = "json"
    XML = "xml"
    IFC = "ifc"
    BCF = "bcf"

    # Unstructured
    PDF = "pdf"
    DOCX = "docx"
    IMAGE = "image"
    VIDEO = "video"

    # Geometric
    DWG = "dwg"
    DXF = "dxf"
    RVT = "rvt"
    NWD = "nwd"
    OBJ = "obj"
    STL = "stl"

    # Schedule
    MPP = "mpp"
    P6 = "p6"
    XER = "xer"

class StorageRecommendation(Enum):
    """Storage system recommendations"""
    RELATIONAL_DB = "relational_database"
    DOCUMENT_DB = "document_database"
    OBJECT_STORAGE = "object_storage"
    GRAPH_DB = "graph_database"
    TIME_SERIES_DB = "time_series_database"
    VECTOR_DB = "vector_database"
    FILE_SYSTEM = "file_system"
    DATA_LAKE = "data_lake"

@dataclass
class DataCharacteristics:
    """Characteristics of a data source"""
    has_schema: bool
    has_relationships: bool
    is_queryable: bool
    is_binary: bool
    has_geometry: bool
    has_temporal: bool
    has_text_content: bool
    avg_record_size: Optional[int] = None  # bytes
    estimated_volume: Optional[str] = None  # small/medium/large/huge
    update_frequency: Optional[str] = None

@dataclass
class DataClassification:
    """Classification result for a data source"""
    source_name: str
    source_type: str
    detected_format: DataFormat
    structure: DataStructure
    characteristics: DataCharacteristics
    storage_recommendation: StorageRecommendation
    processing_tools: List[str]
    integration_options: List[str]
    quality_considerations: List[str]
    confidence: float

@dataclass
class ClassificationReport:
    """Complete classification report"""
    total_sources: int
    classifications: List[DataClassification]
    summary_by_structure: Dict[str, int]
    summary_by_format: Dict[str, int]
    storage_recommendations: Dict[str, List[str]]
    integration_strategy: Dict[str, str]


class DataTypeClassifier:
    """
    Classify construction data by type and recommend processing methods.
    Based on DDC methodology Chapter 2.1.
    """

    def __init__(self):
        self.format_signatures = self._define_format_signatures()
        self.structure_mapping = self._define_structure_mapping()
        self.storage_mapping = self._define_storage_mapping()
        self.processing_tools = self._define_processing_tools()

    def _define_format_signatures(self) -> Dict[str, Dict]:
        """Define format detection signatures"""
        return {
            # File extensions
            ".csv": {"format": DataFormat.CSV, "structure": DataStructure.STRUCTURED},
            ".xlsx": {"format": DataFormat.EXCEL, "structure": DataStructure.STRUCTURED},
            ".xls": {"format": DataFormat.EXCEL, "structure": DataStructure.STRUCTURED},
            ".json": {"format": DataFormat.JSON, "structure": DataStructure.SEMI_STRUCTURED},
            ".xml": {"format": DataFormat.XML, "structure": DataStructure.SEMI_STRUCTURED},
            ".ifc": {"format": DataFormat.IFC, "structure": DataStructure.SEMI_STRUCTURED},
            ".bcf": {"format": DataFormat.BCF, "structure": DataStructure.SEMI_STRUCTURED},
            ".pdf": {"format": DataFormat.PDF, "structure": DataStructure.UNSTRUCTURED},
            ".docx": {"format": DataFormat.DOCX, "structure": DataStructure.UNSTRUCTURED},
            ".dwg": {"format": DataFormat.DWG, "structure": DataStructure.GEOMETRIC},
            ".dxf": {"format": DataFormat.DXF, "structure": DataStructure.GEOMETRIC},
            ".rvt": {"format": DataFormat.RVT, "structure": DataStructure.GEOMETRIC},
            ".nwd": {"format": DataFormat.NWD, "structure": DataStructure.GEOMETRIC},
            ".mpp": {"format": DataFormat.MPP, "structure": DataStructure.TEMPORAL},
            ".xer": {"format": DataFormat.XER, "structure": DataStructure.TEMPORAL},
            ".parquet": {"format": DataFormat.PARQUET, "structure": DataStructure.STRUCTURED},
            ".jpg": {"format": DataFormat.IMAGE, "structure": DataStructure.UNSTRUCTURED},
            ".png": {"format": DataFormat.IMAGE, "structure": DataStructure.UNSTRUCTURED},
            ".mp4": {"format": DataFormat.VIDEO, "structure": DataStructure.UNSTRUCTURED}
        }

    def _define_structure_mapping(self) -> Dict[DataStructure, Dict]:
        """Define characteristics for each structure type"""
        return {
            DataStructure.STRUCTURED: {
                "description": "Tabular data with fixed schema",
                "examples": ["Cost databases", "Material lists", "Vendor records"],
                "query_support": True,
                "schema_required": True
            },
            DataStructure.SEMI_STRUCTURED: {
                "description": "Hierarchical data with flexible schema",
                "examples": ["BIM models (IFC)", "API responses", "Configuration files"],
                "query_support": True,
                "schema_required": False
            },
            DataStructure.UNSTRUCTURED: {
                "description": "No predefined schema or format",
                "examples": ["Contracts", "Photos", "Emails", "Meeting notes"],
                "query_support": False,
                "schema_required": False
            },
            DataStructure.GEOMETRIC: {
                "description": "3D/2D geometric and spatial data",
                "examples": ["CAD drawings", "BIM geometry", "Point clouds"],
                "query_support": True,
                "schema_required": True
            },
            DataStructure.TEMPORAL: {
                "description": "Time-based sequential data",
                "examples": ["Schedules", "Progress data", "Sensor readings"],
                "query_support": True,
                "schema_required": True
            },
            DataStructure.SPATIAL: {
                "description": "Geographic and location data",
                "examples": ["Site maps", "GPS tracks", "GIS layers"],
                "query_support": True,
                "schema_required": True
            }
        }

    def _define_storage_mapping(self) -> Dict[DataStructure, StorageRecommendation]:
        """Map data structures to storage recommendations"""
        return {
            DataStructure.STRUCTURED: StorageRecommendation.RELATIONAL_DB,
            DataStructure.SEMI_STRUCTURED: StorageRecommendation.DOCUMENT_DB,
            DataStructure.UNSTRUCTURED: StorageRecommendation.OBJECT_STORAGE,
            DataStructure.GEOMETRIC: StorageRecommendation.FILE_SYSTEM,
            DataStructure.TEMPORAL: StorageRecommendation.TIME_SERIES_DB,
            DataStructure.SPATIAL: StorageRecommendation.RELATIONAL_DB
        }

    def _define_processing_tools(self) -> Dict[DataFormat, List[str]]:
        """Define processing tools for each format"""
        return {
            DataFormat.CSV: ["pandas", "polars", "duckdb"],
            DataFormat.EXCEL: ["pandas", "openpyxl", "xlrd"],
            DataFormat.JSON: ["json", "pandas", "jq"],
            DataFormat.XML: ["lxml", "ElementTree", "BeautifulSoup"],
            DataFormat.IFC: ["ifcopenshell", "IfcOpenShell", "xBIM"],
            DataFormat.BCF: ["bcfpython", "ifcopenshell"],
            DataFormat.PDF: ["pdfplumber", "PyPDF2", "pdf2image"],
            DataFormat.DOCX: ["python-docx", "mammoth"],
            DataFormat.DWG: ["ezdxf", "Teigha", "ODA SDK"],
            DataFormat.DXF: ["ezdxf", "dxfgrabber"],
            DataFormat.RVT: ["Revit API", "pyRevit", "Dynamo"],
            DataFormat.NWD: ["Navisworks API", "NW API"],
            DataFormat.MPP: ["mpxj", "Project API"],
            DataFormat.XER: ["xerparser", "P6 API"],
            DataFormat.PARQUET: ["pandas", "pyarrow", "polars"],
            DataFormat.IMAGE: ["PIL", "opencv", "scikit-image"],
            DataFormat.VIDEO: ["opencv", "ffmpeg", "moviepy"]
        }

    def classify_source(
        self,
        source_name: str,
        source_type: str,
        file_extension: Optional[str] = None,
        sample_data: Optional[Any] = None,
        metadata: Optional[Dict] = None
    ) -> DataClassification:
        """
        Classify a single data source.

        Args:
            source_name: Name of the data source
            source_type: Type (file, database, api, etc.)
            file_extension: File extension if applicable
            sample_data: Sample of the data for analysis
            metadata: Additional metadata

        Returns:
            Classification result
        """
        # Detect format
        detected_format, structure = self._detect_format(
            file_extension, source_type, sample_data
        )

        # Analyze characteristics
        characteristics = self._analyze_characteristics(
            detected_format, structure, sample_data, metadata
        )

        # Determine storage recommendation
        storage = self._recommend_storage(structure, characteristics)

        # Get processing tools
        tools = self.processing_tools.get(detected_format, [])

        # Determine integration options
        integration = self._get_integration_options(detected_format, structure)

        # Quality considerations
        quality = self._get_quality_considerations(detected_format, structure)

        # Calculate confidence
        confidence = self._calculate_confidence(
            file_extension, sample_data, metadata
        )

        return DataClassification(
            source_name=source_name,
            source_type=source_type,
            detected_format=detected_format,
            structure=structure,
            characteristics=characteristics,
            storage_recommendation=storage,
            processing_tools=tools,
            integration_options=integration,
            quality_considerations=quality,
            confidence=confidence
        )

    def _detect_format(
        self,
        extension: Optional[str],
        source_type: str,
        sample: Optional[Any]
    ) -> Tuple[DataFormat, DataStructure]:
        """Detect data format and structure"""
        # Check file extension
        if extension:
            ext = extension.lower() if extension.startswith('.') else f".{extension.lower()}"
            if ext in self.format_signatures:
                sig = self.format_signatures[ext]
                return sig["format"], sig["structure"]

        # Check source type
        if source_type == "database":
            return DataFormat.SQL, DataStructure.STRUCTURED
        elif source_type == "api":
            return DataFormat.JSON, DataStructure.SEMI_STRUCTURED

        # Analyze sample data
        if sample:
            if isinstance(sample, dict):
                return DataFormat.JSON, DataStructure.SEMI_STRUCTURED
            elif isinstance(sample, list) and all(isinstance(x, dict) for x in sample):
                return DataFormat.JSON, DataStructure.STRUCTURED
            elif isinstance(sample, str):
                if sample.strip().startswith('<'):
                    return DataFormat.XML, DataStructure.SEMI_STRUCTURED
                elif sample.strip().startswith('{'):
                    return DataFormat.JSON, DataStructure.SEMI_STRUCTURED

        # Default
        return DataFormat.JSON, DataStructure.SEMI_STRUCTURED

    def _analyze_characteristics(
        self,
        format: DataFormat,
        structure: DataStructure,
        sample: Optional[Any],
        metadata: Optional[Dict]
    ) -> DataCharacteristics:
        """Analyze data characteristics"""
        return DataCharacteristics(
            has_schema=structure in [DataStructure.STRUCTURED, DataStructure.TEMPORAL],
            has_relationships=format in [DataFormat.IFC, DataFormat.SQL],
            is_queryable=structure != DataStructure.UNSTRUCTURED,
            is_binary=format in [
                DataFormat.DWG, DataFormat.RVT, DataFormat.NWD,
                DataFormat.IMAGE, DataFormat.VIDEO, DataFormat.PDF
            ],
            has_geometry=structure == DataStructure.GEOMETRIC or format == DataFormat.IFC,
            has_temporal=structure == DataStructure.TEMPORAL,
            has_text_content=format in [
                DataFormat.PDF, DataFormat.DOCX, DataFormat.CSV
            ],
            estimated_volume=metadata.get("volume") if metadata else None,
            update_frequency=metadata.get("update_frequency") if metadata else None
        )

    def _recommend_storage(
        self,
        structure: DataStructure,
        characteristics: DataCharacteristics
    ) -> StorageRecommendation:
        """Recommend storage solution"""
        # Special cases
        if characteristics.has_text_content and not characteristics.has_schema:
            return StorageRecommendation.VECTOR_DB

        if characteristics.is_binary and characteristics.estimated_volume == "huge":
            return StorageRecommendation.OBJECT_STORAGE

        if characteristics.has_relationships:
            return StorageRecommendation.GRAPH_DB

        # Default mapping
        return self.storage_mapping.get(structure, StorageRecommendation.FILE_SYSTEM)

    def _get_integration_options(
        self,
        format: DataFormat,
        structure: DataStructure
    ) -> List[str]:
        """Get integration options for the data"""
        options = []

        if structure == DataStructure.STRUCTURED:
            options.extend(["Direct SQL queries", "ETL pipelines", "API export"])
        elif structure == DataStructure.SEMI_STRUCTURED:
            options.extend(["JSON/XML parsing", "Schema validation", "API integration"])
        elif structure == DataStructure.UNSTRUCTURED:
            options.extend(["OCR extraction", "NLP processing", "ML classification"])
        elif structure == DataStructure.GEOMETRIC:
            options.extend(["IFC export", "Geometry extraction", "Clash detection"])

        # Format-specific options
        if format == DataFormat.IFC:
            options.append("IFC import/export via IfcOpenShell")
        elif format == DataFormat.EXCEL:
            options.append("Pandas DataFrame conversion")
        elif format == DataFormat.PDF:
            options.append("PDF text/table extraction")

        return options

    def _get_quality_considerations(
        self,
        format: DataFormat,
        structure: DataStructure
    ) -> List[str]:
        """Get quality considerations"""
        considerations = []

        if structure == DataStructure.STRUCTURED:
            considerations.extend([
                "Validate schema consistency",
                "Check for null/missing values",
                "Verify data types"
            ])
        elif structure == DataStructure.UNSTRUCTURED:
            considerations.extend([
                "OCR accuracy verification",
                "Text encoding issues",
                "Content extraction completeness"
            ])
        elif structure == DataStructure.GEOMETRIC:
            considerations.extend([
                "Model validity (closed solids)",
                "Coordinate system consistency",
                "Unit verification"
            ])

        # Format-specific
        if format == DataFormat.IFC:
            considerations.append("IFC schema version compatibility")
        elif format == DataFormat.EXCEL:
            considerations.append("Formula vs value extraction")

        return considerations

    def _calculate_confidence(
        self,
        extension: Optional[str],
        sample: Optional[Any],
        metadata: Optional[Dict]
    ) -> float:
        """Calculate classification confidence"""
        confidence = 0.5  # Base confidence

        if extension:
            confidence += 0.3  # Extension provides good hint
        if sample:
            confidence += 0.15  # Sample data helps
        if metadata:
            confidence += 0.05  # Metadata adds context

        return min(1.0, confidence)

    def classify_multiple(
        self,
        sources: List[Dict]
    ) -> ClassificationReport:
        """
        Classify multiple data sources.

        Args:
            sources: List of source definitions

        Returns:
            Complete classification report
        """
        classifications = []

        for source in sources:
            classification = self.classify_source(
                source_name=source["name"],
                source_type=source.get("type", "file"),
                file_extension=source.get("extension"),
                sample_data=source.get("sample"),
                metadata=source.get("metadata")
            )
            classifications.append(classification)

        # Generate summaries
        summary_structure = {}
        summary_format = {}
        storage_recs = {}

        for c in classifications:
            # Structure summary
            struct = c.structure.value
            summary_structure[struct] = summary_structure.get(struct, 0) + 1

            # Format summary
            fmt = c.detected_format.value
            summary_format[fmt] = summary_format.get(fmt, 0) + 1

            # Storage recommendations
            storage = c.storage_recommendation.value
            if storage not in storage_recs:
                storage_recs[storage] = []
            storage_recs[storage].append(c.source_name)

        # Integration strategy
        strategy = self._generate_integration_strategy(classifications)

        return ClassificationReport(
            total_sources=len(sources),
            classifications=classifications,
            summary_by_structure=summary_structure,
            summary_by_format=summary_format,
            storage_recommendations=storage_recs,
            integration_strategy=strategy
        )

    def _generate_integration_strategy(
        self,
        classifications: List[DataClassification]
    ) -> Dict[str, str]:
        """Generate integration strategy"""
        strategy = {}

        # Group by structure
        structured = [c for c in classifications if c.structure == DataStructure.STRUCTURED]
        semi = [c for c in classifications if c.structure == DataStructure.SEMI_STRUCTURED]
        unstructured = [c for c in classifications if c.structure == DataStructure.UNSTRUCTURED]
        geometric = [c for c in classifications if c.structure == DataStructure.GEOMETRIC]

        if structured:
            strategy["structured_data"] = (
                "Use ETL pipeline to consolidate into central data warehouse. "
                "Implement SQL-based querying and reporting."
            )

        if semi:
            strategy["semi_structured_data"] = (
                "Use document database for flexible storage. "
                "Implement schema validation at ingestion."
            )

        if unstructured:
            strategy["unstructured_data"] = (
                "Extract text content using OCR/NLP. "
                "Store in vector database for semantic search."
            )

        if geometric:
            strategy["geometric_data"] = (
                "Standardize on IFC format for exchange. "
                "Maintain native formats for editing."
            )

        return strategy

    def generate_report(self, report: ClassificationReport) -> str:
        """Generate classification report"""
        output = f"""
# Data Classification Report

**Total Sources Analyzed:** {report.total_sources}

## Summary by Structure

"""
        for struct, count in report.summary_by_structure.items():
            output += f"- **{struct.title()}**: {count} sources\n"

        output += "\n## Summary by Format\n\n"
        for fmt, count in report.summary_by_format.items():
            output += f"- **{fmt.upper()}**: {count} sources\n"

        output += "\n## Storage Recommendations\n\n"
        for storage, sources in report.storage_recommendations.items():
            output += f"### {storage.replace('_', ' ').title()}\n"
            for src in sources:
                output += f"- {src}\n"
            output += "\n"

        output += "## Integration Strategy\n\n"
        for category, strategy in report.integration_strategy.items():
            output += f"### {category.replace('_', ' ').title()}\n{strategy}\n\n"

        output += "## Detailed Classifications\n\n"
        for c in report.classifications[:10]:
            output += f"""
### {c.source_name}
- **Format:** {c.detected_format.value}
- **Structure:** {c.structure.value}
- **Storage:** {c.storage_recommendation.value}
- **Tools:** {', '.join(c.processing_tools[:3])}
- **Confidence:** {c.confidence:.0%}
"""

        return output
```

## Common Use Cases

### Classify Single Data Source

```python
classifier = DataTypeClassifier()

# Classify a BIM model
classification = classifier.classify_source(
    source_name="Building Model",
    source_type="file",
    file_extension=".ifc",
    metadata={"volume": "large"}
)

print(f"Format: {classification.detected_format.value}")
print(f"Structure: {classification.structure.value}")
print(f"Storage: {classification.storage_recommendation.value}")
print(f"Tools: {classification.processing_tools}")
```

### Classify Multiple Sources

```python
sources = [
    {"name": "Cost Database", "type": "database", "extension": ".sql"},
    {"name": "Building Model", "type": "file", "extension": ".ifc"},
    {"name": "Contract PDFs", "type": "file", "extension": ".pdf"},
    {"name": "Site Photos", "type": "file", "extension": ".jpg"},
    {"name": "Schedule", "type": "file", "extension": ".mpp"}
]

report = classifier.classify_multiple(sources)

print(f"Total: {report.total_sources}")
print(f"By structure: {report.summary_by_structure}")
```

### Generate Classification Report

```python
report_text = classifier.generate_report(report)
print(report_text)

# Save to file
with open("classification_report.md", "w") as f:
    f.write(report_text)
```

## Quick Reference

| Component | Purpose |
|-----------|---------|
| `DataTypeClassifier` | Main classification engine |
| `DataStructure` | Structure types (structured, semi, unstructured) |
| `DataFormat` | File format detection |
| `StorageRecommendation` | Storage system recommendations |
| `DataClassification` | Classification result |
| `ClassificationReport` | Multi-source report |

## Resources

- **Book**: "Data-Driven Construction" by Artem Boiko, Chapter 2.1
- **Website**: https://datadrivenconstruction.io

## Next Steps

- Use [sql-query-builder](../sql-query-builder/SKILL.md) for structured data queries
- Use [pdf-to-structured](../../Chapter-2.4/pdf-to-structured/SKILL.md) for unstructured data
- Use [data-model-designer](../../Chapter-2.5/data-model-designer/SKILL.md) for schema design
