zou 0.19.52__py3-none-any.whl → 0.19.54__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 +1 -1
- zou/app/blueprints/assets/__init__.py +22 -0
- zou/app/blueprints/assets/resources.py +240 -3
- zou/app/blueprints/chats/resources.py +2 -2
- zou/app/blueprints/crud/base.py +10 -6
- zou/app/blueprints/crud/day_off.py +3 -3
- zou/app/blueprints/crud/entity.py +3 -3
- zou/app/blueprints/crud/entity_type.py +15 -0
- zou/app/blueprints/crud/metadata_descriptor.py +16 -3
- zou/app/blueprints/crud/person.py +13 -15
- zou/app/blueprints/crud/preview_background_file.py +3 -3
- zou/app/blueprints/crud/project.py +5 -5
- zou/app/blueprints/crud/schedule_item.py +2 -2
- zou/app/blueprints/crud/task.py +1 -0
- zou/app/blueprints/crud/task_type.py +3 -3
- zou/app/blueprints/persons/resources.py +2 -2
- zou/app/blueprints/previews/resources.py +29 -6
- zou/app/blueprints/projects/resources.py +1 -1
- zou/app/blueprints/shots/resources.py +2 -2
- zou/app/models/base.py +6 -1
- zou/app/models/entity.py +3 -5
- zou/app/services/assets_service.py +76 -1
- zou/app/services/breakdown_service.py +33 -3
- zou/app/services/deletion_service.py +5 -0
- zou/app/services/exception.py +1 -5
- zou/app/services/preview_files_service.py +2 -2
- zou/app/services/tasks_service.py +1 -1
- zou/app/services/time_spents_service.py +4 -4
- zou/app/swagger.py +15 -20
- zou/app/utils/query.py +53 -5
- zou/event_stream.py +1 -0
- zou/migrations/versions/59a7445a966c_add_entity_is_shared.py +41 -0
- {zou-0.19.52.dist-info → zou-0.19.54.dist-info}/METADATA +6 -6
- {zou-0.19.52.dist-info → zou-0.19.54.dist-info}/RECORD +39 -38
- {zou-0.19.52.dist-info → zou-0.19.54.dist-info}/WHEEL +1 -1
- {zou-0.19.52.dist-info → zou-0.19.54.dist-info}/LICENSE +0 -0
- {zou-0.19.52.dist-info → zou-0.19.54.dist-info}/entry_points.txt +0 -0
- {zou-0.19.52.dist-info → zou-0.19.54.dist-info}/top_level.txt +0 -0
|
@@ -18,7 +18,7 @@ from zou.app.utils import events, permissions, fields
|
|
|
18
18
|
|
|
19
19
|
from zou.app.blueprints.crud.base import BaseModelResource, BaseModelsResource
|
|
20
20
|
|
|
21
|
-
from zou.app.services.exception import
|
|
21
|
+
from zou.app.services.exception import WrongParameterException
|
|
22
22
|
|
|
23
23
|
|
|
24
24
|
class ProjectsResource(BaseModelsResource):
|
|
@@ -44,7 +44,7 @@ class ProjectsResource(BaseModelsResource):
|
|
|
44
44
|
if data["production_style"] not in [
|
|
45
45
|
type_name for type_name, _ in PROJECT_STYLES
|
|
46
46
|
]:
|
|
47
|
-
raise
|
|
47
|
+
raise WrongParameterException("Invalid production_style")
|
|
48
48
|
return True
|
|
49
49
|
|
|
50
50
|
def update_data(self, data):
|
|
@@ -71,7 +71,7 @@ class ProjectsResource(BaseModelsResource):
|
|
|
71
71
|
or data["preview_background_file_id"]
|
|
72
72
|
not in data["preview_background_files_ids"]
|
|
73
73
|
):
|
|
74
|
-
raise
|
|
74
|
+
raise WrongParameterException("Invalid preview_background_file_id")
|
|
75
75
|
return data
|
|
76
76
|
|
|
77
77
|
def post_creation(self, project):
|
|
@@ -131,7 +131,7 @@ class ProjectResource(BaseModelResource, ArgsMixin):
|
|
|
131
131
|
data["preview_background_file_id"]
|
|
132
132
|
not in preview_background_files_ids
|
|
133
133
|
):
|
|
134
|
-
raise
|
|
134
|
+
raise WrongParameterException("Invalid preview_background_file_id")
|
|
135
135
|
|
|
136
136
|
return data
|
|
137
137
|
|
|
@@ -167,7 +167,7 @@ class ProjectResource(BaseModelResource, ArgsMixin):
|
|
|
167
167
|
if data["production_style"] not in [
|
|
168
168
|
type_name for type_name, _ in PROJECT_STYLES
|
|
169
169
|
]:
|
|
170
|
-
raise
|
|
170
|
+
raise WrongParameterException("Invalid production_style")
|
|
171
171
|
return data
|
|
172
172
|
|
|
173
173
|
@jwt_required()
|
|
@@ -3,7 +3,7 @@ from zou.app.models.schedule_item import ScheduleItem
|
|
|
3
3
|
from zou.app.blueprints.crud.base import BaseModelResource, BaseModelsResource
|
|
4
4
|
|
|
5
5
|
from zou.app.services import user_service
|
|
6
|
-
from zou.app.services.exception import
|
|
6
|
+
from zou.app.services.exception import WrongParameterException
|
|
7
7
|
|
|
8
8
|
|
|
9
9
|
class ScheduleItemsResource(BaseModelsResource):
|
|
@@ -17,7 +17,7 @@ class ScheduleItemsResource(BaseModelsResource):
|
|
|
17
17
|
object_id=data.get("object_id", None),
|
|
18
18
|
)
|
|
19
19
|
if schedule_item is not None:
|
|
20
|
-
raise
|
|
20
|
+
raise WrongParameterException("A similar schedule item already exists")
|
|
21
21
|
return schedule_item
|
|
22
22
|
|
|
23
23
|
|
zou/app/blueprints/crud/task.py
CHANGED
|
@@ -114,6 +114,7 @@ class TasksResource(BaseModelsResource, ArgsMixin):
|
|
|
114
114
|
if assignees is not None:
|
|
115
115
|
instance.assignees = persons
|
|
116
116
|
instance.save()
|
|
117
|
+
self.emit_create_event(instance.serialize())
|
|
117
118
|
|
|
118
119
|
return tasks_service.get_task_with_relations(str(instance.id)), 201
|
|
119
120
|
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
from zou.app.models.task_type import TaskType
|
|
2
|
-
from zou.app.services.exception import
|
|
2
|
+
from zou.app.services.exception import WrongParameterException
|
|
3
3
|
from zou.app.services import tasks_service
|
|
4
4
|
|
|
5
5
|
from zou.app.blueprints.crud.base import BaseModelResource, BaseModelsResource
|
|
@@ -17,7 +17,7 @@ class TaskTypesResource(BaseModelsResource):
|
|
|
17
17
|
name = data.get("name", None)
|
|
18
18
|
task_type = TaskType.get_by(name=name)
|
|
19
19
|
if task_type is not None:
|
|
20
|
-
raise
|
|
20
|
+
raise WrongParameterException(
|
|
21
21
|
"A task type with similar name already exists"
|
|
22
22
|
)
|
|
23
23
|
return data
|
|
@@ -40,7 +40,7 @@ class TaskTypeResource(BaseModelResource):
|
|
|
40
40
|
if name is not None:
|
|
41
41
|
task_type = TaskType.get_by(name=name)
|
|
42
42
|
if task_type is not None and instance_id != str(task_type.id):
|
|
43
|
-
raise
|
|
43
|
+
raise WrongParameterException(
|
|
44
44
|
"A task type with similar name already exists"
|
|
45
45
|
)
|
|
46
46
|
return data
|
|
@@ -30,7 +30,7 @@ from zou.app.services.exception import (
|
|
|
30
30
|
UnactiveUserException,
|
|
31
31
|
TwoFactorAuthenticationNotEnabledException,
|
|
32
32
|
PersonInProtectedAccounts,
|
|
33
|
-
|
|
33
|
+
WrongParameterException,
|
|
34
34
|
)
|
|
35
35
|
from zou.app.services.auth_service import (
|
|
36
36
|
disable_two_factor_authentication_for_person,
|
|
@@ -779,7 +779,7 @@ class TimeSpentDurationResource(Resource, ArgsMixin):
|
|
|
779
779
|
)["departments"]
|
|
780
780
|
if department_id is not None:
|
|
781
781
|
if department_id not in persons_departments:
|
|
782
|
-
raise
|
|
782
|
+
raise WrongParameterException(
|
|
783
783
|
"Supervisor not allowed to access this department"
|
|
784
784
|
)
|
|
785
785
|
else:
|
|
@@ -35,7 +35,7 @@ from zou.app.utils import (
|
|
|
35
35
|
date_helpers,
|
|
36
36
|
)
|
|
37
37
|
from zou.app.services.exception import (
|
|
38
|
-
|
|
38
|
+
WrongParameterException,
|
|
39
39
|
PreviewBackgroundFileNotFoundException,
|
|
40
40
|
PreviewFileReuploadNotAllowedException,
|
|
41
41
|
)
|
|
@@ -813,7 +813,29 @@ class BasePreviewPictureResource(BasePreviewFileResource):
|
|
|
813
813
|
abort(404)
|
|
814
814
|
|
|
815
815
|
|
|
816
|
-
class
|
|
816
|
+
class BasePreviewFileThumbnailResource(BasePreviewPictureResource):
|
|
817
|
+
"""
|
|
818
|
+
Base class to download a thumbnail for a preview file.
|
|
819
|
+
"""
|
|
820
|
+
|
|
821
|
+
def is_allowed(self, preview_file_id):
|
|
822
|
+
self.preview_file = files_service.get_preview_file(preview_file_id)
|
|
823
|
+
task = tasks_service.get_task(self.preview_file["task_id"])
|
|
824
|
+
entity = entities_service.get_entity(task["entity_id"])
|
|
825
|
+
if (
|
|
826
|
+
entity["preview_file_id"] != preview_file_id
|
|
827
|
+
or not entity["is_shared"]
|
|
828
|
+
or permissions.has_vendor_permissions()
|
|
829
|
+
):
|
|
830
|
+
user_service.check_project_access(task["project_id"])
|
|
831
|
+
user_service.check_entity_access(task["entity_id"])
|
|
832
|
+
self.last_modified = date_helpers.get_datetime_from_string(
|
|
833
|
+
self.preview_file["updated_at"]
|
|
834
|
+
)
|
|
835
|
+
|
|
836
|
+
|
|
837
|
+
class PreviewFileThumbnailResource(BasePreviewFileThumbnailResource):
|
|
838
|
+
|
|
817
839
|
def __init__(self):
|
|
818
840
|
BasePreviewPictureResource.__init__(self, "thumbnails")
|
|
819
841
|
|
|
@@ -832,12 +854,12 @@ class PreviewFilePreviewResource(BasePreviewPictureResource):
|
|
|
832
854
|
BasePreviewPictureResource.__init__(self, "previews")
|
|
833
855
|
|
|
834
856
|
|
|
835
|
-
class PreviewFileThumbnailSquareResource(
|
|
857
|
+
class PreviewFileThumbnailSquareResource(BasePreviewFileThumbnailResource):
|
|
836
858
|
def __init__(self):
|
|
837
859
|
BasePreviewPictureResource.__init__(self, "thumbnails-square")
|
|
838
860
|
|
|
839
861
|
|
|
840
|
-
class PreviewFileOriginalResource(
|
|
862
|
+
class PreviewFileOriginalResource(BasePreviewFileThumbnailResource):
|
|
841
863
|
def __init__(self):
|
|
842
864
|
BasePreviewPictureResource.__init__(self, "original")
|
|
843
865
|
|
|
@@ -1034,7 +1056,8 @@ class ProjectThumbnailResource(BaseThumbnailResource):
|
|
|
1034
1056
|
|
|
1035
1057
|
def check_allowed_to_get(self, instance_id):
|
|
1036
1058
|
super().check_allowed_to_get(instance_id)
|
|
1037
|
-
|
|
1059
|
+
if not permissions.has_manager_permissions():
|
|
1060
|
+
user_service.check_project_access(instance_id)
|
|
1038
1061
|
|
|
1039
1062
|
|
|
1040
1063
|
class CreateProjectThumbnailResource(ProjectThumbnailResource):
|
|
@@ -1074,7 +1097,7 @@ class SetMainPreviewResource(Resource, ArgsMixin):
|
|
|
1074
1097
|
user_service.check_entity_access(task["entity_id"])
|
|
1075
1098
|
if frame_number is not None:
|
|
1076
1099
|
if preview_file["extension"] != "mp4":
|
|
1077
|
-
raise
|
|
1100
|
+
raise WrongParameterException(
|
|
1078
1101
|
"Can't use a given frame on non movie preview"
|
|
1079
1102
|
)
|
|
1080
1103
|
preview_files_service.replace_extracted_frame_for_preview_file(
|
|
@@ -659,7 +659,7 @@ class ProductionMetadataDescriptorsResource(Resource, ArgsMixin):
|
|
|
659
659
|
200:
|
|
660
660
|
description: All metadata descriptors
|
|
661
661
|
"""
|
|
662
|
-
user_service.
|
|
662
|
+
user_service.check_project_access(project_id)
|
|
663
663
|
for_client = permissions.has_client_permissions()
|
|
664
664
|
return projects_service.get_metadata_descriptors(
|
|
665
665
|
project_id, for_client
|
|
@@ -20,7 +20,7 @@ from zou.app.mixin import ArgsMixin
|
|
|
20
20
|
from zou.app.utils import fields, query, permissions
|
|
21
21
|
from zou.app.services.exception import (
|
|
22
22
|
WrongParameterException,
|
|
23
|
-
|
|
23
|
+
WrongParameterException,
|
|
24
24
|
)
|
|
25
25
|
|
|
26
26
|
|
|
@@ -77,7 +77,7 @@ class ShotResource(Resource, ArgsMixin):
|
|
|
77
77
|
user_service.check_manager_project_access(shot["project_id"])
|
|
78
78
|
data = request.json
|
|
79
79
|
if data is None:
|
|
80
|
-
raise
|
|
80
|
+
raise WrongParameterException(
|
|
81
81
|
"Data are empty. Please verify that you sent JSON data and"
|
|
82
82
|
" that you set the right headers."
|
|
83
83
|
)
|
zou/app/models/base.py
CHANGED
|
@@ -184,7 +184,12 @@ class BaseMixin(object):
|
|
|
184
184
|
|
|
185
185
|
@classmethod
|
|
186
186
|
def commit(cls):
|
|
187
|
-
|
|
187
|
+
try:
|
|
188
|
+
db.session.commit()
|
|
189
|
+
except BaseException:
|
|
190
|
+
db.session.rollback()
|
|
191
|
+
db.session.remove()
|
|
192
|
+
raise
|
|
188
193
|
|
|
189
194
|
def save(self):
|
|
190
195
|
"""
|
zou/app/models/entity.py
CHANGED
|
@@ -3,7 +3,6 @@ from sqlalchemy_utils import UUIDType, ChoiceType
|
|
|
3
3
|
from zou.app import db
|
|
4
4
|
from zou.app.models.serializer import SerializerMixin
|
|
5
5
|
from zou.app.models.base import BaseMixin
|
|
6
|
-
from zou.app.utils import fields
|
|
7
6
|
|
|
8
7
|
from sqlalchemy.dialects.postgresql import JSONB
|
|
9
8
|
|
|
@@ -90,10 +89,7 @@ class Entity(db.Model, BaseMixin, SerializerMixin):
|
|
|
90
89
|
tasks and files.
|
|
91
90
|
"""
|
|
92
91
|
|
|
93
|
-
id =
|
|
94
|
-
UUIDType(binary=False), primary_key=True, default=fields.gen_uuid
|
|
95
|
-
)
|
|
96
|
-
|
|
92
|
+
id = BaseMixin.id
|
|
97
93
|
name = db.Column(db.String(160), nullable=False)
|
|
98
94
|
code = db.Column(db.String(160)) # To store sanitized version of name
|
|
99
95
|
description = db.Column(db.Text())
|
|
@@ -104,6 +100,8 @@ class Entity(db.Model, BaseMixin, SerializerMixin):
|
|
|
104
100
|
nb_entities_out = db.Column(db.Integer, default=0)
|
|
105
101
|
is_casting_standby = db.Column(db.Boolean, default=False)
|
|
106
102
|
|
|
103
|
+
is_shared = db.Column(db.Boolean, default=False, nullable=False)
|
|
104
|
+
|
|
107
105
|
status = db.Column(
|
|
108
106
|
ChoiceType(ENTITY_STATUSES), default="running", nullable=False
|
|
109
107
|
)
|
|
@@ -84,7 +84,7 @@ def build_entity_type_asset_type_filter():
|
|
|
84
84
|
return ~EntityType.id.in_(ids_to_exclude)
|
|
85
85
|
|
|
86
86
|
|
|
87
|
-
def get_assets(criterions={}):
|
|
87
|
+
def get_assets(criterions={}, is_admin=False):
|
|
88
88
|
"""
|
|
89
89
|
Get all assets for given criterions.
|
|
90
90
|
"""
|
|
@@ -102,6 +102,12 @@ def get_assets(criterions={}):
|
|
|
102
102
|
query = query.outerjoin(Task)
|
|
103
103
|
query = query.filter(user_service.build_assignee_filter())
|
|
104
104
|
|
|
105
|
+
if "is_shared" in criterions:
|
|
106
|
+
if not is_admin:
|
|
107
|
+
query = (
|
|
108
|
+
query.join(Project).filter(user_service.build_team_filter())
|
|
109
|
+
)
|
|
110
|
+
|
|
105
111
|
if episode_id is not None:
|
|
106
112
|
# Filter based on main episode.
|
|
107
113
|
query = query.filter(Entity.source_id == episode_id)
|
|
@@ -306,6 +312,7 @@ def get_assets_and_tasks(criterions={}, page=1, with_episode_ids=False):
|
|
|
306
312
|
"episode_id": source_id,
|
|
307
313
|
"casting_episode_ids": cast_in_episode_ids.get(asset_id, []),
|
|
308
314
|
"is_casting_standby": asset.is_casting_standby,
|
|
315
|
+
"is_shared": asset.is_shared,
|
|
309
316
|
"data": data,
|
|
310
317
|
"tasks": [],
|
|
311
318
|
}
|
|
@@ -571,6 +578,7 @@ def create_asset(
|
|
|
571
578
|
name,
|
|
572
579
|
description,
|
|
573
580
|
data,
|
|
581
|
+
is_shared=False,
|
|
574
582
|
source_id=None,
|
|
575
583
|
created_by=None,
|
|
576
584
|
):
|
|
@@ -587,6 +595,7 @@ def create_asset(
|
|
|
587
595
|
name=name,
|
|
588
596
|
description=description,
|
|
589
597
|
data=data,
|
|
598
|
+
is_shared=is_shared,
|
|
590
599
|
source_id=source_id,
|
|
591
600
|
created_by=created_by,
|
|
592
601
|
)
|
|
@@ -706,3 +715,69 @@ def cancel_asset(asset_id, force=True):
|
|
|
706
715
|
project_id=str(asset.project_id),
|
|
707
716
|
)
|
|
708
717
|
return asset_dict
|
|
718
|
+
|
|
719
|
+
|
|
720
|
+
def set_shared_assets(
|
|
721
|
+
is_shared=True,
|
|
722
|
+
project_id=None,
|
|
723
|
+
asset_type_id=None,
|
|
724
|
+
asset_ids=None,
|
|
725
|
+
with_events=False
|
|
726
|
+
):
|
|
727
|
+
"""
|
|
728
|
+
Set all assets of a project to is_shared=True or False.
|
|
729
|
+
"""
|
|
730
|
+
|
|
731
|
+
query = Entity.query.filter(build_asset_type_filter()).filter()
|
|
732
|
+
|
|
733
|
+
if project_id is not None:
|
|
734
|
+
query = query.filter(Entity.project_id == project_id)
|
|
735
|
+
|
|
736
|
+
if asset_type_id is not None:
|
|
737
|
+
query = query.filter(Entity.entity_type_id == asset_type_id)
|
|
738
|
+
|
|
739
|
+
if asset_ids is not None:
|
|
740
|
+
query = query.filter(Entity.id.in_(asset_ids))
|
|
741
|
+
|
|
742
|
+
assets = query.all()
|
|
743
|
+
|
|
744
|
+
for asset in assets:
|
|
745
|
+
asset.update_no_commit({"is_shared": is_shared})
|
|
746
|
+
|
|
747
|
+
Entity.commit()
|
|
748
|
+
|
|
749
|
+
for asset in assets:
|
|
750
|
+
asset_id = str(asset.id)
|
|
751
|
+
clear_asset_cache(asset_id)
|
|
752
|
+
if with_events:
|
|
753
|
+
events.emit(
|
|
754
|
+
"asset:update",
|
|
755
|
+
{"asset_id": asset_id},
|
|
756
|
+
project_id=project_id,
|
|
757
|
+
)
|
|
758
|
+
|
|
759
|
+
return Entity.serialize_list(assets, obj_type="Asset")
|
|
760
|
+
|
|
761
|
+
|
|
762
|
+
def get_shared_assets_used_in_project(project_id, episode_id=None):
|
|
763
|
+
"""
|
|
764
|
+
Get all shared assets used in a project.
|
|
765
|
+
"""
|
|
766
|
+
Shot = aliased(Entity, name="shot")
|
|
767
|
+
Sequence = aliased(Entity, name="sequence")
|
|
768
|
+
|
|
769
|
+
assets = (
|
|
770
|
+
Entity.query.filter(build_asset_type_filter())
|
|
771
|
+
.filter(Entity.is_shared == True)
|
|
772
|
+
.join(EntityLink, EntityLink.entity_out_id == Entity.id)
|
|
773
|
+
.join(Shot, EntityLink.entity_in_id == Shot.id)
|
|
774
|
+
.join(Sequence, Shot.parent_id == Sequence.id)
|
|
775
|
+
.filter(Shot.project_id == project_id)
|
|
776
|
+
.filter(Entity.canceled != True)
|
|
777
|
+
.filter(Entity.project_id != project_id)
|
|
778
|
+
)
|
|
779
|
+
|
|
780
|
+
if episode_id is not None and episode_id not in ["main", "all"]:
|
|
781
|
+
assets = assets.filter(Sequence.parent_id == episode_id)
|
|
782
|
+
|
|
783
|
+
return Entity.serialize_list(assets.all(), obj_type="Asset")
|
|
@@ -50,6 +50,8 @@ def get_casting(shot_id):
|
|
|
50
50
|
Entity.preview_file_id,
|
|
51
51
|
Entity.source_id,
|
|
52
52
|
Entity.ready_for,
|
|
53
|
+
Entity.is_shared,
|
|
54
|
+
Entity.project_id,
|
|
53
55
|
)
|
|
54
56
|
.order_by(EntityType.name, Entity.name)
|
|
55
57
|
)
|
|
@@ -61,6 +63,8 @@ def get_casting(shot_id):
|
|
|
61
63
|
entity_preview_file_id,
|
|
62
64
|
episode_id,
|
|
63
65
|
entity_ready_for,
|
|
66
|
+
entity_is_shared,
|
|
67
|
+
entity_project_id,
|
|
64
68
|
) in links:
|
|
65
69
|
casting.append(
|
|
66
70
|
{
|
|
@@ -74,6 +78,8 @@ def get_casting(shot_id):
|
|
|
74
78
|
),
|
|
75
79
|
"nb_occurences": link.nb_occurences,
|
|
76
80
|
"label": link.label,
|
|
81
|
+
"is_shared": entity_is_shared,
|
|
82
|
+
"project_id": entity_project_id,
|
|
77
83
|
}
|
|
78
84
|
)
|
|
79
85
|
return casting
|
|
@@ -93,11 +99,27 @@ def get_production_episodes_casting(project_id):
|
|
|
93
99
|
.join(EntityType, Entity.entity_type_id == EntityType.id)
|
|
94
100
|
.filter(Episode.project_id == project_id)
|
|
95
101
|
.filter(Entity.canceled != True)
|
|
96
|
-
.add_columns(
|
|
97
|
-
|
|
102
|
+
.add_columns(
|
|
103
|
+
Entity.name,
|
|
104
|
+
EntityType.name,
|
|
105
|
+
Entity.preview_file_id,
|
|
106
|
+
Entity.is_shared,
|
|
107
|
+
Entity.project_id,
|
|
108
|
+
)
|
|
109
|
+
.order_by(
|
|
110
|
+
EntityType.name,
|
|
111
|
+
Entity.name,
|
|
112
|
+
)
|
|
98
113
|
)
|
|
99
114
|
|
|
100
|
-
for
|
|
115
|
+
for (
|
|
116
|
+
link,
|
|
117
|
+
entity_name,
|
|
118
|
+
entity_type_name,
|
|
119
|
+
entity_preview_file_id,
|
|
120
|
+
entity_is_shared,
|
|
121
|
+
entity_project_id,
|
|
122
|
+
) in links:
|
|
101
123
|
episode_id = str(link.entity_in_id)
|
|
102
124
|
if episode_id not in castings:
|
|
103
125
|
castings[episode_id] = []
|
|
@@ -112,6 +134,8 @@ def get_production_episodes_casting(project_id):
|
|
|
112
134
|
),
|
|
113
135
|
"nb_occurences": link.nb_occurences,
|
|
114
136
|
"label": link.label,
|
|
137
|
+
"is_shared": entity_is_shared,
|
|
138
|
+
"project_id": entity_project_id,
|
|
115
139
|
}
|
|
116
140
|
)
|
|
117
141
|
return castings
|
|
@@ -139,6 +163,8 @@ def get_sequence_casting(sequence_id, project_id=None, episode_id=None):
|
|
|
139
163
|
Entity.name,
|
|
140
164
|
EntityType.name,
|
|
141
165
|
Entity.preview_file_id,
|
|
166
|
+
Entity.is_shared,
|
|
167
|
+
Entity.project_id,
|
|
142
168
|
Sequence.name,
|
|
143
169
|
)
|
|
144
170
|
)
|
|
@@ -163,6 +189,8 @@ def get_sequence_casting(sequence_id, project_id=None, episode_id=None):
|
|
|
163
189
|
entity_name,
|
|
164
190
|
entity_type_name,
|
|
165
191
|
entity_preview_file_id,
|
|
192
|
+
entity_is_shared,
|
|
193
|
+
entity_project_id,
|
|
166
194
|
sequence_name,
|
|
167
195
|
) in links:
|
|
168
196
|
shot_id = str(link.entity_in_id)
|
|
@@ -180,6 +208,8 @@ def get_sequence_casting(sequence_id, project_id=None, episode_id=None):
|
|
|
180
208
|
),
|
|
181
209
|
"nb_occurences": link.nb_occurences,
|
|
182
210
|
"label": link.label,
|
|
211
|
+
"is_shared": entity_is_shared,
|
|
212
|
+
"project_id": entity_project_id,
|
|
183
213
|
}
|
|
184
214
|
)
|
|
185
215
|
return castings
|
|
@@ -362,6 +362,11 @@ def remove_project(project_id):
|
|
|
362
362
|
|
|
363
363
|
ApiEvent.delete_all_by(project_id=project_id)
|
|
364
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()
|
|
365
370
|
MetadataDescriptor.delete_all_by(project_id=project_id)
|
|
366
371
|
Milestone.delete_all_by(project_id=project_id)
|
|
367
372
|
ScheduleItem.delete_all_by(project_id=project_id)
|
zou/app/services/exception.py
CHANGED
|
@@ -251,7 +251,7 @@ class EntryAlreadyExistsException(Exception):
|
|
|
251
251
|
pass
|
|
252
252
|
|
|
253
253
|
|
|
254
|
-
class
|
|
254
|
+
class WrongParameterException(Exception):
|
|
255
255
|
def __init__(self, message, dict=None):
|
|
256
256
|
super().__init__(message)
|
|
257
257
|
self.dict = dict
|
|
@@ -261,10 +261,6 @@ class WrongIdFormatException(Exception):
|
|
|
261
261
|
pass
|
|
262
262
|
|
|
263
263
|
|
|
264
|
-
class WrongParameterException(Exception):
|
|
265
|
-
pass
|
|
266
|
-
|
|
267
|
-
|
|
268
264
|
class ModelWithRelationsDeletionException(Exception):
|
|
269
265
|
pass
|
|
270
266
|
|
|
@@ -34,7 +34,7 @@ from zou.app.utils import (
|
|
|
34
34
|
thumbnail as thumbnail_utils,
|
|
35
35
|
)
|
|
36
36
|
from zou.app.services.exception import (
|
|
37
|
-
|
|
37
|
+
WrongParameterException,
|
|
38
38
|
PreviewFileNotFoundException,
|
|
39
39
|
ProjectNotFoundException,
|
|
40
40
|
EpisodeNotFoundException,
|
|
@@ -650,7 +650,7 @@ def extract_tile_from_preview_file(preview_file):
|
|
|
650
650
|
extracted_tile_path = movie.generate_tile(preview_file_path)
|
|
651
651
|
return extracted_tile_path
|
|
652
652
|
else:
|
|
653
|
-
return
|
|
653
|
+
return WrongParameterException("Preview file is not a movie")
|
|
654
654
|
|
|
655
655
|
|
|
656
656
|
def reset_movie_files_metadata():
|
|
@@ -1082,7 +1082,7 @@ def get_person_tasks_to_check(project_ids=None, department_ids=None):
|
|
|
1082
1082
|
else:
|
|
1083
1083
|
query = query.filter(user_service.build_open_project_filter())
|
|
1084
1084
|
|
|
1085
|
-
if department_ids
|
|
1085
|
+
if department_ids:
|
|
1086
1086
|
query = query.filter(TaskType.department_id.in_(department_ids))
|
|
1087
1087
|
tasks = []
|
|
1088
1088
|
for (
|
|
@@ -158,7 +158,7 @@ def get_time_spents_for_year(
|
|
|
158
158
|
else:
|
|
159
159
|
query = query.filter(Task.project_id == project_id)
|
|
160
160
|
|
|
161
|
-
if department_ids
|
|
161
|
+
if department_ids:
|
|
162
162
|
query = query.join(TaskType).filter(
|
|
163
163
|
TaskType.department_id.in_(department_ids)
|
|
164
164
|
)
|
|
@@ -200,7 +200,7 @@ def get_time_spents_for_month(
|
|
|
200
200
|
else:
|
|
201
201
|
query = query.filter(Task.project_id == project_id)
|
|
202
202
|
|
|
203
|
-
if department_ids
|
|
203
|
+
if department_ids:
|
|
204
204
|
query = query.join(TaskType).filter(
|
|
205
205
|
TaskType.department_id.in_(department_ids)
|
|
206
206
|
)
|
|
@@ -255,7 +255,7 @@ def get_time_spents(
|
|
|
255
255
|
if project_ids is not None:
|
|
256
256
|
query = query.filter(Task.project_id.in_(project_ids))
|
|
257
257
|
|
|
258
|
-
if department_ids
|
|
258
|
+
if department_ids:
|
|
259
259
|
query = query.join(TaskType)
|
|
260
260
|
query = query.filter(TaskType.department_id.in_(department_ids))
|
|
261
261
|
except DataError:
|
|
@@ -430,7 +430,7 @@ def get_person_time_spent_entries(
|
|
|
430
430
|
else:
|
|
431
431
|
query = query.filter(Task.project_id == project_id)
|
|
432
432
|
|
|
433
|
-
if department_ids
|
|
433
|
+
if department_ids:
|
|
434
434
|
query = query.join(TaskType, TaskType.id == Task.task_type_id).filter(
|
|
435
435
|
TaskType.department_id.in_(department_ids)
|
|
436
436
|
)
|
zou/app/swagger.py
CHANGED
|
@@ -14,6 +14,7 @@ swagger_config = {
|
|
|
14
14
|
"static_url_path": "/docs",
|
|
15
15
|
"swagger_ui": True,
|
|
16
16
|
"specs_route": "/apidocs/",
|
|
17
|
+
"openapi": "3.0.2",
|
|
17
18
|
}
|
|
18
19
|
|
|
19
20
|
|
|
@@ -30,26 +31,18 @@ An easy to use Python client to access this API is available:
|
|
|
30
31
|
|
|
31
32
|
<p>Before you can use any of the endpoints outline below,
|
|
32
33
|
you will have to get a JWT token to authorize your requests.
|
|
34
|
+
</p>
|
|
33
35
|
|
|
34
|
-
|
|
35
|
-
|
|
36
|
+
<p>
|
|
37
|
+
You will find the information to retrieve it in the
|
|
38
|
+
<a href="#tag/Authentication">Zou documentation</a>.
|
|
39
|
+
</p>
|
|
36
40
|
|
|
37
|
-
The response is a JSON object, specifically you'll need to provide the ```access_token``` for your future requests.
|
|
38
|
-
|
|
39
|
-
Here is a complete authentication process as an example (again using curl):
|
|
40
|
-
```
|
|
41
|
-
$ curl -X POST <server_address>/api/auth/login -d "email=<youremail>&password=<yourpassword>"'
|
|
42
|
-
{{"login": true", "access_token": "eyJ0e...", ...}}
|
|
43
|
-
$ jwt=eyJ0e... # Store the access token for easier use
|
|
44
|
-
$ curl -H "Authorization: Bearer $jwt" <server_address>/api/data/projects
|
|
45
|
-
[{{...}},
|
|
46
|
-
{{...}}]
|
|
47
|
-
```
|
|
48
41
|
[OpenAPI definition](/openapi.json)
|
|
49
42
|
"""
|
|
50
43
|
|
|
51
44
|
swagger_template = {
|
|
52
|
-
"openapi": "3.
|
|
45
|
+
"openapi": "3.0.2",
|
|
53
46
|
"info": {
|
|
54
47
|
"title": "Kitsu API",
|
|
55
48
|
"description": description,
|
|
@@ -70,12 +63,14 @@ swagger_template = {
|
|
|
70
63
|
"host": "localhost:8080",
|
|
71
64
|
"basePath": "/api",
|
|
72
65
|
"schemes": ["http", "https"],
|
|
73
|
-
"
|
|
74
|
-
"
|
|
75
|
-
"
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
66
|
+
"components": {
|
|
67
|
+
"securitySchemes": {
|
|
68
|
+
"JWT Authorization": {
|
|
69
|
+
"name": "Authorization",
|
|
70
|
+
"in": "header",
|
|
71
|
+
"type": "apiKey",
|
|
72
|
+
"description": "Format in header: **Authorization: Bearer {token}**. \n\n Value example: Bearer xxxxx.yyyyy.zzzzz",
|
|
73
|
+
}
|
|
79
74
|
}
|
|
80
75
|
},
|
|
81
76
|
"security": [{"JWT Authorization": []}],
|