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.

545 lines
12 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
  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. result.Task, _ = l.DB.Tasks().Find(ctx, id)
  112. result.Item, _ = l.DB.Items().Find(ctx, log.ItemID)
  113. return result, nil
  114. }
  115. func (l *Loader) ListLogs(ctx context.Context, filter models.LogFilter) ([]*models.LogResult, error) {
  116. filter.UserID = auth.UserID(ctx)
  117. logs, err := l.DB.Logs().List(ctx, filter)
  118. if err != nil {
  119. return nil, err
  120. }
  121. taskIDs := stringset.New()
  122. itemIDs := stringset.New()
  123. for _, log := range logs {
  124. taskIDs.Add(log.TaskID)
  125. itemIDs.Add(log.ItemID)
  126. }
  127. tasks, err := l.DB.Tasks().List(ctx, models.TaskFilter{
  128. UserID: auth.UserID(ctx),
  129. IDs: taskIDs.Strings(),
  130. })
  131. if err != nil {
  132. return nil, err
  133. }
  134. items, err := l.DB.Items().List(ctx, models.ItemFilter{
  135. UserID: auth.UserID(ctx),
  136. IDs: itemIDs.Strings(),
  137. })
  138. if err != nil {
  139. return nil, err
  140. }
  141. results := make([]*models.LogResult, len(logs))
  142. for i, log := range logs {
  143. results[i] = &models.LogResult{
  144. Log: *log,
  145. Task: nil,
  146. }
  147. for _, task := range tasks {
  148. if task.ID == log.TaskID {
  149. results[i].Task = task
  150. break
  151. }
  152. }
  153. for _, item := range items {
  154. if item.ID == log.ItemID {
  155. results[i].Item = item
  156. break
  157. }
  158. }
  159. }
  160. return results, nil
  161. }
  162. func (l *Loader) FindProject(ctx context.Context, id string) (*models.ProjectResult, error) {
  163. project, err := l.DB.Projects().Find(ctx, id)
  164. if err != nil {
  165. return nil, err
  166. }
  167. if project.UserID != auth.UserID(ctx) {
  168. return nil, slerrors.NotFound("Goal")
  169. }
  170. result := &models.ProjectResult{Project: *project}
  171. tasks, err := l.DB.Tasks().List(ctx, models.TaskFilter{
  172. UserID: auth.UserID(ctx),
  173. ProjectIDs: []string{project.ID},
  174. })
  175. taskIDs := make([]string, 0, len(tasks))
  176. itemIDs := stringset.New()
  177. for _, task := range tasks {
  178. taskIDs = append(taskIDs, task.ID)
  179. itemIDs.Add(task.ItemID)
  180. }
  181. logs, err := l.DB.Logs().List(ctx, models.LogFilter{
  182. UserID: auth.UserID(ctx),
  183. TaskIDs: taskIDs,
  184. })
  185. if err != nil {
  186. return nil, err
  187. }
  188. items, err := l.DB.Items().List(ctx, models.ItemFilter{
  189. UserID: auth.UserID(ctx),
  190. IDs: itemIDs.Strings(),
  191. })
  192. if err != nil {
  193. return nil, err
  194. }
  195. result.Tasks = make([]*models.TaskResult, len(tasks))
  196. for i, task := range tasks {
  197. result.Tasks[i] = &models.TaskResult{
  198. Logs: []*models.Log{},
  199. }
  200. result.Tasks[i].Task = *task
  201. for _, log := range logs {
  202. if log.TaskID == task.ID {
  203. result.Tasks[i].Logs = append(result.Tasks[i].Logs, log)
  204. }
  205. }
  206. for _, item := range items {
  207. if item.ID == task.ItemID {
  208. result.Tasks[i].Item = item
  209. break
  210. }
  211. }
  212. result.Tasks[i].CompletedAmount = len(result.Tasks[i].Logs)
  213. }
  214. return result, nil
  215. }
  216. func (l *Loader) ListProjects(ctx context.Context, filter models.ProjectFilter) ([]*models.ProjectResult, error) {
  217. filter.UserID = auth.UserID(ctx)
  218. projects, err := l.DB.Projects().List(ctx, filter)
  219. if err != nil {
  220. return nil, err
  221. }
  222. projectIDs := make([]string, 0, len(projects))
  223. for _, project := range projects {
  224. projectIDs = append(projectIDs, project.ID)
  225. }
  226. tasks, err := l.DB.Tasks().List(ctx, models.TaskFilter{
  227. UserID: auth.UserID(ctx),
  228. ProjectIDs: projectIDs,
  229. })
  230. if err != nil {
  231. return nil, err
  232. }
  233. taskIDs := make([]string, 0, len(tasks))
  234. itemIDs := stringset.New()
  235. for _, task := range tasks {
  236. taskIDs = append(taskIDs, task.ID)
  237. itemIDs.Add(task.ItemID)
  238. }
  239. logs, err := l.DB.Logs().List(ctx, models.LogFilter{
  240. UserID: auth.UserID(ctx),
  241. TaskIDs: taskIDs,
  242. })
  243. if err != nil {
  244. return nil, err
  245. }
  246. items, err := l.DB.Items().List(ctx, models.ItemFilter{
  247. UserID: auth.UserID(ctx),
  248. IDs: itemIDs.Strings(),
  249. })
  250. if err != nil {
  251. return nil, err
  252. }
  253. results := make([]*models.ProjectResult, len(projects))
  254. for i, project := range projects {
  255. results[i] = &models.ProjectResult{Project: *project}
  256. results[i].Tasks = make([]*models.TaskResult, 0, 16)
  257. for _, task := range tasks {
  258. if task.ProjectID != project.ID {
  259. continue
  260. }
  261. taskResult := &models.TaskResult{
  262. Task: *task,
  263. Logs: []*models.Log{},
  264. }
  265. for _, log := range logs {
  266. if log.TaskID == task.ID {
  267. taskResult.Logs = append(taskResult.Logs, log)
  268. }
  269. }
  270. for _, item := range items {
  271. if item.ID == task.ItemID {
  272. taskResult.Item = item
  273. break
  274. }
  275. }
  276. taskResult.CompletedAmount = len(taskResult.Logs)
  277. results[i].Tasks = append(results[i].Tasks, taskResult)
  278. }
  279. }
  280. return results, nil
  281. }
  282. func (l *Loader) FindTask(ctx context.Context, id string) (*models.TaskResult, error) {
  283. task, err := l.DB.Tasks().Find(ctx, id)
  284. if err != nil {
  285. return nil, err
  286. }
  287. if task.UserID != auth.UserID(ctx) {
  288. return nil, slerrors.NotFound("Goal")
  289. }
  290. result := &models.TaskResult{Task: *task}
  291. result.Item, _ = l.DB.Items().Find(ctx, task.ItemID)
  292. result.Project, _ = l.DB.Projects().Find(ctx, task.ProjectID)
  293. result.Logs, err = l.DB.Logs().List(ctx, models.LogFilter{
  294. UserID: task.UserID,
  295. TaskIDs: []string{task.ID},
  296. })
  297. if err != nil {
  298. return nil, err
  299. }
  300. result.CompletedAmount = len(result.Logs)
  301. return result, nil
  302. }
  303. func (l *Loader) ListTasks(ctx context.Context, filter models.TaskFilter) ([]*models.TaskResult, error) {
  304. filter.UserID = auth.UserID(ctx)
  305. tasks, err := l.DB.Tasks().List(ctx, filter)
  306. if err != nil {
  307. return nil, err
  308. }
  309. if len(tasks) == 0 {
  310. return []*models.TaskResult{}, nil
  311. }
  312. taskIDs := make([]string, 0, len(tasks))
  313. itemIDs := stringset.New()
  314. projectIDs := stringset.New()
  315. for _, task := range tasks {
  316. taskIDs = append(taskIDs, task.ID)
  317. itemIDs.Add(task.ItemID)
  318. projectIDs.Add(task.ProjectID)
  319. }
  320. logs, err := l.DB.Logs().List(ctx, models.LogFilter{
  321. UserID: auth.UserID(ctx),
  322. TaskIDs: taskIDs,
  323. })
  324. if err != nil {
  325. return nil, err
  326. }
  327. items, err := l.DB.Items().List(ctx, models.ItemFilter{
  328. UserID: auth.UserID(ctx),
  329. IDs: itemIDs.Strings(),
  330. })
  331. if err != nil {
  332. return nil, err
  333. }
  334. projects, err := l.DB.Projects().List(ctx, models.ProjectFilter{
  335. UserID: auth.UserID(ctx),
  336. IDs: projectIDs.Strings(),
  337. })
  338. if err != nil {
  339. return nil, err
  340. }
  341. results := make([]*models.TaskResult, 0, len(tasks))
  342. for _, task := range tasks {
  343. result := &models.TaskResult{
  344. Task: *task,
  345. Logs: []*models.Log{},
  346. }
  347. for _, log := range logs {
  348. if log.TaskID == task.ID {
  349. result.Logs = append(result.Logs, log)
  350. }
  351. }
  352. for _, item := range items {
  353. if item.ID == task.ItemID {
  354. result.Item = item
  355. break
  356. }
  357. }
  358. for _, project := range projects {
  359. if project.ID == task.ProjectID {
  360. result.Project = project
  361. break
  362. }
  363. }
  364. result.CompletedAmount = len(result.Logs)
  365. results = append(results, result)
  366. }
  367. return results, nil
  368. }
  369. func (l *Loader) FindGoal(ctx context.Context, id string) (*models.GoalResult, error) {
  370. goal, err := l.DB.Goals().Find(ctx, id)
  371. if err != nil {
  372. return nil, err
  373. }
  374. if goal.UserID != auth.UserID(ctx) {
  375. return nil, slerrors.NotFound("Goal")
  376. }
  377. return l.populateGoals(ctx, goal)
  378. }
  379. func (l *Loader) ListGoals(ctx context.Context, filter models.GoalFilter) ([]*models.GoalResult, error) {
  380. filter.UserID = auth.UserID(ctx)
  381. goals, err := l.DB.Goals().List(ctx, filter)
  382. if err != nil {
  383. return nil, err
  384. }
  385. results := make([]*models.GoalResult, len(goals))
  386. eg := errgroup.Group{}
  387. for i := range results {
  388. index := i // Required to avoid race condition.
  389. eg.Go(func() error {
  390. res, err := l.populateGoals(ctx, goals[index])
  391. if err != nil {
  392. return err
  393. }
  394. results[index] = res
  395. return nil
  396. })
  397. }
  398. err = eg.Wait()
  399. if err != nil {
  400. return nil, err
  401. }
  402. return results, nil
  403. }
  404. func (l *Loader) populateGoals(ctx context.Context, goal *models.Goal) (*models.GoalResult, error) {
  405. userID := auth.UserID(ctx)
  406. result := &models.GoalResult{
  407. Goal: *goal,
  408. Group: nil,
  409. Items: nil,
  410. Logs: nil,
  411. CompletedAmount: 0,
  412. }
  413. result.Group, _ = l.DB.Groups().Find(ctx, goal.GroupID)
  414. if result.Group != nil {
  415. // Get items
  416. items, err := l.DB.Items().List(ctx, models.ItemFilter{
  417. UserID: userID,
  418. GroupIDs: []string{goal.GroupID},
  419. })
  420. if err != nil {
  421. return nil, err
  422. }
  423. itemIDs := make([]string, 0, len(items))
  424. for _, item := range items {
  425. result.Items = append(result.Items, &models.GoalResultItem{
  426. Item: *item,
  427. CompletedAmount: 0,
  428. })
  429. itemIDs = append(itemIDs, item.ID)
  430. }
  431. // Get logs
  432. logs, err := l.DB.Logs().List(ctx, models.LogFilter{
  433. UserID: userID,
  434. ItemIDs: itemIDs,
  435. MinTime: &goal.StartTime,
  436. MaxTime: &goal.EndTime,
  437. })
  438. // Get tasks
  439. taskIDs := make([]string, 0, len(result.Logs))
  440. for _, log := range logs {
  441. taskIDs = append(taskIDs, log.TaskID)
  442. }
  443. tasks, err := l.DB.Tasks().List(ctx, models.TaskFilter{
  444. UserID: userID,
  445. IDs: taskIDs,
  446. })
  447. // Apply logs
  448. result.Logs = make([]*models.LogResult, 0, len(logs))
  449. for _, log := range logs {
  450. resultLog := &models.LogResult{
  451. Log: *log,
  452. }
  453. for _, task := range tasks {
  454. if task.ID == log.TaskID {
  455. resultLog.Task = task
  456. for _, item := range result.Items {
  457. if task.ItemID == item.ID {
  458. item.CompletedAmount += 1
  459. result.CompletedAmount += item.GroupWeight
  460. break
  461. }
  462. resultLog.Item = &item.Item
  463. }
  464. break
  465. }
  466. }
  467. result.Logs = append(result.Logs, resultLog)
  468. }
  469. }
  470. return result, nil
  471. }