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.

854 lines
19 KiB

4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 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. return result, nil
  284. }
  285. func (l *Loader) ListProjects(ctx context.Context, filter models.ProjectFilter) ([]*models.ProjectResult, error) {
  286. filter.UserID = auth.UserID(ctx)
  287. projects, err := l.DB.Projects().List(ctx, filter)
  288. if err != nil {
  289. return nil, err
  290. }
  291. projectIDs := make([]string, 0, len(projects))
  292. for _, project := range projects {
  293. projectIDs = append(projectIDs, project.ID)
  294. }
  295. tasks, links, err := l.DB.Tasks().ListWithLinks(ctx, models.TaskFilter{
  296. UserID: auth.UserID(ctx),
  297. ProjectIDs: projectIDs,
  298. })
  299. if err != nil {
  300. return nil, err
  301. }
  302. taskIDs := make([]string, 0, len(tasks))
  303. itemIDs := stringset.New()
  304. for _, task := range tasks {
  305. taskIDs = append(taskIDs, task.ID)
  306. itemIDs.Add(task.ItemID)
  307. }
  308. logs, err := l.DB.Logs().List(ctx, models.LogFilter{
  309. UserID: auth.UserID(ctx),
  310. TaskIDs: taskIDs,
  311. })
  312. if err != nil {
  313. return nil, err
  314. }
  315. for _, log := range logs {
  316. if log.SecondaryItemID != nil {
  317. itemIDs.Add(*log.SecondaryItemID)
  318. }
  319. }
  320. items, err := l.DB.Items().List(ctx, models.ItemFilter{
  321. UserID: auth.UserID(ctx),
  322. IDs: itemIDs.Strings(),
  323. })
  324. if err != nil {
  325. return nil, err
  326. }
  327. results := make([]*models.ProjectResult, len(projects))
  328. for i, project := range projects {
  329. results[i] = &models.ProjectResult{Project: *project, Subtractions: []models.ProjectSubtraction{}}
  330. results[i].Tasks = make([]*models.TaskResult, 0, 16)
  331. for _, task := range tasks {
  332. if task.ProjectID != project.ID {
  333. foundLink := false
  334. for _, link := range links {
  335. if link.TaskID == task.ID && link.ProjectID == project.ID {
  336. foundLink = true
  337. break
  338. }
  339. }
  340. if !foundLink {
  341. continue
  342. }
  343. }
  344. taskResult := &models.TaskResult{
  345. Task: *task,
  346. Logs: []*models.LogWithSecondaryItem{},
  347. }
  348. for _, log := range logs {
  349. if log.TaskID == task.ID {
  350. log := models.LogWithSecondaryItem{
  351. Log: *log,
  352. SecondaryItem: nil,
  353. }
  354. if log.SecondaryItemID != nil {
  355. for _, item := range items {
  356. if item.ID == *log.SecondaryItemID {
  357. log.SecondaryItem = item
  358. break
  359. }
  360. }
  361. }
  362. taskResult.Logs = append(taskResult.Logs, &log)
  363. }
  364. }
  365. for _, item := range items {
  366. if item.ID == task.ItemID {
  367. taskResult.Item = item
  368. break
  369. }
  370. }
  371. for _, log := range taskResult.Logs {
  372. if project.StartTime != nil && log.LoggedTime.Before(*project.StartTime) {
  373. results[i].Subtractions = append(results[i].Subtractions, models.ProjectSubtraction{
  374. Item: *taskResult.Item,
  375. Amount: log.ItemAmount,
  376. })
  377. if log.SecondaryItem != nil {
  378. results[i].Subtractions = append(results[i].Subtractions, models.ProjectSubtraction{
  379. Item: *log.SecondaryItem,
  380. Amount: log.SecondaryItemAmount,
  381. })
  382. }
  383. }
  384. taskResult.CompletedAmount += log.Amount(taskResult.ItemID)
  385. }
  386. results[i].Tasks = append(results[i].Tasks, taskResult)
  387. }
  388. }
  389. return results, nil
  390. }
  391. func (l *Loader) FindProjectGroup(ctx context.Context, id string) (*models.ProjectGroupResult, error) {
  392. group, err := l.DB.ProjectGroups().Find(ctx, id)
  393. if err != nil {
  394. return nil, err
  395. }
  396. projects, err := l.ListProjects(ctx, models.ProjectFilter{
  397. UserID: auth.UserID(ctx),
  398. ProjectGroupIDs: []string{group.ID},
  399. })
  400. if err != nil {
  401. return nil, err
  402. }
  403. result := &models.ProjectGroupResult{
  404. ProjectGroup: *group,
  405. Projects: projects,
  406. }
  407. result.RecountTasks()
  408. return result, nil
  409. }
  410. func (l *Loader) ListProjectGroups(ctx context.Context) ([]*models.ProjectGroupResult, error) {
  411. groups, err := l.DB.ProjectGroups().List(ctx, models.ProjectGroupFilter{UserID: auth.UserID(ctx)})
  412. if err != nil {
  413. return nil, err
  414. }
  415. ids := make([]string, 0, len(groups))
  416. for _, group := range groups {
  417. ids = append(ids, group.ID)
  418. }
  419. projects, err := l.ListProjects(ctx, models.ProjectFilter{
  420. UserID: auth.UserID(ctx),
  421. ProjectGroupIDs: ids,
  422. })
  423. results := make([]*models.ProjectGroupResult, 0, len(groups)+1)
  424. for _, group := range groups {
  425. matchingProjects := make([]*models.ProjectResult, 0, len(projects)/len(groups))
  426. for _, project := range projects {
  427. if *project.GroupID == group.ID {
  428. matchingProjects = append(matchingProjects, project)
  429. }
  430. }
  431. result := &models.ProjectGroupResult{
  432. ProjectGroup: *group,
  433. Projects: matchingProjects,
  434. }
  435. result.RecountTasks()
  436. results = append(results, result)
  437. }
  438. ungroupedProjects, err := l.ListProjects(ctx, models.ProjectFilter{
  439. UserID: auth.UserID(ctx),
  440. Ungrouped: true,
  441. })
  442. if err != nil {
  443. return nil, err
  444. }
  445. if len(ungroupedProjects) > 0 {
  446. result := &models.ProjectGroupResult{
  447. ProjectGroup: models.ProjectGroup{
  448. ID: "META_UNGROUPED",
  449. Name: "Ungrouped Projects",
  450. Abbreviation: "OTHER",
  451. CategoryNames: map[string]string{},
  452. },
  453. Projects: ungroupedProjects,
  454. }
  455. result.RecountTasks()
  456. results = append(results, result)
  457. }
  458. return results, nil
  459. }
  460. func (l *Loader) FindTask(ctx context.Context, id string) (*models.TaskResult, error) {
  461. task, err := l.DB.Tasks().Find(ctx, id)
  462. if err != nil {
  463. return nil, err
  464. }
  465. if task.UserID != auth.UserID(ctx) {
  466. return nil, slerrors.NotFound("Goal")
  467. }
  468. result := &models.TaskResult{Task: *task}
  469. result.Item, _ = l.DB.Items().Find(ctx, task.ItemID)
  470. result.Project, _ = l.DB.Projects().Find(ctx, task.ProjectID)
  471. logs, err := l.DB.Logs().List(ctx, models.LogFilter{
  472. UserID: task.UserID,
  473. TaskIDs: []string{task.ID},
  474. })
  475. if err != nil {
  476. return nil, err
  477. }
  478. itemIds := stringset.New()
  479. for _, log := range logs {
  480. if log.SecondaryItemID != nil {
  481. itemIds.Add(*log.SecondaryItemID)
  482. }
  483. }
  484. items, err := l.DB.Items().List(ctx, models.ItemFilter{
  485. UserID: task.UserID,
  486. IDs: itemIds.Strings(),
  487. })
  488. for _, log := range logs {
  489. result.CompletedAmount += log.Amount(result.ItemID)
  490. log := models.LogWithSecondaryItem{
  491. Log: *log,
  492. SecondaryItem: nil,
  493. }
  494. if log.SecondaryItemID != nil {
  495. for _, item := range items {
  496. if item.ID == *log.SecondaryItemID {
  497. log.SecondaryItem = item
  498. break
  499. }
  500. }
  501. }
  502. result.Logs = append(result.Logs, &log)
  503. }
  504. return result, nil
  505. }
  506. func (l *Loader) ListTasks(ctx context.Context, filter models.TaskFilter) ([]*models.TaskResult, error) {
  507. filter.UserID = auth.UserID(ctx)
  508. tasks, err := l.DB.Tasks().List(ctx, filter)
  509. if err != nil {
  510. return nil, err
  511. }
  512. if len(tasks) == 0 {
  513. return []*models.TaskResult{}, nil
  514. }
  515. taskIDs := make([]string, 0, len(tasks))
  516. itemIDs := stringset.New()
  517. projectIDs := stringset.New()
  518. for _, task := range tasks {
  519. taskIDs = append(taskIDs, task.ID)
  520. itemIDs.Add(task.ItemID)
  521. projectIDs.Add(task.ProjectID)
  522. }
  523. logs, err := l.DB.Logs().List(ctx, models.LogFilter{
  524. UserID: auth.UserID(ctx),
  525. TaskIDs: taskIDs,
  526. })
  527. if err != nil {
  528. return nil, err
  529. }
  530. for _, log := range logs {
  531. if log.SecondaryItemID != nil {
  532. itemIDs.Add(*log.SecondaryItemID)
  533. }
  534. }
  535. items, err := l.DB.Items().List(ctx, models.ItemFilter{
  536. UserID: auth.UserID(ctx),
  537. IDs: itemIDs.Strings(),
  538. })
  539. if err != nil {
  540. return nil, err
  541. }
  542. projects, err := l.DB.Projects().List(ctx, models.ProjectFilter{
  543. UserID: auth.UserID(ctx),
  544. IDs: projectIDs.Strings(),
  545. })
  546. if err != nil {
  547. return nil, err
  548. }
  549. results := make([]*models.TaskResult, 0, len(tasks))
  550. for _, task := range tasks {
  551. result := &models.TaskResult{
  552. Task: *task,
  553. Logs: []*models.LogWithSecondaryItem{},
  554. }
  555. for _, log := range logs {
  556. if log.TaskID != task.ID {
  557. continue
  558. }
  559. result.CompletedAmount += log.Amount(result.ItemID)
  560. log := models.LogWithSecondaryItem{
  561. Log: *log,
  562. SecondaryItem: nil,
  563. }
  564. if log.SecondaryItemID != nil {
  565. for _, item := range items {
  566. if item.ID == *log.SecondaryItemID {
  567. log.SecondaryItem = item
  568. break
  569. }
  570. }
  571. }
  572. result.Logs = append(result.Logs, &log)
  573. }
  574. for _, item := range items {
  575. if item.ID == task.ItemID {
  576. result.Item = item
  577. break
  578. }
  579. }
  580. for _, project := range projects {
  581. if project.ID == task.ProjectID {
  582. result.Project = project
  583. break
  584. }
  585. }
  586. for _, log := range result.Logs {
  587. result.CompletedAmount += log.Amount(result.ItemID)
  588. }
  589. results = append(results, result)
  590. }
  591. return results, nil
  592. }
  593. func (l *Loader) FindGoal(ctx context.Context, id string) (*models.GoalResult, error) {
  594. goal, err := l.DB.Goals().Find(ctx, id)
  595. if err != nil {
  596. return nil, err
  597. }
  598. if goal.UserID != auth.UserID(ctx) {
  599. return nil, slerrors.NotFound("Goal")
  600. }
  601. return l.populateGoals(ctx, goal)
  602. }
  603. func (l *Loader) ListGoals(ctx context.Context, filter models.GoalFilter) ([]*models.GoalResult, error) {
  604. filter.UserID = auth.UserID(ctx)
  605. goals, err := l.DB.Goals().List(ctx, filter)
  606. if err != nil {
  607. return nil, err
  608. }
  609. results := make([]*models.GoalResult, len(goals))
  610. eg := errgroup.Group{}
  611. for i := range results {
  612. index := i // Required to avoid race condition.
  613. eg.Go(func() error {
  614. res, err := l.populateGoals(ctx, goals[index])
  615. if err != nil {
  616. return err
  617. }
  618. results[index] = res
  619. return nil
  620. })
  621. }
  622. err = eg.Wait()
  623. if err != nil {
  624. return nil, err
  625. }
  626. return results, nil
  627. }
  628. func (l *Loader) populateGoals(ctx context.Context, goal *models.Goal) (*models.GoalResult, error) {
  629. userID := auth.UserID(ctx)
  630. result := &models.GoalResult{
  631. Goal: *goal,
  632. Group: nil,
  633. Items: nil,
  634. Logs: nil,
  635. CompletedAmount: 0,
  636. }
  637. result.Group, _ = l.DB.Groups().Find(ctx, goal.GroupID)
  638. if result.Group != nil {
  639. // Get items
  640. items, err := l.DB.Items().List(ctx, models.ItemFilter{
  641. UserID: userID,
  642. GroupIDs: []string{goal.GroupID},
  643. })
  644. if err != nil {
  645. return nil, err
  646. }
  647. itemIDs := make([]string, 0, len(items))
  648. for _, item := range items {
  649. result.Items = append(result.Items, &models.GoalResultItem{
  650. Item: *item,
  651. CompletedAmount: 0,
  652. })
  653. itemIDs = append(itemIDs, item.ID)
  654. }
  655. // Get logs
  656. logs, err := l.DB.Logs().List(ctx, models.LogFilter{
  657. UserID: userID,
  658. ItemIDs: itemIDs,
  659. MinTime: &goal.StartTime,
  660. MaxTime: &goal.EndTime,
  661. })
  662. if err != nil {
  663. return nil, err
  664. }
  665. // Get tasks
  666. taskIDs := stringset.New()
  667. for _, log := range logs {
  668. taskIDs.Add(log.TaskID)
  669. }
  670. tasks, err := l.DB.Tasks().List(ctx, models.TaskFilter{
  671. UserID: userID,
  672. IDs: taskIDs.Strings(),
  673. })
  674. if err != nil {
  675. return nil, err
  676. }
  677. projectIDs := stringset.New()
  678. for _, task := range tasks {
  679. projectIDs.Add(task.ProjectID)
  680. }
  681. projects, err := l.DB.Projects().List(ctx, models.ProjectFilter{
  682. UserID: userID,
  683. IDs: projectIDs.Strings(),
  684. })
  685. // Apply logs
  686. result.Logs = make([]*models.GoalResultLog, 0, len(logs))
  687. for _, log := range logs {
  688. resultLog := &models.GoalResultLog{
  689. LogResult: models.LogResult{Log: *log},
  690. }
  691. contributes := false
  692. for _, task := range tasks {
  693. if task.ID == log.TaskID {
  694. resultLog.Task = &models.TaskWithProject{Task: *task}
  695. for _, project := range projects {
  696. if project.ID == task.ProjectID {
  697. resultLog.Task.Project = project
  698. break
  699. }
  700. }
  701. break
  702. }
  703. }
  704. for _, item := range result.Items {
  705. amount := log.Amount(item.ID)
  706. if amount > 0 && goal.Accepts(&item.Item, &resultLog.Task.Task) {
  707. item.CompletedAmount += amount
  708. if goal.Unweighted {
  709. if item.GroupWeight > 0 {
  710. result.CompletedAmount += amount
  711. }
  712. } else {
  713. result.CompletedAmount += amount * item.GroupWeight
  714. }
  715. contributes = true
  716. } else {
  717. amount = 0
  718. }
  719. if item.ID == log.ItemID {
  720. resultLog.Item = &item.Item
  721. if amount > 0 {
  722. resultLog.ItemCounted = true
  723. }
  724. if log.SecondaryItemID == nil {
  725. break
  726. }
  727. }
  728. if log.SecondaryItemID != nil && item.ID == *log.SecondaryItemID {
  729. if amount > 0 {
  730. resultLog.SecondaryItemCounted = true
  731. }
  732. resultLog.SecondaryItem = &item.Item
  733. }
  734. }
  735. if contributes {
  736. result.Logs = append(result.Logs, resultLog)
  737. }
  738. }
  739. }
  740. return result, nil
  741. }