<< All versions
Skill v1.0.0
currentAutomated scan100/100datadrivenconstruction/ddc_skills_for_ai_agents_in_construction/csv-handler
──Details
PublishedSeptember 29, 2026 at 07:55 AM
Content Hashsha256:1d294c4b876fdc6a...
Git SHAce45bbfbdd63
──Files
Files (1 file, 8.8 KB)
SKILL.md8.8 KBactive
SKILL.md · 290 lines · 8.8 KB
version: "1.0.0" name: "csv-handler" description: "Handle CSV files from construction software exports. Auto-detect delimiters, encodings, and clean messy data." homepage: "https://datadrivenconstruction.io" metadata: {"openclaw": {"emoji": "🏷️", "os": ["darwin", "linux", "win32"], "homepage": "https://datadrivenconstruction.io", "requires": {"bins": ["python3"]}}}
CSV Handler for Construction Data
Overview
CSV is the universal exchange format in construction - from scheduling exports to cost databases. This skill handles encoding issues, delimiter detection, and data cleaning.
Python Implementation
python
import pandas as pdimport csvfrom typing import Dict, Any, List, Optional, Tuplefrom pathlib import Pathfrom dataclasses import dataclassimport chardet@dataclassclass CSVProfile:"""Profile of CSV file."""encoding: strdelimiter: strhas_header: boolrow_count: intcolumn_count: intcolumns: List[str]class ConstructionCSVHandler:"""Handle CSV files from construction software."""COMMON_DELIMITERS = [',', ';', '\t', '|']COMMON_ENCODINGS = ['utf-8', 'utf-8-sig', 'latin-1', 'cp1252', 'iso-8859-1']def __init__(self):self.last_profile: Optional[CSVProfile] = Nonedef detect_encoding(self, file_path: str) -> str:"""Detect file encoding."""with open(file_path, 'rb') as f:raw = f.read(10000)result = chardet.detect(raw)return result.get('encoding', 'utf-8') or 'utf-8'def detect_delimiter(self, file_path: str, encoding: str) -> str:"""Detect CSV delimiter."""with open(file_path, 'r', encoding=encoding, errors='replace') as f:sample = f.read(5000)# Count occurrencescounts = {d: sample.count(d) for d in self.COMMON_DELIMITERS}# Return most common that appears consistentlyif counts:return max(counts, key=counts.get)return ','def profile_csv(self, file_path: str) -> CSVProfile:"""Profile CSV file."""encoding = self.detect_encoding(file_path)delimiter = self.detect_delimiter(file_path, encoding)# Read sampledf = pd.read_csv(file_path, encoding=encoding, delimiter=delimiter,nrows=10, on_bad_lines='skip')has_header = not df.columns[0].replace('.', '').replace('-', '').isdigit()# Full row countwith open(file_path, 'r', encoding=encoding, errors='replace') as f:row_count = sum(1 for _ in f) - (1 if has_header else 0)profile = CSVProfile(encoding=encoding,delimiter=delimiter,has_header=has_header,row_count=row_count,column_count=len(df.columns),columns=list(df.columns))self.last_profile = profilereturn profiledef read_csv(self, file_path: str,encoding: Optional[str] = None,delimiter: Optional[str] = None,clean: bool = True) -> pd.DataFrame:"""Read CSV with auto-detection."""# Auto-detect if not providedif encoding is None:encoding = self.detect_encoding(file_path)if delimiter is None:delimiter = self.detect_delimiter(file_path, encoding)# Read with error handlingdf = pd.read_csv(file_path,encoding=encoding,delimiter=delimiter,on_bad_lines='skip',low_memory=False)if clean:df = self.clean_dataframe(df)return dfdef clean_dataframe(self, df: pd.DataFrame) -> pd.DataFrame:"""Clean construction CSV data."""# Clean column namesdf.columns = [self._clean_column_name(c) for c in df.columns]# Remove empty rows and columnsdf = df.dropna(how='all')df = df.dropna(axis=1, how='all')# Strip whitespace from stringsfor col in df.select_dtypes(include=['object']):df[col] = df[col].str.strip() if df[col].dtype == 'object' else df[col]return dfdef _clean_column_name(self, name: str) -> str:"""Clean column name."""if not isinstance(name, str):return str(name)# Remove special characters, replace spacesclean = name.strip().lower()clean = clean.replace(' ', '_').replace('-', '_')clean = ''.join(c for c in clean if c.isalnum() or c == '_')return cleandef merge_csvs(self, file_paths: List[str],on_column: Optional[str] = None) -> pd.DataFrame:"""Merge multiple CSV files."""dfs = []for path in file_paths:df = self.read_csv(path)df['_source_file'] = Path(path).namedfs.append(df)if not dfs:return pd.DataFrame()if on_column and on_column in dfs[0].columns:result = dfs[0]for df in dfs[1:]:result = pd.merge(result, df, on=on_column, how='outer')return resultreturn pd.concat(dfs, ignore_index=True)def split_csv(self, df: pd.DataFrame,group_column: str,output_dir: str) -> List[str]:"""Split CSV by column values."""output_path = Path(output_dir)output_path.mkdir(parents=True, exist_ok=True)files = []for value in df[group_column].unique():subset = df[df[group_column] == value]filename = f"{group_column}_{value}.csv"filepath = output_path / filenamesubset.to_csv(filepath, index=False)files.append(str(filepath))return filesdef convert_types(self, df: pd.DataFrame,type_map: Dict[str, str] = None) -> pd.DataFrame:"""Convert column types intelligently."""df = df.copy()if type_map:for col, dtype in type_map.items():if col in df.columns:try:df[col] = df[col].astype(dtype)except:passelse:# Auto-convertfor col in df.columns:# Try numerictry:df[col] = pd.to_numeric(df[col])continueexcept:pass# Try datetimetry:df[col] = pd.to_datetime(df[col])except:passreturn dfdef export_csv(self, df: pd.DataFrame,file_path: str,encoding: str = 'utf-8-sig',delimiter: str = ',') -> str:"""Export DataFrame to CSV."""df.to_csv(file_path, encoding=encoding, sep=delimiter, index=False)return file_path# Specialized handlersclass ScheduleCSVHandler(ConstructionCSVHandler):"""Handler for project schedule CSVs."""SCHEDULE_COLUMNS = ['task_id', 'task_name', 'start_date', 'end_date','duration', 'predecessors', 'resources']def parse_schedule(self, file_path: str) -> pd.DataFrame:"""Parse schedule CSV."""df = self.read_csv(file_path)# Convert date columnsfor col in df.columns:if 'date' in col.lower() or 'start' in col.lower() or 'end' in col.lower():try:df[col] = pd.to_datetime(df[col])except:passreturn dfclass CostCSVHandler(ConstructionCSVHandler):"""Handler for cost/estimate CSVs."""def parse_costs(self, file_path: str) -> pd.DataFrame:"""Parse cost CSV."""df = self.read_csv(file_path)# Find and convert numeric columnsfor col in df.columns:if any(word in col.lower() for word in ['cost', 'price', 'amount', 'total', 'qty', 'quantity']):df[col] = pd.to_numeric(df[col].replace(r'[\$,]', '', regex=True), errors='coerce')return df
Quick Start
python
handler = ConstructionCSVHandler()# Profile CSV firstprofile = handler.profile_csv("export.csv")print(f"Encoding: {profile.encoding}, Delimiter: '{profile.delimiter}'")# Read with auto-detectiondf = handler.read_csv("export.csv")print(f"Loaded {len(df)} rows, {len(df.columns)} columns")
Common Use Cases
1. Merge Multiple Exports
python
files = ["jan_export.csv", "feb_export.csv", "mar_export.csv"]merged = handler.merge_csvs(files)
2. Split by Category
python
handler.split_csv(df, group_column='category', output_dir='./split_files')
3. Schedule Import
python
schedule_handler = ScheduleCSVHandler()schedule = schedule_handler.parse_schedule("p6_export.csv")
Resources
- DDC Book: Chapter 2.1 - Structured Data