cost module web-ui:

- remove Blender operators in favor of ifc.api functions
- replace BonsaiBIM Data class dependency with new ifc5D2json script
- enable viewing multiple cost schedules
- add Identifaction column
ref #5369
This commit is contained in:
myoualid
2024-09-20 16:26:40 +01:00
parent e8f66abf17
commit e0e9892ff0
7 changed files with 455 additions and 296 deletions
@@ -19,6 +19,7 @@ body {
margin: 0;
padding: 0;
box-sizing: border-box;
height: 100vh;
}
.floating-form {
@@ -39,15 +40,15 @@ body {
.form-header {
display: flex;
justify-content: flex-start;
justify-content: center;
align-items: center;
margin-bottom: 10px;
background-color: #323936;
padding: var(--padding-medium);
font-size: large;
border-bottom: 1px solid #ddd;
border-radius: 6px 6px 0 0;
height: 5%;
position: relative;
}
.form-container {
@@ -58,9 +59,7 @@ body {
.form-section {
margin-bottom: 1em;
height: 65%;
overflow-y: auto;
overflow-x: hidden;
height: 70%;
}
.form-section h3 {
@@ -185,14 +184,21 @@ body {
.table-container {
margin-top: 1em;
overflow-x: auto;
overflow-y: auto;
position: relative;
border: 1px solid #ddd;
display: flex;
flex-direction: row;
}
.table-wrapper {
flex: 1;
overflow: auto;
border: 2px solid #254e3c;
/* shadow to the top */
box-shadow: 0 -5px 5px -0 #254e3c;
}
.floating-form .table-container {
max-height: 80%;
overflow: auto;
}
#cost-items {
@@ -208,9 +214,12 @@ body {
height: 50vh;
}
tr, td, th {
height: 100%;
}
table {
width: 100%;
height: 100%;
border-collapse: collapse;
user-select: none;
font-size: var(--fontSize);
@@ -247,8 +256,9 @@ td {
align-items: center;
}
#cost-items td:first-child {
.flex-container {
display: flex;
justify-content: space-between;
align-items: center;
}
@@ -375,10 +385,6 @@ form table {
[id^="cost-items"] td:nth-child(1) {
width: auto;
}
[id^="cost-items"] th:not(:nth-child(1)),
[id^="cost-items"] td:not(:nth-child(1)) {
width: 10%;
}
[id^="cost-items"] th:last-child,
[id^="cost-items"] td:last-child {
@@ -411,3 +417,7 @@ td {
position: absolute;
transform: translateY(-50%);
}
i {
margin-right: 5px;
}
@@ -222,7 +222,7 @@ button:hover {
border-collapse: collapse;
display: flex;
justify-content: flex-start;
flex-wrap: wrap;
flex-wrap: nowrap;
}
.column-container {
@@ -160,36 +160,6 @@ function handleConnectedClients(data) {
});
}
function handleCostValuesData(data) {
CostUI.createCostValuesForm({
costValues: data.data["cost_values"]["cost_values"],
costItemId: data.data["cost_values"]["cost_item_id"],
callbacks: {
editCostValues: editCostValues,
addCostValue: addCostValue,
deleteCostValue: deleteCostValue,
},
});
CostUI.highlightElement(data.data["cost_values"]["cost_item_id"]);
}
function addCostValue(costItemId) {
executeOperator({ type: "addCostValue", costItemId: costItemId });
}
function editCostValues(costItemId, costValues) {
executeOperator({
type: "editCostValues",
costItemId: costItemId,
costValues: costValues,
});
}
function deleteCostItem(costItemId) {
executeOperator({ type: "deleteCostItem", costItemId: costItemId });
}
function handleThemeData(themeData) {
function arrayToRgbString(arr) {
const [r, g, b, a] = arr.map((num) => Math.round(num * 255));
@@ -291,34 +261,6 @@ function toggleClientList() {
clientList.addClass("show");
}
function addCostItem(costItemId) {
executeOperator({ type: "addCostItem", costItemId: costItemId });
}
function editCostItemName(costItemId, name) {
executeOperator({
type: "editCostItemName",
costItemId: costItemId,
name: name,
});
}
function selectAssignedElements(costItemId) {
executeOperator({ type: "selectAssignedElements", costItemId: costItemId });
}
function handleWebConnect() {
executeOperator({ type: "getPredefinedTypes" });
getCostSchedules();
}
function addSummaryCostItem(costScheduleId) {
executeOperator({
type: "addSummaryCostItem",
costScheduleId: costScheduleId,
});
}
function handleCostSchedulesData(data) {
const blenderId = data.blenderId;
const loadedSchedule = loadedSchedules[blenderId]
@@ -358,28 +300,20 @@ function handleCostSchedulesData(data) {
}
function handleCostItemsData(data) {
const costScheduleId = data.data["cost_items"]["schedule_data"][0]["id"];
const cards = document.querySelectorAll("[id^='schedule-']");
cards.forEach((card) => {
if (card.id === "schedule-" + data.data["cost_items"]["cost_schedule_id"]) {
card.classList.add("highlighted");
} else {
card.classList.remove("highlighted");
}
card.classList.remove("highlighted");
});
const costScheduleId = data.data["cost_items"]["cost_schedule_id"];
loadedSchedules[data.blenderId] = costScheduleId;
CostUI.highlightElement("schedule-" + costScheduleId);
loadedSchedules[data.blenderId] = costScheduleId;
const currency = data.data["cost_items"]["currency"]
? data.data["cost_items"]["currency"]["name"]
: "Undefined";
const costSchedule = data.data["cost_items"]["schedule_data"][0];
CostUI.createCostSchedule({
costItems: data.data["cost_items"]["cost_items"],
costSchedule: costSchedule,
currency: currency,
costScheduleId: costScheduleId,
blenderID: data.blenderId,
callbacks: {
addCostItem: addCostItem,
deleteCostItem: deleteCostItem,
@@ -394,23 +328,32 @@ function handleCostItemsData(data) {
});
}
function enableEditingQuantities(costItemId) {
executeOperator({ type: "enableEditingQuantities", costItemId: costItemId });
function addCostItem(costItemId) {
executeOperator({ type: "addCostItem", costItemId: costItemId });
}
function duplicateCostItem(costItemId) {
executeOperator({ type: "duplicateCostItem", costItemId: costItemId });
function editCostItemName(costItemId, name) {
executeOperator({
type: "editCostItemName",
costItemId: costItemId,
name: name,
});
}
function executeOperator(operator, blenderId) {
const msg = {
sourcePage: "cost",
operator: operator,
};
if (blenderId !== undefined) {
msg.BlenderId = blenderId;
}
socket.emit("web_operator", msg);
function selectAssignedElements(costItemId) {
executeOperator({ type: "selectAssignedElements", costItemId: costItemId });
}
function handleWebConnect() {
executeOperator({ type: "getPredefinedTypes" });
getCostSchedules();
}
function addSummaryCostItem(costScheduleId) {
executeOperator({
type: "addSummaryCostItem",
costScheduleId: costScheduleId,
});
}
function loadCostSchedule(costScheduleId, blenderId) {
@@ -427,3 +370,53 @@ function getCostSchedules(blenderId) {
function enableEditingCostValues(costItemId) {
executeOperator({ type: "enableEditingCostValues", costItemId: costItemId });
}
function enableEditingQuantities(costItemId) {
executeOperator({ type: "enableEditingQuantities", costItemId: costItemId });
}
function duplicateCostItem(costItemId) {
executeOperator({ type: "duplicateCostItem", costItemId: costItemId });
}
function handleCostValuesData(data) {
CostUI.createCostValuesForm({
costValues: data.data["cost_values"]["cost_values"],
costItemId: data.data["cost_values"]["cost_item_id"],
callbacks: {
editCostValues: editCostValues,
addCostValue: addCostValue,
deleteCostValue: deleteCostValue,
},
});
CostUI.highlightElement(data.data["cost_values"]["cost_item_id"]);
}
function addCostValue(costItemId) {
executeOperator({ type: "addCostValue", costItemId: costItemId });
}
function editCostValues(costItemId, costValues) {
executeOperator({
type: "editCostValues",
costItemId: costItemId,
costValues: costValues,
});
}
function deleteCostItem(costItemId) {
CostUI.deleteCostItem(costItemId);
executeOperator({ type: "deleteCostItem", costItemId: costItemId });
}
function executeOperator(operator, blenderId) {
const msg = {
sourcePage: "cost",
operator: operator,
};
if (blenderId !== undefined) {
msg.BlenderId = blenderId;
}
socket.emit("web_operator", msg);
}
@@ -7,17 +7,42 @@ export class CostUI {
}
static removeCostSchedule(id) {
document.getElementById("cost-items-" + id).remove();
const table = document.getElementById("cost-items-" + id);
table ? table.parentElement.remove() : null;
}
static createCostTable(id, currency, callbacks) {
CostUI.isCostScheduleLoaded(id) ? CostUI.removeCostSchedule(id) : null;
static createCostTable({ costSchedule, currency, callbacks }) {
let tableWrapper;
const id = costSchedule.id;
if (CostUI.isCostScheduleLoaded(id)) {
const table = document.getElementById("cost-items-" + id);
tableWrapper = table.parentElement;
table.remove();
} else {
tableWrapper = document.createElement("div");
tableWrapper.classList.add("table-wrapper");
document.getElementById("cost-items").appendChild(tableWrapper);
const tableHeader = document.createElement("div");
tableHeader.classList.add("form-header");
const text = CostUI.Text(
costSchedule.Name,
"fa-solid fa-money-bill-wave",
"x-large"
);
const callback = () => {
tableWrapper.remove();
CostUI.unhighlightElement("schedule-" + id);
};
let closeButton = CostUI.createCloseButton(callback);
tableHeader.appendChild(text);
tableHeader.appendChild(closeButton);
tableWrapper.appendChild(tableHeader);
}
const table = document.createElement("table");
table.id = "cost-items-" + id;
const tbody = document.createElement("tbody");
tbody.setAttribute("id", "cost-items");
const columnHeaders = [
"ID",
"Name",
"Quantity",
"Unit",
@@ -44,7 +69,7 @@ export class CostUI {
table.appendChild(thead);
table.appendChild(tbody);
document.getElementById("cost-items").appendChild(table);
tableWrapper.appendChild(table);
CostUI.createContextMenu(callbacks);
table.get_blender_id = function () {
@@ -54,12 +79,11 @@ export class CostUI {
return [table, tbody];
}
static deleteCostItem(costItemId, callback) {
static deleteCostItem(costItemId) {
const costItemRow = document.getElementById(costItemId);
let expandedState = JSON.parse(localStorage.getItem("expandedState")) || {};
expandedState = CostUI.deleteCostItemRow(costItemRow, expandedState);
localStorage.setItem("expandedState", JSON.stringify(expandedState));
callback(costItemId);
}
static createContextMenu(callbacks) {
@@ -79,16 +103,20 @@ export class CostUI {
event.preventDefault();
const targetRow = event.target.closest("tr");
const targetCell = event.target.closest("td");
if (targetRow && targetRow.parentElement.id === "cost-items") {
const contextMenu = document.getElementById("context-menu");
contextMenu.style.display = "block";
contextMenu.style.left = `${event.pageX}px`;
contextMenu.style.top = `${event.pageY}px`;
contextMenu.targetRow = targetRow;
contextMenu.targetCell = targetCell;
} else {
document.getElementById("context-menu").style.display = "none";
if (targetRow) {
const isCostTable = targetRow.parentElement.parentElement
? targetRow.parentElement.parentElement.id.includes("cost-items")
: false;
if (isCostTable) {
const contextMenu = document.getElementById("context-menu");
contextMenu.style.display = "block";
contextMenu.style.left = `${event.pageX}px`;
contextMenu.style.top = `${event.pageY}px`;
contextMenu.targetRow = targetRow;
contextMenu.targetCell = targetCell;
} else {
document.getElementById("context-menu").style.display = "none";
}
}
});
@@ -102,104 +130,79 @@ export class CostUI {
const editCostValuesButton = document.getElementById(
"edit-cost-values-button"
);
editCostValuesButton.addEventListener("click", function () {
const targetRow = document.getElementById("context-menu").targetRow;
const targetCell = document.getElementById("context-menu").targetCell;
if (targetRow) {
const costItemId = parseInt(targetRow.getAttribute("id"));
callbacks.enableEditingCostValues
? callbacks.enableEditingCostValues(costItemId)
: null;
}
document.getElementById("context-menu").style.display = "none";
});
const addButton = document.getElementById("add-cost-item-button");
if (!addButton.dataset.listenerAdded) {
function addCostItemHandler(e) {
e.stopPropagation();
const targetRow = document.getElementById("context-menu").targetRow;
if (targetRow) {
const costItemId = parseInt(targetRow.getAttribute("id"));
callbacks.addCostItem ? callbacks.addCostItem(costItemId) : null;
}
document.getElementById("context-menu").style.display = "none";
}
addButton.addEventListener("click", addCostItemHandler);
addButton.dataset.listenerAdded = "true";
}
const deleteCostItemButton = document.getElementById("delete-cost-item");
if (deleteCostItemButton && !deleteCostItemButton.hasListener) {
deleteCostItemButton.addEventListener("click", function () {
const targetRow = document.getElementById("context-menu").targetRow;
if (targetRow) {
const costItemId = parseInt(targetRow.getAttribute("id"));
CostUI.deleteCostItem(costItemId, callbacks.deleteCostItem);
}
document.getElementById("context-menu").style.display = "none";
});
deleteCostItemButton.hasListener = true;
}
const duplicateButton = document.getElementById("duplicate-button");
if (duplicateButton && !duplicateButton.hasListener) {
duplicateButton.addEventListener("click", function () {
const targetRow = document.getElementById("context-menu").targetRow;
if (targetRow) {
const costItemId = parseInt(targetRow.getAttribute("id"));
callbacks.duplicateCostItem(costItemId);
}
document.getElementById("context-menu").style.display = "none";
});
duplicateButton.hasListener = true;
}
const getSelectedProducts = document.getElementById("assign-selection");
if (getSelectedProducts && !getSelectedProducts.hasListener) {
getSelectedProducts.addEventListener("click", function () {
const targetRow = document.getElementById("context-menu").targetRow;
if (targetRow) {
const costItemId = parseInt(targetRow.getAttribute("id"));
callbacks.enableEditingQuantities(costItemId);
}
document.getElementById("context-menu").style.display = "none";
});
getSelectedProducts.hasListener = true;
function handleButtonClick(button, callback) {
if (button && !button.hasListener) {
button.addEventListener("click", function () {
const targetRow = document.getElementById("context-menu").targetRow;
if (targetRow) {
const costItemId = parseInt(targetRow.getAttribute("id"));
callback(costItemId);
}
document.getElementById("context-menu").style.display = "none";
});
button.hasListener = true;
}
}
function getColumnNames(tableId) {
const table = document.getElementById(tableId);
const headerRow = table.querySelector("thead tr");
return Array.from(headerRow.children).map((headerCell) =>
headerCell.textContent.trim()
);
// if (deleteCostItemButton && !deleteCostItemButton.hasListener) {
// deleteCostItemButton.addEventListener("click", function () {
// const targetRow = document.getElementById("context-menu").targetRow;
// if (targetRow) {
// const costItemId = parseInt(targetRow.getAttribute("id"));
// CostUI.deleteCostItem(costItemId, callbacks.deleteCostItem);
// }
// document.getElementById("context-menu").style.display = "none";
// });
// deleteCostItemButton.hasListener = true;
// }
handleButtonClick(deleteCostItemButton, callbacks.deleteCostItem);
handleButtonClick(duplicateButton, callbacks.duplicateCostItem);
handleButtonClick(getSelectedProducts, callbacks.enableEditingQuantities);
handleButtonClick(editCostValuesButton, callbacks.enableEditingCostValues);
handleButtonClick(addButton, callbacks.addCostItem);
handleButtonClick(deleteCostItemButton, callbacks.deleteCostItem);
addTableListeners(callbacks);
function addTableListeners(callbacks) {
function getColumnNames(tableId) {
const table = document.getElementById(tableId);
const headerRow = table.querySelector("thead tr");
return Array.from(headerRow.children).map((headerCell) =>
headerCell.textContent.trim()
);
}
document
.getElementById("cost-items")
.addEventListener("click", function (event) {
const targetRow = event.target.closest("tr");
const targetCell = event.target.closest("td");
if (targetRow && targetCell) {
const columnIndex = Array.from(targetRow.children).indexOf(
targetCell
);
const costItemId = parseInt(targetRow.getAttribute("id"));
const columnName = getColumnNames("cost-items")[columnIndex];
if (columnName.includes("Cost") && !columnName.includes("Total")) {
callbacks.enableEditingCostValues
? callbacks.enableEditingCostValues(costItemId)
: null;
}
if (columnName === "Quantity") {
callbacks.enableEditingQuantities
? callbacks.enableEditingQuantities(costItemId)
: null;
}
}
});
}
document
.getElementById("cost-items")
.addEventListener("click", function (event) {
const targetRow = event.target.closest("tr");
const targetCell = event.target.closest("td");
if (targetRow && targetCell) {
const columnIndex = Array.from(targetRow.children).indexOf(
targetCell
);
const costItemId = parseInt(targetRow.getAttribute("id"));
const columnName = getColumnNames("cost-items")[columnIndex];
if (columnName.includes("Cost") && !columnName.includes("Total")) {
callbacks.enableEditingCostValues
? callbacks.enableEditingCostValues(costItemId)
: null;
}
if (columnName === "Quantity") {
callbacks.enableEditingQuantities
? callbacks.enableEditingQuantities(costItemId)
: null;
}
}
});
}
static deleteCostItemRow(targetRow, expandedState) {
@@ -353,19 +356,13 @@ export class CostUI {
return div;
}
static createCostSchedule({
costItems,
currency,
costScheduleId,
blenderID,
callbacks = {},
}) {
const [table, tbody] = CostUI.createCostTable(
blenderID,
static createCostSchedule({ costSchedule, currency, callbacks = {} }) {
const [table, tbody] = CostUI.createCostTable({
costSchedule,
currency,
callbacks
);
if (costItems.length === 0) {
callbacks,
});
if (costSchedule["cost_items"].length === 0) {
const tr = document.createElement("tr");
const td = document.createElement("td");
td.colSpan = 6;
@@ -383,7 +380,13 @@ export class CostUI {
td.appendChild(addSummaryCostItemButton);
addSummaryCostItemButton.classList.add("action-button");
} else {
CostUI.createCostTree(costItems, tbody, 0, null, callbacks);
CostUI.createCostTree(
costSchedule["cost_items"],
tbody,
0,
null,
callbacks
);
CostUI.applyExpandedState();
}
}
@@ -404,9 +407,9 @@ export class CostUI {
);
container.appendChild(row);
if (costItem.is_nested_by && costItem.is_nested_by.length > 0) {
if (costItem.IsNestedBy && costItem.IsNestedBy.length > 0) {
CostUI.createCostTree(
costItem.is_nested_by,
costItem.IsNestedBy,
container,
nestingLevel + 1,
costItem.id,
@@ -434,6 +437,10 @@ export class CostUI {
? CostUI.format_number(costItem.TotalAppliedValue)
: "-";
const row = CostUI.createCostItemRow(costItem, nestingLevel, parentID);
const identification = costItem.Identification
? costItem.Identification
: "XXX";
const idCell = CostUI.createTableCell(identification);
const expandButton = CostUI.createExpandButton(costItem);
const nameCell = CostUI.createNameCell(
costItem,
@@ -456,6 +463,7 @@ export class CostUI {
actionsCell.classList.add("actions-column");
row.appendChild(idCell);
row.appendChild(nameCell);
row.appendChild(totalCostQuantityCell);
row.appendChild(unitSymbolCell);
@@ -491,7 +499,7 @@ export class CostUI {
expandButton.classList.add("fa-solid");
expandButton.classList.add("fa-angle-right");
if (row.is_nested_by && row.is_nested_by.length > 0) {
if (row.IsNestedBy && row.IsNestedBy.length > 0) {
expandButton.classList.add("fa-angle-right");
} else {
expandButton.style.visibility = "hidden";
@@ -614,7 +622,7 @@ export class CostUI {
static createNameCell(costItem, nestingLevel, expandButton, callbacks) {
const nameCell = document.createElement("td");
const nameInput = document.createElement("input");
nameInput.value = costItem.name ? costItem.name : "Unnamed";
nameInput.value = costItem.Name ? costItem.Name : "Unnamed";
nameInput.addEventListener("change", function () {
callbacks.editCostItemName
@@ -631,6 +639,7 @@ export class CostUI {
});
nameCell.style.paddingLeft = nestingLevel * 20 + "px";
nameCell.classList.add("row-container");
nameCell.appendChild(expandButton);
nameCell.appendChild(nameInput);
const widthButton = 20;
@@ -652,7 +661,7 @@ export class CostUI {
const totalCostCell = document.createElement("td");
const totalCost = CostUI.format_number(costItem.TotalCost);
totalCostCell.textContent = totalCost;
if (costItem.is_sum) {
if (costItem.IsSum) {
totalCostCell.textContent = totalCost + " *";
} else if (!costItem.TotalCost) {
const button = CostUI.createButton("Sum", "fa-solid fa-calculator");
@@ -746,7 +755,7 @@ export class CostUI {
const deleteButton = CostUI.createButton("Delete", "fa-solid fa-trash");
deleteButton.addEventListener(
"click",
CostUI.deleteCostItem.bind(null, costItemId, deleteCostItem)
deleteCostItem.bind(null, costItemId)
);
return deleteButton;
}
@@ -1089,6 +1098,13 @@ export class CostUI {
}
static Form({ id, name, icon = "fa-solid fa-file", shouldHide = false }) {
function style(form) {
form.classList.add("floating-form");
form.style.position = "absolute";
form.style.top = "50%";
form.style.left = "50%";
form.style.transform = "translate(-50%, -50%)";
}
if (document.getElementById(id)) {
const formContainer = document
.getElementById(id)
@@ -1098,17 +1114,19 @@ export class CostUI {
}
const form = document.createElement("form");
form.id = id;
form.classList.add("floating-form");
form.style.position = "absolute";
form.style.top = "50%";
form.style.left = "50%";
form.style.transform = "translate(-50%, -50%)";
style(form);
const header = CostUI.createHeader(name, icon);
let closeButton;
if (shouldHide) {
closeButton = CostUI.createHideButton(form);
} else {
closeButton = CostUI.createCloseButton(form);
const callback = () => {
form.remove();
const costItemId = form.id.split("-").pop();
CostUI.unhighlightElement(costItemId);
};
closeButton = CostUI.createCloseButton(callback); // Pass the function reference without invoking it
}
form.appendChild(header);
form.appendChild(closeButton);
@@ -1200,16 +1218,11 @@ export class CostUI {
return hideButton;
}
static createCloseButton(form) {
static createCloseButton(callback) {
const closeButton = document.createElement("span");
closeButton.classList.add("close-button");
closeButton.innerHTML = "×";
closeButton.addEventListener("click", function () {
form.remove();
const costItemId = form.id.split("-").pop();
CostUI.unhighlightElement(costItemId);
});
closeButton.addEventListener("click", callback);
return closeButton;
}
@@ -2114,9 +2127,7 @@ export class CostUI {
});
}
});
}
else if (key.toLowerCase().includes("value")) {
} else if (key.toLowerCase().includes("value")) {
const cell = document.createElement("td");
const input = document.createElement("input");
input.type = "number";
+1 -6
View File
@@ -865,12 +865,7 @@ class Cost(bonsai.core.tool.Cost):
def generate_cost_schedule_browser(cls, cost_chedule) -> None:
if not bpy.context.scene.WebProperties.is_connected:
bpy.ops.bim.connect_websocket_server(page="costing")
cost_schedule_data = cls.create_cost_schedule_json(cost_chedule)
tool.Web.send_webui_data(
data={"cost_items": cost_schedule_data, "cost_schedule_id": cost_chedule.id(), "currency": cls.currency()},
data_key="cost_items",
event="cost_items",
)
tool.Web.load_cost_schedule_web_ui(cost_chedule)
@classmethod
def get_cost_quantities(cls, cost_item: ifcopenshell.entity_instance) -> dict:
+36 -46
View File
@@ -41,6 +41,7 @@ from time import sleep
from pathlib import Path
import bonsai.core.sequence
import bonsai.core.cost
from ifc5d.ifc2json import ifc5D2json
sio = None
ws_process = None
@@ -346,9 +347,11 @@ class Web(bonsai.core.tool.Web):
ifc_file = tool.Ifc.get()
if operator_data["type"] == "getPredefinedTypes":
print("getting predefined types")
predefined_types = ifcopenshell.util.cost.get_cost_schedule_types(ifc_file)
cls.send_webui_data(data=predefined_types, data_key="predefined_types", event="predefined_types")
cls.send_webui_data(
data=ifcopenshell.util.cost.get_cost_schedule_types(ifc_file),
data_key="predefined_types",
event="predefined_types"
)
if operator_data["type"] == "addCostSchedule":
bonsai.core.cost.add_cost_schedule(
tool.Ifc, name=operator_data["name"], predefined_type=operator_data["predefinedType"]
@@ -361,7 +364,6 @@ class Web(bonsai.core.tool.Web):
event="cost_schedules",
)
if operator_data["type"] == "getCostSchedules":
print("getting cost schedules")
cost_schedules_json = [cs.get_info(recursive=True) for cs in ifc_file.by_type("IfcCostSchedule")]
currency = tool.Cost.currency()
cls.send_webui_data(
@@ -370,38 +372,36 @@ class Web(bonsai.core.tool.Web):
event="cost_schedules",
)
if operator_data["type"] == "loadCostSchedule":
print("loading cost schedule")
cost_schedule = ifc_file.by_id(operator_data["costScheduleId"])
bonsai.core.cost.enable_editing_cost_items(tool.Cost, cost_schedule=cost_schedule)
cls.load_cost_schedule_web_ui(cost_schedule)
if operator_data["type"] == "selectAssignedElements":
print("selecting assigned elements")
cost_item = tool.Ifc.get().by_id(operator_data["costItemId"])
products = tool.Cost.get_cost_item_products(cost_item, is_deep=True)
tool.Spatial.select_products(products, unhide=True)
if operator_data["type"] == "addSummaryCostItem":
cost_schedule = ifc_file.by_id(operator_data["costScheduleId"])
bonsai.core.cost.add_summary_cost_item(tool.Ifc, tool.Cost, cost_schedule=cost_schedule)
tool.Ifc.run("cost.add_cost_item", cost_schedule=cost_schedule)
cls.load_cost_schedule_web_ui(cost_schedule)
if operator_data["type"] == "addCostItem":
if not operator_data["costItemId"]:
return
bpy.ops.bim.add_cost_item(cost_item=operator_data["costItemId"])
cost_schedule = tool.Cost.get_cost_schedule(cost_item=tool.Ifc.get().by_id(operator_data["costItemId"]))
cost_item=tool.Ifc.get().by_id(operator_data["costItemId"])
tool.Ifc.run("cost.add_cost_item", cost_item=cost_item)
cost_schedule = tool.Cost.get_cost_schedule(cost_item=cost_item)
cls.load_cost_schedule_web_ui(cost_schedule)
if operator_data["type"] == "deleteCostItem":
cost_schedule = tool.Cost.get_cost_schedule(cost_item=tool.Ifc.get().by_id(operator_data["costItemId"]))
bpy.ops.bim.remove_cost_item(cost_item=operator_data["costItemId"])
cost_item = tool.Ifc.get().by_id(operator_data["costItemId"])
tool.Ifc.run("cost.remove_cost_item", cost_item=cost_item)
cost_schedule = tool.Cost.get_cost_schedule(cost_item=cost_item)
cls.load_cost_schedule_web_ui(cost_schedule)
if operator_data["type"] == "duplicateCostItem":
cost_item = tool.Ifc.get().by_id(operator_data["costItemId"])
cost_schedule = tool.Cost.get_cost_schedule(cost_item=cost_item)
new_cost_item = ifcopenshell.api.cost.copy_cost_item(ifc_file, cost_item)
tool.Ifc.run("cost.copy_cost_item", cost_item=cost_item)
cls.load_cost_schedule_web_ui(cost_schedule)
if operator_data["type"] == "editCostItemName":
cost_item = tool.Ifc.get().by_id(operator_data["costItemId"])
tool.Ifc.run("cost.edit_cost_item", cost_item=cost_item, attributes={"Name": operator_data["name"]})
tool.Cost.load_cost_schedule_tree()
if operator_data["type"] == "enableEditingCostValues":
cost_item = tool.Ifc.get().by_id(operator_data["costItemId"])
cost_values = ifcopenshell.util.cost.get_cost_values(cost_item)
@@ -422,18 +422,15 @@ class Web(bonsai.core.tool.Web):
)
if operator_data["type"] == "addSumCostValue":
cost_item = ifc_file.by_id(operator_data["costItemId"])
value = ifcopenshell.api.cost.add_cost_value(
ifc_file,
parent=cost_item,
)
ifcopenshell.api.cost.edit_cost_value(file=ifc_file, cost_value=value, attributes={"Category": "*"})
value = tool.Ifc.run("cost.add_cost_value", parent=cost_item)
tool.Ifc.run("cost.edit_cost_value", cost_value=value, attributes={"Category": "*"})
cost_schedule = tool.Cost.get_cost_schedule(cost_item=cost_item)
tool.Cost.load_cost_schedule_tree()
cls.load_cost_schedule_web_ui(cost_schedule)
if operator_data["type"] == "deleteCostValue":
bpy.ops.bim.remove_cost_value(parent=operator_data["costItemId"], cost_value=operator_data["costValueId"])
cost_item = ifc_file.by_id(operator_data["costItemId"])
cost_value = ifc_file.by_id(operator_data["costValueId"])
tool.Ifc.run("cost.remove_cost_value", parent=cost_item, cost_value=cost_value)
cost_schedule = tool.Cost.get_cost_schedule(cost_item=cost_item)
cls.load_cost_schedule_web_ui(cost_schedule)
if operator_data["type"] == "editCostValues":
@@ -441,7 +438,6 @@ class Web(bonsai.core.tool.Web):
cost_item = ifc_file.by_id(cost_item_id)
cost_schedule = tool.Cost.get_cost_schedule(cost_item=cost_item)
for value_data in operator_data["costValues"] or []:
print("editing cost value data", value_data)
value = ifc_file.by_id(value_data["id"])
if value_data["costType"] == "FIXED":
attributes = {"AppliedValue": value_data["appliedValue"], "Category": None}
@@ -453,51 +449,43 @@ class Web(bonsai.core.tool.Web):
elif value_data["costType"] == "SUM":
attributes = {"Category": "*"}
ifcopenshell.api.cost.edit_cost_value(file=ifc_file, cost_value=value, attributes=attributes)
print(value.get_info())
tool.Cost.load_cost_schedule_tree()
cls.load_cost_schedule_web_ui(cost_schedule)
if operator_data["type"] == "addProductAssignments":
prop_name = operator_data["propName"]
if prop_name == "count":
prop_name = ""
bpy.ops.bim.assign_cost_item_quantity(
cost_item=operator_data["costItemId"], related_object_type="PRODUCT", prop_name=prop_name
)
bonsai.core.cost.assign_cost_item_quantity(tool.Ifc, tool.Cost, cost_item=ifc_file.by_id(operator_data["costItemId"]), related_object_type="PRODUCT", prop_name=prop_name)
cost_schedule = tool.Cost.get_cost_schedule(cost_item=tool.Ifc.get().by_id(operator_data["costItemId"]))
cls.load_cost_schedule_web_ui(cost_schedule)
if operator_data["type"] == "enableEditingQuantities":
cost_item_id = operator_data["costItemId"]
cost_item = ifc_file.by_id(cost_item_id)
cls.enableEditingCostItemQuantities(cost_item)
cls.load_cost_item_quantities_ui(cost_item)
if operator_data["type"] == "AddCostItemQuantity":
cost_item_id = operator_data["costItemId"]
cost_item = ifc_file.by_id(cost_item_id)
cost_schedule = tool.Cost.get_cost_schedule(cost_item)
bpy.ops.bim.add_cost_item_quantity(cost_item=cost_item_id, ifc_class=operator_data["ifcClass"])
tool.Ifc.run("cost.add_cost_item_quantity", cost_item=cost_item, ifc_class=operator_data["ifcClass"])
cls.load_cost_schedule_web_ui(cost_schedule)
cls.enableEditingCostItemQuantities(cost_item)
cls.load_cost_item_quantities_ui(cost_item)
if operator_data["type"] == "editCostItemQuantity":
cost_item_id = operator_data["costItemId"]
cost_item = ifc_file.by_id(cost_item_id)
cost_schedule = tool.Cost.get_cost_schedule(cost_item)
physical_quantity = tool.Ifc.get().by_id(operator_data["quantityId"])
attributes = operator_data["attributes"]
tool.Ifc.run("cost.edit_cost_item_quantity", physical_quantity=physical_quantity, attributes=attributes)
tool.Cost.load_cost_item_quantities(ifc_file.by_id(operator_data["costItemId"]))
tool.Ifc.run("cost.edit_cost_item_quantity", physical_quantity=physical_quantity, attributes=operator_data["attributes"])
cls.load_cost_schedule_web_ui(cost_schedule)
cls.enableEditingCostItemQuantities(cost_item)
cls.load_cost_item_quantities_ui(cost_item)
if operator_data["type"] == "deleteCostItemQuantity":
bpy.ops.bim.remove_cost_item_quantity(
cost_item=operator_data["costItemId"], physical_quantity=operator_data["quantityId"]
)
cost_item_id = operator_data["costItemId"]
cost_item = ifc_file.by_id(cost_item_id)
cost_item = ifc_file.by_id(operator_data["costItemId"])
physical_quantity = ifc_file.by_id(operator_data["quantityId"])
tool.Ifc.run("cost.remove_cost_item_quantity", cost_item=cost_item, physical_quantity=physical_quantity)
cost_schedule = tool.Cost.get_cost_schedule(cost_item)
cls.load_cost_schedule_web_ui(cost_schedule)
cls.enableEditingCostItemQuantities(cost_item)
cls.load_cost_item_quantities_ui(cost_item)
@classmethod
def enableEditingCostItemQuantities(cls, cost_item):
def load_cost_item_quantities_ui(cls, cost_item : ifcopenshell.entity_instance) -> None:
if not cost_item:
return
selected_products = list(tool.Spatial.get_selected_products())
@@ -520,7 +508,7 @@ class Web(bonsai.core.tool.Web):
)
@classmethod
def selection_data(cls, cost_item, elements):
def selection_data(cls, cost_item : ifcopenshell.entity_instance, elements : list) -> list:
if not elements:
return []
if not cost_item:
@@ -566,13 +554,15 @@ class Web(bonsai.core.tool.Web):
return data
@classmethod
def load_cost_schedule_web_ui(cls, cost_schedule):
json_data = tool.Cost.create_cost_schedule_json(cost_schedule)
def load_cost_schedule_web_ui(cls, cost_schedule :ifcopenshell.entity_instance) -> None:
ifc2json = ifc5D2json()
ifc2json.file_path = tool.Ifc.get()
ifc2json.cost_schedule = cost_schedule
schedule_data = ifc2json.convert_ifc_to_json()
cls.send_webui_data(
data={
"cost_items": json_data,
"cost_schedule_id": cost_schedule.id(),
"currency": tool.Cost.currency(),
"schedule_data": schedule_data,
},
data_key="cost_items",
event="cost_items",