zou 0.20.78__py3-none-any.whl → 0.20.80__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 CHANGED
@@ -1 +1 @@
1
- __version__ = "0.20.78"
1
+ __version__ = "0.20.80"
@@ -572,19 +572,16 @@ class ChangePasswordResource(Resource, ArgsMixin):
572
572
  html = f"""<p>Hello {user["first_name"]},</p>
573
573
 
574
574
  <p>
575
- You have successfully changed your password at this date : {time_string}.
576
-
577
- Your IP when you have changed your password is : {person_IP}.
578
- </p>
579
-
580
- Thank you and see you soon on Kitsu,
575
+ You have successfully changed your password at this date: {time_string}.
581
576
  </p>
582
577
  <p>
583
- {organisation["name"]} Team
578
+ Your IP when you have changed your password is: {person_IP}.
584
579
  </p>
585
580
  """
586
581
  subject = f"{organisation['name']} - Kitsu: password changed"
587
- emails.send_email(subject, html, user["email"])
582
+ title = "Password Changed"
583
+ email_html_body = templates_service.generate_html_body(title, html)
584
+ emails.send_email(subject, email_html_body, user["email"])
588
585
  return {"success": True}
589
586
 
590
587
  except auth.PasswordsNoMatchException:
@@ -778,15 +775,11 @@ This link will expire after 2 hours. After, you have to do a new request to rese
778
775
  This email was sent at this date: {time_string}.
779
776
  The IP of the person who requested this is: {person_IP}.
780
777
  </p>
781
-
782
- Thank you and see you soon on Kitsu,
783
- </p>
784
- <p>
785
- {organisation["name"]} Team
786
- </p>
787
778
  """
788
779
  subject = f"{organisation['name']} - Kitsu: password recovery"
789
- emails.send_email(subject, html, args["email"])
780
+ title = "Password Recovery"
781
+ email_html_body = templates_service.generate_html_body(title, html)
782
+ emails.send_email(subject, email_html_body, args["email"])
790
783
  return {"success": "Reset token sent"}
791
784
 
792
785
 
@@ -498,8 +498,10 @@ class ReplyCommentResource(Resource, ArgsMixin):
498
498
  if comment["person_id"] != current_user["id"]:
499
499
  if permissions.has_client_permissions():
500
500
  author = persons_service.get_person(comment["person_id"])
501
- if current_user["studio_id"] != author["studio_id"] and \
502
- author["role"] == "client":
501
+ if (
502
+ current_user["studio_id"] != author["studio_id"]
503
+ and author["role"] == "client"
504
+ ):
503
505
  raise permissions.PermissionDenied()
504
506
  user_service.check_task_action_access(task_id)
505
507
 
@@ -510,9 +512,7 @@ class ReplyCommentResource(Resource, ArgsMixin):
510
512
  )
511
513
  files = request.files
512
514
  return comments_service.reply_comment(
513
- comment_id,
514
- args["text"],
515
- files=files
515
+ comment_id, args["text"], files=files
516
516
  )
517
517
 
518
518
 
@@ -2575,4 +2575,4 @@ class GuessFromPathResource(Resource, ArgsMixin):
2575
2575
  },
2576
2576
  ["sep", "/"],
2577
2577
  ]
2578
- )
2578
+ )
@@ -14,6 +14,7 @@ from zou.app.services import (
14
14
  time_spents_service,
15
15
  shots_service,
16
16
  user_service,
17
+ templates_service,
17
18
  )
18
19
  from zou.app.utils import (
19
20
  permissions,
@@ -1398,17 +1399,18 @@ class ChangePasswordForPersonResource(Resource, ArgsMixin):
1398
1399
  html = f"""<p>Hello {person["first_name"]},</p>
1399
1400
  <p>
1400
1401
  Your password was changed at this date: {time_string}.
1401
- The IP of the user who changed your password is: {person_IP}.
1402
- If you don't know the person who changed the password, please contact our support team.
1403
1402
  </p>
1404
- Thank you and see you soon on Kitsu,
1403
+ <p>
1404
+ The IP of the user who changed your password is: {person_IP}.
1405
1405
  </p>
1406
1406
  <p>
1407
- {organisation["name"]} Team
1407
+ If you don't know the person who changed the password, please contact our support team.
1408
1408
  </p>
1409
1409
  """
1410
1410
  subject = f"{organisation['name']} - Kitsu: password changed"
1411
- emails.send_email(subject, html, person["email"])
1411
+ title = "Password Changed"
1412
+ email_html_body = templates_service.generate_html_body(title, html)
1413
+ emails.send_email(subject, email_html_body, person["email"])
1412
1414
  return {"success": True}
1413
1415
 
1414
1416
  except auth.PasswordsNoMatchException:
@@ -1497,17 +1499,18 @@ class DisableTwoFactorAuthenticationPersonResource(Resource, ArgsMixin):
1497
1499
  html = f"""<p>Hello {person["first_name"]},</p>
1498
1500
  <p>
1499
1501
  Your two factor authentication was disabled at this date: {time_string}.
1500
- The IP of the user who disabled your two factor authentication is: {person_IP}.
1501
- If you don't know the person who disabled the two factor authentication, please contact our support team.
1502
1502
  </p>
1503
- Thank you and see you soon on Kitsu,
1503
+ <p>
1504
+ The IP of the user who disabled your two factor authentication is: {person_IP}.
1504
1505
  </p>
1505
1506
  <p>
1506
- {organisation["name"]} Team
1507
+ If you don't know the person who disabled the two factor authentication, please contact our support team.
1507
1508
  </p>
1508
1509
  """
1509
1510
  subject = f"{organisation['name']} - Kitsu: two factor authentication disabled"
1510
- emails.send_email(subject, html, person["email"])
1511
+ title = "Two Factor Authentication Disabled"
1512
+ email_html_body = templates_service.generate_html_body(title, html)
1513
+ emails.send_email(subject, email_html_body, person["email"])
1511
1514
  return {"success": True}
1512
1515
 
1513
1516
  except UnactiveUserException:
@@ -727,48 +727,50 @@ class TempPlaylistResource(Resource, ArgsMixin):
727
727
 
728
728
  class NotifyClientsResource(Resource, ArgsMixin):
729
729
 
730
- @jwt_required()
731
- def post(self, playlist_id):
732
- """
733
- Notify clients that given playlist is ready.
734
-
735
- ---
736
- tags:
737
- - Playlists
738
- parameters:
739
- - in: path
740
- name: playlist_id
741
- required: true
742
- schema:
743
- type: string
744
- format: uuid
745
- example: a24a6ea4-ce75-4665-a070-57453082c25
746
- requestBody:
747
- required: true
748
- content:
749
- application/json:
750
- schema:
751
- type: object
752
- properties:
753
- studio_id:
754
- type: string
755
- format: uuid
756
- example: a24a6ea4-ce75-4665-a070-57453082c25
757
- responses:
758
- '200':
759
- description: Clients notified
760
- content:
761
- application/json:
730
+ @jwt_required()
731
+ def post(self, playlist_id):
732
+ """
733
+ Notify clients that given playlist is ready.
734
+
735
+ ---
736
+ tags:
737
+ - Playlists
738
+ parameters:
739
+ - in: path
740
+ name: playlist_id
741
+ required: true
762
742
  schema:
763
- type: object
764
- properties:
765
- status:
766
- type: string
767
- example: success
768
- """
769
- studio_id = request.json.get("studio_id", None)
770
- playlist = playlists_service.get_playlist(playlist_id)
771
- project_id = playlist["project_id"]
772
- user_service.check_manager_project_access(project_id)
773
- notifications_service.notify_clients_playlist_ready(playlist, studio_id)
774
- return {"status": "success"}
743
+ type: string
744
+ format: uuid
745
+ example: a24a6ea4-ce75-4665-a070-57453082c25
746
+ requestBody:
747
+ required: true
748
+ content:
749
+ application/json:
750
+ schema:
751
+ type: object
752
+ properties:
753
+ studio_id:
754
+ type: string
755
+ format: uuid
756
+ example: a24a6ea4-ce75-4665-a070-57453082c25
757
+ responses:
758
+ '200':
759
+ description: Clients notified
760
+ content:
761
+ application/json:
762
+ schema:
763
+ type: object
764
+ properties:
765
+ status:
766
+ type: string
767
+ example: success
768
+ """
769
+ studio_id = request.json.get("studio_id", None)
770
+ playlist = playlists_service.get_playlist(playlist_id)
771
+ project_id = playlist["project_id"]
772
+ user_service.check_manager_project_access(project_id)
773
+ notifications_service.notify_clients_playlist_ready(
774
+ playlist, studio_id
775
+ )
776
+ return {"status": "success"}
@@ -594,7 +594,6 @@ class DoneResource(Resource):
594
594
 
595
595
  class FiltersResource(Resource, ArgsMixin):
596
596
 
597
-
598
597
  def get(self):
599
598
  """
600
599
  Get filters
@@ -701,7 +700,6 @@ class FiltersResource(Resource, ArgsMixin):
701
700
 
702
701
  class FilterResource(Resource, ArgsMixin):
703
702
 
704
-
705
703
  def put(self, filter_id):
706
704
  """
707
705
  Update filter
@@ -14,7 +14,7 @@ from ldap3.core.exceptions import (
14
14
  LDAPInvalidCredentialsResult,
15
15
  )
16
16
 
17
- from zou.app.services import persons_service
17
+ from zou.app.services import persons_service, templates_service
18
18
  from zou.app.models.person import Person
19
19
  from zou.app.services.exception import (
20
20
  EmailOTPAlreadyEnabledException,
@@ -493,17 +493,13 @@ This one time password will expire after 5 minutes. After, you will have to requ
493
493
  This email was sent at this date : {time_string}.
494
494
  The IP of the person who requested this is: {person_IP}.
495
495
  </p>
496
-
497
- Thank you and see you soon on Kitsu,
498
- </p>
499
- <p>
500
- {organisation["name"]} Team
501
- </p>
502
496
  """
503
497
  subject = (
504
498
  f"{organisation['name']} - Kitsu : your verification code is {otp}"
505
499
  )
506
- emails.send_email(subject, html, person["email"])
500
+ title = "Your verification code"
501
+ email_html_body = templates_service.generate_html_body(title, html)
502
+ emails.send_email(subject, email_html_body, person["email"])
507
503
  return True
508
504
 
509
505
 
@@ -79,7 +79,7 @@ def create_comment(
79
79
  files={},
80
80
  created_at="",
81
81
  links=[],
82
- with_hashtags=True
82
+ with_hashtags=True,
83
83
  ):
84
84
  """
85
85
  Create a new comment and related: news, notifications and events.
@@ -136,12 +136,11 @@ def _handle_hashtags(person, task_type, task, text):
136
136
  task_id=_task["id"],
137
137
  text=text + f"\n\n____\nFrom {task_type['name']} task",
138
138
  task_status_id=task_status["id"],
139
- with_hashtags=False
139
+ with_hashtags=False,
140
140
  )
141
141
  return hashtags
142
142
 
143
143
 
144
-
145
144
  def _check_retake_capping(task_status, task):
146
145
  if task_status["is_retake"]:
147
146
  project = projects_service.get_project(task["project_id"])
@@ -629,7 +628,7 @@ def get_comment_department_mention_ids(project_id, text):
629
628
 
630
629
  def get_comment_hashtags(text):
631
630
  """
632
- Check for task type mentions (#full name) in text and returns matching
631
+ Check for task type mentions (#full name) in text and returns matching
633
632
  tags. If all is present, return only all because it includes everything
634
633
  else.
635
634
  """
@@ -650,13 +649,13 @@ def filter_tasks_by_hashtags(tasks, hashtags, original_task_type):
650
649
  return [
651
650
  task
652
651
  for task in tasks
653
- if task["task_type_name"].lower() != \
654
- original_task_type["name"].lower()
652
+ if task["task_type_name"].lower()
653
+ != original_task_type["name"].lower()
655
654
  ]
656
655
  else:
657
656
  hashtag_map = {
658
657
  hashtag: True
659
- for hashtag in hashtags
658
+ for hashtag in hashtags
660
659
  if hashtag != original_task_type["name"].lower()
661
660
  }
662
661
  return [
@@ -3,6 +3,8 @@ from sqlalchemy.exc import IntegrityError
3
3
 
4
4
 
5
5
  from zou.app.models.attachment_file import AttachmentFile
6
+ from zou.app.models.budget import Budget
7
+ from zou.app.models.budget_entry import BudgetEntry
6
8
  from zou.app.models.comment import Comment
7
9
  from zou.app.models.desktop_login_log import DesktopLoginLog
8
10
  from zou.app.models.entity import (
@@ -346,6 +348,11 @@ def remove_project(project_id):
346
348
  for task in tasks:
347
349
  remove_task(task.id, force=True)
348
350
 
351
+ budgets = Budget.get_all_by(project_id=project_id)
352
+ for budget in budgets:
353
+ BudgetEntry.delete_all_by(budget_id=budget.id)
354
+ budget.delete()
355
+
349
356
  EntityLink.query.filter(
350
357
  EntityLink.entity_in_id == Entity.id,
351
358
  Entity.project_id == project_id,
@@ -10,10 +10,12 @@ from zou.app.services import (
10
10
  tasks_service,
11
11
  )
12
12
  from zou.app.stores import queue_store
13
- from zou.app.services.template_services import generate_html_body
13
+ from zou.app.services.templates_service import generate_html_body
14
14
 
15
15
 
16
- def send_notification(person_id, subject, messages, title=""):
16
+ def send_notification(
17
+ person_id, subject, messages, title="", force_email=False
18
+ ):
17
19
  """
18
20
  Send email notification to given person. Use the job queue if it is
19
21
  activated.
@@ -25,7 +27,7 @@ def send_notification(person_id, subject, messages, title=""):
25
27
  discord_message = messages["discord_message"]
26
28
  email_html_body = generate_html_body(title, email_message)
27
29
 
28
- if person["notifications_enabled"]:
30
+ if person["notifications_enabled"] or force_email:
29
31
  if config.ENABLE_JOB_QUEUE:
30
32
  queue_store.job_queue.enqueue(
31
33
  emails.send_email,
@@ -79,7 +81,7 @@ def send_notification(person_id, subject, messages, title=""):
79
81
 
80
82
  def send_comment_notification(person_id, author_id, comment, task):
81
83
  """
82
- Send a notification emali telling that a new comment was posted to person
84
+ Send a notification email telling that a new comment was posted to person
83
85
  matching given person id.
84
86
  """
85
87
  person = persons_service.get_person(person_id)
@@ -155,6 +157,7 @@ _%s_
155
157
  task_status_name,
156
158
  )
157
159
 
160
+ title = "New Comment"
158
161
  messages = {
159
162
  "email_message": email_message,
160
163
  "slack_message": slack_message,
@@ -164,7 +167,7 @@ _%s_
164
167
  },
165
168
  "discord_message": discord_message,
166
169
  }
167
- send_notification(person_id, subject, messages)
170
+ send_notification(person_id, subject, messages, title)
168
171
 
169
172
  return True
170
173
 
@@ -215,7 +218,7 @@ _%s_
215
218
  task_url,
216
219
  comment["text"],
217
220
  )
218
-
221
+ title = "New Mention"
219
222
  messages = {
220
223
  "email_message": email_message,
221
224
  "slack_message": slack_message,
@@ -225,7 +228,7 @@ _%s_
225
228
  },
226
229
  "discord_message": discord_message,
227
230
  }
228
- return send_notification(person_id, subject, messages)
231
+ return send_notification(person_id, subject, messages, title)
229
232
  else:
230
233
  return True
231
234
 
@@ -273,7 +276,7 @@ def send_assignation_notification(person_id, author_id, task):
273
276
  },
274
277
  "discord_message": discord_message,
275
278
  }
276
- return send_notification(person_id, subject, messages)
279
+ return send_notification(person_id, subject, messages, title)
277
280
  return True
278
281
 
279
282
 
@@ -372,7 +375,7 @@ _%s_
372
375
  },
373
376
  "discord_message": discord_message,
374
377
  }
375
- send_notification(person_id, subject, messages)
378
+ send_notification(person_id, subject, messages, title)
376
379
  return True
377
380
 
378
381
 
@@ -397,25 +400,41 @@ def send_playlist_ready_notification(person_id, author_id, playlist):
397
400
  or person["notifications_mattermost_enabled"]
398
401
  or person["notifications_discord_enabled"]
399
402
  ):
403
+
404
+ playlist_url = f"{config.DOMAIN_PROTOCOL}://{config.DOMAIN_NAME}/productions/{playlist['project_id']}/"
405
+
400
406
  if episode is not None:
401
- playlist_url = f"{config.DOMAIN_PROTOCOL}://{config.DOMAIN_NAME}/productions/{playlist['project_id']}/episodes/{episode['id']}/playlists/{playlist['id']}"
407
+ playlist_url += (
408
+ f"episodes/{episode['id']}/playlists/{playlist['id']}"
409
+ )
410
+ elif (
411
+ project["production_type"] == "tvshow"
412
+ and playlist["for_entity"] == "asset"
413
+ ):
414
+ if playlist["is_for_all"] == True:
415
+ playlist_url += f"episodes/all/playlists/{playlist['id']}"
416
+ else:
417
+ playlist_url += f"episodes/main/playlists/{playlist['id']}"
402
418
  else:
403
- playlist_url = f"{config.DOMAIN_PROTOCOL}://{config.DOMAIN_NAME}/productions/{playlist['project_id']}/playlists/{playlist['id']}"
419
+ playlist_url += f"playlists/{playlist['id']}"
404
420
 
405
- title = "Playlist Ready"
421
+ title = "New Playlist Ready"
406
422
  episode_segment = ""
407
423
  if episode is not None:
408
- episode_segment = f"the episode {episode['name']} of"
409
- subject = "[Kitsu] A new playlist is ready"
424
+ episode_segment = f"the episode {episode['name']} of "
425
+ subject = f'[Kitsu] The playlist {playlist["name"]} in project {project["name"]} is ready for review'
410
426
 
411
- email_message = f"""<p><strong>{author["full_name"]}</strong> notifies you that playlist <a href="{playlist_url}">{playlist["name"]}</a> is ready for a review under {episode_segment} the project {project["name"]}.</p>
427
+ email_message = f"""<p><strong>{author["full_name"]}</strong> notifies you that playlist <a href="{playlist_url}">{playlist["name"]}</a> is ready for a review under {episode_segment}the project {project["name"]}.</p>
428
+ """
412
429
 
413
- {len(playlist["shots"])} elements are listed in the playlist.
430
+ if len(playlist["shots"]) > 1:
431
+ email_message += f"""
432
+ <p>{len(playlist["shots"])} elements are listed in the playlist.</p>
414
433
  """
415
434
 
416
- slack_message = f"*{author['full_name']}* notifies you that a playlist <{playlist_url}|{playlist['name']}> is ready for a review under {episode_segment} the project {project['name']}."
435
+ slack_message = f"*{author['full_name']}* notifies you that a playlist <{playlist_url}|{playlist['name']}> is ready for a review under {episode_segment}the project {project['name']}."
417
436
 
418
- discord_message = f"*{author['full_name']}* notifies you that a playlist [{playlist['name']}]({playlist_url}) is ready for a review under {episode_segment} the project {project['name']}."
437
+ discord_message = f"*{author['full_name']}* notifies you that a playlist [{playlist['name']}]({playlist_url}) is ready for a review under {episode_segment}the project {project['name']}."
419
438
  messages = {
420
439
  "email_message": email_message,
421
440
  "slack_message": slack_message,
@@ -425,4 +444,6 @@ def send_playlist_ready_notification(person_id, author_id, playlist):
425
444
  },
426
445
  "discord_message": discord_message,
427
446
  }
428
- send_notification(person_id, subject, messages, title)
447
+ send_notification(
448
+ person_id, subject, messages, title, force_email=True
449
+ )
@@ -376,7 +376,7 @@ def get_entity_tasks(entity):
376
376
  get_tasks = getattr(
377
377
  tasks_service, "get_tasks_for_" + entity_type_name.lower()
378
378
  )
379
- return get_tasks(entity["id"])
379
+ return get_tasks(entity["id"])
380
380
 
381
381
 
382
382
  def remove_entity_link(link_id):
@@ -505,8 +505,7 @@ def notify_clients_playlist_ready(playlist, studio_id=None):
505
505
  author = persons_service.get_current_user()
506
506
  project_id = playlist["project_id"]
507
507
  query = (
508
- Person.query
509
- .join(ProjectPersonLink)
508
+ Person.query.join(ProjectPersonLink)
510
509
  .filter(Person.is_bot == False)
511
510
  .filter(Person.role == "client")
512
511
  .filter(ProjectPersonLink.project_id == project_id)
@@ -529,3 +528,12 @@ def notify_clients_playlist_ready(playlist, studio_id=None):
529
528
  emails_service.send_playlist_ready_notification(
530
529
  recipient_id, author_id, playlist
531
530
  )
531
+ events.emit(
532
+ "notification:new",
533
+ {
534
+ "notification_id": notification["id"],
535
+ "person_id": recipient_id,
536
+ },
537
+ project_id=playlist["project_id"],
538
+ persist=False,
539
+ )
@@ -18,7 +18,7 @@ from zou.app.models.time_spent import TimeSpent
18
18
 
19
19
  from zou.app import config, file_store
20
20
  from zou.app.utils import fields, events, cache, emails, date_helpers
21
- from zou.app.services import index_service, auth_service
21
+ from zou.app.services import index_service, auth_service, templates_service
22
22
  from zou.app.stores import auth_tokens_store
23
23
  from zou.app.services.exception import (
24
24
  PersonNotFoundException,
@@ -441,9 +441,8 @@ def invite_person(person_id):
441
441
  auth_tokens_store.add(
442
442
  "reset-token-%s" % person["email"], token, ttl=3600 * 24 * 7
443
443
  )
444
- subject = (
445
- "You are invited by %s to join their Kitsu production tracker"
446
- % (organisation["name"])
444
+ subject = "You are invited by %s to join their Kitsu platform" % (
445
+ organisation["name"]
447
446
  )
448
447
  params = {"email": person["email"], "token": token}
449
448
  query = urllib.parse.urlencode(params)
@@ -461,27 +460,21 @@ def invite_person(person_id):
461
460
 
462
461
  html = f"""<p>Hello {person["first_name"]},</p>
463
462
  <p>
464
- You are invited by {organisation["name"]} to collaborate on their Kitsu production tracker.
463
+ You are invited by {organisation["name"]} to join their team on Kitsu.
465
464
  </p>
466
465
  <p>
467
466
  Your login is: <strong>{person["email"]}</strong>
468
467
  </p>
469
468
  <p>
470
- You are invited to set your password by following this link: <a href="{reset_url}">{reset_url}</a>
469
+ Set your password to continue:
471
470
  </p>
472
- <p>
473
- This link will expire after one week. After, you have to request to reset your password.
474
- The invitation was sent at this date: {time_string}.
475
- </p>
476
- <p>
477
- Thank you and see you soon on Kitsu,
478
- </p>
479
- <p>
480
- {organisation["name"]} Team
471
+ <p class="cta">
472
+ <a class="button" href="{reset_url}">Set your password</a>
481
473
  </p>
482
474
  """
483
-
484
- emails.send_email(subject, html, person["email"])
475
+ title = "Welcome to Kitsu"
476
+ email_html_body = templates_service.generate_html_body(title, html)
477
+ emails.send_email(subject, email_html_body, person["email"])
485
478
 
486
479
 
487
480
  @cache.memoize_function(120)
@@ -38,6 +38,7 @@ from zou.app.services import (
38
38
  tasks_service,
39
39
  names_service,
40
40
  persons_service,
41
+ templates_service,
41
42
  )
42
43
 
43
44
  from zou.app.services.exception import (
@@ -694,19 +695,17 @@ def build_playlist_job(playlist, job, shots, params, email, full, remote):
694
695
  job["id"],
695
696
  )
696
697
  html = f"""<p>Hello {person.first_name},</p>
697
- <p>Your playlist {playlist["name"]} is available at:
698
- <a href="{playlist_url}">{playlist_url}</a>
698
+ <p>Your playlist {playlist["name"]} build is available:
699
699
  </p>
700
- <p>
701
- Thank you and see you soon on Kitsu,
702
- </p>
703
- <p>
704
- {organisation["name"]} Team
700
+ <p class="cta">
701
+ <a href="{playlist_url}">Download Playlist Build (.mp4)</a>
705
702
  </p>
706
703
  """
707
704
 
708
705
  subject = f"{organisation['name']} - Kitsu: playlist download"
709
- emails.send_email(subject, html, email)
706
+ title = "Playlist Download"
707
+ email_html_body = templates_service.generate_html_body(title, html)
708
+ emails.send_email(subject, email_html_body, email)
710
709
 
711
710
 
712
711
  def get_playlist_file_name(playlist):
@@ -107,7 +107,6 @@ notification_template_end_title = """
107
107
  """
108
108
 
109
109
 
110
-
111
110
  def get_signature():
112
111
  """
113
112
  Build signature for Zou emails.
@@ -115,7 +114,7 @@ def get_signature():
115
114
  organisation = persons_service.get_organisation()
116
115
  return (
117
116
  """
118
- <p>Best,</p>
117
+ <p>Best regards,</p>
119
118
 
120
119
  <p>%s Team</p>"""
121
120
  % organisation["name"]
@@ -123,7 +122,11 @@ def get_signature():
123
122
 
124
123
 
125
124
  def generate_html_body(title, message):
126
- return notification_template_begin + \
127
- title + notification_template_end_title + \
128
- message + get_signature () + \
129
- notification_template_end
125
+ return (
126
+ notification_template_begin
127
+ + title
128
+ + notification_template_end_title
129
+ + message
130
+ + get_signature()
131
+ + notification_template_end
132
+ )
@@ -1461,19 +1461,22 @@ def get_last_notifications(
1461
1461
  full_entity_name, episode_id, entity_preview_file_id = "", None, None
1462
1462
  playlist_id = notification.playlist_id
1463
1463
  playlist_name = ""
1464
+ playlist_for_entity = ""
1465
+ playlist_is_for_all = False
1464
1466
  if notification.playlist_id is None:
1465
1467
  (full_entity_name, episode_id, entity_preview_file_id) = (
1466
1468
  names_service.get_full_entity_name(task_entity_id)
1467
1469
  )
1468
1470
  else:
1469
- playlist = playlists_service.get_playlist(
1470
- notification.playlist_id
1471
- )
1471
+ playlist = playlists_service.get_playlist(notification.playlist_id)
1472
1472
  episode_id = playlist.get("episode_id", None)
1473
1473
  project = projects_service.get_project(playlist["project_id"])
1474
1474
  project_id = project["id"]
1475
1475
  project_name = project["name"]
1476
1476
  playlist_name = playlist["name"]
1477
+ playlist_for_entity = playlist["for_entity"]
1478
+ playlist_is_for_all = playlist["is_for_all"]
1479
+
1477
1480
  preview_file_id = None
1478
1481
  mentions = []
1479
1482
  department_mentions = []
@@ -1539,6 +1542,8 @@ def get_last_notifications(
1539
1542
  "subscription_id": subscription_id,
1540
1543
  "playlist_id": playlist_id,
1541
1544
  "playlist_name": playlist_name,
1545
+ "playlist_for_entity": playlist_for_entity,
1546
+ "playlist_is_for_all": playlist_is_for_all,
1542
1547
  }
1543
1548
  )
1544
1549
  )
@@ -5,31 +5,40 @@ Revises: 1b0ab951adca
5
5
  Create Date: 2025-09-12 16:03:33.551737
6
6
 
7
7
  """
8
+
8
9
  from alembic import op
9
10
  import sqlalchemy as sa
10
11
  import sqlalchemy_utils
11
12
 
12
13
 
13
14
  # revision identifiers, used by Alembic.
14
- revision = '0c05b22194f3'
15
- down_revision = '1b0ab951adca'
15
+ revision = "0c05b22194f3"
16
+ down_revision = "1b0ab951adca"
16
17
  branch_labels = None
17
18
  depends_on = None
18
19
 
19
20
 
20
21
  def upgrade():
21
22
  # ### commands auto generated by Alembic - please adjust! ###
22
- with op.batch_alter_table('entity', schema=None) as batch_op:
23
- batch_op.create_index('ix_entity_entity_type_parent', ['entity_type_id', 'parent_id'], unique=False)
24
- batch_op.create_index('ix_entity_entity_type_project', ['entity_type_id', 'project_id'], unique=False)
23
+ with op.batch_alter_table("entity", schema=None) as batch_op:
24
+ batch_op.create_index(
25
+ "ix_entity_entity_type_parent",
26
+ ["entity_type_id", "parent_id"],
27
+ unique=False,
28
+ )
29
+ batch_op.create_index(
30
+ "ix_entity_entity_type_project",
31
+ ["entity_type_id", "project_id"],
32
+ unique=False,
33
+ )
25
34
 
26
35
  # ### end Alembic commands ###
27
36
 
28
37
 
29
38
  def downgrade():
30
39
  # ### commands auto generated by Alembic - please adjust! ###
31
- with op.batch_alter_table('entity', schema=None) as batch_op:
32
- batch_op.drop_index('ix_entity_entity_type_project')
33
- batch_op.drop_index('ix_entity_entity_type_parent')
40
+ with op.batch_alter_table("entity", schema=None) as batch_op:
41
+ batch_op.drop_index("ix_entity_entity_type_project")
42
+ batch_op.drop_index("ix_entity_entity_type_parent")
34
43
 
35
44
  # ### end Alembic commands ###
@@ -5,6 +5,7 @@ Revises: ce7f46f445dc
5
5
  Create Date: 2025-08-27 14:14:16.237272
6
6
 
7
7
  """
8
+
8
9
  from alembic import op
9
10
  import sqlalchemy as sa
10
11
  import sqlalchemy_utils
@@ -12,33 +13,40 @@ import sqlalchemy_utils
12
13
  import uuid
13
14
 
14
15
  # revision identifiers, used by Alembic.
15
- revision = '1b0ab951adca'
16
- down_revision = 'ce7f46f445dc'
16
+ revision = "1b0ab951adca"
17
+ down_revision = "ce7f46f445dc"
17
18
  branch_labels = None
18
19
  depends_on = None
19
20
 
20
21
 
21
22
  def upgrade():
22
23
  # ### commands auto generated by Alembic - please adjust! ###
23
- with op.batch_alter_table('attachment_file', schema=None) as batch_op:
24
- batch_op.add_column(sa.Column('reply_id', sqlalchemy_utils.types.uuid.UUIDType(binary=False), default=uuid.uuid4, nullable=True))
25
-
26
- with op.batch_alter_table('software', schema=None) as batch_op:
27
- batch_op.alter_column('version',
28
- existing_type=sa.VARCHAR(length=20),
29
- nullable=True)
24
+ with op.batch_alter_table("attachment_file", schema=None) as batch_op:
25
+ batch_op.add_column(
26
+ sa.Column(
27
+ "reply_id",
28
+ sqlalchemy_utils.types.uuid.UUIDType(binary=False),
29
+ default=uuid.uuid4,
30
+ nullable=True,
31
+ )
32
+ )
33
+
34
+ with op.batch_alter_table("software", schema=None) as batch_op:
35
+ batch_op.alter_column(
36
+ "version", existing_type=sa.VARCHAR(length=20), nullable=True
37
+ )
30
38
 
31
39
  # ### end Alembic commands ###
32
40
 
33
41
 
34
42
  def downgrade():
35
43
  # ### commands auto generated by Alembic - please adjust! ###
36
- with op.batch_alter_table('software', schema=None) as batch_op:
37
- batch_op.alter_column('version',
38
- existing_type=sa.VARCHAR(length=20),
39
- nullable=False)
44
+ with op.batch_alter_table("software", schema=None) as batch_op:
45
+ batch_op.alter_column(
46
+ "version", existing_type=sa.VARCHAR(length=20), nullable=False
47
+ )
40
48
 
41
- with op.batch_alter_table('attachment_file', schema=None) as batch_op:
42
- batch_op.drop_column('reply_id')
49
+ with op.batch_alter_table("attachment_file", schema=None) as batch_op:
50
+ batch_op.drop_column("reply_id")
43
51
 
44
52
  # ### end Alembic commands ###
@@ -5,6 +5,7 @@ Revises: 71d546ace0ee
5
5
  Create Date: 2025-10-01 17:25:30.645533
6
6
 
7
7
  """
8
+
8
9
  from alembic import op
9
10
  import sqlalchemy as sa
10
11
  import sqlalchemy_utils
@@ -12,35 +13,60 @@ import sqlalchemy_utils
12
13
  import uuid
13
14
 
14
15
  # revision identifiers, used by Alembic.
15
- revision = '5f1620d191af'
16
- down_revision = '71d546ace0ee'
16
+ revision = "5f1620d191af"
17
+ down_revision = "71d546ace0ee"
17
18
  branch_labels = None
18
19
  depends_on = None
19
20
 
20
21
 
21
22
  def upgrade():
22
23
  # ### commands auto generated by Alembic - please adjust! ###
23
- with op.batch_alter_table('notification', schema=None) as batch_op:
24
- batch_op.add_column(sa.Column('playlist_id', sqlalchemy_utils.types.uuid.UUIDType(binary=False), default=uuid.uuid4, nullable=True))
25
- batch_op.alter_column('task_id',
26
- existing_type=sa.UUID(),
27
- nullable=True)
28
- batch_op.drop_constraint('notification_uc', type_='unique')
29
- batch_op.create_unique_constraint('notification_uc', ['person_id', 'author_id', 'comment_id', 'reply_id', 'playlist_id', 'type'])
30
- batch_op.create_index(batch_op.f('ix_notification_playlist_id'), ['playlist_id'], unique=False)
31
- batch_op.create_foreign_key(None, 'playlist', ['playlist_id'], ['id'])
24
+ with op.batch_alter_table("notification", schema=None) as batch_op:
25
+ batch_op.add_column(
26
+ sa.Column(
27
+ "playlist_id",
28
+ sqlalchemy_utils.types.uuid.UUIDType(binary=False),
29
+ default=uuid.uuid4,
30
+ nullable=True,
31
+ )
32
+ )
33
+ batch_op.alter_column(
34
+ "task_id", existing_type=sa.UUID(), nullable=True
35
+ )
36
+ batch_op.drop_constraint("notification_uc", type_="unique")
37
+ batch_op.create_unique_constraint(
38
+ "notification_uc",
39
+ [
40
+ "person_id",
41
+ "author_id",
42
+ "comment_id",
43
+ "reply_id",
44
+ "playlist_id",
45
+ "type",
46
+ ],
47
+ )
48
+ batch_op.create_index(
49
+ batch_op.f("ix_notification_playlist_id"),
50
+ ["playlist_id"],
51
+ unique=False,
52
+ )
53
+ batch_op.create_foreign_key(None, "playlist", ["playlist_id"], ["id"])
32
54
  # ### end Alembic commands ###
33
55
 
34
56
 
35
57
  def downgrade():
36
58
  # ### commands auto generated by Alembic - please adjust! ###
37
- with op.batch_alter_table('notification', schema=None) as batch_op:
38
- batch_op.drop_constraint(None, type_='foreignkey')
39
- batch_op.drop_index(batch_op.f('ix_notification_playlist_id'))
40
- batch_op.drop_constraint('notification_uc', type_='unique')
41
- batch_op.create_unique_constraint('notification_uc', ['person_id', 'author_id', 'comment_id', 'reply_id', 'type'], postgresql_nulls_not_distinct=False)
42
- batch_op.alter_column('task_id',
43
- existing_type=sa.UUID(),
44
- nullable=False)
45
- batch_op.drop_column('playlist_id')
59
+ with op.batch_alter_table("notification", schema=None) as batch_op:
60
+ batch_op.drop_constraint(None, type_="foreignkey")
61
+ batch_op.drop_index(batch_op.f("ix_notification_playlist_id"))
62
+ batch_op.drop_constraint("notification_uc", type_="unique")
63
+ batch_op.create_unique_constraint(
64
+ "notification_uc",
65
+ ["person_id", "author_id", "comment_id", "reply_id", "type"],
66
+ postgresql_nulls_not_distinct=False,
67
+ )
68
+ batch_op.alter_column(
69
+ "task_id", existing_type=sa.UUID(), nullable=False
70
+ )
71
+ batch_op.drop_column("playlist_id")
46
72
  # ### end Alembic commands ###
@@ -5,28 +5,31 @@ Revises: 0c05b22194f3
5
5
  Create Date: 2025-09-12 16:07:53.775943
6
6
 
7
7
  """
8
+
8
9
  from alembic import op
9
10
  import sqlalchemy as sa
10
11
  import sqlalchemy_utils
11
12
 
12
13
 
13
14
  # revision identifiers, used by Alembic.
14
- revision = '71d546ace0ee'
15
- down_revision = '0c05b22194f3'
15
+ revision = "71d546ace0ee"
16
+ down_revision = "0c05b22194f3"
16
17
  branch_labels = None
17
18
  depends_on = None
18
19
 
19
20
 
20
21
  def upgrade():
21
22
  # ### commands auto generated by Alembic - please adjust! ###
22
- with op.batch_alter_table('task', schema=None) as batch_op:
23
- batch_op.create_index('ix_task_entity_project', ['entity_id', 'project_id'], unique=False)
23
+ with op.batch_alter_table("task", schema=None) as batch_op:
24
+ batch_op.create_index(
25
+ "ix_task_entity_project", ["entity_id", "project_id"], unique=False
26
+ )
24
27
 
25
28
  # ### end Alembic commands ###
26
29
 
27
30
 
28
31
  def downgrade():
29
32
  # ### commands auto generated by Alembic - please adjust! ###
30
- with op.batch_alter_table('task', schema=None) as batch_op:
31
- batch_op.drop_index('ix_task_entity_project')
33
+ with op.batch_alter_table("task", schema=None) as batch_op:
34
+ batch_op.drop_index("ix_task_entity_project")
32
35
  # ### end Alembic commands ###
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: zou
3
- Version: 0.20.78
3
+ Version: 0.20.80
4
4
  Summary: API to store and manage the data of your animation production
5
5
  Home-page: https://zou.cg-wire.com
6
6
  Author: CG Wire
@@ -1,4 +1,4 @@
1
- zou/__init__.py,sha256=E8sEfjV0rVqHMBvKbg9TmpXbI1gbtEK-4a8RgyCqyZE,24
1
+ zou/__init__.py,sha256=vFB6Ec2pY1NJQbGcUirmHHnhMjoRJdqEC1-Zc0frdv4,24
2
2
  zou/cli.py,sha256=N9FyrL4TDgIiCUa-I3xIzOgiVLM14kz8_QLQ48y51oE,22767
3
3
  zou/debug.py,sha256=1fawPbkD4wn0Y9Gk0BiBFSa-CQe5agFi8R9uJYl2Uyk,520
4
4
  zou/event_stream.py,sha256=9O1PE_vDW8p3yfHJNSZ8w0ybD-660x8oGN0izgJdTjM,8575
@@ -12,13 +12,13 @@ zou/app/blueprints/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuF
12
12
  zou/app/blueprints/assets/__init__.py,sha256=T2zhDagHjXF6jRwOQ8vqokZTkBHyY7XtTI0Rlooamjs,2931
13
13
  zou/app/blueprints/assets/resources.py,sha256=H0axmv7opRILsV0AXMnAUEnnydYKcH6xbEUrZfcTbPc,26084
14
14
  zou/app/blueprints/auth/__init__.py,sha256=xP874bMWUnLIirOPSEbpe-Q2fBBQrxZGKd0tLZmNJXk,1128
15
- zou/app/blueprints/auth/resources.py,sha256=2Oj-MqNHWVRZm2uFKjWspeXp2twMdy9LZ3SY0g3An8Q,45997
15
+ zou/app/blueprints/auth/resources.py,sha256=8GHQTG0UvXtYZ8EPjhPlLh5bLsKlboGBa4W4kdfgD3Y,46096
16
16
  zou/app/blueprints/breakdown/__init__.py,sha256=Dp6GWSGxxWIedpyzTTEKpCRUYEo8oVNVyQhwNvTMmQM,1888
17
17
  zou/app/blueprints/breakdown/resources.py,sha256=vpE6aTdSPS3KSuGhgFIXxgYf3LXwjUjc_gwo8jYYJTE,13543
18
18
  zou/app/blueprints/chats/__init__.py,sha256=YGmwGvddg3MgSYVIh-hmkX8t2em9_LblxBeJzFqFJD4,558
19
19
  zou/app/blueprints/chats/resources.py,sha256=ekKH8BghL4MAh-ZzT8IDPVy8cYAbCRAYwzjjkIa4DmU,5934
20
20
  zou/app/blueprints/comments/__init__.py,sha256=WqpJ7-_dK1cInGTFJAxQ7syZtPCotwq2oO20UEnk1h4,1532
21
- zou/app/blueprints/comments/resources.py,sha256=8YLmrj02bJUMdW3G1TF1H-DFxI63EyuMuHEEyxf9RBo,19682
21
+ zou/app/blueprints/comments/resources.py,sha256=sok69NtQfqvQw0OXuTcJfiELGPRH1wpbIWxWhlNjI9w,19696
22
22
  zou/app/blueprints/concepts/__init__.py,sha256=sP_P4mfYvfMcgeE6MHZYP3eD0Lz0Lwit5-CFuVnA-Jg,894
23
23
  zou/app/blueprints/concepts/resources.py,sha256=hXVK0ON5vu2VDporVLrQdb5HjyxpVrXLoTq-ObLiO_Y,10114
24
24
  zou/app/blueprints/crud/__init__.py,sha256=bzjCUL2BAYuufiWcP1n83JAp1TKXEMqTeN6wMOha76M,9667
@@ -89,15 +89,15 @@ zou/app/blueprints/export/csv/task_types.py,sha256=PCEEhOQcdOJv_38i-KNxbkGeNzmQ8
89
89
  zou/app/blueprints/export/csv/tasks.py,sha256=CHFcs9S3eLIz6psE6Q6mZ-OSur_GrpBeLn98Nh9NNcA,4121
90
90
  zou/app/blueprints/export/csv/time_spents.py,sha256=yYPtilOxfQD5mBwyh9h-PbTQBpab-vMrec35tYUw4fQ,2984
91
91
  zou/app/blueprints/files/__init__.py,sha256=7Wty30JW2OXIn-tBFXOWWmPuHnsnxPpH3jNtHvvr9tY,3987
92
- zou/app/blueprints/files/resources.py,sha256=c8QF_D9sC9xmz14hX4EquGB7119tz7r8N8LJS28-GuM,82692
92
+ zou/app/blueprints/files/resources.py,sha256=FxHpmTX6V-qyJqwDlLV4ZSwRKDXJpaLl4MAYukPmFTY,82693
93
93
  zou/app/blueprints/index/__init__.py,sha256=Dh3oQiirpg8RCkfVOuk3irIjSvUvuRf0jPxE6oGubz0,828
94
94
  zou/app/blueprints/index/resources.py,sha256=nLLrFK-gJv0S_eDVPOS36xFes_3MjT3vGqDQHbI5kPk,14728
95
95
  zou/app/blueprints/news/__init__.py,sha256=HxBXjC15dVbotNAZ0CLf02iwUjxJr20kgf8_kT_9nwM,505
96
96
  zou/app/blueprints/news/resources.py,sha256=Zb3nelGgT4EsFyrFY6fPh-oFrjLAsmaHR3mevYYKI2M,11441
97
97
  zou/app/blueprints/persons/__init__.py,sha256=0cnHHw3K_8OEMm0qOi3wKVomSAg9IJSnVjAXabMeHks,3893
98
- zou/app/blueprints/persons/resources.py,sha256=suc9ESdN43f8Vg12QGc8CLrBeexrgRk23qwufLJKud0,46522
98
+ zou/app/blueprints/persons/resources.py,sha256=adKhPS3-ATN6gCVBuCQGpfJPtBFJEo0GoOfGB3QJqfU,46701
99
99
  zou/app/blueprints/playlists/__init__.py,sha256=gI6dV102RwsztOnUDk53BErYd9zrjA43gfNgGeYwfFU,1700
100
- zou/app/blueprints/playlists/resources.py,sha256=nWkMQ84bZ8sw2Kf0tkfru7GroqL6QkWVYqdOipycccY,24030
100
+ zou/app/blueprints/playlists/resources.py,sha256=5IS1urJXjGaCmhFAHPx_GuDWZ0F0RSabYeJRtow_mJI,24224
101
101
  zou/app/blueprints/previews/__init__.py,sha256=ihC6OQ9AUjnZ2JeMnjRh_tKGO0UmAjOwhZnOivc3BnQ,4460
102
102
  zou/app/blueprints/previews/resources.py,sha256=mRFVMl4fi4mptE1qMG68GAIg868HMiE7AFnQYnszSAU,53345
103
103
  zou/app/blueprints/projects/__init__.py,sha256=ZX59ZmMIAI11zNy_RjLacOQnEcSJdwRIr68KwOYiB2k,5918
@@ -137,7 +137,7 @@ zou/app/blueprints/source/shotgun/versions.py,sha256=8Mb35e5p3FLbbiu6AZb9tJErDKz
137
137
  zou/app/blueprints/tasks/__init__.py,sha256=udtTZJVViawRAPu8dO_OoyVzQTheLYWTHeTnrC-2RDA,4331
138
138
  zou/app/blueprints/tasks/resources.py,sha256=WVNu30pj5snz1TXjla5ZzED2CkToTNAsmJF0RcYnYBk,62938
139
139
  zou/app/blueprints/user/__init__.py,sha256=H9zCHcVobC6jq6dTToXKAjnZmDA0a9gChHiIP3BcZsc,4586
140
- zou/app/blueprints/user/resources.py,sha256=HJ0snZJ8OaWjk5b99kqCwfo18VI2EIG3OAWYtaJVSMI,50972
140
+ zou/app/blueprints/user/resources.py,sha256=es959tpQLVCclXCLliI8Sb1nobuB8YfVu0JNuQKlFB4,50970
141
141
  zou/app/file_trees/default.json,sha256=ryUrEmQYE8B_WkzCoQLgmem3N9yNwMIWx9G8p3HfG9o,2310
142
142
  zou/app/file_trees/simple.json,sha256=VBI43Z3rjQxtTpVCq3ktUgS0UB8x-aTowKL9LXuXCFI,3149
143
143
  zou/app/indexer/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
@@ -194,20 +194,20 @@ zou/app/models/time_spent.py,sha256=n7i3FO9g1eE_zATkItoCgrGVqq3iMSfdlKSveEZPloc,
194
194
  zou/app/models/working_file.py,sha256=q0LM3s1ziw_9AmmPDCkwyf1-TJkWTBMgo2LdHyVRwxg,1509
195
195
  zou/app/services/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
196
196
  zou/app/services/assets_service.py,sha256=FFdZPIsabggBm9VW8u2QQrTVlWOG0cilAa8l8sflX5o,24272
197
- zou/app/services/auth_service.py,sha256=hQpNb21xlr5EiTrXnzpFb4W4GDtglubqA2z_-YIBfnk,22897
197
+ zou/app/services/auth_service.py,sha256=u7AK5jNshb4M9_0C2yNqvm9GH9egSLdk1ifLxrDkv3c,22956
198
198
  zou/app/services/backup_service.py,sha256=_ZtZp6wkcVYnHxBosziwLGdrTvsUttXGphiydq53iy8,4840
199
199
  zou/app/services/base_service.py,sha256=OZd0STFh-DyBBdwsmA7DMMnrwv4C8wJUbShvZ1isndU,1383
200
200
  zou/app/services/breakdown_service.py,sha256=-bH1KUq9-No_OKnQtWK4XEU1w7uDPJnzWFMrKNkS1K0,27593
201
201
  zou/app/services/budget_service.py,sha256=uMU1vJr7kyxtkvMz_Nm7DyhW6qvSGYGqRHEQTxsHYCc,5468
202
202
  zou/app/services/chats_service.py,sha256=pqnT-RCltdf9Dp4t-2NtOSawGk0jyNhVPTgERZ_nYvk,8297
203
- zou/app/services/comments_service.py,sha256=Y77sFEsMEf5Q9Od7RgLEmHwSB59_te-bJlX7s7syzfI,22264
203
+ zou/app/services/comments_service.py,sha256=Rey8mWbW2qWrjwIEjIMImYcCHPoivaEImKqwFX5ob0Q,22257
204
204
  zou/app/services/concepts_service.py,sha256=uH0leb23Op48wc1bHlUV2-I97QROsIemKr80F0UwnoM,11213
205
205
  zou/app/services/custom_actions_service.py,sha256=fWISEOOdthadrxeHuacEel5Xj6msn0yWXJQDG1gzvsY,297
206
- zou/app/services/deletion_service.py,sha256=UPojg4oyyCKjUObfwv9nY6tD0SVDeimpv0xgbcSmyyk,17372
206
+ zou/app/services/deletion_service.py,sha256=cVo-zdjTAF23-kyed0qoRH0ICqlr1-iPYTL3T1wwrP8,17627
207
207
  zou/app/services/departments_service.py,sha256=7jLKH7ziToc6uHZsZKdBUzN2Ubi2YloHCX7PecE51LU,4596
208
208
  zou/app/services/edits_service.py,sha256=hfg6Fk9J9W41Qyw96UF35cSgbXqCYci6H8OY5rL_cIk,12075
209
- zou/app/services/emails_service.py,sha256=Vlgi0H5tBgsHVFg2sDkQMpCUgUGg6puCV0hsBaveTco,14299
210
- zou/app/services/entities_service.py,sha256=H9fMuRmrcS_ZxZ7vEo30m93UCRwXCKsIaPU7SYTgoK8,17792
209
+ zou/app/services/emails_service.py,sha256=Td68xv7zs7yojcbNaQIbM4uWzjKuyvs9p2o1SfqzFgA,14931
210
+ zou/app/services/entities_service.py,sha256=mSVoKt06pF9-7H5eD43UpfwOpSGWKjrE7F7eo0IBjw8,17788
211
211
  zou/app/services/events_service.py,sha256=Ew-bY5hqrWLmpbVj1_xd3E2S3JtyAGzdgw2XjudTZjc,2700
212
212
  zou/app/services/exception.py,sha256=VaQbvvv7yJ-lCFOlvAPKkYPuw7MjCaSAo1F40fS3kZ8,4478
213
213
  zou/app/services/file_tree_service.py,sha256=sz2Eq4S5VqEU7AupmKtRzX2W8ouUheTyYWkgiMf2G6o,33857
@@ -215,9 +215,9 @@ zou/app/services/files_service.py,sha256=df1_9v-vwE5HVi9XjtilTPWHuiWunvrwPyQh_IX
215
215
  zou/app/services/index_service.py,sha256=IaffQz_oj6GPEbuDp4a2qnSc6n-hhIcmA_LrTm3Wots,10201
216
216
  zou/app/services/names_service.py,sha256=TOSrintROmxcAlcFQE0i2E3PBLnw81GAztNselpTn18,2947
217
217
  zou/app/services/news_service.py,sha256=oXdEiSaTdkpYutm_UtmNWTDw4BPUBMFJDOEHhJ28q6w,10047
218
- zou/app/services/notifications_service.py,sha256=UAQZfUty2NR8NTRNuTu37LxdqDxVE_xo6DTb_paH0CQ,16840
219
- zou/app/services/persons_service.py,sha256=HjV-su80Y2BO9l5zoBKHMNF0mDGtkWqPhEOs3nQ3nlI,16566
220
- zou/app/services/playlists_service.py,sha256=IKjvT3RZigwXsrhsRFqaWWlm-tZSN6-jgMDf0Hn3w3Q,33179
218
+ zou/app/services/notifications_service.py,sha256=FsZ7rFv8p_4bVn0IlpW7TIo5rDQoupO7ARK9dH3Mdb0,17095
219
+ zou/app/services/persons_service.py,sha256=yPwdIIeI3INNKCqdROTpRzHNsMbxuT8RovV3gadm3I4,16435
220
+ zou/app/services/playlists_service.py,sha256=ftEP7LFyPAXogssOYi3TGZstdn2k9eq5o5FveYC25TI,33282
221
221
  zou/app/services/plugins_service.py,sha256=dbU-2f7t3eo6VISQBsASM8Or4Y8ha6Vt8ZHgtizdOi0,1917
222
222
  zou/app/services/preview_files_service.py,sha256=mxOc1bBjlA3a900QU6fUufBM1JmAzr5D8jvcf_HAkXM,36178
223
223
  zou/app/services/projects_service.py,sha256=TF9WcGLxQUzZkVIp3q4p8uALn2i0JEutvFtkJ0HvMt0,21840
@@ -229,9 +229,9 @@ zou/app/services/status_automations_service.py,sha256=tVio7Sj7inhvKS4UOyRhcdpwr_
229
229
  zou/app/services/sync_service.py,sha256=iWxx1kOGEXympHmSBBQWtDZWNtumdxp8kppee0OefMo,41811
230
230
  zou/app/services/tasks_service.py,sha256=ZLUi6_XhLU_SLwERFHx4QwcXdS-ncrVH_rIl-Lt-9yE,69993
231
231
  zou/app/services/telemetry_services.py,sha256=xQm1h1t_JxSFW59zQGf4NuNdUi1UfMa_6pQ-ytRbmGA,1029
232
- zou/app/services/template_services.py,sha256=26iK-ptX3SyPF3zj1l4dWVRRGcTHxUD990P_wUDyck4,38050
232
+ zou/app/services/templates_service.py,sha256=vz4ACtLW-NZcLB-2uisQgGvU9vGFeIL9fUEaCULX8W0,38070
233
233
  zou/app/services/time_spents_service.py,sha256=AIMDyBkVVxqUqVBqsUhwjLMxYKgHcgoSRJyKJkPORTk,18636
234
- zou/app/services/user_service.py,sha256=xLIBjiz8-JdetUev6it5LzDaqq6rd4NTT7PVdFHvE8I,53746
234
+ zou/app/services/user_service.py,sha256=LduyajzSyDPec2r3opQQPm4H7yTmNDqF3pSblSGHjmU,54028
235
235
  zou/app/stores/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
236
236
  zou/app/stores/auth_tokens_store.py,sha256=-qOJPybLHvnMOq3PWk073OW9HJwOHGhFLZeOIlX1UVw,1290
237
237
  zou/app/stores/file_store.py,sha256=yLQDM6mNbj9oe0vsWdBqun7D8Dw-eSjD1yHCCftX0OI,4045
@@ -279,7 +279,7 @@ zou/migrations/versions/05ac7e8caa21_remove_unique_constraint_for_taskstatus_.py
279
279
  zou/migrations/versions/05b7dc79a416_add_archived_fields_to_main_tables.py,sha256=jNWh_Vq5SxJ5hUeksJ5OBgibijXF3AGX4PIO1pZAhH8,1362
280
280
  zou/migrations/versions/06552e22f9e7_add_comment_updated_by.py,sha256=gPrYe6W4mF6FW3utUPeebgGMh1_KekLE483mp8PzMAE,1407
281
281
  zou/migrations/versions/0bd1e89f2a6f_add_productionscheduleversiontasklink_uc.py,sha256=LMA-qMea-93FiAAGRMU7_KGhZaKIyaCyIw7P5Ii7cAw,1582
282
- zou/migrations/versions/0c05b22194f3_add_performance_indexes.py,sha256=FTNW6FKvyiCQT4DbG5SUxJK3U5uaJ3DEG5K4IDaXwfw,1044
282
+ zou/migrations/versions/0c05b22194f3_add_performance_indexes.py,sha256=O_t7Uz4twBaRu4Tlb1t5VsOeV_7FZV9QYS4H_bj0lSA,1139
283
283
  zou/migrations/versions/0cf5e0e035fa_drop_column_tasktype_for_shots.py,sha256=9p0B2X3Zwhsyo8gq50XEFoZQq5KolhVBTVujHMRWtx4,2008
284
284
  zou/migrations/versions/0ec3762a745d_add_attachment_table.py,sha256=JDvrRgC9lfO8HH3PS1JzauEANz9TaTCGPiJj5lVuGOw,1819
285
285
  zou/migrations/versions/0ef6416a507b_.py,sha256=rT4hvfT5GwS6nCz2kco7V0kJXel-v0xndnEgYsQx1qY,1975
@@ -287,7 +287,7 @@ zou/migrations/versions/10cf267d95c9_fix_schedule_item.py,sha256=p_IDPtPGGnbu-Dq
287
287
  zou/migrations/versions/16328eae4b5f_add_new_constraint_for_timespent.py,sha256=xGq1YSvoo-6RME4sBYIZY4NVUB_qkt2hNMvA-nlJmZ0,1809
288
288
  zou/migrations/versions/16df47d76c64_add_some_indexes.py,sha256=gHwD3WuTw45hY9UTUzrThofO0Wf107mnJrPsqzkb_hA,4413
289
289
  zou/migrations/versions/17ef8f7be758_disallow_null_choicetype.py,sha256=73sljp681nQmrOvxllT-Np8hHjTrQ7CQ9hhkacHMGsg,15432
290
- zou/migrations/versions/1b0ab951adca_add_reply_id_to_attachments.py,sha256=yIZ63DGa0sjyJajER671xzZnctNYt3dyAOBP6LvPvOQ,1324
290
+ zou/migrations/versions/1b0ab951adca_add_reply_id_to_attachments.py,sha256=6VZXncWGmtuuGObjz6sbGfUPb1mbhdYzAXj8s1W82Vg,1410
291
291
  zou/migrations/versions/1bb55759146f_add_table_studio.py,sha256=U0NklqJaX_SwC_Om8rVoJno_vYkeaU69OcW6lvQr8Gc,2024
292
292
  zou/migrations/versions/1cb44194db49_add_file_size_field_to_preview_file.py,sha256=vcsHqK9OQY6V1_lVL4fJSn2T7SRMfCCuFKO6_XjtHSU,707
293
293
  zou/migrations/versions/1e150c2cea4d_add_nb_entities_out.py,sha256=3fBXfaR4Uz2amzZholso3DX4Z20EZkvKn12OrDluilk,713
@@ -348,7 +348,7 @@ zou/migrations/versions/5b980f0dc365_add_comment_links.py,sha256=rh8ZtOkPNq3_RVf
348
348
  zou/migrations/versions/5b9fd9ddfe43_add_homepage_and_contract_fields.py,sha256=KuGKtLnLiakCUaVGSXYdcpY8yp7ITTKbcq-50gkGEoE,1486
349
349
  zou/migrations/versions/5c0498e264bc_add_slack_fields.py,sha256=9SapWaTXJUdpDLlJ8HzVMp1ty773UbvcL2tAjZXRep8,729
350
350
  zou/migrations/versions/5e2ce62632a6_add_workflow_to_project.py,sha256=o-IN2JXCyU86JiSp4vURv5NNNaYv9uzdPB9sx3LtxVE,2826
351
- zou/migrations/versions/5f1620d191af_add_playlist_id_field_to_notification.py,sha256=hzlgb-2L1FTlYhPHTTRnD0CVV3gGXMW5jY7SCE8jw28,1917
351
+ zou/migrations/versions/5f1620d191af_add_playlist_id_field_to_notification.py,sha256=GzDaUP24EBzfClFc68lVoXe4QeJavIApXMGnetQfFkc,2243
352
352
  zou/migrations/versions/5f715f2b6348_add_new_table_productionscheduleversion.py,sha256=SABSJ7zXYc9ebawvwf0INZMuNn11OsfQL0YC4nUM72I,3918
353
353
  zou/migrations/versions/680c64565f9d_for_searchfiltergroup_is_shared.py,sha256=RSq0FRpHEIGKLYi6FoV2u4MFXXtuSrn5YAgdUBgeYLk,859
354
354
  zou/migrations/versions/693cc511d28d_add_taskstatus_priority.py,sha256=MpXSpaOcjiVD0a33Y5UEV-D2Ow0qro_76iVqjFjKztM,3113
@@ -359,7 +359,7 @@ zou/migrations/versions/6d1b2c60f58b_add_milestone_model.py,sha256=ohyR0slad3c2d
359
359
  zou/migrations/versions/6d7fa5a8e9a5_add_timesheets_locked_field_to_org.py,sha256=Ge-Tlsrp_4LnGS39jgx_tkrola1vFO-6bXDK1A4X8Zg,731
360
360
  zou/migrations/versions/6eeaff945706_add_data_field_on_entity_links.py,sha256=d-YgAq6Tnv3mzqGLdw2UkwSy5C_XvcEcm70_EVwFbOc,790
361
361
  zou/migrations/versions/6f6049877105_.py,sha256=c_D0FQdQuwZt3YOUpJbbJ7X9n0-t4de2V5vpzAe3nBY,947
362
- zou/migrations/versions/71d546ace0ee_add_performance_indexes.py,sha256=Mm8lgHQkOp8bENtv8yAYh0r1JWTXfY8Yrt_VQ3w98-4,851
362
+ zou/migrations/versions/71d546ace0ee_add_performance_indexes.py,sha256=ku9jz54z7v4uNA-lmhhVuVHpRIwz1WH0KogFOQ9nhKg,874
363
363
  zou/migrations/versions/7417c8eb70d8_add_for_entity_field_to_playlists.py,sha256=lYo1Co9qbdP9W5MC7oG6uhVTmx_2KOwSaLJN7IWQEDY,922
364
364
  zou/migrations/versions/772a5e43f05b_.py,sha256=w5QB_sW9FCZXBX48R-wKHKukjrlZn7fpYJMxZi3s7wE,1622
365
365
  zou/migrations/versions/7748d3d22925_add_columns_to_searchfilter_table_to_.py,sha256=gkkqHxo5qeyEx26Q961kPPKO3C8NmRqXOsqRVldxk0Y,2938
@@ -473,9 +473,9 @@ zou/remote/normalize_movie.py,sha256=zNfEY3N1UbAHZfddGONTg2Sff3ieLVWd4dfZa1dpnes
473
473
  zou/remote/playlist.py,sha256=AsDo0bgYhDcd6DfNRV6r6Jj3URWwavE2ZN3VkKRPbLU,3293
474
474
  zou/utils/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
475
475
  zou/utils/movie.py,sha256=d67fIL9dVBKt-E_qCGXRbNNdbJaJR5sHvZeX3hf8ldE,16559
476
- zou-0.20.78.dist-info/licenses/LICENSE,sha256=dql8h4yceoMhuzlcK0TT_i-NgTFNIZsgE47Q4t3dUYI,34520
477
- zou-0.20.78.dist-info/METADATA,sha256=wwEFuAVB3Fyg-IBYU_UMLHJ8xLh2733q0WPopW-kpUk,6698
478
- zou-0.20.78.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
479
- zou-0.20.78.dist-info/entry_points.txt,sha256=PelQoIx3qhQ_Tmne7wrLY-1m2izuzgpwokoURwSohy4,130
480
- zou-0.20.78.dist-info/top_level.txt,sha256=4S7G_jk4MzpToeDItHGjPhHx_fRdX52zJZWTD4SL54g,4
481
- zou-0.20.78.dist-info/RECORD,,
476
+ zou-0.20.80.dist-info/licenses/LICENSE,sha256=dql8h4yceoMhuzlcK0TT_i-NgTFNIZsgE47Q4t3dUYI,34520
477
+ zou-0.20.80.dist-info/METADATA,sha256=yaXPHW_CRPLi1FtWP7LKfqFsWO2PZfXPdOg0HvcWyZ8,6698
478
+ zou-0.20.80.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
479
+ zou-0.20.80.dist-info/entry_points.txt,sha256=PelQoIx3qhQ_Tmne7wrLY-1m2izuzgpwokoURwSohy4,130
480
+ zou-0.20.80.dist-info/top_level.txt,sha256=4S7G_jk4MzpToeDItHGjPhHx_fRdX52zJZWTD4SL54g,4
481
+ zou-0.20.80.dist-info/RECORD,,
File without changes