zou 0.20.61__py3-none-any.whl → 0.20.63__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/api.py +2 -0
- zou/app/blueprints/crud/__init__.py +25 -1
- zou/app/blueprints/crud/hardware_item.py +12 -0
- zou/app/blueprints/crud/notification.py +0 -4
- zou/app/blueprints/crud/production_schedule_version.py +114 -0
- zou/app/blueprints/departments/__init__.py +35 -0
- zou/app/blueprints/departments/resources.py +262 -0
- zou/app/blueprints/index/resources.py +2 -9
- zou/app/blueprints/persons/resources.py +1 -3
- zou/app/blueprints/projects/__init__.py +20 -0
- zou/app/blueprints/projects/resources.py +200 -0
- zou/app/blueprints/tasks/resources.py +5 -2
- zou/app/blueprints/user/resources.py +1 -3
- zou/app/models/base.py +15 -4
- zou/app/models/department.py +62 -0
- zou/app/models/entity.py +3 -3
- zou/app/models/hardware_item.py +15 -0
- zou/app/models/person.py +1 -1
- zou/app/models/production_schedule_version.py +81 -0
- zou/app/models/project.py +17 -6
- zou/app/models/software.py +5 -1
- zou/app/models/task.py +7 -10
- zou/app/services/assets_service.py +5 -6
- zou/app/services/concepts_service.py +3 -4
- zou/app/services/deletion_service.py +10 -18
- zou/app/services/departments_service.py +153 -0
- zou/app/services/edits_service.py +3 -4
- zou/app/services/entities_service.py +5 -6
- zou/app/services/exception.py +8 -0
- zou/app/services/file_tree_service.py +2 -2
- zou/app/services/preview_files_service.py +6 -2
- zou/app/services/schedule_service.py +243 -1
- zou/app/services/shots_service.py +3 -4
- zou/app/services/tasks_service.py +5 -1
- zou/app/services/user_service.py +57 -23
- zou/migrations/alembic.ini +1 -1
- zou/migrations/versions/0bd1e89f2a6f_add_productionscheduleversiontasklink_uc.py +58 -0
- zou/migrations/versions/26f96f65cfa3_add_productionversionschedule_uc.py +40 -0
- zou/migrations/versions/3d8e68dffeee_fix_task_person_link.py +56 -0
- zou/migrations/versions/4368137b44e1_productionscheduleversion_add_on_delete_.py +188 -0
- zou/migrations/versions/4bd9bfb73f11_add_fields_to_the_software_table.py +49 -0
- zou/migrations/versions/5f715f2b6348_add_new_table_productionscheduleversion.py +128 -0
- zou/migrations/versions/7a16258f2fab_add_currency_field_to_budgets.py +2 -4
- zou/migrations/versions/9683bd840dee_add_archived_field_to_software.py +33 -0
- zou/migrations/versions/9af2df17a9d5_add_hardware_table.py +48 -0
- zou/migrations/versions/ce7f46f445dc_add_column_estimation_for_.py +37 -0
- zou/migrations/versions/d5665dca188b_add_version_field_to_software.py +35 -0
- zou/migrations/versions/d97f2730bf7b_add_.py +101 -0
- zou/migrations/versions/dde6be40f54f_add_departement_links_tables_for_.py +151 -0
- zou/migrations/versions/e4b48ca33539_add_project_production_schedule_version_.py +56 -0
- zou/migrations/versions/e8bc24998b34_remove_on_delete_cascade_for_.py +59 -0
- {zou-0.20.61.dist-info → zou-0.20.63.dist-info}/METADATA +3 -3
- {zou-0.20.61.dist-info → zou-0.20.63.dist-info}/RECORD +58 -37
- {zou-0.20.61.dist-info → zou-0.20.63.dist-info}/WHEEL +0 -0
- {zou-0.20.61.dist-info → zou-0.20.63.dist-info}/entry_points.txt +0 -0
- {zou-0.20.61.dist-info → zou-0.20.63.dist-info}/licenses/LICENSE +0 -0
- {zou-0.20.61.dist-info → zou-0.20.63.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
from zou.app.models.department import (
|
|
2
|
+
Department,
|
|
3
|
+
SoftwareDepartmentLink,
|
|
4
|
+
HardwareItemDepartmentLink,
|
|
5
|
+
)
|
|
6
|
+
from zou.app.models.hardware_item import HardwareItem
|
|
7
|
+
from zou.app.models.software import Software
|
|
8
|
+
from zou.app.utils import fields
|
|
9
|
+
|
|
10
|
+
from zou.app.services.exception import (
|
|
11
|
+
DepartmentNotFoundException,
|
|
12
|
+
SoftwareNotFoundException,
|
|
13
|
+
HardwareItemNotFoundException,
|
|
14
|
+
)
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def _check_department_exists(department_id):
|
|
18
|
+
department = Department.get(department_id)
|
|
19
|
+
if not department:
|
|
20
|
+
raise DepartmentNotFoundException
|
|
21
|
+
return department
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
def _check_software_exists(software_id):
|
|
25
|
+
software = Software.get(software_id)
|
|
26
|
+
if not software:
|
|
27
|
+
raise SoftwareNotFoundException
|
|
28
|
+
return software
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
def _check_hardware_item_exists(hardware_item_id):
|
|
32
|
+
hardware_item = HardwareItem.get(hardware_item_id)
|
|
33
|
+
if not hardware_item:
|
|
34
|
+
raise HardwareItemNotFoundException
|
|
35
|
+
return hardware_item
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
def get_all_software_for_departments():
|
|
39
|
+
"""
|
|
40
|
+
Get all software items for all departments organized by department
|
|
41
|
+
in a dictionary where the key is the department id and the value is a
|
|
42
|
+
list of linked software items.
|
|
43
|
+
"""
|
|
44
|
+
software_list = (
|
|
45
|
+
Software.query.join(SoftwareDepartmentLink)
|
|
46
|
+
.add_columns(SoftwareDepartmentLink.department_id)
|
|
47
|
+
.all()
|
|
48
|
+
)
|
|
49
|
+
department_map = {}
|
|
50
|
+
for software, department_id in software_list:
|
|
51
|
+
if department_id not in department_map:
|
|
52
|
+
department_map[department_id] = []
|
|
53
|
+
department_map[department_id].append(software.serialize())
|
|
54
|
+
return department_map
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
def get_all_hardware_items_for_departments():
|
|
58
|
+
"""
|
|
59
|
+
Get all hardware items for all departments organized by department
|
|
60
|
+
in a dictionary where the key is the department id and the value is a
|
|
61
|
+
list of linked hardware items.
|
|
62
|
+
"""
|
|
63
|
+
hardware_item_list = (
|
|
64
|
+
HardwareItem.query.join(HardwareItemDepartmentLink)
|
|
65
|
+
.add_columns(HardwareItemDepartmentLink.department_id)
|
|
66
|
+
.all()
|
|
67
|
+
)
|
|
68
|
+
department_map = {}
|
|
69
|
+
for hardware_item, department_id in hardware_item_list:
|
|
70
|
+
if department_id not in department_map:
|
|
71
|
+
department_map[department_id] = []
|
|
72
|
+
department_map[department_id].append(hardware_item.serialize())
|
|
73
|
+
return department_map
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
def get_software_for_department(department_id):
|
|
77
|
+
"""
|
|
78
|
+
Get all software items for a given department.
|
|
79
|
+
"""
|
|
80
|
+
_check_department_exists(department_id)
|
|
81
|
+
return fields.serialize_models(
|
|
82
|
+
Software.query.join(SoftwareDepartmentLink)
|
|
83
|
+
.filter(SoftwareDepartmentLink.department_id == department_id)
|
|
84
|
+
.all()
|
|
85
|
+
)
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
def get_hardware_items_for_department(department_id):
|
|
89
|
+
"""
|
|
90
|
+
Get all hardware items for a given department.
|
|
91
|
+
"""
|
|
92
|
+
_check_department_exists(department_id)
|
|
93
|
+
return fields.serialize_models(
|
|
94
|
+
HardwareItem.query.join(HardwareItemDepartmentLink)
|
|
95
|
+
.filter(HardwareItemDepartmentLink.department_id == department_id)
|
|
96
|
+
.all()
|
|
97
|
+
)
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
def add_software_to_department(department_id, software_id):
|
|
101
|
+
"""
|
|
102
|
+
Add a software item to a department.
|
|
103
|
+
"""
|
|
104
|
+
_check_department_exists(department_id)
|
|
105
|
+
_check_software_exists(software_id)
|
|
106
|
+
link = SoftwareDepartmentLink.get_or_create(
|
|
107
|
+
department_id=department_id, software_id=software_id
|
|
108
|
+
)
|
|
109
|
+
return link.serialize()
|
|
110
|
+
|
|
111
|
+
|
|
112
|
+
def remove_software_from_department(department_id, software_id):
|
|
113
|
+
"""
|
|
114
|
+
Remove a software item from a department.
|
|
115
|
+
"""
|
|
116
|
+
_check_department_exists(department_id)
|
|
117
|
+
_check_software_exists(software_id)
|
|
118
|
+
link = SoftwareDepartmentLink.get_by(
|
|
119
|
+
department_id=department_id, software_id=software_id
|
|
120
|
+
)
|
|
121
|
+
if not link:
|
|
122
|
+
return None
|
|
123
|
+
else:
|
|
124
|
+
link.delete()
|
|
125
|
+
return link.serialize()
|
|
126
|
+
|
|
127
|
+
|
|
128
|
+
def add_hardware_item_to_department(department_id, hardware_item_id):
|
|
129
|
+
"""
|
|
130
|
+
Add a hardware item to a department.
|
|
131
|
+
"""
|
|
132
|
+
_check_department_exists(department_id)
|
|
133
|
+
_check_hardware_item_exists(hardware_item_id)
|
|
134
|
+
link = HardwareItemDepartmentLink.get_or_create(
|
|
135
|
+
department_id=department_id, hardware_item_id=hardware_item_id
|
|
136
|
+
)
|
|
137
|
+
return link.serialize()
|
|
138
|
+
|
|
139
|
+
|
|
140
|
+
def remove_hardware_item_from_department(department_id, hardware_item_id):
|
|
141
|
+
"""
|
|
142
|
+
Remove a hardware item from a department.
|
|
143
|
+
"""
|
|
144
|
+
_check_department_exists(department_id)
|
|
145
|
+
_check_hardware_item_exists(hardware_item_id)
|
|
146
|
+
link = HardwareItemDepartmentLink.get_by(
|
|
147
|
+
department_id=department_id, hardware_item_id=hardware_item_id
|
|
148
|
+
)
|
|
149
|
+
if not link:
|
|
150
|
+
return None
|
|
151
|
+
else:
|
|
152
|
+
link.delete()
|
|
153
|
+
return link.serialize()
|
|
@@ -16,8 +16,7 @@ from zou.app.models.entity import (
|
|
|
16
16
|
)
|
|
17
17
|
from zou.app.models.project import Project
|
|
18
18
|
from zou.app.models.subscription import Subscription
|
|
19
|
-
from zou.app.models.task import Task
|
|
20
|
-
from zou.app.models.task import assignees_table
|
|
19
|
+
from zou.app.models.task import Task, TaskPersonLink
|
|
21
20
|
|
|
22
21
|
from zou.app.services import (
|
|
23
22
|
deletion_service,
|
|
@@ -90,7 +89,7 @@ def get_edits_and_tasks(criterions={}):
|
|
|
90
89
|
Entity.query.join(Project, Entity.project_id == Project.id)
|
|
91
90
|
.outerjoin(Episode, Episode.id == Entity.parent_id)
|
|
92
91
|
.outerjoin(Task, Task.entity_id == Entity.id)
|
|
93
|
-
.outerjoin(
|
|
92
|
+
.outerjoin(TaskPersonLink)
|
|
94
93
|
.add_columns(
|
|
95
94
|
Episode.id,
|
|
96
95
|
Episode.name,
|
|
@@ -108,7 +107,7 @@ def get_edits_and_tasks(criterions={}):
|
|
|
108
107
|
Task.last_comment_date,
|
|
109
108
|
Task.last_preview_file_id,
|
|
110
109
|
Task.nb_assets_ready,
|
|
111
|
-
|
|
110
|
+
TaskPersonLink.person_id,
|
|
112
111
|
Project.id,
|
|
113
112
|
Project.name,
|
|
114
113
|
)
|
|
@@ -18,8 +18,7 @@ from zou.app.models.entity import Entity, EntityLink, EntityConceptLink
|
|
|
18
18
|
from zou.app.models.entity_type import EntityType
|
|
19
19
|
from zou.app.models.preview_file import PreviewFile
|
|
20
20
|
from zou.app.models.project import Project
|
|
21
|
-
from zou.app.models.task import
|
|
22
|
-
from zou.app.models.task import Task
|
|
21
|
+
from zou.app.models.task import Task, TaskPersonLink
|
|
23
22
|
|
|
24
23
|
from zou.app import db
|
|
25
24
|
|
|
@@ -255,7 +254,7 @@ def get_entities_and_tasks(criterions={}):
|
|
|
255
254
|
|
|
256
255
|
query = (
|
|
257
256
|
Entity.query.outerjoin(Task, Task.entity_id == Entity.id)
|
|
258
|
-
.outerjoin(
|
|
257
|
+
.outerjoin(TaskPersonLink)
|
|
259
258
|
.add_columns(
|
|
260
259
|
Task.id,
|
|
261
260
|
Task.task_type_id,
|
|
@@ -272,7 +271,7 @@ def get_entities_and_tasks(criterions={}):
|
|
|
272
271
|
Task.last_comment_date,
|
|
273
272
|
Task.last_preview_file_id,
|
|
274
273
|
Task.difficulty,
|
|
275
|
-
|
|
274
|
+
TaskPersonLink.person_id,
|
|
276
275
|
)
|
|
277
276
|
)
|
|
278
277
|
|
|
@@ -416,7 +415,7 @@ def get_linked_entities_with_tasks(entity_id):
|
|
|
416
415
|
query = (
|
|
417
416
|
Entity.query.join(Project, Project.id == Entity.project_id)
|
|
418
417
|
.outerjoin(Task, Task.entity_id == Entity.id)
|
|
419
|
-
.outerjoin(
|
|
418
|
+
.outerjoin(TaskPersonLink)
|
|
420
419
|
.join(EntityType)
|
|
421
420
|
.add_columns(
|
|
422
421
|
Task.id,
|
|
@@ -433,7 +432,7 @@ def get_linked_entities_with_tasks(entity_id):
|
|
|
433
432
|
Task.last_comment_date,
|
|
434
433
|
Task.nb_assets_ready,
|
|
435
434
|
Task.assigner_id,
|
|
436
|
-
|
|
435
|
+
TaskPersonLink.person_id,
|
|
437
436
|
Project.id,
|
|
438
437
|
Project.name,
|
|
439
438
|
EntityType.name,
|
zou/app/services/exception.py
CHANGED
|
@@ -89,6 +89,10 @@ class SoftwareNotFoundException(NotFound):
|
|
|
89
89
|
pass
|
|
90
90
|
|
|
91
91
|
|
|
92
|
+
class HardwareItemNotFoundException(NotFound):
|
|
93
|
+
pass
|
|
94
|
+
|
|
95
|
+
|
|
92
96
|
class OutputTypeNotFoundException(NotFound):
|
|
93
97
|
pass
|
|
94
98
|
|
|
@@ -287,3 +291,7 @@ class WrongTaskTypeForEntityException(Exception):
|
|
|
287
291
|
|
|
288
292
|
class IsUserLimitReachedException(Exception):
|
|
289
293
|
pass
|
|
294
|
+
|
|
295
|
+
|
|
296
|
+
class ProductionScheduleVersionNotFoundException(NotFound):
|
|
297
|
+
pass
|
|
@@ -789,7 +789,7 @@ def extract_variable_values_from_path(elements, template_elements):
|
|
|
789
789
|
continue
|
|
790
790
|
else:
|
|
791
791
|
raise WrongPathFormatException(
|
|
792
|
-
"{} doesn't match {}"
|
|
792
|
+
f"{elements} doesn't match {template_elements}"
|
|
793
793
|
)
|
|
794
794
|
|
|
795
795
|
data_type = token.group("token")
|
|
@@ -1131,7 +1131,7 @@ def guess_from_path(project_id, file_path, sep="/"):
|
|
|
1131
1131
|
# Some template_element don't have a corresponding token,
|
|
1132
1132
|
# like "05_publish" folder, for example.
|
|
1133
1133
|
for token, token_value in tokens.items():
|
|
1134
|
-
if "<{}>"
|
|
1134
|
+
if f"<{token}>" in template_element:
|
|
1135
1135
|
break
|
|
1136
1136
|
else:
|
|
1137
1137
|
continue
|
|
@@ -522,8 +522,12 @@ def _apply_annotation_deletions(annotations, deletions):
|
|
|
522
522
|
if deletion["time"] in annotation_map:
|
|
523
523
|
annotation = annotation_map[deletion["time"]]
|
|
524
524
|
deleted_object_ids = deletion.get("objects", [])
|
|
525
|
-
|
|
526
|
-
|
|
525
|
+
if "drawing" not in annotation or not isinstance(
|
|
526
|
+
annotation["drawing"], dict
|
|
527
|
+
):
|
|
528
|
+
annotation["drawing"] = {}
|
|
529
|
+
previous_objects = annotation["drawing"].get("objects", [])
|
|
530
|
+
annotation["drawing"]["objects"] = [
|
|
527
531
|
previous_object
|
|
528
532
|
for previous_object in previous_objects
|
|
529
533
|
if previous_object.get("id", "") not in deleted_object_ids
|
|
@@ -1,18 +1,43 @@
|
|
|
1
1
|
from datetime import date, timedelta
|
|
2
|
+
from sqlalchemy.exc import StatementError
|
|
3
|
+
from sqlalchemy.dialects.postgresql import insert
|
|
4
|
+
from sqlalchemy import update
|
|
5
|
+
|
|
2
6
|
|
|
3
7
|
from zou.app.models.entity import Entity
|
|
4
8
|
from zou.app.models.entity_type import EntityType
|
|
5
9
|
from zou.app.models.milestone import Milestone
|
|
6
10
|
from zou.app.models.schedule_item import ScheduleItem
|
|
7
|
-
from zou.app.
|
|
11
|
+
from zou.app.models.task import Task
|
|
12
|
+
from zou.app.models.task_type import TaskType
|
|
13
|
+
from zou.app.models.production_schedule_version import (
|
|
14
|
+
ProductionScheduleVersion,
|
|
15
|
+
ProductionScheduleVersionTaskLink,
|
|
16
|
+
)
|
|
17
|
+
from zou.app.utils import events, fields, cache
|
|
8
18
|
from zou.app.services import (
|
|
9
19
|
assets_service,
|
|
10
20
|
base_service,
|
|
11
21
|
shots_service,
|
|
12
22
|
tasks_service,
|
|
23
|
+
projects_service,
|
|
24
|
+
)
|
|
25
|
+
from zou.app import db
|
|
26
|
+
|
|
27
|
+
from zou.app.services.exception import (
|
|
28
|
+
ProductionScheduleVersionNotFoundException,
|
|
13
29
|
)
|
|
14
30
|
|
|
15
31
|
|
|
32
|
+
def clear_production_schedule_version_cache(production_schedule_version_id):
|
|
33
|
+
cache.cache.delete_memoized(
|
|
34
|
+
get_production_schedule_version, production_schedule_version_id
|
|
35
|
+
)
|
|
36
|
+
cache.cache.delete_memoized(
|
|
37
|
+
get_production_schedule_version, production_schedule_version_id, True
|
|
38
|
+
)
|
|
39
|
+
|
|
40
|
+
|
|
16
41
|
def get_schedule_items(project_id):
|
|
17
42
|
"""
|
|
18
43
|
Get all project schedule items (mainly for sync purpose).
|
|
@@ -204,3 +229,220 @@ def get_milestones_for_project(project_id):
|
|
|
204
229
|
"""
|
|
205
230
|
query = Milestone.query.filter_by(project_id=project_id)
|
|
206
231
|
return [milestone.present() for milestone in query.all()]
|
|
232
|
+
|
|
233
|
+
|
|
234
|
+
def get_production_schedule_version_raw(production_schedule_version_id):
|
|
235
|
+
"""
|
|
236
|
+
Get production schedule version matching given id.
|
|
237
|
+
"""
|
|
238
|
+
try:
|
|
239
|
+
production_schedule_version = ProductionScheduleVersion.get(
|
|
240
|
+
production_schedule_version_id
|
|
241
|
+
)
|
|
242
|
+
except StatementError:
|
|
243
|
+
raise ProductionScheduleVersionNotFoundException
|
|
244
|
+
|
|
245
|
+
if production_schedule_version is None:
|
|
246
|
+
raise ProductionScheduleVersionNotFoundException
|
|
247
|
+
|
|
248
|
+
return production_schedule_version
|
|
249
|
+
|
|
250
|
+
|
|
251
|
+
@cache.memoize_function(120)
|
|
252
|
+
def get_production_schedule_version(
|
|
253
|
+
production_schedule_version_id, relations=False
|
|
254
|
+
):
|
|
255
|
+
"""
|
|
256
|
+
Get production schedule version matching given id and serialize it.
|
|
257
|
+
"""
|
|
258
|
+
return get_production_schedule_version_raw(
|
|
259
|
+
production_schedule_version_id
|
|
260
|
+
).serialize(relations=relations)
|
|
261
|
+
|
|
262
|
+
|
|
263
|
+
def get_production_schedule_version_task_links(
|
|
264
|
+
production_schedule_version_id, task_type_id=None, relations=False
|
|
265
|
+
):
|
|
266
|
+
"""
|
|
267
|
+
Get all task links for given production schedule version.
|
|
268
|
+
"""
|
|
269
|
+
query = ProductionScheduleVersionTaskLink.query.filter_by(
|
|
270
|
+
production_schedule_version_id=production_schedule_version_id
|
|
271
|
+
)
|
|
272
|
+
|
|
273
|
+
if task_type_id is not None:
|
|
274
|
+
query = (
|
|
275
|
+
query.join(Task)
|
|
276
|
+
.join(TaskType)
|
|
277
|
+
.filter(Task.task_type_id == task_type_id)
|
|
278
|
+
)
|
|
279
|
+
|
|
280
|
+
return fields.serialize_models(query.all(), relations=relations)
|
|
281
|
+
|
|
282
|
+
|
|
283
|
+
def update_production_schedule_version(production_schedule_version_id, data):
|
|
284
|
+
"""
|
|
285
|
+
Update production schedule version with given id with data.
|
|
286
|
+
"""
|
|
287
|
+
production_schedule_version = get_production_schedule_version_raw(
|
|
288
|
+
production_schedule_version_id
|
|
289
|
+
)
|
|
290
|
+
|
|
291
|
+
production_schedule_version.update(data)
|
|
292
|
+
clear_production_schedule_version_cache(production_schedule_version_id)
|
|
293
|
+
events.emit(
|
|
294
|
+
"production_schedule_version:update",
|
|
295
|
+
{"production_schedule_version_id": production_schedule_version_id},
|
|
296
|
+
project_id=str(production_schedule_version.project_id),
|
|
297
|
+
)
|
|
298
|
+
return production_schedule_version.serialize()
|
|
299
|
+
|
|
300
|
+
|
|
301
|
+
def set_production_schedule_version_task_links_from_production(
|
|
302
|
+
production_schedule_version_id,
|
|
303
|
+
):
|
|
304
|
+
"""
|
|
305
|
+
Set task links for given production schedule version from tasks in the
|
|
306
|
+
production.
|
|
307
|
+
"""
|
|
308
|
+
production_schedule_version = get_production_schedule_version(
|
|
309
|
+
production_schedule_version_id
|
|
310
|
+
)
|
|
311
|
+
|
|
312
|
+
tasks = (
|
|
313
|
+
db.session.query(Task)
|
|
314
|
+
.filter(Task.project_id == production_schedule_version["project_id"])
|
|
315
|
+
.all()
|
|
316
|
+
)
|
|
317
|
+
|
|
318
|
+
rows = [
|
|
319
|
+
{
|
|
320
|
+
"id": fields.gen_uuid(),
|
|
321
|
+
"production_schedule_version_id": production_schedule_version_id,
|
|
322
|
+
"task_id": task.id,
|
|
323
|
+
"start_date": task.start_date,
|
|
324
|
+
"due_date": task.due_date,
|
|
325
|
+
"estimation": task.estimation,
|
|
326
|
+
}
|
|
327
|
+
for task in tasks
|
|
328
|
+
]
|
|
329
|
+
insert_stmt = insert(ProductionScheduleVersionTaskLink).values(rows)
|
|
330
|
+
insert_stmt = insert_stmt.on_conflict_do_update(
|
|
331
|
+
index_elements=["production_schedule_version_id", "task_id"],
|
|
332
|
+
set_={
|
|
333
|
+
"start_date": insert_stmt.excluded.start_date,
|
|
334
|
+
"due_date": insert_stmt.excluded.due_date,
|
|
335
|
+
"estimation": insert_stmt.excluded.estimation,
|
|
336
|
+
},
|
|
337
|
+
).returning(ProductionScheduleVersionTaskLink)
|
|
338
|
+
|
|
339
|
+
results = db.session.execute(insert_stmt).scalars().all()
|
|
340
|
+
|
|
341
|
+
tasks_map = {task.id: task for task in tasks}
|
|
342
|
+
|
|
343
|
+
for task_link in results:
|
|
344
|
+
task_link.assignees = tasks_map[task_link.task_id].assignees
|
|
345
|
+
|
|
346
|
+
db.session.commit()
|
|
347
|
+
|
|
348
|
+
return fields.serialize_models(results, relations=True)
|
|
349
|
+
|
|
350
|
+
|
|
351
|
+
def set_production_schedule_version_task_links_from_production_schedule_version(
|
|
352
|
+
production_schedule_version_id, other_production_schedule_version_id
|
|
353
|
+
):
|
|
354
|
+
"""
|
|
355
|
+
Set task links for given production schedule version from another.
|
|
356
|
+
"""
|
|
357
|
+
|
|
358
|
+
other_links = (
|
|
359
|
+
db.session.query(ProductionScheduleVersionTaskLink)
|
|
360
|
+
.filter(
|
|
361
|
+
ProductionScheduleVersionTaskLink.production_schedule_version_id
|
|
362
|
+
== other_production_schedule_version_id
|
|
363
|
+
)
|
|
364
|
+
.all()
|
|
365
|
+
)
|
|
366
|
+
|
|
367
|
+
rows = [
|
|
368
|
+
{
|
|
369
|
+
"id": fields.gen_uuid(),
|
|
370
|
+
"production_schedule_version_id": production_schedule_version_id,
|
|
371
|
+
"task_id": links.task_id,
|
|
372
|
+
"start_date": links.start_date,
|
|
373
|
+
"due_date": links.due_date,
|
|
374
|
+
"estimation": links.estimation,
|
|
375
|
+
}
|
|
376
|
+
for links in other_links
|
|
377
|
+
]
|
|
378
|
+
|
|
379
|
+
insert_stmt = insert(ProductionScheduleVersionTaskLink).values(rows)
|
|
380
|
+
insert_stmt = insert_stmt.on_conflict_do_update(
|
|
381
|
+
index_elements=["production_schedule_version_id", "task_id"],
|
|
382
|
+
set_={
|
|
383
|
+
"start_date": insert_stmt.excluded.start_date,
|
|
384
|
+
"due_date": insert_stmt.excluded.due_date,
|
|
385
|
+
"estimation": insert_stmt.excluded.estimation,
|
|
386
|
+
},
|
|
387
|
+
).returning(ProductionScheduleVersionTaskLink)
|
|
388
|
+
|
|
389
|
+
results = db.session.execute(insert_stmt).scalars().all()
|
|
390
|
+
|
|
391
|
+
tasks_map = {link.task_id: link for link in other_links}
|
|
392
|
+
|
|
393
|
+
for task_link in results:
|
|
394
|
+
task_link.assignees = tasks_map[task_link.task_id].assignees
|
|
395
|
+
|
|
396
|
+
db.session.commit()
|
|
397
|
+
|
|
398
|
+
update_production_schedule_version(
|
|
399
|
+
production_schedule_version_id,
|
|
400
|
+
{"production_schedule_from": other_production_schedule_version_id},
|
|
401
|
+
)
|
|
402
|
+
|
|
403
|
+
return fields.serialize_models(results, relations=True)
|
|
404
|
+
|
|
405
|
+
|
|
406
|
+
def apply_production_schedule_version_to_production(
|
|
407
|
+
production_schedule_version_id,
|
|
408
|
+
):
|
|
409
|
+
"""
|
|
410
|
+
Apply production schedule version to production.
|
|
411
|
+
"""
|
|
412
|
+
|
|
413
|
+
stmt = (
|
|
414
|
+
update(Task)
|
|
415
|
+
.values(
|
|
416
|
+
start_date=ProductionScheduleVersionTaskLink.start_date,
|
|
417
|
+
due_date=ProductionScheduleVersionTaskLink.due_date,
|
|
418
|
+
estimation=ProductionScheduleVersionTaskLink.estimation,
|
|
419
|
+
)
|
|
420
|
+
.where(Task.id == ProductionScheduleVersionTaskLink.task_id)
|
|
421
|
+
.where(
|
|
422
|
+
ProductionScheduleVersionTaskLink.production_schedule_version_id
|
|
423
|
+
== production_schedule_version_id
|
|
424
|
+
)
|
|
425
|
+
.returning(Task)
|
|
426
|
+
)
|
|
427
|
+
|
|
428
|
+
results = db.session.execute(stmt).scalars().all()
|
|
429
|
+
|
|
430
|
+
db.session.commit()
|
|
431
|
+
|
|
432
|
+
for task in results:
|
|
433
|
+
events.emit(
|
|
434
|
+
"task:update",
|
|
435
|
+
{"task_id": str(task.id)},
|
|
436
|
+
project_id=str(task.project_id),
|
|
437
|
+
)
|
|
438
|
+
|
|
439
|
+
production_schedule_version = update_production_schedule_version(
|
|
440
|
+
production_schedule_version_id, {"locked": True}
|
|
441
|
+
)
|
|
442
|
+
|
|
443
|
+
projects_service.update_project(
|
|
444
|
+
production_schedule_version["project_id"],
|
|
445
|
+
{"from_schedule_version_id": production_schedule_version_id},
|
|
446
|
+
)
|
|
447
|
+
|
|
448
|
+
return fields.serialize_models(results, relations=True)
|
|
@@ -23,8 +23,7 @@ from zou.app.models.project import Project
|
|
|
23
23
|
from zou.app.models.preview_file import PreviewFile
|
|
24
24
|
from zou.app.models.schedule_item import ScheduleItem
|
|
25
25
|
from zou.app.models.subscription import Subscription
|
|
26
|
-
from zou.app.models.task import Task
|
|
27
|
-
from zou.app.models.task import assignees_table
|
|
26
|
+
from zou.app.models.task import Task, TaskPersonLink
|
|
28
27
|
from zou.app.models.time_spent import TimeSpent
|
|
29
28
|
|
|
30
29
|
from zou.app.services import (
|
|
@@ -234,7 +233,7 @@ def get_shots_and_tasks(criterions={}):
|
|
|
234
233
|
.join(Sequence, Sequence.id == Entity.parent_id)
|
|
235
234
|
.outerjoin(Episode, Episode.id == Sequence.parent_id)
|
|
236
235
|
.outerjoin(Task, Task.entity_id == Entity.id)
|
|
237
|
-
.outerjoin(
|
|
236
|
+
.outerjoin(TaskPersonLink)
|
|
238
237
|
.add_columns(
|
|
239
238
|
Episode.name,
|
|
240
239
|
Episode.id,
|
|
@@ -257,7 +256,7 @@ def get_shots_and_tasks(criterions={}):
|
|
|
257
256
|
Task.nb_assets_ready,
|
|
258
257
|
Task.difficulty,
|
|
259
258
|
Task.nb_drawings,
|
|
260
|
-
|
|
259
|
+
TaskPersonLink.person_id,
|
|
261
260
|
Project.id,
|
|
262
261
|
Project.name,
|
|
263
262
|
)
|
|
@@ -1846,7 +1846,7 @@ def reset_task_data(task_id):
|
|
|
1846
1846
|
return task.serialize(relations=True)
|
|
1847
1847
|
|
|
1848
1848
|
|
|
1849
|
-
def get_persons_tasks_dates():
|
|
1849
|
+
def get_persons_tasks_dates(project_id=None):
|
|
1850
1850
|
"""
|
|
1851
1851
|
For schedule usages, for each active person, it returns the first start
|
|
1852
1852
|
date of all tasks of assigned to this person and the last end date.
|
|
@@ -1861,6 +1861,10 @@ def get_persons_tasks_dates():
|
|
|
1861
1861
|
.group_by(Person.id)
|
|
1862
1862
|
.join(Task.assignees)
|
|
1863
1863
|
)
|
|
1864
|
+
|
|
1865
|
+
if project_id is not None:
|
|
1866
|
+
query = query.filter(Task.project_id == project_id)
|
|
1867
|
+
|
|
1864
1868
|
return [
|
|
1865
1869
|
{
|
|
1866
1870
|
"person_id": str(person_id),
|