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

feat: ttp for all projects should show median value per day (#6656)

Separates out the calculation of the median to its own file and tested

Closes #
[1-2212](https://linear.app/unleash/issue/1-2212/timetoproduction-for-all-projects-should-show-the-change-in-the-median)

---------

Signed-off-by: andreas-unleash <andreas@getunleash.ai>
This commit is contained in:
andreas-unleash 2024-03-21 14:21:53 +02:00 committed by GitHub
parent bce25bf0f1
commit ce4a243165
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 84 additions and 34 deletions

View File

@ -51,19 +51,19 @@ export const chartInfo = {
'How the overall health changes over time for the selected projects.',
},
averageTimeToProduction: {
title: 'Average time to production',
title: 'Median time to production',
tooltip:
'How long does it currently take on average from when a feature flag was created until it was enabled in a "production" type environment. This is calculated only from feature flags of the type "release" and is averaged across the selected projects.',
'How long does it currently take on average from when a feature flag was created until it was enabled in a "production" type environment. This is calculated only from feature flags of the type "release" and is the median across the selected projects.',
},
timeToProduction: {
title: 'Time to production',
tooltip:
'How the average time to production changes over time across all projects.',
'How the median time to production changes over time across all projects.',
},
timeToProductionPerProject: {
title: 'Time to production per project',
tooltip:
'How the average time to production changes over time for the selected projects.',
'How the median time to production changes over time for the selected projects.',
},
metrics: {
title: 'Flag evaluation metrics',

View File

@ -11,6 +11,7 @@ import type { GroupedDataByProject } from '../../hooks/useGroupedProjectTrends';
import { usePlaceholderData } from '../../hooks/usePlaceholderData';
import { TimeToProductionTooltip } from './TimeToProductionTooltip/TimeToProductionTooltip';
import { useTheme } from '@mui/material';
import { medianTimeToProduction } from './median-time-to-production';
interface ITimeToProductionChartProps {
projectFlagTrends: GroupedDataByProject<
@ -19,34 +20,6 @@ interface ITimeToProductionChartProps {
isAggregate?: boolean;
}
type GroupedDataByDate<T> = Record<string, T[]>;
type DateResult<T> = Record<string, T>;
function averageTimeToProduction(
projectsData: ExecutiveSummarySchema['projectFlagTrends'],
): DateResult<number> {
// Group the data by date
const groupedData: GroupedDataByDate<number> = {};
projectsData.forEach((item) => {
const { date, timeToProduction } = item;
if (!groupedData[date]) {
groupedData[date] = [];
}
if (timeToProduction !== undefined) {
groupedData[date].push(timeToProduction);
}
});
// Calculate the average time to production for each date
const averageByDate: DateResult<number> = {};
Object.entries(groupedData).forEach(([date, times]) => {
const sum = times.reduce((acc, curr) => acc + curr, 0);
const average = sum / times.length;
averageByDate[date] = average;
});
return averageByDate;
}
export const TimeToProductionChart: VFC<ITimeToProductionChartProps> = ({
projectFlagTrends,
isAggregate,
@ -59,7 +32,7 @@ export const TimeToProductionChart: VFC<ITimeToProductionChartProps> = ({
);
const aggregatedPerDay = useMemo(() => {
const result = averageTimeToProduction(
const result = medianTimeToProduction(
Object.values(projectsDatasets.datasets).flatMap(
(item) => item.data,
),
@ -74,7 +47,7 @@ export const TimeToProductionChart: VFC<ITimeToProductionChartProps> = ({
return {
datasets: [
{
label: 'Time to production',
label: 'Median time to production',
data,
borderColor: theme.palette.primary.light,
backgroundColor: fillGradientPrimary,

View File

@ -0,0 +1,42 @@
import { medianTimeToProduction } from './median-time-to-production';
import type { ExecutiveSummarySchema } from 'openapi';
describe('medianTimeToProduction', () => {
it('calculates the median with a single date and an odd number of projects', () => {
const projectsData = [
{ date: '2023-03-21', timeToProduction: 10 },
{ date: '2023-03-21', timeToProduction: 20 },
{ date: '2023-03-21', timeToProduction: 30 },
] as unknown as ExecutiveSummarySchema['projectFlagTrends'];
const expected = { '2023-03-21': 20 };
expect(medianTimeToProduction(projectsData)).toEqual(expected);
});
it('calculates the median with a single date and an even number of projects', () => {
const projectsData = [
{ date: '2023-03-22', timeToProduction: 10 },
{ date: '2023-03-22', timeToProduction: 20 },
{ date: '2023-03-22', timeToProduction: 30 },
{ date: '2023-03-22', timeToProduction: 40 },
] as unknown as ExecutiveSummarySchema['projectFlagTrends'];
const expected = { '2023-03-22': 25 };
expect(medianTimeToProduction(projectsData)).toEqual(expected);
});
it('calculates the median for multiple dates with varying numbers of projects', () => {
const projectsData = [
{ date: '2023-03-23', timeToProduction: 5 },
{ date: '2023-03-23', timeToProduction: 15 },
{ date: '2023-03-24', timeToProduction: 10 },
{ date: '2023-03-24', timeToProduction: 20 },
{ date: '2023-03-24', timeToProduction: 30 },
{ date: '2023-03-25', timeToProduction: 25 },
] as unknown as ExecutiveSummarySchema['projectFlagTrends'];
const expected = {
'2023-03-23': 10,
'2023-03-24': 20,
'2023-03-25': 25,
};
expect(medianTimeToProduction(projectsData)).toEqual(expected);
});
});

View File

@ -0,0 +1,35 @@
import type { ExecutiveSummarySchema } from 'openapi';
type GroupedDataByDate<T> = Record<string, T[]>;
type DateResult<T> = Record<string, T>;
export function medianTimeToProduction(
projectsData: ExecutiveSummarySchema['projectFlagTrends'],
): DateResult<number> {
const groupedData: GroupedDataByDate<number> = {};
projectsData.forEach((item) => {
const { date, timeToProduction } = item;
if (!groupedData[date]) {
groupedData[date] = [];
}
if (timeToProduction !== undefined) {
groupedData[date].push(timeToProduction);
}
});
// Calculate the median time to production for each date
const medianByDate: DateResult<number> = {};
Object.entries(groupedData).forEach(([date, times]) => {
const sortedTimes = times.sort((a, b) => a - b);
const midIndex = Math.floor(sortedTimes.length / 2);
const median =
sortedTimes.length % 2 === 0
? (sortedTimes[midIndex - 1] + sortedTimes[midIndex]) / 2
: sortedTimes[midIndex];
medianByDate[date] = median;
});
return medianByDate;
}