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.

issue_list.go 14KB

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>
6 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>
6 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>
6 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>
6 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>
6 years ago
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540
  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. "code.gitea.io/gitea/modules/log"
  8. "github.com/go-xorm/builder"
  9. )
  10. // IssueList defines a list of issues
  11. type IssueList []*Issue
  12. const (
  13. // default variables number on IN () in SQL
  14. defaultMaxInSize = 50
  15. )
  16. func (issues IssueList) getRepoIDs() []int64 {
  17. repoIDs := make(map[int64]struct{}, len(issues))
  18. for _, issue := range issues {
  19. if _, ok := repoIDs[issue.RepoID]; !ok {
  20. repoIDs[issue.RepoID] = struct{}{}
  21. }
  22. }
  23. return keysInt64(repoIDs)
  24. }
  25. func (issues IssueList) loadRepositories(e Engine) ([]*Repository, error) {
  26. if len(issues) == 0 {
  27. return nil, nil
  28. }
  29. repoIDs := issues.getRepoIDs()
  30. repoMaps := make(map[int64]*Repository, len(repoIDs))
  31. var left = len(repoIDs)
  32. for left > 0 {
  33. var limit = defaultMaxInSize
  34. if left < limit {
  35. limit = left
  36. }
  37. err := e.
  38. In("id", repoIDs[:limit]).
  39. Find(&repoMaps)
  40. if err != nil {
  41. return nil, fmt.Errorf("find repository: %v", err)
  42. }
  43. left -= limit
  44. repoIDs = repoIDs[limit:]
  45. }
  46. for _, issue := range issues {
  47. issue.Repo = repoMaps[issue.RepoID]
  48. }
  49. return valuesRepository(repoMaps), nil
  50. }
  51. // LoadRepositories loads issues' all repositories
  52. func (issues IssueList) LoadRepositories() ([]*Repository, error) {
  53. return issues.loadRepositories(x)
  54. }
  55. func (issues IssueList) getPosterIDs() []int64 {
  56. posterIDs := make(map[int64]struct{}, len(issues))
  57. for _, issue := range issues {
  58. if _, ok := posterIDs[issue.PosterID]; !ok {
  59. posterIDs[issue.PosterID] = struct{}{}
  60. }
  61. }
  62. return keysInt64(posterIDs)
  63. }
  64. func (issues IssueList) loadPosters(e Engine) error {
  65. if len(issues) == 0 {
  66. return nil
  67. }
  68. posterIDs := issues.getPosterIDs()
  69. posterMaps := make(map[int64]*User, len(posterIDs))
  70. var left = len(posterIDs)
  71. for left > 0 {
  72. var limit = defaultMaxInSize
  73. if left < limit {
  74. limit = left
  75. }
  76. err := e.
  77. In("id", posterIDs[:limit]).
  78. Find(&posterMaps)
  79. if err != nil {
  80. return err
  81. }
  82. left -= limit
  83. posterIDs = posterIDs[limit:]
  84. }
  85. for _, issue := range issues {
  86. if issue.PosterID <= 0 {
  87. continue
  88. }
  89. var ok bool
  90. if issue.Poster, ok = posterMaps[issue.PosterID]; !ok {
  91. issue.Poster = NewGhostUser()
  92. }
  93. }
  94. return nil
  95. }
  96. func (issues IssueList) getIssueIDs() []int64 {
  97. var ids = make([]int64, 0, len(issues))
  98. for _, issue := range issues {
  99. ids = append(ids, issue.ID)
  100. }
  101. return ids
  102. }
  103. func (issues IssueList) loadLabels(e Engine) error {
  104. if len(issues) == 0 {
  105. return nil
  106. }
  107. type LabelIssue struct {
  108. Label *Label `xorm:"extends"`
  109. IssueLabel *IssueLabel `xorm:"extends"`
  110. }
  111. var issueLabels = make(map[int64][]*Label, len(issues)*3)
  112. var issueIDs = issues.getIssueIDs()
  113. var left = len(issueIDs)
  114. for left > 0 {
  115. var limit = defaultMaxInSize
  116. if left < limit {
  117. limit = left
  118. }
  119. rows, err := e.Table("label").
  120. Join("LEFT", "issue_label", "issue_label.label_id = label.id").
  121. In("issue_label.issue_id", issueIDs[:limit]).
  122. Asc("label.name").
  123. Rows(new(LabelIssue))
  124. if err != nil {
  125. return err
  126. }
  127. for rows.Next() {
  128. var labelIssue LabelIssue
  129. err = rows.Scan(&labelIssue)
  130. if err != nil {
  131. // When there are no rows left and we try to close it, xorm will complain with an error.
  132. // Since that is not relevant for us, we can safely ignore it.
  133. if err := rows.Close(); err != nil {
  134. log.Error("IssueList.loadLabels: Close: %v", err)
  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, xorm will complain with an error.
  141. // Since that is not relevant for us, we can safely ignore it.
  142. if err := rows.Close(); err != nil {
  143. log.Error("IssueList.loadLabels: Close: %v", err)
  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. var 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. var left = len(milestoneIDs)
  169. for left > 0 {
  170. var 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. var assignees = make(map[int64][]*User, len(issues))
  197. var issueIDs = issues.getIssueIDs()
  198. var left = len(issueIDs)
  199. for left > 0 {
  200. var 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. // When there are no rows left and we try to close it, xorm will complain with an error.
  216. // Since that is not relevant for us, we can safely ignore it.
  217. if err := rows.Close(); err != nil {
  218. log.Error("IssueList.loadAssignees: Close: %v", err)
  219. }
  220. return err
  221. }
  222. assignees[assigneeIssue.IssueAssignee.IssueID] = append(assignees[assigneeIssue.IssueAssignee.IssueID], assigneeIssue.Assignee)
  223. }
  224. // When there are no rows left and we try to close it, xorm will complain with an error.
  225. // Since that is not relevant for us, we can safely ignore it.
  226. if err := rows.Close(); err != nil {
  227. log.Error("IssueList.loadAssignees: Close: %v", err)
  228. }
  229. left -= limit
  230. issueIDs = issueIDs[limit:]
  231. }
  232. for _, issue := range issues {
  233. issue.Assignees = assignees[issue.ID]
  234. }
  235. return nil
  236. }
  237. func (issues IssueList) getPullIssueIDs() []int64 {
  238. var ids = make([]int64, 0, len(issues))
  239. for _, issue := range issues {
  240. if issue.IsPull && issue.PullRequest == nil {
  241. ids = append(ids, issue.ID)
  242. }
  243. }
  244. return ids
  245. }
  246. func (issues IssueList) loadPullRequests(e Engine) error {
  247. issuesIDs := issues.getPullIssueIDs()
  248. if len(issuesIDs) == 0 {
  249. return nil
  250. }
  251. pullRequestMaps := make(map[int64]*PullRequest, len(issuesIDs))
  252. var left = len(issuesIDs)
  253. for left > 0 {
  254. var limit = defaultMaxInSize
  255. if left < limit {
  256. limit = left
  257. }
  258. rows, err := e.
  259. In("issue_id", issuesIDs[:limit]).
  260. Rows(new(PullRequest))
  261. if err != nil {
  262. return err
  263. }
  264. for rows.Next() {
  265. var pr PullRequest
  266. err = rows.Scan(&pr)
  267. if err != nil {
  268. // When there are no rows left and we try to close it, xorm will complain with an error.
  269. // Since that is not relevant for us, we can safely ignore it.
  270. if err := rows.Close(); err != nil {
  271. log.Error("IssueList.loadPullRequests: Close: %v", err)
  272. }
  273. return err
  274. }
  275. pullRequestMaps[pr.IssueID] = &pr
  276. }
  277. // When there are no rows left and we try to close it, xorm will complain with an error.
  278. // Since that is not relevant for us, we can safely ignore it.
  279. if err := rows.Close(); err != nil {
  280. log.Error("IssueList.loadPullRequests: Close: %v", err)
  281. }
  282. left -= limit
  283. issuesIDs = issuesIDs[limit:]
  284. }
  285. for _, issue := range issues {
  286. issue.PullRequest = pullRequestMaps[issue.ID]
  287. }
  288. return nil
  289. }
  290. func (issues IssueList) loadAttachments(e Engine) (err error) {
  291. if len(issues) == 0 {
  292. return nil
  293. }
  294. var attachments = make(map[int64][]*Attachment, len(issues))
  295. var issuesIDs = issues.getIssueIDs()
  296. var left = len(issuesIDs)
  297. for left > 0 {
  298. var limit = defaultMaxInSize
  299. if left < limit {
  300. limit = left
  301. }
  302. rows, err := e.Table("attachment").
  303. Join("INNER", "issue", "issue.id = attachment.issue_id").
  304. In("issue.id", issuesIDs[:limit]).
  305. Rows(new(Attachment))
  306. if err != nil {
  307. return err
  308. }
  309. for rows.Next() {
  310. var attachment Attachment
  311. err = rows.Scan(&attachment)
  312. if err != nil {
  313. // When there are no rows left and we try to close it, xorm will complain with an error.
  314. // Since that is not relevant for us, we can safely ignore it.
  315. if err := rows.Close(); err != nil {
  316. log.Error("IssueList.loadAttachments: Close: %v", err)
  317. }
  318. return err
  319. }
  320. attachments[attachment.IssueID] = append(attachments[attachment.IssueID], &attachment)
  321. }
  322. // When there are no rows left and we try to close it, xorm will complain with an error.
  323. // Since that is not relevant for us, we can safely ignore it.
  324. if err := rows.Close(); err != nil {
  325. log.Error("IssueList.loadAttachments: Close: %v", err)
  326. }
  327. left -= limit
  328. issuesIDs = issuesIDs[limit:]
  329. }
  330. for _, issue := range issues {
  331. issue.Attachments = attachments[issue.ID]
  332. }
  333. return nil
  334. }
  335. func (issues IssueList) loadComments(e Engine, cond builder.Cond) (err error) {
  336. if len(issues) == 0 {
  337. return nil
  338. }
  339. var comments = make(map[int64][]*Comment, len(issues))
  340. var issuesIDs = issues.getIssueIDs()
  341. var left = len(issuesIDs)
  342. for left > 0 {
  343. var limit = defaultMaxInSize
  344. if left < limit {
  345. limit = left
  346. }
  347. rows, err := e.Table("comment").
  348. Join("INNER", "issue", "issue.id = comment.issue_id").
  349. In("issue.id", issuesIDs[:limit]).
  350. Where(cond).
  351. Rows(new(Comment))
  352. if err != nil {
  353. return err
  354. }
  355. for rows.Next() {
  356. var comment Comment
  357. err = rows.Scan(&comment)
  358. if err != nil {
  359. // When there are no rows left and we try to close it, xorm will complain with an error.
  360. // Since that is not relevant for us, we can safely ignore it.
  361. if err := rows.Close(); err != nil {
  362. log.Error("IssueList.loadComments: Close: %v", err)
  363. }
  364. return err
  365. }
  366. comments[comment.IssueID] = append(comments[comment.IssueID], &comment)
  367. }
  368. // When there are no rows left and we try to close it, xorm will complain with an error.
  369. // Since that is not relevant for us, we can safely ignore it.
  370. if err := rows.Close(); err != nil {
  371. log.Error("IssueList.loadComments: Close: %v", err)
  372. }
  373. left -= limit
  374. issuesIDs = issuesIDs[limit:]
  375. }
  376. for _, issue := range issues {
  377. issue.Comments = comments[issue.ID]
  378. }
  379. return nil
  380. }
  381. func (issues IssueList) loadTotalTrackedTimes(e Engine) (err error) {
  382. type totalTimesByIssue struct {
  383. IssueID int64
  384. Time int64
  385. }
  386. if len(issues) == 0 {
  387. return nil
  388. }
  389. var trackedTimes = make(map[int64]int64, len(issues))
  390. var ids = make([]int64, 0, len(issues))
  391. for _, issue := range issues {
  392. if issue.Repo.IsTimetrackerEnabled() {
  393. ids = append(ids, issue.ID)
  394. }
  395. }
  396. var left = len(ids)
  397. for left > 0 {
  398. var limit = defaultMaxInSize
  399. if left < limit {
  400. limit = left
  401. }
  402. // select issue_id, sum(time) from tracked_time where issue_id in (<issue ids in current page>) group by issue_id
  403. rows, err := e.Table("tracked_time").
  404. Select("issue_id, sum(time) as time").
  405. In("issue_id", ids[:limit]).
  406. GroupBy("issue_id").
  407. Rows(new(totalTimesByIssue))
  408. if err != nil {
  409. return err
  410. }
  411. for rows.Next() {
  412. var totalTime totalTimesByIssue
  413. err = rows.Scan(&totalTime)
  414. if err != nil {
  415. // When there are no rows left and we try to close it, xorm will complain with an error.
  416. // Since that is not relevant for us, we can safely ignore it.
  417. if err := rows.Close(); err != nil {
  418. log.Error("IssueList.loadTotalTrackedTimes: Close: %v", err)
  419. }
  420. return err
  421. }
  422. trackedTimes[totalTime.IssueID] = totalTime.Time
  423. }
  424. // When there are no rows left and we try to close it, xorm will complain with an error.
  425. // Since that is not relevant for us, we can safely ignore it.
  426. if err := rows.Close(); err != nil {
  427. log.Error("IssueList.loadTotalTrackedTimes: Close: %v", err)
  428. }
  429. left -= limit
  430. ids = ids[limit:]
  431. }
  432. for _, issue := range issues {
  433. issue.TotalTrackedTime = trackedTimes[issue.ID]
  434. }
  435. return nil
  436. }
  437. // loadAttributes loads all attributes, expect for attachments and comments
  438. func (issues IssueList) loadAttributes(e Engine) error {
  439. if _, err := issues.loadRepositories(e); err != nil {
  440. return fmt.Errorf("issue.loadAttributes: loadRepositories: %v", err)
  441. }
  442. if err := issues.loadPosters(e); err != nil {
  443. return fmt.Errorf("issue.loadAttributes: loadPosters: %v", err)
  444. }
  445. if err := issues.loadLabels(e); err != nil {
  446. return fmt.Errorf("issue.loadAttributes: loadLabels: %v", err)
  447. }
  448. if err := issues.loadMilestones(e); err != nil {
  449. return fmt.Errorf("issue.loadAttributes: loadMilestones: %v", err)
  450. }
  451. if err := issues.loadAssignees(e); err != nil {
  452. return fmt.Errorf("issue.loadAttributes: loadAssignees: %v", err)
  453. }
  454. if err := issues.loadPullRequests(e); err != nil {
  455. return fmt.Errorf("issue.loadAttributes: loadPullRequests: %v", err)
  456. }
  457. if err := issues.loadTotalTrackedTimes(e); err != nil {
  458. return fmt.Errorf("issue.loadAttributes: loadTotalTrackedTimes: %v", err)
  459. }
  460. return nil
  461. }
  462. // LoadAttributes loads attributes of the issues, except for attachments and
  463. // comments
  464. func (issues IssueList) LoadAttributes() error {
  465. return issues.loadAttributes(x)
  466. }
  467. // LoadAttachments loads attachments
  468. func (issues IssueList) LoadAttachments() error {
  469. return issues.loadAttachments(x)
  470. }
  471. // LoadComments loads comments
  472. func (issues IssueList) LoadComments() error {
  473. return issues.loadComments(x, builder.NewCond())
  474. }
  475. // LoadDiscussComments loads discuss comments
  476. func (issues IssueList) LoadDiscussComments() error {
  477. return issues.loadComments(x, builder.Eq{"comment.type": CommentTypeComment})
  478. }