From dd7da65b181e374ba76812120d99e8c06948e4f1 Mon Sep 17 00:00:00 2001
From: Odei Maiz <33152403+odeimaiz@users.noreply.github.com>
Date: Mon, 3 Jun 2024 12:55:19 +0200
Subject: [PATCH] =?UTF-8?q?=E2=9C=A8=20Frontend:=20Expose=20``inputs=20req?=
=?UTF-8?q?uired``=20property=20(#5899)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.../class/osparc/dashboard/StudyBrowser.js | 4 +-
.../source/class/osparc/data/model/Node.js | 57 +++++--------
.../class/osparc/data/model/Workbench.js | 11 +--
.../class/osparc/desktop/StudyEditor.js | 4 +-
.../source/class/osparc/form/PortInfoHint.js | 2 +-
.../class/osparc/form/renderer/PropForm.js | 79 +++++++++++++------
.../osparc/form/renderer/PropFormBase.js | 34 +++++++-
.../source/class/osparc/widget/NodeOptions.js | 2 +
8 files changed, 119 insertions(+), 74 deletions(-)
diff --git a/services/static-webserver/client/source/class/osparc/dashboard/StudyBrowser.js b/services/static-webserver/client/source/class/osparc/dashboard/StudyBrowser.js
index 1f4260bbf29..b1a87f56194 100644
--- a/services/static-webserver/client/source/class/osparc/dashboard/StudyBrowser.js
+++ b/services/static-webserver/client/source/class/osparc/dashboard/StudyBrowser.js
@@ -285,8 +285,6 @@ qx.Class.define("osparc.dashboard.StudyBrowser", {
},
_reloadCards: function() {
- this.__addNewStudyButtons();
-
const fetching = this._loadingResourcesBtn ? this._loadingResourcesBtn.getFetching() : false;
const visibility = this._loadingResourcesBtn ? this._loadingResourcesBtn.getVisibility() : "excluded";
@@ -294,6 +292,8 @@ qx.Class.define("osparc.dashboard.StudyBrowser", {
const cards = this._resourcesContainer.reloadCards("studiesList");
this.__configureCards(cards);
+ this.__addNewStudyButtons();
+
const loadMoreBtn = this.__createLoadMoreButton();
loadMoreBtn.set({
fetching,
diff --git a/services/static-webserver/client/source/class/osparc/data/model/Node.js b/services/static-webserver/client/source/class/osparc/data/model/Node.js
index afca119c2c9..488c1b54f6f 100644
--- a/services/static-webserver/client/source/class/osparc/data/model/Node.js
+++ b/services/static-webserver/client/source/class/osparc/data/model/Node.js
@@ -53,7 +53,6 @@ qx.Class.define("osparc.data.model.Node", {
this.setOutputs({});
this.__inputNodes = [];
- this.__exposedNodes = [];
if (study) {
this.setStudy(study);
@@ -136,6 +135,12 @@ qx.Class.define("osparc.data.model.Node", {
event: "changeInputs"
},
+ inputsRequired: {
+ check: "Array",
+ init: [],
+ event: "changeInputsRequired"
+ },
+
outputs: {
check: "Object",
nullable: false,
@@ -228,7 +233,8 @@ qx.Class.define("osparc.data.model.Node", {
"fileUploaded": "qx.event.type.Event",
"showInLogger": "qx.event.type.Data",
"outputListChanged": "qx.event.type.Event",
- "changeInputNodes": "qx.event.type.Event"
+ "changeInputNodes": "qx.event.type.Event",
+ "changeInputsRequired": "qx.event.type.Event"
},
statics: {
@@ -331,7 +337,6 @@ qx.Class.define("osparc.data.model.Node", {
members: {
__metaData: null,
__inputNodes: null,
- __exposedNodes: null,
__settingsForm: null,
__posX: null,
__posY: null,
@@ -512,7 +517,8 @@ qx.Class.define("osparc.data.model.Node", {
}
this.setOutputData(nodeData.outputs);
this.addInputNodes(nodeData.inputNodes);
- this.addOutputNodes(nodeData.outputNodes);
+ // backwards compatible
+ this.setInputsRequired(nodeData.inputsRequired || []);
},
populateStates: function(nodeData) {
@@ -875,43 +881,17 @@ qx.Class.define("osparc.data.model.Node", {
},
// !---- Input Nodes -----
- // ----- Output Nodes -----
- getOutputNodes: function() {
- return this.__exposedNodes;
- },
-
- addOutputNodes: function(outputNodes) {
- if (outputNodes) {
- outputNodes.forEach(outputNode => {
- this.addOutputNode(outputNode);
- });
- }
- },
-
- addOutputNode: function(outputNodeId) {
- if (!this.__exposedNodes.includes(outputNodeId)) {
- this.__exposedNodes.push(outputNodeId);
- this.fireEvent("outputListChanged");
- return true;
- }
- return false;
- },
-
- removeOutputNode: function(outputNodeId) {
- const index = this.__exposedNodes.indexOf(outputNodeId);
+ toggleInputRequired: function(portId) {
+ const inputsRequired = this.getInputsRequired();
+ const index = inputsRequired.indexOf(portId);
if (index > -1) {
- // remove node connection
- this.__exposedNodes.splice(index, 1);
- this.fireEvent("outputListChanged");
+ inputsRequired.splice(index, 1);
+ } else {
+ inputsRequired.push(portId);
}
- return false;
- },
-
- isOutputNode: function(outputNodeId) {
- const index = this.__exposedNodes.indexOf(outputNodeId);
- return (index > -1);
+ this.setInputsRequired(inputsRequired);
+ this.fireEvent("changeInputsRequired");
},
- // !---- Output Nodes -----
canNodeStart: function() {
return this.isDynamic() && ["idle", "failed"].includes(this.getStatus().getInteractive());
@@ -1532,6 +1512,7 @@ qx.Class.define("osparc.data.model.Node", {
inputsUnits: this.__getInputUnits(),
inputAccess: this.getInputAccess(),
inputNodes: this.getInputNodes(),
+ inputsRequired: this.getInputsRequired(),
thumbnail: this.getThumbnail(),
bootOptions: this.getBootOptions()
};
diff --git a/services/static-webserver/client/source/class/osparc/data/model/Workbench.js b/services/static-webserver/client/source/class/osparc/data/model/Workbench.js
index 072913795d4..01da1153297 100644
--- a/services/static-webserver/client/source/class/osparc/data/model/Workbench.js
+++ b/services/static-webserver/client/source/class/osparc/data/model/Workbench.js
@@ -676,12 +676,11 @@ qx.Class.define("osparc.data.model.Workbench", {
if (node === null) {
continue;
}
- this.__addInputOutputNodesAndEdges(node, nodeData.inputNodes, true);
- this.__addInputOutputNodesAndEdges(node, nodeData.outputNodes, false);
+ this.__addInputOutputNodesAndEdges(node, nodeData.inputNodes);
}
},
- __addInputOutputNodesAndEdges: function(node, inputOutputNodeIds, isInput) {
+ __addInputOutputNodesAndEdges: function(node, inputOutputNodeIds) {
if (inputOutputNodeIds) {
inputOutputNodeIds.forEach(inputOutputNodeId => {
const node1 = this.getNode(inputOutputNodeId);
@@ -690,11 +689,7 @@ qx.Class.define("osparc.data.model.Workbench", {
}
const edge = new osparc.data.model.Edge(null, node1, node);
this.addEdge(edge);
- if (isInput) {
- node.addInputNode(inputOutputNodeId);
- } else {
- node.addOutputNode(inputOutputNodeId);
- }
+ node.addInputNode(inputOutputNodeId);
});
}
},
diff --git a/services/static-webserver/client/source/class/osparc/desktop/StudyEditor.js b/services/static-webserver/client/source/class/osparc/desktop/StudyEditor.js
index 9ad894e029a..4b0848288d2 100644
--- a/services/static-webserver/client/source/class/osparc/desktop/StudyEditor.js
+++ b/services/static-webserver/client/source/class/osparc/desktop/StudyEditor.js
@@ -169,7 +169,7 @@ qx.Class.define("osparc.desktop.StudyEditor", {
study.openStudy()
.then(() => {
- this.__lastSavedStudy = study.serialize();
+ this.__lastSavedStudy = osparc.utils.Utils.deepCloneObject(study.serialize());
this.__workbenchView.setStudy(study);
this.__slideshowView.setStudy(study);
@@ -761,7 +761,7 @@ qx.Class.define("osparc.desktop.StudyEditor", {
const newObj = this.getStudy().serialize();
return this.getStudy().updateStudy(newObj, run)
.then(() => {
- this.__lastSavedStudy = osparc.wrapper.JsonDiffPatch.getInstance().clone(newObj);
+ this.__lastSavedStudy = osparc.utils.Utils.deepCloneObject(newObj);
})
.catch(error => {
if ("status" in error && error.status === 409) {
diff --git a/services/static-webserver/client/source/class/osparc/form/PortInfoHint.js b/services/static-webserver/client/source/class/osparc/form/PortInfoHint.js
index 9c1bf89f9b6..70ddcf32bf0 100644
--- a/services/static-webserver/client/source/class/osparc/form/PortInfoHint.js
+++ b/services/static-webserver/client/source/class/osparc/form/PortInfoHint.js
@@ -38,7 +38,7 @@ qx.Class.define("osparc.form.PortInfoHint", {
const color = qx.theme.manager.Color.getInstance().resolve("failed-red");
text += `
${errorMsg}`;
}
- this._hint.setText(text);
+ this.setHintText(text);
this.set({
source: errorMsg ? this.self().ERROR_ICON : osparc.ui.hint.InfoHint.INFO_ICON,
textColor: errorMsg ? "failed-red" : "text"
diff --git a/services/static-webserver/client/source/class/osparc/form/renderer/PropForm.js b/services/static-webserver/client/source/class/osparc/form/renderer/PropForm.js
index 733be9bd936..07fa01cb1f2 100644
--- a/services/static-webserver/client/source/class/osparc/form/renderer/PropForm.js
+++ b/services/static-webserver/client/source/class/osparc/form/renderer/PropForm.js
@@ -286,6 +286,12 @@ qx.Class.define("osparc.form.renderer.PropForm", {
});
});
}
+
+ if (optionsMenu.getChildren().length) {
+ optionsMenu.addSeparator();
+ }
+ const inputRequiredButton = this.__getInputRequiredButton(field.key);
+ optionsMenu.add(inputRequiredButton);
},
__connectToInputNode: function(targetPortId, inputNodeId, outputKey) {
@@ -342,6 +348,26 @@ qx.Class.define("osparc.form.renderer.PropForm", {
return null;
},
+ __populateInputNodePortsMenu: function(inputNodeId, targetPortId, menu, menuBtn) {
+ menuBtn.exclude();
+ menu.removeAll();
+
+ const inputNode = this.getStudy().getWorkbench().getNode(inputNodeId);
+ if (inputNode) {
+ for (const outputKey in inputNode.getOutputs()) {
+ osparc.utils.Ports.arePortsCompatible(inputNode, outputKey, this.getNode(), targetPortId)
+ .then(compatible => {
+ if (compatible) {
+ const paramButton = new qx.ui.menu.Button(inputNode.getOutput(outputKey).label);
+ paramButton.addListener("execute", () => this.__connectToInputNode(targetPortId, inputNodeId, outputKey), this);
+ menu.add(paramButton);
+ menuBtn.show();
+ }
+ });
+ }
+ }
+ },
+
__getSelectFileButton: function(portId) {
const selectFileButton = new qx.ui.menu.Button(this.tr("Select File"));
selectFileButton.addListener("execute", () => this.fireDataEvent("filePickerRequested", {
@@ -366,26 +392,6 @@ qx.Class.define("osparc.form.renderer.PropForm", {
return existingParamBtn;
},
- __populateInputNodePortsMenu: function(inputNodeId, targetPortId, menu, menuBtn) {
- menuBtn.exclude();
- menu.removeAll();
-
- const inputNode = this.getStudy().getWorkbench().getNode(inputNodeId);
- if (inputNode) {
- for (const outputKey in inputNode.getOutputs()) {
- osparc.utils.Ports.arePortsCompatible(inputNode, outputKey, this.getNode(), targetPortId)
- .then(compatible => {
- if (compatible) {
- const paramButton = new qx.ui.menu.Button(inputNode.getOutput(outputKey).label);
- paramButton.addListener("execute", () => this.__connectToInputNode(targetPortId, inputNodeId, outputKey), this);
- menu.add(paramButton);
- menuBtn.show();
- }
- });
- }
- }
- },
-
__populateExistingParamsMenu: function(targetPortId, menu, menuBtn) {
menuBtn.exclude();
menu.removeAll();
@@ -410,6 +416,26 @@ qx.Class.define("osparc.form.renderer.PropForm", {
});
},
+ __getInputRequiredButton: function(portId) {
+ const node = this.getNode();
+ const inputRequiredBtn = new qx.ui.menu.Button(this.tr("Required Input"));
+ const evalButton = () => {
+ if (node.getInputsRequired().includes(portId)) {
+ inputRequiredBtn.set({
+ icon: "@FontAwesome5Regular/check-square/12"
+ });
+ } else {
+ inputRequiredBtn.set({
+ icon: "@FontAwesome5Regular/square/12"
+ });
+ }
+ }
+ node.addListener("changeInputsRequired", () => evalButton(), this);
+ inputRequiredBtn.addListener("execute", () => node.toggleInputRequired(portId), this);
+ evalButton();
+ return inputRequiredBtn;
+ },
+
// overridden
addItems: function(items, names, title, itemOptions, headerOptions) {
this.base(arguments, items, names, title, itemOptions, headerOptions);
@@ -419,6 +445,7 @@ qx.Class.define("osparc.form.renderer.PropForm", {
for (let i = 0; i < items.length; i++) {
const item = items[i];
+ const portId = item.key;
const fieldOpts = this.__createLinkUnlinkStack(item);
if (fieldOpts) {
@@ -428,10 +455,10 @@ qx.Class.define("osparc.form.renderer.PropForm", {
});
}
- this.__createDropMechanism(item, item.key);
+ this.__createDropMechanism(item, portId);
// Notify focus and focus out
- const msgDataFn = (nodeId, portId) => this.__arePortsCompatible(nodeId, portId, this.getNode().getNodeId(), item.key);
+ const msgDataFn = (nodeId, pId) => this.__arePortsCompatible(nodeId, pId, this.getNode().getNodeId(), item.key);
item.addListener("focus", () => {
if (this.getNode()) {
@@ -447,6 +474,14 @@ qx.Class.define("osparc.form.renderer.PropForm", {
row++;
}
+ const evalRequired = () => {
+ for (const portId in this.__ctrlLinkMap) {
+ this.evalFieldRequired(portId);
+ }
+ }
+ this.getNode().addListener("changeInputsRequired", () => evalRequired());
+ evalRequired();
+
// add port button
const addPortButton = this.__addInputPortButton = new qx.ui.form.Button().set({
label: this.tr("Input"),
diff --git a/services/static-webserver/client/source/class/osparc/form/renderer/PropFormBase.js b/services/static-webserver/client/source/class/osparc/form/renderer/PropFormBase.js
index f6110e38204..bda6c498aed 100644
--- a/services/static-webserver/client/source/class/osparc/form/renderer/PropFormBase.js
+++ b/services/static-webserver/client/source/class/osparc/form/renderer/PropFormBase.js
@@ -125,7 +125,7 @@ qx.Class.define("osparc.form.renderer.PropFormBase", {
const label = this._createLabel(names[i], item);
label.set({
- rich: false, // override, required for showing the vut off ellipses
+ rich: false, // override, required for showing the cut off ellipses
toolTipText: names[i]
});
label.setBuddy(item);
@@ -191,6 +191,38 @@ qx.Class.define("osparc.form.renderer.PropFormBase", {
return filteredData;
},
+ evalFieldRequired: function(portId) {
+ const label = this._getLabelFieldChild(portId).child;
+ const inputsRequired = this.getNode().getInputsRequired();
+
+ // add star (*) to the label
+ const requiredSuffix = " *";
+ let newLabel = label.getValue();
+ newLabel = newLabel.replace(requiredSuffix, "");
+ if (inputsRequired.includes(portId)) {
+ newLabel += requiredSuffix;
+ }
+ label.setValue(newLabel);
+
+ // add "required" text to the label's tooltip
+ const toolTipSuffix = "
" + this.tr("Required input: without it, the service will not start/run.");
+ let newToolTip = label.getToolTipText();
+ newToolTip = newToolTip.replace(toolTipSuffix, "");
+ if (inputsRequired.includes(portId)) {
+ newToolTip += toolTipSuffix;
+ }
+ label.setToolTipText(newToolTip);
+
+ // add "required" text to the description
+ const infoButton = this._getInfoFieldChild(portId).child;
+ let newHintText = infoButton.getHintText();
+ newHintText = newHintText.replace(toolTipSuffix, "");
+ if (inputsRequired.includes(portId)) {
+ newHintText += toolTipSuffix;
+ }
+ infoButton.setHintText(newHintText);
+ },
+
getChangedXUnits: function() {
const xUnits = {};
const ctrls = this._form.getControls();
diff --git a/services/static-webserver/client/source/class/osparc/widget/NodeOptions.js b/services/static-webserver/client/source/class/osparc/widget/NodeOptions.js
index 2aafc7f2dd6..180de5bb2cb 100644
--- a/services/static-webserver/client/source/class/osparc/widget/NodeOptions.js
+++ b/services/static-webserver/client/source/class/osparc/widget/NodeOptions.js
@@ -113,6 +113,8 @@ qx.Class.define("osparc.widget.NodeOptions", {
const startStopButton = new osparc.node.StartStopButton();
startStopButton.setNode(node);
this._add(startStopButton);
+
+ startStopButton.getChildControl("stop-button").bind("visibility", instructions, "visibility");
}
sections.forEach(section => this._add(section));