mirror of
https://github.com/IfcOpenShell/IfcOpenShell.git
synced 2026-09-24 00:07:53 +00:00
Fix bug with broken MS Project to IFC conversion
This commit is contained in:
+25
-35
@@ -6,17 +6,16 @@ from datetime import datetime, timedelta, date
|
|||||||
|
|
||||||
|
|
||||||
class ScheduleIfcGenerator:
|
class ScheduleIfcGenerator:
|
||||||
|
|
||||||
def __init__(self, file, output, settings):
|
def __init__(self, file, output, settings):
|
||||||
self.file = file
|
self.file = file
|
||||||
self.work_plan = settings['work_plan']
|
self.work_plan = settings["work_plan"]
|
||||||
self.project = settings['project']
|
self.project = settings["project"]
|
||||||
self.calendars = settings['calendars']
|
self.calendars = settings["calendars"]
|
||||||
self.wbs = settings['wbs']
|
self.wbs = settings["wbs"]
|
||||||
self.root_activites = settings['root_activities']
|
self.root_activites = settings["root_activities"]
|
||||||
self.activities = settings['activities']
|
self.activities = settings["activities"]
|
||||||
self.relationships = settings['relationships']
|
self.relationships = settings["relationships"]
|
||||||
self.resources = settings['resources']
|
self.resources = settings["resources"]
|
||||||
self.output = output
|
self.output = output
|
||||||
self.day_map = {
|
self.day_map = {
|
||||||
"Monday": 1,
|
"Monday": 1,
|
||||||
@@ -27,7 +26,7 @@ class ScheduleIfcGenerator:
|
|||||||
"Saturday": 6,
|
"Saturday": 6,
|
||||||
"Sunday": 7,
|
"Sunday": 7,
|
||||||
}
|
}
|
||||||
|
|
||||||
def create_ifc(self):
|
def create_ifc(self):
|
||||||
if not self.file:
|
if not self.file:
|
||||||
self.file = self.create_boilerplate_ifc()
|
self.file = self.create_boilerplate_ifc()
|
||||||
@@ -48,9 +47,7 @@ class ScheduleIfcGenerator:
|
|||||||
|
|
||||||
def create_calendars(self):
|
def create_calendars(self):
|
||||||
for calendar in self.calendars.values():
|
for calendar in self.calendars.values():
|
||||||
calendar["ifc"] = ifcopenshell.api.run(
|
calendar["ifc"] = ifcopenshell.api.run("sequence.add_work_calendar", self.file, name=calendar["Name"])
|
||||||
"sequence.add_work_calendar", self.file, name=calendar["Name"]
|
|
||||||
)
|
|
||||||
self.process_working_week(calendar["StandardWorkWeek"], calendar["ifc"])
|
self.process_working_week(calendar["StandardWorkWeek"], calendar["ifc"])
|
||||||
self.process_exceptions(calendar.get("HolidayOrExceptions"), calendar["ifc"])
|
self.process_exceptions(calendar.get("HolidayOrExceptions"), calendar["ifc"])
|
||||||
|
|
||||||
@@ -226,12 +223,12 @@ class ScheduleIfcGenerator:
|
|||||||
"Identification": str(activity["Identification"]),
|
"Identification": str(activity["Identification"]),
|
||||||
"Status": activity["Status"],
|
"Status": activity["Status"],
|
||||||
"IsMilestone": activity["StartDate"] == activity["FinishDate"],
|
"IsMilestone": activity["StartDate"] == activity["FinishDate"],
|
||||||
"PredefinedType": "CONSTRUCTION"
|
"PredefinedType": "CONSTRUCTION",
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
task_time = ifcopenshell.api.run("sequence.add_task_time", self.file, task=activity["ifc"])
|
task_time = ifcopenshell.api.run("sequence.add_task_time", self.file, task=activity["ifc"])
|
||||||
calendar = self.calendars[activity["CalendarObjectId"]]
|
calendar = self.calendars[activity["CalendarObjectId"]]
|
||||||
#print(calendar, self.calendars)
|
# print(calendar, self.calendars)
|
||||||
# Seems intermittently crashy - can we investigate for larger files?
|
# Seems intermittently crashy - can we investigate for larger files?
|
||||||
ifcopenshell.api.run(
|
ifcopenshell.api.run(
|
||||||
"control.assign_control",
|
"control.assign_control",
|
||||||
@@ -288,43 +285,36 @@ class ScheduleIfcGenerator:
|
|||||||
lag_value=timedelta(days=lag / float(calendar["HoursPerDay"] or 8)),
|
lag_value=timedelta(days=lag / float(calendar["HoursPerDay"] or 8)),
|
||||||
duration_type="WORKTIME",
|
duration_type="WORKTIME",
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def create_resources(self):
|
def create_resources(self):
|
||||||
# print("Resources", self.resources)
|
# print("Resources", self.resources)
|
||||||
if self.resources:
|
if self.resources:
|
||||||
for id, resource in self.resources.items():
|
for id, resource in self.resources.items():
|
||||||
|
|
||||||
parent = self.resources.get(resource.get("ParentObjectId"))
|
parent = self.resources.get(resource.get("ParentObjectId"))
|
||||||
if parent:
|
if parent:
|
||||||
if not parent.get("ifc"):
|
if not parent.get("ifc"):
|
||||||
parent["ifc"] = ifcopenshell.api.run(
|
parent["ifc"] = ifcopenshell.api.run(
|
||||||
"resource.add_resource",
|
"resource.add_resource",
|
||||||
self.file,
|
self.file,
|
||||||
**{"ifc_class": "IfcCrewResource",
|
**{"ifc_class": "IfcCrewResource", "name": parent["Name"]},
|
||||||
"name": parent['Name']}
|
|
||||||
)
|
)
|
||||||
if parent:
|
if parent:
|
||||||
resource["ifc"] = ifcopenshell.api.run(
|
resource["ifc"] = ifcopenshell.api.run(
|
||||||
"resource.add_resource",
|
"resource.add_resource",
|
||||||
self.file,
|
self.file,
|
||||||
**{"parent_resource": parent["ifc"] if parent else None,
|
**{
|
||||||
"ifc_class": "IfcCrewResource",
|
"parent_resource": parent["ifc"] if parent else None,
|
||||||
"name": resource['Name']
|
"ifc_class": "IfcCrewResource",
|
||||||
}
|
"name": resource["Name"],
|
||||||
)
|
},
|
||||||
|
)
|
||||||
else:
|
else:
|
||||||
resource["ifc"] = ifcopenshell.api.run(
|
resource["ifc"] = ifcopenshell.api.run(
|
||||||
"resource.add_resource",
|
"resource.add_resource", self.file, **{"ifc_class": "IfcCrewResource", "name": resource["Name"]}
|
||||||
self.file,
|
)
|
||||||
**{ "ifc_class": "IfcCrewResource",
|
|
||||||
"name": resource['Name']
|
|
||||||
}
|
|
||||||
)
|
|
||||||
print(self.resources)
|
print(self.resources)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def create_boilerplate_ifc(self):
|
def create_boilerplate_ifc(self):
|
||||||
self.file = ifcopenshell.file(schema="IFC4")
|
self.file = ifcopenshell.file(schema="IFC4")
|
||||||
self.work_plan = self.file.create_entity("IfcWorkPlan")
|
self.work_plan = self.file.create_entity("IfcWorkPlan")
|
||||||
|
|||||||
+138
-29
@@ -1,4 +1,3 @@
|
|||||||
|
|
||||||
# Ifc4D - IFC scheduling utility
|
# Ifc4D - IFC scheduling utility
|
||||||
# Copyright (C) 2021 Dion Moult <dion@thinkmoult.com>
|
# Copyright (C) 2021 Dion Moult <dion@thinkmoult.com>
|
||||||
#
|
#
|
||||||
@@ -23,7 +22,6 @@ import ifcopenshell
|
|||||||
import ifcopenshell.api
|
import ifcopenshell.api
|
||||||
import ifcopenshell.util.date
|
import ifcopenshell.util.date
|
||||||
import xml.etree.ElementTree as ET
|
import xml.etree.ElementTree as ET
|
||||||
from .common import ScheduleIfcGenerator
|
|
||||||
|
|
||||||
|
|
||||||
class MSP2Ifc:
|
class MSP2Ifc:
|
||||||
@@ -34,34 +32,13 @@ class MSP2Ifc:
|
|||||||
self.work_plan = None
|
self.work_plan = None
|
||||||
self.project = {}
|
self.project = {}
|
||||||
self.calendars = {}
|
self.calendars = {}
|
||||||
self.wbs = {}
|
|
||||||
self.root_activites = []
|
|
||||||
self.activities = []
|
|
||||||
self.tasks = {}
|
self.tasks = {}
|
||||||
self.relationships = {}
|
|
||||||
self.resources = {}
|
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):
|
def execute(self):
|
||||||
self.parse_xml()
|
self.parse_xml()
|
||||||
settings = {
|
self.create_ifc()
|
||||||
"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()
|
|
||||||
|
|
||||||
def parse_xml(self):
|
def parse_xml(self):
|
||||||
tree = ET.parse(self.xml)
|
tree = ET.parse(self.xml)
|
||||||
@@ -72,8 +49,8 @@ class MSP2Ifc:
|
|||||||
self.outline_parents = {}
|
self.outline_parents = {}
|
||||||
self.parse_task_xml(project)
|
self.parse_task_xml(project)
|
||||||
self.parse_calendar_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):
|
def parse_relationship_xml(self, task):
|
||||||
relationships = {}
|
relationships = {}
|
||||||
@@ -143,7 +120,139 @@ class MSP2Ifc:
|
|||||||
"Name": calendar.find("pr:Name", self.ns).text,
|
"Name": calendar.find("pr:Name", self.ns).text,
|
||||||
"StandardWorkWeek": week_days,
|
"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):
|
def parse_resources_xml(self, project):
|
||||||
resources_lst = project.find("pr:Resources", self.ns)
|
resources_lst = project.find("pr:Resources", self.ns)
|
||||||
resources = resources_lst.findall("pr:Resource", self.ns)
|
resources = resources_lst.findall("pr:Resource", self.ns)
|
||||||
@@ -152,7 +261,7 @@ class MSP2Ifc:
|
|||||||
name = resource.find("pr:Name", self.ns)
|
name = resource.find("pr:Name", self.ns)
|
||||||
id = resource.find("pr:ID", self.ns).text
|
id = resource.find("pr:ID", self.ns).text
|
||||||
if name is not None:
|
if name is not None:
|
||||||
name = name.text
|
name = name.text
|
||||||
else:
|
else:
|
||||||
# print("- No Name")
|
# print("- No Name")
|
||||||
name = None
|
name = None
|
||||||
|
|||||||
Reference in New Issue
Block a user