Skip to content

Commit 2e85a83

Browse files
committed
Sub tasks
1 parent 7ebbe4b commit 2e85a83

File tree

6 files changed

+385
-16
lines changed

6 files changed

+385
-16
lines changed

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

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,8 @@ qx.Class.define("osparc.data.Job", {
3333
if (jobData["info"] && jobData["info"]["project_name"]) {
3434
this.setProjectName(jobData["info"]["project_name"]);
3535
}
36+
37+
this.__subJobs = [];
3638
},
3739

3840
properties: {
@@ -78,4 +80,27 @@ qx.Class.define("osparc.data.Job", {
7880
init: null,
7981
},
8082
},
83+
84+
members: {
85+
__subJobs: null,
86+
87+
addSubJob: function(subJobData) {
88+
const subJobFound = this.__subJobs.find(subJb => subJb.getNodeId() === subJobData["nodeId"]);
89+
if (subJobFound) {
90+
return subJobFound;
91+
}
92+
93+
const subJob = new osparc.data.SubJob(subJobData);
94+
this.__subJobs.push(subJob);
95+
return subJob;
96+
},
97+
98+
getSubJobs: function() {
99+
return this.__subJobs;
100+
},
101+
102+
clearSubJobs: function() {
103+
this.__subJobs = [];
104+
},
105+
}
81106
});
Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
/* ************************************************************************
2+
3+
osparc - the simcore frontend
4+
5+
https://osparc.io
6+
7+
Copyright:
8+
2025 IT'IS Foundation, https://itis.swiss
9+
10+
License:
11+
MIT: https://opensource.org/licenses/MIT
12+
13+
Authors:
14+
* Odei Maiz (odeimaiz)
15+
16+
************************************************************************ */
17+
18+
qx.Class.define("osparc.data.SubJob", {
19+
extend: qx.core.Object,
20+
21+
construct: function(jobData) {
22+
this.base(arguments);
23+
24+
this.set({
25+
projectUuid: jobData["projectUuid"],
26+
nodeId: jobData["nodeId"],
27+
nodeName: jobData["nodeId"],
28+
state: jobData["state"],
29+
progress: jobData["progress"],
30+
startedAt: jobData["startedAt"] ? new Date(jobData["startedAt"]) : null,
31+
endedAt: jobData["endedAt"] ? new Date(jobData["endedAt"]) : null,
32+
image: jobData["image"] || {},
33+
});
34+
},
35+
36+
properties: {
37+
projectUuid: {
38+
check: "String",
39+
nullable: false,
40+
init: null,
41+
},
42+
43+
nodeId: {
44+
check: "String",
45+
nullable: false,
46+
init: null,
47+
},
48+
49+
nodeName: {
50+
check: "String",
51+
nullable: false,
52+
init: null,
53+
},
54+
55+
state: {
56+
check: "String",
57+
nullable: true,
58+
init: null,
59+
},
60+
61+
progress: {
62+
check: "Number",
63+
nullable: true,
64+
init: null,
65+
},
66+
67+
startedAt: {
68+
check: "Date",
69+
init: null,
70+
nullable: true,
71+
},
72+
73+
endedAt: {
74+
check: "Date",
75+
init: null,
76+
nullable: true,
77+
},
78+
79+
image: {
80+
check: "Object",
81+
nullable: false,
82+
init: null,
83+
},
84+
},
85+
});

services/static-webserver/client/source/class/osparc/jobs/JobsTable.js

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -140,9 +140,11 @@ qx.Class.define("osparc.jobs.JobsTable", {
140140
const rowData = this.getTableModel().getRowData(row);
141141
switch (action) {
142142
case "info": {
143-
const jobInfo = new osparc.jobs.JobInfo(rowData["projectUuid"]);
144-
const win = osparc.jobs.JobInfo.popUpInWindow(jobInfo);
145-
win.setCaption(win.getCaption() + " - " + rowData["projectName"]);
143+
const subJobsTable = new osparc.jobs.SubJobsTable(rowData["projectUuid"]);
144+
osparc.ui.window.Window.popUpInWindow(subJobsTable, "title", 1000, 500).set({
145+
clickAwayClose: false,
146+
showClose: true
147+
});
146148
break;
147149
}
148150
case "stop":
Lines changed: 134 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,134 @@
1+
/* ************************************************************************
2+
3+
osparc - the simcore frontend
4+
5+
https://osparc.io
6+
7+
Copyright:
8+
2025 IT'IS Foundation, https://itis.swiss
9+
10+
License:
11+
MIT: https://opensource.org/licenses/MIT
12+
13+
Authors:
14+
* Odei Maiz (odeimaiz)
15+
16+
************************************************************************ */
17+
18+
19+
qx.Class.define("osparc.jobs.SubJobsTable", {
20+
extend: qx.ui.table.Table,
21+
22+
construct: function(projectUuid) {
23+
this.base(arguments);
24+
25+
const model = new osparc.jobs.SubJobsTableModel(projectUuid);
26+
this.setTableModel(model);
27+
28+
this.set({
29+
statusBarVisible: false,
30+
headerCellHeight: 26,
31+
rowHeight: 26,
32+
});
33+
34+
const columnModel = this.getTableColumnModel();
35+
columnModel.setColumnVisible(this.self().COLS.PROJECT_UUID.column, false);
36+
columnModel.setColumnVisible(this.self().COLS.NODE_ID.column, false);
37+
38+
Object.values(this.self().COLS).forEach(col => columnModel.setColumnWidth(col.column, col.width));
39+
40+
const iconPathInfo = "osparc/circle-info-text.svg";
41+
const fontButtonRendererInfo = new osparc.ui.table.cellrenderer.ImageButtonRenderer("info", iconPathInfo);
42+
columnModel.setDataCellRenderer(this.self().COLS.IMAGE.column, fontButtonRendererInfo);
43+
44+
this.__attachHandlers();
45+
},
46+
47+
statics: {
48+
COLS: {
49+
PROJECT_UUID: {
50+
id: "projectUuid",
51+
column: 0,
52+
label: qx.locale.Manager.tr("Project Id"),
53+
width: 170
54+
},
55+
NODE_ID: {
56+
id: "nodeId",
57+
column: 1,
58+
label: qx.locale.Manager.tr("Node Id"),
59+
width: 170
60+
},
61+
NODE_NAME: {
62+
id: "nodeName",
63+
column: 2,
64+
label: qx.locale.Manager.tr("Node Name"),
65+
width: 170
66+
},
67+
SOLVER: {
68+
id: "solver",
69+
column: 3,
70+
label: qx.locale.Manager.tr("Solver"),
71+
width: 300
72+
},
73+
STATE: {
74+
id: "state",
75+
column: 4,
76+
label: qx.locale.Manager.tr("State"),
77+
width: 100
78+
},
79+
PROGRESS: {
80+
id: "progress",
81+
column: 5,
82+
label: qx.locale.Manager.tr("Progress"),
83+
width: 70
84+
},
85+
START: {
86+
id: "start",
87+
column: 6,
88+
label: qx.locale.Manager.tr("Started"),
89+
width: 130
90+
},
91+
END: {
92+
id: "end",
93+
column: 7,
94+
label: qx.locale.Manager.tr("Ended"),
95+
width: 130
96+
},
97+
IMAGE: {
98+
id: "image",
99+
column: 8,
100+
label: qx.locale.Manager.tr("Info"),
101+
width: 40
102+
},
103+
}
104+
},
105+
106+
members: {
107+
__attachHandlers: function() {
108+
this.addListener("cellTap", e => {
109+
const row = e.getRow();
110+
const target = e.getOriginalTarget();
111+
if (target.closest(".qx-material-button") && (target.tagName === "IMG" || target.tagName === "DIV")) {
112+
const action = target.closest(".qx-material-button").getAttribute("data-action");
113+
if (action) {
114+
this.__handleButtonClick(action, row);
115+
}
116+
}
117+
});
118+
},
119+
120+
__handleButtonClick: function(action, row) {
121+
const rowData = this.getTableModel().getRowData(row);
122+
switch (action) {
123+
case "info": {
124+
const jobInfo = new osparc.jobs.JobInfo(rowData["image"]);
125+
const win = osparc.jobs.JobInfo.popUpInWindow(jobInfo);
126+
win.setCaption(win.getCaption() + " - " + rowData["projectName"]);
127+
break;
128+
}
129+
default:
130+
console.warn(`Unknown action: ${action}`);
131+
}
132+
},
133+
}
134+
});
Lines changed: 119 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,119 @@
1+
/* ************************************************************************
2+
3+
osparc - the simcore frontend
4+
5+
https://osparc.io
6+
7+
Copyright:
8+
2025 IT'IS Foundation, https://itis.swiss
9+
10+
License:
11+
MIT: https://opensource.org/licenses/MIT
12+
13+
Authors:
14+
* Odei Maiz (odeimaiz)
15+
16+
************************************************************************ */
17+
18+
19+
qx.Class.define("osparc.jobs.SubJobsTableModel", {
20+
extend: qx.ui.table.model.Remote,
21+
22+
construct(projectUuid) {
23+
this.base(arguments);
24+
25+
this.setProjectUuid(projectUuid);
26+
27+
const subJobsCols = osparc.jobs.SubJobsTable.COLS;
28+
const colLabels = Object.values(subJobsCols).map(col => col.label);
29+
const colIDs = Object.values(subJobsCols).map(col => col.id);
30+
this.setColumns(colLabels, colIDs);
31+
32+
this.setSortColumnIndexWithoutSortingData(subJobsCols.START.column);
33+
this.setSortAscendingWithoutSortingData(false);
34+
Object.values(subJobsCols).forEach(col => {
35+
this.setColumnSortable(col.column, false);
36+
});
37+
},
38+
39+
properties: {
40+
projectUuid: {
41+
check: "String",
42+
nullable: true
43+
},
44+
45+
isFetching: {
46+
check: "Boolean",
47+
init: false,
48+
event: "changeFetching"
49+
},
50+
},
51+
52+
members: {
53+
// overridden
54+
_loadRowCount() {
55+
osparc.store.Jobs.getInstance().fetchSubJobs(this.getProjectUuid())
56+
.then(subJobs => {
57+
this._onRowCountLoaded(subJobs.length)
58+
})
59+
.catch(() => {
60+
this._onRowCountLoaded(null)
61+
})
62+
},
63+
64+
// overridden
65+
_loadRowData(firstRow, qxLastRow) {
66+
this.setIsFetching(true);
67+
68+
const lastRow = Math.min(qxLastRow, this._rowCount - 1);
69+
// Returns a request promise with given offset and limit
70+
const getFetchPromise = () => {
71+
return osparc.store.Jobs.getInstance().fetchSubJobs(this.getProjectUuid())
72+
.then(subJobs => {
73+
const data = [];
74+
const subJobsCols = osparc.jobs.SubJobsTable.COLS;
75+
subJobs.forEach(subJob => {
76+
data.push({
77+
[subJobsCols.PROJECT_UUID.id]: subJob.getProjectUuid(),
78+
[subJobsCols.NODE_ID.id]: subJob.getNodeId(),
79+
[subJobsCols.NODE_NAME.id]: subJob.getNodeName(),
80+
[subJobsCols.SOLVER.id]: subJob.getImage()["name"] + ":" + subJob.getImage()["tag"],
81+
[subJobsCols.STATE.id]: subJob.getState(),
82+
[subJobsCols.PROGRESS.id]: subJob.getProgress(),
83+
[subJobsCols.START.id]: subJob.getStartedAt() ? osparc.utils.Utils.formatDateAndTime(subJob.getStartedAt()) : "-",
84+
[subJobsCols.END.id]: subJob.getEndedAt() ? osparc.utils.Utils.formatDateAndTime(subJob.getEndedAt()) : "-",
85+
});
86+
});
87+
return data;
88+
});
89+
};
90+
91+
// Divides the model row request into several server requests to comply with the number of rows server limit
92+
const reqLimit = lastRow - firstRow + 1; // Number of requested rows
93+
const nRequests = Math.ceil(reqLimit / this.self().SERVER_MAX_LIMIT);
94+
if (nRequests > 1) {
95+
const requests = [];
96+
for (let i=firstRow; i <= lastRow; i += this.self().SERVER_MAX_LIMIT) {
97+
requests.push(getFetchPromise(i, i > lastRow - this.self().SERVER_MAX_LIMIT + 1 ? reqLimit % this.self().SERVER_MAX_LIMIT : this.self().SERVER_MAX_LIMIT))
98+
}
99+
Promise.all(requests)
100+
.then(responses => this._onRowDataLoaded(responses.flat()))
101+
.catch(err => {
102+
console.error(err);
103+
this._onRowDataLoaded(null);
104+
})
105+
.finally(() => this.setIsFetching(false));
106+
} else {
107+
getFetchPromise(firstRow, reqLimit)
108+
.then(data => {
109+
this._onRowDataLoaded(data);
110+
})
111+
.catch(err => {
112+
console.error(err)
113+
this._onRowDataLoaded(null);
114+
})
115+
.finally(() => this.setIsFetching(false));
116+
}
117+
}
118+
}
119+
})

0 commit comments

Comments
 (0)