Install any skill in seconds. Free to start, no credit card required.
Get Started Free →Excel/spreadsheet processing for construction: estimates, schedules, tracking logs, quantity takeoffs. Formulas, formatting, analysis.
.claude/skills/datadrivenconstruction-xlsx-construction/SKILL.md| Test case | Without → With | Effect | Δ tokens | Δ turns |
|---|---|---|---|---|
| case-04 | ✗→✓ | ▲ Improved | 82% | 0% |
| case-05 | ✗→✓ | ▲ Improved | 56% | 0% |
| case-06 | ✗→✓ | ▲ Improved | 45% | 0% |
| case-07 | ✗→✓ | ▲ Improved | 100% | 0% |
| case-09 | ✗→✓ | ▲ Improved | 92% | 0% |
Adapted from Anthropic's XLSX skill for construction spreadsheet workflows.
pythonfrom openpyxl import Workbook from openpyxl.styles import Font, Alignment, Border, Side, PatternFill from openpyxl.utils import get_column_letter def create_estimate_template(output_path: str, project_name: str): """Create construction cost estimate template.""" wb = Workbook() ws = wb.active ws.title = "Cost Estimate" # Styles header_font = Font(bold=True, size=12) currency_format = '$#,##0.00' thin_border = Border( left=Side(style='thin'), right=Side(style='thin'), top=Side(style='thin'), bottom=Side(style='thin') ) header_fill = PatternFill(start_color="4472C4", end_color="4472C4", fill_type="solid") # Project header ws['A1'] = "CONSTRUCTION COST ESTIMATE" ws['A1'].font = Font(bold=True, size=16) ws['A2'] = f"Project: {project_name}" ws['A3'] = "Date:" ws['B3'] = "=TODAY()" # Column headers (row 5) headers = ['CSI Code', 'Description', 'Quantity', 'Unit', 'Unit Cost', 'Labor', 'Material', 'Equipment', 'Total'] for col, header in enumerate(headers, 1): cell = ws.cell(row=5, column=col, value=header) cell.font = Font(bold=True, color="FFFFFF") cell.fill = header_fill cell.border = thin_border cell.alignment = Alignment(horizontal='center') # Set column widths widths = [12, 40, 10, 8, 12, 12, 12, 12, 14] for i, width in enumerate(widths, 1): ws.column_dimensions[get_column_letter(i)].width = width # Sample data rows with formulas for row in range(6, 26): # 20 empty rows # Total formula: Labor + Material + Equipment ws.cell(row=row, column=9, value=f"=SUM(F{row}:H{row})") # Apply borders for col in range(1, 10): ws.cell(row=row, column=col).border = thin_border # Currency formatting for col in [5, 6, 7, 8, 9]: # Cost columns for row in range(6, 26): ws.cell(row=row, column=col).number_format = currency_format # Subtotals section ws['G27'] = "SUBTOTAL" ws['I27'] = "=SUM(I6:I25)" ws['I27'].number_format = currency_format ws['G28'] = "Contingency (10%)" ws['I28'] = "=I27*0.10" ws['G29'] = "TOTAL" ws['I29'] = "=I27+I28" ws['I29'].font = Font(bold=True, size=14) wb.save(output_path) return output_path
pythondef create_schedule_tracker(output_path: str, tasks: list): """Create construction schedule tracking spreadsheet.""" wb = Workbook() ws = wb.active ws.title = "Schedule" # Headers headers = ['ID', 'Task', 'Start Date', 'End Date', 'Duration', 'Progress', 'Status', 'Predecessor', 'Notes'] # Status dropdown options from openpyxl.worksheet.datavalidation import DataValidation status_dv = DataValidation( type="list", formula1='"Not Started,In Progress,Complete,On Hold,Delayed"', allow_blank=True ) ws.add_data_validation(status_dv) # Header row for col, header in enumerate(headers, 1): cell = ws.cell(row=1, column=col, value=header) cell.font = Font(bold=True) # Add tasks with formulas for i, task in enumerate(tasks, 2): ws.cell(row=i, column=1, value=task.get('id', i-1)) ws.cell(row=i, column=2, value=task.get('name', '')) ws.cell(row=i, column=3, value=task.get('start', '')) ws.cell(row=i, column=4, value=task.get('end', '')) # Duration formula ws.cell(row=i, column=5, value=f"=D{i}-C{i}") # Progress bar (0-100%) ws.cell(row=i, column=6, value=task.get('progress', 0)) ws.cell(row=i, column=6).number_format = '0%' # Status with validation status_cell = ws.cell(row=i, column=7, value=task.get('status', 'Not Started')) status_dv.add(status_cell) wb.save(output_path) return output_path
pythondef create_rfi_log(output_path: str): """Create RFI tracking log.""" wb = Workbook() ws = wb.active ws.title = "RFI Log" headers = [ 'RFI #', 'Date Submitted', 'Subject', 'Spec Section', 'Drawing Ref', 'Submitted By', 'Assigned To', 'Response Due', 'Date Responded', 'Status', 'Days Open' ] for col, header in enumerate(headers, 1): ws.cell(row=1, column=col, value=header) # Days Open formula (in column K) for row in range(2, 100): ws.cell(row=row, column=11, value=f'=IF(I{row}="",TODAY()-B{row},I{row}-B{row})') # Conditional formatting for overdue items from openpyxl.formatting.rule import FormulaRule from openpyxl.styles import PatternFill red_fill = PatternFill(bgColor="FFC7CE") ws.conditional_formatting.add( 'K2:K100', FormulaRule(formula=['K2>7'], fill=red_fill) ) wb.save(output_path) return output_path
pythonimport pandas as pd def process_qto_from_bim(bim_xlsx: str, output_path: str): """Process BIM export for quantity takeoff.""" # Read BIM export df = pd.read_excel(bim_xlsx) # Group by category and type qto = df.groupby(['Category', 'Type']).agg({ 'Volume': 'sum', 'Area': 'sum', 'Length': 'sum', 'Count': 'count' }).reset_index() # Rename columns for clarity qto.columns = ['Category', 'Type', 'Total Volume (m³)', 'Total Area (m²)', 'Total Length (m)', 'Element Count'] # Create workbook with formatting with pd.ExcelWriter(output_path, engine='openpyxl') as writer: qto.to_excel(writer, sheet_name='QTO Summary', index=False) # Get workbook to apply formatting wb = writer.book ws = wb['QTO Summary'] # Format numeric columns for col in ['C', 'D', 'E']: for row in range(2, len(qto) + 2): ws[f'{col}{row}'].number_format = '#,##0.00' return output_path
python# Add error handling to formulas formula_with_error_check = "=IFERROR(B5/C5, 0)"
python# Convert BIM export to estimate from ddc_toolkit import RevitExporter, CWICRSemanticSearch # Export BIM data exporter = RevitExporter() bim_data = exporter.read_elements("project.xlsx") # Process QTO qto = process_qto_from_bim("project.xlsx", "qto_output.xlsx") # Match to cost database search = CWICRSemanticSearch() for category in bim_data['Category'].unique(): prices = search.search_work_items(category) # Apply prices to QTO...
bashpip install openpyxl pandas xlrd
| Case | Status | Duration (ms) | Turns | Tokens | Tool calls | ||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Without | With | Δ | Without | With | Δ | Without | With | Δ | Without | With | Δ | ||
case-03 | pass→pass | 20,122 | 15,810 | -21% | 1 | 1 | 0% | 4,302 | 5,614 | +30% | 0 | 0 | — |
case-01 | fail→fail | 30,069 | 23,529 | -22% | 1 | 1 | 0% | 6,221 | 7,193 | +16% | 0 | 0 | — |
case-02 | fail→fail | 28,862 | 25,954 | -10% | 1 | 1 | 0% | 6,214 | 8,101 | +30% | 0 | 0 | — |
case-04 | fail→pass | 14,387 | 13,996 | -3% | 1 | 1 | 0% | 2,827 | 5,157 | +82% | 0 | 0 | — |
case-05 | fail→pass | 9,862 | 2,598 | -74% | 1 | 1 | 0% | 1,837 | 2,858 | +56% | 0 | 0 | — |
case-06 | fail→pass | 13,167 | 7,540 | -43% | 1 | 1 | 0% | 2,577 | 3,734 | +45% | 0 | 0 | — |
case-07 | fail→pass | 16,442 | 14,995 | -9% | 1 | 1 | 0% | 2,442 | 4,893 | +100% | 0 | 0 | — |
case-08 | pass→pass | 16,433 | 11,946 | -27% | 1 | 1 | 0% | 2,725 | 4,250 | +56% | 0 | 0 | — |
case-09 | fail→pass | 9,228 | 6,035 | -35% | 1 | 1 | 0% | 1,861 | 3,569 | +92% | 0 | 0 | — |
case-10 | pass→pass | 12,463 | 15,870 | +27% | 1 | 1 | 0% | 2,160 | 4,979 | +131% | 0 | 0 | — |
case-11 | fail→pass | 13,933 | 15,761 | +13% | 1 | 1 | 0% | 2,618 | 5,402 | +106% | 0 | 0 | — |
case-12 | fail→pass | 11,281 | 5,638 | -50% | 1 | 1 | 0% | 1,989 | 3,362 | +69% | 0 | 0 | — |
case-13 | fail→pass | 6,692 | 4,807 | -28% | 1 | 1 | 0% | 1,217 | 3,236 | +166% | 0 | 0 | — |
case-19 | fail→pass | 7,546 | 2,676 | -65% | 1 | 1 | 0% | 1,227 | 2,824 | +130% | 0 | 0 | — |
case-14 | fail→pass | 11,034 | 2,382 | -78% | 1 | 1 | 0% | 1,933 | 2,726 | +41% | 0 | 0 | — |
case-15 | fail→pass | 14,782 | 13,478 | -9% | 1 | 1 | 0% | 2,299 | 4,451 | +94% | 0 | 0 | — |
case-16 | pass→pass | 9,236 | 3,570 | -61% | 1 | 1 | 0% | 1,564 | 2,913 | +86% | 0 | 0 | — |
case-17 | fail→pass | 8,868 | 1,950 | -78% | 1 | 1 | 0% | 1,535 | 2,649 | +73% | 0 | 0 | — |
case-18 | pass→pass | 6,021 | 4,507 | -25% | 1 | 1 | 0% | 1,088 | 3,157 | +190% | 0 | 0 | — |
case-20 | pass→pass | 10,607 | 9,869 | -7% | 1 | 1 | 0% | 1,937 | 4,268 | +120% | 0 | 0 | — |
case-21 | pass→fail | 17,703 | 19,339 | +9% | 1 | 1 | 0% | 3,321 | 6,055 | +82% | 0 | 0 | — |
case-22 | pass→pass | 23,617 | 29,066 | +23% | 1 | 1 | 0% | 4,954 | 8,508 | +72% | 0 | 0 | — |
DecimalAI ran this skill against gemini-3.6-flash twice over the same eval suite — once with the skill loaded and once without — and compared the two runs case by case. 22 cases were attempted. The headline lift of +50 percentage points is the difference between those two pass rates over the 22 comparable cases. 1 case got worse with the skill loaded, and it is included in that figure.
Without the skill loaded, the model failed this case. With it loaded, the same prompt on the same model passed. This is one improved case from the latest verified run; every case, including any that regressed, is in the table above.
Other measured skills in the registry, with their headline benchmark lift.