diff --git a/plugins/cicd-statistics/src/charts/logic/analysis.ts b/plugins/cicd-statistics/src/charts/logic/analysis.ts new file mode 100644 index 0000000000..4333ae3a6f --- /dev/null +++ b/plugins/cicd-statistics/src/charts/logic/analysis.ts @@ -0,0 +1,79 @@ +/* + * Copyright 2022 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { FilterStatusType } from '../../apis/types'; +import { ChartableStageAnalysis, ChartableStageDatapoints } from '../types'; + +export function getAnalysis( + values: Array, + status: FilterStatusType, +): ChartableStageAnalysis { + const analysis: ChartableStageAnalysis = { + max: 0, + min: 0, + avg: 0, + med: 0, + }; + + const definedValues = values + .filter(value => typeof value[status] !== 'undefined') + .map(value => value[status]!) + .sort((a, b) => a - b); + + analysis.max = definedValues[definedValues.length - 1] ?? 0; + + analysis.min = definedValues[0] ?? 0; + + analysis.avg = + definedValues.length === 0 + ? 0 + : definedValues.reduce((prev, cur) => prev + cur, 0) / values.length; + + analysis.med = definedValues[Math.ceil(definedValues.length / 2)] ?? 0; + + return analysis; +} + +export function makeCombinedAnalysis( + analysis: Record, + allDurations: Array, +): ChartableStageAnalysis { + if (analysis.succeeded) { + // If succeeded is a viewed status, it's probably what's expected to see + // overall. Otherwise combine all other. + return analysis.succeeded; + } + + const analysisValues = Object.values(analysis); + + const max = analysisValues.reduce((prev, cur) => Math.max(prev, cur.max), 0); + const min = analysisValues.reduce( + (prev, cur) => Math.min(prev, cur.min), + max, + ); + const avg = !allDurations.length + ? 0 + : allDurations.reduce((prev, cur) => prev + cur, 0) / allDurations.length; + allDurations.sort((a, b) => a - b); + const med = allDurations[Math.ceil(allDurations.length / 2)] ?? 0; + + return { + max, + min, + avg, + med, + }; +} diff --git a/plugins/cicd-statistics/src/charts/logic/finalize-stage.ts b/plugins/cicd-statistics/src/charts/logic/finalize-stage.ts index e56c5649ff..bb54fbf41e 100644 --- a/plugins/cicd-statistics/src/charts/logic/finalize-stage.ts +++ b/plugins/cicd-statistics/src/charts/logic/finalize-stage.ts @@ -17,7 +17,7 @@ import { FilterStatusType, statusTypes } from '../../apis/types'; import { Averagify, ChartableStage } from '../types'; import { countBuildsPerDay } from './count-builds-per-day'; -import { getAnalysis } from './get-analysis'; +import { getAnalysis, makeCombinedAnalysis } from './analysis'; import { average } from './utils'; interface FinalizeStageOptions { @@ -54,7 +54,7 @@ export function finalizeStage( countBuildsPerDay(values); - const avgDuration: [duration: number, count: number] = [0, 0]; + const allDurations: Array = []; statusTypes.forEach(status => { analysis[status] = getAnalysis(values, status); @@ -70,8 +70,7 @@ export function finalizeStage( (duration): duration is number => typeof duration !== 'undefined', ); - avgDuration[0] += durationsDense.reduce((prev, cur) => prev + cur, 0); - avgDuration[1] += durationsDense.length; + durationsDense.forEach(dur => allDurations.push(dur)); const averages = durationsDense.map((_, i) => average( @@ -88,16 +87,7 @@ export function finalizeStage( }); }); - const analysisValues = Object.values(analysis); - combinedAnalysis.max = analysisValues.reduce( - (prev, cur) => Math.max(prev, cur.max), - 0, - ); - combinedAnalysis.min = analysisValues.reduce( - (prev, cur) => Math.min(prev, cur.min), - combinedAnalysis.max, - ); - combinedAnalysis.avg = !avgDuration[1] ? 0 : avgDuration[0] / avgDuration[1]; + Object.assign(combinedAnalysis, makeCombinedAnalysis(analysis, allDurations)); stage.stages.forEach(subStage => finalizeStage(subStage, options)); } diff --git a/plugins/cicd-statistics/src/charts/logic/get-analysis.ts b/plugins/cicd-statistics/src/charts/logic/get-analysis.ts deleted file mode 100644 index 493355991d..0000000000 --- a/plugins/cicd-statistics/src/charts/logic/get-analysis.ts +++ /dev/null @@ -1,49 +0,0 @@ -/* - * Copyright 2022 The Backstage Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import { FilterStatusType } from '../../apis/types'; -import { ChartableStageAnalysis, ChartableStageDatapoints } from '../types'; - -export function getAnalysis( - values: Array, - status: FilterStatusType, -): ChartableStageAnalysis { - const analysis: ChartableStageAnalysis = { - max: 0, - min: 0, - avg: 0, - }; - - const definedValues = values.filter( - value => typeof value[status] !== 'undefined', - ); - - analysis.max = definedValues.reduce( - (prev, cur) => Math.max(prev, cur[status]!), - 0, - ); - analysis.min = definedValues.reduce( - (prev, cur) => Math.min(prev, cur[status]!), - analysis.max, - ); - analysis.avg = - definedValues.length === 0 - ? 0 - : definedValues.reduce((prev, cur) => prev + cur[status]!, 0) / - values.length; - - return analysis; -} diff --git a/plugins/cicd-statistics/src/charts/logic/utils.ts b/plugins/cicd-statistics/src/charts/logic/utils.ts index eaed81ac89..0c3900d025 100644 --- a/plugins/cicd-statistics/src/charts/logic/utils.ts +++ b/plugins/cicd-statistics/src/charts/logic/utils.ts @@ -38,17 +38,17 @@ export function getOrSetStage( export function makeStage(name: string): ChartableStage { return { analysis: { - unknown: { avg: 0, max: 0, min: 0 }, - enqueued: { avg: 0, max: 0, min: 0 }, - scheduled: { avg: 0, max: 0, min: 0 }, - running: { avg: 0, max: 0, min: 0 }, - aborted: { avg: 0, max: 0, min: 0 }, - succeeded: { avg: 0, max: 0, min: 0 }, - failed: { avg: 0, max: 0, min: 0 }, - stalled: { avg: 0, max: 0, min: 0 }, - expired: { avg: 0, max: 0, min: 0 }, + unknown: { avg: 0, med: 0, max: 0, min: 0 }, + enqueued: { avg: 0, med: 0, max: 0, min: 0 }, + scheduled: { avg: 0, med: 0, max: 0, min: 0 }, + running: { avg: 0, med: 0, max: 0, min: 0 }, + aborted: { avg: 0, med: 0, max: 0, min: 0 }, + succeeded: { avg: 0, med: 0, max: 0, min: 0 }, + failed: { avg: 0, med: 0, max: 0, min: 0 }, + stalled: { avg: 0, med: 0, max: 0, min: 0 }, + expired: { avg: 0, med: 0, max: 0, min: 0 }, }, - combinedAnalysis: { avg: 0, max: 0, min: 0 }, + combinedAnalysis: { avg: 0, med: 0, max: 0, min: 0 }, statusSet: new Set(), name, values: [], diff --git a/plugins/cicd-statistics/src/charts/stage-chart.tsx b/plugins/cicd-statistics/src/charts/stage-chart.tsx index eceb37a35c..3e18bd17cd 100644 --- a/plugins/cicd-statistics/src/charts/stage-chart.tsx +++ b/plugins/cicd-statistics/src/charts/stage-chart.tsx @@ -104,7 +104,8 @@ export function StageChart(props: StageChartProps) { > }> - {stage.name} (avg {formatDuration(stage.combinedAnalysis.avg)}) + {stage.name} (med {formatDuration(stage.combinedAnalysis.med)}, avg{' '} + {formatDuration(stage.combinedAnalysis.avg)}) diff --git a/plugins/cicd-statistics/src/charts/types.ts b/plugins/cicd-statistics/src/charts/types.ts index 42b3cf4f04..75a72a8a4f 100644 --- a/plugins/cicd-statistics/src/charts/types.ts +++ b/plugins/cicd-statistics/src/charts/types.ts @@ -30,9 +30,14 @@ export type ChartableStageDatapoints = { }; export interface ChartableStageAnalysis { + /** Maximum duration */ max: number; + /** Minimum duration */ min: number; + /** Average duration */ avg: number; + /** Median duration */ + med: number; } export interface ChartableStage {