MCPInstall

qto-report

Generate Quantity Take-Off (QTO) reports from BIM/CAD data. Extract volumes, areas, counts by category. Group elements, apply calculation rules, and create cost estimates automatically.

Installs
121
Last 24h
—
Trending rank
—
Security
Not audited

Install

Works with every agent the skills CLI supports.

npx skills add https://github.com/datadrivenconstruction/ddc_skills_for_ai_agents_in_construction --skill qto-report
  1. 01

    Install

    Run the command in your project. The CLI asks which agents to install for.

  2. 02

    Describe the task

    Ask your agent for something this skill covers. It loads qto-report automatically when relevant.

  3. 03

    Review the result

    The agent follows the SKILL.md steps, scripts and references bundled with the skill.

SKILL.md

The instructions your agent reads when the skill is triggered.

Quantity Take-Off (QTO) Report Generation

Overview

Based on DDC methodology (Chapter 3.2), this skill automates the extraction and grouping of quantities from BIM/CAD data. QTO is the foundation for cost estimation, scheduling, and project planning in construction.

Book Reference: "Quantity Take-Off и автоматическое создание смет" / "QTO and Automated Estimates"

"QTO Quantity Take-Off: группировка данных по атрибутам позволяет автоматически извлекать объемы и количества из BIM-моделей для расчета стоимости." — DDC Book, Chapter 3.2

5D BIM Concept

The QTO process is central to 5D BIM:

  • 3D: Geometry (volume, area, length)
  • 4D: Time (schedule integration)
  • 5D: Cost (quantity × unit price)

Quick Start

import pandas as pd

# Load BIM element data
df = pd.read_csv("revit_export.csv")

# Generate QTO by category
qto = df.groupby('Category').agg({
    'Volume': 'sum',
    'Area': 'sum',
    'ElementId': 'count'
}).rename(columns={'ElementId': 'Count'})

# Calculate cost (if unit prices available)
qto['Unit_Price'] = [150, 80, 450, 200]  # $/m³
qto['Total_Cost'] = qto['Volume'] * qto['Unit_Price']

qto.to_excel("qto_report.xlsx")

Core QTO Functions

Basic QTO by Category

import pandas as pd

def generate_qto(df, group_by='Category'):
    """
    Generate Quantity Take-Off grouped by specified column

    Args:
        df: DataFrame with BIM elements
        group_by: Column(s) to group by

    Returns:
        QTO summary DataFrame
    """
    # Define aggregations based on available columns
    agg_dict = {}

    if 'Volume' in df.columns:
        agg_dict['Volume'] = 'sum'
    if 'Area' in df.columns:
        agg_dict['Area'] = 'sum'
    if 'Length' in df.columns:
        agg_dict['Length'] = 'sum'
    if 'Count' in df.columns:
        agg_dict['Count'] = 'sum'
    else:
        agg_dict['ElementId'] = 'count'

    qto = df.groupby(group_by).agg(agg_dict)

    if 'ElementId' in agg_dict:
        qto = qto.rename(columns={'ElementId': 'Count'})

    return qto.round(2)

# Usage
qto = generate_qto(df, group_by='Category')
print(qto)

Multi-Level QTO

def generate_multi_level_qto(df):
    """Generate QTO grouped by multiple levels"""
    qto = df.groupby(['Level', 'Category', 'Material']).agg({
        'Volume': ['sum', 'count'],
        'Area': 'sum'
    }).round(2)

    # Flatten column names
    qto.columns = ['Volume_m3', 'Element_Count', 'Area_m2']

    # Add percentages
    qto['Volume_Pct'] = (qto['Volume_m3'] /
                          qto['Volume_m3'].sum() * 100).round(1)

    return qto.sort_values('Volume_m3', ascending=False)

# Usage
qto = generate_multi_level_qto(df)
qto.to_excel("qto_multi_level.xlsx")

QTO with Pivot Table

def generate_qto_pivot(df, values='Volume', index='Level', columns='Category'):
    """Generate QTO as pivot table"""
    pivot = pd.pivot_table(
        df,
        values=values,
        index=index,
        columns=columns,
        aggfunc='sum',
        fill_value=0,
        margins=True,
        margins_name='TOTAL'
    ).round(2)

    return pivot

# Usage - Volume by Level and Category
qto_pivot = generate_qto_pivot(df, values='Volume')
qto_pivot.to_excel("qto_pivot.xlsx")

Cost Calculation from QTO

Apply Unit Prices

def calculate_cost_from_qto(qto_df, prices_df, quantity_col='Volume'):
    """
    Calculate costs by applying unit prices to quantities

    Args:
        qto_df: QTO DataFrame with quantities
        prices_df: DataFrame with Category and Unit_Price
        quantity_col: Column containing quantities
    """
    # Merge with prices
    result = qto_df.reset_index().merge(
        prices_df, on='Category', how='left'
    )

    # Calculate costs
    result['Total_Cost'] = result[quantity_col] * result['Unit_Price']
    result['Cost_Pct'] = (result['Total_Cost'] /
                          result['Total_Cost'].sum() * 100).round(1)

    # Summary
    grand_total = result['Total_Cost'].sum()
    print(f"Grand Total: ${grand_total:,.2f}")

    return result

# Unit prices database
prices = pd.DataFrame({
    'Category': ['Wall', 'Floor', 'Column', 'Beam', 'Foundation'],
    'Unit_Price': [150, 80, 450, 200, 120],  # $/m³
    'Unit': ['m³', 'm³', 'm³', 'm³', 'm³']
})

# Calculate
cost_estimate = calculate_cost_from_qto(qto, prices)
cost_estimate.to_excel("cost_estimate.xlsx", index=False)

Apply Rules from Excel

def apply_excel_rules(df, rules_path):
    """
    Apply calculation rules defined in Excel file

    Excel format:
    | Category | Formula_Type | Factor | Unit |
    | Wall     | volume       | 1.05   | m³   |
    | Floor    | area         | 1.10   | m²   |
    """
    rules = pd.read_excel(rules_path)

    results = []
    for _, rule in rules.iterrows():
        category = rule['Category']
        formula_type = rule['Formula_Type']
        factor = rule['Factor']

        category_data = df[df['Category'] == category].copy()

        if formula_type == 'volume':
            category_data['Quantity'] = category_data['Volume'] * factor
        elif formula_type == 'area':
            category_data['Quantity'] = category_data['Area'] * factor
        elif formula_type == 'length':
            category_data['Quantity'] = category_data['Length'] * factor
        elif formula_type == 'count':
            category_data['Quantity'] = category_data.groupby('Category').ngroup() + 1

        category_data['Unit'] = rule['Unit']
        results.append(category_data)

    return pd.concat(results, ignore_index=True)

# Usage
df_with_quantities = apply_excel_rules(df, "calculation_rules.xlsx")

BIM Data Extraction Patterns

From Revit Export (CSV)

def process_revit_export(csv_path):
    """Process standard Revit schedule export"""
    df = pd.read_csv(csv_path)

    # Standardize column names
    column_mapping = {
        'Family and Type': 'Type',
        'Volume': 'Volume',
        'Area': 'Area',
        'Count': 'Count',
        'Level': 'Level',
        'Category': 'Category'
    }

    df = df.rename(columns={
        k: v for k, v in column_mapping.items()
        if k in df.columns
    })

    # Convert volume from cubic feet to cubic meters (if needed)
    if 'Volume' in df.columns:
        # Revit exports in cubic feet by default
        df['Volume_m3'] = df['Volume'] * 0.0283168

    return df

# Usage
df = process_revit_export("revit_schedule.csv")
qto = generate_qto(df)

From IFC Export

# Using IfcOpenShell
import ifcopenshell
import pandas as pd

def extract_qto_from_ifc(ifc_path):
    """Extract quantities from IFC file"""
    ifc = ifcopenshell.open(ifc_path)

    elements = []
    for element in ifc.by_type("IfcBuildingElement"):
        # Get properties
        props = {
            'GlobalId': element.GlobalId,
            'Name': element.Name,
            'Type': element.is_a(),
            'Material': None,
            'Volume': None,
            'Area': None
        }

        # Extract quantities from property sets
        for definition in element.IsDefinedBy:
            if definition.is_a('IfcRelDefinesByProperties'):
                pset = definition.RelatingPropertyDefinition
                if pset.is_a('IfcElementQuantity'):
                    for qty in pset.Quantities:
                        if qty.is_a('IfcQuantityVolume'):
                            props['Volume'] = qty.VolumeValue
                        elif qty.is_a('IfcQuantityArea'):
                            props['Area'] = qty.AreaValue

        elements.append(props)

    return pd.DataFrame(elements)

# Usage
df = extract_qto_from_ifc("model.ifc")
qto = generate_qto(df, group_by='Type')

Advanced QTO Reports

Detailed Material Breakdown

def material_breakdown_qto(df):
    """Detailed breakdown by material type"""
    breakdown = df.groupby(['Category', 'Material', 'Type']).agg({
        'Volume': 'sum',
        'Area': 'sum',
        'ElementId': 'nunique'
    }).rename(columns={'ElementId': 'Unique_Elements'})

    # Add subtotals for each category
    category_totals = df.groupby('Category').agg({
        'Volume': 'sum',
        'Area': 'sum'
    })

    breakdown['Category_Volume_Pct'] = breakdown.apply(
        lambda row: (row['Volume'] /
                    category_totals.loc[row.name[0], 'Volume'] * 100),
        axis=1
    ).round(1)

    return breakdown

# Usage
material_qto = material_breakdown_qto(df)
material_qto.to_excel("material_breakdown.xlsx")

QTO with Waste Factors

def qto_with_waste(df, waste_factors):
    """
    Apply waste factors to quantities

    Args:
        waste_factors: dict like {'Concrete': 1.05, 'Steel': 1.03}
    """
    qto = df.groupby(['Category', 'Material']).agg({
        'Volume': 'sum'
    }).reset_index()

    # Apply waste factors
    qto['Waste_Factor'] = qto['Material'].map(waste_factors).fillna(1.0)
    qto['Net_Volume'] = qto['Volume']
    qto['Gross_Volume'] = qto['Volume'] * qto['Waste_Factor']
    qto['Waste_Volume'] = qto['Gross_Volume'] - qto['Net_Volume']

    return qto

# Usage
waste = {'Concrete': 1.05, 'Brick': 1.08, 'Steel': 1.03}
qto = qto_with_waste(df, waste)

QTO Comparison (Design vs As-Built)

def compare_qto(design_df, asbuilt_df, group_by='Category'):
    """Compare designed vs as-built quantities"""
    design_qto = design_df.groupby(group_by)['Volume'].sum()
    asbuilt_qto = asbuilt_df.groupby(group_by)['Volume'].sum()

    comparison = pd.DataFrame({
        'Design': design_qto,
        'AsBuilt': asbuilt_qto
    })

    comparison['Difference'] = comparison['AsBuilt'] - comparison['Design']
    comparison['Variance_%'] = (
        (comparison['AsBuilt'] - comparison['Design']) /
        comparison['Design'] * 100
    ).round(1)

    return comparison

# Usage
comparison = compare_qto(design_df, asbuilt_df)
print(comparison)

Export Functions

Export to Multiple Formats

def export_qto_report(qto_df, base_name, include_charts=True):
    """Export QTO to Excel with formatting and charts"""
    from openpyxl import Workbook
    from openpyxl.chart import BarChart, Reference

    # Excel with multiple sheets
    with pd.ExcelWriter(f"{base_name}.xlsx", engine='openpyxl') as writer:
        # Summary sheet
        qto_df.to_excel(writer, sheet_name='Summary')

        # Detailed data
        if hasattr(qto_df, 'reset_index'):
            qto_df.reset_index().to_excel(
                writer, sheet_name='Details', index=False
            )

    # CSV for integration
    qto_df.to_csv(f"{base_name}.csv")

    # JSON for API
    qto_df.reset_index().to_json(
        f"{base_name}.json", orient='records', indent=2
    )

    print(f"Exported: {base_name}.xlsx, .csv, .json")

# Usage
export_qto_report(qto, "project_qto")

Quick Reference

TaskCode
Basic QTOdf.groupby('Category')['Volume'].sum()
Multi-column QTOdf.groupby(['Level', 'Category']).agg({...})
Pivot QTOpd.pivot_table(df, values='Volume', ...)
Apply pricesqto.merge(prices, on='Category')
Calculate costdf['Cost'] = df['Volume'] * df['Unit_Price']
Add waste factordf['Gross'] = df['Net'] * waste_factor

Resources

Next Steps

  • See cost-estimation-resource for detailed cost calculations
  • See auto-estimate-generator for automated estimate creation
  • See gantt-chart for 4D scheduling integration
  • See co2-estimation for carbon footprint calculations

FAQ

What does the qto-report skill do?

Generate Quantity Take-Off (QTO) reports from BIM/CAD data. Extract volumes, areas, counts by category. Group elements, apply calculation rules, and create cost estimates automatically.

How do I install qto-report?

Run npx skills add https://github.com/datadrivenconstruction/ddc_skills_for_ai_agents_in_construction --skill qto-report in your project. The skills CLI detects the agents you use and copies the skill into the right folder. For Claude Code you can also place the skill folder at ~/.claude/skills/qto-report/ (personal) or .claude/skills/qto-report/ (project).

Which AI agents can use qto-report?

Any agent that supports the open Agent Skills format (a folder with a SKILL.md file), including Claude Code, Codex, Cursor, GitHub Copilot, Gemini CLI, Windsurf, Cline, OpenCode and more.

Is qto-report safe to install?

No partner security audit is available yet. Skills can run commands and scripts, so read the SKILL.md and any bundled files before installing.

Who maintains qto-report?

It is published by datadrivenconstruction in datadrivenconstruction/ddc_skills_for_ai_agents_in_construction. It has 121 recorded installs.

More from datadrivenconstruction/ddc_skills_for_ai_agents_in_construction

View all →

warranty-tracker

datadrivenconstruction/ddc_skills_for_ai_agents_in_construction

Track and manage construction warranties. Monitor expiration dates, claims, and manufacturer documentation.

AI & Agents996 installs

cad-to-data

datadrivenconstruction/ddc_skills_for_ai_agents_in_construction

Convert CAD/BIM files to structured data. Extract element data from Revit, IFC, DWG, DGN files.

AI & Agents333 installs

dwg-to-excel

datadrivenconstruction/ddc_skills_for_ai_agents_in_construction

Convert AutoCAD DWG files (1983-2026) to Excel databases using DwgExporter CLI. Extract layers, blocks, attributes, and geometry data without Autodesk licenses.

Data & Databases309 installs

drawing-analyzer

datadrivenconstruction/ddc_skills_for_ai_agents_in_construction

Open the skill to read its SKILL.md instructions.

AI & Agents259 installs

permit-tracking-automation

datadrivenconstruction/ddc_skills_for_ai_agents_in_construction

Automate construction permit tracking and management. Monitor application status, track renewal deadlines, manage document requirements, and integrate with municipal systems.

cost-estimation-resource

datadrivenconstruction/ddc_skills_for_ai_agents_in_construction

Calculate construction costs using resource-based method. Estimate project costs from work items, physical resource norms, and current prices.

AI & Agents193 installs

Popular docs & writing skills

View category →

grill-with-docs

mattpocock/skills

A relentless interview to sharpen a plan or design, which also creates docs (ADR's and glossary) as we go.

Docs & Writing1.2M installs

lark-markdown

open.feishu.cn

飞书 Markdown:查看、创建、上传、编辑和比较飞书中的原生 Markdown 文件。当用户要操作飞书 Markdown 文件,或比较其远端版本及本地草稿时使用。纯本地 Markdown 文件操作不触发本 skill。不负责将 Markdown 导入为飞书在线文档,也不负责文件搜索、权限、评论、移动、删除等云空间管理操作。

Docs & Writing778.7K installs

writing-beats

mattpocock/skills

Writing, exploit; assemble raw material into a journey of beats, grounding each term before a beat leans on it.

Docs & Writing490.7K installs

writing-shape

mattpocock/skills

Writing, exploit: shape raw material into an article, paragraph by paragraph.

Docs & Writing490.2K installs

writing-fragments

mattpocock/skills

Writing, explore: mine raw fragments, no structure yet.

Docs & Writing489.6K installs

writing-for-agents

mattpocock/skills

Writing documents for agents. Use when creating or editing skills, or modifying AGENTS.md or CLAUDE.md.

Docs & Writing479.3K installs