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

Process preparation: Ftrack addon can ensure that is process ready #109

Merged
Merged
Show file tree
Hide file tree
Changes from 14 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
111 changes: 106 additions & 5 deletions client/ayon_ftrack/ftrack_addon.py
Original file line number Diff line number Diff line change
@@ -1,13 +1,24 @@
import os
import tempfile
import json

import requests
import ayon_api

from ayon_core.addon import (
AYONAddon,
ITrayAddon,
IPluginPaths,
)
from ayon_core.lib import Logger
from ayon_core.lib import Logger, run_ayon_launcher_process
from ayon_core.settings import get_project_settings, get_studio_settings
from ayon_core.tools.tray import get_tray_server_url

from ayon_ftrack.lib.credentials import (
save_credentials,
get_credentials,
check_credentials,
)

from .version import __version__

Expand Down Expand Up @@ -44,6 +55,9 @@ def initialize(self, settings):
self.timers_manager_connector = None
self._timers_manager_addon = None

def webserver_initialization(self, web_manager):
self._tray_wrapper.webserver_initialization(web_manager)

def get_ftrack_url(self):
"""Resolved ftrack url.

Expand Down Expand Up @@ -140,8 +154,7 @@ def create_ftrack_session(self, **session_kwargs):
api_user = os.environ.get("FTRACK_API_USER")

if not api_key or not api_user:
from .lib import credentials
cred = credentials.get_credentials()
cred = get_credentials()
api_user = cred.get("username")
api_key = cred.get("api_key")

Expand Down Expand Up @@ -178,6 +191,72 @@ def stop_timer(self):
if self._tray_wrapper:
self._tray_wrapper.stop_timer_manager()

def ensure_is_process_ready(self, context):
"""Ensure addon is ready for process.

Args:
context (ProcessContext): Process context.

"""
# Safe to support older ayon-core without 'ProcessPreparationError'
from ayon_core.addon import ProcessPreparationError
from ayon_ftrack.common import is_ftrack_enabled_in_settings

# Do not continue if Ftrack is not enabled in settings
if context.project_name:
settings = get_project_settings(context.project_name)
else:
settings = get_studio_settings()

if not is_ftrack_enabled_in_settings(settings):
return

# Not sure if this should crash or silently continue?
server_url = self.get_ftrack_url()
if not server_url:
return

username = os.getenv("FTRACK_API_USER")
api_key = os.getenv("FTRACK_API_KEY")

if (
username and api_key
and check_credentials(username, api_key, server_url)
):
self.set_credentials_to_env(username, api_key)
return

username, api_key = self.get_credentials()
if (
username and api_key
and check_credentials(username, api_key, server_url)
):
self.set_credentials_to_env(username, api_key)
return

if context.headless:
raise ProcessPreparationError(
"Ftrack credentials are not set. Cannot handle the situation"
" in headless mode."
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This needs some artist friendly message. Marking @BigRoy and @mkolar for suggestions.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
"Ftrack credentials are not set. Cannot handle the situation"
" in headless mode."
"Ftrack login details are missing. Unable to proceed without a user interface."

)

username, api_key = self._ask_for_credentials(server_url)
if username and api_key:
self.set_credentials_to_env(username, api_key)
# Send the credentials to the running tray
save_credentials(username, api_key, self.get_ftrack_url())
tray_url = get_tray_server_url()
if tray_url:
requests.post(
f"{tray_url}/addons/ftrack/credentials",
json={"username": username, "api_key": api_key},
)
return

raise ProcessPreparationError(
"Failed to log to ftrack. Cannot continue with the process."
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This needs some artist friendly message. Marking @BigRoy and @mkolar for suggestions.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
"Failed to log to ftrack. Cannot continue with the process."
"Unable to connect to Ftrack. The process cannot proceed without this connection."

)

def register_timers_manager(self, timers_manager_addon):
self._timers_manager_addon = timers_manager_addon

Expand Down Expand Up @@ -212,11 +291,33 @@ def get_task_time(self, project_name, folder_path, task_name):
def get_credentials(self):
# type: () -> tuple
"""Get local Ftrack credentials."""
from .lib import credentials

cred = credentials.get_credentials(self.ftrack_url)
cred = get_credentials(self.ftrack_url)
return cred.get("username"), cred.get("api_key")

@staticmethod
def _ask_for_credentials(ftrack_url):
login_script = os.path.join(
FTRACK_ADDON_DIR, "tray", "login_dialog.py"
)
with tempfile.NamedTemporaryFile(
mode="w", prefix="ay_ftrack", suffix=".json", delete=False
) as tmp:
json_path = tmp.name
json.dump({"server_url": ftrack_url}, tmp.file)

run_ayon_launcher_process(
"--skip-bootstrap",
login_script, json_path,
add_sys_paths=True,
creationflags=0,

)

with open(json_path, "r") as stream:
data = json.load(stream)
return data.get("username"), data.get("api_key")


def _check_ftrack_url(url):
import requests
Expand Down
80 changes: 57 additions & 23 deletions client/ayon_ftrack/tray/ftrack_tray.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@

import ftrack_api
from qtpy import QtCore, QtWidgets, QtGui
from aiohttp.web import Response, json_response

from ayon_core import resources
from ayon_core.lib import Logger
Expand All @@ -17,8 +18,8 @@


class FtrackTrayWrapper:
def __init__(self, module):
self._addon = module
def __init__(self, addon):
self._addon = addon
self.log = Logger.get_logger(self.__class__.__name__)

self.thread_action_server = None
Expand All @@ -30,7 +31,7 @@ def __init__(self, module):
self.bool_action_thread_running = False
self.bool_timer_event = False

self.widget_login = login_dialog.CredentialsDialog(module)
self.widget_login = login_dialog.TrayCredentialsDialog(addon)
self.widget_login.login_changed.connect(self.on_login_change)
self.widget_login.logout_signal.connect(self.on_logout)

Expand All @@ -43,6 +44,38 @@ def __init__(self, module):
resources.get_resource("icons", "circle_orange.png")
)

def webserver_initialization(self, web_manager):
web_manager.add_addon_route(
self._addon.name,
"credentials",
"POST",
self._web_credentials_change
)
web_manager.add_addon_route(
self._addon.name,
"credentials",
"GET",
self._web_get_credentials
)

async def _web_credentials_change(self, request):
data = await request.json()
username = data.get("username")
api_key = data.get("api_key")
self.set_credentials(username, api_key)
return Response(status=200)

async def _web_get_credentials(self, _):
username = api_key = None
if self.bool_logged:
username = self.widget_login.username
api_key = self.widget_login.api_key

return json_response({
"username": username,
"api_key": api_key
})

def show_login_widget(self):
self.widget_login.show()
self.widget_login.activateWindow()
Expand All @@ -52,31 +85,33 @@ def show_ftrack_browser(self):
QtGui.QDesktopServices.openUrl(self._addon.ftrack_url)

def validate(self):
validation = False
cred = credentials.get_credentials()
ft_user = cred.get("username")
ft_api_key = cred.get("api_key")
validation = credentials.check_credentials(ft_user, ft_api_key)
validation = self.set_credentials(
cred.get("username"), cred.get("api_key")
)
if not validation:
self.log.info("Please sign in to Ftrack")
self.bool_logged = False
self.show_login_widget()
self.set_menu_visibility()

return validation

def set_credentials(self, username, api_key):
validation = credentials.check_credentials(username, api_key)
if validation:
self.widget_login.set_credentials(ft_user, ft_api_key)
self._addon.set_credentials_to_env(ft_user, ft_api_key)
self.widget_login.set_credentials(username, api_key)
self._addon.set_credentials_to_env(username, api_key)
self.log.info("Connected to Ftrack successfully")
self.on_login_change()

return validation

if not validation and ft_user and ft_api_key:
if not validation and username and api_key:
server = self._addon.get_ftrack_url()
self.log.warning(
"Current Ftrack credentials are not valid. {}: {} - {}".format(
str(os.environ.get("FTRACK_SERVER")), ft_user, ft_api_key
)
f"Current Ftrack credentials are not valid. {server}:"
f" {username} - {api_key}"
)

self.log.info("Please sign in to Ftrack")
self.bool_logged = False
self.show_login_widget()
self.set_menu_visibility()

return validation

# Necessary - login_dialog works with this method after logging in
Expand All @@ -85,10 +120,9 @@ def on_login_change(self):

if self.action_credentials:
self.action_credentials.setIcon(self.icon_logged)
username, _ = self.widget_login.get_credentials()
self.action_credentials.setToolTip(
"Logged as user \"{}\"".format(
self.widget_login.user_input.text()
)
f"Logged as user \"{username}\""
)

self.set_menu_visibility()
Expand Down
Loading