Skip to content

Commit 97b0f7e

Browse files
Merge branch 'master' into add-listing-of-comp-runs
2 parents 9bc685a + e0f204c commit 97b0f7e

File tree

7 files changed

+111
-78
lines changed

7 files changed

+111
-78
lines changed

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

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -260,14 +260,15 @@ qx.Class.define("osparc.dashboard.NewPlusMenu", {
260260
}
261261
},
262262

263-
__addFromResourceButton: function(menuButton, category) {
264-
let idx = null;
263+
__addFromResourceButton: function(menuButton, category, idx = null) {
265264
if (category) {
266265
idx = this.__getLastIdxFromCategory(category);
267266
}
268-
if (idx) {
267+
if (category && idx) {
269268
menuButton["categoryId"] = category;
270269
this.addAt(menuButton, idx+1);
270+
} else if (idx) {
271+
this.addAt(menuButton, idx);
271272
} else {
272273
this.addAt(menuButton, this.__itemIdx);
273274
this.__itemIdx++;
@@ -369,9 +370,12 @@ qx.Class.define("osparc.dashboard.NewPlusMenu", {
369370
addListenerToButton(menuButton, latestMetadata);
370371
} else if ("myMostUsed" in buttonConfig) {
371372
const excludeFrontend = true;
372-
const excludeDeprecated = true
373+
const excludeDeprecated = true;
374+
const old = this.__itemIdx;
375+
this.__itemIdx += buttonConfig["myMostUsed"];
373376
osparc.store.Services.getServicesLatestList(excludeFrontend, excludeDeprecated)
374-
.then(servicesList => {
377+
.then(srvList => {
378+
const servicesList = srvList.filter(srv => srv !== null);
375379
osparc.service.Utils.sortObjectsBasedOn(servicesList, {
376380
"sort": "hits",
377381
"order": "down"
@@ -385,7 +389,7 @@ qx.Class.define("osparc.dashboard.NewPlusMenu", {
385389
allowGrowX: true,
386390
});
387391
this.__addIcon(menuButton, null, latestMetadata);
388-
this.__addFromResourceButton(menuButton, buttonConfig["category"]);
392+
this.__addFromResourceButton(menuButton, buttonConfig["category"], old+i);
389393
addListenerToButton(menuButton, latestMetadata);
390394
}
391395
}

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,7 @@ qx.Class.define("osparc.dashboard.ServiceBrowser", {
6666
const excludeFrontend = true;
6767
const excludeDeprecated = true
6868
osparc.store.Services.getServicesLatestList(excludeFrontend, excludeDeprecated)
69-
.then(servicesList => this.__setServicesToList(servicesList));
69+
.then(servicesList => this.__setServicesToList(servicesList.filter(service => service !== null)));
7070
},
7171

7272
_updateServiceData: function(serviceData) {

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

Lines changed: 0 additions & 68 deletions
Original file line numberDiff line numberDiff line change
@@ -888,78 +888,10 @@ qx.Class.define("osparc.dashboard.StudyBrowser", {
888888
// this one is different since it groups all new buttons in one new button
889889
this.__addTIPPlusButton();
890890
break;
891-
default:
892-
this.__addPlusButtons();
893-
break;
894891
}
895892
}
896893
},
897894

898-
__addPlusButtons: function() {
899-
const plusButtonConfig = osparc.store.Products.getInstance().getNewStudiesUiConfig();
900-
if (plusButtonConfig) {
901-
plusButtonConfig["resources"].forEach(newStudyData => {
902-
if (newStudyData["resourceType"] === "study") {
903-
this.__addEmptyStudyPlusButton(newStudyData);
904-
} else if (newStudyData["resourceType"] === "service") {
905-
this.__addNewStudyFromServiceButton(newStudyData);
906-
}
907-
});
908-
}
909-
},
910-
911-
__addEmptyStudyPlusButton: function(newStudyData) {
912-
const mode = this._resourcesContainer.getMode();
913-
const defTitle = this.tr("Empty") + " " + osparc.product.Utils.getStudyAlias({
914-
firstUpperCase: true
915-
});
916-
const title = newStudyData["title"] || defTitle;
917-
const desc = newStudyData["description"] || this.tr("Start with an empty study");
918-
const newEmptyStudyBtn = (mode === "grid") ? new osparc.dashboard.GridButtonNew(title, desc) : new osparc.dashboard.ListButtonNew(title, desc);
919-
newEmptyStudyBtn.setCardKey("new-study");
920-
newEmptyStudyBtn.subscribeToFilterGroup("searchBarFilter");
921-
osparc.utils.Utils.setIdToWidget(newEmptyStudyBtn, newStudyData["idToWidget"]);
922-
newEmptyStudyBtn.addListener("tap", () => this.__newEmptyStudyBtnClicked(newStudyData["newStudyLabel"]));
923-
this._resourcesContainer.addNonResourceCard(newEmptyStudyBtn);
924-
},
925-
926-
__addNewStudyFromServiceButton: function(newStudyData) {
927-
if ("expectedKey" in newStudyData) {
928-
const key = newStudyData["expectedKey"];
929-
const latestMetadata = osparc.store.Services.getLatest(key);
930-
if (!latestMetadata) {
931-
return;
932-
}
933-
const title = newStudyData.title + " " + osparc.service.Utils.extractVersionDisplay(latestMetadata);
934-
const desc = newStudyData.description;
935-
const mode = this._resourcesContainer.getMode();
936-
const newStudyFromServiceButton = (mode === "grid") ? new osparc.dashboard.GridButtonNew(title, desc) : new osparc.dashboard.ListButtonNew(title, desc);
937-
newStudyFromServiceButton.setCardKey("new-"+key);
938-
if (newStudyData["idToWidget"]) {
939-
osparc.utils.Utils.setIdToWidget(newStudyFromServiceButton, newStudyData["idToWidget"]);
940-
}
941-
newStudyFromServiceButton.addListener("tap", () => this.__newStudyFromServiceBtnClicked(latestMetadata["key"], latestMetadata["version"], newStudyData.newStudyLabel));
942-
this._resourcesContainer.addNonResourceCard(newStudyFromServiceButton);
943-
} else if ("myMostUsed" in newStudyData) {
944-
const excludeFrontend = true;
945-
const excludeDeprecated = true
946-
osparc.store.Services.getServicesLatestList(excludeFrontend, excludeDeprecated)
947-
.then(servicesList => {
948-
osparc.service.Utils.sortObjectsBasedOn(servicesList, {
949-
"sort": "hits",
950-
"order": "down"
951-
});
952-
for (let i=0; i<newStudyData["myMostUsed"]; i++) {
953-
const latestMetadata = servicesList[i];
954-
const mode = this._resourcesContainer.getMode();
955-
const newStudyFromServiceButton = (mode === "grid") ? new osparc.dashboard.GridButtonNew(latestMetadata["name"]) : new osparc.dashboard.ListButtonNew(latestMetadata["name"]);
956-
newStudyFromServiceButton.addListener("tap", () => this.__newStudyFromServiceBtnClicked(latestMetadata["key"], latestMetadata["version"], latestMetadata["name"]));
957-
this._resourcesContainer.addNonResourceCard(newStudyFromServiceButton);
958-
}
959-
});
960-
}
961-
},
962-
963895
__addTIPPlusButton: function() {
964896
const mode = this._resourcesContainer.getMode();
965897
const title = this.tr("New Plan");

services/static-webserver/client/source/class/osparc/workbench/ServiceCatalog.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -201,7 +201,7 @@ qx.Class.define("osparc.workbench.ServiceCatalog", {
201201
const excludeDeprecated = true;
202202
osparc.store.Services.getServicesLatestList(excludeFrontend, excludeDeprecated)
203203
.then(servicesList => {
204-
this.__servicesLatest = servicesList;
204+
this.__servicesLatest = servicesList.filter(service => service !== null);
205205
this.__updateList();
206206
});
207207
},

services/storage/src/simcore_service_storage/simcore_s3_dsm.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1312,6 +1312,7 @@ async def create_s3_export(
13121312

13131313
await create_and_upload_export(
13141314
get_s3_client(self.app),
1315+
ProjectRepository.instance(get_db_engine(self.app)),
13151316
self.simcore_bucket_name,
13161317
source_object_keys=source_object_keys,
13171318
destination_object_keys=destination_object_key,

services/storage/src/simcore_service_storage/utils/simcore_s3_dsm_utils.py

Lines changed: 42 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,9 @@
88
from aws_library.s3._constants import STREAM_READER_CHUNK_SIZE
99
from aws_library.s3._models import S3ObjectKey
1010
from models_library.api_schemas_storage.storage_schemas import S3BucketName
11-
from models_library.projects import ProjectID
11+
from models_library.projects import ProjectID, ProjectIDStr
1212
from models_library.projects_nodes_io import (
13+
NodeIDStr,
1314
SimcoreS3DirectoryID,
1415
SimcoreS3FileID,
1516
StorageFileID,
@@ -27,6 +28,7 @@
2728
from ..models import FileMetaData, FileMetaDataAtDB, GenericCursor, PathMetaData
2829
from ..modules.db.access_layer import AccessLayerRepository
2930
from ..modules.db.file_meta_data import FileMetaDataRepository, TotalChildren
31+
from ..modules.db.projects import ProjectRepository
3032
from .utils import convert_db_to_model
3133

3234

@@ -165,17 +167,55 @@ def _base_path_parent(base_path: UserSelectionStr, s3_object: S3ObjectKey) -> st
165167
return f"{result}"
166168

167169

170+
def _get_project_ids(user_selecton: set[UserSelectionStr]) -> list[ProjectID]:
171+
results = []
172+
for selected in user_selecton:
173+
project_id = ProjectID(Path(selected).parts[0])
174+
results.append(project_id)
175+
return results
176+
177+
178+
def _replace_node_id_project_id_in_path(
179+
ids_names_map: dict[ProjectID, dict[ProjectIDStr | NodeIDStr, str]], path: str
180+
) -> str:
181+
path_parts = Path(path).parts
182+
if len(path_parts) == 0:
183+
return path
184+
185+
if len(path_parts) == 1:
186+
return ids_names_map[ProjectID(path)][path].replace("/", "_")
187+
188+
project_id_str = path_parts[0]
189+
project_id = ProjectID(project_id_str)
190+
node_id_str = path_parts[1]
191+
return "/".join(
192+
(
193+
ids_names_map[project_id][project_id_str].replace("/", "_"),
194+
ids_names_map[project_id][node_id_str].replace("/", "_"),
195+
*path_parts[2:],
196+
)
197+
)
198+
199+
168200
async def create_and_upload_export(
169201
s3_client: SimcoreS3API,
202+
project_repository: ProjectRepository,
170203
bucket: S3BucketName,
171204
*,
172205
source_object_keys: set[tuple[UserSelectionStr, StorageFileID]],
173206
destination_object_keys: StorageFileID,
174207
progress_bar: ProgressBarData,
175208
) -> None:
209+
ids_names_map = await project_repository.get_project_id_and_node_id_to_names_map(
210+
project_uuids=_get_project_ids(user_selecton={x[0] for x in source_object_keys})
211+
)
212+
176213
archive_entries: ArchiveEntries = [
177214
(
178-
_base_path_parent(selection, s3_object),
215+
_base_path_parent(
216+
_replace_node_id_project_id_in_path(ids_names_map, selection),
217+
_replace_node_id_project_id_in_path(ids_names_map, s3_object),
218+
),
179219
await s3_client.get_bytes_streamer_from_object(bucket, s3_object),
180220
)
181221
for (selection, s3_object) in source_object_keys

services/storage/tests/unit/test_simcore_s3_dsm_utils.py

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,16 @@
11
from pathlib import Path
2+
from typing import Final
3+
from uuid import UUID
24

35
import pytest
46
from aws_library.s3._models import S3ObjectKey
7+
from models_library.projects import ProjectID, ProjectIDStr
8+
from models_library.projects_nodes_io import NodeIDStr
9+
from simcore_service_storage.models import NodeID
510
from simcore_service_storage.utils.simcore_s3_dsm_utils import (
611
UserSelectionStr,
712
_base_path_parent,
13+
_replace_node_id_project_id_in_path,
814
compute_file_id_prefix,
915
ensure_user_selection_from_same_base_directory,
1016
)
@@ -73,3 +79,53 @@ def test_ensure_user_selection_from_same_base_directory(
7379
ensure_user_selection_from_same_base_directory([f"{x}" for x in user_selection])
7480
== expected
7581
)
82+
83+
84+
_PID1: Final[ProjectID] = UUID(int=1)
85+
_PID2: Final[ProjectID] = UUID(int=2)
86+
_NID1: Final[NodeID] = UUID(int=3)
87+
_NID2: Final[NodeID] = UUID(int=4)
88+
_IDS_NAMES_MAP: Final[dict[ProjectID, dict[ProjectIDStr | NodeIDStr, str]]] = {
89+
_PID1: {
90+
f"{_PID1}": "project one",
91+
f"{_NID1}": "project one -> node one",
92+
f"{_NID2}": "project one -> node two",
93+
},
94+
_PID2: {
95+
f"{_PID2}": "/project/two/",
96+
f"{_NID1}": "/project/two/->/node/one/",
97+
f"{_NID2}": "/project/two/->/node/two/",
98+
},
99+
}
100+
101+
102+
@pytest.mark.parametrize(
103+
"path, expected",
104+
[
105+
("", ""),
106+
(f"{_PID1}", "project one"),
107+
(f"{_PID1}/{_NID1}", "project one/project one -> node one"),
108+
(f"{_PID1}/{_NID1}/something", "project one/project one -> node one/something"),
109+
(f"{_PID1}/{_NID1}/{_NID2}", f"project one/project one -> node one/{_NID2}"),
110+
(
111+
f"{_PID1}/{_NID1}/{_NID2}/something",
112+
f"project one/project one -> node one/{_NID2}/something",
113+
),
114+
(f"{_PID2}", "_project_two_"),
115+
(f"{_PID2}/{_NID1}", "_project_two_/_project_two_->_node_one_"),
116+
(
117+
f"{_PID2}/{_NID1}/something",
118+
"_project_two_/_project_two_->_node_one_/something",
119+
),
120+
(
121+
f"{_PID2}/{_NID1}/{_NID2}",
122+
f"_project_two_/_project_two_->_node_one_/{_NID2}",
123+
),
124+
(
125+
f"{_PID2}/{_NID1}/{_NID2}/something",
126+
f"_project_two_/_project_two_->_node_one_/{_NID2}/something",
127+
),
128+
],
129+
)
130+
def test__replace_node_id_project_id_in_path(path: str, expected: str):
131+
assert _replace_node_id_project_id_in_path(_IDS_NAMES_MAP, path) == expected

0 commit comments

Comments
 (0)