zou 0.19.14__py3-none-any.whl → 0.20.11__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (165) hide show
  1. zou/__init__.py +1 -1
  2. zou/app/__init__.py +10 -2
  3. zou/app/api.py +2 -0
  4. zou/app/blueprints/assets/__init__.py +22 -0
  5. zou/app/blueprints/assets/resources.py +241 -4
  6. zou/app/blueprints/auth/__init__.py +4 -0
  7. zou/app/blueprints/auth/resources.py +154 -22
  8. zou/app/blueprints/breakdown/resources.py +4 -4
  9. zou/app/blueprints/chats/__init__.py +22 -0
  10. zou/app/blueprints/chats/resources.py +199 -0
  11. zou/app/blueprints/comments/resources.py +36 -19
  12. zou/app/blueprints/crud/__init__.py +12 -0
  13. zou/app/blueprints/crud/attachment_file.py +14 -5
  14. zou/app/blueprints/crud/base.py +29 -28
  15. zou/app/blueprints/crud/chat.py +13 -0
  16. zou/app/blueprints/crud/chat_message.py +13 -0
  17. zou/app/blueprints/crud/comments.py +85 -29
  18. zou/app/blueprints/crud/custom_action.py +1 -1
  19. zou/app/blueprints/crud/day_off.py +47 -9
  20. zou/app/blueprints/crud/department.py +1 -25
  21. zou/app/blueprints/crud/entity.py +46 -5
  22. zou/app/blueprints/crud/entity_type.py +13 -1
  23. zou/app/blueprints/crud/event.py +1 -1
  24. zou/app/blueprints/crud/file_status.py +1 -1
  25. zou/app/blueprints/crud/metadata_descriptor.py +24 -10
  26. zou/app/blueprints/crud/organisation.py +22 -5
  27. zou/app/blueprints/crud/output_file.py +1 -1
  28. zou/app/blueprints/crud/output_type.py +1 -1
  29. zou/app/blueprints/crud/person.py +32 -24
  30. zou/app/blueprints/crud/playlist.py +1 -1
  31. zou/app/blueprints/crud/preview_background_file.py +6 -7
  32. zou/app/blueprints/crud/preview_file.py +1 -1
  33. zou/app/blueprints/crud/project.py +14 -6
  34. zou/app/blueprints/crud/project_status.py +1 -1
  35. zou/app/blueprints/crud/schedule_item.py +4 -2
  36. zou/app/blueprints/crud/software.py +1 -1
  37. zou/app/blueprints/crud/status_automation.py +1 -1
  38. zou/app/blueprints/crud/studio.py +33 -0
  39. zou/app/blueprints/crud/task.py +47 -3
  40. zou/app/blueprints/crud/task_status.py +1 -1
  41. zou/app/blueprints/crud/task_type.py +4 -4
  42. zou/app/blueprints/crud/working_file.py +4 -8
  43. zou/app/blueprints/events/resources.py +13 -12
  44. zou/app/blueprints/export/csv/assets.py +15 -6
  45. zou/app/blueprints/export/csv/edits.py +15 -5
  46. zou/app/blueprints/export/csv/playlists.py +1 -1
  47. zou/app/blueprints/export/csv/shots.py +15 -5
  48. zou/app/blueprints/export/csv/time_spents.py +1 -1
  49. zou/app/blueprints/files/resources.py +22 -23
  50. zou/app/blueprints/index/resources.py +38 -29
  51. zou/app/blueprints/news/resources.py +25 -11
  52. zou/app/blueprints/persons/__init__.py +5 -2
  53. zou/app/blueprints/persons/resources.py +126 -120
  54. zou/app/blueprints/previews/__init__.py +18 -8
  55. zou/app/blueprints/previews/resources.py +569 -328
  56. zou/app/blueprints/projects/resources.py +1 -1
  57. zou/app/blueprints/search/resources.py +18 -6
  58. zou/app/blueprints/shots/__init__.py +5 -0
  59. zou/app/blueprints/shots/resources.py +134 -4
  60. zou/app/blueprints/source/__init__.py +6 -6
  61. zou/app/blueprints/source/csv/assets.py +10 -3
  62. zou/app/blueprints/source/csv/base.py +1 -1
  63. zou/app/blueprints/source/csv/edits.py +10 -3
  64. zou/app/blueprints/source/csv/shots.py +10 -3
  65. zou/app/blueprints/source/{edl.py → otio.py} +84 -41
  66. zou/app/blueprints/tasks/__init__.py +3 -2
  67. zou/app/blueprints/tasks/resources.py +83 -52
  68. zou/app/blueprints/user/__init__.py +9 -0
  69. zou/app/blueprints/user/resources.py +170 -12
  70. zou/app/config.py +10 -0
  71. zou/app/mixin.py +6 -5
  72. zou/app/models/attachment_file.py +10 -4
  73. zou/app/models/base.py +18 -13
  74. zou/app/models/build_job.py +7 -4
  75. zou/app/models/chat.py +44 -0
  76. zou/app/models/chat_message.py +37 -0
  77. zou/app/models/comment.py +1 -0
  78. zou/app/models/day_off.py +3 -0
  79. zou/app/models/entity.py +4 -6
  80. zou/app/models/entity_type.py +2 -0
  81. zou/app/models/organisation.py +14 -15
  82. zou/app/models/person.py +6 -1
  83. zou/app/models/project.py +3 -0
  84. zou/app/models/search_filter.py +11 -0
  85. zou/app/models/search_filter_group.py +10 -0
  86. zou/app/models/serializer.py +17 -17
  87. zou/app/models/status_automation.py +2 -0
  88. zou/app/models/studio.py +13 -0
  89. zou/app/models/subscription.py +2 -2
  90. zou/app/models/task.py +6 -1
  91. zou/app/models/task_status.py +1 -0
  92. zou/app/models/task_type.py +1 -0
  93. zou/app/models/working_file.py +1 -1
  94. zou/app/services/assets_service.py +101 -14
  95. zou/app/services/auth_service.py +17 -44
  96. zou/app/services/breakdown_service.py +37 -5
  97. zou/app/services/chats_service.py +279 -0
  98. zou/app/services/comments_service.py +110 -65
  99. zou/app/services/concepts_service.py +4 -12
  100. zou/app/services/deletion_service.py +43 -30
  101. zou/app/services/edits_service.py +5 -11
  102. zou/app/services/emails_service.py +4 -4
  103. zou/app/services/entities_service.py +17 -2
  104. zou/app/services/events_service.py +12 -4
  105. zou/app/services/exception.py +5 -5
  106. zou/app/services/names_service.py +7 -2
  107. zou/app/services/news_service.py +17 -9
  108. zou/app/services/persons_service.py +38 -21
  109. zou/app/services/playlists_service.py +8 -7
  110. zou/app/services/preview_files_service.py +137 -10
  111. zou/app/services/projects_service.py +5 -14
  112. zou/app/services/shots_service.py +221 -49
  113. zou/app/services/sync_service.py +46 -42
  114. zou/app/services/tasks_service.py +185 -46
  115. zou/app/services/time_spents_service.py +67 -20
  116. zou/app/services/user_service.py +350 -107
  117. zou/app/stores/auth_tokens_store.py +2 -1
  118. zou/app/stores/file_store.py +18 -0
  119. zou/app/stores/publisher_store.py +7 -7
  120. zou/app/stores/queue_store.py +1 -0
  121. zou/app/swagger.py +36 -20
  122. zou/app/utils/cache.py +2 -0
  123. zou/app/utils/commands.py +104 -7
  124. zou/app/utils/csv_utils.py +1 -4
  125. zou/app/utils/date_helpers.py +33 -17
  126. zou/app/utils/dbhelpers.py +14 -1
  127. zou/app/utils/emails.py +2 -2
  128. zou/app/utils/fido.py +22 -0
  129. zou/app/utils/flask.py +1 -0
  130. zou/app/utils/query.py +54 -6
  131. zou/app/utils/redis.py +11 -0
  132. zou/app/utils/saml.py +51 -0
  133. zou/app/utils/string.py +2 -0
  134. zou/app/utils/thumbnail.py +4 -2
  135. zou/cli.py +76 -18
  136. zou/debug.py +4 -2
  137. zou/event_stream.py +122 -165
  138. zou/job_settings.py +1 -0
  139. zou/migrations/env.py +0 -0
  140. zou/migrations/utils/base.py +6 -6
  141. zou/migrations/versions/1bb55759146f_add_table_studio.py +67 -0
  142. zou/migrations/versions/1fab8c420678_add_attachments_to_message_chats.py +56 -0
  143. zou/migrations/versions/23122f290ca2_add_entity_chat_models.py +149 -0
  144. zou/migrations/versions/32f134ff1201_add_is_shared_flag_to_filters.py +33 -0
  145. zou/migrations/versions/57222395f2be_add_statusautomation_import_last_revision.py +41 -0
  146. zou/migrations/versions/59a7445a966c_add_entity_is_shared.py +41 -0
  147. zou/migrations/versions/5b980f0dc365_add_comment_links.py +35 -0
  148. zou/migrations/versions/680c64565f9d_for_searchfiltergroup_is_shared.py +35 -0
  149. zou/migrations/versions/8e67c183bed7_add_preference_fields.py +71 -0
  150. zou/migrations/versions/92b40d79ad3f_allow_message_attachments.py +38 -0
  151. zou/migrations/versions/971dbf5a0faf_add_short_name_for_asset_type_entity_.py +33 -0
  152. zou/migrations/versions/9b85c14fa8a7_add_day_off_new_columns.py +68 -0
  153. zou/migrations/versions/9d3bb33c6fc6_add_department_keys_to_filter_models.py +73 -0
  154. zou/migrations/versions/a252a094e977_add_descriptions_for_entities_tasks_and_.py +40 -0
  155. zou/migrations/versions/be56dc0fb760_for_is_shared_disallow_nullable.py +102 -0
  156. zou/migrations/versions/ca28796a2a62_add_is_done_field_to_the_task_model.py +108 -0
  157. zou/migrations/versions/f344b867a911_for_description_of_entity_task_working_.py +75 -0
  158. zou/remote/config_payload.py +2 -1
  159. zou/utils/movie.py +14 -4
  160. {zou-0.19.14.dist-info → zou-0.20.11.dist-info}/METADATA +75 -69
  161. {zou-0.19.14.dist-info → zou-0.20.11.dist-info}/RECORD +164 -135
  162. {zou-0.19.14.dist-info → zou-0.20.11.dist-info}/WHEEL +1 -1
  163. {zou-0.19.14.dist-info → zou-0.20.11.dist-info}/LICENSE +0 -0
  164. {zou-0.19.14.dist-info → zou-0.20.11.dist-info}/entry_points.txt +0 -0
  165. {zou-0.19.14.dist-info → zou-0.20.11.dist-info}/top_level.txt +0 -0
@@ -2,7 +2,7 @@ import datetime
2
2
 
3
3
 
4
4
  from flask import abort, request
5
- from flask_restful import Resource
5
+ from flask_restful import Resource, inputs
6
6
  from flask_jwt_extended import jwt_required
7
7
 
8
8
  from zou.app.services.exception import (
@@ -28,7 +28,7 @@ from zou.app.services import (
28
28
  user_service,
29
29
  concepts_service,
30
30
  )
31
- from zou.app.utils import events, query, permissions
31
+ from zou.app.utils import events, query, permissions, date_helpers
32
32
  from zou.app.mixin import ArgsMixin
33
33
 
34
34
 
@@ -69,13 +69,8 @@ class AddPreviewResource(Resource, ArgsMixin):
69
69
  """
70
70
  args = self.get_args([("revision", 0, False, int)])
71
71
 
72
- task = tasks_service.get_task(task_id)
73
- user_service.check_project_access(task["project_id"])
74
- user_service.check_entity_access(task["entity_id"])
72
+ user_service.check_task_access(task_id)
75
73
 
76
- tasks_service.clear_comment_cache(comment_id)
77
- comment = tasks_service.get_comment(comment_id)
78
- tasks_service.get_task_status(comment["task_status_id"])
79
74
  person = persons_service.get_current_user()
80
75
  preview_file = tasks_service.add_preview_file_to_comment(
81
76
  comment_id, person["id"], task_id, args["revision"]
@@ -122,9 +117,7 @@ class AddExtraPreviewResource(Resource):
122
117
  201:
123
118
  description: Preview added to given comment
124
119
  """
125
- task = tasks_service.get_task(task_id)
126
- user_service.check_project_access(task["project_id"])
127
- user_service.check_entity_access(task["entity_id"])
120
+ user_service.check_task_access(task_id)
128
121
  tasks_service.get_comment(comment_id)
129
122
 
130
123
  person = persons_service.get_current_user()
@@ -194,9 +187,7 @@ class TaskPreviewsResource(Resource):
194
187
  200:
195
188
  description: Previews linked to given task
196
189
  """
197
- task = tasks_service.get_task(task_id)
198
- user_service.check_project_access(task["project_id"])
199
- user_service.check_entity_access(task["entity_id"])
190
+ user_service.check_task_access(task_id)
200
191
  return files_service.get_preview_files_for_task(task_id)
201
192
 
202
193
 
@@ -223,9 +214,7 @@ class TaskCommentsResource(Resource):
223
214
  200:
224
215
  description: Comments linked to given task
225
216
  """
226
- task = tasks_service.get_task(task_id)
227
- user_service.check_project_access(task["project_id"])
228
- user_service.check_entity_access(task["entity_id"])
217
+ user_service.check_task_access(task_id)
229
218
  is_client = permissions.has_client_permissions()
230
219
  is_manager = permissions.has_manager_permissions()
231
220
  is_supervisor = permissions.has_supervisor_permissions()
@@ -756,7 +745,7 @@ class ToReviewResource(Resource, ArgsMixin):
756
745
  try:
757
746
  task = tasks_service.get_task(task_id)
758
747
  user_service.check_project_access(task["project_id"])
759
- user_service.check_entity_access(task["project_id"])
748
+ user_service.check_entity_access(task["entity_id"])
760
749
 
761
750
  if person_id is not None:
762
751
  person = persons_service.get_person(person_id)
@@ -766,7 +755,7 @@ class ToReviewResource(Resource, ArgsMixin):
766
755
  preview_path = self.get_preview_path(task, name, revision)
767
756
 
768
757
  task = tasks_service.task_to_review(
769
- task["id"], person, comment, preview_path, change_status
758
+ task_id, person, comment, preview_path, change_status
770
759
  )
771
760
  except PersonNotFoundException:
772
761
  return {"error": True, "message": "Cannot find given person."}, 400
@@ -793,7 +782,11 @@ class ToReviewResource(Resource, ArgsMixin):
793
782
  ("comment", ""),
794
783
  ("name", "main"),
795
784
  {"name": "revision", "default": 1, "type": int},
796
- {"name": "change_status", "default": True, "type": bool},
785
+ {
786
+ "name": "change_status",
787
+ "default": True,
788
+ "type": inputs.boolean,
789
+ },
797
790
  ]
798
791
  )
799
792
 
@@ -844,7 +837,7 @@ class ClearAssignationResource(Resource, ArgsMixin):
844
837
  tasks = []
845
838
  for task_id in task_ids:
846
839
  try:
847
- user_service.check_task_departement_access_for_unassign(
840
+ user_service.check_task_department_access_for_unassign(
848
841
  task_id, person_id
849
842
  )
850
843
  tasks_service.clear_assignation(task_id, person_id=person_id)
@@ -909,7 +902,6 @@ class TasksAssignResource(Resource, ArgsMixin):
909
902
  200:
910
903
  description: Given tasks lists assigned to given person
911
904
  """
912
- user_service.check_person_is_not_bot(person_id)
913
905
  args = self.get_args(
914
906
  [
915
907
  {
@@ -925,11 +917,14 @@ class TasksAssignResource(Resource, ArgsMixin):
925
917
  current_user = persons_service.get_current_user()
926
918
  for task_id in args["task_ids"]:
927
919
  try:
928
- user_service.check_task_departement_access(task_id, person_id)
929
- task = self.assign_task(task_id, person_id, current_user["id"])
930
- notifications_service.create_assignation_notification(
920
+ user_service.check_person_is_not_bot(person_id)
921
+ user_service.check_task_department_access(task_id, person_id)
922
+ task = tasks_service.assign_task(
931
923
  task_id, person_id, current_user["id"]
932
924
  )
925
+ notifications_service.create_assignation_notification(
926
+ task_id, person_id
927
+ )
933
928
  tasks.append(task)
934
929
  except TaskNotFoundException:
935
930
  pass
@@ -942,9 +937,6 @@ class TasksAssignResource(Resource, ArgsMixin):
942
937
 
943
938
  return tasks
944
939
 
945
- def assign_task(self, task_id, person_id, assigner_id):
946
- return tasks_service.assign_task(task_id, person_id, assigner_id)
947
-
948
940
 
949
941
  class TaskAssignResource(Resource, ArgsMixin):
950
942
  """
@@ -993,11 +985,13 @@ class TaskAssignResource(Resource, ArgsMixin):
993
985
  ]
994
986
  )
995
987
  person_id = args["person_id"]
988
+ current_user = persons_service.get_current_user()
996
989
  try:
997
990
  user_service.check_person_is_not_bot(person_id)
998
- task = tasks_service.get_task(task_id)
999
- user_service.check_task_departement_access(task_id, person_id)
1000
- self.assign_task(task_id, person_id)
991
+ user_service.check_task_department_access(task_id, person_id)
992
+ task = tasks_service.assign_task(
993
+ task_id, person_id, current_user["id"]
994
+ )
1001
995
  notifications_service.create_assignation_notification(
1002
996
  task_id, person_id
1003
997
  )
@@ -1007,9 +1001,6 @@ class TaskAssignResource(Resource, ArgsMixin):
1007
1001
 
1008
1002
  return task
1009
1003
 
1010
- def assign_task(self, task_id, person_id):
1011
- return tasks_service.assign_task(task_id, person_id)
1012
-
1013
1004
 
1014
1005
  class TaskFullResource(Resource):
1015
1006
  """
@@ -1134,7 +1125,7 @@ class SetTimeSpentResource(Resource, ArgsMixin):
1134
1125
  time_spent = tasks_service.create_or_update_time_spent(
1135
1126
  task_id,
1136
1127
  person_id,
1137
- datetime.datetime.strptime(date, "%Y-%m-%d"),
1128
+ date_helpers.get_date_from_string(date),
1138
1129
  args["duration"],
1139
1130
  )
1140
1131
  return time_spent, 201
@@ -1275,9 +1266,7 @@ class GetTimeSpentResource(Resource):
1275
1266
  404:
1276
1267
  description: Wrong date format
1277
1268
  """
1278
- task = tasks_service.get_task(task_id)
1279
- user_service.check_project_access(task["project_id"])
1280
- user_service.check_entity_access(task["entity_id"])
1269
+ user_service.check_task_access(task_id)
1281
1270
  return tasks_service.get_time_spents(task_id)
1282
1271
 
1283
1272
 
@@ -1313,9 +1302,7 @@ class GetTimeSpentDateResource(Resource):
1313
1302
  description: Wrong date format
1314
1303
  """
1315
1304
  try:
1316
- task = tasks_service.get_task(task_id)
1317
- user_service.check_project_access(task["project_id"])
1318
- user_service.check_entity_access(task["entity_id"])
1305
+ user_service.check_task_access(task_id)
1319
1306
  return tasks_service.get_time_spents(task_id, date)
1320
1307
  except WrongDateFormatException:
1321
1308
  abort(404)
@@ -1465,7 +1452,6 @@ class ProjectTasksResource(Resource, ArgsMixin):
1465
1452
  """
1466
1453
 
1467
1454
  @jwt_required()
1468
- @permissions.require_admin
1469
1455
  def get(self, project_id):
1470
1456
  """
1471
1457
  Retrieve all tasks related to given project.
@@ -1480,13 +1466,34 @@ class ProjectTasksResource(Resource, ArgsMixin):
1480
1466
  type: string
1481
1467
  format: UUID
1482
1468
  x-example: a24a6ea4-ce75-4665-a070-57453082c25
1469
+ - in: query
1470
+ name: page
1471
+ required: False
1472
+ type: integer
1473
+ x-example: 1
1474
+ - in: query
1475
+ name: task_type_id
1476
+ required: False
1477
+ type: string
1478
+ format: UUID
1479
+ x-example: a24a6ea4-ce75-4665-a070-57453082c25
1480
+ - in: query
1481
+ name: episode_id
1482
+ required: False
1483
+ type: string
1484
+ format: UUID
1485
+ x-example: a24a6ea4-ce75-4665-a070-57453082c25
1483
1486
  responses:
1484
1487
  200:
1485
1488
  description: All tasks related to given project
1486
1489
  """
1487
1490
  projects_service.get_project(project_id)
1488
1491
  page = self.get_page()
1489
- return tasks_service.get_tasks_for_project(project_id, page)
1492
+ task_type_id = self.get_task_type_id()
1493
+ episode_id = self.get_episode_id()
1494
+ return tasks_service.get_tasks_for_project(
1495
+ project_id, page, task_type_id=task_type_id, episode_id=episode_id
1496
+ )
1490
1497
 
1491
1498
 
1492
1499
  class ProjectCommentsResource(Resource, ArgsMixin):
@@ -1496,7 +1503,6 @@ class ProjectCommentsResource(Resource, ArgsMixin):
1496
1503
  """
1497
1504
 
1498
1505
  @jwt_required()
1499
- @permissions.require_admin
1500
1506
  def get(self, project_id):
1501
1507
  """
1502
1508
  Retrieve all comments to tasks related to given project.
@@ -1511,13 +1517,25 @@ class ProjectCommentsResource(Resource, ArgsMixin):
1511
1517
  type: string
1512
1518
  format: UUID
1513
1519
  x-example: a24a6ea4-ce75-4665-a070-57453082c25
1520
+ - in: query
1521
+ name: limit
1522
+ type: integer
1523
+ default: 100
1524
+ x-example: 100
1514
1525
  responses:
1515
1526
  200:
1516
1527
  description: All comments to tasks related to given project
1517
1528
  """
1518
1529
  projects_service.get_project(project_id)
1530
+ user_service.check_project_access(project_id)
1531
+ if (
1532
+ permissions.has_vendor_permissions()
1533
+ or permissions.has_client_permissions()
1534
+ ):
1535
+ raise permissions.PermissionDenied
1519
1536
  page = self.get_page()
1520
- return tasks_service.get_comments_for_project(project_id, page)
1537
+ limit = self.get_limit()
1538
+ return tasks_service.get_comments_for_project(project_id, page, limit)
1521
1539
 
1522
1540
 
1523
1541
  class ProjectPreviewFilesResource(Resource, ArgsMixin):
@@ -1576,16 +1594,10 @@ class SetTaskMainPreviewResource(Resource):
1576
1594
  preview_file = preview_files_service.get_last_preview_file_for_task(
1577
1595
  task_id
1578
1596
  )
1579
- entity = entities_service.get_entity(task["entity_id"])
1580
1597
  if preview_file is not None:
1581
- entities_service.update_entity_preview(
1598
+ entity = entities_service.update_entity_preview(
1582
1599
  task["entity_id"], preview_file["id"]
1583
1600
  )
1584
- assets_service.clear_asset_cache(entity["id"])
1585
- shots_service.clear_shot_cache(entity["id"])
1586
- edits_service.clear_edit_cache(entity["id"])
1587
- shots_service.clear_episode_cache(entity["id"])
1588
- shots_service.clear_sequence_cache(entity["id"])
1589
1601
  return entity
1590
1602
 
1591
1603
 
@@ -1739,3 +1751,22 @@ class OpenTasksResource(Resource, ArgsMixin):
1739
1751
  page=args["page"],
1740
1752
  limit=args["limit"],
1741
1753
  )
1754
+
1755
+
1756
+ class OpenTasksStatsResource(Resource, ArgsMixin):
1757
+
1758
+ @jwt_required()
1759
+ def get(self):
1760
+ """
1761
+ Return task amount, task done amount, total estimation, total duration
1762
+ by status by task type by project for open projects. It aggregates the
1763
+ result at the project level.
1764
+ ---
1765
+ tags:
1766
+ - Tasks
1767
+ responses:
1768
+ 200:
1769
+ description: A dict organized by project that contains
1770
+ the results for each task type and status pairs.
1771
+ """
1772
+ return tasks_service.get_open_tasks_stats()
@@ -11,6 +11,8 @@ from zou.app.blueprints.user.resources import (
11
11
  ContextResource,
12
12
  DateTimeSpentsResource,
13
13
  DayOffResource,
14
+ ChatsResource,
15
+ JoinChatResource,
14
16
  OpenProjectsResource,
15
17
  ProjectEpisodesResource,
16
18
  ProjectSequencesResource,
@@ -31,6 +33,7 @@ from zou.app.blueprints.user.resources import (
31
33
  FilterGroupResource,
32
34
  FilterGroupsResource,
33
35
  DesktopLoginLogsResource,
36
+ MarkAllNotificationsAsReadResource,
34
37
  NotificationsResource,
35
38
  NotificationResource,
36
39
  HasTaskSubscribedResource,
@@ -85,6 +88,8 @@ routes = [
85
88
  ("/data/user/notifications", NotificationsResource),
86
89
  ("/data/user/notifications/<notification_id>", NotificationResource),
87
90
  ("/data/user/tasks/<task_id>/subscribed", HasTaskSubscribedResource),
91
+ ("/data/user/chats", ChatsResource),
92
+ ("/actions/user/chats/<entity_id>/join", JoinChatResource),
88
93
  ("/actions/user/tasks/<task_id>/subscribe", TaskSubscribeResource),
89
94
  ("/actions/user/tasks/<task_id>/unsubscribe", TaskUnsubscribeResource),
90
95
  ("/actions/user/clear-avatar", ClearAvatarResource),
@@ -104,6 +109,10 @@ routes = [
104
109
  "/actions/user/sequences/<sequence_id>/task-types/<task_type_id>/unsubscribe",
105
110
  SequenceUnsubscribeResource,
106
111
  ),
112
+ (
113
+ "/actions/user/notifications/mark-all-as-read",
114
+ MarkAllNotificationsAsReadResource,
115
+ ),
107
116
  ]
108
117
 
109
118
  blueprint = Blueprint("user", "user")
@@ -1,20 +1,18 @@
1
- import datetime
2
-
3
- from flask import abort
4
- from flask_restful import Resource
1
+ from flask import abort, request
2
+ from flask_restful import Resource, inputs
5
3
 
6
4
  from zou.app.mixin import ArgsMixin
7
-
8
5
  from zou.app.services import (
9
6
  assets_service,
7
+ chats_service,
8
+ entities_service,
10
9
  persons_service,
11
10
  projects_service,
12
11
  shots_service,
13
12
  time_spents_service,
14
13
  user_service,
15
14
  )
16
-
17
-
15
+ from zou.app.utils import date_helpers
18
16
  from zou.app.services.exception import WrongDateFormatException
19
17
 
20
18
 
@@ -544,6 +542,9 @@ class FiltersResource(Resource, ArgsMixin):
544
542
  arguments["query"],
545
543
  arguments["project_id"],
546
544
  arguments["entity_type"],
545
+ arguments["is_shared"],
546
+ arguments["search_filter_group_id"],
547
+ department_id=arguments["department_id"],
547
548
  ),
548
549
  201,
549
550
  )
@@ -556,6 +557,9 @@ class FiltersResource(Resource, ArgsMixin):
556
557
  ("list_type", "todo", True),
557
558
  ("project_id", None, False),
558
559
  ("entity_type", None, False),
560
+ ("is_shared", False, False, inputs.boolean),
561
+ ("search_filter_group_id", None, False),
562
+ ("department_id", None, False),
559
563
  ]
560
564
  )
561
565
 
@@ -587,6 +591,9 @@ class FilterResource(Resource, ArgsMixin):
587
591
  ("name", None, False),
588
592
  ("search_query", None, False),
589
593
  ("search_filter_group_id", None, False),
594
+ ("is_shared", None, False, inputs.boolean),
595
+ ("project_id", None, None),
596
+ ("department_id", None, None),
590
597
  ]
591
598
  )
592
599
  data = self.clear_empty_fields(
@@ -659,19 +666,27 @@ class FilterGroupsResource(Resource, ArgsMixin):
659
666
  name: entity_type
660
667
  required: False
661
668
  type: string
669
+ - in: formData
670
+ name: is_shared
671
+ required: False
672
+ type: boolean
662
673
  - in: formData
663
674
  name: project_id
664
675
  required: True
665
676
  type: string
666
677
  format: UUID
667
678
  example: a24a6ea4-ce75-4665-a070-57453082c25
679
+ - in: formData
680
+ name: department_id
681
+ required: False
682
+ format: UUID
683
+ example: a24a6ea4-ce75-4665-a070-57453082c25
668
684
  responses:
669
685
  201:
670
686
  description: Filter groups for the current user and only for
671
687
  open projects created.
672
688
  """
673
689
  arguments = self.get_arguments()
674
-
675
690
  return (
676
691
  user_service.create_filter_group(
677
692
  arguments["list_type"],
@@ -679,6 +694,8 @@ class FilterGroupsResource(Resource, ArgsMixin):
679
694
  arguments["color"],
680
695
  arguments["project_id"],
681
696
  arguments["entity_type"],
697
+ arguments["is_shared"],
698
+ arguments["department_id"],
682
699
  ),
683
700
  201,
684
701
  )
@@ -690,7 +707,9 @@ class FilterGroupsResource(Resource, ArgsMixin):
690
707
  ("color", "", True),
691
708
  ("list_type", "todo", True),
692
709
  ("project_id", None, False),
710
+ ("is_shared", False, False, inputs.boolean),
693
711
  ("entity_type", None, False),
712
+ ("department_id", None, False),
694
713
  ]
695
714
  )
696
715
 
@@ -727,6 +746,28 @@ class FilterGroupResource(Resource, ArgsMixin):
727
746
  type: string
728
747
  format: UUID
729
748
  x-example: a24a6ea4-ce75-4665-a070-57453082c25
749
+ - in: formData
750
+ name: name
751
+ type: string
752
+ x-example: Name of the filter group
753
+ - in: formData
754
+ name: color
755
+ type: string
756
+ x-example: Color of the filter group
757
+ - in: formData
758
+ name: is_shared
759
+ type: boolean
760
+ x-example: True
761
+ - in: formData
762
+ name: project_id
763
+ type: string
764
+ format: UUID
765
+ x-example: a24a6ea4-ce75-4665-a070-57453082c25
766
+ - in: formData
767
+ name: department_id
768
+ type: string
769
+ format: UUID
770
+ x-example: a24a6ea4-ce75-4665-a070-57453082c25
730
771
  responses:
731
772
  200:
732
773
  description: Given filter group with updated data
@@ -735,8 +776,12 @@ class FilterGroupResource(Resource, ArgsMixin):
735
776
  [
736
777
  ("name", None, False),
737
778
  ("color", None, False),
779
+ ("is_shared", None, False, inputs.boolean),
780
+ ("project_id", None, None),
781
+ ("department_id", None, None),
738
782
  ]
739
783
  )
784
+
740
785
  data = self.clear_empty_fields(data)
741
786
  user_filter = user_service.update_filter_group(filter_group_id, data)
742
787
  return user_filter, 200
@@ -799,7 +844,9 @@ class DesktopLoginLogsResource(Resource, ArgsMixin):
799
844
  201:
800
845
  description: Desktop login log created
801
846
  """
802
- arguments = self.get_args(["date", datetime.datetime.utcnow()])
847
+ arguments = self.get_args(
848
+ ["date", date_helpers.get_utc_now_datetime()]
849
+ )
803
850
  current_user = persons_service.get_current_user()
804
851
  desktop_login_log = persons_service.create_desktop_login_logs(
805
852
  current_user["id"], arguments["date"]
@@ -836,13 +883,22 @@ class NotificationsResource(Resource, ArgsMixin):
836
883
  task_status_id,
837
884
  notification_type,
838
885
  ) = self.get_arguments()
886
+
887
+ read = None
888
+ if request.args.get("read", None) is not None:
889
+ read = self.get_bool_parameter("read")
890
+ watching = None
891
+ if request.args.get("watching", None) is not None:
892
+ watching = self.get_bool_parameter("watching")
893
+ print("watching", watching)
839
894
  notifications = user_service.get_last_notifications(
840
895
  before=before,
841
896
  task_type_id=task_type_id,
842
897
  task_status_id=task_status_id,
843
898
  notification_type=notification_type,
899
+ read=read,
900
+ watching=watching,
844
901
  )
845
- user_service.mark_notifications_as_read()
846
902
  return notifications
847
903
 
848
904
  def get_arguments(self):
@@ -855,7 +911,7 @@ class NotificationsResource(Resource, ArgsMixin):
855
911
  )
856
912
 
857
913
 
858
- class NotificationResource(Resource):
914
+ class NotificationResource(Resource, ArgsMixin):
859
915
  """
860
916
  Return notification matching given id, only if it's a notification that
861
917
  belongs to current user.
@@ -882,6 +938,42 @@ class NotificationResource(Resource):
882
938
  """
883
939
  return user_service.get_notification(notification_id)
884
940
 
941
+ def put(self, notification_id):
942
+ """
943
+ Change notification read status.
944
+ ---
945
+ tags:
946
+ - User
947
+ parameters:
948
+ - in: path
949
+ name: notification_id
950
+ required: True
951
+ type: string
952
+ format: UUID
953
+ x-example: a24a6ea4-ce75-4665-a070-57453082c25
954
+ responses:
955
+ 200:
956
+ description: Notification
957
+ """
958
+ data = self.get_args([("read", None, False, inputs.boolean)])
959
+ return user_service.update_notification(notification_id, data["read"])
960
+
961
+
962
+ class MarkAllNotificationsAsReadResource(Resource):
963
+
964
+ def post(self):
965
+ """
966
+ Mark all notifications as read for the current user.
967
+ ---
968
+ tags:
969
+ - User
970
+ responses:
971
+ 200:
972
+ description: All notifications marked as read
973
+ """
974
+ user_service.mark_notifications_as_read()
975
+ return {"success": True}
976
+
885
977
 
886
978
  class HasTaskSubscribedResource(Resource):
887
979
  """
@@ -893,7 +985,7 @@ class HasTaskSubscribedResource(Resource):
893
985
  Return true if current user has subscribed to given task.
894
986
  ---
895
987
  tags:
896
- - User
988
+ - User
897
989
  parameters:
898
990
  - in: path
899
991
  name: task_id
@@ -1259,3 +1351,69 @@ class ClearAvatarResource(Resource):
1259
1351
  user = persons_service.get_current_user()
1260
1352
  persons_service.clear_avatar(user["id"])
1261
1353
  return "", 204
1354
+
1355
+
1356
+ class ChatsResource(Resource):
1357
+
1358
+ def get(self):
1359
+ """
1360
+ Return chats where user is participant.
1361
+ ---
1362
+ tags:
1363
+ - User
1364
+ responses:
1365
+ 200:
1366
+ description: Chats where user is participant
1367
+ """
1368
+ user = persons_service.get_current_user()
1369
+ return chats_service.get_chats_for_person(user["id"])
1370
+
1371
+
1372
+ class JoinChatResource(Resource):
1373
+
1374
+ def post(self, entity_id):
1375
+ """
1376
+ Join chat for given entity (be listed as participant).
1377
+ ---
1378
+ tags:
1379
+ - User
1380
+ parameters:
1381
+ - in: path
1382
+ name: entity_id
1383
+ required: True
1384
+ type: string
1385
+ format: UUID
1386
+ x-example: a24a6ea4-ce75-4665-a070-57453082c25
1387
+ responses:
1388
+ 201:
1389
+ description: Chat joined
1390
+ """
1391
+ entity = entities_service.get_entity(entity_id)
1392
+ user_service.check_project_access(entity["project_id"])
1393
+ user_service.check_entity_access(entity["id"])
1394
+ person = persons_service.get_current_user()
1395
+ return chats_service.join_chat(entity_id, person["id"])
1396
+
1397
+ def delete(self, entity_id):
1398
+ """
1399
+ Leave chat for given entity (be removed from participants).
1400
+ ---
1401
+ tags:
1402
+ - User
1403
+ parameters:
1404
+ - in: path
1405
+ name: entity_id
1406
+ required: True
1407
+ type: string
1408
+ format: UUID
1409
+ x-example: a24a6ea4-ce75-4665-a070-57453082c25
1410
+ responses:
1411
+ 204:
1412
+ description: empty response
1413
+ """
1414
+ entity = entities_service.get_entity(entity_id)
1415
+ user_service.check_project_access(entity["project_id"])
1416
+ user_service.check_entity_access(entity["id"])
1417
+ person = persons_service.get_current_user()
1418
+ chats_service.leave_chat(entity_id, person["id"])
1419
+ return "", 204