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.

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