From 72c42968fa87e37750ed041ae4f6a97b2757e079 Mon Sep 17 00:00:00 2001 From: Jonas Franz Date: Tue, 9 Jan 2018 23:43:25 +0100 Subject: [PATCH 01/18] Show total tracked time in issue and milestone list Show total tracked time at issue page Signed-off-by: Jonas Franz --- models/issue.go | 26 +++++++++++++++++++ models/issue_milestone.go | 24 +++++++++++++++++ models/issue_tracked_time.go | 6 ++++- options/locale/locale_en-US.ini | 2 +- templates/repo/issue/list.tmpl | 4 +++ templates/repo/issue/milestones.tmpl | 1 + .../repo/issue/view_content/sidebar.tmpl | 2 +- templates/user/dashboard/issues.tmpl | 4 +++ 8 files changed, 66 insertions(+), 3 deletions(-) diff --git a/models/issue.go b/models/issue.go index d10c521db6d8f..66371e0b8e8d8 100644 --- a/models/issue.go +++ b/models/issue.go @@ -68,6 +68,24 @@ func init() { issueTasksDonePat = regexp.MustCompile(issueTasksDoneRegexpStr) } +func (issue *Issue) totalTimes(e Engine) (string, error) { + times, err := GetTrackedTimes(FindTrackedTimesOptions{IssueID: issue.ID}) + if err != nil { + return "", err + } + var totalTime int64 + for _, trackedTime := range times { + totalTime += trackedTime.Time + } + return secToTime(totalTime), nil +} + +// TotalTimes returns the total amount of tracked time for the issue +func (issue *Issue) TotalTimes() string { + times, _ := issue.totalTimes(x) + return times +} + func (issue *Issue) loadRepo(e Engine) (err error) { if issue.Repo == nil { issue.Repo, err = getRepositoryByID(e, issue.RepoID) @@ -78,6 +96,14 @@ func (issue *Issue) loadRepo(e Engine) (err error) { return nil } +// IsTimetrackerEnabled returns true if the repo enables timetracking +func (issue *Issue) IsTimetrackerEnabled() bool { + if issue.loadRepo(x) != nil { + return false + } + return issue.Repo.IsTimetrackerEnabled() +} + // GetPullRequest returns the issue pull request func (issue *Issue) GetPullRequest() (pr *PullRequest, err error) { if !issue.IsPull { diff --git a/models/issue_milestone.go b/models/issue_milestone.go index 761b598e9218a..1b0e71fe50c9a 100644 --- a/models/issue_milestone.go +++ b/models/issue_milestone.go @@ -86,6 +86,30 @@ func (m *Milestone) APIFormat() *api.Milestone { return apiMilestone } +func (m *Milestone) totalTimes(e Engine) (string, error) { + times, err := GetTrackedTimes(FindTrackedTimesOptions{MilestoneID: m.ID}) + if err != nil { + return "", err + } + var totalTime int64 + for _, trackedTime := range times { + totalTime += trackedTime.Time + } + return secToTime(totalTime), nil +} + +// TotalTimes returns the amount of tracked time of the issues inside the milestone +func (m *Milestone) TotalTimes() string { + times, _ := m.totalTimes(x) + return times +} + +// MustGetRepo returns milestone's repository by ignoring errors +func (m *Milestone) MustGetRepo() *Repository { + repo, _ := GetRepositoryByID(m.RepoID) + return repo +} + // NewMilestone creates new milestone of repository. func NewMilestone(m *Milestone) (err error) { sess := x.NewSession() diff --git a/models/issue_tracked_time.go b/models/issue_tracked_time.go index c314f8f44f726..fd3c212c2858e 100644 --- a/models/issue_tracked_time.go +++ b/models/issue_tracked_time.go @@ -44,6 +44,7 @@ type FindTrackedTimesOptions struct { IssueID int64 UserID int64 RepositoryID int64 + MilestoneID int64 } // ToCond will convert each condition into a xorm-Cond @@ -58,12 +59,15 @@ func (opts *FindTrackedTimesOptions) ToCond() builder.Cond { if opts.RepositoryID != 0 { cond = cond.And(builder.Eq{"issue.repo_id": opts.RepositoryID}) } + if opts.MilestoneID != 0 { + cond = cond.And(builder.Eq{"issue.milestone_id": opts.MilestoneID}) + } return cond } // GetTrackedTimes returns all tracked times that fit to the given options. func GetTrackedTimes(options FindTrackedTimesOptions) (trackedTimes []*TrackedTime, err error) { - if options.RepositoryID > 0 { + if options.RepositoryID > 0 || options.MilestoneID > 0 { err = x.Join("INNER", "issue", "issue.id = tracked_time.issue_id").Where(options.ToCond()).Find(&trackedTimes) return } diff --git a/options/locale/locale_en-US.ini b/options/locale/locale_en-US.ini index a1cb077599e76..90f3b3f265682 100644 --- a/options/locale/locale_en-US.ini +++ b/options/locale/locale_en-US.ini @@ -731,7 +731,7 @@ issues.add_time_minutes = Minutes issues.add_time_sum_to_small = No time was entered issues.cancel_tracking = Cancel issues.cancel_tracking_history = `cancelled time tracking %s` -issues.time_spent_total = Total time spent +issues.time_spent_from_all_authors = `Total time spent: %s` pulls.desc = Pulls management your code review and merge requests pulls.new = New Pull Request diff --git a/templates/repo/issue/list.tmpl b/templates/repo/issue/list.tmpl index 642458ff1e43c..eb72ae35cfe28 100644 --- a/templates/repo/issue/list.tmpl +++ b/templates/repo/issue/list.tmpl @@ -198,6 +198,10 @@ {{.NumComments}} {{end}} + {{if and .IsTimetrackerEnabled .TotalTimes}} + {{.TotalTimes}} + {{end}} +

{{$.i18n.Tr "repo.issues.opened_by" $timeStr .Poster.HomeLink .Poster.Name | Safe}} {{$tasks := .GetTasks}} diff --git a/templates/repo/issue/milestones.tmpl b/templates/repo/issue/milestones.tmpl index de52bd42f0429..284afd5894fb1 100644 --- a/templates/repo/issue/milestones.tmpl +++ b/templates/repo/issue/milestones.tmpl @@ -64,6 +64,7 @@ {{$.i18n.Tr "repo.issues.open_tab" .NumOpenIssues}} {{$.i18n.Tr "repo.issues.close_tab" .NumClosedIssues}} + {{if and .TotalTimes .MustGetRepo.IsTimetrackerEnabled}} {{.TotalTimes}}{{end}} {{if $.IsRepositoryWriter}} diff --git a/templates/repo/issue/view_content/sidebar.tmpl b/templates/repo/issue/view_content/sidebar.tmpl index 23fd91b0bd833..1e2ae3a82122e 100644 --- a/templates/repo/issue/view_content/sidebar.tmpl +++ b/templates/repo/issue/view_content/sidebar.tmpl @@ -171,7 +171,7 @@ {{if gt (len .WorkingUsers) 0}}

- {{.i18n.Tr "repo.issues.time_spent_total"}} + {{.i18n.Tr "repo.issues.time_spent_from_all_authors" $.Issue.TotalTimes | Safe}}
{{range $user, $trackedtime := .WorkingUsers}}
diff --git a/templates/user/dashboard/issues.tmpl b/templates/user/dashboard/issues.tmpl index 534df1177a807..1cb50f3ab28f7 100644 --- a/templates/user/dashboard/issues.tmpl +++ b/templates/user/dashboard/issues.tmpl @@ -80,6 +80,10 @@ {{.NumComments}} {{end}} + {{if and .IsTimetrackerEnabled .TotalTimes}} + {{.TotalTimes}} + {{end}} +

{{$.i18n.Tr "repo.issues.opened_by" $timeStr .Poster.HomeLink .Poster.Name | Safe}} {{if .Assignee}} From 187503832073280c4c994753778e1d69c307a000 Mon Sep 17 00:00:00 2001 From: Jonas Franz Date: Wed, 10 Jan 2018 01:42:15 +0100 Subject: [PATCH 02/18] Optimizing TotalTimes by using SumInt Signed-off-by: Jonas Franz --- models/issue.go | 7 ++----- models/issue_milestone.go | 7 ++----- 2 files changed, 4 insertions(+), 10 deletions(-) diff --git a/models/issue.go b/models/issue.go index 66371e0b8e8d8..db467fd8f74e6 100644 --- a/models/issue.go +++ b/models/issue.go @@ -69,14 +69,11 @@ func init() { } func (issue *Issue) totalTimes(e Engine) (string, error) { - times, err := GetTrackedTimes(FindTrackedTimesOptions{IssueID: issue.ID}) + opts := FindTrackedTimesOptions{IssueID: issue.ID} + totalTime, err := e.Where(opts.ToCond()).SumInt(&TrackedTime{}, "time") if err != nil { return "", err } - var totalTime int64 - for _, trackedTime := range times { - totalTime += trackedTime.Time - } return secToTime(totalTime), nil } diff --git a/models/issue_milestone.go b/models/issue_milestone.go index 1b0e71fe50c9a..273163b8e8a82 100644 --- a/models/issue_milestone.go +++ b/models/issue_milestone.go @@ -87,14 +87,11 @@ func (m *Milestone) APIFormat() *api.Milestone { } func (m *Milestone) totalTimes(e Engine) (string, error) { - times, err := GetTrackedTimes(FindTrackedTimesOptions{MilestoneID: m.ID}) + opts := FindTrackedTimesOptions{MilestoneID: m.ID} + totalTime, err := e.Where(opts.ToCond()).SumInt(&TrackedTime{}, "time") if err != nil { return "", err } - var totalTime int64 - for _, trackedTime := range times { - totalTime += trackedTime.Time - } return secToTime(totalTime), nil } From fda4dd41a51c469f99a2ded70445f9f5421be514 Mon Sep 17 00:00:00 2001 From: Jonas Franz Date: Sat, 3 Feb 2018 13:04:22 +0100 Subject: [PATCH 03/18] Fixing wrong total times for milestones caused by a missing JOIN Adding unit tests for total times Signed-off-by: Jonas Franz --- models/issue.go | 2 +- models/issue_milestone.go | 2 +- models/issue_milestone_test.go | 8 ++++++++ models/issue_test.go | 8 ++++++++ models/issue_tracked_time.go | 15 ++++++++++----- 5 files changed, 28 insertions(+), 7 deletions(-) diff --git a/models/issue.go b/models/issue.go index db467fd8f74e6..70d8f5676aa29 100644 --- a/models/issue.go +++ b/models/issue.go @@ -70,7 +70,7 @@ func init() { func (issue *Issue) totalTimes(e Engine) (string, error) { opts := FindTrackedTimesOptions{IssueID: issue.ID} - totalTime, err := e.Where(opts.ToCond()).SumInt(&TrackedTime{}, "time") + totalTime, err := opts.ToSession(e).SumInt(&TrackedTime{}, "time") if err != nil { return "", err } diff --git a/models/issue_milestone.go b/models/issue_milestone.go index 273163b8e8a82..1a36d6aa9f94c 100644 --- a/models/issue_milestone.go +++ b/models/issue_milestone.go @@ -88,7 +88,7 @@ func (m *Milestone) APIFormat() *api.Milestone { func (m *Milestone) totalTimes(e Engine) (string, error) { opts := FindTrackedTimesOptions{MilestoneID: m.ID} - totalTime, err := e.Where(opts.ToCond()).SumInt(&TrackedTime{}, "time") + totalTime, err := opts.ToSession(e).SumInt(&TrackedTime{}, "time") if err != nil { return "", err } diff --git a/models/issue_milestone_test.go b/models/issue_milestone_test.go index f7987d45adee7..a81a22fd1e7d8 100644 --- a/models/issue_milestone_test.go +++ b/models/issue_milestone_test.go @@ -251,3 +251,11 @@ func TestDeleteMilestoneByRepoID(t *testing.T) { assert.NoError(t, DeleteMilestoneByRepoID(NonexistentID, NonexistentID)) } + +func TestMilestone_TotalTimes(t *testing.T) { + assert.NoError(t, PrepareTestDatabase()) + ms, err := GetMilestoneByRepoID(1, 1) + assert.NoError(t, err) + times := ms.TotalTimes() + assert.Equal(t, "1h 1min 2s", times) +} diff --git a/models/issue_test.go b/models/issue_test.go index 851fe684fbc79..71da444429643 100644 --- a/models/issue_test.go +++ b/models/issue_test.go @@ -279,3 +279,11 @@ func TestGetUserIssueStats(t *testing.T) { assert.Equal(t, test.ExpectedIssueStats, *stats) } } + +func TestIssue_TotalTimes(t *testing.T) { + assert.NoError(t, PrepareTestDatabase()) + ms, err := GetIssueByID(2) + assert.NoError(t, err) + times := ms.TotalTimes() + assert.Equal(t, "1h 1min 2s", times) +} diff --git a/models/issue_tracked_time.go b/models/issue_tracked_time.go index fd3c212c2858e..4280b083efced 100644 --- a/models/issue_tracked_time.go +++ b/models/issue_tracked_time.go @@ -11,6 +11,7 @@ import ( api "code.gitea.io/sdk/gitea" "github.com/go-xorm/builder" + "github.com/go-xorm/xorm" ) // TrackedTime represents a time that was spent for a specific issue. @@ -65,13 +66,17 @@ func (opts *FindTrackedTimesOptions) ToCond() builder.Cond { return cond } +// ToSession will convert the given options to a xorm Session by using the conditions from ToCond and joining with issue table if required +func (opts *FindTrackedTimesOptions) ToSession(e Engine) *xorm.Session { + if opts.RepositoryID > 0 || opts.MilestoneID > 0 { + return e.Join("INNER", "issue", "issue.id = tracked_time.issue_id").Where(opts.ToCond()) + } + return x.Where(opts.ToCond()) +} + // GetTrackedTimes returns all tracked times that fit to the given options. func GetTrackedTimes(options FindTrackedTimesOptions) (trackedTimes []*TrackedTime, err error) { - if options.RepositoryID > 0 || options.MilestoneID > 0 { - err = x.Join("INNER", "issue", "issue.id = tracked_time.issue_id").Where(options.ToCond()).Find(&trackedTimes) - return - } - err = x.Where(options.ToCond()).Find(&trackedTimes) + err = options.ToSession(x).Find(&trackedTimes) return } From 983c1e1205d4409a2c76dc6d6c03479d68a0a14e Mon Sep 17 00:00:00 2001 From: Jonas Franz Date: Sat, 3 Feb 2018 15:38:31 +0100 Subject: [PATCH 04/18] Logging error instead of ignoring it Signed-off-by: Jonas Franz --- models/issue.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/models/issue.go b/models/issue.go index 70d8f5676aa29..cf627fc2e5b24 100644 --- a/models/issue.go +++ b/models/issue.go @@ -95,7 +95,8 @@ func (issue *Issue) loadRepo(e Engine) (err error) { // IsTimetrackerEnabled returns true if the repo enables timetracking func (issue *Issue) IsTimetrackerEnabled() bool { - if issue.loadRepo(x) != nil { + if err := issue.loadRepo(x); err != nil { + log.Error(4, fmt.Sprintf("An error occured while checking if timetracker is enabled: %v", err)) return false } return issue.Repo.IsTimetrackerEnabled() From 26a1e021fb8ad8b5a2fb25de1082a6716f5bcafb Mon Sep 17 00:00:00 2001 From: Jonas Franz Date: Sat, 3 Feb 2018 16:20:58 +0100 Subject: [PATCH 05/18] Correcting spelling mistakes Signed-off-by: Jonas Franz --- models/issue.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/models/issue.go b/models/issue.go index cf627fc2e5b24..54f31b7aa9219 100644 --- a/models/issue.go +++ b/models/issue.go @@ -96,7 +96,7 @@ func (issue *Issue) loadRepo(e Engine) (err error) { // IsTimetrackerEnabled returns true if the repo enables timetracking func (issue *Issue) IsTimetrackerEnabled() bool { if err := issue.loadRepo(x); err != nil { - log.Error(4, fmt.Sprintf("An error occured while checking if timetracker is enabled: %v", err)) + log.Error(4, fmt.Sprintf("An error occurred while checking if timetracker is enabled: %v", err)) return false } return issue.Repo.IsTimetrackerEnabled() From f1cfe62fff582b3723eded052e186377f6a9fa90 Mon Sep 17 00:00:00 2001 From: Jonas Franz Date: Sat, 3 Feb 2018 18:58:09 +0100 Subject: [PATCH 06/18] Change error message to a short version Signed-off-by: Jonas Franz --- models/issue.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/models/issue.go b/models/issue.go index 54f31b7aa9219..eb6027b14f5b8 100644 --- a/models/issue.go +++ b/models/issue.go @@ -96,7 +96,7 @@ func (issue *Issue) loadRepo(e Engine) (err error) { // IsTimetrackerEnabled returns true if the repo enables timetracking func (issue *Issue) IsTimetrackerEnabled() bool { if err := issue.loadRepo(x); err != nil { - log.Error(4, fmt.Sprintf("An error occurred while checking if timetracker is enabled: %v", err)) + log.Error(4, fmt.Sprintf("loadRepo: %v", err)) return false } return issue.Repo.IsTimetrackerEnabled() From c5c812c5d0b7e5382bff884950a6034a8d1fd331 Mon Sep 17 00:00:00 2001 From: Jonas Franz Date: Mon, 26 Feb 2018 23:02:47 +0100 Subject: [PATCH 07/18] Add error handling to TotalTimes Add variable for totalTimes Signed-off-by: Jonas Franz --- models/issue.go | 3 ++- templates/user/dashboard/issues.tmpl | 6 +++--- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/models/issue.go b/models/issue.go index 00f4e3ba29649..d6fd3c15143f6 100644 --- a/models/issue.go +++ b/models/issue.go @@ -80,7 +80,8 @@ func (issue *Issue) totalTimes(e Engine) (string, error) { // TotalTimes returns the total amount of tracked time for the issue func (issue *Issue) TotalTimes() string { - times, _ := issue.totalTimes(x) + times, err := issue.totalTimes(x) + log.Error(4,"TotalTimes: %v", err) return times } diff --git a/templates/user/dashboard/issues.tmpl b/templates/user/dashboard/issues.tmpl index 8481d671977f6..520dec9a4f1a2 100644 --- a/templates/user/dashboard/issues.tmpl +++ b/templates/user/dashboard/issues.tmpl @@ -79,9 +79,9 @@ {{if .NumComments}} {{.NumComments}} {{end}} - - {{if and .IsTimetrackerEnabled .TotalTimes}} - {{.TotalTimes}} + {{$totalTimes := .TotalTimes}} + {{if and .IsTimetrackerEnabled $totalTimes}} + {{$totalTimes}} {{end}}

From 8481f2dacc4d0c3a346c15709361f461b5ddd666 Mon Sep 17 00:00:00 2001 From: Jonas Franz Date: Sun, 11 Mar 2018 11:42:35 +0100 Subject: [PATCH 08/18] Introduce TotalTrackedTimes as variable of issue Load TotalTrackedTimes by loading attributes of IssueList Load TotalTrackedTimes by loading attributes of single issue Add Sec2Time as helper to use it in templates Signed-off-by: Jonas Franz --- models/issue.go | 20 ++++----- models/issue_list.go | 41 +++++++++++++++++++ models/issue_list_test.go | 5 +++ models/issue_milestone.go | 2 +- models/issue_stopwatch.go | 5 ++- models/issue_tracked_time.go | 4 +- modules/templates/helper.go | 1 + templates/repo/issue/list.tmpl | 4 +- .../repo/issue/view_content/sidebar.tmpl | 2 +- templates/user/dashboard/issues.tmpl | 5 +-- 10 files changed, 67 insertions(+), 22 deletions(-) diff --git a/models/issue.go b/models/issue.go index d6fd3c15143f6..06d209f9a035c 100644 --- a/models/issue.go +++ b/models/issue.go @@ -54,6 +54,7 @@ type Issue struct { Attachments []*Attachment `xorm:"-"` Comments []*Comment `xorm:"-"` Reactions ReactionList `xorm:"-"` + TotalTrackedTime int64 `xorm:"-"` } var ( @@ -69,20 +70,13 @@ func init() { issueTasksDonePat = regexp.MustCompile(issueTasksDoneRegexpStr) } -func (issue *Issue) totalTimes(e Engine) (string, error) { +func (issue *Issue) loadTotalTimes(e Engine) (err error) { opts := FindTrackedTimesOptions{IssueID: issue.ID} - totalTime, err := opts.ToSession(e).SumInt(&TrackedTime{}, "time") + issue.TotalTrackedTime, err = opts.ToSession(e).SumInt(&TrackedTime{}, "time") if err != nil { - return "", err + return err } - return secToTime(totalTime), nil -} - -// TotalTimes returns the total amount of tracked time for the issue -func (issue *Issue) TotalTimes() string { - times, err := issue.totalTimes(x) - log.Error(4,"TotalTimes: %v", err) - return times + return nil } func (issue *Issue) loadRepo(e Engine) (err error) { @@ -251,6 +245,10 @@ func (issue *Issue) loadAttributes(e Engine) (err error) { return err } + if err = issue.loadTotalTimes(e); err != nil { + return err + } + return issue.loadReactions(e) } diff --git a/models/issue_list.go b/models/issue_list.go index 4910915cd09a9..f984b8d4788e9 100644 --- a/models/issue_list.go +++ b/models/issue_list.go @@ -290,6 +290,43 @@ func (issues IssueList) loadComments(e Engine) (err error) { return nil } +func (issues IssueList) loadTotalTrackedTimes(e Engine) (err error) { + type totalTimesByIssue struct { + IssueID int64 + Time int64 + } + if len(issues) == 0 { + return nil + } + var trackedTimes = make(map[int64]int64, len(issues)) + + // select issue_id, sum(time) from tracked_time where issue_id in () group by issue_id + rows, err := e.Table("tracked_time"). + Select("issue_id, sum(time) as time"). + In("issue_id", issues.getIssueIDs()). + GroupBy("issue_id"). + Rows(new(totalTimesByIssue)) + if err != nil { + return err + } + + defer rows.Close() + + for rows.Next() { + var totalTime totalTimesByIssue + err = rows.Scan(&totalTime) + if err != nil { + return err + } + trackedTimes[totalTime.IssueID] = totalTime.Time + } + + for _, issue := range issues { + issue.TotalTrackedTime = trackedTimes[issue.ID] + } + return nil +} + // loadAttributes loads all attributes, expect for attachments and comments func (issues IssueList) loadAttributes(e Engine) (err error) { if _, err = issues.loadRepositories(e); err != nil { @@ -316,6 +353,10 @@ func (issues IssueList) loadAttributes(e Engine) (err error) { return } + if err = issues.loadTotalTrackedTimes(e); err != nil { + return + } + return nil } diff --git a/models/issue_list_test.go b/models/issue_list_test.go index 958e074662a86..f27440ec2e9be 100644 --- a/models/issue_list_test.go +++ b/models/issue_list_test.go @@ -61,5 +61,10 @@ func TestIssueList_LoadAttributes(t *testing.T) { for _, comment := range issue.Comments { assert.EqualValues(t, issue.ID, comment.IssueID) } + if issue.ID == int64(1) { + assert.Equal(t, int64(400), issue.TotalTrackedTime) + } else if issue.ID == int64(2) { + assert.Equal(t, int64(3662), issue.TotalTrackedTime) + } } } diff --git a/models/issue_milestone.go b/models/issue_milestone.go index 1a36d6aa9f94c..506daa4453d57 100644 --- a/models/issue_milestone.go +++ b/models/issue_milestone.go @@ -92,7 +92,7 @@ func (m *Milestone) totalTimes(e Engine) (string, error) { if err != nil { return "", err } - return secToTime(totalTime), nil + return SecToTime(totalTime), nil } // TotalTimes returns the amount of tracked time of the issues inside the milestone diff --git a/models/issue_stopwatch.go b/models/issue_stopwatch.go index 92b1bb9a5fce8..178b76c5dd4b9 100644 --- a/models/issue_stopwatch.go +++ b/models/issue_stopwatch.go @@ -69,7 +69,7 @@ func CreateOrStopIssueStopwatch(user *User, issue *Issue) error { Doer: user, Issue: issue, Repo: issue.Repo, - Content: secToTime(timediff), + Content: SecToTime(timediff), Type: CommentTypeStopTracking, }); err != nil { return err @@ -124,7 +124,8 @@ func CancelStopwatch(user *User, issue *Issue) error { return nil } -func secToTime(duration int64) string { +// SecToTime converts an amount of seconds to a human-readable string (example: 66s -> 1min 6s) +func SecToTime(duration int64) string { seconds := duration % 60 minutes := (duration / (60)) % 60 hours := duration / (60 * 60) diff --git a/models/issue_tracked_time.go b/models/issue_tracked_time.go index 4280b083efced..6592f06d732cd 100644 --- a/models/issue_tracked_time.go +++ b/models/issue_tracked_time.go @@ -94,7 +94,7 @@ func AddTime(user *User, issue *Issue, time int64) (*TrackedTime, error) { Issue: issue, Repo: issue.Repo, Doer: user, - Content: secToTime(time), + Content: SecToTime(time), Type: CommentTypeAddTimeManual, }); err != nil { return nil, err @@ -124,7 +124,7 @@ func TotalTimes(options FindTrackedTimesOptions) (map[*User]string, error) { } return nil, err } - totalTimes[user] = secToTime(total) + totalTimes[user] = SecToTime(total) } return totalTimes, nil } diff --git a/modules/templates/helper.go b/modules/templates/helper.go index 98900c75388cd..2563933ddda08 100644 --- a/modules/templates/helper.go +++ b/modules/templates/helper.go @@ -181,6 +181,7 @@ func NewFuncMap() []template.FuncMap { }, "Printf": fmt.Sprintf, "Escape": Escape, + "Sec2Time": models.SecToTime, }} } diff --git a/templates/repo/issue/list.tmpl b/templates/repo/issue/list.tmpl index ab1dcd19e9c8b..b5c18fdd793cf 100644 --- a/templates/repo/issue/list.tmpl +++ b/templates/repo/issue/list.tmpl @@ -198,8 +198,8 @@ {{.NumComments}} {{end}} - {{if and .IsTimetrackerEnabled .TotalTimes}} - {{.TotalTimes}} + {{if and .IsTimetrackerEnabled .TotalTrackedTime}} + {{.TotalTrackedTime | Sec2Time}} {{end}}

diff --git a/templates/repo/issue/view_content/sidebar.tmpl b/templates/repo/issue/view_content/sidebar.tmpl index 1e2ae3a82122e..38d81b5c4457c 100644 --- a/templates/repo/issue/view_content/sidebar.tmpl +++ b/templates/repo/issue/view_content/sidebar.tmpl @@ -171,7 +171,7 @@ {{if gt (len .WorkingUsers) 0}}

- {{.i18n.Tr "repo.issues.time_spent_from_all_authors" $.Issue.TotalTimes | Safe}} + {{.i18n.Tr "repo.issues.time_spent_from_all_authors" ($.Issue.TotalTrackedTime | Sec2Time) | Safe}}
{{range $user, $trackedtime := .WorkingUsers}}
diff --git a/templates/user/dashboard/issues.tmpl b/templates/user/dashboard/issues.tmpl index 520dec9a4f1a2..2b4acfd15fffc 100644 --- a/templates/user/dashboard/issues.tmpl +++ b/templates/user/dashboard/issues.tmpl @@ -79,9 +79,8 @@ {{if .NumComments}} {{.NumComments}} {{end}} - {{$totalTimes := .TotalTimes}} - {{if and .IsTimetrackerEnabled $totalTimes}} - {{$totalTimes}} + {{if and .IsTimetrackerEnabled .TotalTrackedTime}} + {{.TotalTrackedTime | Sec2Time}} {{end}}

From 4cac53ebe4b5f8417418f13f275b7796997d9be1 Mon Sep 17 00:00:00 2001 From: Jonas Franz Date: Sun, 11 Mar 2018 11:48:09 +0100 Subject: [PATCH 09/18] Fixed test + gofmt Signed-off-by: Jonas Franz --- models/issue.go | 8 ++++---- models/issue_list.go | 2 +- models/issue_test.go | 6 +++--- modules/templates/helper.go | 4 ++-- 4 files changed, 10 insertions(+), 10 deletions(-) diff --git a/models/issue.go b/models/issue.go index 06d209f9a035c..47e8418a0488e 100644 --- a/models/issue.go +++ b/models/issue.go @@ -51,10 +51,10 @@ type Issue struct { UpdatedUnix util.TimeStamp `xorm:"INDEX updated"` ClosedUnix util.TimeStamp `xorm:"INDEX"` - Attachments []*Attachment `xorm:"-"` - Comments []*Comment `xorm:"-"` - Reactions ReactionList `xorm:"-"` - TotalTrackedTime int64 `xorm:"-"` + Attachments []*Attachment `xorm:"-"` + Comments []*Comment `xorm:"-"` + Reactions ReactionList `xorm:"-"` + TotalTrackedTime int64 `xorm:"-"` } var ( diff --git a/models/issue_list.go b/models/issue_list.go index f984b8d4788e9..31d6034cc6f8e 100644 --- a/models/issue_list.go +++ b/models/issue_list.go @@ -293,7 +293,7 @@ func (issues IssueList) loadComments(e Engine) (err error) { func (issues IssueList) loadTotalTrackedTimes(e Engine) (err error) { type totalTimesByIssue struct { IssueID int64 - Time int64 + Time int64 } if len(issues) == 0 { return nil diff --git a/models/issue_test.go b/models/issue_test.go index 71da444429643..d98debb17850e 100644 --- a/models/issue_test.go +++ b/models/issue_test.go @@ -280,10 +280,10 @@ func TestGetUserIssueStats(t *testing.T) { } } -func TestIssue_TotalTimes(t *testing.T) { +func TestIssue_loadTotalTimes(t *testing.T) { assert.NoError(t, PrepareTestDatabase()) ms, err := GetIssueByID(2) assert.NoError(t, err) - times := ms.TotalTimes() - assert.Equal(t, "1h 1min 2s", times) + assert.NoError(t, ms.loadTotalTimes(x)) + assert.Equal(t, int64(3662), ms.TotalTrackedTime) } diff --git a/modules/templates/helper.go b/modules/templates/helper.go index 2563933ddda08..8dfa6dec8ab4a 100644 --- a/modules/templates/helper.go +++ b/modules/templates/helper.go @@ -179,8 +179,8 @@ func NewFuncMap() []template.FuncMap { } return dict, nil }, - "Printf": fmt.Sprintf, - "Escape": Escape, + "Printf": fmt.Sprintf, + "Escape": Escape, "Sec2Time": models.SecToTime, }} } From ec0d41c02d9f1c20a39b256ddb8a3c3356f5d404 Mon Sep 17 00:00:00 2001 From: Jonas Franz Date: Mon, 16 Apr 2018 22:37:32 +0200 Subject: [PATCH 10/18] Load TotalTrackedTimes via MilestoneList instead of single requests Signed-off-by: Jonas Franz --- models/issue_milestone.go | 84 ++++++++++++++++++++-------- models/issue_milestone_test.go | 11 ++-- routers/repo/issue.go | 6 ++ templates/repo/issue/milestones.tmpl | 2 +- 4 files changed, 73 insertions(+), 30 deletions(-) diff --git a/models/issue_milestone.go b/models/issue_milestone.go index 506daa4453d57..6de32ce9590b3 100644 --- a/models/issue_milestone.go +++ b/models/issue_milestone.go @@ -29,6 +29,8 @@ type Milestone struct { DeadlineString string `xorm:"-"` DeadlineUnix util.TimeStamp ClosedDateUnix util.TimeStamp + + TotalTrackedTime int64 `xorm:"-"` } // BeforeInsert is invoked from XORM before inserting an object of this type. @@ -86,27 +88,6 @@ func (m *Milestone) APIFormat() *api.Milestone { return apiMilestone } -func (m *Milestone) totalTimes(e Engine) (string, error) { - opts := FindTrackedTimesOptions{MilestoneID: m.ID} - totalTime, err := opts.ToSession(e).SumInt(&TrackedTime{}, "time") - if err != nil { - return "", err - } - return SecToTime(totalTime), nil -} - -// TotalTimes returns the amount of tracked time of the issues inside the milestone -func (m *Milestone) TotalTimes() string { - times, _ := m.totalTimes(x) - return times -} - -// MustGetRepo returns milestone's repository by ignoring errors -func (m *Milestone) MustGetRepo() *Repository { - repo, _ := GetRepositoryByID(m.RepoID) - return repo -} - // NewMilestone creates new milestone of repository. func NewMilestone(m *Milestone) (err error) { sess := x.NewSession() @@ -144,14 +125,70 @@ func GetMilestoneByRepoID(repoID, id int64) (*Milestone, error) { return getMilestoneByRepoID(x, repoID, id) } +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)) + + // SELECT milestone_id, sum(time) as time FROM `issue` + // INNER JOIN `milestone` ON issue.milestone_id = milestone.id + // LEFT JOIN `tracked_time` ON tracked_time.issue_id = issue.id + // WHERE `milestone_id` IN (?) GROUP 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"). + 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 (milestones MilestoneList) LoadTotalTrackedTimes() error { + return milestones.loadTotalTrackedTimes(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 milestones of a repository. -func GetMilestonesByRepoID(repoID int64) ([]*Milestone, error) { +func GetMilestonesByRepoID(repoID int64) (MilestoneList, error) { miles := make([]*Milestone, 0, 10) return miles, x.Where("repo_id = ?", repoID).Find(&miles) } // GetMilestones returns a list of milestones of given repository and status. -func GetMilestones(repoID int64, page int, isClosed bool, sortType string) ([]*Milestone, error) { +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 { @@ -172,7 +209,6 @@ func GetMilestones(repoID int64, page int, isClosed bool, sortType string) ([]*M default: sess.Asc("deadline_unix") } - return miles, sess.Find(&miles) } diff --git a/models/issue_milestone_test.go b/models/issue_milestone_test.go index 6d6c01b5a4e9d..cef567296d5de 100644 --- a/models/issue_milestone_test.go +++ b/models/issue_milestone_test.go @@ -254,10 +254,11 @@ func TestDeleteMilestoneByRepoID(t *testing.T) { assert.NoError(t, DeleteMilestoneByRepoID(NonexistentID, NonexistentID)) } -func TestMilestone_TotalTimes(t *testing.T) { +func TestMilestoneList_LoadTotalTrackedTimes(t *testing.T) { assert.NoError(t, PrepareTestDatabase()) - ms, err := GetMilestoneByRepoID(1, 1) - assert.NoError(t, err) - times := ms.TotalTimes() - assert.Equal(t, "1h 1min 2s", times) + miles := MilestoneList{ + AssertExistsAndLoadBean(t, &Milestone{ID: 1}).(*Milestone), + } + assert.NoError(t, miles.LoadTotalTrackedTimes()) + assert.Equal(t, miles[0].TotalTrackedTime, 3661) } diff --git a/routers/repo/issue.go b/routers/repo/issue.go index 234937b1af750..51516b828c274 100644 --- a/routers/repo/issue.go +++ b/routers/repo/issue.go @@ -1139,6 +1139,12 @@ func Milestones(ctx *context.Context) { ctx.ServerError("GetMilestones", err) return } + if ctx.Repo.Repository.IsTimetrackerEnabled() { + if miles.LoadTotalTrackedTimes(); err != nil { + ctx.ServerError("LoadTotalTrackedTimes", err) + return + } + } for _, m := range miles { m.RenderedContent = string(markdown.Render([]byte(m.Content), ctx.Repo.RepoLink, ctx.Repo.Repository.ComposeMetas())) } diff --git a/templates/repo/issue/milestones.tmpl b/templates/repo/issue/milestones.tmpl index 284afd5894fb1..369da2e630118 100644 --- a/templates/repo/issue/milestones.tmpl +++ b/templates/repo/issue/milestones.tmpl @@ -64,7 +64,7 @@ {{$.i18n.Tr "repo.issues.open_tab" .NumOpenIssues}} {{$.i18n.Tr "repo.issues.close_tab" .NumClosedIssues}} - {{if and .TotalTimes .MustGetRepo.IsTimetrackerEnabled}} {{.TotalTimes}}{{end}} + {{if .TotalTrackedTime}} {{.TotalTrackedTime|Sec2Time}}{{end}}

{{if $.IsRepositoryWriter}} From 02dc1cf8647c97549c3943b5f7a830c059f44159 Mon Sep 17 00:00:00 2001 From: Jonas Franz Date: Mon, 16 Apr 2018 22:42:50 +0200 Subject: [PATCH 11/18] Add documentation for MilestoneList Signed-off-by: Jonas Franz --- models/issue_milestone.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/models/issue_milestone.go b/models/issue_milestone.go index 6de32ce9590b3..1229a74c37389 100644 --- a/models/issue_milestone.go +++ b/models/issue_milestone.go @@ -125,6 +125,7 @@ func GetMilestoneByRepoID(repoID, id int64) (*Milestone, error) { return getMilestoneByRepoID(x, repoID, id) } +// MilestoneList is a list of milestones offering additional functionality type MilestoneList []*Milestone func (milestones MilestoneList) loadTotalTrackedTimes(e Engine) error { @@ -169,6 +170,7 @@ func (milestones MilestoneList) loadTotalTrackedTimes(e Engine) error { return nil } +// LoadTotalTrackedTimes loads for every milestone in the list the TotalTrackedTime func (milestones MilestoneList) LoadTotalTrackedTimes() error { return milestones.loadTotalTrackedTimes(x) } From 0ba269b70c290053afa12d8ff633908ca8f7eed8 Mon Sep 17 00:00:00 2001 From: Jonas Franz Date: Mon, 16 Apr 2018 22:48:09 +0200 Subject: [PATCH 12/18] Add documentation for MilestoneList Signed-off-by: Jonas Franz --- models/issue_milestone.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/models/issue_milestone.go b/models/issue_milestone.go index 1229a74c37389..96b9d7f6c4dd0 100644 --- a/models/issue_milestone.go +++ b/models/issue_milestone.go @@ -170,7 +170,7 @@ func (milestones MilestoneList) loadTotalTrackedTimes(e Engine) error { return nil } -// LoadTotalTrackedTimes loads for every milestone in the list the TotalTrackedTime +// LoadTotalTrackedTimes loads for every milestone in the list the TotalTrackedTime by a batch request func (milestones MilestoneList) LoadTotalTrackedTimes() error { return milestones.loadTotalTrackedTimes(x) } From 70e9bbd4830c05e0064465e10e5eda955821f767 Mon Sep 17 00:00:00 2001 From: Jonas Franz Date: Mon, 16 Apr 2018 23:04:50 +0200 Subject: [PATCH 13/18] Fix test Signed-off-by: Jonas Franz --- models/issue_milestone_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/models/issue_milestone_test.go b/models/issue_milestone_test.go index cef567296d5de..30d1a90988d3e 100644 --- a/models/issue_milestone_test.go +++ b/models/issue_milestone_test.go @@ -260,5 +260,5 @@ func TestMilestoneList_LoadTotalTrackedTimes(t *testing.T) { AssertExistsAndLoadBean(t, &Milestone{ID: 1}).(*Milestone), } assert.NoError(t, miles.LoadTotalTrackedTimes()) - assert.Equal(t, miles[0].TotalTrackedTime, 3661) + assert.Equal(t, miles[0].TotalTrackedTime, 3662) } From 221a327869f76437fa4face06b7dcd5804cb0e4a Mon Sep 17 00:00:00 2001 From: Jonas Franz Date: Thu, 19 Apr 2018 14:45:18 +0200 Subject: [PATCH 14/18] Change comment from SQL query to description Signed-off-by: Jonas Franz --- models/issue_milestone.go | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/models/issue_milestone.go b/models/issue_milestone.go index 96b9d7f6c4dd0..1f852e5502f44 100644 --- a/models/issue_milestone.go +++ b/models/issue_milestone.go @@ -138,10 +138,7 @@ func (milestones MilestoneList) loadTotalTrackedTimes(e Engine) error { } var trackedTimes = make(map[int64]int64, len(milestones)) - // SELECT milestone_id, sum(time) as time FROM `issue` - // INNER JOIN `milestone` ON issue.milestone_id = milestone.id - // LEFT JOIN `tracked_time` ON tracked_time.issue_id = issue.id - // WHERE `milestone_id` IN (?) GROUP BY milestone_id + // 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"). From ca5243cda677b095b9c29aae03a212e9491eddde Mon Sep 17 00:00:00 2001 From: Jonas Franz Date: Thu, 19 Apr 2018 22:07:15 +0200 Subject: [PATCH 15/18] Fix unit test by using int64 instead of int Signed-off-by: Jonas Franz --- models/issue_milestone_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/models/issue_milestone_test.go b/models/issue_milestone_test.go index 30d1a90988d3e..0b4b781bf516d 100644 --- a/models/issue_milestone_test.go +++ b/models/issue_milestone_test.go @@ -260,5 +260,5 @@ func TestMilestoneList_LoadTotalTrackedTimes(t *testing.T) { AssertExistsAndLoadBean(t, &Milestone{ID: 1}).(*Milestone), } assert.NoError(t, miles.LoadTotalTrackedTimes()) - assert.Equal(t, miles[0].TotalTrackedTime, 3662) + assert.Equal(t, miles[0].TotalTrackedTime, int64(3662)) } From d6237809c472de482cc31fb954af1a8fd6ef54b4 Mon Sep 17 00:00:00 2001 From: Jonas Franz Date: Thu, 19 Apr 2018 22:11:09 +0200 Subject: [PATCH 16/18] Fix unit test by using int64 instead of int Signed-off-by: Jonas Franz --- models/issue_milestone_test.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/models/issue_milestone_test.go b/models/issue_milestone_test.go index 0b4b781bf516d..692c789b50c47 100644 --- a/models/issue_milestone_test.go +++ b/models/issue_milestone_test.go @@ -259,6 +259,8 @@ func TestMilestoneList_LoadTotalTrackedTimes(t *testing.T) { miles := MilestoneList{ AssertExistsAndLoadBean(t, &Milestone{ID: 1}).(*Milestone), } + assert.NoError(t, miles.LoadTotalTrackedTimes()) + assert.Equal(t, miles[0].TotalTrackedTime, int64(3662)) } From 6a97bf716507eafe760bf5d974f6c7f8d0af8503 Mon Sep 17 00:00:00 2001 From: Jonas Franz Date: Fri, 20 Apr 2018 20:07:26 +0200 Subject: [PATCH 17/18] Check if timetracker is enabled Signed-off-by: Jonas Franz --- models/issue.go | 7 ++++--- models/issue_list.go | 9 ++++++++- models/issue_milestone_test.go | 2 +- templates/repo/issue/list.tmpl | 2 +- templates/user/dashboard/issues.tmpl | 2 +- 5 files changed, 15 insertions(+), 7 deletions(-) diff --git a/models/issue.go b/models/issue.go index 47e8418a0488e..190b3875307d0 100644 --- a/models/issue.go +++ b/models/issue.go @@ -244,9 +244,10 @@ func (issue *Issue) loadAttributes(e Engine) (err error) { if err = issue.loadComments(e); err != nil { return err } - - if err = issue.loadTotalTimes(e); err != nil { - return err + if issue.IsTimetrackerEnabled() { + if err = issue.loadTotalTimes(e); err != nil { + return err + } } return issue.loadReactions(e) diff --git a/models/issue_list.go b/models/issue_list.go index 31d6034cc6f8e..a29fe31810580 100644 --- a/models/issue_list.go +++ b/models/issue_list.go @@ -300,10 +300,17 @@ func (issues IssueList) loadTotalTrackedTimes(e Engine) (err error) { } var trackedTimes = make(map[int64]int64, len(issues)) + var ids = make([]int64, 0, len(issues)) + for _, issue := range issues { + if issue.IsTimetrackerEnabled() { + ids = append(ids, issue.ID) + } + } + // select issue_id, sum(time) from tracked_time where issue_id in () group by issue_id rows, err := e.Table("tracked_time"). Select("issue_id, sum(time) as time"). - In("issue_id", issues.getIssueIDs()). + In("issue_id", ids). GroupBy("issue_id"). Rows(new(totalTimesByIssue)) if err != nil { diff --git a/models/issue_milestone_test.go b/models/issue_milestone_test.go index 692c789b50c47..c9b53f4f4a348 100644 --- a/models/issue_milestone_test.go +++ b/models/issue_milestone_test.go @@ -259,7 +259,7 @@ func TestMilestoneList_LoadTotalTrackedTimes(t *testing.T) { miles := MilestoneList{ AssertExistsAndLoadBean(t, &Milestone{ID: 1}).(*Milestone), } - + assert.NoError(t, miles.LoadTotalTrackedTimes()) assert.Equal(t, miles[0].TotalTrackedTime, int64(3662)) diff --git a/templates/repo/issue/list.tmpl b/templates/repo/issue/list.tmpl index 67c751fa4ef9a..180a5dea6cfd7 100644 --- a/templates/repo/issue/list.tmpl +++ b/templates/repo/issue/list.tmpl @@ -198,7 +198,7 @@ {{.NumComments}} {{end}} - {{if and .IsTimetrackerEnabled .TotalTrackedTime}} + {{if .TotalTrackedTime}} {{.TotalTrackedTime | Sec2Time}} {{end}} diff --git a/templates/user/dashboard/issues.tmpl b/templates/user/dashboard/issues.tmpl index 46bc11dff1869..d0b6511b870a2 100644 --- a/templates/user/dashboard/issues.tmpl +++ b/templates/user/dashboard/issues.tmpl @@ -79,7 +79,7 @@ {{if .NumComments}} {{.NumComments}} {{end}} - {{if and .IsTimetrackerEnabled .TotalTrackedTime}} + {{if .TotalTrackedTime}} {{.TotalTrackedTime | Sec2Time}} {{end}} From 62f9962e04d439d37d774b3b124e6b00bd69d4b0 Mon Sep 17 00:00:00 2001 From: Jonas Franz Date: Sat, 21 Apr 2018 12:05:16 +0200 Subject: [PATCH 18/18] Fix test by enabling timetracking Signed-off-by: Jonas Franz --- models/issue_list.go | 2 +- models/issue_list_test.go | 4 +++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/models/issue_list.go b/models/issue_list.go index a29fe31810580..01a1a15f44392 100644 --- a/models/issue_list.go +++ b/models/issue_list.go @@ -302,7 +302,7 @@ func (issues IssueList) loadTotalTrackedTimes(e Engine) (err error) { var ids = make([]int64, 0, len(issues)) for _, issue := range issues { - if issue.IsTimetrackerEnabled() { + if issue.Repo.IsTimetrackerEnabled() { ids = append(ids, issue.ID) } } diff --git a/models/issue_list_test.go b/models/issue_list_test.go index f27440ec2e9be..9197e0615aa4b 100644 --- a/models/issue_list_test.go +++ b/models/issue_list_test.go @@ -7,6 +7,8 @@ package models import ( "testing" + "code.gitea.io/gitea/modules/setting" + "github.com/stretchr/testify/assert" ) @@ -29,7 +31,7 @@ func TestIssueList_LoadRepositories(t *testing.T) { func TestIssueList_LoadAttributes(t *testing.T) { assert.NoError(t, PrepareTestDatabase()) - + setting.Service.EnableTimetracking = true issueList := IssueList{ AssertExistsAndLoadBean(t, &Issue{ID: 1}).(*Issue), AssertExistsAndLoadBean(t, &Issue{ID: 2}).(*Issue),