diff --git a/src/ifcfm/ifcfm/parser.py b/src/ifcfm/ifcfm/parser.py index b23f1697d6..d055063f69 100644 --- a/src/ifcfm/ifcfm/parser.py +++ b/src/ifcfm/ifcfm/parser.py @@ -29,28 +29,83 @@ class Parser: def __init__(self, logger): self.logger = logger self.file = None - self.sheets = [ - "actors", - "facilities", - "floors", - "spaces", - "zones", - "types", - "components", - "systems", - "assemblies", - "connections", - "spares", - "resources", - "jobs", - "impacts", - "documents", - "attributes", - "coordinates", - "issues", - ] - for sheet in self.sheets: - setattr(self, sheet, {}) + self.categories = { + "actors": self.get_actors, + "facilities": self.get_facilities, + "floors": self.get_floors, + "spaces": self.get_spaces, + "zones": self.get_zones, + "types": self.get_types, + "components": self.get_components, + "systems": self.get_systems, + # "assemblies", + # "connections", + # "spares", + # "resources", + # "jobs", + # "impacts", + "documents": self.get_documents, + # "attributes", + # "coordinates", + # "issues", + } + # COBie + self.custom_parameters = { + "types": { + "AssetType": lambda e, p: None, + "ManufacturerOrganizationName": lambda e, p: None, + "ModelNumber": lambda e, p: None, + "WarrantyGuarantorParts": lambda e, p: None, + "WarrantyDurationParts": lambda e, p: None, + "WarrantyGuarantorLabour": lambda e, p: None, + "WarrantyDurationLabour": lambda e, p: None, + "DurationUnit": lambda e, p: "months", + "WarrantyDescription": lambda e, p: None, + "ReplacementCost": lambda e, p: None, + "ExpectedLife": lambda e, p: None, + "NominalLength": lambda e, p: None, + "NominalWidth": lambda e, p: None, + "NominalHeight": lambda e, p: None, + }, + "components": { + "SerialNumber": lambda e, p: None, + "InstallationDate": lambda e, p: None, + "WarrantyStartDate": lambda e, p: None, + "TagNumber": lambda e, p: None, + "BarCode": lambda e, p: None, + "AssetIdentifier": lambda e, p: None, + } + } + # AOH-BEM + self.custom_parameters = { + "types": { + "ProcurementMethod": lambda e, p: None, + "ManufacturerOrganizationName": lambda e, p: None, + "SupplierOrganizationName": lambda e, p: None, + "ModelNumber": lambda e, p: None, + "WarrantyOrganizationName": lambda e, p: None, + "WarrantyDuration": lambda e, p: None, + "SpecificationSection": lambda e, p: None, + "SubmittalID": lambda e, p: None, + "ProductURL": lambda e, p: None, + }, + "components": { + "InstallationDate": lambda e, p: None, + "WarrantyStartDate": lambda e, p: None, + "InstalledModelNumber": lambda e, p: None, + "SerialNumber": lambda e, p: None, + "BarCode": lambda e, p: None, + "TagNumber": lambda e, p: None, + "OwnerAssetID": lambda e, p: None, + "FluidHotFeedName": lambda e, p: None, + "FluidColdFeedName": lambda e, p: None, + "ElectricPanelName": lambda e, p: None, + "ElectricCircuitName": lambda e, p: None, + "ControlledByName": lambda e, p: None, + "InterlockedWithName": lambda e, p: None, + "PartOfAssemblyName": lambda e, p: None, + } + } self.picklists = { "Category-Role": [], "Category-Facility": [], @@ -70,25 +125,9 @@ class Parser: def parse(self, files): self.files = files - - self.get_actors() - 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() + for category, get_category in self.categories.items(): + setattr(self, category, {}) + get_category() def get_actors(self): for ifc in self.files.values(): @@ -180,6 +219,10 @@ class Parser: return result return round(result, decimals) + def get_custom_parameters(self, category, data, element, psets): + for parameter, get_parameter in self.custom_parameters.get(category, {}).items(): + data[parameter] = get_parameter(element, psets) + def get_spaces(self): for key, ifc in self.files.items(): if "arch" not in key: @@ -190,8 +233,7 @@ class Parser: primary_keys.append(name) # TODO: not correct mapping psets = ifcopenshell.util.element.get_psets(element) - - self.spaces[name] = { + data = { "Name": name, "AuthorOrganizationName": element.OwnerHistory.OwningUser.TheOrganization.Name, "AuthorDate": ifcopenshell.util.date.ifc2datetime(element.OwnerHistory.CreationDate).isoformat(), @@ -205,6 +247,8 @@ class Parser: "AreaGross": self.get_property(psets, "Qto_SpaceBaseQuantities", "GrossFloorArea", decimals=2), "AreaNet": self.get_property(psets, "Qto_SpaceBaseQuantities", "NetFloorArea", decimals=2), } + self.get_custom_parameters("spaces", data, element, psets) + self.spaces[name] = data def get_zones(self): for ifc in self.files.values(): @@ -249,27 +293,19 @@ class Parser: for element in ifcopenshell.util.fm.get_fmhem_types(self.files[ifc_file]): name = element.Name primary_keys.append(name) - category = self.get_classification(element) - - self.types[name] = { + psets = ifcopenshell.util.element.get_psets(element) + data = { "Name": name, "AuthorOrganizationName": element.OwnerHistory.OwningUser.TheOrganization.Name, "AuthorDate": ifcopenshell.util.date.ifc2datetime(element.OwnerHistory.CreationDate).isoformat(), - "Category": category, - "ProcurementMethod": None, + "Category": self.get_classification(element), "Description": element.Description, - "ManufacturerOrganizationName": None, - "SupplierOrganizationName": None, - "ModelNumber": None, - "WarrantyOrganizationName": None, - "WarrantyDuration": None, "ModelSoftware": element.OwnerHistory.OwningApplication.ApplicationFullName, "ModelObject": "{}[{}]".format(element.is_a(), ifcopenshell.util.element.get_predefined_type(element)), "ModelID": element.GlobalId, - "SpecificationSection": None, - "SubmittalID": None, - "ProductURL": None, } + self.get_custom_parameters("types", data, element, psets) + self.types[name] = data def get_components(self): for discipline, ifc in self.files.items(): @@ -292,31 +328,23 @@ class Parser: space = ifcopenshell.util.element.get_container(element) space_name = space.Name if space.is_a("IfcSpace") else None - self.components[name] = { + psets = ifcopenshell.util.element.get_psets(element) + + data = { "Name": name, "AuthorOrganizationName": element.OwnerHistory.OwningUser.TheOrganization.Name, "AuthorDate": ifcopenshell.util.date.ifc2datetime(element.OwnerHistory.CreationDate).isoformat(), "TypeName": element_type.Name, "SpaceName": space_name, - "InstallationDate": None, - "WarrantyStartDate": None, - "ModelSoftware": element.OwnerHistory.OwningApplication.ApplicationFullName, - "ModelObject": "{}[{}]".format(element.is_a(), ifcopenshell.util.element.get_predefined_type(element)), - "ModelID": element.GlobalId, - "InstalledModelNumber": None, - "SerialNumber": None, - "BarCode": None, - "TagNumber": None, - "OwnerAssetID": None, "SystemName": system, - "FluidHotFeedName": None, - "FluidColdFeedName": None, - "ElectricPanelName": None, - "ElectricCircuitName": None, - "ControlledByName": None, - "InterlockedWithName": None, - "PartOfAssemblyName": None, + "ModelSoftware": element.OwnerHistory.OwningApplication.ApplicationFullName, + "ModelObject": "{}[{}]".format( + element.is_a(), ifcopenshell.util.element.get_predefined_type(element) + ), + "ModelID": element.GlobalId, } + self.get_custom_parameters("components", data, element, psets) + self.components[name] = data def get_documents(self): for ifc in self.files.values(): @@ -331,7 +359,9 @@ class Parser: identification = referenced_document.ItemReference author_date = None if referenced_document.CreationTime: - author_date = ifcopenshell.util.date.ifc2datetime(referenced_document.CreationTime).isoformat() + author_date = ifcopenshell.util.date.ifc2datetime( + referenced_document.CreationTime + ).isoformat() else: referenced_document = element.ReferencedDocument identification = referenced_document.Identification diff --git a/src/ifcfm/ifcfm/writer.py b/src/ifcfm/ifcfm/writer.py index c82db4cd66..bb102ab63c 100644 --- a/src/ifcfm/ifcfm/writer.py +++ b/src/ifcfm/ifcfm/writer.py @@ -1,4 +1,3 @@ - # IfcFM - IFC for facility management # Copyright (C) 2021 Dion Moult # @@ -70,7 +69,6 @@ class Writer: def __init__(self, parser, filename=None): self.filename = filename self.parser = parser - self.sheets = [] self.sheet_data = {} self.colours = { "r": "fdff8e", # Required @@ -90,415 +88,164 @@ class Writer: "p": "b8dd73", # Project specific "n": "000000", # Not used } + self.sheets = { + "Actor": { + "data": self.parser.actors, + "fields": [ + "Name", + "Category", + "Email", + "Phone", + "CompanyURL", + "Department", + "Address1", + "Address2", + "StateRegion", + "PostalCode", + "Country", + ], + "colours": "rirrrrrrrrr", + "order": ["Name"], + }, + "Facility": { + "data": self.parser.facilities, + "fields": [ + "Name", + "AuthorOrganizationName", + "AuthorDate", + "Category", + "ProjectName", + "SiteName", + "LinearUnits", + "AreaUnits", + "AreaMeasurement", + "Phase", + "ModelSoftware", + "ModelProjectID", + "ModelSiteID", + "ModelBuildingID", + ], + "colours": "ririrrrrrreeee", + "order": ["Name"], + }, + "Floor": { + "data": self.parser.floors, + "fields": [ + "Name", + "AuthorOrganizationName", + "AuthorDate", + "Category", + "ModelSoftware", + "ModelObject", + "ModelID", + "Elevation", + ], + "colours": "ririeeer", + "order": ["Elevation"], + }, + "Space": { + "data": self.parser.spaces, + "fields": [ + "Name", + "AuthorOrganizationName", + "AuthorDate", + "Category", + "LevelName", + "Description", + "ModelSoftware", + "ModelID", + "BuildingRoomNumber", + "UsableHeight", + "AreaGross", + "AreaNet", + ], + "colours": "ririireerrrr", + "order": ["LevelName", "Name"], + }, + "Zone": { + "data": self.parser.zones, + "fields": [ + "Name", + "AuthorOrganizationName", + "AuthorDate", + "Category", + "SpaceName", + "ModelSoftware", + "ModelID", + "ParentZoneName", + ], + "colours": "ririieei", + "order": ["Name", "SpaceName"], + }, + "Type": { + "data": self.parser.types, + "fields": [ + "Name", + "AuthorOrganizationName", + "AuthorDate", + "Category", + "Description", + "ModelSoftware", + "ModelObject", + "ModelID", + ], + "colours": "ririreee", + "order": ["ModelObject", "Name"], + }, + "Component": { + "data": self.parser.components, + "fields": [ + "Name", + "AuthorOrganizationName", + "AuthorDate", + "TypeName", + "SpaceName", + "SystemName", + "ModelSoftware", + "ModelObject", + "ModelID", + ], + "colours": "ririiieee", + "order": ["ModelObject", "Name"], + }, + "System": { + "data": self.parser.systems, + "fields": [ + "Name", + "AuthorOrganizationName", + "AuthorDate", + "Category", + "ModelSoftware", + "ModelID", + "ParentSystemName", + ], + "colours": "ririeei", + "order": ["Name"], + }, + "Document": { + "data": self.parser.documents, + "fields": [ + "Name", + "AuthorOrganizationName", + "AuthorDate", + "Category", + "WorksheetName", + "WorksheetRow", + "Revision", + "Location", + "Description", + "SpecificationSection", + "SubmittalID", + "SourceURL", + ], + "colours": "ririiirrrrer", + "order": ["Name"], + }, + } def write(self): - self.sheets = [ - "Actor", - "Facility", - "Floor", - "Space", - "Zone", - "Type", - "Component", - "System", - # "Assembly", - # "Connection", - # "Spare", - # "Resource", - # "Job", - # "Impact", - "Document", - # "Attribute", - # "Coordinate", - # "Issue", - ] - self.write_data( - "Actor", - self.parser.actors, - [ - "Name", - "Category", - "Email", - "Phone", - "CompanyURL", - "Department", - "Address1", - "Address2", - "StateRegion", - "PostalCode", - "Country", - ], - "rirrrrrrrrr", - ["Name"], - ) - self.write_data( - "Facility", - self.parser.facilities, - [ - "Name", - "AuthorOrganizationName", - "AuthorDate", - "Category", - "ProjectName", - "SiteName", - "LinearUnits", - "AreaUnits", - "AreaMeasurement", - "Phase", - "ModelSoftware", - "ModelProjectID", - "ModelSiteID", - "ModelBuildingID", - ], - "ririrrrrrreeee", - ["Name"], - ) - self.write_data( - "Floor", - self.parser.floors, - [ - "Name", - "AuthorOrganizationName", - "AuthorDate", - "Category", - "ModelSoftware", - "ModelObject", - "ModelID", - "Elevation", - ], - "ririeeer", - ["Elevation"], - ) - self.write_data( - "Space", - self.parser.spaces, - [ - "Name", - "AuthorOrganizationName", - "AuthorDate", - "Category", - "LevelName", - "Description", - "ModelSoftware", - "ModelID", - "BuildingRoomNumber", - "UsableHeight", - "AreaGross", - "AreaNet", - ], - "ririireerrrr", - ["LevelName", "Name"], - ) - self.write_data( - "Zone", - self.parser.zones, - [ - "Name", - "AuthorOrganizationName", - "AuthorDate", - "Category", - "SpaceName", - "ModelSoftware", - "ModelID", - "ParentZoneName", - ], - "ririieei", - ["Name", "SpaceName"], - ) - self.write_data( - "Type", - self.parser.types, - [ - "Name", - "AuthorOrganizationName", - "AuthorDate", - "Category", - "ProcurementMethod", - "Description", - "ManufacturerOrganizationName", - "SupplierOrganizationName", - "ModelNumber", - "WarrantyOrganizationName", - "WarrantyDuration", - "ModelSoftware", - "ModelObject", - "ModelID", - "SpecificationSection", - "SubmittalID", - "ProductURL", - ], - "ririiriirireeerrr", - ["ModelObject", "Name"], - ) - self.write_data( - "Component", - self.parser.components, - [ - "Name", - "AuthorOrganizationName", - "AuthorDate", - "TypeName", - "SpaceName", - "InstallationDate", - "WarrantyStartDate", - "ModelSoftware", - "ModelObject", - "ModelID", - "InstalledModelNumber", - "SerialNumber", - "BarCode", - "TagNumber", - "OwnerAssetID", - "SystemName", - "FluidHotFeedName", - "FluidColdFeedName", - "ElectricPanelName", - "ElectricCircuitName", - "ControlledByName", - "InterlockedWithName", - "PartOfAssemblyName", - ], - "ririirreeerrrrriiiiiiii", - ["ModelObject", "Name"], - ) - self.write_data( - "System", - self.parser.systems, - [ - "Name", - "AuthorOrganizationName", - "AuthorDate", - "Category", - "ModelSoftware", - "ModelID", - "ParentSystemName", - ], - "ririeei", - ["Name"], - ) - # self.write_data( - # "Assembly", - # self.parser.assemblies, - # "Name", - # [ - # "Name", - # "CreatedBy", - # "CreatedOn", - # "SheetName", - # "ParentName", - # "ChildNames", - # "AssemblyType", - # "ExtSystem", - # "ExtObject", - # "ExtIdentifier", - # "Description", - # ], - # "rirrrrreeeo", - # self.parser.custom_data["assemblies"], - # ) - # self.write_data( - # "Connection", - # self.parser.connections, - # "Name", - # [ - # "Name", - # "CreatedBy", - # "CreatedOn", - # "ConnectionType", - # "SheetName", - # "RowName1", - # "RowName2", - # "RealizingElement", - # "PortName1", - # "PortName2", - # "ExtSystem", - # "ExtObject", - # "ExtIdentifier", - # "Description", - # ], - # "ririiiiiiieeeo", - # self.parser.custom_data["connections"], - # ) - # self.write_data( - # "Spare", - # self.parser.spares, - # "Name", - # [ - # "Name", - # "CreatedBy", - # "CreatedOn", - # "Category", - # "TypeName", - # "Suppliers", - # "ExtSystem", - # "ExtObject", - # "ExtIdentifier", - # "Description", - # "SetNumber", - # "PartNumber", - # ], - # "ririiieeeooo", - # self.parser.custom_data["spares"], - # ) - # self.write_data( - # "Resource", - # self.parser.resources, - # "Name", - # [ - # "Name", - # "CreatedBy", - # "CreatedOn", - # "Category", - # "ExtSystem", - # "ExtObject", - # "ExtIdentifier", - # "Description", - # ], - # "ririeeeo", - # self.parser.custom_data["resources"], - # ) - # self.write_data( - # "Job", - # self.parser.jobs, - # "Name", - # [ - # "Name", - # "CreatedBy", - # "CreatedOn", - # "Category", - # "Status", - # "TypeName", - # "Description", - # "Duration", - # "DurationUnit", - # "Start", - # "TaskStartUnit", - # "Frequency", - # "FrequencyUnit", - # "ExtSystem", - # "ExtObject", - # "ExtIdentifier", - # "TaskNumber", - # "Priors", - # "ResourceNames", - # ], - # "ririiirriririeeeoii", - # self.parser.custom_data["jobs"], - # ) - # self.write_data( - # "Impact", - # self.parser.impacts, - # "Name", - # [ - # "Name", - # "CreatedBy", - # "CreatedOn", - # "ImpactType", - # "ImpactStage", - # "SheetName", - # "RowName", - # "Value", - # "Unit", - # "LeadInTime", - # "Duration", - # "LeadOutTime", - # "ExtSystem", - # "ExtObject", - # "ExtIdentifier", - # "Description", - # ], - # "ririiiirioooeeeo", - # self.parser.custom_data["impacts"], - # ) - self.write_data( - "Document", - self.parser.documents, - [ - "Name", - "AuthorOrganizationName", - "AuthorDate", - "Category", - "WorksheetName", - "WorksheetRow", - "Revision", - "Location", - "Description", - "SpecificationSection", - "SubmittalID", - "SourceURL", - ], - "ririiirrrrer", - ["Name"], - ) - # self.write_data( - # "Attribute", - # self.parser.attributes, - # "Name", - # [ - # "Name", - # "CreatedBy", - # "CreatedOn", - # "Category", - # "SheetName", - # "RowName", - # "Value", - # "Unit", - # "ExtSystem", - # "ExtObject", - # "ExtIdentifier", - # "Description", - # "AllowedValues", - # ], - # "ririiirreeeoo", - # ) - # self.write_data( - # "Coordinate", - # self.parser.coordinates, - # "Name", - # [ - # "Name", - # "CreatedBy", - # "CreatedOn", - # "Category", - # "SheetName", - # "RowName", - # "CoordinateXAxis", - # "CoordinateYAxis", - # "CoordinateZAxis", - # "ExtSystem", - # "ExtObject", - # "ExtIdentifier", - # "ClockwiseRotation", - # "ElevationalRotation", - # "YawRotation", - # ], - # "ririiooooeeeooo", - # ) - # self.write_data( - # "Issue", - # self.parser.issues, - # "Name", - # [ - # "Name", - # "CreatedBy", - # "CreatedOn", - # "Type", - # "Risk", - # "Chance", - # "Impact", - # "SheetName1", - # "RowName1", - # "SheetName2", - # "RowName2", - # "Description", - # "Owner", - # "Mitigation", - # "ExtSystem", - # "ExtObject", - # "ExtIdentifier", - # ], - # "ririooooooooooeee", - # ) + for category, spec in self.sheets.items(): + self.write_data(category, spec["data"], spec["fields"], spec["colours"], spec["order"]) def write_data(self, sheet, data, fieldnames, colours, sort_fields): self.sheet_data[sheet] = {"headers": fieldnames, "colours": colours, "rows": []} @@ -530,7 +277,7 @@ class XlsWriter(Writer): self.cell_formats[key] = self.workbook.add_format() self.cell_formats[key].set_bg_color(value) - for sheet in self.sheets: + for sheet in self.sheets.keys(): self.write_worksheet(sheet) self.workbook.close() @@ -567,7 +314,7 @@ class OdsWriter(Writer): self.doc.automaticstyles.addElement(style) self.cell_formats[key] = style - for sheet in self.sheets: + for sheet in self.sheets.keys(): self.write_table(sheet) self.doc.save(self.filename, True)