@@ -71,129 +222,3 @@
class="jobs-progress" />
-
diff --git a/client/src/components/WorkflowInvocationState/util.ts b/client/src/components/WorkflowInvocationState/util.ts
new file mode 100644
index 00000000000..bbda2253e93
--- /dev/null
+++ b/client/src/components/WorkflowInvocationState/util.ts
@@ -0,0 +1,71 @@
+import { InvocationJobsSummary } from "@/api/invocations";
+
+export const NON_TERMINAL_STATES = ["new", "queued", "running", "waiting"];
+export const ERROR_STATES = ["error", "deleted"];
+export const TERMINAL_STATES = ["ok", "skipped"].concat(ERROR_STATES);
+export const POPULATED_STATE_FAILED = "failed";
+
+function countStates(jobSummary: InvocationJobsSummary | null, queryStates: string[]): number {
+ let count = 0;
+ const states = jobSummary?.states;
+ if (states) {
+ for (const state of queryStates) {
+ count += jobSummary.states[state] || 0;
+ }
+ }
+ return count;
+}
+
+export function jobCount(jobSummary: InvocationJobsSummary | null) {
+ const states = jobSummary?.states;
+ let count = 0;
+ if (states) {
+ for (const index in states) {
+ const stateCount = states[index];
+ if (stateCount) {
+ count += stateCount;
+ }
+ }
+ }
+ return count;
+}
+
+export function okCount(jobSummary: InvocationJobsSummary): number {
+ return countStates(jobSummary, ["ok", "skipped"]);
+}
+
+export function runningCount(jobSummary: InvocationJobsSummary): number {
+ return countStates(jobSummary, ["running"]);
+}
+
+export function numTerminal(jobSummary: InvocationJobsSummary): number {
+ return countStates(jobSummary, TERMINAL_STATES);
+}
+
+export function errorCount(jobSummary: InvocationJobsSummary): number {
+ return countStates(jobSummary, ERROR_STATES);
+}
+
+function isNew(jobSummary: InvocationJobsSummary) {
+ return jobSummary.populated_state && jobSummary.populated_state == "new";
+}
+
+function anyWithStates(jobSummary: InvocationJobsSummary, queryStates: string[]) {
+ const states = jobSummary.states;
+ for (const index in queryStates) {
+ const state: string = queryStates[index] as string;
+ if ((states[state] || 0) > 0) {
+ return true;
+ }
+ }
+ return false;
+}
+
+export function isTerminal(jobSummary: InvocationJobsSummary) {
+ if (isNew(jobSummary)) {
+ return false;
+ } else {
+ const anyNonTerminal = anyWithStates(jobSummary, NON_TERMINAL_STATES);
+ return !anyNonTerminal;
+ }
+}
diff --git a/client/src/components/admin/JobsList.vue b/client/src/components/admin/JobsList.vue
index 25045f32832..98a3a782874 100644
--- a/client/src/components/admin/JobsList.vue
+++ b/client/src/components/admin/JobsList.vue
@@ -101,8 +101,8 @@ import JobsTable from "components/admin/JobsTable";
import Heading from "components/Common/Heading";
import filtersMixin from "components/Indices/filtersMixin";
import { jobsProvider } from "components/providers/JobProvider";
+import { NON_TERMINAL_STATES } from "components/WorkflowInvocationState/util";
import { getAppRoot } from "onload/loadConfig";
-import JOB_STATES_MODEL from "utils/job-states-model";
import { errorMessageAsString } from "utils/simple-error";
import { commonJobFields } from "./JobFields";
@@ -211,7 +211,7 @@ export default {
const unfinishedJobs = [];
const finishedJobs = [];
newVal.forEach((item) => {
- if (JOB_STATES_MODEL.NON_TERMINAL_STATES.includes(item.state)) {
+ if (NON_TERMINAL_STATES.includes(item.state)) {
unfinishedJobs.push(item);
} else {
finishedJobs.push(item);
diff --git a/client/src/components/providers/utils.js b/client/src/components/providers/utils.js
index 04d0626ace8..7ac65b87c03 100644
--- a/client/src/components/providers/utils.js
+++ b/client/src/components/providers/utils.js
@@ -1,8 +1,8 @@
+import { NON_TERMINAL_STATES } from "components/WorkflowInvocationState/util";
import { snakeCase } from "lodash";
-import JOB_STATES_MODEL from "utils/job-states-model";
export function stateIsTerminal(result) {
- return !JOB_STATES_MODEL.NON_TERMINAL_STATES.includes(result.state);
+ return !NON_TERMINAL_STATES.includes(result.state);
}
export const HasAttributesMixin = {
diff --git a/client/src/utils/job-states-model.js b/client/src/utils/job-states-model.js
deleted file mode 100644
index 09fa58c9c43..00000000000
--- a/client/src/utils/job-states-model.js
+++ /dev/null
@@ -1,222 +0,0 @@
-import Backbone from "backbone";
-import { getAppRoot } from "onload/loadConfig";
-import AJAX_QUEUE from "utils/ajax-queue";
-
-/** ms between fetches when checking running jobs/datasets for updates */
-var UPDATE_DELAY = 2000;
-var NON_TERMINAL_STATES = ["new", "queued", "running", "waiting"];
-var ERROR_STATES = ["error", "deleted"];
-var TERMINAL_STATES = ["ok", "skipped"].concat(ERROR_STATES);
-const POPULATED_STATE_FAILED = "failed";
-/** Fetch state on add or just wait for polling to start. */
-var FETCH_STATE_ON_ADD = false;
-var BATCH_FETCH_STATE = true;
-
-var JobStatesSummary = Backbone.Model.extend({
- url: function () {
- return `${getAppRoot()}api/histories/${this.attributes.history_id}/contents/dataset_collections/${
- this.attributes.collection_id
- }/jobs_summary`;
- },
-
- hasDetails: function () {
- return this.has("populated_state");
- },
-
- new: function () {
- return !this.hasDetails() || this.get("populated_state") == "new";
- },
-
- populationFailed: function () {
- return this.get("populated_state") === POPULATED_STATE_FAILED;
- },
-
- errored: function () {
- return this.populationFailed() || this.anyWithStates(ERROR_STATES);
- },
-
- states: function () {
- return this.get("states") || {};
- },
-
- anyWithState: function (queryState) {
- return (this.states()[queryState] || 0) > 0;
- },
-
- anyWithStates: function (queryStates) {
- var states = this.states();
- for (var index in queryStates) {
- if ((states[queryStates[index]] || 0) > 0) {
- return true;
- }
- }
- return false;
- },
-
- numWithStates: function (queryStates) {
- var states = this.states();
- var count = 0;
- for (var index in queryStates) {
- count += states[queryStates[index]] || 0;
- }
- return count;
- },
-
- numInError: function () {
- return this.numWithStates(ERROR_STATES);
- },
-
- numTerminal: function () {
- return this.numWithStates(TERMINAL_STATES);
- },
-
- running: function () {
- return this.anyWithState("running");
- },
-
- terminal: function () {
- if (this.new()) {
- return false;
- } else {
- var anyNonTerminal = this.anyWithStates(NON_TERMINAL_STATES);
- return !anyNonTerminal;
- }
- },
-
- jobCount: function () {
- var states = this.states();
- var count = 0;
- for (var index in states) {
- count += states[index];
- }
- return count;
- },
-
- toString: function () {
- return `JobStatesSummary(id=${this.get("id")})`;
- },
-});
-
-var JobStatesSummaryCollection = Backbone.Collection.extend({
- model: JobStatesSummary,
-
- initialize: function () {
- /* By default we wait for a polling update to do model fetch because
- FETCH_STATE_ON_ADD is false to load the application and target components
- as quickly as possible. that said if the polling is turned off
- (!this.active) and collections are added - we need to fetch those still.
- This happens for instance in the single history view where a history is
- shown in a static way and not polled.
- */
- if (FETCH_STATE_ON_ADD) {
- this.on({
- add: (model) => model.fetch(),
- });
- } else {
- this.on({
- add: (model) => {
- if (!this.active) {
- model.fetch();
- }
- },
- });
- }
-
- /** cached timeout id for the dataset updater */
- this.updateTimeoutId = null;
- // this.checkForUpdates();
- this.active = true;
- },
-
- trackModel: function (historyContent) {
- if (historyContent.has("job_states_summary")) {
- // already tracked...
- return;
- }
-
- const historyId = this.historyId;
- if (historyContent.attributes.history_content_type === "dataset_collection") {
- var jobSourceType = historyContent.attributes.job_source_type;
- var jobSourceId = historyContent.attributes.job_source_id;
- if (jobSourceType) {
- this.add({
- id: jobSourceId,
- model: jobSourceType,
- history_id: historyId,
- collection_id: historyContent.attributes.id,
- });
- historyContent.jobStatesSummary = this.get(jobSourceId);
- }
- }
- },
-
- url: function () {
- var nonTerminalModels = this.models.filter((model) => {
- return !model.terminal();
- });
- var ids = nonTerminalModels
- .map((summary) => {
- return summary.get("id");
- })
- .join(",");
- var types = nonTerminalModels
- .map((summary) => {
- return summary.get("model");
- })
- .join(",");
- return `${getAppRoot()}api/histories/${this.historyId}/jobs_summary?ids=${ids}&types=${types}`;
- },
-
- monitor: function () {
- this.clearUpdateTimeout();
- if (!this.active) {
- return;
- }
-
- var _delayThenMonitorAgain = () => {
- this.updateTimeoutId = setTimeout(() => {
- this.monitor();
- }, UPDATE_DELAY);
- };
-
- var nonTerminalModels = this.models.filter((model) => {
- return !model.terminal();
- });
-
- if (nonTerminalModels.length > 0 && !BATCH_FETCH_STATE) {
- // Allow models to fetch their own details.
- var updateFunctions = nonTerminalModels.map((summary) => {
- return () => {
- return summary.fetch();
- };
- });
-
- return new AJAX_QUEUE.AjaxQueue(updateFunctions).done(_delayThenMonitorAgain);
- } else if (nonTerminalModels.length > 0) {
- // Batch fetch updated state...
- this.fetch({ remove: false }).done(_delayThenMonitorAgain);
- } else {
- _delayThenMonitorAgain();
- }
- },
-
- /** clear the timeout and the cached timeout id */
- clearUpdateTimeout: function () {
- if (this.updateTimeoutId) {
- clearTimeout(this.updateTimeoutId);
- this.updateTimeoutId = null;
- }
- },
-
- toString: function () {
- return `JobStatesSummaryCollection()`;
- },
-});
-
-export default {
- JobStatesSummary,
- JobStatesSummaryCollection,
- FETCH_STATE_ON_ADD,
- NON_TERMINAL_STATES,
- ERROR_STATES,
-};