From 67522e03d103a284b90718d61e27a92fa9c02403 Mon Sep 17 00:00:00 2001 From: Dion Moult Date: Fri, 27 May 2022 17:17:20 +1000 Subject: [PATCH] Fix import error bugs when importing P6XML to enterprise environments --- src/ifc4d/ifc4d/ifc2p6.py | 69 ++++++++++++++++++++++++++++----------- 1 file changed, 50 insertions(+), 19 deletions(-) diff --git a/src/ifc4d/ifc4d/ifc2p6.py b/src/ifc4d/ifc4d/ifc2p6.py index 8ff8d426a1..9aa178dd7c 100644 --- a/src/ifc4d/ifc4d/ifc2p6.py +++ b/src/ifc4d/ifc4d/ifc2p6.py @@ -34,6 +34,8 @@ class Ifc2P6: self.element_map = {} self.hours_per_day = 8 self.project_tasks = [] + self.starts = [] + self.finishes = [] # P6XML does not understand patterns in holiday dates. Instead, it # expects a list of every day that is a holiday between a date range. # This allows you to specify the date range where the export should @@ -51,8 +53,8 @@ class Ifc2P6: self.schedule = self.file.by_type("IfcWorkSchedule")[0] - self.create_obs() - self.create_calendars() + # It seems as though OBSes aren't necessary and cause permission issues on P6 enterprise setups + # self.create_obs() self.create_projects() tree = ET.ElementTree(self.root) @@ -65,14 +67,23 @@ class Ifc2P6: ET.SubElement(obs, "Id").text = element.Name or "X" ET.SubElement(obs, "Name").text = element.LongName or "Unnamed" - def create_calendars(self): + def create_calendars(self, work_schedule): for calendar in self.file.by_type("IfcWorkCalendar"): - self.create_calendar(calendar) + self.create_calendar(calendar, work_schedule) - def create_calendar(self, calendar): - el = ET.SubElement(self.root, "Calendar") + def create_calendar(self, calendar, work_schedule): + el = ET.SubElement(self.element_map[work_schedule], "Calendar") ET.SubElement(el, "Name").text = calendar.Name or "Unnamed" self.link_element(calendar, el) + ET.SubElement(el, "BaseCalendarObjectId") + ET.SubElement(el, "HoursPerDay").text = "8" + ET.SubElement(el, "HoursPerMonth").text = "172" + ET.SubElement(el, "HoursPerWeek").text = "40" + ET.SubElement(el, "HoursPerYear").text = "2000" + ET.SubElement(el, "IsDefault").text = "0" + ET.SubElement(el, "IsPersonal").text = "0" + ET.SubElement(el, "ProjectObjectId").text = self.id_map[work_schedule] + ET.SubElement(el, "Type").text = "Project" working_week = self.auto_detect_working_week(calendar) @@ -81,13 +92,17 @@ class Ifc2P6: work_week = ET.SubElement(el, "StandardWorkWeek") - for day, time_periods in working_week.items(): + for day in ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"]: work_hours = ET.SubElement(work_week, "StandardWorkHours") - day = ET.SubElement(work_hours, "DayOfWeek").text = day - for time_period in time_periods: - work_time = ET.SubElement(work_hours, "WorkTime") - ET.SubElement(work_time, "Start").text = time_period["Start"] - ET.SubElement(work_time, "Finish").text = time_period["Finish"] + ET.SubElement(work_hours, "DayOfWeek").text = day + if day in working_week: + print(working_week) + for time_period in working_week[day]: + work_time = ET.SubElement(work_hours, "WorkTime") + ET.SubElement(work_time, "Start").text = time_period["Start"] + ET.SubElement(work_time, "Finish").text = time_period["Finish"] + else: + ET.SubElement(work_hours, "WorkTime") holidays = ET.SubElement(el, "HolidayOrExceptions") for holiday in self.auto_detect_holidays(calendar): @@ -152,24 +167,34 @@ class Ifc2P6: def create_project(self, work_schedule): project = ET.SubElement(self.root, "Project") self.link_element(work_schedule, project) - - ET.SubElement(project, "ActivityDefaultCalendarObjectId").text = self.id_map[ - self.file.by_type("IfcWorkCalendar")[0] - ] + activity_calendar = ET.SubElement(project, "ActivityDefaultCalendarObjectId") ET.SubElement(project, "Name").text = work_schedule.Name or "Unnamed" ET.SubElement(project, "Id").text = work_schedule.Identification or "X" - ET.SubElement(project, "OBSObjectId").text = self.id_map[self.file.by_type("IfcProject")[0]] + data_date = ET.SubElement(project, "DataDate") + planned_start_date = ET.SubElement(project, "PlannedStartDate") + # ET.SubElement(project, "OBSObjectId").text = self.id_map[self.file.by_type("IfcProject")[0]] + # Note: order matters for P6 enterprise imports. + # Root elements like Name must come first. Then calendar, WBS, activities, and relationships. + # Changing the order will result in import failures (but not for all P6 versions, it seems?) + self.create_calendars(work_schedule) self.project_tasks = [] + self.activities = [] self.create_wbs(self.get_subtasks(work_schedule), work_schedule) + self.create_activities() self.create_relationships(self.project_tasks, work_schedule) + activity_calendar.text = self.id_map[self.file.by_type("IfcWorkCalendar")[0]] + start_date = sorted(self.starts)[0] + data_date.text = start_date + planned_start_date.text = start_date + def create_wbs(self, tasks, work_schedule, parent=None): self.project_tasks.extend(tasks) for task in tasks: subtasks = self.get_subtasks(task) if not subtasks: - self.create_activity(task, work_schedule, parent=parent) + self.activities.append({"task": task, "work_schedule": work_schedule, "parent": parent}) continue wbs = ET.SubElement(self.element_map[work_schedule], "WBS") self.link_element(task, wbs) @@ -181,6 +206,10 @@ class Ifc2P6: ET.SubElement(wbs, "ProjectObjectId").text = self.id_map[work_schedule] self.create_wbs(subtasks, work_schedule, parent=task) + def create_activities(self): + for activity in self.activities: + self.create_activity(activity["task"], activity["work_schedule"], parent=activity["parent"]) + def create_activity(self, task, work_schedule, parent=None): if not parent: return @@ -198,9 +227,11 @@ class Ifc2P6: if task.TaskTime.ScheduleStart: ET.SubElement(activity, "PlannedStartDate").text = task.TaskTime.ScheduleStart ET.SubElement(activity, "StartDate").text = task.TaskTime.ScheduleStart + self.starts.append(task.TaskTime.ScheduleStart) if task.TaskTime.ScheduleFinish: ET.SubElement(activity, "PlannedFinishDate").text = task.TaskTime.ScheduleFinish ET.SubElement(activity, "FinishDate").text = task.TaskTime.ScheduleFinish + self.finishes.append(task.TaskTime.ScheduleFinish) ET.SubElement(activity, "ProjectObjectId").text = self.id_map[work_schedule] ET.SubElement(activity, "ActualDuration").text = "0" ET.SubElement(activity, "ActualFinishDate") @@ -218,7 +249,7 @@ class Ifc2P6: "RemainingLateFinishDate": task.TaskTime.LateFinish, } for key, value in data_map.items(): - el = ET.SubElement(activity, "RemainingEarlyStartDate") + el = ET.SubElement(activity, key) if value: el.text = value