From 717e54100399484098819a1137c30c8959374f0e Mon Sep 17 00:00:00 2001 From: andreas-unleash Date: Wed, 3 Apr 2024 11:58:10 +0300 Subject: [PATCH] fix: make TTP stat show last week calculation (#6766) Currently the median time to production stat is showing the aggregated median across all dates. This pr changes the calculation to only use the last week summary like the rest of the stat widgets. Screenshot 2024-04-03 at 11 25 31 --------- Signed-off-by: andreas-unleash --- .../src/component/insights/InsightsCharts.tsx | 5 +- .../hooks/useFilteredFlagsSummary.test.ts | 12 ++++ .../insights/hooks/useFilteredFlagsSummary.ts | 19 ++++++ .../insights/hooks/useInsightsData.ts | 6 -- .../hooks/useMedianTimeToProduction.test.ts | 63 ------------------- .../hooks/useMedianTimeToProduction.ts | 43 ------------- 6 files changed, 33 insertions(+), 115 deletions(-) delete mode 100644 frontend/src/component/insights/hooks/useMedianTimeToProduction.test.ts delete mode 100644 frontend/src/component/insights/hooks/useMedianTimeToProduction.ts diff --git a/frontend/src/component/insights/InsightsCharts.tsx b/frontend/src/component/insights/InsightsCharts.tsx index 9a3a71f6d5..438032346f 100644 --- a/frontend/src/component/insights/InsightsCharts.tsx +++ b/frontend/src/component/insights/InsightsCharts.tsx @@ -40,8 +40,8 @@ interface IChartsProps { averageUsers: number; averageHealth?: string; flagsPerUser?: string; + medianTimeToProduction?: number; }; - medianTimeToProduction: number; loading: boolean; projects: string[]; } @@ -71,7 +71,6 @@ export const InsightsCharts: VFC = ({ userTrends, groupedProjectsData, flagTrends, - medianTimeToProduction, groupedMetricsData, environmentTypeTrends, loading, @@ -167,7 +166,7 @@ export const InsightsCharts: VFC = ({ { potentiallyStale: 0, users: 1, date: '', + timeToProduction: 4, }, { week: '2024-01', @@ -26,6 +27,7 @@ describe('useFilteredFlagTrends', () => { potentiallyStale: 0, users: 2, date: '', + timeToProduction: 5, }, { week: '2024-02', @@ -36,6 +38,7 @@ describe('useFilteredFlagTrends', () => { potentiallyStale: 1, users: 1, date: '', + timeToProduction: 4, }, { week: '2024-02', @@ -46,6 +49,7 @@ describe('useFilteredFlagTrends', () => { potentiallyStale: 0, users: 3, date: '', + timeToProduction: 2, }, ], { total: 1 } as unknown as InstanceInsightsSchemaUsers, @@ -60,6 +64,7 @@ describe('useFilteredFlagTrends', () => { averageUsers: 2, averageHealth: '79', flagsPerUser: '14.00', + medianTimeToProduction: 3, }); }); @@ -76,6 +81,7 @@ describe('useFilteredFlagTrends', () => { potentiallyStale: 0, users: 0, date: '', + timeToProduction: 4, }, ], { total: 1 } as unknown as InstanceInsightsSchemaUsers, @@ -90,6 +96,7 @@ describe('useFilteredFlagTrends', () => { averageUsers: 0, averageHealth: '100', flagsPerUser: '5.00', + medianTimeToProduction: 4, }); }); @@ -106,6 +113,7 @@ describe('useFilteredFlagTrends', () => { potentiallyStale: 0, users: 0, date: '', + timeToProduction: 2, }, { week: '2024-01', @@ -116,6 +124,7 @@ describe('useFilteredFlagTrends', () => { potentiallyStale: 0, users: 3, date: '', + timeToProduction: 5, }, ], { total: 1 } as unknown as InstanceInsightsSchemaUsers, @@ -130,6 +139,7 @@ describe('useFilteredFlagTrends', () => { averageUsers: 1.5, averageHealth: '100', flagsPerUser: '10.00', + medianTimeToProduction: 3.5, }); }); @@ -146,6 +156,7 @@ describe('useFilteredFlagTrends', () => { potentiallyStale: 0, users: 0, date: '', + timeToProduction: 0, }, ], { total: 1 } as unknown as InstanceInsightsSchemaUsers, @@ -160,6 +171,7 @@ describe('useFilteredFlagTrends', () => { averageUsers: 0, averageHealth: undefined, flagsPerUser: '0.00', + medianTimeToProduction: 0, }); }); }); diff --git a/frontend/src/component/insights/hooks/useFilteredFlagsSummary.ts b/frontend/src/component/insights/hooks/useFilteredFlagsSummary.ts index 2635ea64bb..8bfbe78851 100644 --- a/frontend/src/component/insights/hooks/useFilteredFlagsSummary.ts +++ b/frontend/src/component/insights/hooks/useFilteredFlagsSummary.ts @@ -4,6 +4,10 @@ import type { InstanceInsightsSchemaUsers, } from 'openapi'; +const validTimeToProduction = ( + item: InstanceInsightsSchemaProjectFlagTrendsItem, +) => Boolean(item) && typeof item.timeToProduction === 'number'; + // NOTE: should we move project filtering to the backend? export const useFilteredFlagsSummary = ( filteredProjectFlagTrends: InstanceInsightsSchemaProjectFlagTrendsItem[], @@ -42,6 +46,20 @@ export const useFilteredFlagsSummary = ( }, ); + const timesToProduction: number[] = lastWeekSummary + .filter(validTimeToProduction) + .map((item) => item.timeToProduction!); // Non-null assertion is safe due to filter + + // Calculate median timeToProduction for lastWeekSummary + timesToProduction.sort((a, b) => a - b); + const midIndex = Math.floor(timesToProduction.length / 2); + const medianTimeToProduction = + timesToProduction.length % 2 === 0 + ? (timesToProduction[midIndex - 1] + + timesToProduction[midIndex]) / + 2 + : timesToProduction[midIndex]; + const flagsPerUserCalculation = sum.total / users.total; const flagsPerUser = Number.isNaN(flagsPerUserCalculation) ? 'N/A' @@ -54,5 +72,6 @@ export const useFilteredFlagsSummary = ( averageHealth: sum.total ? ((sum.active / (sum.total || 1)) * 100).toFixed(0) : undefined, + medianTimeToProduction, }; }, [filteredProjectFlagTrends]); diff --git a/frontend/src/component/insights/hooks/useInsightsData.ts b/frontend/src/component/insights/hooks/useInsightsData.ts index 7499e56737..7c1b72b1b6 100644 --- a/frontend/src/component/insights/hooks/useInsightsData.ts +++ b/frontend/src/component/insights/hooks/useInsightsData.ts @@ -3,7 +3,6 @@ import type { InstanceInsightsSchema } from 'openapi'; import { useFilteredTrends } from './useFilteredTrends'; import { useGroupedProjectTrends } from './useGroupedProjectTrends'; import { useFilteredFlagsSummary } from './useFilteredFlagsSummary'; -import { useMedianTimeToProduction } from './useMedianTimeToProduction'; export const useInsightsData = ( executiveDashboardData: InstanceInsightsSchema, @@ -27,9 +26,6 @@ export const useInsightsData = ( executiveDashboardData.users, ); - const medianTimeToProduction = - useMedianTimeToProduction(groupedProjectsData); - return useMemo( () => ({ ...executiveDashboardData, @@ -40,7 +36,6 @@ export const useInsightsData = ( users: executiveDashboardData.users, environmentTypeTrends: executiveDashboardData.environmentTypeTrends, summary, - medianTimeToProduction, }), [ executiveDashboardData, @@ -50,7 +45,6 @@ export const useInsightsData = ( metricsData, groupedMetricsData, summary, - medianTimeToProduction, ], ); }; diff --git a/frontend/src/component/insights/hooks/useMedianTimeToProduction.test.ts b/frontend/src/component/insights/hooks/useMedianTimeToProduction.test.ts deleted file mode 100644 index 9ea99efbad..0000000000 --- a/frontend/src/component/insights/hooks/useMedianTimeToProduction.test.ts +++ /dev/null @@ -1,63 +0,0 @@ -import { useMedianTimeToProduction } from './useMedianTimeToProduction'; -import { renderHook } from '@testing-library/react-hooks'; - -describe('useMedianTimeToProduction', () => { - test('returns 0 when projectsData is empty', () => { - const projectsData = {}; - const { result } = renderHook(() => - useMedianTimeToProduction(projectsData), - ); - expect(result.current).toBe(0); - }); - - test('calculates median time to production correctly with even number of projects', () => { - const projectsData = { - project1: [ - { timeToProduction: 10, date: '2023-01-01' }, - { timeToProduction: 20, date: '2023-02-01' }, - ], - project2: [ - { timeToProduction: 15, date: '2023-01-15' }, - { timeToProduction: 25, date: '2023-02-15' }, - ], - project3: [{ timeToProduction: 30, date: '2023-01-20' }], - } as any; - const { result } = renderHook(() => - useMedianTimeToProduction(projectsData), - ); - // With sorted timeToProductions [10, 15, 20, 25, 30], median is the middle value, 20. - expect(result.current).toBe(20); - }); - - test('calculates median time to production correctly with odd number of projects', () => { - const projectsData = { - project1: [ - { timeToProduction: 10, date: '2023-01-01' }, - { timeToProduction: 20, date: '2023-02-01' }, - ], - project2: [{ timeToProduction: 15, date: '2023-01-15' }], - } as any; - const { result } = renderHook(() => - useMedianTimeToProduction(projectsData), - ); - // With sorted timeToProductions [10, 15, 20], median is the middle value, 15. - expect(result.current).toBe(15); - }); - - test('correctly handles all valid time to production values', () => { - const projectsData = { - project1: [{ timeToProduction: 10, date: '2023-01-01' }], - project2: [{ timeToProduction: 25, date: '2023-01-10' }], - project3: [{ date: '2023-01-15' }], - project4: [ - { timeToProduction: 30, date: '2023-02-01' }, - { timeToProduction: 20, date: '2023-02-02' }, - ], - } as any; - const { result } = renderHook(() => - useMedianTimeToProduction(projectsData), - ); - // With sorted timeToProductions [10, 20, 25, 30], the median is the average of 20 and 25, i.e., 22.5. - expect(result.current).toBe(22.5); - }); -}); diff --git a/frontend/src/component/insights/hooks/useMedianTimeToProduction.ts b/frontend/src/component/insights/hooks/useMedianTimeToProduction.ts deleted file mode 100644 index 6ae3df7500..0000000000 --- a/frontend/src/component/insights/hooks/useMedianTimeToProduction.ts +++ /dev/null @@ -1,43 +0,0 @@ -import { useMemo } from 'react'; -import type { - InstanceInsightsSchema, - InstanceInsightsSchemaProjectFlagTrendsItem, -} from 'openapi'; -import type { GroupedDataByProject } from './useGroupedProjectTrends'; - -const validTrend = (trend: InstanceInsightsSchemaProjectFlagTrendsItem) => - Boolean(trend) && Boolean(trend.timeToProduction); - -export const useMedianTimeToProduction = ( - projectsData: GroupedDataByProject< - InstanceInsightsSchema['projectFlagTrends'] - >, -) => - useMemo(() => { - const timesToProduction: number[] = []; - - Object.values(projectsData).forEach((trends) => { - trends.forEach((trend) => { - if (validTrend(trend)) { - timesToProduction.push(trend.timeToProduction!); - } - }); - }); - - if (timesToProduction.length === 0) { - return 0; - } - - timesToProduction.sort((a, b) => a - b); - - const midIndex = Math.floor(timesToProduction.length / 2); - - const median = - timesToProduction.length % 2 === 0 - ? (timesToProduction[midIndex - 1] + - timesToProduction[midIndex]) / - 2 - : timesToProduction[midIndex]; - - return median; - }, [projectsData]);