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
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
from flask import Blueprint
|
|
2
|
+
|
|
3
|
+
from zou.app.utils.api import configure_api_from_blueprint
|
|
4
|
+
|
|
5
|
+
from zou.app.blueprints.chats.resources import (
|
|
6
|
+
ChatResource,
|
|
7
|
+
ChatMessagesResource,
|
|
8
|
+
ChatMessageResource,
|
|
9
|
+
)
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
routes = [
|
|
13
|
+
("/data/entities/<entity_id>/chat", ChatResource),
|
|
14
|
+
("/data/entities/<entity_id>/chat/messages", ChatMessagesResource),
|
|
15
|
+
(
|
|
16
|
+
"/data/entities/<entity_id>/chat/messages/<chat_message_id>",
|
|
17
|
+
ChatMessageResource,
|
|
18
|
+
),
|
|
19
|
+
]
|
|
20
|
+
|
|
21
|
+
blueprint = Blueprint("chats", "chats")
|
|
22
|
+
api = configure_api_from_blueprint(blueprint, routes)
|
|
@@ -0,0 +1,199 @@
|
|
|
1
|
+
from flask import request
|
|
2
|
+
from flask_restful import Resource, reqparse
|
|
3
|
+
from flask_jwt_extended import jwt_required
|
|
4
|
+
|
|
5
|
+
from zou.app.utils import permissions
|
|
6
|
+
|
|
7
|
+
from zou.app.services import (
|
|
8
|
+
chats_service,
|
|
9
|
+
entities_service,
|
|
10
|
+
persons_service,
|
|
11
|
+
user_service,
|
|
12
|
+
)
|
|
13
|
+
from zou.app.services.exception import WrongParameterException
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class ChatResource(Resource):
|
|
17
|
+
|
|
18
|
+
@jwt_required()
|
|
19
|
+
def get(self, entity_id):
|
|
20
|
+
"""
|
|
21
|
+
Get chat details.
|
|
22
|
+
---
|
|
23
|
+
tags:
|
|
24
|
+
- Chat
|
|
25
|
+
parameters:
|
|
26
|
+
- in: path
|
|
27
|
+
name: entity_id
|
|
28
|
+
description: ID of the entity related to the chat
|
|
29
|
+
type: integer
|
|
30
|
+
required: true
|
|
31
|
+
x-example: a24a6ea4-ce75-4665-a070-57453082c25
|
|
32
|
+
responses:
|
|
33
|
+
200:
|
|
34
|
+
description: Chat information
|
|
35
|
+
"""
|
|
36
|
+
entity = entities_service.get_entity(entity_id)
|
|
37
|
+
user_service.check_project_access(entity["project_id"])
|
|
38
|
+
user_service.check_entity_access(entity["id"])
|
|
39
|
+
chat = chats_service.get_chat(entity_id)
|
|
40
|
+
chat["messages"] = chats_service.get_chat_messages(entity_id)
|
|
41
|
+
return chat
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
class ChatMessagesResource(Resource):
|
|
45
|
+
|
|
46
|
+
@jwt_required()
|
|
47
|
+
def get(self, entity_id):
|
|
48
|
+
"""
|
|
49
|
+
Get chat messages for an entity.
|
|
50
|
+
---
|
|
51
|
+
tags:
|
|
52
|
+
- Chat
|
|
53
|
+
parameters:
|
|
54
|
+
- in: path
|
|
55
|
+
name: entity_id
|
|
56
|
+
description: ID of the entity related to the chat
|
|
57
|
+
type: integer
|
|
58
|
+
required: true
|
|
59
|
+
x-example: a24a6ea4-ce75-4665-a070-57453082c25
|
|
60
|
+
responses:
|
|
61
|
+
200:
|
|
62
|
+
description: Chat messages
|
|
63
|
+
"""
|
|
64
|
+
entity = entities_service.get_entity(entity_id)
|
|
65
|
+
user_service.check_project_access(entity["project_id"])
|
|
66
|
+
user_service.check_entity_access(entity["id"])
|
|
67
|
+
return chats_service.get_chat_messages_for_entity(entity_id)
|
|
68
|
+
|
|
69
|
+
@jwt_required()
|
|
70
|
+
def post(self, entity_id):
|
|
71
|
+
"""
|
|
72
|
+
Create a new chat message.
|
|
73
|
+
---
|
|
74
|
+
tags:
|
|
75
|
+
- Chat
|
|
76
|
+
parameters:
|
|
77
|
+
- in: path
|
|
78
|
+
name: entity_id
|
|
79
|
+
description: ID of the entity related to the chat
|
|
80
|
+
type: integer
|
|
81
|
+
required: true
|
|
82
|
+
x-example: a24a6ea4-ce75-4665-a070-57453082c25
|
|
83
|
+
- in: body
|
|
84
|
+
name: message
|
|
85
|
+
description: Message to send
|
|
86
|
+
type: string
|
|
87
|
+
required: true
|
|
88
|
+
x-example: Hello, world!
|
|
89
|
+
- in: formData
|
|
90
|
+
name: files
|
|
91
|
+
description: Files to attach
|
|
92
|
+
type: file
|
|
93
|
+
required: false
|
|
94
|
+
responses:
|
|
95
|
+
201:
|
|
96
|
+
description: Chat message created
|
|
97
|
+
400:
|
|
98
|
+
description: Not participant of the chat
|
|
99
|
+
"""
|
|
100
|
+
entity = entities_service.get_entity(entity_id)
|
|
101
|
+
user_service.check_project_access(entity["project_id"])
|
|
102
|
+
user_service.check_entity_access(entity["id"])
|
|
103
|
+
|
|
104
|
+
person = persons_service.get_current_user()
|
|
105
|
+
if request.is_json:
|
|
106
|
+
location = ["values", "json"]
|
|
107
|
+
else:
|
|
108
|
+
location = ["values", "form"]
|
|
109
|
+
parser = reqparse.RequestParser()
|
|
110
|
+
parser.add_argument(
|
|
111
|
+
"message", type=str, required=True, location=location
|
|
112
|
+
)
|
|
113
|
+
args = parser.parse_args()
|
|
114
|
+
message = args["message"]
|
|
115
|
+
files = request.files
|
|
116
|
+
|
|
117
|
+
chat = chats_service.get_chat(entity_id)
|
|
118
|
+
if person["id"] not in chat["participants"]:
|
|
119
|
+
raise WrongParameterException(
|
|
120
|
+
"You are not a participant of this chat"
|
|
121
|
+
)
|
|
122
|
+
|
|
123
|
+
return (
|
|
124
|
+
chats_service.create_chat_message(
|
|
125
|
+
chat["id"], person["id"], message, files=files
|
|
126
|
+
),
|
|
127
|
+
201,
|
|
128
|
+
)
|
|
129
|
+
|
|
130
|
+
|
|
131
|
+
class ChatMessageResource(Resource):
|
|
132
|
+
|
|
133
|
+
@jwt_required()
|
|
134
|
+
def get(self, entity_id, chat_message_id):
|
|
135
|
+
"""
|
|
136
|
+
Get chat message.
|
|
137
|
+
---
|
|
138
|
+
tags:
|
|
139
|
+
- Chat
|
|
140
|
+
parameters:
|
|
141
|
+
- in: path
|
|
142
|
+
name: entity_id
|
|
143
|
+
description: ID of the entity related to the chat
|
|
144
|
+
type: integer
|
|
145
|
+
required: true
|
|
146
|
+
x-example: a24a6ea4-ce75-4665-a070-57453082c25
|
|
147
|
+
- in: path
|
|
148
|
+
name: chat_message_id
|
|
149
|
+
description: ID of the chat message
|
|
150
|
+
type: integer
|
|
151
|
+
required: true
|
|
152
|
+
x-example: 1
|
|
153
|
+
responses:
|
|
154
|
+
200:
|
|
155
|
+
description: Chat message
|
|
156
|
+
"""
|
|
157
|
+
entity = entities_service.get_entity(entity_id)
|
|
158
|
+
user_service.check_project_access(entity["project_id"])
|
|
159
|
+
user_service.check_entity_access(entity["id"])
|
|
160
|
+
return chats_service.get_chat_message(chat_message_id)
|
|
161
|
+
|
|
162
|
+
@jwt_required()
|
|
163
|
+
def delete(self, entity_id, chat_message_id):
|
|
164
|
+
"""
|
|
165
|
+
Delete chat message.
|
|
166
|
+
---
|
|
167
|
+
tags:
|
|
168
|
+
- Chat
|
|
169
|
+
parameters:
|
|
170
|
+
- in: path
|
|
171
|
+
name: entity_id
|
|
172
|
+
description: ID of the entity related to the chat
|
|
173
|
+
type: integer
|
|
174
|
+
required: true
|
|
175
|
+
x-example: a24a6ea4-ce75-4665-a070-57453082c25
|
|
176
|
+
- in: path
|
|
177
|
+
name: chat_message_id
|
|
178
|
+
description: ID of the chat message
|
|
179
|
+
type: integer
|
|
180
|
+
required: true
|
|
181
|
+
x-example: 1
|
|
182
|
+
responses:
|
|
183
|
+
204:
|
|
184
|
+
description: Empty response
|
|
185
|
+
"""
|
|
186
|
+
entity = entities_service.get_entity(entity_id)
|
|
187
|
+
user_service.check_project_access(entity["project_id"])
|
|
188
|
+
user_service.check_entity_access(entity["id"])
|
|
189
|
+
|
|
190
|
+
chat_message = chats_service.get_chat_message(chat_message_id)
|
|
191
|
+
current_user = persons_service.get_current_user()
|
|
192
|
+
if (
|
|
193
|
+
chat_message["person_id"] != current_user["id"]
|
|
194
|
+
or not permissions.has_admin_permissions
|
|
195
|
+
):
|
|
196
|
+
raise permissions.PermissionDenied
|
|
197
|
+
chats_service.delete_chat_message(chat_message_id)
|
|
198
|
+
|
|
199
|
+
return "", 204
|
|
@@ -5,11 +5,13 @@ from flask_restful import Resource, reqparse
|
|
|
5
5
|
from flask_jwt_extended import jwt_required
|
|
6
6
|
|
|
7
7
|
from zou.app.mixin import ArgsMixin
|
|
8
|
-
from zou.app.utils import permissions
|
|
8
|
+
from zou.app.utils import permissions, date_helpers
|
|
9
9
|
|
|
10
10
|
from zou.app.services import (
|
|
11
|
+
chats_service,
|
|
11
12
|
comments_service,
|
|
12
13
|
deletion_service,
|
|
14
|
+
entities_service,
|
|
13
15
|
persons_service,
|
|
14
16
|
tasks_service,
|
|
15
17
|
user_service,
|
|
@@ -53,10 +55,18 @@ class DownloadAttachmentResource(Resource):
|
|
|
53
55
|
attachment_file = comments_service.get_attachment_file(
|
|
54
56
|
attachment_file_id
|
|
55
57
|
)
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
58
|
+
if attachment_file["comment_id"] is not None:
|
|
59
|
+
comment = tasks_service.get_comment(attachment_file["comment_id"])
|
|
60
|
+
user_service.check_task_access(comment["object_id"])
|
|
61
|
+
elif attachment_file["chat_message_id"] is not None:
|
|
62
|
+
message = chats_service.get_chat_message(
|
|
63
|
+
attachment_file["chat_message_id"]
|
|
64
|
+
)
|
|
65
|
+
chat = chats_service.get_chat_by_id(message["chat_id"])
|
|
66
|
+
entity = entities_service.get_entity(chat["object_id"])
|
|
67
|
+
user_service.check_project_access(entity["project_id"])
|
|
68
|
+
else:
|
|
69
|
+
raise permissions.PermissionDenied()
|
|
60
70
|
try:
|
|
61
71
|
file_path = comments_service.get_attachment_file_path(
|
|
62
72
|
attachment_file
|
|
@@ -68,6 +78,9 @@ class DownloadAttachmentResource(Resource):
|
|
|
68
78
|
as_attachment=False,
|
|
69
79
|
download_name=attachment_file["name"],
|
|
70
80
|
max_age=config.CLIENT_CACHE_MAX_AGE,
|
|
81
|
+
last_modified=date_helpers.get_datetime_from_string(
|
|
82
|
+
attachment_file["updated_at"]
|
|
83
|
+
),
|
|
71
84
|
)
|
|
72
85
|
except Exception:
|
|
73
86
|
current_app.logger.error(
|
|
@@ -107,9 +120,7 @@ class AckCommentResource(Resource):
|
|
|
107
120
|
200:
|
|
108
121
|
description: Comment acknowledged
|
|
109
122
|
"""
|
|
110
|
-
|
|
111
|
-
user_service.check_project_access(task["project_id"])
|
|
112
|
-
user_service.check_entity_access(task["entity_id"])
|
|
123
|
+
user_service.check_task_access(task_id)
|
|
113
124
|
return comments_service.acknowledge_comment(comment_id)
|
|
114
125
|
|
|
115
126
|
|
|
@@ -175,11 +186,10 @@ class CommentTaskResource(Resource):
|
|
|
175
186
|
person_id,
|
|
176
187
|
created_at,
|
|
177
188
|
checklist,
|
|
189
|
+
links,
|
|
178
190
|
) = self.get_arguments()
|
|
179
191
|
|
|
180
|
-
|
|
181
|
-
user_service.check_project_access(task["project_id"])
|
|
182
|
-
user_service.check_entity_access(task["entity_id"])
|
|
192
|
+
user_service.check_task_access(task_id)
|
|
183
193
|
user_service.check_task_status_access(task_status_id)
|
|
184
194
|
files = request.files
|
|
185
195
|
|
|
@@ -194,6 +204,7 @@ class CommentTaskResource(Resource):
|
|
|
194
204
|
checklist,
|
|
195
205
|
files,
|
|
196
206
|
created_at,
|
|
207
|
+
links,
|
|
197
208
|
)
|
|
198
209
|
return comment, 201
|
|
199
210
|
|
|
@@ -208,9 +219,17 @@ class CommentTaskResource(Resource):
|
|
|
208
219
|
default=[],
|
|
209
220
|
location=location,
|
|
210
221
|
)
|
|
222
|
+
parser.add_argument(
|
|
223
|
+
"links",
|
|
224
|
+
type=str,
|
|
225
|
+
action="append",
|
|
226
|
+
default=[],
|
|
227
|
+
location=location,
|
|
228
|
+
)
|
|
211
229
|
else:
|
|
212
230
|
location = "values"
|
|
213
231
|
parser.add_argument("checklist", default="[]", location=location)
|
|
232
|
+
parser.add_argument("links", default="[]", location=location)
|
|
214
233
|
parser.add_argument(
|
|
215
234
|
"task_status_id",
|
|
216
235
|
required=True,
|
|
@@ -231,6 +250,7 @@ class CommentTaskResource(Resource):
|
|
|
231
250
|
if request.is_json
|
|
232
251
|
else json.loads(args["checklist"])
|
|
233
252
|
),
|
|
253
|
+
(args["links"] if request.is_json else json.loads(args["links"])),
|
|
234
254
|
)
|
|
235
255
|
|
|
236
256
|
|
|
@@ -398,6 +418,7 @@ class CommentManyTasksResource(Resource):
|
|
|
398
418
|
[],
|
|
399
419
|
{},
|
|
400
420
|
None,
|
|
421
|
+
comment.get("links", []),
|
|
401
422
|
)
|
|
402
423
|
result.append(comment)
|
|
403
424
|
except KeyError:
|
|
@@ -408,8 +429,8 @@ class CommentManyTasksResource(Resource):
|
|
|
408
429
|
allowed_comments = []
|
|
409
430
|
for comment in comments:
|
|
410
431
|
try:
|
|
411
|
-
task = tasks_service.
|
|
412
|
-
comment["object_id"],
|
|
432
|
+
task = tasks_service.get_task(
|
|
433
|
+
comment["object_id"], relations=True
|
|
413
434
|
)
|
|
414
435
|
if (
|
|
415
436
|
person["role"] == "supervisor"
|
|
@@ -469,9 +490,7 @@ class ReplyCommentResource(Resource, ArgsMixin):
|
|
|
469
490
|
]
|
|
470
491
|
)
|
|
471
492
|
|
|
472
|
-
|
|
473
|
-
user_service.check_project_access(task["project_id"])
|
|
474
|
-
user_service.check_entity_access(task["entity_id"])
|
|
493
|
+
user_service.check_task_access(task_id)
|
|
475
494
|
return comments_service.reply_comment(comment_id, args["text"])
|
|
476
495
|
|
|
477
496
|
|
|
@@ -510,9 +529,7 @@ class DeleteReplyCommentResource(Resource):
|
|
|
510
529
|
200:
|
|
511
530
|
description: Given comment reply deleted
|
|
512
531
|
"""
|
|
513
|
-
|
|
514
|
-
user_service.check_project_access(task["project_id"])
|
|
515
|
-
user_service.check_entity_access(task["entity_id"])
|
|
532
|
+
user_service.check_task_access(task_id)
|
|
516
533
|
reply = comments_service.get_reply(comment_id, reply_id)
|
|
517
534
|
current_user = persons_service.get_current_user()
|
|
518
535
|
if reply["person_id"] != current_user["id"]:
|
|
@@ -123,6 +123,12 @@ from zou.app.blueprints.crud.preview_background_file import (
|
|
|
123
123
|
PreviewBackgroundFileResource,
|
|
124
124
|
PreviewBackgroundFilesResource,
|
|
125
125
|
)
|
|
126
|
+
from zou.app.blueprints.crud.chat import ChatResource, ChatsResource
|
|
127
|
+
from zou.app.blueprints.crud.chat_message import (
|
|
128
|
+
ChatMessageResource,
|
|
129
|
+
ChatMessagesResource,
|
|
130
|
+
)
|
|
131
|
+
from zou.app.blueprints.crud.studio import StudioResource, StudiosResource
|
|
126
132
|
|
|
127
133
|
routes = [
|
|
128
134
|
("/data/persons", PersonsResource),
|
|
@@ -195,11 +201,17 @@ routes = [
|
|
|
195
201
|
("/data/subscriptions/<instance_id>", SubscriptionResource),
|
|
196
202
|
("/data/entity-links/", EntityLinksResource),
|
|
197
203
|
("/data/entity-links/<instance_id>", EntityLinkResource),
|
|
204
|
+
("/data/chats/", ChatsResource),
|
|
205
|
+
("/data/chats/<instance_id>", ChatResource),
|
|
206
|
+
("/data/chat-messages/", ChatMessagesResource),
|
|
207
|
+
("/data/chat-messages/<instance_id>", ChatMessageResource),
|
|
198
208
|
("/data/preview-background-files", PreviewBackgroundFilesResource),
|
|
199
209
|
(
|
|
200
210
|
"/data/preview-background-files/<instance_id>",
|
|
201
211
|
PreviewBackgroundFileResource,
|
|
202
212
|
),
|
|
213
|
+
("/data/studios", StudiosResource),
|
|
214
|
+
("/data/studios/<instance_id>", StudioResource),
|
|
203
215
|
]
|
|
204
216
|
|
|
205
217
|
blueprint = Blueprint("/data", "data")
|
|
@@ -2,7 +2,9 @@ from zou.app.models.attachment_file import AttachmentFile
|
|
|
2
2
|
|
|
3
3
|
from zou.app.blueprints.crud.base import BaseModelResource, BaseModelsResource
|
|
4
4
|
|
|
5
|
-
from zou.app.services import tasks_service, user_service
|
|
5
|
+
from zou.app.services import chats_service, tasks_service, user_service
|
|
6
|
+
|
|
7
|
+
from zou.app.utils.permissions import PermissionDenied
|
|
6
8
|
|
|
7
9
|
|
|
8
10
|
class AttachmentFilesResource(BaseModelsResource):
|
|
@@ -16,8 +18,15 @@ class AttachmentFileResource(BaseModelResource):
|
|
|
16
18
|
|
|
17
19
|
def check_read_permissions(self, instance):
|
|
18
20
|
attachment_file = instance
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
21
|
+
if attachment_file["comment_id"] is not None:
|
|
22
|
+
comment = tasks_service.get_comment(attachment_file["comment_id"])
|
|
23
|
+
user_service.check_task_access(comment["object_id"])
|
|
24
|
+
elif attachment_file["chat_message_id"] is not None:
|
|
25
|
+
message = chats_service.get_chat_message(
|
|
26
|
+
attachment_file["chat_message_id"]
|
|
27
|
+
)
|
|
28
|
+
chat = chats_service.get_chat(message["chat_id"])
|
|
29
|
+
user_service.check_entity_access(chat["object_id"])
|
|
30
|
+
else:
|
|
31
|
+
raise PermissionDenied()
|
|
23
32
|
return True
|
zou/app/blueprints/crud/base.py
CHANGED
|
@@ -12,7 +12,6 @@ from sqlalchemy.inspection import inspect
|
|
|
12
12
|
from zou.app.mixin import ArgsMixin
|
|
13
13
|
from zou.app.utils import events, fields, permissions, query
|
|
14
14
|
from zou.app.services.exception import (
|
|
15
|
-
ArgumentsException,
|
|
16
15
|
WrongParameterException,
|
|
17
16
|
)
|
|
18
17
|
|
|
@@ -27,7 +26,10 @@ class BaseModelsResource(Resource, ArgsMixin):
|
|
|
27
26
|
if query is None:
|
|
28
27
|
query = self.model.query
|
|
29
28
|
|
|
30
|
-
return self.
|
|
29
|
+
return self.serialize_list(query.all(), relations=relations)
|
|
30
|
+
|
|
31
|
+
def serialize_list(self, entries, relations=False):
|
|
32
|
+
return self.model.serialize_list(entries, relations=relations)
|
|
31
33
|
|
|
32
34
|
def paginated_entries(self, query, page, limit=None, relations=False):
|
|
33
35
|
total = query.count()
|
|
@@ -35,7 +37,11 @@ class BaseModelsResource(Resource, ArgsMixin):
|
|
|
35
37
|
offset = (page - 1) * limit
|
|
36
38
|
|
|
37
39
|
nb_pages = int(math.ceil(total / float(limit)))
|
|
38
|
-
query = query.order_by(
|
|
40
|
+
query = query.order_by(
|
|
41
|
+
self.model.updated_at.desc(),
|
|
42
|
+
self.model.created_at.desc(),
|
|
43
|
+
self.model.id,
|
|
44
|
+
)
|
|
39
45
|
query = query.limit(limit)
|
|
40
46
|
query = query.offset(offset)
|
|
41
47
|
|
|
@@ -75,7 +81,11 @@ class BaseModelsResource(Resource, ArgsMixin):
|
|
|
75
81
|
) and isinstance(
|
|
76
82
|
field_key.property, orm.properties.RelationshipProperty
|
|
77
83
|
)
|
|
78
|
-
value_is_list =
|
|
84
|
+
value_is_list = (
|
|
85
|
+
hasattr(value, "__len__")
|
|
86
|
+
and len(value) > 0
|
|
87
|
+
and value[0] == "["
|
|
88
|
+
)
|
|
79
89
|
|
|
80
90
|
if key == "name" and field_key is not None:
|
|
81
91
|
name_filter.append(value)
|
|
@@ -119,7 +129,7 @@ class BaseModelsResource(Resource, ArgsMixin):
|
|
|
119
129
|
|
|
120
130
|
return query
|
|
121
131
|
|
|
122
|
-
def check_read_permissions(self):
|
|
132
|
+
def check_read_permissions(self, options=None):
|
|
123
133
|
return permissions.check_admin_permissions()
|
|
124
134
|
|
|
125
135
|
def add_project_permission_filter(self, query):
|
|
@@ -157,13 +167,14 @@ class BaseModelsResource(Resource, ArgsMixin):
|
|
|
157
167
|
description: Permission denied
|
|
158
168
|
"""
|
|
159
169
|
try:
|
|
160
|
-
self.check_read_permissions()
|
|
161
170
|
query = self.model.query
|
|
162
171
|
if not request.args:
|
|
172
|
+
self.check_read_permissions()
|
|
163
173
|
query = self.add_project_permission_filter(query)
|
|
164
174
|
return self.all_entries(query)
|
|
165
175
|
else:
|
|
166
176
|
options = request.args
|
|
177
|
+
self.check_read_permissions(options)
|
|
167
178
|
query = self.apply_filters(query, options)
|
|
168
179
|
query = self.add_project_permission_filter(query)
|
|
169
180
|
page = int(options.get("page", "-1"))
|
|
@@ -229,7 +240,7 @@ class BaseModelsResource(Resource, ArgsMixin):
|
|
|
229
240
|
try:
|
|
230
241
|
data = request.json
|
|
231
242
|
if data is None:
|
|
232
|
-
raise
|
|
243
|
+
raise WrongParameterException(
|
|
233
244
|
"Data are empty. Please verify that you sent JSON data and"
|
|
234
245
|
" that you set the right headers."
|
|
235
246
|
)
|
|
@@ -245,18 +256,11 @@ class BaseModelsResource(Resource, ArgsMixin):
|
|
|
245
256
|
TypeError,
|
|
246
257
|
IntegrityError,
|
|
247
258
|
StatementError,
|
|
259
|
+
KeyError,
|
|
248
260
|
) as exception:
|
|
249
261
|
current_app.logger.error(str(exception), exc_info=1)
|
|
250
262
|
return {"message": str(exception)}, 400
|
|
251
263
|
|
|
252
|
-
except ArgumentsException as exception:
|
|
253
|
-
current_app.logger.error(str(exception), exc_info=1)
|
|
254
|
-
return (
|
|
255
|
-
exception.dict
|
|
256
|
-
if exception.dict is not None
|
|
257
|
-
else {"message": str(exception)}
|
|
258
|
-
), 400
|
|
259
|
-
|
|
260
264
|
def emit_create_event(self, instance_dict):
|
|
261
265
|
return events.emit(
|
|
262
266
|
"%s:new" % self.model.__tablename__.replace("_", "-"),
|
|
@@ -280,10 +284,14 @@ class BaseModelResource(Resource, ArgsMixin):
|
|
|
280
284
|
abort(404)
|
|
281
285
|
return instance
|
|
282
286
|
|
|
283
|
-
def
|
|
287
|
+
def get_serialized_instance(self, instance_id, relations=True):
|
|
288
|
+
instance = self.get_model_or_404(instance_id)
|
|
289
|
+
return self.serialize_instance(instance, relations=relations)
|
|
290
|
+
|
|
291
|
+
def check_read_permissions(self, instance_dict):
|
|
284
292
|
return permissions.check_admin_permissions()
|
|
285
293
|
|
|
286
|
-
def check_update_permissions(self,
|
|
294
|
+
def check_update_permissions(self, instance_dict, data):
|
|
287
295
|
return permissions.check_admin_permissions()
|
|
288
296
|
|
|
289
297
|
def check_delete_permissions(self, instance_dict):
|
|
@@ -328,8 +336,9 @@ class BaseModelResource(Resource, ArgsMixin):
|
|
|
328
336
|
"""
|
|
329
337
|
relations = self.get_bool_parameter("relations", "true")
|
|
330
338
|
try:
|
|
331
|
-
|
|
332
|
-
|
|
339
|
+
result = self.get_serialized_instance(
|
|
340
|
+
instance_id, relations=relations
|
|
341
|
+
)
|
|
333
342
|
self.check_read_permissions(result)
|
|
334
343
|
result = self.clean_get_result(result)
|
|
335
344
|
|
|
@@ -397,7 +406,7 @@ class BaseModelResource(Resource, ArgsMixin):
|
|
|
397
406
|
try:
|
|
398
407
|
data = self.get_arguments()
|
|
399
408
|
if data is None:
|
|
400
|
-
raise
|
|
409
|
+
raise WrongParameterException(
|
|
401
410
|
"Data are empty. Please verify that you sent JSON data and"
|
|
402
411
|
" that you set the right headers."
|
|
403
412
|
)
|
|
@@ -420,14 +429,6 @@ class BaseModelResource(Resource, ArgsMixin):
|
|
|
420
429
|
current_app.logger.error(str(exception), exc_info=1)
|
|
421
430
|
return {"message": str(exception)}, 400
|
|
422
431
|
|
|
423
|
-
except ArgumentsException as exception:
|
|
424
|
-
current_app.logger.error(str(exception), exc_info=1)
|
|
425
|
-
return (
|
|
426
|
-
exception.dict
|
|
427
|
-
if exception.dict is not None
|
|
428
|
-
else {"message": str(exception)}
|
|
429
|
-
), 400
|
|
430
|
-
|
|
431
432
|
@jwt_required()
|
|
432
433
|
def delete(self, instance_id):
|
|
433
434
|
"""
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
from zou.app.models.chat import Chat
|
|
2
|
+
|
|
3
|
+
from zou.app.blueprints.crud.base import BaseModelResource, BaseModelsResource
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class ChatsResource(BaseModelsResource):
|
|
7
|
+
def __init__(self):
|
|
8
|
+
BaseModelsResource.__init__(self, Chat)
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class ChatResource(BaseModelResource):
|
|
12
|
+
def __init__(self):
|
|
13
|
+
BaseModelResource.__init__(self, Chat)
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
from zou.app.models.chat_message import ChatMessage
|
|
2
|
+
|
|
3
|
+
from zou.app.blueprints.crud.base import BaseModelResource, BaseModelsResource
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class ChatMessagesResource(BaseModelsResource):
|
|
7
|
+
def __init__(self):
|
|
8
|
+
BaseModelsResource.__init__(self, ChatMessage)
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class ChatMessageResource(BaseModelResource):
|
|
12
|
+
def __init__(self):
|
|
13
|
+
BaseModelResource.__init__(self, ChatMessage)
|