diff --git a/client/src/components/job-lifecycle/job-lifecycle.component.jsx b/client/src/components/job-lifecycle/job-lifecycle.component.jsx index 58475e300..6ddbcfc51 100644 --- a/client/src/components/job-lifecycle/job-lifecycle.component.jsx +++ b/client/src/components/job-lifecycle/job-lifecycle.component.jsx @@ -1,27 +1,12 @@ import React, {useCallback, useEffect, useState} from 'react'; import moment from "moment"; import axios from 'axios'; -import {Card, Space, Table} from 'antd'; +import {Badge, Card, Space, Table, Tag} from 'antd'; import {gql, useQuery} from "@apollo/client"; import {DateTimeFormatterFunction} from "../../utils/DateFormatter"; import {isEmpty} from "lodash"; -import {Bar, BarChart, CartesianGrid, Legend, Tooltip, YAxis} from "recharts"; - -const transformDataForChart = (durations) => { - const output = {}; - // output.amt = durations.total; - // output.name = 'Total'; - durations.summations.forEach((summation) => { - output[summation.status] = summation.value; - }); - return [output]; -} -const getColor = (key) => { - // Generate a random color - const randomColor = '#' + Math.floor(Math.random() * 16777215).toString(16); - return randomColor; -}; +require('./job-lifecycle.styles.scss'); export function JobLifecycleComponent({job, ...rest}) { const [loading, setLoading] = useState(true); @@ -124,34 +109,92 @@ export function JobLifecycleComponent({job, ...rest}) { {!loading ? ( lifecycleData && lifecycleData.lifecycle && lifecycleData.durations ? ( - - - - - - - - { - Object.keys(transformDataForChart(lifecycleData.durations)[0]).map((key) => { - return ( - - ) - }) - } - + + + Statuses + + + )} + style={{width: '100%'}} + > + + {lifecycleData.durations.summations.map((key, index, array) => { + const isFirst = index === 0; + const isLast = index === array.length - 1; + + return ( + + {Math.round(key.percentage)}% + + ); + })} + + + + {lifecycleData.durations.summations.map((key) => ( + + + {key.status} ({key.roundedPercentage}) + + + ))} + - - + + Accumulated Time: {lifecycleData.durations.humanReadableTotal} + + + + + + Transitions + + > + )}> + diff --git a/client/src/components/job-lifecycle/job-lifecycle.styles.scss b/client/src/components/job-lifecycle/job-lifecycle.styles.scss new file mode 100644 index 000000000..e69de29bb diff --git a/client/src/pages/jobs-detail/jobs-detail.page.component.jsx b/client/src/pages/jobs-detail/jobs-detail.page.component.jsx index 0f335d982..b2e8159c8 100644 --- a/client/src/pages/jobs-detail/jobs-detail.page.component.jsx +++ b/client/src/pages/jobs-detail/jobs-detail.page.component.jsx @@ -289,13 +289,6 @@ export function JobsDetailPage({ form={form} /> - Lifecycle} - key="lifecycle" - > - - - Lifecycle} + key="lifecycle" + > + + + + diff --git a/server/utils/calculateStatusDuration.js b/server/utils/calculateStatusDuration.js index ae0ef8238..b7d432e32 100644 --- a/server/utils/calculateStatusDuration.js +++ b/server/utils/calculateStatusDuration.js @@ -1,17 +1,23 @@ -const moment = require('moment'); const durationToHumanReadable = require("./durationToHumanReadable"); -/** - * Calculate the duration of each status of a job - * @param transitions - * @returns {{}} - */ +const moment = require("moment"); +const _ = require("lodash"); +const crypto = require('crypto'); + +const getColor = (key) => { + const hash = crypto.createHash('sha256'); + hash.update(key); + const hashedKey = hash.digest('hex'); + const num = parseInt(hashedKey, 16); + return '#' + (num % 16777215).toString(16).padStart(6, '0'); +}; + const calculateStatusDuration = (transitions) => { let statusDuration = {}; let totalDuration = 0; let summations = []; transitions.forEach((transition, index) => { - let duration = transition.duration_minutes; + let duration = transition.duration; totalDuration += duration; if (!transition.prev_value) { @@ -42,16 +48,31 @@ const calculateStatusDuration = (transitions) => { } }); + // Calculate the percentage for each status +// Calculate the percentage for each status + let totalPercentage = 0; + const statusKeys = Object.keys(statusDuration); + statusKeys.forEach((status, index) => { + if (index !== statusKeys.length - 1) { + const percentage = (statusDuration[status].value / totalDuration) * 100; + totalPercentage += percentage; + statusDuration[status].percentage = percentage; + } else { + statusDuration[status].percentage = 100 - totalPercentage; + } + }); + for (let [status, {value, humanReadable}] of Object.entries(statusDuration)) { if (status !== 'total') { - summations.push({status, value, humanReadable}); + summations.push({status, value, humanReadable, percentage: statusDuration[status].percentage, color: getColor(status), roundedPercentage: `${Math.round(statusDuration[status].percentage)}%`}); } } const humanReadableTotal = durationToHumanReadable(moment.duration(totalDuration)); return { - summations, + summations: _.orderBy(summations, ['value'], ['asc']), + totalStatuses: summations.length, total: totalDuration, humanReadableTotal };