Skip to content

Commit d869d32

Browse files
committed
Merge branch 'main' into lunny/issue_dev
2 parents 478fbd5 + 5cada75 commit d869d32

File tree

30 files changed

+1382
-984
lines changed

30 files changed

+1382
-984
lines changed

.eslintrc.yaml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -524,6 +524,7 @@ rules:
524524
no-jquery/no-data: [0]
525525
no-jquery/no-deferred: [2]
526526
no-jquery/no-delegate: [2]
527+
no-jquery/no-done-fail: [2]
527528
no-jquery/no-each-collection: [0]
528529
no-jquery/no-each-util: [0]
529530
no-jquery/no-each: [0]
@@ -538,6 +539,7 @@ rules:
538539
no-jquery/no-find-util: [2]
539540
no-jquery/no-find: [0]
540541
no-jquery/no-fx-interval: [2]
542+
no-jquery/no-fx: [2]
541543
no-jquery/no-global-eval: [2]
542544
no-jquery/no-global-selector: [0]
543545
no-jquery/no-grep: [2]

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ _testmain.go
2828
*.exe
2929
*.test
3030
*.prof
31+
*.tsbuildInfo
3132

3233
*coverage.out
3334
coverage.all

models/issues/comment.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,8 @@ const (
114114

115115
CommentTypePin // 36 pin Issue
116116
CommentTypeUnpin // 37 unpin Issue
117+
118+
CommentTypeChangeTimeEstimate // 38 Change time estimate
117119
)
118120

119121
var commentStrings = []string{
@@ -155,6 +157,7 @@ var commentStrings = []string{
155157
"pull_cancel_scheduled_merge",
156158
"pin",
157159
"unpin",
160+
"change_time_estimate",
158161
}
159162

160163
func (t CommentType) String() string {

models/issues/issue.go

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -147,6 +147,9 @@ type Issue struct {
147147

148148
// For view issue page.
149149
ShowRole RoleDescriptor `xorm:"-"`
150+
151+
// Time estimate
152+
TimeEstimate int64 `xorm:"NOT NULL DEFAULT 0"`
150153
}
151154

152155
var (
@@ -934,3 +937,28 @@ func insertIssue(ctx context.Context, issue *Issue) error {
934937

935938
return nil
936939
}
940+
941+
// ChangeIssueTimeEstimate changes the plan time of this issue, as the given user.
942+
func ChangeIssueTimeEstimate(ctx context.Context, issue *Issue, doer *user_model.User, timeEstimate int64) error {
943+
return db.WithTx(ctx, func(ctx context.Context) error {
944+
if err := UpdateIssueCols(ctx, &Issue{ID: issue.ID, TimeEstimate: timeEstimate}, "time_estimate"); err != nil {
945+
return fmt.Errorf("updateIssueCols: %w", err)
946+
}
947+
948+
if err := issue.LoadRepo(ctx); err != nil {
949+
return fmt.Errorf("loadRepo: %w", err)
950+
}
951+
952+
opts := &CreateCommentOptions{
953+
Type: CommentTypeChangeTimeEstimate,
954+
Doer: doer,
955+
Repo: issue.Repo,
956+
Issue: issue,
957+
Content: fmt.Sprintf("%d", timeEstimate),
958+
}
959+
if _, err := CreateComment(ctx, opts); err != nil {
960+
return fmt.Errorf("createComment: %w", err)
961+
}
962+
return nil
963+
})
964+
}

models/migrations/migrations.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -368,7 +368,8 @@ func prepareMigrationTasks() []*migration {
368368
newMigration(308, "Add index(user_id, is_deleted) for action table", v1_23.AddNewIndexForUserDashboard),
369369
newMigration(309, "Improve Notification table indices", v1_23.ImproveNotificationTableIndices),
370370
newMigration(310, "Add Priority to ProtectedBranch", v1_23.AddPriorityToProtectedBranch),
371-
newMigration(311, "Add table issue_dev_link", v1_23.CreateTableIssueDevLink),
371+
newMigration(311, "Add TimeEstimate to Issue table", v1_23.AddTimeEstimateColumnToIssueTable),
372+
newMigration(312, "Add table issue_dev_link", v1_23.CreateTableIssueDevLink),
372373
}
373374
return preparedMigrations
374375
}

models/migrations/v1_23/v311.go

Lines changed: 5 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -4,19 +4,13 @@
44
package v1_23 //nolint
55

66
import (
7-
"code.gitea.io/gitea/modules/timeutil"
8-
97
"xorm.io/xorm"
108
)
119

12-
func CreateTableIssueDevLink(x *xorm.Engine) error {
13-
type IssueDevLink struct {
14-
ID int64 `xorm:"pk autoincr"`
15-
IssueID int64 `xorm:"INDEX"`
16-
LinkType int
17-
LinkedRepoID int64 `xorm:"INDEX"` // it can link to self repo or other repo
18-
LinkIndex string // branch name, pull request number or commit sha
19-
CreatedUnix timeutil.TimeStamp `xorm:"INDEX created"`
10+
func AddTimeEstimateColumnToIssueTable(x *xorm.Engine) error {
11+
type Issue struct {
12+
TimeEstimate int64 `xorm:"NOT NULL DEFAULT 0"`
2013
}
21-
return x.Sync(new(IssueDevLink))
14+
15+
return x.Sync(new(Issue))
2216
}

models/migrations/v1_23/v312.go

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
// Copyright 2024 The Gitea Authors. All rights reserved.
2+
// SPDX-License-Identifier: MIT
3+
4+
package v1_23 //nolint
5+
6+
import (
7+
"code.gitea.io/gitea/modules/timeutil"
8+
9+
"xorm.io/xorm"
10+
)
11+
12+
func CreateTableIssueDevLink(x *xorm.Engine) error {
13+
type IssueDevLink struct {
14+
ID int64 `xorm:"pk autoincr"`
15+
IssueID int64 `xorm:"INDEX"`
16+
LinkType int
17+
LinkedRepoID int64 `xorm:"INDEX"` // it can link to self repo or other repo
18+
LinkIndex string // branch name, pull request number or commit sha
19+
CreatedUnix timeutil.TimeStamp `xorm:"INDEX created"`
20+
}
21+
return x.Sync(new(IssueDevLink))
22+
}

modules/templates/helper.go

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,9 @@ func NewFuncMap() template.FuncMap {
7070
"FileSize": base.FileSize,
7171
"CountFmt": base.FormatNumberSI,
7272
"Sec2Time": util.SecToTime,
73+
74+
"TimeEstimateString": timeEstimateString,
75+
7376
"LoadTimes": func(startTime time.Time) string {
7477
return fmt.Sprint(time.Since(startTime).Nanoseconds()/1e6) + "ms"
7578
},
@@ -282,6 +285,14 @@ func userThemeName(user *user_model.User) string {
282285
return setting.UI.DefaultTheme
283286
}
284287

288+
func timeEstimateString(timeSec any) string {
289+
v, _ := util.ToInt64(timeSec)
290+
if v == 0 {
291+
return ""
292+
}
293+
return util.TimeEstimateString(v)
294+
}
295+
285296
func panicIfDevOrTesting() {
286297
if !setting.IsProd || setting.IsInTesting {
287298
panic("legacy template functions are for backward compatibility only, do not use them in new code")

modules/util/time_str.go

Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
// Copyright 2024 Gitea. All rights reserved.
2+
// SPDX-License-Identifier: MIT
3+
4+
package util
5+
6+
import (
7+
"fmt"
8+
"regexp"
9+
"strconv"
10+
"strings"
11+
"sync"
12+
)
13+
14+
type timeStrGlobalVarsType struct {
15+
units []struct {
16+
name string
17+
num int64
18+
}
19+
re *regexp.Regexp
20+
}
21+
22+
// When tracking working time, only hour/minute/second units are accurate and could be used.
23+
// For other units like "day", it depends on "how many working hours in a day": 6 or 7 or 8?
24+
// So at the moment, we only support hour/minute/second units.
25+
// In the future, it could be some configurable options to help users
26+
// to convert the working time to different units.
27+
28+
var timeStrGlobalVars = sync.OnceValue[*timeStrGlobalVarsType](func() *timeStrGlobalVarsType {
29+
v := &timeStrGlobalVarsType{}
30+
v.re = regexp.MustCompile(`(?i)(\d+)\s*([hms])`)
31+
v.units = []struct {
32+
name string
33+
num int64
34+
}{
35+
{"h", 60 * 60},
36+
{"m", 60},
37+
{"s", 1},
38+
}
39+
return v
40+
})
41+
42+
func TimeEstimateParse(timeStr string) (int64, error) {
43+
if timeStr == "" {
44+
return 0, nil
45+
}
46+
var total int64
47+
matches := timeStrGlobalVars().re.FindAllStringSubmatchIndex(timeStr, -1)
48+
if len(matches) == 0 {
49+
return 0, fmt.Errorf("invalid time string: %s", timeStr)
50+
}
51+
if matches[0][0] != 0 || matches[len(matches)-1][1] != len(timeStr) {
52+
return 0, fmt.Errorf("invalid time string: %s", timeStr)
53+
}
54+
for _, match := range matches {
55+
amount, err := strconv.ParseInt(timeStr[match[2]:match[3]], 10, 64)
56+
if err != nil {
57+
return 0, fmt.Errorf("invalid time string: %v", err)
58+
}
59+
unit := timeStr[match[4]:match[5]]
60+
found := false
61+
for _, u := range timeStrGlobalVars().units {
62+
if strings.ToLower(unit) == u.name {
63+
total += amount * u.num
64+
found = true
65+
break
66+
}
67+
}
68+
if !found {
69+
return 0, fmt.Errorf("invalid time unit: %s", unit)
70+
}
71+
}
72+
return total, nil
73+
}
74+
75+
func TimeEstimateString(amount int64) string {
76+
var timeParts []string
77+
for _, u := range timeStrGlobalVars().units {
78+
if amount >= u.num {
79+
num := amount / u.num
80+
amount %= u.num
81+
timeParts = append(timeParts, fmt.Sprintf("%d%s", num, u.name))
82+
}
83+
}
84+
return strings.Join(timeParts, " ")
85+
}

modules/util/time_str_test.go

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
// Copyright 2024 Gitea. All rights reserved.
2+
// SPDX-License-Identifier: MIT
3+
4+
package util
5+
6+
import (
7+
"testing"
8+
9+
"github.com/stretchr/testify/assert"
10+
)
11+
12+
func TestTimeStr(t *testing.T) {
13+
t.Run("Parse", func(t *testing.T) {
14+
// Test TimeEstimateParse
15+
tests := []struct {
16+
input string
17+
output int64
18+
err bool
19+
}{
20+
{"1h", 3600, false},
21+
{"1m", 60, false},
22+
{"1s", 1, false},
23+
{"1h 1m 1s", 3600 + 60 + 1, false},
24+
{"1d1x", 0, true},
25+
}
26+
for _, test := range tests {
27+
t.Run(test.input, func(t *testing.T) {
28+
output, err := TimeEstimateParse(test.input)
29+
if test.err {
30+
assert.NotNil(t, err)
31+
} else {
32+
assert.Nil(t, err)
33+
}
34+
assert.Equal(t, test.output, output)
35+
})
36+
}
37+
})
38+
t.Run("String", func(t *testing.T) {
39+
tests := []struct {
40+
input int64
41+
output string
42+
}{
43+
{3600, "1h"},
44+
{60, "1m"},
45+
{1, "1s"},
46+
{3600 + 1, "1h 1s"},
47+
}
48+
for _, test := range tests {
49+
t.Run(test.output, func(t *testing.T) {
50+
output := TimeEstimateString(test.input)
51+
assert.Equal(t, test.output, output)
52+
})
53+
}
54+
})
55+
}

0 commit comments

Comments
 (0)