Update milestone counters on new issue. (#16183)
Co-authored-by: 6543 <6543@obermui.de> Co-authored-by: zeripath <art27@cantab.net>
This commit is contained in:
parent
6a083a7234
commit
36c158bc93
|
@ -141,6 +141,12 @@ func (milestone *Milestone) checkForConsistency(t *testing.T) {
|
||||||
actual := getCount(t, x.Where("is_closed=?", true), &Issue{MilestoneID: milestone.ID})
|
actual := getCount(t, x.Where("is_closed=?", true), &Issue{MilestoneID: milestone.ID})
|
||||||
assert.EqualValues(t, milestone.NumClosedIssues, actual,
|
assert.EqualValues(t, milestone.NumClosedIssues, actual,
|
||||||
"Unexpected number of closed issues for milestone %+v", milestone)
|
"Unexpected number of closed issues for milestone %+v", milestone)
|
||||||
|
|
||||||
|
completeness := 0
|
||||||
|
if milestone.NumIssues > 0 {
|
||||||
|
completeness = milestone.NumClosedIssues * 100 / milestone.NumIssues
|
||||||
|
}
|
||||||
|
assert.Equal(t, completeness, milestone.Completeness)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (label *Label) checkForConsistency(t *testing.T) {
|
func (label *Label) checkForConsistency(t *testing.T) {
|
||||||
|
|
|
@ -647,8 +647,10 @@ func (issue *Issue) doChangeStatus(e *xorm.Session, doer *User, isMergePull bool
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update issue count of milestone
|
// Update issue count of milestone
|
||||||
if err := updateMilestoneClosedNum(e, issue.MilestoneID); err != nil {
|
if issue.MilestoneID > 0 {
|
||||||
return nil, err
|
if err := updateMilestoneCounters(e, issue.MilestoneID); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := issue.updateClosedNum(e); err != nil {
|
if err := issue.updateClosedNum(e); err != nil {
|
||||||
|
@ -907,7 +909,7 @@ func newIssue(e *xorm.Session, doer *User, opts NewIssueOptions) (err error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
if opts.Issue.MilestoneID > 0 {
|
if opts.Issue.MilestoneID > 0 {
|
||||||
if _, err = e.Exec("UPDATE `milestone` SET num_issues=num_issues+1 WHERE id=?", opts.Issue.MilestoneID); err != nil {
|
if err := updateMilestoneCounters(e, opts.Issue.MilestoneID); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -129,8 +129,12 @@ func GetMilestoneByRepoIDANDName(repoID int64, name string) (*Milestone, error)
|
||||||
|
|
||||||
// GetMilestoneByID returns the milestone via id .
|
// GetMilestoneByID returns the milestone via id .
|
||||||
func GetMilestoneByID(id int64) (*Milestone, error) {
|
func GetMilestoneByID(id int64) (*Milestone, error) {
|
||||||
|
return getMilestoneByID(x, id)
|
||||||
|
}
|
||||||
|
|
||||||
|
func getMilestoneByID(e Engine, id int64) (*Milestone, error) {
|
||||||
var m Milestone
|
var m Milestone
|
||||||
has, err := x.ID(id).Get(&m)
|
has, err := e.ID(id).Get(&m)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
} else if !has {
|
} else if !has {
|
||||||
|
@ -155,10 +159,6 @@ func UpdateMilestone(m *Milestone, oldIsClosed bool) error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := updateMilestoneCompleteness(sess, m.ID); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// if IsClosed changed, update milestone numbers of repository
|
// if IsClosed changed, update milestone numbers of repository
|
||||||
if oldIsClosed != m.IsClosed {
|
if oldIsClosed != m.IsClosed {
|
||||||
if err := updateRepoMilestoneNum(sess, m.RepoID); err != nil {
|
if err := updateRepoMilestoneNum(sess, m.RepoID); err != nil {
|
||||||
|
@ -171,23 +171,31 @@ func UpdateMilestone(m *Milestone, oldIsClosed bool) error {
|
||||||
|
|
||||||
func updateMilestone(e Engine, m *Milestone) error {
|
func updateMilestone(e Engine, m *Milestone) error {
|
||||||
m.Name = strings.TrimSpace(m.Name)
|
m.Name = strings.TrimSpace(m.Name)
|
||||||
_, err := e.ID(m.ID).AllCols().
|
_, err := e.ID(m.ID).AllCols().Update(m)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return updateMilestoneCounters(e, m.ID)
|
||||||
|
}
|
||||||
|
|
||||||
|
// updateMilestoneCounters calculates NumIssues, NumClosesIssues and Completeness
|
||||||
|
func updateMilestoneCounters(e Engine, id int64) error {
|
||||||
|
_, err := e.ID(id).
|
||||||
SetExpr("num_issues", builder.Select("count(*)").From("issue").Where(
|
SetExpr("num_issues", builder.Select("count(*)").From("issue").Where(
|
||||||
builder.Eq{"milestone_id": m.ID},
|
builder.Eq{"milestone_id": id},
|
||||||
)).
|
)).
|
||||||
SetExpr("num_closed_issues", builder.Select("count(*)").From("issue").Where(
|
SetExpr("num_closed_issues", builder.Select("count(*)").From("issue").Where(
|
||||||
builder.Eq{
|
builder.Eq{
|
||||||
"milestone_id": m.ID,
|
"milestone_id": id,
|
||||||
"is_closed": true,
|
"is_closed": true,
|
||||||
},
|
},
|
||||||
)).
|
)).
|
||||||
Update(m)
|
Update(&Milestone{})
|
||||||
return err
|
if err != nil {
|
||||||
}
|
return err
|
||||||
|
}
|
||||||
func updateMilestoneCompleteness(e Engine, milestoneID int64) error {
|
_, err = e.Exec("UPDATE `milestone` SET completeness=100*num_closed_issues/(CASE WHEN num_issues > 0 THEN num_issues ELSE 1 END) WHERE id=?",
|
||||||
_, err := e.Exec("UPDATE `milestone` SET completeness=100*num_closed_issues/(CASE WHEN num_issues > 0 THEN num_issues ELSE 1 END) WHERE id=?",
|
id,
|
||||||
milestoneID,
|
|
||||||
)
|
)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -256,25 +264,15 @@ func changeMilestoneAssign(e *xorm.Session, doer *User, issue *Issue, oldMilesto
|
||||||
}
|
}
|
||||||
|
|
||||||
if oldMilestoneID > 0 {
|
if oldMilestoneID > 0 {
|
||||||
if err := updateMilestoneTotalNum(e, oldMilestoneID); err != nil {
|
if err := updateMilestoneCounters(e, oldMilestoneID); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if issue.IsClosed {
|
|
||||||
if err := updateMilestoneClosedNum(e, oldMilestoneID); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if issue.MilestoneID > 0 {
|
if issue.MilestoneID > 0 {
|
||||||
if err := updateMilestoneTotalNum(e, issue.MilestoneID); err != nil {
|
if err := updateMilestoneCounters(e, issue.MilestoneID); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if issue.IsClosed {
|
|
||||||
if err := updateMilestoneClosedNum(e, issue.MilestoneID); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if oldMilestoneID > 0 || issue.MilestoneID > 0 {
|
if oldMilestoneID > 0 || issue.MilestoneID > 0 {
|
||||||
|
@ -622,29 +620,6 @@ func updateRepoMilestoneNum(e Engine, repoID int64) error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
func updateMilestoneTotalNum(e Engine, milestoneID int64) (err error) {
|
|
||||||
if _, err = e.Exec("UPDATE `milestone` SET num_issues=(SELECT count(*) FROM issue WHERE milestone_id=?) WHERE id=?",
|
|
||||||
milestoneID,
|
|
||||||
milestoneID,
|
|
||||||
); err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
return updateMilestoneCompleteness(e, milestoneID)
|
|
||||||
}
|
|
||||||
|
|
||||||
func updateMilestoneClosedNum(e Engine, milestoneID int64) (err error) {
|
|
||||||
if _, err = e.Exec("UPDATE `milestone` SET num_closed_issues=(SELECT count(*) FROM issue WHERE milestone_id=? AND is_closed=?) WHERE id=?",
|
|
||||||
milestoneID,
|
|
||||||
true,
|
|
||||||
milestoneID,
|
|
||||||
); err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
return updateMilestoneCompleteness(e, milestoneID)
|
|
||||||
}
|
|
||||||
|
|
||||||
// _____ _ _ _____ _
|
// _____ _ _ _____ _
|
||||||
// |_ _| __ __ _ ___| | _____ __| |_ _(_)_ __ ___ ___ ___
|
// |_ _| __ __ _ ___| | _____ __| |_ _(_)_ __ ___ ___ ___
|
||||||
// | || '__/ _` |/ __| |/ / _ \/ _` | | | | | '_ ` _ \ / _ \/ __|
|
// | || '__/ _` |/ __| |/ / _ \/ _` | | | | | '_ ` _ \ / _ \/ __|
|
||||||
|
|
|
@ -215,7 +215,7 @@ func TestChangeMilestoneStatus(t *testing.T) {
|
||||||
CheckConsistencyFor(t, &Repository{ID: milestone.RepoID}, &Milestone{})
|
CheckConsistencyFor(t, &Repository{ID: milestone.RepoID}, &Milestone{})
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestUpdateMilestoneClosedNum(t *testing.T) {
|
func TestUpdateMilestoneCounters(t *testing.T) {
|
||||||
assert.NoError(t, PrepareTestDatabase())
|
assert.NoError(t, PrepareTestDatabase())
|
||||||
issue := AssertExistsAndLoadBean(t, &Issue{MilestoneID: 1},
|
issue := AssertExistsAndLoadBean(t, &Issue{MilestoneID: 1},
|
||||||
"is_closed=0").(*Issue)
|
"is_closed=0").(*Issue)
|
||||||
|
@ -224,14 +224,14 @@ func TestUpdateMilestoneClosedNum(t *testing.T) {
|
||||||
issue.ClosedUnix = timeutil.TimeStampNow()
|
issue.ClosedUnix = timeutil.TimeStampNow()
|
||||||
_, err := x.ID(issue.ID).Cols("is_closed", "closed_unix").Update(issue)
|
_, err := x.ID(issue.ID).Cols("is_closed", "closed_unix").Update(issue)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.NoError(t, updateMilestoneClosedNum(x, issue.MilestoneID))
|
assert.NoError(t, updateMilestoneCounters(x, issue.MilestoneID))
|
||||||
CheckConsistencyFor(t, &Milestone{})
|
CheckConsistencyFor(t, &Milestone{})
|
||||||
|
|
||||||
issue.IsClosed = false
|
issue.IsClosed = false
|
||||||
issue.ClosedUnix = 0
|
issue.ClosedUnix = 0
|
||||||
_, err = x.ID(issue.ID).Cols("is_closed", "closed_unix").Update(issue)
|
_, err = x.ID(issue.ID).Cols("is_closed", "closed_unix").Update(issue)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.NoError(t, updateMilestoneClosedNum(x, issue.MilestoneID))
|
assert.NoError(t, updateMilestoneCounters(x, issue.MilestoneID))
|
||||||
CheckConsistencyFor(t, &Milestone{})
|
CheckConsistencyFor(t, &Milestone{})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue