Files
IfcOpenShell/src/ifc4d/ifc4d/p62ifc.py
T
2026-01-26 17:10:02 +05:00

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)