-
-
Notifications
You must be signed in to change notification settings - Fork 5.6k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Issue time estimate, meaningful time tracking (#23113)
Redesign the time tracker side bar, and add "time estimate" support (in "1d 2m" format) Closes #23112 --------- Co-authored-by: stuzer05 <[email protected]> Co-authored-by: Yarden Shoham <[email protected]> Co-authored-by: silverwind <[email protected]> Co-authored-by: 6543 <[email protected]> Co-authored-by: wxiaoguang <[email protected]>
- Loading branch information
1 parent
c5422fa
commit 936665b
Showing
21 changed files
with
390 additions
and
164 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
// Copyright 2024 The Gitea Authors. All rights reserved. | ||
// SPDX-License-Identifier: MIT | ||
|
||
package v1_23 //nolint | ||
|
||
import ( | ||
"xorm.io/xorm" | ||
) | ||
|
||
func AddTimeEstimateColumnToIssueTable(x *xorm.Engine) error { | ||
type Issue struct { | ||
TimeEstimate int64 `xorm:"NOT NULL DEFAULT 0"` | ||
} | ||
|
||
return x.Sync(new(Issue)) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,85 @@ | ||
// Copyright 2024 Gitea. All rights reserved. | ||
// SPDX-License-Identifier: MIT | ||
|
||
package util | ||
|
||
import ( | ||
"fmt" | ||
"regexp" | ||
"strconv" | ||
"strings" | ||
"sync" | ||
) | ||
|
||
type timeStrGlobalVarsType struct { | ||
units []struct { | ||
name string | ||
num int64 | ||
} | ||
re *regexp.Regexp | ||
} | ||
|
||
// When tracking working time, only hour/minute/second units are accurate and could be used. | ||
// For other units like "day", it depends on "how many working hours in a day": 6 or 7 or 8? | ||
// So at the moment, we only support hour/minute/second units. | ||
// In the future, it could be some configurable options to help users | ||
// to convert the working time to different units. | ||
|
||
var timeStrGlobalVars = sync.OnceValue[*timeStrGlobalVarsType](func() *timeStrGlobalVarsType { | ||
v := &timeStrGlobalVarsType{} | ||
v.re = regexp.MustCompile(`(?i)(\d+)\s*([hms])`) | ||
v.units = []struct { | ||
name string | ||
num int64 | ||
}{ | ||
{"h", 60 * 60}, | ||
{"m", 60}, | ||
{"s", 1}, | ||
} | ||
return v | ||
}) | ||
|
||
func TimeEstimateParse(timeStr string) (int64, error) { | ||
if timeStr == "" { | ||
return 0, nil | ||
} | ||
var total int64 | ||
matches := timeStrGlobalVars().re.FindAllStringSubmatchIndex(timeStr, -1) | ||
if len(matches) == 0 { | ||
return 0, fmt.Errorf("invalid time string: %s", timeStr) | ||
} | ||
if matches[0][0] != 0 || matches[len(matches)-1][1] != len(timeStr) { | ||
return 0, fmt.Errorf("invalid time string: %s", timeStr) | ||
} | ||
for _, match := range matches { | ||
amount, err := strconv.ParseInt(timeStr[match[2]:match[3]], 10, 64) | ||
if err != nil { | ||
return 0, fmt.Errorf("invalid time string: %v", err) | ||
} | ||
unit := timeStr[match[4]:match[5]] | ||
found := false | ||
for _, u := range timeStrGlobalVars().units { | ||
if strings.ToLower(unit) == u.name { | ||
total += amount * u.num | ||
found = true | ||
break | ||
} | ||
} | ||
if !found { | ||
return 0, fmt.Errorf("invalid time unit: %s", unit) | ||
} | ||
} | ||
return total, nil | ||
} | ||
|
||
func TimeEstimateString(amount int64) string { | ||
var timeParts []string | ||
for _, u := range timeStrGlobalVars().units { | ||
if amount >= u.num { | ||
num := amount / u.num | ||
amount %= u.num | ||
timeParts = append(timeParts, fmt.Sprintf("%d%s", num, u.name)) | ||
} | ||
} | ||
return strings.Join(timeParts, " ") | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,55 @@ | ||
// Copyright 2024 Gitea. All rights reserved. | ||
// SPDX-License-Identifier: MIT | ||
|
||
package util | ||
|
||
import ( | ||
"testing" | ||
|
||
"github.com/stretchr/testify/assert" | ||
) | ||
|
||
func TestTimeStr(t *testing.T) { | ||
t.Run("Parse", func(t *testing.T) { | ||
// Test TimeEstimateParse | ||
tests := []struct { | ||
input string | ||
output int64 | ||
err bool | ||
}{ | ||
{"1h", 3600, false}, | ||
{"1m", 60, false}, | ||
{"1s", 1, false}, | ||
{"1h 1m 1s", 3600 + 60 + 1, false}, | ||
{"1d1x", 0, true}, | ||
} | ||
for _, test := range tests { | ||
t.Run(test.input, func(t *testing.T) { | ||
output, err := TimeEstimateParse(test.input) | ||
if test.err { | ||
assert.NotNil(t, err) | ||
} else { | ||
assert.Nil(t, err) | ||
} | ||
assert.Equal(t, test.output, output) | ||
}) | ||
} | ||
}) | ||
t.Run("String", func(t *testing.T) { | ||
tests := []struct { | ||
input int64 | ||
output string | ||
}{ | ||
{3600, "1h"}, | ||
{60, "1m"}, | ||
{1, "1s"}, | ||
{3600 + 1, "1h 1s"}, | ||
} | ||
for _, test := range tests { | ||
t.Run(test.output, func(t *testing.T) { | ||
output := TimeEstimateString(test.input) | ||
assert.Equal(t, test.output, output) | ||
}) | ||
} | ||
}) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.