Commit 5ee329f6 authored by Brandon Labuschagne's avatar Brandon Labuschagne Committed by Martin Wortschack

Resolve "[VSA] "Days to completion" chart should display averages"

parent b2e96410
...@@ -50,7 +50,7 @@ export default { ...@@ -50,7 +50,7 @@ export default {
<p> <p>
{{ {{
s__( s__(
'CycleAnalytics|The total time spent in the selected stage for the items that were completed on each date. Data limited to the last 500 items.', 'CycleAnalytics|The average time spent in the selected stage for the items that were completed on each date. Data limited to the last 500 items.',
) )
}} }}
</p> </p>
...@@ -63,7 +63,7 @@ export default { ...@@ -63,7 +63,7 @@ export default {
<scatterplot <scatterplot
v-if="hasData" v-if="hasData"
:x-axis-title="s__('CycleAnalytics|Date')" :x-axis-title="s__('CycleAnalytics|Date')"
:y-axis-title="s__('CycleAnalytics|Total days to completion')" :y-axis-title="s__('CycleAnalytics|Average days to completion')"
:tooltip-date-format="$options.durationChartTooltipDateFormat" :tooltip-date-format="$options.durationChartTooltipDateFormat"
:scatter-data="durationChartPlottableData" :scatter-data="durationChartPlottableData"
/> />
......
...@@ -132,8 +132,8 @@ export const prepareStageErrors = (stages, errors) => ...@@ -132,8 +132,8 @@ export const prepareStageErrors = (stages, errors) =>
* selected: true, * selected: true,
* data: [ * data: [
* { * {
* 'duration_in_seconds': 1234, * 'average_duration_in_seconds': 1234,
* 'finished_at': '2019-09-02T18:25:43.511Z' * 'date': '2019-09-02T18:25:43.511Z'
* }, * },
* ... * ...
* ] * ]
...@@ -144,31 +144,31 @@ export const prepareStageErrors = (stages, errors) => ...@@ -144,31 +144,31 @@ export const prepareStageErrors = (stages, errors) =>
* The data is then transformed and flattened into the following format; * The data is then transformed and flattened into the following format;
* [ * [
* { * {
* 'duration_in_seconds': 1234, * 'average_duration_in_seconds': 1234,
* 'finished_at': '2019-09-02' * 'date': '2019-09-02'
* }, * },
* ... * ...
* ] * ]
* *
* @param {Array} data - The duration data for selected stages * @param {Array} data - The duration data for selected stages
* @returns {Array} An array with each item being an object containing the duration_in_seconds and finished_at values for an event * @returns {Array} An array with each item being an object containing the average_duration_in_seconds and date values for an event
*/ */
export const flattenDurationChartData = (data) => export const flattenDurationChartData = (data) =>
data data
.map((stage) => .map((stage) =>
stage.data.map((event) => { stage.data.map((event) => {
const date = new Date(event.finished_at); const date = new Date(event.date);
return { return {
...event, ...event,
finished_at: dateFormat(date, dateFormats.isoDate), date: dateFormat(date, dateFormats.isoDate),
}; };
}), }),
) )
.flat(); .flat();
/** /**
* Takes the duration data for selected stages, groups the data by day and calculates the total duration * Takes the duration data for selected stages, groups the data by day and calculates the average duration
* per day. * per day, for stages with values on that specific day.
* *
* The received data is expected to be the following format; One top level object in the array per stage, * The received data is expected to be the following format; One top level object in the array per stage,
* each potentially having multiple data entries. * each potentially having multiple data entries.
...@@ -178,8 +178,8 @@ export const flattenDurationChartData = (data) => ...@@ -178,8 +178,8 @@ export const flattenDurationChartData = (data) =>
* selected: true, * selected: true,
* data: [ * data: [
* { * {
* 'duration_in_seconds': 1234, * 'average_duration_in_seconds': 1234,
* 'finished_at': '2019-09-02T18:25:43.511Z' * 'date': '2019-09-02T18:25:43.511Z'
* }, * },
* ... * ...
* ] * ]
...@@ -203,12 +203,11 @@ export const flattenDurationChartData = (data) => ...@@ -203,12 +203,11 @@ export const flattenDurationChartData = (data) =>
* @param {Array} data - The duration data for selected stages * @param {Array} data - The duration data for selected stages
* @param {Date} startDate - The globally selected Value Stream Analytics start date * @param {Date} startDate - The globally selected Value Stream Analytics start date
* @param {Date} endDate - The globally selected Value Stream Analytics end date * @param {Date} endDate - The globally selected Value Stream Analytics end date
* @returns {Array} An array with each item being another arry of three items (plottable date, computed total, tooltip display date) * @returns {Array} An array with each item being another arry of three items (plottable date, computed average, tooltip display date)
*/ */
export const getDurationChartData = (data, startDate, endDate) => { export const getDurationChartData = (data, startDate, endDate) => {
const flattenedData = flattenDurationChartData(data); const flattenedData = flattenDurationChartData(data);
const eventData = []; const eventData = [];
const endOfDay = newDate(endDate); const endOfDay = newDate(endDate);
endOfDay.setHours(23, 59, 59); // make sure we're at the end of the day endOfDay.setHours(23, 59, 59); // make sure we're at the end of the day
...@@ -218,11 +217,13 @@ export const getDurationChartData = (data, startDate, endDate) => { ...@@ -218,11 +217,13 @@ export const getDurationChartData = (data, startDate, endDate) => {
currentDate = dayAfter(currentDate) currentDate = dayAfter(currentDate)
) { ) {
const currentISODate = dateFormat(newDate(currentDate), dateFormats.isoDate); const currentISODate = dateFormat(newDate(currentDate), dateFormats.isoDate);
const valuesForDay = flattenedData.filter((object) => object.finished_at === currentISODate); const valuesForDay = flattenedData.filter((object) => object.date === currentISODate);
const summedData = valuesForDay.reduce((total, value) => total + value.duration_in_seconds, 0); const averagedData =
const summedDataInDays = secondsToDays(summedData); valuesForDay.reduce((total, value) => total + value.average_duration_in_seconds, 0) /
valuesForDay.length;
const averagedDataInDays = secondsToDays(averagedData);
if (summedDataInDays) eventData.push([currentISODate, summedDataInDays, currentISODate]); if (averagedDataInDays) eventData.push([currentISODate, averagedDataInDays, currentISODate]);
} }
return eventData; return eventData;
......
...@@ -28,7 +28,7 @@ export default { ...@@ -28,7 +28,7 @@ export default {
cycleAnalyticsStagePath: cycleAnalyticsStagePath:
'/groups/:id/-/analytics/value_stream_analytics/value_streams/:value_stream_id/stages/:stage_id', '/groups/:id/-/analytics/value_stream_analytics/value_streams/:value_stream_id/stages/:stage_id',
cycleAnalyticsDurationChartPath: cycleAnalyticsDurationChartPath:
'/groups/:id/-/analytics/value_stream_analytics/value_streams/:value_stream_id/stages/:stage_id/duration_chart', '/groups/:id/-/analytics/value_stream_analytics/value_streams/:value_stream_id/stages/:stage_id/average_duration_chart',
cycleAnalyticsGroupLabelsPath: '/groups/:namespace_path/-/labels.json', cycleAnalyticsGroupLabelsPath: '/groups/:namespace_path/-/labels.json',
codeReviewAnalyticsPath: '/api/:version/analytics/code_review', codeReviewAnalyticsPath: '/api/:version/analytics/code_review',
groupActivityIssuesPath: '/api/:version/analytics/group_activity/issues_count', groupActivityIssuesPath: '/api/:version/analytics/group_activity/issues_count',
......
---
title: VSA - Update duration chart to use averages
merge_request: 59453
author:
type: changed
...@@ -355,7 +355,7 @@ RSpec.describe 'Group value stream analytics filters and data', :js do ...@@ -355,7 +355,7 @@ RSpec.describe 'Group value stream analytics filters and data', :js do
it 'will have data available' do it 'will have data available' do
duration_chart_content = page.find('[data-testid="vsa-duration-chart"]') duration_chart_content = page.find('[data-testid="vsa-duration-chart"]')
expect(duration_chart_content).not_to have_text(_("There is no data available. Please change your selection.")) expect(duration_chart_content).not_to have_text(_("There is no data available. Please change your selection."))
expect(duration_chart_content).to have_text(_('Total days to completion')) expect(duration_chart_content).to have_text(s_('CycleAnalytics|Average days to completion'))
tasks_by_type_chart_content = page.find('.js-tasks-by-type-chart') tasks_by_type_chart_content = page.find('.js-tasks-by-type-chart')
expect(tasks_by_type_chart_content).not_to have_text(_("There is no data available. Please change your selection.")) expect(tasks_by_type_chart_content).not_to have_text(_("There is no data available. Please change your selection."))
...@@ -370,7 +370,7 @@ RSpec.describe 'Group value stream analytics filters and data', :js do ...@@ -370,7 +370,7 @@ RSpec.describe 'Group value stream analytics filters and data', :js do
it 'will filter the data' do it 'will filter the data' do
duration_chart_content = page.find('[data-testid="vsa-duration-chart"]') duration_chart_content = page.find('[data-testid="vsa-duration-chart"]')
expect(duration_chart_content).not_to have_text(_('Total days to completion')) expect(duration_chart_content).not_to have_text(s_('CycleAnalytics|Average days to completion'))
expect(duration_chart_content).to have_text(_("There is no data available. Please change your selection.")) expect(duration_chart_content).to have_text(_("There is no data available. Please change your selection."))
tasks_by_type_chart_content = page.find('.js-tasks-by-type-chart') tasks_by_type_chart_content = page.find('.js-tasks-by-type-chart')
......
...@@ -13,7 +13,7 @@ exports[`DurationChart renders the duration chart 1`] = ` ...@@ -13,7 +13,7 @@ exports[`DurationChart renders the duration chart 1`] = `
<p> <p>
The total time spent in the selected stage for the items that were completed on each date. Data limited to the last 500 items. The average time spent in the selected stage for the items that were completed on each date. Data limited to the last 500 items.
</p> </p>
...@@ -25,10 +25,10 @@ exports[`DurationChart renders the duration chart 1`] = ` ...@@ -25,10 +25,10 @@ exports[`DurationChart renders the duration chart 1`] = `
<scatterplot-stub <scatterplot-stub
medianlinedata="" medianlinedata=""
scatterdata="2019-01-01,29,2019-01-01,2019-01-02,100,2019-01-02" scatterdata="2019-01-01,14,2019-01-01,2019-01-02,50,2019-01-02"
tooltipdateformat="mmm d, yyyy" tooltipdateformat="mmm d, yyyy"
xaxistitle="Date" xaxistitle="Date"
yaxistitle="Total days to completion" yaxistitle="Average days to completion"
/> />
</div> </div>
`; `;
...@@ -30,7 +30,7 @@ export const endpoints = { ...@@ -30,7 +30,7 @@ export const endpoints = {
groupLabels: /groups\/[A-Z|a-z|\d|\-|_]+\/-\/labels.json/, groupLabels: /groups\/[A-Z|a-z|\d|\-|_]+\/-\/labels.json/,
recentActivityData: /analytics\/value_stream_analytics\/summary/, recentActivityData: /analytics\/value_stream_analytics\/summary/,
timeMetricsData: /analytics\/value_stream_analytics\/time_summary/, timeMetricsData: /analytics\/value_stream_analytics\/time_summary/,
durationData: /analytics\/value_stream_analytics\/value_streams\/\w+\/stages\/\w+\/duration_chart/, durationData: /analytics\/value_stream_analytics\/value_streams\/\w+\/stages\/\w+\/average_duration_chart/,
stageData: /analytics\/value_stream_analytics\/value_streams\/\w+\/stages\/\w+\/records/, stageData: /analytics\/value_stream_analytics\/value_streams\/\w+\/stages\/\w+\/records/,
stageMedian: /analytics\/value_stream_analytics\/value_streams\/\w+\/stages\/\w+\/median/, stageMedian: /analytics\/value_stream_analytics\/value_streams\/\w+\/stages\/\w+\/median/,
baseStagesEndpoint: /analytics\/value_stream_analytics\/value_streams\/\w+\/stages$/, baseStagesEndpoint: /analytics\/value_stream_analytics\/value_streams\/\w+\/stages$/,
...@@ -241,12 +241,12 @@ export const taskByTypeFilters = { ...@@ -241,12 +241,12 @@ export const taskByTypeFilters = {
export const rawDurationData = [ export const rawDurationData = [
{ {
duration_in_seconds: 1234000, average_duration_in_seconds: 1234000,
finished_at: '2019-01-01T00:00:00.000Z', date: '2019-01-01T00:00:00.000Z',
}, },
{ {
duration_in_seconds: 4321000, average_duration_in_seconds: 4321000,
finished_at: '2019-01-02T00:00:00.000Z', date: '2019-01-02T00:00:00.000Z',
}, },
]; ];
...@@ -264,25 +264,25 @@ export const transformedDurationData = [ ...@@ -264,25 +264,25 @@ export const transformedDurationData = [
]; ];
export const flattenedDurationData = [ export const flattenedDurationData = [
{ duration_in_seconds: 1234000, finished_at: '2019-01-01' }, { average_duration_in_seconds: 1234000, date: '2019-01-01' },
{ duration_in_seconds: 4321000, finished_at: '2019-01-02' }, { average_duration_in_seconds: 4321000, date: '2019-01-02' },
{ duration_in_seconds: 1234000, finished_at: '2019-01-01' }, { average_duration_in_seconds: 1234000, date: '2019-01-01' },
{ duration_in_seconds: 4321000, finished_at: '2019-01-02' }, { average_duration_in_seconds: 4321000, date: '2019-01-02' },
]; ];
export const durationChartPlottableData = [ export const durationChartPlottableData = [
['2019-01-01', 29, '2019-01-01'], ['2019-01-01', 14, '2019-01-01'],
['2019-01-02', 100, '2019-01-02'], ['2019-01-02', 50, '2019-01-02'],
]; ];
export const rawDurationMedianData = [ export const rawDurationMedianData = [
{ {
duration_in_seconds: 1234000, average_duration_in_seconds: 1234000,
finished_at: '2018-12-01T00:00:00.000Z', date: '2018-12-01T00:00:00.000Z',
}, },
{ {
duration_in_seconds: 4321000, average_duration_in_seconds: 4321000,
finished_at: '2018-12-02T00:00:00.000Z', date: '2018-12-02T00:00:00.000Z',
}, },
]; ];
......
...@@ -553,7 +553,7 @@ describe('Api', () => { ...@@ -553,7 +553,7 @@ describe('Api', () => {
const params = { ...defaultParams }; const params = { ...defaultParams };
const expectedUrl = valueStreamBaseUrl({ const expectedUrl = valueStreamBaseUrl({
id: valueStreamId, id: valueStreamId,
resource: `stages/${stageId}/duration_chart`, resource: `stages/${stageId}/average_duration_chart`,
}); });
mock.onGet(expectedUrl).reply(httpStatus.OK, response); mock.onGet(expectedUrl).reply(httpStatus.OK, response);
......
...@@ -9829,6 +9829,9 @@ msgstr "" ...@@ -9829,6 +9829,9 @@ msgstr ""
msgid "CycleAnalytics|All stages" msgid "CycleAnalytics|All stages"
msgstr "" msgstr ""
msgid "CycleAnalytics|Average days to completion"
msgstr ""
msgid "CycleAnalytics|Date" msgid "CycleAnalytics|Date"
msgstr "" msgstr ""
...@@ -9875,13 +9878,10 @@ msgstr "" ...@@ -9875,13 +9878,10 @@ msgstr ""
msgid "CycleAnalytics|Tasks by type" msgid "CycleAnalytics|Tasks by type"
msgstr "" msgstr ""
msgid "CycleAnalytics|The given date range is larger than 180 days" msgid "CycleAnalytics|The average time spent in the selected stage for the items that were completed on each date. Data limited to the last 500 items."
msgstr ""
msgid "CycleAnalytics|The total time spent in the selected stage for the items that were completed on each date. Data limited to the last 500 items."
msgstr "" msgstr ""
msgid "CycleAnalytics|Total days to completion" msgid "CycleAnalytics|The given date range is larger than 180 days"
msgstr "" msgstr ""
msgid "CycleAnalytics|Type of work" msgid "CycleAnalytics|Type of work"
...@@ -33268,9 +33268,6 @@ msgstr "" ...@@ -33268,9 +33268,6 @@ msgstr ""
msgid "Total cores (CPUs)" msgid "Total cores (CPUs)"
msgstr "" msgstr ""
msgid "Total days to completion"
msgstr ""
msgid "Total issues" msgid "Total issues"
msgstr "" msgstr ""
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment