mirror of
https://github.com/IfcOpenShell/IfcOpenShell.git
synced 2026-08-09 09:21:46 +00:00
You can now import work schedules from csv
This commit is contained in:
@@ -81,6 +81,7 @@ classes = (
|
||||
operator.GenerateGanttChart,
|
||||
operator.GuessDateRange,
|
||||
operator.ImportMSP,
|
||||
operator.ImportCSV,
|
||||
operator.ImportP6,
|
||||
operator.ImportP6XER,
|
||||
operator.ImportPP,
|
||||
@@ -148,6 +149,7 @@ def menu_func_export(self, context):
|
||||
|
||||
|
||||
def menu_func_import(self, context):
|
||||
self.layout.operator(operator.ImportCSV.bl_idname, text="Work Schedule (.csv)")
|
||||
self.layout.operator(operator.ImportP6.bl_idname, text="P6 (.xml)")
|
||||
self.layout.operator(operator.ImportP6XER.bl_idname, text="P6 (.xer)")
|
||||
self.layout.operator(operator.ImportPP.bl_idname, text="Powerproject (.pp)")
|
||||
|
||||
@@ -527,6 +527,31 @@ class DisableEditingWorkCalendar(bpy.types.Operator):
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
class ImportCSV(bpy.types.Operator, tool.Ifc.Operator, ImportHelper):
|
||||
bl_idname = "import_csv.bim"
|
||||
bl_label = "Import CSV"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
filename_ext = ".csv"
|
||||
filter_glob: bpy.props.StringProperty(default="*.csv", options={"HIDDEN"})
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
ifc_file = IfcStore.get_file()
|
||||
return ifc_file is not None
|
||||
|
||||
def execute(self, context):
|
||||
from ifc4d.csv4d2ifc import Csv2Ifc
|
||||
|
||||
self.file = tool.Ifc.get()
|
||||
start = time.time()
|
||||
csv2ifc = Csv2Ifc()
|
||||
csv2ifc.csv = self.filepath
|
||||
csv2ifc.file = self.file
|
||||
csv2ifc.execute()
|
||||
print("Imported in %s seconds" % (time.time() - start))
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
class ImportP6(bpy.types.Operator, ImportHelper):
|
||||
bl_idname = "import_p6.bim"
|
||||
bl_label = "Import P6"
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
Hierarchy,Identification,Name,ScheduleStart,ScheduleFinish,ScheduleDuration,ActualStart,ActualFinish,ActualDuration,Relationships
|
||||
1,1,Demolition,,,,,,,
|
||||
,,,,,,,,,
|
||||
2,1.1,Building A,,,,,,,
|
||||
,,,,,,,,,
|
||||
3,1.1.1,Soft strip,01/01/23,,5d,07/01/23,10/01/23,,111FS112
|
||||
3,1.1.2,Hard Strip,,,5d,,,,112FS113
|
||||
3,1.1.3,Hazmat,,,7d,,,,113FS121
|
||||
,,,,,,,,,
|
||||
2,1.2,Building B,,,,,,,
|
||||
,,,,,,,,,
|
||||
3,1.2.1,Soft strip,,,3d,,,,121FS122
|
||||
3,1.2.2,Hard Strip,,,4h,,,,122FS123
|
||||
3,1.2.3,Hazmat,,,2d16h,,,,
|
||||
|
@@ -0,0 +1,201 @@
|
||||
# Ifc4D - IFC scheduling utility
|
||||
# Copyright (C) 2021-2022 Dion Moult <dion@thinkmoult.com>, Yassine Oualid <yassine@sigmadimensions.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 csv
|
||||
import ifcopenshell
|
||||
import ifcopenshell.api
|
||||
import locale
|
||||
import re
|
||||
|
||||
|
||||
class Csv2Ifc:
|
||||
def __init__(self):
|
||||
self.csv = None
|
||||
self.file = None
|
||||
self.tasks = []
|
||||
self.work_schedule = None
|
||||
self.work_plan = None
|
||||
self.is_schedule_of_rates = False
|
||||
self.units = {}
|
||||
|
||||
def execute(self):
|
||||
self.parse_csv()
|
||||
self.create_ifc()
|
||||
|
||||
def parse_csv(self):
|
||||
self.parents = {}
|
||||
self.headers = {}
|
||||
locale.setlocale(locale.LC_ALL, "") # set the system locale
|
||||
with open(self.csv, "r", encoding="ISO-8859-1") as csv_file:
|
||||
reader = csv.reader(csv_file)
|
||||
for row in reader:
|
||||
if not row[0]:
|
||||
continue
|
||||
if row[0] == "Hierarchy":
|
||||
for i, col in enumerate(row):
|
||||
if not col:
|
||||
continue
|
||||
self.headers[col] = i
|
||||
continue
|
||||
task = self.get_row_task(row)
|
||||
hierarchy_key = int(row[0])
|
||||
if hierarchy_key == 1:
|
||||
self.tasks.append(task)
|
||||
else:
|
||||
self.parents[hierarchy_key - 1]["children"].append(task)
|
||||
self.parents[hierarchy_key] = task
|
||||
|
||||
def parse_task_rel(self, task_relationships_str):
|
||||
rels = []
|
||||
if task_relationships_str:
|
||||
for rel_str in task_relationships_str.split(";"):
|
||||
rel_parts = re.split(r"(\d+)", rel_str)
|
||||
task_1 = rel_parts[1]
|
||||
rel_type = rel_parts[2]
|
||||
task_2 = rel_parts[3]
|
||||
rels.append(
|
||||
{
|
||||
"rel_type": rel_type,
|
||||
"task_1": task_1,
|
||||
"task_2": task_2,
|
||||
}
|
||||
)
|
||||
return rels
|
||||
|
||||
def get_row_task(self, row):
|
||||
hours_per_day = 8
|
||||
hierarchy = row[self.headers["Hierarchy"]]
|
||||
identification = row[self.headers["Identification"]]
|
||||
task_name = row[self.headers["Name"]]
|
||||
|
||||
task_relationships = self.parse_task_rel(row[self.headers["Relationships"]])
|
||||
|
||||
scheduled_start_date = ifcopenshell.util.date.string_to_date(row[self.headers["ScheduleStart"]])
|
||||
scheduled_finish_date = ifcopenshell.util.date.string_to_date(row[self.headers["ScheduleFinish"]])
|
||||
scheduled_duration = ifcopenshell.util.date.string_to_duration(row[self.headers["ScheduleDuration"]])
|
||||
actual_start_date = ifcopenshell.util.date.string_to_date(row[self.headers["ActualStart"]])
|
||||
actual_finish_date = ifcopenshell.util.date.string_to_date(row[self.headers["ActualFinish"]])
|
||||
actual_duration = ifcopenshell.util.date.string_to_duration(row[self.headers["ActualDuration"]])
|
||||
|
||||
return {
|
||||
"Hierarchy": hierarchy,
|
||||
"Identification": identification,
|
||||
"Name": task_name,
|
||||
"Relationships": task_relationships,
|
||||
"ScheduleStart": scheduled_start_date,
|
||||
"ScheduleFinish": scheduled_finish_date,
|
||||
"ScheduleDuration": scheduled_duration,
|
||||
"ActualStart": actual_start_date,
|
||||
"ActualFinish": actual_finish_date,
|
||||
"ActualDuration": actual_duration,
|
||||
"children": [],
|
||||
}
|
||||
|
||||
def create_ifc(self):
|
||||
if not self.file:
|
||||
self.create_boilerplate_ifc()
|
||||
if not self.work_plan:
|
||||
self.work_plan = ifcopenshell.api.run("sequence.add_work_plan", self.file)
|
||||
self.work_schedule = self.create_work_schedule()
|
||||
self.create_tasks(self.tasks)
|
||||
self.create_rel_sequences(self.tasks)
|
||||
|
||||
def create_boilerplate_ifc(self):
|
||||
self.file = ifcopenshell.file(schema="IFC4")
|
||||
self.work_plan = self.file.create_entity("IfcWorkPlan")
|
||||
|
||||
def create_tasks(self, tasks, parent=None):
|
||||
for task in tasks:
|
||||
self.create_task(task, parent)
|
||||
|
||||
def create_rel_sequences(self, tasks=None):
|
||||
self.sequence_type_map = {
|
||||
"FF": "FINISH_FINISH",
|
||||
"FS": "FINISH_START",
|
||||
"SF": "START_FINISH",
|
||||
"SS": "START_START",
|
||||
}
|
||||
for task in tasks:
|
||||
self.create_rel_sequence(task)
|
||||
|
||||
def create_work_schedule(self):
|
||||
return ifcopenshell.api.run("sequence.add_work_schedule", self.file, name="import", work_plan=self.work_plan)
|
||||
|
||||
def create_task(self, task, parent_task=None):
|
||||
if parent_task is None:
|
||||
task["ifc"] = ifcopenshell.api.run("sequence.add_task", self.file, work_schedule=self.work_schedule)
|
||||
else:
|
||||
task["ifc"] = ifcopenshell.api.run("sequence.add_task", self.file, parent_task=parent_task)
|
||||
|
||||
ifcopenshell.api.run(
|
||||
"sequence.edit_task",
|
||||
self.file,
|
||||
task=task["ifc"],
|
||||
attributes={
|
||||
"Name": task["Name"],
|
||||
"Identification": task["Identification"],
|
||||
# "IsMilestone": task["ScheduleStart"] == task["ScheduleFinish"],
|
||||
},
|
||||
)
|
||||
task_time = ifcopenshell.api.run("sequence.add_task_time", self.file, task=task["ifc"])
|
||||
ifcopenshell.api.run(
|
||||
"sequence.edit_task_time",
|
||||
self.file,
|
||||
task_time=task_time,
|
||||
attributes={
|
||||
"ScheduleStart": task["ScheduleStart"],
|
||||
"ScheduleFinish": task["ScheduleFinish"],
|
||||
"DurationType": "WORKTIME" if task["ScheduleDuration"] else None,
|
||||
"ScheduleDuration": task["ScheduleDuration"] if task["ScheduleDuration"] else None,
|
||||
"ActualStart": task["ActualStart"],
|
||||
"ActualFinish": task["ActualFinish"],
|
||||
"ActualDuration": task["ActualDuration"],
|
||||
},
|
||||
)
|
||||
self.create_tasks(task["children"], task["ifc"])
|
||||
|
||||
def get_entity_by_identification(self, tasks, task_identification):
|
||||
for task in tasks or []:
|
||||
if task["Identification"].replace(".", "").replace(" ", "") == task_identification.replace(" ", ""):
|
||||
return task["ifc"]
|
||||
else:
|
||||
entity = self.get_entity_by_identification(task["children"], task_identification)
|
||||
if entity:
|
||||
return entity
|
||||
return None
|
||||
|
||||
def create_rel_sequence(self, task=None):
|
||||
for rel in task["Relationships"] or []:
|
||||
task_1 = self.get_entity_by_identification(self.tasks, rel["task_1"])
|
||||
task_2 = self.get_entity_by_identification(self.tasks, rel["task_2"])
|
||||
rel_type = self.sequence_type_map[rel["rel_type"]]
|
||||
rel_sequence = ifcopenshell.api.run(
|
||||
"sequence.assign_sequence",
|
||||
self.file,
|
||||
related_process=task_2,
|
||||
relating_process=task_1,
|
||||
)
|
||||
if rel_type:
|
||||
ifcopenshell.api.run(
|
||||
"sequence.edit_sequence",
|
||||
self.file,
|
||||
rel_sequence=rel_sequence,
|
||||
attributes={"SequenceType": rel_type},
|
||||
)
|
||||
self.create_rel_sequences(task["children"])
|
||||
Reference in New Issue
Block a user