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.
- zou/__init__.py +1 -1
- zou/app/__init__.py +10 -2
- zou/app/api.py +2 -0
- zou/app/blueprints/assets/__init__.py +22 -0
- zou/app/blueprints/assets/resources.py +241 -4
- zou/app/blueprints/auth/__init__.py +4 -0
- zou/app/blueprints/auth/resources.py +154 -22
- zou/app/blueprints/breakdown/resources.py +4 -4
- zou/app/blueprints/chats/__init__.py +22 -0
- zou/app/blueprints/chats/resources.py +199 -0
- zou/app/blueprints/comments/resources.py +36 -19
- zou/app/blueprints/crud/__init__.py +12 -0
- zou/app/blueprints/crud/attachment_file.py +14 -5
- zou/app/blueprints/crud/base.py +29 -28
- zou/app/blueprints/crud/chat.py +13 -0
- zou/app/blueprints/crud/chat_message.py +13 -0
- zou/app/blueprints/crud/comments.py +85 -29
- zou/app/blueprints/crud/custom_action.py +1 -1
- zou/app/blueprints/crud/day_off.py +47 -9
- zou/app/blueprints/crud/department.py +1 -25
- zou/app/blueprints/crud/entity.py +46 -5
- zou/app/blueprints/crud/entity_type.py +13 -1
- zou/app/blueprints/crud/event.py +1 -1
- zou/app/blueprints/crud/file_status.py +1 -1
- zou/app/blueprints/crud/metadata_descriptor.py +24 -10
- zou/app/blueprints/crud/organisation.py +22 -5
- zou/app/blueprints/crud/output_file.py +1 -1
- zou/app/blueprints/crud/output_type.py +1 -1
- zou/app/blueprints/crud/person.py +32 -24
- zou/app/blueprints/crud/playlist.py +1 -1
- zou/app/blueprints/crud/preview_background_file.py +6 -7
- zou/app/blueprints/crud/preview_file.py +1 -1
- zou/app/blueprints/crud/project.py +14 -6
- zou/app/blueprints/crud/project_status.py +1 -1
- zou/app/blueprints/crud/schedule_item.py +4 -2
- zou/app/blueprints/crud/software.py +1 -1
- zou/app/blueprints/crud/status_automation.py +1 -1
- zou/app/blueprints/crud/studio.py +33 -0
- zou/app/blueprints/crud/task.py +47 -3
- zou/app/blueprints/crud/task_status.py +1 -1
- zou/app/blueprints/crud/task_type.py +4 -4
- zou/app/blueprints/crud/working_file.py +4 -8
- zou/app/blueprints/events/resources.py +13 -12
- zou/app/blueprints/export/csv/assets.py +15 -6
- zou/app/blueprints/export/csv/edits.py +15 -5
- zou/app/blueprints/export/csv/playlists.py +1 -1
- zou/app/blueprints/export/csv/shots.py +15 -5
- zou/app/blueprints/export/csv/time_spents.py +1 -1
- zou/app/blueprints/files/resources.py +22 -23
- zou/app/blueprints/index/resources.py +38 -29
- zou/app/blueprints/news/resources.py +25 -11
- zou/app/blueprints/persons/__init__.py +5 -2
- zou/app/blueprints/persons/resources.py +126 -120
- zou/app/blueprints/previews/__init__.py +18 -8
- zou/app/blueprints/previews/resources.py +569 -328
- zou/app/blueprints/projects/resources.py +1 -1
- zou/app/blueprints/search/resources.py +18 -6
- zou/app/blueprints/shots/__init__.py +5 -0
- zou/app/blueprints/shots/resources.py +134 -4
- zou/app/blueprints/source/__init__.py +6 -6
- zou/app/blueprints/source/csv/assets.py +10 -3
- zou/app/blueprints/source/csv/base.py +1 -1
- zou/app/blueprints/source/csv/edits.py +10 -3
- zou/app/blueprints/source/csv/shots.py +10 -3
- zou/app/blueprints/source/{edl.py → otio.py} +82 -41
- zou/app/blueprints/tasks/__init__.py +3 -2
- zou/app/blueprints/tasks/resources.py +83 -52
- zou/app/blueprints/user/__init__.py +9 -0
- zou/app/blueprints/user/resources.py +170 -12
- zou/app/config.py +10 -0
- zou/app/mixin.py +6 -5
- zou/app/models/attachment_file.py +10 -4
- zou/app/models/base.py +18 -13
- zou/app/models/build_job.py +7 -4
- zou/app/models/chat.py +44 -0
- zou/app/models/chat_message.py +37 -0
- zou/app/models/comment.py +1 -0
- zou/app/models/day_off.py +3 -0
- zou/app/models/entity.py +4 -6
- zou/app/models/entity_type.py +2 -0
- zou/app/models/organisation.py +14 -15
- zou/app/models/person.py +6 -1
- zou/app/models/project.py +3 -0
- zou/app/models/search_filter.py +11 -0
- zou/app/models/search_filter_group.py +10 -0
- zou/app/models/serializer.py +17 -17
- zou/app/models/status_automation.py +2 -0
- zou/app/models/studio.py +13 -0
- zou/app/models/subscription.py +2 -2
- zou/app/models/task.py +6 -1
- zou/app/models/task_status.py +1 -0
- zou/app/models/task_type.py +1 -0
- zou/app/models/working_file.py +1 -1
- zou/app/services/assets_service.py +101 -14
- zou/app/services/auth_service.py +17 -44
- zou/app/services/breakdown_service.py +37 -5
- zou/app/services/chats_service.py +279 -0
- zou/app/services/comments_service.py +110 -65
- zou/app/services/concepts_service.py +4 -12
- zou/app/services/deletion_service.py +43 -30
- zou/app/services/edits_service.py +5 -11
- zou/app/services/emails_service.py +4 -4
- zou/app/services/entities_service.py +17 -2
- zou/app/services/events_service.py +12 -4
- zou/app/services/exception.py +5 -5
- zou/app/services/names_service.py +7 -2
- zou/app/services/news_service.py +17 -9
- zou/app/services/persons_service.py +38 -21
- zou/app/services/playlists_service.py +8 -7
- zou/app/services/preview_files_service.py +137 -10
- zou/app/services/projects_service.py +5 -14
- zou/app/services/shots_service.py +221 -49
- zou/app/services/sync_service.py +46 -42
- zou/app/services/tasks_service.py +185 -46
- zou/app/services/time_spents_service.py +67 -20
- zou/app/services/user_service.py +350 -107
- zou/app/stores/auth_tokens_store.py +2 -1
- zou/app/stores/file_store.py +18 -0
- zou/app/stores/publisher_store.py +7 -7
- zou/app/stores/queue_store.py +1 -0
- zou/app/swagger.py +36 -20
- zou/app/utils/cache.py +2 -0
- zou/app/utils/commands.py +104 -7
- zou/app/utils/csv_utils.py +1 -4
- zou/app/utils/date_helpers.py +33 -17
- zou/app/utils/dbhelpers.py +14 -1
- zou/app/utils/emails.py +2 -2
- zou/app/utils/fido.py +22 -0
- zou/app/utils/query.py +54 -6
- zou/app/utils/redis.py +11 -0
- zou/app/utils/saml.py +51 -0
- zou/app/utils/string.py +2 -0
- zou/app/utils/thumbnail.py +4 -2
- zou/cli.py +76 -18
- zou/debug.py +4 -2
- zou/event_stream.py +122 -165
- zou/job_settings.py +1 -0
- zou/migrations/env.py +0 -0
- zou/migrations/utils/base.py +6 -6
- zou/migrations/versions/1bb55759146f_add_table_studio.py +67 -0
- zou/migrations/versions/1fab8c420678_add_attachments_to_message_chats.py +56 -0
- zou/migrations/versions/23122f290ca2_add_entity_chat_models.py +149 -0
- zou/migrations/versions/32f134ff1201_add_is_shared_flag_to_filters.py +33 -0
- zou/migrations/versions/57222395f2be_add_statusautomation_import_last_revision.py +41 -0
- zou/migrations/versions/59a7445a966c_add_entity_is_shared.py +41 -0
- zou/migrations/versions/5b980f0dc365_add_comment_links.py +35 -0
- zou/migrations/versions/680c64565f9d_for_searchfiltergroup_is_shared.py +35 -0
- zou/migrations/versions/8e67c183bed7_add_preference_fields.py +71 -0
- zou/migrations/versions/92b40d79ad3f_allow_message_attachments.py +38 -0
- zou/migrations/versions/971dbf5a0faf_add_short_name_for_asset_type_entity_.py +33 -0
- zou/migrations/versions/9b85c14fa8a7_add_day_off_new_columns.py +68 -0
- zou/migrations/versions/9d3bb33c6fc6_add_department_keys_to_filter_models.py +73 -0
- zou/migrations/versions/a252a094e977_add_descriptions_for_entities_tasks_and_.py +40 -0
- zou/migrations/versions/be56dc0fb760_for_is_shared_disallow_nullable.py +102 -0
- zou/migrations/versions/ca28796a2a62_add_is_done_field_to_the_task_model.py +108 -0
- zou/migrations/versions/f344b867a911_for_description_of_entity_task_working_.py +75 -0
- zou/remote/config_payload.py +2 -1
- zou/utils/movie.py +14 -4
- {zou-0.19.15.dist-info → zou-0.20.11.dist-info}/METADATA +75 -69
- {zou-0.19.15.dist-info → zou-0.20.11.dist-info}/RECORD +163 -134
- {zou-0.19.15.dist-info → zou-0.20.11.dist-info}/WHEEL +1 -1
- {zou-0.19.15.dist-info → zou-0.20.11.dist-info}/LICENSE +0 -0
- {zou-0.19.15.dist-info → zou-0.20.11.dist-info}/entry_points.txt +0 -0
- {zou-0.19.15.dist-info → zou-0.20.11.dist-info}/top_level.txt +0 -0
zou/app/services/sync_service.py
CHANGED
|
@@ -42,10 +42,11 @@ from zou.app.models.task import Task
|
|
|
42
42
|
from zou.app.models.task_status import TaskStatus
|
|
43
43
|
from zou.app.models.task_type import TaskType
|
|
44
44
|
from zou.app.models.time_spent import TimeSpent
|
|
45
|
+
from zou.app.models.studio import Studio
|
|
45
46
|
|
|
46
47
|
from zou.app.services import deletion_service, tasks_service, projects_service
|
|
47
48
|
from zou.app.stores import file_store
|
|
48
|
-
from zou.app.utils import events
|
|
49
|
+
from zou.app.utils import events, date_helpers
|
|
49
50
|
from zou.app import app, config
|
|
50
51
|
|
|
51
52
|
|
|
@@ -102,6 +103,7 @@ event_name_model_map = {
|
|
|
102
103
|
"subscription": Subscription,
|
|
103
104
|
"search-filter": SearchFilter,
|
|
104
105
|
"search-filter-group": SearchFilterGroup,
|
|
106
|
+
"studio": Studio,
|
|
105
107
|
"task": Task,
|
|
106
108
|
"task-status": TaskStatus,
|
|
107
109
|
"task-type": TaskType,
|
|
@@ -140,6 +142,7 @@ event_name_model_path_map = {
|
|
|
140
142
|
"search-filter": "search-filters",
|
|
141
143
|
"search-filter-group": "search-filter-groups",
|
|
142
144
|
"subscription": "subscriptions",
|
|
145
|
+
"studio": "studios",
|
|
143
146
|
"task": "tasks",
|
|
144
147
|
"task-status": "task-status",
|
|
145
148
|
"task-type": "task-types",
|
|
@@ -169,14 +172,15 @@ project_events = [
|
|
|
169
172
|
]
|
|
170
173
|
|
|
171
174
|
main_events = [
|
|
172
|
-
"
|
|
173
|
-
"organisation",
|
|
174
|
-
"project-status",
|
|
175
|
+
"studio",
|
|
175
176
|
"department",
|
|
176
177
|
"task-type",
|
|
177
178
|
"task-status",
|
|
178
179
|
"custom-action",
|
|
180
|
+
"organisation",
|
|
181
|
+
"project-status",
|
|
179
182
|
"asset-type",
|
|
183
|
+
"person",
|
|
180
184
|
"project",
|
|
181
185
|
]
|
|
182
186
|
|
|
@@ -319,14 +323,14 @@ def run_other_sync(project=None, with_events=False):
|
|
|
319
323
|
sync_entries("events", ApiEvent, project=project)
|
|
320
324
|
|
|
321
325
|
|
|
322
|
-
def run_last_events_sync(minutes=0,
|
|
326
|
+
def run_last_events_sync(minutes=0, limit=300):
|
|
323
327
|
"""
|
|
324
328
|
Retrieve last events from source instance and import related data and
|
|
325
329
|
action.
|
|
326
330
|
"""
|
|
327
|
-
path = "events/last?
|
|
331
|
+
path = "events/last?limit=%s" % limit
|
|
328
332
|
if minutes > 0:
|
|
329
|
-
now =
|
|
333
|
+
now = date_helpers.get_utc_now_datetime()
|
|
330
334
|
min_before = now - datetime.timedelta(minutes=minutes)
|
|
331
335
|
after = min_before.strftime("%Y-%m-%dT%H:%M:%S")
|
|
332
336
|
path += "&before=%s" % now.strftime("%Y-%m-%dT%H:%M:%S")
|
|
@@ -342,14 +346,14 @@ def run_last_events_sync(minutes=0, page_size=300):
|
|
|
342
346
|
pass
|
|
343
347
|
|
|
344
348
|
|
|
345
|
-
def run_last_events_files(minutes=0,
|
|
349
|
+
def run_last_events_files(minutes=0, limit=50):
|
|
346
350
|
"""
|
|
347
351
|
Retrieve last events from source instance and import related data and
|
|
348
352
|
action.
|
|
349
353
|
"""
|
|
350
|
-
path = "events/last?only_files=true&
|
|
354
|
+
path = "events/last?only_files=true&limit=%s" % limit
|
|
351
355
|
if minutes > 0:
|
|
352
|
-
now =
|
|
356
|
+
now = date_helpers.get_utc_now_datetime()
|
|
353
357
|
min_before = now - datetime.timedelta(minutes=minutes)
|
|
354
358
|
after = min_before.strftime("%Y-%m-%dT%H:%M:%S")
|
|
355
359
|
path += "&before=%s" % now.strftime("%Y-%m-%dT%H:%M:%S")
|
|
@@ -404,35 +408,35 @@ def sync_entries(model_name, model, project=None):
|
|
|
404
408
|
"""
|
|
405
409
|
instances = []
|
|
406
410
|
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
411
|
+
page = 1
|
|
412
|
+
init = True
|
|
413
|
+
results = {"nb_pages": 2}
|
|
414
|
+
params = {
|
|
415
|
+
"relations": "true",
|
|
416
|
+
}
|
|
417
|
+
if model_name == "persons":
|
|
418
|
+
params["with_pass_hash"] = "true"
|
|
419
|
+
if project is not None and model_name in [
|
|
420
|
+
"projects",
|
|
421
|
+
"search-filters",
|
|
422
|
+
"search-filter-groups",
|
|
423
|
+
]:
|
|
414
424
|
project = gazu.project.get_project_by_name(project)
|
|
415
425
|
if model_name == "projects":
|
|
416
|
-
|
|
426
|
+
params = {"id": project["id"]}
|
|
417
427
|
elif model_name in ["search-filters", "search-filter-groups"]:
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
"%s?relations=true&page=%d" % (model_name, page)
|
|
431
|
-
)
|
|
432
|
-
instances += results["data"]
|
|
433
|
-
page += 1
|
|
434
|
-
init = False
|
|
435
|
-
model.create_from_import_list(results["data"])
|
|
428
|
+
params = {"project_id": project["id"]}
|
|
429
|
+
while init or results["nb_pages"] >= page:
|
|
430
|
+
params["page"] = page
|
|
431
|
+
results = gazu.client.fetch_all(model_name, params=params)
|
|
432
|
+
if model_name == "task-status" and results["data"]:
|
|
433
|
+
results["data"] = [
|
|
434
|
+
r for r in results["data"] if not r["for_concept"]
|
|
435
|
+
]
|
|
436
|
+
instances += results["data"]
|
|
437
|
+
page += 1
|
|
438
|
+
init = False
|
|
439
|
+
model.create_from_import_list(results["data"])
|
|
436
440
|
|
|
437
441
|
logger.info("%s %s synced." % (len(instances), model_name))
|
|
438
442
|
|
|
@@ -1144,19 +1148,19 @@ def download_preview_background_from_another_instance(
|
|
|
1144
1148
|
"""
|
|
1145
1149
|
Download all files link to preview background file entry.
|
|
1146
1150
|
"""
|
|
1147
|
-
extension = preview_background.extension
|
|
1148
|
-
|
|
1149
1151
|
preview_background_file_id = str(preview_background.id)
|
|
1150
1152
|
for prefix in [
|
|
1151
1153
|
"thumbnails",
|
|
1152
1154
|
"preview-backgrounds",
|
|
1153
1155
|
]:
|
|
1156
|
+
extension = (
|
|
1157
|
+
"png" if prefix == "thumbnails" else preview_background.extension
|
|
1158
|
+
)
|
|
1154
1159
|
if prefix == "preview-backgrounds":
|
|
1155
1160
|
path = f"/pictures/preview-background-files/{preview_background_file_id}.{extension}"
|
|
1156
1161
|
elif prefix == "thumbnails":
|
|
1157
1162
|
path = f"/pictures/thumbnails/preview-background-files/{preview_background_file_id}.png"
|
|
1158
1163
|
|
|
1159
|
-
extension = "png" if prefix == "thumbnails" else extension
|
|
1160
1164
|
file_path = f"/tmp/{prefix}-{preview_background_file_id}.{extension}"
|
|
1161
1165
|
download_file_from_another_instance(
|
|
1162
1166
|
path,
|
|
@@ -1169,9 +1173,9 @@ def download_preview_background_from_another_instance(
|
|
|
1169
1173
|
force,
|
|
1170
1174
|
dict_errors,
|
|
1171
1175
|
)
|
|
1172
|
-
|
|
1173
|
-
|
|
1174
|
-
|
|
1176
|
+
logger.info(
|
|
1177
|
+
f"{index:0{len(str(total))}}/{total} Preview background file {preview_background_file_id} processed."
|
|
1178
|
+
)
|
|
1175
1179
|
|
|
1176
1180
|
|
|
1177
1181
|
def download_attachment_files_from_another_instance(
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import collections
|
|
2
|
-
import datetime
|
|
3
2
|
import uuid
|
|
4
3
|
|
|
5
4
|
from sqlalchemy.exc import StatementError, IntegrityError, DataError
|
|
6
5
|
from sqlalchemy.sql import func
|
|
6
|
+
from sqlalchemy.sql.expression import case
|
|
7
7
|
from sqlalchemy.orm import aliased
|
|
8
8
|
|
|
9
9
|
from zou.app import app, db
|
|
@@ -29,8 +29,15 @@ from zou.app.models.task import Task
|
|
|
29
29
|
from zou.app.models.task_type import TaskType
|
|
30
30
|
from zou.app.models.task_status import TaskStatus
|
|
31
31
|
from zou.app.models.time_spent import TimeSpent
|
|
32
|
-
|
|
33
|
-
|
|
32
|
+
from zou.app.models.studio import Studio
|
|
33
|
+
|
|
34
|
+
from zou.app.utils import (
|
|
35
|
+
cache,
|
|
36
|
+
fields,
|
|
37
|
+
query as query_utils,
|
|
38
|
+
permissions,
|
|
39
|
+
date_helpers,
|
|
40
|
+
)
|
|
34
41
|
|
|
35
42
|
|
|
36
43
|
from zou.app.services.exception import (
|
|
@@ -41,6 +48,7 @@ from zou.app.services.exception import (
|
|
|
41
48
|
TaskStatusNotFoundException,
|
|
42
49
|
TaskTypeNotFoundException,
|
|
43
50
|
DepartmentNotFoundException,
|
|
51
|
+
StudioNotFoundException,
|
|
44
52
|
WrongDateFormatException,
|
|
45
53
|
TimeSpentNotFoundException,
|
|
46
54
|
)
|
|
@@ -75,16 +83,20 @@ def clear_department_cache(department_id):
|
|
|
75
83
|
cache.cache.delete_memoized(get_departments)
|
|
76
84
|
|
|
77
85
|
|
|
86
|
+
def clear_studio_cache(studio_id):
|
|
87
|
+
cache.cache.delete_memoized(get_studio, studio_id)
|
|
88
|
+
cache.cache.delete_memoized(get_studios)
|
|
89
|
+
|
|
90
|
+
|
|
78
91
|
def clear_task_cache(task_id):
|
|
79
92
|
cache.cache.delete_memoized(get_task, task_id)
|
|
80
93
|
cache.cache.delete_memoized(get_task, task_id, True)
|
|
81
|
-
cache.cache.delete_memoized(get_task_with_relations, task_id)
|
|
82
94
|
|
|
83
95
|
|
|
84
96
|
@cache.memoize_function(120)
|
|
85
97
|
def clear_comment_cache(comment_id):
|
|
86
98
|
cache.cache.delete_memoized(get_comment, comment_id)
|
|
87
|
-
cache.cache.delete_memoized(
|
|
99
|
+
cache.cache.delete_memoized(get_comment, comment_id, True)
|
|
88
100
|
|
|
89
101
|
|
|
90
102
|
@cache.memoize_function(120)
|
|
@@ -92,6 +104,11 @@ def get_departments():
|
|
|
92
104
|
return fields.serialize_models(Department.get_all())
|
|
93
105
|
|
|
94
106
|
|
|
107
|
+
@cache.memoize_function(120)
|
|
108
|
+
def get_studios():
|
|
109
|
+
return fields.serialize_models(Studio.get_all())
|
|
110
|
+
|
|
111
|
+
|
|
95
112
|
@cache.memoize_function(120)
|
|
96
113
|
def get_task_types():
|
|
97
114
|
return fields.serialize_models(TaskType.get_all())
|
|
@@ -154,6 +171,22 @@ def get_department(department_id):
|
|
|
154
171
|
return department.serialize()
|
|
155
172
|
|
|
156
173
|
|
|
174
|
+
@cache.memoize_function(120)
|
|
175
|
+
def get_studio(studio_id):
|
|
176
|
+
"""
|
|
177
|
+
Get studio matching given id as a dictionary.
|
|
178
|
+
"""
|
|
179
|
+
try:
|
|
180
|
+
studio = Studio.get(studio_id)
|
|
181
|
+
except StatementError:
|
|
182
|
+
raise StudioNotFoundException
|
|
183
|
+
|
|
184
|
+
if studio is None:
|
|
185
|
+
raise StudioNotFoundException
|
|
186
|
+
|
|
187
|
+
return studio.serialize()
|
|
188
|
+
|
|
189
|
+
|
|
157
190
|
def get_department_from_task_type(task_type_id):
|
|
158
191
|
"""
|
|
159
192
|
Get department of given task type as dictionary
|
|
@@ -216,14 +249,6 @@ def get_task(task_id, relations=False):
|
|
|
216
249
|
return get_task_raw(task_id).serialize(relations=relations)
|
|
217
250
|
|
|
218
251
|
|
|
219
|
-
@cache.memoize_function(120)
|
|
220
|
-
def get_task_with_relations(task_id):
|
|
221
|
-
"""
|
|
222
|
-
Get task matching given id as a dictionary.
|
|
223
|
-
"""
|
|
224
|
-
return get_task_raw(task_id).serialize(relations=True)
|
|
225
|
-
|
|
226
|
-
|
|
227
252
|
def get_task_by_shotgun_id(shotgun_id):
|
|
228
253
|
"""
|
|
229
254
|
Get task matching given shotgun id as a dictionary.
|
|
@@ -362,7 +387,7 @@ def _convert_rows_to_detailed_tasks(rows, relations=False):
|
|
|
362
387
|
entity_name,
|
|
363
388
|
) = entry
|
|
364
389
|
|
|
365
|
-
task =
|
|
390
|
+
task = get_task(str(task_object.id), relations=relations)
|
|
366
391
|
task["project_name"] = project_name
|
|
367
392
|
task["task_type_name"] = task_type_name
|
|
368
393
|
task["task_status_name"] = task_status_name
|
|
@@ -740,21 +765,11 @@ def get_comment_raw(comment_id):
|
|
|
740
765
|
|
|
741
766
|
|
|
742
767
|
@cache.memoize_function(120)
|
|
743
|
-
def get_comment(comment_id):
|
|
768
|
+
def get_comment(comment_id, relations=False):
|
|
744
769
|
"""
|
|
745
770
|
Return comment matching give id as a dict.
|
|
746
771
|
"""
|
|
747
|
-
|
|
748
|
-
return comment.serialize()
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
@cache.memoize_function(120)
|
|
752
|
-
def get_comment_with_relations(comment_id):
|
|
753
|
-
"""
|
|
754
|
-
Return comment matching give id as a dict with joins information.
|
|
755
|
-
"""
|
|
756
|
-
comment = get_comment_raw(comment_id)
|
|
757
|
-
return comment.serialize(relations=True)
|
|
772
|
+
return get_comment_raw(comment_id).serialize(relations=relations)
|
|
758
773
|
|
|
759
774
|
|
|
760
775
|
def get_comment_by_preview_file_id(preview_file_id):
|
|
@@ -951,7 +966,7 @@ def get_person_tasks(person_id, projects, is_done=None):
|
|
|
951
966
|
except EpisodeNotFoundException:
|
|
952
967
|
episode_name = "MP"
|
|
953
968
|
|
|
954
|
-
task_dict =
|
|
969
|
+
task_dict = get_task(str(task.id), relations=True)
|
|
955
970
|
if entity_type_name == "Sequence" and entity_parent_id is not None:
|
|
956
971
|
episode_id = entity_parent_id
|
|
957
972
|
episode = shots_service.get_episode(episode_id)
|
|
@@ -1048,7 +1063,7 @@ def get_person_tasks_to_check(project_ids=None, department_ids=None):
|
|
|
1048
1063
|
else:
|
|
1049
1064
|
query = query.filter(user_service.build_open_project_filter())
|
|
1050
1065
|
|
|
1051
|
-
if department_ids
|
|
1066
|
+
if department_ids:
|
|
1052
1067
|
query = query.filter(TaskType.department_id.in_(department_ids))
|
|
1053
1068
|
tasks = []
|
|
1054
1069
|
for (
|
|
@@ -1083,7 +1098,7 @@ def get_person_tasks_to_check(project_ids=None, department_ids=None):
|
|
|
1083
1098
|
if episode_id is None:
|
|
1084
1099
|
episode_id = entity_source_id
|
|
1085
1100
|
|
|
1086
|
-
task_dict =
|
|
1101
|
+
task_dict = get_task(str(task.id), relations=True)
|
|
1087
1102
|
if entity_type_name == "Sequence" and entity_parent_id is not None:
|
|
1088
1103
|
episode_id = entity_parent_id
|
|
1089
1104
|
episode = shots_service.get_episode(episode_id)
|
|
@@ -1261,7 +1276,10 @@ def update_task(task_id, data):
|
|
|
1261
1276
|
task = get_task_raw(task_id)
|
|
1262
1277
|
|
|
1263
1278
|
if is_finished(task, data):
|
|
1264
|
-
data["end_date"] =
|
|
1279
|
+
data["end_date"] = date_helpers.get_utc_now_datetime()
|
|
1280
|
+
|
|
1281
|
+
if is_done(task, data):
|
|
1282
|
+
data["done_date"] = date_helpers.get_utc_now_datetime()
|
|
1265
1283
|
|
|
1266
1284
|
task.update(data)
|
|
1267
1285
|
clear_task_cache(task_id)
|
|
@@ -1456,7 +1474,7 @@ def delete_time_spent(task_id, person_id, date):
|
|
|
1456
1474
|
|
|
1457
1475
|
def is_finished(task, data):
|
|
1458
1476
|
"""
|
|
1459
|
-
Return True if task status is set to
|
|
1477
|
+
Return True if task status is set to feedback request.
|
|
1460
1478
|
"""
|
|
1461
1479
|
if "task_status_id" in data:
|
|
1462
1480
|
task_status = get_task_status_raw(task.task_status_id)
|
|
@@ -1469,6 +1487,18 @@ def is_finished(task, data):
|
|
|
1469
1487
|
return False
|
|
1470
1488
|
|
|
1471
1489
|
|
|
1490
|
+
def is_done(task, data):
|
|
1491
|
+
"""
|
|
1492
|
+
Return True if task status is set to done.
|
|
1493
|
+
"""
|
|
1494
|
+
if "task_status_id" in data:
|
|
1495
|
+
task_status = get_task_status_raw(task.task_status_id)
|
|
1496
|
+
new_task_status = get_task_status_raw(data["task_status_id"])
|
|
1497
|
+
return new_task_status.id != task_status.id and new_task_status.is_done
|
|
1498
|
+
else:
|
|
1499
|
+
return False
|
|
1500
|
+
|
|
1501
|
+
|
|
1472
1502
|
def clear_assignation(task_id, person_id=None):
|
|
1473
1503
|
"""
|
|
1474
1504
|
Clear task assignation and emit a *task:unassign* event.
|
|
@@ -1616,20 +1646,14 @@ def update_preview_file_info(preview_file):
|
|
|
1616
1646
|
project = projects_service.get_project(task.project_id)
|
|
1617
1647
|
|
|
1618
1648
|
if project["is_set_preview_automated"]:
|
|
1619
|
-
entity_id = str(task.entity_id)
|
|
1620
1649
|
entity = entities_service.update_entity_preview(
|
|
1621
|
-
entity_id,
|
|
1650
|
+
task.entity_id,
|
|
1622
1651
|
preview_file["id"],
|
|
1623
1652
|
)
|
|
1624
|
-
assets_service.clear_asset_cache(entity_id)
|
|
1625
|
-
edits_service.clear_edit_cache(entity_id)
|
|
1626
|
-
shots_service.clear_shot_cache(entity_id)
|
|
1627
|
-
shots_service.clear_episode_cache(entity_id)
|
|
1628
|
-
shots_service.clear_sequence_cache(entity_id)
|
|
1629
1653
|
return entity
|
|
1630
1654
|
|
|
1631
1655
|
|
|
1632
|
-
def get_comments_for_project(project_id, page=0):
|
|
1656
|
+
def get_comments_for_project(project_id, page=0, limit=None):
|
|
1633
1657
|
"""
|
|
1634
1658
|
Return all comments for given project.
|
|
1635
1659
|
"""
|
|
@@ -1638,7 +1662,9 @@ def get_comments_for_project(project_id, page=0):
|
|
|
1638
1662
|
.filter(Task.project_id == project_id)
|
|
1639
1663
|
.order_by(Comment.updated_at.desc())
|
|
1640
1664
|
)
|
|
1641
|
-
return query_utils.get_paginated_results(
|
|
1665
|
+
return query_utils.get_paginated_results(
|
|
1666
|
+
query, page, limit, relations=True
|
|
1667
|
+
)
|
|
1642
1668
|
|
|
1643
1669
|
|
|
1644
1670
|
def get_time_spents_for_project(project_id, page=0):
|
|
@@ -1649,18 +1675,38 @@ def get_time_spents_for_project(project_id, page=0):
|
|
|
1649
1675
|
return query_utils.get_paginated_results(query, page)
|
|
1650
1676
|
|
|
1651
1677
|
|
|
1652
|
-
def get_tasks_for_project(
|
|
1678
|
+
def get_tasks_for_project(
|
|
1679
|
+
project_id, page=0, task_type_id=None, episode_id=None
|
|
1680
|
+
):
|
|
1653
1681
|
"""
|
|
1654
1682
|
Return all tasks for given project.
|
|
1655
1683
|
"""
|
|
1656
1684
|
query = Task.query.filter(Task.project_id == project_id).order_by(
|
|
1657
1685
|
Task.updated_at.desc()
|
|
1658
1686
|
)
|
|
1687
|
+
if task_type_id is not None:
|
|
1688
|
+
query = query.filter(Task.task_type_id == task_type_id)
|
|
1689
|
+
if episode_id is not None:
|
|
1690
|
+
Sequence = aliased(Entity, name="sequence")
|
|
1691
|
+
query = (
|
|
1692
|
+
query.join(Entity, Entity.id == Task.entity_id)
|
|
1693
|
+
.join(Sequence, Sequence.id == Entity.parent_id)
|
|
1694
|
+
.filter(Sequence.parent_id == episode_id)
|
|
1695
|
+
)
|
|
1696
|
+
|
|
1697
|
+
if permissions.has_vendor_permissions():
|
|
1698
|
+
query = query.filter(user_service.build_assignee_filter())
|
|
1699
|
+
elif not permissions.has_admin_permissions():
|
|
1700
|
+
query = query.join(Project).filter(
|
|
1701
|
+
user_service.build_related_projects_filter()
|
|
1702
|
+
)
|
|
1703
|
+
return query
|
|
1704
|
+
|
|
1659
1705
|
return query_utils.get_paginated_results(query, page, relations=True)
|
|
1660
1706
|
|
|
1661
1707
|
|
|
1662
1708
|
def get_full_task(task_id, user_id):
|
|
1663
|
-
task =
|
|
1709
|
+
task = get_task(task_id, relations=True)
|
|
1664
1710
|
task_type = get_task_type(task["task_type_id"])
|
|
1665
1711
|
project = projects_service.get_project(task["project_id"])
|
|
1666
1712
|
task_status = get_task_status(task["task_status_id"])
|
|
@@ -1724,6 +1770,7 @@ def reset_task_data(task_id):
|
|
|
1724
1770
|
real_start_date = None
|
|
1725
1771
|
last_comment_date = None
|
|
1726
1772
|
end_date = None
|
|
1773
|
+
done_date = None
|
|
1727
1774
|
entity = entities_service.get_entity(task.entity_id)
|
|
1728
1775
|
task_status_id = get_default_status(
|
|
1729
1776
|
for_concept=entity["entity_type_id"]
|
|
@@ -1736,6 +1783,7 @@ def reset_task_data(task_id):
|
|
|
1736
1783
|
.add_columns(
|
|
1737
1784
|
TaskStatus.is_retake,
|
|
1738
1785
|
TaskStatus.is_feedback_request,
|
|
1786
|
+
TaskStatus.is_done,
|
|
1739
1787
|
TaskStatus.short_name,
|
|
1740
1788
|
)
|
|
1741
1789
|
.all()
|
|
@@ -1746,6 +1794,7 @@ def reset_task_data(task_id):
|
|
|
1746
1794
|
comment,
|
|
1747
1795
|
task_status_is_retake,
|
|
1748
1796
|
task_status_is_feedback_request,
|
|
1797
|
+
task_status_is_done,
|
|
1749
1798
|
task_status_short_name,
|
|
1750
1799
|
) in comments:
|
|
1751
1800
|
if task_status_is_retake and not previous_is_retake:
|
|
@@ -1758,6 +1807,11 @@ def reset_task_data(task_id):
|
|
|
1758
1807
|
if task_status_is_feedback_request:
|
|
1759
1808
|
end_date = comment.created_at
|
|
1760
1809
|
|
|
1810
|
+
print("ok", task_status_is_done)
|
|
1811
|
+
if task_status_is_done:
|
|
1812
|
+
done_date = comment.created_at
|
|
1813
|
+
print(done_date)
|
|
1814
|
+
|
|
1761
1815
|
task_status_id = comment.task_status_id
|
|
1762
1816
|
last_comment_date = comment.created_at
|
|
1763
1817
|
|
|
@@ -1773,12 +1827,13 @@ def reset_task_data(task_id):
|
|
|
1773
1827
|
"real_start_date": real_start_date,
|
|
1774
1828
|
"last_comment_date": last_comment_date,
|
|
1775
1829
|
"end_date": end_date,
|
|
1830
|
+
"done_date": done_date,
|
|
1776
1831
|
"task_status_id": task_status_id,
|
|
1777
1832
|
}
|
|
1778
1833
|
)
|
|
1779
1834
|
project_id = str(task.project_id)
|
|
1780
1835
|
events.emit("task:update", {"task_id": task.id}, project_id)
|
|
1781
|
-
return task.serialize()
|
|
1836
|
+
return task.serialize(relations=True)
|
|
1782
1837
|
|
|
1783
1838
|
|
|
1784
1839
|
def get_persons_tasks_dates():
|
|
@@ -1907,14 +1962,20 @@ def get_open_tasks(
|
|
|
1907
1962
|
query_stats = query_stats.filter(TaskStatus.id == task_status_id)
|
|
1908
1963
|
|
|
1909
1964
|
if person_id is not None:
|
|
1910
|
-
|
|
1911
|
-
|
|
1965
|
+
if person_id == "unassigned":
|
|
1966
|
+
query = query.filter(Task.assignees == None)
|
|
1967
|
+
query_stats = query_stats.filter(Task.assignees == None)
|
|
1968
|
+
else:
|
|
1969
|
+
query = query.filter(Task.assignees.any(id=person_id))
|
|
1970
|
+
query_stats = query_stats.filter(Task.assignees.any(id=person_id))
|
|
1912
1971
|
|
|
1913
1972
|
if start_date is not None:
|
|
1973
|
+
start_date = func.cast(start_date, Task.start_date.type)
|
|
1914
1974
|
query = query.filter(Task.start_date >= start_date)
|
|
1915
1975
|
query_stats = query_stats.filter(Task.start_date >= start_date)
|
|
1916
1976
|
|
|
1917
1977
|
if due_date is not None:
|
|
1978
|
+
due_date = func.cast(due_date, Task.due_date.type)
|
|
1918
1979
|
query = query.filter(Task.due_date <= due_date)
|
|
1919
1980
|
query_stats = query_stats.filter(Task.due_date <= due_date)
|
|
1920
1981
|
|
|
@@ -1972,7 +2033,7 @@ def get_open_tasks(
|
|
|
1972
2033
|
except EpisodeNotFoundException:
|
|
1973
2034
|
episode_name = "MP"
|
|
1974
2035
|
|
|
1975
|
-
task_dict =
|
|
2036
|
+
task_dict = get_task(str(task.id), relations=True)
|
|
1976
2037
|
if entity_type_name == "Sequence" and entity_parent_id is not None:
|
|
1977
2038
|
episode_id = entity_parent_id
|
|
1978
2039
|
episode = shots_service.get_episode(episode_id)
|
|
@@ -1998,6 +2059,7 @@ def get_open_tasks(
|
|
|
1998
2059
|
"duration": task.duration,
|
|
1999
2060
|
"start_date": fields.serialize_value(task.start_date),
|
|
2000
2061
|
"due_date": fields.serialize_value(task.due_date),
|
|
2062
|
+
"done_date": fields.serialize_value(task.done_date),
|
|
2001
2063
|
"type_name": task_type_name,
|
|
2002
2064
|
"task_type_for_entity": task_type_for_entity,
|
|
2003
2065
|
"status_name": task_status_name,
|
|
@@ -2043,3 +2105,80 @@ def get_open_tasks(
|
|
|
2043
2105
|
"page": page or 1,
|
|
2044
2106
|
}
|
|
2045
2107
|
return result
|
|
2108
|
+
|
|
2109
|
+
|
|
2110
|
+
def get_open_tasks_stats():
|
|
2111
|
+
"""
|
|
2112
|
+
Return the amount of tasks, done tasks, estimation, and duration for each
|
|
2113
|
+
status in open projects. Aggregate the amounts for each project.
|
|
2114
|
+
"""
|
|
2115
|
+
Sequence = aliased(Entity, name="sequence")
|
|
2116
|
+
Episode = aliased(Entity, name="episode")
|
|
2117
|
+
|
|
2118
|
+
from zou.app import db
|
|
2119
|
+
|
|
2120
|
+
query_stats = (
|
|
2121
|
+
db.session.query(
|
|
2122
|
+
func.count().label("amount"),
|
|
2123
|
+
func.count(case({TaskStatus.is_done: Task.id})).label(
|
|
2124
|
+
"amount_done"
|
|
2125
|
+
),
|
|
2126
|
+
func.sum(Task.duration).label("total_duration"),
|
|
2127
|
+
func.sum(Task.estimation).label("total_estimation"),
|
|
2128
|
+
)
|
|
2129
|
+
.join(TaskType, Task.task_type_id == TaskType.id)
|
|
2130
|
+
.join(TaskStatus, Task.task_status_id == TaskStatus.id)
|
|
2131
|
+
.join(Entity, Entity.id == Task.entity_id)
|
|
2132
|
+
.join(EntityType, EntityType.id == Entity.entity_type_id)
|
|
2133
|
+
.join(Project, Project.id == Task.project_id)
|
|
2134
|
+
.join(ProjectStatus, ProjectStatus.id == Project.project_status_id)
|
|
2135
|
+
.filter(TaskType.for_entity != "Concept")
|
|
2136
|
+
.group_by(Project.id, TaskType.id, TaskStatus.id)
|
|
2137
|
+
.add_columns(
|
|
2138
|
+
Project.id.label("project_id"),
|
|
2139
|
+
TaskType.id.label("task_type_id"),
|
|
2140
|
+
TaskStatus.id.label("task_status_id"),
|
|
2141
|
+
)
|
|
2142
|
+
)
|
|
2143
|
+
|
|
2144
|
+
if permissions.has_admin_permissions():
|
|
2145
|
+
query_stats = query_stats.filter(ProjectStatus.name == "Open")
|
|
2146
|
+
else:
|
|
2147
|
+
query_stats = query_stats.filter(
|
|
2148
|
+
user_service.build_related_projects_filter()
|
|
2149
|
+
)
|
|
2150
|
+
|
|
2151
|
+
stats_status = query_stats.all()
|
|
2152
|
+
|
|
2153
|
+
statuses_stats = [
|
|
2154
|
+
{
|
|
2155
|
+
"task_status_id": stat.task_status_id,
|
|
2156
|
+
"task_type_id": stat.task_type_id,
|
|
2157
|
+
"project_id": stat.project_id,
|
|
2158
|
+
"amount": stat.amount,
|
|
2159
|
+
"amount_done": stat.amount_done,
|
|
2160
|
+
"total_duration": stat.total_duration,
|
|
2161
|
+
"total_estimation": stat.total_estimation,
|
|
2162
|
+
}
|
|
2163
|
+
for stat in stats_status
|
|
2164
|
+
]
|
|
2165
|
+
|
|
2166
|
+
stats_map = {}
|
|
2167
|
+
for stat in statuses_stats:
|
|
2168
|
+
project_id = stat["project_id"]
|
|
2169
|
+
if project_id not in stats_map:
|
|
2170
|
+
stats_map[project_id] = {
|
|
2171
|
+
"amount": 0,
|
|
2172
|
+
"amount_done": 0,
|
|
2173
|
+
"total_duration": 0,
|
|
2174
|
+
"total_estimation": 0,
|
|
2175
|
+
"task_types": [],
|
|
2176
|
+
}
|
|
2177
|
+
project_stats = stats_map[project_id]
|
|
2178
|
+
project_stats["amount"] += stat["amount"]
|
|
2179
|
+
project_stats["amount_done"] += stat["amount_done"]
|
|
2180
|
+
project_stats["total_duration"] += stat["total_duration"]
|
|
2181
|
+
project_stats["total_estimation"] += stat["total_estimation"]
|
|
2182
|
+
project_stats["task_types"].append(stat)
|
|
2183
|
+
|
|
2184
|
+
return stats_map
|