diff --git a/src/blenderbim/blenderbim/bim/module/sequence/__init__.py b/src/blenderbim/blenderbim/bim/module/sequence/__init__.py index df151b8ef5..9184ae5ab8 100644 --- a/src/blenderbim/blenderbim/bim/module/sequence/__init__.py +++ b/src/blenderbim/blenderbim/bim/module/sequence/__init__.py @@ -41,6 +41,7 @@ classes = ( operator.AssignProduct, operator.UnassignProduct, operator.GenerateGanttChart, + operator.ImportP6, prop.WorkPlan, prop.BIMWorkPlanProperties, prop.Task, @@ -56,13 +57,19 @@ classes = ( ) +def menu_func_import(self, context): + self.layout.operator(operator.ImportP6.bl_idname, text="P6 (.xml)") + + def register(): bpy.types.Scene.BIMWorkPlanProperties = bpy.props.PointerProperty(type=prop.BIMWorkPlanProperties) bpy.types.Scene.BIMWorkScheduleProperties = bpy.props.PointerProperty(type=prop.BIMWorkScheduleProperties) bpy.types.Scene.BIMWorkCalendarProperties = bpy.props.PointerProperty(type=prop.BIMWorkCalendarProperties) + bpy.types.TOPBAR_MT_file_import.append(menu_func_import) def unregister(): del bpy.types.Scene.BIMWorkPlanProperties del bpy.types.Scene.BIMWorkScheduleProperties del bpy.types.Scene.BIMWorkCalendarProperties + bpy.types.TOPBAR_MT_file_import.remove(menu_func_import) diff --git a/src/blenderbim/blenderbim/bim/module/sequence/operator.py b/src/blenderbim/blenderbim/bim/module/sequence/operator.py index dcb00ca14c..3e49d5a28e 100644 --- a/src/blenderbim/blenderbim/bim/module/sequence/operator.py +++ b/src/blenderbim/blenderbim/bim/module/sequence/operator.py @@ -1,12 +1,14 @@ import os import bpy import json +import time import pystache import webbrowser import ifcopenshell.api from datetime import datetime from dateutil import parser from blenderbim.bim.ifc import IfcStore +from bpy_extras.io_utils import ImportHelper from ifcopenshell.api.sequence.data import Data @@ -808,3 +810,23 @@ class DisableEditingWorkCalendar(bpy.types.Operator): def execute(self, context): context.scene.BIMWorkCalendarProperties.active_work_calendar_id = 0 return {"FINISHED"} + + +class ImportP6(bpy.types.Operator, ImportHelper): + bl_idname = "import_p6.bim" + bl_label = "Import P6" + filename_ext = ".xml" + filter_glob: bpy.props.StringProperty(default="*.xml", options={"HIDDEN"}) + + def execute(self, context): + from ifcp6.p62ifc import P62Ifc + self.file = IfcStore.get_file() + start = time.time() + p62ifc = P62Ifc() + p62ifc.xml = self.filepath + p62ifc.file = self.file + p62ifc.work_plan = self.file.by_type("IfcWorkPlan")[0] + p62ifc.execute() + Data.load(IfcStore.get_file()) + print("Import finished in {:.2f} seconds".format(time.time() - start)) + return {"FINISHED"} diff --git a/src/blenderbim/p6_to_ifc.py b/src/blenderbim/p6_to_ifc.py deleted file mode 100644 index 68ab8bd74f..0000000000 --- a/src/blenderbim/p6_to_ifc.py +++ /dev/null @@ -1,129 +0,0 @@ -import ifcopenshell -import ifcopenshell.util.date -import xml.etree.ElementTree as ET - - -class P6ToIfc: - def __init__(self): - self.project = {} - self.wbs = {} - - def execute(self): - self.parse_xml() - self.create_ifc() - - def parse_xml(self): - tree = ET.parse("p6.xml") - ns = {"pr": "http://xmlns.oracle.com/Primavera/P6/V19.12/API/BusinessObjects"} - root = tree.getroot() - project = root.find("pr:Project", ns) - self.project["Name"] = project.find("pr:Name", ns).text - - for wbs in project.findall("pr:WBS", ns): - self.wbs[wbs.find("pr:ObjectId", ns).text] = { - "Name": wbs.find("pr:Name", ns).text, - "ParentObjectId": wbs.find("pr:ParentObjectId", ns).text, - "ifc": None, - "rel": None, - "activities": [], - } - - for activity in project.findall("pr:Activity", ns): - self.wbs[activity.find("pr:WBSObjectId", ns).text]["activities"].append( - { - "Name": activity.find("pr:Name", ns).text, - "Identification": activity.find("pr:Id", ns).text, - "StartDate": activity.find("pr:StartDate", ns).text, - "FinishDate": activity.find("pr:FinishDate", ns).text, - "ifc": None, - } - ) - - def get_wbs(self, wbs): - return {"Name": wbs.find("pr:Name", ns).text, "subtasks": []} - - def create_ifc(self): - self.file = ifcopenshell.file(schema="IFC4") - # self.file = ifcopenshell.file(schema="IFC2X3") - self.root = self.file.create_entity( - "IfcTask", **{"GlobalId": ifcopenshell.guid.new(), "Name": self.project["Name"]} - ) - self.root_rel = self.create_rel_nests(self.root) - - for wbs in self.wbs.values(): - wbs["ifc"] = self.file.create_entity( - "IfcTask", **{"GlobalId": ifcopenshell.guid.new(), "Name": wbs["Name"]} - ) - if wbs["ParentObjectId"]: - parent_wbs = self.wbs[wbs["ParentObjectId"]] - if not parent_wbs["rel"]: - parent_wbs["rel"] = self.create_rel_nests(parent_wbs["ifc"]) - rel = parent_wbs["rel"] - else: - rel = self.root_rel - self.append_to_rel(rel, wbs["ifc"]) - - for activity in wbs["activities"]: - if not wbs["rel"]: - wbs["rel"] = self.create_rel_nests(wbs["ifc"]) - - if self.file.schema == "IFC2X3": - activity["TimeForTask"] = self.file.create_entity( - "IfcScheduleTimeControl", - **{ - "ScheduleStart": self.file.create_entity( - "IfcCalendarDate", - **ifcopenshell.util.date.datetime2ifc(activity["StartDate"], "IfcCalendarDate") - ), - "ScheduleFinish": self.file.create_entity( - "IfcCalendarDate", - **ifcopenshell.util.date.datetime2ifc(activity["FinishDate"], "IfcCalendarDate") - ), - } - ) - else: - activity["TaskTime"] = self.file.create_entity( - "IfcTaskTime", - **{"ScheduleStart": activity["StartDate"], "ScheduleFinish": activity["FinishDate"]} - ) - - is_milestone = activity["StartDate"] == activity["FinishDate"] - activity["ifc"] = self.file.create_entity( - "IfcTask", - **{ - "GlobalId": ifcopenshell.guid.new(), - "Name": activity["Name"], - "Identification": activity["Identification"], - "IsMilestone": is_milestone, - } - ) - - if self.file.schema == "IFC2X3": - # Invalid, but reading the IFC2X3 docs gives me a headache - self.file.create_entity( - "IfcRelAssignsTasks", - **{ - "GlobalId": ifcopenshell.guid.new(), - "RelatedObjects": [activity["ifc"]], - "TimeForTask": activity["TimeForTask"], - } - ) - else: - activity["ifc"].TaskTime = activity["TaskTime"] - - self.append_to_rel(wbs["rel"], activity["ifc"]) - self.file.write("p6.ifc") - - def create_rel_nests(self, relating_object): - return self.file.create_entity( - "IfcRelNests", **{"GlobalId": ifcopenshell.guid.new(), "RelatingObject": relating_object} - ) - - def append_to_rel(self, rel, element): - related_objects = list(rel.RelatedObjects or []) - related_objects.append(element) - rel.RelatedObjects = related_objects - - -p6_to_ifc = P6ToIfc() -p6_to_ifc.execute() diff --git a/src/ifcp6/ifcp6/p62ifc.py b/src/ifcp6/ifcp6/p62ifc.py new file mode 100644 index 0000000000..8e9a2deddd --- /dev/null +++ b/src/ifcp6/ifcp6/p62ifc.py @@ -0,0 +1,114 @@ +import ifcopenshell +import ifcopenshell.api +import ifcopenshell.util.date +import xml.etree.ElementTree as ET +from datetime import datetime + + +class P62Ifc: + def __init__(self): + self.xml = None + self.file = None + self.work_plan = None + self.project = {} + self.wbs = {} + self.activity = {} + + def execute(self): + self.parse_xml() + self.create_ifc() + + def parse_xml(self): + tree = ET.parse(self.xml) + ns = {"pr": "http://xmlns.oracle.com/Primavera/P6/V19.12/API/BusinessObjects"} + root = tree.getroot() + project = root.find("pr:Project", ns) + self.project["Name"] = project.find("pr:Name", ns).text + + for wbs in project.findall("pr:WBS", ns): + self.wbs[wbs.find("pr:ObjectId", ns).text] = { + "Name": wbs.find("pr:Name", ns).text, + "Code": wbs.find("pr:Code", ns).text, + "ParentObjectId": wbs.find("pr:ParentObjectId", ns).text, + "ifc": None, + "rel": None, + "activities": [], + } + + for activity in project.findall("pr:Activity", ns): + self.wbs[activity.find("pr:WBSObjectId", ns).text]["activities"].append( + { + "Name": activity.find("pr:Name", ns).text, + "Identification": activity.find("pr:Id", ns).text, + "StartDate": datetime.fromisoformat(activity.find("pr:StartDate", ns).text), + "FinishDate": datetime.fromisoformat(activity.find("pr:FinishDate", ns).text), + "Status": activity.find("pr:Status", ns).text, + "ifc": None, + } + ) + + def get_wbs(self, wbs): + return {"Name": wbs.find("pr:Name", ns).text, "subtasks": []} + + def create_ifc(self): + if not self.file: + self.file = self.create_boilerplate_ifc() + work_schedule = self.create_work_schedule() + self.create_tasks(work_schedule) + + def create_work_schedule(self): + return ifcopenshell.api.run( + "sequence.add_work_schedule", self.file, name=self.project["Name"], work_plan=self.work_plan + ) + + def create_tasks(self, work_schedule): + for wbs in self.wbs.values(): + self.create_task_from_wbs(wbs, work_schedule) + + def create_task_from_wbs(self, wbs, work_schedule): + wbs["ifc"] = ifcopenshell.api.run( + "sequence.add_task", + self.file, + work_schedule=None if wbs["ParentObjectId"] else work_schedule, + parent_task=self.wbs[wbs["ParentObjectId"]]["ifc"] if wbs["ParentObjectId"] else None, + ) + ifcopenshell.api.run( + "sequence.edit_task", + self.file, + task=wbs["ifc"], + attributes={"Name": wbs["Name"], "Identification": wbs["Code"]}, + ) + for activity in wbs["activities"]: + self.create_task_from_activity(activity, wbs, work_schedule) + + def create_task_from_activity(self, activity, wbs, work_schedule): + activity["ifc"] = ifcopenshell.api.run( + "sequence.add_task", + self.file, + parent_task=wbs["ifc"], + ) + ifcopenshell.api.run( + "sequence.edit_task", + self.file, + task=activity["ifc"], + attributes={ + "Name": activity["Name"], + "Identification": activity["Identification"], + "Status": activity["Status"], + "IsMilestone": activity["StartDate"] == activity["FinishDate"], + }, + ) + task_time = ifcopenshell.api.run("sequence.add_task_time", self.file, task=activity["ifc"]) + ifcopenshell.api.run( + "sequence.edit_task_time", + self.file, + task_time=task_time, + attributes={ + "ScheduleStart": activity["StartDate"], + "ScheduleFinish": activity["FinishDate"], + }, + ) + + def create_boilerplate_ifc(self): + self.file = ifcopenshell.file(schema="IFC4") + self.work_plan = self.file.create_entity("IfcWorkPlan")