Fix bug with broken MS Project to IFC conversion

This commit is contained in:
Dion Moult
2022-02-21 13:51:26 +11:00
parent c938d6ae9b
commit 5c9984a12a
2 changed files with 163 additions and 64 deletions
+25 -35
View File
@@ -6,17 +6,16 @@ from datetime import datetime, timedelta, date
class ScheduleIfcGenerator:
def __init__(self, file, output, settings):
self.file = file
self.work_plan = settings['work_plan']
self.project = settings['project']
self.calendars = settings['calendars']
self.wbs = settings['wbs']
self.root_activites = settings['root_activities']
self.activities = settings['activities']
self.relationships = settings['relationships']
self.resources = settings['resources']
self.work_plan = settings["work_plan"]
self.project = settings["project"]
self.calendars = settings["calendars"]
self.wbs = settings["wbs"]
self.root_activites = settings["root_activities"]
self.activities = settings["activities"]
self.relationships = settings["relationships"]
self.resources = settings["resources"]
self.output = output
self.day_map = {
"Monday": 1,
@@ -27,7 +26,7 @@ class ScheduleIfcGenerator:
"Saturday": 6,
"Sunday": 7,
}
def create_ifc(self):
if not self.file:
self.file = self.create_boilerplate_ifc()
@@ -48,9 +47,7 @@ class ScheduleIfcGenerator:
def create_calendars(self):
for calendar in self.calendars.values():
calendar["ifc"] = ifcopenshell.api.run(
"sequence.add_work_calendar", self.file, name=calendar["Name"]
)
calendar["ifc"] = ifcopenshell.api.run("sequence.add_work_calendar", self.file, name=calendar["Name"])
self.process_working_week(calendar["StandardWorkWeek"], calendar["ifc"])
self.process_exceptions(calendar.get("HolidayOrExceptions"), calendar["ifc"])
@@ -226,12 +223,12 @@ class ScheduleIfcGenerator:
"Identification": str(activity["Identification"]),
"Status": activity["Status"],
"IsMilestone": activity["StartDate"] == activity["FinishDate"],
"PredefinedType": "CONSTRUCTION"
"PredefinedType": "CONSTRUCTION",
},
)
task_time = ifcopenshell.api.run("sequence.add_task_time", self.file, task=activity["ifc"])
calendar = self.calendars[activity["CalendarObjectId"]]
#print(calendar, self.calendars)
# print(calendar, self.calendars)
# Seems intermittently crashy - can we investigate for larger files?
ifcopenshell.api.run(
"control.assign_control",
@@ -288,43 +285,36 @@ class ScheduleIfcGenerator:
lag_value=timedelta(days=lag / float(calendar["HoursPerDay"] or 8)),
duration_type="WORKTIME",
)
def create_resources(self):
# print("Resources", self.resources)
if self.resources:
for id, resource in self.resources.items():
parent = self.resources.get(resource.get("ParentObjectId"))
if parent:
if not parent.get("ifc"):
parent["ifc"] = ifcopenshell.api.run(
"resource.add_resource",
self.file,
**{"ifc_class": "IfcCrewResource",
"name": parent['Name']}
**{"ifc_class": "IfcCrewResource", "name": parent["Name"]},
)
if parent:
resource["ifc"] = ifcopenshell.api.run(
"resource.add_resource",
self.file,
**{"parent_resource": parent["ifc"] if parent else None,
"ifc_class": "IfcCrewResource",
"name": resource['Name']
}
)
"resource.add_resource",
self.file,
**{
"parent_resource": parent["ifc"] if parent else None,
"ifc_class": "IfcCrewResource",
"name": resource["Name"],
},
)
else:
resource["ifc"] = ifcopenshell.api.run(
"resource.add_resource",
self.file,
**{ "ifc_class": "IfcCrewResource",
"name": resource['Name']
}
)
"resource.add_resource", self.file, **{"ifc_class": "IfcCrewResource", "name": resource["Name"]}
)
print(self.resources)
def create_boilerplate_ifc(self):
self.file = ifcopenshell.file(schema="IFC4")
self.work_plan = self.file.create_entity("IfcWorkPlan")
+138 -29
View File
@@ -1,4 +1,3 @@
# Ifc4D - IFC scheduling utility
# Copyright (C) 2021 Dion Moult <dion@thinkmoult.com>
#
@@ -23,7 +22,6 @@ import ifcopenshell
import ifcopenshell.api
import ifcopenshell.util.date
import xml.etree.ElementTree as ET
from .common import ScheduleIfcGenerator
class MSP2Ifc:
@@ -34,34 +32,13 @@ class MSP2Ifc:
self.work_plan = None
self.project = {}
self.calendars = {}
self.wbs = {}
self.root_activites = []
self.activities = []
self.tasks = {}
self.relationships = {}
self.resources = {}
self.output = None
self.RESOURCE_TYPES_MAPPING = {
'1' : "LABOR",
'0' : "MATERIAL",
'2' : None
}
self.RESOURCE_TYPES_MAPPING = {"1": "LABOR", "0": "MATERIAL", "2": None}
def execute(self):
self.parse_xml()
settings = {
"work_plan":self.work_plan,
"project": self.project,
"calendars": self.calendars,
"wbs": self.wbs,
"root_activities": self.root_activites,
"activities": self.activities,
"relationships": self.relationships,
"resources": self.resources
}
ifcCreator = ScheduleIfcGenerator(self.file, self.output, settings)
ifcCreator.create_ifc()
self.create_ifc()
def parse_xml(self):
tree = ET.parse(self.xml)
@@ -72,8 +49,8 @@ class MSP2Ifc:
self.outline_parents = {}
self.parse_task_xml(project)
self.parse_calendar_xml(project)
self.parse_resources_xml(project)
# TODO Doesn't do anything right now
# self.parse_resources_xml(project)
def parse_relationship_xml(self, task):
relationships = {}
@@ -143,7 +120,139 @@ class MSP2Ifc:
"Name": calendar.find("pr:Name", self.ns).text,
"StandardWorkWeek": week_days,
}
def create_ifc(self):
if not self.file:
self.create_boilerplate_ifc()
if not self.work_plan:
self.work_plan = ifcopenshell.api.run("sequence.add_work_plan", self.file)
work_schedule = self.create_work_schedule()
self.create_tasks(work_schedule)
self.create_calendars()
self.create_rel_sequences()
def create_boilerplate_ifc(self):
self.file = ifcopenshell.file(schema="IFC4")
self.work_plan = self.file.create_entity("IfcWorkPlan")
def create_tasks(self, work_schedule):
for task_id in self.tasks:
task = self.tasks[task_id]
if task["OutlineLevel"] == 0:
self.create_task(task, work_schedule=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(self, task, work_schedule=None, parent_task=None):
task["ifc"] = ifcopenshell.api.run(
"sequence.add_task",
self.file,
work_schedule=work_schedule if work_schedule else None,
parent_task=parent_task["ifc"] if parent_task else None,
)
ifcopenshell.api.run(
"sequence.edit_task",
self.file,
task=task["ifc"],
attributes={
"Name": task["Name"],
"Identification": task["OutlineNumber"],
"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,
},
)
for subtask_id in task["subtasks"]:
self.create_task(self.tasks[subtask_id], parent_task=task)
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 = [int(day["DayType"])]
for day2 in week:
if day["DayType"] == day2["DayType"]:
continue
if day["WorkingTimes"] == day2["WorkingTimes"]:
weekday_component.append(int(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 = {
"1": "START_START",
"2": "START_FINISH",
"3": "FINISH_START",
"4": "FINISH_FINISH",
"0": "NOTDEFINED",
}
for task in self.tasks.values():
if not task["PredecessorTasks"]:
continue
for predecessor in task["PredecessorTasks"].values():
rel_sequence = ifcopenshell.api.run(
"sequence.assign_sequence",
self.file,
related_process=task["ifc"],
relating_process=self.tasks[predecessor["PredecessorTask"]]["ifc"],
)
if predecessor["Type"]:
ifcopenshell.api.run(
"sequence.edit_sequence",
self.file,
rel_sequence=rel_sequence,
attributes={"SequenceType": self.sequence_type_map[predecessor["Type"]]},
)
def parse_resources_xml(self, project):
resources_lst = project.find("pr:Resources", self.ns)
resources = resources_lst.findall("pr:Resource", self.ns)
@@ -152,7 +261,7 @@ class MSP2Ifc:
name = resource.find("pr:Name", self.ns)
id = resource.find("pr:ID", self.ns).text
if name is not None:
name = name.text
name = name.text
else:
# print("- No Name")
name = None