@ -69,25 +69,6 @@ func (m *Milestone) State() api.StateType {
return api . StateOpen
return api . StateOpen
}
}
// APIFormat returns this Milestone in API format.
func ( m * Milestone ) APIFormat ( ) * api . Milestone {
apiMilestone := & api . Milestone {
ID : m . ID ,
State : m . State ( ) ,
Title : m . Name ,
Description : m . Content ,
OpenIssues : m . NumOpenIssues ,
ClosedIssues : m . NumClosedIssues ,
}
if m . IsClosed {
apiMilestone . Closed = m . ClosedDateUnix . AsTimePtr ( )
}
if m . DeadlineUnix . Year ( ) < 9999 {
apiMilestone . Deadline = m . DeadlineUnix . AsTimePtr ( )
}
return apiMilestone
}
// NewMilestone creates new milestone of repository.
// NewMilestone creates new milestone of repository.
func NewMilestone ( m * Milestone ) ( err error ) {
func NewMilestone ( m * Milestone ) ( err error ) {
sess := x . NewSession ( )
sess := x . NewSession ( )
@ -149,157 +130,6 @@ func GetMilestoneByID(id int64) (*Milestone, error) {
return & m , nil
return & m , nil
}
}
// MilestoneList is a list of milestones offering additional functionality
type MilestoneList [ ] * Milestone
func ( milestones MilestoneList ) loadTotalTrackedTimes ( e Engine ) error {
type totalTimesByMilestone struct {
MilestoneID int64
Time int64
}
if len ( milestones ) == 0 {
return nil
}
var trackedTimes = make ( map [ int64 ] int64 , len ( milestones ) )
// Get total tracked time by milestone_id
rows , err := e . Table ( "issue" ) .
Join ( "INNER" , "milestone" , "issue.milestone_id = milestone.id" ) .
Join ( "LEFT" , "tracked_time" , "tracked_time.issue_id = issue.id" ) .
Where ( "tracked_time.deleted = ?" , false ) .
Select ( "milestone_id, sum(time) as time" ) .
In ( "milestone_id" , milestones . getMilestoneIDs ( ) ) .
GroupBy ( "milestone_id" ) .
Rows ( new ( totalTimesByMilestone ) )
if err != nil {
return err
}
defer rows . Close ( )
for rows . Next ( ) {
var totalTime totalTimesByMilestone
err = rows . Scan ( & totalTime )
if err != nil {
return err
}
trackedTimes [ totalTime . MilestoneID ] = totalTime . Time
}
for _ , milestone := range milestones {
milestone . TotalTrackedTime = trackedTimes [ milestone . ID ]
}
return nil
}
func ( m * Milestone ) loadTotalTrackedTime ( e Engine ) error {
type totalTimesByMilestone struct {
MilestoneID int64
Time int64
}
totalTime := & totalTimesByMilestone { MilestoneID : m . ID }
has , err := e . Table ( "issue" ) .
Join ( "INNER" , "milestone" , "issue.milestone_id = milestone.id" ) .
Join ( "LEFT" , "tracked_time" , "tracked_time.issue_id = issue.id" ) .
Where ( "tracked_time.deleted = ?" , false ) .
Select ( "milestone_id, sum(time) as time" ) .
Where ( "milestone_id = ?" , m . ID ) .
GroupBy ( "milestone_id" ) .
Get ( totalTime )
if err != nil {
return err
} else if ! has {
return nil
}
m . TotalTrackedTime = totalTime . Time
return nil
}
// LoadTotalTrackedTimes loads for every milestone in the list the TotalTrackedTime by a batch request
func ( milestones MilestoneList ) LoadTotalTrackedTimes ( ) error {
return milestones . loadTotalTrackedTimes ( x )
}
// LoadTotalTrackedTime loads the tracked time for the milestone
func ( m * Milestone ) LoadTotalTrackedTime ( ) error {
return m . loadTotalTrackedTime ( x )
}
func ( milestones MilestoneList ) getMilestoneIDs ( ) [ ] int64 {
var ids = make ( [ ] int64 , 0 , len ( milestones ) )
for _ , ms := range milestones {
ids = append ( ids , ms . ID )
}
return ids
}
// GetMilestonesByRepoID returns all opened milestones of a repository.
func GetMilestonesByRepoID ( repoID int64 , state api . StateType , listOptions ListOptions ) ( MilestoneList , error ) {
sess := x . Where ( "repo_id = ?" , repoID )
switch state {
case api . StateClosed :
sess = sess . And ( "is_closed = ?" , true )
case api . StateAll :
break
case api . StateOpen :
fallthrough
default :
sess = sess . And ( "is_closed = ?" , false )
}
if listOptions . Page != 0 {
sess = listOptions . setSessionPagination ( sess )
}
miles := make ( [ ] * Milestone , 0 , listOptions . PageSize )
return miles , sess . Asc ( "deadline_unix" ) . Asc ( "id" ) . Find ( & miles )
}
// GetMilestones returns a list of milestones of given repository and status.
func GetMilestones ( repoID int64 , page int , isClosed bool , sortType string ) ( MilestoneList , error ) {
miles := make ( [ ] * Milestone , 0 , setting . UI . IssuePagingNum )
sess := x . Where ( "repo_id = ? AND is_closed = ?" , repoID , isClosed )
if page > 0 {
sess = sess . Limit ( setting . UI . IssuePagingNum , ( page - 1 ) * setting . UI . IssuePagingNum )
}
switch sortType {
case "furthestduedate" :
sess . Desc ( "deadline_unix" )
case "leastcomplete" :
sess . Asc ( "completeness" )
case "mostcomplete" :
sess . Desc ( "completeness" )
case "leastissues" :
sess . Asc ( "num_issues" )
case "mostissues" :
sess . Desc ( "num_issues" )
default :
sess . Asc ( "deadline_unix" )
}
return miles , sess . Find ( & miles )
}
func updateMilestone ( e Engine , m * Milestone ) error {
m . Name = strings . TrimSpace ( m . Name )
_ , err := e . ID ( m . ID ) . AllCols ( ) .
SetExpr ( "num_issues" , builder . Select ( "count(*)" ) . From ( "issue" ) . Where (
builder . Eq { "milestone_id" : m . ID } ,
) ) .
SetExpr ( "num_closed_issues" , builder . Select ( "count(*)" ) . From ( "issue" ) . Where (
builder . Eq {
"milestone_id" : m . ID ,
"is_closed" : true ,
} ,
) ) .
Update ( m )
return err
}
// UpdateMilestone updates information of given milestone.
// UpdateMilestone updates information of given milestone.
func UpdateMilestone ( m * Milestone , oldIsClosed bool ) error {
func UpdateMilestone ( m * Milestone , oldIsClosed bool ) error {
sess := x . NewSession ( )
sess := x . NewSession ( )
@ -330,6 +160,22 @@ func UpdateMilestone(m *Milestone, oldIsClosed bool) error {
return sess . Commit ( )
return sess . Commit ( )
}
}
func updateMilestone ( e Engine , m * Milestone ) error {
m . Name = strings . TrimSpace ( m . Name )
_ , err := e . ID ( m . ID ) . AllCols ( ) .
SetExpr ( "num_issues" , builder . Select ( "count(*)" ) . From ( "issue" ) . Where (
builder . Eq { "milestone_id" : m . ID } ,
) ) .
SetExpr ( "num_closed_issues" , builder . Select ( "count(*)" ) . From ( "issue" ) . Where (
builder . Eq {
"milestone_id" : m . ID ,
"is_closed" : true ,
} ,
) ) .
Update ( m )
return err
}
func updateMilestoneCompleteness ( e Engine , milestoneID int64 ) error {
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=?" ,
milestoneID ,
milestoneID ,
@ -337,35 +183,6 @@ func updateMilestoneCompleteness(e Engine, milestoneID int64) error {
return err
return err
}
}
func countRepoMilestones ( e Engine , repoID int64 ) ( int64 , error ) {
return e .
Where ( "repo_id=?" , repoID ) .
Count ( new ( Milestone ) )
}
func countRepoClosedMilestones ( e Engine , repoID int64 ) ( int64 , error ) {
return e .
Where ( "repo_id=? AND is_closed=?" , repoID , true ) .
Count ( new ( Milestone ) )
}
// CountRepoClosedMilestones returns number of closed milestones in given repository.
func CountRepoClosedMilestones ( repoID int64 ) ( int64 , error ) {
return countRepoClosedMilestones ( x , repoID )
}
// MilestoneStats returns number of open and closed milestones of given repository.
func MilestoneStats ( repoID int64 ) ( open int64 , closed int64 , err error ) {
open , err = x .
Where ( "repo_id=? AND is_closed=?" , repoID , false ) .
Count ( new ( Milestone ) )
if err != nil {
return 0 , 0 , nil
}
closed , err = CountRepoClosedMilestones ( repoID )
return open , closed , err
}
// ChangeMilestoneStatus changes the milestone open/closed status.
// ChangeMilestoneStatus changes the milestone open/closed status.
func ChangeMilestoneStatus ( m * Milestone , isClosed bool ) ( err error ) {
func ChangeMilestoneStatus ( m * Milestone , isClosed bool ) ( err error ) {
sess := x . NewSession ( )
sess := x . NewSession ( )
@ -390,39 +207,6 @@ func ChangeMilestoneStatus(m *Milestone, isClosed bool) (err error) {
return sess . Commit ( )
return sess . Commit ( )
}
}
func updateRepoMilestoneNum ( e Engine , repoID int64 ) error {
_ , err := e . Exec ( "UPDATE `repository` SET num_milestones=(SELECT count(*) FROM milestone WHERE repo_id=?),num_closed_milestones=(SELECT count(*) FROM milestone WHERE repo_id=? AND is_closed=?) WHERE id=?" ,
repoID ,
repoID ,
true ,
repoID ,
)
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 )
}
func changeMilestoneAssign ( e * xorm . Session , doer * User , issue * Issue , oldMilestoneID int64 ) error {
func changeMilestoneAssign ( e * xorm . Session , doer * User , issue * Issue , oldMilestoneID int64 ) error {
if err := updateIssueCols ( e , issue , "milestone_id" ) ; err != nil {
if err := updateIssueCols ( e , issue , "milestone_id" ) ; err != nil {
return err
return err
@ -535,37 +319,66 @@ func DeleteMilestoneByRepoID(repoID, id int64) error {
return sess . Commit ( )
return sess . Commit ( )
}
}
// CountMilestones map from repo conditions to number of milestones matching the options`
// MilestoneList is a list of milestones offering additional functionality
func CountMilestones ( repoCond builder . Cond , isClosed bool ) ( map [ int64 ] int64 , error ) {
type MilestoneList [ ] * Milestone
sess := x . Where ( "is_closed = ?" , isClosed )
if repoCond . IsValid ( ) {
func ( milestones MilestoneList ) getMilestoneIDs ( ) [ ] int64 {
sess . In ( "repo_id" , builder . Select ( "id" ) . From ( "repository" ) . Where ( repoCond ) )
var ids = make ( [ ] int64 , 0 , len ( milestones ) )
for _ , ms := range milestones {
ids = append ( ids , ms . ID )
}
}
return ids
}
countsSlice := make ( [ ] * struct {
// GetMilestonesByRepoID returns all opened milestones of a repository.
RepoID int64
func GetMilestonesByRepoID ( repoID int64 , state api . StateType , listOptions ListOptions ) ( MilestoneList , error ) {
Count int64
sess := x . Where ( "repo_id = ?" , repoID )
} , 0 , 10 )
if err := sess . GroupBy ( "repo_id" ) .
switch state {
Select ( "repo_id AS repo_id, COUNT(*) AS count" ) .
case api . StateClosed :
Table ( "milestone" ) .
sess = sess . And ( "is_closed = ?" , true )
Find ( & countsSlice ) ; err != nil {
return nil , err
case api . StateAll :
break
case api . StateOpen :
fallthrough
default :
sess = sess . And ( "is_closed = ?" , false )
}
}
countMap := make ( map [ int64 ] int64 , len ( countsSlice ) )
if listOptions . Page != 0 {
for _ , c := range countsSlice {
sess = listOptions . setSessionPagination ( sess )
countMap [ c . RepoID ] = c . Count
}
}
return countMap , nil
miles := make ( [ ] * Milestone , 0 , listOptions . PageSize )
return miles , sess . Asc ( "deadline_unix" ) . Asc ( "id" ) . Find ( & miles )
}
}
// CountMilestonesByRepoIDs map from repoIDs to number of milestones matching the options`
// GetMilestones returns a list of milestones of given repository and status.
func CountMilestonesByRepoIDs ( repoIDs [ ] int64 , isClosed bool ) ( map [ int64 ] int64 , error ) {
func GetMilestones ( repoID int64 , page int , isClosed bool , sortType string ) ( MilestoneList , error ) {
return CountMilestones (
miles := make ( [ ] * Milestone , 0 , setting . UI . IssuePagingNum )
builder . In ( "repo_id" , repoIDs ) ,
sess := x . Where ( "repo_id = ? AND is_closed = ?" , repoID , isClosed )
isClosed ,
if page > 0 {
)
sess = sess . Limit ( setting . UI . IssuePagingNum , ( page - 1 ) * setting . UI . IssuePagingNum )
}
switch sortType {
case "furthestduedate" :
sess . Desc ( "deadline_unix" )
case "leastcomplete" :
sess . Asc ( "completeness" )
case "mostcomplete" :
sess . Desc ( "completeness" )
case "leastissues" :
sess . Asc ( "num_issues" )
case "mostissues" :
sess . Desc ( "num_issues" )
default :
sess . Asc ( "deadline_unix" )
}
return miles , sess . Find ( & miles )
}
}
// SearchMilestones search milestones
// SearchMilestones search milestones
@ -606,6 +419,13 @@ func GetMilestonesByRepoIDs(repoIDs []int64, page int, isClosed bool, sortType s
)
)
}
}
// ____ _ _
// / ___|| |_ __ _| |_ ___
// \___ \| __/ _` | __/ __|
// ___) | || (_| | |_\__ \
// |____/ \__\__,_|\__|___/
//
// MilestonesStats represents milestone statistic information.
// MilestonesStats represents milestone statistic information.
type MilestonesStats struct {
type MilestonesStats struct {
OpenCount , ClosedCount int64
OpenCount , ClosedCount int64
@ -616,8 +436,8 @@ func (m MilestonesStats) Total() int64 {
return m . OpenCount + m . ClosedCount
return m . OpenCount + m . ClosedCount
}
}
// GetMilestonesStats returns milestone statistic information for dashboard by given conditions.
// GetMilestonesStatsByRepoCond returns milestone statistic information for dashboard by given conditions.
func GetMilestonesStats ( repoCond builder . Cond ) ( * MilestonesStats , error ) {
func GetMilestonesStatsByRepoCond ( repoCond builder . Cond ) ( * MilestonesStats , error ) {
var err error
var err error
stats := & MilestonesStats { }
stats := & MilestonesStats { }
@ -641,3 +461,158 @@ func GetMilestonesStats(repoCond builder.Cond) (*MilestonesStats, error) {
return stats , nil
return stats , nil
}
}
func countRepoMilestones ( e Engine , repoID int64 ) ( int64 , error ) {
return e .
Where ( "repo_id=?" , repoID ) .
Count ( new ( Milestone ) )
}
func countRepoClosedMilestones ( e Engine , repoID int64 ) ( int64 , error ) {
return e .
Where ( "repo_id=? AND is_closed=?" , repoID , true ) .
Count ( new ( Milestone ) )
}
// CountRepoClosedMilestones returns number of closed milestones in given repository.
func CountRepoClosedMilestones ( repoID int64 ) ( int64 , error ) {
return countRepoClosedMilestones ( x , repoID )
}
// CountMilestonesByRepoCond map from repo conditions to number of milestones matching the options`
func CountMilestonesByRepoCond ( repoCond builder . Cond , isClosed bool ) ( map [ int64 ] int64 , error ) {
sess := x . Where ( "is_closed = ?" , isClosed )
if repoCond . IsValid ( ) {
sess . In ( "repo_id" , builder . Select ( "id" ) . From ( "repository" ) . Where ( repoCond ) )
}
countsSlice := make ( [ ] * struct {
RepoID int64
Count int64
} , 0 , 10 )
if err := sess . GroupBy ( "repo_id" ) .
Select ( "repo_id AS repo_id, COUNT(*) AS count" ) .
Table ( "milestone" ) .
Find ( & countsSlice ) ; err != nil {
return nil , err
}
countMap := make ( map [ int64 ] int64 , len ( countsSlice ) )
for _ , c := range countsSlice {
countMap [ c . RepoID ] = c . Count
}
return countMap , nil
}
func updateRepoMilestoneNum ( e Engine , repoID int64 ) error {
_ , err := e . Exec ( "UPDATE `repository` SET num_milestones=(SELECT count(*) FROM milestone WHERE repo_id=?),num_closed_milestones=(SELECT count(*) FROM milestone WHERE repo_id=? AND is_closed=?) WHERE id=?" ,
repoID ,
repoID ,
true ,
repoID ,
)
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 )
}
// _____ _ _ _____ _
// |_ _| __ __ _ ___| | _____ __| |_ _(_)_ __ ___ ___ ___
// | || '__/ _` |/ __| |/ / _ \/ _` | | | | | '_ ` _ \ / _ \/ __|
// | || | | (_| | (__| < __/ (_| | | | | | | | | | | __/\__ \
// |_||_| \__,_|\___|_|\_\___|\__,_| |_| |_|_| |_| |_|\___||___/
//
func ( milestones MilestoneList ) loadTotalTrackedTimes ( e Engine ) error {
type totalTimesByMilestone struct {
MilestoneID int64
Time int64
}
if len ( milestones ) == 0 {
return nil
}
var trackedTimes = make ( map [ int64 ] int64 , len ( milestones ) )
// Get total tracked time by milestone_id
rows , err := e . Table ( "issue" ) .
Join ( "INNER" , "milestone" , "issue.milestone_id = milestone.id" ) .
Join ( "LEFT" , "tracked_time" , "tracked_time.issue_id = issue.id" ) .
Where ( "tracked_time.deleted = ?" , false ) .
Select ( "milestone_id, sum(time) as time" ) .
In ( "milestone_id" , milestones . getMilestoneIDs ( ) ) .
GroupBy ( "milestone_id" ) .
Rows ( new ( totalTimesByMilestone ) )
if err != nil {
return err
}
defer rows . Close ( )
for rows . Next ( ) {
var totalTime totalTimesByMilestone
err = rows . Scan ( & totalTime )
if err != nil {
return err
}
trackedTimes [ totalTime . MilestoneID ] = totalTime . Time
}
for _ , milestone := range milestones {
milestone . TotalTrackedTime = trackedTimes [ milestone . ID ]
}
return nil
}
func ( m * Milestone ) loadTotalTrackedTime ( e Engine ) error {
type totalTimesByMilestone struct {
MilestoneID int64
Time int64
}
totalTime := & totalTimesByMilestone { MilestoneID : m . ID }
has , err := e . Table ( "issue" ) .
Join ( "INNER" , "milestone" , "issue.milestone_id = milestone.id" ) .
Join ( "LEFT" , "tracked_time" , "tracked_time.issue_id = issue.id" ) .
Where ( "tracked_time.deleted = ?" , false ) .
Select ( "milestone_id, sum(time) as time" ) .
Where ( "milestone_id = ?" , m . ID ) .
GroupBy ( "milestone_id" ) .
Get ( totalTime )
if err != nil {
return err
} else if ! has {
return nil
}
m . TotalTrackedTime = totalTime . Time
return nil
}
// LoadTotalTrackedTimes loads for every milestone in the list the TotalTrackedTime by a batch request
func ( milestones MilestoneList ) LoadTotalTrackedTimes ( ) error {
return milestones . loadTotalTrackedTimes ( x )
}
// LoadTotalTrackedTime loads the tracked time for the milestone
func ( m * Milestone ) LoadTotalTrackedTime ( ) error {
return m . loadTotalTrackedTime ( x )
}