Skip to content

Commit 108705c

Browse files
author
user
committed
rebase of earlier feature/worktime-for-org branch
1 parent 3361bbf commit 108705c

File tree

16 files changed

+796
-0
lines changed

16 files changed

+796
-0
lines changed
Lines changed: 308 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,308 @@
1+
// Copyright 2022 The Gitea Authors. All rights reserved.
2+
// Use of this source code is governed by a MIT-style
3+
// license that can be found in the LICENSE file.
4+
// SPDX-License-Identifier: MIT
5+
6+
package orgtime_test
7+
8+
import (
9+
"path/filepath"
10+
"testing"
11+
12+
"code.gitea.io/gitea/models/db"
13+
"code.gitea.io/gitea/models/organization"
14+
"code.gitea.io/gitea/models/unittest"
15+
16+
_ "code.gitea.io/gitea/models/issues"
17+
18+
"github.com/stretchr/testify/assert"
19+
)
20+
21+
// TestMain sets up the testing environment specifically for testing org times.
22+
func TestMain(m *testing.M) {
23+
unittest.MainTest(m, &unittest.TestOptions{
24+
GiteaRootPath: filepath.Join("..", "..", ".."),
25+
FixtureFiles: []string{
26+
"user.yml",
27+
"org_user.yml",
28+
"repository.yml",
29+
"issue.yml",
30+
"milestone.yml",
31+
"tracked_time.yml",
32+
},
33+
})
34+
}
35+
36+
// TestTimesPrepareDB prepares the database for the following tests.
37+
func TestTimesPrepareDB(t *testing.T) {
38+
assert.NoError(t, unittest.PrepareTestDatabase())
39+
}
40+
41+
// TestTimesByRepos tests TimesByRepos functionality
42+
func TestTimesByRepos(t *testing.T) {
43+
kases := []struct {
44+
name string
45+
unixfrom int64
46+
unixto int64
47+
orgname int64
48+
expected []organization.ResultTimesByRepos
49+
}{
50+
{
51+
name: "Full sum for org 1",
52+
unixfrom: 0,
53+
unixto: 9223372036854775807,
54+
orgname: 1,
55+
expected: []organization.ResultTimesByRepos(nil),
56+
},
57+
{
58+
name: "Full sum for org 2",
59+
unixfrom: 0,
60+
unixto: 9223372036854775807,
61+
orgname: 2,
62+
expected: []organization.ResultTimesByRepos{
63+
{
64+
Name: "repo1",
65+
SumTime: 4083,
66+
},
67+
{
68+
Name: "repo2",
69+
SumTime: 75,
70+
},
71+
},
72+
},
73+
{
74+
name: "Simple time bound",
75+
unixfrom: 946684801,
76+
unixto: 946684802,
77+
orgname: 2,
78+
expected: []organization.ResultTimesByRepos{
79+
{
80+
Name: "repo1",
81+
SumTime: 3662,
82+
},
83+
},
84+
},
85+
{
86+
name: "Both times inclusive",
87+
unixfrom: 946684801,
88+
unixto: 946684801,
89+
orgname: 2,
90+
expected: []organization.ResultTimesByRepos{
91+
{
92+
Name: "repo1",
93+
SumTime: 3661,
94+
},
95+
},
96+
},
97+
{
98+
name: "Should ignore deleted",
99+
unixfrom: 947688814,
100+
unixto: 947688815,
101+
orgname: 2,
102+
expected: []organization.ResultTimesByRepos{
103+
{
104+
Name: "repo2",
105+
SumTime: 71,
106+
},
107+
},
108+
},
109+
}
110+
111+
// Run test kases
112+
for _, kase := range kases {
113+
t.Run(kase.name, func(t *testing.T) {
114+
org, err := organization.GetOrgByID(db.DefaultContext, kase.orgname)
115+
assert.NoError(t, err)
116+
results, err := org.GetTimesByRepos(kase.unixfrom, kase.unixto)
117+
assert.NoError(t, err)
118+
assert.Equal(t, kase.expected, results)
119+
})
120+
}
121+
}
122+
123+
// TestTimesByMilestones tests TimesByMilestones functionality
124+
func TestTimesByMilestones(t *testing.T) {
125+
kases := []struct {
126+
name string
127+
unixfrom int64
128+
unixto int64
129+
orgname int64
130+
expected []organization.ResultTimesByMilestones
131+
}{
132+
{
133+
name: "Full sum for org 1",
134+
unixfrom: 0,
135+
unixto: 9223372036854775807,
136+
orgname: 1,
137+
expected: []organization.ResultTimesByMilestones(nil),
138+
},
139+
{
140+
name: "Full sum for org 2",
141+
unixfrom: 0,
142+
unixto: 9223372036854775807,
143+
orgname: 2,
144+
expected: []organization.ResultTimesByMilestones{
145+
{
146+
RepoName: "repo1",
147+
Name: "",
148+
ID: "",
149+
SumTime: 401,
150+
HideRepoName: false,
151+
},
152+
{
153+
RepoName: "repo1",
154+
Name: "milestone1",
155+
ID: "1",
156+
SumTime: 3682,
157+
HideRepoName: false,
158+
},
159+
{
160+
RepoName: "repo2",
161+
Name: "",
162+
ID: "",
163+
SumTime: 75,
164+
HideRepoName: false,
165+
},
166+
},
167+
},
168+
{
169+
name: "Simple time bound",
170+
unixfrom: 946684801,
171+
unixto: 946684802,
172+
orgname: 2,
173+
expected: []organization.ResultTimesByMilestones{
174+
{
175+
RepoName: "repo1",
176+
Name: "milestone1",
177+
ID: "1",
178+
SumTime: 3662,
179+
HideRepoName: false,
180+
},
181+
},
182+
},
183+
{
184+
name: "Both times inclusive",
185+
unixfrom: 946684801,
186+
unixto: 946684801,
187+
orgname: 2,
188+
expected: []organization.ResultTimesByMilestones{
189+
{
190+
RepoName: "repo1",
191+
Name: "milestone1",
192+
ID: "1",
193+
SumTime: 3661,
194+
HideRepoName: false,
195+
},
196+
},
197+
},
198+
{
199+
name: "Should ignore deleted",
200+
unixfrom: 947688814,
201+
unixto: 947688815,
202+
orgname: 2,
203+
expected: []organization.ResultTimesByMilestones{
204+
{
205+
RepoName: "repo2",
206+
Name: "",
207+
ID: "",
208+
SumTime: 71,
209+
HideRepoName: false,
210+
},
211+
},
212+
},
213+
}
214+
215+
// Run test kases
216+
for _, kase := range kases {
217+
t.Run(kase.name, func(t *testing.T) {
218+
org, err := organization.GetOrgByID(db.DefaultContext, kase.orgname)
219+
assert.NoError(t, err)
220+
results, err := org.GetTimesByMilestones(kase.unixfrom, kase.unixto)
221+
assert.NoError(t, err)
222+
assert.Equal(t, kase.expected, results)
223+
})
224+
}
225+
}
226+
227+
// TestTimesByMembers tests TimesByMembers functionality
228+
func TestTimesByMembers(t *testing.T) {
229+
kases := []struct {
230+
name string
231+
unixfrom int64
232+
unixto int64
233+
orgname int64
234+
expected []organization.ResultTimesByMembers
235+
}{
236+
{
237+
name: "Full sum for org 1",
238+
unixfrom: 0,
239+
unixto: 9223372036854775807,
240+
orgname: 1,
241+
expected: []organization.ResultTimesByMembers(nil),
242+
},
243+
{
244+
// Test case: Sum of times forever in org no. 2
245+
name: "Full sum for org 2",
246+
unixfrom: 0,
247+
unixto: 9223372036854775807,
248+
orgname: 2,
249+
expected: []organization.ResultTimesByMembers{
250+
{
251+
Name: "user2",
252+
SumTime: 3666,
253+
},
254+
{
255+
Name: "user1",
256+
SumTime: 491,
257+
},
258+
},
259+
},
260+
{
261+
name: "Simple time bound",
262+
unixfrom: 946684801,
263+
unixto: 946684802,
264+
orgname: 2,
265+
expected: []organization.ResultTimesByMembers{
266+
{
267+
Name: "user2",
268+
SumTime: 3662,
269+
},
270+
},
271+
},
272+
{
273+
name: "Both times inclusive",
274+
unixfrom: 946684801,
275+
unixto: 946684801,
276+
orgname: 2,
277+
expected: []organization.ResultTimesByMembers{
278+
{
279+
Name: "user2",
280+
SumTime: 3661,
281+
},
282+
},
283+
},
284+
{
285+
name: "Should ignore deleted",
286+
unixfrom: 947688814,
287+
unixto: 947688815,
288+
orgname: 2,
289+
expected: []organization.ResultTimesByMembers{
290+
{
291+
Name: "user1",
292+
SumTime: 71,
293+
},
294+
},
295+
},
296+
}
297+
298+
// Run test kases
299+
for _, kase := range kases {
300+
t.Run(kase.name, func(t *testing.T) {
301+
org, err := organization.GetOrgByID(db.DefaultContext, kase.orgname)
302+
assert.NoError(t, err)
303+
results, err := org.GetTimesByMembers(kase.unixfrom, kase.unixto)
304+
assert.NoError(t, err)
305+
assert.Equal(t, kase.expected, results)
306+
})
307+
}
308+
}

models/organization/org_times.go

Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
// Copyright 2022 The Gitea Authors. All rights reserved.
2+
// Use of this source code is governed by a MIT-style
3+
// license that can be found in the LICENSE file.
4+
// SPDX-License-Identifier: MIT
5+
6+
package organization
7+
8+
import (
9+
"code.gitea.io/gitea/models/db"
10+
11+
"xorm.io/builder"
12+
)
13+
14+
// ResultTimesByRepos is a struct for DB query results
15+
type ResultTimesByRepos struct {
16+
Name string
17+
SumTime int64
18+
}
19+
20+
// ResultTimesByMilestones is a struct for DB query results
21+
type ResultTimesByMilestones struct {
22+
RepoName string
23+
Name string
24+
ID string
25+
SumTime int64
26+
HideRepoName bool
27+
}
28+
29+
// ResultTimesByMembers is a struct for DB query results
30+
type ResultTimesByMembers struct {
31+
Name string
32+
SumTime int64
33+
}
34+
35+
// GetTimesByRepos fetches data from DB to serve TimesByRepos.
36+
func (org *Organization) GetTimesByRepos(unixfrom, unixto int64) (results []ResultTimesByRepos, err error) {
37+
// Get the data from the DB
38+
err = db.GetEngine(db.DefaultContext).
39+
Select("repository.name, SUM(tracked_time.time) AS sum_time").
40+
Table("tracked_time").
41+
Join("INNER", "issue", "tracked_time.issue_id = issue.id").
42+
Join("INNER", "repository", "issue.repo_id = repository.id").
43+
Where(builder.Eq{"repository.owner_id": org.ID}).
44+
And(builder.Eq{"tracked_time.deleted": false}).
45+
And(builder.Gte{"tracked_time.created_unix": unixfrom}).
46+
And(builder.Lte{"tracked_time.created_unix": unixto}).
47+
GroupBy("repository.id").
48+
OrderBy("repository.name").
49+
Find(&results)
50+
return results, err
51+
}
52+
53+
// GetTimesByMilestones gets the actual data from the DB to serve TimesByMilestones.
54+
func (org *Organization) GetTimesByMilestones(unixfrom, unixto int64) (results []ResultTimesByMilestones, err error) {
55+
err = db.GetEngine(db.DefaultContext).
56+
Select("repository.name AS repo_name, milestone.name, milestone.id, SUM(tracked_time.time) AS sum_time").
57+
Table("tracked_time").
58+
Join("INNER", "issue", "tracked_time.issue_id = issue.id").
59+
Join("INNER", "repository", "issue.repo_id = repository.id").
60+
Join("LEFT", "milestone", "issue.milestone_id = milestone.id").
61+
Where(builder.Eq{"repository.owner_id": org.ID}).
62+
And(builder.Eq{"tracked_time.deleted": false}).
63+
And(builder.Gte{"tracked_time.created_unix": unixfrom}).
64+
And(builder.Lte{"tracked_time.created_unix": unixto}).
65+
GroupBy("repository.id, milestone.id").
66+
OrderBy("repository.name, milestone.deadline_unix, milestone.id").
67+
Find(&results)
68+
69+
return results, err
70+
}
71+
72+
// getTimesByMembers gets the actual data from the DB to serve TimesByMembers.
73+
func (org *Organization) GetTimesByMembers(unixfrom, unixto int64) (results []ResultTimesByMembers, err error) {
74+
err = db.GetEngine(db.DefaultContext).
75+
Select("user.name, SUM(tracked_time.time) AS sum_time").
76+
Table("tracked_time").
77+
Join("INNER", "issue", "tracked_time.issue_id = issue.id").
78+
Join("INNER", "repository", "issue.repo_id = repository.id").
79+
Join("INNER", "user", "tracked_time.user_id = user.id").
80+
Where(builder.Eq{"repository.owner_id": org.ID}).
81+
And(builder.Eq{"tracked_time.deleted": false}).
82+
And(builder.Gte{"tracked_time.created_unix": unixfrom}).
83+
And(builder.Lte{"tracked_time.created_unix": unixto}).
84+
GroupBy("user.id").
85+
OrderBy("sum_time DESC").
86+
Find(&results)
87+
return results, err
88+
}

0 commit comments

Comments
 (0)