From b18b128e52fbb45d5322f2ca99075f2ec2c95043 Mon Sep 17 00:00:00 2001 From: Thomas Heartman Date: Mon, 20 Oct 2025 12:17:18 +0200 Subject: [PATCH] feat: add median per week calculation for the new flags in production widget (#10829) --- .../calculateMedian.test.ts | 162 ++++++++++++++++++ .../calculateMedian.ts | 27 +++ 2 files changed, 189 insertions(+) create mode 100644 frontend/src/component/insights/componentsStat/NewProductionFlagsStats/calculateMedian.test.ts create mode 100644 frontend/src/component/insights/componentsStat/NewProductionFlagsStats/calculateMedian.ts diff --git a/frontend/src/component/insights/componentsStat/NewProductionFlagsStats/calculateMedian.test.ts b/frontend/src/component/insights/componentsStat/NewProductionFlagsStats/calculateMedian.test.ts new file mode 100644 index 0000000000..635d9343f8 --- /dev/null +++ b/frontend/src/component/insights/componentsStat/NewProductionFlagsStats/calculateMedian.test.ts @@ -0,0 +1,162 @@ +import { calculateMedian } from './calculateMedian.ts'; + +test('works with empty data', () => { + const result = calculateMedian([]); + expect(result).toBe('N/A'); +}); + +test('correctly identifies the median in odd-length arrays', () => { + const data = [ + { + data: [ + { + week: '2024-01', + date: '2024-01-07 01:00:00', + newProductionFlags: 99, + }, + { + week: '2024-02', + date: '2024-01-14 01:00:00', + newProductionFlags: 0, + }, + { + week: '2024-03', + date: '2024-01-21 01:00:00', + newProductionFlags: 3, + }, + ], + }, + ]; + const result = calculateMedian(data); + expect(result).toBe(3); +}); + +test('correctly identifies the median in even-length arrays', () => { + const data = [ + { + data: [ + { + week: '2024-01', + date: '2024-01-07 01:00:00', + newProductionFlags: 0, + }, + { + week: '2024-02', + date: '2024-01-14 01:00:00', + newProductionFlags: 10, + }, + { + week: '2024-03', + date: '2024-01-21 01:00:00', + newProductionFlags: 12, + }, + { + week: '2024-04', + date: '2024-01-28 01:00:00', + newProductionFlags: 50, + }, + ], + }, + ]; + const result = calculateMedian(data); + expect(result).toBe(11); +}); + +test('handles single week data', () => { + const data = [ + { + data: [ + { + week: '2024-01', + date: '2024-01-07 01:00:00', + newProductionFlags: 7, + }, + ], + }, + ]; + const result = calculateMedian(data); + expect(result).toBe(7); +}); + +test('handles multiple projects with overlapping weeks', () => { + const data = [ + { + data: [ + { + week: '2024-01', + date: '2024-01-07 01:00:00', + newProductionFlags: 11, + }, + { + week: '2024-02', + date: '2024-01-14 01:00:00', + newProductionFlags: 3, + }, + ], + }, + { + data: [ + { + week: '2024-02', + date: '2024-01-14 01:00:00', + newProductionFlags: 7, + }, + { + week: '2024-03', + date: '2024-01-21 01:00:00', + newProductionFlags: 100, + }, + ], + }, + { + data: [ + { + week: '2024-01', + date: '2024-01-07 01:00:00', + newProductionFlags: 1, + }, + { + week: '2024-04', + date: '2024-01-21 01:00:00', + newProductionFlags: 20, + }, + ], + }, + ]; + const result = calculateMedian(data); + expect(result).toBe(16); +}); +describe('handling weeks with no production flags data', () => { + test('it disregards solo entries without production flags data', () => { + const data = [ + { data: [{ week: '2024-02', date: '2024-01-14 01:00:00' }] }, + ]; + const result = calculateMedian(data); + expect(result).toBe('N/A'); + }); + test('weeks without production flags data is disregarded in larger sets', () => { + const data = [ + { + data: [ + { week: '2024-01', date: '2024-01-14 01:00:00' }, + { + week: '2024-02', + date: '2024-01-14 01:00:00', + newProductionFlags: 7, + }, + { + week: '2024-03', + date: '2024-01-21 01:00:00', + newProductionFlags: 3, + }, + ], + }, + { + data: [{ week: '2024-03', date: '2024-01-21 01:00:00' }], + }, + ]; + + const result = calculateMedian(data); + expect(result).toBe(5); + }); +}); diff --git a/frontend/src/component/insights/componentsStat/NewProductionFlagsStats/calculateMedian.ts b/frontend/src/component/insights/componentsStat/NewProductionFlagsStats/calculateMedian.ts new file mode 100644 index 0000000000..60efbc55af --- /dev/null +++ b/frontend/src/component/insights/componentsStat/NewProductionFlagsStats/calculateMedian.ts @@ -0,0 +1,27 @@ +import type { WeekData } from 'component/insights/componentsChart/NewProductionFlagsChart/types.ts'; + +export const calculateMedian = ( + datasets: { data: WeekData[] }[], +): string | number => { + const weekData: Record = {}; + for (const set of datasets) { + for (const { week, newProductionFlags } of set.data) { + if (newProductionFlags === undefined) { + continue; + } + weekData[week] = (weekData[week] ?? 0) + newProductionFlags; + } + } + + const values: number[] = Object.values(weekData).toSorted((a, b) => a - b); + if (values.length === 0) { + return 'N/A'; + } + const middle = Math.floor(values.length / 2); + + if (values.length % 2 === 0) { + return (values[middle - 1] + values[middle]) / 2; + } else { + return values[middle]; + } +};