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.

508 lines
11 KiB

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