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

Synchronisation issues #91

Merged
merged 26 commits into from
May 7, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
3d6099e
Update data dictionary to set sync status to "Failed" if ID is not se…
jakubjezek001 Apr 24, 2024
9067dc7
Merge branch 'develop' into bugfix/services_fixes
jakubjezek001 Apr 24, 2024
7733ea9
Merge branch 'develop' into bugfix/services_fixes
jakubjezek001 Apr 24, 2024
75c31bd
Update Ayon entities from ShotGrid events, handle status sync and ent…
jakubjezek001 Apr 25, 2024
59d0ede
Update custom attributes mapping and add TODO for handling status_lis…
jakubjezek001 Apr 29, 2024
c865427
Update custom attributes handling and entity creation logic in AyonSh…
jakubjezek001 Apr 29, 2024
1b5f9be
Update custom attributes map and logging in ShotgridListener. Refacto…
jakubjezek001 May 2, 2024
320fff9
Merge branch 'develop' into bugfix/services_fixes
jakubjezek001 May 2, 2024
4d260db
Refactor commit_changes handling in match_shotgrid_hierarchy_in_ayon
jakubjezek001 May 2, 2024
1651b66
warning is not generating events
jakubjezek001 May 2, 2024
d032c83
ayon python api version increase
jakubjezek001 May 2, 2024
ebd8b25
Update services/shotgrid_common/ayon_shotgrid_hub/update_from_shotgri…
jakubjezek001 May 2, 2024
bcf7ef5
Update services/shotgrid_common/ayon_shotgrid_hub/update_from_shotgri…
jakubjezek001 May 2, 2024
6f208d7
Update services/shotgrid_common/ayon_shotgrid_hub/update_from_shotgri…
jakubjezek001 May 2, 2024
0cd159e
Update services/shotgrid_common/utils.py
jakubjezek001 May 2, 2024
5c72f96
Update services/shotgrid_common/utils.py
jakubjezek001 May 2, 2024
4884df3
Update Ayon entity creation and update process
jakubjezek001 May 2, 2024
0858265
Merge branch 'bugfix/services_fixes' of https://github.com/ynput/ayon…
jakubjezek001 May 2, 2024
37e4272
removed unused variable
iLLiCiTiT May 3, 2024
2ebc397
removed unnecessary condition
iLLiCiTiT May 3, 2024
ed21f1f
move indentation
iLLiCiTiT May 3, 2024
7bcfe0c
Refactor entity retrieval to prevent duplicates
jakubjezek001 May 3, 2024
281e531
Remove debug logging statements and update entity creation and proces…
jakubjezek001 May 3, 2024
9ef6410
clearing code
jakubjezek001 May 6, 2024
d3ab820
comment on wrong line position
jakubjezek001 May 6, 2024
a4514d7
Update custom attributes mapping
jakubjezek001 May 7, 2024
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
@@ -1,6 +1,6 @@
annotated-types==0.6.0
appdirs==1.4.4
ayon-python-api==1.0.4
ayon-python-api>=1.0.4
certifi==2024.2.2
charset-normalizer==3.3.2
colorama==0.4.6
Expand Down
9 changes: 3 additions & 6 deletions services/leecher/leecher/listener.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,8 +38,6 @@ def __init__(self, func: Union[Callable, None] = None):
else:
self.func = func

logging.debug(f"Callback method is {self.func}.")
jakubjezek001 marked this conversation as resolved.
Show resolved Hide resolved

try:
ayon_api.init_service()
self.settings = ayon_api.get_service_addon_settings()
Expand Down Expand Up @@ -71,8 +69,10 @@ def __init__(self, func: Union[Callable, None] = None):
for attr in self.settings["compatibility_settings"]["custom_attribs_map"] # noqa: E501
if attr["sg"]
}

# TODO: implement a way to handle status_list and tags
self.custom_attribs_map.update({
"status": "status_list",
# "status": "status_list",
"tags": "tags"
})

Expand Down Expand Up @@ -171,8 +171,6 @@ def _get_last_event_processed(self, sg_filters):
)
last_event_id = last_event["id"]

logging.debug(f"Last non-processed SG Event is {last_event}")

return last_event_id

def start_listening(self):
Expand Down Expand Up @@ -221,7 +219,6 @@ def start_listening(self):
event["event_type"].endswith("_Change")
and event["attribute_name"].replace("sg_", "") not in list(self.custom_attribs_map.values())
):
logging.debug(f"Skipping event for attribute change '{event['attribute_name'].replace('sg_', '')}', as we can't handle it.")
last_event_id = event.get("id", None)
continue

Expand Down
6 changes: 0 additions & 6 deletions services/processor/processor/processor.py
Original file line number Diff line number Diff line change
Expand Up @@ -96,8 +96,6 @@ def __init__(self):
self.handlers_map = self._get_handlers()
if not self.handlers_map:
logging.error("No handlers found for the processor, aborting.")
else:
logging.debug(f"Found these handlers: {self.handlers_map}")
jakubjezek001 marked this conversation as resolved.
Show resolved Hide resolved

def _get_handlers(self):
""" Import the handlers found in the `handlers` directory.
Expand Down Expand Up @@ -145,10 +143,6 @@ def start_processing(self):
will trigger the `handlers/project_sync.py` since that one has the attribute
REGISTER_EVENT_TYPE = ["create-project"]
"""
logging.debug(
"Querying for `shotgrid.event` events "
f"every {self.sg_polling_frequency} seconds..."
)
while True:
try:
event = ayon_api.enroll_event_job(
Expand Down
16 changes: 10 additions & 6 deletions services/shotgrid_common/ayon_shotgrid_hub/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,12 @@ class AyonShotgridHub:
custom_attribs_types (dict): A dictionary mapping AYON attribute types
to Shotgrid field types.
"""

custom_attribs_map = {
"status": "status_list",
"tags": "tags"
}

def __init__(self,
project_name,
project_code,
Expand Down Expand Up @@ -100,15 +106,11 @@ def __init__(self,
else:
self.sg_project_code_field = "code"

self.custom_attribs_map = {
"status": "status_list",
"tags": "tags"
}
# add custom attributes from settings
if custom_attribs_map:
self.custom_attribs_map.update(custom_attribs_map)

if custom_attribs_types:
self.custom_attribs_types = custom_attribs_types
self.custom_attribs_types = custom_attribs_types

if sg_enabled_entities:
self.sg_enabled_entities = sg_enabled_entities
Expand Down Expand Up @@ -363,8 +365,10 @@ def react_to_shotgrid_event(self, sg_event):
case "attribute_change":
update_ayon_entity_from_sg_event(
sg_event,
self._sg_project,
self._sg,
self._ay_project,
self.sg_enabled_entities,
self.sg_project_code_field,
self.custom_attribs_map,
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,6 @@ def match_ayon_hierarchy_in_shotgrid(

while sg_ay_dicts_deck:
(sg_ay_parent_entity, ay_entity) = sg_ay_dicts_deck.popleft()
logging.debug(f"Processing {ay_entity})")

sg_ay_dict = None

Expand All @@ -85,7 +84,8 @@ def match_ayon_hierarchy_in_shotgrid(

if sg_entity_id in sg_ay_dicts:
sg_ay_dict = sg_ay_dicts[sg_entity_id]
logging.info(f"Entity already exists in Shotgrid {sg_ay_dict}")
logging.info(
f"Entity already exists in Shotgrid {sg_ay_dict['name']}")

if sg_ay_dict["data"][CUST_FIELD_CODE_ID] != ay_entity.id:
logging.error(
Expand Down Expand Up @@ -148,7 +148,8 @@ def match_ayon_hierarchy_in_shotgrid(
SHOTGRID_TYPE_ATTRIB,
sg_ay_dict["attribs"][SHOTGRID_TYPE_ATTRIB]
)
entity_hub.commit_changes()

entity_hub.commit_changes()

if sg_ay_dict is None:
# Shotgrid doesn't have the concept of "Folders"
Expand Down Expand Up @@ -268,9 +269,6 @@ def _create_new_entity(
log_traceback(e)
raise e

logging.debug(f"Created new entity: {sg_entity}")
logging.debug(f"Parent is: {sg_parent_entity}")

sg_ay_dict = get_sg_entity_as_ay_dict(
sg_session,
sg_entity["type"],
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,9 +29,9 @@ def match_shotgrid_hierarchy_in_ayon(
"""Replicate a Shotgrid project into AYON.

This function creates a "deck" which we keep increasing while traversing
the Shotgrid project and finding new childrens, this is more efficient than
creating a dictionary with the whle Shotgrid project structure since we
`popleft` the elements when procesing them.
the Shotgrid project and finding new children, this is more efficient than
creating a dictionary with the while Shotgrid project structure since we
`popleft` the elements when processing them.

Args:
entity_hub (ayon_api.entity_hub.EntityHub): The AYON EntityHub.
Expand All @@ -51,14 +51,34 @@ def match_shotgrid_hierarchy_in_ayon(
sg_ay_dicts_deck = collections.deque()

# Append the project's direct children.
for sg_ay_dict_child in sg_ay_dicts_parents[sg_project["id"]]:
sg_ay_dicts_deck.append((entity_hub.project_entity, sg_ay_dict_child))
for sg_ay_dict_child_id in sg_ay_dicts_parents[sg_project["id"]]:
sg_ay_dicts_deck.append(
(entity_hub.project_entity, sg_ay_dict_child_id)
)

sg_project_sync_status = "Synced"
processed_ids = set()

while sg_ay_dicts_deck:
(ay_parent_entity, sg_ay_dict) = sg_ay_dicts_deck.popleft()
logging.debug(f"Processing {sg_ay_dict} with parent {ay_parent_entity}")
(ay_parent_entity, sg_ay_dict_child_id) = sg_ay_dicts_deck.popleft()
sg_ay_dict = sg_ay_dicts[sg_ay_dict_child_id]
sg_entity_id = sg_ay_dict["attribs"][SHOTGRID_ID_ATTRIB]
if sg_entity_id in processed_ids:
msg = (
f"Entity {sg_entity_id} already processed, skipping..."
f"Sg Ay Dict: {sg_ay_dict} - "
f"Ay Parent Entity: {ay_parent_entity}"
)
logging.warning(msg)

# append msg to temp file for debugging
with open("/service/processed_ids.txt", "a") as f:
f.write(f"{msg}\n")
continue

processed_ids.add(sg_entity_id)

logging.info(f"Deck size: {len(sg_ay_dicts_deck)}")

ay_entity = None
sg_entity_sync_status = "Synced"
Expand All @@ -75,9 +95,6 @@ def match_shotgrid_hierarchy_in_ayon(
for child in ay_parent_entity.children:
if child.name.lower() == name.lower():
ay_entity = child
logging.debug(
f"Found another entity with the same name: {ay_entity.name} <{ay_entity.id}>."
)
break

# If we couldn't find it we create it.
Expand All @@ -86,7 +103,7 @@ def match_shotgrid_hierarchy_in_ayon(
ay_entity = get_asset_category(
entity_hub,
ay_parent_entity,
sg_ay_dict["name"]
sg_ay_dict
)

if not ay_entity:
Expand All @@ -96,20 +113,16 @@ def match_shotgrid_hierarchy_in_ayon(
sg_ay_dict
)
else:
logging.debug(
f"Entity {ay_entity.name} <{ay_entity.id}> exists in AYON. "
"Making sure the stored ShotGrid Data matches."
)

ay_sg_id_attrib = ay_entity.attribs.get(
SHOTGRID_ID_ATTRIB
)

if ay_sg_id_attrib != str(sg_ay_dict["attribs"][SHOTGRID_ID_ATTRIB]): # noqa
# If the ShotGrid ID in AYON doesn't match the one in ShotGrid
if str(ay_sg_id_attrib) != str(sg_entity_id): # noqa
logging.error(
f"The AYON entity {ay_entity.name} <{ay_entity.id}> has the " # noqa
f"ShotgridId {ay_sg_id_attrib}, while the ShotGrid ID " # noqa
f"should be {sg_ay_dict['attribs'][SHOTGRID_ID_ATTRIB]}"
f"should be {sg_entity_id}"
)
sg_entity_sync_status = "Failed"
sg_project_sync_status = "Failed"
Expand All @@ -118,47 +131,55 @@ def match_shotgrid_hierarchy_in_ayon(
ay_entity, sg_ay_dict, custom_attribs_map
)

# skip if no ay_entity is found
# perhaps due Task with project entity as parent
if not ay_entity:
logging.error(f"Entity {sg_ay_dict} not found in AYON.")
continue

# Update SG entity with new created data
sg_ay_dict["data"][CUST_FIELD_CODE_ID] = ay_entity.id
sg_ay_dicts[sg_ay_dict["attribs"][SHOTGRID_ID_ATTRIB]] = sg_ay_dict

entity_id = sg_ay_dict["name"]
sg_ay_dicts[sg_entity_id] = sg_ay_dict

# If the entity is not a "Folder" or "AssetCategory" we update the
# entity ID and sync status in Shotgrid and AYON
if sg_ay_dict["attribs"][SHOTGRID_TYPE_ATTRIB] not in [
"Folder", "AssetCategory"
]:
if (
if (
sg_ay_dict["attribs"][SHOTGRID_TYPE_ATTRIB] not in [
"Folder", "AssetCategory"
]
and (
sg_ay_dict["data"][CUST_FIELD_CODE_ID] != ay_entity.id
or sg_ay_dict["data"][CUST_FIELD_CODE_SYNC] != sg_entity_sync_status # noqa
):
logging.debug("Updating AYON entity ID and sync status in SG and AYON")
update_data = {
CUST_FIELD_CODE_ID: ay_entity.id,
CUST_FIELD_CODE_SYNC: sg_entity_sync_status
}
sg_session.update(
sg_ay_dict["attribs"][SHOTGRID_TYPE_ATTRIB],
sg_ay_dict["attribs"][SHOTGRID_ID_ATTRIB],
update_data
)
ay_entity.data.update(
update_data
)

entity_id = sg_ay_dict["attribs"][SHOTGRID_ID_ATTRIB]

try:
entity_hub.commit_changes()
except Exception as e:
logging.error(f"Unable to create entity {sg_ay_dict} in AYON!")
log_traceback(e)
)
):
# logging.debug(
# "Updating AYON entity ID and sync status in SG and AYON")
update_data = {
CUST_FIELD_CODE_ID: ay_entity.id,
CUST_FIELD_CODE_SYNC: sg_entity_sync_status
}
# Update Shotgrid entity with Ayon ID and sync status
sg_session.update(
sg_ay_dict["attribs"][SHOTGRID_TYPE_ATTRIB],
sg_entity_id,
update_data
)
ay_entity.data.update(update_data)

# If the entity has children, add it to the deck
for sg_child in sg_ay_dicts_parents.get(entity_id, []):
sg_ay_dicts_deck.append((ay_entity, sg_child))

for sg_child_id in sg_ay_dicts_parents.get(sg_entity_id, []):
sg_ay_dicts_deck.append((ay_entity, sg_child_id))

try:
entity_hub.commit_changes()
except Exception as e:
logging.error("Unable to commit all entities to AYON!")
log_traceback(e)

logging.info(
"Processed entities successfully!. "
f"Amount of entities: {len(processed_ids)}"
)
# Sync project attributes from Shotgrid to AYON
entity_hub.project_entity.attribs.set(
SHOTGRID_ID_ATTRIB,
Expand All @@ -171,8 +192,7 @@ def match_shotgrid_hierarchy_in_ayon(
for ay_attrib, sg_attrib in custom_attribs_map.items():
attrib_value = sg_project.get(sg_attrib) \
or sg_project.get(f"sg_{sg_attrib}")

logging.debug(f"Checking {sg_attrib} -> {attrib_value}")

if attrib_value is None:
continue

Expand Down Expand Up @@ -210,6 +230,14 @@ def _create_new_entity(entity_hub, parent_entity, sg_ay_dict):
sg_ay_dict (dict): Ayon ShotGrid entity to create.
"""
if sg_ay_dict["type"].lower() == "task":
# only create if parent_entity type is not project
if parent_entity.entity_type == "project":
logging.warning(
f"Can't create task '{sg_ay_dict['name']}' under project "
"'{parent_entity.name}'. Parent should not be project it self!"
)
return

ay_entity = entity_hub.add_new_task(
sg_ay_dict["task_type"],
name=sg_ay_dict["name"],
Expand All @@ -229,16 +257,22 @@ def _create_new_entity(entity_hub, parent_entity, sg_ay_dict):
attribs=sg_ay_dict["attribs"],
data=sg_ay_dict["data"],
)

# TODO: this doesn't work yet
status = sg_ay_dict["attribs"].get("status")
if status:
ay_entity.status = status
# TODO: Implement status update
try:
# INFO: it was causing error so trying to set status directly
ay_entity.status = status
except ValueError as e:
# `ValueError: Status ip is not available on project.`
# logging.warning(f"Status sync not implemented: {e}")
pass

tags = sg_ay_dict["attribs"].get("tags")
if tags:
ay_entity.tags = [tag["name"] for tag in tags]

logging.debug(f"Created new entity: {ay_entity.name} ({ay_entity.id})")
logging.debug(f"Parent is: {parent_entity.name} ({parent_entity.id})")
logging.info(f"Created new entity: {ay_entity.name} ({ay_entity.id})")
return ay_entity
Loading