diff --git a/services/static-webserver/client/source/class/osparc/dashboard/ResourceBrowserBase.js b/services/static-webserver/client/source/class/osparc/dashboard/ResourceBrowserBase.js index 5ff218af4518..8daed6a8036b 100644 --- a/services/static-webserver/client/source/class/osparc/dashboard/ResourceBrowserBase.js +++ b/services/static-webserver/client/source/class/osparc/dashboard/ResourceBrowserBase.js @@ -483,6 +483,10 @@ qx.Class.define("osparc.dashboard.ResourceBrowserBase", { }, _addTaskCard: function(task, cardTitle, cardIcon) { + if (!this._resourcesContainer) { + return null; + } + if (task) { const taskPlaceholders = this._resourcesContainer.getCards().filter(card => osparc.dashboard.ResourceBrowserBase.isCardTaskPlaceholder(card)); if (taskPlaceholders.find(taskPlaceholder => taskPlaceholder.getTask() === task)) { @@ -505,6 +509,10 @@ qx.Class.define("osparc.dashboard.ResourceBrowserBase", { }, _removeTaskCard: function(task) { + if (!this._resourcesContainer) { + return; + } + if (task) { const taskPlaceholders = this._resourcesContainer.getCards().filter(card => osparc.dashboard.ResourceBrowserBase.isCardTaskPlaceholder(card)); const taskCard = taskPlaceholders.find(taskPlaceholder => taskPlaceholder.getTask() === task); diff --git a/services/static-webserver/client/source/class/osparc/dashboard/ResourceDetails.js b/services/static-webserver/client/source/class/osparc/dashboard/ResourceDetails.js index d066ff1ab103..6677e485689a 100644 --- a/services/static-webserver/client/source/class/osparc/dashboard/ResourceDetails.js +++ b/services/static-webserver/client/source/class/osparc/dashboard/ResourceDetails.js @@ -356,27 +356,9 @@ qx.Class.define("osparc.dashboard.ResourceDetails", { } }); - const resourceData = this.__resourceData; - if (!osparc.utils.Resources.isService(resourceData)) { - const title = osparc.product.Utils.getStudyAlias({firstUpperCase: true}) + this.tr(" Files..."); - const iconSrc = "@FontAwesome5Solid/file/22"; - const dataAccess = new qx.ui.basic.Atom().set({ - label: title, - icon: iconSrc, - font: "text-14", - padding: 8, - paddingLeft: 12, - gap: 14, - cursor: "pointer", - }); - dataAccess.addListener("tap", () => osparc.widget.StudyDataManager.popUpInWindow(resourceData["uuid"])); - this.addWidgetToTabs(dataAccess); - if (resourceData["resourceType"] === "study") { - const canShowData = osparc.study.Utils.canShowStudyData(resourceData); - dataAccess.setEnabled(canShowData); - } - } + this.__getActivityOverviewPopUp(); + this.__getProjectFilesPopUp(); if (selectedTabId) { const pageFound = tabsView.getChildren().find(page => page.tabId === selectedTabId); @@ -814,6 +796,52 @@ qx.Class.define("osparc.dashboard.ResourceDetails", { page.addToHeader(toolbar); page.addToContent(createFunction); return page; - } + }, + + __getProjectFilesPopUp: function() { + const resourceData = this.__resourceData; + if (!osparc.utils.Resources.isService(resourceData)) { + const title = osparc.product.Utils.getStudyAlias({firstUpperCase: true}) + this.tr(" Files..."); + const iconSrc = "@FontAwesome5Solid/file/22"; + const dataAccess = new qx.ui.basic.Atom().set({ + label: title, + icon: iconSrc, + font: "text-14", + padding: 8, + paddingLeft: 12, + gap: 14, + cursor: "pointer", + }); + dataAccess.addListener("tap", () => osparc.widget.StudyDataManager.popUpInWindow(resourceData["uuid"])); + this.addWidgetToTabs(dataAccess); + + if (resourceData["resourceType"] === "study") { + const canShowData = osparc.study.Utils.canShowStudyData(resourceData); + dataAccess.setEnabled(canShowData); + } + } + }, + + __getActivityOverviewPopUp: function() { + const resourceData = this.__resourceData; + if ( + osparc.desktop.credits.Utils.areWalletsEnabled() && + osparc.utils.Resources.isStudy(resourceData) + ) { + const title = this.tr("Activity Overview..."); + const iconSrc = "@FontAwesome5Solid/tasks/22"; + const dataAccess = new qx.ui.basic.Atom().set({ + label: title, + icon: iconSrc, + font: "text-14", + padding: 8, + paddingLeft: 10, + gap: 12, // align with the rest of the tabs + cursor: "pointer", + }); + dataAccess.addListener("tap", () => osparc.jobs.ActivityOverview.popUpInWindow(resourceData)); + this.addWidgetToTabs(dataAccess); + } + }, } }); diff --git a/services/static-webserver/client/source/class/osparc/data/Job.js b/services/static-webserver/client/source/class/osparc/data/Job.js index 1a143cbff42e..34b7d1ec190c 100644 --- a/services/static-webserver/client/source/class/osparc/data/Job.js +++ b/services/static-webserver/client/source/class/osparc/data/Job.js @@ -23,11 +23,12 @@ qx.Class.define("osparc.data.Job", { this.set({ projectUuid: jobData["projectUuid"], - state: jobData["state"], + state: jobData["state"] || "UNKNOWN", submittedAt: jobData["submittedAt"] ? new Date(jobData["submittedAt"]) : null, startedAt: jobData["startedAt"] ? new Date(jobData["startedAt"]) : null, endedAt: jobData["endedAt"] ? new Date(jobData["endedAt"]) : null, info: jobData["info"] || null, + customMetadata: jobData["customMetadata"] || null, }); if (jobData["info"] && jobData["info"]["project_name"]) { @@ -76,11 +77,32 @@ qx.Class.define("osparc.data.Job", { info: { check: "Object", - nullable: false, + nullable: true, + init: null, + }, + + customMetadata: { + check: "Object", + nullable: true, init: null, }, }, + statics: { + STATUS_LABELS: { + "UNKNOWN": "Unknown", + "NOT_STARTED": "Not Started", + "PUBLISHED": "Published", + "PENDING": "Pending", + "RUNNING": "Running", + "SUCCESS": "Success", + "FAILED": "Failed", + "ABORTED": "Aborted", + "WAITING_FOR_RESOURCES": "Waiting for Resources", + "WAITING_FOR_CLUSTER": "Waiting for Cluster", + }, + }, + members: { __subJobs: null, diff --git a/services/static-webserver/client/source/class/osparc/data/Resources.js b/services/static-webserver/client/source/class/osparc/data/Resources.js index f91a8a5062f4..dcffb8738285 100644 --- a/services/static-webserver/client/source/class/osparc/data/Resources.js +++ b/services/static-webserver/client/source/class/osparc/data/Resources.js @@ -334,13 +334,25 @@ qx.Class.define("osparc.data.Resources", { }, } }, - "jobs": { + "runPipeline": { + useCache: false, + endpoints: { + startPipeline: { + method: "POST", + url: statics.API + "/computations/{studyId}:start" + }, + stopPipeline: { + method: "POST", + url: statics.API + "/computations/{studyId}:stop" + }, + } + }, + "jobsActive": { useCache: false, // handled in osparc.store.Jobs endpoints: { getPage: { method: "GET", - // url: statics.API + "/computations/-/iterations/latest?offset={offset}&limit={limit}&order_by={orderBy}" - url: statics.API + "/computations/-/iterations/latest?offset={offset}&limit={limit}&order_by=%7B%22field%22:%22submitted_at%22,%22direction%22:%22desc%22%7D" + url: statics.API + "/computations/-/iterations/latest?offset={offset}&limit={limit}&order_by=%7B%22field%22:%22submitted_at%22,%22direction%22:%22desc%22%7D&filter_only_running=true" }, } }, diff --git a/services/static-webserver/client/source/class/osparc/data/SubJob.js b/services/static-webserver/client/source/class/osparc/data/SubJob.js index 6642827635a5..7b7cbfd51e9a 100644 --- a/services/static-webserver/client/source/class/osparc/data/SubJob.js +++ b/services/static-webserver/client/source/class/osparc/data/SubJob.js @@ -29,7 +29,7 @@ qx.Class.define("osparc.data.SubJob", { progress: subJobData["progress"], startedAt: subJobData["startedAt"] ? new Date(subJobData["startedAt"]) : null, endedAt: subJobData["endedAt"] ? new Date(subJobData["endedAt"]) : null, - osparcCredits: subJobData["osparcCredits"] || null, + osparcCredits: subJobData["osparcCredits"] === null ? null : -1*parseFloat(subJobData["osparcCredits"]), image: subJobData["image"] || {}, logDownloadLink: subJobData["logDownloadLink"] || null, }); diff --git a/services/static-webserver/client/source/class/osparc/desktop/MainPage.js b/services/static-webserver/client/source/class/osparc/desktop/MainPage.js index 6d6095b2d4aa..2cf130e98716 100644 --- a/services/static-webserver/client/source/class/osparc/desktop/MainPage.js +++ b/services/static-webserver/client/source/class/osparc/desktop/MainPage.js @@ -69,7 +69,7 @@ qx.Class.define("osparc.desktop.MainPage", { preloadPromises.push(osparc.store.Tags.getInstance().fetchTags()); preloadPromises.push(osparc.store.Products.getInstance().fetchUiConfig()); preloadPromises.push(osparc.store.PollTasks.getInstance().fetchTasks()); - preloadPromises.push(osparc.store.Jobs.getInstance().fetchJobs()); + preloadPromises.push(osparc.store.Jobs.getInstance().fetchJobsActive()); Promise.all(preloadPromises) .then(() => { const mainStack = this.__createMainStack(); diff --git a/services/static-webserver/client/source/class/osparc/desktop/StudyEditor.js b/services/static-webserver/client/source/class/osparc/desktop/StudyEditor.js index eaae2e78fb35..9110b9ca8140 100644 --- a/services/static-webserver/client/source/class/osparc/desktop/StudyEditor.js +++ b/services/static-webserver/client/source/class/osparc/desktop/StudyEditor.js @@ -569,64 +569,64 @@ qx.Class.define("osparc.desktop.StudyEditor", { return; } - this.getStudy().setPipelineRunning(true); this.updateStudyDocument() .then(() => { this.__requestStartPipeline(this.getStudy().getUuid(), partialPipeline); }) .catch(() => { this.getStudyLogger().error(null, "Run failed"); - this.getStudy().setPipelineRunning(false); }); }, __requestStartPipeline: function(studyId, partialPipeline = [], forceRestart = false) { - const url = "/computations/" + encodeURIComponent(studyId) + ":start"; - const req = new osparc.io.request.ApiRequest(url, "POST"); - req.addListener("success", this.__onPipelineSubmitted, this); - req.addListener("error", () => { - this.getStudyLogger().error(null, "Error submitting pipeline"); - this.getStudy().setPipelineRunning(false); - }, this); - req.addListener("fail", async e => { - if (e.getTarget().getStatus() == "409") { - this.getStudyLogger().error(null, "Pipeline is already running"); - } else if (e.getTarget().getStatus() == "422") { - this.getStudyLogger().info(null, "The pipeline is up-to-date"); - const msg = this.tr("The pipeline is up-to-date. Do you want to re-run it?"); - const win = new osparc.ui.window.Confirmation(msg).set({ - caption: this.tr("Re-run"), - confirmText: this.tr("Run"), - confirmAction: "create" - }); - win.center(); - win.open(); - win.addListener("close", () => { - if (win.getConfirmed()) { - this.__requestStartPipeline(studyId, partialPipeline, true); - } - }, this); - } else if (e.getTarget().getStatus() == "402") { - const msg = await e.getTarget().getResponse().error.errors[0].message; - osparc.FlashMessenger.logAs(msg, "WARNING"); - } else { - this.getStudyLogger().error(null, "Unsuccessful pipeline submission"); - } - this.getStudy().setPipelineRunning(false); - }, this); + this.getStudy().setPipelineRunning(true); - const requestData = { - "subgraph": partialPipeline, - "force_restart": forceRestart - }; - req.setRequestData(requestData); - req.send(); if (partialPipeline.length) { this.getStudyLogger().info(null, "Starting partial pipeline"); } else { this.getStudyLogger().info(null, "Starting pipeline"); } + const params = { + url: { + "studyId": studyId + }, + data: { + "subgraph": partialPipeline, + "force_restart": forceRestart, + } + }; + osparc.data.Resources.fetch("runPipeline", "startPipeline", params) + .then(() => this.__onPipelineSubmitted()) + .catch(err => { + let msg = err.message; + const errStatus = err.status; + if (errStatus == "409") { + this.getStudyLogger().error(null, "Pipeline is already running"); + } else if (errStatus == "422") { + this.getStudyLogger().info(null, "The pipeline is up-to-date"); + msg = this.tr("The pipeline is up-to-date. Do you want to re-run it?"); + const win = new osparc.ui.window.Confirmation(msg).set({ + caption: this.tr("Re-run"), + confirmText: this.tr("Run"), + confirmAction: "create" + }); + win.center(); + win.open(); + win.addListener("close", () => { + if (win.getConfirmed()) { + this.__requestStartPipeline(studyId, partialPipeline, true); + } + }, this); + } else if (err.status == "402") { + osparc.FlashMessenger.logAs(msg, "WARNING"); + } else { + osparc.FlashMessenger.logAs(msg, "WARNING"); + this.getStudyLogger().error(null, "Unsuccessful pipeline submission"); + } + this.getStudy().setPipelineRunning(false); + }); + return true; }, @@ -662,19 +662,20 @@ qx.Class.define("osparc.desktop.StudyEditor", { return; } - this.__requestStopPipeline(this.getStudy().getUuid()); + this.__requestStopPipeline(); }, - __requestStopPipeline: function(studyId) { - const url = "/computations/" + encodeURIComponent(studyId) + ":stop"; - const req = new osparc.io.request.ApiRequest(url, "POST"); - req.addListener("success", () => this.getStudyLogger().debug(null, "Pipeline aborting"), this); - req.addListener("error", () => this.getStudyLogger().error(null, "Error stopping pipeline"), this); - req.addListener("fail", () => this.getStudyLogger().error(null, "Failed stopping pipeline"), this); - req.send(); - + __requestStopPipeline: function() { this.getStudyLogger().info(null, "Stopping pipeline"); - return true; + + const params = { + url: { + "studyId": this.getStudy().getUuid() + }, + }; + osparc.data.Resources.fetch("runPipeline", "stopPipeline", params) + .then(() => this.getStudyLogger().debug(null, "Stopping pipeline"), this) + .catch(() => this.getStudyLogger().error(null, "Error stopping pipeline"), this); }, // ------------------ START/STOP PIPELINE ------------------ diff --git a/services/static-webserver/client/source/class/osparc/jobs/ActivityCenterWindow.js b/services/static-webserver/client/source/class/osparc/jobs/ActivityCenterWindow.js index cd56d3198f6d..7f2fe3b7b023 100644 --- a/services/static-webserver/client/source/class/osparc/jobs/ActivityCenterWindow.js +++ b/services/static-webserver/client/source/class/osparc/jobs/ActivityCenterWindow.js @@ -24,8 +24,8 @@ qx.Class.define("osparc.jobs.ActivityCenterWindow", { this.set({ layout: new qx.ui.layout.VBox(), modal: true, - width: 1100, - height: 500, + width: this.self().WIDTH, + height: this.self().HEIGHT, showMaximize: false, showMinimize: false, }); @@ -34,6 +34,9 @@ qx.Class.define("osparc.jobs.ActivityCenterWindow", { }, statics: { + WIDTH: 1000, + HEIGHT: 500, + openWindow: function() { const runsWindow = new osparc.jobs.ActivityCenterWindow(); runsWindow.center(); diff --git a/services/static-webserver/client/source/class/osparc/jobs/ActivityOverview.js b/services/static-webserver/client/source/class/osparc/jobs/ActivityOverview.js new file mode 100644 index 000000000000..0f985f4e03da --- /dev/null +++ b/services/static-webserver/client/source/class/osparc/jobs/ActivityOverview.js @@ -0,0 +1,43 @@ +/* ************************************************************************ + + osparc - the simcore frontend + + https://osparc.io + + Copyright: + 2025 IT'IS Foundation, https://itis.swiss + + License: + MIT: https://opensource.org/licenses/MIT + + Authors: + * Odei Maiz (odeimaiz) + +************************************************************************ */ + +qx.Class.define("osparc.jobs.ActivityOverview", { + extend: qx.ui.core.Widget, + + construct: function(projectData) { + this.base(arguments); + + this._setLayout(new qx.ui.layout.VBox(15)); + + this.__buildLayout(projectData); + }, + + statics: { + popUpInWindow: function(projectData) { + const activityOverview = new osparc.jobs.ActivityOverview(projectData); + const title = qx.locale.Manager.tr("Activity Overview"); + return osparc.ui.window.Window.popUpInWindow(activityOverview, title, osparc.jobs.ActivityCenterWindow.WIDTH, osparc.jobs.ActivityCenterWindow.HEIGHT); + }, + }, + + members: { + __buildLayout: function(projectData) { + const subRunsTable = new osparc.jobs.SubRunsTable(projectData["uuid"]); + this._add(subRunsTable); + }, + } +}); diff --git a/services/static-webserver/client/source/class/osparc/jobs/JobsButton.js b/services/static-webserver/client/source/class/osparc/jobs/JobsButton.js index c0df3017ee9c..b5362ab59529 100644 --- a/services/static-webserver/client/source/class/osparc/jobs/JobsButton.js +++ b/services/static-webserver/client/source/class/osparc/jobs/JobsButton.js @@ -27,14 +27,14 @@ qx.Class.define("osparc.jobs.JobsButton", { width: 30, alignX: "center", cursor: "pointer", - visibility: "excluded", toolTipText: this.tr("Activity Center"), }); this.addListener("tap", () => osparc.jobs.ActivityCenterWindow.openWindow(), this); const jobsStore = osparc.store.Jobs.getInstance(); - jobsStore.addListener("changeJobs", e => this.__updateJobsButton(), this); + jobsStore.addListener("changeJobsActive", e => this.__updateJobsButton(e.getData()), this); + jobsStore.fetchJobsActive(); }, members: { @@ -71,14 +71,12 @@ qx.Class.define("osparc.jobs.JobsButton", { return control || this.base(arguments, id); }, - __updateJobsButton: function() { + __updateJobsButton: function(nActiveJobs) { this.getChildControl("icon"); const number = this.getChildControl("number"); - const jobsStore = osparc.store.Jobs.getInstance(); - const nJobs = jobsStore.getJobs().length > 20 ? "20+" : jobsStore.getJobs().length; + const nJobs = nActiveJobs > osparc.store.Jobs.SERVER_MAX_LIMIT ? (osparc.store.Jobs.SERVER_MAX_LIMIT + "+") : nActiveJobs; number.setValue(nJobs.toString()); - nJobs ? this.show() : this.exclude(); }, } }); diff --git a/services/static-webserver/client/source/class/osparc/jobs/RunsTable.js b/services/static-webserver/client/source/class/osparc/jobs/RunsTable.js index 41fc06821a0c..9d16c3225377 100644 --- a/services/static-webserver/client/source/class/osparc/jobs/RunsTable.js +++ b/services/static-webserver/client/source/class/osparc/jobs/RunsTable.js @@ -36,9 +36,13 @@ qx.Class.define("osparc.jobs.RunsTable", { Object.values(this.self().COLS).forEach(col => columnModel.setColumnWidth(col.column, col.width)); - const iconPathStop = "osparc/circle-stop-text.svg"; - const fontButtonRendererStop = new osparc.ui.table.cellrenderer.ImageButtonRenderer("stop", iconPathStop); - columnModel.setDataCellRenderer(this.self().COLS.ACTION_STOP.column, fontButtonRendererStop); + const iconPathStop = "osparc/icons/circle-xmark-text.svg"; + const fontButtonRendererStop = new osparc.ui.table.cellrenderer.ImageButtonRenderer("cancel", iconPathStop); + columnModel.setDataCellRenderer(this.self().COLS.ACTION_CANCEL.column, fontButtonRendererStop); + + const iconPathInfo = "osparc/icons/circle-info-text.svg"; + const fontButtonRendererInfo = new osparc.ui.table.cellrenderer.ImageButtonRenderer("info", iconPathInfo); + columnModel.setDataCellRenderer(this.self().COLS.ACTION_INFO.column, fontButtonRendererInfo); this.__attachHandlers(); }, @@ -53,20 +57,20 @@ qx.Class.define("osparc.jobs.RunsTable", { id: "projectUuid", column: 0, label: qx.locale.Manager.tr("Project Id"), - width: 170 + width: 200 }, PROJECT_NAME: { id: "projectName", column: 1, label: qx.locale.Manager.tr("Project"), - width: 170, + width: 150, sortable: true }, STATE: { id: "state", column: 2, label: qx.locale.Manager.tr("Status"), - width: 170 + width: 150 }, SUBMIT: { id: "submit", @@ -89,11 +93,17 @@ qx.Class.define("osparc.jobs.RunsTable", { width: 130, sortable: true }, - ACTION_STOP: { - id: "action_stop", + ACTION_CANCEL: { + id: "action_cancel", column: 6, - label: "", - width: 40 + label: qx.locale.Manager.tr("Cancel"), + width: 50 + }, + ACTION_INFO: { + id: "action_info", + column: 7, + label: qx.locale.Manager.tr("Info"), + width: 50 }, } }, @@ -122,17 +132,35 @@ qx.Class.define("osparc.jobs.RunsTable", { }, __handleButtonClick: function(action, row) { + this.resetSelection(); const rowData = this.getTableModel().getRowData(row); switch (action) { case "info": { - this.fireDataEvent("runSelected", rowData); + const job = osparc.store.Jobs.getInstance().getJob(rowData["projectUuid"]); + if (!job) { + return; + } + const allInfo = { + "image": job.getInfo() ? osparc.utils.Utils.deepCloneObject(job.getInfo()) : {}, + "customMetadata": job.getCustomMetadata() ? osparc.utils.Utils.deepCloneObject(job.getCustomMetadata()) : {}, + } + const runInfo = new osparc.jobs.Info(allInfo); + const win = osparc.jobs.Info.popUpInWindow(runInfo); + win.setCaption(rowData["projectName"]); break; } - case "run": - case "stop": - case "logs": { - const msg = `I wish I could ${action} the job ${rowData["projectUuid"]}`; - osparc.FlashMessenger.logAs(msg, "WARNING"); + case "cancel": { + const params = { + url: { + "studyId": rowData["projectUuid"], + }, + }; + osparc.data.Resources.fetch("runPipeline", "stopPipeline", params) + .then(() => { + const msg = this.tr("Stopping pipeline"); + osparc.FlashMessenger.logAs(msg, "INFO"); + }) + .catch(err => osparc.FlashMessenger.logError(err)); break; } default: diff --git a/services/static-webserver/client/source/class/osparc/jobs/RunsTableModel.js b/services/static-webserver/client/source/class/osparc/jobs/RunsTableModel.js index cd75159534aa..da829b670989 100644 --- a/services/static-webserver/client/source/class/osparc/jobs/RunsTableModel.js +++ b/services/static-webserver/client/source/class/osparc/jobs/RunsTableModel.js @@ -60,7 +60,7 @@ qx.Class.define("osparc.jobs.RunsTableModel", { const offset = 0; const limit = 1; const resolveWResponse = true; - osparc.store.Jobs.getInstance().fetchJobs(offset, limit, JSON.stringify(this.getOrderBy()), resolveWResponse) + osparc.store.Jobs.getInstance().fetchJobsActive(offset, limit, JSON.stringify(this.getOrderBy()), resolveWResponse) .then(resp => { this._onRowCountLoaded(resp["_meta"].total) }) @@ -76,7 +76,7 @@ qx.Class.define("osparc.jobs.RunsTableModel", { const lastRow = Math.min(qxLastRow, this._rowCount - 1); // Returns a request promise with given offset and limit const getFetchPromise = (offset, limit) => { - return osparc.store.Jobs.getInstance().fetchJobs(offset, limit, JSON.stringify(this.getOrderBy())) + return osparc.store.Jobs.getInstance().fetchJobsActive(offset, limit, JSON.stringify(this.getOrderBy())) .then(jobs => { const data = []; const jobsCols = osparc.jobs.RunsTable.COLS; @@ -84,7 +84,7 @@ qx.Class.define("osparc.jobs.RunsTableModel", { data.push({ [jobsCols.PROJECT_UUID.id]: job.getProjectUuid(), [jobsCols.PROJECT_NAME.id]: job.getProjectName(), - [jobsCols.STATE.id]: job.getState(), + [jobsCols.STATE.id]: osparc.data.Job.STATUS_LABELS[job.getState()] || job.getState(), [jobsCols.SUBMIT.id]: job.getSubmittedAt() ? osparc.utils.Utils.formatDateAndTime(job.getSubmittedAt()) : "-", [jobsCols.START.id]: job.getStartedAt() ? osparc.utils.Utils.formatDateAndTime(job.getStartedAt()) : "-", [jobsCols.END.id]: job.getEndedAt() ? osparc.utils.Utils.formatDateAndTime(job.getEndedAt()) : "-", diff --git a/services/static-webserver/client/source/class/osparc/jobs/SubRunsTable.js b/services/static-webserver/client/source/class/osparc/jobs/SubRunsTable.js index 2f457bb3efbb..ecdb587ac827 100644 --- a/services/static-webserver/client/source/class/osparc/jobs/SubRunsTable.js +++ b/services/static-webserver/client/source/class/osparc/jobs/SubRunsTable.js @@ -37,11 +37,11 @@ qx.Class.define("osparc.jobs.SubRunsTable", { Object.values(this.self().COLS).forEach(col => columnModel.setColumnWidth(col.column, col.width)); - const iconPathInfo = "osparc/circle-info-text.svg"; + const iconPathInfo = "osparc/icons/circle-info-text.svg"; const fontButtonRendererInfo = new osparc.ui.table.cellrenderer.ImageButtonRenderer("info", iconPathInfo); columnModel.setDataCellRenderer(this.self().COLS.INFO.column, fontButtonRendererInfo); - const iconPathLogs = "osparc/logs-text.svg"; + const iconPathLogs = "osparc/icons/logs-text.svg"; const fontButtonRendererLogs = new osparc.ui.table.cellrenderer.ImageButtonRenderer("logs", iconPathLogs); columnModel.setDataCellRenderer(this.self().COLS.LOGS.column, fontButtonRendererLogs); @@ -54,25 +54,25 @@ qx.Class.define("osparc.jobs.SubRunsTable", { id: "projectUuid", column: 0, label: qx.locale.Manager.tr("Project Id"), - width: 170 + width: 200 }, NODE_ID: { id: "nodeId", column: 1, label: qx.locale.Manager.tr("Node Id"), - width: 170 + width: 200 }, NODE_NAME: { id: "nodeName", column: 2, label: qx.locale.Manager.tr("Node"), - width: 170 + width: 100 }, APP: { id: "app", column: 3, label: qx.locale.Manager.tr("App"), - width: 150 + width: 100 }, STATE: { id: "state", @@ -108,7 +108,7 @@ qx.Class.define("osparc.jobs.SubRunsTable", { id: "credits", column: 9, label: qx.locale.Manager.tr("Credits"), - width: 70 + width: 50 }, INFO: { id: "info", @@ -145,6 +145,7 @@ qx.Class.define("osparc.jobs.SubRunsTable", { }, __handleButtonClick: function(action, row) { + this.resetSelection(); const rowData = this.getTableModel().getRowData(row); switch (action) { case "info": { @@ -161,6 +162,23 @@ qx.Class.define("osparc.jobs.SubRunsTable", { win.setCaption(rowData["nodeName"]); break; } + case "logs": { + const job = osparc.store.Jobs.getInstance().getJob(rowData["projectUuid"]); + if (!job) { + return; + } + const subJob = job.getSubJob(rowData["nodeId"]); + if (!subJob) { + return; + } + const logDownloadLink = subJob.getLogDownloadLink() + if (logDownloadLink) { + osparc.utils.Utils.downloadLink(logDownloadLink, "GET", rowData["nodeName"] + ".logs"); + } else { + osparc.component.message.FlashMessenger.getInstance().logAsWarning(this.tr("No logs available")); + } + break; + } default: console.warn(`Unknown action: ${action}`); break; diff --git a/services/static-webserver/client/source/class/osparc/jobs/SubRunsTableModel.js b/services/static-webserver/client/source/class/osparc/jobs/SubRunsTableModel.js index 33f65f03a85d..eeafa9ea2515 100644 --- a/services/static-webserver/client/source/class/osparc/jobs/SubRunsTableModel.js +++ b/services/static-webserver/client/source/class/osparc/jobs/SubRunsTableModel.js @@ -96,7 +96,7 @@ qx.Class.define("osparc.jobs.SubRunsTableModel", { [subJobsCols.NODE_ID.id]: subJob.getNodeId(), [subJobsCols.NODE_NAME.id]: subJob.getNodeName(), [subJobsCols.APP.id]: appName + ":" + displayVersion, - [subJobsCols.STATE.id]: subJob.getState(), + [subJobsCols.STATE.id]: osparc.data.Job.STATUS_LABELS[subJob.getState()] || subJob.getState(), [subJobsCols.PROGRESS.id]: subJob.getProgress() * 100 + "%", [subJobsCols.START.id]: startedAt ? osparc.utils.Utils.formatDateAndTime(startedAt) : "-", [subJobsCols.END.id]: endedAt ? osparc.utils.Utils.formatDateAndTime(endedAt) : "-", diff --git a/services/static-webserver/client/source/class/osparc/store/Jobs.js b/services/static-webserver/client/source/class/osparc/store/Jobs.js index 183d25c621fb..87ea782e18a6 100644 --- a/services/static-webserver/client/source/class/osparc/store/Jobs.js +++ b/services/static-webserver/client/source/class/osparc/store/Jobs.js @@ -25,7 +25,11 @@ qx.Class.define("osparc.store.Jobs", { init: [], nullable: true, event: "changeJobs" - } + }, + }, + + events: { + "changeJobsActive": "qx.event.type.Data", }, statics: { @@ -33,7 +37,7 @@ qx.Class.define("osparc.store.Jobs", { }, members: { - fetchJobs: function( + fetchJobsActive: function( offset = 0, limit = this.self().SERVER_MAX_LIMIT, orderBy = { @@ -52,18 +56,19 @@ qx.Class.define("osparc.store.Jobs", { const options = { resolveWResponse: true }; - return osparc.data.Resources.fetch("jobs", "getPage", params, options) + return osparc.data.Resources.fetch("jobsActive", "getPage", params, options) .then(jobsResp => { - const jobs = []; + this.fireDataEvent("changeJobsActive", jobsResp["_meta"]["total"]); + const jobsActive = []; if ("data" in jobsResp) { - jobsResp["data"].forEach(jobData => { - jobs.push(this.addJob(jobData)); + jobsResp["data"].forEach(jobActiveData => { + jobsActive.push(this.__addJob(jobActiveData)); }); } if (resolveWResponse) { return jobsResp; } - return jobs; + return jobsActive; }) .catch(err => console.error(err)); }, @@ -85,7 +90,7 @@ qx.Class.define("osparc.store.Jobs", { .catch(err => console.error(err)); }, - addJob: function(jobData) { + __addJob: function(jobData) { const jobs = this.getJobs(); const jobFound = jobs.find(job => job.getProjectUuid() === jobData["projectUuid"]); if (jobFound) { @@ -94,18 +99,20 @@ qx.Class.define("osparc.store.Jobs", { } const job = new osparc.data.Job(jobData); jobs.push(job); - this.fireDataEvent("changeJobs"); return job; }, addSubJob: function(subJobData) { - const jobs = this.getJobs(); - const jobFound = jobs.find(job => job.getProjectUuid() === subJobData["projectUuid"]); - if (jobFound) { - const subJob = jobFound.addSubJob(subJobData); - return subJob; + let job = this.getJob(subJobData["projectUuid"]); + if (!job) { + const jobs = this.getJobs(); + job = new osparc.data.Job({ + "projectUuid": subJobData["projectUuid"], + }); + jobs.push(job); } - return null; + const subJob = job.addSubJob(subJobData); + return subJob; }, getJob: function(projectUuid) { diff --git a/services/static-webserver/client/source/class/osparc/widget/logger/LoggerModel.js b/services/static-webserver/client/source/class/osparc/widget/logger/LoggerModel.js index edca2766438e..953c4448cafb 100644 --- a/services/static-webserver/client/source/class/osparc/widget/logger/LoggerModel.js +++ b/services/static-webserver/client/source/class/osparc/widget/logger/LoggerModel.js @@ -83,13 +83,13 @@ qx.Class.define("osparc.widget.logger.LoggerModel", { let iconSource = ""; switch (logLevel) { case logLevels.INFO: - iconSource = "osparc/circle-info-solid.svg"; + iconSource = "osparc/icons/circle-info-solid.svg"; break; case logLevels.WARNING: - iconSource = "osparc/circle-exclamation-solid.svg"; + iconSource = "osparc/icons/circle-exclamation-solid.svg"; break; case logLevels.ERROR: - iconSource = "osparc/circle-xmark-solid.svg"; + iconSource = "osparc/icons/circle-xmark-solid.svg"; break; } return iconSource; diff --git a/services/static-webserver/client/source/resource/osparc/circle-stop-text.svg b/services/static-webserver/client/source/resource/osparc/circle-stop-text.svg deleted file mode 100644 index 1470fd0f2952..000000000000 --- a/services/static-webserver/client/source/resource/osparc/circle-stop-text.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/services/static-webserver/client/source/resource/osparc/circle-exclamation-solid.svg b/services/static-webserver/client/source/resource/osparc/icons/circle-exclamation-solid.svg similarity index 87% rename from services/static-webserver/client/source/resource/osparc/circle-exclamation-solid.svg rename to services/static-webserver/client/source/resource/osparc/icons/circle-exclamation-solid.svg index fa80fe70833c..8f3a586bfa80 100644 --- a/services/static-webserver/client/source/resource/osparc/circle-exclamation-solid.svg +++ b/services/static-webserver/client/source/resource/osparc/icons/circle-exclamation-solid.svg @@ -1 +1 @@ - \ No newline at end of file + diff --git a/services/static-webserver/client/source/resource/osparc/circle-info-solid.svg b/services/static-webserver/client/source/resource/osparc/icons/circle-info-solid.svg similarity index 87% rename from services/static-webserver/client/source/resource/osparc/circle-info-solid.svg rename to services/static-webserver/client/source/resource/osparc/icons/circle-info-solid.svg index 64e1e26721a3..5e28757f57f6 100644 --- a/services/static-webserver/client/source/resource/osparc/circle-info-solid.svg +++ b/services/static-webserver/client/source/resource/osparc/icons/circle-info-solid.svg @@ -1 +1 @@ - \ No newline at end of file + diff --git a/services/static-webserver/client/source/resource/osparc/circle-info-text.svg b/services/static-webserver/client/source/resource/osparc/icons/circle-info-text.svg similarity index 87% rename from services/static-webserver/client/source/resource/osparc/circle-info-text.svg rename to services/static-webserver/client/source/resource/osparc/icons/circle-info-text.svg index b753c59738b3..5b80476aa3a4 100644 --- a/services/static-webserver/client/source/resource/osparc/circle-info-text.svg +++ b/services/static-webserver/client/source/resource/osparc/icons/circle-info-text.svg @@ -1 +1 @@ - \ No newline at end of file + diff --git a/services/static-webserver/client/source/resource/osparc/circle-xmark-solid.svg b/services/static-webserver/client/source/resource/osparc/icons/circle-xmark-solid.svg similarity index 97% rename from services/static-webserver/client/source/resource/osparc/circle-xmark-solid.svg rename to services/static-webserver/client/source/resource/osparc/icons/circle-xmark-solid.svg index e7074e6aadf9..2844d4d2e155 100644 --- a/services/static-webserver/client/source/resource/osparc/circle-xmark-solid.svg +++ b/services/static-webserver/client/source/resource/osparc/icons/circle-xmark-solid.svg @@ -1 +1 @@ - \ No newline at end of file + diff --git a/services/static-webserver/client/source/resource/osparc/icons/circle-xmark-text.svg b/services/static-webserver/client/source/resource/osparc/icons/circle-xmark-text.svg new file mode 100644 index 000000000000..134422e82d53 --- /dev/null +++ b/services/static-webserver/client/source/resource/osparc/icons/circle-xmark-text.svg @@ -0,0 +1 @@ + diff --git a/services/static-webserver/client/source/resource/osparc/logs-text.svg b/services/static-webserver/client/source/resource/osparc/icons/logs-text.svg similarity index 96% rename from services/static-webserver/client/source/resource/osparc/logs-text.svg rename to services/static-webserver/client/source/resource/osparc/icons/logs-text.svg index 20dd82103ce4..115dfe4b2655 100644 --- a/services/static-webserver/client/source/resource/osparc/logs-text.svg +++ b/services/static-webserver/client/source/resource/osparc/icons/logs-text.svg @@ -1 +1 @@ - \ No newline at end of file + diff --git a/services/static-webserver/client/source/resource/osparc/trash-text.svg b/services/static-webserver/client/source/resource/osparc/trash-text.svg deleted file mode 100644 index 0cbfe135be55..000000000000 --- a/services/static-webserver/client/source/resource/osparc/trash-text.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file