A fork of Gitea (see branch `mj`) adding Majority Judgment Polls 𐄷 over Issues and Merge Requests. https://git.mieuxvoter.fr
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

557 lines
13 KiB

Shows total tracked time in issue and milestone list (#3341) * Show total tracked time in issue and milestone list Show total tracked time at issue page Signed-off-by: Jonas Franz <info@jonasfranz.software> * Optimizing TotalTimes by using SumInt Signed-off-by: Jonas Franz <info@jonasfranz.software> * Fixing wrong total times for milestones caused by a missing JOIN Adding unit tests for total times Signed-off-by: Jonas Franz <info@jonasfranz.software> * Logging error instead of ignoring it Signed-off-by: Jonas Franz <info@jonasfranz.software> * Correcting spelling mistakes Signed-off-by: Jonas Franz <info@jonasfranz.software> * Change error message to a short version Signed-off-by: Jonas Franz <info@jonasfranz.software> * Add error handling to TotalTimes Add variable for totalTimes Signed-off-by: Jonas Franz <info@jonasfranz.de> * Introduce TotalTrackedTimes as variable of issue Load TotalTrackedTimes by loading attributes of IssueList Load TotalTrackedTimes by loading attributes of single issue Add Sec2Time as helper to use it in templates Signed-off-by: Jonas Franz <info@jonasfranz.software> * Fixed test + gofmt Signed-off-by: Jonas Franz <info@jonasfranz.software> * Load TotalTrackedTimes via MilestoneList instead of single requests Signed-off-by: Jonas Franz <info@jonasfranz.software> * Add documentation for MilestoneList Signed-off-by: Jonas Franz <info@jonasfranz.software> * Add documentation for MilestoneList Signed-off-by: Jonas Franz <info@jonasfranz.software> * Fix test Signed-off-by: Jonas Franz <info@jonasfranz.software> * Change comment from SQL query to description Signed-off-by: Jonas Franz <info@jonasfranz.software> * Fix unit test by using int64 instead of int Signed-off-by: Jonas Franz <info@jonasfranz.software> * Fix unit test by using int64 instead of int Signed-off-by: Jonas Franz <info@jonasfranz.software> * Check if timetracker is enabled Signed-off-by: Jonas Franz <info@jonasfranz.software> * Fix test by enabling timetracking Signed-off-by: Jonas Franz <info@jonasfranz.de>
4 years ago
Shows total tracked time in issue and milestone list (#3341) * Show total tracked time in issue and milestone list Show total tracked time at issue page Signed-off-by: Jonas Franz <info@jonasfranz.software> * Optimizing TotalTimes by using SumInt Signed-off-by: Jonas Franz <info@jonasfranz.software> * Fixing wrong total times for milestones caused by a missing JOIN Adding unit tests for total times Signed-off-by: Jonas Franz <info@jonasfranz.software> * Logging error instead of ignoring it Signed-off-by: Jonas Franz <info@jonasfranz.software> * Correcting spelling mistakes Signed-off-by: Jonas Franz <info@jonasfranz.software> * Change error message to a short version Signed-off-by: Jonas Franz <info@jonasfranz.software> * Add error handling to TotalTimes Add variable for totalTimes Signed-off-by: Jonas Franz <info@jonasfranz.de> * Introduce TotalTrackedTimes as variable of issue Load TotalTrackedTimes by loading attributes of IssueList Load TotalTrackedTimes by loading attributes of single issue Add Sec2Time as helper to use it in templates Signed-off-by: Jonas Franz <info@jonasfranz.software> * Fixed test + gofmt Signed-off-by: Jonas Franz <info@jonasfranz.software> * Load TotalTrackedTimes via MilestoneList instead of single requests Signed-off-by: Jonas Franz <info@jonasfranz.software> * Add documentation for MilestoneList Signed-off-by: Jonas Franz <info@jonasfranz.software> * Add documentation for MilestoneList Signed-off-by: Jonas Franz <info@jonasfranz.software> * Fix test Signed-off-by: Jonas Franz <info@jonasfranz.software> * Change comment from SQL query to description Signed-off-by: Jonas Franz <info@jonasfranz.software> * Fix unit test by using int64 instead of int Signed-off-by: Jonas Franz <info@jonasfranz.software> * Fix unit test by using int64 instead of int Signed-off-by: Jonas Franz <info@jonasfranz.software> * Check if timetracker is enabled Signed-off-by: Jonas Franz <info@jonasfranz.software> * Fix test by enabling timetracking Signed-off-by: Jonas Franz <info@jonasfranz.de>
4 years ago
Shows total tracked time in issue and milestone list (#3341) * Show total tracked time in issue and milestone list Show total tracked time at issue page Signed-off-by: Jonas Franz <info@jonasfranz.software> * Optimizing TotalTimes by using SumInt Signed-off-by: Jonas Franz <info@jonasfranz.software> * Fixing wrong total times for milestones caused by a missing JOIN Adding unit tests for total times Signed-off-by: Jonas Franz <info@jonasfranz.software> * Logging error instead of ignoring it Signed-off-by: Jonas Franz <info@jonasfranz.software> * Correcting spelling mistakes Signed-off-by: Jonas Franz <info@jonasfranz.software> * Change error message to a short version Signed-off-by: Jonas Franz <info@jonasfranz.software> * Add error handling to TotalTimes Add variable for totalTimes Signed-off-by: Jonas Franz <info@jonasfranz.de> * Introduce TotalTrackedTimes as variable of issue Load TotalTrackedTimes by loading attributes of IssueList Load TotalTrackedTimes by loading attributes of single issue Add Sec2Time as helper to use it in templates Signed-off-by: Jonas Franz <info@jonasfranz.software> * Fixed test + gofmt Signed-off-by: Jonas Franz <info@jonasfranz.software> * Load TotalTrackedTimes via MilestoneList instead of single requests Signed-off-by: Jonas Franz <info@jonasfranz.software> * Add documentation for MilestoneList Signed-off-by: Jonas Franz <info@jonasfranz.software> * Add documentation for MilestoneList Signed-off-by: Jonas Franz <info@jonasfranz.software> * Fix test Signed-off-by: Jonas Franz <info@jonasfranz.software> * Change comment from SQL query to description Signed-off-by: Jonas Franz <info@jonasfranz.software> * Fix unit test by using int64 instead of int Signed-off-by: Jonas Franz <info@jonasfranz.software> * Fix unit test by using int64 instead of int Signed-off-by: Jonas Franz <info@jonasfranz.software> * Check if timetracker is enabled Signed-off-by: Jonas Franz <info@jonasfranz.software> * Fix test by enabling timetracking Signed-off-by: Jonas Franz <info@jonasfranz.de>
4 years ago
Shows total tracked time in issue and milestone list (#3341) * Show total tracked time in issue and milestone list Show total tracked time at issue page Signed-off-by: Jonas Franz <info@jonasfranz.software> * Optimizing TotalTimes by using SumInt Signed-off-by: Jonas Franz <info@jonasfranz.software> * Fixing wrong total times for milestones caused by a missing JOIN Adding unit tests for total times Signed-off-by: Jonas Franz <info@jonasfranz.software> * Logging error instead of ignoring it Signed-off-by: Jonas Franz <info@jonasfranz.software> * Correcting spelling mistakes Signed-off-by: Jonas Franz <info@jonasfranz.software> * Change error message to a short version Signed-off-by: Jonas Franz <info@jonasfranz.software> * Add error handling to TotalTimes Add variable for totalTimes Signed-off-by: Jonas Franz <info@jonasfranz.de> * Introduce TotalTrackedTimes as variable of issue Load TotalTrackedTimes by loading attributes of IssueList Load TotalTrackedTimes by loading attributes of single issue Add Sec2Time as helper to use it in templates Signed-off-by: Jonas Franz <info@jonasfranz.software> * Fixed test + gofmt Signed-off-by: Jonas Franz <info@jonasfranz.software> * Load TotalTrackedTimes via MilestoneList instead of single requests Signed-off-by: Jonas Franz <info@jonasfranz.software> * Add documentation for MilestoneList Signed-off-by: Jonas Franz <info@jonasfranz.software> * Add documentation for MilestoneList Signed-off-by: Jonas Franz <info@jonasfranz.software> * Fix test Signed-off-by: Jonas Franz <info@jonasfranz.software> * Change comment from SQL query to description Signed-off-by: Jonas Franz <info@jonasfranz.software> * Fix unit test by using int64 instead of int Signed-off-by: Jonas Franz <info@jonasfranz.software> * Fix unit test by using int64 instead of int Signed-off-by: Jonas Franz <info@jonasfranz.software> * Check if timetracker is enabled Signed-off-by: Jonas Franz <info@jonasfranz.software> * Fix test by enabling timetracking Signed-off-by: Jonas Franz <info@jonasfranz.de>
4 years ago
Shows total tracked time in issue and milestone list (#3341) * Show total tracked time in issue and milestone list Show total tracked time at issue page Signed-off-by: Jonas Franz <info@jonasfranz.software> * Optimizing TotalTimes by using SumInt Signed-off-by: Jonas Franz <info@jonasfranz.software> * Fixing wrong total times for milestones caused by a missing JOIN Adding unit tests for total times Signed-off-by: Jonas Franz <info@jonasfranz.software> * Logging error instead of ignoring it Signed-off-by: Jonas Franz <info@jonasfranz.software> * Correcting spelling mistakes Signed-off-by: Jonas Franz <info@jonasfranz.software> * Change error message to a short version Signed-off-by: Jonas Franz <info@jonasfranz.software> * Add error handling to TotalTimes Add variable for totalTimes Signed-off-by: Jonas Franz <info@jonasfranz.de> * Introduce TotalTrackedTimes as variable of issue Load TotalTrackedTimes by loading attributes of IssueList Load TotalTrackedTimes by loading attributes of single issue Add Sec2Time as helper to use it in templates Signed-off-by: Jonas Franz <info@jonasfranz.software> * Fixed test + gofmt Signed-off-by: Jonas Franz <info@jonasfranz.software> * Load TotalTrackedTimes via MilestoneList instead of single requests Signed-off-by: Jonas Franz <info@jonasfranz.software> * Add documentation for MilestoneList Signed-off-by: Jonas Franz <info@jonasfranz.software> * Add documentation for MilestoneList Signed-off-by: Jonas Franz <info@jonasfranz.software> * Fix test Signed-off-by: Jonas Franz <info@jonasfranz.software> * Change comment from SQL query to description Signed-off-by: Jonas Franz <info@jonasfranz.software> * Fix unit test by using int64 instead of int Signed-off-by: Jonas Franz <info@jonasfranz.software> * Fix unit test by using int64 instead of int Signed-off-by: Jonas Franz <info@jonasfranz.software> * Check if timetracker is enabled Signed-off-by: Jonas Franz <info@jonasfranz.software> * Fix test by enabling timetracking Signed-off-by: Jonas Franz <info@jonasfranz.de>
4 years ago
Shows total tracked time in issue and milestone list (#3341) * Show total tracked time in issue and milestone list Show total tracked time at issue page Signed-off-by: Jonas Franz <info@jonasfranz.software> * Optimizing TotalTimes by using SumInt Signed-off-by: Jonas Franz <info@jonasfranz.software> * Fixing wrong total times for milestones caused by a missing JOIN Adding unit tests for total times Signed-off-by: Jonas Franz <info@jonasfranz.software> * Logging error instead of ignoring it Signed-off-by: Jonas Franz <info@jonasfranz.software> * Correcting spelling mistakes Signed-off-by: Jonas Franz <info@jonasfranz.software> * Change error message to a short version Signed-off-by: Jonas Franz <info@jonasfranz.software> * Add error handling to TotalTimes Add variable for totalTimes Signed-off-by: Jonas Franz <info@jonasfranz.de> * Introduce TotalTrackedTimes as variable of issue Load TotalTrackedTimes by loading attributes of IssueList Load TotalTrackedTimes by loading attributes of single issue Add Sec2Time as helper to use it in templates Signed-off-by: Jonas Franz <info@jonasfranz.software> * Fixed test + gofmt Signed-off-by: Jonas Franz <info@jonasfranz.software> * Load TotalTrackedTimes via MilestoneList instead of single requests Signed-off-by: Jonas Franz <info@jonasfranz.software> * Add documentation for MilestoneList Signed-off-by: Jonas Franz <info@jonasfranz.software> * Add documentation for MilestoneList Signed-off-by: Jonas Franz <info@jonasfranz.software> * Fix test Signed-off-by: Jonas Franz <info@jonasfranz.software> * Change comment from SQL query to description Signed-off-by: Jonas Franz <info@jonasfranz.software> * Fix unit test by using int64 instead of int Signed-off-by: Jonas Franz <info@jonasfranz.software> * Fix unit test by using int64 instead of int Signed-off-by: Jonas Franz <info@jonasfranz.software> * Check if timetracker is enabled Signed-off-by: Jonas Franz <info@jonasfranz.software> * Fix test by enabling timetracking Signed-off-by: Jonas Franz <info@jonasfranz.de>
4 years ago
Shows total tracked time in issue and milestone list (#3341) * Show total tracked time in issue and milestone list Show total tracked time at issue page Signed-off-by: Jonas Franz <info@jonasfranz.software> * Optimizing TotalTimes by using SumInt Signed-off-by: Jonas Franz <info@jonasfranz.software> * Fixing wrong total times for milestones caused by a missing JOIN Adding unit tests for total times Signed-off-by: Jonas Franz <info@jonasfranz.software> * Logging error instead of ignoring it Signed-off-by: Jonas Franz <info@jonasfranz.software> * Correcting spelling mistakes Signed-off-by: Jonas Franz <info@jonasfranz.software> * Change error message to a short version Signed-off-by: Jonas Franz <info@jonasfranz.software> * Add error handling to TotalTimes Add variable for totalTimes Signed-off-by: Jonas Franz <info@jonasfranz.de> * Introduce TotalTrackedTimes as variable of issue Load TotalTrackedTimes by loading attributes of IssueList Load TotalTrackedTimes by loading attributes of single issue Add Sec2Time as helper to use it in templates Signed-off-by: Jonas Franz <info@jonasfranz.software> * Fixed test + gofmt Signed-off-by: Jonas Franz <info@jonasfranz.software> * Load TotalTrackedTimes via MilestoneList instead of single requests Signed-off-by: Jonas Franz <info@jonasfranz.software> * Add documentation for MilestoneList Signed-off-by: Jonas Franz <info@jonasfranz.software> * Add documentation for MilestoneList Signed-off-by: Jonas Franz <info@jonasfranz.software> * Fix test Signed-off-by: Jonas Franz <info@jonasfranz.software> * Change comment from SQL query to description Signed-off-by: Jonas Franz <info@jonasfranz.software> * Fix unit test by using int64 instead of int Signed-off-by: Jonas Franz <info@jonasfranz.software> * Fix unit test by using int64 instead of int Signed-off-by: Jonas Franz <info@jonasfranz.software> * Check if timetracker is enabled Signed-off-by: Jonas Franz <info@jonasfranz.software> * Fix test by enabling timetracking Signed-off-by: Jonas Franz <info@jonasfranz.de>
4 years ago
  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. "xorm.io/builder"
  8. )
  9. // IssueList defines a list of issues
  10. type IssueList []*Issue
  11. const (
  12. // default variables number on IN () in SQL
  13. defaultMaxInSize = 50
  14. )
  15. func (issues IssueList) getRepoIDs() []int64 {
  16. repoIDs := make(map[int64]struct{}, len(issues))
  17. for _, issue := range issues {
  18. if _, ok := repoIDs[issue.RepoID]; !ok {
  19. repoIDs[issue.RepoID] = struct{}{}
  20. }
  21. }
  22. return keysInt64(repoIDs)
  23. }
  24. func (issues IssueList) loadRepositories(e Engine) ([]*Repository, error) {
  25. if len(issues) == 0 {
  26. return nil, nil
  27. }
  28. repoIDs := issues.getRepoIDs()
  29. repoMaps := make(map[int64]*Repository, len(repoIDs))
  30. left := len(repoIDs)
  31. for left > 0 {
  32. limit := defaultMaxInSize
  33. if left < limit {
  34. limit = left
  35. }
  36. err := e.
  37. In("id", repoIDs[:limit]).
  38. Find(&repoMaps)
  39. if err != nil {
  40. return nil, fmt.Errorf("find repository: %v", err)
  41. }
  42. left -= limit
  43. repoIDs = repoIDs[limit:]
  44. }
  45. for _, issue := range issues {
  46. issue.Repo = repoMaps[issue.RepoID]
  47. if issue.PullRequest != nil {
  48. issue.PullRequest.BaseRepo = issue.Repo
  49. }
  50. }
  51. return valuesRepository(repoMaps), nil
  52. }
  53. // LoadRepositories loads issues' all repositories
  54. func (issues IssueList) LoadRepositories() ([]*Repository, error) {
  55. return issues.loadRepositories(x)
  56. }
  57. func (issues IssueList) getPosterIDs() []int64 {
  58. posterIDs := make(map[int64]struct{}, len(issues))
  59. for _, issue := range issues {
  60. if _, ok := posterIDs[issue.PosterID]; !ok {
  61. posterIDs[issue.PosterID] = struct{}{}
  62. }
  63. }
  64. return keysInt64(posterIDs)
  65. }
  66. func (issues IssueList) loadPosters(e Engine) error {
  67. if len(issues) == 0 {
  68. return nil
  69. }
  70. posterIDs := issues.getPosterIDs()
  71. posterMaps := make(map[int64]*User, len(posterIDs))
  72. left := len(posterIDs)
  73. for left > 0 {
  74. limit := defaultMaxInSize
  75. if left < limit {
  76. limit = left
  77. }
  78. err := e.
  79. In("id", posterIDs[:limit]).
  80. Find(&posterMaps)
  81. if err != nil {
  82. return err
  83. }
  84. left -= limit
  85. posterIDs = posterIDs[limit:]
  86. }
  87. for _, issue := range issues {
  88. if issue.PosterID <= 0 {
  89. continue
  90. }
  91. var ok bool
  92. if issue.Poster, ok = posterMaps[issue.PosterID]; !ok {
  93. issue.Poster = NewGhostUser()
  94. }
  95. }
  96. return nil
  97. }
  98. func (issues IssueList) getIssueIDs() []int64 {
  99. ids := make([]int64, 0, len(issues))
  100. for _, issue := range issues {
  101. ids = append(ids, issue.ID)
  102. }
  103. return ids
  104. }
  105. func (issues IssueList) loadLabels(e Engine) error {
  106. if len(issues) == 0 {
  107. return nil
  108. }
  109. type LabelIssue struct {
  110. Label *Label `xorm:"extends"`
  111. IssueLabel *IssueLabel `xorm:"extends"`
  112. }
  113. issueLabels := make(map[int64][]*Label, len(issues)*3)
  114. issueIDs := issues.getIssueIDs()
  115. left := len(issueIDs)
  116. for left > 0 {
  117. limit := defaultMaxInSize
  118. if left < limit {
  119. limit = left
  120. }
  121. rows, err := e.Table("label").
  122. Join("LEFT", "issue_label", "issue_label.label_id = label.id").
  123. In("issue_label.issue_id", issueIDs[:limit]).
  124. Asc("label.name").
  125. Rows(new(LabelIssue))
  126. if err != nil {
  127. return err
  128. }
  129. for rows.Next() {
  130. var labelIssue LabelIssue
  131. err = rows.Scan(&labelIssue)
  132. if err != nil {
  133. if err1 := rows.Close(); err1 != nil {
  134. return fmt.Errorf("IssueList.loadLabels: Close: %v", err1)
  135. }
  136. return err
  137. }
  138. issueLabels[labelIssue.IssueLabel.IssueID] = append(issueLabels[labelIssue.IssueLabel.IssueID], labelIssue.Label)
  139. }
  140. // When there are no rows left and we try to close it.
  141. // Since that is not relevant for us, we can safely ignore it.
  142. if err1 := rows.Close(); err1 != nil {
  143. return fmt.Errorf("IssueList.loadLabels: Close: %v", err1)
  144. }
  145. left -= limit
  146. issueIDs = issueIDs[limit:]
  147. }
  148. for _, issue := range issues {
  149. issue.Labels = issueLabels[issue.ID]
  150. }
  151. return nil
  152. }
  153. func (issues IssueList) getMilestoneIDs() []int64 {
  154. ids := make(map[int64]struct{}, len(issues))
  155. for _, issue := range issues {
  156. if _, ok := ids[issue.MilestoneID]; !ok {
  157. ids[issue.MilestoneID] = struct{}{}
  158. }
  159. }
  160. return keysInt64(ids)
  161. }
  162. func (issues IssueList) loadMilestones(e Engine) error {
  163. milestoneIDs := issues.getMilestoneIDs()
  164. if len(milestoneIDs) == 0 {
  165. return nil
  166. }
  167. milestoneMaps := make(map[int64]*Milestone, len(milestoneIDs))
  168. left := len(milestoneIDs)
  169. for left > 0 {
  170. limit := defaultMaxInSize
  171. if left < limit {
  172. limit = left
  173. }
  174. err := e.
  175. In("id", milestoneIDs[:limit]).
  176. Find(&milestoneMaps)
  177. if err != nil {
  178. return err
  179. }
  180. left -= limit
  181. milestoneIDs = milestoneIDs[limit:]
  182. }
  183. for _, issue := range issues {
  184. issue.Milestone = milestoneMaps[issue.MilestoneID]
  185. }
  186. return nil
  187. }
  188. func (issues IssueList) loadAssignees(e Engine) error {
  189. if len(issues) == 0 {
  190. return nil
  191. }
  192. type AssigneeIssue struct {
  193. IssueAssignee *IssueAssignees `xorm:"extends"`
  194. Assignee *User `xorm:"extends"`
  195. }
  196. assignees := make(map[int64][]*User, len(issues))
  197. issueIDs := issues.getIssueIDs()
  198. left := len(issueIDs)
  199. for left > 0 {
  200. limit := defaultMaxInSize
  201. if left < limit {
  202. limit = left
  203. }
  204. rows, err := e.Table("issue_assignees").
  205. Join("INNER", "`user`", "`user`.id = `issue_assignees`.assignee_id").
  206. In("`issue_assignees`.issue_id", issueIDs[:limit]).
  207. Rows(new(AssigneeIssue))
  208. if err != nil {
  209. return err
  210. }
  211. for rows.Next() {
  212. var assigneeIssue AssigneeIssue
  213. err = rows.Scan(&assigneeIssue)
  214. if err != nil {
  215. if err1 := rows.Close(); err1 != nil {
  216. return fmt.Errorf("IssueList.loadAssignees: Close: %v", err1)
  217. }
  218. return err
  219. }
  220. assignees[assigneeIssue.IssueAssignee.IssueID] = append(assignees[assigneeIssue.IssueAssignee.IssueID], assigneeIssue.Assignee)
  221. }
  222. if err1 := rows.Close(); err1 != nil {
  223. return fmt.Errorf("IssueList.loadAssignees: Close: %v", err1)
  224. }
  225. left -= limit
  226. issueIDs = issueIDs[limit:]
  227. }
  228. for _, issue := range issues {
  229. issue.Assignees = assignees[issue.ID]
  230. }
  231. return nil
  232. }
  233. func (issues IssueList) getPullIssueIDs() []int64 {
  234. ids := make([]int64, 0, len(issues))
  235. for _, issue := range issues {
  236. if issue.IsPull && issue.PullRequest == nil {
  237. ids = append(ids, issue.ID)
  238. }
  239. }
  240. return ids
  241. }
  242. func (issues IssueList) loadPullRequests(e Engine) error {
  243. issuesIDs := issues.getPullIssueIDs()
  244. if len(issuesIDs) == 0 {
  245. return nil
  246. }
  247. pullRequestMaps := make(map[int64]*PullRequest, len(issuesIDs))
  248. left := len(issuesIDs)
  249. for left > 0 {
  250. limit := defaultMaxInSize
  251. if left < limit {
  252. limit = left
  253. }
  254. rows, err := e.
  255. In("issue_id", issuesIDs[:limit]).
  256. Rows(new(PullRequest))
  257. if err != nil {
  258. return err
  259. }
  260. for rows.Next() {
  261. var pr PullRequest
  262. err = rows.Scan(&pr)
  263. if err != nil {
  264. if err1 := rows.Close(); err1 != nil {
  265. return fmt.Errorf("IssueList.loadPullRequests: Close: %v", err1)
  266. }
  267. return err
  268. }
  269. pullRequestMaps[pr.IssueID] = &pr
  270. }
  271. if err1 := rows.Close(); err1 != nil {
  272. return fmt.Errorf("IssueList.loadPullRequests: Close: %v", err1)
  273. }
  274. left -= limit
  275. issuesIDs = issuesIDs[limit:]
  276. }
  277. for _, issue := range issues {
  278. issue.PullRequest = pullRequestMaps[issue.ID]
  279. }
  280. return nil
  281. }
  282. func (issues IssueList) loadAttachments(e Engine) (err error) {
  283. if len(issues) == 0 {
  284. return nil
  285. }
  286. attachments := make(map[int64][]*Attachment, len(issues))
  287. issuesIDs := issues.getIssueIDs()
  288. left := len(issuesIDs)
  289. for left > 0 {
  290. limit := defaultMaxInSize
  291. if left < limit {
  292. limit = left
  293. }
  294. rows, err := e.Table("attachment").
  295. Join("INNER", "issue", "issue.id = attachment.issue_id").
  296. In("issue.id", issuesIDs[:limit]).
  297. Rows(new(Attachment))
  298. if err != nil {
  299. return err
  300. }
  301. for rows.Next() {
  302. var attachment Attachment
  303. err = rows.Scan(&attachment)
  304. if err != nil {
  305. if err1 := rows.Close(); err1 != nil {
  306. return fmt.Errorf("IssueList.loadAttachments: Close: %v", err1)
  307. }
  308. return err
  309. }
  310. attachments[attachment.IssueID] = append(attachments[attachment.IssueID], &attachment)
  311. }
  312. if err1 := rows.Close(); err1 != nil {
  313. return fmt.Errorf("IssueList.loadAttachments: Close: %v", err1)
  314. }
  315. left -= limit
  316. issuesIDs = issuesIDs[limit:]
  317. }
  318. for _, issue := range issues {
  319. issue.Attachments = attachments[issue.ID]
  320. }
  321. return nil
  322. }
  323. func (issues IssueList) loadComments(e Engine, cond builder.Cond) (err error) {
  324. if len(issues) == 0 {
  325. return nil
  326. }
  327. comments := make(map[int64][]*Comment, len(issues))
  328. issuesIDs := issues.getIssueIDs()
  329. left := len(issuesIDs)
  330. for left > 0 {
  331. limit := defaultMaxInSize
  332. if left < limit {
  333. limit = left
  334. }
  335. rows, err := e.Table("comment").
  336. Join("INNER", "issue", "issue.id = comment.issue_id").
  337. In("issue.id", issuesIDs[:limit]).
  338. Where(cond).
  339. Rows(new(Comment))
  340. if err != nil {
  341. return err
  342. }
  343. for rows.Next() {
  344. var comment Comment
  345. err = rows.Scan(&comment)
  346. if err != nil {
  347. if err1 := rows.Close(); err1 != nil {
  348. return fmt.Errorf("IssueList.loadComments: Close: %v", err1)
  349. }
  350. return err
  351. }
  352. comments[comment.IssueID] = append(comments[comment.IssueID], &comment)
  353. }
  354. if err1 := rows.Close(); err1 != nil {
  355. return fmt.Errorf("IssueList.loadComments: Close: %v", err1)
  356. }
  357. left -= limit
  358. issuesIDs = issuesIDs[limit:]
  359. }
  360. for _, issue := range issues {
  361. issue.Comments = comments[issue.ID]
  362. }
  363. return nil
  364. }
  365. func (issues IssueList) loadTotalTrackedTimes(e Engine) (err error) {
  366. type totalTimesByIssue struct {
  367. IssueID int64
  368. Time int64
  369. }
  370. if len(issues) == 0 {
  371. return nil
  372. }
  373. trackedTimes := make(map[int64]int64, len(issues))
  374. ids := make([]int64, 0, len(issues))
  375. for _, issue := range issues {
  376. if issue.Repo.IsTimetrackerEnabled() {
  377. ids = append(ids, issue.ID)
  378. }
  379. }
  380. left := len(ids)
  381. for left > 0 {
  382. limit := defaultMaxInSize
  383. if left < limit {
  384. limit = left
  385. }
  386. // select issue_id, sum(time) from tracked_time where issue_id in (<issue ids in current page>) group by issue_id
  387. rows, err := e.Table("tracked_time").
  388. Where("deleted = ?", false).
  389. Select("issue_id, sum(time) as time").
  390. In("issue_id", ids[:limit]).
  391. GroupBy("issue_id").
  392. Rows(new(totalTimesByIssue))
  393. if err != nil {
  394. return err
  395. }
  396. for rows.Next() {
  397. var totalTime totalTimesByIssue
  398. err = rows.Scan(&totalTime)
  399. if err != nil {
  400. if err1 := rows.Close(); err1 != nil {
  401. return fmt.Errorf("IssueList.loadTotalTrackedTimes: Close: %v", err1)
  402. }
  403. return err
  404. }
  405. trackedTimes[totalTime.IssueID] = totalTime.Time
  406. }
  407. if err1 := rows.Close(); err1 != nil {
  408. return fmt.Errorf("IssueList.loadTotalTrackedTimes: Close: %v", err1)
  409. }
  410. left -= limit
  411. ids = ids[limit:]
  412. }
  413. for _, issue := range issues {
  414. issue.TotalTrackedTime = trackedTimes[issue.ID]
  415. }
  416. return nil
  417. }
  418. // loadAttributes loads all attributes, expect for attachments and comments
  419. func (issues IssueList) loadAttributes(e Engine) error {
  420. if _, err := issues.loadRepositories(e); err != nil {
  421. return fmt.Errorf("issue.loadAttributes: loadRepositories: %v", err)
  422. }
  423. if err := issues.loadPosters(e); err != nil {
  424. return fmt.Errorf("issue.loadAttributes: loadPosters: %v", err)
  425. }
  426. if err := issues.loadLabels(e); err != nil {
  427. return fmt.Errorf("issue.loadAttributes: loadLabels: %v", err)
  428. }
  429. if err := issues.loadMilestones(e); err != nil {
  430. return fmt.Errorf("issue.loadAttributes: loadMilestones: %v", err)
  431. }
  432. if err := issues.loadAssignees(e); err != nil {
  433. return fmt.Errorf("issue.loadAttributes: loadAssignees: %v", err)
  434. }
  435. if err := issues.loadPullRequests(e); err != nil {
  436. return fmt.Errorf("issue.loadAttributes: loadPullRequests: %v", err)
  437. }
  438. if err := issues.loadTotalTrackedTimes(e); err != nil {
  439. return fmt.Errorf("issue.loadAttributes: loadTotalTrackedTimes: %v", err)
  440. }
  441. return nil
  442. }
  443. // LoadAttributes loads attributes of the issues, except for attachments and
  444. // comments
  445. func (issues IssueList) LoadAttributes() error {
  446. return issues.loadAttributes(x)
  447. }
  448. // LoadAttachments loads attachments
  449. func (issues IssueList) LoadAttachments() error {
  450. return issues.loadAttachments(x)
  451. }
  452. // LoadComments loads comments
  453. func (issues IssueList) LoadComments() error {
  454. return issues.loadComments(x, builder.NewCond())
  455. }
  456. // LoadDiscussComments loads discuss comments
  457. func (issues IssueList) LoadDiscussComments() error {
  458. return issues.loadComments(x, builder.Eq{"comment.type": CommentTypeComment})
  459. }
  460. // LoadPullRequests loads pull requests
  461. func (issues IssueList) LoadPullRequests() error {
  462. return issues.loadPullRequests(x)
  463. }
  464. // GetApprovalCounts returns a map of issue ID to slice of approval counts
  465. // FIXME: only returns official counts due to double counting of non-official approvals
  466. func (issues IssueList) GetApprovalCounts() (map[int64][]*ReviewCount, error) {
  467. return issues.getApprovalCounts(x)
  468. }
  469. func (issues IssueList) getApprovalCounts(e Engine) (map[int64][]*ReviewCount, error) {
  470. rCounts := make([]*ReviewCount, 0, 2*len(issues))
  471. ids := make([]int64, len(issues))
  472. for i, issue := range issues {
  473. ids[i] = issue.ID
  474. }
  475. sess := e.In("issue_id", ids)
  476. err := sess.Select("issue_id, type, count(id) as `count`").
  477. Where("official = ? AND dismissed = ?", true, false).
  478. GroupBy("issue_id, type").
  479. OrderBy("issue_id").
  480. Table("review").
  481. Find(&rCounts)
  482. if err != nil {
  483. return nil, err
  484. }
  485. approvalCountMap := make(map[int64][]*ReviewCount, len(issues))
  486. for _, c := range rCounts {
  487. approvalCountMap[c.IssueID] = append(approvalCountMap[c.IssueID], c)
  488. }
  489. return approvalCountMap, nil
  490. }