Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 11 additions & 8 deletions backend/core/models/domainlayer/ticket/sprint.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,10 @@ limitations under the License.
package ticket

import (
"time"

"github.com/apache/incubator-devlake/core/models/common"
"github.com/apache/incubator-devlake/core/models/domainlayer"
"time"
)

var (
Expand All @@ -31,13 +32,15 @@ var (

type Sprint struct {
domainlayer.DomainEntity
Name string `gorm:"type:varchar(255)"`
Url string `gorm:"type:varchar(255)"`
Status string `gorm:"type:varchar(100)"`
StartedDate *time.Time
EndedDate *time.Time
CompletedDate *time.Time
OriginalBoardID string `gorm:"type:varchar(255)"`
Name string `gorm:"type:varchar(255)"`
Url string `gorm:"type:varchar(255)"`
Status string `gorm:"type:varchar(100)"`
StartedDate *time.Time
EndedDate *time.Time
CompletedDate *time.Time
OriginalBoardID string `gorm:"type:varchar(255)"`
CommittedStoryPoint *float64
CompletedStoryPoint *float64
}

func (Sprint) TableName() string {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
/*
Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements. See the NOTICE file distributed with
this work for additional information regarding copyright ownership.
The ASF licenses this file to You under the Apache License, Version 2.0
(the "License"); you may not use this file except in compliance with
the License. You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package migrationscripts

import (
"github.com/apache/incubator-devlake/core/context"
"github.com/apache/incubator-devlake/core/errors"
"github.com/apache/incubator-devlake/core/plugin"
)

var _ plugin.MigrationScript = (*addSprintVelocityFields)(nil)

type sprint20260722 struct {
CommittedStoryPoint *float64
CompletedStoryPoint *float64
}

func (sprint20260722) TableName() string {
return "sprints"
}

type addSprintVelocityFields struct{}

func (script *addSprintVelocityFields) Up(basicRes context.BasicRes) errors.Error {
return basicRes.GetDal().AutoMigrate(new(sprint20260722))
}

func (*addSprintVelocityFields) Version() uint64 {
return 20260722100000
}

func (*addSprintVelocityFields) Name() string {
return "add committed_story_point/completed_story_point to sprints"
}
1 change: 1 addition & 0 deletions backend/core/models/migrationscripts/register.go
Original file line number Diff line number Diff line change
Expand Up @@ -148,5 +148,6 @@ func All() []plugin.MigrationScript {
new(changeIssueComponentToText),
new(changeCqIssueCodeBlocksComponentToText),
new(addCqProjectMetricsHistory),
new(addSprintVelocityFields),
}
}
5 changes: 5 additions & 0 deletions backend/plugins/jira/impl/impl.go
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,7 @@ func (p Jira) GetTablesInfo() []dal.Tabler {
&models.JiraServerInfo{},
&models.JiraSprint{},
&models.JiraSprintIssue{},
&models.JiraSprintReport{},
&models.JiraStatus{},
&models.JiraWorklog{},
&models.JiraIssueComment{},
Expand Down Expand Up @@ -138,6 +139,9 @@ func (p Jira) SubTaskMetas() []plugin.SubTaskMeta {
tasks.CollectSprintsMeta,
tasks.ExtractSprintsMeta,

tasks.CollectSprintReportMeta,
tasks.ExtractSprintReportMeta,

tasks.CollectEpicsMeta,
tasks.ExtractEpicsMeta,

Expand All @@ -153,6 +157,7 @@ func (p Jira) SubTaskMetas() []plugin.SubTaskMeta {

tasks.ConvertSprintsMeta,
tasks.ConvertSprintIssuesMeta,
tasks.ConvertSprintReportMeta,

tasks.CollectDevelopmentPanelMeta,
tasks.ExtractDevelopmentPanelMeta,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
/*
Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements. See the NOTICE file distributed with
this work for additional information regarding copyright ownership.
The ASF licenses this file to You under the Apache License, Version 2.0
(the "License"); you may not use this file except in compliance with
the License. You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package migrationscripts

import (
"github.com/apache/incubator-devlake/core/context"
"github.com/apache/incubator-devlake/core/errors"
"github.com/apache/incubator-devlake/helpers/migrationhelper"
)

type jiraSprintReport20260722 struct {
ConnectionId uint64 `gorm:"primaryKey"`
BoardId uint64 `gorm:"primaryKey"`
SprintId uint64 `gorm:"primaryKey"`
IssueId uint64 `gorm:"primaryKey"`

IssueKey string `gorm:"type:varchar(255)"`
Bucket string `gorm:"type:varchar(32);index"`
Done bool
StoryPointsAtSprintStart *float64
StoryPointsAtSprintEnd *float64
}

func (jiraSprintReport20260722) TableName() string {
return "_tool_jira_sprint_reports"
}

type addSprintReportTable struct{}

func (script *addSprintReportTable) Up(basicRes context.BasicRes) errors.Error {
return migrationhelper.AutoMigrateTables(basicRes, &jiraSprintReport20260722{})
}

func (*addSprintReportTable) Version() uint64 {
return 20260722000000
}

func (*addSprintReportTable) Name() string {
return "add _tool_jira_sprint_reports table to persist Jira's frozen Sprint Report snapshot"
}
1 change: 1 addition & 0 deletions backend/plugins/jira/models/migrationscripts/register.go
Original file line number Diff line number Diff line change
Expand Up @@ -58,5 +58,6 @@ func All() []plugin.MigrationScript {
new(addSubQueryToBoards),
new(changeFixVersionsToText20260707),
new(addExtraJQLToScopeConfig),
new(addSprintReportTable),
}
}
64 changes: 64 additions & 0 deletions backend/plugins/jira/models/sprint_report.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
/*
Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements. See the NOTICE file distributed with
this work for additional information regarding copyright ownership.
The ASF licenses this file to You under the Apache License, Version 2.0
(the "License"); you may not use this file except in compliance with
the License. You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package models

import (
"github.com/apache/incubator-devlake/core/models/common"
)

// Sprint Report bucket values, mirroring the four buckets returned by
// GET rest/greenhopper/1.0/rapid/charts/sprintreport. Jira computes these
// once, at sprint close, so persisting them (rather than reconstructing
// from resolution_date) is what makes committed/completed velocity exact.
const (
SprintReportBucketCompleted = "completed"
SprintReportBucketNotCompleted = "notCompleted"
SprintReportBucketPunted = "punted"
SprintReportBucketCompletedInOtherSprint = "completedInOtherSprint"
)

// JiraSprintReport is a frozen, per-(board, sprint, issue) snapshot taken
// from Jira's Sprint Report at sprint close. Unlike JiraSprintIssue (which
// is derived from each issue's live resolution_date and therefore
// mis-attributes carryover issues), this table stores Jira's own
// point-in-time bucketing, so it doesn't drift.
type JiraSprintReport struct {
common.NoPKModel
ConnectionId uint64 `gorm:"primaryKey"`
BoardId uint64 `gorm:"primaryKey"`
SprintId uint64 `gorm:"primaryKey"`
IssueId uint64 `gorm:"primaryKey"`

IssueKey string `gorm:"type:varchar(255)"`
// Bucket is one of the SprintReportBucket* constants above.
Bucket string `gorm:"type:varchar(32);index"`
Done bool

// StoryPointsAtSprintStart is estimateStatistic.statFieldValue.value in
// Jira's response ("BOS points") — the estimate as it stood when the
// sprint began, i.e. what should be summed for *committed* velocity.
StoryPointsAtSprintStart *float64
// StoryPointsAtSprintEnd is currentEstimateStatistic.statFieldValue.value
// ("EOS points") — the estimate as of sprint close, i.e. what should be
// summed (for Bucket == completed) for *completed* velocity.
StoryPointsAtSprintEnd *float64
}

func (JiraSprintReport) TableName() string {
return "_tool_jira_sprint_reports"
}
73 changes: 73 additions & 0 deletions backend/plugins/jira/tasks/apiv2models/sprint_report.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
/*
Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements. See the NOTICE file distributed with
this work for additional information regarding copyright ownership.
The ASF licenses this file to You under the Apache License, Version 2.0
(the "License"); you may not use this file except in compliance with
the License. You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package apiv2models

import "time"

// SprintReportInput drives the per-(board, sprint) Sprint Report collector.
// It's what gets iterated over via the DAL cursor, and re-attached to each
// raw row so the extractor knows which board/sprint a response belongs to.
type SprintReportInput struct {
BoardId uint64 `json:"board_id"`
SprintId uint64 `json:"sprint_id"`
// UpdateTime is the sprint's CompleteDate; used as the incremental-sync
// watermark since a sprint report only exists/changes once a sprint closes.
UpdateTime *time.Time `json:"update_time"`
}

// SprintReportStatFieldValue mirrors Jira's
// {statFieldValue: {value, text}} shape used for per-issue point estimates.
type SprintReportStatFieldValue struct {
Value *float64 `json:"value"`
Text string `json:"text"`
}

type SprintReportStatistic struct {
StatFieldValue SprintReportStatFieldValue `json:"statFieldValue"`
}

// SprintReportIssue is one entry inside any of the four bucket lists
// (completedIssues, issuesNotCompletedInCurrentSprint, puntedIssues,
// issuesCompletedInAnotherSprint) in the Sprint Report response.
type SprintReportIssue struct {
Id uint64 `json:"id"`
Key string `json:"key"`
TypeName string `json:"typeName"`
Done bool `json:"done"`
// EstimateStatistic is the issue's estimate as of sprint *start*
// ("BOS points" / committed).
EstimateStatistic SprintReportStatistic `json:"estimateStatistic"`
// CurrentEstimateStatistic is the issue's estimate as of sprint *close*
// ("EOS points" / completed).
CurrentEstimateStatistic SprintReportStatistic `json:"currentEstimateStatistic"`
}

// SprintReportContents is the "contents" object of the Sprint Report
// response — the frozen snapshot Jira takes at sprint close.
type SprintReportContents struct {
CompletedIssues []SprintReportIssue `json:"completedIssues"`
IssuesNotCompletedInCurrentSprint []SprintReportIssue `json:"issuesNotCompletedInCurrentSprint"`
PuntedIssues []SprintReportIssue `json:"puntedIssues"`
IssuesCompletedInAnotherSprint []SprintReportIssue `json:"issuesCompletedInAnotherSprint"`
}

// SprintReport is the top-level response of
// GET rest/greenhopper/1.0/rapid/charts/sprintreport?rapidViewId=&sprintId=
type SprintReport struct {
Contents SprintReportContents `json:"contents"`
}
Loading