zou 0.19.15__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 (164) 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} +82 -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/query.py +54 -6
  130. zou/app/utils/redis.py +11 -0
  131. zou/app/utils/saml.py +51 -0
  132. zou/app/utils/string.py +2 -0
  133. zou/app/utils/thumbnail.py +4 -2
  134. zou/cli.py +76 -18
  135. zou/debug.py +4 -2
  136. zou/event_stream.py +122 -165
  137. zou/job_settings.py +1 -0
  138. zou/migrations/env.py +0 -0
  139. zou/migrations/utils/base.py +6 -6
  140. zou/migrations/versions/1bb55759146f_add_table_studio.py +67 -0
  141. zou/migrations/versions/1fab8c420678_add_attachments_to_message_chats.py +56 -0
  142. zou/migrations/versions/23122f290ca2_add_entity_chat_models.py +149 -0
  143. zou/migrations/versions/32f134ff1201_add_is_shared_flag_to_filters.py +33 -0
  144. zou/migrations/versions/57222395f2be_add_statusautomation_import_last_revision.py +41 -0
  145. zou/migrations/versions/59a7445a966c_add_entity_is_shared.py +41 -0
  146. zou/migrations/versions/5b980f0dc365_add_comment_links.py +35 -0
  147. zou/migrations/versions/680c64565f9d_for_searchfiltergroup_is_shared.py +35 -0
  148. zou/migrations/versions/8e67c183bed7_add_preference_fields.py +71 -0
  149. zou/migrations/versions/92b40d79ad3f_allow_message_attachments.py +38 -0
  150. zou/migrations/versions/971dbf5a0faf_add_short_name_for_asset_type_entity_.py +33 -0
  151. zou/migrations/versions/9b85c14fa8a7_add_day_off_new_columns.py +68 -0
  152. zou/migrations/versions/9d3bb33c6fc6_add_department_keys_to_filter_models.py +73 -0
  153. zou/migrations/versions/a252a094e977_add_descriptions_for_entities_tasks_and_.py +40 -0
  154. zou/migrations/versions/be56dc0fb760_for_is_shared_disallow_nullable.py +102 -0
  155. zou/migrations/versions/ca28796a2a62_add_is_done_field_to_the_task_model.py +108 -0
  156. zou/migrations/versions/f344b867a911_for_description_of_entity_task_working_.py +75 -0
  157. zou/remote/config_payload.py +2 -1
  158. zou/utils/movie.py +14 -4
  159. {zou-0.19.15.dist-info → zou-0.20.11.dist-info}/METADATA +75 -69
  160. {zou-0.19.15.dist-info → zou-0.20.11.dist-info}/RECORD +163 -134
  161. {zou-0.19.15.dist-info → zou-0.20.11.dist-info}/WHEEL +1 -1
  162. {zou-0.19.15.dist-info → zou-0.20.11.dist-info}/LICENSE +0 -0
  163. {zou-0.19.15.dist-info → zou-0.20.11.dist-info}/entry_points.txt +0 -0
  164. {zou-0.19.15.dist-info → zou-0.20.11.dist-info}/top_level.txt +0 -0
@@ -2,7 +2,7 @@ from datetime import timedelta
2
2
  from operator import itemgetter
3
3
  from sqlalchemy.orm import aliased
4
4
  from sqlalchemy.exc import IntegrityError, StatementError
5
- from sqlalchemy import func
5
+ from sqlalchemy import func, or_
6
6
 
7
7
  from zou.app.utils import (
8
8
  cache,
@@ -20,6 +20,7 @@ from zou.app.models.entity import (
20
20
  )
21
21
  from zou.app.models.person import Person
22
22
  from zou.app.models.project import Project
23
+ from zou.app.models.preview_file import PreviewFile
23
24
  from zou.app.models.schedule_item import ScheduleItem
24
25
  from zou.app.models.subscription import Subscription
25
26
  from zou.app.models.task import Task
@@ -49,7 +50,7 @@ from zou.app.services.exception import (
49
50
 
50
51
  def clear_shot_cache(shot_id):
51
52
  cache.cache.delete_memoized(get_shot, shot_id)
52
- cache.cache.delete_memoized(get_shot_with_relations, shot_id)
53
+ cache.cache.delete_memoized(get_shot, shot_id, True)
53
54
  cache.cache.delete_memoized(get_full_shot, shot_id)
54
55
 
55
56
 
@@ -250,8 +251,10 @@ def get_shots_and_tasks(criterions={}):
250
251
  Task.end_date,
251
252
  Task.start_date,
252
253
  Task.due_date,
254
+ Task.done_date,
253
255
  Task.last_comment_date,
254
256
  Task.nb_assets_ready,
257
+ Task.difficulty,
255
258
  assignees_table.columns.person,
256
259
  Project.id,
257
260
  Project.name,
@@ -299,8 +302,10 @@ def get_shots_and_tasks(criterions={}):
299
302
  task_end_date,
300
303
  task_start_date,
301
304
  task_due_date,
305
+ task_done_date,
302
306
  task_last_comment_date,
303
307
  task_nb_assets_ready,
308
+ task_difficulty,
304
309
  person_id,
305
310
  project_id,
306
311
  project_name,
@@ -354,6 +359,7 @@ def get_shots_and_tasks(criterions={}):
354
359
  "duration": task_duration,
355
360
  "due_date": task_due_date,
356
361
  "end_date": task_end_date,
362
+ "done_date": task_done_date,
357
363
  "entity_id": shot_id,
358
364
  "estimation": task_estimation,
359
365
  "is_subscribed": subscription_map.get(task_id, False),
@@ -363,6 +369,7 @@ def get_shots_and_tasks(criterions={}):
363
369
  "real_start_date": task_real_start_date,
364
370
  "retake_count": task_retake_count,
365
371
  "start_date": task_start_date,
372
+ "difficulty": task_difficulty,
366
373
  "task_status_id": task_status_id,
367
374
  "task_type_id": task_type_id,
368
375
  "assignees": [],
@@ -395,19 +402,13 @@ def get_shot_raw(shot_id):
395
402
 
396
403
 
397
404
  @cache.memoize_function(120)
398
- def get_shot(shot_id):
405
+ def get_shot(shot_id, relations=False):
399
406
  """
400
407
  Return given shot as a dictionary.
401
408
  """
402
- return get_shot_raw(shot_id).serialize(obj_type="Shot")
403
-
404
-
405
- @cache.memoize_function(120)
406
- def get_shot_with_relations(shot_id):
407
- """
408
- Return given shot as a dictionary.
409
- """
410
- return get_shot_raw(shot_id).serialize(obj_type="Shot", relations=True)
409
+ return get_shot_raw(shot_id).serialize(
410
+ obj_type="Shot", relations=relations
411
+ )
411
412
 
412
413
 
413
414
  @cache.memoize_function(120)
@@ -419,7 +420,7 @@ def get_full_shot(shot_id):
419
420
  shots = get_shots_and_tasks({"id": shot_id})
420
421
  if len(shots) > 0:
421
422
  shot = shots[0]
422
- shot.update(get_shot_with_relations(shot_id))
423
+ shot.update(get_shot(shot_id, relations=True))
423
424
  return shot
424
425
  else:
425
426
  raise ShotNotFoundException
@@ -1112,24 +1113,31 @@ def get_base_entity_type_name(entity_dict):
1112
1113
  return type_name
1113
1114
 
1114
1115
 
1115
- def get_weighted_quotas(project_id, task_type_id, detail_level):
1116
+ def get_weighted_quotas(
1117
+ project_id, task_type_id, studio_id=None, feedback=True
1118
+ ):
1116
1119
  """
1117
1120
  Build quota statistics. It counts the number of frames done for each day.
1118
- A shot is considered done at the first feedback request. If time spent is
1119
- filled for it, it weights the result with the frame number with the time
1120
- spents. If there is no time spent, it considers that the work was done
1121
- from the wip date to the feedback date.
1121
+ A shot is considered done at the first feedback request or at last
1122
+ approval.
1123
+
1124
+ If time spent is filled for it, it weights the result with the frame
1125
+ number with the time spents. If there is no time spent, it considers that
1126
+ the work was done from the wip date to the feedback date (or approval date).
1122
1127
  It computes the shot count and the number of seconds too.
1128
+
1129
+ If the `feedback` flag is set to True, it uses the feedback date
1130
+ (real_end_date), if feedback is set to False, it uses the approval date
1131
+ (done_date).
1123
1132
  """
1124
1133
  fps = projects_service.get_project_fps(project_id)
1125
1134
  timezone = user_service.get_timezone()
1126
1135
  shot_type = get_shot_type()
1127
1136
  quotas = {}
1128
1137
  query = (
1129
- Task.query.filter(Task.project_id == project_id)
1130
- .filter(Entity.entity_type_id == shot_type["id"])
1138
+ Task.query.filter(Entity.entity_type_id == shot_type["id"])
1139
+ .filter(Task.project_id == project_id)
1131
1140
  .filter(Task.task_type_id == task_type_id)
1132
- .filter(Task.end_date != None)
1133
1141
  .join(Entity, Entity.id == Task.entity_id)
1134
1142
  .join(Project, Project.id == Task.project_id)
1135
1143
  .join(TimeSpent, Task.id == TimeSpent.task_id)
@@ -1140,6 +1148,19 @@ def get_weighted_quotas(project_id, task_type_id, detail_level):
1140
1148
  TimeSpent.person_id,
1141
1149
  )
1142
1150
  )
1151
+
1152
+ if feedback:
1153
+ query = query.filter(Task.end_date != None)
1154
+ else:
1155
+ query = query.filter(Task.done_date != None)
1156
+
1157
+ if studio_id is not None:
1158
+ persons_from_studio = Person.query.filter(
1159
+ Person.studio_id == studio_id
1160
+ ).all()
1161
+ query = query.filter(
1162
+ or_(*[Task.assignees.contains(p) for p in persons_from_studio])
1163
+ )
1143
1164
  result = query.all()
1144
1165
 
1145
1166
  for task, nb_frames, date, duration, person_id in result:
@@ -1153,7 +1174,6 @@ def get_weighted_quotas(project_id, task_type_id, detail_level):
1153
1174
  .filter(Entity.entity_type_id == shot_type["id"])
1154
1175
  .filter(Task.task_type_id == task_type_id)
1155
1176
  .filter(Task.real_start_date != None)
1156
- .filter(Task.end_date != None)
1157
1177
  .filter(TimeSpent.id == None)
1158
1178
  .join(Entity, Entity.id == Task.entity_id)
1159
1179
  .join(Project, Project.id == Task.project_id)
@@ -1161,19 +1181,32 @@ def get_weighted_quotas(project_id, task_type_id, detail_level):
1161
1181
  .join(Task.assignees)
1162
1182
  .add_columns(Entity.nb_frames, Person.id)
1163
1183
  )
1184
+
1185
+ if feedback:
1186
+ query = query.filter(Task.end_date != None)
1187
+ else:
1188
+ query = query.filter(Task.done_date != None)
1189
+
1190
+ if studio_id is not None:
1191
+ query = query.filter(
1192
+ or_(*[Task.assignees.contains(p) for p in persons_from_studio])
1193
+ )
1164
1194
  result = query.all()
1165
1195
 
1166
1196
  for task, nb_frames, person_id in result:
1197
+ date = task.done_date
1198
+ if feedback:
1199
+ date = task.end_date
1200
+
1167
1201
  business_days = (
1168
- date_helpers.get_business_days(task.real_start_date, task.end_date)
1169
- + 1
1202
+ date_helpers.get_business_days(task.real_start_date, date) + 1
1170
1203
  )
1171
1204
  if nb_frames is not None:
1172
1205
  nb_frames = round(nb_frames / business_days) or 0
1173
1206
  else:
1174
1207
  nb_frames = 0
1175
- date = task.real_start_date
1176
- for x in range((task.end_date - task.real_start_date).days + 1):
1208
+
1209
+ for x in range((date - task.real_start_date).days + 1):
1177
1210
  if date.weekday() < 5:
1178
1211
  _add_quota_entry(
1179
1212
  quotas, str(person_id), date, timezone, nb_frames, fps
@@ -1182,11 +1215,13 @@ def get_weighted_quotas(project_id, task_type_id, detail_level):
1182
1215
  return quotas
1183
1216
 
1184
1217
 
1185
- def get_raw_quotas(project_id, task_type_id, detail_level):
1218
+ def get_raw_quotas(project_id, task_type_id, studio_id=None, feedback=True):
1186
1219
  """
1187
1220
  Build quota statistics in a raw way. It counts the number of frames done
1188
1221
  for each day. A shot is considered done at the first feedback request (end
1189
- date). It considers that all the work was done at the end date.
1222
+ date) or approval date (done_date).
1223
+
1224
+ It considers that all the work was done at the end date.
1190
1225
  It computes the shot count and the number of seconds too.
1191
1226
  """
1192
1227
  fps = projects_service.get_project_fps(project_id)
@@ -1197,16 +1232,32 @@ def get_raw_quotas(project_id, task_type_id, detail_level):
1197
1232
  Task.query.filter(Task.project_id == project_id)
1198
1233
  .filter(Entity.entity_type_id == shot_type["id"])
1199
1234
  .filter(Task.task_type_id == task_type_id)
1200
- .filter(Task.end_date != None)
1201
1235
  .join(Entity, Entity.id == Task.entity_id)
1202
1236
  .join(Project, Project.id == Task.project_id)
1203
1237
  .join(Task.assignees)
1204
1238
  .add_columns(Entity.nb_frames, Person.id)
1205
1239
  )
1240
+
1241
+ if feedback:
1242
+ query = query.filter(Task.end_date != None)
1243
+ else:
1244
+ query = query.filter(Task.done_date != None)
1245
+
1246
+ if studio_id is not None:
1247
+ persons_from_studio = Person.query.filter(
1248
+ Person.studio_id == studio_id
1249
+ ).all()
1250
+ query = query.filter(
1251
+ or_(*[Task.assignees.contains(p) for p in persons_from_studio])
1252
+ )
1253
+
1206
1254
  result = query.all()
1207
1255
 
1208
1256
  for task, nb_frames, person_id in result:
1209
- date = task.end_date
1257
+ date = task.done_date
1258
+ if feedback:
1259
+ date = task.end_date
1260
+
1210
1261
  if nb_frames is None:
1211
1262
  nb_frames = 0
1212
1263
  _add_quota_entry(
@@ -1280,7 +1331,13 @@ def _init_quota_person(quotas, person_id):
1280
1331
 
1281
1332
 
1282
1333
  def get_month_quota_shots(
1283
- person_id, year, month, project_id=None, task_type_id=None, weighted=True
1334
+ person_id,
1335
+ year,
1336
+ month,
1337
+ project_id=None,
1338
+ task_type_id=None,
1339
+ weighted=True,
1340
+ feedback=True,
1284
1341
  ):
1285
1342
  """
1286
1343
  Return shots that are included in quota comptutation for given
@@ -1295,6 +1352,7 @@ def get_month_quota_shots(
1295
1352
  end,
1296
1353
  project_id=project_id,
1297
1354
  task_type_id=task_type_id,
1355
+ feedback=feedback,
1298
1356
  )
1299
1357
  else:
1300
1358
  return get_raw_quota_shots_between(
@@ -1303,11 +1361,18 @@ def get_month_quota_shots(
1303
1361
  end,
1304
1362
  project_id=project_id,
1305
1363
  task_type_id=task_type_id,
1364
+ feedback=feedback,
1306
1365
  )
1307
1366
 
1308
1367
 
1309
1368
  def get_week_quota_shots(
1310
- person_id, year, week, project_id=None, task_type_id=None, weighted=True
1369
+ person_id,
1370
+ year,
1371
+ week,
1372
+ project_id=None,
1373
+ task_type_id=None,
1374
+ weighted=True,
1375
+ feedback=True,
1311
1376
  ):
1312
1377
  """
1313
1378
  Return shots that are included in quota comptutation for given
@@ -1322,6 +1387,7 @@ def get_week_quota_shots(
1322
1387
  end,
1323
1388
  project_id=project_id,
1324
1389
  task_type_id=task_type_id,
1390
+ feedback=feedback,
1325
1391
  )
1326
1392
  else:
1327
1393
  return get_raw_quota_shots_between(
@@ -1330,6 +1396,7 @@ def get_week_quota_shots(
1330
1396
  end,
1331
1397
  project_id=project_id,
1332
1398
  task_type_id=task_type_id,
1399
+ feedback=feedback,
1333
1400
  )
1334
1401
 
1335
1402
 
@@ -1341,13 +1408,14 @@ def get_day_quota_shots(
1341
1408
  project_id=None,
1342
1409
  task_type_id=None,
1343
1410
  weighted=True,
1411
+ feedback=True,
1344
1412
  ):
1345
1413
  """
1346
1414
  Return shots that are included in quota comptutation for given
1347
1415
  person and day.
1348
1416
  """
1349
1417
  start, end = date_helpers.get_day_interval(year, month, day)
1350
- start, end = _get_timezoned_interval(start, end)
1418
+ # start, end = _get_timezoned_interval(start, end)
1351
1419
  if weighted:
1352
1420
  return get_weighted_quota_shots_between(
1353
1421
  person_id,
@@ -1355,6 +1423,7 @@ def get_day_quota_shots(
1355
1423
  end,
1356
1424
  project_id=project_id,
1357
1425
  task_type_id=task_type_id,
1426
+ feedback=feedback,
1358
1427
  )
1359
1428
  else:
1360
1429
  return get_raw_quota_shots_between(
@@ -1363,11 +1432,12 @@ def get_day_quota_shots(
1363
1432
  end,
1364
1433
  project_id=project_id,
1365
1434
  task_type_id=task_type_id,
1435
+ feedback=feedback,
1366
1436
  )
1367
1437
 
1368
1438
 
1369
1439
  def get_weighted_quota_shots_between(
1370
- person_id, start, end, project_id=None, task_type_id=None
1440
+ person_id, start, end, project_id=None, task_type_id=None, feedback=True
1371
1441
  ):
1372
1442
  """
1373
1443
  Get all shots leading to a quota computation during the given period.
@@ -1387,7 +1457,6 @@ def get_weighted_quota_shots_between(
1387
1457
  Entity.query.filter(Entity.entity_type_id == shot_type["id"])
1388
1458
  .filter(Task.project_id == project_id)
1389
1459
  .filter(Task.task_type_id == task_type_id)
1390
- .filter(Task.end_date != None)
1391
1460
  .filter(TimeSpent.person_id == person_id)
1392
1461
  .filter(TimeSpent.date >= func.cast(start, TimeSpent.date.type))
1393
1462
  .filter(TimeSpent.date < func.cast(end, TimeSpent.date.type))
@@ -1396,11 +1465,17 @@ def get_weighted_quota_shots_between(
1396
1465
  .join(TimeSpent, Task.id == TimeSpent.task_id)
1397
1466
  .add_columns(Task.duration, TimeSpent.duration)
1398
1467
  )
1468
+
1469
+ if feedback:
1470
+ query = query.filter(Task.end_date != None)
1471
+ else:
1472
+ query = query.filter(Task.done_date != None)
1473
+
1399
1474
  query_shots = query.all()
1400
1475
  for entity, task_duration, duration in query_shots:
1401
1476
  shot = entity.serialize()
1402
1477
  if shot["id"] not in already_listed:
1403
- full_name, _ = names_service.get_full_entity_name(shot["id"])
1478
+ full_name, _, _ = names_service.get_full_entity_name(shot["id"])
1404
1479
  shot["full_name"] = full_name
1405
1480
  shot["weight"] = round(duration / task_duration, 2) or 0
1406
1481
  shots.append(shot)
@@ -1409,22 +1484,36 @@ def get_weighted_quota_shots_between(
1409
1484
  shot = already_listed[shot["id"]]
1410
1485
  shot["weight"] += round(duration / task_duration, 2)
1411
1486
 
1412
- start = date_helpers.get_datetime_from_string(start)
1413
- end = date_helpers.get_datetime_from_string(end)
1487
+ print(start, end)
1488
+ if type(start) is str:
1489
+ start = date_helpers.get_datetime_from_string(start)
1490
+ if type(end) is str:
1491
+ end = date_helpers.get_datetime_from_string(end)
1414
1492
  query = (
1415
1493
  Entity.query.filter(Entity.entity_type_id == shot_type["id"])
1416
1494
  .filter(Task.project_id == project_id)
1417
1495
  .filter(Task.task_type_id == task_type_id)
1418
- .filter(Task.end_date != None)
1419
1496
  .filter(Task.real_start_date != None)
1420
1497
  .filter(Task.assignees.contains(person))
1421
- .filter((Task.real_start_date <= end) & (Task.end_date >= start))
1422
1498
  .filter(TimeSpent.id == None)
1423
1499
  .join(Task, Entity.id == Task.entity_id)
1424
1500
  .join(Project, Project.id == Task.project_id)
1425
1501
  .outerjoin(TimeSpent, TimeSpent.task_id == Task.id)
1426
- .add_columns(Task.real_start_date, Task.end_date)
1427
1502
  )
1503
+
1504
+ if feedback:
1505
+ query = (
1506
+ query.filter(Task.end_date != None)
1507
+ .filter((Task.real_start_date <= end) & (Task.end_date >= start))
1508
+ .add_columns(Task.real_start_date, Task.end_date)
1509
+ )
1510
+ else:
1511
+ query = (
1512
+ query.filter(Task.done_date != None)
1513
+ .filter((Task.real_start_date <= end) & (Task.done_date >= start))
1514
+ .add_columns(Task.real_start_date, Task.done_date)
1515
+ )
1516
+
1428
1517
  query_shots = query.all()
1429
1518
 
1430
1519
  for entity, task_start, task_end in query_shots:
@@ -1433,7 +1522,7 @@ def get_weighted_quota_shots_between(
1433
1522
  business_days = (
1434
1523
  date_helpers.get_business_days(task_start, task_end) + 1
1435
1524
  )
1436
- full_name, _ = names_service.get_full_entity_name(shot["id"])
1525
+ full_name, _, _ = names_service.get_full_entity_name(shot["id"])
1437
1526
  shot["full_name"] = full_name
1438
1527
  multiplicator = 1
1439
1528
  if task_start >= start and task_end <= end:
@@ -1454,7 +1543,7 @@ def get_weighted_quota_shots_between(
1454
1543
 
1455
1544
 
1456
1545
  def get_raw_quota_shots_between(
1457
- person_id, start, end, project_id=None, task_type_id=None
1546
+ person_id, start, end, project_id=None, task_type_id=None, feedback=True
1458
1547
  ):
1459
1548
  """
1460
1549
  Get all shots leading to a quota computation during the given period.
@@ -1467,21 +1556,31 @@ def get_raw_quota_shots_between(
1467
1556
  Entity.query.filter(Entity.entity_type_id == shot_type["id"])
1468
1557
  .filter(Task.project_id == project_id)
1469
1558
  .filter(Task.task_type_id == task_type_id)
1470
- .filter(
1559
+ .filter(Task.assignees.contains(person))
1560
+ .join(Task, Entity.id == Task.entity_id)
1561
+ .join(Project, Project.id == Task.project_id)
1562
+ )
1563
+
1564
+ if feedback:
1565
+ query = query.filter(
1471
1566
  Task.end_date.between(
1472
1567
  func.cast(start, Task.end_date.type),
1473
1568
  func.cast(end, Task.end_date.type),
1474
1569
  )
1475
1570
  )
1476
- .filter(Task.assignees.contains(person))
1477
- .join(Task, Entity.id == Task.entity_id)
1478
- .join(Project, Project.id == Task.project_id)
1479
- )
1571
+ else:
1572
+ query = query.filter(
1573
+ Task.done_date.between(
1574
+ func.cast(start, Task.done_date.type),
1575
+ func.cast(end, Task.done_date.type),
1576
+ )
1577
+ )
1578
+
1480
1579
  query_shots = query.all()
1481
1580
 
1482
1581
  for entity in query_shots:
1483
1582
  shot = entity.serialize()
1484
- full_name, _ = names_service.get_full_entity_name(shot["id"])
1583
+ full_name, _, _ = names_service.get_full_entity_name(shot["id"])
1485
1584
  shot["full_name"] = full_name
1486
1585
  shot["weight"] = 1
1487
1586
  shots.append(shot)
@@ -1503,3 +1602,76 @@ def get_all_raw_shots():
1503
1602
  """
1504
1603
  query = Entity.query.filter(Entity.entity_type_id == get_shot_type()["id"])
1505
1604
  return query.all()
1605
+
1606
+
1607
+ def set_frames_from_task_type_preview_files(
1608
+ project_id,
1609
+ task_type_id,
1610
+ episode_id=None,
1611
+ ):
1612
+ from zou.app import db
1613
+
1614
+ shot_type = get_shot_type()
1615
+ Shot = aliased(Entity)
1616
+ Sequence = aliased(Entity)
1617
+
1618
+ if episode_id is not None:
1619
+ subquery = (
1620
+ db.session.query(
1621
+ Shot.id.label("entity_id"),
1622
+ func.max(PreviewFile.created_at).label("max_created_at"),
1623
+ )
1624
+ .join(Task, PreviewFile.task_id == Task.id)
1625
+ .join(Shot, Task.entity_id == Shot.id)
1626
+ .join(Sequence, Sequence.id == Shot.parent_id)
1627
+ .filter(Shot.project_id == project_id)
1628
+ .filter(Shot.entity_type_id == shot_type["id"])
1629
+ .filter(Task.task_type_id == task_type_id)
1630
+ .filter(Sequence.parent_id == episode_id)
1631
+ .group_by(Shot.id)
1632
+ .subquery()
1633
+ )
1634
+ else:
1635
+ subquery = (
1636
+ db.session.query(
1637
+ Shot.id.label("entity_id"),
1638
+ func.max(PreviewFile.created_at).label("max_created_at"),
1639
+ )
1640
+ .join(Task, PreviewFile.task_id == Task.id)
1641
+ .join(Shot, Task.entity_id == Shot.id)
1642
+ .filter(Shot.project_id == project_id)
1643
+ .filter(Shot.entity_type_id == shot_type["id"])
1644
+ .filter(Task.task_type_id == task_type_id)
1645
+ .group_by(Shot.id)
1646
+ .subquery()
1647
+ )
1648
+
1649
+ query = (
1650
+ db.session.query(Shot, PreviewFile.duration)
1651
+ .join(Task, Task.entity_id == Shot.id)
1652
+ .join(subquery, (Shot.id == subquery.c.entity_id))
1653
+ .join(
1654
+ PreviewFile,
1655
+ (PreviewFile.task_id == Task.id)
1656
+ & (PreviewFile.created_at == subquery.c.max_created_at),
1657
+ )
1658
+ .filter(Task.task_type_id == task_type_id)
1659
+ .filter(Shot.project_id == project_id)
1660
+ )
1661
+
1662
+ results = query.all()
1663
+ project = projects_service.get_project(project_id)
1664
+ updates = []
1665
+ for shot, preview_duration in results:
1666
+ nb_frames = round(preview_duration * float(project["fps"]))
1667
+ updates.append(
1668
+ {
1669
+ "id": shot.id,
1670
+ "nb_frames": nb_frames,
1671
+ }
1672
+ )
1673
+ clear_shot_cache(str(shot.id))
1674
+
1675
+ db.session.bulk_update_mappings(Shot, updates)
1676
+ db.session.commit()
1677
+ return updates