mirror of
				https://github.com/Unleash/unleash.git
				synced 2025-10-27 11:02:16 +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:
		
							parent
							
								
									bce25bf0f1
								
							
						
					
					
						commit
						ce4a243165
					
				| @ -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', | ||||
|  | ||||
| @ -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, | ||||
|  | ||||
| @ -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); | ||||
|     }); | ||||
| }); | ||||
| @ -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; | ||||
| } | ||||
		Loading…
	
		Reference in New Issue
	
	Block a user