Вы не можете выбрать более 25 тем Темы должны начинаться с буквы или цифры, могут содержать дефисы(-) и должны содержать не более 35 символов.

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192
  1. // Copyright 2017 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. package models
  5. import (
  6. "fmt"
  7. "time"
  8. "code.gitea.io/gitea/modules/timeutil"
  9. )
  10. // Stopwatch represents a stopwatch for time tracking.
  11. type Stopwatch struct {
  12. ID int64 `xorm:"pk autoincr"`
  13. IssueID int64 `xorm:"INDEX"`
  14. UserID int64 `xorm:"INDEX"`
  15. CreatedUnix timeutil.TimeStamp `xorm:"created"`
  16. }
  17. func getStopwatch(e Engine, userID, issueID int64) (sw *Stopwatch, exists bool, err error) {
  18. sw = new(Stopwatch)
  19. exists, err = e.
  20. Where("user_id = ?", userID).
  21. And("issue_id = ?", issueID).
  22. Get(sw)
  23. return
  24. }
  25. // GetUserStopwatches return list of all stopwatches of a user
  26. func GetUserStopwatches(userID int64, listOptions ListOptions) ([]*Stopwatch, error) {
  27. sws := make([]*Stopwatch, 0, 8)
  28. sess := x.Where("stopwatch.user_id = ?", userID)
  29. if listOptions.Page != 0 {
  30. sess = listOptions.setSessionPagination(sess)
  31. }
  32. err := sess.Find(&sws)
  33. if err != nil {
  34. return nil, err
  35. }
  36. return sws, nil
  37. }
  38. // StopwatchExists returns true if the stopwatch exists
  39. func StopwatchExists(userID int64, issueID int64) bool {
  40. _, exists, _ := getStopwatch(x, userID, issueID)
  41. return exists
  42. }
  43. // HasUserStopwatch returns true if the user has a stopwatch
  44. func HasUserStopwatch(userID int64) (exists bool, sw *Stopwatch, err error) {
  45. sw = new(Stopwatch)
  46. exists, err = x.
  47. Where("user_id = ?", userID).
  48. Get(sw)
  49. return
  50. }
  51. // CreateOrStopIssueStopwatch will create or remove a stopwatch and will log it into issue's timeline.
  52. func CreateOrStopIssueStopwatch(user *User, issue *Issue) error {
  53. sw, exists, err := getStopwatch(x, user.ID, issue.ID)
  54. if err != nil {
  55. return err
  56. }
  57. if err := issue.loadRepo(x); err != nil {
  58. return err
  59. }
  60. if exists {
  61. // Create tracked time out of the time difference between start date and actual date
  62. timediff := time.Now().Unix() - int64(sw.CreatedUnix)
  63. // Create TrackedTime
  64. tt := &TrackedTime{
  65. Created: time.Now(),
  66. IssueID: issue.ID,
  67. UserID: user.ID,
  68. Time: timediff,
  69. }
  70. if _, err := x.Insert(tt); err != nil {
  71. return err
  72. }
  73. if _, err := CreateComment(&CreateCommentOptions{
  74. Doer: user,
  75. Issue: issue,
  76. Repo: issue.Repo,
  77. Content: SecToTime(timediff),
  78. Type: CommentTypeStopTracking,
  79. }); err != nil {
  80. return err
  81. }
  82. if _, err := x.Delete(sw); err != nil {
  83. return err
  84. }
  85. } else {
  86. //if another stopwatch is running: stop it
  87. exists, sw, err := HasUserStopwatch(user.ID)
  88. if err != nil {
  89. return err
  90. }
  91. if exists {
  92. issue, err := getIssueByID(x, sw.IssueID)
  93. if err != nil {
  94. return err
  95. }
  96. if err := CreateOrStopIssueStopwatch(user, issue); err != nil {
  97. return err
  98. }
  99. }
  100. // Create stopwatch
  101. sw = &Stopwatch{
  102. UserID: user.ID,
  103. IssueID: issue.ID,
  104. }
  105. if _, err := x.Insert(sw); err != nil {
  106. return err
  107. }
  108. if _, err := CreateComment(&CreateCommentOptions{
  109. Doer: user,
  110. Issue: issue,
  111. Repo: issue.Repo,
  112. Type: CommentTypeStartTracking,
  113. }); err != nil {
  114. return err
  115. }
  116. }
  117. return nil
  118. }
  119. // CancelStopwatch removes the given stopwatch and logs it into issue's timeline.
  120. func CancelStopwatch(user *User, issue *Issue) error {
  121. sw, exists, err := getStopwatch(x, user.ID, issue.ID)
  122. if err != nil {
  123. return err
  124. }
  125. if exists {
  126. if _, err := x.Delete(sw); err != nil {
  127. return err
  128. }
  129. if err := issue.loadRepo(x); err != nil {
  130. return err
  131. }
  132. if _, err := CreateComment(&CreateCommentOptions{
  133. Doer: user,
  134. Issue: issue,
  135. Repo: issue.Repo,
  136. Type: CommentTypeCancelTracking,
  137. }); err != nil {
  138. return err
  139. }
  140. }
  141. return nil
  142. }
  143. // SecToTime converts an amount of seconds to a human-readable string (example: 66s -> 1min 6s)
  144. func SecToTime(duration int64) string {
  145. seconds := duration % 60
  146. minutes := (duration / (60)) % 60
  147. hours := duration / (60 * 60)
  148. var hrs string
  149. if hours > 0 {
  150. hrs = fmt.Sprintf("%dh", hours)
  151. }
  152. if minutes > 0 {
  153. if hours == 0 {
  154. hrs = fmt.Sprintf("%dmin", minutes)
  155. } else {
  156. hrs = fmt.Sprintf("%s %dmin", hrs, minutes)
  157. }
  158. }
  159. if seconds > 0 {
  160. if hours == 0 && minutes == 0 {
  161. hrs = fmt.Sprintf("%ds", seconds)
  162. } else {
  163. hrs = fmt.Sprintf("%s %ds", hrs, seconds)
  164. }
  165. }
  166. return hrs
  167. }