Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Sync: Folder and Task statuses #147

Merged
merged 4 commits into from
Oct 16, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion service_tools/requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ colorama==0.4.6
ftrack-python-api==2.3.3
future==0.18.2
idna==3.4
ayon-python-api==1.0.0rc3
ayon-python-api==1.0.10
pydantic==1.10.2
pyparsing==2.4.7
python-dateutil==2.8.2
Expand Down
2 changes: 1 addition & 1 deletion services/leecher/pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ authors = ["Ynput s.r.o. <[email protected]>"]

[tool.poetry.dependencies]
python = ">=3.9,<3.10"
ayon-python-api = "1.0.0"
ayon-python-api = "1.0.10"
ftrack-python-api = "2.3.3"

[tool.poetry.dev-dependencies]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,6 @@ class SyncProcess:
interest_base_types = ["show", "task"]
ignore_ent_types = ["Milestone"]
ignore_change_keys = [
"statusid",
"thumbid",
"priorityid",
]
Expand Down Expand Up @@ -118,6 +117,7 @@ def __init__(self, event_handler, session, event, log):
self._ft_std_cust_attrs = None
self._ft_object_type_name_by_id = None
self._ft_task_type_name_by_id = None
self._ft_status_names_by_id = None

self._created_entity_by_ftrack_id = {}
self._hierarchy_changed_by_ftrack_id = {}
Expand Down Expand Up @@ -409,6 +409,16 @@ def ft_task_type_name_by_id(self):
}
return self._ft_task_type_name_by_id

@property
def ft_status_names_by_id(self):
if self._ft_status_names_by_id is None:
statuses = self.session.query("select id, name from Status").all()
self._ft_status_names_by_id = {
statuse["id"]: statuse["name"]
for statuse in statuses
}
return self._ft_status_names_by_id

@property
def has_valid_entity_types(self):
return self._has_valid_entity_types
Expand Down Expand Up @@ -1256,6 +1266,27 @@ def _update_project_task_types(self):

project_entity.task_types = new_task_types

def _update_project_statuses(self):
# TODO implement statuses sync to AYON project
return
project_entity = self.entity_hub.project_entity
src_statuses = {
statuse.name.lower(): statuse
for statuse in project_entity.statuses
}
new_statuses = []
project_schema = self.ft_project["project_schema"]
for task_type in project_schema["task_type_schema"]["types"]:
status_name = task_type["name"]
ayon_status = src_statuses.get(status_name.lower())
if ayon_status is None:
new_statuses.append({
"name": status_name,
"color": task_type["color"]
})

project_entity.statuses = new_statuses

def _propagate_task_type_changes(self, task_type_changes):
if not task_type_changes:
return
Expand Down Expand Up @@ -1289,6 +1320,44 @@ def _propagate_task_type_changes(self, task_type_changes):
self.log.debug(
f"Changed task type {prev_task_type} -> {new_type_name}")

def _propagate_status_changes(self, status_changes):
if not status_changes:
return

project_entity = self.entity_hub.project_entity
ayon_statuses_by_name = {
status.name.lower(): status
for status in project_entity.statuses
}
ft_status_names_by_id = self.ft_status_names_by_id
to_change = []
project_need_update = False
for ftrack_id, (entity, info) in status_changes.items():
new_status_id = info["changes"]["statusid"]["new"]
new_status_name = ft_status_names_by_id[new_status_id]
if entity.status.lower() == new_status_name.lower():
continue

ayon_status = ayon_statuses_by_name.get(new_status_name.lower())
if (
ayon_status is None
or entity.entity_type not in ayon_status.scope
):
# TODO implement statuses sync to AYON project
continue
project_need_update = True

to_change.append((entity, new_status_name))

if project_need_update:
self._update_project_statuses()

for entity, new_status_name in to_change:
prev_status_name = entity.status
entity.status = new_status_name
self.log.debug(
f"Changed status {prev_status_name} -> {new_status_name}")

def _propagate_attrib_changes(self):
std_cust_attr = self.ft_std_cust_attrs
hier_cust_attr = self.ft_hier_cust_attrs
Expand All @@ -1298,6 +1367,7 @@ def _propagate_attrib_changes(self):
# set all attributes from ftrack
created_ftrack_ids = set(self._created_entity_by_ftrack_id.keys())
task_type_changes = {}
status_changes = {}
for ftrack_id, info in self.entities_by_action["update"].items():
if ftrack_id in created_ftrack_ids:
continue
Expand Down Expand Up @@ -1327,8 +1397,14 @@ def _propagate_attrib_changes(self):

for key, change_info in info["changes"].items():
value = change_info["new"]
if key == "typeid" and entity.entity_type == "task":
task_type_changes[ftrack_id] = (entity, info)
if key == "typeid":
if entity.entity_type == "task":
task_type_changes[ftrack_id] = (entity, info)
continue

if key == "statusid":
status_changes[ftrack_id] = (entity, info)
continue

default_attr_key = DEFAULT_ATTRS_MAPPING.get(key)

Expand Down Expand Up @@ -1365,6 +1441,7 @@ def _propagate_attrib_changes(self):
entity.attribs[dst_key] = value

self._propagate_task_type_changes(task_type_changes)
self._propagate_status_changes(status_changes)

def _create_ft_attr_operation(
self, conf_id, entity_id, is_new, new_value, old_value=None
Expand Down
48 changes: 45 additions & 3 deletions services/processor/processor/lib/sync_from_ftrack.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,15 @@
import collections
import time
import logging
from typing import Any, Dict

import arrow
from ayon_api import (
get_project,
create_project,
slugify_string,
)
from ayon_api.entity_hub import EntityHub
from ayon_api.entity_hub import EntityHub, BaseEntity
import ftrack_api
from ftrack_common import (
CUST_ATTR_KEY_SERVER_ID,
Expand Down Expand Up @@ -211,7 +212,9 @@ def sync_to_server(self):
self._report_items.extend([
{
"type": "label",
"value": "## Project does not exist in AYON"
"value": (
f"## Project '{project_name}' does not exist in AYON"
)
},
{
"type": "label",
Expand Down Expand Up @@ -310,7 +313,7 @@ def sync_to_server(self):
self.log.info("Querying project hierarchy from ftrack")
ft_entities = ft_session.query((
"select id, name, parent_id, type_id, object_type_id, status_id"
", start_date, end_date, description" #, bid, status_id"
", start_date, end_date, description, status_id"
" from TypedContext where project_id is \"{}\""
).format(ft_project["id"])).all()
t_ft_entities_4 = time.perf_counter()
Expand Down Expand Up @@ -686,9 +689,44 @@ def _add_children_to_queue(ft_entity_id):
for child in entity.children:
deactivate_queue.append(child)

def _set_entity_status(
self,
ft_entity: ftrack_api.entity.base.Entity,
entity: BaseEntity,
ftrack_statuses: Dict[str, str],
ayon_statuses: Dict[str, Any],
):
# QUESTION should we log all invalid/missing statuses?
# QUESTION should we update AYON project statuses if status
# is not available?
if entity.entity_type not in ("folder", "task"):
return

ft_status_name = ftrack_statuses.get(ft_entity.get("status_id"))
if ft_status_name is None:
return

ayon_status = ayon_statuses.get(ft_status_name.lower())
if ayon_status is None:
return

scope = ayon_status.scope
if entity.entity_type in scope:
entity.set_status(ayon_status["name"])

def update_attributes_from_ftrack(
self, cust_attr_value_by_entity_id, ft_entities_by_id
):
ftrack_statuses = {
status["id"]: status["name"]
for status in self._ft_session.query(
"select id, name from Status"
).all()
}
ayon_statuses = {
status["name"].lower(): status
for status in self._entity_hub.project_entity["statuses"]
}
hierarchy_queue = collections.deque()
hierarchy_queue.append(self._entity_hub.project_entity)
while hierarchy_queue:
Expand All @@ -710,6 +748,10 @@ def update_attributes_from_ftrack(
entity.attribs[FTRACK_ID_ATTRIB] = ftrack_id
entity.attribs[FTRACK_PATH_ATTRIB] = path

self._set_entity_status(
ft_entity, entity, ftrack_statuses, ayon_statuses
)

for attr_name, value in (
("startDate", ft_entity["start_date"]),
("endDate", ft_entity["end_date"]),
Expand Down
2 changes: 1 addition & 1 deletion services/processor/pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ authors = ["Ynput s.r.o. <[email protected]>"]

[tool.poetry.dependencies]
python = ">=3.9,<3.10"
ayon-python-api = "1.0.0"
ayon-python-api = "1.0.10"
ftrack-python-api = "2.3.3"

[tool.poetry.dev-dependencies]
Expand Down