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.
Files changed (39) hide show
  1. zou/__init__.py +1 -1
  2. zou/app/__init__.py +1 -1
  3. zou/app/blueprints/assets/__init__.py +22 -0
  4. zou/app/blueprints/assets/resources.py +240 -3
  5. zou/app/blueprints/chats/resources.py +2 -2
  6. zou/app/blueprints/crud/base.py +10 -6
  7. zou/app/blueprints/crud/day_off.py +3 -3
  8. zou/app/blueprints/crud/entity.py +3 -3
  9. zou/app/blueprints/crud/entity_type.py +15 -0
  10. zou/app/blueprints/crud/metadata_descriptor.py +16 -3
  11. zou/app/blueprints/crud/person.py +13 -15
  12. zou/app/blueprints/crud/preview_background_file.py +3 -3
  13. zou/app/blueprints/crud/project.py +5 -5
  14. zou/app/blueprints/crud/schedule_item.py +2 -2
  15. zou/app/blueprints/crud/task.py +1 -0
  16. zou/app/blueprints/crud/task_type.py +3 -3
  17. zou/app/blueprints/persons/resources.py +2 -2
  18. zou/app/blueprints/previews/resources.py +29 -6
  19. zou/app/blueprints/projects/resources.py +1 -1
  20. zou/app/blueprints/shots/resources.py +2 -2
  21. zou/app/models/base.py +6 -1
  22. zou/app/models/entity.py +3 -5
  23. zou/app/services/assets_service.py +76 -1
  24. zou/app/services/breakdown_service.py +33 -3
  25. zou/app/services/deletion_service.py +5 -0
  26. zou/app/services/exception.py +1 -5
  27. zou/app/services/preview_files_service.py +2 -2
  28. zou/app/services/tasks_service.py +1 -1
  29. zou/app/services/time_spents_service.py +4 -4
  30. zou/app/swagger.py +15 -20
  31. zou/app/utils/query.py +53 -5
  32. zou/event_stream.py +1 -0
  33. zou/migrations/versions/59a7445a966c_add_entity_is_shared.py +41 -0
  34. {zou-0.19.52.dist-info → zou-0.19.54.dist-info}/METADATA +6 -6
  35. {zou-0.19.52.dist-info → zou-0.19.54.dist-info}/RECORD +39 -38
  36. {zou-0.19.52.dist-info → zou-0.19.54.dist-info}/WHEEL +1 -1
  37. {zou-0.19.52.dist-info → zou-0.19.54.dist-info}/LICENSE +0 -0
  38. {zou-0.19.52.dist-info → zou-0.19.54.dist-info}/entry_points.txt +0 -0
  39. {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 ArgumentsException
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 ArgumentsException("Invalid production_style")
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 ArgumentsException("Invalid preview_background_file_id")
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 ArgumentsException("Invalid preview_background_file_id")
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 ArgumentsException("Invalid production_style")
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 ArgumentsException
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 ArgumentsException("A similar schedule item already exists")
20
+ raise WrongParameterException("A similar schedule item already exists")
21
21
  return schedule_item
22
22
 
23
23
 
@@ -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 ArgumentsException
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 ArgumentsException(
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 ArgumentsException(
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
- ArgumentsException,
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 ArgumentsException(
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
- ArgumentsException,
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 PreviewFileThumbnailResource(BasePreviewPictureResource):
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(BasePreviewPictureResource):
857
+ class PreviewFileThumbnailSquareResource(BasePreviewFileThumbnailResource):
836
858
  def __init__(self):
837
859
  BasePreviewPictureResource.__init__(self, "thumbnails-square")
838
860
 
839
861
 
840
- class PreviewFileOriginalResource(BasePreviewPictureResource):
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
- user_service.check_project_access(instance_id)
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 ArgumentsException(
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.check_manager_project_access(project_id)
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
- ArgumentsException,
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 ArgumentsException(
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
- db.session.commit()
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 = db.Column(
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(Entity.name, EntityType.name, Entity.preview_file_id)
97
- .order_by(EntityType.name, Entity.name)
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 link, entity_name, entity_type_name, entity_preview_file_id in links:
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)
@@ -251,7 +251,7 @@ class EntryAlreadyExistsException(Exception):
251
251
  pass
252
252
 
253
253
 
254
- class ArgumentsException(Exception):
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
- ArgumentsException,
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 ArgumentsException("Preview file is not a movie")
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 is not None:
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 is not None:
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 is not None:
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 is not None:
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 is not None:
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
- You can get a authorization token using a (form-encoded) POST request to ```/auth/login```.
35
- With curl this would look something like ```curl -X POST <server_address>/auth/login -d "email=<youremail>&password=<yourpassword>```.
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.1",
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
- "securityDefinitions": {
74
- "JWT Authorization": {
75
- "name": "Authorization",
76
- "in": "header",
77
- "type": "apiKey",
78
- "description": "Format in header: **Authorization: Bearer {token}**. \n\n Value example: Bearer xxxxx.yyyyy.zzzzz",
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": []}],