mirror of
https://github.com/IfcOpenShell/IfcOpenShell.git
synced 2026-08-05 23:41:44 +00:00
218 lines
9.2 KiB
Python
218 lines
9.2 KiB
Python
# Ifc4D - IFC scheduling utility
|
|
# Copyright (C) 2021, 2022 Dion Moult <dion@thinkmoult.com>
|
|
#
|
|
# This file is part of Ifc4D.
|
|
#
|
|
# Ifc4D is free software: you can redistribute it and/or modify
|
|
# it under the terms of the GNU Lesser General Public License as published by
|
|
# the Free Software Foundation, either version 3 of the License, or
|
|
# (at your option) any later version.
|
|
#
|
|
# Ifc4D is distributed in the hope that it will be useful,
|
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
# GNU Lesser General Public License for more details.
|
|
#
|
|
# You should have received a copy of the GNU Lesser General Public License
|
|
# along with Ifc4D. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
import datetime
|
|
import xml.etree.ElementTree as ET
|
|
|
|
|
|
from .common import ScheduleIfcGenerator
|
|
|
|
|
|
class P62Ifc:
|
|
def __init__(self):
|
|
self.xml = None
|
|
self.file = None
|
|
self.work_plan = None
|
|
self.project = {}
|
|
self.calendars = {}
|
|
self.wbs = {}
|
|
self.root_activites = []
|
|
self.activities = {}
|
|
self.relationships = {}
|
|
self.resources = {}
|
|
self.output = None
|
|
self.day_map = {
|
|
"Monday": 1,
|
|
"Tuesday": 2,
|
|
"Wednesday": 3,
|
|
"Thursday": 4,
|
|
"Friday": 5,
|
|
"Saturday": 6,
|
|
"Sunday": 7,
|
|
}
|
|
self.sequence_type_map = {
|
|
"Start to Start": "START_START",
|
|
"Start to Finish": "START_FINISH",
|
|
"Finish to Start": "FINISH_START",
|
|
"Finish to Finish": "FINISH_FINISH",
|
|
}
|
|
self.resource_type_map = {
|
|
"Labor": "LABOR",
|
|
"Mat": "MATERIAL",
|
|
"Equip": "EQUIPMENT",
|
|
# https://www.primaverascheduling.com/category/primavera-resources/
|
|
"Nonlabor": "EQUIPMENT",
|
|
}
|
|
|
|
def execute(self):
|
|
import time
|
|
|
|
start = time.time()
|
|
print("Started")
|
|
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)
|
|
end = time.time()
|
|
|
|
ifcCreator.create_ifc()
|
|
end2 = time.time()
|
|
print("Parsing time is", end - start)
|
|
print("IFC Creation took", end2 - end)
|
|
print("Overall Time", end2 - start)
|
|
|
|
def parse_xml(self):
|
|
tree = ET.parse(self.xml)
|
|
root = tree.getroot()
|
|
self.ns = {"pr": root.tag[1:].partition("}")[0]}
|
|
project = root.find("pr:Project", self.ns)
|
|
self.project["Name"] = project.findtext("pr:Name") or "Unnamed"
|
|
self.parse_calendar_xml(root)
|
|
self.parse_calendar_xml(project)
|
|
self.parse_wbs_xml(project)
|
|
self.parse_activity_xml(project)
|
|
self.parse_relationship_xml(project)
|
|
self.parse_resources_xml(root)
|
|
|
|
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 is not None:
|
|
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,
|
|
"HoursPerDay": calendar.find("pr:HoursPerDay", 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": [],
|
|
}
|
|
|
|
def parse_activity_xml(self, project):
|
|
for activity in project.findall("pr:Activity", self.ns):
|
|
activity_type = activity.find("pr:Type", self.ns).text
|
|
if activity_type == "Level of Effort":
|
|
continue
|
|
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", 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),
|
|
"PlannedDuration": activity.find("pr:PlannedDuration", self.ns).text,
|
|
"Status": activity.find("pr:Status", self.ns).text,
|
|
"CalendarObjectId": activity.find("pr:CalendarObjectId", self.ns).text,
|
|
"ifc": None,
|
|
}
|
|
|
|
def parse_relationship_xml(self, project):
|
|
for relationship in project.findall("pr:Relationship", self.ns):
|
|
predecessor = relationship.find("pr:PredecessorActivityObjectId", self.ns).text
|
|
successor = relationship.find("pr:SuccessorActivityObjectId", self.ns).text
|
|
if predecessor not in self.activities or successor not in self.activities:
|
|
continue
|
|
self.relationships[relationship.find("pr:ObjectId", self.ns).text] = {
|
|
"PredecessorActivity": predecessor,
|
|
"SuccessorActivity": successor,
|
|
"Type": self.sequence_type_map[relationship.find("pr:Type", self.ns).text],
|
|
"Lag": relationship.find("pr:Lag", self.ns).text,
|
|
}
|
|
|
|
def get_wbs(self, wbs):
|
|
return {"Name": wbs.find("pr:Name", self.ns).text, "subtasks": []}
|
|
|
|
def parse_resources_xml(self, project):
|
|
resources = project.findall("pr:Resource", self.ns)
|
|
for resource in resources:
|
|
id = resource.find("pr:ObjectId", self.ns).text
|
|
self.resources[id] = {
|
|
"Name": resource.find("pr:Name", self.ns).text,
|
|
"Code": resource.find("pr:Id", self.ns).text,
|
|
"ParentObjectId": resource.find("pr:ParentObjectId", self.ns).text,
|
|
"Type": self.resource_type_map[resource.find("pr:ResourceType", self.ns).text],
|
|
"ifc": None,
|
|
"rel": None,
|
|
}
|
|
print("Resource found", self.resources)
|