FEature to import Microsoft Project .xml Schedules and convert to IFC schema

This commit is contained in:
bosonprojets
2021-05-06 02:40:04 +00:00
parent a031bffe6c
commit d19d4fca3e
3 changed files with 229 additions and 0 deletions
@@ -61,6 +61,7 @@ classes = (
operator.UnassignProduct,
operator.GenerateGanttChart,
operator.ImportP6,
operator.ImportMSP,
operator.LoadTaskProperties,
operator.SelectTaskRelatedProducts,
operator.VisualiseWorkScheduleDate,
@@ -82,6 +83,7 @@ classes = (
def menu_func_import(self, context):
self.layout.operator(operator.ImportP6.bl_idname, text="P6 (.xml)")
self.layout.operator(operator.ImportMSP.bl_idname, text="Microsoft Project (.xml)")
def register():
@@ -868,6 +868,26 @@ class ImportP6(bpy.types.Operator, ImportHelper):
print("Import finished in {:.2f} seconds".format(time.time() - start))
return {"FINISHED"}
class ImportMSP(bpy.types.Operator, ImportHelper):
bl_idname = "import_msp.bim"
bl_label = "Import MSP"
filename_ext = ".xml"
filter_glob: bpy.props.StringProperty(default="*.xml", options={"HIDDEN"})
def execute(self, context):
from ifcp6.msp2ifc import MSP2Ifc
self.file = IfcStore.get_file()
start = time.time()
msp2ifc = MSP2Ifc()
msp2ifc.xml = self.filepath
msp2ifc.file = self.file
msp2ifc.work_plan = self.file.by_type("IfcWorkPlan")[0] if self.file.by_type("IfcWorkPlan") else None
msp2ifc.execute()
Data.load(IfcStore.get_file())
print("Import finished in {:.2f} seconds".format(time.time() - start))
return {"FINISHED"}
class EnableEditingWorkCalendarTimes(bpy.types.Operator):
bl_idname = "bim.enable_editing_work_calendar_times"
+207
View File
@@ -0,0 +1,207 @@
import datetime
from datetime import timedelta
import ifcopenshell
import ifcopenshell.api
import ifcopenshell.util.date
import xml.etree.ElementTree as ET
import blenderbim.bim.ifc
class MSP2Ifc:
def __init__(self):
self.xml = None
self.file = None
self.ns = None
self.work_plan = None
self.project = {}
self.calendars = {}
self.wbs = {}
self.root_activites = []
self.tasks = {}
self.relationships = {}
self.day_map = {
"2": 1,
"3": 2,
"4": 3,
"5": 4,
"6": 5,
"7": 6,
"1": 7,
}
def execute(self):
self.parse_xml()
self.create_ifc()
def parse_xml(self):
tree = ET.parse(self.xml)
project = tree.getroot()
self.ns = {"pr": project.tag[1:].partition("}")[0]}
self.project["Name"] = project.find("pr:Name", self.ns).text
self.parse_task_xml(project)
self.parse_calendar_xml(project)
def parse_task_xml(self, project):
for task in project.find("pr:Tasks", self.ns):
task_id = task.find("pr:UID", self.ns).text
task_index_level = task.find("pr:OutlineLevel", self.ns).text
wbs_id = task.find("pr:WBS", self.ns).text
relationship = task.find("pr:PredecessorLink", self.ns)
self.tasks[task_id] = {
"Name": task.find("pr:Name", self.ns).text,
"UID": task.find("pr:UID", self.ns).text,
"Start": datetime.datetime.fromisoformat(task.find("pr:Start", self.ns).text),
"Finish": datetime.datetime.fromisoformat(task.find("pr:Finish", self.ns).text),
"Duration": ifcopenshell.util.date.ifc2datetime(task.find("pr:Duration", self.ns).text),
"Priority": task.find("pr:Priority", self.ns).text,
"CalendarUID": task.find("pr:CalendarUID", self.ns).text,
"PredecessorTask": relationship.find("pr:PredecessorUID", self.ns).text if relationship else None,
"ifc": None,
}
def parse_calendar_xml(self, project):
for calendar in project.find("pr:Calendars", self.ns).findall("pr:Calendar", self.ns):
calendar_id = calendar.find("pr:UID", self.ns).text
week_days = []
for week_day in calendar.find("pr:WeekDays", self.ns).findall(
"pr:WeekDay", self.ns
):
working_times = []
if week_day.find("pr:WorkingTimes", self.ns):
for working_time in week_day.find("pr:WorkingTimes", self.ns).findall("pr:WorkingTime", self.ns):
if working_time.find("pr:FromTime", self.ns) is None:
continue
working_times.append(
{
"Start": datetime.time.fromisoformat(working_time.find("pr:FromTime", self.ns).text),
"Finish": datetime.time.fromisoformat(working_time.find("pr:ToTime", self.ns).text),
}
)
week_days.append(
{
"DayType": week_day.find("pr:DayType", self.ns).text,
"WorkingTimes": working_times,
"ifc": None,
}
)
exceptions = {}
self.calendars[calendar_id] = {
"Name": calendar.find("pr:Name", self.ns).text,
"StandardWorkWeek": week_days,
}
def create_ifc(self):
work_schedule = self.create_work_schedule()
self.create_tasks(work_schedule)
self.create_calendars()
self.create_rel_sequences()
def create_tasks(self, work_schedule):
for task_id in self.tasks:
self.create_task_from_task(self.tasks[task_id], None, 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_calendars(self):
for calendar in self.calendars.values():
calendar["ifc"] = ifcopenshell.api.run(
"sequence.add_work_calendar", self.file, name=calendar["Name"]
)
self.process_working_week(calendar["StandardWorkWeek"], calendar["ifc"])
def create_task_from_task(self, task, wbs, work_schedule):
task["ifc"] = ifcopenshell.api.run(
"sequence.add_task",
self.file,
work_schedule=None if wbs else work_schedule,
)
ifcopenshell.api.run(
"sequence.edit_task",
self.file,
task=task["ifc"],
attributes={
"Name": task["Name"],
"Identification": task["UID"],
"IsMilestone": task["Start"] == task["Finish"],
},
)
task_time = ifcopenshell.api.run("sequence.add_task_time", self.file, task=task["ifc"])
ifcopenshell.api.run(
"sequence.edit_task_time",
self.file,
task_time=task_time,
attributes={
"ScheduleStart": task["Start"],
"ScheduleFinish": task["Finish"],
"DurationType": "WORKTIME" if task["Duration"] else None,
"ScheduleDuration": task["Duration"] if task["Duration"] else None,
},
)
def process_working_week(self, week, calendar):
for day in week:
if day["ifc"]:
continue
day["ifc"] = ifcopenshell.api.run(
"sequence.add_work_time", self.file, work_calendar=calendar, time_type="WorkingTimes"
)
weekday_component = [self.day_map[day["DayType"]]]
for day2 in week:
if day["DayType"] == day2["DayType"]:
continue
if day["WorkingTimes"] == day2["WorkingTimes"]:
weekday_component.append(self.day_map[day2["DayType"]])
# Don't process the next day, as we can group it
day2["ifc"] = day["ifc"]
work_time_name = "Weekdays: {}".format(", ".join([str(c) for c in sorted(weekday_component)]))
ifcopenshell.api.run(
"sequence.edit_work_time",
self.file,
work_time=day["ifc"],
attributes={"Name": work_time_name},
)
recurrence = ifcopenshell.api.run(
"sequence.assign_recurrence_pattern", self.file, parent=day["ifc"], recurrence_type="WEEKLY"
)
ifcopenshell.api.run(
"sequence.edit_recurrence_pattern",
self.file,
recurrence_pattern=recurrence,
attributes={"WeekdayComponent": weekday_component},
)
for work_time in day["WorkingTimes"]:
ifcopenshell.api.run(
"sequence.add_time_period",
self.file,
recurrence_pattern=recurrence,
start_time=work_time["Start"],
end_time=work_time["Finish"],
)
def create_rel_sequences(self):
self.sequence_type_map = {
"Start to Start": "START_START",
"Start to Finish": "START_FINISH",
"Finish to Start": "FINISH_START",
"Finish to Finish": "FINISH_FINISH",
}
for key, value in self.tasks.items():
rel_sequence = ifcopenshell.api.run(
"sequence.assign_sequence",
self.file,
related_process = self.tasks[key]["ifc"],
relating_process = self.tasks[self.tasks[key]["PredecessorTask"]]["ifc"]
)
ifcopenshell.api.run(
"sequence.edit_sequence",
self.file,
rel_sequence=rel_sequence,
)