Commit 332ead95 authored by Heinrich Lee Yu's avatar Heinrich Lee Yu

Merge branch 'issue-233707-expose-timebox-total-stats' into 'master'

Expose timebox stats explicitly

See merge request gitlab-org/gitlab!46774
parents cf36f02d 5708ff63
......@@ -20806,6 +20806,41 @@ Time represented in ISO 8601
"""
scalar Time
"""
Represents the time report stats for timeboxes
"""
type TimeReportStats {
"""
Completed issues metrics
"""
complete: TimeboxMetrics
"""
Incomplete issues metrics
"""
incomplete: TimeboxMetrics
"""
Total issues metrics
"""
total: TimeboxMetrics
}
"""
Represents measured stats metrics for timeboxes
"""
type TimeboxMetrics {
"""
The count metric
"""
count: Int!
"""
The weight metric
"""
weight: Int!
}
"""
Represents a historically accurate report about the timebox
"""
......@@ -20814,6 +20849,11 @@ type TimeboxReport {
Daily scope and completed totals for burnup charts
"""
burnupTimeSeries: [BurnupChartDailyTotals!]
"""
Represents the time report stats for the timebox
"""
stats: TimeReportStats
}
interface TimeboxReportInterface {
......
......@@ -60459,6 +60459,110 @@
"enumValues": null,
"possibleTypes": null
},
{
"kind": "OBJECT",
"name": "TimeReportStats",
"description": "Represents the time report stats for timeboxes",
"fields": [
{
"name": "complete",
"description": "Completed issues metrics",
"args": [
],
"type": {
"kind": "OBJECT",
"name": "TimeboxMetrics",
"ofType": null
},
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "incomplete",
"description": "Incomplete issues metrics",
"args": [
],
"type": {
"kind": "OBJECT",
"name": "TimeboxMetrics",
"ofType": null
},
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "total",
"description": "Total issues metrics",
"args": [
],
"type": {
"kind": "OBJECT",
"name": "TimeboxMetrics",
"ofType": null
},
"isDeprecated": false,
"deprecationReason": null
}
],
"inputFields": null,
"interfaces": [
],
"enumValues": null,
"possibleTypes": null
},
{
"kind": "OBJECT",
"name": "TimeboxMetrics",
"description": "Represents measured stats metrics for timeboxes",
"fields": [
{
"name": "count",
"description": "The count metric",
"args": [
],
"type": {
"kind": "NON_NULL",
"name": null,
"ofType": {
"kind": "SCALAR",
"name": "Int",
"ofType": null
}
},
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "weight",
"description": "The weight metric",
"args": [
],
"type": {
"kind": "NON_NULL",
"name": null,
"ofType": {
"kind": "SCALAR",
"name": "Int",
"ofType": null
}
},
"isDeprecated": false,
"deprecationReason": null
}
],
"inputFields": null,
"interfaces": [
],
"enumValues": null,
"possibleTypes": null
},
{
"kind": "OBJECT",
"name": "TimeboxReport",
......@@ -60485,6 +60589,20 @@
},
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "stats",
"description": "Represents the time report stats for the timebox",
"args": [
],
"type": {
"kind": "OBJECT",
"name": "TimeReportStats",
"ofType": null
},
"isDeprecated": false,
"deprecationReason": null
}
],
"inputFields": null,
......@@ -3057,6 +3057,25 @@ Represents a requirement test report.
| `id` | ID! | ID of the test report |
| `state` | TestReportState! | State of the test report |
### TimeReportStats
Represents the time report stats for timeboxes.
| Field | Type | Description |
| ----- | ---- | ----------- |
| `complete` | TimeboxMetrics | Completed issues metrics |
| `incomplete` | TimeboxMetrics | Incomplete issues metrics |
| `total` | TimeboxMetrics | Total issues metrics |
### TimeboxMetrics
Represents measured stats metrics for timeboxes.
| Field | Type | Description |
| ----- | ---- | ----------- |
| `count` | Int! | The count metric |
| `weight` | Int! | The weight metric |
### TimeboxReport
Represents a historically accurate report about the timebox.
......@@ -3064,6 +3083,7 @@ Represents a historically accurate report about the timebox.
| Field | Type | Description |
| ----- | ---- | ----------- |
| `burnupTimeSeries` | BurnupChartDailyTotals! => Array | Daily scope and completed totals for burnup charts |
| `stats` | TimeReportStats | Represents the time report stats for the timebox |
### Timelog
......
# frozen_string_literal: true
module Types
# rubocop: disable Graphql/AuthorizeTypes
class TimeReportStatsType < BaseObject
graphql_name 'TimeReportStats'
description 'Represents the time report stats for timeboxes'
field :complete, ::Types::TimeboxMetricsType, null: true,
description: 'Completed issues metrics'
field :incomplete, ::Types::TimeboxMetricsType, null: true,
description: 'Incomplete issues metrics'
field :total, ::Types::TimeboxMetricsType, null: true,
description: 'Total issues metrics'
end
end
# frozen_string_literal: true
module Types
# rubocop: disable Graphql/AuthorizeTypes
class TimeboxMetricsType < BaseObject
graphql_name 'TimeboxMetrics'
description 'Represents measured stats metrics for timeboxes'
field :count, GraphQL::INT_TYPE, null: false,
description: 'The count metric'
field :weight, GraphQL::INT_TYPE, null: false,
description: 'The weight metric'
end
end
......@@ -6,6 +6,8 @@ module Types
graphql_name 'TimeboxReport'
description 'Represents a historically accurate report about the timebox'
field :stats, ::Types::TimeReportStatsType, null: true,
description: 'Represents the time report stats for the timebox'
field :burnup_time_series, [::Types::BurnupChartDailyTotalsType], null: true,
description: 'Daily scope and completed totals for burnup charts'
end
......
......@@ -36,7 +36,8 @@ class TimeboxReportService
end
ServiceResponse.success(payload: {
burnup_time_series: chart_data
burnup_time_series: chart_data,
stats: build_stats
})
end
......@@ -218,4 +219,24 @@ class TimeboxReportService
raise ArgumentError, 'Cannot handle timebox type'
end
end
def build_stats
stats_data = chart_data.last
return unless stats_data
{
complete: {
count: stats_data[:completed_count],
weight: stats_data[:completed_weight]
},
incomplete: {
count: stats_data[:scope_count] - stats_data[:completed_count],
weight: stats_data[:scope_weight] - stats_data[:completed_weight]
},
total: {
count: stats_data[:scope_count],
weight: stats_data[:scope_weight]
}
}
end
end
---
title: Expose timebox stats explicitly
merge_request: 46774
author:
type: changed
......@@ -34,7 +34,13 @@ RSpec.describe Resolvers::TimeboxReportResolver do
end
it 'returns burnup chart data' do
expect(subject).to eq(burnup_time_series: [
expect(subject).to eq(
stats: {
complete: { count: 0, weight: 0 },
incomplete: { count: 2, weight: 0 },
total: { count: 2, weight: 0 }
},
burnup_time_series: [
{
date: start_date + 4.days,
scope_count: 1,
......
......@@ -56,15 +56,21 @@ RSpec.shared_examples 'timebox chart' do |timebox_type|
create(:resource_state_event, issue: issues[3], state: :closed, created_at: timebox_start_date - 6.days)
expect(response.success?).to eq(true)
expect(response.payload[:stats]).to eq({
complete: { count: 2, weight: 7 },
incomplete: { count: 2, weight: 3 },
total: { count: 4, weight: 10 }
})
expect(response.payload[:burnup_time_series]).to eq([
{
date: timebox_start_date,
scope_count: 4,
scope_weight: 10,
completed_count: 2,
completed_weight: 7
}
])
{
date: timebox_start_date,
scope_count: 4,
scope_weight: 10,
completed_count: 2,
completed_weight: 7
}
])
end
it 'updates counts and weight when the milestone is added or removed' do
......@@ -98,36 +104,41 @@ RSpec.shared_examples 'timebox chart' do |timebox_type|
create(:"resource_#{timebox_type}_event", issue: issues[0], "#{timebox_type}" => timebox, action: :remove, created_at: timebox_start_date + 21.days)
expect(response.success?).to eq(true)
expect(response.payload[:stats]).to eq({
complete: { count: 0, weight: 0 },
incomplete: { count: 1, weight: 0 },
total: { count: 1, weight: 0 }
})
expect(response.payload[:burnup_time_series]).to eq([
{
date: timebox_start_date + 4.days,
scope_count: 2,
scope_weight: 2,
completed_count: 0,
completed_weight: 0
},
{
date: timebox_start_date + 5.days,
scope_count: 3,
scope_weight: 5,
completed_count: 1,
completed_weight: 3
},
{
date: timebox_start_date + 6.days,
scope_count: 2,
scope_weight: 3,
completed_count: 1,
completed_weight: 3
},
{
date: timebox_start_date + 7.days,
scope_count: 1,
scope_weight: 0,
completed_count: 0,
completed_weight: 0
}
])
{
date: timebox_start_date + 4.days,
scope_count: 2,
scope_weight: 2,
completed_count: 0,
completed_weight: 0
},
{
date: timebox_start_date + 5.days,
scope_count: 3,
scope_weight: 5,
completed_count: 1,
completed_weight: 3
},
{
date: timebox_start_date + 6.days,
scope_count: 2,
scope_weight: 3,
completed_count: 1,
completed_weight: 3
},
{
date: timebox_start_date + 7.days,
scope_count: 1,
scope_weight: 0,
completed_count: 0,
completed_weight: 0
}
])
end
it 'updates the completed counts when issue state is changed' do
......@@ -159,57 +170,62 @@ RSpec.shared_examples 'timebox chart' do |timebox_type|
create(:resource_state_event, issue: issues[1], state: :closed, created_at: timebox_start_date + 9.days)
expect(response.success?).to eq(true)
expect(response.payload[:stats]).to eq({
complete: { count: 0, weight: 0 },
incomplete: { count: 1, weight: 2 },
total: { count: 1, weight: 2 }
})
expect(response.payload[:burnup_time_series]).to eq([
{
date: timebox_start_date,
scope_count: 1,
scope_weight: 2,
completed_count: 0,
completed_weight: 0
},
{
date: timebox_start_date + 1.day,
scope_count: 1,
scope_weight: 2,
completed_count: 1,
completed_weight: 2
},
{
date: timebox_start_date + 3.days,
scope_count: 1,
scope_weight: 2,
completed_count: 0,
completed_weight: 0
},
{
date: timebox_start_date + 4.days,
scope_count: 2,
scope_weight: 5,
completed_count: 0,
completed_weight: 0
},
{
date: timebox_start_date + 5.days,
scope_count: 2,
scope_weight: 5,
completed_count: 0,
completed_weight: 0
},
{
date: timebox_start_date + 7.days,
scope_count: 2,
scope_weight: 5,
completed_count: 1,
completed_weight: 3
},
{
date: timebox_start_date + 8.days,
scope_count: 1,
scope_weight: 2,
completed_count: 0,
completed_weight: 0
}
])
{
date: timebox_start_date,
scope_count: 1,
scope_weight: 2,
completed_count: 0,
completed_weight: 0
},
{
date: timebox_start_date + 1.day,
scope_count: 1,
scope_weight: 2,
completed_count: 1,
completed_weight: 2
},
{
date: timebox_start_date + 3.days,
scope_count: 1,
scope_weight: 2,
completed_count: 0,
completed_weight: 0
},
{
date: timebox_start_date + 4.days,
scope_count: 2,
scope_weight: 5,
completed_count: 0,
completed_weight: 0
},
{
date: timebox_start_date + 5.days,
scope_count: 2,
scope_weight: 5,
completed_count: 0,
completed_weight: 0
},
{
date: timebox_start_date + 7.days,
scope_count: 2,
scope_weight: 5,
completed_count: 1,
completed_weight: 3
},
{
date: timebox_start_date + 8.days,
scope_count: 1,
scope_weight: 2,
completed_count: 0,
completed_weight: 0
}
])
end
it 'updates the weight totals when issue weight is changed' do
......@@ -230,43 +246,48 @@ RSpec.shared_examples 'timebox chart' do |timebox_type|
create(:resource_weight_event, issue: issues[0], weight: 10, created_at: timebox_start_date + 5.days)
expect(response.success?).to eq(true)
expect(response.payload[:stats]).to eq({
complete: { count: 1, weight: 1 },
incomplete: { count: 0, weight: 0 },
total: { count: 1, weight: 1 }
})
expect(response.payload[:burnup_time_series]).to eq([
{
date: timebox_start_date,
scope_count: 1,
scope_weight: 0,
completed_count: 0,
completed_weight: 0
},
{
date: timebox_start_date + 1.day,
scope_count: 1,
scope_weight: 2,
completed_count: 0,
completed_weight: 0
},
{
date: timebox_start_date + 2.days,
scope_count: 2,
scope_weight: 7,
completed_count: 1,
completed_weight: 5
},
{
date: timebox_start_date + 3.days,
scope_count: 2,
scope_weight: 3,
completed_count: 1,
completed_weight: 1
},
{
date: timebox_start_date + 4.days,
scope_count: 1,
scope_weight: 1,
completed_count: 1,
completed_weight: 1
}
])
{
date: timebox_start_date,
scope_count: 1,
scope_weight: 0,
completed_count: 0,
completed_weight: 0
},
{
date: timebox_start_date + 1.day,
scope_count: 1,
scope_weight: 2,
completed_count: 0,
completed_weight: 0
},
{
date: timebox_start_date + 2.days,
scope_count: 2,
scope_weight: 7,
completed_count: 1,
completed_weight: 5
},
{
date: timebox_start_date + 3.days,
scope_count: 2,
scope_weight: 3,
completed_count: 1,
completed_weight: 1
},
{
date: timebox_start_date + 4.days,
scope_count: 1,
scope_weight: 1,
completed_count: 1,
completed_weight: 1
}
])
end
end
end
......
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