mirror of
https://github.com/IfcOpenShell/IfcOpenShell.git
synced 2026-08-09 17:31:45 +00:00
BBIM 4D: You can now visualize & print work schedule baselines with Gantt Charts
This commit is contained in:
@@ -6,7 +6,7 @@
|
||||
var json_data = `{{{json_data}}}`;
|
||||
</script>
|
||||
|
||||
<div class="no-print">
|
||||
<div id="options" class="no-print">
|
||||
<h4>Choose a language:
|
||||
<select id="lang" class="sweet_button" onchange="set_language(event)">
|
||||
<option value='cn'>Chinese (cn)</option>
|
||||
@@ -25,29 +25,31 @@ var json_data = `{{{json_data}}}`;
|
||||
<option value='sv'>Swedish (sv)</option>
|
||||
<option value='tr'>Turkish (tr)</option>
|
||||
</select>
|
||||
<h4>
|
||||
<div id="print_options">
|
||||
<select class="sweet_button" id="print_page_size">
|
||||
<option value="210,297">A4 Portrait</option>
|
||||
<option value="297,210">A4 Landscape</option>
|
||||
<option value="297,420">A3 Portrait</option>
|
||||
<option value="420,297">A3 Landscape</option>
|
||||
<option value="420,594">A2 Portrait</option>
|
||||
<option value="594,420">A2 Landscape</option>
|
||||
<option value="594,841">A1 Portrait</option>
|
||||
<option value="841,594">A1 Landscape</option>
|
||||
<option value="841,1189">A0 Portrait</option>
|
||||
<option value="1189,841">A0 Landscape</option>
|
||||
</select>
|
||||
</div>
|
||||
</h4>
|
||||
|
||||
<br>
|
||||
</div>
|
||||
<div class="top-right no-print" id="print_options">
|
||||
<select class="sweet_button" id="print_page_size">
|
||||
<option value="210,297">A4 Portrait</option>
|
||||
<option value="297,210">A4 Landscape</option>
|
||||
<option value="297,420">A3 Portrait</option>
|
||||
<option value="420,297">A3 Landscape</option>
|
||||
<option value="420,594">A2 Portrait</option>
|
||||
<option value="594,420">A2 Landscape</option>
|
||||
<option value="594,841">A1 Portrait</option>
|
||||
<option value="841,594">A1 Landscape</option>
|
||||
<option value="841,1189">A0 Portrait</option>
|
||||
<option value="1189,841">A0 Landscape</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div id="schedule-data"></div>
|
||||
<div id="schedule-data" class="no-print"></div>
|
||||
<div id="schedule-header" style=""></div>
|
||||
<div style="position:relative" class="gantt" id="GanttChartDIV"></div>
|
||||
|
||||
<script>
|
||||
var name = `{{{name}}}`;
|
||||
setupPage(name);
|
||||
var data = `{{{data}}}`;
|
||||
setupPage(data);
|
||||
create_gantt_chart(json_data)
|
||||
</script>
|
||||
@@ -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: <name>"
|
||||
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);
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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"
|
||||
|
||||
@@ -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):
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user