Skip to content

Commit

Permalink
Merge pull request #102 from ynput/enhancement/AY-5537_Shotgrid-renam…
Browse files Browse the repository at this point in the history
…ing-assets

Asset renaming synchronization
  • Loading branch information
jakubjezek001 authored May 31, 2024
2 parents 0ecc74d + 4feec83 commit 1f3ceb1
Show file tree
Hide file tree
Showing 20 changed files with 204 additions and 136 deletions.
2 changes: 1 addition & 1 deletion client/ayon_shotgrid/version.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
# -*- coding: utf-8 -*-
"""Package declaring shotgrid addon version."""
__version__ = "0.4.2-dev.3"
__version__ = "0.4.2-dev.5"
2 changes: 1 addition & 1 deletion package.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
name = "shotgrid"
title = "Shotgrid"
version = "0.4.2-dev.3"
version = "0.4.2-dev.5"
client_dir = "ayon_shotgrid"

services = {
Expand Down
15 changes: 6 additions & 9 deletions service_tools/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -92,22 +92,19 @@ def main():
sys.path.insert(0, path)

if service_name == "processor":
from processor import ShotgridProcessor
from processor import service_main

shotgrid_processor = ShotgridProcessor()
sys.exit(shotgrid_processor.start_processing())
service_main()

elif service_name == "leecher":
from leecher import ShotgridListener
from leecher import service_main

shotgrid_listener = ShotgridListener()
sys.exit(shotgrid_listener.start_listening())
service_main()

else:
from transmitter import ShotgridTransmitter
from transmitter import service_main

shotgrid_transmitter = ShotgridTransmitter()
sys.exit(shotgrid_transmitter.start_processing())
service_main()


if __name__ == "__main__":
Expand Down
4 changes: 2 additions & 2 deletions services/leecher/leecher/__init__.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
from .listener import ShotgridListener
from .listener import ShotgridListener, service_main


__all__ = (
"service_main",
"ShotgridListener",
)

8 changes: 2 additions & 6 deletions services/leecher/leecher/__main__.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,5 @@
import sys

from .listener import ShotgridListener
from .listener import service_main


if __name__ == "__main__":
shotgrid_listener = ShotgridListener()
sys.exit(shotgrid_listener.start_listening())

service_main()
55 changes: 33 additions & 22 deletions services/leecher/leecher/listener.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,9 @@
import time
import signal
import socket
from pprint import pformat
import traceback
from typing import Any
from pprint import pformat

from utils import get_logger

Expand All @@ -36,7 +37,6 @@ def __init__(self):
self.log.info("Initializing the Shotgrid Listener.")

try:
ayon_api.init_service()
self.settings = ayon_api.get_service_addon_settings()
service_settings = self.settings["service_settings"]

Expand All @@ -47,6 +47,14 @@ def __init__(self):
# get server op related ShotGrid script api properties
shotgrid_secret = ayon_api.get_secret(
service_settings["script_key"])

if isinstance(shotgrid_secret, list):
raise ValueError(
"Shotgrid API Key not found. Make sure to set it in the "
"Addon System settings. "
"`ayon+settings://shotgrid/service_settings/script_key`"
)

self.sg_api_key = shotgrid_secret.get("value")
if not self.sg_api_key:
raise ValueError(
Expand Down Expand Up @@ -129,19 +137,19 @@ def _build_shotgrid_filters(self, sg_projects):

filters.append(["project", "in", sg_projects])

sg_event_types = []

# TODO: Create a complex filter so skip event types "_Change" that
# we don't handle.
for entity_type in self.sg_enabled_entities:
for event_name in SG_EVENT_TYPES:
sg_event_types.append(event_name.format(entity_type))

if sg_event_types:
if sg_event_types := self._get_supported_event_types():
filters.append(["event_type", "in", sg_event_types])

return filters

def _get_supported_event_types(self) -> list[str]:
sg_event_types = []
for entity_type in self.sg_enabled_entities:
sg_event_types.extend(
event_name.format(entity_type) for event_name in SG_EVENT_TYPES
)
return sg_event_types

def _get_last_event_processed(self, sg_filters):
"""Find the Event ID for the last SG processed event.
Expand Down Expand Up @@ -169,14 +177,6 @@ def _get_last_event_processed(self, sg_filters):

return last_event_id

def _get_supported_event_types(self) -> list[str]:
sg_event_types = []
for entity_type in self.sg_enabled_entities:
sg_event_types.extend(
event_name.format(entity_type) for event_name in SG_EVENT_TYPES
)
return sg_event_types

def start_listening(self):
"""Main loop querying the Shotgrid database for new events
Expand Down Expand Up @@ -259,10 +259,14 @@ def start_listening(self):

self.send_shotgrid_event_to_ayon(event, sg_projects_by_id)

except Exception as err:
self.log.error(err, exc_info=True)
except Exception:
self.log.error(traceback.format_exc())

self.log.debug(
f"Leecher waiting {self.shotgrid_polling_frequency} seconds..."
)
time.sleep(self.shotgrid_polling_frequency)
continue

def _is_api_user_event(self, event: dict[str, Any]) -> bool:
"""Check if the event was caused by an API user.
Expand Down Expand Up @@ -297,7 +301,8 @@ def send_shotgrid_event_to_ayon(
# fix non serializable datetime
payload["created_at"] = payload["created_at"].isoformat()

if payload.get("meta", {}).get("entity_type", "Undefined") == "Project":
payload_meta = payload.get("meta", {})
if payload_meta.get("entity_type", "Undefined") == "Project":
project_name = payload.get("entity", {}).get("name", "Undefined")
project_id = payload.get("entity", {}).get("id", "Undefined")
else:
Expand Down Expand Up @@ -325,3 +330,9 @@ def send_shotgrid_event_to_ayon(
)

self.log.info("Dispatched Ayon event with payload:", payload)


def service_main():
ayon_api.init_service()
shotgrid_listener = ShotgridListener()
sys.exit(shotgrid_listener.start_listening())
2 changes: 1 addition & 1 deletion services/leecher/pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[tool.poetry]
name = "shotgrid-leecher"
version = "0.4.2-dev.3"
version = "0.4.2-dev.5"
description = "Shotgrid Integration for Ayon"
authors = ["Oscar Domingo <[email protected]>"]

Expand Down
7 changes: 5 additions & 2 deletions services/processor/processor/__init__.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
from .processor import ShotgridProcessor
from .processor import (
ShotgridProcessor,
service_main,
)


__all__ = (
"ShotgridProcessor",
"service_main",
)

7 changes: 3 additions & 4 deletions services/processor/processor/__main__.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import sys
from .processor import ShotgridProcessor
from .processor import service_main


if __name__ == "__main__":
shotgrid_processor = ShotgridProcessor()
sys.exit(shotgrid_processor.start_processing())
service_main()
12 changes: 5 additions & 7 deletions services/processor/processor/handlers/shotgrid_event.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,27 +9,25 @@

def process_event(
sg_processor,
**kwargs,
event,
):
"""React to Shotgrid Events.
Events with the action `shotgrid-event` will trigger this
function, where we attempt to replicate a change coming form Shotgrid, like
creating a new Shot, renaming a Task, etc.
"""
sg_payload = kwargs.get("sg_payload", {})
sg_payload = event.get("sg_payload", {})
if not sg_payload:
raise ValueError("The Event payload is empty!")

if not sg_payload.get("meta", {}):
raise ValueError("The Event payload is missing the action to perform!")

hub = AyonShotgridHub(
kwargs.get("project_name"),
kwargs.get("project_code"),
sg_processor.sg_url,
sg_processor.sg_api_key,
sg_processor.sg_script_name,
sg_processor.get_sg_connection(),
event.get("project_name"),
event.get("project_code"),
sg_project_code_field=sg_processor.sg_project_code_field,
custom_attribs_map=sg_processor.custom_attribs_map,
custom_attribs_types=sg_processor.custom_attribs_types,
Expand Down
13 changes: 5 additions & 8 deletions services/processor/processor/handlers/sync_projects.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@

def process_event(
sg_processor,
**kwargs,
event,
):
"""Synchronize a project between AYON and Shotgrid.
Expand All @@ -18,11 +18,9 @@ def process_event(
Shotgrid or AYON, and replicate it's structure in the other platform.
"""
hub = AyonShotgridHub(
kwargs.get("project_name"),
kwargs.get("project_code"),
sg_processor.sg_url,
sg_processor.sg_api_key,
sg_processor.sg_script_name,
sg_processor.get_sg_connection(),
event.get("project_name"),
event.get("project_code"),
sg_project_code_field=sg_processor.sg_project_code_field,
custom_attribs_map=sg_processor.custom_attribs_map,
custom_attribs_types=sg_processor.custom_attribs_types,
Expand All @@ -32,6 +30,5 @@ def process_event(
# This will ensure that the project exists in both platforms.
hub.create_project()
sync_source = (
"ayon" if kwargs.get("action") == "sync-from-ayon" else "shotgrid"
)
"ayon" if event.get("action") == "sync-from-ayon" else "shotgrid")
hub.synchronize_projects(source=sync_source)
51 changes: 49 additions & 2 deletions services/processor/processor/processor.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,19 +6,22 @@
related events.
"""
import os
import sys
from pprint import pformat
import time
import types
import socket
import importlib.machinery
import traceback

import ayon_api
import shotgun_api3

from utils import get_logger


class ShotgridProcessor:

_sg: shotgun_api3.Shotgun = None
log = get_logger(__file__)

def __init__(self):
Expand Down Expand Up @@ -54,6 +57,14 @@ def __init__(self):
# get server op related ShotGrid script api properties
shotgrid_secret = ayon_api.get_secret(
service_settings["script_key"])

if isinstance(shotgrid_secret, list):
raise ValueError(
"Shotgrid API Key not found. Make sure to set it in the "
"Addon System settings. "
"`ayon+settings://shotgrid/service_settings/script_key`"
)

self.sg_api_key = shotgrid_secret.get("value")
if not self.sg_api_key:
raise ValueError(
Expand Down Expand Up @@ -136,6 +147,33 @@ def _get_handlers(self):

return handlers_dict

def get_sg_connection(self):
"""Ensure we can talk to AYON and Shotgrid.
Start connections to the APIs and catch any possible error, we abort if
this steps fails for any reason.
"""

if self._sg is None:
try:
self._sg = shotgun_api3.Shotgun(
self.sg_url,
script_name=self.sg_script_name,
api_key=self.sg_api_key
)
except Exception as e:
self.log.error("Unable to create Shotgrid Session.")
raise e

try:
self._sg.connect()

except Exception as e:
self.log.error("Unable to connect to Shotgrid.")
raise e

return self._sg

def start_processing(self):
"""Enroll AYON events of topic `shotgrid.event`
Expand Down Expand Up @@ -192,9 +230,11 @@ def start_processing(self):
),
status="finished"
)
self.log.debug(
f"processing event {pformat(payload)}")
handler.process_event(
self,
**payload,
payload,
)

except Exception:
Expand Down Expand Up @@ -231,3 +271,10 @@ def start_processing(self):

except Exception:
self.log.error(traceback.format_exc())


def service_main():
ayon_api.init_service()

shotgrid_processor = ShotgridProcessor()
sys.exit(shotgrid_processor.start_processing())
2 changes: 1 addition & 1 deletion services/processor/pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[tool.poetry]
name = "shotgrid-processor"
version = "0.4.2-dev.3"
version = "0.4.2-dev.5"
description = "Shotgrid Integration for Ayon"
authors = ["Oscar Domingo <[email protected]>"]

Expand Down
Loading

0 comments on commit 1f3ceb1

Please sign in to comment.