From 30bb6d05c5fc771db916dd758330cdd71410c283 Mon Sep 17 00:00:00 2001
From: Sigma Dimensions <79010126+myoualid@users.noreply.github.com>
Date: Wed, 3 May 2023 04:33:16 +0100
Subject: [PATCH] BBIM 4D: You can now visualize & print work schedule
baselines with Gantt Charts
---
.../blenderbim/bim/data/gantt/index.mustache | 40 ++++++------
.../bim/module/sequence/gantt/index.js | 56 +++++++++++++---
.../bim/module/sequence/gantt/main.css | 64 +++++++++++++++++--
.../blenderbim/bim/module/sequence/ui.py | 6 +-
src/blenderbim/blenderbim/core/sequence.py | 2 +-
src/blenderbim/blenderbim/tool/sequence.py | 59 ++++++++++++-----
6 files changed, 176 insertions(+), 51 deletions(-)
diff --git a/src/blenderbim/blenderbim/bim/data/gantt/index.mustache b/src/blenderbim/blenderbim/bim/data/gantt/index.mustache
index a368889cc2..6ee49cefde 100644
--- a/src/blenderbim/blenderbim/bim/data/gantt/index.mustache
+++ b/src/blenderbim/blenderbim/bim/data/gantt/index.mustache
@@ -6,7 +6,7 @@
var json_data = `{{{json_data}}}`;
-
+
Choose a language:
-
-
-
-
+
+
+
+
+
-
+
+
\ No newline at end of file
diff --git a/src/blenderbim/blenderbim/bim/module/sequence/gantt/index.js b/src/blenderbim/blenderbim/bim/module/sequence/gantt/index.js
index 05096293da..ab03471d2d 100644
--- a/src/blenderbim/blenderbim/bim/module/sequence/gantt/index.js
+++ b/src/blenderbim/blenderbim/bim/module/sequence/gantt/index.js
@@ -23,6 +23,7 @@ function create_gantt_chart(json_data) {
JSGantt.parseJSONString(json_data, g);
g.Draw();
}
+
function set_language(e){
lang = e && e.target ? e.target.value : "en";
g.setOptions({
@@ -31,7 +32,27 @@ function set_language(e){
g.Draw();
}
-function setupPage(name){
+
+function draw_table(json_object){
+ let table = document.createElement("table");
+ table.classList.add("table-description");
+ for (let key in json_object){
+ let row = document.createElement("tr");
+ let key_cell = document.createElement("td");
+ let key_text = document.createTextNode(key);
+ key_cell.appendChild(key_text);
+ row.appendChild(key_cell);
+ let value_cell = document.createElement("td");
+ let value_text = document.createTextNode(json_object[key]);
+ value_cell.appendChild(value_text);
+ row.appendChild(value_cell);
+ table.appendChild(row);
+ }
+ return table;
+}
+
+function setupPage(workScheduleData){
+ workScheduleData = JSON.parse(workScheduleData);
let print_button = document.createElement("button");
print_button.id = "print-schedule";
print_button.innerHTML = "Print";
@@ -40,18 +61,37 @@ function setupPage(name){
g.Draw();
var values = document.getElementById("print_page_size").value.split(",")
let css =
- "@media print {\n @page {\n size: " + values[0] + "mm " + values[1] + "mm;\n }\n /* set gantt container to the same width as the page */\n .gantt {\n width: " + values[0] + "mm;\n }\n };";
+ "@media print {\n @page {\n size: " + values[0] + "mm " + values[1] + "mm;\n }\n";
g.printChart(values[0], values[1], css);
g.setTotalHeight(900);
g.Draw();
});
document.getElementById("print_options").appendChild(print_button);
- // add a nice layout to show the schedule name
- let schedule_name = document.createElement("h1");
- // create string to say "Schedule:
"
- let schedule_name_string = document.createTextNode("Schedule: " + name);
- // add the string to the h1 element
+ let table = draw_table(workScheduleData)
+ document.getElementById("schedule-data").appendChild(table);
+ table.style.display = "none";
+ let toggle_button = document.createElement("button");
+ toggle_button.id = "toggle-table";
+ toggle_button.innerHTML = "Show Schedule Information";
+ toggle_button.onclick = function(){
+ if (table.style.display === "none"){
+ table.style.display = "table";
+ toggle_button.innerHTML = "Hide Schedule Information";
+ } else {
+ toggle_button.innerHTML = "Show Schedule Information";
+ table.style.display = "none";
+ }
+ }
+ document.getElementById("options").appendChild(toggle_button);
+
+ let schedule_name = document.createElement("h2");
+ let schedule_name_string = document.createTextNode("Schedule: " + workScheduleData.Name);
schedule_name.appendChild(schedule_name_string);
- document.getElementById("schedule-data").appendChild(schedule_name);
+ document.getElementById("schedule-header").appendChild(schedule_name);
+
+ let creation_date = document.createElement("h2");
+ let creation_date_string = document.createTextNode("Created: " + new Date(workScheduleData.CreationDate).toLocaleDateString());
+ creation_date.appendChild(creation_date_string);
+ document.getElementById("schedule-header").appendChild(creation_date);
}
\ No newline at end of file
diff --git a/src/blenderbim/blenderbim/bim/module/sequence/gantt/main.css b/src/blenderbim/blenderbim/bim/module/sequence/gantt/main.css
index cb76c9c069..857565f492 100644
--- a/src/blenderbim/blenderbim/bim/module/sequence/gantt/main.css
+++ b/src/blenderbim/blenderbim/bim/module/sequence/gantt/main.css
@@ -24,11 +24,6 @@
background: #D4B483;
}
-.gmainleft {
- overflow: visible;
- flex: 0 1 auto;
-}
-
.gname.ggroupitem {
background-color: #4c92ec;
font-weight: bold;
@@ -63,8 +58,7 @@
.gmainleft {
resize: horizontal;
height:100%;
- overflow-x: visible;
- flex: 0 1 auto;
+ /* flex: 0 1 auto; */
}
.gmainright {
@@ -113,10 +107,66 @@ button, .sweet_button {
box-shadow: 0 0 0.7rem rgba(95, 95, 95, 0.4);
}
+h1 {
+ font-size: 2rem;
+ font-weight: 500;
+ line-height: 1.25;
+ color: black;
+ margin: 0.5rem;
+}
+h2 {
+ font-size: 1.5rem;
+ font-weight: 500;
+ line-height: 1.25;
+ color: black;
+ margin: 0.5rem;
+}
+
h4 {
font-size: 1.2rem;
font-weight: 500;
line-height: 1.25;
color: black;
+ margin: 0.5rem;
}
+.table-description {
+/* create a github style table similar to a wild car */
+ margin-bottom: 1rem;
+ width : 50%;
+ background-color: transparent;
+ border-collapse: collapse;
+ border-spacing: 0;
+ border-color: grey;
+ border: 1px solid #e1e1e1;
+ border-radius: 0.25rem;
+ box-shadow: 0 0 0.7rem rgba(95, 95, 95, 0.4);
+ text-align: center;
+}
+
+.table-description tr {
+ display: table-row;
+ vertical-align: inherit;
+ border-color: inherit;
+ border-bottom: 1px solid #e1e1e1;
+}
+
+.table-description thead {
+ display: table-header-group;
+ vertical-align: middle;
+ border-color: inherit;
+ background-color: #95A5A6;
+}
+
+.top-right {
+ position: absolute;
+ top: 8px;
+ right: 16px;
+}
+
+#schedule-header {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ margin-bottom: 1rem;
+}
\ No newline at end of file
diff --git a/src/blenderbim/blenderbim/bim/module/sequence/ui.py b/src/blenderbim/blenderbim/bim/module/sequence/ui.py
index d238fd8bb2..63277d5f5d 100644
--- a/src/blenderbim/blenderbim/bim/module/sequence/ui.py
+++ b/src/blenderbim/blenderbim/bim/module/sequence/ui.py
@@ -485,10 +485,13 @@ class BIM_PT_work_schedules(Panel):
baseline_row.label(
text="{} @ {}".format(baseline["name"], baseline["date"]), icon="RESTRICT_INSTANCED_OFF"
)
- baseline_row.operator("bim.remove_work_schedule", text="", icon="X").work_schedule = baseline["id"]
+ baseline_row.operator("bim.generate_gantt_chart", text="Compare", icon="NLA").work_schedule = baseline[
+ "id"
+ ]
baseline_row.operator(
"bim.enable_editing_work_schedule_tasks", text="", icon="ACTION"
).work_schedule = baseline["id"]
+ baseline_row.operator("bim.remove_work_schedule", text="", icon="X").work_schedule = baseline["id"]
def draw_readonly_work_schedule_ui(self, work_schedule_id):
if self.props.active_work_schedule_id == work_schedule_id:
@@ -515,6 +518,7 @@ class BIM_PT_work_schedules(Panel):
"active_task_index",
)
+
class BIM_PT_task_icom(Panel):
bl_label = "IFC Task ICOM"
bl_idname = "BIM_PT_task_icom"
diff --git a/src/blenderbim/blenderbim/core/sequence.py b/src/blenderbim/blenderbim/core/sequence.py
index d400a6bedd..f27fbc71b2 100644
--- a/src/blenderbim/blenderbim/core/sequence.py
+++ b/src/blenderbim/blenderbim/core/sequence.py
@@ -543,7 +543,7 @@ def visualise_work_schedule_date(sequence, work_schedule=None):
def generate_gantt_chart(sequence, work_schedule):
json = sequence.create_tasks_json(work_schedule)
- sequence.generate_gantt_browser_chart(json)
+ sequence.generate_gantt_browser_chart(json, work_schedule)
def load_product_related_tasks(sequence, product=None):
diff --git a/src/blenderbim/blenderbim/tool/sequence.py b/src/blenderbim/blenderbim/tool/sequence.py
index 2e3e18eb08..c33b940ec4 100644
--- a/src/blenderbim/blenderbim/tool/sequence.py
+++ b/src/blenderbim/blenderbim/tool/sequence.py
@@ -1171,7 +1171,6 @@ class Sequence(blenderbim.core.tool.Sequence):
@classmethod
def show_snapshot(cls, product_states):
- print(product_states)
bpy.context.scene.frame_start = 1
bpy.context.scene.frame_end = 2
for obj in bpy.data.objects:
@@ -1312,7 +1311,6 @@ class Sequence(blenderbim.core.tool.Sequence):
def clear_object_animation(cls, obj):
if obj.animation_data:
obj.animation_data_clear()
- print("Cleared animation for", obj.name)
@classmethod
def clear_objects_animation(cls, include_blender_objects=True):
@@ -1475,32 +1473,60 @@ class Sequence(blenderbim.core.tool.Sequence):
"USERDEFINED": "FS",
"NOTDEFINED": "FS",
}
-
+ is_baseline = False
+ if work_schedule.PredefinedType == "BASELINE":
+ is_baseline = True
tasks_json = []
for task in ifcopenshell.util.sequence.get_root_tasks(work_schedule):
- cls.create_new_task_json(task, tasks_json, sequence_type_map)
+ cls.create_new_task_json(task, tasks_json, sequence_type_map, is_baseline=is_baseline)
return tasks_json
@classmethod
- def create_new_task_json(cls, task, json, type_map=None):
+ def create_new_task_json(cls, task, json, type_map=None, is_baseline=False):
task_time = task.TaskTime
+ resources = ifcopenshell.util.sequence.get_task_resources(task, is_deep=False)
+
+ string_resources = ""
+ resources_usage = ""
+ for resource in resources:
+ string_resources += resource.Name + ", "
+ resources_usage += str(resource.Usage.ScheduleUsage) + ", " if resource.Usage else "-, "
+
+ schedule_start = task_time.ScheduleStart if task_time else ""
+ schedule_finish = task_time.ScheduleFinish if task_time else ""
+
+ declared_by_task = task.IsDeclaredBy[0].RelatingObject if is_baseline else None
+ if declared_by_task and declared_by_task.TaskTime:
+ compare_start = declared_by_task.TaskTime.ScheduleStart
+ compare_finish = declared_by_task.TaskTime.ScheduleFinish
+ else:
+ compare_start = schedule_start
+ compare_finish = schedule_finish
+
data = {
"pID": task.id(),
"pName": task.Name,
"pCaption": task.Name,
- "pStart": task_time.ScheduleStart if task_time else "",
- "pEnd": task_time.ScheduleFinish if task_time else "",
- "pPlanStart": task_time.ScheduleStart if task_time else "",
- "pPlanEnd": task_time.ScheduleFinish if task_time else "",
+ "pStart": declared_by_task.TaskTime.ScheduleStart
+ if declared_by_task and declared_by_task.TaskTime
+ else schedule_start,
+ "pEnd": declared_by_task.TaskTime.ScheduleFinish
+ if declared_by_task and declared_by_task.TaskTime
+ else schedule_finish,
+ "pPlanStart": schedule_start,
+ "pPlanEnd": schedule_finish,
"pMile": 1 if task.IsMilestone else 0,
+ "pRes": string_resources,
"pComp": 0,
"pGroup": 1 if task.IsNestedBy else 0,
"pParent": task.Nests[0].RelatingObject.id() if task.Nests else 0,
"pOpen": 1,
"pCost": 1,
- "ifcduration": str(ifcopenshell.util.date.ifc2datetime(task_time.ScheduleDuration)) if (task_time and task_time.ScheduleDuration) else "",
+ "ifcduration": str(ifcopenshell.util.date.ifc2datetime(task_time.ScheduleDuration))
+ if (task_time and task_time.ScheduleDuration)
+ else "",
+ "resourceUsage": resources_usage,
}
-
if task_time and task_time.IsCritical:
data["pClass"] = "gtaskred"
elif data["pGroup"]:
@@ -1515,14 +1541,17 @@ class Sequence(blenderbim.core.tool.Sequence):
)
json.append(data)
for nested_task in ifcopenshell.util.sequence.get_nested_tasks(task):
- cls.create_new_task_json(nested_task, json, type_map)
+ cls.create_new_task_json(nested_task, json, type_map, is_baseline)
@classmethod
- def generate_gantt_browser_chart(cls, task_json):
- active_work_schedule = cls.get_active_work_schedule()
+ def generate_gantt_browser_chart(cls, task_json, work_schedule):
with open(os.path.join(bpy.context.scene.BIMProperties.data_dir, "gantt", "index.html"), "w") as f:
with open(os.path.join(bpy.context.scene.BIMProperties.data_dir, "gantt", "index.mustache"), "r") as t:
- f.write(pystache.render(t.read(), {"json_data": json.dumps(task_json), "name":active_work_schedule.Name or "Unnamed"}))
+ f.write(
+ pystache.render(
+ t.read(), {"json_data": json.dumps(task_json), "data": json.dumps(work_schedule.get_info())}
+ )
+ )
webbrowser.open("file://" + os.path.join(bpy.context.scene.BIMProperties.data_dir, "gantt", "index.html"))
@classmethod