1
0
mirror of https://github.com/Unleash/unleash.git synced 2025-01-31 00:16:47 +01:00

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.
<img width="1665" alt="Screenshot 2024-04-03 at 11 25 31"
src="https://github.com/Unleash/unleash/assets/104830839/c6869b48-99bd-4f5b-a25e-7e0e3a2dc9ef">

---------

Signed-off-by: andreas-unleash <andreas@getunleash.ai>
This commit is contained in:
andreas-unleash 2024-04-03 11:58:10 +03:00 committed by GitHub
parent fe6aaf7739
commit 717e541003
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
6 changed files with 33 additions and 115 deletions

View File

@ -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<IChartsProps> = ({
userTrends,
groupedProjectsData,
flagTrends,
medianTimeToProduction,
groupedMetricsData,
environmentTypeTrends,
loading,
@ -167,7 +166,7 @@ export const InsightsCharts: VFC<IChartsProps> = ({
</ChartWidget>
<Widget {...chartInfo.medianTimeToProduction}>
<TimeToProduction
daysToProduction={medianTimeToProduction}
daysToProduction={summary.medianTimeToProduction}
/>
</Widget>
<ChartWidget

View File

@ -16,6 +16,7 @@ describe('useFilteredFlagTrends', () => {
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,
});
});
});

View File

@ -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]);

View File

@ -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,
],
);
};

View File

@ -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);
});
});

View File

@ -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]);