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.

527 lines
12 KiB

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. IDs: 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. IDs: 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.Logs, err = l.DB.Logs().List(ctx, models.LogFilter{
  293. UserID: task.UserID,
  294. IDs: []string{task.ID},
  295. })
  296. if err != nil {
  297. return nil, err
  298. }
  299. result.CompletedAmount = len(result.Logs)
  300. return result, nil
  301. }
  302. func (l *Loader) ListTasks(ctx context.Context, filter models.TaskFilter) ([]*models.TaskResult, error) {
  303. filter.UserID = auth.UserID(ctx)
  304. tasks, err := l.DB.Tasks().List(ctx, filter)
  305. if err != nil {
  306. return nil, err
  307. }
  308. if len(tasks) == 0 {
  309. return []*models.TaskResult{}, nil
  310. }
  311. taskIDs := make([]string, 0, len(tasks))
  312. itemIDs := stringset.New()
  313. for _, task := range tasks {
  314. taskIDs = append(taskIDs, task.ID)
  315. itemIDs.Add(task.ItemID)
  316. }
  317. logs, err := l.DB.Logs().List(ctx, models.LogFilter{
  318. UserID: auth.UserID(ctx),
  319. IDs: taskIDs,
  320. })
  321. if err != nil {
  322. return nil, err
  323. }
  324. items, err := l.DB.Items().List(ctx, models.ItemFilter{
  325. UserID: auth.UserID(ctx),
  326. IDs: itemIDs.Strings(),
  327. })
  328. if err != nil {
  329. return nil, err
  330. }
  331. results := make([]*models.TaskResult, 0, len(tasks))
  332. for _, task := range tasks {
  333. result := &models.TaskResult{
  334. Task: *task,
  335. Logs: []*models.Log{},
  336. }
  337. for _, log := range logs {
  338. if log.TaskID != task.ID {
  339. result.Logs = append(result.Logs, log)
  340. }
  341. }
  342. for _, item := range items {
  343. if item.ID == task.ItemID {
  344. result.Item = item
  345. break
  346. }
  347. }
  348. result.CompletedAmount = len(result.Logs)
  349. results = append(results, result)
  350. }
  351. return results, nil
  352. }
  353. func (l *Loader) FindGoal(ctx context.Context, id string) (*models.GoalResult, error) {
  354. goal, err := l.DB.Goals().Find(ctx, id)
  355. if err != nil {
  356. return nil, err
  357. }
  358. if goal.UserID != auth.UserID(ctx) {
  359. return nil, slerrors.NotFound("Goal")
  360. }
  361. return l.populateGoals(ctx, goal)
  362. }
  363. func (l *Loader) ListGoals(ctx context.Context, filter models.GoalFilter) ([]*models.GoalResult, error) {
  364. filter.UserID = auth.UserID(ctx)
  365. goals, err := l.DB.Goals().List(ctx, filter)
  366. if err != nil {
  367. return nil, err
  368. }
  369. results := make([]*models.GoalResult, len(goals))
  370. eg := errgroup.Group{}
  371. for i := range results {
  372. index := i // Required to avoid race condition.
  373. eg.Go(func() error {
  374. res, err := l.populateGoals(ctx, goals[index])
  375. if err != nil {
  376. return err
  377. }
  378. results[index] = res
  379. return nil
  380. })
  381. }
  382. err = eg.Wait()
  383. if err != nil {
  384. return nil, err
  385. }
  386. return results, nil
  387. }
  388. func (l *Loader) populateGoals(ctx context.Context, goal *models.Goal) (*models.GoalResult, error) {
  389. userID := auth.UserID(ctx)
  390. result := &models.GoalResult{
  391. Goal: *goal,
  392. Group: nil,
  393. Items: nil,
  394. Logs: nil,
  395. CompletedAmount: 0,
  396. }
  397. result.Group, _ = l.DB.Groups().Find(ctx, goal.GroupID)
  398. if result.Group != nil {
  399. // Get items
  400. items, err := l.DB.Items().List(ctx, models.ItemFilter{
  401. UserID: userID,
  402. GroupIDs: []string{goal.GroupID},
  403. })
  404. if err != nil {
  405. return nil, err
  406. }
  407. itemIDs := make([]string, 0, len(items))
  408. for _, item := range items {
  409. result.Items = append(result.Items, &models.GoalResultItem{
  410. Item: *item,
  411. CompletedAmount: 0,
  412. })
  413. itemIDs = append(itemIDs, item.ID)
  414. }
  415. // Get logs
  416. logs, err := l.DB.Logs().List(ctx, models.LogFilter{
  417. UserID: userID,
  418. ItemIDs: itemIDs,
  419. MinTime: &goal.StartTime,
  420. MaxTime: &goal.EndTime,
  421. })
  422. // Get tasks
  423. taskIDs := make([]string, 0, len(result.Logs))
  424. for _, log := range logs {
  425. taskIDs = append(taskIDs, log.TaskID)
  426. }
  427. tasks, err := l.DB.Tasks().List(ctx, models.TaskFilter{
  428. UserID: userID,
  429. IDs: taskIDs,
  430. })
  431. // Apply logs
  432. result.Logs = make([]*models.LogResult, 0, len(logs))
  433. for _, log := range logs {
  434. resultLog := &models.LogResult{
  435. Log: *log,
  436. }
  437. for _, task := range tasks {
  438. if task.ID == log.TaskID {
  439. resultLog.Task = task
  440. for _, item := range result.Items {
  441. if task.ItemID == item.ID {
  442. item.CompletedAmount += 1
  443. result.CompletedAmount += item.GroupWeight
  444. break
  445. }
  446. resultLog.Item = &item.Item
  447. }
  448. break
  449. }
  450. }
  451. result.Logs = append(result.Logs, resultLog)
  452. }
  453. }
  454. return result, nil
  455. }