Skip to content

Commit c03fb85

Browse files
Merge branch 'master' into clean-pydantic-model-dump-warnings
2 parents 175a5dd + df0b6c7 commit c03fb85

File tree

52 files changed

+1265
-569
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

52 files changed

+1265
-569
lines changed

api/specs/web-server/_storage.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
StorageAsyncJobResult,
2727
StorageAsyncJobStatus,
2828
StorageLocationPathParams,
29+
StoragePathComputeSizeParams,
2930
)
3031
from models_library.generics import Envelope
3132
from models_library.projects_nodes_io import LocationID
@@ -68,6 +69,15 @@ async def list_storage_paths(
6869
"""Lists the files/directories in WorkingDirectory"""
6970

7071

72+
@router.post(
73+
"/storage/locations/{location_id}/paths/{path}:size",
74+
response_model=Envelope[StorageAsyncJobGet],
75+
status_code=status.HTTP_202_ACCEPTED,
76+
)
77+
async def compute_path_size(_path: Annotated[StoragePathComputeSizeParams, Depends()]):
78+
"""Compute the size of a path"""
79+
80+
7181
@router.get(
7282
"/storage/locations/{location_id}/datasets",
7383
response_model=Envelope[list[DatasetMetaData]],

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

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,6 @@
11
from pathlib import Path
22
from typing import Annotated, Any
33

4-
from models_library.api_schemas_storage.storage_schemas import (
5-
DEFAULT_NUMBER_OF_PATHS_PER_PAGE,
6-
MAX_NUMBER_OF_PATHS_PER_PAGE,
7-
)
84
from pydantic import BaseModel, Field
95

106
from ..api_schemas_rpc_async_jobs.async_jobs import (
@@ -14,6 +10,10 @@
1410
AsyncJobStatus,
1511
)
1612
from ..api_schemas_storage.data_export_async_jobs import DataExportTaskStartInput
13+
from ..api_schemas_storage.storage_schemas import (
14+
DEFAULT_NUMBER_OF_PATHS_PER_PAGE,
15+
MAX_NUMBER_OF_PATHS_PER_PAGE,
16+
)
1717
from ..progress_bar import ProgressReport
1818
from ..projects_nodes_io import LocationID, StorageFileID
1919
from ..rest_pagination import (
@@ -26,6 +26,10 @@ class StorageLocationPathParams(BaseModel):
2626
location_id: LocationID
2727

2828

29+
class StoragePathComputeSizeParams(StorageLocationPathParams):
30+
path: Path
31+
32+
2933
class ListPathsQueryParams(InputSchema, CursorQueryParameters):
3034
file_filter: Path | None = None
3135

packages/service-library/src/servicelib/rabbitmq/rpc_interfaces/async_jobs/async_jobs.py

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ async def abort(
2323
*,
2424
rpc_namespace: RPCNamespace,
2525
job_id: AsyncJobId,
26-
job_id_data: AsyncJobNameData
26+
job_id_data: AsyncJobNameData,
2727
) -> AsyncJobAbort:
2828
result = await rabbitmq_rpc_client.request(
2929
rpc_namespace,
@@ -41,7 +41,7 @@ async def get_status(
4141
*,
4242
rpc_namespace: RPCNamespace,
4343
job_id: AsyncJobId,
44-
job_id_data: AsyncJobNameData
44+
job_id_data: AsyncJobNameData,
4545
) -> AsyncJobStatus:
4646
result = await rabbitmq_rpc_client.request(
4747
rpc_namespace,
@@ -59,7 +59,7 @@ async def get_result(
5959
*,
6060
rpc_namespace: RPCNamespace,
6161
job_id: AsyncJobId,
62-
job_id_data: AsyncJobNameData
62+
job_id_data: AsyncJobNameData,
6363
) -> AsyncJobResult:
6464
result = await rabbitmq_rpc_client.request(
6565
rpc_namespace,
@@ -77,7 +77,7 @@ async def list_jobs(
7777
*,
7878
rpc_namespace: RPCNamespace,
7979
filter_: str,
80-
job_id_data: AsyncJobNameData
80+
job_id_data: AsyncJobNameData,
8181
) -> list[AsyncJobGet]:
8282
result: list[AsyncJobGet] = await rabbitmq_rpc_client.request(
8383
rpc_namespace,
@@ -95,7 +95,7 @@ async def submit_job(
9595
rpc_namespace: RPCNamespace,
9696
method_name: str,
9797
job_id_data: AsyncJobNameData,
98-
**kwargs
98+
**kwargs,
9999
) -> AsyncJobGet:
100100
result = await rabbitmq_rpc_client.request(
101101
rpc_namespace,
@@ -104,5 +104,5 @@ async def submit_job(
104104
**kwargs,
105105
timeout_s=_DEFAULT_TIMEOUT_S,
106106
)
107-
assert isinstance(result, AsyncJobGet)
107+
assert isinstance(result, AsyncJobGet) # nosec
108108
return result

packages/service-library/src/servicelib/rabbitmq/rpc_interfaces/storage/__init__.py

Whitespace-only changes.
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
from pathlib import Path
2+
3+
from models_library.api_schemas_rpc_async_jobs.async_jobs import (
4+
AsyncJobNameData,
5+
)
6+
from models_library.api_schemas_storage import STORAGE_RPC_NAMESPACE
7+
from models_library.api_schemas_webserver.storage import StorageAsyncJobGet
8+
from models_library.projects_nodes_io import LocationID
9+
from models_library.rabbitmq_basic_types import RPCMethodName
10+
from models_library.users import UserID
11+
12+
from ..._client_rpc import RabbitMQRPCClient
13+
from ..async_jobs.async_jobs import submit_job
14+
15+
16+
async def compute_path_size(
17+
client: RabbitMQRPCClient,
18+
*,
19+
user_id: UserID,
20+
product_name: str,
21+
location_id: LocationID,
22+
path: Path,
23+
) -> tuple[StorageAsyncJobGet, AsyncJobNameData]:
24+
job_id_data = AsyncJobNameData(user_id=user_id, product_name=product_name)
25+
async_job_rpc_get = await submit_job(
26+
rabbitmq_rpc_client=client,
27+
rpc_namespace=STORAGE_RPC_NAMESPACE,
28+
method_name=RPCMethodName("compute_path_size"),
29+
job_id_data=job_id_data,
30+
location_id=location_id,
31+
path=path,
32+
)
33+
return StorageAsyncJobGet.from_rpc_schema(async_job_rpc_get), job_id_data

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

Lines changed: 4 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -140,6 +140,7 @@ qx.Class.define("osparc.dashboard.GridButtonBase", {
140140
const hGrid = new qx.ui.layout.Grid().set({
141141
spacing: 6,
142142
});
143+
hGrid.setRowFlex(0, 1);
143144
hGrid.setColumnFlex(1, 1);
144145
hGrid.setColumnAlign(0, "right", "middle");
145146
hGrid.setColumnAlign(1, "left", "middle");
@@ -149,6 +150,7 @@ qx.Class.define("osparc.dashboard.GridButtonBase", {
149150
paddingBottom: 6,
150151
paddingRight: 4,
151152
maxWidth: this.self().ITEM_WIDTH,
153+
minHeight: 32 + 6,
152154
maxHeight: this.self().ITEM_HEIGHT
153155
});
154156
control.setLayout(hGrid);
@@ -283,18 +285,11 @@ qx.Class.define("osparc.dashboard.GridButtonBase", {
283285

284286
// overridden
285287
_applyThumbnail: function(value, old) {
286-
if (value.includes("@FontAwesome5Solid/")) {
288+
if (qx.util.ResourceManager.getInstance().isFontUri(value)) {
287289
value += this.self().THUMBNAIL_SIZE;
288-
const image = this.getChildControl("thumbnail").set({
290+
this.getChildControl("thumbnail").set({
289291
source: value,
290292
});
291-
292-
[
293-
"appear",
294-
"loaded"
295-
].forEach(eventName => {
296-
image.addListener(eventName, () => this.__fitThumbnailHeight(), this);
297-
});
298293
} else {
299294
let source = osparc.product.Utils.getThumbnailUrl();
300295
osparc.utils.Utils.checkImageExists(value)

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ qx.Class.define("osparc.dashboard.ServiceBrowser", {
4848
// overridden
4949
initResources: function() {
5050
this._resourcesList = [];
51-
osparc.store.Services.getServicesLatest(false)
51+
osparc.store.Services.getServicesLatest()
5252
.then(services => {
5353
// Show "Contact Us" message if services.length === 0
5454
// Most probably is a product-stranger user (it can also be that the catalog is down)

services/static-webserver/client/source/class/osparc/share/Collaborators.js

Lines changed: 24 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -335,6 +335,21 @@ qx.Class.define("osparc.share.Collaborators", {
335335
});
336336
item.addListener("removeMember", e => {
337337
const orgMember = e.getData();
338+
if (
339+
["study", "template"].includes(this._resourceType) &&
340+
!osparc.share.CollaboratorsStudy.canCollaboratorBeRemoved(this._serializedDataCopy, orgMember["gid"])
341+
) {
342+
let msg = this.tr("Collaborator can't be removed:");
343+
msg += this._serializedDataCopy["name"] + this.tr(" needs at least one owner.");
344+
if (
345+
Object.keys(this._serializedDataCopy["accessRights"]).length === 1 &&
346+
Object.values(this._serializedDataCopy["accessRights"])[0]["delete"]
347+
) {
348+
msg += "<br>" + this.tr("You might want to delete it instead.");
349+
}
350+
osparc.FlashMessenger.logError(msg);
351+
return;
352+
}
338353
this._deleteMember(orgMember, item);
339354
});
340355
}
@@ -349,11 +364,8 @@ qx.Class.define("osparc.share.Collaborators", {
349364
__getLeaveStudyButton: function() {
350365
const myGid = osparc.auth.Data.getInstance().getGroupId();
351366
if (
352-
(this._resourceType === "study") &&
353-
// check if I'm part of the access rights (not through an organization)
354-
Object.keys(this._serializedDataCopy["accessRights"]).includes(myGid.toString()) &&
355-
// check also user is not "prjOwner". Backend will silently not let the frontend remove that user.
356-
(this._serializedDataCopy["prjOwner"] !== osparc.auth.Data.getInstance().getEmail())
367+
["study", "template"].includes(this._resourceType) &&
368+
osparc.share.CollaboratorsStudy.canCollaboratorBeRemoved(this._serializedDataCopy, myGid)
357369
) {
358370
const leaveText = this.tr("Leave") + " " + osparc.product.Utils.getStudyAlias({
359371
firstUpperCase: true
@@ -363,29 +375,14 @@ qx.Class.define("osparc.share.Collaborators", {
363375
visibility: Object.keys(this._serializedDataCopy["accessRights"]).includes(myGid.toString()) ? "visible" : "excluded"
364376
});
365377
leaveButton.addListener("execute", () => {
366-
let msg = `"${this._serializedDataCopy["name"]}" ` + this.tr("will no longer be listed.");
367-
if (!osparc.share.CollaboratorsStudy.checkRemoveCollaborator(this._serializedDataCopy, myGid)) {
368-
msg += "<br>";
369-
msg += this.tr("If you remove yourself, there won't be any other Owners.");
378+
const collaborator = {
379+
gid: myGid,
380+
name: osparc.store.Groups.getInstance().getGroupMe().getLabel(),
370381
}
371-
const win = new osparc.ui.window.Confirmation(msg).set({
372-
caption: leaveText,
373-
confirmText: this.tr("Leave"),
374-
confirmAction: "delete"
375-
});
376-
win.open();
377-
win.addListener("close", () => {
378-
if (win.getConfirmed()) {
379-
const collaborator = {
380-
gid: myGid,
381-
name: osparc.store.Groups.getInstance().getGroupMe().getLabel(),
382-
}
383-
this._deleteMember(collaborator)
384-
.then(() => {
385-
qx.event.message.Bus.dispatchByName("reloadStudies");
386-
});
387-
}
388-
}, this);
382+
this._deleteMember(collaborator)
383+
.then(() => {
384+
qx.event.message.Bus.dispatchByName("reloadStudies");
385+
});
389386
}, this);
390387
return leaveButton;
391388
}

services/static-webserver/client/source/class/osparc/share/CollaboratorsStudy.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -92,7 +92,7 @@ qx.Class.define("osparc.share.CollaboratorsStudy", {
9292
},
9393

9494
// checks that if the user to remove is an owner, there will still be another owner
95-
checkRemoveCollaborator: function(studyData, gid) {
95+
canCollaboratorBeRemoved: function(studyData, gid) {
9696
const ownerGids = this.__getDeleters(studyData);
9797
if (ownerGids.includes(gid.toString())) {
9898
return ownerGids.length > 1;

0 commit comments

Comments
 (0)