| name | as-built-documentation |
| description | Automate as-built documentation and digital handover for construction. Compile project records, generate O&M manuals, create asset databases, and ensure complete project closeout. |
As-Built Documentation
Overview
This skill implements automated as-built documentation and digital handover for construction projects. Compile accurate records, generate operation manuals, and ensure complete documentation for facility management.
Capabilities:
- As-built drawing management
- O&M manual generation
- Asset data compilation
- Warranty tracking
- Document organization
- BIM-to-FM handover
Quick Start
from dataclasses import dataclass, field
from datetime import date, datetime
from typing import List, Dict, Optional
from enum import Enum
class DocumentType(Enum):
DRAWING = "drawing"
SPECIFICATION = "specification"
SUBMITTAL = "submittal"
WARRANTY = "warranty"
CERTIFICATE = "certificate"
MANUAL = "manual"
TEST_REPORT = "test_report"
COMMISSIONING = "commissioning"
PHOTO = "photo"
class DocumentStatus(Enum):
DRAFT = "draft"
REVIEWED = "reviewed"
APPROVED = "approved"
AS_BUILT = "as_built"
FINAL = "final"
@dataclass
class HandoverDocument:
doc_id: str
doc_type: DocumentType
title: str
file_path: str
version: str
status: DocumentStatus
system: str
uploaded_date: date
approved_by: str = ""
@dataclass
class AssetRecord:
asset_id: str
asset_name: str
asset_type: str
manufacturer: str
model: str
serial_number: str
location: str
install_date: date
warranty_end: date
documents: List[str] = field(default_factory=list)
def check_handover_completeness(documents: List[HandoverDocument],
required_types: List[DocumentType]) -> Dict:
"""Check if all required documents are present"""
present_types = {doc.doc_type for doc in documents if doc.status == DocumentStatus.FINAL}
missing = set(required_types) - present_types
return {
'complete': len(missing) == 0,
'total_required': len(required_types),
'total_present': len(present_types),
'missing_types': [t.value for t in missing]
}
documents = [
HandoverDocument("DOC-001", DocumentType.DRAWING, "Floor Plans As-Built",
"/docs/floorplans.pdf", "3.0", DocumentStatus.FINAL, "Architecture", date.today()),
HandoverDocument("DOC-002", DocumentType.MANUAL, "HVAC O&M Manual",
"/docs/hvac_om.pdf", "1.0", DocumentStatus.FINAL, "HVAC", date.today()),
]
required = [DocumentType.DRAWING, DocumentType.MANUAL, DocumentType.WARRANTY]
status = check_handover_completeness(documents, required)
print(f"Complete: {status['complete']}, Missing: {status['missing_types']}")
Comprehensive Handover System
Document Management
from dataclasses import dataclass, field
from datetime import date, datetime, timedelta
from typing import List, Dict, Optional, Tuple
from enum import Enum
import uuid
import os
class BuildingSystem(Enum):
ARCHITECTURAL = "architectural"
STRUCTURAL = "structural"
MECHANICAL = "mechanical"
ELECTRICAL = "electrical"
PLUMBING = "plumbing"
FIRE_PROTECTION = "fire_protection"
CONTROLS = "controls"
ELEVATOR = "elevator"
CIVIL = "civil"
LANDSCAPE = "landscape"
@dataclass
class DocumentRequirement:
requirement_id: str
doc_type: DocumentType
system: BuildingSystem
description: str
is_mandatory: bool = True
quantity: int = 1
format_requirements: str = "PDF"
@dataclass
class ProjectDocument:
doc_id: str
requirement_id: Optional[str]
doc_type: DocumentType
system: BuildingSystem
title: str
description:
file_path:
file_size_mb:
:
version:
revision_date: date
author:
reviewer:
status: DocumentStatus
metadata: = field(default_factory=)
related_assets: [] = field(default_factory=)
supersedes: [] =
:
STANDARD_REQUIREMENTS = {
BuildingSystem.MECHANICAL: [
DocumentRequirement(, DocumentType.DRAWING, BuildingSystem.MECHANICAL,
),
DocumentRequirement(, DocumentType.MANUAL, BuildingSystem.MECHANICAL,
),
DocumentRequirement(, DocumentType.WARRANTY, BuildingSystem.MECHANICAL,
),
DocumentRequirement(, DocumentType.TEST_REPORT, BuildingSystem.MECHANICAL,
),
DocumentRequirement(, DocumentType.COMMISSIONING, BuildingSystem.MECHANICAL,
),
],
BuildingSystem.ELECTRICAL: [
DocumentRequirement(, DocumentType.DRAWING, BuildingSystem.ELECTRICAL,
),
DocumentRequirement(, DocumentType.MANUAL, BuildingSystem.ELECTRICAL,
),
DocumentRequirement(, DocumentType.WARRANTY, BuildingSystem.ELECTRICAL,
),
DocumentRequirement(, DocumentType.TEST_REPORT, BuildingSystem.ELECTRICAL,
),
DocumentRequirement(, DocumentType.CERTIFICATE, BuildingSystem.ELECTRICAL,
),
],
BuildingSystem.PLUMBING: [
DocumentRequirement(, DocumentType.DRAWING, BuildingSystem.PLUMBING,
),
DocumentRequirement(, DocumentType.MANUAL, BuildingSystem.PLUMBING,
),
DocumentRequirement(, DocumentType.TEST_REPORT, BuildingSystem.PLUMBING,
),
],
BuildingSystem.FIRE_PROTECTION: [
DocumentRequirement(, DocumentType.DRAWING, BuildingSystem.FIRE_PROTECTION,
),
DocumentRequirement(, DocumentType.MANUAL, BuildingSystem.FIRE_PROTECTION,
),
DocumentRequirement(, DocumentType.CERTIFICATE, BuildingSystem.FIRE_PROTECTION,
),
DocumentRequirement(, DocumentType.TEST_REPORT, BuildingSystem.FIRE_PROTECTION,
),
],
BuildingSystem.ARCHITECTURAL: [
DocumentRequirement(, DocumentType.DRAWING, BuildingSystem.ARCHITECTURAL,
),
DocumentRequirement(, DocumentType.SPECIFICATION, BuildingSystem.ARCHITECTURAL,
),
DocumentRequirement(, DocumentType.WARRANTY, BuildingSystem.ARCHITECTURAL,
),
]
}
():
.project_id = project_id
.project_name = project_name
.requirements: [, DocumentRequirement] = {}
.documents: [, ProjectDocument] = {}
._load_standard_requirements()
():
system, reqs .STANDARD_REQUIREMENTS.items():
req reqs:
.requirements[req.requirement_id] = req
():
.requirements[requirement.requirement_id] = requirement
() -> ProjectDocument:
doc_id =
file_size = os.path.getsize(file_path) / ( * ) os.path.exists(file_path)
file_format = os.path.splitext(file_path)[].upper().replace(, )
doc = ProjectDocument(
doc_id=doc_id,
requirement_id=requirement_id,
doc_type=doc_type,
system=system,
title=title,
description=,
file_path=file_path,
file_size_mb=file_size,
=file_format,
version=,
revision_date=date.today(),
author=author,
reviewer=,
status=DocumentStatus.DRAFT,
related_assets=related_assets []
)
.documents[doc_id] = doc
doc
() -> :
doc = .documents.get(doc_id)
doc:
doc.status = DocumentStatus.APPROVED
doc.reviewer = reviewer
() -> :
doc = .documents.get(doc_id)
doc doc.status == DocumentStatus.APPROVED:
doc.status = DocumentStatus.FINAL
() -> :
summary = {
: (.requirements),
: (.documents),
: {},
: {},
: {}
}
doc .documents.values():
status = doc.status.value
summary[][status] = summary[].get(status, ) +
system = doc.system.value
system summary[]:
summary[][system] = {: , : }
summary[][system][] +=
doc.status == DocumentStatus.FINAL:
summary[][system][] +=
req_id, req .requirements.items():
matching_docs = [
d d .documents.values()
d.requirement_id == req_id d.status == DocumentStatus.FINAL
]
summary[][req_id] = {
: req.description,
: req.system.value,
: req.quantity,
: (matching_docs),
: (matching_docs) >= req.quantity
}
summary
() -> [DocumentRequirement]:
missing = []
req_id, req .requirements.items():
req.is_mandatory:
matching_docs = [
d d .documents.values()
d.requirement_id == req_id d.status == DocumentStatus.FINAL
]
(matching_docs) < req.quantity:
missing.append(req)
missing
Asset Registry
from datetime import date, timedelta
from typing import List, Dict, Optional
@dataclass
class MaintenanceSchedule:
schedule_id: str
task_description: str
frequency: str
next_due: date
responsible_party: str
estimated_duration_hours: float
@dataclass
class AssetDetails:
asset_id: str
asset_tag: str
asset_name: str
asset_type: str
category: str
manufacturer: str
model_number: str
serial_number: str
part_number: str = ""
building: str = ""
floor: str = ""
room: str = ""
coordinates: Tuple[float, float, float] = (0, 0, 0)
install_date: date = None
installed_by: str = ""
cost: =
warranty_start: date =
warranty_end: date =
warranty_provider: =
warranty_terms: =
specifications: = field(default_factory=)
manuals: [] = field(default_factory=)
drawings: [] = field(default_factory=)
photos: [] = field(default_factory=)
maintenance_schedules: [MaintenanceSchedule] = field(default_factory=)
service_contacts: [] = field(default_factory=)
ifc_guid: =
revit_element_id: =
:
():
.project_id = project_id
.assets: [, AssetDetails] = {}
():
.assets[asset.asset_id] = asset
():
item bim_data:
asset = AssetDetails(
asset_id=,
asset_tag=item.get(, ),
asset_name=item.get(, ),
asset_type=item.get(, ),
category=item.get(, ),
manufacturer=item.get(, ),
model_number=item.get(, ),
serial_number=item.get(, ),
building=item.get(, ),
floor=item.get(, ),
room=item.get(, ),
ifc_guid=item.get(, ),
revit_element_id=item.get(, )
)
key, value item.get(, {}).items():
asset.specifications[key] = value
.assets[asset.asset_id] = asset
():
asset = .assets.get(asset_id)
asset:
asset.warranty_start = start_date
asset.warranty_end = start_date + timedelta(days= * duration_years)
asset.warranty_provider = provider
asset.warranty_terms = terms
():
asset = .assets.get(asset_id)
asset:
asset.maintenance_schedules.append(schedule)
() -> :
today = date.today()
report = {
: (.assets),
: ,
: ,
: [],
: []
}
asset .assets.values():
asset.warranty_end:
report[] +=
asset.warranty_end >= today:
report[] +=
days_remaining = (asset.warranty_end - today).days
days_remaining <= :
report[].append({
: asset.asset_id,
: asset.asset_name,
: asset.warranty_end.isoformat(),
: days_remaining
})
:
report[].append({
: asset.asset_id,
: asset.asset_name,
: asset.warranty_end.isoformat()
})
report
() -> []:
export_data = []
asset .assets.values():
export_data.append({
: asset.asset_id,
: asset.asset_tag,
: asset.asset_name,
: asset.asset_type,
: asset.category,
: asset.manufacturer,
: asset.model_number,
: asset.serial_number,
: ,
: asset.install_date.isoformat() asset.install_date ,
: asset.warranty_end.isoformat() asset.warranty_end ,
: asset.specifications
})
export_data
O&M Manual Generator
class OMManualGenerator:
"""Generate O&M manuals from project data"""
def __init__(self, doc_manager: DocumentManager, asset_registry: AssetRegistry):
self.docs = doc_manager
self.assets = asset_registry
def generate_system_manual(self, system: BuildingSystem,
output_path: str) -> Dict:
"""Generate O&M manual for building system"""
system_docs = [
d for d in self.docs.documents.values()
if d.system == system and d.status == DocumentStatus.FINAL
]
system_assets = [
a for a in self.assets.assets.values()
if a.asset_type.lower() in system.value.lower() or
system.value.lower() in a.category.lower()
]
manual_content = {
'system': system.value,
'generated_date': date.today().isoformat(),
'sections': []
}
manual_content['sections'].append({
'title': 'System Overview',
'content': f"Overview of {system.value} system",
'subsections': []
})
equipment_list = []
asset system_assets:
equipment_list.append({
: asset.asset_tag,
: asset.asset_name,
: asset.manufacturer,
: asset.model_number,
:
})
manual_content[].append({
: ,
: equipment_list
})
manual_content[].append({
: ,
: ,
: [d.doc_id d system_docs d.doc_type == DocumentType.MANUAL]
})
maintenance_tasks = []
asset system_assets:
schedule asset.maintenance_schedules:
maintenance_tasks.append({
: asset.asset_name,
: schedule.task_description,
: schedule.frequency,
: schedule.estimated_duration_hours
})
manual_content[].append({
: ,
: maintenance_tasks
})
warranties = []
asset system_assets:
asset.warranty_end:
warranties.append({
: asset.asset_name,
: asset.warranty_provider,
: asset.warranty_end.isoformat(),
: asset.warranty_terms
})
manual_content[].append({
: ,
: warranties
})
contacts = []
asset system_assets:
contacts.extend(asset.service_contacts)
manual_content[].append({
: ,
: ({c[]: c c contacts}.values())
})
manual_content[].append({
: ,
: [
{: d.doc_id, : d.title, : d.doc_type.value}
d system_docs
]
})
manual_content
() -> :
building_manual = {
: .docs.project_name,
: date.today().isoformat(),
: {}
}
system BuildingSystem:
building_manual[][system.value] = .generate_system_manual(
system, output_path
)
building_manual
Handover Checklist
def generate_handover_checklist(doc_manager: DocumentManager,
asset_registry: AssetRegistry) -> Dict:
"""Generate comprehensive handover checklist"""
checklist = {
'generated': date.today().isoformat(),
'project': doc_manager.project_name,
'overall_status': 'incomplete',
'categories': []
}
doc_status = doc_manager.get_status_summary()
missing_docs = doc_manager.get_missing_documents()
checklist['categories'].append({
'name': 'Documents',
'complete': len(missing_docs) == 0,
'items': [
{
'item': req.description,
'system': req.system.value,
'status': 'complete' if req.requirement_id not in [m.requirement_id for m in missing_docs] else 'missing'
}
for req in doc_manager.requirements.values()
]
})
warranty_report = asset_registry.get_warranty_report()
checklist['categories'].append({
'name': 'Asset Registration',
'complete': warranty_report['total_assets'] > 0,
'items': [
{'item': 'All major equipment registered', : warranty_report[] > },
{: , : warranty_report[] > },
{: , : (a.maintenance_schedules a asset_registry.assets.values()) }
]
})
checklist[].append({
: ,
: ,
: [
{: , : },
{: , : },
{: , : }
]
})
all_complete = (cat[] cat checklist[])
checklist[] = all_complete
checklist
Quick Reference
| Document Type | Typical Source | When Required |
|---|
| As-Built Drawings | Contractor | Substantial completion |
| O&M Manuals | Manufacturer/Contractor | Before training |
| Warranties | Manufacturers | At installation |
| Test Reports | Testing agency | After testing |
| Certificates | Authorities | Final inspection |
| Training Records | Contractor | Before handover |
Resources
- COBie Standard: Construction Operations Building Information Exchange
- ASHRAE Guideline 0: Commissioning process
- IFMA: Facility Management resources
- DDC Website: https://datadrivenconstruction.io
Next Steps
- See
bim-validation-pipeline for BIM handover
- See
document-classification-nlp for document processing
- See
digital-twin-sync for FM integration