You can now import work schedules from csv

This commit is contained in:
Sigma Dimensions
2023-03-28 04:04:48 +00:00
parent fab76bbcde
commit 8083a635a8
4 changed files with 242 additions and 0 deletions
@@ -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"
+14
View File
@@ -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,,,,
1 Hierarchy Identification Name ScheduleStart ScheduleFinish ScheduleDuration ActualStart ActualFinish ActualDuration Relationships
2 1 1 Demolition
3
4 2 1.1 Building A
5
6 3 1.1.1 Soft strip 01/01/23 5d 07/01/23 10/01/23 111FS112
7 3 1.1.2 Hard Strip 5d 112FS113
8 3 1.1.3 Hazmat 7d 113FS121
9
10 2 1.2 Building B
11
12 3 1.2.1 Soft strip 3d 121FS122
13 3 1.2.2 Hard Strip 4h 122FS123
14 3 1.2.3 Hazmat 2d16h
+201
View File
@@ -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"])