From 39b08f6057916c8f93652d730ae0f854e65315d8 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Fri, 1 Mar 2019 17:10:29 +0100 Subject: [PATCH] ftrack server added to pype/ftrack --- pype/ftrack/__init__.py | 1 + pype/ftrack/ftrack_server/__init__.py | 8 + .../{ => ftrack_server}/event_server.py | 2 +- pype/ftrack/ftrack_server/event_server_cli.py | 121 ++++++++++++++ pype/ftrack/ftrack_server/ftrack_server.py | 157 ++++++++++++++++++ 5 files changed, 288 insertions(+), 1 deletion(-) create mode 100644 pype/ftrack/ftrack_server/__init__.py rename pype/ftrack/{ => ftrack_server}/event_server.py (96%) create mode 100644 pype/ftrack/ftrack_server/event_server_cli.py create mode 100644 pype/ftrack/ftrack_server/ftrack_server.py diff --git a/pype/ftrack/__init__.py b/pype/ftrack/__init__.py index 1224715000..bf18979e91 100644 --- a/pype/ftrack/__init__.py +++ b/pype/ftrack/__init__.py @@ -1 +1,2 @@ from .lib import * +from .ftrack_server import * diff --git a/pype/ftrack/ftrack_server/__init__.py b/pype/ftrack/ftrack_server/__init__.py new file mode 100644 index 0000000000..b7f8651da0 --- /dev/null +++ b/pype/ftrack/ftrack_server/__init__.py @@ -0,0 +1,8 @@ +from .ftrack_server import FtrackServer +from . import event_server, event_server_cli + +__all__ = [ + 'event_server', + 'event_server_cli', + 'FtrackServer' +] diff --git a/pype/ftrack/event_server.py b/pype/ftrack/ftrack_server/event_server.py similarity index 96% rename from pype/ftrack/event_server.py rename to pype/ftrack/ftrack_server/event_server.py index 9c6207d6a2..24cc5f22ee 100644 --- a/pype/ftrack/event_server.py +++ b/pype/ftrack/ftrack_server/event_server.py @@ -1,6 +1,6 @@ import sys from pype.ftrack import credentials, login_dialog as login_dialog -from FtrackServer import FtrackServer +from . import FtrackServer from app.vendor.Qt import QtWidgets from pype import api diff --git a/pype/ftrack/ftrack_server/event_server_cli.py b/pype/ftrack/ftrack_server/event_server_cli.py new file mode 100644 index 0000000000..b09a1cd851 --- /dev/null +++ b/pype/ftrack/ftrack_server/event_server_cli.py @@ -0,0 +1,121 @@ +import os +import json +import appdirs +import ftrack_api +from . import FtrackServer +from app import api + +log = api.Logger.getLogger(__name__) + + +def check_cred(user, key): + os.environ["FTRACK_API_USER"] = user + os.environ["FTRACK_API_KEY"] = key + + try: + session = ftrack_api.Session() + session.close() + return True + except Exception: + return False + + +def ask_yes_no(): + possible_yes = ["y", "yes"] + possible_no = ["n", "no"] + log.info("Y/N:") + cont = input() + if cont.lower() in possible_yes: + return True + elif cont.lower() in possible_no: + return False + else: + log.info( + "Invalid input. Possible entries: [y, yes, n, no]. Try it again:" + ) + return ask_yes_no() + + +def cli_login(): + config_path = os.path.normpath(appdirs.user_data_dir('pype-app', 'pype')) + config_name = 'ftrack_event_cred.json' + event_credentials_file = os.path.join(config_path, config_name) + + if not os.path.isdir(config_path): + os.makedirs(config_path) + if not os.path.exists(event_credentials_file): + open(event_credentials_file, 'w').close() + enter_cred = True + + with open(event_credentials_file, 'r') as fp: + try: + cred_data = json.load(fp) + except Exception: + cred_data = {} + + user = cred_data.get("FTRACK_API_USER", None) + key = cred_data.get("FTRACK_API_KEY", None) + auto = cred_data.get("AUTO_CONNECT", False) + if user is None or key is None: + log.info("Credentials are not set. Do you want to enter them now? (Y/N)") + if ask_yes_no() is False: + log.info("Exiting...") + return + elif check_cred(user, key): + if auto is False: + log.info("Do you want to log with username {}? (Y/N)".format( + cred_data["FTRACK_API_USER"] + )) + if ask_yes_no(): + enter_cred = False + else: + enter_cred = False + else: + log.info( + "Stored credentials are not valid. " + "Do you want enter them now?(Y/N)" + ) + if ask_yes_no() is False: + log.info("Exiting...") + return + + while enter_cred: + log.info("Please enter Ftrack API User:") + user = input() + log.info("And now enter Ftrack API Key:") + key = input() + if check_cred(user, key): + export = { + "FTRACK_API_USER": user, + "FTRACK_API_KEY": key + } + log.info( + "Credentials are valid." + " Do you want to auto-connect next time?(Y/N)" + ) + if ask_yes_no(): + export["AUTO_CONNECT"] = True + + with open(event_credentials_file, 'w') as fp: + json.dump(export, fp) + enter_cred = False + break + else: + log.info( + "Entered credentials are not valid." + " Do you want to try it again?(Y/N)" + ) + if ask_yes_no() is False: + log.info("Exiting...") + return + + server = FtrackServer('event') + server.run_server() + + +def main(): + cli_login() + + +if (__name__ == ('__main__')): + main() diff --git a/pype/ftrack/ftrack_server/ftrack_server.py b/pype/ftrack/ftrack_server/ftrack_server.py new file mode 100644 index 0000000000..91caff216e --- /dev/null +++ b/pype/ftrack/ftrack_server/ftrack_server.py @@ -0,0 +1,157 @@ +import os +import sys +import types +import importlib +import ftrack_api +import time +import logging +from app.api import Logger + +log = Logger.getLogger(__name__) + +""" +# Required - Needed for connection to Ftrack +FTRACK_SERVER # Ftrack server e.g. "https://myFtrack.ftrackapp.com" +FTRACK_API_KEY # Ftrack user's API key "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx" +FTRACK_API_USER # Ftrack username e.g. "user.name" + +# Required - Paths to folder with actions +FTRACK_ACTIONS_PATH # Paths to folders where are located actions + - EXAMPLE: "M:/FtrackApi/../actions/" +FTRACK_EVENTS_PATH # Paths to folders where are located actions + - EXAMPLE: "M:/FtrackApi/../events/" + +# Required - Needed for import included modules +PYTHONPATH # Path to ftrack_api and paths to all modules used in actions + - path to ftrack_action_handler, etc. +""" + + +class FtrackServer(): + def __init__(self, type='action'): + """ + - 'type' is by default set to 'action' - Runs Action server + - enter 'event' for Event server + + EXAMPLE FOR EVENT SERVER: + ... + server = FtrackServer('event') + server.run_server() + .. + """ + # set Ftrack logging to Warning only - OPTIONAL + ftrack_log = logging.getLogger("ftrack_api") + ftrack_log.setLevel(logging.WARNING) + + self.type = type + self.actionsAvailable = True + self.eventsAvailable = True + # Separate all paths + if "FTRACK_ACTIONS_PATH" in os.environ: + all_action_paths = os.environ["FTRACK_ACTIONS_PATH"] + self.actionsPaths = all_action_paths.split(os.pathsep) + else: + self.actionsAvailable = False + + if "FTRACK_EVENTS_PATH" in os.environ: + all_event_paths = os.environ["FTRACK_EVENTS_PATH"] + self.eventsPaths = all_event_paths.split(os.pathsep) + else: + self.eventsAvailable = False + + def stop_session(self): + if self.session.event_hub.connected is True: + self.session.event_hub.disconnect() + self.session.close() + self.session = None + + def set_files(self, paths): + # Iterate all paths + functions = [] + for path in paths: + # add path to PYTHON PATH + if path not in sys.path: + sys.path.append(path) + + # Get all modules with functions + for file in os.listdir(path): + # Get only .py files with action functions + try: + if '.pyc' in file or '.py' not in file: + continue + + ignore = 'ignore_me' + mod = importlib.import_module(os.path.splitext(file)[0]) + importlib.reload(mod) + mod_functions = dict( + [ + (name, function) + for name, function in mod.__dict__.items() + if isinstance(function, types.FunctionType) or + name == ignore + ] + ) + # Don't care about ignore_me files + if ( + ignore in mod_functions and + mod_functions[ignore] is True + ): + continue + # separate files by register function + if 'register' not in mod_functions: + msg = ( + '"{0}" - Missing register method' + ).format(file, self.type) + log.warning(msg) + continue + + functions.append({ + 'name': file, + 'register': mod_functions['register'] + }) + except Exception as e: + msg = 'Loading of file "{}" failed ({})'.format( + file, str(e) + ) + log.warning(msg) + + if len(functions) < 1: + raise Exception + + for function in functions: + try: + function['register'](self.session) + except Exception as e: + msg = '"{}" - register was not successful ({})'.format( + function['name'], str(e) + ) + log.warning(msg) + time.sleep(0.05) + + def run_server(self): + self.session = ftrack_api.Session(auto_connect_event_hub=True,) + + if self.type.lower() == 'event': + if self.eventsAvailable is False: + msg = ( + 'FTRACK_EVENTS_PATH is not set' + ', event server won\'t launch' + ) + log.error(msg) + return + self.set_files(self.eventsPaths) + else: + if self.actionsAvailable is False: + msg = ( + 'FTRACK_ACTIONS_PATH is not set' + ', action server won\'t launch' + ) + log.error(msg) + return + self.set_files(self.actionsPaths) + + log.info(60*"*") + log.info('Registration of actions/events has finished!') + + # keep event_hub on session running + self.session.event_hub.wait()