mirror of
https://github.com/IfcOpenShell/IfcOpenShell.git
synced 2026-08-11 18:16:40 +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:
|
||||
|
||||
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
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user