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.

858 lines
19 KiB

3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
  1. package services
  2. import (
  3. "context"
  4. "github.com/AchievementNetwork/stringset"
  5. "github.com/gissleh/stufflog/database"
  6. "github.com/gissleh/stufflog/internal/auth"
  7. "github.com/gissleh/stufflog/internal/slerrors"
  8. "github.com/gissleh/stufflog/models"
  9. "golang.org/x/sync/errgroup"
  10. )
  11. // Loader loads the stuff.
  12. type Loader struct {
  13. DB database.Database
  14. }
  15. func (l *Loader) FindGroup(ctx context.Context, id string) (*models.GroupResult, error) {
  16. group, err := l.DB.Groups().Find(ctx, id)
  17. if err != nil {
  18. return nil, err
  19. }
  20. if group.UserID != auth.UserID(ctx) {
  21. return nil, slerrors.NotFound("Goal")
  22. }
  23. result := &models.GroupResult{Group: *group}
  24. result.Items, err = l.DB.Items().List(ctx, models.ItemFilter{
  25. UserID: auth.UserID(ctx),
  26. GroupIDs: []string{group.ID},
  27. })
  28. if err != nil {
  29. return nil, err
  30. }
  31. return result, nil
  32. }
  33. func (l *Loader) ListGroups(ctx context.Context, filter models.GroupFilter) ([]*models.GroupResult, error) {
  34. filter.UserID = auth.UserID(ctx)
  35. groups, err := l.DB.Groups().List(ctx, filter)
  36. if err != nil {
  37. return nil, err
  38. }
  39. groupIDs := make([]string, 0, len(groups))
  40. for _, group := range groups {
  41. groupIDs = append(groupIDs, group.ID)
  42. }
  43. items, err := l.DB.Items().List(ctx, models.ItemFilter{
  44. UserID: auth.UserID(ctx),
  45. GroupIDs: groupIDs,
  46. })
  47. results := make([]*models.GroupResult, len(groups))
  48. for i, group := range groups {
  49. results[i] = &models.GroupResult{Group: *group, Items: []*models.Item{}}
  50. for _, item := range items {
  51. if item.GroupID == group.ID {
  52. results[i].Items = append(results[i].Items, item)
  53. }
  54. }
  55. }
  56. return results, nil
  57. }
  58. func (l *Loader) FindItem(ctx context.Context, id string) (*models.ItemResult, error) {
  59. item, err := l.DB.Items().Find(ctx, id)
  60. if err != nil {
  61. return nil, err
  62. }
  63. if item.UserID != auth.UserID(ctx) {
  64. return nil, slerrors.NotFound("Item")
  65. }
  66. result := &models.ItemResult{Item: *item}
  67. result.Group, err = l.DB.Groups().Find(ctx, item.GroupID)
  68. if err != nil {
  69. return nil, err
  70. }
  71. return result, nil
  72. }
  73. func (l *Loader) ListItems(ctx context.Context, filter models.ItemFilter) ([]*models.ItemResult, error) {
  74. filter.UserID = auth.UserID(ctx)
  75. items, err := l.DB.Items().List(ctx, filter)
  76. if err != nil {
  77. return nil, err
  78. }
  79. groupIDs := make([]string, 0, len(items))
  80. for _, item := range items {
  81. groupIDs = append(groupIDs, item.GroupID)
  82. }
  83. groups, err := l.DB.Groups().List(ctx, models.GroupFilter{
  84. UserID: auth.UserID(ctx),
  85. IDs: groupIDs,
  86. })
  87. results := make([]*models.ItemResult, len(items))
  88. for i, item := range items {
  89. results[i] = &models.ItemResult{Item: *item}
  90. for _, group := range groups {
  91. if item.GroupID == group.ID {
  92. results[i].Group = group
  93. break
  94. }
  95. }
  96. }
  97. return results, nil
  98. }
  99. func (l *Loader) FindLog(ctx context.Context, id string) (*models.LogResult, error) {
  100. log, err := l.DB.Logs().Find(ctx, id)
  101. if err != nil {
  102. return nil, err
  103. }
  104. if log.UserID != auth.UserID(ctx) {
  105. return nil, slerrors.NotFound("Goal")
  106. }
  107. result := &models.LogResult{
  108. Log: *log,
  109. Task: nil,
  110. }
  111. task, err := l.DB.Tasks().Find(ctx, log.TaskID)
  112. if err != nil {
  113. return nil, err
  114. }
  115. project, err := l.DB.Projects().Find(ctx, task.ProjectID)
  116. if err != nil {
  117. return nil, err
  118. }
  119. result.Task = &models.TaskWithProject{
  120. Task: *task,
  121. Project: project,
  122. }
  123. result.Item, _ = l.DB.Items().Find(ctx, log.ItemID)
  124. if log.SecondaryItemID != nil {
  125. result.SecondaryItem, _ = l.DB.Items().Find(ctx, *log.SecondaryItemID)
  126. }
  127. return result, nil
  128. }
  129. func (l *Loader) ListLogs(ctx context.Context, filter models.LogFilter) ([]*models.LogResult, error) {
  130. filter.UserID = auth.UserID(ctx)
  131. logs, err := l.DB.Logs().List(ctx, filter)
  132. if err != nil {
  133. return nil, err
  134. }
  135. taskIDs := stringset.New()
  136. itemIDs := stringset.New()
  137. projectIDs := stringset.New()
  138. for _, log := range logs {
  139. taskIDs.Add(log.TaskID)
  140. itemIDs.Add(log.ItemID)
  141. if log.SecondaryItemID != nil {
  142. itemIDs.Add(*log.SecondaryItemID)
  143. }
  144. }
  145. tasks, err := l.DB.Tasks().List(ctx, models.TaskFilter{
  146. UserID: auth.UserID(ctx),
  147. IDs: taskIDs.Strings(),
  148. })
  149. if err != nil {
  150. return nil, err
  151. }
  152. for _, task := range tasks {
  153. projectIDs.Add(task.ProjectID)
  154. }
  155. items, err := l.DB.Items().List(ctx, models.ItemFilter{
  156. UserID: auth.UserID(ctx),
  157. IDs: itemIDs.Strings(),
  158. })
  159. if err != nil {
  160. return nil, err
  161. }
  162. projects, err := l.DB.Projects().List(ctx, models.ProjectFilter{
  163. UserID: auth.UserID(ctx),
  164. IDs: projectIDs.Strings(),
  165. })
  166. if err != nil {
  167. return nil, err
  168. }
  169. results := make([]*models.LogResult, len(logs))
  170. for i, log := range logs {
  171. results[i] = &models.LogResult{
  172. Log: *log,
  173. Task: nil,
  174. }
  175. for _, task := range tasks {
  176. if task.ID == log.TaskID {
  177. results[i].Task = &models.TaskWithProject{Task: *task}
  178. break
  179. }
  180. }
  181. if results[i].Task != nil {
  182. for _, project := range projects {
  183. if project.ID == results[i].Task.ProjectID {
  184. results[i].Task.Project = project
  185. break
  186. }
  187. }
  188. }
  189. for _, item := range items {
  190. if item.ID == log.ItemID {
  191. results[i].Item = item
  192. }
  193. if log.SecondaryItemID != nil && item.ID == *log.SecondaryItemID {
  194. results[i].SecondaryItem = item
  195. }
  196. }
  197. }
  198. return results, nil
  199. }
  200. func (l *Loader) FindProject(ctx context.Context, id string) (*models.ProjectResult, error) {
  201. project, err := l.DB.Projects().Find(ctx, id)
  202. if err != nil {
  203. return nil, err
  204. }
  205. if project.UserID != auth.UserID(ctx) {
  206. return nil, slerrors.NotFound("Goal")
  207. }
  208. result := &models.ProjectResult{Project: *project, Subtractions: []models.ProjectSubtraction{}}
  209. tasks, err := l.DB.Tasks().List(ctx, models.TaskFilter{
  210. UserID: auth.UserID(ctx),
  211. ProjectIDs: []string{project.ID},
  212. })
  213. taskIDs := make([]string, 0, len(tasks))
  214. itemIDs := stringset.New()
  215. for _, task := range tasks {
  216. taskIDs = append(taskIDs, task.ID)
  217. itemIDs.Add(task.ItemID)
  218. }
  219. logs, err := l.DB.Logs().List(ctx, models.LogFilter{
  220. UserID: auth.UserID(ctx),
  221. TaskIDs: taskIDs,
  222. })
  223. for _, log := range logs {
  224. if log.SecondaryItemID != nil {
  225. itemIDs.Add(*log.SecondaryItemID)
  226. }
  227. }
  228. if err != nil {
  229. return nil, err
  230. }
  231. items, err := l.DB.Items().List(ctx, models.ItemFilter{
  232. UserID: auth.UserID(ctx),
  233. IDs: itemIDs.Strings(),
  234. })
  235. if err != nil {
  236. return nil, err
  237. }
  238. result.Tasks = make([]*models.TaskResult, len(tasks))
  239. for i, task := range tasks {
  240. result.Tasks[i] = &models.TaskResult{
  241. Logs: []*models.LogWithSecondaryItem{},
  242. }
  243. result.Tasks[i].Task = *task
  244. for _, log := range logs {
  245. if log.TaskID == task.ID {
  246. log := models.LogWithSecondaryItem{
  247. Log: *log,
  248. SecondaryItem: nil,
  249. }
  250. if log.SecondaryItemID != nil {
  251. for _, item := range items {
  252. if item.ID == *log.SecondaryItemID {
  253. log.SecondaryItem = item
  254. break
  255. }
  256. }
  257. }
  258. result.Tasks[i].Logs = append(result.Tasks[i].Logs, &log)
  259. }
  260. }
  261. for _, item := range items {
  262. if item.ID == task.ItemID {
  263. result.Tasks[i].Item = item
  264. break
  265. }
  266. }
  267. for _, log := range result.Tasks[i].Logs {
  268. if project.StartTime != nil && log.LoggedTime.Before(*project.StartTime) {
  269. result.Subtractions = append(result.Subtractions, models.ProjectSubtraction{
  270. Item: *result.Tasks[i].Item,
  271. Amount: log.ItemAmount,
  272. })
  273. if log.SecondaryItemID != nil {
  274. result.Subtractions = append(result.Subtractions, models.ProjectSubtraction{
  275. Item: *log.SecondaryItem,
  276. Amount: log.SecondaryItemAmount,
  277. })
  278. }
  279. }
  280. result.Tasks[i].CompletedAmount += log.Amount(log.ItemID)
  281. }
  282. }
  283. result.SortTasks()
  284. return result, nil
  285. }
  286. func (l *Loader) ListProjects(ctx context.Context, filter models.ProjectFilter) ([]*models.ProjectResult, error) {
  287. filter.UserID = auth.UserID(ctx)
  288. projects, err := l.DB.Projects().List(ctx, filter)
  289. if err != nil {
  290. return nil, err
  291. }
  292. projectIDs := make([]string, 0, len(projects))
  293. for _, project := range projects {
  294. projectIDs = append(projectIDs, project.ID)
  295. }
  296. tasks, links, err := l.DB.Tasks().ListWithLinks(ctx, models.TaskFilter{
  297. UserID: auth.UserID(ctx),
  298. ProjectIDs: projectIDs,
  299. })
  300. if err != nil {
  301. return nil, err
  302. }
  303. taskIDs := make([]string, 0, len(tasks))
  304. itemIDs := stringset.New()
  305. for _, task := range tasks {
  306. taskIDs = append(taskIDs, task.ID)
  307. itemIDs.Add(task.ItemID)
  308. }
  309. logs, err := l.DB.Logs().List(ctx, models.LogFilter{
  310. UserID: auth.UserID(ctx),
  311. TaskIDs: taskIDs,
  312. })
  313. if err != nil {
  314. return nil, err
  315. }
  316. for _, log := range logs {
  317. if log.SecondaryItemID != nil {
  318. itemIDs.Add(*log.SecondaryItemID)
  319. }
  320. }
  321. items, err := l.DB.Items().List(ctx, models.ItemFilter{
  322. UserID: auth.UserID(ctx),
  323. IDs: itemIDs.Strings(),
  324. })
  325. if err != nil {
  326. return nil, err
  327. }
  328. results := make([]*models.ProjectResult, len(projects))
  329. for i, project := range projects {
  330. results[i] = &models.ProjectResult{Project: *project, Subtractions: []models.ProjectSubtraction{}}
  331. results[i].Tasks = make([]*models.TaskResult, 0, 16)
  332. for _, task := range tasks {
  333. if task.ProjectID != project.ID {
  334. foundLink := false
  335. for _, link := range links {
  336. if link.TaskID == task.ID && link.ProjectID == project.ID {
  337. foundLink = true
  338. break
  339. }
  340. }
  341. if !foundLink {
  342. continue
  343. }
  344. }
  345. taskResult := &models.TaskResult{
  346. Task: *task,
  347. Logs: []*models.LogWithSecondaryItem{},
  348. }
  349. for _, log := range logs {
  350. if log.TaskID == task.ID {
  351. log := models.LogWithSecondaryItem{
  352. Log: *log,
  353. SecondaryItem: nil,
  354. }
  355. if log.SecondaryItemID != nil {
  356. for _, item := range items {
  357. if item.ID == *log.SecondaryItemID {
  358. log.SecondaryItem = item
  359. break
  360. }
  361. }
  362. }
  363. taskResult.Logs = append(taskResult.Logs, &log)
  364. }
  365. }
  366. for _, item := range items {
  367. if item.ID == task.ItemID {
  368. taskResult.Item = item
  369. break
  370. }
  371. }
  372. for _, log := range taskResult.Logs {
  373. if project.StartTime != nil && log.LoggedTime.Before(*project.StartTime) {
  374. results[i].Subtractions = append(results[i].Subtractions, models.ProjectSubtraction{
  375. Item: *taskResult.Item,
  376. Amount: log.ItemAmount,
  377. })
  378. if log.SecondaryItem != nil {
  379. results[i].Subtractions = append(results[i].Subtractions, models.ProjectSubtraction{
  380. Item: *log.SecondaryItem,
  381. Amount: log.SecondaryItemAmount,
  382. })
  383. }
  384. }
  385. taskResult.CompletedAmount += log.Amount(taskResult.ItemID)
  386. }
  387. results[i].Tasks = append(results[i].Tasks, taskResult)
  388. }
  389. results[i].SortTasks()
  390. }
  391. return results, nil
  392. }
  393. func (l *Loader) FindProjectGroup(ctx context.Context, id string) (*models.ProjectGroupResult, error) {
  394. group, err := l.DB.ProjectGroups().Find(ctx, id)
  395. if err != nil {
  396. return nil, err
  397. }
  398. projects, err := l.ListProjects(ctx, models.ProjectFilter{
  399. UserID: auth.UserID(ctx),
  400. ProjectGroupIDs: []string{group.ID},
  401. })
  402. if err != nil {
  403. return nil, err
  404. }
  405. result := &models.ProjectGroupResult{
  406. ProjectGroup: *group,
  407. Projects: projects,
  408. }
  409. result.RecountTasks()
  410. return result, nil
  411. }
  412. func (l *Loader) ListProjectGroups(ctx context.Context) ([]*models.ProjectGroupResult, error) {
  413. groups, err := l.DB.ProjectGroups().List(ctx, models.ProjectGroupFilter{UserID: auth.UserID(ctx)})
  414. if err != nil {
  415. return nil, err
  416. }
  417. ids := make([]string, 0, len(groups))
  418. for _, group := range groups {
  419. ids = append(ids, group.ID)
  420. }
  421. projects, err := l.ListProjects(ctx, models.ProjectFilter{
  422. UserID: auth.UserID(ctx),
  423. ProjectGroupIDs: ids,
  424. })
  425. results := make([]*models.ProjectGroupResult, 0, len(groups)+1)
  426. for _, group := range groups {
  427. matchingProjects := make([]*models.ProjectResult, 0, len(projects)/len(groups))
  428. for _, project := range projects {
  429. if *project.GroupID == group.ID {
  430. matchingProjects = append(matchingProjects, project)
  431. }
  432. }
  433. result := &models.ProjectGroupResult{
  434. ProjectGroup: *group,
  435. Projects: matchingProjects,
  436. }
  437. result.RecountTasks()
  438. results = append(results, result)
  439. }
  440. ungroupedProjects, err := l.ListProjects(ctx, models.ProjectFilter{
  441. UserID: auth.UserID(ctx),
  442. Ungrouped: true,
  443. })
  444. if err != nil {
  445. return nil, err
  446. }
  447. if len(ungroupedProjects) > 0 {
  448. result := &models.ProjectGroupResult{
  449. ProjectGroup: models.ProjectGroup{
  450. ID: "META_UNGROUPED",
  451. Name: "Ungrouped Projects",
  452. Abbreviation: "OTHER",
  453. CategoryNames: map[string]string{},
  454. },
  455. Projects: ungroupedProjects,
  456. }
  457. result.RecountTasks()
  458. results = append(results, result)
  459. }
  460. return results, nil
  461. }
  462. func (l *Loader) FindTask(ctx context.Context, id string) (*models.TaskResult, error) {
  463. task, err := l.DB.Tasks().Find(ctx, id)
  464. if err != nil {
  465. return nil, err
  466. }
  467. if task.UserID != auth.UserID(ctx) {
  468. return nil, slerrors.NotFound("Goal")
  469. }
  470. result := &models.TaskResult{Task: *task}
  471. result.Item, _ = l.DB.Items().Find(ctx, task.ItemID)
  472. result.Project, _ = l.DB.Projects().Find(ctx, task.ProjectID)
  473. logs, err := l.DB.Logs().List(ctx, models.LogFilter{
  474. UserID: task.UserID,
  475. TaskIDs: []string{task.ID},
  476. })
  477. if err != nil {
  478. return nil, err
  479. }
  480. itemIds := stringset.New()
  481. for _, log := range logs {
  482. if log.SecondaryItemID != nil {
  483. itemIds.Add(*log.SecondaryItemID)
  484. }
  485. }
  486. items, err := l.DB.Items().List(ctx, models.ItemFilter{
  487. UserID: task.UserID,
  488. IDs: itemIds.Strings(),
  489. })
  490. for _, log := range logs {
  491. result.CompletedAmount += log.Amount(result.ItemID)
  492. log := models.LogWithSecondaryItem{
  493. Log: *log,
  494. SecondaryItem: nil,
  495. }
  496. if log.SecondaryItemID != nil {
  497. for _, item := range items {
  498. if item.ID == *log.SecondaryItemID {
  499. log.SecondaryItem = item
  500. break
  501. }
  502. }
  503. }
  504. result.Logs = append(result.Logs, &log)
  505. }
  506. return result, nil
  507. }
  508. func (l *Loader) ListTasks(ctx context.Context, filter models.TaskFilter) ([]*models.TaskResult, error) {
  509. filter.UserID = auth.UserID(ctx)
  510. tasks, err := l.DB.Tasks().List(ctx, filter)
  511. if err != nil {
  512. return nil, err
  513. }
  514. if len(tasks) == 0 {
  515. return []*models.TaskResult{}, nil
  516. }
  517. taskIDs := make([]string, 0, len(tasks))
  518. itemIDs := stringset.New()
  519. projectIDs := stringset.New()
  520. for _, task := range tasks {
  521. taskIDs = append(taskIDs, task.ID)
  522. itemIDs.Add(task.ItemID)
  523. projectIDs.Add(task.ProjectID)
  524. }
  525. logs, err := l.DB.Logs().List(ctx, models.LogFilter{
  526. UserID: auth.UserID(ctx),
  527. TaskIDs: taskIDs,
  528. })
  529. if err != nil {
  530. return nil, err
  531. }
  532. for _, log := range logs {
  533. if log.SecondaryItemID != nil {
  534. itemIDs.Add(*log.SecondaryItemID)
  535. }
  536. }
  537. items, err := l.DB.Items().List(ctx, models.ItemFilter{
  538. UserID: auth.UserID(ctx),
  539. IDs: itemIDs.Strings(),
  540. })
  541. if err != nil {
  542. return nil, err
  543. }
  544. projects, err := l.DB.Projects().List(ctx, models.ProjectFilter{
  545. UserID: auth.UserID(ctx),
  546. IDs: projectIDs.Strings(),
  547. })
  548. if err != nil {
  549. return nil, err
  550. }
  551. results := make([]*models.TaskResult, 0, len(tasks))
  552. for _, task := range tasks {
  553. result := &models.TaskResult{
  554. Task: *task,
  555. Logs: []*models.LogWithSecondaryItem{},
  556. }
  557. for _, log := range logs {
  558. if log.TaskID != task.ID {
  559. continue
  560. }
  561. result.CompletedAmount += log.Amount(result.ItemID)
  562. log := models.LogWithSecondaryItem{
  563. Log: *log,
  564. SecondaryItem: nil,
  565. }
  566. if log.SecondaryItemID != nil {
  567. for _, item := range items {
  568. if item.ID == *log.SecondaryItemID {
  569. log.SecondaryItem = item
  570. break
  571. }
  572. }
  573. }
  574. result.Logs = append(result.Logs, &log)
  575. }
  576. for _, item := range items {
  577. if item.ID == task.ItemID {
  578. result.Item = item
  579. break
  580. }
  581. }
  582. for _, project := range projects {
  583. if project.ID == task.ProjectID {
  584. result.Project = project
  585. break
  586. }
  587. }
  588. for _, log := range result.Logs {
  589. result.CompletedAmount += log.Amount(result.ItemID)
  590. }
  591. results = append(results, result)
  592. }
  593. return results, nil
  594. }
  595. func (l *Loader) FindGoal(ctx context.Context, id string) (*models.GoalResult, error) {
  596. goal, err := l.DB.Goals().Find(ctx, id)
  597. if err != nil {
  598. return nil, err
  599. }
  600. if goal.UserID != auth.UserID(ctx) {
  601. return nil, slerrors.NotFound("Goal")
  602. }
  603. return l.populateGoals(ctx, goal)
  604. }
  605. func (l *Loader) ListGoals(ctx context.Context, filter models.GoalFilter) ([]*models.GoalResult, error) {
  606. filter.UserID = auth.UserID(ctx)
  607. goals, err := l.DB.Goals().List(ctx, filter)
  608. if err != nil {
  609. return nil, err
  610. }
  611. results := make([]*models.GoalResult, len(goals))
  612. eg := errgroup.Group{}
  613. for i := range results {
  614. index := i // Required to avoid race condition.
  615. eg.Go(func() error {
  616. res, err := l.populateGoals(ctx, goals[index])
  617. if err != nil {
  618. return err
  619. }
  620. results[index] = res
  621. return nil
  622. })
  623. }
  624. err = eg.Wait()
  625. if err != nil {
  626. return nil, err
  627. }
  628. return results, nil
  629. }
  630. func (l *Loader) populateGoals(ctx context.Context, goal *models.Goal) (*models.GoalResult, error) {
  631. userID := auth.UserID(ctx)
  632. result := &models.GoalResult{
  633. Goal: *goal,
  634. Group: nil,
  635. Items: nil,
  636. Logs: nil,
  637. CompletedAmount: 0,
  638. }
  639. result.Group, _ = l.DB.Groups().Find(ctx, goal.GroupID)
  640. if result.Group != nil {
  641. // Get items
  642. items, err := l.DB.Items().List(ctx, models.ItemFilter{
  643. UserID: userID,
  644. GroupIDs: []string{goal.GroupID},
  645. })
  646. if err != nil {
  647. return nil, err
  648. }
  649. itemIDs := make([]string, 0, len(items))
  650. for _, item := range items {
  651. result.Items = append(result.Items, &models.GoalResultItem{
  652. Item: *item,
  653. CompletedAmount: 0,
  654. })
  655. itemIDs = append(itemIDs, item.ID)
  656. }
  657. // Get logs
  658. logs, err := l.DB.Logs().List(ctx, models.LogFilter{
  659. UserID: userID,
  660. ItemIDs: itemIDs,
  661. MinTime: &goal.StartTime,
  662. MaxTime: &goal.EndTime,
  663. })
  664. if err != nil {
  665. return nil, err
  666. }
  667. // Get tasks
  668. taskIDs := stringset.New()
  669. for _, log := range logs {
  670. taskIDs.Add(log.TaskID)
  671. }
  672. tasks, err := l.DB.Tasks().List(ctx, models.TaskFilter{
  673. UserID: userID,
  674. IDs: taskIDs.Strings(),
  675. })
  676. if err != nil {
  677. return nil, err
  678. }
  679. projectIDs := stringset.New()
  680. for _, task := range tasks {
  681. projectIDs.Add(task.ProjectID)
  682. }
  683. projects, err := l.DB.Projects().List(ctx, models.ProjectFilter{
  684. UserID: userID,
  685. IDs: projectIDs.Strings(),
  686. })
  687. // Apply logs
  688. result.Logs = make([]*models.GoalResultLog, 0, len(logs))
  689. for _, log := range logs {
  690. resultLog := &models.GoalResultLog{
  691. LogResult: models.LogResult{Log: *log},
  692. }
  693. contributes := false
  694. for _, task := range tasks {
  695. if task.ID == log.TaskID {
  696. resultLog.Task = &models.TaskWithProject{Task: *task}
  697. for _, project := range projects {
  698. if project.ID == task.ProjectID {
  699. resultLog.Task.Project = project
  700. break
  701. }
  702. }
  703. break
  704. }
  705. }
  706. for _, item := range result.Items {
  707. amount := log.Amount(item.ID)
  708. if amount > 0 && goal.Accepts(&item.Item, &resultLog.Task.Task) {
  709. item.CompletedAmount += amount
  710. if goal.Unweighted {
  711. if item.GroupWeight > 0 {
  712. result.CompletedAmount += amount
  713. }
  714. } else {
  715. result.CompletedAmount += amount * item.GroupWeight
  716. }
  717. contributes = true
  718. } else {
  719. amount = 0
  720. }
  721. if item.ID == log.ItemID {
  722. resultLog.Item = &item.Item
  723. if amount > 0 {
  724. resultLog.ItemCounted = true
  725. }
  726. if log.SecondaryItemID == nil {
  727. break
  728. }
  729. }
  730. if log.SecondaryItemID != nil && item.ID == *log.SecondaryItemID {
  731. if amount > 0 {
  732. resultLog.SecondaryItemCounted = true
  733. }
  734. resultLog.SecondaryItem = &item.Item
  735. }
  736. }
  737. if contributes {
  738. result.Logs = append(result.Logs, resultLog)
  739. }
  740. }
  741. }
  742. return result, nil
  743. }