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 d42080a05f6..a06ec485d48 100644 --- a/services/static-webserver/client/source/class/osparc/dashboard/CardBase.js +++ b/services/static-webserver/client/source/class/osparc/dashboard/CardBase.js @@ -530,22 +530,20 @@ qx.Class.define("osparc.dashboard.CardBase", { __evalSelectedButton: function() { if ( + this.hasChildControl("menu-selection-stack") && this.hasChildControl("menu-button") && this.hasChildControl("tick-selected") && this.hasChildControl("tick-unselected") ) { - const menuButton = this.getChildControl("menu-button"); - const tick = this.getChildControl("tick-selected"); - const untick = this.getChildControl("tick-unselected"); + const menuButtonStack = this.getChildControl("menu-selection-stack"); if (this.isResourceType("study") && this.isMultiSelectionMode()) { + const tick = this.getChildControl("tick-selected"); + const untick = this.getChildControl("tick-unselected"); const selected = this.getSelected(); - menuButton.setVisibility("excluded"); - tick.setVisibility(selected ? "visible" : "excluded"); - untick.setVisibility(selected ? "excluded" : "visible"); + menuButtonStack.setSelection(selected ? [tick] : [untick]); } else { - menuButton.setVisibility("visible"); - tick.setVisibility("excluded"); - untick.setVisibility("excluded"); + const menuButton = this.getChildControl("menu-button"); + menuButtonStack.setSelection([menuButton]); } } }, diff --git a/services/static-webserver/client/source/class/osparc/dashboard/DataBrowser.js b/services/static-webserver/client/source/class/osparc/dashboard/DataBrowser.js index f8ffd3d73d0..2305feaa8a4 100644 --- a/services/static-webserver/client/source/class/osparc/dashboard/DataBrowser.js +++ b/services/static-webserver/client/source/class/osparc/dashboard/DataBrowser.js @@ -60,7 +60,7 @@ qx.Class.define("osparc.dashboard.DataBrowser", { this.addListener("appear", () => { const treeFolderView = this.getChildControl("tree-folder-view"); - treeFolderView.getChildControl("folder-tree").populateTree(); + treeFolderView.getChildControl("folder-tree").populateLocations(); treeFolderView.getChildControl("folder-viewer").setFolder(treeFolderView.getChildControl("folder-tree").getModel()); }, this); }, @@ -84,7 +84,7 @@ qx.Class.define("osparc.dashboard.DataBrowser", { const foldersTree = treeFolderView.getChildControl("folder-tree"); foldersTree.resetCache(); - foldersTree.populateTree(); + foldersTree.populateLocations(); const folderViewer = treeFolderView.getChildControl("folder-viewer"); folderViewer.resetFolder(); @@ -94,7 +94,7 @@ qx.Class.define("osparc.dashboard.DataBrowser", { // After deleting a file, try to keep the user in the same folder. // If the folder doesn't longer exist, open the closest available parent - const path = fileMetadata["fileUuid"].split("/"); + const pathParts = fileMetadata["fileUuid"].split("/"); const treeFolderView = this.getChildControl("tree-folder-view"); const foldersTree = treeFolderView.getChildControl("folder-tree"); @@ -102,18 +102,18 @@ qx.Class.define("osparc.dashboard.DataBrowser", { const openSameFolder = () => { // drop last, which is the file - path.pop(); - treeFolderView.openPath(path); + pathParts.pop(); + treeFolderView.openPath(pathParts); }; folderViewer.resetFolder(); const locationId = fileMetadata["locationId"]; - const datasetId = path[0]; + const path = pathParts[0]; foldersTree.resetCache(); - foldersTree.populateTree() + foldersTree.populateLocations() .then(datasetPromises => { Promise.all(datasetPromises) - .then(() => foldersTree.requestDatasetFiles(locationId, datasetId)) + .then(() => foldersTree.requestPathItems(locationId, path)) .then(() => openSameFolder()); }) .catch(err => console.error(err)); diff --git a/services/static-webserver/client/source/class/osparc/dashboard/GridButtonBase.js b/services/static-webserver/client/source/class/osparc/dashboard/GridButtonBase.js index 74b2da3c990..a04e872c894 100644 --- a/services/static-webserver/client/source/class/osparc/dashboard/GridButtonBase.js +++ b/services/static-webserver/client/source/class/osparc/dashboard/GridButtonBase.js @@ -138,13 +138,16 @@ qx.Class.define("osparc.dashboard.GridButtonBase", { } case "header": { const hGrid = new qx.ui.layout.Grid().set({ - spacing: 0, // the sub-elements will take care of the padding + spacing: 6, }); hGrid.setColumnFlex(1, 1); - hGrid.setRowAlign(0, "left", "middle"); + hGrid.setColumnAlign(0, "right", "middle"); + hGrid.setColumnAlign(1, "left", "middle"); + hGrid.setColumnAlign(2, "center", "middle"); control = new qx.ui.container.Composite().set({ backgroundColor: "background-card-overlay", - padding: 0, + paddingBottom: 6, + paddingRight: 4, maxWidth: this.self().ITEM_WIDTH, maxHeight: this.self().ITEM_HEIGHT }); @@ -189,37 +192,23 @@ qx.Class.define("osparc.dashboard.GridButtonBase", { control = new qx.ui.basic.Label().set({ textColor: "contrasted-text-light", font: "text-14", - padding: this.self().TITLE_PADDING, - maxWidth: this.self().ITEM_WIDTH, }); layout = this.getChildControl("header"); - layout.addAt(control, 0, { + layout.add(control, { column: 1, row: 0, }); break; - case "subtitle": - control = new qx.ui.container.Composite(new qx.ui.layout.HBox(6)).set({ - anonymous: true, - height: 20, - padding: 6, - paddingLeft: 20, // align with icon + case "subtitle-icon": { + control = new qx.ui.basic.Image().set({ + allowGrowX: false, + allowShrinkX: false, }); layout = this.getChildControl("header"); - layout.addAt(control, 0, { + layout.add(control, { column: 0, row: 1, - colSpan: 2, - }); - break; - case "subtitle-icon": { - control = new qx.ui.basic.Image().set({ - alignY: "middle", - allowGrowX: false, - allowShrinkX: false }); - const subtitleLayout = this.getChildControl("subtitle"); - subtitleLayout.addAt(control, 0); break; } case "subtitle-text": { @@ -233,9 +222,10 @@ qx.Class.define("osparc.dashboard.GridButtonBase", { font: "text-12", allowGrowY: false }); - const subtitleLayout = this.getChildControl("subtitle"); - subtitleLayout.addAt(control, 1, { - flex: 1 + layout = this.getChildControl("header"); + layout.add(control, { + column: 1, + row: 1, }); break; } @@ -269,9 +259,10 @@ qx.Class.define("osparc.dashboard.GridButtonBase", { width: 13, margin: [0, 1] }); - layout = this.getChildControl("subtitle"); - layout.set({ - visibility: "visible" + layout = this.getChildControl("header"); + layout.add(control, { + column: 2, + row: 1, }); layout.addAt(control, 2); 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 675951f1ab6..ad0bf2ec3ae 100644 --- a/services/static-webserver/client/source/class/osparc/dashboard/GridButtonItem.js +++ b/services/static-webserver/client/source/class/osparc/dashboard/GridButtonItem.js @@ -91,7 +91,18 @@ qx.Class.define("osparc.dashboard.GridButtonItem", { layout = this.getChildControl("main-layout"); layout.add(control, osparc.dashboard.GridButtonBase.POS.TAGS); break; - case "menu-button": + case "menu-selection-stack": + control = new qx.ui.container.Stack(); + control.set({ + alignX: "center", + alignY: "middle" + }); + this.getChildControl("header").add(control, { + column: 2, + row: 0, + }); + break; + case "menu-button": { this.getChildControl("title").set({ maxWidth: osparc.dashboard.GridButtonBase.ITEM_WIDTH - osparc.dashboard.CardBase.ICON_SIZE - this.self().MENU_BTN_DIMENSIONS - 2, }); @@ -110,12 +121,11 @@ qx.Class.define("osparc.dashboard.GridButtonItem", { "border-radius": `${this.self().MENU_BTN_DIMENSIONS / 2}px` }); osparc.utils.Utils.setIdToWidget(control, "studyItemMenuButton"); - this._add(control, { - top: 6, - right: 6, - }); + const menuSelectionStack = this.getChildControl("menu-selection-stack"); + menuSelectionStack.addAt(control, 0); break; - case "tick-unselected": + } + case "tick-unselected": { control = new qx.ui.basic.Atom().set({ appearance: "form-button-outlined", width: this.self().MENU_BTN_DIMENSIONS, @@ -126,12 +136,11 @@ qx.Class.define("osparc.dashboard.GridButtonItem", { control.getContentElement().setStyles({ "border-radius": `${this.self().MENU_BTN_DIMENSIONS / 2}px` }); - this._add(control, { - top: 8, - right: 8 - }); + const menuSelectionStack = this.getChildControl("menu-selection-stack"); + menuSelectionStack.addAt(control, 1); break; - case "tick-selected": + } + case "tick-selected": { control = new qx.ui.basic.Image().set({ appearance: "form-button", width: this.self().MENU_BTN_DIMENSIONS, @@ -146,11 +155,10 @@ qx.Class.define("osparc.dashboard.GridButtonItem", { control.getContentElement().setStyles({ "border-radius": `${this.self().MENU_BTN_DIMENSIONS / 2}px` }); - this._add(control, { - top: 8, - right: 8 - }); + const menuSelectionStack = this.getChildControl("menu-selection-stack"); + menuSelectionStack.addAt(control, 2); break; + } case "lock-status": control = new osparc.ui.basic.Thumbnail(); this._add(control, { diff --git a/services/static-webserver/client/source/class/osparc/dashboard/ListButtonItem.js b/services/static-webserver/client/source/class/osparc/dashboard/ListButtonItem.js index f5dbbdda082..3cfb70d3b7d 100644 --- a/services/static-webserver/client/source/class/osparc/dashboard/ListButtonItem.js +++ b/services/static-webserver/client/source/class/osparc/dashboard/ListButtonItem.js @@ -132,8 +132,25 @@ qx.Class.define("osparc.dashboard.ListButtonItem", { column: osparc.dashboard.ListButtonBase.POS.OPTIONS }); break; - case "tick-unselected": { + case "menu-button": { + control = new qx.ui.form.MenuButton().set({ + appearance: "form-button-outlined", + padding: [0, 8], + maxWidth: this.self().MENU_BTN_DIMENSIONS, + maxHeight: this.self().MENU_BTN_DIMENSIONS, + icon: "@FontAwesome5Solid/ellipsis-v/14", + focusable: false + }); + // make it circular + control.getContentElement().setStyles({ + "border-radius": `${this.self().MENU_BTN_DIMENSIONS / 2}px` + }); + osparc.utils.Utils.setIdToWidget(control, "studyItemMenuButton"); const menuSelectionStack = this.getChildControl("menu-selection-stack"); + menuSelectionStack.addAt(control, 0); + break; + } + case "tick-unselected": { control = new qx.ui.basic.Atom().set({ appearance: "form-button-outlined", width: this.self().MENU_BTN_DIMENSIONS, @@ -143,11 +160,11 @@ qx.Class.define("osparc.dashboard.ListButtonItem", { control.getContentElement().setStyles({ "border-radius": `${this.self().MENU_BTN_DIMENSIONS / 2}px` }); + const menuSelectionStack = this.getChildControl("menu-selection-stack"); menuSelectionStack.addAt(control, 1); break; } case "tick-selected": { - const menuSelectionStack = this.getChildControl("menu-selection-stack"); control = new qx.ui.basic.Image("@FontAwesome5Solid/check/12").set({ appearance: "form-button-outlined", width: this.self().MENU_BTN_DIMENSIONS, @@ -158,25 +175,8 @@ qx.Class.define("osparc.dashboard.ListButtonItem", { control.getContentElement().setStyles({ "border-radius": `${this.self().MENU_BTN_DIMENSIONS / 2}px` }); - menuSelectionStack.addAt(control, 2); - break; - } - case "menu-button": { const menuSelectionStack = this.getChildControl("menu-selection-stack"); - control = new qx.ui.form.MenuButton().set({ - appearance: "form-button-outlined", - padding: [0, 8], - maxWidth: this.self().MENU_BTN_DIMENSIONS, - maxHeight: this.self().MENU_BTN_DIMENSIONS, - icon: "@FontAwesome5Solid/ellipsis-v/14", - focusable: false - }); - // make it circular - control.getContentElement().setStyles({ - "border-radius": `${this.self().MENU_BTN_DIMENSIONS / 2}px` - }); - osparc.utils.Utils.setIdToWidget(control, "studyItemMenuButton"); - menuSelectionStack.addAt(control, 0); + menuSelectionStack.addAt(control, 2); break; } } diff --git a/services/static-webserver/client/source/class/osparc/data/Converters.js b/services/static-webserver/client/source/class/osparc/data/Converters.js index c5cae8bd25c..d567b63e561 100644 --- a/services/static-webserver/client/source/class/osparc/data/Converters.js +++ b/services/static-webserver/client/source/class/osparc/data/Converters.js @@ -24,40 +24,6 @@ qx.Class.define("osparc.data.Converters", { type: "static", statics: { - __mergeFileTreeChildren: function(one, two) { - let newDir = true; - for (let i=0; i { - if (a["label"] > b["label"]) { - return 1; - } - if (a["label"] < b["label"]) { - return -1; - } - return 0; - }); - children.forEach(child => { - if ("children" in child) { - this.sortFiles(child["children"]); - } - }); - } - }, - sortModelByLabel: function(model) { model.getChildren().sort((a, b) => { if (a.getLabel() > b.getLabel()) { @@ -70,96 +36,44 @@ qx.Class.define("osparc.data.Converters", { }); }, - fromDSMToVirtualTreeModel: function(datasetId, files) { - let children = []; - for (let i=0; i 0) { + // simcore: studyId + nodeId + fileId + // datcore: datasetId + return splitted[0]; + } + return null; + }, + + displayPathToLabel: function(encodedDisplayPath, options) { + const parts = encodedDisplayPath.split("/"); + const decodedParts = parts.map(decodeURIComponent); + if (options.first) { + return decodedParts[0]; + } else if (options.last) { + return decodedParts[decodedParts.length-1]; + } else if ("pos" in options && options["pos"] < decodedParts.length) { + return decodedParts[options["pos"]]; + } + return decodedParts[0]; + }, } }); diff --git a/services/static-webserver/client/source/class/osparc/data/Resources.js b/services/static-webserver/client/source/class/osparc/data/Resources.js index f6a257dabf9..8fcc7a4b005 100644 --- a/services/static-webserver/client/source/class/osparc/data/Resources.js +++ b/services/static-webserver/client/source/class/osparc/data/Resources.js @@ -1176,46 +1176,42 @@ qx.Class.define("osparc.data.Resources", { "storageLocations": { useCache: true, endpoints: { - get: { + getLocations: { method: "GET", url: statics.API + "/storage/locations" } } }, /* - * STORAGE DATASETS + * STORAGE FILES */ - "storageDatasets": { + "storageFiles": { useCache: false, endpoints: { - getByLocation: { - method: "GET", - url: statics.API + "/storage/locations/{locationId}/datasets" + copy: { + method: "PUT", + url: statics.API + "/storage/locations/{toLoc}/files/{fileName}?extra_location={fromLoc}&extra_source={fileUuid}" + }, + delete: { + method: "DELETE", + url: statics.API + "/storage/locations/{locationId}/files/{fileUuid}" } } }, /* - * STORAGE FILES + * STORAGE PATHS */ - "storageFiles": { + "storagePaths": { useCache: false, endpoints: { - getByLocationAndDataset: { + getDatasets: { method: "GET", - url: statics.API + "/storage/locations/{locationId}/datasets/{datasetId}/metadata" + url: statics.API + "/storage/locations/{locationId}/paths?size=1000" }, - getByNode: { + getPaths: { method: "GET", - url: statics.API + "/storage/locations/0/files/metadata?uuid_filter={nodeId}" - }, - put: { - method: "PUT", - url: statics.API + "/storage/locations/{toLoc}/files/{fileName}?extra_location={fromLoc}&extra_source={fileUuid}" + url: statics.API + "/storage/locations/{locationId}/paths?file_filter={path}&size=1000" }, - delete: { - method: "DELETE", - url: statics.API + "/storage/locations/{locationId}/files/{fileUuid}" - } } }, /* diff --git a/services/static-webserver/client/source/class/osparc/desktop/WorkbenchView.js b/services/static-webserver/client/source/class/osparc/desktop/WorkbenchView.js index 603b746dc76..1281742898e 100644 --- a/services/static-webserver/client/source/class/osparc/desktop/WorkbenchView.js +++ b/services/static-webserver/client/source/class/osparc/desktop/WorkbenchView.js @@ -54,8 +54,8 @@ qx.Class.define("osparc.desktop.WorkbenchView", { }); }, - openStudyDataManager: function(node) { - const win = osparc.widget.StudyDataManager.popUpInWindow(null, node.getNodeId(), node.getLabel()); + openNodeDataManager: function(node) { + const win = osparc.widget.StudyDataManager.popUpInWindow(node.getStudy().getUuid(), node.getNodeId(), node.getLabel()); const closeBtn = win.getChildControl("close-button"); osparc.utils.Utils.setIdToWidget(closeBtn, "nodeDataManagerCloseBtn"); } @@ -369,7 +369,7 @@ qx.Class.define("osparc.desktop.WorkbenchView", { dragMechanism: true, hideRoot: true }); - filesTree.populateTree(); + filesTree.populateLocations(); const storagePage = this.__storagePage = this.__createTabPage("@FontAwesome5Solid/database", this.tr("Storage"), filesTree, this.self().PRIMARY_COL_BG_COLOR); tabViewPrimary.add(storagePage); @@ -1046,7 +1046,7 @@ qx.Class.define("osparc.desktop.WorkbenchView", { allowGrowY: false }); osparc.utils.Utils.setIdToWidget(nodeFilesBtn, "nodeFilesBtn"); - nodeFilesBtn.addListener("execute", () => this.self().openStudyDataManager(node)); + nodeFilesBtn.addListener("execute", () => this.self().openNodeDataManager(node)); outputsBox.add(nodeFilesBtn); const outputs = new osparc.desktop.PanelView(this.tr("Outputs"), outputsBox); diff --git a/services/static-webserver/client/source/class/osparc/file/FileLabelWithActions.js b/services/static-webserver/client/source/class/osparc/file/FileLabelWithActions.js index 2e270c26709..780279d026b 100644 --- a/services/static-webserver/client/source/class/osparc/file/FileLabelWithActions.js +++ b/services/static-webserver/client/source/class/osparc/file/FileLabelWithActions.js @@ -182,6 +182,7 @@ qx.Class.define("osparc.file.FileLabelWithActions", { } else if (this.__selection.length) { const selection = this.__selection[0]; if (selection) { + toBeDeleted.push(selection); if (osparc.file.FilesTree.isDir(selection)) { isFolderSelected = true; } diff --git a/services/static-webserver/client/source/class/osparc/file/FilePicker.js b/services/static-webserver/client/source/class/osparc/file/FilePicker.js index ae721a2f04e..c7449eb6233 100644 --- a/services/static-webserver/client/source/class/osparc/file/FilePicker.js +++ b/services/static-webserver/client/source/class/osparc/file/FilePicker.js @@ -111,7 +111,7 @@ qx.Class.define("osparc.file.FilePicker", { return osparc.file.FilePicker.isOutputFromStore(outputs) || osparc.file.FilePicker.isOutputDownloadLink(outputs); }, - setOutputValue: function(node, outputValue) { + __setOutputValue: function(node, outputValue) { node.setOutputData({ "outFile": outputValue }); @@ -127,7 +127,7 @@ qx.Class.define("osparc.file.FilePicker", { setOutputValueFromStore: function(node, store, dataset, path, label) { if (store !== undefined && path) { - osparc.file.FilePicker.setOutputValue(node, { + this.__setOutputValue(node, { store, dataset, path, @@ -138,7 +138,7 @@ qx.Class.define("osparc.file.FilePicker", { setOutputValueFromLink: function(node, downloadLink, label) { if (downloadLink) { - osparc.file.FilePicker.setOutputValue(node, { + this.__setOutputValue(node, { downloadLink, label: label ? label : "" }); @@ -146,7 +146,7 @@ qx.Class.define("osparc.file.FilePicker", { }, resetOutputValue: function(node) { - osparc.file.FilePicker.setOutputValue(node, null); + this.__setOutputValue(node, null); }, getOutputFileMetadata: function(node) { @@ -155,17 +155,19 @@ qx.Class.define("osparc.file.FilePicker", { const params = { url: { locationId: outValue.store, - datasetId: outValue.dataset + path: outValue.path } }; - osparc.data.Resources.fetch("storageFiles", "getByLocationAndDataset", params) - .then(files => { - const fileMetadata = files.find(file => file.file_id === outValue.path); - if (fileMetadata) { - resolve(fileMetadata); - } else { - reject(); + osparc.data.Resources.fetch("storagePaths", "getPaths", params) + .then(pagResp => { + if (pagResp["items"]) { + const file = pagResp["items"].find(item => item.path === outValue.path); + if (file) { + resolve(file["file_meta_data"]); + return; + } } + reject(); }) .catch(() => reject()); }); @@ -274,7 +276,7 @@ qx.Class.define("osparc.file.FilePicker", { if (this.__filesTree) { this.__selectedFileFound = false; this.__filesTree.resetCache(); - this.__filesTree.populateTree(); + this.__filesTree.populateLocations(); } }, @@ -585,9 +587,9 @@ qx.Class.define("osparc.file.FilePicker", { folderViewer.setFolder(parent); } }, this); - folderViewer.addListener("requestDatasetFiles", e => { + folderViewer.addListener("requestPathItems", e => { const data = e.getData(); - filesTree.requestDatasetFiles(data.locationId, data.datasetId); + filesTree.requestPathItems(data.locationId, data.path); }, this); const selectBtn = this.__selectButton = new qx.ui.form.Button(this.tr("Select")).set({ diff --git a/services/static-webserver/client/source/class/osparc/file/FileTreeItem.js b/services/static-webserver/client/source/class/osparc/file/FileTreeItem.js index 5b96a32dc8f..7ef50a49ed4 100644 --- a/services/static-webserver/client/source/class/osparc/file/FileTreeItem.js +++ b/services/static-webserver/client/source/class/osparc/file/FileTreeItem.js @@ -48,19 +48,21 @@ qx.Class.define("osparc.file.FileTreeItem", { this.set({ indent: 12, // defaults to 19, decorator: "rounded", + alignY: "middle", }); - - // create a date format like "Oct. 19, 2018 11:31 AM" - this._dateFormat = new qx.util.format.DateFormat( - qx.locale.Date.getDateFormat("medium") + " " + - qx.locale.Date.getTimeFormat("short") - ); }, properties: { + loaded: { + check: "Boolean", + event: "changeLoaded", + init: true, + nullable: false + }, + location: { check: "String", - event: "changePath", + event: "changeLocation", nullable: true }, @@ -70,6 +72,12 @@ qx.Class.define("osparc.file.FileTreeItem", { nullable: true }, + displayPath: { + check: "String", + event: "changeDisplayPath", + nullable: true + }, + pathLabel: { check: "Array", event: "changePathLabel", @@ -83,26 +91,12 @@ qx.Class.define("osparc.file.FileTreeItem", { nullable: true }, - isDataset: { - check: "Boolean", - event: "changeIsDataset", - init: false, - nullable: false - }, - datasetId: { check: "String", event: "changeDatasetId", nullable: true }, - loaded: { - check: "Boolean", - event: "changeLoaded", - init: true, - nullable: false - }, - fileId: { check: "String", event: "changeFileId", @@ -119,12 +113,17 @@ qx.Class.define("osparc.file.FileTreeItem", { check: "String", event: "changeSize", nullable: true - } - }, + }, - members: { // eslint-disable-line qx-rules/no-refs-in-members - _dateFormat: null, + type: { + check: ["folder", "file", "loading"], + event: "changeType", + init: null, + nullable: false, + }, + }, + members: { // overridden _addWidgets: function() { // Here's our indentation and tree-lines @@ -145,38 +144,32 @@ qx.Class.define("osparc.file.FileTreeItem", { // Add lastModified const lastModifiedWidget = new qx.ui.basic.Label().set({ maxWidth: 140, - textAlign: "right" + textAlign: "right", + alignY: "middle", + paddingLeft: 10, }); - let that = this; this.bind("lastModified", lastModifiedWidget, "value", { - converter: function(value) { - if (value === null) { - return ""; - } - const date = new Date(value); - return that._dateFormat.format(date); // eslint-disable-line no-underscore-dangle - } + converter: value => value ? osparc.utils.Utils.formatDateAndTime(new Date(value)) : "" }); this.addWidget(lastModifiedWidget); // Add size const sizeWidget = new qx.ui.basic.Label().set({ - maxWidth: 70, - textAlign: "right" + maxWidth: 90, + textAlign: "right", + alignY: "middle", + paddingLeft: 10, }); this.bind("size", sizeWidget, "value", { - converter: function(value) { - if (value === null) { - return ""; - } - return osparc.utils.Utils.bytesToSize(value); - } + converter: value => value ? osparc.utils.Utils.bytesToSize(value) : "" }); this.addWidget(sizeWidget); }, __applyItemId: function(value, old) { - osparc.utils.Utils.setIdToWidget(this, "fileTreeItem_" + value); + if (value) { + osparc.utils.Utils.setIdToWidget(this, "fileTreeItem_" + value); + } }, // override @@ -194,9 +187,4 @@ qx.Class.define("osparc.file.FileTreeItem", { } } }, - - destruct: function() { - this._dateFormat.dispose(); - this._dateFormat = null; - } }); diff --git a/services/static-webserver/client/source/class/osparc/file/FilesTree.js b/services/static-webserver/client/source/class/osparc/file/FilesTree.js index 1d828881cae..76fd6050315 100644 --- a/services/static-webserver/client/source/class/osparc/file/FilesTree.js +++ b/services/static-webserver/client/source/class/osparc/file/FilesTree.js @@ -48,18 +48,9 @@ qx.Class.define("osparc.file.FilesTree", { font: "text-14", }); - this.resetChecks(); + this.__resetChecks(); this.addListener("tap", this.__selectionChanged, this); - - // Listen to "Enter" key - this.addListener("keypress", keyEvent => { - if (keyEvent.getKeyIdentifier() === "Enter") { - this.__itemSelected(); - } - }, this); - - this.__loadPaths = {}; }, properties: { @@ -75,153 +66,122 @@ qx.Class.define("osparc.file.FilesTree", { }, events: { - "selectionChanged": "qx.event.type.Event", // tap - "itemSelected": "qx.event.type.Event", // dbltap + "selectionChanged": "qx.event.type.Event", "fileCopied": "qx.event.type.Data", "filesAddedToTree": "qx.event.type.Event" }, statics: { isDir: function(item) { - let isDir = false; - if (item["get"+qx.lang.String.firstUp("path")]) { - if (item.getPath() !== null) { - isDir = true; - } - } - return isDir; + return item.getType() === "folder"; }, isFile: function(item) { - let isFile = false; - if (item["set"+qx.lang.String.firstUp("fileId")]) { - isFile = true; - } - return isFile; + return item.getType() === "file"; }, addLoadingChild: function(parent) { - const loadingModel = qx.data.marshal.Json.createModel({ - label: "Loading...", - location: null, - path: null, - children: [], - icon: "@FontAwesome5Solid/circle-notch/12" - }, true); + const loadingData = osparc.data.Converters.createLoadingEntry(); + const loadingModel = qx.data.marshal.Json.createModel(loadingData, true); parent.getChildren().append(loadingModel); }, removeLoadingChild: function(parent) { for (let i = parent.getChildren().length - 1; i >= 0; i--) { - if (parent.getChildren().toArray()[i].getLabel() === "Loading...") { - parent.getChildren().toArray() - .splice(i, 1); + if (parent.getChildren().toArray()[i].getType() === "loading") { + parent.getChildren().toArray().splice(i, 1); } } }, - - attachPathLabel: function(srcPathLabel, data) { - data["pathLabel"] = srcPathLabel.concat(data["label"]); - if ("children" in data) { - data.children.forEach(child => this.self().attachPathLabel(data["pathLabel"], child)); - } - } }, members: { __locations: null, - __datasets: null, + __pathModels: null, __loadPaths: null, - resetChecks: function() { - this.__locations = new Set(); - this.__datasets = new Set(); - }, - resetCache: function() { - this.resetChecks(); + this.__resetChecks(); const dataStore = osparc.store.Data.getInstance(); dataStore.resetCache(); }, + populateLocations: function() { + this.__resetChecks(); + + const treeName = "My Data"; + this.__resetTree(treeName); + const rootModel = this.getModel(); + rootModel.getChildren().removeAll(); + this.self().addLoadingChild(rootModel); + + this.set({ + hideRoot: true + }); + const dataStore = osparc.store.Data.getInstance(); + return dataStore.getLocations() + .then(locations => { + const datasetPromises = []; + if (this.__locations.size === 0) { + this.__resetChecks(); + this.__locationsToRoot(locations); + for (let i=0; i { - const { - files - } = data; - - if (files.length && "project_name" in files[0]) { - this.__resetTree(files[0]["project_name"]); + const locationId = 0; + const path = studyId; + return dataStore.getItemsByLocationAndPath(locationId, path) + .then(items => { + if (items.length) { + const studyName = osparc.data.Converters.displayPathToLabel(items[0]["display_path"], { first: true }); + studyModel.setLabel(studyName); } - studyModel = this.getModel(); - this.__filesToDataset("0", studyId, files, studyModel); + this.__itemsToTree(locationId, path, items, studyModel); - // select study item this.setSelection(new qx.data.Array([studyModel])); this.__selectionChanged(); }); }, - populateNodeTree(nodeId) { + populateNodeTree(studyId, nodeId) { const treeName = "Node Files"; this.__resetTree(treeName); - const rootModel = this.getModel(); - this.self().addLoadingChild(rootModel); + const nodeModel = this.getModel(); + this.self().addLoadingChild(nodeModel); const dataStore = osparc.store.Data.getInstance(); - return dataStore.getNodeFiles(nodeId) - .then(files => { - const newChildren = osparc.data.Converters.fromDSMToVirtualTreeModel(null, files); - if (newChildren.length && // location - newChildren[0].children.length && // study - newChildren[0].children[0].children.length) { // node - const nodeData = newChildren[0].children[0].children[0]; - const nodeTreeName = nodeData.label; - this.__resetTree(nodeTreeName, nodeId); - const rootNodeModel = this.getModel(); - if (nodeData.children.length) { - const nodeItemsOnly = nodeData.children; - this.__itemsToNode(nodeItemsOnly); - } - this.openNode(rootNodeModel); + const locationId = 0; + const path = encodeURIComponent(studyId) + "/" + encodeURIComponent(nodeId); + return dataStore.getItemsByLocationAndPath(locationId, path) + .then(items => { + this.__itemsToTree(0, path, items, nodeModel); - // select node item - this.setSelection(new qx.data.Array([rootNodeModel])); - this.__selectionChanged(); - } else { - rootModel.getChildren().removeAll(); - } + this.setSelection(new qx.data.Array([nodeModel])); + this.__selectionChanged(); }); }, - populateTree: function() { - return this.__populateLocations(); - }, - loadFilePath: function(outFileVal) { const locationId = outFileVal.store; + const path = outFileVal.path; let datasetId = "dataset" in outFileVal ? outFileVal.dataset : null; - const pathId = outFileVal.path; if (datasetId === null) { - const splitted = pathId.split("/"); - if (splitted.length === 3) { - // simcore.s3 - datasetId = splitted[0]; - } + datasetId = osparc.data.Converters.pathToDatasetId(path); } - this.__addToLoadFilePath(locationId, datasetId, pathId); - this.__populateLocations(); - }, - - __addToLoadFilePath: function(locationId, datasetId, pathId) { if (datasetId) { if (!(locationId in this.__loadPaths)) { this.__loadPaths[locationId] = {}; @@ -229,32 +189,24 @@ qx.Class.define("osparc.file.FilesTree", { if (!(datasetId in this.__loadPaths[locationId])) { this.__loadPaths[locationId][datasetId] = new Set(); } - this.__loadPaths[locationId][datasetId].add(pathId); + this.__loadPaths[locationId][datasetId].add(path); } - }, - __hasLocationNeedToBeLoaded: function(locationId) { - return (locationId in this.__loadPaths) && (Object.keys(this.__loadPaths[locationId]).length > 0); + this.populateLocations(); }, - __hasDatasetNeedToBeLoaded: function(locationId, datasetId) { - return (locationId in this.__loadPaths) && (datasetId in this.__loadPaths[locationId]) && (this.__loadPaths[locationId][datasetId].size > 0); + requestPathItems: function(locationId, path) { + const dataStore = osparc.store.Data.getInstance(); + return dataStore.getItemsByLocationAndPath(locationId, path) + .then(items => { + return this.__itemsToTree(locationId, path, items); + }); }, - __filesReceived: function(locationId, datasetId, files) { - if (this.__hasDatasetNeedToBeLoaded(locationId, datasetId)) { - const paths = Array.from(this.__loadPaths[locationId][datasetId]); - for (let i=0; i { c.bindDefaultProperties(item, id); c.bindProperty("itemId", "itemId", null, item, id); + c.bindProperty("displayPath", "displayPath", null, item, id); c.bindProperty("fileId", "fileId", null, item, id); c.bindProperty("location", "location", null, item, id); - c.bindProperty("isDataset", "isDataset", null, item, id); c.bindProperty("datasetId", "datasetId", null, item, id); c.bindProperty("loaded", "loaded", null, item, id); c.bindProperty("path", "path", null, item, id); @@ -286,64 +239,34 @@ qx.Class.define("osparc.file.FilesTree", { c.bindProperty("lastModified", "lastModified", null, item, id); c.bindProperty("size", "size", null, item, id); c.bindProperty("icon", "icon", null, item, id); + c.bindProperty("type", "type", null, item, id); }, configureItem: item => { - const openButton = item.getChildControl("open"); - openButton.addListener("tap", () => { - if (item.isOpen() && !item.getLoaded() && item.getIsDataset()) { - item.setLoaded(true); + item.addListener("changeOpen", e => { + if (e.getData() && !item.getLoaded()) { const locationId = item.getLocation(); - const datasetId = item.getPath(); - this.requestDatasetFiles(locationId, datasetId); + const path = item.getPath(); + this.requestPathItems(locationId, path); } }, this); - item.addListener("dbltap", () => this.__itemSelected(), this); this.__addDragAndDropMechanisms(item); } }); }, - __populateLocations: function() { - this.resetChecks(); - - const treeName = "My Data"; - this.__resetTree(treeName); - const rootModel = this.getModel(); - rootModel.getChildren().removeAll(); - this.self().addLoadingChild(rootModel); - - this.set({ - hideRoot: true - }); - const dataStore = osparc.store.Data.getInstance(); - return dataStore.getLocations() - .then(locations => { - const datasetPromises = []; - if (this.__locations.size === 0) { - this.resetChecks(); - this.__locationsToRoot(locations); - for (let i=0; i 0); + }, + + __hasDatasetNeedToBeLoaded: function(locationId, datasetId) { + return (locationId in this.__loadPaths) && (datasetId in this.__loadPaths[locationId]) && (this.__loadPaths[locationId][datasetId].size > 0); + }, + + __filesReceived: function(locationId, datasetId, files) { + if (this.__hasDatasetNeedToBeLoaded(locationId, datasetId)) { + const paths = Array.from(this.__loadPaths[locationId][datasetId]); + for (let i=0; i { const { location, - datasets + items, } = data; if (location === locationId && !this.__locations.has(locationId)) { - this.__datasetsToLocation(location, datasets); + this.__itemsToLocation(location, items); } }); }, - requestDatasetFiles: function(locationId, datasetId) { - if (this.__datasets.has(datasetId)) { - return null; - } - - const dataStore = osparc.store.Data.getInstance(); - return dataStore.getFilesByLocationAndDataset(locationId, datasetId) - .then(data => { - const { - location, - dataset, - files - } = data; - this.__filesToDataset(location, dataset, files); - }); - }, - __getLocationModel: function(locationId) { const rootModel = this.getModel(); const locationModels = rootModel.getChildren(); @@ -405,34 +335,77 @@ qx.Class.define("osparc.file.FilesTree", { return null; }, - __getDatasetModel: function(locationId, datasetId) { - const locationModel = this.__getLocationModel(locationId); - const datasetModels = locationModel.getChildren(); - for (let i=0; i entry["locationId"] == locationId && entry["path"] === path); + if (modelFound) { + return modelFound["model"]; } return null; }, - __itemsToNode: function(files) { - const currentModel = this.getModel(); - this.self().removeLoadingChild(currentModel); + __createModel: function(locationId, path, data) { + const model = qx.data.marshal.Json.createModel(data, true); + this.__pathModels.push({ + locationId, + path, + model, + }); + return model; + }, - files.forEach(file => this.self().attachPathLabel(currentModel.getPathLabel(), file)); - const newModelToAdd = qx.data.marshal.Json.createModel(files, true); - currentModel.getChildren().append(newModelToAdd); - this.setModel(currentModel); - this.fireEvent("filesAddedToTree"); + __itemsToTree: function(locationId, path, items, parentModel) { + if (!parentModel) { + parentModel = this.__getModelFromPath(locationId, path); + } + if (parentModel) { + if ("setLoaded" in parentModel) { + parentModel.setLoaded(true); + } + parentModel.getChildren().removeAll(); + const itemModels = []; + items.forEach(item => { + if (item["file_meta_data"]) { + const datasetId = osparc.data.Converters.pathToDatasetId(path); + const data = osparc.data.Converters.createFileEntry( + item["display_path"], + locationId, + item["path"], + datasetId, + item["file_meta_data"], + ); + const model = this.__createModel(locationId, item["path"], data); + itemModels.push(model); + } else { + const data = osparc.data.Converters.createFolderEntry( + item["display_path"], + locationId, + item["path"] + ); + data.loaded = false; + const model = this.__createModel(locationId, item["path"], data); + itemModels.push(model); + this.__pathModels.push({ + locationId, + path: item["path"], + model, + }); + this.self().addLoadingChild(model); + } + }); + parentModel.getChildren().append(itemModels); + // sort files + osparc.data.Converters.sortModelByLabel(parentModel); - return newModelToAdd; - }, + this.__rerender(parentModel); - __datasetsToLocation: function(locationId, datasets) { - const dataStore = osparc.store.Data.getInstance(); + this.fireEvent("filesAddedToTree"); + } + this.__filesReceived(locationId, path, items); + return parentModel || null; + }, + + __itemsToLocation: function(locationId, items) { const locationModel = this.__getLocationModel(locationId); if (!locationModel) { return; @@ -440,71 +413,38 @@ qx.Class.define("osparc.file.FilesTree", { this.__locations.add(locationId); locationModel.getChildren().removeAll(); let openThis = null; - datasets.forEach(dataset => { - const datasetData = osparc.data.Converters.createDirEntry( - dataset.display_name, + const datasetItems = []; + items.forEach(item => { + const datasetData = osparc.data.Converters.createFolderEntry( + item["display_path"], locationId, - dataset.dataset_id + item["path"] ); - datasetData.isDataset = true; datasetData.loaded = false; datasetData["pathLabel"] = locationModel.getPathLabel().concat(datasetData["label"]); - const datasetModel = qx.data.marshal.Json.createModel(datasetData, true); + const datasetModel = this.__createModel(locationId, item["path"], datasetData); + datasetItems.push(datasetModel); this.self().addLoadingChild(datasetModel); - locationModel.getChildren().append(datasetModel); // add cached files - const datasetId = dataset.dataset_id; - const cachedData = dataStore.getFilesByLocationAndDatasetCached(locationId, datasetId); - if (cachedData) { - this.__filesToDataset(cachedData.location, cachedData.dataset, cachedData.files); - } - - if (this.__hasDatasetNeedToBeLoaded(locationId, datasetId)) { + const path = item["path"]; + if (this.__hasDatasetNeedToBeLoaded(locationId, path)) { openThis = datasetModel; } }); + locationModel.getChildren().append(datasetItems); // sort datasets osparc.data.Converters.sortModelByLabel(locationModel); this.__rerender(locationModel); if (openThis) { - const datasetId = openThis.getItemId(); + const path = openThis.getItemId(); this.openNodeAndParents(openThis); - this.requestDatasetFiles(locationId, datasetId); + this.requestPathItems(locationId, path); } }, - __filesToDataset: function(locationId, datasetId, files, model) { - if (this.__datasets.has(datasetId)) { - return; - } - - const datasetModel = model ? model : this.__getDatasetModel(locationId, datasetId); - if (datasetModel) { - datasetModel.getChildren().removeAll(); - if (files.length) { - const locationData = osparc.data.Converters.fromDSMToVirtualTreeModel(datasetId, files); - const datasetData = locationData[0].children; - datasetData[0].children.forEach(data => { - this.self().attachPathLabel(datasetModel.getPathLabel(), data); - const filesModel = qx.data.marshal.Json.createModel(data, true); - datasetModel.getChildren().append(filesModel); - }); - } - // sort files - osparc.data.Converters.sortModelByLabel(datasetModel); - - this.__rerender(datasetModel); - - this.__datasets.add(datasetId); - this.fireEvent("filesAddedToTree"); - } - - this.__filesReceived(locationId, datasetId, files); - }, - __rerender: function(item) { // Hack to trigger a rebuild of the item. // Without this sometimes the arrow giving access to the children is not rendered @@ -518,7 +458,7 @@ qx.Class.define("osparc.file.FilesTree", { const root = this.getModel(); const list = []; this.__getItemsInTree(root, list); - return list.find(element => element.getChildren && element.getChildren().contains(childItem)); + return list.find(element => element.getChildren && childItem.getPath && element.getChildren().toArray().find(child => child.getPath() === childItem.getPath())); }, findItemId: function(itemId) { @@ -594,13 +534,6 @@ qx.Class.define("osparc.file.FilesTree", { } }, - __itemSelected: function() { - let selectedItem = this.getSelectedItem(); - if (selectedItem) { - this.fireEvent("itemSelected"); - } - }, - __addDragAndDropMechanisms: function(item) { if (this.isDragMechanism()) { this.__createDragMechanism(item); diff --git a/services/static-webserver/client/source/class/osparc/file/FolderContent.js b/services/static-webserver/client/source/class/osparc/file/FolderContent.js index bc6c680d926..98cd8d94080 100644 --- a/services/static-webserver/client/source/class/osparc/file/FolderContent.js +++ b/services/static-webserver/client/source/class/osparc/file/FolderContent.js @@ -22,7 +22,8 @@ qx.Class.define("osparc.file.FolderContent", { this.base(arguments); this.getChildControl("icons-layout"); - this.getChildControl("table"); + const table = this.getChildControl("table"); + this.__attachListenersToTable(table); }, properties: { @@ -55,7 +56,7 @@ qx.Class.define("osparc.file.FolderContent", { "selectionChanged": "qx.event.type.Data", // tap "multiSelectionChanged": "qx.event.type.Data", // tap "openItemSelected": "qx.event.type.Data", // dbltap - "requestDatasetFiles": "qx.event.type.Data", + "requestPathItems": "qx.event.type.Data", }, statics: { @@ -77,6 +78,10 @@ qx.Class.define("osparc.file.FolderContent", { return item; }, + getIcon: function(entry) { + return osparc.file.FilesTree.isDir(entry) ? "@MaterialIcons/folder" : "@MaterialIcons/insert_drive_file"; + }, + T_POS: { TYPE: 0, NAME: 1, @@ -131,16 +136,16 @@ qx.Class.define("osparc.file.FolderContent", { return control || this.base(arguments, id); }, - __convertEntries: function(content) { + __convertChildren: function(children) { const datas = []; - content.forEach(entry => { + children.forEach(child => { const data = { - icon: entry.getIcon ? entry.getIcon() : this.__getIcon(entry), - label: entry.getLabel(), - lastModified: entry.getLastModified ? osparc.utils.Utils.formatDateAndTime(new Date(entry.getLastModified())) : "", - size: entry.getSize ? osparc.utils.Utils.bytesToSize(entry.getSize()) : "", - itemId: entry.getItemId ? entry.getItemId() : null, - entry: entry, + icon: child.getIcon ? child.getIcon() : this.self().getIcon(child), + label: child.getLabel(), + lastModified: child.getLastModified ? osparc.utils.Utils.formatDateAndTime(new Date(child.getLastModified())) : "", + size: child.getSize ? osparc.utils.Utils.bytesToSize(child.getSize()) : "", + itemId: child.getItemId ? child.getItemId() : null, + entry: child, }; datas.push(data); }); @@ -191,14 +196,10 @@ qx.Class.define("osparc.file.FolderContent", { return items; }, - __getIcon: function(entry) { - return osparc.file.FilesTree.isDir(entry) ? "@MaterialIcons/folder" : "@MaterialIcons/insert_drive_file"; - }, - __getEntries: function() { if (this.getFolder()) { const children = this.getFolder().getChildren().toArray(); - return this.__convertEntries(children); + return this.__convertChildren(children); } return []; }, @@ -206,9 +207,9 @@ qx.Class.define("osparc.file.FolderContent", { __applyFolder: function(folder) { if (folder) { if (folder.getLoaded && !folder.getLoaded()) { - this.fireDataEvent("requestDatasetFiles", { + this.fireDataEvent("requestPathItems", { locationId: folder.getLocation(), - datasetId: folder.getPath() + path: folder.getPath() }); } @@ -225,7 +226,6 @@ qx.Class.define("osparc.file.FolderContent", { if (this.getMode() === "list") { const table = this.getChildControl("table"); table.setData(entries); - this.__attachListenersToTableItem(table); } else if (this.getMode() === "icons") { const iconsLayout = this.getChildControl("icons-layout"); iconsLayout.removeAll(); @@ -306,7 +306,7 @@ qx.Class.define("osparc.file.FolderContent", { }, this); }, - __attachListenersToTableItem: function(table) { + __attachListenersToTable: function(table) { table.addListener("cellTap", e => { if (e.getNativeEvent().ctrlKey) { this.setMultiSelect(true); diff --git a/services/static-webserver/client/source/class/osparc/file/FolderViewer.js b/services/static-webserver/client/source/class/osparc/file/FolderViewer.js index 914709dbcdd..932c2ed9381 100644 --- a/services/static-webserver/client/source/class/osparc/file/FolderViewer.js +++ b/services/static-webserver/client/source/class/osparc/file/FolderViewer.js @@ -42,11 +42,11 @@ qx.Class.define("osparc.file.FolderViewer", { const selectedFileLayout = this.getChildControl("selected-file-layout"); this.bind("folder", this.getChildControl("folder-up"), "enabled", { - converter: folder => Boolean(folder && folder.getPathLabel && folder.getPathLabel().length > 1) + converter: folder => Boolean(folder && folder.getDisplayPath && folder.getDisplayPath()) }); this.bind("folder", this.getChildControl("folder-path"), "value", { - converter: folder => folder ? folder.getPathLabel().join(" / ") : this.tr("Select folder") + converter: folder => folder && folder.getDisplayPath ? folder.getDisplayPath() : this.tr("Select folder") }); this.bind("folder", folderContent, "folder"); @@ -69,7 +69,7 @@ qx.Class.define("osparc.file.FolderViewer", { multiSelectButton.setValue(false); }); - folderContent.addListener("requestDatasetFiles", e => this.fireDataEvent("requestDatasetFiles", e.getData())); + folderContent.addListener("requestPathItems", e => this.fireDataEvent("requestPathItems", e.getData())); folderContent.addListener("selectionChanged", e => { const selectionData = e.getData(); selectedFileLayout.setItemSelected(selectionData); @@ -81,9 +81,6 @@ qx.Class.define("osparc.file.FolderViewer", { folderContent.addListener("openItemSelected", e => { const entry = e.getData(); this.fireDataEvent("openItemSelected", entry); - if (osparc.file.FilesTree.isDir(entry)) { - this.setFolder(entry); - } }); }, @@ -100,7 +97,7 @@ qx.Class.define("osparc.file.FolderViewer", { events: { "openItemSelected": "qx.event.type.Data", // dbltap "folderUp": "qx.event.type.Data", - "requestDatasetFiles": "qx.event.type.Data" + "requestPathItems": "qx.event.type.Data" }, members: { @@ -128,6 +125,7 @@ qx.Class.define("osparc.file.FolderViewer", { marginLeft: 10, marginRight: 10 }); + control.bind("value", control, "toolTipText"); header.addAt(control, 1, { flex: 1 }); diff --git a/services/static-webserver/client/source/class/osparc/file/TreeFolderView.js b/services/static-webserver/client/source/class/osparc/file/TreeFolderView.js index d85836d5ed7..fe228c0e7f7 100644 --- a/services/static-webserver/client/source/class/osparc/file/TreeFolderView.js +++ b/services/static-webserver/client/source/class/osparc/file/TreeFolderView.js @@ -84,18 +84,38 @@ qx.Class.define("osparc.file.TreeFolderView", { const folderTree = this.getChildControl("folder-tree"); const folderViewer = this.getChildControl("folder-viewer"); - // Connect elements folderTree.addListener("selectionChanged", () => { - const selectedFolder = folderTree.getSelectedItem(); - if (selectedFolder && (osparc.file.FilesTree.isDir(selectedFolder) || (selectedFolder.getChildren && selectedFolder.getChildren().length))) { - folderViewer.setFolder(selectedFolder); + const selectedModel = folderTree.getSelectedItem(); + if (selectedModel) { + if (selectedModel.getPath() && !selectedModel.getLoaded()) { + folderTree.requestPathItems(selectedModel.getLocation(), selectedModel.getPath()) + .then(pathModel => { + if (osparc.file.FilesTree.isDir(pathModel)) { + folderViewer.setFolder(pathModel); + } + }); + } else if (osparc.file.FilesTree.isDir(selectedModel)) { + folderViewer.setFolder(selectedModel); + } } }, this); folderViewer.addListener("openItemSelected", e => { - const data = e.getData(); - folderTree.openNodeAndParents(data); - folderTree.setSelection(new qx.data.Array([data])); + const selectedModel = e.getData(); + if (selectedModel) { + if (selectedModel.getPath() && !selectedModel.getLoaded()) { + folderTree.requestPathItems(selectedModel.getLocation(), selectedModel.getPath()) + .then(pathModel => { + folderTree.openNodeAndParents(pathModel); + folderTree.setSelection(new qx.data.Array([pathModel])); + if (osparc.file.FilesTree.isDir(pathModel)) { + folderViewer.setFolder(pathModel); + } + }); + } else if (osparc.file.FilesTree.isDir(selectedModel)) { + folderViewer.setFolder(selectedModel); + } + } }, this); folderViewer.addListener("folderUp", e => { @@ -103,14 +123,11 @@ qx.Class.define("osparc.file.TreeFolderView", { const parent = folderTree.getParent(currentFolder); if (parent) { folderTree.setSelection(new qx.data.Array([parent])); - folderViewer.setFolder(parent); + if (osparc.file.FilesTree.isDir(parent)) { + folderViewer.setFolder(parent); + } } }, this); - - folderViewer.addListener("requestDatasetFiles", e => { - const data = e.getData(); - folderTree.requestDatasetFiles(data.locationId, data.datasetId); - }, this); }, openPath: function(path) { diff --git a/services/static-webserver/client/source/class/osparc/pricing/PlanListItem.js b/services/static-webserver/client/source/class/osparc/pricing/PlanListItem.js index a76527e35fd..9906b10ab3a 100644 --- a/services/static-webserver/client/source/class/osparc/pricing/PlanListItem.js +++ b/services/static-webserver/client/source/class/osparc/pricing/PlanListItem.js @@ -120,7 +120,6 @@ qx.Class.define("osparc.pricing.PlanListItem", { control = new qx.ui.basic.Label().set({ font: "text-14", alignY: "middle", - width: 35, }); this._add(control, { row: 0, @@ -132,7 +131,6 @@ qx.Class.define("osparc.pricing.PlanListItem", { control = new qx.ui.basic.Label().set({ font: "text-14", alignY: "middle", - width: 80, }); this._add(control, { row: 0, @@ -164,7 +162,6 @@ qx.Class.define("osparc.pricing.PlanListItem", { control = new qx.ui.basic.Label().set({ font: "text-14", alignY: "middle", - width: 60, }); this.bind("classification", control, "value"); this._add(control, { diff --git a/services/static-webserver/client/source/class/osparc/store/Data.js b/services/static-webserver/client/source/class/osparc/store/Data.js index d592f78ea67..92f66acc1e2 100644 --- a/services/static-webserver/client/source/class/osparc/store/Data.js +++ b/services/static-webserver/client/source/class/osparc/store/Data.js @@ -38,14 +38,10 @@ qx.Class.define("osparc.store.Data", { members: { __locationsCached: null, __datasetsByLocationCached: null, - __filesByLocationAndDatasetCached: null, resetCache: function() { this.__locationsCached = []; this.__datasetsByLocationCached = {}; - this.__filesByLocationAndDatasetCached = {}; - - osparc.store.Store.getInstance().reset("storageLocations"); }, getLocationsCached: function() { @@ -62,8 +58,7 @@ qx.Class.define("osparc.store.Data", { if (cachedData) { resolve(cachedData); } else { - // Get available storage locations - osparc.data.Resources.get("storageLocations") + osparc.data.Resources.fetch("storageLocations", "getLocations") .then(locations => { // Add them to cache this.__locationsCached = locations; @@ -90,14 +85,13 @@ qx.Class.define("osparc.store.Data", { }, getDatasetsByLocation: function(locationId) { - const emptyData = { + const data = { location: locationId, - datasets: [] + items: [] }; return new Promise((resolve, reject) => { - // Get list of datasets if (locationId === 1 && !osparc.data.Permissions.getInstance().canDo("storage.datcore.read")) { - reject(emptyData); + reject(data); } const cachedData = this.getDatasetsByLocationCached(locationId); @@ -109,96 +103,40 @@ qx.Class.define("osparc.store.Data", { locationId } }; - osparc.data.Resources.fetch("storageDatasets", "getByLocation", params) - .then(datasets => { - const data = { - location: locationId, - datasets: [] - }; - if (datasets && datasets.length>0) { - data.datasets = datasets; + osparc.data.Resources.fetch("storagePaths", "getDatasets", params) + .then(pagResp => { + if (pagResp["items"] && pagResp["items"].length>0) { + data.items = pagResp["items"]; } // Add it to cache - this.__datasetsByLocationCached[locationId] = data.datasets; + this.__datasetsByLocationCached[locationId] = data.items; resolve(data); }) .catch(err => { console.error(err); - reject(emptyData); + reject(data); }); } }); }, - getFilesByLocationAndDatasetCached: function(locationId, datasetId) { - const cache = this.__filesByLocationAndDatasetCached; - if (locationId in cache && datasetId in cache[locationId]) { - const data = { - location: locationId, - dataset: datasetId, - files: cache[locationId][datasetId] - }; - return data; - } - return null; - }, - - getFilesByLocationAndDataset: function(locationId, datasetId) { - const emptyData = { - location: locationId, - dataset: datasetId, - files: [] - }; + getItemsByLocationAndPath: function(locationId, path) { return new Promise((resolve, reject) => { // Get list of file meta data if (locationId === 1 && !osparc.data.Permissions.getInstance().canDo("storage.datcore.read")) { - reject(emptyData); - } - - const cachedData = this.getFilesByLocationAndDatasetCached(locationId, datasetId); - if (cachedData) { - resolve(cachedData); - } else { - const params = { - url: { - locationId, - datasetId - } - }; - osparc.data.Resources.fetch("storageFiles", "getByLocationAndDataset", params) - .then(files => { - const data = { - location: locationId, - dataset: datasetId, - files: files && files.length>0 ? files : [] - }; - // Add it to cache - if (!(locationId in this.__filesByLocationAndDatasetCached)) { - this.__filesByLocationAndDatasetCached[locationId] = {}; - } - this.__filesByLocationAndDatasetCached[locationId][datasetId] = data.files; - resolve(data); - }) - .catch(err => { - console.error(err); - reject(emptyData); - }); + reject([]); } - }); - }, - getNodeFiles: function(nodeId) { - return new Promise((resolve, reject) => { const params = { url: { - nodeId: encodeURIComponent(nodeId) + locationId, + path, } }; - osparc.data.Resources.fetch("storageFiles", "getByNode", params) - .then(files => { - console.log("Node Files", files); - if (files && files.length>0) { - resolve(files); + osparc.data.Resources.fetch("storagePaths", "getPaths", params) + .then(pagResp => { + if (pagResp["items"] && pagResp["items"].length>0) { + resolve(pagResp["items"]); } else { resolve([]); } @@ -241,6 +179,7 @@ qx.Class.define("osparc.store.Data", { resolve(presignedLinkData); }) .catch(err => { + console.log("remove the FP"); console.error(err); reject(err); }); @@ -264,7 +203,7 @@ qx.Class.define("osparc.store.Data", { fileUuid: encodeURIComponent(fileUuid) } }; - osparc.data.Resources.fetch("storageFiles", "put", params) + osparc.data.Resources.fetch("storageFiles", "copy", params) .then(files => { const data = { data: files, diff --git a/services/static-webserver/client/source/class/osparc/widget/StudyDataManager.js b/services/static-webserver/client/source/class/osparc/widget/StudyDataManager.js index 2626265b0cf..4671432bfad 100644 --- a/services/static-webserver/client/source/class/osparc/widget/StudyDataManager.js +++ b/services/static-webserver/client/source/class/osparc/widget/StudyDataManager.js @@ -42,16 +42,10 @@ qx.Class.define("osparc.widget.StudyDataManager", { this._setLayout(new qx.ui.layout.VBox(10)); - if (studyId) { - this.set({ - studyId - }); - } + this.setStudyId(studyId); if (nodeId) { - this.set({ - nodeId - }); + this.setNodeId(nodeId); } this.__buildLayout(); @@ -113,11 +107,10 @@ qx.Class.define("osparc.widget.StudyDataManager", { const foldersTree = treeFolderView.getChildControl("folder-tree"); foldersTree.resetCache(); - if (this.getStudyId()) { - foldersTree.populateStudyTree(this.getStudyId()); - } if (this.getNodeId()) { - foldersTree.populateNodeTree(this.getNodeId()); + foldersTree.populateNodeTree(this.getStudyId(), this.getNodeId()); + } else if (this.getStudyId()) { + foldersTree.populateStudyTree(this.getStudyId()); } const folderViewer = treeFolderView.getChildControl("folder-viewer"); @@ -144,13 +137,12 @@ qx.Class.define("osparc.widget.StudyDataManager", { treeFolderView.openPath(path); }; - if (this.getStudyId()) { - foldersTree.populateStudyTree(this.getStudyId()) + if (this.getNodeId()) { + foldersTree.populateNodeTree(this.getStudyId(), this.getNodeId()) .then(() => openSameFolder()) .catch(err => console.error(err)); - } - if (this.getNodeId()) { - foldersTree.populateNodeTree(this.getNodeId()) + } else if (this.getStudyId()) { + foldersTree.populateStudyTree(this.getStudyId()) .then(() => openSameFolder()) .catch(err => console.error(err)); } diff --git a/services/static-webserver/client/source/resource/osparc/ui_config.json b/services/static-webserver/client/source/resource/osparc/ui_config.json index 2616e8a0709..54be5e1da8c 100644 --- a/services/static-webserver/client/source/resource/osparc/ui_config.json +++ b/services/static-webserver/client/source/resource/osparc/ui_config.json @@ -106,14 +106,18 @@ "resourceType": "service", "expectedKey": "simcore/services/dynamic/s4l-ui", "title": "Sim4Life", - "newStudyLabel": "New S4L project", + "newStudyLabel": "New Sim4Life", "idToWidget": "startS4LButton" }, { "resourceType": "service", "expectedKey": "simcore/services/dynamic/s4l-jupyter", "title": "Jupyter Lab", - "icon": "https://upload.wikimedia.org/wikipedia/commons/3/38/Jupyter_logo.svg", "newStudyLabel": "New S4L Jupyter Lab" + }, { + "resourceType": "service", + "expectedKey": "simcore/services/dynamic/iseg-web", + "title": "Segmentation", + "newStudyLabel": "Segmentation" }] } }, @@ -123,14 +127,18 @@ "resourceType": "service", "expectedKey": "simcore/services/dynamic/s4l-ui", "title": "Sim4Life", - "newStudyLabel": "New S4L project", + "newStudyLabel": "New Sim4Life", "idToWidget": "startS4LButton" }, { "resourceType": "service", "expectedKey": "simcore/services/dynamic/s4l-jupyter", - "icon": "https://upload.wikimedia.org/wikipedia/commons/3/38/Jupyter_logo.svg", "title": "Jupyter Lab", "newStudyLabel": "New S4L Jupyter Lab" + }, { + "resourceType": "service", + "expectedKey": "simcore/services/dynamic/iseg-web", + "title": "Segmentation", + "newStudyLabel": "Segmentation" }] } }, diff --git a/tests/e2e-playwright/tests/sleepers/test_sleepers.py b/tests/e2e-playwright/tests/sleepers/test_sleepers.py index 0940b37609d..72a506ef979 100644 --- a/tests/e2e-playwright/tests/sleepers/test_sleepers.py +++ b/tests/e2e-playwright/tests/sleepers/test_sleepers.py @@ -217,7 +217,7 @@ def test_sleepers( sleeper.click() # waiting for this response is not enough, the frontend needs some time to show the files # therefore _get_file_names is wrapped with tenacity - with page.expect_response(re.compile(r"files/metadata")): + with page.expect_response(re.compile(r"paths\?file_filter=")): page.get_by_test_id("nodeFilesBtn").click() output_file_names_found = _get_file_names(page) diff --git a/tests/e2e/tutorials/tutorialBase.js b/tests/e2e/tutorials/tutorialBase.js index 76c7edcb3de..936649b04eb 100644 --- a/tests/e2e/tutorials/tutorialBase.js +++ b/tests/e2e/tutorials/tutorialBase.js @@ -26,6 +26,7 @@ class TutorialBase { this.__responsesQueue = null; this.__services = null; + this.__studyId = null; this.__interval = null; @@ -216,8 +217,9 @@ class TutorialBase { let resp = null; try { resp = await this.__responsesQueue.waitUntilResponse(":open"); - } - catch (err) { + const studyId = this.__studyId = resp["data"]["uuid"]; + console.log("Study ID:", studyId); + } catch (err) { console.error("Error:", this.__templateName, "could not be started", err); throw (err); } @@ -234,10 +236,9 @@ class TutorialBase { await auto.dashboardNewTIPlan(this.__page); await this.__responsesQueue.waitUntilResponse("projects?from_study="); resp = await this.__responsesQueue.waitUntilResponse(":open"); - const studyId = resp["data"]["uuid"]; + const studyId = this.__studyId = resp["data"]["uuid"]; console.log("Study ID:", studyId); - } - catch (err) { + } catch (err) { console.error(`Error: Classic TI could not be started:\n`, err); throw (err); } @@ -254,10 +255,9 @@ class TutorialBase { await this.waitFor(2000); await auto.dashboardStartSim4LifeLite(this.__page); resp = await this.__responsesQueue.waitUntilResponse(":open"); - const studyId = resp["data"]["uuid"]; + const studyId = this.__studyId = resp["data"]["uuid"]; console.log("Study ID:", studyId); - } - catch (err) { + } catch (err) { console.error(`Error: Sim4Life Lite could not be started:\n`, err); throw (err); } @@ -274,10 +274,9 @@ class TutorialBase { await this.__goTo(); resp = await this.__responsesQueue.waitUntilResponse(":open", openStudyTimeout); await this.__printMe(); - const studyId = resp["data"]["uuid"]; + const studyId = this.__studyId = resp["data"]["uuid"]; console.log("Study ID:", studyId); - } - catch (err) { + } catch (err) { console.error("Error:", this.__templateName, "could not be started", err); throw (err); } @@ -294,10 +293,9 @@ class TutorialBase { assert(templateFound, "Expected template, got nothing. TIP: did you inject templates in database??") await this.__responsesQueue.waitUntilResponse("projects?from_study="); resp = await this.__responsesQueue.waitUntilResponse(":open"); - const studyId = resp["data"]["uuid"]; + const studyId = this.__studyId = resp["data"]["uuid"]; console.log("Study ID:", studyId); - } - catch (err) { + } catch (err) { console.error(`Error: "${this.__templateName}" template could not be started:\n`, err); throw (err); } @@ -314,10 +312,9 @@ class TutorialBase { const serviceFound = await auto.dashboardOpenService(this.__page, this.__templateName); assert(serviceFound, "Expected service, got nothing. TIP: is it available??"); resp = await this.__responsesQueue.waitUntilResponse(":open"); - const studyId = resp["data"]["uuid"]; + const studyId = this.__studyId = resp["data"]["uuid"]; console.log("Study ID:", studyId); - } - catch (err) { + } catch (err) { console.error(`Error: "${this.__templateName}" service could not be started:\n`, err); throw (err); } @@ -448,24 +445,26 @@ class TutorialBase { } async openNodeFiles(nodeId) { - this.__responsesQueue.addResponseListener("storage/locations/0/files/metadata?uuid_filter=" + nodeId); + const pathFilter = `${this.__studyId}/${nodeId}`; + const path = "storage/locations/0/files/paths?file_filter=" + pathFilter; + this.__responsesQueue.addResponseListener(path); await auto.openNodeFiles(this.__page); try { - await this.__responsesQueue.waitUntilResponse("storage/locations/0/files/metadata?uuid_filter=" + nodeId); - } - catch (err) { + await this.__responsesQueue.waitUntilResponse(path); + } catch (err) { console.error("Error: open node files", err); throw (err); } } async openNodeFilesAppMode(nodeId) { - this.__responsesQueue.addResponseListener("storage/locations/0/files/metadata?uuid_filter=" + nodeId); + const pathFilter = `${this.__studyId}/${nodeId}`; + const path = "storage/locations/0/files/paths?file_filter=" + pathFilter; + this.__responsesQueue.addResponseListener(path); await auto.openNodeFilesAppMode(this.__page); try { - await this.__responsesQueue.waitUntilResponse("storage/locations/0/files/metadata?uuid_filter=" + nodeId); - } - catch (err) { + await this.__responsesQueue.waitUntilResponse(path); + } catch (err) { console.error("Error: open node files", err); throw (err); } @@ -532,8 +531,7 @@ class TutorialBase { const nodeId = await auto.openNode(this.__page, nodePos); await this.openNodeFiles(nodeId); await this.__checkNItemsInFolder(fileNames, openOutputsFolder); - } - catch (err) { + } catch (err) { console.error("Error: Checking Node Outputs:", err); throw (err) } @@ -543,8 +541,7 @@ class TutorialBase { try { await this.openNodeFilesAppMode(nodeId); await this.__checkNItemsInFolder(fileNames, openOutputsFolder); - } - catch (err) { + } catch (err) { console.error("Error: Checking Node Outputs:", err); throw (err) }