Files
IfcOpenShell/src/ifcblenderexport/cobie.py
T

1115 lines
59 KiB
Python
Raw Normal View History

import time
import datetime
import ifcopenshell
import logging
import pprint
import csv
from openpyxl import Workbook
from openpyxl.styles import PatternFill
class IfcCobieParser():
def __init__(self, logger):
self.logger = logger
self.file = None
self.contacts = {}
self.facilities = {}
self.floors = {}
self.spaces = {}
self.zones = {}
self.types = {}
self.components = {}
self.systems = {}
self.assemblies = {}
self.connections = {}
self.spares = {}
self.resources = {}
self.jobs = {}
self.impacts = {}
self.documents = {}
self.attributes = {}
self.coordinates = {}
self.issues = {}
self.type_assets = [
'IfcDoorStyle',
'IfcBuildingElementProxyType',
'IfcChimneyType',
'IfcCoveringType',
'IfcDoorType',
'IfcFootingType',
'IfcPileType',
'IfcRoofType',
'IfcShadingDeviceType',
'IfcWindowType',
'IfcDistributionControlElementType',
'IfcDistributionChamberElementType',
'IfcEnergyConversionDeviceType',
'IfcFlowControllerType',
'IfcFlowMovingDeviceType',
'IfcFlowStorageDeviceType',
'IfcFlowTerminalType',
'IfcFlowTreatmentDeviceType',
'IfcElementAssemblyType',
'IfcBuildingElementPartType',
'IfcDiscreteAccessoryType',
'IfcMechanicalFastenerType',
'IfcReinforcingElementType',
'IfcVibrationIsolatorType',
'IfcFurnishingElementType',
'IfcGeographicElementType',
'IfcTransportElementType',
'IfcSpatialZoneType',
'IfcWindowStyle',
]
self.component_assets = [
'IfcBuildingElementProxy',
'IfcChimney',
'IfcCovering',
'IfcDoor',
'IfcShadingDevice',
'IfcWindow',
'IfcDistributionControlElement',
'IfcDistributionChamberElement',
'IfcEnergyConversionDevice',
'IfcFlowController',
'IfcFlowMovingDevice',
'IfcFlowStorageDevice',
'IfcFlowTerminal',
'IfcFlowTreatmentDevice',
'IfcDiscreteAccessory',
'IfcTendon',
'IfcTendonAnchor',
'IfcVibrationIsolator',
'IfcFurnishingElement',
'IfcGeographicElement',
'IfcTransportElement',
]
self.picklists = {
'Category-Role': [],
'Category-Facility': [],
'FloorType': [],
'Category-Space': [],
'ZoneType': [],
'Category-Product': [],
'AssetType': [],
'DurationUnit': ['day'], # See note about hardcoded day below
'Category-Element': [],
'SpareType': [],
'ApprovalBy': [],
'StageType': [],
'objType': []
}
self.default_date = (datetime.datetime(1970, 1, 1) + datetime.timedelta(seconds = -2177452801)).isoformat()
def parse(self):
self.file = ifcopenshell.open('C:/cygwin64/home/moud308/Projects/ifc2cobie/arch.ifc')
self.get_contacts()
self.get_facilities()
self.get_floors()
self.get_spaces()
self.get_zones()
self.get_types()
self.get_components()
self.get_systems()
self.get_assemblies()
self.get_connections()
self.get_spares()
self.get_resources()
self.get_jobs()
self.get_impacts()
self.get_documents()
self.get_attributes()
self.get_coordinates()
self.get_issues()
def get_contacts(self):
histories = self.file.by_type('IfcOwnerHistory')
for history in histories:
email = self.get_email_from_history(history)
if not email:
continue
postal_address = self.get_postal_address_from_history(history)
self.contacts[email] = {
'CreatedBy': email,
'CreatedOn': datetime.datetime.fromtimestamp(history.CreationDate).isoformat() if history.CreationDate else datetime.datetime.now().isoformat(),
'Category': self.get_category_from_history(history),
'Company': history.OwningUser.TheOrganization.Name or 'n/a',
'Phone': self.get_phone_from_history(history),
'ExtSystem': self.get_ext_system_from_history(history),
'ExtObject': self.get_ext_object_from_history(history),
'ExtIdentifier': history.OwningUser.ThePerson.Id if self.file.schema == 'IFC2X3' else history.OwningUser.ThePerson.Identification,
'Department': self.get_department_from_history(history),
'OrganizationCode': (history.OwningUser.TheOrganization.Id or 'n/a') if self.file.schema == 'IFC2X3' else (history.OwningUser.TheOrganization.Identification or 'n/a'),
'GivenName': self.get_name_from_person(history.OwningUser.ThePerson, 'GivenName'),
'FamilyName': self.get_name_from_person(history.OwningUser.ThePerson, 'FamilyName'),
'Street': self.get_attribute_from_address(postal_address, 'AddressLines'),
'PostalBox': self.get_attribute_from_address(postal_address, 'PostalBox'),
'Town': self.get_attribute_from_address(postal_address, 'Town'),
'StateRegion': self.get_attribute_from_address(postal_address, 'Region'),
'PostalCode': self.get_attribute_from_address(postal_address, 'PostalCode'),
'Country': self.get_attribute_from_address(postal_address, 'Country')
}
def get_facilities(self):
buildings = self.file.by_type('IfcBuilding')
for building in buildings:
building_name = self.get_object_name(building)
units = self.get_units_from_building(building)
self.facilities[building_name] = {
'CreatedBy': self.get_email_from_history(building.OwnerHistory),
'CreatedOn': self.get_created_on_from_history(building.OwnerHistory),
'Category': self.get_category_from_object(building, 'Category-Facility'),
'ProjectName': self.get_project_name_from_building(building),
'SiteName': self.get_site_name_from_building(building),
'LinearUnits': self.get_unit_type_from_units(units, 'LENGTHUNIT'),
'AreaUnits': self.get_unit_type_from_units(units, 'AREAUNIT'),
'VolumeUnits': self.get_unit_type_from_units(units, 'VOLUMEUNIT'),
'CostUnit': self.get_monetary_unit_from_units(units),
'AreaMeasurement': self.get_area_measurement_from_building(building),
'ExternalSystem': self.get_ext_system_from_history(building.OwnerHistory),
'ExternalProjectObject': self.get_ext_project_object(),
'ExternalProjectIdentifier': self.get_project_globalid_from_building(building),
'ExternalSiteObject': self.get_ext_site_object(),
'ExternalSiteIdentifier': self.get_site_globalid_from_building(building),
'ExternalFacilityObject': self.get_ext_object(building),
'ExternalFacilityIdentifier': building.GlobalId,
'Description': self.get_object_attribute(building, 'Description', default='n/a'),
'ProjectDescription': self.get_object_attribute(self.get_parent_spatial_element(building, 'IfcProject'), 'Description', default='n/a'),
'SiteDescription': self.get_object_attribute(self.get_parent_spatial_element(building, 'IfcSite'), 'Description', default='n/a'),
'Phase': self.get_object_attribute(self.get_parent_spatial_element(building, 'IfcProject'), 'Phase', default='n/a')
}
def get_floors(self):
storeys = self.file.by_type('IfcBuildingStorey')
for storey in storeys:
storey_name = self.get_object_name(storey)
self.floors[storey_name] = {
'CreatedBy': self.get_email_from_history(storey.OwnerHistory),
'CreatedOn': self.get_created_on_from_history(storey.OwnerHistory),
'Category': self.get_category_from_object(storey, 'FloorType'),
'ExtSystem': self.get_ext_system_from_history(storey.OwnerHistory),
'ExtObject': self.get_ext_object(storey),
'ExtIdentifier': storey.GlobalId,
'Description': self.get_object_attribute(storey, 'Description', default='n/a'),
'Elevation': self.get_object_attribute(storey, 'Elevation', default='n/a'),
'Height': self.get_height_from_storey(storey)
}
def get_spaces(self):
spaces = self.file.by_type('IfcSpace')
for space in spaces:
space_name = self.get_object_name(space)
self.spaces[space_name] = {
'CreatedBy': self.get_email_from_history(space.OwnerHistory),
'CreatedOn': self.get_created_on_from_history(space.OwnerHistory),
'Category': self.get_category_from_object(space, 'Category-Space'),
'FloorName': self.get_object_attribute(self.get_parent_spatial_element(space, 'IfcBuildingStorey'), 'Name', is_primary_key=True, default='n/a'),
'Description': self.get_object_attribute(space, 'Description', default='n/a'),
'ExtSystem': self.get_ext_system_from_history(space.OwnerHistory),
'ExtObject': self.get_ext_object(space),
'ExtIdentifier': space.GlobalId,
'RoomTag': self.get_pset_value_from_object(space, 'COBie_Space', 'RoomTag', 'n/a'),
'UsableHeight': self.get_usable_height_from_space(space),
'GrossArea': self.get_gross_area_from_space(space),
'NetArea': self.get_net_area_from_space(space),
}
def get_zones(self):
zones = self.file.by_type('IfcZone')
for zone in zones:
zone_name = self.get_object_name(zone)
self.zones[zone_name] = {
'CreatedBy': self.get_email_from_history(zone.OwnerHistory),
'CreatedOn': self.get_created_on_from_history(zone.OwnerHistory),
'Category': self.get_category_from_object(zone, 'ZoneType'),
'SpaceNames': self.get_grouped_product_names_from_object(zone, 'IfcSpace'),
'ExtSystem': self.get_ext_system_from_history(zone.OwnerHistory),
'ExtObject': self.get_ext_object(zone),
'ExtIdentifier': zone.GlobalId,
'Description': self.get_object_attribute(zone, 'Description', default='n/a'),
}
def get_types(self):
types = self.file.by_type('IfcTypeObject')
for type in types:
is_a_type_asset = False
for type_asset in self.type_assets:
if type.is_a(type_asset):
is_a_type_asset = True
if not is_a_type_asset:
self.logger.warning('A type which is not an asset was found for {}'.format(type))
continue
# The responsibility matrix states to parse IfcMaterial and
# IfcMaterialLayerSet too, but it doesn't make much sense, so I
# don't parse it.
type_name = self.get_object_name(type)
self.types[type_name] = {
'CreatedBy': self.get_email_from_history(type.OwnerHistory),
'CreatedOn': self.get_created_on_from_history(type.OwnerHistory),
'Category': self.get_category_from_object(type, 'Category-Product'),
# The responsibility matrix states two possible fallbacks. I
# choose the 'n/a' option as opposed to repeating the name.
'Description': self.get_object_attribute(type, 'Description', default='n/a'),
'AssetType': self.get_pset_value_from_object(type, 'COBie_Asset', 'AssetType', 'n/a', 'AssetType'),
'Manufacturer': self.get_contact_pset_value_from_object(type, 'Pset_ManufacturerTypeInformation', 'Manufacturer'),
'ModelNumber': self.get_pset_value_from_object(type, 'Pset_ManufacturerTypeInformation', 'ModelLabel', 'n/a'),
# The responsibility matrix talks about using the Pset_Warranty
# values, but Pset_Warranty only applies to objects, not types,
# and so they are ignored.
'WarrantyGuarantorParts': self.get_contact_pset_value_from_object(type, 'COBie_Warranty', 'WarrantyGuarantorParts'),
'WarrantyDurationParts': self.get_pset_value_from_object(type, 'COBie_Warranty', 'WarrantyDurationParts', 0),
'WarrantyGuarantorLabor': self.get_contact_pset_value_from_object(type, 'COBie_Warranty', 'WarrantyGuarantorLabor'),
'WarrantyDurationLabor': self.get_pset_value_from_object(type, 'COBie_Warranty', 'WarrantyDurationLabor', 0),
# TODO: this may be derived from the duration values above, but
# until it is clarified, it will be hardcoded as 'day'
'WarrantyDurationUnit': 'day',
'ExtSystem': self.get_ext_system_from_history(type.OwnerHistory),
'ExtObject': self.get_ext_object(type),
'ExtIdentifier': type.GlobalId,
'ReplacementCost': self.get_pset_value_from_object(type, 'COBie_EconomicImpactValues', 'ReplacementCost', 'n/a'),
'ExpectedLife': self.get_expected_life_from_type(type),
# See note about WarrantyDurationUnit above
'DurationUnit': 'day',
'NominalLength': self.get_pset_value_from_object(type, 'COBie_Specification', 'NominalLength', 0),
'NominalWidth': self.get_pset_value_from_object(type, 'COBie_Specification', 'NominalWidth', 0),
'NominalHeight': self.get_pset_value_from_object(type, 'COBie_Specification', 'NominalHeight', 0),
'ModelReference': self.get_pset_value_from_object(type, 'Pset_ManufacturerTypeInformation', 'ModelReference', 'n/a'),
'Shape': self.get_pset_value_from_object(type, 'COBie_Specification', 'Shape', 'n/a'),
'Size': self.get_pset_value_from_object(type, 'COBie_Specification', 'Size', 'n/a'),
# The responsbility matrix allows the British spelling of
# "colour". I, however, do not.
'Color': self.get_pset_value_from_object(type, 'COBie_Specification', 'Color', 'n/a'),
'Finish': self.get_pset_value_from_object(type, 'COBie_Specification', 'Finish', 'n/a'),
'Grade': self.get_pset_value_from_object(type, 'COBie_Specification', 'Grade', 'n/a'),
'Material': self.get_pset_value_from_object(type, 'COBie_Specification', 'Material', 'n/a'),
'Constituents': self.get_pset_value_from_object(type, 'COBie_Specification', 'Constituents', 'n/a'),
'Features': self.get_pset_value_from_object(type, 'COBie_Specification', 'Features', 'n/a'),
'AccessibilityPerformance': self.get_pset_value_from_object(type, 'COBie_Specification', 'AccessibilityPerformance', 'n/a'),
'CodePerformance': self.get_pset_value_from_object(type, 'COBie_Specification', 'CodePerformance', 'n/a'),
'SustainabilityPerformance': self.get_pset_value_from_object(type, 'COBie_Specification', 'SustainabilityPerformance', 'n/a'),
}
def get_components(self):
components = self.file.by_type('IfcElement')
for component in components:
if not self.is_object_a_component_asset(component):
self.logger.warning('A component which is not an asset was found for {}'.format(component))
continue
component_name = self.get_object_name(component)
self.components[component_name] = {
'CreatedBy': self.get_email_from_history(component.OwnerHistory),
'CreatedOn': self.get_created_on_from_history(component.OwnerHistory),
'TypeName': self.get_type_name_from_object(component),
'Space': self.get_space_name_from_component(component),
'Description': self.get_object_attribute(component, 'Description', default='n/a'),
'ExtSystem': self.get_ext_system_from_history(component.OwnerHistory),
'ExtObject': self.get_ext_object(component),
'ExtIdentifier': component.GlobalId,
'SerialNumber': self.get_pset_value_from_object(component, 'Pset_ManufacturerOccurence', 'SerialNumber', 'n/a'),
'InstallationDate': self.get_pset_value_from_object(component, 'COBie_Component', 'InstallationDate', self.default_date),
'WarrantyStartDate': self.get_pset_value_from_object(component, 'COBie_Component', 'WarrantyStartDate', self.default_date),
'TagNumber': self.get_pset_value_from_object(component, 'COBie_Component', 'TagNumber', 'n/a'),
'BarCode': self.get_pset_value_from_object(component, 'Pset_ManufacturerOccurence', 'BarCode', 'n/a'),
'AssetIdentifier': self.get_pset_value_from_object(component, 'COBie_Component', 'AssetIdentifier', 'n/a'),
}
def get_systems(self):
systems = self.file.by_type('IfcSystem')
for system in systems:
system_name = self.get_object_name(system)
self.systems[system_name] = {
'CreatedBy': self.get_email_from_history(system.OwnerHistory),
'CreatedOn': self.get_created_on_from_history(system.OwnerHistory),
'Category': self.get_category_from_object(system, 'Category-Element'),
'ComponentNames': self.get_grouped_product_names_from_object(system, 'IfcProduct'),
'ExtSystem': self.get_ext_system_from_history(system.OwnerHistory),
'ExtObject': self.get_ext_object(system),
'ExtIdentifier': system.GlobalId,
'Description': self.get_object_attribute(system, 'Description', default='n/a'),
}
def get_assemblies(self):
assemblies = self.file.by_type('IfcRelAggregates')
for assembly in assemblies:
assembly_name = self.get_object_name(assembly)
if not self.is_object_a_component_asset(assembly.RelatingObject):
continue
self.assemblies[assembly_name] = {
'CreatedBy': self.get_email_from_history(assembly.OwnerHistory),
'CreatedOn': self.get_created_on_from_history(assembly.OwnerHistory),
'SheetName': 'Assembly',
'ParentName': self.get_object_name(assembly.RelatingObject),
'ChildNames': ','.join([o.Name for o in assembly.RelatedObjects]),
'AssemblyType': 'n/a', # I don't understand this field
'ExtSystem': self.get_ext_system_from_history(assembly.OwnerHistory),
'ExtObject': self.get_ext_object(assembly),
'ExtIdentifier': assembly.GlobalId,
'Description': self.get_object_attribute(assembly, 'Description', default='n/a'),
}
def get_connections(self):
connections = self.file.by_type('IfcRelConnects')
for connection in connections:
connection_name = self.get_object_name(connection)
self.connections[connection_name] = {
'CreatedBy': self.get_email_from_history(connection.OwnerHistory),
'CreatedOn': self.get_created_on_from_history(connection.OwnerHistory),
# There is ambiguity for what the ConnectionType mapping should be
'ConnectionType': self.get_object_attribute(connection, 'Description', default='n/a'),
'SheetName': 'Connections',
'RowName1': self.get_row_name_from_connection(connection, 'RelatingElement'),
'RowName2': self.get_row_name_from_connection(connection, 'RelatedElement'),
'RealizingElement': self.get_port_name_from_connection(connection, 'RealizingElement'),
'PortName1': self.get_port_name_from_connection(connection, 'RelatingPort'),
'PortName2': self.get_port_name_from_connection(connection, 'RelatedPort'),
'ExtSystem': self.get_ext_system_from_history(connection.OwnerHistory),
'ExtObject': self.get_ext_object(connection),
'ExtIdentifier': connection.GlobalId,
'Description': self.get_object_attribute(connection, 'Description', default='n/a'),
}
def get_spares(self):
spares = self.file.by_type('IfcConstructionProductResource')
for spare in spares:
spare_name = self.get_object_name(spare)
self.spares[spare_name] = {
'CreatedBy': self.get_email_from_history(spare.OwnerHistory),
'CreatedOn': self.get_created_on_from_history(spare.OwnerHistory),
'Category': self.get_category_from_object(spare, 'SpareType'),
'TypeName': self.get_type_name_from_object(spare),
'Suppliers': self.get_contact_pset_value_from_object(spare, 'COBie_Spare', 'Suppliers'),
'ExtSystem': self.get_ext_system_from_history(spare.OwnerHistory),
'ExtObject': self.get_ext_object(spare),
'ExtIdentifier': spare.GlobalId,
'Description': self.get_object_attribute(spare, 'Description', default='n/a'),
'SetNumber': self.get_contact_pset_value_from_object(spare, 'COBie_Spare', 'SetNumber'),
'PartNumber': self.get_contact_pset_value_from_object(spare, 'COBie_Spare', 'PartNumber'),
}
def get_resources(self):
resources = self.file.by_type('IfcConstructionProductResource')
for resource in resources:
resource_name = self.get_object_name(resource)
self.resources[resource_name] = {
'CreatedBy': self.get_email_from_history(resource.OwnerHistory),
'CreatedOn': self.get_created_on_from_history(resource.OwnerHistory),
'Category': self.get_object_attribute(resource, 'ObjectType', picklist='ResourceType', default='n/a'),
'ExtSystem': self.get_ext_system_from_history(resource.OwnerHistory),
'ExtObject': self.get_ext_object(resource),
'ExtIdentifier': resource.GlobalId,
'Description': self.get_object_attribute(resource, 'Description', default='n/a'),
}
def get_jobs(self):
jobs = self.file.by_type('IfcTask')
for job in jobs:
job_name = self.get_object_name(job)
task_time = job.TaskTime
self.jobs[job_name] = {
'CreatedBy': self.get_email_from_history(job.OwnerHistory),
'CreatedOn': self.get_created_on_from_history(job.OwnerHistory),
'Category': self.get_object_attribute(job, 'ObjectType', picklist='JobType', default='n/a'),
'Status': self.get_object_attribute(job, 'Status', picklist='JobStatusType', default='n/a'),
'TypeName': self.get_type_name_from_object(job),
'Description': self.get_object_attribute(job, 'Description', default='n/a'),
'Duration': self.get_object_attribute(task_time, 'ScheduleDuration', default=0),
'DurationUnit': 'day',
'Start': self.get_object_attribute(task_time, 'ScheduleStart', default=0),
'TaskStartUnit': 'day',
'Frequency': self.get_object_attribute(task_time.Recurrence, 'Occurrences', default=0) if hasattr(task_time, 'Recurrence') else 0,
'FrequencyUnit': 'day',
'ExtSystem': self.get_ext_system_from_history(job.OwnerHistory),
'ExtObject': self.get_ext_object(job),
'ExtIdentifier': job.GlobalId,
'TaskNumber': self.get_object_attribute(job, 'Identification'),
'Priors': self.get_priors_from_job(job),
'ResourceNames': self.get_resource_names_from_job(job),
}
# Impacts is not explicitly defined as a mapping in the responsibliity
# matrix. This is my best guess. This data should not be relied upon until
# this is clarified.
def get_impacts(self):
impacts = self.file.by_type('IfcPropertySet')
for impact in impacts:
if impact.Name != 'Pset_EnvironmentalImpactValues' \
or not impact.HasProperties:
continue
for property in impact.HasProperties:
property_name = '{}-{}'.format(property.id(), self.get_object_name(property))
self.impacts[property_name] = {
'CreatedBy': self.get_email_from_history(impact.OwnerHistory),
'CreatedOn': self.get_created_on_from_history(impact.OwnerHistory),
'ImpactType': None,
'ImpactStage': None,
'SheetName': 'Impacts',
'RowName': 'n/a',
'Value': self.get_object_attribute(property, 'NominalValue', default='n/a') if property.is_a('IfcPropertySingleValue') else 'n/a',
'Unit': '{}{}'.format(property.Unit.Prefix, property.Unit.Name) if hasattr(property, 'Unit') and property.Unit else 'n/a',
'LeadInTime': self.get_object_attribute(property, 'NominalValue', default='n/a') if property.is_a('IfcPropertySingleValue') and property.Name == 'LeadInTime' else 'n/a',
'Duration': self.get_object_attribute(property, 'NominalValue', default='n/a') if property.is_a('IfcPropertySingleValue') and property.Name == 'Duration' else 'n/a',
'LeadOutTime': self.get_object_attribute(property, 'NominalValue', default='n/a') if property.is_a('IfcPropertySingleValue') and property.Name == 'LeadOutTime' else 'n/a',
'ExtSystem': self.get_ext_system_from_history(impact.OwnerHistory),
'ExtObject': self.get_ext_object(impact),
'ExtIdentifier': impact.GlobalId,
'Description': self.get_object_attribute(impact, 'Description', default='n/a'),
}
def get_documents(self):
documents = self.file.by_type('IfcDocumentInformation')
for document in documents:
document_name = self.get_object_name(document)
self.documents[document_name] = {
'CreatedBy': self.get_email_from_history(document.OwnerHistory),
'CreatedOn': self.get_created_on_from_history(document.OwnerHistory),
'Category': 'n/a', # I am not sure what this mapping is meant to be
'ApprovalBy': self.get_object_attribute(document, 'IntendedUse', picklist='ApprovalBy', default='Information Only'),
'Stage': self.get_object_attribute(document, 'Scope', picklist='StageType', default='Required'),
'SheetName': 'Documents',
'RowName': 'n/a',
'Directory': self.get_directory_from_document(document),
'File': self.get_file_from_document(document),
'ExtSystem': self.get_ext_system_from_history(document.OwnerHistory),
'ExtObject': self.get_ext_object(document),
'ExtIdentifier': document.GlobalId,
'Description': self.get_object_attribute(document, 'Description', default=document_name),
'Reference': document_name,
}
# Attributes is not explicitly defined as a mapping in the responsibliity
# matrix. This is my best guess. This data should not be relied upon until
# this is clarified.
def get_attributes(self):
attributes = self.file.by_type('IfcPropertySet')
for attribute in attributes:
if not attribute.HasProperties:
continue
for property in attribute.HasProperties:
property_name = '{}-{}'.format(property.id(), self.get_object_name(property))
self.attributes[property_name] = {
'CreatedBy': self.get_email_from_history(attribute.OwnerHistory),
'CreatedOn': self.get_created_on_from_history(attribute.OwnerHistory),
'Category': 'n/a', # I am not sure what this mapping is meant to be
'SheetName': 'Attributes',
'RowName': 'n/a', # I am not sure what this mapping is meant to be
'Value': self.get_object_attribute(property, 'NominalValue', default='n/a') if property.is_a('IfcPropertySingleValue') else 'n/a',
'Unit': '{}{}'.format(property.Unit.Prefix, property.Unit.Name) if hasattr(property, 'Unit') and property.Unit else 'n/a',
'ExtSystem': self.get_ext_system_from_history(attribute.OwnerHistory),
'ExtObject': self.get_ext_object(attribute),
'ExtIdentifier': attribute.GlobalId,
'Description': self.get_object_attribute(attribute, 'Description', default='n/a'),
# I'm holding off implementing this until I understand a bit
# more about attributes
'AllowedValues': 'n/a',
}
def get_coordinates(self):
coordinates = self.file.by_type('IfcBuildingStorey') \
+ self.file.by_type('IfcSpace') \
+ self.file.by_type('IfcProduct')
for coordinate in coordinates:
coordinate_name = '{}/{}'.format(coordinate.is_a(), self.get_object_name(coordinate))
self.coordinates[coordinate_name] = {
'CreatedBy': self.get_email_from_history(coordinate.OwnerHistory),
'CreatedOn': self.get_created_on_from_history(coordinate.OwnerHistory),
'Category': 'Location', # I am not sure what this mapping is meant to be
'SheetName': 'Coordinates',
'RowName': 'n/a',
'CoordinateXAxis': coordinate.ObjectPlacement.RelativePlacement.Location.Coordinates[0],
'CoordinateYAxis': coordinate.ObjectPlacement.RelativePlacement.Location.Coordinates[1],
'CoordinateZAxis': coordinate.ObjectPlacement.RelativePlacement.Location.Coordinates[2],
'ExtSystem': self.get_ext_system_from_history(coordinate.OwnerHistory),
'ExtObject': self.get_ext_object(coordinate),
'ExtIdentifier': coordinate.GlobalId,
# Holding off implementing this, see Bug #688:
# https://github.com/IfcOpenShell/IfcOpenShell/issues/688
'ClockwiseRotation': 'n/a', # X axis
'ElevationalRotation': 'n/a', # Y axis
'YawRotation': 'n/a', # Z axis
}
# I don't fully understand this worksheet. Don't trust this data.
def get_issues(self):
issues = self.file.by_type('IfcApproval')
for issue in issues:
issue_name = self.get_object_name(issue)
self.issues[issue_name] = {
'CreatedBy': self.get_email_from_history(issue.OwnerHistory),
'CreatedOn': self.get_created_on_from_history(issue.OwnerHistory),
'Type': 'n/a', # How do we get to the Pset_Risk from the IfcApproval?
'Risk': 'n/a', # How do we get to the Pset_Risk from the IfcApproval?
'Chance': 'n/a', # How do we get to the Pset_Risk from the IfcApproval?
'Impact': 'n/a', # How do we get to the Pset_Risk from the IfcApproval?
'SheetName1': 'n/a',
'RowName1': 'n/a',
'SheetName2': 'n/a',
'RowName2': 'n/a',
'Description': self.get_object_attribute(issue, 'Description', default='n/a'),
'Owner': self.get_email_from_history(issue.RequestingApproval), # Is this correct?
'Mitigation': 'n/a',
'ExtSystem': self.get_ext_system_from_history(issue.OwnerHistory),
'ExtObject': self.get_ext_object(issue),
'ExtIdentifier': issue.GlobalId,
}
def get_directory_from_document(self, document):
if self.file.schema == 'IFC2X3':
if document.HasDocumentReferences:
for reference in document.HasDocumentReferences:
return self.get_object_attribute(reference, 'Location', default='n/a')
else:
return self.get_object_attribute(document, 'Location', default='n/a')
def get_file_from_document(self, document):
if self.file.schema == 'IFC2X3':
if document.HasDocumentReferences:
for reference in document.HasDocumentReferences:
return self.get_object_attribute(reference, 'Name', default='n/a')
else:
self.get_object_attribute(document, 'Identification', default='n/a')
def get_resource_names_from_job(self, job):
names = []
if job.OperatesOn \
and job.OperatesOn.RelatedObjects:
for object in job.OperatesOn.RelatedObjects:
names.append(object.Name)
return ','.join(names)
def get_priors_from_job(self, job):
# The responsibility matrix is vague as to whether it expects a task
# name or a task identification. I chose task name.
if job.IsSuccessorFrom \
and job.IsSuccessorFrom.RelatingProcess \
and job.IsSuccessorFrom.RelatingProcess.is_a('IfcTask'):
return self.get_object_name(job.IsSuccessorFrom.RelatingProcess)
def get_row_name_from_connection(self, connection, key):
if not connection.is_a('IfcRelConnectsElements'):
return None
object = getattr(connection, key)
if self.is_object_a_component_asset(object):
return object.Name
self.logger.error('The connected object relationship {} is not a component asset for {}'.format(key, connection))
def get_port_name_from_connection(self, connection, key):
if not connection.is_a('IfcRelConnectsPorts'):
return None
object = getattr(connection, key)
if self.is_object_a_component_asset(object):
return object.Name
self.logger.error('The connected object relationship {} is not a component asset for {}'.format(key, connection))
def is_object_a_component_asset(self, object):
is_an_asset = False
for asset in self.component_assets:
if object.is_a(asset):
is_an_asset = True
return is_an_asset
def get_space_name_from_component(self, component):
for relationship in component.ContainedInStructure:
if relationship.RelatingStructure.is_a('IfcSpace') \
and relationship.RelatingStructure.Name:
return relationship.RelatingStructure.Name
self.logger.error('A related space name could not be determined for {}'.format(component))
def get_type_name_from_object(self, object):
if self.file.schema == 'IFC2X3':
for relationship in object.IsDefinedBy:
if relationship.is_a('IfcRelDefinesByType') \
and relationship.RelatingType.Name:
return relationship.RelatingType.Name
else:
for relationship in object.IsTypedBy:
if relationship.RelatingType.Name:
return relationship.RelatingType.Name
self.logger.error('A related type name could not be determined for {}'.format(object))
def get_expected_life_from_type(self, type):
if self.file.schema == 'IFC2X3':
return self.get_pset_value_from_object(type, 'COBie_ServiceLife', 'ServiceLifeDuration', 'n/a')
return self.get_pset_value_from_object(type, 'Pset_ServiceLife', 'ServiceLifeDuration', 'n/a')
def get_contact_pset_value_from_object(self, object, pset_name, property_name):
result = self.get_pset_value_from_object(object, pset_name, property_name)
if not result:
self.logger.error('No property {} in {} was found for {}'.format(property_name, pset_name, object))
if result not in self.contacts:
self.logger.error('A coresponding {} contact in {} was not found for {}'.format(property_name, pset_name, object))
return result
def get_pset_value_from_object(self, object, pset_name, property_name, default=None, picklist=None):
pset = self.get_pset_from_object(object, pset_name)
if not pset:
if picklist:
self.picklists[picklist].append(default)
return default
property = self.get_property_from_pset(pset, property_name, default)
if picklist:
self.picklists[picklist].append(property)
return property
def get_grouped_product_names_from_object(self, object, type):
names = []
for relationship in object.IsGroupedBy:
for related_object in relationship.RelatedObjects:
if related_object.is_a(type):
names.append(related_object.Name)
if names:
return ','.join(names)
self.logger.error('No related {} were found for {}'.format(type, object))
def get_net_area_from_space(self, space):
qto = self.get_qto_from_object(space, 'Qto_SpaceBaseQuantities')
if not qto:
return 'n/a'
return self.get_property_from_qto(qto, 'NetFloorArea', 'AreaValue')
def get_gross_area_from_space(self, space):
qto = self.get_qto_from_object(space, 'Qto_SpaceBaseQuantities')
if not qto:
return 'n/a'
return self.get_property_from_qto(qto, 'GrossFloorArea', 'AreaValue')
def get_usable_height_from_space(self, space):
qto = self.get_qto_from_object(space, 'Qto_SpaceBaseQuantities')
if not qto:
return 'n/a'
return self.get_property_from_qto(qto, 'FinishCeilingHeight', 'LengthValue')
def get_qto_from_object(self, object, name):
for relationship in object.IsDefinedBy:
if relationship.RelatingPropertyDefinition.is_a('IfcQuantitySet') \
and relationship.RelatingPropertyDefinition.Name == name:
return relationship.RelatingPropertyDefinition
self.logger.warning('The qto {} was not found for {}'.format(name, object))
def get_property_from_qto(self, qto, name, attribute):
for property in qto.Quantities:
if property.Name == name:
return getattr(property, attribute)
self.logger.warning('The quantity value {} was not found for {}'.format(name, qto))
return 'n/a'
def get_property_from_pset(self, pset, name, default=None):
for property in pset.HasProperties:
if property.Name == name:
return property.NominalValue
self.logger.warning('The property {} was not found for {}'.format(name, pset))
return default
def get_pset_from_object(self, object, name):
if object.is_a('IfcTypeObject'):
if object.HasPropertySets:
for pset in object.HasPropertySets:
if pset.is_a('IfcPropertySet') \
and pset.Name == name:
return pset
else:
for relationship in object.IsDefinedBy:
if relationship.is_a('IfcRelDefinesByProperties') \
and relationship.RelatingPropertyDefinition.is_a('IfcPropertySet') \
and relationship.RelatingPropertyDefinition.Name == name:
return relationship.RelatingPropertyDefinition
self.logger.warning('The pset {} was not found for {}'.format(name, object))
def get_height_from_storey(self, storey):
for relationship in storey.IsDefinedBy:
if not relationship.RelatingPropertyDefinition.is_a('IfcElementQuantity'):
continue
for quantity in relationship.RelatingPropertyDefinition.Quantities:
if quantity.is_a('IfcQuantityLength') \
and quantity.LengthValue:
return quantity.LengthValue
self.logger.warning('A height length value was not found for {}'.format(storey))
return 'n/a'
def get_created_on_from_history(self, history):
if history.CreationDate:
return datetime.datetime.fromtimestamp(history.CreationDate).isoformat()
self.logger.warning('A created on date was not found for {}'.format(history))
return self.default_date
def get_object_attribute(self, object, attribute, is_primary_key = False, picklist = None, default=None):
result = getattr(object, attribute)
if result:
if picklist:
self.picklists[picklist].append(result)
return result
if is_primary_key:
self.logger.error('The primary key attribute {} was not found for {}'.format(attribute, object))
else:
self.logger.warning('The attribute {} was not found for {}'.format(attribute, object))
return default
def get_ext_project_object(self):
self.picklists['objType'].append('IfcProject')
return 'IfcProject'
def get_ext_site_object(self):
self.picklists['objType'].append('IfcSite')
return 'IfcSite'
def get_ext_object(self, object):
self.picklists['objType'].append(object.is_a())
return object.is_a()
def get_ext_system_from_history(self, history):
return history.OwningApplication.ApplicationFullName
def get_object_name(self, object):
if not object.Name:
self.logger.error('A primary key name was not found for {}'.format(object))
return 'Object{}'.format(object.id())
return object.Name
def get_area_measurement_from_building(self, building):
for relationship in building.IsDefinedBy:
if relationship.RelatingPropertyDefinition.is_a('IfcElementQuantity') \
and relationship.RelatingPropertyDefinition.MethodOfMeasurement:
return relationship.RelatingPropertyDefinition.MethodOfMeasurement
self.logger.warning('A method of measurement was not defined for {}'.format(building))
def get_unit_type_from_units(self, units, type):
for unit in units:
if unit.UnitType == type:
if unit.is_a('IfcSIUnit') and unit.Prefix:
return '{}{}'.format(unit.Prefix, unit.Name)
return unit.Name
self.logger.error('A unit {} was not defined in this project for {}'.format(type, units))
def get_monetary_unit_from_units(self, units):
for unit in units:
if unit.is_a('IfcMonetaryUnit'):
return unit.Currency
self.logger.error('A monetary unit could not be found for {}'.format(units))
def get_project_globalid_from_building(self, building):
return self.get_parent_spatial_element(building, 'IfcProject').GlobalId
def get_site_globalid_from_building(self, building):
return self.get_parent_spatial_element(building, 'IfcSite').GlobalId
def get_project_name_from_building(self, building):
project = self.get_parent_spatial_element(building, 'IfcProject')
if project.Name:
return project.Name
self.logger.error('The project name is empty for {}'.format(project))
return 'n/a'
def get_site_name_from_building(self, building):
site = self.get_parent_spatial_element(building, 'IfcSite')
if site.Name:
return site.Name
self.logger.error('The site name is empty for {}'.format(site))
return 'n/a'
def get_units_from_building(self, building):
return self.get_parent_spatial_element(building, 'IfcProject').UnitsInContext.Units
def get_parent_spatial_element(self, child, name):
for relationship in child.Decomposes:
if relationship.RelatingObject.is_a(name):
return relationship.RelatingObject
return self.get_parent_spatial_element(relationship.RelatingObject, name)
return None
def get_category_from_object(self, object, picklist):
class_identification = None
class_name = None
for association in object.HasAssociations:
if not association.is_a('IfcRelAssociatesClassification'):
continue
if not association.RelatingClassification.is_a('IfcClassificationReference'):
continue
if self.file.schema == 'IFC2X3':
class_identification = association.RelatingClassification.ItemReference
else:
class_identification = association.RelatingClassification.Identification
class_name = association.RelatingClassification.Name
break
if not class_identification or class_name:
self.logger.error('The classification has invalid identification and name for {}'.format(object))
result = '{}:{}'.format(class_identification, class_name)
self.picklists[picklist].append(result)
return result
# The responsibility matrix lists a fallback, but it is a very
# cumbersome check, and so it is not implemented here.
def get_name_from_person(self, person, attribute):
name = getattr(person, attribute)
if not name.isalpha():
self.logger.warning('The person\'s {} seems to be badly formatted ("{}") for {}'.format(attribute, name, person))
return name if name else 'n/a'
def get_attribute_from_address(self, address, attribute):
result = getattr(address, attribute)
if not result:
self.logger.warning('The address {} seems to not exist for {}'.format(attribute, address))
return 'n/a'
return result
def get_email_from_history(self, history):
person = history.OwningUser.ThePerson
organisation = history.OwningUser.TheOrganization
email = self.get_email_from_person_or_organisation(person)
if email:
return email
email = self.get_email_from_person_or_organisation(organisation)
if email:
return email
given_name = person.GivenName if person.GivenName else "unknown"
family_name = person.FamilyName if person.FamilyName else "unknown"
organisation_name = organisation.Name if organisation.Name else "unknown"
if given_name == 'unknown' \
and family_name == 'unknown' \
and organisation_name == 'unknown':
self.logger.error('No primary key could be determined from {}'.format(history))
return '{}{}@{}'.format(given_name, family_name, organisation_name)
def get_postal_address_from_history(self, history):
for address in (history.OwningUser.ThePerson.Addresses or []):
if address.is_a('IfcPostalAddress'):
return address
for address in (history.OwningUser.TheOrganization.Addresses or []):
if address.is_a('IfcPostalAddress'):
return address
return self.file.createIfcPostalAddress()
def get_category_from_history(self, history):
roles = []
both = history.OwningUser
person = both.ThePerson
organisation = both.TheOrganization
for role in ((both.Roles or []) + (person.Roles or []) + (organisation.Roles or [])):
roles.append(self.get_role(role))
result = ','.join(set(roles))
if not result:
self.logger.error('No roles could be found for {}'.format(history))
return
self.picklists['Category-Role'].append(result)
return result
def get_phone_from_history(self, history):
person = history.OwningUser.ThePerson
organisation = history.OwningUser.TheOrganization
phone = self.get_phone_from_person_or_organisation(person)
if phone:
return phone
phone = self.get_phone_from_person_or_organisation(organisation)
if phone:
return phone
return 'n/a'
def get_ext_object_from_history(self, history):
result = history.OwningUser.is_a()
self.picklists['objType'].append(result)
return result
def get_department_from_history(self, history):
organisation = history.OwningUser.TheOrganization
department = self.get_internal_location_from_organisation(organisation)
if department:
return department
last_department = None
for relationship in organisation.Relates:
for related_organisation in relationship.RelatedOrganizations:
department = self.get_internal_location_from_organisation(related_organisation)
if department:
last_department = department
if last_department:
return last_department
return history.OwningUser.TheOrganization.Name or 'n/a'
def get_internal_location_from_organisation(self, organisation):
for address in (organisation.Addresses or []):
if address.is_a('IfcPostalAddress'):
return address.InternalLocation
self.logger.warning('An internal location was not found for {}'.format(organisation))
def get_role(self, role):
if role.Role == 'USERDEFINED':
return role.UserDefinedRole
return role.Role
def get_phone_from_person_or_organisation(self, person_or_org):
for address in (person_or_org.Addresses or []):
if address.is_a('IfcTelecomAddress'):
return address.TelephoneNumbers[0]
self.logger.warning('A phone was not found for {}'.format(person_or_org))
def get_email_from_person_or_organisation(self, person_or_org):
for address in (person_or_org.Addresses or []):
if address.is_a('IfcTelecomAddress'):
return address.ElectronicMailAddresses[0]
self.logger.warning('An email address was not found for {}'.format(person_or_org))
class CobieCsvWriter():
def __init__(self, parser):
self.parser = parser
def write(self):
self.write_file('Contact', self.parser.contacts, 'Email',
['Email', 'CreatedBy', 'CreatedOn', 'Category', 'Company',
'Phone', 'ExtSystem', 'ExtObject', 'ExtIdentifier',
'Department', 'OrganizationCode', 'GivenName', 'FamilyName',
'Street', 'PostalBox', 'Town', 'StateRegion', 'PostalCode',
'Country'])
self.write_file('Facility', self.parser.facilities, 'Name',
['Name', 'CreatedBy', 'CreatedOn', 'Category', 'ProjectName',
'SiteName', 'LinearUnits', 'AreaUnits', 'VolumeUnits', 'CostUnit',
'AreaMeasurement', 'ExternalSystem', 'ExternalProjectObject',
'ExternalProjectIdentifier', 'ExternalSiteObject',
'ExternalSiteIdentifier', 'ExternalFacilityObject',
'ExternalFacilityIdentifier', 'Description', 'ProjectDescription',
'SiteDescription', 'Phase'])
self.write_file('Floor', self.parser.floors, 'Name',
['Name', 'CreatedBy', 'CreatedOn', 'Category', 'ExtSystem',
'ExtObject', 'ExtIdentifier', 'Description', 'Elevation', 'Height'])
self.write_file('Space', self.parser.spaces, 'Name',
['Name', 'CreatedBy', 'CreatedOn', 'Category', 'FloorName',
'Description', 'ExtSystem', 'ExtObject', 'ExtIdentifier', 'RoomTag',
'UsableHeight', 'GrossArea', 'NetArea'])
self.write_file('Zone', self.parser.zones, 'Name',
['Name', 'CreatedBy', 'CreatedOn', 'Category', 'SpaceNames',
'ExtSystem', 'ExtObject', 'ExtIdentifier', 'Description',])
self.write_file('Type', self.parser.types, 'Name',
['Name', 'CreatedBy', 'CreatedOn', 'Category', 'Description',
'AssetType', 'Manufacturer', 'ModelNumber',
'WarrantyGuarantorParts', 'WarrantyDurationParts',
'WarrantyGuarantorLabor', 'WarrantyDurationLabor',
'WarrantyDurationUnit', 'ExtSystem', 'ExtObject', 'ExtIdentifier',
'ReplacementCost', 'ExpectedLife', 'DurationUnit', 'NominalLength',
'NominalWidth', 'NominalHeight', 'ModelReference', 'Shape', 'Size',
'Color', 'Finish', 'Grade', 'Material', 'Constituents', 'Features',
'AccessibilityPerformance', 'CodePerformance',
'SustainabilityPerformance',])
self.write_file('Component', self.parser.components, 'Name',
['Name', 'CreatedBy', 'CreatedOn', 'TypeName', 'Space',
'Description', 'ExtSystem', 'ExtObject', 'ExtIdentifier',
'SerialNumber', 'InstallationDate', 'WarrantyStartDate',
'TagNumber', 'BarCode', 'AssetIdentifier',])
self.write_file('System', self.parser.systems, 'Name',
['Name', 'CreatedBy', 'CreatedOn', 'Category', 'ComponentNames',
'ExtSystem', 'ExtObject', 'ExtIdentifier', 'Description',])
self.write_file('Assembly', self.parser.assemblies, 'Name',
['Name', 'CreatedBy', 'CreatedOn', 'SheetName', 'ParentName',
'ChildNames', 'AssemblyType', 'ExtSystem', 'ExtObject',
'ExtIdentifier', 'Description',])
self.write_file('Connection', self.parser.connections, 'Name',
['Name', 'CreatedBy', 'CreatedOn', 'ConnectionType', 'SheetName',
'RowName1', 'RowName2', 'RealizingElement', 'PortName1',
'PortName2', 'ExtSystem', 'ExtObject', 'ExtIdentifier',
'Description',])
self.write_file('Spare', self.parser.spares, 'Name',
['Name', 'CreatedBy', 'CreatedOn', 'Category', 'TypeName',
'Suppliers', 'ExtSystem', 'ExtObject', 'ExtIdentifier',
'Description', 'SetNumber', 'PartNumber',])
self.write_file('Resource', self.parser.resources, 'Name',
['Name', 'CreatedBy', 'CreatedOn', 'Category', 'ExtSystem',
'ExtObject', 'ExtIdentifier', 'Description',])
self.write_file('Job', self.parser.jobs, 'Name',
['Name', 'CreatedBy', 'CreatedOn', 'Category', 'Status', 'TypeName',
'Description', 'Duration', 'DurationUnit', 'Start', 'TaskStartUnit',
'Frequency', 'FrequencyUnit', 'ExtSystem', 'ExtObject',
'ExtIdentifier', 'TaskNumber', 'Priors', 'ResourceNames',])
self.write_file('Impact', self.parser.impacts, 'Name',
['Name', 'CreatedBy', 'CreatedOn', 'ImpactType', 'ImpactStage',
'SheetName', 'RowName', 'Value', 'Unit', 'LeadInTime', 'Duration',
'LeadOutTime', 'ExtSystem', 'ExtObject', 'ExtIdentifier',
'Description',])
self.write_file('Document', self.parser.documents, 'Name',
['Name', 'CreatedBy', 'CreatedOn', 'Category', 'ApprovalBy',
'Stage', 'SheetName', 'RowName', 'Directory', 'File', 'ExtSystem',
'ExtObject', 'ExtIdentifier', 'Description', 'Reference',])
self.write_file('Attribute', self.parser.attributes, 'Name',
['Name', 'CreatedBy', 'CreatedOn', 'Category', 'SheetName',
'RowName', 'Value', 'Unit', 'ExtSystem', 'ExtObject',
'ExtIdentifier', 'Description', 'AllowedValues',])
self.write_file('Coordinate', self.parser.coordinates, 'Name',
['Name', 'CreatedBy', 'CreatedOn', 'Category', 'SheetName',
'RowName', 'CoordinateXAxis', 'CoordinateYAxis', 'CoordinateZAxis',
'ExtSystem', 'ExtObject', 'ExtIdentifier', 'ClockwiseRotation',
'ElevationalRotation', 'YawRotation',])
self.write_file('Issue', self.parser.issues, 'Name',
['Name', 'CreatedBy', 'CreatedOn', 'Type', 'Risk', 'Chance',
'Impact', 'SheetName1', 'RowName1', 'SheetName2', 'RowName2',
'Description', 'Owner', 'Mitigation', 'ExtSystem', 'ExtObject',
'ExtIdentifier',])
def write_file(self, csv_name, data, primary_key, fieldnames):
with open('C:/cygwin64/home/moud308/Projects/ifc2cobie/{}.csv'.format(csv_name), 'w', newline='') as file:
writer = csv.DictWriter(file, fieldnames=fieldnames)
writer.writeheader()
for name, row in data.items():
row[primary_key] = name
writer.writerow(row)
class CobieXlsWriter(CobieCsvWriter):
def write(self):
self.colours = {
'r': 'fdff8e', # Required
'i': 'fdcd94', # Internal reference
'e': 'cd95ff', # External reference
'o': 'cdffc8', # Optional
's': 'c0c0c0', # Secondary information
'p': '9ccaff', # Project specific
'n': '000000' # Not used
}
super().write()
self.workbook = Workbook()
self.workbook.remove(self.workbook.active)
self.write_worksheet('Contact', self.colours['r'], 'ririrreeeoooooooooo')
self.write_worksheet('Facility', self.colours['r'], 'ririrriiiireeeeeeeoooo')
self.write_worksheet('Floor', self.colours['r'], 'ririeeeooo')
self.write_worksheet('Space', self.colours['r'], 'ririireeeoooo')
self.write_worksheet('Zone', self.colours['r'], 'ririieeeo')
self.write_worksheet('Type', self.colours['r'], 'riririoooooooeeeooooooooooooooooooo')
self.write_worksheet('Component', self.colours['r'], 'ririireeeoooooo')
self.write_worksheet('System', self.colours['r'], 'ririieeeo')
self.write_worksheet('Assembly', self.colours['r'], 'rirrrrreeeo')
self.write_worksheet('Connection', self.colours['r'], 'ririiiiiiieeeo')
self.write_worksheet('Spare', self.colours['r'], 'ririiieeeooo')
self.write_worksheet('Resource', self.colours['r'], 'ririeeeo')
self.write_worksheet('Job', self.colours['r'], 'ririiirriririeeeoii')
self.write_worksheet('Impact', self.colours['r'], 'ririiiirioooeeeo')
self.write_worksheet('Document', self.colours['r'], 'ririiiiirreeeoo')
self.write_worksheet('Attribute', self.colours['r'], 'ririiirreeeoo')
self.write_worksheet('Coordinate', self.colours['r'], 'ririiooooeeeooo')
self.write_worksheet('Issue', self.colours['r'], 'ririooooooooooeee')
self.workbook.save(filename='C:/cygwin64/home/moud308/Projects/ifc2cobie/out.xlsx')
self.workbook.close()
def write_worksheet(self, name, tab_colour, fill_map):
worksheet = self.workbook.create_sheet(name)
worksheet.sheet_properties.tabColor = tab_colour
with open('C:/cygwin64/home/moud308/Projects/ifc2cobie/{}.csv'.format(name),
'r') as f:
reader = csv.reader(f)
for r, row in enumerate(reader):
for c, col in enumerate(row):
cell = worksheet.cell(column=c+1, row=r+1, value=col)
if r == 0:
cell.fill = PatternFill(fill_type='solid', fgColor=self.colours['s'])
else:
cell.fill = PatternFill(fill_type='solid', fgColor=self.colours[fill_map[c]])
start = time.time()
logging.basicConfig(
filename='C:/cygwin64/home/moud308/Projects/ifc2cobie/process.log',
filemode='a', level=logging.DEBUG)
logger = logging.getLogger('IFCtoCOBie')
logger.info('Starting conversion')
parser = IfcCobieParser(logger)
parser.parse()
#writer = CobieCsvWriter(parser)
#writer.write()
writer = CobieXlsWriter(parser)
writer.write()
logger.info('Finished conversion in {}s'.format(time.time() - start))