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.
- 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} +84 -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/flask.py +1 -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.14.dist-info → zou-0.20.11.dist-info}/METADATA +75 -69
- {zou-0.19.14.dist-info → zou-0.20.11.dist-info}/RECORD +164 -135
- {zou-0.19.14.dist-info → zou-0.20.11.dist-info}/WHEEL +1 -1
- {zou-0.19.14.dist-info → zou-0.20.11.dist-info}/LICENSE +0 -0
- {zou-0.19.14.dist-info → zou-0.20.11.dist-info}/entry_points.txt +0 -0
- {zou-0.19.14.dist-info → zou-0.20.11.dist-info}/top_level.txt +0 -0
|
@@ -31,7 +31,7 @@ from zou.app.models.task import Task
|
|
|
31
31
|
from zou.app.models.time_spent import TimeSpent
|
|
32
32
|
from zou.app.models.working_file import WorkingFile
|
|
33
33
|
|
|
34
|
-
from zou.app.utils import events, fields
|
|
34
|
+
from zou.app.utils import events, fields, date_helpers
|
|
35
35
|
from zou.app.stores import file_store
|
|
36
36
|
from zou.app import config
|
|
37
37
|
|
|
@@ -149,12 +149,12 @@ def remove_task(task_id, force=False):
|
|
|
149
149
|
return task_serialized
|
|
150
150
|
|
|
151
151
|
|
|
152
|
-
def remove_preview_file_by_id(preview_file_id):
|
|
152
|
+
def remove_preview_file_by_id(preview_file_id, force=False):
|
|
153
153
|
preview_file = PreviewFile.get(preview_file_id)
|
|
154
|
-
return remove_preview_file(preview_file)
|
|
154
|
+
return remove_preview_file(preview_file, force=force)
|
|
155
155
|
|
|
156
156
|
|
|
157
|
-
def remove_preview_file(preview_file):
|
|
157
|
+
def remove_preview_file(preview_file, force=False):
|
|
158
158
|
"""
|
|
159
159
|
Remove all files related to given preview file, then remove the preview file
|
|
160
160
|
entry from the database.
|
|
@@ -169,13 +169,13 @@ def remove_preview_file(preview_file):
|
|
|
169
169
|
if news is not None:
|
|
170
170
|
news.update({"preview_file_id": None})
|
|
171
171
|
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
172
|
+
if config.REMOVE_FILES or force:
|
|
173
|
+
if preview_file.extension == "png":
|
|
174
|
+
clear_picture_files(preview_file.id)
|
|
175
|
+
elif preview_file.extension == "mp4":
|
|
176
|
+
clear_movie_files(preview_file.id)
|
|
177
|
+
else:
|
|
178
|
+
clear_generic_files(preview_file.id)
|
|
179
179
|
|
|
180
180
|
preview_file.comments = []
|
|
181
181
|
preview_file.save()
|
|
@@ -202,7 +202,9 @@ def remove_preview_file(preview_file):
|
|
|
202
202
|
return preview_file.serialize()
|
|
203
203
|
|
|
204
204
|
|
|
205
|
-
def remove_preview_background_file_by_id(
|
|
205
|
+
def remove_preview_background_file_by_id(
|
|
206
|
+
preview_background_file_id, force=False
|
|
207
|
+
):
|
|
206
208
|
"""
|
|
207
209
|
Remove all files related to given preview background file, then remove the
|
|
208
210
|
preview background file entry from the database.
|
|
@@ -210,16 +212,15 @@ def remove_preview_background_file_by_id(preview_background_file_id):
|
|
|
210
212
|
preview_background_file = PreviewBackgroundFile.get(
|
|
211
213
|
preview_background_file_id
|
|
212
214
|
)
|
|
213
|
-
return remove_preview_background_file(preview_background_file)
|
|
215
|
+
return remove_preview_background_file(preview_background_file, force=force)
|
|
214
216
|
|
|
215
217
|
|
|
216
|
-
def remove_preview_background_file(preview_background_file):
|
|
218
|
+
def remove_preview_background_file(preview_background_file, force=False):
|
|
217
219
|
"""
|
|
218
220
|
Remove all files related to given preview background file, then remove the
|
|
219
221
|
preview background file entry from the database.
|
|
220
222
|
"""
|
|
221
|
-
|
|
222
|
-
# clear_preview_background_files(preview_background_file.id)
|
|
223
|
+
clear_preview_background_files(preview_background_file.id, force=force)
|
|
223
224
|
preview_background_file.delete()
|
|
224
225
|
return preview_background_file.serialize()
|
|
225
226
|
|
|
@@ -240,25 +241,26 @@ def remove_attachment_file(attachment_file):
|
|
|
240
241
|
Remove all files related to given attachment file, then remove the
|
|
241
242
|
attachment file entry from the database.
|
|
242
243
|
"""
|
|
243
|
-
|
|
244
|
-
|
|
244
|
+
if config.REMOVE_FILES:
|
|
245
|
+
file_store.remove_file("attachments", str(attachment_file.id))
|
|
245
246
|
attachment_dict = attachment_file.serialize()
|
|
246
247
|
attachment_file.delete()
|
|
247
248
|
return attachment_dict
|
|
248
249
|
|
|
249
250
|
|
|
250
|
-
def clear_preview_background_files(preview_background_id):
|
|
251
|
+
def clear_preview_background_files(preview_background_id, force=False):
|
|
251
252
|
"""
|
|
252
253
|
Remove all files related to given preview background file.
|
|
253
254
|
"""
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
255
|
+
if config.REMOVE_FILES or force:
|
|
256
|
+
for image_type in [
|
|
257
|
+
"thumbnails",
|
|
258
|
+
"preview-backgrounds",
|
|
259
|
+
]:
|
|
260
|
+
try:
|
|
261
|
+
file_store.remove_picture(image_type, preview_background_id)
|
|
262
|
+
except BaseException:
|
|
263
|
+
pass
|
|
262
264
|
|
|
263
265
|
|
|
264
266
|
def clear_picture_files(preview_file_id):
|
|
@@ -360,6 +362,11 @@ def remove_project(project_id):
|
|
|
360
362
|
|
|
361
363
|
ApiEvent.delete_all_by(project_id=project_id)
|
|
362
364
|
Entity.delete_all_by(project_id=project_id)
|
|
365
|
+
|
|
366
|
+
descriptors = MetadataDescriptor.query.filter_by(project_id=project_id)
|
|
367
|
+
for descriptor in descriptors:
|
|
368
|
+
descriptor.departments = []
|
|
369
|
+
descriptor.save()
|
|
363
370
|
MetadataDescriptor.delete_all_by(project_id=project_id)
|
|
364
371
|
Milestone.delete_all_by(project_id=project_id)
|
|
365
372
|
ScheduleItem.delete_all_by(project_id=project_id)
|
|
@@ -440,7 +447,9 @@ def remove_old_events(days_old=90):
|
|
|
440
447
|
"""
|
|
441
448
|
Remove events older than *days_old*.
|
|
442
449
|
"""
|
|
443
|
-
limit_date =
|
|
450
|
+
limit_date = date_helpers.get_utc_now_datetime() - datetime.timedelta(
|
|
451
|
+
days=days_old
|
|
452
|
+
)
|
|
444
453
|
ApiEvent.query.filter(ApiEvent.created_at < limit_date).delete()
|
|
445
454
|
ApiEvent.commit()
|
|
446
455
|
|
|
@@ -449,7 +458,9 @@ def remove_old_login_logs(days_old=90):
|
|
|
449
458
|
"""
|
|
450
459
|
Remove login logs older than *days_old*.
|
|
451
460
|
"""
|
|
452
|
-
limit_date =
|
|
461
|
+
limit_date = date_helpers.get_utc_now_datetime() - datetime.timedelta(
|
|
462
|
+
days=days_old
|
|
463
|
+
)
|
|
453
464
|
LoginLog.query.filter(LoginLog.created_at < limit_date).delete()
|
|
454
465
|
LoginLog.commit()
|
|
455
466
|
|
|
@@ -458,7 +469,9 @@ def remove_old_notifications(days_old=90):
|
|
|
458
469
|
"""
|
|
459
470
|
Remove notifications older than *days_old*.
|
|
460
471
|
"""
|
|
461
|
-
limit_date =
|
|
472
|
+
limit_date = date_helpers.get_utc_now_datetime() - datetime.timedelta(
|
|
473
|
+
days=days_old
|
|
474
|
+
)
|
|
462
475
|
Notification.query.filter(Notification.created_at < limit_date).delete()
|
|
463
476
|
Notification.commit()
|
|
464
477
|
|
|
@@ -33,7 +33,7 @@ from zou.app.services.exception import (
|
|
|
33
33
|
|
|
34
34
|
def clear_edit_cache(edit_id):
|
|
35
35
|
cache.cache.delete_memoized(get_edit, edit_id)
|
|
36
|
-
cache.cache.delete_memoized(
|
|
36
|
+
cache.cache.delete_memoized(get_edit, edit_id, True)
|
|
37
37
|
cache.cache.delete_memoized(get_full_edit, edit_id)
|
|
38
38
|
|
|
39
39
|
|
|
@@ -241,19 +241,13 @@ def get_edit_raw(edit_id):
|
|
|
241
241
|
|
|
242
242
|
|
|
243
243
|
@cache.memoize_function(120)
|
|
244
|
-
def get_edit(edit_id):
|
|
244
|
+
def get_edit(edit_id, relations=False):
|
|
245
245
|
"""
|
|
246
246
|
Return given edit as a dictionary.
|
|
247
247
|
"""
|
|
248
|
-
return get_edit_raw(edit_id).serialize(
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
@cache.memoize_function(120)
|
|
252
|
-
def get_edit_with_relations(edit_id):
|
|
253
|
-
"""
|
|
254
|
-
Return given edit as a dictionary.
|
|
255
|
-
"""
|
|
256
|
-
return get_edit_raw(edit_id).serialize(obj_type="Edit", relations=True)
|
|
248
|
+
return get_edit_raw(edit_id).serialize(
|
|
249
|
+
obj_type="Edit", relations=relations
|
|
250
|
+
)
|
|
257
251
|
|
|
258
252
|
|
|
259
253
|
@cache.memoize_function(120)
|
|
@@ -37,7 +37,7 @@ def send_notification(person_id, subject, messages):
|
|
|
37
37
|
)
|
|
38
38
|
|
|
39
39
|
if person["notifications_slack_enabled"]:
|
|
40
|
-
organisation = persons_service.get_organisation()
|
|
40
|
+
organisation = persons_service.get_organisation(sensitive=True)
|
|
41
41
|
userid = person["notifications_slack_userid"]
|
|
42
42
|
token = organisation.get("chat_token_slack", "")
|
|
43
43
|
if config.ENABLE_JOB_QUEUE:
|
|
@@ -49,7 +49,7 @@ def send_notification(person_id, subject, messages):
|
|
|
49
49
|
chats.send_to_slack(token, userid, slack_message)
|
|
50
50
|
|
|
51
51
|
if person["notifications_mattermost_enabled"]:
|
|
52
|
-
organisation = persons_service.get_organisation()
|
|
52
|
+
organisation = persons_service.get_organisation(sensitive=True)
|
|
53
53
|
userid = person["notifications_mattermost_userid"]
|
|
54
54
|
webhook = organisation.get("chat_webhook_mattermost", "")
|
|
55
55
|
if config.ENABLE_JOB_QUEUE:
|
|
@@ -61,7 +61,7 @@ def send_notification(person_id, subject, messages):
|
|
|
61
61
|
chats.send_to_mattermost(webhook, userid, mattermost_message)
|
|
62
62
|
|
|
63
63
|
if person["notifications_discord_enabled"]:
|
|
64
|
-
organisation = persons_service.get_organisation()
|
|
64
|
+
organisation = persons_service.get_organisation(sensitive=True)
|
|
65
65
|
userid = person["notifications_discord_userid"]
|
|
66
66
|
token = organisation.get("chat_token_discord", "")
|
|
67
67
|
if config.ENABLE_JOB_QUEUE:
|
|
@@ -296,7 +296,7 @@ def get_task_descriptors(person_id, task):
|
|
|
296
296
|
project = projects_service.get_project(task["project_id"])
|
|
297
297
|
task_type = tasks_service.get_task_type(task["task_type_id"])
|
|
298
298
|
entity = entities_service.get_entity(task["entity_id"])
|
|
299
|
-
|
|
299
|
+
entity_name, episode_id, _ = names_service.get_full_entity_name(
|
|
300
300
|
entity["id"]
|
|
301
301
|
)
|
|
302
302
|
|
|
@@ -2,6 +2,9 @@ from zou.app.services import (
|
|
|
2
2
|
base_service,
|
|
3
3
|
projects_service,
|
|
4
4
|
notifications_service,
|
|
5
|
+
assets_service,
|
|
6
|
+
shots_service,
|
|
7
|
+
edits_service,
|
|
5
8
|
)
|
|
6
9
|
from zou.app.utils import (
|
|
7
10
|
date_helpers,
|
|
@@ -108,12 +111,13 @@ def update_entity_preview(entity_id, preview_file_id):
|
|
|
108
111
|
if entity is None:
|
|
109
112
|
raise EntityNotFoundException
|
|
110
113
|
|
|
114
|
+
entity_id = str(entity.id)
|
|
111
115
|
preview_file = PreviewFile.get(preview_file_id)
|
|
112
116
|
if preview_file is None:
|
|
113
117
|
raise PreviewFileNotFoundException
|
|
114
118
|
|
|
115
119
|
entity.update({"preview_file_id": preview_file.id})
|
|
116
|
-
clear_entity_cache(
|
|
120
|
+
clear_entity_cache(entity_id)
|
|
117
121
|
events.emit(
|
|
118
122
|
"preview-file:set-main",
|
|
119
123
|
{"entity_id": entity_id, "preview_file_id": preview_file_id},
|
|
@@ -125,9 +129,14 @@ def update_entity_preview(entity_id, preview_file_id):
|
|
|
125
129
|
entity_type_name = entity_type.name.lower()
|
|
126
130
|
events.emit(
|
|
127
131
|
"%s:update" % entity_type_name,
|
|
128
|
-
{"%s_id" % entity_type_name:
|
|
132
|
+
{"%s_id" % entity_type_name: entity_id},
|
|
129
133
|
project_id=str(entity.project_id),
|
|
130
134
|
)
|
|
135
|
+
assets_service.clear_asset_cache(entity_id)
|
|
136
|
+
edits_service.clear_edit_cache(entity_id)
|
|
137
|
+
shots_service.clear_shot_cache(entity_id)
|
|
138
|
+
shots_service.clear_episode_cache(entity_id)
|
|
139
|
+
shots_service.clear_sequence_cache(entity_id)
|
|
131
140
|
return entity.serialize()
|
|
132
141
|
|
|
133
142
|
|
|
@@ -240,7 +249,9 @@ def get_entities_and_tasks(criterions={}):
|
|
|
240
249
|
Task.end_date,
|
|
241
250
|
Task.start_date,
|
|
242
251
|
Task.due_date,
|
|
252
|
+
Task.done_date,
|
|
243
253
|
Task.last_comment_date,
|
|
254
|
+
Task.difficulty,
|
|
244
255
|
assignees_table.columns.person,
|
|
245
256
|
)
|
|
246
257
|
)
|
|
@@ -269,7 +280,9 @@ def get_entities_and_tasks(criterions={}):
|
|
|
269
280
|
task_end_date,
|
|
270
281
|
task_start_date,
|
|
271
282
|
task_due_date,
|
|
283
|
+
task_done_date,
|
|
272
284
|
task_last_comment_date,
|
|
285
|
+
task_difficulty,
|
|
273
286
|
person_id,
|
|
274
287
|
) in query.all():
|
|
275
288
|
entity_id = str(entity.id)
|
|
@@ -305,6 +318,7 @@ def get_entities_and_tasks(criterions={}):
|
|
|
305
318
|
"entity_id": entity_id,
|
|
306
319
|
"end_date": task_end_date,
|
|
307
320
|
"due_date": task_due_date,
|
|
321
|
+
"done_date": task_done_date,
|
|
308
322
|
"duration": task_duration,
|
|
309
323
|
"is_subscribed": subscription_map.get(task_id, False),
|
|
310
324
|
"last_comment_date": task_last_comment_date,
|
|
@@ -312,6 +326,7 @@ def get_entities_and_tasks(criterions={}):
|
|
|
312
326
|
"real_start_date": task_real_start_date,
|
|
313
327
|
"retake_count": task_retake_count,
|
|
314
328
|
"start_date": task_start_date,
|
|
329
|
+
"difficulty": task_difficulty,
|
|
315
330
|
"task_status_id": str(task_status_id),
|
|
316
331
|
"task_type_id": str(task_type_id),
|
|
317
332
|
"assignees": [],
|
|
@@ -5,7 +5,12 @@ from sqlalchemy import func
|
|
|
5
5
|
|
|
6
6
|
|
|
7
7
|
def get_last_events(
|
|
8
|
-
after=None,
|
|
8
|
+
after=None,
|
|
9
|
+
before=None,
|
|
10
|
+
limit=100,
|
|
11
|
+
only_files=False,
|
|
12
|
+
project_id=None,
|
|
13
|
+
name=None,
|
|
9
14
|
):
|
|
10
15
|
"""
|
|
11
16
|
Return last 100 events published. If before parameter is set, it returns
|
|
@@ -38,7 +43,10 @@ def get_last_events(
|
|
|
38
43
|
if project_id is not None:
|
|
39
44
|
query = query.filter(ApiEvent.project_id == project_id)
|
|
40
45
|
|
|
41
|
-
|
|
46
|
+
if name is not None:
|
|
47
|
+
query = query.filter(ApiEvent.name == name)
|
|
48
|
+
|
|
49
|
+
events = query.limit(limit).all()
|
|
42
50
|
return [
|
|
43
51
|
fields.serialize_dict(
|
|
44
52
|
{
|
|
@@ -63,7 +71,7 @@ def create_login_log(person_id, ip_address, origin):
|
|
|
63
71
|
return login_log.serialize()
|
|
64
72
|
|
|
65
73
|
|
|
66
|
-
def get_last_login_logs(before=None,
|
|
74
|
+
def get_last_login_logs(before=None, limit=100):
|
|
67
75
|
"""
|
|
68
76
|
Return last 100 login logs published. If before parameter is set, it returns
|
|
69
77
|
last 100 login logs before this date.
|
|
@@ -77,7 +85,7 @@ def get_last_login_logs(before=None, page_size=100):
|
|
|
77
85
|
LoginLog.created_at < func.cast(before, LoginLog.created_at.type)
|
|
78
86
|
)
|
|
79
87
|
|
|
80
|
-
login_logs = query.limit(
|
|
88
|
+
login_logs = query.limit(limit).all()
|
|
81
89
|
return [
|
|
82
90
|
{
|
|
83
91
|
"created_at": fields.serialize_value(created_at),
|
zou/app/services/exception.py
CHANGED
|
@@ -49,6 +49,10 @@ class DepartmentNotFoundException(NotFound):
|
|
|
49
49
|
pass
|
|
50
50
|
|
|
51
51
|
|
|
52
|
+
class StudioNotFoundException(NotFound):
|
|
53
|
+
pass
|
|
54
|
+
|
|
55
|
+
|
|
52
56
|
class TaskStatusNotFoundException(NotFound):
|
|
53
57
|
pass
|
|
54
58
|
|
|
@@ -247,7 +251,7 @@ class EntryAlreadyExistsException(Exception):
|
|
|
247
251
|
pass
|
|
248
252
|
|
|
249
253
|
|
|
250
|
-
class
|
|
254
|
+
class WrongParameterException(Exception):
|
|
251
255
|
def __init__(self, message, dict=None):
|
|
252
256
|
super().__init__(message)
|
|
253
257
|
self.dict = dict
|
|
@@ -257,10 +261,6 @@ class WrongIdFormatException(Exception):
|
|
|
257
261
|
pass
|
|
258
262
|
|
|
259
263
|
|
|
260
|
-
class WrongParameterException(Exception):
|
|
261
|
-
pass
|
|
262
|
-
|
|
263
|
-
|
|
264
264
|
class ModelWithRelationsDeletionException(Exception):
|
|
265
265
|
pass
|
|
266
266
|
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import slugify
|
|
2
2
|
|
|
3
|
+
from zou.app.utils import cache
|
|
4
|
+
|
|
3
5
|
from zou.app.services import (
|
|
4
6
|
entities_service,
|
|
5
7
|
files_service,
|
|
@@ -10,6 +12,9 @@ from zou.app.services import (
|
|
|
10
12
|
)
|
|
11
13
|
|
|
12
14
|
|
|
15
|
+
cache.memoize_function(1200)
|
|
16
|
+
|
|
17
|
+
|
|
13
18
|
def get_full_entity_name(entity_id):
|
|
14
19
|
"""
|
|
15
20
|
Get full entity name whether it's an asset or a shot. If it's a shot
|
|
@@ -47,7 +52,7 @@ def get_full_entity_name(entity_id):
|
|
|
47
52
|
asset_type = entities_service.get_entity_type(entity["entity_type_id"])
|
|
48
53
|
episode_id = entity["source_id"]
|
|
49
54
|
name = "%s / %s" % (asset_type["name"], entity["name"])
|
|
50
|
-
return (name, episode_id)
|
|
55
|
+
return (name, episode_id, entity["preview_file_id"])
|
|
51
56
|
|
|
52
57
|
|
|
53
58
|
def get_preview_file_name(preview_file_id):
|
|
@@ -61,7 +66,7 @@ def get_preview_file_name(preview_file_id):
|
|
|
61
66
|
task = tasks_service.get_task(preview_file["task_id"])
|
|
62
67
|
task_type = tasks_service.get_task_type(task["task_type_id"])
|
|
63
68
|
project = projects_service.get_project(task["project_id"])
|
|
64
|
-
(entity_name, _) = get_full_entity_name(task["entity_id"])
|
|
69
|
+
(entity_name, _, _) = get_full_entity_name(task["entity_id"])
|
|
65
70
|
|
|
66
71
|
if (
|
|
67
72
|
organisation["use_original_file_name"]
|
zou/app/services/news_service.py
CHANGED
|
@@ -92,16 +92,17 @@ def get_last_news_for_project(
|
|
|
92
92
|
task_status_id=None,
|
|
93
93
|
author_id=None,
|
|
94
94
|
page=1,
|
|
95
|
-
|
|
95
|
+
limit=50,
|
|
96
96
|
before=None,
|
|
97
97
|
after=None,
|
|
98
98
|
episode_id=None,
|
|
99
|
+
current_user=None,
|
|
99
100
|
):
|
|
100
101
|
"""
|
|
101
102
|
Return last 50 news for given project. Add related information to make it
|
|
102
103
|
displayable.
|
|
103
104
|
"""
|
|
104
|
-
offset = (page - 1) *
|
|
105
|
+
offset = (page - 1) * limit
|
|
105
106
|
|
|
106
107
|
query = (
|
|
107
108
|
News.query.order_by(News.created_at.desc())
|
|
@@ -120,6 +121,9 @@ def get_last_news_for_project(
|
|
|
120
121
|
|
|
121
122
|
if len(project_ids) > 0:
|
|
122
123
|
query = query.filter(Project.id.in_(project_ids))
|
|
124
|
+
elif current_user is not None:
|
|
125
|
+
if current_user.role.code != "admin":
|
|
126
|
+
query = query.filter(Project.team.contains(current_user))
|
|
123
127
|
|
|
124
128
|
if entity_id is not None:
|
|
125
129
|
query = query.filter(Entity.id == entity_id)
|
|
@@ -154,7 +158,7 @@ def get_last_news_for_project(
|
|
|
154
158
|
News.created_at < func.cast(before, News.created_at.type)
|
|
155
159
|
)
|
|
156
160
|
|
|
157
|
-
(total, nb_pages) = _get_news_total(query,
|
|
161
|
+
(total, nb_pages) = _get_news_total(query, limit)
|
|
158
162
|
|
|
159
163
|
query = query.add_columns(
|
|
160
164
|
Project.id,
|
|
@@ -168,7 +172,7 @@ def get_last_news_for_project(
|
|
|
168
172
|
Entity.preview_file_id,
|
|
169
173
|
)
|
|
170
174
|
|
|
171
|
-
query = query.limit(
|
|
175
|
+
query = query.limit(limit)
|
|
172
176
|
query = query.offset(offset)
|
|
173
177
|
news_list = query.all()
|
|
174
178
|
result = []
|
|
@@ -185,7 +189,7 @@ def get_last_news_for_project(
|
|
|
185
189
|
preview_file_annotations,
|
|
186
190
|
entity_preview_file_id,
|
|
187
191
|
) in news_list:
|
|
188
|
-
|
|
192
|
+
full_entity_name, episode_id, _ = names_service.get_full_entity_name(
|
|
189
193
|
task_entity_id
|
|
190
194
|
)
|
|
191
195
|
|
|
@@ -217,15 +221,15 @@ def get_last_news_for_project(
|
|
|
217
221
|
"data": result,
|
|
218
222
|
"total": total,
|
|
219
223
|
"nb_pages": nb_pages,
|
|
220
|
-
"limit":
|
|
224
|
+
"limit": limit,
|
|
221
225
|
"offset": offset,
|
|
222
226
|
"page": page,
|
|
223
227
|
}
|
|
224
228
|
|
|
225
229
|
|
|
226
|
-
def _get_news_total(query,
|
|
230
|
+
def _get_news_total(query, limit):
|
|
227
231
|
total = query.count()
|
|
228
|
-
nb_pages = int(math.ceil(total / float(
|
|
232
|
+
nb_pages = int(math.ceil(total / float(limit)))
|
|
229
233
|
return total, nb_pages
|
|
230
234
|
|
|
231
235
|
|
|
@@ -239,6 +243,7 @@ def get_news_stats_for_project(
|
|
|
239
243
|
author_id=None,
|
|
240
244
|
before=None,
|
|
241
245
|
after=None,
|
|
246
|
+
current_user=None,
|
|
242
247
|
):
|
|
243
248
|
"""
|
|
244
249
|
Return the number of news by task status for given project and filters.
|
|
@@ -262,6 +267,9 @@ def get_news_stats_for_project(
|
|
|
262
267
|
|
|
263
268
|
if len(project_ids) > 0:
|
|
264
269
|
query = query.filter(Project.id.in_(project_ids))
|
|
270
|
+
elif current_user is not None:
|
|
271
|
+
if current_user.role.code != "admin":
|
|
272
|
+
query = query.filter(Project.team.contains(current_user))
|
|
265
273
|
|
|
266
274
|
if task_status_id is not None:
|
|
267
275
|
query = query.filter(Comment.task_status_id == task_status_id)
|
|
@@ -306,4 +314,4 @@ def get_news_for_entity(entity_id):
|
|
|
306
314
|
"""
|
|
307
315
|
Get all news related to a given entity.
|
|
308
316
|
"""
|
|
309
|
-
return get_last_news_for_project(entity_id=entity_id,
|
|
317
|
+
return get_last_news_for_project(entity_id=entity_id, limit=2000)
|
|
@@ -16,8 +16,8 @@ from zou.app.models.organisation import Organisation
|
|
|
16
16
|
from zou.app.models.person import Person
|
|
17
17
|
from zou.app.models.time_spent import TimeSpent
|
|
18
18
|
|
|
19
|
-
from zou.app import config
|
|
20
|
-
from zou.app.utils import fields, events, cache, emails
|
|
19
|
+
from zou.app import config, file_store
|
|
20
|
+
from zou.app.utils import fields, events, cache, emails, date_helpers
|
|
21
21
|
from zou.app.services import index_service, auth_service
|
|
22
22
|
from zou.app.stores import auth_tokens_store
|
|
23
23
|
from zou.app.services.exception import (
|
|
@@ -36,8 +36,9 @@ def clear_person_cache():
|
|
|
36
36
|
cache.cache.delete_memoized(get_persons)
|
|
37
37
|
|
|
38
38
|
|
|
39
|
-
def
|
|
39
|
+
def clear_organisation_cache():
|
|
40
40
|
cache.cache.delete_memoized(get_organisation)
|
|
41
|
+
cache.cache.delete_memoized(get_organisation, True)
|
|
41
42
|
|
|
42
43
|
|
|
43
44
|
@cache.memoize_function(120)
|
|
@@ -210,6 +211,7 @@ def create_person(
|
|
|
210
211
|
ldap_uid=None,
|
|
211
212
|
is_bot=False,
|
|
212
213
|
expiration_date=None,
|
|
214
|
+
studio_id=None,
|
|
213
215
|
active=True,
|
|
214
216
|
serialize=True,
|
|
215
217
|
):
|
|
@@ -223,12 +225,14 @@ def create_person(
|
|
|
223
225
|
if expiration_date is not None:
|
|
224
226
|
try:
|
|
225
227
|
if (
|
|
226
|
-
|
|
228
|
+
date_helpers.get_date_from_string(expiration_date).date()
|
|
227
229
|
< datetime.date.today()
|
|
228
230
|
):
|
|
229
231
|
raise WrongParameterException(
|
|
230
232
|
"Expiration date can't be in the past."
|
|
231
233
|
)
|
|
234
|
+
except WrongParameterException:
|
|
235
|
+
raise
|
|
232
236
|
except:
|
|
233
237
|
raise WrongParameterException("Expiration date is not valid.")
|
|
234
238
|
|
|
@@ -245,6 +249,7 @@ def create_person(
|
|
|
245
249
|
ldap_uid=ldap_uid,
|
|
246
250
|
is_bot=is_bot,
|
|
247
251
|
expiration_date=expiration_date,
|
|
252
|
+
studio_id=studio_id,
|
|
248
253
|
active=active,
|
|
249
254
|
)
|
|
250
255
|
if is_bot:
|
|
@@ -274,18 +279,25 @@ def update_password(email, password):
|
|
|
274
279
|
return person.serialize()
|
|
275
280
|
|
|
276
281
|
|
|
277
|
-
def update_person(person_id, data):
|
|
282
|
+
def update_person(person_id, data, bypass_protected_accounts=False):
|
|
278
283
|
"""
|
|
279
284
|
Update person entry with data given in parameter.
|
|
280
285
|
"""
|
|
281
286
|
person = Person.get(person_id)
|
|
282
287
|
if (
|
|
283
|
-
|
|
288
|
+
not bypass_protected_accounts
|
|
284
289
|
and person.email in config.PROTECTED_ACCOUNTS
|
|
285
290
|
):
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
291
|
+
message = None
|
|
292
|
+
if data.get("active") is False:
|
|
293
|
+
message = (
|
|
294
|
+
"Can't set this person as inactive it's a protected account."
|
|
295
|
+
)
|
|
296
|
+
elif data.get("role") is not None:
|
|
297
|
+
message = "Can't change the role of this person it's a protected account."
|
|
298
|
+
|
|
299
|
+
if message is not None:
|
|
300
|
+
raise PersonInProtectedAccounts(message)
|
|
289
301
|
|
|
290
302
|
if "email" in data and data["email"] is not None:
|
|
291
303
|
data["email"] = data["email"].strip()
|
|
@@ -301,6 +313,8 @@ def update_person(person_id, data):
|
|
|
301
313
|
raise WrongParameterException(
|
|
302
314
|
"Expiration date can't be in the past."
|
|
303
315
|
)
|
|
316
|
+
except WrongParameterException:
|
|
317
|
+
raise
|
|
304
318
|
except:
|
|
305
319
|
raise WrongParameterException("Expiration date is not valid.")
|
|
306
320
|
|
|
@@ -372,7 +386,9 @@ def update_person_last_presence(person_id):
|
|
|
372
386
|
date = log.date
|
|
373
387
|
elif time_spent is not None:
|
|
374
388
|
date = time_spent.date
|
|
375
|
-
return update_person(
|
|
389
|
+
return update_person(
|
|
390
|
+
person_id, {"last_presence": date}, bypass_protected_accounts=True
|
|
391
|
+
)
|
|
376
392
|
|
|
377
393
|
|
|
378
394
|
def get_presence_logs(year, month):
|
|
@@ -438,7 +454,7 @@ def invite_person(person_id):
|
|
|
438
454
|
)
|
|
439
455
|
|
|
440
456
|
time_string = format_datetime(
|
|
441
|
-
|
|
457
|
+
date_helpers.get_utc_now_datetime(),
|
|
442
458
|
tzinfo=person["timezone"],
|
|
443
459
|
locale=person["locale"],
|
|
444
460
|
)
|
|
@@ -469,14 +485,14 @@ Thank you and see you soon on Kitsu,
|
|
|
469
485
|
|
|
470
486
|
|
|
471
487
|
@cache.memoize_function(120)
|
|
472
|
-
def get_organisation():
|
|
488
|
+
def get_organisation(sensitive=False):
|
|
473
489
|
"""
|
|
474
490
|
Return organisation set up on this instance. It creates it if none exists.
|
|
475
491
|
"""
|
|
476
492
|
organisation = Organisation.query.first()
|
|
477
493
|
if organisation is None:
|
|
478
494
|
organisation = Organisation.create(name="Kitsu")
|
|
479
|
-
return organisation.present()
|
|
495
|
+
return organisation.present(sensitive=sensitive)
|
|
480
496
|
|
|
481
497
|
|
|
482
498
|
def update_organisation(organisation_id, data):
|
|
@@ -486,7 +502,7 @@ def update_organisation(organisation_id, data):
|
|
|
486
502
|
organisation = Organisation.get(organisation_id)
|
|
487
503
|
organisation.update(data)
|
|
488
504
|
events.emit("organisation:update", {"organisation_id": organisation_id})
|
|
489
|
-
|
|
505
|
+
clear_organisation_cache()
|
|
490
506
|
return organisation.present()
|
|
491
507
|
|
|
492
508
|
|
|
@@ -535,11 +551,11 @@ def clear_avatar(person_id):
|
|
|
535
551
|
person = get_person_raw(person_id)
|
|
536
552
|
person.update({"has_avatar": False})
|
|
537
553
|
clear_person_cache()
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
554
|
+
if config.REMOVE_FILES:
|
|
555
|
+
try:
|
|
556
|
+
file_store.remove_picture("thumbnails", person_id)
|
|
557
|
+
except BaseException:
|
|
558
|
+
pass
|
|
543
559
|
return person.serialize()
|
|
544
560
|
|
|
545
561
|
|
|
@@ -558,9 +574,10 @@ def create_access_token_for_raw_person(person):
|
|
|
558
574
|
if person.expiration_date is not None:
|
|
559
575
|
expires_delta = (
|
|
560
576
|
datetime.datetime.combine(
|
|
561
|
-
person.expiration_date,
|
|
577
|
+
person.expiration_date,
|
|
578
|
+
datetime.datetime.max.time(),
|
|
562
579
|
)
|
|
563
|
-
-
|
|
580
|
+
- date_helpers.get_utc_now_datetime()
|
|
564
581
|
)
|
|
565
582
|
access_token = create_access_token(
|
|
566
583
|
identity=person.id,
|