Implement support of importing calendars from P6

This commit is contained in:
Dion Moult
2021-04-30 14:53:40 +10:00
parent 2f53e43b90
commit 79dcf809ed
5 changed files with 240 additions and 43 deletions
@@ -278,7 +278,8 @@ class BIM_PT_work_calendars(Panel):
row = self.layout.row(align=True)
row.label(text=work_calendar["Name"] or "Unnamed", icon="VIEW_ORTHO")
if self.props.active_work_calendar_id == work_calendar_id:
row.operator("bim.edit_work_calendar", text="", icon="CHECKMARK")
if self.props.is_editing == "ATTRIBUTES":
row.operator("bim.edit_work_calendar", text="", icon="CHECKMARK")
row.operator("bim.disable_editing_work_calendar", text="", icon="CANCEL")
elif self.props.active_work_calendar_id:
row.operator("bim.remove_work_calendar", text="", icon="X").work_calendar = work_calendar_id
@@ -313,7 +314,7 @@ class BIM_PT_work_calendars(Panel):
def draw_work_time_ui(self, work_time, time_type):
row = self.layout.row(align=True)
row.label(
text=work_time["Name"] or "Unnamed", icon="MESH_GRID" if time_type == "WorkingTimes" else "LIGHTPROBE_GRID"
text=work_time["Name"] or "Unnamed", icon="AUTO" if time_type == "WorkingTimes" else "HOME"
)
if work_time["Start"] or work_time["Finish"]:
row.label(text="{} - {}".format(work_time["Start"] or "*", work_time["Finish"] or "*"))
@@ -4,7 +4,7 @@ import ifcopenshell.api
class Usecase:
def __init__(self, file, **settings):
self.file = file
self.settings = {"name": "Unnamed", "predefined_type": "NOTDEFINED", "working_times": [], "exception_times": []}
self.settings = {"name": "Unnamed", "predefined_type": "NOTDEFINED"}
for key, value in settings.items():
self.settings[key] = value
@@ -19,3 +19,4 @@ class Usecase:
if len(self.file.get_inverse(self.settings["parent"].Recurrence)) == 1:
self.file.remove(self.settings["parent"].Recurrence)
self.settings["parent"].Recurrence = recurrence
return recurrence
@@ -1,6 +1,3 @@
import ifcopenshell.util.date
class Usecase:
def __init__(self, file, **settings):
self.file = file
@@ -10,17 +7,4 @@ class Usecase:
def execute(self):
for name, value in self.settings["attributes"].items():
if name == "TimePeriods" and value:
periods = []
for period in value:
periods.append(
self.file.create_entity(
"IfcTimePeriod",
**{
"StartTime": ifcopenshell.util.date.datetime2ifc(period[0]),
"EndTime": ifcopenshell.util.date.datetime2ifc(period[1]),
},
)
)
value = periods
setattr(self.settings["recurrence_pattern"], name, value)
+235 -24
View File
@@ -1,8 +1,8 @@
import datetime
import ifcopenshell
import ifcopenshell.api
import ifcopenshell.util.date
import xml.etree.ElementTree as ET
from datetime import datetime
class P62Ifc:
@@ -11,10 +11,20 @@ class P62Ifc:
self.file = None
self.work_plan = None
self.project = {}
self.calendars = {}
self.wbs = {}
self.root_activites = []
self.activities = {}
self.relationships = {}
self.day_map = {
"Monday": 1,
"Tuesday": 2,
"Wednesday": 3,
"Thursday": 4,
"Friday": 5,
"Saturday": 6,
"Sunday": 7,
}
def execute(self):
self.parse_xml()
@@ -22,58 +32,259 @@ class P62Ifc:
def parse_xml(self):
tree = ET.parse(self.xml)
ns = {"pr": "http://xmlns.oracle.com/Primavera/P6/V19.12/API/BusinessObjects"}
self.ns = {"pr": "http://xmlns.oracle.com/Primavera/P6/V19.12/API/BusinessObjects"}
root = tree.getroot()
project = root.find("pr:Project", ns)
self.project["Name"] = project.find("pr:Name", ns).text
project = root.find("pr:Project", self.ns)
self.project["Name"] = project.find("pr:Name", self.ns).text
self.parse_calendar_xml(project)
self.parse_wbs_xml(project)
self.parse_activity_xml(project)
self.parse_relationship_xml(project)
for wbs in project.findall("pr:WBS", ns):
self.wbs[wbs.find("pr:ObjectId", ns).text] = {
"Name": wbs.find("pr:Name", ns).text,
"Code": wbs.find("pr:Code", ns).text,
"ParentObjectId": wbs.find("pr:ParentObjectId", ns).text,
def parse_calendar_xml(self, project):
for calendar in project.findall("pr:Calendar", self.ns):
calendar_id = calendar.find("pr:ObjectId", self.ns).text
standard_work_week = []
for standard_work_hour in calendar.find("pr:StandardWorkWeek", self.ns).findall(
"pr:StandardWorkHours", self.ns
):
work_times = []
for work_time in standard_work_hour.findall("pr:WorkTime", self.ns):
if work_time.find("pr:Start", self.ns) is None:
continue
work_times.append(
{
"Start": datetime.time.fromisoformat(work_time.find("pr:Start", self.ns).text),
"Finish": datetime.time.fromisoformat(work_time.find("pr:Finish", self.ns).text),
}
)
standard_work_week.append(
{
"DayOfWeek": standard_work_hour.find("pr:DayOfWeek", self.ns).text,
"WorkTimes": work_times,
"ifc": None,
}
)
exceptions = {}
holiday_or_exceptions = calendar.find("pr:HolidayOrExceptions", self.ns)
holiday_or_exception = []
if holiday_or_exceptions:
holiday_or_exception = holiday_or_exceptions.findall("pr:HolidayOrException", self.ns)
for exception in holiday_or_exception:
d = datetime.datetime.fromisoformat(exception.find("pr:Date", self.ns).text).date()
month = exceptions.setdefault(d.year, {}).setdefault(d.month, {})
month.setdefault("FullDay", [])
month.setdefault("WorkTime", [])
work_times = []
for work_time in exception.findall("pr:WorkTime", self.ns):
if work_time.find("pr:Start", self.ns) is None:
continue
work_times.append(
{
"Start": datetime.time.fromisoformat(work_time.find("pr:Start", self.ns).text),
"Finish": datetime.time.fromisoformat(work_time.find("pr:Finish", self.ns).text),
}
)
if work_times:
exceptions[d.year][d.month]["WorkTime"].append({"Day": d.day, "WorkTimes": work_times, "ifc": None})
else:
exceptions[d.year][d.month]["FullDay"].append(d.day)
self.calendars[calendar_id] = {
"Name": calendar.find("pr:Name", self.ns).text,
"Type": calendar.find("pr:Type", self.ns).text,
"StandardWorkWeek": standard_work_week,
"HolidayOrExceptions": exceptions,
}
def parse_wbs_xml(self, project):
for wbs in project.findall("pr:WBS", self.ns):
self.wbs[wbs.find("pr:ObjectId", self.ns).text] = {
"Name": wbs.find("pr:Name", self.ns).text,
"Code": wbs.find("pr:Code", self.ns).text,
"ParentObjectId": wbs.find("pr:ParentObjectId", self.ns).text,
"ifc": None,
"rel": None,
"activities": [],
}
for activity in project.findall("pr:Activity", ns):
activity_id = activity.find("pr:ObjectId", ns).text
wbs_id = activity.find("pr:WBSObjectId", ns).text
def parse_activity_xml(self, project):
for activity in project.findall("pr:Activity", self.ns):
activity_id = activity.find("pr:ObjectId", self.ns).text
wbs_id = activity.find("pr:WBSObjectId", self.ns).text
if wbs_id:
self.wbs[wbs_id]["activities"].append(activity_id)
else:
self.root_activites.append(activity_id)
self.activities[activity_id] = {
"Name": activity.find("pr:Name", ns).text,
"Identification": activity.find("pr:Id", ns).text,
"StartDate": datetime.fromisoformat(activity.find("pr:StartDate", ns).text),
"FinishDate": datetime.fromisoformat(activity.find("pr:FinishDate", ns).text),
"Status": activity.find("pr:Status", ns).text,
"Name": activity.find("pr:Name", self.ns).text,
"Identification": activity.find("pr:Id", self.ns).text,
"StartDate": datetime.datetime.fromisoformat(activity.find("pr:StartDate", self.ns).text),
"FinishDate": datetime.datetime.fromisoformat(activity.find("pr:FinishDate", self.ns).text),
"Status": activity.find("pr:Status", self.ns).text,
"ifc": None,
}
for relationship in project.findall("pr:Relationship", ns):
self.relationships[relationship.find("pr:ObjectId", ns).text] = {
"PredecessorActivity": relationship.find("pr:PredecessorActivityObjectId", ns).text,
"SuccessorActivity": relationship.find("pr:SuccessorActivityObjectId", ns).text,
def parse_relationship_xml(self, project):
for relationship in project.findall("pr:Relationship", self.ns):
self.relationships[relationship.find("pr:ObjectId", self.ns).text] = {
"PredecessorActivity": relationship.find("pr:PredecessorActivityObjectId", self.ns).text,
"SuccessorActivity": relationship.find("pr:SuccessorActivityObjectId", self.ns).text,
}
def get_wbs(self, wbs):
return {"Name": wbs.find("pr:Name", ns).text, "subtasks": []}
return {"Name": wbs.find("pr:Name", self.ns).text, "subtasks": []}
def create_ifc(self):
if not self.file:
self.file = self.create_boilerplate_ifc()
work_schedule = self.create_work_schedule()
self.create_tasks(work_schedule)
self.create_rel_sequences()
self.create_calendars()
# self.create_tasks(work_schedule)
# self.create_rel_sequences()
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"], predefined_type=calendar["Type"]
)
self.process_working_week(calendar["StandardWorkWeek"], calendar["ifc"])
self.process_exceptions(calendar["HolidayOrExceptions"], calendar["ifc"])
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 = [self.day_map[day["DayOfWeek"]]]
for day2 in week:
if day["DayOfWeek"] == day2["DayOfWeek"]:
continue
if day["WorkTimes"] == day2["WorkTimes"]:
weekday_component.append(self.day_map[day2["DayOfWeek"]])
# 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["WorkTimes"]:
ifcopenshell.api.run(
"sequence.add_time_period",
self.file,
recurrence_pattern=recurrence,
start_time=work_time["Start"],
end_time=work_time["Finish"],
)
def process_exceptions(self, exceptions, calendar):
for year, year_data in exceptions.items():
for month, month_data in year_data.items():
if month_data["FullDay"]:
self.process_full_day_exceptions(year, month, month_data, calendar)
if month_data["WorkTime"]:
self.process_work_time_exceptions(year, month, month_data, calendar)
def process_full_day_exceptions(self, year, month, month_data, calendar):
work_time = ifcopenshell.api.run(
"sequence.add_work_time", self.file, work_calendar=calendar, time_type="ExceptionTimes"
)
ifcopenshell.api.run(
"sequence.edit_work_time",
self.file,
work_time=work_time,
attributes={
"Name": f"{year}-{month}",
"Start": datetime.date(year, 1, 1),
"Finish": datetime.date(year, 12, 31),
},
)
recurrence = ifcopenshell.api.run(
"sequence.assign_recurrence_pattern",
self.file,
parent=work_time,
recurrence_type="YEARLY_BY_DAY_OF_MONTH",
)
ifcopenshell.api.run(
"sequence.edit_recurrence_pattern",
self.file,
recurrence_pattern=recurrence,
attributes={"DayComponent": month_data["FullDay"], "MonthComponent": [month], "Occurrences": 1},
)
def process_work_time_exceptions(self, year, month, month_data, calendar):
for day in month_data["WorkTime"]:
if day["ifc"]:
continue
day["ifc"] = ifcopenshell.api.run(
"sequence.add_work_time", self.file, work_calendar=calendar, time_type="ExceptionTimes"
)
day_component = [day["Day"]]
for day2 in month_data["WorkTime"]:
if day["Day"] == day2["Day"]:
continue
if day["WorkTimes"] == day2["WorkTimes"]:
day_component.append(day2["Day"])
# Don't process the next day, as we can group it
day2["ifc"] = day["ifc"]
ifcopenshell.api.run(
"sequence.edit_work_time",
self.file,
work_time=day["ifc"],
attributes={
"Name": "{}-{}-{}".format(year, month, ", ".join([str(d) for d in day_component])),
"Start": datetime.date(year, 1, 1),
"Finish": datetime.date(year, 12, 31),
},
)
recurrence = ifcopenshell.api.run(
"sequence.assign_recurrence_pattern",
self.file,
parent=day["ifc"],
recurrence_type="YEARLY_BY_DAY_OF_MONTH",
)
ifcopenshell.api.run(
"sequence.edit_recurrence_pattern",
self.file,
recurrence_pattern=recurrence,
attributes={"DayComponent": day_component, "MonthComponent": [month], "Occurrences": 1},
)
for work_time in day["WorkTimes"]:
ifcopenshell.api.run(
"sequence.add_time_period",
self.file,
recurrence_pattern=recurrence,
start_time=work_time["Start"],
end_time=work_time["Finish"],
)
def create_tasks(self, work_schedule):
for wbs in self.wbs.values():
self.create_task_from_wbs(wbs, work_schedule)