Skip to content

Commit d7f1575

Browse files
authored
✨ Frontend: Usage overview (#4455)
1 parent 5f8fe65 commit d7f1575

File tree

8 files changed

+317
-5
lines changed

8 files changed

+317
-5
lines changed
Lines changed: 181 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,181 @@
1+
/* ************************************************************************
2+
3+
osparc - the simcore frontend
4+
5+
https://osparc.io
6+
7+
Copyright:
8+
2023 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.component.resourceUsage.Overview", {
19+
extend: qx.ui.core.Widget,
20+
21+
construct: function() {
22+
this.base(arguments);
23+
24+
this._setLayout(new qx.ui.layout.VBox(15));
25+
26+
const loadingImage = this.getChildControl("loading-image");
27+
loadingImage.show();
28+
const table = this.getChildControl("usage-table");
29+
table.exclude();
30+
31+
this.__fetchData();
32+
},
33+
34+
statics: {
35+
ITEMS_PER_PAGE: 15,
36+
37+
popUpInWindow: function() {
38+
const title = qx.locale.Manager.tr("Usage Overview");
39+
const noteEditor = new osparc.component.resourceUsage.Overview();
40+
const viewWidth = 900;
41+
const viewHeight = 450;
42+
const win = osparc.ui.window.Window.popUpInWindow(noteEditor, title, viewWidth, viewHeight);
43+
win.center();
44+
win.open();
45+
return win;
46+
}
47+
},
48+
49+
members: {
50+
__prevRequestParams: null,
51+
__nextRequestParams: null,
52+
53+
_createChildControlImpl: function(id) {
54+
let control;
55+
switch (id) {
56+
case "loading-image":
57+
control = new qx.ui.basic.Image().set({
58+
source: "@FontAwesome5Solid/circle-notch/64",
59+
alignX: "center",
60+
alignY: "middle"
61+
});
62+
control.getContentElement().addClass("rotate");
63+
this._add(control);
64+
break;
65+
case "usage-table":
66+
control = new osparc.component.resourceUsage.OverviewTable().set({
67+
height: (this.self().ITEMS_PER_PAGE*20 + 40)
68+
});
69+
this._add(control);
70+
break;
71+
case "page-buttons":
72+
control = new qx.ui.container.Composite(new qx.ui.layout.HBox(5)).set({
73+
allowGrowX: true,
74+
alignX: "center",
75+
alignY: "middle"
76+
});
77+
this._add(control);
78+
break;
79+
case "prev-page-button": {
80+
control = new qx.ui.form.Button().set({
81+
icon: "@FontAwesome5Solid/chevron-left/12",
82+
allowGrowX: false
83+
});
84+
control.addListener("execute", () => this.__fetchData(this.__getPrevRequest()));
85+
const pageButtons = this.getChildControl("page-buttons");
86+
pageButtons.add(control);
87+
break;
88+
}
89+
case "current-page-label": {
90+
control = new qx.ui.basic.Label().set({
91+
font: "text-14",
92+
textAlign: "center",
93+
alignY: "middle"
94+
});
95+
const pageButtons = this.getChildControl("page-buttons");
96+
pageButtons.add(control);
97+
break;
98+
}
99+
case "next-page-button": {
100+
control = new qx.ui.form.Button().set({
101+
icon: "@FontAwesome5Solid/chevron-right/12",
102+
allowGrowX: false
103+
});
104+
control.addListener("execute", () => this.__fetchData(this.__getNextRequest()));
105+
const pageButtons = this.getChildControl("page-buttons");
106+
pageButtons.add(control);
107+
break;
108+
}
109+
}
110+
return control || this.base(arguments, id);
111+
},
112+
113+
__fetchData: function(request) {
114+
const loadingImage = this.getChildControl("loading-image");
115+
loadingImage.show();
116+
const table = this.getChildControl("usage-table");
117+
table.exclude();
118+
119+
if (request === undefined) {
120+
request = this.__getNextRequest();
121+
}
122+
request
123+
.then(resp => {
124+
const data = resp["data"];
125+
this.__setData(data);
126+
this.__prevRequestParams = resp["_links"]["prev"];
127+
this.__nextRequestParams = resp["_links"]["next"];
128+
this.__evaluatePageButtons(resp);
129+
})
130+
.finally(() => {
131+
loadingImage.exclude();
132+
table.show();
133+
});
134+
},
135+
136+
__getPrevRequest: function() {
137+
const params = {
138+
url: {
139+
offset: osparc.component.resourceUsage.Overview.ITEMS_PER_PAGE,
140+
limit: osparc.component.resourceUsage.Overview.ITEMS_PER_PAGE
141+
}
142+
};
143+
if (this.__prevRequestParams) {
144+
params.url.offset = osparc.utils.Utils.getParamFromURL(this.__prevRequestParams, "offset");
145+
params.url.limit = osparc.utils.Utils.getParamFromURL(this.__prevRequestParams, "limit");
146+
}
147+
const options = {
148+
resolveWResponse: true
149+
};
150+
return osparc.data.Resources.fetch("resourceUsage", "getPage", params, undefined, options);
151+
},
152+
153+
__getNextRequest: function() {
154+
const params = {
155+
url: {
156+
offset: 0,
157+
limit: osparc.component.resourceUsage.Overview.ITEMS_PER_PAGE
158+
}
159+
};
160+
if (this.__nextRequestParams) {
161+
params.url.offset = osparc.utils.Utils.getParamFromURL(this.__nextRequestParams, "offset");
162+
params.url.limit = osparc.utils.Utils.getParamFromURL(this.__nextRequestParams, "limit");
163+
}
164+
const options = {
165+
resolveWResponse: true
166+
};
167+
return osparc.data.Resources.fetch("resourceUsage", "getPage", params, undefined, options);
168+
},
169+
170+
__setData: function(data) {
171+
const table = this.getChildControl("usage-table");
172+
table.addData(data);
173+
},
174+
175+
__evaluatePageButtons:function(resp) {
176+
this.getChildControl("prev-page-button").setEnabled(Boolean(this.__prevRequestParams));
177+
this.getChildControl("current-page-label").setValue(((resp["_meta"]["offset"]/this.self().ITEMS_PER_PAGE)+1).toString());
178+
this.getChildControl("next-page-button").setEnabled(Boolean(this.__nextRequestParams));
179+
}
180+
}
181+
});
Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
/* ************************************************************************
2+
3+
osparc - the simcore frontend
4+
5+
https://osparc.io
6+
7+
Copyright:
8+
2023 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.component.resourceUsage.OverviewTable", {
19+
extend: osparc.ui.table.Table,
20+
21+
construct: function() {
22+
const cols = this.self().COLUMNS;
23+
const model = this.__model = new qx.ui.table.model.Simple();
24+
const colNames = Object.values(cols).map(col => col.title);
25+
model.setColumns(colNames);
26+
27+
this.base(arguments, model, {
28+
tableColumnModel: obj => new qx.ui.table.columnmodel.Resize(obj),
29+
statusBarVisible: false
30+
});
31+
const columnModel = this.getTableColumnModel();
32+
columnModel.getBehavior().setWidth(this.self().COLUMNS.duration.pos, 60);
33+
columnModel.getBehavior().setWidth(this.self().COLUMNS.processors.pos, 80);
34+
columnModel.getBehavior().setWidth(this.self().COLUMNS.coreHours.pos, 80);
35+
columnModel.getBehavior().setWidth(this.self().COLUMNS.status.pos, 70);
36+
columnModel.setDataCellRenderer(this.self().COLUMNS.duration.pos, new qx.ui.table.cellrenderer.Number());
37+
columnModel.setDataCellRenderer(this.self().COLUMNS.processors.pos, new qx.ui.table.cellrenderer.Number());
38+
columnModel.setDataCellRenderer(this.self().COLUMNS.coreHours.pos, new qx.ui.table.cellrenderer.Number());
39+
},
40+
41+
statics: {
42+
COLUMNS: {
43+
project: {
44+
pos: 0,
45+
title: "Project"
46+
},
47+
node: {
48+
pos: 1,
49+
title: "Node"
50+
},
51+
service: {
52+
pos: 2,
53+
title: "Service"
54+
},
55+
start: {
56+
pos: 3,
57+
title: "Start"
58+
},
59+
duration: {
60+
pos: 4,
61+
title: "Duration"
62+
},
63+
processors: {
64+
pos: 5,
65+
title: "Processors"
66+
},
67+
coreHours: {
68+
pos: 6,
69+
title: "Core Hours"
70+
},
71+
status: {
72+
pos: 7,
73+
title: "Status"
74+
}
75+
}
76+
},
77+
78+
members: {
79+
__model: null,
80+
81+
addData: function(datas) {
82+
const newDatas = [];
83+
if (datas) {
84+
const cols = this.self().COLUMNS;
85+
datas.forEach(data => {
86+
const newData = [];
87+
newData[cols["project"].pos] = data["project_name"] ? data["project_name"] : data["project_uuid"];
88+
newData[cols["node"].pos] = data["node_label"] ? data["node_label"] : data["node_uuid"];
89+
if (data["service_key"]) {
90+
const parts = data["service_key"].split("/");
91+
const serviceName = parts.pop();
92+
newData[cols["service"].pos] = serviceName + ":" + data["service_version"];
93+
}
94+
newData[cols["start"].pos] = osparc.utils.Utils.formatDateAndTime(new Date(data["start_time"]));
95+
newData[cols["duration"].pos] = data["duration"];
96+
newData[cols["processors"].pos] = data["processors"];
97+
newData[cols["coreHours"].pos] = data["core_hours"];
98+
newData[cols["status"].pos] = qx.lang.String.firstUp(data["status"]);
99+
newDatas.push(newData);
100+
});
101+
}
102+
this.setData(newDatas);
103+
}
104+
}
105+
});

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ qx.Class.define("osparc.dashboard.ResourceMoreOptions", {
4141

4242
statics: {
4343
WIDTH: 715,
44-
HEIGHT: 715,
44+
HEIGHT: 720,
4545

4646
popUpInWindow: function(moreOpts) {
4747
const title = qx.locale.Manager.tr("Details");

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

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -132,7 +132,8 @@ qx.Class.define("osparc.data.Permissions", {
132132
"study.nodestree.uuid.read",
133133
"study.filestree.uuid.read",
134134
"study.logger.debug.read",
135-
"statics.read"
135+
"statics.read",
136+
"usage.all.read"
136137
],
137138
"admin": []
138139
};

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

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -209,6 +209,15 @@ qx.Class.define("osparc.data.Resources", {
209209
}
210210
}
211211
},
212+
"resourceUsage": {
213+
useCache: true,
214+
endpoints: {
215+
getPage: {
216+
method: "GET",
217+
url: statics.API + "/resource-usage/containers?offset={offset}&limit={limit}"
218+
}
219+
}
220+
},
212221
/*
213222
* NODES
214223
*/

services/static-webserver/client/source/class/osparc/navigation/UserMenuButton.js

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,11 @@ qx.Class.define("osparc.navigation.UserMenuButton", {
9999
control.addListener("execute", () => osparc.desktop.organizations.OrganizationsWindow.openWindow(), this);
100100
this.getMenu().add(control);
101101
break;
102+
case "usage-overview":
103+
control = new qx.ui.menu.Button(this.tr("Usage Overview"));
104+
control.addListener("execute", () => osparc.component.resourceUsage.Overview.popUpInWindow(), this);
105+
this.getMenu().add(control);
106+
break;
102107
case "clusters":
103108
control = new qx.ui.menu.Button(this.tr("Clusters"));
104109
control.exclude();
@@ -156,6 +161,9 @@ qx.Class.define("osparc.navigation.UserMenuButton", {
156161
} else {
157162
this.getChildControl("preferences");
158163
this.getChildControl("organizations");
164+
if (osparc.data.Permissions.getInstance().canDo("usage.all.read")) {
165+
this.getChildControl("usage-overview");
166+
}
159167
this.getChildControl("clusters");
160168
}
161169
if (osparc.product.tutorial.Utils.getTutorial()) {
@@ -189,6 +197,9 @@ qx.Class.define("osparc.navigation.UserMenuButton", {
189197
} else {
190198
this.getChildControl("preferences");
191199
this.getChildControl("organizations");
200+
if (osparc.data.Permissions.getInstance().canDo("usage.all.read")) {
201+
this.getChildControl("usage-overview");
202+
}
192203
this.getChildControl("clusters");
193204
}
194205
this.getMenu().addSeparator();

services/static-webserver/client/source/class/osparc/store/Store.js

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,10 @@ qx.Class.define("osparc.store.Store", {
7070
check: "Array",
7171
init: []
7272
},
73+
resourceUsage: {
74+
check: "Array",
75+
init: []
76+
},
7377
nodesInStudyResources: {
7478
check: "Array",
7579
init: []

services/static-webserver/client/source/class/osparc/utils/Utils.js

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -712,9 +712,10 @@ qx.Class.define("osparc.utils.Utils", {
712712
return parsedFragment;
713713
},
714714

715-
getParamFromURL: (url, param) => {
716-
const urlParams = new URLSearchParams(url);
717-
return urlParams.get(param);
715+
getParamFromURL: (urlStr, param) => {
716+
const url = new URL(urlStr);
717+
const args = new URLSearchParams(url.search);
718+
return args.get(param);
718719
},
719720

720721
hasParamFromURL: (url, param) => {

0 commit comments

Comments
 (0)