From f7427d8b50f89e19af728ba4aa8643deacc3d63c Mon Sep 17 00:00:00 2001 From: stuzer05 Date: Fri, 24 Feb 2023 17:22:59 +0200 Subject: [PATCH] Commit --- models/issues/comment.go | 169 +++++++++++------- models/issues/issue.go | 99 ++++++++-- modules/structs/issue.go | 36 ++-- options/locale/locale_en-US.ini | 3 +- routers/web/repo/issue.go | 7 +- routers/web/repo/issue_timetrack.go | 4 +- services/forms/repo_form.go | 3 +- services/issue/issue.go | 9 +- .../repo/issue/view_content/comments.tmpl | 5 +- .../repo/issue/view_content/sidebar.tmpl | 10 +- web_src/js/features/repo-issue.js | 6 +- 11 files changed, 230 insertions(+), 121 deletions(-) diff --git a/models/issues/comment.go b/models/issues/comment.go index 6e00950848..e96695d623 100644 --- a/models/issues/comment.go +++ b/models/issues/comment.go @@ -6,9 +6,12 @@ package issues import ( + "code.gitea.io/gitea/modules/translation" "context" "fmt" + "math" "strconv" + "strings" "unicode/utf8" "code.gitea.io/gitea/models/db" @@ -305,8 +308,7 @@ type Comment struct { CommitsNum int64 `xorm:"-"` IsForcePush bool `xorm:"-"` - TimeEstimateHours int - TimeEstimateMinutes int + TimeEstimate int64 } func init() { @@ -793,40 +795,39 @@ func CreateComment(ctx context.Context, opts *CreateCommentOptions) (_ *Comment, } comment := &Comment{ - Type: opts.Type, - PosterID: opts.Doer.ID, - Poster: opts.Doer, - IssueID: opts.Issue.ID, - LabelID: LabelID, - OldMilestoneID: opts.OldMilestoneID, - MilestoneID: opts.MilestoneID, - OldProjectID: opts.OldProjectID, - ProjectID: opts.ProjectID, - TimeID: opts.TimeID, - RemovedAssignee: opts.RemovedAssignee, - AssigneeID: opts.AssigneeID, - AssigneeTeamID: opts.AssigneeTeamID, - CommitID: opts.CommitID, - CommitSHA: opts.CommitSHA, - Line: opts.LineNum, - Content: opts.Content, - OldTitle: opts.OldTitle, - NewTitle: opts.NewTitle, - OldRef: opts.OldRef, - NewRef: opts.NewRef, - DependentIssueID: opts.DependentIssueID, - TreePath: opts.TreePath, - ReviewID: opts.ReviewID, - Patch: opts.Patch, - RefRepoID: opts.RefRepoID, - RefIssueID: opts.RefIssueID, - RefCommentID: opts.RefCommentID, - RefAction: opts.RefAction, - RefIsPull: opts.RefIsPull, - IsForcePush: opts.IsForcePush, - Invalidated: opts.Invalidated, - TimeEstimateHours: opts.TimeEstimateHours, - TimeEstimateMinutes: opts.TimeEstimateMinutes, + Type: opts.Type, + PosterID: opts.Doer.ID, + Poster: opts.Doer, + IssueID: opts.Issue.ID, + LabelID: LabelID, + OldMilestoneID: opts.OldMilestoneID, + MilestoneID: opts.MilestoneID, + OldProjectID: opts.OldProjectID, + ProjectID: opts.ProjectID, + TimeID: opts.TimeID, + RemovedAssignee: opts.RemovedAssignee, + AssigneeID: opts.AssigneeID, + AssigneeTeamID: opts.AssigneeTeamID, + CommitID: opts.CommitID, + CommitSHA: opts.CommitSHA, + Line: opts.LineNum, + Content: opts.Content, + OldTitle: opts.OldTitle, + NewTitle: opts.NewTitle, + OldRef: opts.OldRef, + NewRef: opts.NewRef, + DependentIssueID: opts.DependentIssueID, + TreePath: opts.TreePath, + ReviewID: opts.ReviewID, + Patch: opts.Patch, + RefRepoID: opts.RefRepoID, + RefIssueID: opts.RefIssueID, + RefCommentID: opts.RefCommentID, + RefAction: opts.RefAction, + RefIsPull: opts.RefIsPull, + IsForcePush: opts.IsForcePush, + Invalidated: opts.Invalidated, + TimeEstimate: opts.TimeEstimate, } if _, err = e.Insert(comment); err != nil { return nil, err @@ -970,36 +971,35 @@ type CreateCommentOptions struct { Issue *Issue Label *Label - DependentIssueID int64 - OldMilestoneID int64 - MilestoneID int64 - OldProjectID int64 - ProjectID int64 - TimeID int64 - AssigneeID int64 - AssigneeTeamID int64 - RemovedAssignee bool - OldTitle string - NewTitle string - OldRef string - NewRef string - CommitID int64 - CommitSHA string - Patch string - LineNum int64 - TreePath string - ReviewID int64 - Content string - Attachments []string // UUIDs of attachments - RefRepoID int64 - RefIssueID int64 - RefCommentID int64 - RefAction references.XRefAction - RefIsPull bool - IsForcePush bool - Invalidated bool - TimeEstimateHours int - TimeEstimateMinutes int + DependentIssueID int64 + OldMilestoneID int64 + MilestoneID int64 + OldProjectID int64 + ProjectID int64 + TimeID int64 + AssigneeID int64 + AssigneeTeamID int64 + RemovedAssignee bool + OldTitle string + NewTitle string + OldRef string + NewRef string + CommitID int64 + CommitSHA string + Patch string + LineNum int64 + TreePath string + ReviewID int64 + Content string + Attachments []string // UUIDs of attachments + RefRepoID int64 + RefIssueID int64 + RefCommentID int64 + RefAction references.XRefAction + RefIsPull bool + IsForcePush bool + Invalidated bool + TimeEstimate int64 } // GetCommentByID returns the comment by given ID. @@ -1259,3 +1259,40 @@ func FixCommentTypeLabelWithOutsideLabels(ctx context.Context) (int64, error) { func (c *Comment) HasOriginalAuthor() bool { return c.OriginalAuthor != "" && c.OriginalAuthorID != 0 } + +// TimeEstimateStr returns formatted time estimate string from seconds (e.g. "2 weeks 4 days 12 hours 5 minutes") +func (c *Comment) TimeEstimateToStrTranslated(lang translation.Locale) string { + var timeParts []string + + timeSeconds := float64(c.TimeEstimate) + + // Format weeks + weeks := math.Floor(timeSeconds / (60 * 60 * 24 * 7)) + if weeks > 0 { + timeParts = append(timeParts, lang.Tr("tool.hours", int64(weeks))) + } + timeSeconds -= weeks * (60 * 60 * 24 * 7) + + // Format days + days := math.Floor(timeSeconds / (60 * 60 * 24)) + if days > 0 { + timeParts = append(timeParts, lang.Tr("tool.days", int64(days))) + } + timeSeconds -= days * (60 * 60 * 24) + + // Format hours + hours := math.Floor(timeSeconds / (60 * 60)) + if hours > 0 { + timeParts = append(timeParts, lang.Tr("tool.hours", int64(hours))) + } + timeSeconds -= hours * (60 * 60) + + // Format minutes + minutes := math.Floor(timeSeconds / (60)) + if minutes > 0 { + timeParts = append(timeParts, lang.Tr("tool.minutes", int64(minutes))) + } + timeSeconds -= minutes * (60) + + return strings.Join(timeParts[:], " ") +} diff --git a/models/issues/issue.go b/models/issues/issue.go index a31f221e1a..6ec042000f 100644 --- a/models/issues/issue.go +++ b/models/issues/issue.go @@ -7,8 +7,10 @@ package issues import ( "context" "fmt" + "math" "regexp" "sort" + "strconv" "strings" "code.gitea.io/gitea/models/db" @@ -148,8 +150,7 @@ type Issue struct { ShowRole RoleDescriptor `xorm:"-"` // Time estimate - TimeEstimateHours int - TimeEstimateMinutes int + TimeEstimate int64 } var ( @@ -779,14 +780,14 @@ func ChangeIssueTitle(issue *Issue, doer *user_model.User, oldTitle string) (err } // ChangeIssueTimeEstimate changes the plan time of this issue, as the given user. -func ChangeIssueTimeEstimate(issue *Issue, doer *user_model.User, timeEstimateHours, timeEstimateMinutes int) (err error) { +func ChangeIssueTimeEstimate(issue *Issue, doer *user_model.User, timeEstimate int64) (err error) { ctx, committer, err := db.TxContext(db.DefaultContext) if err != nil { return err } defer committer.Close() - if err = UpdateIssueCols(ctx, &Issue{ID: issue.ID, TimeEstimateHours: timeEstimateHours, TimeEstimateMinutes: timeEstimateMinutes}, "time_estimate_hours", "time_estimate_minutes"); err != nil { + if err = UpdateIssueCols(ctx, &Issue{ID: issue.ID, TimeEstimate: timeEstimate}, "time_estimate"); err != nil { return fmt.Errorf("updateIssueCols: %w", err) } @@ -795,12 +796,12 @@ func ChangeIssueTimeEstimate(issue *Issue, doer *user_model.User, timeEstimateHo } opts := &CreateCommentOptions{ - Type: CommentTypeChangeTimeEstimate, - Doer: doer, - Repo: issue.Repo, - Issue: issue, - TimeEstimateHours: timeEstimateHours, - TimeEstimateMinutes: timeEstimateMinutes, + Type: CommentTypeChangeTimeEstimate, + Doer: doer, + Repo: issue.Repo, + Issue: issue, + Content: util.SecToTime(timeEstimate), + TimeEstimate: timeEstimate, } if _, err = CreateComment(ctx, opts); err != nil { return fmt.Errorf("createComment: %w", err) @@ -2472,3 +2473,81 @@ func DeleteOrphanedIssues(ctx context.Context) error { func (issue *Issue) HasOriginalAuthor() bool { return issue.OriginalAuthor != "" && issue.OriginalAuthorID != 0 } + +// TimeEstimateFromStr returns time estimate in seconds from formatted string +func (issue *Issue) TimeEstimateFromStr(timeStr string) int64 { + timeTotal := 0 + + // Time match regex + rWeeks, _ := regexp.Compile("([\\d]+)w") + rDays, _ := regexp.Compile("([\\d]+)d") + rHours, _ := regexp.Compile("([\\d]+)h") + rMinutes, _ := regexp.Compile("([\\d]+)m") + + // Find time weeks + timeStrMatches := rWeeks.FindStringSubmatch(timeStr) + if len(timeStrMatches) > 0 { + raw, _ := strconv.Atoi(timeStrMatches[1]) + timeTotal += raw * (60 * 60 * 24 * 7) + } + + // Find time days + timeStrMatches = rDays.FindStringSubmatch(timeStr) + if len(timeStrMatches) > 0 { + raw, _ := strconv.Atoi(timeStrMatches[1]) + timeTotal += raw * (60 * 60 * 24) + } + + // Find time hours + timeStrMatches = rHours.FindStringSubmatch(timeStr) + if len(timeStrMatches) > 0 { + raw, _ := strconv.Atoi(timeStrMatches[1]) + timeTotal += raw * (60 * 60) + } + + // Find time minutes + timeStrMatches = rMinutes.FindStringSubmatch(timeStr) + if len(timeStrMatches) > 0 { + raw, _ := strconv.Atoi(timeStrMatches[1]) + timeTotal += raw * (60) + } + + return int64(timeTotal) +} + +// TimeEstimateStr returns formatted time estimate string from seconds (e.g. "2w 4d 12h 5m") +func (issue *Issue) TimeEstimateToStr() string { + var timeParts []string + + timeSeconds := float64(issue.TimeEstimate) + + // Format weeks + weeks := math.Floor(timeSeconds / (60 * 60 * 24 * 7)) + if weeks > 0 { + timeParts = append(timeParts, fmt.Sprintf("%dw", int64(weeks))) + } + timeSeconds -= weeks * (60 * 60 * 24 * 7) + + // Format days + days := math.Floor(timeSeconds / (60 * 60 * 24)) + if days > 0 { + timeParts = append(timeParts, fmt.Sprintf("%dd", int64(days))) + } + timeSeconds -= days * (60 * 60 * 24) + + // Format hours + hours := math.Floor(timeSeconds / (60 * 60)) + if hours > 0 { + timeParts = append(timeParts, fmt.Sprintf("%dh", int64(hours))) + } + timeSeconds -= hours * (60 * 60) + + // Format minutes + minutes := math.Floor(timeSeconds / (60)) + if minutes > 0 { + timeParts = append(timeParts, fmt.Sprintf("%dm", int64(minutes))) + } + timeSeconds -= minutes * (60) + + return strings.Join(timeParts[:], " ") +} diff --git a/modules/structs/issue.go b/modules/structs/issue.go index 51eb9e1314..d06abcc3c3 100644 --- a/modules/structs/issue.go +++ b/modules/structs/issue.go @@ -41,21 +41,20 @@ type RepositoryMeta struct { // Issue represents an issue in a repository // swagger:model type Issue struct { - ID int64 `json:"id"` - URL string `json:"url"` - HTMLURL string `json:"html_url"` - Index int64 `json:"number"` - Poster *User `json:"user"` - OriginalAuthor string `json:"original_author"` - OriginalAuthorID int64 `json:"original_author_id"` - Title string `json:"title"` - Body string `json:"body"` - Ref string `json:"ref"` - Attachments []*Attachment `json:"assets"` - Labels []*Label `json:"labels"` - Milestone *Milestone `json:"milestone"` - TimeEstimateHours int64 `json:"time_estimate_hours"` - TimeEstimateMinutes int64 `json:"time_estimate_minutes"` + ID int64 `json:"id"` + URL string `json:"url"` + HTMLURL string `json:"html_url"` + Index int64 `json:"number"` + Poster *User `json:"user"` + OriginalAuthor string `json:"original_author"` + OriginalAuthorID int64 `json:"original_author_id"` + Title string `json:"title"` + Body string `json:"body"` + Ref string `json:"ref"` + Attachments []*Attachment `json:"assets"` + Labels []*Label `json:"labels"` + Milestone *Milestone `json:"milestone"` + TimeEstimate int `json:"time_estimate"` // deprecated Assignee *User `json:"assignee"` Assignees []*User `json:"assignees"` @@ -108,10 +107,9 @@ type EditIssueOption struct { Milestone *int64 `json:"milestone"` State *string `json:"state"` // swagger:strfmt date-time - Deadline *time.Time `json:"due_date"` - RemoveDeadline *bool `json:"unset_due_date"` - TimeEstimateHours int `json:"time_estimate_hours"` - TimeEstimateMinutes int `json:"time_estimate_minutes"` + Deadline *time.Time `json:"due_date"` + RemoveDeadline *bool `json:"unset_due_date"` + TimeEstimate *string `json:"time_estimate"` } // EditDeadlineOption options for creating a deadline diff --git a/options/locale/locale_en-US.ini b/options/locale/locale_en-US.ini index 4a7307553f..9edd394e77 100644 --- a/options/locale/locale_en-US.ini +++ b/options/locale/locale_en-US.ini @@ -1302,7 +1302,8 @@ issues.remove_assignee_at = `was unassigned by %s %s` issues.remove_self_assignment = `removed their assignment %s` issues.change_title_at = `changed title from %s to %s %s` issues.time_estimate = `Time Estimate` -issues.change_time_estimate_at = `changed time estimate to %d hour %d minutes %s` +issues.add_time_estimate = `3w 4d 12h` +issues.change_time_estimate_at = `changed time estimate to %s %s` issues.remove_time_estimate = `removed time estimate %s` issues.change_ref_at = `changed reference from %s to %s %s` issues.remove_ref_at = `removed reference %s %s` diff --git a/routers/web/repo/issue.go b/routers/web/repo/issue.go index 158a831ddb..f75f57e6df 100644 --- a/routers/web/repo/issue.go +++ b/routers/web/repo/issue.go @@ -1982,17 +1982,16 @@ func UpdateIssueTimeEstimate(ctx *context.Context) { return } - timeEstimateHours := ctx.FormInt("time_estimate_hours") - timeEstimateMinutes := ctx.FormInt("time_estimate_minutes") + total := issue.TimeEstimateFromStr(ctx.FormString("time_estimate")) - if issue.TimeEstimateHours == timeEstimateHours && issue.TimeEstimateMinutes == timeEstimateMinutes { + if issue.TimeEstimate == total { ctx.JSON(http.StatusOK, map[string]interface{}{ "status": "ok", }) return } - if err := issue_service.ChangeTimeEstimate(issue, ctx.Doer, timeEstimateHours, timeEstimateMinutes); err != nil { + if err := issue_service.ChangeTimeEstimate(issue, ctx.Doer, total); err != nil { ctx.ServerError("ChangeTimeEstimate", err) return } diff --git a/routers/web/repo/issue_timetrack.go b/routers/web/repo/issue_timetrack.go index 7dc7d0797d..64426fbb1d 100644 --- a/routers/web/repo/issue_timetrack.go +++ b/routers/web/repo/issue_timetrack.go @@ -34,7 +34,7 @@ func AddTimeManually(c *context.Context) { return } - total := time.Duration(form.Hours)*time.Hour + time.Duration(form.Minutes)*time.Minute + total := issue.TimeEstimateFromStr(form.TimeString) if total <= 0 { c.Flash.Error(c.Tr("repo.issues.add_time_sum_to_small")) @@ -42,7 +42,7 @@ func AddTimeManually(c *context.Context) { return } - if _, err := issues_model.AddTime(c.Doer, issue, int64(total.Seconds()), time.Now()); err != nil { + if _, err := issues_model.AddTime(c.Doer, issue, total, time.Now()); err != nil { c.ServerError("AddTime", err) return } diff --git a/services/forms/repo_form.go b/services/forms/repo_form.go index e9645b5ab7..ac328464a6 100644 --- a/services/forms/repo_form.go +++ b/services/forms/repo_form.go @@ -872,8 +872,7 @@ func (f *DeleteRepoFileForm) Validate(req *http.Request, errs binding.Errors) bi // AddTimeManuallyForm form that adds spent time manually. type AddTimeManuallyForm struct { - Hours int `binding:"Range(0,1000)"` - Minutes int `binding:"Range(0,1000)"` + TimeString string } // Validate validates the fields diff --git a/services/issue/issue.go b/services/issue/issue.go index 49c5354eca..fc1308f8dc 100644 --- a/services/issue/issue.go +++ b/services/issue/issue.go @@ -61,12 +61,11 @@ func ChangeTitle(issue *issues_model.Issue, doer *user_model.User, title string) return nil } -// ChangeTitle changes the title of this issue, as the given user. -func ChangeTimeEstimate(issue *issues_model.Issue, doer *user_model.User, timeEstimateHours, timeEstimateMinutes int) (err error) { - issue.TimeEstimateHours = timeEstimateHours - issue.TimeEstimateMinutes = timeEstimateMinutes +// ChangeTimeEstimate changes the time estimate of this issue, as the given user. +func ChangeTimeEstimate(issue *issues_model.Issue, doer *user_model.User, timeEstimate int64) (err error) { + issue.TimeEstimate = timeEstimate - if err = issues_model.ChangeIssueTimeEstimate(issue, doer, timeEstimateHours, timeEstimateMinutes); err != nil { + if err = issues_model.ChangeIssueTimeEstimate(issue, doer, timeEstimate); err != nil { return } diff --git a/templates/repo/issue/view_content/comments.tmpl b/templates/repo/issue/view_content/comments.tmpl index fe9446bcd5..52751a4616 100644 --- a/templates/repo/issue/view_content/comments.tmpl +++ b/templates/repo/issue/view_content/comments.tmpl @@ -799,10 +799,11 @@ {{template "shared/user/avatarlink" Dict "Context" $.Context "user" .Poster}} {{template "shared/user/authorlink" .Poster}} - {{if and (eq .TimeEstimateHours 0) (eq .TimeEstimateMinutes 0)}} + {{if and (eq .TimeEstimate 0)}} {{$.locale.Tr "repo.issues.remove_time_estimate" $createdStr | Safe}} {{else}} - {{$.locale.Tr "repo.issues.change_time_estimate_at" (.TimeEstimateHours) (.TimeEstimateMinutes) $createdStr | Safe}} + {{$timeStr := .TimeEstimateToStrTranslated $.locale}} + {{$.locale.Tr "repo.issues.change_time_estimate_at" $timeStr $createdStr | Safe}} {{end}} diff --git a/templates/repo/issue/view_content/sidebar.tmpl b/templates/repo/issue/view_content/sidebar.tmpl index c45921f37d..14e6b09b89 100644 --- a/templates/repo/issue/view_content/sidebar.tmpl +++ b/templates/repo/issue/view_content/sidebar.tmpl @@ -364,9 +364,8 @@
{{$.CsrfTokenHtml}} -
- - +
+