Skip to content

Commit 7ce6297

Browse files
Merge branch 'master' into is7528/add-name-when-listing-tasks
2 parents 2416227 + 0137533 commit 7ce6297

File tree

24 files changed

+687
-159
lines changed

24 files changed

+687
-159
lines changed
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
from datetime import datetime
2+
from typing import Annotated, TypeAlias
3+
4+
from models_library.projects import ProjectID
5+
from models_library.rpc_pagination import PageRpc
6+
from pydantic import BaseModel, ConfigDict, Field
7+
8+
9+
class ProjectRpcGet(BaseModel):
10+
"""
11+
Minimal information about a project that (for now) will fullfill
12+
the needs of the api-server. Specifically, the fields needed in
13+
project to call create_job_from_project
14+
"""
15+
16+
uuid: Annotated[
17+
ProjectID,
18+
Field(description="project unique identifier"),
19+
]
20+
name: Annotated[
21+
str,
22+
Field(description="project display name"),
23+
]
24+
description: str
25+
26+
# timestamps
27+
creation_date: datetime
28+
last_change_date: datetime
29+
30+
model_config = ConfigDict(
31+
extra="forbid",
32+
populate_by_name=True,
33+
)
34+
35+
36+
PageRpcProjectRpcGet: TypeAlias = PageRpc[
37+
# WARNING: keep this definition in models_library and not in the RPC interface
38+
# otherwise the metaclass PageRpc[*] will create *different* classes in server/client side
39+
# and will fail to serialize/deserialize these parameters when transmitted/received
40+
ProjectRpcGet
41+
]

packages/service-library/src/servicelib/rabbitmq/rpc_interfaces/webserver/projects.py

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,16 @@
11
import logging
2+
from typing import cast
23

34
from models_library.api_schemas_webserver import WEBSERVER_RPC_NAMESPACE
45
from models_library.products import ProductName
56
from models_library.projects import ProjectID
67
from models_library.rabbitmq_basic_types import RPCMethodName
8+
from models_library.rest_pagination import PageOffsetInt
9+
from models_library.rpc.webserver.projects import PageRpcProjectRpcGet
10+
from models_library.rpc_pagination import (
11+
DEFAULT_NUMBER_OF_ITEMS_PER_PAGE,
12+
PageLimitInt,
13+
)
714
from models_library.users import UserID
815
from pydantic import TypeAdapter, validate_call
916
from servicelib.logging_utils import log_decorator
@@ -32,3 +39,29 @@ async def mark_project_as_job(
3239
job_parent_resource_name=job_parent_resource_name,
3340
)
3441
assert result is None
42+
43+
44+
@log_decorator(_logger, level=logging.DEBUG)
45+
@validate_call(config={"arbitrary_types_allowed": True})
46+
async def list_projects_marked_as_jobs(
47+
rpc_client: RabbitMQRPCClient,
48+
*,
49+
product_name: ProductName,
50+
user_id: UserID,
51+
# pagination
52+
offset: PageOffsetInt = 0,
53+
limit: PageLimitInt = DEFAULT_NUMBER_OF_ITEMS_PER_PAGE,
54+
# filters
55+
job_parent_resource_name_filter: str | None = None,
56+
) -> PageRpcProjectRpcGet:
57+
result = await rpc_client.request(
58+
WEBSERVER_RPC_NAMESPACE,
59+
TypeAdapter(RPCMethodName).validate_python("list_projects_marked_as_jobs"),
60+
product_name=product_name,
61+
user_id=user_id,
62+
offset=offset,
63+
limit=limit,
64+
job_parent_resource_name_filter=job_parent_resource_name_filter,
65+
)
66+
assert TypeAdapter(PageRpcProjectRpcGet).validate_python(result) # nosec
67+
return cast(PageRpcProjectRpcGet, result)

services/director-v2/openapi.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,7 @@
6666
"computations"
6767
],
6868
"summary": "Create Computation",
69-
"description": "Create and optionally starts it",
69+
"description": "Create and optionally start a new computation",
7070
"operationId": "create_computation_v2_computations_post",
7171
"requestBody": {
7272
"content": {

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

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -384,7 +384,12 @@ qx.Class.define("osparc.dashboard.CardBase", {
384384
},
385385

386386
uiMode: {
387-
check: ["workbench", "guided", "app", "standalone"], // "guided" is no longer used
387+
check: [
388+
"workbench", // =auto, the frontend decides the icon and default view
389+
"app", "guided", // "guided" is no longer used
390+
"standalone",
391+
"pipeline",
392+
],
388393
nullable: true,
389394
apply: "__applyUiMode"
390395
},

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -481,7 +481,7 @@ qx.Class.define("osparc.dashboard.ResourceDetails", {
481481
if (
482482
osparc.utils.Resources.isService(resourceData) ||
483483
!osparc.product.Utils.showStudyPreview() ||
484-
!(osparc.study.Utils.getUiMode(resourceData) === "workbench")
484+
["app", "guided", "standalone"].includes(osparc.study.Utils.getUiMode(resourceData))
485485
) {
486486
// there is no pipelining or don't show it
487487
return null;

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

Lines changed: 45 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1789,17 +1789,52 @@ qx.Class.define("osparc.dashboard.StudyBrowser", {
17891789
convertToPipelineButton["convertToPipelineButton"] = true;
17901790
const uiMode = osparc.study.Utils.getUiMode(studyData);
17911791
convertToPipelineButton.setVisibility(uiMode === "standalone" ? "visible" : "excluded");
1792-
convertToPipelineButton.addListener("execute", () => {
1793-
this.__updateUIMode(studyData, "workbench")
1794-
.catch(err => osparc.FlashMessenger.logError(err, this.tr("Something went wrong while converting to pipeline")));
1795-
}, this);
1792+
convertToPipelineButton.addListener("execute", () => this.__convertToPipelineClicked(studyData), this);
17961793
return convertToPipelineButton;
17971794
},
17981795

1796+
__convertToPipelineClicked: function(studyData) {
1797+
let message = this.tr("Would you like to convert this project to a pipeline?");
1798+
message += "<br>" + this.tr("Alternatively, you can create a copy of the project and convert the copy instead.");
1799+
const confirmationWin = new osparc.ui.window.Confirmation();
1800+
confirmationWin.set({
1801+
caption: this.tr("Convert to Pipeline"),
1802+
confirmText: this.tr("Convert"),
1803+
confirmAction: "create",
1804+
message,
1805+
});
1806+
confirmationWin.getChildControl("cancel-button").exclude();
1807+
const copyOptionButton = new qx.ui.form.Button().set({
1808+
appearance: "form-button-text",
1809+
label: this.tr("Create a copy and convert it"),
1810+
});
1811+
confirmationWin.getChildControl("buttons-layout").addAt(copyOptionButton, 0);
1812+
confirmationWin.addListener("close", () => {
1813+
if (confirmationWin.getConfirmed()) {
1814+
this.__updateUIMode(studyData, "pipeline")
1815+
.then(() => osparc.FlashMessenger.logAs(this.tr("Project converted to pipeline"), "INFO"))
1816+
.catch(err => osparc.FlashMessenger.logError(err, this.tr("Something went wrong while converting to pipeline")));
1817+
}
1818+
});
1819+
copyOptionButton.addListener("execute", () => {
1820+
confirmationWin.close();
1821+
this.__duplicateStudy(studyData)
1822+
.then(task => {
1823+
task.addListener("resultReceived", e => {
1824+
const copiedStudy = e.getData();
1825+
this.__updateUIMode(copiedStudy, "pipeline")
1826+
.then(() => osparc.FlashMessenger.logAs(this.tr("Project's copy converted to pipeline"), "INFO"))
1827+
.catch(err => osparc.FlashMessenger.logError(err, this.tr("Something went wrong while converting the copy to pipeline")));
1828+
}, this);
1829+
});
1830+
}, this);
1831+
confirmationWin.open();
1832+
},
1833+
17991834
__updateUIMode: function(studyData, uiMode) {
18001835
const studyUI = osparc.utils.Utils.deepCloneObject(studyData["ui"]);
18011836
studyUI["mode"] = uiMode;
1802-
return osparc.info.StudyUtils.patchStudyData(studyData, "ui", studyUI)
1837+
return osparc.store.Study.patchStudyData(studyData, "ui", studyUI)
18031838
.then(() => this._updateStudyData(studyData))
18041839
},
18051840

@@ -1889,21 +1924,11 @@ qx.Class.define("osparc.dashboard.StudyBrowser", {
18891924
},
18901925

18911926
__duplicateStudy: function(studyData) {
1892-
const text = this.tr("Duplicate process started and added to the background tasks");
1893-
osparc.FlashMessenger.logAs(text, "INFO");
1894-
1895-
const params = {
1896-
url: {
1897-
"studyId": studyData["uuid"]
1898-
}
1899-
};
1900-
const options = {
1901-
pollTask: true
1902-
};
1903-
const fetchPromise = osparc.data.Resources.fetch("studies", "duplicate", params, options);
1904-
const pollTasks = osparc.store.PollTasks.getInstance();
1905-
pollTasks.createPollingTask(fetchPromise)
1906-
.then(task => this.__taskDuplicateReceived(task, studyData["name"]))
1927+
osparc.study.Utils.duplicateStudy(studyData)
1928+
.then(task => {
1929+
this.__taskDuplicateReceived(task, studyData["name"]);
1930+
return task;
1931+
})
19071932
.catch(err => osparc.FlashMessenger.logError(err, this.tr("Something went wrong while duplicating")));
19081933
},
19091934

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

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -102,9 +102,13 @@ qx.Class.define("osparc.data.PollTask", {
102102
},
103103

104104
extractProgress: function(updateData) {
105+
const toNumberWithMaxTwoDecimals = value => {
106+
return Math.round(Number(value) * 100) / 100;
107+
};
108+
105109
if ("task_progress" in updateData) {
106110
const taskProgress = updateData["task_progress"];
107-
const percent = taskProgress["percent"] ? parseFloat(taskProgress["percent"].toFixed(3)) : taskProgress["percent"];
111+
const percent = taskProgress["percent"] ? toNumberWithMaxTwoDecimals(taskProgress["percent"]) : taskProgress["percent"];
108112
return percent;
109113
}
110114
return 0;

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

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,12 @@ qx.Class.define("osparc.data.model.StudyUI", {
6363
},
6464

6565
mode: {
66-
check: ["workbench", "guided", "app", "standalone"], // "guided" is no longer used
66+
check: [
67+
"workbench", // =auto, the frontend decides the icon and default view
68+
"app", "guided", // "guided" is no longer used
69+
"standalone",
70+
"pipeline",
71+
],
6772
init: "workbench",
6873
nullable: true,
6974
event: "changeMode",

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,7 @@ qx.Class.define("osparc.desktop.SlideshowView", {
7272
const nodeId = e.getData();
7373
this.__hideNode(nodeId);
7474
}, this);
75-
slideshowToolbar.addListener("slidesStop", () => this.getStudy().getUi().setMode("workbench"), this);
75+
slideshowToolbar.addListener("slidesStop", () => this.getStudy().getUi().setMode("pipeline"), this);
7676
this._add(slideshowToolbar);
7777

7878
const mainView = this.__mainView = new qx.ui.container.Composite(new qx.ui.layout.HBox().set({

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

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -707,14 +707,19 @@ qx.Class.define("osparc.desktop.StudyEditor", {
707707
this.__viewsStack.setSelection([this.__slideshowView]);
708708
this.__slideshowView.startSlides();
709709
break;
710-
case "standalone": {
710+
case "standalone":
711711
this.__viewsStack.setSelection([this.__workbenchView]);
712712
this.__workbenchView.openFirstNode();
713713
break;
714-
}
714+
case "pipeline":
715+
this.__viewsStack.setSelection([this.__workbenchView]);
716+
this.__workbenchView.setMaximized(false);
717+
this.__workbenchView.showPipeline();
718+
break;
715719
case "workbench":
716720
default: {
717721
this.__viewsStack.setSelection([this.__workbenchView]);
722+
// OM: Is this needed?
718723
if (oldUIMode === "standalone") {
719724
// in this transition, show workbenchUI
720725
this.__workbenchView.setMaximized(false);

0 commit comments

Comments
 (0)