Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Show total TrackedTime on issue/pull/milestone lists #26672

Merged
merged 44 commits into from
Oct 19, 2023
Merged
Show file tree
Hide file tree
Changes from 42 commits
Commits
Show all changes
44 commits
Select commit Hold shift + click to select a range
377d160
Show total TrackedTime in issue list and milestone issue list [partia…
6543 Jun 6, 2023
056ea96
rm diff thats to be in next pull
6543 Aug 22, 2023
7e7a2c5
adjust to renamed opt
6543 Aug 22, 2023
b96699e
Add GetIssueTotalTrackedTime, witch returns the total tracked time fo…
6543 Aug 22, 2023
a14d804
adjust to current codebase
6543 Aug 22, 2023
b9db680
adjust code-scope [see with no whitespace changes]
6543 Aug 22, 2023
ecccc50
add TotalTrackedTime Data for template
6543 Aug 22, 2023
2f4728d
Merge branch 'main' into upstream_trackedTimeSummaryOnIssueList
6543 Aug 23, 2023
e2349bc
Update routers/web/repo/issue.go
6543 Aug 23, 2023
c50d2f1
Merge branch 'main' into upstream_trackedTimeSummaryOnIssueList
6543 Aug 30, 2023
b18c837
Merge branch 'main' into upstream_trackedTimeSummaryOnIssueList
6543 Sep 25, 2023
e9766f1
fix
6543 Sep 25, 2023
b3e9420
comment
6543 Sep 25, 2023
26c34c7
Merge branch 'main' into upstream_trackedTimeSummaryOnIssueList
6543 Sep 30, 2023
cbf2965
WIP: WebUI
6543 Sep 30, 2023
bffb2b2
TTT in MS looks good now ;)
6543 Sep 30, 2023
4706f89
WIP: TTT on issue list
6543 Sep 30, 2023
f80779d
try vertical align ?!?
6543 Sep 30, 2023
0463116
Merge branch 'main' into upstream_trackedTimeSummaryOnIssueList
6543 Sep 30, 2023
b110d85
TTT in issue list looks good now ;)
6543 Sep 30, 2023
4cd950c
fix lint & wip test
6543 Sep 30, 2023
aa562fc
context
6543 Oct 1, 2023
db387c0
fix and extend tests
6543 Oct 1, 2023
ae166a0
Merge branch 'main' into upstream_trackedTimeSummaryOnIssueList
6543 Oct 1, 2023
ecb01c2
Merge branch 'main' into upstream_trackedTimeSummaryOnIssueList
6543 Oct 1, 2023
969bb41
Merge branch 'main' into upstream_trackedTimeSummaryOnIssueList
6543 Oct 2, 2023
fc27f22
Merge branch 'main' into upstream_trackedTimeSummaryOnIssueList
6543 Oct 2, 2023
3a1dab8
Update options/locale/locale_en-US.ini
6543 Oct 3, 2023
bc900f4
Merge branch 'main' into upstream_trackedTimeSummaryOnIssueList
6543 Oct 3, 2023
f1b4128
Merge branch 'main' into upstream_trackedTimeSummaryOnIssueList
6543 Oct 4, 2023
d4ffd1b
Merge branch 'main' into upstream_trackedTimeSummaryOnIssueList
6543 Oct 7, 2023
10d18bd
Merge branch 'main' into upstream_trackedTimeSummaryOnIssueList
6543 Oct 8, 2023
5385025
Merge branch 'main' into upstream_trackedTimeSummaryOnIssueList
6543 Oct 9, 2023
a82da3a
Merge branch 'main' into upstream_trackedTimeSummaryOnIssueList
6543 Oct 12, 2023
2d55b1a
Merge branch 'main' into upstream_trackedTimeSummaryOnIssueList
6543 Oct 13, 2023
04594bc
only exec if tt in repo is enabled
6543 Oct 13, 2023
beb3b64
Merge branch 'main' into upstream_trackedTimeSummaryOnIssueList
6543 Oct 14, 2023
3b1b7a0
Merge branch 'main' into upstream_trackedTimeSummaryOnIssueList
6543 Oct 15, 2023
697c4a1
Merge branch 'main' into upstream_trackedTimeSummaryOnIssueList
6543 Oct 15, 2023
d102bd8
Merge branch 'main' into upstream_trackedTimeSummaryOnIssueList
6543 Oct 16, 2023
b809a91
Merge branch 'main' into upstream_trackedTimeSummaryOnIssueList
6543 Oct 17, 2023
77e3d4e
Merge branch 'main' into upstream_trackedTimeSummaryOnIssueList
silverwind Oct 17, 2023
d7f0392
Merge branch 'main' into upstream_trackedTimeSummaryOnIssueList
6543 Oct 19, 2023
d0bedae
Merge branch 'main' into upstream_trackedTimeSummaryOnIssueList
6543 Oct 19, 2023
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
6 changes: 6 additions & 0 deletions models/issues/issue_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -191,6 +191,12 @@ func TestIssues(t *testing.T) {
},
[]int64{}, // issues with **both** label 1 and 2, none of these issues matches, TODO: add more tests
},
{
issues_model.IssuesOptions{
MilestoneIDs: []int64{1},
},
[]int64{2},
},
} {
issues, err := issues_model.Issues(db.DefaultContext, &test.Opts)
assert.NoError(t, err)
Expand Down
44 changes: 44 additions & 0 deletions models/issues/tracked_time.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import (
"code.gitea.io/gitea/modules/util"

"xorm.io/builder"
"xorm.io/xorm"
)

// TrackedTime represents a time that was spent for a specific issue.
Expand Down Expand Up @@ -325,3 +326,46 @@ func GetTrackedTimeByID(ctx context.Context, id int64) (*TrackedTime, error) {
}
return time, nil
}

// GetIssueTotalTrackedTime returns the total tracked time for issues by given conditions.
func GetIssueTotalTrackedTime(ctx context.Context, opts *IssuesOptions, isClosed bool) (int64, error) {
if len(opts.IssueIDs) <= MaxQueryParameters {
return getIssueTotalTrackedTimeChunk(ctx, opts, isClosed, opts.IssueIDs)
}

// If too long a list of IDs is provided,
// we get the statistics in smaller chunks and get accumulates
var accum int64
for i := 0; i < len(opts.IssueIDs); {
chunk := i + MaxQueryParameters
if chunk > len(opts.IssueIDs) {
chunk = len(opts.IssueIDs)
}
time, err := getIssueTotalTrackedTimeChunk(ctx, opts, isClosed, opts.IssueIDs[i:chunk])
if err != nil {
return 0, err
}
accum += time
i = chunk
}
return accum, nil
}

func getIssueTotalTrackedTimeChunk(ctx context.Context, opts *IssuesOptions, isClosed bool, issueIDs []int64) (int64, error) {
sumSession := func(opts *IssuesOptions, issueIDs []int64) *xorm.Session {
sess := db.GetEngine(ctx).
Table("tracked_time").
Where("tracked_time.deleted = ?", false).
Join("INNER", "issue", "tracked_time.issue_id = issue.id")

return applyIssuesOptions(sess, opts, issueIDs)
}

type trackedTime struct {
Time int64
}

return sumSession(opts, issueIDs).
And("issue.is_closed = ?", isClosed).
SumInt(new(trackedTime), "tracked_time.time")
}
12 changes: 12 additions & 0 deletions models/issues/tracked_time_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -115,3 +115,15 @@ func TestTotalTimesForEachUser(t *testing.T) {
assert.NoError(t, err)
assert.Len(t, total, 2)
}

func TestGetIssueTotalTrackedTime(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())

ttt, err := issues_model.GetIssueTotalTrackedTime(db.DefaultContext, &issues_model.IssuesOptions{MilestoneIDs: []int64{1}}, false)
assert.NoError(t, err)
assert.EqualValues(t, 3682, ttt)

ttt, err = issues_model.GetIssueTotalTrackedTime(db.DefaultContext, &issues_model.IssuesOptions{MilestoneIDs: []int64{1}}, true)
assert.NoError(t, err)
assert.EqualValues(t, 0, ttt)
}
1 change: 1 addition & 0 deletions options/locale/locale_en-US.ini
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ template = Template
language = Language
notifications = Notifications
active_stopwatch = Active Time Tracker
tracked_time_summary = Summary of tracked time based on filters of issue list
create_new = Create…
user_profile_and_more = Profile and Settings…
signed_in_as = Signed in as
Expand Down
78 changes: 42 additions & 36 deletions routers/web/repo/issue.go
Original file line number Diff line number Diff line change
Expand Up @@ -198,46 +198,43 @@ func issues(ctx *context.Context, milestoneID, projectID int64, isPullOption uti
}

var issueStats *issues_model.IssueStats
{
statsOpts := &issues_model.IssuesOptions{
RepoIDs: []int64{repo.ID},
LabelIDs: labelIDs,
MilestoneIDs: mileIDs,
ProjectID: projectID,
AssigneeID: assigneeID,
MentionedID: mentionedID,
PosterID: posterID,
ReviewRequestedID: reviewRequestedID,
ReviewedID: reviewedID,
IsPull: isPullOption,
IssueIDs: nil,
}
if keyword != "" {
allIssueIDs, err := issueIDsFromSearch(ctx, keyword, statsOpts)
if err != nil {
if issue_indexer.IsAvailable(ctx) {
ctx.ServerError("issueIDsFromSearch", err)
return
}
ctx.Data["IssueIndexerUnavailable"] = true
statsOpts := &issues_model.IssuesOptions{
RepoIDs: []int64{repo.ID},
LabelIDs: labelIDs,
MilestoneIDs: mileIDs,
ProjectID: projectID,
AssigneeID: assigneeID,
MentionedID: mentionedID,
PosterID: posterID,
ReviewRequestedID: reviewRequestedID,
ReviewedID: reviewedID,
IsPull: isPullOption,
IssueIDs: nil,
}
if keyword != "" {
allIssueIDs, err := issueIDsFromSearch(ctx, keyword, statsOpts)
if err != nil {
if issue_indexer.IsAvailable(ctx) {
ctx.ServerError("issueIDsFromSearch", err)
return
}
statsOpts.IssueIDs = allIssueIDs
ctx.Data["IssueIndexerUnavailable"] = true
return
}
if keyword != "" && len(statsOpts.IssueIDs) == 0 {
// So it did search with the keyword, but no issue found.
// Just set issueStats to empty.
issueStats = &issues_model.IssueStats{}
} else {
// So it did search with the keyword, and found some issues. It needs to get issueStats of these issues.
// Or the keyword is empty, so it doesn't need issueIDs as filter, just get issueStats with statsOpts.
issueStats, err = issues_model.GetIssueStats(ctx, statsOpts)
if err != nil {
ctx.ServerError("GetIssueStats", err)
return
}
statsOpts.IssueIDs = allIssueIDs
}
if keyword != "" && len(statsOpts.IssueIDs) == 0 {
// So it did search with the keyword, but no issue found.
// Just set issueStats to empty.
issueStats = &issues_model.IssueStats{}
} else {
// So it did search with the keyword, and found some issues. It needs to get issueStats of these issues.
// Or the keyword is empty, so it doesn't need issueIDs as filter, just get issueStats with statsOpts.
issueStats, err = issues_model.GetIssueStats(ctx, statsOpts)
if err != nil {
ctx.ServerError("GetIssueStats", err)
return
}

}

isShowClosed := ctx.FormString("state") == "closed"
Expand All @@ -246,6 +243,15 @@ func issues(ctx *context.Context, milestoneID, projectID int64, isPullOption uti
isShowClosed = true
}

if repo.IsTimetrackerEnabled(ctx) {
totalTrackedTime, err := issues_model.GetIssueTotalTrackedTime(ctx, statsOpts, isShowClosed)
if err != nil {
ctx.ServerError("GetIssueTotalTrackedTime", err)
return
}
ctx.Data["TotalTrackedTime"] = totalTrackedTime
}

page := ctx.FormInt("page")
if page <= 1 {
page = 1
Expand Down
9 changes: 9 additions & 0 deletions templates/repo/issue/filters.tmpl
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,15 @@
<input type="checkbox" autocomplete="off" class="issue-checkbox-all gt-mr-4" title="{{ctx.Locale.Tr "repo.issues.action_check_all"}}">
{{end}}
{{template "repo/issue/openclose" .}}
<!-- Total Tracked Time -->
{{if .TotalTrackedTime}}
<div class="ui compact tiny secondary menu">
<span class="item" data-tooltip-content='{{ctx.Locale.Tr "tracked_time_summary"}}'>
{{svg "octicon-clock"}}
{{.TotalTrackedTime | Sec2Time}}
</span>
</div>
{{end}}
</div>
<div class="issue-list-toolbar-right">
<div class="ui secondary filter menu labels">
Expand Down
9 changes: 9 additions & 0 deletions templates/repo/issue/list.tmpl
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,15 @@
<div id="issue-actions" class="issue-list-toolbar gt-hidden">
<div class="issue-list-toolbar-left">
{{template "repo/issue/openclose" .}}
<!-- Total Tracked Time -->
6543 marked this conversation as resolved.
Show resolved Hide resolved
{{if .TotalTrackedTime}}
<div class="ui compact tiny secondary menu">
<span class="item" data-tooltip-content='{{ctx.Locale.Tr "tracked_time_summary"}}'>
{{svg "octicon-clock"}}
{{.TotalTrackedTime | Sec2Time}}
</span>
</div>
{{end}}
</div>
<div class="issue-list-toolbar-right">
{{template "repo/issue/filter_actions" .}}
Expand Down
6 changes: 6 additions & 0 deletions templates/repo/issue/milestone_issues.tmpl
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,12 @@
{{end}}
</div>
<div class="gt-mr-3">{{ctx.Locale.Tr "repo.milestones.completeness" .Milestone.Completeness | Safe}}</div>
{{if .TotalTrackedTime}}
<div data-tooltip-content='{{ctx.Locale.Tr "tracked_time_summary"}}'>
{{svg "octicon-clock"}}
{{.TotalTrackedTime | Sec2Time}}
</div>
{{end}}
</div>
</div>
<div class="divider"></div>
Expand Down