mirror of
https://github.com/IfcOpenShell/IfcOpenShell.git
synced 2026-09-21 10:23:41 +00:00
Implement natural sorting for task trees, and support nested tasks in MS Project XML imports
This commit is contained in:
@@ -1,3 +1,4 @@
|
|||||||
|
import re
|
||||||
import os
|
import os
|
||||||
import bpy
|
import bpy
|
||||||
import json
|
import json
|
||||||
@@ -258,10 +259,11 @@ class EnableEditingTasks(bpy.types.Operator):
|
|||||||
self.tprops.tasks.remove(0)
|
self.tprops.tasks.remove(0)
|
||||||
|
|
||||||
self.contracted_tasks = json.loads(self.props.contracted_tasks)
|
self.contracted_tasks = json.loads(self.props.contracted_tasks)
|
||||||
sort_keys = {
|
self.sort_keys = {
|
||||||
i: Data.tasks[i]["Identification"] for i in Data.work_schedules[self.work_schedule]["RelatedObjects"]
|
i: Data.tasks[i]["Identification"] for i in Data.work_schedules[self.work_schedule]["RelatedObjects"]
|
||||||
}
|
}
|
||||||
for related_object_id in sorted(sort_keys, key=sort_keys.__getitem__):
|
|
||||||
|
for related_object_id in sorted(self.sort_keys, key=self.natural_sort_key):
|
||||||
self.create_new_task_li(related_object_id, 0)
|
self.create_new_task_li(related_object_id, 0)
|
||||||
bpy.ops.bim.load_task_properties()
|
bpy.ops.bim.load_task_properties()
|
||||||
self.props.editing_type = "TASKS"
|
self.props.editing_type = "TASKS"
|
||||||
@@ -276,10 +278,13 @@ class EnableEditingTasks(bpy.types.Operator):
|
|||||||
if task["RelatedObjects"]:
|
if task["RelatedObjects"]:
|
||||||
new.has_children = True
|
new.has_children = True
|
||||||
if new.is_expanded:
|
if new.is_expanded:
|
||||||
sort_keys = {i: Data.tasks[i]["Identification"] for i in task["RelatedObjects"]}
|
self.sort_keys = {i: Data.tasks[i]["Identification"] for i in task["RelatedObjects"]}
|
||||||
for related_object_id in sorted(sort_keys, key=sort_keys.__getitem__):
|
for related_object_id in sorted(self.sort_keys, key=self.natural_sort_key):
|
||||||
self.create_new_task_li(related_object_id, level_index + 1)
|
self.create_new_task_li(related_object_id, level_index + 1)
|
||||||
return {"FINISHED"}
|
|
||||||
|
def natural_sort_key(self, i, _nsre=re.compile("([0-9]+)")):
|
||||||
|
s = self.sort_keys[i]
|
||||||
|
return [int(text) if text.isdigit() else text.lower() for text in _nsre.split(s)]
|
||||||
|
|
||||||
|
|
||||||
class LoadTaskProperties(bpy.types.Operator):
|
class LoadTaskProperties(bpy.types.Operator):
|
||||||
@@ -868,6 +873,7 @@ class ImportP6(bpy.types.Operator, ImportHelper):
|
|||||||
print("Import finished in {:.2f} seconds".format(time.time() - start))
|
print("Import finished in {:.2f} seconds".format(time.time() - start))
|
||||||
return {"FINISHED"}
|
return {"FINISHED"}
|
||||||
|
|
||||||
|
|
||||||
class ImportMSP(bpy.types.Operator, ImportHelper):
|
class ImportMSP(bpy.types.Operator, ImportHelper):
|
||||||
bl_idname = "import_msp.bim"
|
bl_idname = "import_msp.bim"
|
||||||
bl_label = "Import MSP"
|
bl_label = "Import MSP"
|
||||||
|
|||||||
@@ -37,6 +37,8 @@ class MSP2Ifc:
|
|||||||
project = tree.getroot()
|
project = tree.getroot()
|
||||||
self.ns = {"pr": project.tag[1:].partition("}")[0]}
|
self.ns = {"pr": project.tag[1:].partition("}")[0]}
|
||||||
self.project["Name"] = project.find("pr:Name", self.ns).text
|
self.project["Name"] = project.find("pr:Name", self.ns).text
|
||||||
|
self.outline_level = 0
|
||||||
|
self.outline_parents = {}
|
||||||
self.parse_task_xml(project)
|
self.parse_task_xml(project)
|
||||||
self.parse_calendar_xml(project)
|
self.parse_calendar_xml(project)
|
||||||
|
|
||||||
@@ -47,15 +49,25 @@ class MSP2Ifc:
|
|||||||
task_index_level = task.find("pr:OutlineLevel", self.ns).text
|
task_index_level = task.find("pr:OutlineLevel", self.ns).text
|
||||||
wbs_id = task.find("pr:WBS", self.ns).text
|
wbs_id = task.find("pr:WBS", self.ns).text
|
||||||
relationship = task.find("pr:PredecessorLink", self.ns)
|
relationship = task.find("pr:PredecessorLink", self.ns)
|
||||||
|
outline_level = int(task.find("pr:OutlineLevel", self.ns).text)
|
||||||
|
|
||||||
|
if outline_level != 0:
|
||||||
|
parent_task = self.tasks[self.outline_parents[outline_level-1]]
|
||||||
|
parent_task["subtasks"].append(task_id)
|
||||||
|
self.outline_level = outline_level
|
||||||
|
self.outline_parents[outline_level] = task_id
|
||||||
|
|
||||||
self.tasks[task_id] = {
|
self.tasks[task_id] = {
|
||||||
"Name": task.find("pr:Name", self.ns).text,
|
"Name": task.find("pr:Name", self.ns).text,
|
||||||
"UID": task.find("pr:UID", self.ns).text,
|
"OutlineNumber": task.find("pr:OutlineNumber", self.ns).text,
|
||||||
|
"OutlineLevel": outline_level,
|
||||||
"Start": datetime.datetime.fromisoformat(task.find("pr:Start", 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),
|
"Finish": datetime.datetime.fromisoformat(task.find("pr:Finish", self.ns).text),
|
||||||
"Duration": ifcopenshell.util.date.ifc2datetime(task.find("pr:Duration", self.ns).text),
|
"Duration": ifcopenshell.util.date.ifc2datetime(task.find("pr:Duration", self.ns).text),
|
||||||
"Priority": task.find("pr:Priority", self.ns).text,
|
"Priority": task.find("pr:Priority", self.ns).text,
|
||||||
"CalendarUID": task.find("pr:CalendarUID", self.ns).text,
|
"CalendarUID": task.find("pr:CalendarUID", self.ns).text,
|
||||||
"PredecessorTask": relationship.find("pr:PredecessorUID", self.ns).text if relationship else None,
|
"PredecessorTask": relationship.find("pr:PredecessorUID", self.ns).text if relationship else None,
|
||||||
|
"subtasks": [],
|
||||||
"ifc": None,
|
"ifc": None,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -99,7 +111,9 @@ class MSP2Ifc:
|
|||||||
|
|
||||||
def create_tasks(self, work_schedule):
|
def create_tasks(self, work_schedule):
|
||||||
for task_id in self.tasks:
|
for task_id in self.tasks:
|
||||||
self.create_task_from_task(self.tasks[task_id], None, work_schedule)
|
task = self.tasks[task_id]
|
||||||
|
if task["OutlineLevel"] == 0:
|
||||||
|
self.create_task(task, work_schedule=work_schedule)
|
||||||
|
|
||||||
def create_work_schedule(self):
|
def create_work_schedule(self):
|
||||||
return ifcopenshell.api.run(
|
return ifcopenshell.api.run(
|
||||||
@@ -113,11 +127,12 @@ class MSP2Ifc:
|
|||||||
)
|
)
|
||||||
self.process_working_week(calendar["StandardWorkWeek"], calendar["ifc"])
|
self.process_working_week(calendar["StandardWorkWeek"], calendar["ifc"])
|
||||||
|
|
||||||
def create_task_from_task(self, task, wbs, work_schedule):
|
def create_task(self, task, work_schedule=None, parent_task=None):
|
||||||
task["ifc"] = ifcopenshell.api.run(
|
task["ifc"] = ifcopenshell.api.run(
|
||||||
"sequence.add_task",
|
"sequence.add_task",
|
||||||
self.file,
|
self.file,
|
||||||
work_schedule=None if wbs else work_schedule,
|
work_schedule=work_schedule if work_schedule else None,
|
||||||
|
parent_task=parent_task["ifc"] if parent_task else None,
|
||||||
)
|
)
|
||||||
ifcopenshell.api.run(
|
ifcopenshell.api.run(
|
||||||
"sequence.edit_task",
|
"sequence.edit_task",
|
||||||
@@ -125,7 +140,7 @@ class MSP2Ifc:
|
|||||||
task=task["ifc"],
|
task=task["ifc"],
|
||||||
attributes={
|
attributes={
|
||||||
"Name": task["Name"],
|
"Name": task["Name"],
|
||||||
"Identification": task["UID"],
|
"Identification": task["OutlineNumber"],
|
||||||
"IsMilestone": task["Start"] == task["Finish"],
|
"IsMilestone": task["Start"] == task["Finish"],
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
@@ -141,6 +156,8 @@ class MSP2Ifc:
|
|||||||
"ScheduleDuration": task["Duration"] 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):
|
def process_working_week(self, week, calendar):
|
||||||
for day in week:
|
for day in week:
|
||||||
@@ -193,12 +210,14 @@ class MSP2Ifc:
|
|||||||
"Finish to Start": "FINISH_START",
|
"Finish to Start": "FINISH_START",
|
||||||
"Finish to Finish": "FINISH_FINISH",
|
"Finish to Finish": "FINISH_FINISH",
|
||||||
}
|
}
|
||||||
for key, value in self.tasks.items():
|
for task in self.tasks.values():
|
||||||
|
if not task["PredecessorTask"]:
|
||||||
|
continue
|
||||||
rel_sequence = ifcopenshell.api.run(
|
rel_sequence = ifcopenshell.api.run(
|
||||||
"sequence.assign_sequence",
|
"sequence.assign_sequence",
|
||||||
self.file,
|
self.file,
|
||||||
related_process = self.tasks[key]["ifc"],
|
related_process = task["ifc"],
|
||||||
relating_process = self.tasks[self.tasks[key]["PredecessorTask"]]["ifc"]
|
relating_process = self.tasks[task["PredecessorTask"]]["ifc"]
|
||||||
)
|
)
|
||||||
ifcopenshell.api.run(
|
ifcopenshell.api.run(
|
||||||
"sequence.edit_sequence",
|
"sequence.edit_sequence",
|
||||||
|
|||||||
Reference in New Issue
Block a user