Skip to content

Commit 981f27a

Browse files
authoredMar 18, 2025··
🎨 [Frontend] Study (and Node) :size (#7371)
1 parent 864190c commit 981f27a

File tree

11 files changed

+200
-32
lines changed

11 files changed

+200
-32
lines changed
 

‎services/static-webserver/client/source/class/osparc/FlashMessenger.js

+9-3
Original file line numberDiff line numberDiff line change
@@ -57,13 +57,19 @@ qx.Class.define("osparc.FlashMessenger", {
5757
MAX_DISPLAYED: 3,
5858

5959
extractMessage: function(input, defaultMessage = "") {
60+
const isValidString = val => {
61+
return (
62+
typeof val === "string" ||
63+
(osparc.utils.Utils.isObject(val) && ("basename" in val) && (val.basename === "LocalizedString"))
64+
);
65+
}
6066
if (input) {
61-
if (typeof input === "string") {
67+
if (isValidString(input)) {
6268
return input;
6369
} else if (osparc.utils.Utils.isObject(input) && "message" in input) {
64-
if (typeof input["message"] === "string") {
70+
if (isValidString(input["message"])) {
6571
return input["message"];
66-
} else if (osparc.utils.Utils.isObject(input["message"]) && "message" in input["message"] && typeof input["message"]["message"] === "string") {
72+
} else if (osparc.utils.Utils.isObject(input["message"]) && "message" in input["message"] && isValidString(input["message"]["message"])) {
6773
return input["message"]["message"];
6874
}
6975
}

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

+1-1
Original file line numberDiff line numberDiff line change
@@ -199,7 +199,7 @@ qx.Class.define("osparc.dashboard.ResourceDetails", {
199199
const updatedStudyData = values[0];
200200
const studyServices = values[1];
201201
openButton.setFetching(false);
202-
const updatableServices = osparc.metadata.ServicesInStudyUpdate.updatableNodeIds(updatedStudyData.workbench, studyServices["services"]);
202+
const updatableServices = osparc.study.Utils.updatableNodeIds(updatedStudyData.workbench, studyServices["services"]);
203203
if (updatableServices.length && osparc.data.model.Study.canIWrite(updatedStudyData["accessRights"])) {
204204
this.__confirmUpdate();
205205
} else {

‎services/static-webserver/client/source/class/osparc/data/Resources.js

+24
Original file line numberDiff line numberDiff line change
@@ -1212,6 +1212,10 @@ qx.Class.define("osparc.data.Resources", {
12121212
method: "GET",
12131213
url: statics.API + "/storage/locations/{locationId}/paths?file_filter={path}&size=1000"
12141214
},
1215+
requestSize: {
1216+
method: "POST",
1217+
url: statics.API + "/storage/locations/0/paths/{pathId}:size"
1218+
},
12151219
}
12161220
},
12171221
/*
@@ -1230,6 +1234,26 @@ qx.Class.define("osparc.data.Resources", {
12301234
}
12311235
}
12321236
},
1237+
/*
1238+
* STORAGE ASYNC
1239+
*/
1240+
"storageAsyncJobs": {
1241+
useCache: false,
1242+
endpoints: {
1243+
jobStatus: {
1244+
method: "GET",
1245+
url: statics.API + "/storage/async-jobs/{jobId}/status"
1246+
},
1247+
jobResult: {
1248+
method: "GET",
1249+
url: statics.API + "/storage/async-jobs/{jobId}/result"
1250+
},
1251+
abortJob: {
1252+
method: "POST",
1253+
url: statics.API + "/storage/async-jobs/{jobId}/abort"
1254+
},
1255+
}
1256+
},
12331257
/*
12341258
* ACTIVITY
12351259
*/

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

+1-1
Original file line numberDiff line numberDiff line change
@@ -530,7 +530,7 @@ qx.Class.define("osparc.data.model.Node", {
530530
},
531531

532532
initIframeHandler: function() {
533-
if (this.isDynamic()) {
533+
if (this.isDynamic() && this.__iframeHandler === null) {
534534
this.__iframeHandler = new osparc.data.model.IframeHandler(this.getStudy(), this);
535535
}
536536
},
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
/* ************************************************************************
2+
3+
osparc - the simcore frontend
4+
5+
https://osparc.io
6+
7+
Copyright:
8+
2025 IT'IS Foundation, https://itis.swiss
9+
10+
License:
11+
MIT: https://opensource.org/licenses/MIT
12+
13+
Authors:
14+
* Odei Maiz (odeimaiz)
15+
16+
************************************************************************ */
17+
18+
qx.Class.define("osparc.file.StorageAsyncJob", {
19+
extend: qx.core.Object,
20+
21+
construct: function(jobId, interval = 1000) {
22+
this.base(arguments);
23+
24+
this.setPollInterval(interval);
25+
26+
this.setJobId(jobId);
27+
},
28+
29+
events: {
30+
"resultReceived": "qx.event.type.Data",
31+
"taskAborted": "qx.event.type.Event",
32+
"pollingError": "qx.event.type.Data",
33+
},
34+
35+
properties: {
36+
pollInterval: {
37+
check: "Number",
38+
nullable: false,
39+
init: 1000
40+
},
41+
42+
jobId: {
43+
check: "String",
44+
nullable: false,
45+
apply: "fetchStatus",
46+
},
47+
},
48+
49+
members: {
50+
__retries: null,
51+
__aborting: null,
52+
53+
fetchStatus: function() {
54+
const jobId = this.getJobId();
55+
osparc.data.Resources.fetch("storageAsyncJobs", "jobStatus", { url: { jobId } })
56+
.then(status => {
57+
if (this.__aborting) {
58+
return;
59+
}
60+
if (status["done"]) {
61+
this.__fetchResults();
62+
} else {
63+
setTimeout(() => this.fetchStatus(), this.getPollInterval());
64+
}
65+
})
66+
.catch(err => {
67+
if (this.__retries > 0) {
68+
this.__retries--;
69+
this.fetchStatus();
70+
return;
71+
}
72+
this.fireDataEvent("pollingError", err);
73+
});
74+
},
75+
76+
__fetchResults: function() {
77+
const jobId = this.getJobId();
78+
osparc.data.Resources.fetch("storageAsyncJobs", "jobResult", { url: { jobId } })
79+
.then(resp => {
80+
this.fireDataEvent("resultReceived", resp["result"]);
81+
})
82+
.catch(err => {
83+
console.error(err);
84+
this.fireDataEvent("pollingError", err);
85+
});
86+
},
87+
88+
abortRequested: function() {
89+
this.__aborting = true;
90+
const jobId = this.getJobId();
91+
osparc.data.Resources.fetch("storageAsyncJobs", "result", { url: { jobId } })
92+
.then(() => this.fireEvent("taskAborted"))
93+
.catch(err => {
94+
throw err;
95+
});
96+
}
97+
}
98+
});

‎services/static-webserver/client/source/class/osparc/file/TreeFolderView.js

+43-2
Original file line numberDiff line numberDiff line change
@@ -40,14 +40,29 @@ qx.Class.define("osparc.file.TreeFolderView", {
4040
_createChildControlImpl: function(id) {
4141
let control;
4242
switch (id) {
43+
case "header-layout":
44+
control = new qx.ui.container.Composite(new qx.ui.layout.HBox()).set({
45+
marginLeft: 8
46+
});
47+
this._addAt(control, 0);
48+
break;
4349
case "reload-button":
4450
control = new qx.ui.form.Button().set({
4551
label: this.tr("Reload"),
4652
font: "text-14",
4753
icon: "@FontAwesome5Solid/sync-alt/14",
4854
allowGrowX: false
4955
});
50-
this._add(control);
56+
this.getChildControl("header-layout").add(control);
57+
break;
58+
case "total-size-label":
59+
control = new qx.ui.basic.Atom().set({
60+
label: this.tr("Calculating Size"),
61+
font: "text-14",
62+
icon: "@FontAwesome5Solid/spinner/14",
63+
allowGrowX: false
64+
});
65+
this.getChildControl("header-layout").add(control);
5166
break;
5267
case "tree-folder-layout":
5368
control = new qx.ui.splitpane.Pane("horizontal");
@@ -80,7 +95,6 @@ qx.Class.define("osparc.file.TreeFolderView", {
8095
},
8196

8297
__buildLayout: function() {
83-
this.getChildControl("reload-button");
8498
const folderTree = this.getChildControl("folder-tree");
8599
const folderViewer = this.getChildControl("folder-viewer");
86100

@@ -146,6 +160,33 @@ qx.Class.define("osparc.file.TreeFolderView", {
146160
} else {
147161
folderViewer.resetFolder();
148162
}
163+
},
164+
165+
requestSize: function(pathId) {
166+
const totalSize = this.getChildControl("total-size-label");
167+
totalSize.getChildControl("icon").getContentElement().addClass("rotate");
168+
169+
osparc.data.Resources.fetch("storagePaths", "requestSize", { url: { pathId } })
170+
.then(resp => {
171+
const jobId = resp["job_id"];
172+
if (jobId) {
173+
const asyncJob = new osparc.file.StorageAsyncJob(jobId);
174+
asyncJob.addListener("resultReceived", e => {
175+
const size = e.getData();
176+
totalSize.set({
177+
icon: null,
178+
label: this.tr("Total size: ") + osparc.utils.Utils.bytesToSize(size),
179+
});
180+
});
181+
asyncJob.addListener("pollingError", e => {
182+
totalSize.hide();
183+
});
184+
}
185+
})
186+
.catch(err => {
187+
console.error(err);
188+
totalSize.hide();
189+
});
149190
}
150191
}
151192
});

‎services/static-webserver/client/source/class/osparc/metadata/ServicesInStudyUpdate.js

-12
Original file line numberDiff line numberDiff line change
@@ -27,18 +27,6 @@ qx.Class.define("osparc.metadata.ServicesInStudyUpdate", {
2727
UPDATE_BUTTON: Object.keys(osparc.metadata.ServicesInStudy.GRID_POS).length+2
2828
},
2929

30-
updatableNodeIds: function(workbench, studyServices) {
31-
const nodeIds = [];
32-
for (const nodeId in workbench) {
33-
const node = workbench[nodeId];
34-
const studyServiceFound = studyServices.find(studyService => studyService["key"] === node["key"] && studyService["release"]["version"] === node["version"]);
35-
if (studyServiceFound && studyServiceFound["release"] && studyServiceFound["release"]["compatibility"]) {
36-
nodeIds.push(nodeId);
37-
}
38-
}
39-
return nodeIds;
40-
},
41-
4230
colorVersionLabel: function(versionLabel, metadata) {
4331
const isDeprecated = osparc.service.Utils.isDeprecated(metadata);
4432
const isRetired = osparc.service.Utils.isRetired(metadata);

‎services/static-webserver/client/source/class/osparc/store/Services.js

+5-5
Original file line numberDiff line numberDiff line change
@@ -61,9 +61,9 @@ qx.Class.define("osparc.store.Services", {
6161
getLatestCompatible: function(key, version) {
6262
const services = this.__servicesCached;
6363
if (key in services && version in services[key]) {
64-
const serviceMD = services[key][version];
65-
if (serviceMD["compatibility"] && serviceMD["compatibility"]["canUpdateTo"]) {
66-
const canUpdateTo = serviceMD["compatibility"]["canUpdateTo"];
64+
const historyEntry = osparc.service.Utils.extractVersionFromHistory(services[key][version]);
65+
if (historyEntry["compatibility"] && historyEntry["compatibility"]["canUpdateTo"]) {
66+
const canUpdateTo = historyEntry["compatibility"]["canUpdateTo"];
6767
return {
6868
key: "key" in canUpdateTo ? canUpdateTo["key"] : key, // key is optional
6969
version: canUpdateTo["version"]
@@ -240,7 +240,7 @@ qx.Class.define("osparc.store.Services", {
240240
},
241241

242242
getStudyServicesMetadata: function(studyData) {
243-
const wbServices = new Set(osparc.study.Utils.extractUniqueServices(studyData["workbench"]));
243+
const wbServices = osparc.study.Utils.extractUniqueServices(studyData["workbench"]);
244244
const promises = [];
245245
wbServices.forEach(srv => {
246246
promises.push(this.getService(srv["key"], srv["version"]));
@@ -251,7 +251,7 @@ qx.Class.define("osparc.store.Services", {
251251
getInaccessibleServices: function(workbench) {
252252
const allServices = this.__servicesCached;
253253
const unaccessibleServices = [];
254-
const wbServices = new Set(osparc.study.Utils.extractUniqueServices(workbench));
254+
const wbServices = osparc.study.Utils.extractUniqueServices(workbench);
255255
wbServices.forEach(srv => {
256256
if (srv.key in allServices && srv.version in allServices[srv.key]) {
257257
return;

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

+15-3
Original file line numberDiff line numberDiff line change
@@ -35,14 +35,14 @@ qx.Class.define("osparc.study.Utils", {
3535
},
3636

3737
extractUniqueServices: function(workbench) {
38-
const services = [];
38+
const services = new Set([]);
3939
Object.values(workbench).forEach(srv => {
40-
services.push({
40+
services.add({
4141
key: srv.key,
4242
version: srv.version
4343
});
4444
});
45-
return services;
45+
return Array.from(services);
4646
},
4747

4848
getCantExecuteServices: function(studyServices = []) {
@@ -83,6 +83,18 @@ qx.Class.define("osparc.study.Utils", {
8383
return isUpdatable;
8484
},
8585

86+
updatableNodeIds: function(workbench, studyServices) {
87+
const nodeIds = [];
88+
for (const nodeId in workbench) {
89+
const node = workbench[nodeId];
90+
const studyServiceFound = studyServices.find(studyService => studyService["key"] === node["key"] && studyService["release"]["version"] === node["version"]);
91+
if (studyServiceFound && studyServiceFound["release"] && studyServiceFound["release"]["compatibility"]) {
92+
nodeIds.push(nodeId);
93+
}
94+
}
95+
return nodeIds;
96+
},
97+
8698

8799
createStudyFromService: function(key, version, existingStudies, newStudyLabel, contextProps = {}) {
88100
return new Promise((resolve, reject) => {

‎services/static-webserver/client/source/class/osparc/widget/StudyDataManager.js

+2-3
Original file line numberDiff line numberDiff line change
@@ -95,9 +95,6 @@ qx.Class.define("osparc.widget.StudyDataManager", {
9595
const treeFolderView = this.getChildControl("tree-folder-view");
9696
treeFolderView.getChildControl("folder-tree").setBackgroundColor("window-popup-background");
9797

98-
const reloadButton = treeFolderView.getChildControl("reload-button");
99-
reloadButton.addListener("execute", () => this.__reloadTree(), this);
100-
10198
const selectedFileLayout = treeFolderView.getChildControl("folder-viewer").getChildControl("selected-file-layout");
10299
selectedFileLayout.addListener("fileDeleted", e => this.__fileDeleted(e.getData()), this);
103100
},
@@ -109,8 +106,10 @@ qx.Class.define("osparc.widget.StudyDataManager", {
109106
foldersTree.resetCache();
110107
if (this.getNodeId()) {
111108
foldersTree.populateNodeTree(this.getStudyId(), this.getNodeId());
109+
treeFolderView.requestSize(this.getStudyId(), this.getNodeId());
112110
} else if (this.getStudyId()) {
113111
foldersTree.populateStudyTree(this.getStudyId());
112+
treeFolderView.requestSize(this.getStudyId());
114113
}
115114

116115
const folderViewer = treeFolderView.getChildControl("folder-viewer");

‎tests/e2e/tutorials/tutorialBase.js

+2-2
Original file line numberDiff line numberDiff line change
@@ -446,7 +446,7 @@ class TutorialBase {
446446

447447
async openNodeFiles(nodeId) {
448448
const pathFilter = `${this.__studyId}/${nodeId}`;
449-
const path = "storage/locations/0/files/paths?file_filter=" + pathFilter;
449+
const path = "storage/locations/0/paths?file_filter=" + pathFilter;
450450
this.__responsesQueue.addResponseListener(path);
451451
await auto.openNodeFiles(this.__page);
452452
try {
@@ -459,7 +459,7 @@ class TutorialBase {
459459

460460
async openNodeFilesAppMode(nodeId) {
461461
const pathFilter = `${this.__studyId}/${nodeId}`;
462-
const path = "storage/locations/0/files/paths?file_filter=" + pathFilter;
462+
const path = "storage/locations/0/paths?file_filter=" + pathFilter;
463463
this.__responsesQueue.addResponseListener(path);
464464
await auto.openNodeFilesAppMode(this.__page);
465465
try {

0 commit comments

Comments
 (0)
Please sign in to comment.