From 82db9ba93b822a6ae254ba2173ecd33e1f31d339 Mon Sep 17 00:00:00 2001 From: odeimaiz Date: Wed, 16 Jul 2025 12:16:45 +0200 Subject: [PATCH 01/40] FUNCTIONS --- .../osparc/dashboard/ResourceBrowserFilter.js | 21 +++++++++++++++++++ .../dashboard/ResourceContainerManager.js | 3 +++ .../class/osparc/dashboard/StudyBrowser.js | 3 +++ .../osparc/dashboard/StudyBrowserHeader.js | 5 +++++ .../client/source/class/osparc/store/Store.js | 1 + 5 files changed, 33 insertions(+) diff --git a/services/static-webserver/client/source/class/osparc/dashboard/ResourceBrowserFilter.js b/services/static-webserver/client/source/class/osparc/dashboard/ResourceBrowserFilter.js index 0cb9a6a55cb4..a0bcb0f0e086 100644 --- a/services/static-webserver/client/source/class/osparc/dashboard/ResourceBrowserFilter.js +++ b/services/static-webserver/client/source/class/osparc/dashboard/ResourceBrowserFilter.js @@ -36,6 +36,7 @@ qx.Class.define("osparc.dashboard.ResourceBrowserFilter", { events: { "templatesContext": "qx.event.type.Event", "publicTemplatesContext": "qx.event.type.Event", + "functionsContext": "qx.event.type.Event", "trashContext": "qx.event.type.Event", "changeTab": "qx.event.type.Data", "trashStudyRequested": "qx.event.type.Data", @@ -50,6 +51,7 @@ qx.Class.define("osparc.dashboard.ResourceBrowserFilter", { __workspacesAndFoldersTree: null, __templatesButton: null, __publicProjectsButton: null, + __functionsButton: null, __trashButton: null, __sharedWithButtons: null, __tagButtons: null, @@ -102,6 +104,7 @@ qx.Class.define("osparc.dashboard.ResourceBrowserFilter", { this.__templatesButton.setValue(context === osparc.dashboard.StudyBrowser.CONTEXT.TEMPLATES); this.__publicProjectsButton.setValue(context === osparc.dashboard.StudyBrowser.CONTEXT.PUBLIC_TEMPLATES); + this.__functionsButton.setValue(context === osparc.dashboard.StudyBrowser.CONTEXT.FUNCTIONS); this.__trashButton.setValue(context === osparc.dashboard.StudyBrowser.CONTEXT.TRASH); }, @@ -164,6 +167,24 @@ qx.Class.define("osparc.dashboard.ResourceBrowserFilter", { return publicProjectsButton; }, + __createFunctions: function() { + const functionsButton = this.__functionsButton = new qx.ui.toolbar.RadioButton().set({ + value: false, + appearance: "filter-toggle-button", + label: this.tr("Functions"), + icon: "@MaterialIcons/functions/18", + paddingLeft: 10, // align it with the context + }); + osparc.utils.Utils.setIdToWidget(functionsButton, "functionsFilterItem"); + functionsButton.addListener("changeValue", e => { + const functionsEnabled = e.getData(); + if (functionsEnabled) { + this.fireEvent("functionsContext"); + } + }); + return functionsButton; + }, + /* TRASH BIN */ __createTrashBin: function() { const trashButton = this.__trashButton = new qx.ui.toolbar.RadioButton().set({ diff --git a/services/static-webserver/client/source/class/osparc/dashboard/ResourceContainerManager.js b/services/static-webserver/client/source/class/osparc/dashboard/ResourceContainerManager.js index ffa57c14175d..4695abb0cd16 100644 --- a/services/static-webserver/client/source/class/osparc/dashboard/ResourceContainerManager.js +++ b/services/static-webserver/client/source/class/osparc/dashboard/ResourceContainerManager.js @@ -161,6 +161,9 @@ qx.Class.define("osparc.dashboard.ResourceContainerManager", { case osparc.dashboard.StudyBrowser.CONTEXT.SEARCH_PUBLIC_TEMPLATES: text = this.tr("No Public Projects found"); break; + case osparc.dashboard.StudyBrowser.CONTEXT.FUNCTIONS: + text = this.tr("No Functions found"); + break; } break; } 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 50f497028fa0..79dce8f4c326 100644 --- a/services/static-webserver/client/source/class/osparc/dashboard/StudyBrowser.js +++ b/services/static-webserver/client/source/class/osparc/dashboard/StudyBrowser.js @@ -53,6 +53,7 @@ qx.Class.define("osparc.dashboard.StudyBrowser", { WORKSPACES: "workspaces", TEMPLATES: "templates", PUBLIC_TEMPLATES: "publicTemplates", + FUNCTIONS: "functions", TRASH: "trash", SEARCH_PROJECTS: "searchProjects", SEARCH_TEMPLATES: "searchTemplates", @@ -67,6 +68,7 @@ qx.Class.define("osparc.dashboard.StudyBrowser", { "workspaces", "templates", "publicTemplates", + "functions", "trash", "searchProjects", "searchTemplates", @@ -1141,6 +1143,7 @@ qx.Class.define("osparc.dashboard.StudyBrowser", { this._resourceFilter.addListener("templatesContext", () => this._changeContext(osparc.dashboard.StudyBrowser.CONTEXT.TEMPLATES)); this._resourceFilter.addListener("publicTemplatesContext", () => this._changeContext(osparc.dashboard.StudyBrowser.CONTEXT.PUBLIC_TEMPLATES)); + this._resourceFilter.addListener("functionsContext", () => this._changeContext(osparc.dashboard.StudyBrowser.CONTEXT.FUNCTIONS)); this._resourceFilter.addListener("trashContext", () => this._changeContext(osparc.dashboard.StudyBrowser.CONTEXT.TRASH)); this._searchBarFilter.addListener("filterChanged", e => { diff --git a/services/static-webserver/client/source/class/osparc/dashboard/StudyBrowserHeader.js b/services/static-webserver/client/source/class/osparc/dashboard/StudyBrowserHeader.js index 9eb45031dbbb..d91e62ab485e 100644 --- a/services/static-webserver/client/source/class/osparc/dashboard/StudyBrowserHeader.js +++ b/services/static-webserver/client/source/class/osparc/dashboard/StudyBrowserHeader.js @@ -307,6 +307,11 @@ qx.Class.define("osparc.dashboard.StudyBrowserHeader", { title.setValue(this.tr("Public Projects")); break; } + case osparc.dashboard.StudyBrowser.CONTEXT.FUNCTIONS: { + this.__setIcon("@MaterialIcons/functions/26"); + title.setValue(this.tr("Functions")); + break; + } case osparc.dashboard.StudyBrowser.CONTEXT.TRASH: { this.__setIcon("@FontAwesome5Solid/trash/24"); title.setValue(this.tr("Recently Deleted")); diff --git a/services/static-webserver/client/source/class/osparc/store/Store.js b/services/static-webserver/client/source/class/osparc/store/Store.js index a020653ed193..d0c326539a6d 100644 --- a/services/static-webserver/client/source/class/osparc/store/Store.js +++ b/services/static-webserver/client/source/class/osparc/store/Store.js @@ -81,6 +81,7 @@ qx.Class.define("osparc.store.Store", { "workspaces", "templates", "publicTemplates", + "functions", "trash", "searchProjects", "searchTemplates", From 40a76720655cc986c2b3d1e8549913855406d69e Mon Sep 17 00:00:00 2001 From: odeimaiz Date: Wed, 16 Jul 2025 12:41:39 +0200 Subject: [PATCH 02/40] StudyBrowser --- .../class/osparc/dashboard/StudyBrowser.js | 29 ++++++++++++++++++- 1 file changed, 28 insertions(+), 1 deletion(-) 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 79dce8f4c326..b3c4482bdf41 100644 --- a/services/static-webserver/client/source/class/osparc/dashboard/StudyBrowser.js +++ b/services/static-webserver/client/source/class/osparc/dashboard/StudyBrowser.js @@ -329,6 +329,12 @@ qx.Class.define("osparc.dashboard.StudyBrowser", { this.__addResourcesToList(filteredTemplates); break; } + case osparc.dashboard.StudyBrowser.CONTEXT.FUNCTIONS: { + const functions = resp["data"]; + functions.forEach(func => func["resourceType"] = "func"); + this.__addResourcesToList(functions); + break; + } } if (this._resourcesContainer.getFlatList()) { this._resourcesContainer.getFlatList().nextRequest = resp["_links"]["next"]; @@ -860,6 +866,8 @@ qx.Class.define("osparc.dashboard.StudyBrowser", { requestParams.templateType = osparc.data.model.StudyUI.TEMPLATE_TYPE; requestParams.accessRights = "public"; break; + case osparc.dashboard.StudyBrowser.CONTEXT.FUNCTIONS: + break; case osparc.dashboard.StudyBrowser.CONTEXT.SEARCH_PROJECTS: { requestParams.type = "user"; break; @@ -921,6 +929,9 @@ qx.Class.define("osparc.dashboard.StudyBrowser", { // The distinction is done in the frontend request = osparc.store.Templates.searchTemplatesPaginated(params, options); break; + case osparc.dashboard.StudyBrowser.CONTEXT.FUNCTIONS: + // OM: implement this + request = osparc.store.Functions.fetchFunctionsPaginated(params, options); } return request; }, @@ -1159,11 +1170,16 @@ qx.Class.define("osparc.dashboard.StudyBrowser", { case osparc.dashboard.StudyBrowser.CONTEXT.SEARCH_PUBLIC_TEMPLATES: searchContext = osparc.dashboard.StudyBrowser.CONTEXT.SEARCH_PUBLIC_TEMPLATES; break; + case osparc.dashboard.StudyBrowser.CONTEXT.FUNCTIONS: + searchContext = null; + break; default: searchContext = osparc.dashboard.StudyBrowser.CONTEXT.SEARCH_PROJECTS; break; } - this._changeContext(searchContext); + if (searchContext) { + this._changeContext(searchContext); + } } else { let backToContext = osparc.dashboard.StudyBrowser.CONTEXT.PROJECTS; switch (this.getCurrentContext()) { @@ -1209,6 +1225,7 @@ qx.Class.define("osparc.dashboard.StudyBrowser", { this._resourcesList = []; this._resourcesContainer.setResourcesToList(this._resourcesList); this._resourcesContainer.reloadCards("studies"); + this._searchBarFilter.setEnabled(true); switch (this.getCurrentContext()) { case osparc.dashboard.StudyBrowser.CONTEXT.PROJECTS: @@ -1257,6 +1274,16 @@ qx.Class.define("osparc.dashboard.StudyBrowser", { this.invalidateStudies(); this.__reloadStudies(); break; + case osparc.dashboard.StudyBrowser.CONTEXT.FUNCTIONS: + this._searchBarFilter.resetFilters(); + this._searchBarFilter.getChildControl("text-field").setPlaceholder("Search in Functions"); + this._searchBarFilter.setEnabled(false); + this._toolbar.exclude(); + this._loadingResourcesBtn.setFetching(false); + // OM: implement this + this.invalidateFunctions(); + this.__reloadStudies(); + break; case osparc.dashboard.StudyBrowser.CONTEXT.TRASH: this._searchBarFilter.resetFilters(); this._searchBarFilter.getChildControl("text-field").setPlaceholder("Search in My Projects"); From 0b7a2d1ee4cff32081442e147cf6df0f433995c4 Mon Sep 17 00:00:00 2001 From: odeimaiz Date: Wed, 16 Jul 2025 12:46:05 +0200 Subject: [PATCH 03/40] showFunctions --- .../class/osparc/dashboard/ResourceBrowserFilter.js | 3 +++ .../client/source/class/osparc/product/Utils.js | 12 ++++++++++++ 2 files changed, 15 insertions(+) diff --git a/services/static-webserver/client/source/class/osparc/dashboard/ResourceBrowserFilter.js b/services/static-webserver/client/source/class/osparc/dashboard/ResourceBrowserFilter.js index a0bcb0f0e086..99f05a368fb9 100644 --- a/services/static-webserver/client/source/class/osparc/dashboard/ResourceBrowserFilter.js +++ b/services/static-webserver/client/source/class/osparc/dashboard/ResourceBrowserFilter.js @@ -68,6 +68,9 @@ qx.Class.define("osparc.dashboard.ResourceBrowserFilter", { if (osparc.product.Utils.showPublicProjects()) { this._add(this.__createPublicProjects()); } + if (osparc.product.Utils.showFunctions()) { + this._add(this.__createFunctions()); + } this._add(this.__createTrashBin()); this._add(filtersSpacer); const scrollView = new qx.ui.container.Scroll(); diff --git a/services/static-webserver/client/source/class/osparc/product/Utils.js b/services/static-webserver/client/source/class/osparc/product/Utils.js index 42195453a311..75a9529fc310 100644 --- a/services/static-webserver/client/source/class/osparc/product/Utils.js +++ b/services/static-webserver/client/source/class/osparc/product/Utils.js @@ -310,6 +310,18 @@ qx.Class.define("osparc.product.Utils", { return true; }, + showFunctions: function() { + if (osparc.utils.DisabledPlugins.isFunctionsDisabled()) { + return false; + } + + return [ + "osparc", + "s4l", + "s4lacad", + ].includes(osparc.product.Utils.getProductName()); + }, + showQuality: function() { return this.isProduct("osparc"); }, From a5192546c58db7b2dd9158b51178086e87ce8704 Mon Sep 17 00:00:00 2001 From: odeimaiz Date: Wed, 16 Jul 2025 12:47:19 +0200 Subject: [PATCH 04/40] minor --- .../source/class/osparc/dashboard/ResourceBrowserFilter.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/services/static-webserver/client/source/class/osparc/dashboard/ResourceBrowserFilter.js b/services/static-webserver/client/source/class/osparc/dashboard/ResourceBrowserFilter.js index 99f05a368fb9..df60183ed3d6 100644 --- a/services/static-webserver/client/source/class/osparc/dashboard/ResourceBrowserFilter.js +++ b/services/static-webserver/client/source/class/osparc/dashboard/ResourceBrowserFilter.js @@ -175,7 +175,7 @@ qx.Class.define("osparc.dashboard.ResourceBrowserFilter", { value: false, appearance: "filter-toggle-button", label: this.tr("Functions"), - icon: "@MaterialIcons/functions/18", + icon: "@MaterialIcons/functions/20", paddingLeft: 10, // align it with the context }); osparc.utils.Utils.setIdToWidget(functionsButton, "functionsFilterItem"); From 6515bc4a16890252914cf3a1571891735c4b5a9c Mon Sep 17 00:00:00 2001 From: odeimaiz Date: Wed, 16 Jul 2025 12:58:09 +0200 Subject: [PATCH 05/40] minor --- .../client/source/class/osparc/store/Templates.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/services/static-webserver/client/source/class/osparc/store/Templates.js b/services/static-webserver/client/source/class/osparc/store/Templates.js index 62d90bd64008..ef03e8625dfc 100644 --- a/services/static-webserver/client/source/class/osparc/store/Templates.js +++ b/services/static-webserver/client/source/class/osparc/store/Templates.js @@ -60,8 +60,8 @@ qx.Class.define("osparc.store.Templates", { .catch(err => osparc.FlashMessenger.logError(err)); }, - fetchTemplate: function(templateId) { - return osparc.store.Study.getInstance().getOne(templateId) + fetchTemplate: function(studyId) { + return osparc.store.Study.getInstance().getOne(studyId) .catch(err => console.error(err)); }, From 7637521c816b74144628c6d2d5b91e621576d66f Mon Sep 17 00:00:00 2001 From: odeimaiz Date: Wed, 16 Jul 2025 12:58:27 +0200 Subject: [PATCH 06/40] minor --- .../client/source/class/osparc/dashboard/StudyBrowser.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 b3c4482bdf41..06bc1eaf9e36 100644 --- a/services/static-webserver/client/source/class/osparc/dashboard/StudyBrowser.js +++ b/services/static-webserver/client/source/class/osparc/dashboard/StudyBrowser.js @@ -331,7 +331,7 @@ qx.Class.define("osparc.dashboard.StudyBrowser", { } case osparc.dashboard.StudyBrowser.CONTEXT.FUNCTIONS: { const functions = resp["data"]; - functions.forEach(func => func["resourceType"] = "func"); + functions.forEach(func => func["resourceType"] = "function"); this.__addResourcesToList(functions); break; } From de43e871d780e54f98ec5cc3c576040f5a7d299c Mon Sep 17 00:00:00 2001 From: odeimaiz Date: Wed, 16 Jul 2025 13:04:42 +0200 Subject: [PATCH 07/40] checkFunctionPermissions --- .../client/source/class/osparc/data/Permissions.js | 5 +++++ .../client/source/class/osparc/product/Utils.js | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/services/static-webserver/client/source/class/osparc/data/Permissions.js b/services/static-webserver/client/source/class/osparc/data/Permissions.js index 8d0a957abc9c..22008182f348 100644 --- a/services/static-webserver/client/source/class/osparc/data/Permissions.js +++ b/services/static-webserver/client/source/class/osparc/data/Permissions.js @@ -310,6 +310,11 @@ qx.Class.define("osparc.data.Permissions", { return false; } + // This needs to be provided by the backend + if (action === "readFunctions") { + return true; + } + if ( this.__functionPermissions && action in this.__functionPermissions diff --git a/services/static-webserver/client/source/class/osparc/product/Utils.js b/services/static-webserver/client/source/class/osparc/product/Utils.js index 75a9529fc310..fc0fde7c8978 100644 --- a/services/static-webserver/client/source/class/osparc/product/Utils.js +++ b/services/static-webserver/client/source/class/osparc/product/Utils.js @@ -311,7 +311,7 @@ qx.Class.define("osparc.product.Utils", { }, showFunctions: function() { - if (osparc.utils.DisabledPlugins.isFunctionsDisabled()) { + if (!osparc.data.Permissions.getInstance().checkFunctionPermissions("readFunctions")) { return false; } From 923171e22afff8a1bea27137c2525931fdb5855b Mon Sep 17 00:00:00 2001 From: odeimaiz Date: Wed, 16 Jul 2025 13:09:37 +0200 Subject: [PATCH 08/40] Functions store --- .../source/class/osparc/store/Functions.js | 119 ++++++++++++++++++ .../class/osparc/study/CreateFunction.js | 69 +--------- 2 files changed, 120 insertions(+), 68 deletions(-) create mode 100644 services/static-webserver/client/source/class/osparc/store/Functions.js diff --git a/services/static-webserver/client/source/class/osparc/store/Functions.js b/services/static-webserver/client/source/class/osparc/store/Functions.js new file mode 100644 index 000000000000..69545dcffbe9 --- /dev/null +++ b/services/static-webserver/client/source/class/osparc/store/Functions.js @@ -0,0 +1,119 @@ +/* ************************************************************************ + + osparc - the simcore frontend + + https://osparc.io + + Copyright: + 2025 IT'IS Foundation, https://itis.swiss + + License: + MIT: https://opensource.org/licenses/MIT + + Authors: + * Odei Maiz (odeimaiz) + +************************************************************************ */ + +qx.Class.define("osparc.store.Functions", { + type: "static", + + statics: { + __functions: null, + __functionsPromiseCached: null, + + __createFunctionData: function(templateData, name, description, defaultInputs = {}, exposedInputs = {}, exposedOutputs = {}) { + const functionData = { + "projectId": templateData["uuid"], + "title": name, + "description": description, + "function_class": "PROJECT", + "inputSchema": { + "schema_class": "application/schema+json", + "schema_content": { + "type": "object", + "properties": {}, + "required": [] + } + }, + "outputSchema": { + "schema_class": "application/schema+json", + "schema_content": { + "type": "object", + "properties": {}, + "required": [] + } + }, + "defaultInputs": {}, + }; + + const parameters = osparc.study.Utils.extractFunctionableParameters(templateData["workbench"]); + parameters.forEach(parameter => { + const parameterKey = parameter["label"]; + if (exposedInputs[parameterKey]) { + const parameterMetadata = osparc.store.Services.getMetadata(parameter["key"], parameter["version"]); + if (parameterMetadata) { + const type = osparc.service.Utils.getParameterType(parameterMetadata); + functionData["inputSchema"]["schema_content"]["properties"][parameterKey] = { + "type": type, + }; + functionData["inputSchema"]["schema_content"]["required"].push(parameterKey); + } + } + if (parameterKey in defaultInputs) { + functionData["defaultInputs"][parameterKey] = defaultInputs[parameterKey]; + } + }); + + const probes = osparc.study.Utils.extractFunctionableProbes(templateData["workbench"]); + probes.forEach(probe => { + const probeLabel = probe["label"]; + if (exposedOutputs[probeLabel]) { + const probeMetadata = osparc.store.Services.getMetadata(probe["key"], probe["version"]); + if (probeMetadata) { + const type = osparc.service.Utils.getProbeType(probeMetadata); + functionData["outputSchema"]["schema_content"]["properties"][probeLabel] = { + "type": type, + }; + functionData["outputSchema"]["schema_content"]["required"].push(probeLabel); + } + } + }); + + return functionData; + }, + + registerFunction: function(templateData, name, description, defaultInputs, exposedInputs, exposedOutputs) { + const functionData = this.self().__createFunctionData(templateData, name, description, defaultInputs, exposedInputs, exposedOutputs); + const params = { + data: functionData, + }; + return osparc.data.Resources.fetch("functions", "create", params); + }, + + fetchFunctionsPaginated: function(params, options) { + return osparc.data.Resources.fetch("functions", "getPage", params, options) + .then(response => { + const functions = response["data"]; + functions.forEach(func => func["resourceType"] = "function"); + return response; + }) + .catch(err => osparc.FlashMessenger.logError(err)); + }, + + fetchFunction: function(functionId) { + return osparc.store.Study.getInstance().getOne(functionId) + .catch(err => console.error(err)); + }, + + getFunction: function(functionId) { + if (this.__functions) { + const func = this.__functions.find(t => t["functionId"] === functionId); + if (func) { + return new osparc.data.model.Function(func); + } + } + return null; + }, + } +}); diff --git a/services/static-webserver/client/source/class/osparc/study/CreateFunction.js b/services/static-webserver/client/source/class/osparc/study/CreateFunction.js index 0c409af43d29..adb9f3b476d7 100644 --- a/services/static-webserver/client/source/class/osparc/study/CreateFunction.js +++ b/services/static-webserver/client/source/class/osparc/study/CreateFunction.js @@ -32,69 +32,6 @@ qx.Class.define("osparc.study.CreateFunction", { this.__buildLayout(); }, - statics: { - createFunctionData: function(projectData, name, description, defaultInputs = {}, exposedInputs = {}, exposedOutputs = {}) { - const functionData = { - "projectId": projectData["uuid"], - "title": name, - "description": description, - "function_class": "PROJECT", - "inputSchema": { - "schema_class": "application/schema+json", - "schema_content": { - "type": "object", - "properties": {}, - "required": [] - } - }, - "outputSchema": { - "schema_class": "application/schema+json", - "schema_content": { - "type": "object", - "properties": {}, - "required": [] - } - }, - "defaultInputs": {}, - }; - - const parameters = osparc.study.Utils.extractFunctionableParameters(projectData["workbench"]); - parameters.forEach(parameter => { - const parameterKey = parameter["label"]; - if (exposedInputs[parameterKey]) { - const parameterMetadata = osparc.store.Services.getMetadata(parameter["key"], parameter["version"]); - if (parameterMetadata) { - const type = osparc.service.Utils.getParameterType(parameterMetadata); - functionData["inputSchema"]["schema_content"]["properties"][parameterKey] = { - "type": type, - }; - functionData["inputSchema"]["schema_content"]["required"].push(parameterKey); - } - } - if (parameterKey in defaultInputs) { - functionData["defaultInputs"][parameterKey] = defaultInputs[parameterKey]; - } - }); - - const probes = osparc.study.Utils.extractFunctionableProbes(projectData["workbench"]); - probes.forEach(probe => { - const probeLabel = probe["label"]; - if (exposedOutputs[probeLabel]) { - const probeMetadata = osparc.store.Services.getMetadata(probe["key"], probe["version"]); - if (probeMetadata) { - const type = osparc.service.Utils.getProbeType(probeMetadata); - functionData["outputSchema"]["schema_content"]["properties"][probeLabel] = { - "type": type, - }; - functionData["outputSchema"]["schema_content"]["required"].push(probeLabel); - } - } - }); - - return functionData; - } - }, - members: { __studyData: null, __form: null, @@ -335,11 +272,7 @@ qx.Class.define("osparc.study.CreateFunction", { const nameField = this.__form.getItem("name"); const descriptionField = this.__form.getItem("description"); - const functionData = this.self().createFunctionData(templateData, nameField.getValue(), descriptionField.getValue(), defaultInputs, exposedInputs, exposedOutputs); - const params = { - data: functionData, - }; - osparc.data.Resources.fetch("functions", "create", params) + osparc.store.Functions.registerFunction(templateData, nameField.getValue(), descriptionField.getValue(), defaultInputs, exposedInputs, exposedOutputs) .then(() => osparc.FlashMessenger.logAs(this.tr("Function created"), "INFO")) .catch(err => osparc.FlashMessenger.logError(err)) .finally(() => this.__createFunctionBtn.setFetching(false)); From a2ac3f7b8308bfab7aec548d25355c280a718265 Mon Sep 17 00:00:00 2001 From: odeimaiz Date: Wed, 16 Jul 2025 13:11:36 +0200 Subject: [PATCH 09/40] invalidateFunctions --- .../client/source/class/osparc/dashboard/StudyBrowser.js | 9 ++++++++- .../client/source/class/osparc/store/Functions.js | 7 +++++++ 2 files changed, 15 insertions(+), 1 deletion(-) 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 06bc1eaf9e36..22d3ab487ef0 100644 --- a/services/static-webserver/client/source/class/osparc/dashboard/StudyBrowser.js +++ b/services/static-webserver/client/source/class/osparc/dashboard/StudyBrowser.js @@ -944,6 +944,14 @@ qx.Class.define("osparc.dashboard.StudyBrowser", { } }, + invalidateFunctions: function() { + osparc.store.Functions.invalidateFunctions(); + this.__resetStudiesList(); + if (this._resourcesContainer.getFlatList()) { + this._resourcesContainer.getFlatList().nextRequest = null; + } + }, + __addNewPlusButton: function() { const newPlusButton = new osparc.dashboard.NewPlusButton(); this._leftFilters.add(newPlusButton); @@ -1280,7 +1288,6 @@ qx.Class.define("osparc.dashboard.StudyBrowser", { this._searchBarFilter.setEnabled(false); this._toolbar.exclude(); this._loadingResourcesBtn.setFetching(false); - // OM: implement this this.invalidateFunctions(); this.__reloadStudies(); break; diff --git a/services/static-webserver/client/source/class/osparc/store/Functions.js b/services/static-webserver/client/source/class/osparc/store/Functions.js index 69545dcffbe9..77897b63803f 100644 --- a/services/static-webserver/client/source/class/osparc/store/Functions.js +++ b/services/static-webserver/client/source/class/osparc/store/Functions.js @@ -115,5 +115,12 @@ qx.Class.define("osparc.store.Functions", { } return null; }, + + invalidateFunctions: function() { + this.__functions = null; + if (this.__functionsPromiseCached) { + this.__functionsPromiseCached = null; + } + }, } }); From 6aba3cd11cb58002ef57d7aa64a0a50063deba1b Mon Sep 17 00:00:00 2001 From: odeimaiz Date: Wed, 16 Jul 2025 14:26:23 +0200 Subject: [PATCH 10/40] __dummyResponse --- .../source/class/osparc/store/Functions.js | 33 +++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/services/static-webserver/client/source/class/osparc/store/Functions.js b/services/static-webserver/client/source/class/osparc/store/Functions.js index 77897b63803f..1fa64ee40014 100644 --- a/services/static-webserver/client/source/class/osparc/store/Functions.js +++ b/services/static-webserver/client/source/class/osparc/store/Functions.js @@ -92,6 +92,13 @@ qx.Class.define("osparc.store.Functions", { }, fetchFunctionsPaginated: function(params, options) { + const isBackendReady = false; + if (!isBackendReady) { + return new Promise(resolve => { + const response = this.__dummyResponse(); + resolve(response); + }); + } return osparc.data.Resources.fetch("functions", "getPage", params, options) .then(response => { const functions = response["data"]; @@ -122,5 +129,31 @@ qx.Class.define("osparc.store.Functions", { this.__functionsPromiseCached = null; } }, + + __dummyResponse: function() { + return { + "_meta": { + "limit": 10, + "total": 1, + "offset": 0, + "count": 1 + }, + "data": [{ + "uuid": "0fab79c3-14b8-4625-a455-6dcbf74eb4f2", + "functionClass": "PROJECT", + "title": "Potential Function II", + "description": "Function description", + "inputSchema": {"schema_class": "application/schema+json", "schema_content": {"type": "object", "required": ["X"], "properties": {"X": {"type": "number"}}}}, + "outputSchema": {"schema_class": "application/schema+json", "schema_content": {"type": "object", "required": ["Out 1", "Out_2"], "properties": {"Out 1": {"type": "number"}, "Out_2": {"type": "number"}}}}, + "defaultInputs": {"X": 2, "Y": 1}, + "creationDate": "2025-05-16T12:22:31.063Z", + "lastChangeDate": "2025-05-16T12:22:33.804Z", + "accessRights": {"3": {"read": true, "write": true, "delete": true}, "5": {"read": true, "write": false, "delete": false}}, + "thumbnail": "https://img.freepik.com/premium-vector/image-icon-design-vector-template_1309674-940.jpg", + "workbench": {"50a50309-1dfc-5ad5-b2d9-c11697641f0b": {"key": "simcore/services/comp/itis/sleeper", "version": "2.2.0", "label": "sleeper", "inputs": {"input_2": 2, "input_3": false, "input_4": 0, "input_5": 0}, "inputsRequired": [], "inputNodes": ["2e348481-5042-5148-9196-590574747297", "69873032-770a-536b-adb6-0e6ea01720a4"]}, "2e348481-5042-5148-9196-590574747297": {"key": "simcore/services/frontend/parameter/number", "version": "1.0.0", "label": "X", "inputs": {}, "inputsRequired": [], "inputNodes": [], "outputs": {"out_1": 1}, "runHash": null}, "70e1de1a-a8b0-59e3-b19e-ea20f78765ce": {"key": "simcore/services/frontend/iterator-consumer/probe/number", "version": "1.0.0", "label": "Out 1", "inputs": {"in_1": 0}, "inputsRequired": [], "inputNodes": ["50a50309-1dfc-5ad5-b2d9-c11697641f0b"]}, "69873032-770a-536b-adb6-0e6ea01720a4": {"key": "simcore/services/frontend/parameter/number", "version": "1.0.0", "label": "Y", "inputs": {}, "inputsRequired": [], "inputNodes": [], "outputs": {"out_1": 1}, "runHash": null}, "24f856c3-408c-5ab4-ad01-e99630a355fe": {"key": "simcore/services/frontend/iterator-consumer/probe/number", "version": "1.0.0", "label": "Out_2", "inputs": {"in_1": 0}, "inputsRequired": [], "inputNodes": ["50a50309-1dfc-5ad5-b2d9-c11697641f0b"]}}, + "ui": {"24f856c3-408c-5ab4-ad01-e99630a355fe": {"position": {"x": 540, "y": 240}}, "2e348481-5042-5148-9196-590574747297": {"position": {"x": 120, "y": 140}}, "50a50309-1dfc-5ad5-b2d9-c11697641f0b": {"position": {"x": 300, "y": 180}}, "69873032-770a-536b-adb6-0e6ea01720a4": {"position": {"x": 120, "y": 240}}, "70e1de1a-a8b0-59e3-b19e-ea20f78765ce": {"position": {"x": 540, "y": 140}}}, + }] + }; + }, } }); From 73c66925bab95831dd6c4637a0a6e93334e28beb Mon Sep 17 00:00:00 2001 From: odeimaiz Date: Wed, 16 Jul 2025 14:41:03 +0200 Subject: [PATCH 11/40] not needed --- .../client/source/class/osparc/store/Functions.js | 15 --------------- 1 file changed, 15 deletions(-) diff --git a/services/static-webserver/client/source/class/osparc/store/Functions.js b/services/static-webserver/client/source/class/osparc/store/Functions.js index 1fa64ee40014..5f1cde7d7e3b 100644 --- a/services/static-webserver/client/source/class/osparc/store/Functions.js +++ b/services/static-webserver/client/source/class/osparc/store/Functions.js @@ -108,21 +108,6 @@ qx.Class.define("osparc.store.Functions", { .catch(err => osparc.FlashMessenger.logError(err)); }, - fetchFunction: function(functionId) { - return osparc.store.Study.getInstance().getOne(functionId) - .catch(err => console.error(err)); - }, - - getFunction: function(functionId) { - if (this.__functions) { - const func = this.__functions.find(t => t["functionId"] === functionId); - if (func) { - return new osparc.data.model.Function(func); - } - } - return null; - }, - invalidateFunctions: function() { this.__functions = null; if (this.__functionsPromiseCached) { From 6dfe4f4d33bcad0eff97a24eb843f309b69da02d Mon Sep 17 00:00:00 2001 From: odeimaiz Date: Wed, 16 Jul 2025 14:58:53 +0200 Subject: [PATCH 12/40] showing card --- .../source/class/osparc/dashboard/CardBase.js | 49 ++++++++++++------- .../class/osparc/dashboard/GridButtonItem.js | 1 + .../source/class/osparc/store/Functions.js | 8 ++- 3 files changed, 37 insertions(+), 21 deletions(-) diff --git a/services/static-webserver/client/source/class/osparc/dashboard/CardBase.js b/services/static-webserver/client/source/class/osparc/dashboard/CardBase.js index 779b105d8e74..cbcbd2f8b63b 100644 --- a/services/static-webserver/client/source/class/osparc/dashboard/CardBase.js +++ b/services/static-webserver/client/source/class/osparc/dashboard/CardBase.js @@ -326,6 +326,7 @@ qx.Class.define("osparc.dashboard.CardBase", { check: [ "study", "template", + "function", "tutorial", "hypertool", "service", @@ -527,6 +528,11 @@ qx.Class.define("osparc.dashboard.CardBase", { owner = resourceData.prjOwner ? resourceData.prjOwner : ""; workbench = resourceData.workbench ? resourceData.workbench : {}; break; + case "function": + uuid = resourceData.uuid ? resourceData.uuid : null; + owner = ""; + workbench = resourceData.workbench ? resourceData.workbench : {}; + break; case "service": uuid = resourceData.key ? resourceData.key : null; owner = resourceData.owner ? resourceData.owner : resourceData.contact; @@ -555,26 +561,31 @@ qx.Class.define("osparc.dashboard.CardBase", { workbench }); - if ([ - "study", - "template", - "tutorial", - "hypertool" - ].includes(resourceData["resourceType"])) { - osparc.store.Services.getStudyServices(resourceData.uuid) - .then(resp => { - const services = resp["services"]; - resourceData["services"] = services; - this.setServices(services); - }) - .catch(err => { - resourceData["services"] = null; - this.setServices(null); - console.error(err); - }); + switch (resourceData["resourceType"]) { + case "study": + case "template": + case "tutorial": + case "hypertool": { + osparc.store.Services.getStudyServices(resourceData.uuid) + .then(resp => { + const services = resp["services"]; + resourceData["services"] = services; + this.setServices(services); + }) + .catch(err => { + resourceData["services"] = null; + this.setServices(null); + console.error(err); + }); + + osparc.study.Utils.guessIcon(resourceData) + .then(iconSource => this.setIcon(iconSource)); - osparc.study.Utils.guessIcon(resourceData) - .then(iconSource => this.setIcon(iconSource)); + break; + } + case "function": + this.setIcon(osparc.data.model.StudyUI.PIPELINE_ICON); + break; } }, diff --git a/services/static-webserver/client/source/class/osparc/dashboard/GridButtonItem.js b/services/static-webserver/client/source/class/osparc/dashboard/GridButtonItem.js index 3cce2f18165d..7675d48e19ac 100644 --- a/services/static-webserver/client/source/class/osparc/dashboard/GridButtonItem.js +++ b/services/static-webserver/client/source/class/osparc/dashboard/GridButtonItem.js @@ -190,6 +190,7 @@ qx.Class.define("osparc.dashboard.GridButtonItem", { "template", "tutorial", "hypertool", + "function", ].includes(this.getResourceType())) { const dateBy = this.getChildControl("date-by"); dateBy.set({ diff --git a/services/static-webserver/client/source/class/osparc/store/Functions.js b/services/static-webserver/client/source/class/osparc/store/Functions.js index 5f1cde7d7e3b..076d08e79d34 100644 --- a/services/static-webserver/client/source/class/osparc/store/Functions.js +++ b/services/static-webserver/client/source/class/osparc/store/Functions.js @@ -96,6 +96,7 @@ qx.Class.define("osparc.store.Functions", { if (!isBackendReady) { return new Promise(resolve => { const response = this.__dummyResponse(); + response["params"] = params; resolve(response); }); } @@ -126,7 +127,7 @@ qx.Class.define("osparc.store.Functions", { "data": [{ "uuid": "0fab79c3-14b8-4625-a455-6dcbf74eb4f2", "functionClass": "PROJECT", - "title": "Potential Function II", + "name": "Potential Function II", "description": "Function description", "inputSchema": {"schema_class": "application/schema+json", "schema_content": {"type": "object", "required": ["X"], "properties": {"X": {"type": "number"}}}}, "outputSchema": {"schema_class": "application/schema+json", "schema_content": {"type": "object", "required": ["Out 1", "Out_2"], "properties": {"Out 1": {"type": "number"}, "Out_2": {"type": "number"}}}}, @@ -137,7 +138,10 @@ qx.Class.define("osparc.store.Functions", { "thumbnail": "https://img.freepik.com/premium-vector/image-icon-design-vector-template_1309674-940.jpg", "workbench": {"50a50309-1dfc-5ad5-b2d9-c11697641f0b": {"key": "simcore/services/comp/itis/sleeper", "version": "2.2.0", "label": "sleeper", "inputs": {"input_2": 2, "input_3": false, "input_4": 0, "input_5": 0}, "inputsRequired": [], "inputNodes": ["2e348481-5042-5148-9196-590574747297", "69873032-770a-536b-adb6-0e6ea01720a4"]}, "2e348481-5042-5148-9196-590574747297": {"key": "simcore/services/frontend/parameter/number", "version": "1.0.0", "label": "X", "inputs": {}, "inputsRequired": [], "inputNodes": [], "outputs": {"out_1": 1}, "runHash": null}, "70e1de1a-a8b0-59e3-b19e-ea20f78765ce": {"key": "simcore/services/frontend/iterator-consumer/probe/number", "version": "1.0.0", "label": "Out 1", "inputs": {"in_1": 0}, "inputsRequired": [], "inputNodes": ["50a50309-1dfc-5ad5-b2d9-c11697641f0b"]}, "69873032-770a-536b-adb6-0e6ea01720a4": {"key": "simcore/services/frontend/parameter/number", "version": "1.0.0", "label": "Y", "inputs": {}, "inputsRequired": [], "inputNodes": [], "outputs": {"out_1": 1}, "runHash": null}, "24f856c3-408c-5ab4-ad01-e99630a355fe": {"key": "simcore/services/frontend/iterator-consumer/probe/number", "version": "1.0.0", "label": "Out_2", "inputs": {"in_1": 0}, "inputsRequired": [], "inputNodes": ["50a50309-1dfc-5ad5-b2d9-c11697641f0b"]}}, "ui": {"24f856c3-408c-5ab4-ad01-e99630a355fe": {"position": {"x": 540, "y": 240}}, "2e348481-5042-5148-9196-590574747297": {"position": {"x": 120, "y": 140}}, "50a50309-1dfc-5ad5-b2d9-c11697641f0b": {"position": {"x": 300, "y": 180}}, "69873032-770a-536b-adb6-0e6ea01720a4": {"position": {"x": 120, "y": 240}}, "70e1de1a-a8b0-59e3-b19e-ea20f78765ce": {"position": {"x": 540, "y": 140}}}, - }] + }], + "_links": { + "next": null, + }, }; }, } From 2de533d566d78440d3fe29f91ce4e409da9d1745 Mon Sep 17 00:00:00 2001 From: odeimaiz Date: Wed, 16 Jul 2025 15:07:59 +0200 Subject: [PATCH 13/40] no menu button --- .../source/class/osparc/dashboard/StudyBrowser.js | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 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 22d3ab487ef0..38e55c2e533a 100644 --- a/services/static-webserver/client/source/class/osparc/dashboard/StudyBrowser.js +++ b/services/static-webserver/client/source/class/osparc/dashboard/StudyBrowser.js @@ -1658,11 +1658,16 @@ qx.Class.define("osparc.dashboard.StudyBrowser", { _populateCardMenu: function(card) { const studyData = card.getResourceData(); - if (studyData["resourceType"] === "template") { - // The Study Browser can also list templates - this._populateTemplateCardMenu(card); - } else { - this.__populateStudyCardMenu(card); + switch (studyData["resourceType"]) { + case "study": + this.__populateStudyCardMenu(card); + break; + case "template": + this._populateTemplateCardMenu(card); + break; + case "function": + card.getChildControl("menu-selection-stack").exclude(); + break; } }, From 01b5096ce047e124d7f0a2c4ee509694529118cd Mon Sep 17 00:00:00 2001 From: odeimaiz Date: Wed, 16 Jul 2025 15:15:08 +0200 Subject: [PATCH 14/40] fetchFunction --- .../class/osparc/dashboard/ResourceDetails.js | 2 +- .../source/class/osparc/store/Functions.js | 21 +++++++++++++++++++ 2 files changed, 22 insertions(+), 1 deletion(-) diff --git a/services/static-webserver/client/source/class/osparc/dashboard/ResourceDetails.js b/services/static-webserver/client/source/class/osparc/dashboard/ResourceDetails.js index 503182100313..1994d885b3a7 100644 --- a/services/static-webserver/client/source/class/osparc/dashboard/ResourceDetails.js +++ b/services/static-webserver/client/source/class/osparc/dashboard/ResourceDetails.js @@ -34,7 +34,7 @@ qx.Class.define("osparc.dashboard.ResourceDetails", { break; } case "function": { - latestPromise = osparc.store.Templates.fetchTemplate(resourceData["uuid"]); + latestPromise = osparc.store.Functions.fetchFunction(resourceData["uuid"]); break; } case "service": { diff --git a/services/static-webserver/client/source/class/osparc/store/Functions.js b/services/static-webserver/client/source/class/osparc/store/Functions.js index 076d08e79d34..f7c109a6555a 100644 --- a/services/static-webserver/client/source/class/osparc/store/Functions.js +++ b/services/static-webserver/client/source/class/osparc/store/Functions.js @@ -109,6 +109,27 @@ qx.Class.define("osparc.store.Functions", { .catch(err => osparc.FlashMessenger.logError(err)); }, + fetchFunction: function(functionId) { + const isBackendReady = false; + if (!isBackendReady) { + return new Promise(resolve => { + const response = this.__dummyResponse(); + resolve(response["data"][0]); + }); + } + const params = { + url: { + "functionId": functionId + } + }; + return osparc.data.Resources.fetch("functions", "getOne", params, options) + .then(func => { + func["resourceType"] = "function"; + return func; + }) + .catch(err => osparc.FlashMessenger.logError(err)); + }, + invalidateFunctions: function() { this.__functions = null; if (this.__functionsPromiseCached) { From 0396cd316531cf3bc69503bf5d17085f4862b2ca Mon Sep 17 00:00:00 2001 From: odeimaiz Date: Wed, 16 Jul 2025 15:40:50 +0200 Subject: [PATCH 15/40] Function model --- .../class/osparc/dashboard/ResourceDetails.js | 9 +- .../class/osparc/data/model/Function.js | 138 ++++++++++++++++++ 2 files changed, 146 insertions(+), 1 deletion(-) create mode 100644 services/static-webserver/client/source/class/osparc/data/model/Function.js diff --git a/services/static-webserver/client/source/class/osparc/dashboard/ResourceDetails.js b/services/static-webserver/client/source/class/osparc/dashboard/ResourceDetails.js index 1994d885b3a7..695afcff6561 100644 --- a/services/static-webserver/client/source/class/osparc/dashboard/ResourceDetails.js +++ b/services/static-webserver/client/source/class/osparc/dashboard/ResourceDetails.js @@ -57,7 +57,6 @@ qx.Class.define("osparc.dashboard.ResourceDetails", { case "template": case "tutorial": case "hypertool": - case "function": // when getting the latest study data, the debt information was lost if (osparc.study.Utils.isInDebt(this.__resourceData)) { const studyStore = osparc.store.Study.getInstance(); @@ -71,6 +70,12 @@ qx.Class.define("osparc.dashboard.ResourceDetails", { this.__addPages(); }) break; + case "function": { + this.__resourceModel = new osparc.data.model.Function(latestResourceData); + this.__resourceModel["resourceType"] = resourceData["resourceType"]; + this.__addPages(); + break; + } case "service": { this.__resourceModel = new osparc.data.model.Service(latestResourceData); this.__resourceModel["resourceType"] = resourceData["resourceType"]; @@ -372,6 +377,8 @@ qx.Class.define("osparc.dashboard.ResourceDetails", { if (this.__resourceData["resourceType"] === "function") { // for now, we only want the preview page + // OM: careful here, the one coming from the MMUX services is marked as "function" + this.__addInfoPage(); this.__addPreviewPage(); this.fireEvent("pagesAdded"); return; diff --git a/services/static-webserver/client/source/class/osparc/data/model/Function.js b/services/static-webserver/client/source/class/osparc/data/model/Function.js new file mode 100644 index 000000000000..31b4ebece065 --- /dev/null +++ b/services/static-webserver/client/source/class/osparc/data/model/Function.js @@ -0,0 +1,138 @@ +/* ************************************************************************ + + osparc - the simcore frontend + + https://osparc.io + + Copyright: + 2025 IT'IS Foundation, https://itis.swiss + + License: + MIT: https://opensource.org/licenses/MIT + + Authors: + * Odei Maiz (odeimaiz) + +************************************************************************ */ + +/** + * Class that stores Function data. + */ + +qx.Class.define("osparc.data.model.Function", { + extend: qx.core.Object, + + /** + * @param functionData {Object} Object containing the serialized Function Data + */ + construct: function(functionData) { + this.base(arguments); + + this.set({ + uuid: functionData.uuid, + functionType: functionData.functionClass, + name: functionData.name, + description: functionData.description, + inputSchema: functionData.inputSchema || this.getInputSchema(), + outputSchema: functionData.outputSchema || this.getOutputSchema(), + defaultInputs: functionData.defaultInputs || this.getDefaultInputs(), + accessRights: functionData.accessRights || this.getAccessRights(), + creationDate: functionData.creationDate ? new Date(functionData.creationDate) : this.getCreationDate(), + lastChangeDate: functionData.lastChangeDate ? new Date(functionData.lastChangeDate) : this.getLastChangeDate(), + thumbnail: functionData.thumbnail || this.getThumbnail(), + }); + + const wbData = functionData.workbench || this.getWorkbench(); + const workbench = new osparc.data.model.Workbench(wbData, functionData.ui); + this.setWorkbench(workbench); + workbench.setFunction(this); + + this.getWorkbench().buildWorkbench(); + }, + + properties: { + uuid: { + check: "String", + nullable: false, + event: "changeUuid", + init: "" + }, + + functionType: { + check: ["PROJECT"], + nullable: false, + event: "changeFunctionType", + init: null + }, + + name: { + check: "String", + nullable: false, + event: "changeName", + init: "New Study" + }, + + description: { + check: "String", + nullable: true, + event: "changeDescription", + init: null + }, + + inputSchema: { + check: "Object", + nullable: false, + event: "changeInputSchema", + init: {} + }, + + outputSchema: { + check: "Object", + nullable: false, + event: "changeOutputSchema", + init: {} + }, + + defaultInputs: { + check: "Object", + nullable: false, + event: "changeDefaultInputs", + init: {} + }, + + accessRights: { + check: "Object", + nullable: false, + event: "changeAccessRights", + init: {} + }, + + creationDate: { + check: "Date", + nullable: false, + event: "changeCreationDate", + init: new Date() + }, + + lastChangeDate: { + check: "Date", + nullable: false, + event: "changeLastChangeDate", + init: new Date() + }, + + thumbnail: { + check: "String", + nullable: true, + event: "changeThumbnail", + init: null + }, + + workbench: { + check: "osparc.data.model.Workbench", + nullable: false, + event: "changeWorkbench", + init: {} + }, + }, +}); From c5af08fbedf2a63667db8960327bb489106375de Mon Sep 17 00:00:00 2001 From: odeimaiz Date: Wed, 16 Jul 2025 15:41:29 +0200 Subject: [PATCH 16/40] minor --- .../source/class/osparc/study/StudyPreview.js | 48 +++++++++---------- 1 file changed, 24 insertions(+), 24 deletions(-) diff --git a/services/static-webserver/client/source/class/osparc/study/StudyPreview.js b/services/static-webserver/client/source/class/osparc/study/StudyPreview.js index fe2a967b2b75..74161b3bc155 100644 --- a/services/static-webserver/client/source/class/osparc/study/StudyPreview.js +++ b/services/static-webserver/client/source/class/osparc/study/StudyPreview.js @@ -26,38 +26,38 @@ qx.Class.define("osparc.study.StudyPreview", { this._setLayout(new qx.ui.layout.VBox(5)); - this.__study = study; - - this.__buildPreview(); + if (study instanceof osparc.data.model.Study) { + const uiMode = study.getUi().getMode(); + if (["workbench", "pipeline"].includes(uiMode)) { + this.__buildPreview(study); + } + } else if (study instanceof osparc.data.model.Function) { + this.__buildPreview(study); + } }, members: { - __study: null, - - __buildPreview: function() { - const study = this.__study; - + __buildPreview: function(study) { const workbenchReady = () => { - if (!study.isPipelineEmpty()) { - const workbenchUIPreview = new osparc.workbench.WorkbenchUIPreview(); + const workbenchUIPreview = new osparc.workbench.WorkbenchUIPreview(); + if (study instanceof osparc.data.model.Study) { workbenchUIPreview.setStudy(study); - workbenchUIPreview.loadModel(study.getWorkbench()); - workbenchUIPreview.setMaxHeight(550); - this._add(workbenchUIPreview); + } else if (study instanceof osparc.data.model.Function) { + workbenchUIPreview.setFunction(study); } + workbenchUIPreview.loadModel(study.getWorkbench()); + workbenchUIPreview.setMaxHeight(550); + this._add(workbenchUIPreview); }; - const uiMode = study.getUi().getMode(); - if (["workbench", "pipeline"].includes(uiMode)) { - if (study.getWorkbench().isDeserialized()) { - workbenchReady(); - } else { - study.getWorkbench().addListenerOnce("changeDeserialized", e => { - if (e.getData()) { - workbenchReady(); - } - }, this); - } + if (study.getWorkbench().isDeserialized()) { + workbenchReady(); + } else { + study.getWorkbench().addListenerOnce("changeDeserialized", e => { + if (e.getData()) { + workbenchReady(); + } + }, this); } } } From 2535a3baa418a3d550e77e1b46a51681ee62c152 Mon Sep 17 00:00:00 2001 From: odeimaiz Date: Wed, 16 Jul 2025 15:54:29 +0200 Subject: [PATCH 17/40] functionedTemplate --- .../source/class/osparc/dashboard/ResourceDetails.js | 10 ++++------ .../source/class/osparc/widget/PersistentIframe.js | 2 +- 2 files changed, 5 insertions(+), 7 deletions(-) diff --git a/services/static-webserver/client/source/class/osparc/dashboard/ResourceDetails.js b/services/static-webserver/client/source/class/osparc/dashboard/ResourceDetails.js index 695afcff6561..976028247380 100644 --- a/services/static-webserver/client/source/class/osparc/dashboard/ResourceDetails.js +++ b/services/static-webserver/client/source/class/osparc/dashboard/ResourceDetails.js @@ -29,18 +29,16 @@ qx.Class.define("osparc.dashboard.ResourceDetails", { case "study": case "template": case "tutorial": - case "hypertool": { + case "hypertool": + case "functionedTemplate": latestPromise = osparc.store.Study.getInstance().getOne(resourceData["uuid"]); break; - } - case "function": { + case "function": latestPromise = osparc.store.Functions.fetchFunction(resourceData["uuid"]); break; - } - case "service": { + case "service": latestPromise = osparc.store.Services.getService(resourceData["key"], resourceData["version"]); break; - } } latestPromise diff --git a/services/static-webserver/client/source/class/osparc/widget/PersistentIframe.js b/services/static-webserver/client/source/class/osparc/widget/PersistentIframe.js index 81f4d9bb7ec7..e657bca19f51 100644 --- a/services/static-webserver/client/source/class/osparc/widget/PersistentIframe.js +++ b/services/static-webserver/client/source/class/osparc/widget/PersistentIframe.js @@ -336,7 +336,7 @@ qx.Class.define("osparc.widget.PersistentIframe", { const templateId = data["message"]["functionId"]; const functionData = { "uuid": templateId, - "resourceType": "function", + "resourceType": "functionedTemplate", }; const { resourceDetails, From 101ea8e22b504bc1a153ecb0f7cacf79922b5e95 Mon Sep 17 00:00:00 2001 From: odeimaiz Date: Wed, 16 Jul 2025 15:57:47 +0200 Subject: [PATCH 18/40] set function --- .../client/source/class/osparc/data/model/Workbench.js | 7 +++++++ 1 file changed, 7 insertions(+) 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 3e0d3eea769a..a396677e4836 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 @@ -68,6 +68,13 @@ qx.Class.define("osparc.data.model.Workbench", { event: "changeStudy" }, + function: { + check: "osparc.data.model.Function", + init: null, + nullable: true, + event: "changeFunction" + }, + deserialized: { check: "Boolean", init: false, From 938ebfbc33b969067b3a7977773e04118f605ad4 Mon Sep 17 00:00:00 2001 From: odeimaiz Date: Wed, 16 Jul 2025 15:59:28 +0200 Subject: [PATCH 19/40] minor --- .../source/class/osparc/dashboard/ResourceDetails.js | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/services/static-webserver/client/source/class/osparc/dashboard/ResourceDetails.js b/services/static-webserver/client/source/class/osparc/dashboard/ResourceDetails.js index 976028247380..ecc9cf5eeb91 100644 --- a/services/static-webserver/client/source/class/osparc/dashboard/ResourceDetails.js +++ b/services/static-webserver/client/source/class/osparc/dashboard/ResourceDetails.js @@ -29,16 +29,20 @@ qx.Class.define("osparc.dashboard.ResourceDetails", { case "study": case "template": case "tutorial": - case "hypertool": - case "functionedTemplate": + case "hypertool": { latestPromise = osparc.store.Study.getInstance().getOne(resourceData["uuid"]); break; + } + case "functionedTemplate": + latestPromise = osparc.store.Templates.fetchTemplate(resourceData["uuid"]); + break; case "function": latestPromise = osparc.store.Functions.fetchFunction(resourceData["uuid"]); break; - case "service": + case "service": { latestPromise = osparc.store.Services.getService(resourceData["key"], resourceData["version"]); break; + } } latestPromise From 6daa51e10580175581a16a1a0fe135f3c220d7f5 Mon Sep 17 00:00:00 2001 From: odeimaiz Date: Wed, 16 Jul 2025 15:59:55 +0200 Subject: [PATCH 20/40] minor --- .../client/source/class/osparc/dashboard/ResourceDetails.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/services/static-webserver/client/source/class/osparc/dashboard/ResourceDetails.js b/services/static-webserver/client/source/class/osparc/dashboard/ResourceDetails.js index ecc9cf5eeb91..b7ac2f7af533 100644 --- a/services/static-webserver/client/source/class/osparc/dashboard/ResourceDetails.js +++ b/services/static-webserver/client/source/class/osparc/dashboard/ResourceDetails.js @@ -33,9 +33,10 @@ qx.Class.define("osparc.dashboard.ResourceDetails", { latestPromise = osparc.store.Study.getInstance().getOne(resourceData["uuid"]); break; } - case "functionedTemplate": + case "functionedTemplate": { latestPromise = osparc.store.Templates.fetchTemplate(resourceData["uuid"]); break; + } case "function": latestPromise = osparc.store.Functions.fetchFunction(resourceData["uuid"]); break; From 71ab85de85b76c670b0cd605033788544a3fc62c Mon Sep 17 00:00:00 2001 From: odeimaiz Date: Wed, 16 Jul 2025 16:01:27 +0200 Subject: [PATCH 21/40] more --- .../source/class/osparc/dashboard/ResourceDetails.js | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/services/static-webserver/client/source/class/osparc/dashboard/ResourceDetails.js b/services/static-webserver/client/source/class/osparc/dashboard/ResourceDetails.js index b7ac2f7af533..7ec69d6852b6 100644 --- a/services/static-webserver/client/source/class/osparc/dashboard/ResourceDetails.js +++ b/services/static-webserver/client/source/class/osparc/dashboard/ResourceDetails.js @@ -60,6 +60,7 @@ qx.Class.define("osparc.dashboard.ResourceDetails", { case "template": case "tutorial": case "hypertool": + case "functionedTemplate": // when getting the latest study data, the debt information was lost if (osparc.study.Utils.isInDebt(this.__resourceData)) { const studyStore = osparc.store.Study.getInstance(); @@ -378,9 +379,12 @@ qx.Class.define("osparc.dashboard.ResourceDetails", { // removeAll osparc.utils.Utils.removeAllChildren(tabsView); - if (this.__resourceData["resourceType"] === "function") { + if (this.__resourceData["resourceType"] === "functionedTemplate") { // for now, we only want the preview page - // OM: careful here, the one coming from the MMUX services is marked as "function" + this.__addPreviewPage(); + this.fireEvent("pagesAdded"); + return; + } else if (this.__resourceData["resourceType"] === "function") { this.__addInfoPage(); this.__addPreviewPage(); this.fireEvent("pagesAdded"); From eaeddc551050267f0f30cb31d7820b8b742a4242 Mon Sep 17 00:00:00 2001 From: odeimaiz Date: Wed, 16 Jul 2025 16:18:53 +0200 Subject: [PATCH 22/40] FunctionLarge --- .../class/osparc/dashboard/ResourceDetails.js | 13 +- .../source/class/osparc/info/FunctionLarge.js | 194 ++++++++++++++++++ .../source/class/osparc/info/StudyLarge.js | 4 - .../source/class/osparc/utils/Resources.js | 4 + 4 files changed, 210 insertions(+), 5 deletions(-) create mode 100644 services/static-webserver/client/source/class/osparc/info/FunctionLarge.js diff --git a/services/static-webserver/client/source/class/osparc/dashboard/ResourceDetails.js b/services/static-webserver/client/source/class/osparc/dashboard/ResourceDetails.js index 7ec69d6852b6..7d29bcbf3429 100644 --- a/services/static-webserver/client/source/class/osparc/dashboard/ResourceDetails.js +++ b/services/static-webserver/client/source/class/osparc/dashboard/ResourceDetails.js @@ -166,6 +166,10 @@ qx.Class.define("osparc.dashboard.ResourceDetails", { __addToolbarButtons: function(page) { const resourceData = this.__resourceData; + if (this.__resourceData["resourceType"] === "function") { + return; // no toolbar buttons for functions + } + const toolbar = this.self().createToolbar(); page.addToHeader(toolbar); @@ -386,7 +390,7 @@ qx.Class.define("osparc.dashboard.ResourceDetails", { return; } else if (this.__resourceData["resourceType"] === "function") { this.__addInfoPage(); - this.__addPreviewPage(); + // this.__addPreviewPage(); this.fireEvent("pagesAdded"); return; } @@ -462,6 +466,13 @@ qx.Class.define("osparc.dashboard.ResourceDetails", { const updatedData = e.getData(); this.__fireUpdateEvent(resourceData, updatedData); }); + } else if (osparc.utils.Resources.isFunction(resourceData)) { + infoCard = new osparc.info.FunctionLarge(resourceModel); + infoCard.addListener("updateFunction", e => { + const updatedData = e.getData(); + this.__fireUpdateEvent(resourceData, updatedData); + }); + infoCard.addListener("openTags", () => this.openTags()); } else { infoCard = new osparc.info.StudyLarge(resourceModel, false); infoCard.addListener("updateStudy", e => { diff --git a/services/static-webserver/client/source/class/osparc/info/FunctionLarge.js b/services/static-webserver/client/source/class/osparc/info/FunctionLarge.js new file mode 100644 index 000000000000..6749f6372f41 --- /dev/null +++ b/services/static-webserver/client/source/class/osparc/info/FunctionLarge.js @@ -0,0 +1,194 @@ +/* ************************************************************************ + + osparc - the simcore frontend + + https://osparc.io + + Copyright: + 2025 IT'IS Foundation, https://itis.swiss + + License: + MIT: https://opensource.org/licenses/MIT + + Authors: + * Odei Maiz (odeimaiz) + +************************************************************************ */ + + +qx.Class.define("osparc.info.FunctionLarge", { + extend: osparc.info.CardLarge, + + /** + * @param func {osparc.data.model.Function} Function model + */ + construct: function(func) { + this.base(arguments); + + this.setFunction(func); + + this.setOpenOptions(false); + + this._attachHandlers(); + }, + + events: { + "updateFunction": "qx.event.type.Data", + }, + + properties: { + function: { + check: "osparc.data.model.Function", + init: null, + nullable: false + } + }, + + members: { + __canIWrite: function() { + // return osparc.data.model.Function.canIWrite(this.getFunction().getAccessRights()); + return true; + }, + + _rebuildLayout: function() { + this._removeAll(); + + const vBox = new qx.ui.container.Composite(new qx.ui.layout.VBox(10)); + + const infoElements = this.__infoElements(); + const isStudy = true; + const infoLayout = osparc.info.Utils.infoElementsToLayout(infoElements, isStudy); + vBox.add(infoLayout); + + // Copy Id button + const text = "Function Id"; + const copyIdButton = new qx.ui.form.Button(null, "@FontAwesome5Solid/copy/12").set({ + label: text, + toolTipText: "Copy " + text, + marginTop: 15, + allowGrowX: false + }); + copyIdButton.addListener("execute", () => osparc.utils.Utils.copyTextToClipboard(this.getFunction().getUuid())); + vBox.add(copyIdButton); + + // All in a scroll container + const scrollContainer = new qx.ui.container.Scroll(); + scrollContainer.add(vBox); + + this._add(scrollContainer, { + flex: 1 + }); + }, + + __infoElements: function() { + const canIWrite = this.__canIWrite(); + + const infoLayout = { + "TITLE": { + view: osparc.info.StudyUtils.createTitle(this.getFunction()), + action: { + button: osparc.utils.Utils.getEditButton(canIWrite), + callback: canIWrite ? this.__openTitleEditor : null, + ctx: this + } + }, + "THUMBNAIL": { + view: this.__createThumbnail(), + action: null + }, + "DESCRIPTION": { + view: osparc.info.StudyUtils.createDescription(this.getFunction()), + action: { + button: osparc.utils.Utils.getEditButton(canIWrite), + callback: canIWrite ? this.__openDescriptionEditor : null, + ctx: this + } + }, + /* + "AUTHOR": { + label: this.tr("Author"), + view: osparc.info.StudyUtils.createOwner(this.getFunction()), + action: null + }, + */ + "ACCESS_RIGHTS": { + label: this.tr("Access"), + view: osparc.info.StudyUtils.createAccessRights(this.getFunction()), + action: { + button: osparc.utils.Utils.getLinkButton(canIWrite), + callback: this.isOpenOptions() ? this.__openAccessRights : "openAccessRights", + ctx: this + } + }, + "CREATED": { + label: this.tr("Created"), + view: osparc.info.StudyUtils.createCreationDate(this.getFunction()), + action: null + }, + "MODIFIED": { + label: this.tr("Modified"), + view: osparc.info.StudyUtils.createLastChangeDate(this.getFunction()), + action: null + }, + }; + return infoLayout; + }, + + __createThumbnail: function() { + const maxWidth = 190; + const maxHeight = 220; + const thumb = osparc.info.StudyUtils.createThumbnail(this.getFunction(), maxWidth, maxHeight); + thumb.set({ + maxWidth: 120, + maxHeight: 139 + }); + thumb.getChildControl("image").set({ + width: 120, + height: 139, + scale: true, + }); + + return thumb; + }, + + __openTitleEditor: function() { + const title = this.tr("Edit Title"); + const titleEditor = new osparc.widget.Renamer(this.getFunction().getName(), null, title); + titleEditor.addListener("labelChanged", e => { + titleEditor.close(); + const newLabel = e.getData()["newLabel"]; + this.__patchFunction("name", newLabel); + }, this); + titleEditor.center(); + titleEditor.open(); + }, + + __openDescriptionEditor: function() { + const title = this.tr("Edit Description"); + const textEditor = new osparc.editor.MarkdownEditor(this.getStudy().getDescription()); + textEditor.setMaxHeight(570); + const win = osparc.ui.window.Window.popUpInWindow(textEditor, title, 400, 300); + textEditor.addListener("textChanged", e => { + win.close(); + const newDescription = e.getData(); + this.__patchFunction("description", newDescription); + }, this); + textEditor.addListener("cancel", () => { + win.close(); + }, this); + }, + + __patchFunction: function(fieldKey, value) { + this.getStudy().patchStudy({[fieldKey]: value}) + .then(studyData => { + studyData["resourceType"] = this.getStudy().getTemplateType() ? "template" : "study"; + this.fireDataEvent("updateStudy", studyData); + qx.event.message.Bus.getInstance().dispatchByName("updateStudy", studyData); + }) + .catch(err => { + const msg = this.tr("An issue occurred while updating the information."); + osparc.FlashMessenger.logError(err, msg); + }); + } + } +}); diff --git a/services/static-webserver/client/source/class/osparc/info/StudyLarge.js b/services/static-webserver/client/source/class/osparc/info/StudyLarge.js index 3b6c2c88b619..c97be0a9196c 100644 --- a/services/static-webserver/client/source/class/osparc/info/StudyLarge.js +++ b/services/static-webserver/client/source/class/osparc/info/StudyLarge.js @@ -236,10 +236,6 @@ qx.Class.define("osparc.info.StudyLarge", { return infoLayout; }, - __createStudyId: function() { - return osparc.info.StudyUtils.createUuid(this.getStudy()); - }, - __createThumbnail: function() { const maxWidth = 190; const maxHeight = 220; diff --git a/services/static-webserver/client/source/class/osparc/utils/Resources.js b/services/static-webserver/client/source/class/osparc/utils/Resources.js index 9d5f3c331f91..fb6a33663d0a 100644 --- a/services/static-webserver/client/source/class/osparc/utils/Resources.js +++ b/services/static-webserver/client/source/class/osparc/utils/Resources.js @@ -39,6 +39,10 @@ qx.Class.define("osparc.utils.Resources", { return ((hypertoolData["resourceType"] === "hypertool") && ("uuid" in hypertoolData)); }, + isFunction: function(functionData) { + return ((functionData["resourceType"] === "function") && ("uuid" in functionData)); + }, + isService: function(serviceData) { return ((serviceData["resourceType"] === "service") && ("key" in serviceData) && ("version" in serviceData)); }, From 95700cfd60569a887ad0e1f209427c678ae39b2c Mon Sep 17 00:00:00 2001 From: odeimaiz Date: Wed, 16 Jul 2025 16:42:49 +0200 Subject: [PATCH 23/40] canIWrite --- .../source/class/osparc/data/model/Function.js | 17 +++++++++++++++++ .../source/class/osparc/info/FunctionLarge.js | 10 +--------- 2 files changed, 18 insertions(+), 9 deletions(-) diff --git a/services/static-webserver/client/source/class/osparc/data/model/Function.js b/services/static-webserver/client/source/class/osparc/data/model/Function.js index 31b4ebece065..c67828c49e10 100644 --- a/services/static-webserver/client/source/class/osparc/data/model/Function.js +++ b/services/static-webserver/client/source/class/osparc/data/model/Function.js @@ -135,4 +135,21 @@ qx.Class.define("osparc.data.model.Function", { init: {} }, }, + + statics: { + canIWrite: function(accessRights) { + const groupsStore = osparc.store.Groups.getInstance(); + const orgIDs = groupsStore.getOrganizationIds(); + orgIDs.push(groupsStore.getMyGroupId()); + if (orgIDs.length) { + let canWrite = false; + for (let i=0; i Date: Wed, 16 Jul 2025 16:45:07 +0200 Subject: [PATCH 24/40] minor --- .../source/class/osparc/data/model/Function.js | 17 +++++++---------- .../source/class/osparc/info/FunctionLarge.js | 6 +----- 2 files changed, 8 insertions(+), 15 deletions(-) diff --git a/services/static-webserver/client/source/class/osparc/data/model/Function.js b/services/static-webserver/client/source/class/osparc/data/model/Function.js index c67828c49e10..b35383cdc65e 100644 --- a/services/static-webserver/client/source/class/osparc/data/model/Function.js +++ b/services/static-webserver/client/source/class/osparc/data/model/Function.js @@ -139,17 +139,14 @@ qx.Class.define("osparc.data.model.Function", { statics: { canIWrite: function(accessRights) { const groupsStore = osparc.store.Groups.getInstance(); - const orgIDs = groupsStore.getOrganizationIds(); - orgIDs.push(groupsStore.getMyGroupId()); - if (orgIDs.length) { - let canWrite = false; - for (let i=0; i Date: Wed, 16 Jul 2025 16:50:25 +0200 Subject: [PATCH 25/40] expand dummy --- .../source/class/osparc/store/Functions.js | 52 +++++++++++++++++-- 1 file changed, 48 insertions(+), 4 deletions(-) diff --git a/services/static-webserver/client/source/class/osparc/store/Functions.js b/services/static-webserver/client/source/class/osparc/store/Functions.js index f7c109a6555a..86e774f4c0cb 100644 --- a/services/static-webserver/client/source/class/osparc/store/Functions.js +++ b/services/static-webserver/client/source/class/osparc/store/Functions.js @@ -150,12 +150,56 @@ qx.Class.define("osparc.store.Functions", { "functionClass": "PROJECT", "name": "Potential Function II", "description": "Function description", - "inputSchema": {"schema_class": "application/schema+json", "schema_content": {"type": "object", "required": ["X"], "properties": {"X": {"type": "number"}}}}, - "outputSchema": {"schema_class": "application/schema+json", "schema_content": {"type": "object", "required": ["Out 1", "Out_2"], "properties": {"Out 1": {"type": "number"}, "Out_2": {"type": "number"}}}}, - "defaultInputs": {"X": 2, "Y": 1}, + "inputSchema": { + "schema_class": "application/schema+json", + "schema_content": { + "type": "object", + "required": [ + "X" + ], + "properties": { + "X": { + "type": "number" + } + } + } + }, + "outputSchema": { + "schema_class": "application/schema+json", + "schema_content": { + "type": "object", + "required": [ + "Out 1", + "Out_2" + ], + "properties": { + "Out 1": { + "type": "number" + }, + "Out_2": { + "type": "number" + } + } + } + }, + "defaultInputs": { + "X": 2, + "Y": 1 + }, "creationDate": "2025-05-16T12:22:31.063Z", "lastChangeDate": "2025-05-16T12:22:33.804Z", - "accessRights": {"3": {"read": true, "write": true, "delete": true}, "5": {"read": true, "write": false, "delete": false}}, + "accessRights": { + "3": { + "read": true, + "write": true, + "delete": true + }, + "5": { + "read": true, + "write": false, + "delete": false + } + }, "thumbnail": "https://img.freepik.com/premium-vector/image-icon-design-vector-template_1309674-940.jpg", "workbench": {"50a50309-1dfc-5ad5-b2d9-c11697641f0b": {"key": "simcore/services/comp/itis/sleeper", "version": "2.2.0", "label": "sleeper", "inputs": {"input_2": 2, "input_3": false, "input_4": 0, "input_5": 0}, "inputsRequired": [], "inputNodes": ["2e348481-5042-5148-9196-590574747297", "69873032-770a-536b-adb6-0e6ea01720a4"]}, "2e348481-5042-5148-9196-590574747297": {"key": "simcore/services/frontend/parameter/number", "version": "1.0.0", "label": "X", "inputs": {}, "inputsRequired": [], "inputNodes": [], "outputs": {"out_1": 1}, "runHash": null}, "70e1de1a-a8b0-59e3-b19e-ea20f78765ce": {"key": "simcore/services/frontend/iterator-consumer/probe/number", "version": "1.0.0", "label": "Out 1", "inputs": {"in_1": 0}, "inputsRequired": [], "inputNodes": ["50a50309-1dfc-5ad5-b2d9-c11697641f0b"]}, "69873032-770a-536b-adb6-0e6ea01720a4": {"key": "simcore/services/frontend/parameter/number", "version": "1.0.0", "label": "Y", "inputs": {}, "inputsRequired": [], "inputNodes": [], "outputs": {"out_1": 1}, "runHash": null}, "24f856c3-408c-5ab4-ad01-e99630a355fe": {"key": "simcore/services/frontend/iterator-consumer/probe/number", "version": "1.0.0", "label": "Out_2", "inputs": {"in_1": 0}, "inputsRequired": [], "inputNodes": ["50a50309-1dfc-5ad5-b2d9-c11697641f0b"]}}, "ui": {"24f856c3-408c-5ab4-ad01-e99630a355fe": {"position": {"x": 540, "y": 240}}, "2e348481-5042-5148-9196-590574747297": {"position": {"x": 120, "y": 140}}, "50a50309-1dfc-5ad5-b2d9-c11697641f0b": {"position": {"x": 300, "y": 180}}, "69873032-770a-536b-adb6-0e6ea01720a4": {"position": {"x": 120, "y": 240}}, "70e1de1a-a8b0-59e3-b19e-ea20f78765ce": {"position": {"x": 540, "y": 140}}}, From ce13d34c96841bd2b65a44967c450cde7a1903fb Mon Sep 17 00:00:00 2001 From: odeimaiz Date: Wed, 16 Jul 2025 17:00:58 +0200 Subject: [PATCH 26/40] inputs, default inputs and outputs --- .../source/class/osparc/info/FunctionLarge.js | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/services/static-webserver/client/source/class/osparc/info/FunctionLarge.js b/services/static-webserver/client/source/class/osparc/info/FunctionLarge.js index ee024b2dc572..0b0ef9882699 100644 --- a/services/static-webserver/client/source/class/osparc/info/FunctionLarge.js +++ b/services/static-webserver/client/source/class/osparc/info/FunctionLarge.js @@ -59,6 +59,21 @@ qx.Class.define("osparc.info.FunctionLarge", { const infoLayout = osparc.info.Utils.infoElementsToLayout(infoElements, isStudy); vBox.add(infoLayout); + // inputs, default inputs and outputs + const info = { + "Inputs": this.getFunction().getInputSchema()["schema_content"], + "Default Inputs": this.getFunction().getDefaultInputs(), + "Outputs": this.getFunction().getOutputSchema()["schema_content"], + }; + const divId = "function-info-viewer"; + const htmlEmbed = osparc.wrapper.JsonFormatter.getInstance().createContainer(divId); + vBox.add(htmlEmbed, { + flex: 1 + }); + vBox.addListener("appear", () => { + osparc.wrapper.JsonFormatter.getInstance().setJson(info, divId); + }); + // Copy Id button const text = "Function Id"; const copyIdButton = new qx.ui.form.Button(null, "@FontAwesome5Solid/copy/12").set({ From a4eceb946999112c501d888103ad2baef4878050 Mon Sep 17 00:00:00 2001 From: odeimaiz Date: Wed, 16 Jul 2025 17:25:30 +0200 Subject: [PATCH 27/40] new message from MMUX --- .../class/osparc/widget/PersistentIframe.js | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/services/static-webserver/client/source/class/osparc/widget/PersistentIframe.js b/services/static-webserver/client/source/class/osparc/widget/PersistentIframe.js index e657bca19f51..6536ba238b36 100644 --- a/services/static-webserver/client/source/class/osparc/widget/PersistentIframe.js +++ b/services/static-webserver/client/source/class/osparc/widget/PersistentIframe.js @@ -332,12 +332,21 @@ qx.Class.define("osparc.widget.PersistentIframe", { } case "openFunction": { // this is the MetaModeling service trying to show function/template information - if (data["message"] && data["message"]["functionId"]) { - const templateId = data["message"]["functionId"]; - const functionData = { - "uuid": templateId, + let functionData = null; + if (data["message"] && data["message"]["uuid"]) { + // new version, the uuid is from the function + functionData = { + "uuid": data["message"]["uuid"], + "resourceType": "function", + }; + } else if (data["message"] && data["message"]["functionId"]) { + // old version, the uuid is from the template + functionData = { + "uuid": data["message"]["functionId"], "resourceType": "functionedTemplate", }; + } + if (functionData) { const { resourceDetails, window, From 66af5ed74cca5710764280949f2043ed4820ca94 Mon Sep 17 00:00:00 2001 From: odeimaiz Date: Thu, 17 Jul 2025 08:00:14 +0200 Subject: [PATCH 28/40] fetch services --- .../source/class/osparc/dashboard/ResourceDetails.js | 12 ++++++++---- .../client/source/class/osparc/store/Functions.js | 2 +- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/services/static-webserver/client/source/class/osparc/dashboard/ResourceDetails.js b/services/static-webserver/client/source/class/osparc/dashboard/ResourceDetails.js index 7d29bcbf3429..e8c0b6865a9d 100644 --- a/services/static-webserver/client/source/class/osparc/dashboard/ResourceDetails.js +++ b/services/static-webserver/client/source/class/osparc/dashboard/ResourceDetails.js @@ -72,12 +72,16 @@ qx.Class.define("osparc.dashboard.ResourceDetails", { this.__resourceModel["resourceType"] = resourceData["resourceType"]; this.__resourceData["services"] = resourceData["services"]; this.__addPages(); - }) + }); break; case "function": { - this.__resourceModel = new osparc.data.model.Function(latestResourceData); - this.__resourceModel["resourceType"] = resourceData["resourceType"]; - this.__addPages(); + osparc.store.Services.getStudyServicesMetadata(latestResourceData) + .finally(() => { + this.__resourceModel = new osparc.data.model.Function(latestResourceData); + this.__resourceModel["resourceType"] = resourceData["resourceType"]; + this.__resourceData["services"] = resourceData["services"]; + this.__addPages(); + }); break; } case "service": { diff --git a/services/static-webserver/client/source/class/osparc/store/Functions.js b/services/static-webserver/client/source/class/osparc/store/Functions.js index 86e774f4c0cb..d412247ef826 100644 --- a/services/static-webserver/client/source/class/osparc/store/Functions.js +++ b/services/static-webserver/client/source/class/osparc/store/Functions.js @@ -201,7 +201,7 @@ qx.Class.define("osparc.store.Functions", { } }, "thumbnail": "https://img.freepik.com/premium-vector/image-icon-design-vector-template_1309674-940.jpg", - "workbench": {"50a50309-1dfc-5ad5-b2d9-c11697641f0b": {"key": "simcore/services/comp/itis/sleeper", "version": "2.2.0", "label": "sleeper", "inputs": {"input_2": 2, "input_3": false, "input_4": 0, "input_5": 0}, "inputsRequired": [], "inputNodes": ["2e348481-5042-5148-9196-590574747297", "69873032-770a-536b-adb6-0e6ea01720a4"]}, "2e348481-5042-5148-9196-590574747297": {"key": "simcore/services/frontend/parameter/number", "version": "1.0.0", "label": "X", "inputs": {}, "inputsRequired": [], "inputNodes": [], "outputs": {"out_1": 1}, "runHash": null}, "70e1de1a-a8b0-59e3-b19e-ea20f78765ce": {"key": "simcore/services/frontend/iterator-consumer/probe/number", "version": "1.0.0", "label": "Out 1", "inputs": {"in_1": 0}, "inputsRequired": [], "inputNodes": ["50a50309-1dfc-5ad5-b2d9-c11697641f0b"]}, "69873032-770a-536b-adb6-0e6ea01720a4": {"key": "simcore/services/frontend/parameter/number", "version": "1.0.0", "label": "Y", "inputs": {}, "inputsRequired": [], "inputNodes": [], "outputs": {"out_1": 1}, "runHash": null}, "24f856c3-408c-5ab4-ad01-e99630a355fe": {"key": "simcore/services/frontend/iterator-consumer/probe/number", "version": "1.0.0", "label": "Out_2", "inputs": {"in_1": 0}, "inputsRequired": [], "inputNodes": ["50a50309-1dfc-5ad5-b2d9-c11697641f0b"]}}, + "workbench": {"50a50309-1dfc-5ad5-b2d9-c11697641f0b": {"key": "simcore/services/comp/itis/sleeper", "version": "2.1.6", "label": "sleeper", "inputs": {"input_2": 2, "input_3": false, "input_4": 0, "input_5": 0}, "inputsRequired": [], "inputNodes": ["2e348481-5042-5148-9196-590574747297", "69873032-770a-536b-adb6-0e6ea01720a4"]}, "2e348481-5042-5148-9196-590574747297": {"key": "simcore/services/frontend/parameter/number", "version": "1.0.0", "label": "X", "inputs": {}, "inputsRequired": [], "inputNodes": [], "outputs": {"out_1": 1}, "runHash": null}, "70e1de1a-a8b0-59e3-b19e-ea20f78765ce": {"key": "simcore/services/frontend/iterator-consumer/probe/number", "version": "1.0.0", "label": "Out 1", "inputs": {"in_1": 0}, "inputsRequired": [], "inputNodes": ["50a50309-1dfc-5ad5-b2d9-c11697641f0b"]}, "69873032-770a-536b-adb6-0e6ea01720a4": {"key": "simcore/services/frontend/parameter/number", "version": "1.0.0", "label": "Y", "inputs": {}, "inputsRequired": [], "inputNodes": [], "outputs": {"out_1": 1}, "runHash": null}, "24f856c3-408c-5ab4-ad01-e99630a355fe": {"key": "simcore/services/frontend/iterator-consumer/probe/number", "version": "1.0.0", "label": "Out_2", "inputs": {"in_1": 0}, "inputsRequired": [], "inputNodes": ["50a50309-1dfc-5ad5-b2d9-c11697641f0b"]}}, "ui": {"24f856c3-408c-5ab4-ad01-e99630a355fe": {"position": {"x": 540, "y": 240}}, "2e348481-5042-5148-9196-590574747297": {"position": {"x": 120, "y": 140}}, "50a50309-1dfc-5ad5-b2d9-c11697641f0b": {"position": {"x": 300, "y": 180}}, "69873032-770a-536b-adb6-0e6ea01720a4": {"position": {"x": 120, "y": 240}}, "70e1de1a-a8b0-59e3-b19e-ea20f78765ce": {"position": {"x": 540, "y": 140}}}, }], "_links": { From 8420c55edd35511a0f36f6387d1cd6550d5306d5 Mon Sep 17 00:00:00 2001 From: odeimaiz Date: Thu, 17 Jul 2025 08:12:44 +0200 Subject: [PATCH 29/40] minor --- .../class/osparc/desktop/ZoomButtons.js | 12 ++++--- .../class/osparc/workbench/WorkbenchUI.js | 35 +++++++++---------- 2 files changed, 24 insertions(+), 23 deletions(-) diff --git a/services/static-webserver/client/source/class/osparc/desktop/ZoomButtons.js b/services/static-webserver/client/source/class/osparc/desktop/ZoomButtons.js index 78a61438ea1c..2da87a0cddef 100644 --- a/services/static-webserver/client/source/class/osparc/desktop/ZoomButtons.js +++ b/services/static-webserver/client/source/class/osparc/desktop/ZoomButtons.js @@ -28,8 +28,6 @@ * */ -const ZOOM_BUTTON_SIZE = 32; - qx.Class.define("osparc.desktop.ZoomButtons", { extend: qx.ui.toolbar.ToolBar, @@ -52,6 +50,10 @@ qx.Class.define("osparc.desktop.ZoomButtons", { "zoomReset": "qx.event.type.Event" }, + statics: { + ZOOM_BUTTON_SIZE: 32, + }, + members: { __buildLayout: function() { this.add(this.__getZoomOutButton()); @@ -64,9 +66,9 @@ qx.Class.define("osparc.desktop.ZoomButtons", { appearance: "form-button-outlined", padding: [5, 5], marginLeft: 10, - width: ZOOM_BUTTON_SIZE, - height: ZOOM_BUTTON_SIZE, - maxHeight: ZOOM_BUTTON_SIZE + width: this.self().ZOOM_BUTTON_SIZE, + height: this.self().ZOOM_BUTTON_SIZE, + maxHeight: this.self().ZOOM_BUTTON_SIZE, }); if (tooltip) { btn.setToolTipText(tooltip); diff --git a/services/static-webserver/client/source/class/osparc/workbench/WorkbenchUI.js b/services/static-webserver/client/source/class/osparc/workbench/WorkbenchUI.js index 367f470b1d84..8ca3a4e2c4ec 100644 --- a/services/static-webserver/client/source/class/osparc/workbench/WorkbenchUI.js +++ b/services/static-webserver/client/source/class/osparc/workbench/WorkbenchUI.js @@ -33,9 +33,6 @@ * */ -const BUTTON_SIZE = 38; -const NODE_INPUTS_WIDTH = 210; - qx.Class.define("osparc.workbench.WorkbenchUI", { extend: qx.ui.core.Widget, @@ -56,16 +53,8 @@ qx.Class.define("osparc.workbench.WorkbenchUI", { }, statics: { - getDashedBorderStyle(isRight) { - const side = isRight ? "right" : "left"; - const borderStyle = {}; - borderStyle["background-image"] = `linear-gradient(to bottom, #3D3D3D 50%, rgba(255, 255, 255, 0) 0%)`; - borderStyle["background-position"] = side; - borderStyle["background-size"] = "5px 50px"; - borderStyle["background-repeat"] = "repeat-y"; - return borderStyle; - }, - + BUTTON_SIZE: 38, + NODE_INPUTS_WIDTH: 210, ZOOM_VALUES: [ 0.1, 0.2, @@ -83,7 +72,17 @@ qx.Class.define("osparc.workbench.WorkbenchUI", { 2, 2.5, 3 - ] + ], + + getDashedBorderStyle(isRight) { + const side = isRight ? "right" : "left"; + const borderStyle = {}; + borderStyle["background-image"] = `linear-gradient(to bottom, #3D3D3D 50%, rgba(255, 255, 255, 0) 0%)`; + borderStyle["background-position"] = side; + borderStyle["background-size"] = "5px 50px"; + borderStyle["background-repeat"] = "repeat-y"; + return borderStyle; + }, }, events: { @@ -227,8 +226,8 @@ qx.Class.define("osparc.workbench.WorkbenchUI", { __addDeleteItemButton: function() { const deleteItemButton = this.__deleteItemButton = new qx.ui.form.Button().set({ icon: "@FontAwesome5Solid/trash/18", - width: BUTTON_SIZE, - height: BUTTON_SIZE, + width: this.self().BUTTON_SIZE, + height: this.self().BUTTON_SIZE, visibility: "excluded" }); deleteItemButton.addListener("execute", () => { @@ -271,8 +270,8 @@ qx.Class.define("osparc.workbench.WorkbenchUI", { const label = isInput ? this.tr("INPUTS") : this.tr("OUTPUTS"); const inputOutputNodesLayout = new qx.ui.container.Composite(new qx.ui.layout.VBox(5)); inputOutputNodesLayout.set({ - width: NODE_INPUTS_WIDTH, - maxWidth: NODE_INPUTS_WIDTH, + width: this.self().NODE_INPUTS_WIDTH, + maxWidth: this.self().NODE_INPUTS_WIDTH, allowGrowX: false, padding: [0, 6] }); From a73dfd965b07847e053282cbf543201ec3e4086c Mon Sep 17 00:00:00 2001 From: odeimaiz Date: Thu, 17 Jul 2025 08:42:27 +0200 Subject: [PATCH 30/40] WorkbenchUIPreview2 --- .../class/osparc/data/model/Function.js | 17 ++-- .../osparc/workbench/WorkbenchUIPreview2.js | 89 +++++++++++++++++++ 2 files changed, 96 insertions(+), 10 deletions(-) create mode 100644 services/static-webserver/client/source/class/osparc/workbench/WorkbenchUIPreview2.js diff --git a/services/static-webserver/client/source/class/osparc/data/model/Function.js b/services/static-webserver/client/source/class/osparc/data/model/Function.js index b35383cdc65e..8c0458cdbb30 100644 --- a/services/static-webserver/client/source/class/osparc/data/model/Function.js +++ b/services/static-webserver/client/source/class/osparc/data/model/Function.js @@ -42,12 +42,10 @@ qx.Class.define("osparc.data.model.Function", { thumbnail: functionData.thumbnail || this.getThumbnail(), }); - const wbData = functionData.workbench || this.getWorkbench(); - const workbench = new osparc.data.model.Workbench(wbData, functionData.ui); - this.setWorkbench(workbench); - workbench.setFunction(this); - - this.getWorkbench().buildWorkbench(); + const wbData = functionData.workbench || {}; + const wbUiData = functionData.ui || {}; + const workbenchUIPreview = new osparc.workbench.WorkbenchUIPreview2(wbData, wbUiData); + this.setWorkbenchUiPreview(workbenchUIPreview); }, properties: { @@ -128,11 +126,10 @@ qx.Class.define("osparc.data.model.Function", { init: null }, - workbench: { - check: "osparc.data.model.Workbench", + workbenchUiPreview: { + check: "osparc.workbench.WorkbenchUIPreview2", nullable: false, - event: "changeWorkbench", - init: {} + init: null, }, }, diff --git a/services/static-webserver/client/source/class/osparc/workbench/WorkbenchUIPreview2.js b/services/static-webserver/client/source/class/osparc/workbench/WorkbenchUIPreview2.js new file mode 100644 index 000000000000..5ecdc68836ab --- /dev/null +++ b/services/static-webserver/client/source/class/osparc/workbench/WorkbenchUIPreview2.js @@ -0,0 +1,89 @@ +/* ************************************************************************ + + osparc - the simcore frontend + + https://osparc.io + + Copyright: + 2021 IT'IS Foundation, https://itis.swiss + + License: + MIT: https://opensource.org/licenses/MIT + + Authors: + * Odei Maiz (odeimaiz) + +************************************************************************ */ + +/** + * @ignore(SVGElement) + */ + +/** + * *Example* + * + * Here is a little example of how to use the widget. + * + *
+ *   let workbenchUIPreview = new osparc.workbench.WorkbenchUIPreview();
+ *   this.getRoot().add(workbenchUIPreview);
+ * 
+ */ + +qx.Class.define("osparc.workbench.WorkbenchUIPreview2", { + extend: osparc.workbench.WorkbenchUI, + + construct: function(wbData, wbDataUi) { + this.base(arguments); + + this.set({ + backgroundColor: "background-main" + }); + }, + + members: { + // overridden + _addItemsToLayout: function() { + this._addWorkbenchLayer(); + }, + + // overridden + _loadModel: function(model) { + this._clearAll(); + this.resetSelection(); + this._currentModel = model; + if (model) { + qx.ui.core.queue.Visibility.flush(); + + // create nodes + const nodes = model.getNodes(); + for (const nodeId in nodes) { + const node = nodes[nodeId]; + const nodeUI = this._createNodeUI(nodeId); + nodeUI.setIsMovable(false); + this._addNodeUIToWorkbench(nodeUI, node.getPosition()); + } + qx.ui.core.queue.Layout.flush(); + + // create edges + for (const nodeId in nodes) { + const node = nodes[nodeId]; + const inputNodeIDs = node.getInputNodes(); + inputNodeIDs.forEach(inputNodeId => { + if (inputNodeId in nodes) { + this._createEdgeBetweenNodes(inputNodeId, nodeId, false); + } + }); + } + + const maxScale = 0.7; + this._fitScaleToNodes(maxScale); + } + }, + + // overridden + _addEventListeners: function() { + this.addListenerOnce("appear", this._listenToMouseWheel, this); + } + } +}); From c31f73f71e10c265ad9f253d48ede34f2850dd5e Mon Sep 17 00:00:00 2001 From: odeimaiz Date: Thu, 17 Jul 2025 09:27:16 +0200 Subject: [PATCH 31/40] minor --- .../client/source/class/osparc/workbench/NodeUI.js | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/services/static-webserver/client/source/class/osparc/workbench/NodeUI.js b/services/static-webserver/client/source/class/osparc/workbench/NodeUI.js index 80fb1522b45e..41bf33df07e3 100644 --- a/services/static-webserver/client/source/class/osparc/workbench/NodeUI.js +++ b/services/static-webserver/client/source/class/osparc/workbench/NodeUI.js @@ -298,9 +298,11 @@ qx.Class.define("osparc.workbench.NodeUI", { node.addListener("changeMarker", () => updateMarker()); updateMarker(); - node.getStudy().bind("pipelineRunning", this._deleteBtn, "enabled", { - converter: running => !running - }); + if (node.getStudy()) { + node.getStudy().bind("pipelineRunning", this._deleteBtn, "enabled", { + converter: running => !running + }); + } const evaluateLifeCycleIcon = () => { const deprecatedIcon = this.getChildControl("deprecated-icon"); From 293819b0b227655d249355a254bded3e38e5ccfa Mon Sep 17 00:00:00 2001 From: odeimaiz Date: Thu, 17 Jul 2025 09:27:34 +0200 Subject: [PATCH 32/40] __buildPreview --- .../osparc/workbench/WorkbenchUIPreview2.js | 67 ++++++++++--------- 1 file changed, 35 insertions(+), 32 deletions(-) diff --git a/services/static-webserver/client/source/class/osparc/workbench/WorkbenchUIPreview2.js b/services/static-webserver/client/source/class/osparc/workbench/WorkbenchUIPreview2.js index 5ecdc68836ab..06d21dbdebc9 100644 --- a/services/static-webserver/client/source/class/osparc/workbench/WorkbenchUIPreview2.js +++ b/services/static-webserver/client/source/class/osparc/workbench/WorkbenchUIPreview2.js @@ -39,51 +39,54 @@ qx.Class.define("osparc.workbench.WorkbenchUIPreview2", { this.set({ backgroundColor: "background-main" }); + + this.__wbData = wbData || {}; + this.__wbDataUi = wbDataUi || {}; + + this.__buildPreview(); }, members: { + __wbData: null, + __wbDataUi: null, + // overridden _addItemsToLayout: function() { this._addWorkbenchLayer(); }, // overridden - _loadModel: function(model) { - this._clearAll(); - this.resetSelection(); - this._currentModel = model; - if (model) { - qx.ui.core.queue.Visibility.flush(); - - // create nodes - const nodes = model.getNodes(); - for (const nodeId in nodes) { - const node = nodes[nodeId]; - const nodeUI = this._createNodeUI(nodeId); - nodeUI.setIsMovable(false); - this._addNodeUIToWorkbench(nodeUI, node.getPosition()); - } - qx.ui.core.queue.Layout.flush(); - - // create edges - for (const nodeId in nodes) { - const node = nodes[nodeId]; - const inputNodeIDs = node.getInputNodes(); - inputNodeIDs.forEach(inputNodeId => { - if (inputNodeId in nodes) { - this._createEdgeBetweenNodes(inputNodeId, nodeId, false); - } - }); - } - - const maxScale = 0.7; - this._fitScaleToNodes(maxScale); - } + _loadModel: function() { + return; // no model to load, this is a preview }, // overridden _addEventListeners: function() { this.addListenerOnce("appear", this._listenToMouseWheel, this); - } + }, + + __buildPreview: function() { + this._clearAll(); + this.resetSelection(); + + // create nodes + const nodes = this.__wbData.nodes || {}; + Object.entries(nodes).forEach(([nodeId, nodeData]) => { + // we assume that the metadata was fetched before + const serviceMetadata = osparc.store.Services.getMetadata(nodeData["key"], nodeData["version"]); + const node = osparc.data.model.Node(null, serviceMetadata, nodeId); + const nodeUI = new osparc.workbench.NodeUI(node); + nodeUI.setIsMovable(false); + this._addNodeUIToWorkbench(nodeUI, node.position); + }); + qx.ui.core.queue.Layout.flush(); + + // create edges + const edges = this.__wbData.edges || {}; + for (const edgeId in edges) { + const edge = edges[edgeId]; + this._createEdge(edge.from, edge.to, edgeId); + } + }, } }); From c4b6e98b3b230cdd2175ba7641089f2ca5e9b4fb Mon Sep 17 00:00:00 2001 From: odeimaiz Date: Thu, 17 Jul 2025 09:57:24 +0200 Subject: [PATCH 33/40] Function preview --- .../class/osparc/dashboard/ResourceDetails.js | 9 +++- .../class/osparc/data/model/Function.js | 19 +++++---- .../source/class/osparc/data/model/Node.js | 3 ++ .../source/class/osparc/store/Functions.js | 5 ++- .../class/osparc/study/FunctionPreview.js | 41 +++++++++++++++++++ .../class/osparc/workbench/WorkbenchUI.js | 2 +- .../osparc/workbench/WorkbenchUIPreview2.js | 9 ++-- 7 files changed, 72 insertions(+), 16 deletions(-) create mode 100644 services/static-webserver/client/source/class/osparc/study/FunctionPreview.js diff --git a/services/static-webserver/client/source/class/osparc/dashboard/ResourceDetails.js b/services/static-webserver/client/source/class/osparc/dashboard/ResourceDetails.js index e8c0b6865a9d..b94ffa6bfea9 100644 --- a/services/static-webserver/client/source/class/osparc/dashboard/ResourceDetails.js +++ b/services/static-webserver/client/source/class/osparc/dashboard/ResourceDetails.js @@ -394,7 +394,7 @@ qx.Class.define("osparc.dashboard.ResourceDetails", { return; } else if (this.__resourceData["resourceType"] === "function") { this.__addInfoPage(); - // this.__addPreviewPage(); + this.__addPreviewPage(); this.fireEvent("pagesAdded"); return; } @@ -575,7 +575,12 @@ qx.Class.define("osparc.dashboard.ResourceDetails", { const lazyLoadContent = () => { const resourceModel = this.__resourceModel; - const preview = new osparc.study.StudyPreview(resourceModel); + let preview = null; + if (osparc.utils.Resources.isFunction(this.__resourceData)) { + preview = new osparc.study.FunctionPreview(resourceModel); + } else { + preview = new osparc.study.StudyPreview(resourceModel); + } page.addToContent(preview); this.__widgets.push(preview); } diff --git a/services/static-webserver/client/source/class/osparc/data/model/Function.js b/services/static-webserver/client/source/class/osparc/data/model/Function.js index 8c0458cdbb30..eeb59b7e40fa 100644 --- a/services/static-webserver/client/source/class/osparc/data/model/Function.js +++ b/services/static-webserver/client/source/class/osparc/data/model/Function.js @@ -40,12 +40,9 @@ qx.Class.define("osparc.data.model.Function", { creationDate: functionData.creationDate ? new Date(functionData.creationDate) : this.getCreationDate(), lastChangeDate: functionData.lastChangeDate ? new Date(functionData.lastChangeDate) : this.getLastChangeDate(), thumbnail: functionData.thumbnail || this.getThumbnail(), + workbenchData: functionData.workbench || this.getWorkbenchData(), + functionUIData: functionData.ui || this.functionUIData(), }); - - const wbData = functionData.workbench || {}; - const wbUiData = functionData.ui || {}; - const workbenchUIPreview = new osparc.workbench.WorkbenchUIPreview2(wbData, wbUiData); - this.setWorkbenchUiPreview(workbenchUIPreview); }, properties: { @@ -126,10 +123,16 @@ qx.Class.define("osparc.data.model.Function", { init: null }, - workbenchUiPreview: { - check: "osparc.workbench.WorkbenchUIPreview2", + workbenchData: { + check: "Object", + nullable: false, + init: {}, + }, + + functionUIData: { + check: "Object", nullable: false, - init: null, + init: {}, }, }, 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 048f69ebfc0c..a5f8b2f25484 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 @@ -577,6 +577,9 @@ qx.Class.define("osparc.data.model.Node", { */ __addSettings: function(inputs) { const form = this.__settingsForm = new osparc.form.Auto(inputs); + if (!this.getStudy()) { + return; + } const propsForm = new osparc.form.renderer.PropForm(form, this, this.getStudy()); this.setPropsForm(propsForm); propsForm.addListener("linkFieldModified", e => { diff --git a/services/static-webserver/client/source/class/osparc/store/Functions.js b/services/static-webserver/client/source/class/osparc/store/Functions.js index d412247ef826..13c9c2d49806 100644 --- a/services/static-webserver/client/source/class/osparc/store/Functions.js +++ b/services/static-webserver/client/source/class/osparc/store/Functions.js @@ -202,7 +202,10 @@ qx.Class.define("osparc.store.Functions", { }, "thumbnail": "https://img.freepik.com/premium-vector/image-icon-design-vector-template_1309674-940.jpg", "workbench": {"50a50309-1dfc-5ad5-b2d9-c11697641f0b": {"key": "simcore/services/comp/itis/sleeper", "version": "2.1.6", "label": "sleeper", "inputs": {"input_2": 2, "input_3": false, "input_4": 0, "input_5": 0}, "inputsRequired": [], "inputNodes": ["2e348481-5042-5148-9196-590574747297", "69873032-770a-536b-adb6-0e6ea01720a4"]}, "2e348481-5042-5148-9196-590574747297": {"key": "simcore/services/frontend/parameter/number", "version": "1.0.0", "label": "X", "inputs": {}, "inputsRequired": [], "inputNodes": [], "outputs": {"out_1": 1}, "runHash": null}, "70e1de1a-a8b0-59e3-b19e-ea20f78765ce": {"key": "simcore/services/frontend/iterator-consumer/probe/number", "version": "1.0.0", "label": "Out 1", "inputs": {"in_1": 0}, "inputsRequired": [], "inputNodes": ["50a50309-1dfc-5ad5-b2d9-c11697641f0b"]}, "69873032-770a-536b-adb6-0e6ea01720a4": {"key": "simcore/services/frontend/parameter/number", "version": "1.0.0", "label": "Y", "inputs": {}, "inputsRequired": [], "inputNodes": [], "outputs": {"out_1": 1}, "runHash": null}, "24f856c3-408c-5ab4-ad01-e99630a355fe": {"key": "simcore/services/frontend/iterator-consumer/probe/number", "version": "1.0.0", "label": "Out_2", "inputs": {"in_1": 0}, "inputsRequired": [], "inputNodes": ["50a50309-1dfc-5ad5-b2d9-c11697641f0b"]}}, - "ui": {"24f856c3-408c-5ab4-ad01-e99630a355fe": {"position": {"x": 540, "y": 240}}, "2e348481-5042-5148-9196-590574747297": {"position": {"x": 120, "y": 140}}, "50a50309-1dfc-5ad5-b2d9-c11697641f0b": {"position": {"x": 300, "y": 180}}, "69873032-770a-536b-adb6-0e6ea01720a4": {"position": {"x": 120, "y": 240}}, "70e1de1a-a8b0-59e3-b19e-ea20f78765ce": {"position": {"x": 540, "y": 140}}}, + "ui": { + "workbench": {"24f856c3-408c-5ab4-ad01-e99630a355fe": {"position": {"x": 540, "y": 240}}, "2e348481-5042-5148-9196-590574747297": {"position": {"x": 120, "y": 140}}, "50a50309-1dfc-5ad5-b2d9-c11697641f0b": {"position": {"x": 300, "y": 180}}, "69873032-770a-536b-adb6-0e6ea01720a4": {"position": {"x": 120, "y": 240}}, "70e1de1a-a8b0-59e3-b19e-ea20f78765ce": {"position": {"x": 540, "y": 140}}}, + "mode": "pipeline", + }, }], "_links": { "next": null, diff --git a/services/static-webserver/client/source/class/osparc/study/FunctionPreview.js b/services/static-webserver/client/source/class/osparc/study/FunctionPreview.js new file mode 100644 index 000000000000..5c9c0aafb1ef --- /dev/null +++ b/services/static-webserver/client/source/class/osparc/study/FunctionPreview.js @@ -0,0 +1,41 @@ +/* ************************************************************************ + + osparc - the simcore frontend + + https://osparc.io + + Copyright: + 2025 IT'IS Foundation, https://itis.swiss + + License: + MIT: https://opensource.org/licenses/MIT + + Authors: + * Odei Maiz (odeimaiz) + +************************************************************************ */ + +qx.Class.define("osparc.study.FunctionPreview", { + extend: qx.ui.core.Widget, + + /** + * @param func {osparc.data.model.Function} Function model + */ + construct: function(func) { + this.base(arguments); + + this._setLayout(new qx.ui.layout.VBox(5)); + + this.__buildPreview(func); + }, + + members: { + __buildPreview: function(func) { + const wbData = func.getWorkbenchData(); + const functionUiData = func.getFunctionUIData(); + const workbenchUIPreview = new osparc.workbench.WorkbenchUIPreview2(wbData, functionUiData["workbench"] || {}); + workbenchUIPreview.setMaxHeight(550); + this._add(workbenchUIPreview); + } + } +}); diff --git a/services/static-webserver/client/source/class/osparc/workbench/WorkbenchUI.js b/services/static-webserver/client/source/class/osparc/workbench/WorkbenchUI.js index 8ca3a4e2c4ec..d3a0671bad6a 100644 --- a/services/static-webserver/client/source/class/osparc/workbench/WorkbenchUI.js +++ b/services/static-webserver/client/source/class/osparc/workbench/WorkbenchUI.js @@ -418,7 +418,7 @@ qx.Class.define("osparc.workbench.WorkbenchUI", { nodeUI.addListener("appear", () => this.__updateNodeUIPos(nodeUI), this); - const isStudyReadOnly = this.getStudy().isReadOnly(); + const isStudyReadOnly = this.isPropertyInitialized("study") ? this.getStudy().isReadOnly() : true; nodeUI.set({ movable: !isStudyReadOnly, selectable: !isStudyReadOnly, diff --git a/services/static-webserver/client/source/class/osparc/workbench/WorkbenchUIPreview2.js b/services/static-webserver/client/source/class/osparc/workbench/WorkbenchUIPreview2.js index 06d21dbdebc9..45ad1037950f 100644 --- a/services/static-webserver/client/source/class/osparc/workbench/WorkbenchUIPreview2.js +++ b/services/static-webserver/client/source/class/osparc/workbench/WorkbenchUIPreview2.js @@ -43,7 +43,9 @@ qx.Class.define("osparc.workbench.WorkbenchUIPreview2", { this.__wbData = wbData || {}; this.__wbDataUi = wbDataUi || {}; - this.__buildPreview(); + setTimeout(() => { + this.__buildPreview(); + }, 200); }, members: { @@ -70,11 +72,10 @@ qx.Class.define("osparc.workbench.WorkbenchUIPreview2", { this.resetSelection(); // create nodes - const nodes = this.__wbData.nodes || {}; - Object.entries(nodes).forEach(([nodeId, nodeData]) => { + Object.entries(this.__wbData).forEach(([nodeId, nodeData]) => { // we assume that the metadata was fetched before const serviceMetadata = osparc.store.Services.getMetadata(nodeData["key"], nodeData["version"]); - const node = osparc.data.model.Node(null, serviceMetadata, nodeId); + const node = new osparc.data.model.Node(null, serviceMetadata, nodeId); const nodeUI = new osparc.workbench.NodeUI(node); nodeUI.setIsMovable(false); this._addNodeUIToWorkbench(nodeUI, node.position); From 06c0cda07e1d4d5ed14beb3a71c5d3e67659d0d1 Mon Sep 17 00:00:00 2001 From: odeimaiz Date: Thu, 17 Jul 2025 10:22:41 +0200 Subject: [PATCH 34/40] to build the preview page we need the underlying template data --- .../class/osparc/dashboard/ResourceDetails.js | 5 +- .../class/osparc/study/FunctionPreview.js | 41 -------- .../osparc/workbench/WorkbenchUIPreview2.js | 93 ------------------- 3 files changed, 3 insertions(+), 136 deletions(-) delete mode 100644 services/static-webserver/client/source/class/osparc/study/FunctionPreview.js delete mode 100644 services/static-webserver/client/source/class/osparc/workbench/WorkbenchUIPreview2.js diff --git a/services/static-webserver/client/source/class/osparc/dashboard/ResourceDetails.js b/services/static-webserver/client/source/class/osparc/dashboard/ResourceDetails.js index b94ffa6bfea9..a4d77c63fbf6 100644 --- a/services/static-webserver/client/source/class/osparc/dashboard/ResourceDetails.js +++ b/services/static-webserver/client/source/class/osparc/dashboard/ResourceDetails.js @@ -394,7 +394,8 @@ qx.Class.define("osparc.dashboard.ResourceDetails", { return; } else if (this.__resourceData["resourceType"] === "function") { this.__addInfoPage(); - this.__addPreviewPage(); + // to build the preview page we need the underlying template data + // this.__addPreviewPage(); this.fireEvent("pagesAdded"); return; } @@ -577,7 +578,7 @@ qx.Class.define("osparc.dashboard.ResourceDetails", { const resourceModel = this.__resourceModel; let preview = null; if (osparc.utils.Resources.isFunction(this.__resourceData)) { - preview = new osparc.study.FunctionPreview(resourceModel); + preview = new osparc.study.StudyPreview(resourceModel); } else { preview = new osparc.study.StudyPreview(resourceModel); } diff --git a/services/static-webserver/client/source/class/osparc/study/FunctionPreview.js b/services/static-webserver/client/source/class/osparc/study/FunctionPreview.js deleted file mode 100644 index 5c9c0aafb1ef..000000000000 --- a/services/static-webserver/client/source/class/osparc/study/FunctionPreview.js +++ /dev/null @@ -1,41 +0,0 @@ -/* ************************************************************************ - - osparc - the simcore frontend - - https://osparc.io - - Copyright: - 2025 IT'IS Foundation, https://itis.swiss - - License: - MIT: https://opensource.org/licenses/MIT - - Authors: - * Odei Maiz (odeimaiz) - -************************************************************************ */ - -qx.Class.define("osparc.study.FunctionPreview", { - extend: qx.ui.core.Widget, - - /** - * @param func {osparc.data.model.Function} Function model - */ - construct: function(func) { - this.base(arguments); - - this._setLayout(new qx.ui.layout.VBox(5)); - - this.__buildPreview(func); - }, - - members: { - __buildPreview: function(func) { - const wbData = func.getWorkbenchData(); - const functionUiData = func.getFunctionUIData(); - const workbenchUIPreview = new osparc.workbench.WorkbenchUIPreview2(wbData, functionUiData["workbench"] || {}); - workbenchUIPreview.setMaxHeight(550); - this._add(workbenchUIPreview); - } - } -}); diff --git a/services/static-webserver/client/source/class/osparc/workbench/WorkbenchUIPreview2.js b/services/static-webserver/client/source/class/osparc/workbench/WorkbenchUIPreview2.js deleted file mode 100644 index 45ad1037950f..000000000000 --- a/services/static-webserver/client/source/class/osparc/workbench/WorkbenchUIPreview2.js +++ /dev/null @@ -1,93 +0,0 @@ -/* ************************************************************************ - - osparc - the simcore frontend - - https://osparc.io - - Copyright: - 2021 IT'IS Foundation, https://itis.swiss - - License: - MIT: https://opensource.org/licenses/MIT - - Authors: - * Odei Maiz (odeimaiz) - -************************************************************************ */ - -/** - * @ignore(SVGElement) - */ - -/** - * *Example* - * - * Here is a little example of how to use the widget. - * - *
- *   let workbenchUIPreview = new osparc.workbench.WorkbenchUIPreview();
- *   this.getRoot().add(workbenchUIPreview);
- * 
- */ - -qx.Class.define("osparc.workbench.WorkbenchUIPreview2", { - extend: osparc.workbench.WorkbenchUI, - - construct: function(wbData, wbDataUi) { - this.base(arguments); - - this.set({ - backgroundColor: "background-main" - }); - - this.__wbData = wbData || {}; - this.__wbDataUi = wbDataUi || {}; - - setTimeout(() => { - this.__buildPreview(); - }, 200); - }, - - members: { - __wbData: null, - __wbDataUi: null, - - // overridden - _addItemsToLayout: function() { - this._addWorkbenchLayer(); - }, - - // overridden - _loadModel: function() { - return; // no model to load, this is a preview - }, - - // overridden - _addEventListeners: function() { - this.addListenerOnce("appear", this._listenToMouseWheel, this); - }, - - __buildPreview: function() { - this._clearAll(); - this.resetSelection(); - - // create nodes - Object.entries(this.__wbData).forEach(([nodeId, nodeData]) => { - // we assume that the metadata was fetched before - const serviceMetadata = osparc.store.Services.getMetadata(nodeData["key"], nodeData["version"]); - const node = new osparc.data.model.Node(null, serviceMetadata, nodeId); - const nodeUI = new osparc.workbench.NodeUI(node); - nodeUI.setIsMovable(false); - this._addNodeUIToWorkbench(nodeUI, node.position); - }); - qx.ui.core.queue.Layout.flush(); - - // create edges - const edges = this.__wbData.edges || {}; - for (const edgeId in edges) { - const edge = edges[edgeId]; - this._createEdge(edge.from, edge.to, edgeId); - } - }, - } -}); From e95dfb9bb9c78888e83756faabf812c2cc482f2c Mon Sep 17 00:00:00 2001 From: odeimaiz Date: Thu, 17 Jul 2025 14:14:12 +0200 Subject: [PATCH 35/40] updateFunctionData --- .../source/class/osparc/dashboard/CardBase.js | 2 ++ .../class/osparc/dashboard/ResourceBrowserBase.js | 8 +++++++- .../osparc/dashboard/ResourceContainerManager.js | 2 ++ .../class/osparc/dashboard/ResourceDetails.js | 7 ++++++- .../source/class/osparc/dashboard/StudyBrowser.js | 14 ++++++++++++++ 5 files changed, 31 insertions(+), 2 deletions(-) diff --git a/services/static-webserver/client/source/class/osparc/dashboard/CardBase.js b/services/static-webserver/client/source/class/osparc/dashboard/CardBase.js index cbcbd2f8b63b..b1938e7b5dc1 100644 --- a/services/static-webserver/client/source/class/osparc/dashboard/CardBase.js +++ b/services/static-webserver/client/source/class/osparc/dashboard/CardBase.js @@ -41,6 +41,7 @@ qx.Class.define("osparc.dashboard.CardBase", { "updateStudy": "qx.event.type.Data", "updateTemplate": "qx.event.type.Data", "updateTutorial": "qx.event.type.Data", + "updateFunction": "qx.event.type.Data", "updateService": "qx.event.type.Data", "updateHypertool": "qx.event.type.Data", "publishTemplate": "qx.event.type.Data", @@ -1051,6 +1052,7 @@ qx.Class.define("osparc.dashboard.CardBase", { "updateStudy", "updateTemplate", "updateTutorial", + "updateFunction", "updateService", "updateHypertool", ].forEach(ev => { diff --git a/services/static-webserver/client/source/class/osparc/dashboard/ResourceBrowserBase.js b/services/static-webserver/client/source/class/osparc/dashboard/ResourceBrowserBase.js index 271981b06bf7..a62dcd6908f0 100644 --- a/services/static-webserver/client/source/class/osparc/dashboard/ResourceBrowserBase.js +++ b/services/static-webserver/client/source/class/osparc/dashboard/ResourceBrowserBase.js @@ -282,6 +282,7 @@ qx.Class.define("osparc.dashboard.ResourceBrowserBase", { resourcesContainer.addListener("updateStudy", e => this._updateStudyData(e.getData())); resourcesContainer.addListener("updateTemplate", e => this._updateTemplateData(e.getData())); resourcesContainer.addListener("updateTutorial", e => this._updateTutorialData(e.getData())); + resourcesContainer.addListener("updateFunction", e => this._updateFunctionData(e.getData())); resourcesContainer.addListener("updateService", e => this._updateServiceData(e.getData())); resourcesContainer.addListener("updateHypertool", e => this._updateHypertoolData(e.getData())); resourcesContainer.addListener("publishTemplate", e => this.fireDataEvent("publishTemplate", e.getData())); @@ -692,7 +693,11 @@ qx.Class.define("osparc.dashboard.ResourceBrowserBase", { } }, - updateTutorialData: function(tutorialData) { + _updateTutorialData: function(tutorialData) { + throw new Error("Abstract method called!"); + }, + + _updateFunctionData: function(functionData) { throw new Error("Abstract method called!"); }, @@ -926,6 +931,7 @@ qx.Class.define("osparc.dashboard.ResourceBrowserBase", { resourceDetails.addListener("updateStudy", e => this._updateStudyData(e.getData())); resourceDetails.addListener("updateTemplate", e => this._updateTemplateData(e.getData())); resourceDetails.addListener("updateTutorial", e => this._updateTutorialData(e.getData())); + resourceDetails.addListener("updateFunction", e => this._updateFunctionData(e.getData())); resourceDetails.addListener("updateService", e => this._updateServiceData(e.getData())); resourceDetails.addListener("updateHypertool", e => this._updateHypertoolData(e.getData())); resourceDetails.addListener("publishTemplate", e => { diff --git a/services/static-webserver/client/source/class/osparc/dashboard/ResourceContainerManager.js b/services/static-webserver/client/source/class/osparc/dashboard/ResourceContainerManager.js index 4695abb0cd16..6af8a37004fc 100644 --- a/services/static-webserver/client/source/class/osparc/dashboard/ResourceContainerManager.js +++ b/services/static-webserver/client/source/class/osparc/dashboard/ResourceContainerManager.js @@ -76,6 +76,7 @@ qx.Class.define("osparc.dashboard.ResourceContainerManager", { "updateStudy": "qx.event.type.Data", "updateTemplate": "qx.event.type.Data", "updateTutorial": "qx.event.type.Data", + "updateFunction": "qx.event.type.Data", "updateService": "qx.event.type.Data", "updateHypertool": "qx.event.type.Data", "publishTemplate": "qx.event.type.Data", @@ -300,6 +301,7 @@ qx.Class.define("osparc.dashboard.ResourceContainerManager", { "updateStudy", "updateTemplate", "updateTutorial", + "updateFunction", "updateService", "updateHypertool", "publishTemplate", diff --git a/services/static-webserver/client/source/class/osparc/dashboard/ResourceDetails.js b/services/static-webserver/client/source/class/osparc/dashboard/ResourceDetails.js index a4d77c63fbf6..98dea7f79c60 100644 --- a/services/static-webserver/client/source/class/osparc/dashboard/ResourceDetails.js +++ b/services/static-webserver/client/source/class/osparc/dashboard/ResourceDetails.js @@ -75,6 +75,8 @@ qx.Class.define("osparc.dashboard.ResourceDetails", { }); break; case "function": { + // use function's underlying template info to fetch services metadata + // osparc.store.Templates.fetchTemplate(resourceData["uuid"]); osparc.store.Services.getStudyServicesMetadata(latestResourceData) .finally(() => { this.__resourceModel = new osparc.data.model.Function(latestResourceData); @@ -105,6 +107,7 @@ qx.Class.define("osparc.dashboard.ResourceDetails", { "updateStudy": "qx.event.type.Data", "updateTemplate": "qx.event.type.Data", "updateTutorial": "qx.event.type.Data", + "updateFunction": "qx.event.type.Data", "updateService": "qx.event.type.Data", "updateHypertool": "qx.event.type.Data", "publishTemplate": "qx.event.type.Data", @@ -440,6 +443,9 @@ qx.Class.define("osparc.dashboard.ResourceDetails", { case "tutorial": this.fireDataEvent("updateTutorial", updatedData); break; + case "function": + this.fireDataEvent("updateFunction", updatedData); + break; case "hypertool": this.fireDataEvent("updateHypertool", updatedData); break; @@ -477,7 +483,6 @@ qx.Class.define("osparc.dashboard.ResourceDetails", { const updatedData = e.getData(); this.__fireUpdateEvent(resourceData, updatedData); }); - infoCard.addListener("openTags", () => this.openTags()); } else { infoCard = new osparc.info.StudyLarge(resourceModel, false); infoCard.addListener("updateStudy", e => { 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 38e55c2e533a..f18139fb52f9 100644 --- a/services/static-webserver/client/source/class/osparc/dashboard/StudyBrowser.js +++ b/services/static-webserver/client/source/class/osparc/dashboard/StudyBrowser.js @@ -1648,6 +1648,20 @@ qx.Class.define("osparc.dashboard.StudyBrowser", { this.base(arguments, templateData); }, + _updateFunctionData: function(functionData) { + functionData["resourceType"] = "function"; + + const index = this._resourcesList.findIndex(study => study["uuid"] === studyData["uuid"]); + if (index === -1) { + // add it in first position, most likely it's a new study + this._resourcesList.unshift(functionData); + } else { + this._resourcesList[index] = functionData; + } + // it will render the studies in the right order + this._reloadCards(); + }, + __removeFromStudyList: function(studyId) { const idx = this._resourcesList.findIndex(study => study["uuid"] === studyId); if (idx > -1) { From 7966ca880e030c2382f233d9fd45b4f614d69ea1 Mon Sep 17 00:00:00 2001 From: odeimaiz Date: Thu, 17 Jul 2025 14:17:17 +0200 Subject: [PATCH 36/40] minor --- .../source/class/osparc/dashboard/ResourceDetails.js | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/services/static-webserver/client/source/class/osparc/dashboard/ResourceDetails.js b/services/static-webserver/client/source/class/osparc/dashboard/ResourceDetails.js index 98dea7f79c60..54a0f3f20fd8 100644 --- a/services/static-webserver/client/source/class/osparc/dashboard/ResourceDetails.js +++ b/services/static-webserver/client/source/class/osparc/dashboard/ResourceDetails.js @@ -581,12 +581,7 @@ qx.Class.define("osparc.dashboard.ResourceDetails", { const lazyLoadContent = () => { const resourceModel = this.__resourceModel; - let preview = null; - if (osparc.utils.Resources.isFunction(this.__resourceData)) { - preview = new osparc.study.StudyPreview(resourceModel); - } else { - preview = new osparc.study.StudyPreview(resourceModel); - } + const preview = new osparc.study.StudyPreview(resourceModel); page.addToContent(preview); this.__widgets.push(preview); } From 5fefda0d17f15e7860e080d301446453e858f412 Mon Sep 17 00:00:00 2001 From: odeimaiz Date: Thu, 17 Jul 2025 14:25:16 +0200 Subject: [PATCH 37/40] last changes --- .../source/class/osparc/dashboard/StudyBrowser.js | 5 +++-- .../client/source/class/osparc/data/Permissions.js | 2 +- .../client/source/class/osparc/data/model/Node.js | 3 --- .../source/class/osparc/data/model/Workbench.js | 7 ------- .../source/class/osparc/study/StudyPreview.js | 14 +++----------- .../client/source/class/osparc/workbench/NodeUI.js | 8 +++----- 6 files changed, 10 insertions(+), 29 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 2349a1b50c16..98edbc5ccf57 100644 --- a/services/static-webserver/client/source/class/osparc/dashboard/StudyBrowser.js +++ b/services/static-webserver/client/source/class/osparc/dashboard/StudyBrowser.js @@ -930,7 +930,6 @@ qx.Class.define("osparc.dashboard.StudyBrowser", { request = osparc.store.Templates.searchTemplatesPaginated(params, options); break; case osparc.dashboard.StudyBrowser.CONTEXT.FUNCTIONS: - // OM: implement this request = osparc.store.Functions.fetchFunctionsPaginated(params, options); } return request; @@ -1179,6 +1178,7 @@ qx.Class.define("osparc.dashboard.StudyBrowser", { searchContext = osparc.dashboard.StudyBrowser.CONTEXT.SEARCH_PUBLIC_TEMPLATES; break; case osparc.dashboard.StudyBrowser.CONTEXT.FUNCTIONS: + // functions are not searchable yet searchContext = null; break; default: @@ -1286,6 +1286,7 @@ qx.Class.define("osparc.dashboard.StudyBrowser", { this._searchBarFilter.resetFilters(); this._searchBarFilter.getChildControl("text-field").setPlaceholder("Search in Functions"); this._searchBarFilter.setEnabled(false); + // functions can't be sorted yet this._toolbar.exclude(); this._loadingResourcesBtn.setFetching(false); this.invalidateFunctions(); @@ -1651,7 +1652,7 @@ qx.Class.define("osparc.dashboard.StudyBrowser", { _updateFunctionData: function(functionData) { functionData["resourceType"] = "function"; - const index = this._resourcesList.findIndex(study => study["uuid"] === studyData["uuid"]); + const index = this._resourcesList.findIndex(func => func["uuid"] === functionData["uuid"]); if (index === -1) { // add it in first position, most likely it's a new study this._resourcesList.unshift(functionData); diff --git a/services/static-webserver/client/source/class/osparc/data/Permissions.js b/services/static-webserver/client/source/class/osparc/data/Permissions.js index 22008182f348..477b08ffd662 100644 --- a/services/static-webserver/client/source/class/osparc/data/Permissions.js +++ b/services/static-webserver/client/source/class/osparc/data/Permissions.js @@ -312,7 +312,7 @@ qx.Class.define("osparc.data.Permissions", { // This needs to be provided by the backend if (action === "readFunctions") { - return true; + return osparc.utils.Utils.isDevelopmentPlatform(); } if ( 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 a5f8b2f25484..048f69ebfc0c 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 @@ -577,9 +577,6 @@ qx.Class.define("osparc.data.model.Node", { */ __addSettings: function(inputs) { const form = this.__settingsForm = new osparc.form.Auto(inputs); - if (!this.getStudy()) { - return; - } const propsForm = new osparc.form.renderer.PropForm(form, this, this.getStudy()); this.setPropsForm(propsForm); propsForm.addListener("linkFieldModified", e => { 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 a396677e4836..3e0d3eea769a 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 @@ -68,13 +68,6 @@ qx.Class.define("osparc.data.model.Workbench", { event: "changeStudy" }, - function: { - check: "osparc.data.model.Function", - init: null, - nullable: true, - event: "changeFunction" - }, - deserialized: { check: "Boolean", init: false, diff --git a/services/static-webserver/client/source/class/osparc/study/StudyPreview.js b/services/static-webserver/client/source/class/osparc/study/StudyPreview.js index 74161b3bc155..209e35d13f6b 100644 --- a/services/static-webserver/client/source/class/osparc/study/StudyPreview.js +++ b/services/static-webserver/client/source/class/osparc/study/StudyPreview.js @@ -26,12 +26,8 @@ qx.Class.define("osparc.study.StudyPreview", { this._setLayout(new qx.ui.layout.VBox(5)); - if (study instanceof osparc.data.model.Study) { - const uiMode = study.getUi().getMode(); - if (["workbench", "pipeline"].includes(uiMode)) { - this.__buildPreview(study); - } - } else if (study instanceof osparc.data.model.Function) { + const uiMode = study.getUi().getMode(); + if (["workbench", "pipeline"].includes(uiMode)) { this.__buildPreview(study); } }, @@ -40,11 +36,7 @@ qx.Class.define("osparc.study.StudyPreview", { __buildPreview: function(study) { const workbenchReady = () => { const workbenchUIPreview = new osparc.workbench.WorkbenchUIPreview(); - if (study instanceof osparc.data.model.Study) { - workbenchUIPreview.setStudy(study); - } else if (study instanceof osparc.data.model.Function) { - workbenchUIPreview.setFunction(study); - } + workbenchUIPreview.setStudy(study); workbenchUIPreview.loadModel(study.getWorkbench()); workbenchUIPreview.setMaxHeight(550); this._add(workbenchUIPreview); diff --git a/services/static-webserver/client/source/class/osparc/workbench/NodeUI.js b/services/static-webserver/client/source/class/osparc/workbench/NodeUI.js index 41bf33df07e3..80fb1522b45e 100644 --- a/services/static-webserver/client/source/class/osparc/workbench/NodeUI.js +++ b/services/static-webserver/client/source/class/osparc/workbench/NodeUI.js @@ -298,11 +298,9 @@ qx.Class.define("osparc.workbench.NodeUI", { node.addListener("changeMarker", () => updateMarker()); updateMarker(); - if (node.getStudy()) { - node.getStudy().bind("pipelineRunning", this._deleteBtn, "enabled", { - converter: running => !running - }); - } + node.getStudy().bind("pipelineRunning", this._deleteBtn, "enabled", { + converter: running => !running + }); const evaluateLifeCycleIcon = () => { const deprecatedIcon = this.getChildControl("deprecated-icon"); From 969fb4f56b202070ff61d56a63d0c04aded78cc8 Mon Sep 17 00:00:00 2001 From: odeimaiz Date: Thu, 17 Jul 2025 14:42:22 +0200 Subject: [PATCH 38/40] robot is right --- .../class/osparc/data/model/Function.js | 30 ++++++++++++++++++- .../source/class/osparc/info/FunctionLarge.js | 11 ++++--- .../source/class/osparc/store/Functions.js | 4 +-- 3 files changed, 36 insertions(+), 9 deletions(-) diff --git a/services/static-webserver/client/source/class/osparc/data/model/Function.js b/services/static-webserver/client/source/class/osparc/data/model/Function.js index eeb59b7e40fa..58f89f7fc998 100644 --- a/services/static-webserver/client/source/class/osparc/data/model/Function.js +++ b/services/static-webserver/client/source/class/osparc/data/model/Function.js @@ -41,7 +41,7 @@ qx.Class.define("osparc.data.model.Function", { lastChangeDate: functionData.lastChangeDate ? new Date(functionData.lastChangeDate) : this.getLastChangeDate(), thumbnail: functionData.thumbnail || this.getThumbnail(), workbenchData: functionData.workbench || this.getWorkbenchData(), - functionUIData: functionData.ui || this.functionUIData(), + functionUIData: functionData.ui || this.getFunctionUIData(), }); }, @@ -148,5 +148,33 @@ qx.Class.define("osparc.data.model.Function", { } return canWrite; }, + }, + + members: { + serialize: function(clean = true) { + let jsonObject = {}; + const propertyKeys = this.self().getProperties(); + propertyKeys.forEach(key => { + jsonObject[key] = this.get(key); + }); + return jsonObject; + }, + + patchFunction: function(functionChanges) { + return osparc.store.Study.getInstance().patchStudy(this.getUuid(), functionChanges) + .then(() => { + Object.keys(functionChanges).forEach(fieldKey => { + const upKey = qx.lang.String.firstUp(fieldKey); + const setter = "set" + upKey; + this[setter](functionChanges[fieldKey]); + }) + this.set({ + lastChangeDate: new Date() + }); + const functionData = this.serialize(); + resolve(functionData); + }) + .catch(err => reject(err)); + }, } }); diff --git a/services/static-webserver/client/source/class/osparc/info/FunctionLarge.js b/services/static-webserver/client/source/class/osparc/info/FunctionLarge.js index 0b0ef9882699..4e90c9642ee7 100644 --- a/services/static-webserver/client/source/class/osparc/info/FunctionLarge.js +++ b/services/static-webserver/client/source/class/osparc/info/FunctionLarge.js @@ -168,7 +168,7 @@ qx.Class.define("osparc.info.FunctionLarge", { __openDescriptionEditor: function() { const title = this.tr("Edit Description"); - const textEditor = new osparc.editor.MarkdownEditor(this.getStudy().getDescription()); + const textEditor = new osparc.editor.MarkdownEditor(this.getFunction().getDescription()); textEditor.setMaxHeight(570); const win = osparc.ui.window.Window.popUpInWindow(textEditor, title, 400, 300); textEditor.addListener("textChanged", e => { @@ -182,11 +182,10 @@ qx.Class.define("osparc.info.FunctionLarge", { }, __patchFunction: function(fieldKey, value) { - this.getStudy().patchStudy({[fieldKey]: value}) - .then(studyData => { - studyData["resourceType"] = this.getStudy().getTemplateType() ? "template" : "study"; - this.fireDataEvent("updateStudy", studyData); - qx.event.message.Bus.getInstance().dispatchByName("updateStudy", studyData); + this.getFunction().patchFunction({[fieldKey]: value}) + .then(functionData => { + this.fireDataEvent("updateFunction", functionData); + qx.event.message.Bus.getInstance().dispatchByName("updateFunction", functionData); }) .catch(err => { const msg = this.tr("An issue occurred while updating the information."); diff --git a/services/static-webserver/client/source/class/osparc/store/Functions.js b/services/static-webserver/client/source/class/osparc/store/Functions.js index 13c9c2d49806..487e844c9113 100644 --- a/services/static-webserver/client/source/class/osparc/store/Functions.js +++ b/services/static-webserver/client/source/class/osparc/store/Functions.js @@ -84,7 +84,7 @@ qx.Class.define("osparc.store.Functions", { }, registerFunction: function(templateData, name, description, defaultInputs, exposedInputs, exposedOutputs) { - const functionData = this.self().__createFunctionData(templateData, name, description, defaultInputs, exposedInputs, exposedOutputs); + const functionData = this.__createFunctionData(templateData, name, description, defaultInputs, exposedInputs, exposedOutputs); const params = { data: functionData, }; @@ -122,7 +122,7 @@ qx.Class.define("osparc.store.Functions", { "functionId": functionId } }; - return osparc.data.Resources.fetch("functions", "getOne", params, options) + return osparc.data.Resources.fetch("functions", "getOne", params) .then(func => { func["resourceType"] = "function"; return func; From 2d58bb39d0d9a614aa278990d8601340497708e0 Mon Sep 17 00:00:00 2001 From: odeimaiz Date: Thu, 17 Jul 2025 16:20:35 +0200 Subject: [PATCH 39/40] pb-function --- .../client/source/class/osparc/theme/Appearance.js | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/services/static-webserver/client/source/class/osparc/theme/Appearance.js b/services/static-webserver/client/source/class/osparc/theme/Appearance.js index 2dcb2db7d46f..906313beff3a 100644 --- a/services/static-webserver/client/source/class/osparc/theme/Appearance.js +++ b/services/static-webserver/client/source/class/osparc/theme/Appearance.js @@ -129,6 +129,10 @@ qx.Theme.define("osparc.theme.Appearance", { } }, + "pb-function": { + include: "pb-template", + }, + "pb-hypertool": { include: "pb-template", }, From 287b3a1a29ae00d43622a4bd288d2e1e2cb279cb Mon Sep 17 00:00:00 2001 From: odeimaiz Date: Thu, 17 Jul 2025 16:32:15 +0200 Subject: [PATCH 40/40] list view for functions --- .../class/osparc/dashboard/StudyBrowser.js | 25 +++++++++++-------- 1 file changed, 14 insertions(+), 11 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 98edbc5ccf57..3135e4651951 100644 --- a/services/static-webserver/client/source/class/osparc/dashboard/StudyBrowser.js +++ b/services/static-webserver/client/source/class/osparc/dashboard/StudyBrowser.js @@ -106,8 +106,9 @@ qx.Class.define("osparc.dashboard.StudyBrowser", { }, members: { - __dontShowTutorial: null, + __dontQuickStart: null, __header: null, + __sortByButton: null, __workspacesList: null, __foldersList: null, __loadingFolders: null, @@ -349,8 +350,8 @@ qx.Class.define("osparc.dashboard.StudyBrowser", { // Show Quick Start if there are no studies in the root folder of the personal workspace const quickStartInfo = osparc.product.quickStart.Utils.getQuickStart(); if (quickStartInfo) { - const dontShow = osparc.utils.Utils.localCache.getLocalStorageItem(quickStartInfo.localStorageStr); - if (dontShow === "true" || this.__dontShowTutorial) { + const dontShowQuickStart = osparc.utils.Utils.localCache.getLocalStorageItem(quickStartInfo.localStorageStr); + if (dontShowQuickStart === "true" || this.__dontQuickStart) { return; } const nStudies = "_meta" in resp ? resp["_meta"]["total"] : 0; @@ -364,7 +365,7 @@ qx.Class.define("osparc.dashboard.StudyBrowser", { quickStartWindow.center(); quickStartWindow.open(); quickStartWindow.addListener("close", () => { - this.__dontShowTutorial = true; + this.__dontQuickStart = true; }, this); } } @@ -1233,13 +1234,17 @@ qx.Class.define("osparc.dashboard.StudyBrowser", { this._resourcesList = []; this._resourcesContainer.setResourcesToList(this._resourcesList); this._resourcesContainer.reloadCards("studies"); + // functions will disable it this._searchBarFilter.setEnabled(true); + // workspaces will exclude it + this._toolbar.show(); + // functions will exclude it + this.__sortByButton.show(); switch (this.getCurrentContext()) { case osparc.dashboard.StudyBrowser.CONTEXT.PROJECTS: this._searchBarFilter.resetFilters(); this._searchBarFilter.getChildControl("text-field").setPlaceholder("Search in My Projects"); - this._toolbar.show(); this.__reloadFolders(); this._loadingResourcesBtn.setFetching(false); this.invalidateStudies(); @@ -1248,12 +1253,12 @@ qx.Class.define("osparc.dashboard.StudyBrowser", { case osparc.dashboard.StudyBrowser.CONTEXT.WORKSPACES: this._searchBarFilter.resetFilters(); this._searchBarFilter.getChildControl("text-field").setPlaceholder("Search in My Projects"); + // workspaces can't be sorted and don't support list view this._toolbar.exclude(); this.__reloadWorkspaces(); break; case osparc.dashboard.StudyBrowser.CONTEXT.SEARCH_PROJECTS: this._searchBarFilter.getChildControl("text-field").setPlaceholder("Search in My Projects"); - this._toolbar.show(); this.__reloadWorkspaces(); this.__reloadFolders(); this._loadingResourcesBtn.setFetching(false); @@ -1266,7 +1271,6 @@ qx.Class.define("osparc.dashboard.StudyBrowser", { this._searchBarFilter.resetFilters(); } this._searchBarFilter.getChildControl("text-field").setPlaceholder("Search in Templates"); - this._toolbar.show(); this._loadingResourcesBtn.setFetching(false); this.invalidateStudies(); this.__reloadStudies(); @@ -1277,7 +1281,6 @@ qx.Class.define("osparc.dashboard.StudyBrowser", { this._searchBarFilter.resetFilters(); } this._searchBarFilter.getChildControl("text-field").setPlaceholder("Search in Public Projects"); - this._toolbar.show(); this._loadingResourcesBtn.setFetching(false); this.invalidateStudies(); this.__reloadStudies(); @@ -1285,9 +1288,10 @@ qx.Class.define("osparc.dashboard.StudyBrowser", { case osparc.dashboard.StudyBrowser.CONTEXT.FUNCTIONS: this._searchBarFilter.resetFilters(); this._searchBarFilter.getChildControl("text-field").setPlaceholder("Search in Functions"); + // functions can't be searched yet this._searchBarFilter.setEnabled(false); // functions can't be sorted yet - this._toolbar.exclude(); + this.__sortByButton.exclude(); this._loadingResourcesBtn.setFetching(false); this.invalidateFunctions(); this.__reloadStudies(); @@ -1295,7 +1299,6 @@ qx.Class.define("osparc.dashboard.StudyBrowser", { case osparc.dashboard.StudyBrowser.CONTEXT.TRASH: this._searchBarFilter.resetFilters(); this._searchBarFilter.getChildControl("text-field").setPlaceholder("Search in My Projects"); - this._toolbar.show(); this.__reloadWorkspaces(); this.__reloadFolders(); this._loadingResourcesBtn.setFetching(false); @@ -1365,7 +1368,7 @@ qx.Class.define("osparc.dashboard.StudyBrowser", { }, __addSortByButton: function() { - const sortByButton = new osparc.dashboard.SortedByMenuButton(); + const sortByButton = this.__sortByButton = new osparc.dashboard.SortedByMenuButton(); sortByButton.set({ appearance: "form-button-outlined" });