From 4fd6083878420e8246dbd57e2c317957a414ac7c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fabi=C3=A0=20Serra=20Arrizabalaga?= Date: Thu, 6 Jun 2024 18:36:27 +0200 Subject: [PATCH 01/19] Add support to sync task assignees and statuses for all entities (including versions) --- services/leecher/leecher/listener.py | 6 +- services/processor/processor/processor.py | 6 ++ .../ayon_shotgrid_hub/__init__.py | 4 +- .../match_ayon_hierarchy_in_shotgrid.py | 8 +- .../match_shotgrid_hierarchy_in_ayon.py | 48 ++++++--- .../ayon_shotgrid_hub/update_from_ayon.py | 6 +- .../ayon_shotgrid_hub/update_from_shotgrid.py | 91 +++++++++++++--- services/shotgrid_common/constants.py | 1 + services/shotgrid_common/utils.py | 101 +++++++++++++++--- .../transmitter/transmitter/transmitter.py | 7 ++ 10 files changed, 233 insertions(+), 45 deletions(-) diff --git a/services/leecher/leecher/listener.py b/services/leecher/leecher/listener.py index 3874577b..7f1bf262 100644 --- a/services/leecher/leecher/listener.py +++ b/services/leecher/leecher/listener.py @@ -76,10 +76,10 @@ def __init__(self): } self.custom_sg_attribs = set(self.custom_attribs_map.values()) - # TODO: implement a way to handle status_list and tags self.custom_attribs_map.update({ - # "status": "status_list", - "tags": "tags" + "status": "status_list", + "tags": "tags", + "assignees": "task_assignees" }) self.sg_enabled_entities = self.settings["compatibility_settings"]["shotgrid_enabled_entities"] # noqa: E501 diff --git a/services/processor/processor/processor.py b/services/processor/processor/processor.py index f8e05bce..95bf04c2 100644 --- a/services/processor/processor/processor.py +++ b/services/processor/processor/processor.py @@ -91,6 +91,12 @@ def __init__(self): for attr in self.settings["compatibility_settings"]["custom_attribs_map"] if attr["sg"] } + self.custom_attribs_map.update({ + "status": "status_list", + "tags": "tags", + "assignees": "task_assignees" + }) + self.custom_attribs_types = { attr["sg"]: (attr["type"], attr["scope"]) for attr in self.settings["compatibility_settings"]["custom_attribs_map"] diff --git a/services/shotgrid_common/ayon_shotgrid_hub/__init__.py b/services/shotgrid_common/ayon_shotgrid_hub/__init__.py index 680d6dab..13496197 100644 --- a/services/shotgrid_common/ayon_shotgrid_hub/__init__.py +++ b/services/shotgrid_common/ayon_shotgrid_hub/__init__.py @@ -70,7 +70,8 @@ class AyonShotgridHub: log = get_logger(__file__) custom_attribs_map = { "status": "status_list", - "tags": "tags" + "tags": "tags", + "assignees": "task_assignees" } def __init__(self, @@ -395,6 +396,7 @@ def react_to_ayon_event(self, ayon_event): ayon_event, self._sg, self._ay_project, + self.custom_attribs_map, ) case "entity.task.attrib_changed" | "entity.folder.attrib_changed": attrib_key = next(iter(ayon_event["payload"]["newValue"])) diff --git a/services/shotgrid_common/ayon_shotgrid_hub/match_ayon_hierarchy_in_shotgrid.py b/services/shotgrid_common/ayon_shotgrid_hub/match_ayon_hierarchy_in_shotgrid.py index ee691aa0..d9a71213 100644 --- a/services/shotgrid_common/ayon_shotgrid_hub/match_ayon_hierarchy_in_shotgrid.py +++ b/services/shotgrid_common/ayon_shotgrid_hub/match_ayon_hierarchy_in_shotgrid.py @@ -192,7 +192,9 @@ def match_ayon_hierarchy_in_shotgrid( ) # entity was not synced before and need to be created - if not sg_entity_id or not sg_ay_dict: + # We only create new entities for Folders/Tasks entities + # For Version entities we only try update the status if it already exists + if sg_entity_type != "Version" and (not sg_entity_id or not sg_ay_dict): sg_parent_entity = sg_session.find_one( sg_ay_parent_entity["attribs"][SHOTGRID_TYPE_ATTRIB], filters=[[ @@ -214,6 +216,10 @@ def match_ayon_hierarchy_in_shotgrid( sg_ay_dicts[sg_entity_id] = sg_ay_dict sg_ay_dicts_parents[sg_parent_entity["id"]].add(sg_entity_id) + if not sg_ay_dict: + log.warning(f"AYON entity {ay_entity} not found in SG, ignoring it") + continue + # add Shotgrid ID and type to AYON entity ay_entity.attribs.set( SHOTGRID_ID_ATTRIB, diff --git a/services/shotgrid_common/ayon_shotgrid_hub/match_shotgrid_hierarchy_in_ayon.py b/services/shotgrid_common/ayon_shotgrid_hub/match_shotgrid_hierarchy_in_ayon.py index fa125232..b52b59a2 100644 --- a/services/shotgrid_common/ayon_shotgrid_hub/match_shotgrid_hierarchy_in_ayon.py +++ b/services/shotgrid_common/ayon_shotgrid_hub/match_shotgrid_hierarchy_in_ayon.py @@ -98,7 +98,8 @@ def match_shotgrid_hierarchy_in_ayon( # If we haven't found the ay_entity by its id, check by its name # to avoid creating duplicates and erroring out if ay_entity is None: - name = slugify_string(sg_ay_dict["name"]) + # Use min_length=0 so names like '_edit_shot' don't become 'edit_shot' + name = slugify_string(sg_ay_dict["name"], min_length=0) for child in ay_parent_entity.children: if child.name.lower() == name.lower(): ay_entity = child @@ -113,7 +114,9 @@ def match_shotgrid_hierarchy_in_ayon( sg_ay_dict ) - if not ay_entity: + # We only create new entities for Folders/Tasks entities + # For Version entities we only try update the status if it already exists + if not ay_entity and sg_ay_dict["type"] != "version": ay_entity = _create_new_entity( entity_hub, ay_parent_entity, @@ -135,7 +138,10 @@ def match_shotgrid_hierarchy_in_ayon( sg_project_sync_status = "Failed" else: update_ay_entity_custom_attributes( - ay_entity, sg_ay_dict, custom_attribs_map + ay_entity, + sg_ay_dict, + custom_attribs_map, + ay_project=entity_hub.project_entity ) # skip if no ay_entity is found @@ -258,7 +264,7 @@ def _create_new_entity( attribs=sg_ay_dict["attribs"], data=sg_ay_dict["data"], ) - else: + elif sg_ay_dict["type"].lower() == "folder": ay_entity = entity_hub.add_new_folder( sg_ay_dict["folder_type"], name=sg_ay_dict["name"], @@ -269,19 +275,37 @@ def _create_new_entity( data=sg_ay_dict["data"], ) - # TODO: this doesn't work yet - status = sg_ay_dict["attribs"].get("status") + status = sg_ay_dict.get("status") if status: - # TODO: Implement status update + # Entity hub expects the statuses to be provided with the `name` and + # not the `short_name` (which is what we get from SG) so we convert + # the short name back to the long name before setting it + status_mapping = { + status.short_name: status.name for status in entity_hub.project_entity.statuses + } + new_status_name = status_mapping.get(status) + if not new_status_name: + log.warning( + "Status with short name '%s' doesn't exist in project", status + ) + else: + try: + # INFO: it was causing error so trying to set status directly + ay_entity.status = new_status_name + except ValueError as e: + # `ValueError: Status ip is not available on project.` + # NOTE: this doesn't really raise exception? + log.warning(f"Status sync not implemented: {e}") + + assignees = sg_ay_dict.get("assignees") + if assignees: try: # INFO: it was causing error so trying to set status directly - ay_entity.status = status + ay_entity.assignees = assignees except ValueError as e: - # `ValueError: Status ip is not available on project.` - # log.warning(f"Status sync not implemented: {e}") - pass + log.warning(f"Assignees sync not implemented: {e}") - tags = sg_ay_dict["attribs"].get("tags") + tags = sg_ay_dict.get("tags") if tags: ay_entity.tags = [tag["name"] for tag in tags] diff --git a/services/shotgrid_common/ayon_shotgrid_hub/update_from_ayon.py b/services/shotgrid_common/ayon_shotgrid_hub/update_from_ayon.py index 56de6067..e262cc99 100644 --- a/services/shotgrid_common/ayon_shotgrid_hub/update_from_ayon.py +++ b/services/shotgrid_common/ayon_shotgrid_hub/update_from_ayon.py @@ -386,6 +386,7 @@ def _create_sg_entity( or ay_entity.entity_type != "task" and ay_entity.folder_type == "AssetCategory" ): + # TODO: why not?? # AssetCategory should not be created in Shotgrid # task should not be child of AssetCategory return @@ -410,7 +411,7 @@ def _create_sg_entity( sg_field_name: ay_entity.name, CUST_FIELD_CODE_ID: ay_entity.id, } - else: + elif ay_entity.entity_type == "folder": data = { "project": sg_project, parent_field: { @@ -421,6 +422,9 @@ def _create_sg_entity( CUST_FIELD_CODE_ID: ay_entity.id, } + if not data: + return + # Fill up data with any extra attributes from Ayon we want to sync to SG data.update(get_sg_custom_attributes_data( sg_session, diff --git a/services/shotgrid_common/ayon_shotgrid_hub/update_from_shotgrid.py b/services/shotgrid_common/ayon_shotgrid_hub/update_from_shotgrid.py index e342debb..da2c7a48 100644 --- a/services/shotgrid_common/ayon_shotgrid_hub/update_from_shotgrid.py +++ b/services/shotgrid_common/ayon_shotgrid_hub/update_from_shotgrid.py @@ -111,14 +111,15 @@ def create_ay_entity_from_sg_event( log.debug("ShotGrid Entity exists in AYON.") # Ensure Ayon Entity has the correct ShotGrid ID ayon_entity_sg_id = str( - ay_entity.attribs.get_attribute(SHOTGRID_ID_ATTRIB).value) - # Ensure Ayon Entity has the correct Shotgrid ID - ay_shotgrid_id = str( - sg_ay_dict["attribs"].get(SHOTGRID_ID_ATTRIB, "")) - if ayon_entity_sg_id != ay_shotgrid_id: + ay_entity.attribs.get(SHOTGRID_ID_ATTRIB, "") + ) + sg_entity_sg_id = str( + sg_ay_dict["attribs"].get(SHOTGRID_ID_ATTRIB, "") + ) + if ayon_entity_sg_id != sg_entity_sg_id: ay_entity.attribs.set( SHOTGRID_ID_ATTRIB, - ay_shotgrid_id + sg_entity_sg_id ) ay_entity.attribs.set( SHOTGRID_TYPE_ATTRIB, @@ -126,7 +127,10 @@ def create_ay_entity_from_sg_event( ) update_ay_entity_custom_attributes( - ay_entity, sg_ay_dict, custom_attribs_map + ay_entity, + sg_ay_dict, + custom_attribs_map, + ay_project=ayon_entity_hub.project_entity ) return ay_entity @@ -184,7 +188,7 @@ def create_ay_entity_from_sg_event( parent_id=ay_parent_entity.id, attribs=sg_ay_dict["attribs"] ) - else: + elif sg_ay_dict["type"].lower() == "folder": ay_entity = ayon_entity_hub.add_new_folder( sg_ay_dict["folder_type"], name=sg_ay_dict["name"], @@ -194,6 +198,9 @@ def create_ay_entity_from_sg_event( attribs=sg_ay_dict["attribs"] ) + if not ay_entity: + return + log.debug(f"Created new AYON entity: {ay_entity}") ay_entity.attribs.set( SHOTGRID_ID_ATTRIB, @@ -204,6 +211,45 @@ def create_ay_entity_from_sg_event( sg_ay_dict["attribs"].get(SHOTGRID_TYPE_ATTRIB, "") ) + status = sg_ay_dict.get("status") + if status: + # Entity hub expects the statuses to be provided with the `name` and + # not the `short_name` (which is what we get from SG) so we convert + # the short name back to the long name before setting it + status_mapping = { + status.short_name: status.name + for status in ayon_entity_hub.project_entity.statuses + } + new_status_name = status_mapping.get(status) + if not new_status_name: + log.warning( + "Status with short name '%s' doesn't exist in project", status + ) + else: + try: + # INFO: it was causing error so trying to set status directly + ay_entity.status = new_status_name + except ValueError as e: + # `ValueError: Status ip is not available on project.` + # NOTE: this doesn't really raise exception? + log.warning(f"Status sync not implemented: {e}") + + assignees = sg_ay_dict.get("assignees") + if assignees: + try: + # INFO: it was causing error so trying to set status directly + ay_entity.assignees = assignees + except ValueError as e: + log.warning(f"Assignees sync not implemented: {e}") + + tags = sg_ay_dict.get("tags") + if tags: + try: + # INFO: it was causing error so trying to set status directly + ay_entity.tags = [tag["name"] for tag in tags] + except ValueError as e: + log.warning(f"Tags sync not implemented: {e}") + try: ayon_entity_hub.commit_changes() @@ -295,13 +341,17 @@ def update_ayon_entity_from_sg_event( # Ensure Ayon Entity has the correct ShotGrid ID ayon_entity_sg_id = str( - ay_entity.attribs.get_attribute(SHOTGRID_ID_ATTRIB).value) + ay_entity.attribs.get(SHOTGRID_ID_ATTRIB, "") + ) sg_entity_sg_id = str( sg_ay_dict["attribs"].get(SHOTGRID_ID_ATTRIB, "") ) log.debug(f"Updating Ayon Entity: {ay_entity.name}") - if ayon_entity_sg_id != sg_entity_sg_id: + # We need to check for existence in `ayon_entity_sg_id` as it could be + # that it's a new entity and it doesn't have a ShotGrid ID yet. + if ayon_entity_sg_id and ayon_entity_sg_id != sg_entity_sg_id: + log.error("Mismatching ShotGrid IDs, aborting...") raise ValueError("Mismatching ShotGrid IDs, aborting...") ay_entity.name = sg_ay_dict["name"] @@ -309,7 +359,10 @@ def update_ayon_entity_from_sg_event( # TODO: Only update the updated fields in the event update_ay_entity_custom_attributes( - ay_entity, sg_ay_dict, custom_attribs_map + ay_entity, + sg_ay_dict, + custom_attribs_map, + ay_project=ayon_entity_hub.project_entity ) ayon_entity_hub.commit_changes() @@ -322,6 +375,15 @@ def update_ayon_entity_from_sg_event( CUST_FIELD_CODE_ID: ay_entity.id } ) + + ay_entity.attribs.set( + SHOTGRID_ID_ATTRIB, + sg_ay_dict["attribs"].get(SHOTGRID_ID_ATTRIB, "") + ) + ay_entity.attribs.set( + SHOTGRID_TYPE_ATTRIB, + sg_ay_dict["attribs"].get(SHOTGRID_TYPE_ATTRIB, "") + ) return ay_entity @@ -342,9 +404,10 @@ def remove_ayon_entity_from_sg_event( """ # for now we are ignoring Task type entities # TODO: Handle Task entities - if sg_event["entity_type"] == "Task": - log.info("Ignoring Task entity.") - return + # Why? + # if sg_event["entity_type"] == "Task": + # log.info("Ignoring Task entity.") + # return sg_ay_dict = get_sg_entity_as_ay_dict( sg_session, diff --git a/services/shotgrid_common/constants.py b/services/shotgrid_common/constants.py index 54157b5f..518e8cf3 100644 --- a/services/shotgrid_common/constants.py +++ b/services/shotgrid_common/constants.py @@ -129,6 +129,7 @@ "content", "entity", "step", + "task_assignees", CUST_FIELD_CODE_ID, CUST_FIELD_CODE_SYNC, ] diff --git a/services/shotgrid_common/utils.py b/services/shotgrid_common/utils.py index 5b085d9e..db739d78 100644 --- a/services/shotgrid_common/utils.py +++ b/services/shotgrid_common/utils.py @@ -92,6 +92,7 @@ def _sg_to_ay_dict( ay_entity_type = "folder" task_type = None folder_type = None + exception_attribs = {"status", "assignees", "tags"} if sg_entity["type"] == "Task": ay_entity_type = "task" @@ -111,10 +112,14 @@ def _sg_to_ay_dict( name = slugify_string(task_type) elif sg_entity["type"] == "Project": - name = slugify_string(sg_entity[project_code_field]) + name = slugify_string(sg_entity[project_code_field], min_length=0) label = sg_entity[project_code_field] + elif sg_entity["type"] == "Version": + ay_entity_type = "version" + name = slugify_string(sg_entity["code"], min_length=0) + label = sg_entity["code"] else: - name = slugify_string(sg_entity["code"]) + name = slugify_string(sg_entity["code"], min_length=0) label = sg_entity["code"] folder_type = sg_entity["type"] @@ -146,8 +151,11 @@ def _sg_to_ay_dict( # If no value in SG entity skip if sg_value is None: continue - - sg_ay_dict["attribs"][ay_attrib] = sg_value + + if ay_attrib in exception_attribs: + sg_ay_dict[ay_attrib] = sg_value + else: + sg_ay_dict["attribs"][ay_attrib] = sg_value if task_type: sg_ay_dict["task_type"] = task_type @@ -584,8 +592,7 @@ def get_sg_entities( if not project_code_field: project_code_field = "code" - # TODO: Add support to sync versions too - entities_to_ignore = ["Version"] + entities_to_ignore = [] sg_ay_dicts = { sg_project["id"]: _sg_to_ay_dict( @@ -652,6 +659,20 @@ def get_sg_entities( parent_id = cat_ent_name + # Transform task_assignees list of dictionary entries + # to just a list of the login names as used in AYON DB + # so it's easier later to set + task_assignees = sg_entity.get("task_assignees") + if task_assignees: + task_assignees_list = [] + for assignee in task_assignees: + sg_user = get_sg_user_by_id( + sg_session, assignee["id"], extra_fields=["login"] + ) + task_assignees_list.append(sg_user["login"]) + + sg_entity["task_assignees"] = task_assignees_list + sg_ay_dict = _sg_to_ay_dict( sg_entity, project_code_field, @@ -714,6 +735,20 @@ def get_sg_entity_as_ay_dict( if not sg_entity: return {} + + # Transform task_assignees list of dictionary entries + # to just a list of the login names as used in AYON DB + # so it's easier later to set + task_assignees = sg_entity.get("task_assignees") + if task_assignees: + task_assignees_list = [] + for assignee in task_assignees: + sg_user = get_sg_user_by_id( + sg_session, assignee["id"], extra_fields=["login"] + ) + task_assignees_list.append(sg_user["login"]) + + sg_entity["task_assignees"] = task_assignees_list sg_ay_dict = _sg_to_ay_dict( sg_entity, project_code_field, custom_attribs_map @@ -786,6 +821,37 @@ def get_sg_missing_ay_attributes(sg_session: shotgun_api3.Shotgun): return missing_attrs +def get_sg_user_by_id( + sg_session: shotgun_api3.Shotgun, + user_id: int, + extra_fields: Optional[list] = None +) -> dict: + """ Find a user in ShotGrid by its id. + + Args: + sg_session (shotgun_api3.Shotgun): Shotgun Session object. + user_id (int): The user ID to look for. + extra_fields (Optional[list]): List of optional fields to query. + Returns: + sg_project (dict): ShotGrid Project dict. + """ + common_fields = list(SG_COMMON_ENTITY_FIELDS) + + if extra_fields: + common_fields.extend(extra_fields) + + sg_user = sg_session.find_one( + "HumanUser", + [["id", "is", user_id]], + fields=common_fields, + ) + + if not sg_user: + raise ValueError(f"Unable to find HumanUser {user_id} in ShotGrid.") + + return sg_user + + def get_sg_project_by_id( sg_session: shotgun_api3.Shotgun, project_id: int, @@ -1054,26 +1120,35 @@ def update_ay_entity_custom_attributes( sg_ay_dict: dict, custom_attribs_map: dict, values_to_update: Optional[list] = None, + ay_project: ProjectEntity = None, ): """Update Ayon entity custom attributes from ShotGrid dictionary""" for ay_attrib, _ in custom_attribs_map.items(): if values_to_update and ay_attrib not in values_to_update: continue - attrib_value = sg_ay_dict["attribs"].get(ay_attrib) + attrib_value = sg_ay_dict["attribs"].get(ay_attrib) or sg_ay_dict.get(ay_attrib) if attrib_value is None: continue if ay_attrib == "tags": - log.warning("Tags update is not supported yet.") ay_entity.tags = [tag["name"] for tag in attrib_value] elif ay_attrib == "status": - # TODO: Implement status update + # Entity hub expects the statuses to be provided with the `name` and + # not the `short_name` (which is what we get from SG) so we convert + # the short name back to the long name before setting it + status_mapping = { + status.short_name: status.name for status in ay_project.statuses + } + new_status_name = status_mapping.get(attrib_value) + try: + ay_entity.status = new_status_name + except ValueError as e: + logging.warning(f"Status sync not implemented: {e}") + elif ay_attrib == "assignees": try: - # INFO: it was causing error so trying to set status directly - ay_entity.status = attrib_value + ay_entity.assignees = attrib_value except ValueError as e: - # `ValueError: Status ip is not available on project.` - log.warning(f"Status sync not implemented: {e}") + logging.warning(f"Assignees sync not implemented: {e}") else: ay_entity.attribs.set(ay_attrib, attrib_value) diff --git a/services/transmitter/transmitter/transmitter.py b/services/transmitter/transmitter/transmitter.py index bdef3f7a..02ecea87 100644 --- a/services/transmitter/transmitter/transmitter.py +++ b/services/transmitter/transmitter/transmitter.py @@ -76,6 +76,12 @@ def __init__(self): for attr in custom_attribs_map if attr["sg"] } + self.custom_attribs_map.update({ + "status": "status_list", + "tags": "tags", + "assignees": "task_assignees" + }) + self.custom_attribs_types = { attr["sg"]: (attr["type"], attr["scope"]) for attr in custom_attribs_map @@ -142,6 +148,7 @@ def start_processing(self): "entity.folder.attrib_changed", "entity.folder.status_changed", "entity.folder.tags_changed", + "entity.version.status_changed", ] while True: From b7469d0324bfdef02f1fbd3b700dded557ed2fef Mon Sep 17 00:00:00 2001 From: farrizabalaga Date: Fri, 7 Jun 2024 00:48:04 +0200 Subject: [PATCH 02/19] Filter task assignments so it's only HumanUsers --- services/shotgrid_common/utils.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/services/shotgrid_common/utils.py b/services/shotgrid_common/utils.py index db739d78..38137d91 100644 --- a/services/shotgrid_common/utils.py +++ b/services/shotgrid_common/utils.py @@ -666,6 +666,10 @@ def get_sg_entities( if task_assignees: task_assignees_list = [] for assignee in task_assignees: + # Skip task assignments that aren't from a human user (i.e. groups) + # TODO: add support for group assignments + if assignee["type"] != "HumanUser": + continue sg_user = get_sg_user_by_id( sg_session, assignee["id"], extra_fields=["login"] ) @@ -743,6 +747,10 @@ def get_sg_entity_as_ay_dict( if task_assignees: task_assignees_list = [] for assignee in task_assignees: + # Skip task assignments that aren't from a human user (i.e. groups) + # TODO: add support for group assignments + if assignee["type"] != "HumanUser": + continue sg_user = get_sg_user_by_id( sg_session, assignee["id"], extra_fields=["login"] ) From 6d037f6293b76e3e3ac6e7523959524c12ed5029 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fabi=C3=A0=20Serra=20Arrizabalaga?= Date: Mon, 24 Jun 2024 16:13:38 +0200 Subject: [PATCH 03/19] Remove comment added to discuss requirement in PR --- services/shotgrid_common/ayon_shotgrid_hub/update_from_ayon.py | 1 - 1 file changed, 1 deletion(-) diff --git a/services/shotgrid_common/ayon_shotgrid_hub/update_from_ayon.py b/services/shotgrid_common/ayon_shotgrid_hub/update_from_ayon.py index e262cc99..0117da4a 100644 --- a/services/shotgrid_common/ayon_shotgrid_hub/update_from_ayon.py +++ b/services/shotgrid_common/ayon_shotgrid_hub/update_from_ayon.py @@ -386,7 +386,6 @@ def _create_sg_entity( or ay_entity.entity_type != "task" and ay_entity.folder_type == "AssetCategory" ): - # TODO: why not?? # AssetCategory should not be created in Shotgrid # task should not be child of AssetCategory return From 8012809187013bb9a7bdbeab8f504184208dc871 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Tue, 3 Dec 2024 17:13:28 +0100 Subject: [PATCH 04/19] Added get_user_by_sg_id endpoint and its usage Endpoint queries DB for user with matchin sg_user_id and uses its name as assignee --- server/__init__.py | 41 ++++++++++++++- services/shotgrid_common/utils.py | 83 ++++++++++++++++++------------- 2 files changed, 88 insertions(+), 36 deletions(-) diff --git a/server/__init__.py b/server/__init__.py index 8ef337d0..045aacc5 100644 --- a/server/__init__.py +++ b/server/__init__.py @@ -1,10 +1,12 @@ from typing import Any, Type +from nxtools import logging +from fastapi import Path +from ayon_server.entities import UserEntity from ayon_server.addons import BaseServerAddon from ayon_server.lib.postgres import Postgres -from .settings import ShotgridSettings -from nxtools import logging +from .settings import ShotgridSettings SG_ID_ATTRIB = "shotgridId" SG_TYPE_ATTRIB = "shotgridType" @@ -16,6 +18,15 @@ class ShotgridAddon(BaseServerAddon): frontend_scopes: dict[str, Any] = {"settings": {}} + def initialize(self) -> None: + + # returning user for SG id value + self.add_endpoint( + "/get_user_by_sg_id/{sg_user_id}", + self.get_user_by_sg_id, + method="GET", + ) + async def setup(self): need_restart = await self.create_shotgrid_attributes() if need_restart: @@ -110,3 +121,29 @@ async def create_shotgrid_attributes(self) -> bool: else: logging.debug("Shotgrid Attributes already exist.") return False + + async def get_user_by_sg_id( + self, + sg_user_id: str = Path( + ..., + description="Id of Shotgrid user ", + example="123", + ) + ) -> UserEntity.model.main_model | None: + """Queries user for specific 'sg_user_id' field in 'data'. + + Field added during user synchronization to be explicit, not depending that + SG login will be same as AYON (which is not as @ is not allowed in AYON) + """ + query = f""" + SELECT + * + FROM public.users + WHERE data ? 'sg_user_id' AND data->>'sg_user_id' = '{sg_user_id}'; + """ + + res = await Postgres.fetch(query) + if not res: + return None + user = await UserEntity.load(res[0]["name"]) + return user.payload diff --git a/services/shotgrid_common/utils.py b/services/shotgrid_common/utils.py index 5c3601dd..2f46f226 100644 --- a/services/shotgrid_common/utils.py +++ b/services/shotgrid_common/utils.py @@ -867,23 +867,7 @@ def get_sg_entities( parent_id = cat_ent_name - # Transform task_assignees list of dictionary entries - # to just a list of the login names as used in AYON DB - # so it's easier later to set - task_assignees = sg_entity.get("task_assignees") - if task_assignees: - task_assignees_list = [] - for assignee in task_assignees: - # Skip task assignments that aren't from a human user (i.e. groups) - # TODO: add support for group assignments - if assignee["type"] != "HumanUser": - continue - sg_user = get_sg_user_by_id( - sg_session, assignee["id"], extra_fields=["login"] - ) - task_assignees_list.append(sg_user["login"]) - - sg_entity["task_assignees"] = task_assignees_list + _add_task_assignees(sg_entity) sg_ay_dict = _sg_to_ay_dict( sg_entity, @@ -948,23 +932,7 @@ def get_sg_entity_as_ay_dict( if not sg_entity: return {} - # Transform task_assignees list of dictionary entries - # to just a list of the login names as used in AYON DB - # so it's easier later to set - task_assignees = sg_entity.get("task_assignees") - if task_assignees: - task_assignees_list = [] - for assignee in task_assignees: - # Skip task assignments that aren't from a human user (i.e. groups) - # TODO: add support for group assignments - if assignee["type"] != "HumanUser": - continue - sg_user = get_sg_user_by_id( - sg_session, assignee["id"], extra_fields=["login"] - ) - task_assignees_list.append(sg_user["login"]) - - sg_entity["task_assignees"] = task_assignees_list + _add_task_assignees(sg_entity) sg_ay_dict = _sg_to_ay_dict( sg_entity, project_code_field, custom_attribs_map @@ -1476,3 +1444,50 @@ def create_new_ayon_entity( log.error("AYON Entity could not be created", exc_info=True) return ay_entity + + +def get_user_by_sg_id(sg_user_id): + """Returns ayon user for particular `sg_user_id` + + Calls SG addon endpoint to query 'users' table + + Args: + sg_user_id (str) + Returns: + (Optional[dict[str, Any]]) + """ + addon_name = ayon_api.get_service_addon_name() + addon_version = ayon_api.get_service_addon_version() + variant = ayon_api.get_default_settings_variant() + endpoint_url = ( + f"addons/{addon_name}/{addon_version}/" + f"get_user_by_sg_id/{sg_user_id}" + f"?variant={variant}" + ) + + response = ayon_api.get(endpoint_url) + if response.status_code != 200: + print(response.content) + raise RuntimeError(response.text) + + return response.data + + +def _add_task_assignees(sg_entity): + # Transform task_assignees list of dictionary entries + # to just a list of the login names as used in AYON DB + # so it's easier later to set + task_assignees = sg_entity.get("task_assignees") + if not task_assignees: + return + + task_assignees_list = [] + for assignee in task_assignees: + # Skip task assignments that aren't from a human user (i.e. groups) + # TODO: add support for group assignments + if assignee["type"] != "HumanUser": + continue + ayon_user = get_user_by_sg_id(assignee["id"]) + task_assignees_list.append(ayon_user["name"]) + + sg_entity["task_assignees"] = task_assignees_list From cfd10d56eec42286bdd8516b535bbd9a0f381b8e Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Tue, 3 Dec 2024 17:38:41 +0100 Subject: [PATCH 05/19] Fix use empty value instead of None sg_entity.get(sg_attrib) might return empty ('' or []) which is valid value, so no need to query `sg_{sg_attrib}` which will be None. Solves completely removing tags from item in SG >> removing them in AYON --- services/shotgrid_common/utils.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/services/shotgrid_common/utils.py b/services/shotgrid_common/utils.py index 2f46f226..51ac4072 100644 --- a/services/shotgrid_common/utils.py +++ b/services/shotgrid_common/utils.py @@ -173,7 +173,8 @@ def _sg_to_ay_dict( if custom_attribs_map: for ay_attrib, sg_attrib in custom_attribs_map.items(): - sg_value = sg_entity.get(sg_attrib) or sg_entity.get(f"sg_{sg_attrib}") + sg_value = (sg_entity.get(f"sg_{sg_attrib}") + or sg_entity.get(sg_attrib)) # If no value in SG entity skip if sg_value is None: From ce9598b790fafcec46d286ddb1629865dc3125b0 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Wed, 4 Dec 2024 15:37:49 +0100 Subject: [PATCH 06/19] Added synchronization of assignee from AYON It assumes that users are synchronized with SG first, eg. they have data["sg_user_id"] --- .../shotgrid_common/ayon_shotgrid_hub/__init__.py | 8 +++++++- .../ayon_shotgrid_hub/update_from_ayon.py | 13 ++++++++++++- services/transmitter/transmitter/transmitter.py | 1 + 3 files changed, 20 insertions(+), 2 deletions(-) diff --git a/services/shotgrid_common/ayon_shotgrid_hub/__init__.py b/services/shotgrid_common/ayon_shotgrid_hub/__init__.py index 57a1c87a..e969dd45 100644 --- a/services/shotgrid_common/ayon_shotgrid_hub/__init__.py +++ b/services/shotgrid_common/ayon_shotgrid_hub/__init__.py @@ -433,7 +433,13 @@ def react_to_ayon_event(self, ayon_event): self._ay_project, self.custom_attribs_map, ) - case "entity.task.status_changed" | "entity.folder.status_changed" | "entity.task.tags_changed" | "entity.folder.tags_changed": + case ( + "entity.task.status_changed" | + "entity.folder.status_changed" | + "entity.task.tags_changed" | + "entity.folder.tags_changed" | + "entity.task.assignees_changed" + ): # TODO: for some reason the payload here is not a dict but we know # we always want to update the entity update_sg_entity_from_ayon_event( diff --git a/services/shotgrid_common/ayon_shotgrid_hub/update_from_ayon.py b/services/shotgrid_common/ayon_shotgrid_hub/update_from_ayon.py index 326ab1e4..f60649ca 100644 --- a/services/shotgrid_common/ayon_shotgrid_hub/update_from_ayon.py +++ b/services/shotgrid_common/ayon_shotgrid_hub/update_from_ayon.py @@ -213,7 +213,18 @@ def update_sg_entity_from_ayon_event( new_attribs["tags"].append( {"name": tag_name, "id": tag_id, "type": "Tag"} ) - + elif ayon_event["topic"].endswith("assignees_changed"): + sg_assignees = [] + for user_name in new_attribs: + ayon_user = ayon_api.get_user(user_name) + if not ayon_user or not ayon_user["data"].get("sg_user_id"): + log.warning(f"User {user_name} is not synched to SG yet.") + continue + sg_assignees.append( + {"type": "HumanUser", + "id": ayon_user["data"]["sg_user_id"]} + ) + new_attribs = {"assignees": sg_assignees} else: log.warning( "Unknown event type, skipping update of custom attribs.") diff --git a/services/transmitter/transmitter/transmitter.py b/services/transmitter/transmitter/transmitter.py index 198997b6..92d411d0 100644 --- a/services/transmitter/transmitter/transmitter.py +++ b/services/transmitter/transmitter/transmitter.py @@ -139,6 +139,7 @@ def start_processing(self): "entity.task.deleted", "entity.task.renamed", "entity.task.create", + "entity.task.assignees_changed", "entity.task.attrib_changed", "entity.task.status_changed", "entity.task.tags_changed", From 60b4970116dac6c661c9b640c9b75470490a055f Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Wed, 4 Dec 2024 17:13:24 +0100 Subject: [PATCH 07/19] Ruff --- .../shotgrid_common/ayon_shotgrid_hub/update_from_shotgrid.py | 2 +- services/shotgrid_common/utils.py | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/services/shotgrid_common/ayon_shotgrid_hub/update_from_shotgrid.py b/services/shotgrid_common/ayon_shotgrid_hub/update_from_shotgrid.py index bb40352c..d98185ba 100644 --- a/services/shotgrid_common/ayon_shotgrid_hub/update_from_shotgrid.py +++ b/services/shotgrid_common/ayon_shotgrid_hub/update_from_shotgrid.py @@ -428,7 +428,7 @@ def update_ayon_entity_from_sg_event( CUST_FIELD_CODE_ID: ay_entity.id } ) - + ay_entity.attribs.set( SHOTGRID_ID_ATTRIB, sg_ay_dict["attribs"].get(SHOTGRID_ID_ATTRIB, "") diff --git a/services/shotgrid_common/utils.py b/services/shotgrid_common/utils.py index b345fe25..cba626f5 100644 --- a/services/shotgrid_common/utils.py +++ b/services/shotgrid_common/utils.py @@ -185,7 +185,7 @@ def _sg_to_ay_dict( # If no value in SG entity skip if sg_value is None: continue - + if ay_attrib in exception_attribs: sg_ay_dict[ay_attrib] = sg_value else: @@ -947,7 +947,7 @@ def get_sg_entity_as_ay_dict( if not sg_entity: return {} - + _add_task_assignees(sg_entity) sg_ay_dict = _sg_to_ay_dict( From e3cc88ea8d616d362a819b0f3872f87365fa3b68 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Thu, 5 Dec 2024 11:27:11 +0100 Subject: [PATCH 08/19] Added debug logging --- services/shotgrid_common/utils.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/services/shotgrid_common/utils.py b/services/shotgrid_common/utils.py index cba626f5..5024a138 100644 --- a/services/shotgrid_common/utils.py +++ b/services/shotgrid_common/utils.py @@ -1494,6 +1494,7 @@ def _add_task_assignees(sg_entity): # to just a list of the login names as used in AYON DB # so it's easier later to set task_assignees = sg_entity.get("task_assignees") + log.debug(f"Received '{task_assignees}' from SG.") if not task_assignees: return @@ -1504,6 +1505,8 @@ def _add_task_assignees(sg_entity): if assignee["type"] != "HumanUser": continue ayon_user = get_user_by_sg_id(assignee["id"]) + if not ayon_user: + log.warning(f"Didn't find user for '{assignee['id']}'") task_assignees_list.append(ayon_user["name"]) - + log.debug(f"Adding '{task_assignees_list}' from SG.") sg_entity["task_assignees"] = task_assignees_list From 205b02f2b41ca103fe28450ccecb0a2d2e3d1094 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Thu, 5 Dec 2024 16:39:17 +0100 Subject: [PATCH 09/19] Formatting change Co-authored-by: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> --- services/shotgrid_common/ayon_shotgrid_hub/__init__.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/services/shotgrid_common/ayon_shotgrid_hub/__init__.py b/services/shotgrid_common/ayon_shotgrid_hub/__init__.py index 27a50161..9faa3a83 100644 --- a/services/shotgrid_common/ayon_shotgrid_hub/__init__.py +++ b/services/shotgrid_common/ayon_shotgrid_hub/__init__.py @@ -437,11 +437,11 @@ def react_to_ayon_event(self, ayon_event): self.custom_attribs_map, ) case ( - "entity.task.status_changed" | - "entity.folder.status_changed" | - "entity.task.tags_changed" | - "entity.folder.tags_changed" | - "entity.task.assignees_changed" + "entity.task.status_changed" + | "entity.folder.status_changed" + | "entity.task.tags_changed" + | "entity.folder.tags_changed" + | "entity.task.assignees_changed" ): # TODO: for some reason the payload here is not a dict but we know # we always want to update the entity From baccf5e613badcf43ab6f7f8a340e98720da099b Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Thu, 5 Dec 2024 16:39:37 +0100 Subject: [PATCH 10/19] Formatting change Co-authored-by: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> --- services/shotgrid_common/utils.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/services/shotgrid_common/utils.py b/services/shotgrid_common/utils.py index 5024a138..5394c4dd 100644 --- a/services/shotgrid_common/utils.py +++ b/services/shotgrid_common/utils.py @@ -1417,8 +1417,8 @@ def create_new_ayon_entity( # not the `short_name` (which is what we get from SG) so we convert # the short name back to the long name before setting it status_mapping = { - status.short_name: status.name for status in - entity_hub.project_entity.statuses + status.short_name: status.name + for status in entity_hub.project_entity.statuses } new_status_name = status_mapping.get(status) if not new_status_name: From 833cafc62644c09eeeca92cb05422ff9b06d4dd2 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Thu, 5 Dec 2024 16:47:37 +0100 Subject: [PATCH 11/19] Removed unnecessary try-except block --- services/shotgrid_common/utils.py | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/services/shotgrid_common/utils.py b/services/shotgrid_common/utils.py index 5394c4dd..b514fee5 100644 --- a/services/shotgrid_common/utils.py +++ b/services/shotgrid_common/utils.py @@ -1436,11 +1436,7 @@ def create_new_ayon_entity( assignees = sg_ay_dict.get("assignees") if assignees: - try: - # INFO: it was causing error so trying to set status directly - ay_entity.assignees = assignees - except ValueError as e: - log.warning(f"Assignees sync not implemented: {e}") + ay_entity.assignees = assignees tags = sg_ay_dict.get("tags") if tags: From 64496369573b701bd84e3a7546541ea5768d59a1 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Thu, 5 Dec 2024 16:49:01 +0100 Subject: [PATCH 12/19] Refactor renamed exception_attribs --- services/shotgrid_common/utils.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/services/shotgrid_common/utils.py b/services/shotgrid_common/utils.py index b514fee5..80932763 100644 --- a/services/shotgrid_common/utils.py +++ b/services/shotgrid_common/utils.py @@ -117,7 +117,7 @@ def _sg_to_ay_dict( ay_entity_type = "folder" task_type = None folder_type = None - exception_attribs = {"status", "assignees", "tags"} + root_level_attributes = {"status", "assignees", "tags"} if sg_entity["type"] == "Task": ay_entity_type = "task" @@ -186,7 +186,7 @@ def _sg_to_ay_dict( if sg_value is None: continue - if ay_attrib in exception_attribs: + if ay_attrib in root_level_attributes: sg_ay_dict[ay_attrib] = sg_value else: sg_ay_dict["attribs"][ay_attrib] = sg_value From 7d74132d5ac6b4d97a311481e98d056359c0d29a Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Thu, 5 Dec 2024 17:11:05 +0100 Subject: [PATCH 13/19] Use more restrictive endpoint returning only AYON user name No need to expose user data unnecessary --- server/__init__.py | 16 +++++++--------- services/shotgrid_common/utils.py | 17 +++++++++-------- 2 files changed, 16 insertions(+), 17 deletions(-) diff --git a/server/__init__.py b/server/__init__.py index 045aacc5..6a2e77bc 100644 --- a/server/__init__.py +++ b/server/__init__.py @@ -1,4 +1,4 @@ -from typing import Any, Type +from typing import Any, Type, Optional from nxtools import logging from fastapi import Path @@ -22,8 +22,8 @@ def initialize(self) -> None: # returning user for SG id value self.add_endpoint( - "/get_user_by_sg_id/{sg_user_id}", - self.get_user_by_sg_id, + "/get_ayon_name_by_sg_id/{sg_user_id}", + self.get_ayon_name_by_sg_id, method="GET", ) @@ -122,14 +122,14 @@ async def create_shotgrid_attributes(self) -> bool: logging.debug("Shotgrid Attributes already exist.") return False - async def get_user_by_sg_id( + async def get_ayon_name_by_sg_id( self, sg_user_id: str = Path( ..., description="Id of Shotgrid user ", example="123", ) - ) -> UserEntity.model.main_model | None: + ) -> Optional[str]: """Queries user for specific 'sg_user_id' field in 'data'. Field added during user synchronization to be explicit, not depending that @@ -143,7 +143,5 @@ async def get_user_by_sg_id( """ res = await Postgres.fetch(query) - if not res: - return None - user = await UserEntity.load(res[0]["name"]) - return user.payload + if res: + return res[0]["name"] diff --git a/services/shotgrid_common/utils.py b/services/shotgrid_common/utils.py index 80932763..a25977c5 100644 --- a/services/shotgrid_common/utils.py +++ b/services/shotgrid_common/utils.py @@ -1458,22 +1458,23 @@ def create_new_ayon_entity( return ay_entity -def get_user_by_sg_id(sg_user_id): - """Returns ayon user for particular `sg_user_id` +def get_ayon_name_by_sg_id(sg_user_id): + """Returns AYON user name for particular `sg_user_id` - Calls SG addon endpoint to query 'users' table + Calls SG addon endpoint to query 'users' table limit need to loop through + all users. Args: sg_user_id (str) Returns: - (Optional[dict[str, Any]]) + (Optional[str]) """ addon_name = ayon_api.get_service_addon_name() addon_version = ayon_api.get_service_addon_version() variant = ayon_api.get_default_settings_variant() endpoint_url = ( f"addons/{addon_name}/{addon_version}/" - f"get_user_by_sg_id/{sg_user_id}" + f"get_ayon_name_by_sg_id/{sg_user_id}" f"?variant={variant}" ) @@ -1500,9 +1501,9 @@ def _add_task_assignees(sg_entity): # TODO: add support for group assignments if assignee["type"] != "HumanUser": continue - ayon_user = get_user_by_sg_id(assignee["id"]) - if not ayon_user: + ayon_user_name = get_ayon_name_by_sg_id(assignee["id"]) + if not ayon_user_name: log.warning(f"Didn't find user for '{assignee['id']}'") - task_assignees_list.append(ayon_user["name"]) + task_assignees_list.append(ayon_user_name) log.debug(f"Adding '{task_assignees_list}' from SG.") sg_entity["task_assignees"] = task_assignees_list From 9c7bf6549d8bb9c6ca0fef243c4fbc51afc3e246 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Thu, 5 Dec 2024 18:11:37 +0100 Subject: [PATCH 14/19] Removed unnecessary import --- server/__init__.py | 1 - 1 file changed, 1 deletion(-) diff --git a/server/__init__.py b/server/__init__.py index 6a2e77bc..38fefe93 100644 --- a/server/__init__.py +++ b/server/__init__.py @@ -2,7 +2,6 @@ from nxtools import logging from fastapi import Path -from ayon_server.entities import UserEntity from ayon_server.addons import BaseServerAddon from ayon_server.lib.postgres import Postgres From a835b4ee3c91e8f7226bcac9678594f0890d5dca Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Fri, 6 Dec 2024 18:17:12 +0100 Subject: [PATCH 15/19] Use better handling of status scop Co-authored-by: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> --- services/shotgrid_common/utils.py | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/services/shotgrid_common/utils.py b/services/shotgrid_common/utils.py index a25977c5..a5b2f71a 100644 --- a/services/shotgrid_common/utils.py +++ b/services/shotgrid_common/utils.py @@ -1338,9 +1338,17 @@ def update_ay_entity_custom_attributes( # not the `short_name` (which is what we get from SG) so we convert # the short name back to the long name before setting it status_mapping = { - status.short_name: status.name for status in ay_project.statuses + status.short_name: status + for status in ay_project.statuses } - new_status_name = status_mapping.get(attrib_value) + new_status = status_mapping.get(attrib_value) + if ay_entity.entity_type in new_status.scope: + ay_entity.status = new_status.name + else: + logging.warning( + f"Status '{attrb_value}' not available" + f" for {entity.entity_type}." + ) try: ay_entity.status = new_status_name except ValueError as e: From c55398ffe905a520087df9e6016e434b8ac35a41 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Fri, 6 Dec 2024 18:17:52 +0100 Subject: [PATCH 16/19] Use better protection for old ayon-python-api for assignees Co-authored-by: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> --- services/shotgrid_common/utils.py | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/services/shotgrid_common/utils.py b/services/shotgrid_common/utils.py index a5b2f71a..8cd0a509 100644 --- a/services/shotgrid_common/utils.py +++ b/services/shotgrid_common/utils.py @@ -1354,10 +1354,13 @@ def update_ay_entity_custom_attributes( except ValueError as e: logging.warning(f"Status sync not implemented: {e}") elif ay_attrib == "assignees": - try: + if hasattr(ay_entity, "assignees"): ay_entity.assignees = attrib_value - except ValueError as e: - logging.warning(f"Assignees sync not implemented: {e}") + else: + logging.warning( + "Assignees sync not available with current" + " ayon-python-api version." + ) else: ay_entity.attribs.set(ay_attrib, attrib_value) From fc107e1dc7fa078a98c5089c6d265d6dd7a518be Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Fri, 6 Dec 2024 18:19:34 +0100 Subject: [PATCH 17/19] Fix assignment on nonexisting ayon user --- services/shotgrid_common/utils.py | 1 + 1 file changed, 1 insertion(+) diff --git a/services/shotgrid_common/utils.py b/services/shotgrid_common/utils.py index 8cd0a509..0ec26b5e 100644 --- a/services/shotgrid_common/utils.py +++ b/services/shotgrid_common/utils.py @@ -1515,6 +1515,7 @@ def _add_task_assignees(sg_entity): ayon_user_name = get_ayon_name_by_sg_id(assignee["id"]) if not ayon_user_name: log.warning(f"Didn't find user for '{assignee['id']}'") + continue task_assignees_list.append(ayon_user_name) log.debug(f"Adding '{task_assignees_list}' from SG.") sg_entity["task_assignees"] = task_assignees_list From f75318473bcba621dcfbfe51dd4881c3bfcffd08 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Fri, 6 Dec 2024 18:22:08 +0100 Subject: [PATCH 18/19] Fix broken merge --- services/shotgrid_common/utils.py | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/services/shotgrid_common/utils.py b/services/shotgrid_common/utils.py index 0ec26b5e..319abba3 100644 --- a/services/shotgrid_common/utils.py +++ b/services/shotgrid_common/utils.py @@ -1346,13 +1346,9 @@ def update_ay_entity_custom_attributes( ay_entity.status = new_status.name else: logging.warning( - f"Status '{attrb_value}' not available" - f" for {entity.entity_type}." + f"Status '{attrib_value}' not available" + f" for {ay_entity.entity_type}." ) - try: - ay_entity.status = new_status_name - except ValueError as e: - logging.warning(f"Status sync not implemented: {e}") elif ay_attrib == "assignees": if hasattr(ay_entity, "assignees"): ay_entity.assignees = attrib_value From 486feda97fbe0750ca37638a5044e14fd6c44352 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Fri, 6 Dec 2024 18:29:45 +0100 Subject: [PATCH 19/19] Fix creation of version Version couldnt be created in AYON yet. --- services/shotgrid_common/utils.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/services/shotgrid_common/utils.py b/services/shotgrid_common/utils.py index 319abba3..f0b9d0cc 100644 --- a/services/shotgrid_common/utils.py +++ b/services/shotgrid_common/utils.py @@ -1397,6 +1397,9 @@ def create_new_ayon_entity( attribs=sg_ay_dict["attribs"], data=sg_ay_dict["data"] ) + elif sg_ay_dict["type"].lower() == "version": + log.warning("Cannot create new versions yet.") + return else: ay_entity = entity_hub.add_new_folder( folder_type=sg_ay_dict["folder_type"],