Skip to content

Commit eeb6a1c

Browse files
authored
✨ [Frontend] Template type: Hypertools (#7531)
1 parent 0137533 commit eeb6a1c

File tree

12 files changed

+153
-26
lines changed

12 files changed

+153
-26
lines changed

packages/models-library/src/models_library/api_schemas_webserver/projects_ui.py

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
"""
2-
Models Front-end UI
2+
Models Front-end UI
33
"""
44

55
from typing import Annotated, Literal, NotRequired
@@ -20,6 +20,7 @@
2020

2121
from ..projects_nodes_io import NodeID, NodeIDStr
2222
from ..utils.common_validators import empty_str_to_none_pre_validator
23+
from ._base import OutputSchema
2324
from .projects_nodes_ui import MarkerUI, PositionUI
2425

2526

@@ -79,14 +80,15 @@ def _update_json_schema_extra(schema: JsonDict) -> None:
7980
)
8081

8182

82-
class StudyUI(BaseModel):
83+
class StudyUI(OutputSchema):
8384
# Model fully controlled by the UI and stored under `projects.ui`
8485
icon: HttpUrl | None = None
8586

8687
workbench: dict[NodeIDStr, WorkbenchUI] | None = None
8788
slideshow: dict[NodeIDStr, SlideshowUI] | None = None
88-
current_node_id: Annotated[NodeID | None, Field(alias="currentNodeId")] = None
89+
current_node_id: NodeID | None = None
8990
annotations: dict[NodeIDStr, AnnotationUI] | None = None
91+
template_type: Literal["hypertool"] | None = None
9092

9193
_empty_is_none = field_validator("*", mode="before")(
9294
empty_str_to_none_pre_validator
@@ -169,6 +171,7 @@ def _update_json_schema_extra(schema: JsonDict) -> None:
169171
},
170172
},
171173
"current_node_id": "4b3345e5-861f-47b0-8b52-a4508449be79",
174+
"template_type": "hypertool",
172175
},
173176
]
174177
}

services/static-webserver/client/source/class/osparc/dashboard/Dashboard.js

Lines changed: 28 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,7 @@ qx.Class.define("osparc.dashboard.Dashboard", {
7979
members: {
8080
__studyBrowser: null,
8181
__templateBrowser: null,
82+
__hypertoolBrowser: null,
8283
__serviceBrowser: null,
8384
__dataBrowser: null,
8485

@@ -90,6 +91,10 @@ qx.Class.define("osparc.dashboard.Dashboard", {
9091
return this.__templateBrowser;
9192
},
9293

94+
getHypertoolBrowser: function() {
95+
return this.__hypertoolBrowser;
96+
},
97+
9398
getServiceBrowser: function() {
9499
return this.__serviceBrowser;
95100
},
@@ -118,6 +123,14 @@ qx.Class.define("osparc.dashboard.Dashboard", {
118123
icon: "@FontAwesome5Solid/copy/"+tabIconSize,
119124
buildLayout: this.__createTemplateBrowser
120125
});
126+
tabs.push({
127+
id: "hypertoolsTab",
128+
buttonId: "hypertoolsTabBtn",
129+
label: this.tr("HYPERTOOLS"),
130+
icon: "@FontAwesome5Solid/copy/"+tabIconSize,
131+
initVisibility: "excluded",
132+
buildLayout: this.__createHypertoolsBrowser
133+
});
121134
}
122135
if (permissions.canDo("dashboard.services.read")) {
123136
tabs.push({
@@ -128,16 +141,17 @@ qx.Class.define("osparc.dashboard.Dashboard", {
128141
buildLayout: this.__createServiceBrowser
129142
});
130143
}
131-
if (permissions.canDo("dashboard.data.read") && osparc.product.Utils.isProduct("osparc")) {
144+
if (permissions.canDo("dashboard.data.read")) {
132145
tabs.push({
133146
id: "dataTab",
134147
buttonId: "dataTabBtn",
135148
label: this.tr("DATA"),
136149
icon: "@FontAwesome5Solid/folder/"+tabIconSize,
150+
initVisibility: osparc.product.Utils.isProduct("osparc") ? "visible" : "excluded",
137151
buildLayout: this.__createDataBrowser
138152
});
139153
}
140-
tabs.forEach(({id, buttonId, label, icon, buildLayout}) => {
154+
tabs.forEach(({id, buttonId, label, icon, initVisibility, buildLayout}) => {
141155
const tabPage = new qx.ui.tabview.Page(label, icon).set({
142156
appearance: "dashboard-page"
143157
});
@@ -146,6 +160,7 @@ qx.Class.define("osparc.dashboard.Dashboard", {
146160
tabButton.set({
147161
minWidth: 50,
148162
maxHeight: 36,
163+
visibility: initVisibility ? initVisibility : "visible",
149164
});
150165
tabButton.ttt = label;
151166
tabButton.getChildControl("label").set({
@@ -171,6 +186,10 @@ qx.Class.define("osparc.dashboard.Dashboard", {
171186
this.setSelection([tabFound]);
172187
}
173188
}, this);
189+
viewLayout.addListener("showTab", e => {
190+
const showTab = e.getData();
191+
tabButton.setVisibility(showTab ? "visible" : "excluded");
192+
})
174193
const scrollerMainView = new qx.ui.container.Scroll();
175194
scrollerMainView.add(viewLayout);
176195
tabPage.add(scrollerMainView);
@@ -187,6 +206,7 @@ qx.Class.define("osparc.dashboard.Dashboard", {
187206
[
188207
this.__studyBrowser,
189208
this.__templateBrowser,
209+
this.__hypertoolBrowser,
190210
this.__serviceBrowser,
191211
this.__dataBrowser
192212
].forEach(resourceBrowser => {
@@ -208,6 +228,12 @@ qx.Class.define("osparc.dashboard.Dashboard", {
208228
return templatesView;
209229
},
210230

231+
__createHypertoolsBrowser: function() {
232+
const templateType = osparc.data.model.StudyUI.HYPERTOOL_TYPE;
233+
const hypertoolsView = this.__hypertoolBrowser = new osparc.dashboard.TemplateBrowser(templateType);
234+
return hypertoolsView;
235+
},
236+
211237
__createServiceBrowser: function() {
212238
const servicesView = this.__serviceBrowser = new osparc.dashboard.ServiceBrowser();
213239
return servicesView;

services/static-webserver/client/source/class/osparc/dashboard/ResourceBrowserBase.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,8 @@ qx.Class.define("osparc.dashboard.ResourceBrowserBase", {
8888

8989
events: {
9090
"changeTab": "qx.event.type.Data",
91-
"publishTemplate": "qx.event.type.Data"
91+
"showTab": "qx.event.type.Data",
92+
"publishTemplate": "qx.event.type.Data",
9293
},
9394

9495
statics: {

services/static-webserver/client/source/class/osparc/dashboard/ResourceContainerManager.js

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -315,7 +315,16 @@ qx.Class.define("osparc.dashboard.ResourceContainerManager", {
315315
__rebuildLayout: function(resourceType) {
316316
this.__cleanAll();
317317
if (this.getGroupBy()) {
318-
const noGroupContainer = this.__createGroupContainer("no-group", "No Group", "transparent");
318+
let groupTitle = "No Group";
319+
switch (this.getGroupBy()) {
320+
case "tags":
321+
groupTitle = "Not Tagged";
322+
break;
323+
case "shared":
324+
groupTitle = "Not Shared";
325+
break;
326+
}
327+
const noGroupContainer = this.__createGroupContainer("no-group", groupTitle, "transparent");
319328
this.__groupedContainers.add(noGroupContainer);
320329
this._add(this.__groupedContainers);
321330
} else {

services/static-webserver/client/source/class/osparc/dashboard/TemplateBrowser.js

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,12 +18,15 @@
1818
qx.Class.define("osparc.dashboard.TemplateBrowser", {
1919
extend: osparc.dashboard.ResourceBrowserBase,
2020

21-
construct: function() {
21+
construct: function(templateType = null) {
2222
this._resourceType = "template";
23+
this.__templateType = templateType;
24+
2325
this.base(arguments);
2426
},
2527

2628
members: {
29+
__templateType: null,
2730
__updateAllButton: null,
2831

2932
// overridden
@@ -97,7 +100,8 @@ qx.Class.define("osparc.dashboard.TemplateBrowser", {
97100

98101
__setResourcesToList: function(templatesList) {
99102
templatesList.forEach(template => template["resourceType"] = "template");
100-
this._resourcesList = templatesList;
103+
this._resourcesList = templatesList.filter(template => osparc.study.Utils.extractTemplateType(template) === this.__templateType);
104+
this.fireDataEvent("showTab", Boolean(this._resourcesList.length));
101105
this._reloadCards();
102106
},
103107

services/static-webserver/client/source/class/osparc/data/model/StudyUI.js

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,8 @@ qx.Class.define("osparc.data.model.StudyUI", {
3333
slideshow: new osparc.data.model.Slideshow(studyDataUI && studyDataUI.slideshow ? studyDataUI.slideshow : this.getSlideshow()),
3434
currentNodeId: studyDataUI && studyDataUI.currentNodeId ? studyDataUI.currentNodeId : this.initCurrentNodeId(),
3535
mode: studyDataUI && studyDataUI.mode ? studyDataUI.mode : this.initMode(),
36-
annotations: {}
36+
annotations: {},
37+
templateType: studyDataUI && studyDataUI.templateType ? studyDataUI.templateType : null,
3738
});
3839

3940
if ("annotations" in studyDataUI) {
@@ -79,7 +80,18 @@ qx.Class.define("osparc.data.model.StudyUI", {
7980
check: "Object",
8081
init: {},
8182
nullable: true
82-
}
83+
},
84+
85+
templateType: {
86+
check: [null, "hypertool"],
87+
init: null,
88+
nullable: true,
89+
event: "changeTemplateType",
90+
},
91+
},
92+
93+
statics: {
94+
HYPERTOOL_TYPE: "hypertool",
8395
},
8496

8597
members: {

services/static-webserver/client/source/class/osparc/desktop/MainPage.js

Lines changed: 25 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -225,12 +225,17 @@ qx.Class.define("osparc.desktop.MainPage", {
225225
const text = this.tr("Started template creation and added to the background tasks");
226226
osparc.FlashMessenger.logAs(text, "INFO");
227227

228+
const studyId = data["studyData"].uuid;
229+
const studyName = data["studyData"].name;
230+
const copyData = data["copyData"];
231+
const templateAccessRights = data["accessRights"];
232+
const templateType = data["templateType"];
233+
228234
const params = {
229235
url: {
230-
"study_id": data["studyData"].uuid,
231-
"copy_data": data["copyData"]
236+
"study_id": studyId,
237+
"copy_data": copyData
232238
},
233-
data: data["studyData"]
234239
};
235240
const options = {
236241
pollTask: true
@@ -240,12 +245,27 @@ qx.Class.define("osparc.desktop.MainPage", {
240245
pollTasks.createPollingTask(fetchPromise)
241246
.then(task => {
242247
const templateBrowser = this.__dashboard.getTemplateBrowser();
248+
const hypertoolBrowser = this.__dashboard.getHypertoolBrowser();
243249
if (templateBrowser) {
244-
templateBrowser.taskToTemplateReceived(task, data["studyData"].name);
250+
templateBrowser.taskToTemplateReceived(task, studyName);
245251
}
246252
task.addListener("resultReceived", e => {
247253
const templateData = e.getData();
248-
osparc.store.Study.addCollaborators(templateData, data["accessRights"]);
254+
// these operations need to be done after template creation
255+
osparc.store.Study.addCollaborators(templateData, templateAccessRights);
256+
if (templateType) {
257+
const studyUI = osparc.utils.Utils.deepCloneObject(templateData["ui"]);
258+
studyUI["templateType"] = templateType;
259+
osparc.store.Study.patchStudyData(templateData, "ui", studyUI)
260+
.then(() => {
261+
if (templateBrowser) {
262+
templateBrowser.reloadResources();
263+
}
264+
if (hypertoolBrowser) {
265+
hypertoolBrowser.reloadResources();
266+
}
267+
});
268+
}
249269
});
250270
})
251271
.catch(errMsg => {

services/static-webserver/client/source/class/osparc/share/PublishTemplate.js renamed to services/static-webserver/client/source/class/osparc/share/ShareTemplateWith.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@
2222
* - Product everyone
2323
*/
2424

25-
qx.Class.define("osparc.share.PublishTemplate", {
25+
qx.Class.define("osparc.share.ShareTemplateWith", {
2626
extend: qx.ui.core.Widget,
2727

2828
/**

services/static-webserver/client/source/class/osparc/study/SaveAsTemplate.js

Lines changed: 35 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -43,17 +43,38 @@ qx.Class.define("osparc.study.SaveAsTemplate", {
4343

4444
members: {
4545
__studyDataClone: null,
46-
__shareWith: null,
46+
__form: null,
4747
__publishTemplateBtn: null,
48-
__copyWData: null,
4948

5049
__buildLayout: function() {
51-
const publishWithData = this.__copyWData = new qx.ui.form.CheckBox(this.tr("Publish with data")).set({
52-
value: true
53-
});
54-
this._add(publishWithData);
50+
const form = this.__form = new qx.ui.form.Form();
51+
this._add(new qx.ui.form.renderer.Single(form));
5552

56-
const shareWith = this.__shareWith = new osparc.share.PublishTemplate(this.__studyDataClone);
53+
const publishWithData = new qx.ui.form.CheckBox().set({
54+
value: true,
55+
iconPosition: "right",
56+
});
57+
form.add(publishWithData, this.tr("Publish with data"), null, "publishWithData");
58+
59+
if (osparc.utils.DisabledPlugins.isHypertoolsEnabled()) {
60+
const templateTypeSB = new qx.ui.form.SelectBox().set({
61+
allowGrowX: false,
62+
});
63+
const templateTypes = [{
64+
label: "Tutorial",
65+
id: null,
66+
}, {
67+
label: "Hypertool",
68+
id: osparc.data.model.StudyUI.HYPERTOOL_TYPE,
69+
}]
70+
templateTypes.forEach(tempType => {
71+
const tItem = new qx.ui.form.ListItem(tempType.label, null, tempType.id);
72+
templateTypeSB.add(tItem);
73+
});
74+
form.add(templateTypeSB, this.tr("Template Type"), null, "templateType");
75+
}
76+
77+
const shareWith = this.__shareWith = new osparc.share.ShareTemplateWith(this.__studyDataClone);
5778
this._add(shareWith);
5879

5980
const publishTemplateBtn = this.__publishTemplateBtn = new qx.ui.form.Button().set({
@@ -67,6 +88,10 @@ qx.Class.define("osparc.study.SaveAsTemplate", {
6788
},
6889

6990
__publishTemplate: function() {
91+
const publishWithDataCB = this.__form.getItem("publishWithData");
92+
const templateTypeSB = this.__form.getItem("templateType");
93+
const templateType = templateTypeSB ? templateTypeSB.getSelection()[0].getModel() : null;
94+
7095
const readAccessRole = osparc.data.Roles.STUDY["read"];
7196
// AccessRights will be POSTed after the template is created.
7297
// No need to add myself, backend will automatically do it
@@ -79,8 +104,9 @@ qx.Class.define("osparc.study.SaveAsTemplate", {
79104

80105
this.fireDataEvent("publishTemplate", {
81106
"studyData": this.__studyDataClone,
82-
"copyData": this.__copyWData.getValue(),
83-
"accessRights": accessRights
107+
"copyData": publishWithDataCB.getValue(),
108+
"accessRights": accessRights,
109+
"templateType": templateType,
84110
});
85111
},
86112

services/static-webserver/client/source/class/osparc/study/Utils.js

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -205,6 +205,13 @@ qx.Class.define("osparc.study.Utils", {
205205
return pollTasks.createPollingTask(fetchPromise)
206206
},
207207

208+
extractTemplateType: function(templateData) {
209+
if (templateData && templateData["ui"] && templateData["ui"]["templateType"]) {
210+
return templateData["ui"]["templateType"];
211+
}
212+
return null;
213+
},
214+
208215
isAnyLinkedNodeMissing: function(studyData) {
209216
const existingNodeIds = Object.keys(studyData["workbench"]);
210217
const linkedNodeIds = osparc.data.model.Workbench.getLinkedNodeIds(studyData["workbench"]);

services/static-webserver/client/source/class/osparc/utils/DisabledPlugins.js

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,13 @@ qx.Class.define("osparc.utils.DisabledPlugins", {
6767
return false;
6868
},
6969

70+
isHypertoolsEnabled: function() {
71+
if (osparc.store.StaticInfo.getInstance().isDevFeaturesEnabled() && osparc.product.Utils.isS4LProduct()) {
72+
return true;
73+
}
74+
return false;
75+
},
76+
7077
__isPluginDisabled: function(key) {
7178
const statics = osparc.store.Store.getInstance().get("statics");
7279
if (statics) {

0 commit comments

Comments
 (0)