From 2537b2973266a2bf227520ddae540f6381abc43b Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Mon, 2 Dec 2024 15:47:16 +0000 Subject: [PATCH] deploy: 57c4df3ebe8aff2fd54ac8471c78191cdd2af76b --- .buildinfo | 4 + .nojekyll | 0 _images/AYON_blackG_dot.svg | 41 + _modules/ayon_api/_api.html | 6821 ++++++++ _modules/ayon_api/entity_hub.html | 3889 +++++ _modules/ayon_api/events.html | 239 + _modules/ayon_api/exceptions.html | 308 + _modules/ayon_api/graphql.html | 1250 ++ _modules/ayon_api/graphql_queries.html | 873 ++ _modules/ayon_api/operations.html | 1663 ++ _modules/ayon_api/server_api.html | 8939 +++++++++++ _modules/ayon_api/utils.html | 1115 ++ _modules/index.html | 195 + _sources/ayon_api.constants.rst.txt | 7 + _sources/ayon_api.entity_hub.rst.txt | 7 + _sources/ayon_api.events.rst.txt | 7 + _sources/ayon_api.exceptions.rst.txt | 7 + _sources/ayon_api.graphql.rst.txt | 7 + _sources/ayon_api.graphql_queries.rst.txt | 7 + _sources/ayon_api.operations.rst.txt | 7 + _sources/ayon_api.rst.txt | 24 + _sources/ayon_api.server_api.rst.txt | 7 + _sources/ayon_api.utils.rst.txt | 7 + _sources/ayon_api.version.rst.txt | 7 + _sources/index.rst.txt | 68 + _sources/modules.rst.txt | 7 + _static/AYON_blackG_dot.svg | 41 + .../_sphinx_javascript_frameworks_compat.js | 134 + _static/basic.css | 900 ++ _static/css/badge_only.css | 1 + _static/css/fonts/fira-mono-latin-400.woff | Bin 0 -> 21024 bytes _static/css/fonts/fira-mono-latin-400.woff2 | Bin 0 -> 16836 bytes _static/css/fonts/fira-mono-latin-500.woff | Bin 0 -> 20820 bytes _static/css/fonts/fira-mono-latin-500.woff2 | Bin 0 -> 16632 bytes _static/css/fonts/fira-mono-latin-700.woff | Bin 0 -> 22628 bytes _static/css/fonts/fira-mono-latin-700.woff2 | Bin 0 -> 18236 bytes _static/css/fonts/fontawesome-webfont.eot | Bin 0 -> 165742 bytes _static/css/fonts/fontawesome-webfont.svg | 2671 ++++ _static/css/fonts/fontawesome-webfont.ttf | Bin 0 -> 165548 bytes _static/css/fonts/fontawesome-webfont.woff | Bin 0 -> 98024 bytes _static/css/fonts/fontawesome-webfont.woff2 | Bin 0 -> 77160 bytes _static/css/fonts/inter-italic-var.woff2 | Bin 0 -> 240688 bytes _static/css/fonts/inter-roman-var.woff2 | Bin 0 -> 226368 bytes _static/css/theme.css | 4 + _static/doctools.js | 156 + _static/documentation_options.js | 14 + _static/favicon.ico | Bin 0 -> 15086 bytes _static/file.png | Bin 0 -> 286 bytes _static/jquery-3.6.0.js | 10881 +++++++++++++ _static/jquery.js | 2 + _static/js/badge_only.js | 1 + _static/js/html5shiv-printshiv.min.js | 4 + _static/js/html5shiv.min.js | 4 + _static/js/theme.js | 1 + _static/language_data.js | 199 + _static/minus.png | Bin 0 -> 90 bytes _static/plus.png | Bin 0 -> 90 bytes _static/pygments.css | 44 + _static/searchtools.js | 566 + _static/sphinx_highlight.js | 144 + _static/underscore-1.13.1.js | 2042 +++ _static/underscore.js | 6 + ayon_api.constants.html | 458 + ayon_api.entity_hub.html | 3062 ++++ ayon_api.events.html | 469 + ayon_api.exceptions.html | 551 + ayon_api.graphql.html | 1119 ++ ayon_api.graphql_queries.html | 543 + ayon_api.html | 13059 ++++++++++++++++ ayon_api.operations.html | 1487 ++ ayon_api.server_api.html | 6064 +++++++ ayon_api.utils.html | 1335 ++ ayon_api.version.html | 456 + genindex.html | 3007 ++++ index.html | 713 + modules.html | 871 ++ objects.inv | Bin 0 -> 7628 bytes py-modindex.html | 256 + search.html | 205 + searchindex.js | 1 + 80 files changed, 76977 insertions(+) create mode 100644 .buildinfo create mode 100644 .nojekyll create mode 100644 _images/AYON_blackG_dot.svg create mode 100644 _modules/ayon_api/_api.html create mode 100644 _modules/ayon_api/entity_hub.html create mode 100644 _modules/ayon_api/events.html create mode 100644 _modules/ayon_api/exceptions.html create mode 100644 _modules/ayon_api/graphql.html create mode 100644 _modules/ayon_api/graphql_queries.html create mode 100644 _modules/ayon_api/operations.html create mode 100644 _modules/ayon_api/server_api.html create mode 100644 _modules/ayon_api/utils.html create mode 100644 _modules/index.html create mode 100644 _sources/ayon_api.constants.rst.txt create mode 100644 _sources/ayon_api.entity_hub.rst.txt create mode 100644 _sources/ayon_api.events.rst.txt create mode 100644 _sources/ayon_api.exceptions.rst.txt create mode 100644 _sources/ayon_api.graphql.rst.txt create mode 100644 _sources/ayon_api.graphql_queries.rst.txt create mode 100644 _sources/ayon_api.operations.rst.txt create mode 100644 _sources/ayon_api.rst.txt create mode 100644 _sources/ayon_api.server_api.rst.txt create mode 100644 _sources/ayon_api.utils.rst.txt create mode 100644 _sources/ayon_api.version.rst.txt create mode 100644 _sources/index.rst.txt create mode 100644 _sources/modules.rst.txt create mode 100644 _static/AYON_blackG_dot.svg create mode 100644 _static/_sphinx_javascript_frameworks_compat.js create mode 100644 _static/basic.css create mode 100644 _static/css/badge_only.css create mode 100644 _static/css/fonts/fira-mono-latin-400.woff create mode 100644 _static/css/fonts/fira-mono-latin-400.woff2 create mode 100644 _static/css/fonts/fira-mono-latin-500.woff create mode 100644 _static/css/fonts/fira-mono-latin-500.woff2 create mode 100644 _static/css/fonts/fira-mono-latin-700.woff create mode 100644 _static/css/fonts/fira-mono-latin-700.woff2 create mode 100644 _static/css/fonts/fontawesome-webfont.eot create mode 100644 _static/css/fonts/fontawesome-webfont.svg create mode 100644 _static/css/fonts/fontawesome-webfont.ttf create mode 100644 _static/css/fonts/fontawesome-webfont.woff create mode 100644 _static/css/fonts/fontawesome-webfont.woff2 create mode 100644 _static/css/fonts/inter-italic-var.woff2 create mode 100644 _static/css/fonts/inter-roman-var.woff2 create mode 100644 _static/css/theme.css create mode 100644 _static/doctools.js create mode 100644 _static/documentation_options.js create mode 100644 _static/favicon.ico create mode 100644 _static/file.png create mode 100644 _static/jquery-3.6.0.js create mode 100644 _static/jquery.js create mode 100644 _static/js/badge_only.js create mode 100644 _static/js/html5shiv-printshiv.min.js create mode 100644 _static/js/html5shiv.min.js create mode 100644 _static/js/theme.js create mode 100644 _static/language_data.js create mode 100644 _static/minus.png create mode 100644 _static/plus.png create mode 100644 _static/pygments.css create mode 100644 _static/searchtools.js create mode 100644 _static/sphinx_highlight.js create mode 100644 _static/underscore-1.13.1.js create mode 100644 _static/underscore.js create mode 100644 ayon_api.constants.html create mode 100644 ayon_api.entity_hub.html create mode 100644 ayon_api.events.html create mode 100644 ayon_api.exceptions.html create mode 100644 ayon_api.graphql.html create mode 100644 ayon_api.graphql_queries.html create mode 100644 ayon_api.html create mode 100644 ayon_api.operations.html create mode 100644 ayon_api.server_api.html create mode 100644 ayon_api.utils.html create mode 100644 ayon_api.version.html create mode 100644 genindex.html create mode 100644 index.html create mode 100644 modules.html create mode 100644 objects.inv create mode 100644 py-modindex.html create mode 100644 search.html create mode 100644 searchindex.js diff --git a/.buildinfo b/.buildinfo new file mode 100644 index 0000000000..c2c943d023 --- /dev/null +++ b/.buildinfo @@ -0,0 +1,4 @@ +# Sphinx build info version 1 +# This file hashes the configuration used when building these files. When it is not found, a full rebuild will be done. +config: 87ab68531d5384dbeb8f654928af476b +tags: 645f666f9bcd5a90fca523b33c5a78b7 diff --git a/.nojekyll b/.nojekyll new file mode 100644 index 0000000000..e69de29bb2 diff --git a/_images/AYON_blackG_dot.svg b/_images/AYON_blackG_dot.svg new file mode 100644 index 0000000000..badc20685b --- /dev/null +++ b/_images/AYON_blackG_dot.svg @@ -0,0 +1,41 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/_modules/ayon_api/_api.html b/_modules/ayon_api/_api.html new file mode 100644 index 0000000000..4283069682 --- /dev/null +++ b/_modules/ayon_api/_api.html @@ -0,0 +1,6821 @@ + + + + + + + + + + + ayon_api._api — ayon-python-api 1.0.11 documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + +
+ + + + + +
+ +
+ +
+ + + +
+
+
+ +

Source code for ayon_api._api

+"""Singleton based server api for direct access.
+
+This implementation will be probably the most used part of package. Gives
+option to have singleton connection to Server URL based on environment variable
+values. All public functions and classes are imported in '__init__.py' so
+they're available directly in top module import.
+
+Function that are just wrappers for ServerAPI object are generated
+automatically, and changing them manually can cause issues.
+"""
+
+import os
+import socket
+import typing
+from typing import Optional, List, Dict, Iterable, Generator, Any
+
+from .constants import (
+    SERVER_URL_ENV_KEY,
+    SERVER_API_ENV_KEY,
+)
+from .server_api import ServerAPI, _PLACEHOLDER
+from .exceptions import FailedServiceInit
+from .utils import (
+    NOT_SET,
+    SortOrder,
+    get_default_settings_variant as _get_default_settings_variant,
+)
+
+if typing.TYPE_CHECKING:
+    from ._typing import ActivityType, ActivityReferenceType
+
+
+
[docs]class GlobalServerAPI(ServerAPI): + """Extended server api which also handles storing tokens and url. + + Created object expect to have set environment variables + 'AYON_SERVER_URL'. Also is expecting filled 'AYON_API_KEY' + but that can be filled afterwards with calling 'login' method. + """ + + def __init__( + self, + site_id=None, + client_version=None, + default_settings_variant=None, + ssl_verify=None, + cert=None, + ): + url = self.get_url() + token = self.get_token() + + super(GlobalServerAPI, self).__init__( + url, + token, + site_id, + client_version, + default_settings_variant, + ssl_verify, + cert, + # We want to make sure that server and api key validation + # happens all the time in 'GlobalServerAPI'. + create_session=False, + ) + self.validate_server_availability() + self.create_session() + +
[docs] def login(self, username, password): + """Login to the server or change user. + + If user is the same as current user and token is available the + login is skipped. + + """ + previous_token = self._access_token + super(GlobalServerAPI, self).login(username, password) + if self.has_valid_token and previous_token != self._access_token: + os.environ[SERVER_API_ENV_KEY] = self._access_token
+ +
[docs] @staticmethod + def get_url(): + return os.environ.get(SERVER_URL_ENV_KEY)
+ +
[docs] @staticmethod + def get_token(): + return os.environ.get(SERVER_API_ENV_KEY)
+ +
[docs] @staticmethod + def set_environments(url, token): + """Change url and token environemnts in currently running process. + + Args: + url (str): New server url. + token (str): User's token. + + """ + os.environ[SERVER_URL_ENV_KEY] = url or "" + os.environ[SERVER_API_ENV_KEY] = token or ""
+ + +class GlobalContext: + """Singleton connection holder. + + Goal is to avoid create connection on import which can be dangerous in + some cases. + + """ + _connection = None + + @classmethod + def is_connection_created(cls): + return cls._connection is not None + + @classmethod + def change_token(cls, url, token): + GlobalServerAPI.set_environments(url, token) + if cls._connection is None: + return + + if cls._connection.get_base_url() == url: + cls._connection.set_token(token) + else: + cls.close_connection() + + @classmethod + def close_connection(cls): + if cls._connection is not None: + cls._connection.close_session() + cls._connection = None + + @classmethod + def create_connection(cls, *args, **kwargs): + if cls._connection is not None: + cls.close_connection() + cls._connection = GlobalServerAPI(*args, **kwargs) + return cls._connection + + @classmethod + def get_server_api_connection(cls): + if cls._connection is None: + cls.create_connection() + return cls._connection + + +
[docs]class ServiceContext: + """Helper for services running under server. + + When service is running from server the process receives information about + connection from environment variables. This class helps to initialize the + values without knowing environment variables (that may change over time). + + All what must be done is to call 'init_service' function/method. The + arguments are for cases when the service is running in specific environment + and their values are e.g. loaded from private file or for testing purposes. + + """ + token = None + server_url = None + addon_name = None + addon_version = None + service_name = None + +
[docs] @classmethod + def init_service( + cls, + token=None, + server_url=None, + addon_name=None, + addon_version=None, + service_name=None, + connect=True + ): + token = token or os.environ.get("AYON_API_KEY") + server_url = server_url or os.environ.get("AYON_SERVER_URL") + if not server_url: + raise FailedServiceInit("URL to server is not set") + + if not token: + raise FailedServiceInit( + "Token to server {} is not set".format(server_url) + ) + + addon_name = addon_name or os.environ.get("AYON_ADDON_NAME") + addon_version = addon_version or os.environ.get("AYON_ADDON_VERSION") + service_name = service_name or os.environ.get("AYON_SERVICE_NAME") + + cls.token = token + cls.server_url = server_url + cls.addon_name = addon_name + cls.addon_version = addon_version + cls.service_name = service_name or socket.gethostname() + + # Make sure required environments for GlobalServerAPI are set + GlobalServerAPI.set_environments(cls.server_url, cls.token) + + if connect: + print("Connecting to server \"{}\"".format(server_url)) + con = GlobalContext.get_server_api_connection() + user = con.get_user() + print("Logged in as user \"{}\"".format(user["name"]))
+ + +
[docs]def init_service(*args, **kwargs): + """Initialize current connection from service. + + The service expect specific environment variables. The variables must all + be set to make the connection work as a service. + + """ + ServiceContext.init_service(*args, **kwargs)
+ + +
[docs]def get_service_addon_name(): + """Name of addon which initialized service connection. + + Service context must be initialized to be able to use this function. Call + 'init_service' on you service start to do so. + + Returns: + Union[str, None]: Name of addon or None. + + """ + return ServiceContext.addon_name
+ + +
[docs]def get_service_addon_version(): + """Version of addon which initialized service connection. + + Service context must be initialized to be able to use this function. Call + 'init_service' on you service start to do so. + + Returns: + Union[str, None]: Version of addon or None. + + """ + return ServiceContext.addon_version
+ + +
[docs]def get_service_name(): + """Name of service. + + Service context must be initialized to be able to use this function. Call + 'init_service' on you service start to do so. + + Returns: + Union[str, None]: Name of service if service was registered. + + """ + return ServiceContext.service_name
+ + +
[docs]def get_service_addon_settings(project_name=None): + """Addon settings of service which initialized service. + + Service context must be initialized to be able to use this function. Call + 'init_service' on you service start to do so. + + Args: + project_name (Optional[str]): Project name. + + Returns: + Dict[str, Any]: Addon settings. + + Raises: + ValueError: When service was not initialized. + + """ + addon_name = get_service_addon_name() + addon_version = get_service_addon_version() + if addon_name is None or addon_version is None: + raise ValueError("Service is not initialized") + return get_addon_settings( + addon_name, addon_version, project_name=project_name + )
+ + +
[docs]def is_connection_created(): + """Is global connection created. + + Returns: + bool: True if connection was connected. + + """ + return GlobalContext.is_connection_created()
+ + +
[docs]def create_connection(site_id=None, client_version=None): + """Create global connection. + + Args: + site_id (str): Machine site id/name. + client_version (str): Desktop app version. + + Returns: + GlobalServerAPI: Created connection. + + """ + return GlobalContext.create_connection(site_id, client_version)
+ + +
[docs]def close_connection(): + """Close global connection if is connected.""" + GlobalContext.close_connection()
+ + +
[docs]def change_token(url, token): + """Change connection token for url. + + This function can be also used to change url. + + Args: + url (str): Server url. + token (str): API key token. + + """ + GlobalContext.change_token(url, token)
+ + +
[docs]def set_environments(url, token): + """Set global environments for global connection. + + Args: + url (Union[str, None]): Url to server or None to unset environments. + token (Union[str, None]): API key token to be used for connection. + + """ + GlobalServerAPI.set_environments(url, token)
+ + +
[docs]def get_server_api_connection(): + """Access to global scope object of GlobalServerAPI. + + This access expect to have set environment variables 'AYON_SERVER_URL' + and 'AYON_API_KEY'. + + Returns: + GlobalServerAPI: Object of connection to server. + + """ + return GlobalContext.get_server_api_connection()
+ + +
[docs]def get_default_settings_variant(): + """Default variant used for settings. + + Returns: + Union[str, None]: name of variant or None. + + """ + if not GlobalContext.is_connection_created(): + return _get_default_settings_variant() + con = get_server_api_connection() + return con.get_default_settings_variant()
+ + +# ------------------------------------------------ +# This content is generated automatically. +# ------------------------------------------------ +
[docs]def get_base_url(): + con = get_server_api_connection() + return con.get_base_url()
+ + +
[docs]def get_rest_url(): + con = get_server_api_connection() + return con.get_rest_url()
+ + +
[docs]def get_ssl_verify(): + """Enable ssl verification. + + Returns: + bool: Current state of ssl verification. + + """ + con = get_server_api_connection() + return con.get_ssl_verify()
+ + +
[docs]def set_ssl_verify( + ssl_verify, +): + """Change ssl verification state. + + Args: + ssl_verify (Union[bool, str, None]): Enabled/disable + ssl verification, can be a path to file. + + """ + con = get_server_api_connection() + return con.set_ssl_verify( + ssl_verify=ssl_verify, + )
+ + +
[docs]def get_cert(): + """Current cert file used for connection to server. + + Returns: + Union[str, None]: Path to cert file. + + """ + con = get_server_api_connection() + return con.get_cert()
+ + +
[docs]def set_cert( + cert, +): + """Change cert file used for connection to server. + + Args: + cert (Union[str, None]): Path to cert file. + + """ + con = get_server_api_connection() + return con.set_cert( + cert=cert, + )
+ + +
[docs]def get_timeout(): + """Current value for requests timeout. + + Returns: + float: Timeout value in seconds. + + """ + con = get_server_api_connection() + return con.get_timeout()
+ + +
[docs]def set_timeout( + timeout, +): + """Change timeout value for requests. + + Args: + timeout (Union[float, None]): Timeout value in seconds. + + """ + con = get_server_api_connection() + return con.set_timeout( + timeout=timeout, + )
+ + +
[docs]def get_max_retries(): + """Current value for requests max retries. + + Returns: + int: Max retries value. + + """ + con = get_server_api_connection() + return con.get_max_retries()
+ + +
[docs]def set_max_retries( + max_retries, +): + """Change max retries value for requests. + + Args: + max_retries (Union[int, None]): Max retries value. + + """ + con = get_server_api_connection() + return con.set_max_retries( + max_retries=max_retries, + )
+ + +
[docs]def is_service_user(): + """Check if connection is using service API key. + + Returns: + bool: Used api key belongs to service user. + + """ + con = get_server_api_connection() + return con.is_service_user()
+ + +
[docs]def get_site_id(): + """Site id used for connection. + + Site id tells server from which machine/site is connection created and + is used for default site overrides when settings are received. + + Returns: + Union[str, None]: Site id value or None if not filled. + + """ + con = get_server_api_connection() + return con.get_site_id()
+ + +
[docs]def set_site_id( + site_id, +): + """Change site id of connection. + + Behave as specific site for server. It affects default behavior of + settings getter methods. + + Args: + site_id (Union[str, None]): Site id value, or 'None' to unset. + + """ + con = get_server_api_connection() + return con.set_site_id( + site_id=site_id, + )
+ + +
[docs]def get_client_version(): + """Version of client used to connect to server. + + Client version is AYON client build desktop application. + + Returns: + str: Client version string used in connection. + + """ + con = get_server_api_connection() + return con.get_client_version()
+ + +
[docs]def set_client_version( + client_version, +): + """Set version of client used to connect to server. + + Client version is AYON client build desktop application. + + Args: + client_version (Union[str, None]): Client version string. + + """ + con = get_server_api_connection() + return con.set_client_version( + client_version=client_version, + )
+ + +
[docs]def set_default_settings_variant( + variant, +): + """Change default variant for addon settings. + + Note: + It is recommended to set only 'production' or 'staging' variants + as default variant. + + Args: + variant (str): Settings variant name. It is possible to use + 'production', 'staging' or name of dev bundle. + + """ + con = get_server_api_connection() + return con.set_default_settings_variant( + variant=variant, + )
+ + +
[docs]def get_sender(): + """Sender used to send requests. + + Returns: + Union[str, None]: Sender name or None. + + """ + con = get_server_api_connection() + return con.get_sender()
+ + +
[docs]def set_sender( + sender, +): + """Change sender used for requests. + + Args: + sender (Union[str, None]): Sender name or None. + + """ + con = get_server_api_connection() + return con.set_sender( + sender=sender, + )
+ + +
[docs]def get_sender_type(): + """Sender type used to send requests. + + Sender type is supported since AYON server 1.5.5 . + + Returns: + Union[str, None]: Sender type or None. + + """ + con = get_server_api_connection() + return con.get_sender_type()
+ + +
[docs]def set_sender_type( + sender_type, +): + """Change sender type used for requests. + + Args: + sender_type (Union[str, None]): Sender type or None. + + """ + con = get_server_api_connection() + return con.set_sender_type( + sender_type=sender_type, + )
+ + +
[docs]def get_info(): + """Get information about current used api key. + + By default, the 'info' contains only 'uptime' and 'version'. With + logged user info also contains information about user and machines on + which was logged in. + + Todos: + Use this method for validation of token instead of 'get_user'. + + Returns: + dict[str, Any]: Information from server. + + """ + con = get_server_api_connection() + return con.get_info()
+ + +
[docs]def get_server_version(): + """Get server version. + + Version should match semantic version (https://semver.org/). + + Returns: + str: Server version. + + """ + con = get_server_api_connection() + return con.get_server_version()
+ + +
[docs]def get_server_version_tuple(): + """Get server version as tuple. + + Version should match semantic version (https://semver.org/). + + This function only returns first three numbers of version. + + Returns: + Tuple[int, int, int, Union[str, None], Union[str, None]]: Server + version. + + """ + con = get_server_api_connection() + return con.get_server_version_tuple()
+ + +
[docs]def get_users( + project_name=None, + usernames=None, + fields=None, +): + """Get Users. + + Only administrators and managers can fetch all users. For other users + it is required to pass in 'project_name' filter. + + Args: + project_name (Optional[str]): Project name. + usernames (Optional[Iterable[str]]): Filter by usernames. + fields (Optional[Iterable[str]]): Fields to be queried + for users. + + Returns: + Generator[dict[str, Any]]: Queried users. + + """ + con = get_server_api_connection() + return con.get_users( + project_name=project_name, + usernames=usernames, + fields=fields, + )
+ + +
[docs]def get_user_by_name( + username, + project_name=None, + fields=None, +): + """Get user by name using GraphQl. + + Only administrators and managers can fetch all users. For other users + it is required to pass in 'project_name' filter. + + Args: + username (str): Username. + project_name (Optional[str]): Define scope of project. + fields (Optional[Iterable[str]]): Fields to be queried + for users. + + Returns: + Union[dict[str, Any], None]: User info or None if user is not + found. + + """ + con = get_server_api_connection() + return con.get_user_by_name( + username=username, + project_name=project_name, + fields=fields, + )
+ + +
[docs]def get_user( + username=None, +): + """Get user info using REST endpoit. + + Args: + username (Optional[str]): Username. + + Returns: + Union[dict[str, Any], None]: User info or None if user is not + found. + + """ + con = get_server_api_connection() + return con.get_user( + username=username, + )
+ + +
[docs]def raw_post( + entrypoint, + **kwargs, +): + con = get_server_api_connection() + return con.raw_post( + entrypoint=entrypoint, + **kwargs, + )
+ + +
[docs]def raw_put( + entrypoint, + **kwargs, +): + con = get_server_api_connection() + return con.raw_put( + entrypoint=entrypoint, + **kwargs, + )
+ + +
[docs]def raw_patch( + entrypoint, + **kwargs, +): + con = get_server_api_connection() + return con.raw_patch( + entrypoint=entrypoint, + **kwargs, + )
+ + +
[docs]def raw_get( + entrypoint, + **kwargs, +): + con = get_server_api_connection() + return con.raw_get( + entrypoint=entrypoint, + **kwargs, + )
+ + +
[docs]def raw_delete( + entrypoint, + **kwargs, +): + con = get_server_api_connection() + return con.raw_delete( + entrypoint=entrypoint, + **kwargs, + )
+ + +
[docs]def post( + entrypoint, + **kwargs, +): + con = get_server_api_connection() + return con.post( + entrypoint=entrypoint, + **kwargs, + )
+ + +
[docs]def put( + entrypoint, + **kwargs, +): + con = get_server_api_connection() + return con.put( + entrypoint=entrypoint, + **kwargs, + )
+ + +
[docs]def patch( + entrypoint, + **kwargs, +): + con = get_server_api_connection() + return con.patch( + entrypoint=entrypoint, + **kwargs, + )
+ + +
[docs]def get( + entrypoint, + **kwargs, +): + con = get_server_api_connection() + return con.get( + entrypoint=entrypoint, + **kwargs, + )
+ + +
[docs]def delete( + entrypoint, + **kwargs, +): + con = get_server_api_connection() + return con.delete( + entrypoint=entrypoint, + **kwargs, + )
+ + +
[docs]def get_event( + event_id, +): + """Query full event data by id. + + Events received using event server do not contain full information. To + get the full event information is required to receive it explicitly. + + Args: + event_id (str): Event id. + + Returns: + dict[str, Any]: Full event data. + + """ + con = get_server_api_connection() + return con.get_event( + event_id=event_id, + )
+ + +
[docs]def get_events( + topics: Optional[Iterable[str]] = None, + event_ids: Optional[Iterable[str]] = None, + project_names: Optional[Iterable[str]] = None, + statuses: Optional[Iterable[str]] = None, + users: Optional[Iterable[str]] = None, + include_logs: Optional[bool] = None, + has_children: Optional[bool] = None, + newer_than: Optional[str] = None, + older_than: Optional[str] = None, + fields: Optional[Iterable[str]] = None, + limit: Optional[int] = None, + order: Optional[SortOrder] = None, + states: Optional[Iterable[str]] = None, +): + """Get events from server with filtering options. + + Notes: + Not all event happen on a project. + + Args: + topics (Optional[Iterable[str]]): Name of topics. + event_ids (Optional[Iterable[str]]): Event ids. + project_names (Optional[Iterable[str]]): Project on which + event happened. + statuses (Optional[Iterable[str]]): Filtering by statuses. + users (Optional[Iterable[str]]): Filtering by users + who created/triggered an event. + include_logs (Optional[bool]): Query also log events. + has_children (Optional[bool]): Event is with/without children + events. If 'None' then all events are returned, default. + newer_than (Optional[str]): Return only events newer than given + iso datetime string. + older_than (Optional[str]): Return only events older than given + iso datetime string. + fields (Optional[Iterable[str]]): Fields that should be received + for each event. + limit (Optional[int]): Limit number of events to be fetched. + order (Optional[SortOrder]): Order events in ascending + or descending order. It is recommended to set 'limit' + when used descending. + states (Optional[Iterable[str]]): DEPRECATED Filtering by states. + Use 'statuses' instead. + + Returns: + Generator[dict[str, Any]]: Available events matching filters. + + """ + con = get_server_api_connection() + return con.get_events( + topics=topics, + event_ids=event_ids, + project_names=project_names, + statuses=statuses, + users=users, + include_logs=include_logs, + has_children=has_children, + newer_than=newer_than, + older_than=older_than, + fields=fields, + limit=limit, + order=order, + states=states, + )
+ + +
[docs]def update_event( + event_id, + sender=None, + project_name=None, + username=None, + status=None, + description=None, + summary=None, + payload=None, + progress=None, + retries=None, +): + """Update event data. + + Args: + event_id (str): Event id. + sender (Optional[str]): New sender of event. + project_name (Optional[str]): New project name. + username (Optional[str]): New username. + status (Optional[str]): New event status. Enum: "pending", + "in_progress", "finished", "failed", "aborted", "restarted" + description (Optional[str]): New description. + summary (Optional[dict[str, Any]]): New summary. + payload (Optional[dict[str, Any]]): New payload. + progress (Optional[int]): New progress. Range [0-100]. + retries (Optional[int]): New retries. + + """ + con = get_server_api_connection() + return con.update_event( + event_id=event_id, + sender=sender, + project_name=project_name, + username=username, + status=status, + description=description, + summary=summary, + payload=payload, + progress=progress, + retries=retries, + )
+ + +
[docs]def dispatch_event( + topic, + sender=None, + event_hash=None, + project_name=None, + username=None, + depends_on=None, + description=None, + summary=None, + payload=None, + finished=True, + store=True, + dependencies=None, +): + """Dispatch event to server. + + Args: + topic (str): Event topic used for filtering of listeners. + sender (Optional[str]): Sender of event. + event_hash (Optional[str]): Event hash. + project_name (Optional[str]): Project name. + depends_on (Optional[str]): Add dependency to another event. + username (Optional[str]): Username which triggered event. + description (Optional[str]): Description of event. + summary (Optional[dict[str, Any]]): Summary of event that can + be used for simple filtering on listeners. + payload (Optional[dict[str, Any]]): Full payload of event data with + all details. + finished (Optional[bool]): Mark event as finished on dispatch. + store (Optional[bool]): Store event in event queue for possible + future processing otherwise is event send only + to active listeners. + dependencies (Optional[list[str]]): Deprecated. + List of event id dependencies. + + Returns: + RestApiResponse: Response from server. + + """ + con = get_server_api_connection() + return con.dispatch_event( + topic=topic, + sender=sender, + event_hash=event_hash, + project_name=project_name, + username=username, + depends_on=depends_on, + description=description, + summary=summary, + payload=payload, + finished=finished, + store=store, + dependencies=dependencies, + )
+ + +
[docs]def delete_event( + event_id: str, +): + """Delete event by id. + + Supported since AYON server 1.6.0. + + Args: + event_id (str): Event id. + + Returns: + RestApiResponse: Response from server. + + """ + con = get_server_api_connection() + return con.delete_event( + event_id=event_id, + )
+ + +
[docs]def enroll_event_job( + source_topic, + target_topic, + sender, + description=None, + sequential=None, + events_filter=None, + max_retries=None, + ignore_older_than=None, + ignore_sender_types=None, +): + """Enroll job based on events. + + Enroll will find first unprocessed event with 'source_topic' and will + create new event with 'target_topic' for it and return the new event + data. + + Use 'sequential' to control that only single target event is created + at same time. Creation of new target events is blocked while there is + at least one unfinished event with target topic, when set to 'True'. + This helps when order of events matter and more than one process using + the same target is running at the same time. + + Make sure the new event has updated status to '"finished"' status + when you're done with logic + + Target topic should not clash with other processes/services. + + Created target event have 'dependsOn' key where is id of source topic. + + Use-case: + - Service 1 is creating events with topic 'my.leech' + - Service 2 process 'my.leech' and uses target topic 'my.process' + - this service can run on 1-n machines + - all events must be processed in a sequence by their creation + time and only one event can be processed at a time + - in this case 'sequential' should be set to 'True' so only + one machine is actually processing events, but if one goes + down there are other that can take place + - Service 3 process 'my.leech' and uses target topic 'my.discover' + - this service can run on 1-n machines + - order of events is not important + - 'sequential' should be 'False' + + Args: + source_topic (str): Source topic to enroll. + target_topic (str): Topic of dependent event. + sender (str): Identifier of sender (e.g. service name or username). + description (Optional[str]): Human readable text shown + in target event. + sequential (Optional[bool]): The source topic must be processed + in sequence. + events_filter (Optional[dict[str, Any]]): Filtering conditions + to filter the source event. For more technical specifications + look to server backed 'ayon_server.sqlfilter.Filter'. + TODO: Add example of filters. + max_retries (Optional[int]): How many times can be event retried. + Default value is based on server (3 at the time of this PR). + ignore_older_than (Optional[int]): Ignore events older than + given number in days. + ignore_sender_types (Optional[List[str]]): Ignore events triggered + by given sender types. + + Returns: + Union[None, dict[str, Any]]: None if there is no event matching + filters. Created event with 'target_topic'. + + """ + con = get_server_api_connection() + return con.enroll_event_job( + source_topic=source_topic, + target_topic=target_topic, + sender=sender, + description=description, + sequential=sequential, + events_filter=events_filter, + max_retries=max_retries, + ignore_older_than=ignore_older_than, + ignore_sender_types=ignore_sender_types, + )
+ + +
[docs]def get_activities( + project_name: str, + activity_ids: Optional[Iterable[str]] = None, + activity_types: Optional[Iterable["ActivityType"]] = None, + entity_ids: Optional[Iterable[str]] = None, + entity_names: Optional[Iterable[str]] = None, + entity_type: Optional[str] = None, + changed_after: Optional[str] = None, + changed_before: Optional[str] = None, + reference_types: Optional[Iterable["ActivityReferenceType"]] = None, + fields: Optional[Iterable[str]] = None, + limit: Optional[int] = None, + order: Optional[SortOrder] = None, +) -> Generator[Dict[str, Any], None, None]: + """Get activities from server with filtering options. + + Args: + project_name (str): Project on which activities happened. + activity_ids (Optional[Iterable[str]]): Activity ids. + activity_types (Optional[Iterable[ActivityType]]): Activity types. + entity_ids (Optional[Iterable[str]]): Entity ids. + entity_names (Optional[Iterable[str]]): Entity names. + entity_type (Optional[str]): Entity type. + changed_after (Optional[str]): Return only activities changed + after given iso datetime string. + changed_before (Optional[str]): Return only activities changed + before given iso datetime string. + reference_types (Optional[Iterable[ActivityReferenceType]]): + Reference types filter. Defaults to `['origin']`. + fields (Optional[Iterable[str]]): Fields that should be received + for each activity. + limit (Optional[int]): Limit number of activities to be fetched. + order (Optional[SortOrder]): Order activities in ascending + or descending order. It is recommended to set 'limit' + when used descending. + + Returns: + Generator[dict[str, Any]]: Available activities matching filters. + + """ + con = get_server_api_connection() + return con.get_activities( + project_name=project_name, + activity_ids=activity_ids, + activity_types=activity_types, + entity_ids=entity_ids, + entity_names=entity_names, + entity_type=entity_type, + changed_after=changed_after, + changed_before=changed_before, + reference_types=reference_types, + fields=fields, + limit=limit, + order=order, + )
+ + +
[docs]def get_activity_by_id( + project_name: str, + activity_id: str, + reference_types: Optional[Iterable["ActivityReferenceType"]] = None, + fields: Optional[Iterable[str]] = None, +) -> Optional[Dict[str, Any]]: + """Get activity by id. + + Args: + project_name (str): Project on which activity happened. + activity_id (str): Activity id. + fields (Optional[Iterable[str]]): Fields that should be received + for each activity. + + Returns: + Optional[Dict[str, Any]]: Activity data or None if activity is not + found. + + """ + con = get_server_api_connection() + return con.get_activity_by_id( + project_name=project_name, + activity_id=activity_id, + reference_types=reference_types, + fields=fields, + )
+ + +
[docs]def create_activity( + project_name: str, + entity_id: str, + entity_type: str, + activity_type: "ActivityType", + activity_id: Optional[str] = None, + body: Optional[str] = None, + file_ids: Optional[List[str]] = None, + timestamp: Optional[str] = None, + data: Optional[Dict[str, Any]] = None, +) -> str: + """Create activity on a project. + + Args: + project_name (str): Project on which activity happened. + entity_id (str): Entity id. + entity_type (str): Entity type. + activity_type (ActivityType): Activity type. + activity_id (Optional[str]): Activity id. + body (Optional[str]): Activity body. + file_ids (Optional[List[str]]): List of file ids attached + to activity. + timestamp (Optional[str]): Activity timestamp. + data (Optional[Dict[str, Any]]): Additional data. + + Returns: + str: Activity id. + + """ + con = get_server_api_connection() + return con.create_activity( + project_name=project_name, + entity_id=entity_id, + entity_type=entity_type, + activity_type=activity_type, + activity_id=activity_id, + body=body, + file_ids=file_ids, + timestamp=timestamp, + data=data, + )
+ + +
[docs]def update_activity( + project_name: str, + activity_id: str, + body: Optional[str] = None, + file_ids: Optional[List[str]] = None, + append_file_ids: Optional[bool] = False, + data: Optional[Dict[str, Any]] = None, +): + """Update activity by id. + + Args: + project_name (str): Project on which activity happened. + activity_id (str): Activity id. + body (str): Activity body. + file_ids (Optional[List[str]]): List of file ids attached + to activity. + append_file_ids (Optional[bool]): Append file ids to existing + list of file ids. + data (Optional[Dict[str, Any]]): Update data in activity. + + """ + con = get_server_api_connection() + return con.update_activity( + project_name=project_name, + activity_id=activity_id, + body=body, + file_ids=file_ids, + append_file_ids=append_file_ids, + data=data, + )
+ + +
[docs]def delete_activity( + project_name: str, + activity_id: str, +): + """Delete activity by id. + + Args: + project_name (str): Project on which activity happened. + activity_id (str): Activity id to remove. + + """ + con = get_server_api_connection() + return con.delete_activity( + project_name=project_name, + activity_id=activity_id, + )
+ + +
[docs]def download_file_to_stream( + endpoint, + stream, + chunk_size=None, + progress=None, +): + """Download file from AYON server to IOStream. + + Endpoint can be full url (must start with 'base_url' of api object). + + Progress object can be used to track download. Can be used when + download happens in thread and other thread want to catch changes over + time. + + Todos: + Use retries and timeout. + Return RestApiResponse. + + Args: + endpoint (str): Endpoint or URL to file that should be downloaded. + stream (Union[io.BytesIO, BinaryIO]): Stream where output will + be stored. + chunk_size (Optional[int]): Size of chunks that are received + in single loop. + progress (Optional[TransferProgress]): Object that gives ability + to track download progress. + + """ + con = get_server_api_connection() + return con.download_file_to_stream( + endpoint=endpoint, + stream=stream, + chunk_size=chunk_size, + progress=progress, + )
+ + +
[docs]def download_file( + endpoint, + filepath, + chunk_size=None, + progress=None, +): + """Download file from AYON server. + + Endpoint can be full url (must start with 'base_url' of api object). + + Progress object can be used to track download. Can be used when + download happens in thread and other thread want to catch changes over + time. + + Todos: + Use retries and timeout. + Return RestApiResponse. + + Args: + endpoint (str): Endpoint or URL to file that should be downloaded. + filepath (str): Path where file will be downloaded. + chunk_size (Optional[int]): Size of chunks that are received + in single loop. + progress (Optional[TransferProgress]): Object that gives ability + to track download progress. + + """ + con = get_server_api_connection() + return con.download_file( + endpoint=endpoint, + filepath=filepath, + chunk_size=chunk_size, + progress=progress, + )
+ + +
[docs]def upload_file_from_stream( + endpoint, + stream, + progress, + request_type, + **kwargs, +): + """Upload file to server from bytes. + + Todos: + Use retries and timeout. + Return RestApiResponse. + + Args: + endpoint (str): Endpoint or url where file will be uploaded. + stream (Union[io.BytesIO, BinaryIO]): File content stream. + progress (Optional[TransferProgress]): Object that gives ability + to track upload progress. + request_type (Optional[RequestType]): Type of request that will + be used to upload file. + **kwargs (Any): Additional arguments that will be passed + to request function. + + Returns: + requests.Response: Response object + + """ + con = get_server_api_connection() + return con.upload_file_from_stream( + endpoint=endpoint, + stream=stream, + progress=progress, + request_type=request_type, + **kwargs, + )
+ + +
[docs]def upload_file( + endpoint, + filepath, + progress=None, + request_type=None, + **kwargs, +): + """Upload file to server. + + Todos: + Use retries and timeout. + Return RestApiResponse. + + Args: + endpoint (str): Endpoint or url where file will be uploaded. + filepath (str): Source filepath. + progress (Optional[TransferProgress]): Object that gives ability + to track upload progress. + request_type (Optional[RequestType]): Type of request that will + be used to upload file. + **kwargs (Any): Additional arguments that will be passed + to request function. + + Returns: + requests.Response: Response object + + """ + con = get_server_api_connection() + return con.upload_file( + endpoint=endpoint, + filepath=filepath, + progress=progress, + request_type=request_type, + **kwargs, + )
+ + +
[docs]def upload_reviewable( + project_name, + version_id, + filepath, + label=None, + content_type=None, + filename=None, + progress=None, + headers=None, + **kwargs, +): + """Upload reviewable file to server. + + Args: + project_name (str): Project name. + version_id (str): Version id. + filepath (str): Reviewable file path to upload. + label (Optional[str]): Reviewable label. Filled automatically + server side with filename. + content_type (Optional[str]): MIME type of the file. + filename (Optional[str]): User as original filename. Filename from + 'filepath' is used when not filled. + progress (Optional[TransferProgress]): Progress. + headers (Optional[Dict[str, Any]]): Headers. + + Returns: + RestApiResponse: Server response. + + """ + con = get_server_api_connection() + return con.upload_reviewable( + project_name=project_name, + version_id=version_id, + filepath=filepath, + label=label, + content_type=content_type, + filename=filename, + progress=progress, + headers=headers, + **kwargs, + )
+ + +
[docs]def trigger_server_restart(): + """Trigger server restart. + + Restart may be required when a change of specific value happened on + server. + + """ + con = get_server_api_connection() + return con.trigger_server_restart()
+ + +
[docs]def query_graphql( + query, + variables=None, +): + """Execute GraphQl query. + + Args: + query (str): GraphQl query string. + variables (Optional[dict[str, Any]): Variables that can be + used in query. + + Returns: + GraphQlResponse: Response from server. + + """ + con = get_server_api_connection() + return con.query_graphql( + query=query, + variables=variables, + )
+ + +
[docs]def get_graphql_schema(): + con = get_server_api_connection() + return con.get_graphql_schema()
+ + +
[docs]def get_server_schema(): + """Get server schema with info, url paths, components etc. + + Todos: + Cache schema - How to find out it is outdated? + + Returns: + dict[str, Any]: Full server schema. + + """ + con = get_server_api_connection() + return con.get_server_schema()
+ + +
[docs]def get_schemas(): + """Get components schema. + + Name of components does not match entity type names e.g. 'project' is + under 'ProjectModel'. We should find out some mapping. Also, there + are properties which don't have information about reference to object + e.g. 'config' has just object definition without reference schema. + + Returns: + dict[str, Any]: Component schemas. + + """ + con = get_server_api_connection() + return con.get_schemas()
+ + +
[docs]def get_attributes_schema( + use_cache=True, +): + con = get_server_api_connection() + return con.get_attributes_schema( + use_cache=use_cache, + )
+ + +
[docs]def reset_attributes_schema(): + con = get_server_api_connection() + return con.reset_attributes_schema()
+ + +
[docs]def set_attribute_config( + attribute_name, + data, + scope, + position=None, + builtin=False, +): + con = get_server_api_connection() + return con.set_attribute_config( + attribute_name=attribute_name, + data=data, + scope=scope, + position=position, + builtin=builtin, + )
+ + +
[docs]def remove_attribute_config( + attribute_name, +): + """Remove attribute from server. + + This can't be un-done, please use carefully. + + Args: + attribute_name (str): Name of attribute to remove. + + """ + con = get_server_api_connection() + return con.remove_attribute_config( + attribute_name=attribute_name, + )
+ + +
[docs]def get_attributes_for_type( + entity_type, +): + """Get attribute schemas available for an entity type. + + Example:: + + ``` + # Example attribute schema + { + # Common + "type": "integer", + "title": "Clip Out", + "description": null, + "example": 1, + "default": 1, + # These can be filled based on value of 'type' + "gt": null, + "ge": null, + "lt": null, + "le": null, + "minLength": null, + "maxLength": null, + "minItems": null, + "maxItems": null, + "regex": null, + "enum": null + } + ``` + + Args: + entity_type (str): Entity type for which should be attributes + received. + + Returns: + dict[str, dict[str, Any]]: Attribute schemas that are available + for entered entity type. + + """ + con = get_server_api_connection() + return con.get_attributes_for_type( + entity_type=entity_type, + )
+ + +
[docs]def get_attributes_fields_for_type( + entity_type, +): + """Prepare attribute fields for entity type. + + Returns: + set[str]: Attributes fields for entity type. + + """ + con = get_server_api_connection() + return con.get_attributes_fields_for_type( + entity_type=entity_type, + )
+ + +
[docs]def get_default_fields_for_type( + entity_type, +): + """Default fields for entity type. + + Returns most of commonly used fields from server. + + Args: + entity_type (str): Name of entity type. + + Returns: + set[str]: Fields that should be queried from server. + + """ + con = get_server_api_connection() + return con.get_default_fields_for_type( + entity_type=entity_type, + )
+ + +
[docs]def get_addons_info( + details=True, +): + """Get information about addons available on server. + + Args: + details (Optional[bool]): Detailed data with information how + to get client code. + + """ + con = get_server_api_connection() + return con.get_addons_info( + details=details, + )
+ + +
[docs]def get_addon_endpoint( + addon_name, + addon_version, + *subpaths, +): + """Calculate endpoint to addon route. + + Examples: + + >>> api = ServerAPI("https://your.url.com") + >>> api.get_addon_url( + ... "example", "1.0.0", "private", "my.zip") + 'addons/example/1.0.0/private/my.zip' + + Args: + addon_name (str): Name of addon. + addon_version (str): Version of addon. + *subpaths (str): Any amount of subpaths that are added to + addon url. + + Returns: + str: Final url. + + """ + con = get_server_api_connection() + return con.get_addon_endpoint( + addon_name=addon_name, + addon_version=addon_version, + *subpaths, + )
+ + +
[docs]def get_addon_url( + addon_name, + addon_version, + *subpaths, + use_rest=True, +): + """Calculate url to addon route. + + Examples: + + >>> api = ServerAPI("https://your.url.com") + >>> api.get_addon_url( + ... "example", "1.0.0", "private", "my.zip") + 'https://your.url.com/api/addons/example/1.0.0/private/my.zip' + + Args: + addon_name (str): Name of addon. + addon_version (str): Version of addon. + *subpaths (str): Any amount of subpaths that are added to + addon url. + use_rest (Optional[bool]): Use rest endpoint. + + Returns: + str: Final url. + + """ + con = get_server_api_connection() + return con.get_addon_url( + addon_name=addon_name, + addon_version=addon_version, + *subpaths, + use_rest=use_rest, + )
+ + +
[docs]def download_addon_private_file( + addon_name, + addon_version, + filename, + destination_dir, + destination_filename=None, + chunk_size=None, + progress=None, +): + """Download a file from addon private files. + + This method requires to have authorized token available. Private files + are not under '/api' restpoint. + + Args: + addon_name (str): Addon name. + addon_version (str): Addon version. + filename (str): Filename in private folder on server. + destination_dir (str): Where the file should be downloaded. + destination_filename (Optional[str]): Name of destination + filename. Source filename is used if not passed. + chunk_size (Optional[int]): Download chunk size. + progress (Optional[TransferProgress]): Object that gives ability + to track download progress. + + Returns: + str: Filepath to downloaded file. + + """ + con = get_server_api_connection() + return con.download_addon_private_file( + addon_name=addon_name, + addon_version=addon_version, + filename=filename, + destination_dir=destination_dir, + destination_filename=destination_filename, + chunk_size=chunk_size, + progress=progress, + )
+ + +
[docs]def get_installers( + version=None, + platform_name=None, +): + """Information about desktop application installers on server. + + Desktop application installers are helpers to download/update AYON + desktop application for artists. + + Args: + version (Optional[str]): Filter installers by version. + platform_name (Optional[str]): Filter installers by platform name. + + Returns: + list[dict[str, Any]]: + + """ + con = get_server_api_connection() + return con.get_installers( + version=version, + platform_name=platform_name, + )
+ + +
[docs]def create_installer( + filename, + version, + python_version, + platform_name, + python_modules, + runtime_python_modules, + checksum, + checksum_algorithm, + file_size, + sources=None, +): + """Create new installer information on server. + + This step will create only metadata. Make sure to upload installer + to the server using 'upload_installer' method. + + Runtime python modules are modules that are required to run AYON + desktop application, but are not added to PYTHONPATH for any + subprocess. + + Args: + filename (str): Installer filename. + version (str): Version of installer. + python_version (str): Version of Python. + platform_name (str): Name of platform. + python_modules (dict[str, str]): Python modules that are available + in installer. + runtime_python_modules (dict[str, str]): Runtime python modules + that are available in installer. + checksum (str): Installer file checksum. + checksum_algorithm (str): Type of checksum used to create checksum. + file_size (int): File size. + sources (Optional[list[dict[str, Any]]]): List of sources that + can be used to download file. + + """ + con = get_server_api_connection() + return con.create_installer( + filename=filename, + version=version, + python_version=python_version, + platform_name=platform_name, + python_modules=python_modules, + runtime_python_modules=runtime_python_modules, + checksum=checksum, + checksum_algorithm=checksum_algorithm, + file_size=file_size, + sources=sources, + )
+ + +
[docs]def update_installer( + filename, + sources, +): + """Update installer information on server. + + Args: + filename (str): Installer filename. + sources (list[dict[str, Any]]): List of sources that + can be used to download file. Fully replaces existing sources. + + """ + con = get_server_api_connection() + return con.update_installer( + filename=filename, + sources=sources, + )
+ + +
[docs]def delete_installer( + filename, +): + """Delete installer from server. + + Args: + filename (str): Installer filename. + + """ + con = get_server_api_connection() + return con.delete_installer( + filename=filename, + )
+ + +
[docs]def download_installer( + filename, + dst_filepath, + chunk_size=None, + progress=None, +): + """Download installer file from server. + + Args: + filename (str): Installer filename. + dst_filepath (str): Destination filepath. + chunk_size (Optional[int]): Download chunk size. + progress (Optional[TransferProgress]): Object that gives ability + to track download progress. + + """ + con = get_server_api_connection() + return con.download_installer( + filename=filename, + dst_filepath=dst_filepath, + chunk_size=chunk_size, + progress=progress, + )
+ + +
[docs]def upload_installer( + src_filepath, + dst_filename, + progress=None, +): + """Upload installer file to server. + + Args: + src_filepath (str): Source filepath. + dst_filename (str): Destination filename. + progress (Optional[TransferProgress]): Object that gives ability + to track download progress. + + Returns: + requests.Response: Response object. + + """ + con = get_server_api_connection() + return con.upload_installer( + src_filepath=src_filepath, + dst_filename=dst_filename, + progress=progress, + )
+ + +
[docs]def get_dependency_packages(): + """Information about dependency packages on server. + + To download dependency package, use 'download_dependency_package' + method and pass in 'filename'. + + Example data structure:: + + { + "packages": [ + { + "filename": str, + "platform": str, + "checksum": str, + "checksumAlgorithm": str, + "size": int, + "sources": list[dict[str, Any]], + "supportedAddons": dict[str, str], + "pythonModules": dict[str, str] + } + ] + } + + Returns: + dict[str, Any]: Information about dependency packages known for + server. + + """ + con = get_server_api_connection() + return con.get_dependency_packages()
+ + +
[docs]def create_dependency_package( + filename, + python_modules, + source_addons, + installer_version, + checksum, + checksum_algorithm, + file_size, + sources=None, + platform_name=None, +): + """Create dependency package on server. + + The package will be created on a server, it is also required to upload + the package archive file (using :meth:`upload_dependency_package`). + + Args: + filename (str): Filename of dependency package. + python_modules (dict[str, str]): Python modules in dependency + package:: + + {"<module name>": "<module version>", ...} + + source_addons (dict[str, str]): Name of addons for which is + dependency package created:: + + {"<addon name>": "<addon version>", ...} + + installer_version (str): Version of installer for which was + package created. + checksum (str): Checksum of archive file where dependencies are. + checksum_algorithm (str): Algorithm used to calculate checksum. + file_size (Optional[int]): Size of file. + sources (Optional[list[dict[str, Any]]]): Information about + sources from where it is possible to get file. + platform_name (Optional[str]): Name of platform for which is + dependency package targeted. Default value is + current platform. + + """ + con = get_server_api_connection() + return con.create_dependency_package( + filename=filename, + python_modules=python_modules, + source_addons=source_addons, + installer_version=installer_version, + checksum=checksum, + checksum_algorithm=checksum_algorithm, + file_size=file_size, + sources=sources, + platform_name=platform_name, + )
+ + +
[docs]def update_dependency_package( + filename, + sources, +): + """Update dependency package metadata on server. + + Args: + filename (str): Filename of dependency package. + sources (list[dict[str, Any]]): Information about + sources from where it is possible to get file. Fully replaces + existing sources. + + """ + con = get_server_api_connection() + return con.update_dependency_package( + filename=filename, + sources=sources, + )
+ + +
[docs]def delete_dependency_package( + filename, + platform_name=None, +): + """Remove dependency package for specific platform. + + Args: + filename (str): Filename of dependency package. + platform_name (Optional[str]): Deprecated. + + """ + con = get_server_api_connection() + return con.delete_dependency_package( + filename=filename, + platform_name=platform_name, + )
+ + +
[docs]def download_dependency_package( + src_filename, + dst_directory, + dst_filename, + platform_name=None, + chunk_size=None, + progress=None, +): + """Download dependency package from server. + + This method requires to have authorized token available. The package + is only downloaded. + + Args: + src_filename (str): Filename of dependency pacakge. + For server version 0.2.0 and lower it is name of package + to download. + dst_directory (str): Where the file should be downloaded. + dst_filename (str): Name of destination filename. + platform_name (Optional[str]): Deprecated. + chunk_size (Optional[int]): Download chunk size. + progress (Optional[TransferProgress]): Object that gives ability + to track download progress. + + Returns: + str: Filepath to downloaded file. + + """ + con = get_server_api_connection() + return con.download_dependency_package( + src_filename=src_filename, + dst_directory=dst_directory, + dst_filename=dst_filename, + platform_name=platform_name, + chunk_size=chunk_size, + progress=progress, + )
+ + +
[docs]def upload_dependency_package( + src_filepath, + dst_filename, + platform_name=None, + progress=None, +): + """Upload dependency package to server. + + Args: + src_filepath (str): Path to a package file. + dst_filename (str): Dependency package filename or name of package + for server version 0.2.0 or lower. Must be unique. + platform_name (Optional[str]): Deprecated. + progress (Optional[TransferProgress]): Object to keep track about + upload state. + + """ + con = get_server_api_connection() + return con.upload_dependency_package( + src_filepath=src_filepath, + dst_filename=dst_filename, + platform_name=platform_name, + progress=progress, + )
+ + +
[docs]def delete_addon( + addon_name: str, + purge: Optional[bool] = None, +): + """Delete addon from server. + + Delete all versions of addon from server. + + Args: + addon_name (str): Addon name. + purge (Optional[bool]): Purge all data related to the addon. + + """ + con = get_server_api_connection() + return con.delete_addon( + addon_name=addon_name, + purge=purge, + )
+ + +
[docs]def delete_addon_version( + addon_name: str, + addon_version: str, + purge: Optional[bool] = None, +): + """Delete addon version from server. + + Delete all versions of addon from server. + + Args: + addon_name (str): Addon name. + addon_version (str): Addon version. + purge (Optional[bool]): Purge all data related to the addon. + + """ + con = get_server_api_connection() + return con.delete_addon_version( + addon_name=addon_name, + addon_version=addon_version, + purge=purge, + )
+ + +
[docs]def upload_addon_zip( + src_filepath, + progress=None, +): + """Upload addon zip file to server. + + File is validated on server. If it is valid, it is installed. It will + create an event job which can be tracked (tracking part is not + implemented yet). + + Example output:: + + {'eventId': 'a1bfbdee27c611eea7580242ac120003'} + + Args: + src_filepath (str): Path to a zip file. + progress (Optional[TransferProgress]): Object to keep track about + upload state. + + Returns: + dict[str, Any]: Response data from server. + + """ + con = get_server_api_connection() + return con.upload_addon_zip( + src_filepath=src_filepath, + progress=progress, + )
+ + +
[docs]def get_bundles(): + """Server bundles with basic information. + + This is example output:: + + { + "bundles": [ + { + "name": "my_bundle", + "createdAt": "2023-06-12T15:37:02.420260", + "installerVersion": "1.0.0", + "addons": { + "core": "1.2.3" + }, + "dependencyPackages": { + "windows": "a_windows_package123.zip", + "linux": "a_linux_package123.zip", + "darwin": "a_mac_package123.zip" + }, + "isProduction": False, + "isStaging": False + } + ], + "productionBundle": "my_bundle", + "stagingBundle": "test_bundle" + } + + Returns: + dict[str, Any]: Server bundles with basic information. + + """ + con = get_server_api_connection() + return con.get_bundles()
+ + +
[docs]def create_bundle( + name, + addon_versions, + installer_version, + dependency_packages=None, + is_production=None, + is_staging=None, + is_dev=None, + dev_active_user=None, + dev_addons_config=None, +): + """Create bundle on server. + + Bundle cannot be changed once is created. Only isProduction, isStaging + and dependency packages can change after creation. In case dev bundle + is created, it is possible to change anything, but it is not possible + to mark bundle as dev and production or staging at the same time. + + Development addon config can define custom path to client code. It is + used only for dev bundles. + + Example of 'dev_addons_config':: + + ```json + { + "core": { + "enabled": true, + "path": "/path/to/ayon-core/client" + } + } + ``` + + Args: + name (str): Name of bundle. + addon_versions (dict[str, str]): Addon versions. + installer_version (Union[str, None]): Installer version. + dependency_packages (Optional[dict[str, str]]): Dependency + package names. Keys are platform names and values are name of + packages. + is_production (Optional[bool]): Bundle will be marked as + production. + is_staging (Optional[bool]): Bundle will be marked as staging. + is_dev (Optional[bool]): Bundle will be marked as dev. + dev_active_user (Optional[str]): Username that will be assigned + to dev bundle. Can be used only if 'is_dev' is set to 'True'. + dev_addons_config (Optional[dict[str, Any]]): Configuration for + dev addons. Can be used only if 'is_dev' is set to 'True'. + + """ + con = get_server_api_connection() + return con.create_bundle( + name=name, + addon_versions=addon_versions, + installer_version=installer_version, + dependency_packages=dependency_packages, + is_production=is_production, + is_staging=is_staging, + is_dev=is_dev, + dev_active_user=dev_active_user, + dev_addons_config=dev_addons_config, + )
+ + +
[docs]def update_bundle( + bundle_name, + addon_versions=None, + installer_version=None, + dependency_packages=None, + is_production=None, + is_staging=None, + is_dev=None, + dev_active_user=None, + dev_addons_config=None, +): + """Update bundle on server. + + Dependency packages can be update only for single platform. Others + will be left untouched. Use 'None' value to unset dependency package + from bundle. + + Args: + bundle_name (str): Name of bundle. + addon_versions (Optional[dict[str, str]]): Addon versions, + possible only for dev bundles. + installer_version (Optional[str]): Installer version, possible + only for dev bundles. + dependency_packages (Optional[dict[str, str]]): Dependency pacakge + names that should be used with the bundle. + is_production (Optional[bool]): Bundle will be marked as + production. + is_staging (Optional[bool]): Bundle will be marked as staging. + is_dev (Optional[bool]): Bundle will be marked as dev. + dev_active_user (Optional[str]): Username that will be assigned + to dev bundle. Can be used only for dev bundles. + dev_addons_config (Optional[dict[str, Any]]): Configuration for + dev addons. Can be used only for dev bundles. + + """ + con = get_server_api_connection() + return con.update_bundle( + bundle_name=bundle_name, + addon_versions=addon_versions, + installer_version=installer_version, + dependency_packages=dependency_packages, + is_production=is_production, + is_staging=is_staging, + is_dev=is_dev, + dev_active_user=dev_active_user, + dev_addons_config=dev_addons_config, + )
+ + +
[docs]def check_bundle_compatibility( + name, + addon_versions, + installer_version, + dependency_packages=None, + is_production=None, + is_staging=None, + is_dev=None, + dev_active_user=None, + dev_addons_config=None, +): + """Check bundle compatibility. + + Can be used as per-flight validation before creating bundle. + + Args: + name (str): Name of bundle. + addon_versions (dict[str, str]): Addon versions. + installer_version (Union[str, None]): Installer version. + dependency_packages (Optional[dict[str, str]]): Dependency + package names. Keys are platform names and values are name of + packages. + is_production (Optional[bool]): Bundle will be marked as + production. + is_staging (Optional[bool]): Bundle will be marked as staging. + is_dev (Optional[bool]): Bundle will be marked as dev. + dev_active_user (Optional[str]): Username that will be assigned + to dev bundle. Can be used only if 'is_dev' is set to 'True'. + dev_addons_config (Optional[dict[str, Any]]): Configuration for + dev addons. Can be used only if 'is_dev' is set to 'True'. + + Returns: + Dict[str, Any]: Server response, with 'success' and 'issues'. + + """ + con = get_server_api_connection() + return con.check_bundle_compatibility( + name=name, + addon_versions=addon_versions, + installer_version=installer_version, + dependency_packages=dependency_packages, + is_production=is_production, + is_staging=is_staging, + is_dev=is_dev, + dev_active_user=dev_active_user, + dev_addons_config=dev_addons_config, + )
+ + +
[docs]def delete_bundle( + bundle_name, +): + """Delete bundle from server. + + Args: + bundle_name (str): Name of bundle to delete. + + """ + con = get_server_api_connection() + return con.delete_bundle( + bundle_name=bundle_name, + )
+ + +
[docs]def get_project_anatomy_presets(): + """Anatomy presets available on server. + + Content has basic information about presets. Example output:: + + [ + { + "name": "netflix_VFX", + "primary": false, + "version": "1.0.0" + }, + { + ... + }, + ... + ] + + Returns: + list[dict[str, str]]: Anatomy presets available on server. + + """ + con = get_server_api_connection() + return con.get_project_anatomy_presets()
+ + +
[docs]def get_default_anatomy_preset_name(): + """Name of default anatomy preset. + + Primary preset is used as default preset. But when primary preset is + not set a built-in is used instead. Built-in preset is named '_'. + + Returns: + str: Name of preset that can be used by + 'get_project_anatomy_preset'. + + """ + con = get_server_api_connection() + return con.get_default_anatomy_preset_name()
+ + +
[docs]def get_project_anatomy_preset( + preset_name=None, +): + """Anatomy preset values by name. + + Get anatomy preset values by preset name. Primary preset is returned + if preset name is set to 'None'. + + Args: + preset_name (Optional[str]): Preset name. + + Returns: + dict[str, Any]: Anatomy preset values. + + """ + con = get_server_api_connection() + return con.get_project_anatomy_preset( + preset_name=preset_name, + )
+ + +
[docs]def get_build_in_anatomy_preset(): + """Get built-in anatomy preset. + + Returns: + dict[str, Any]: Built-in anatomy preset. + + """ + con = get_server_api_connection() + return con.get_build_in_anatomy_preset()
+ + +
[docs]def get_project_root_overrides( + project_name, +): + """Root overrides per site name. + + Method is based on logged user and can't be received for any other + user on server. + + Output will contain only roots per site id used by logged user. + + Args: + project_name (str): Name of project. + + Returns: + dict[str, dict[str, str]]: Root values by root name by site id. + + """ + con = get_server_api_connection() + return con.get_project_root_overrides( + project_name=project_name, + )
+ + +
[docs]def get_project_roots_by_site( + project_name, +): + """Root overrides per site name. + + Method is based on logged user and can't be received for any other + user on server. + + Output will contain only roots per site id used by logged user. + + Deprecated: + Use 'get_project_root_overrides' instead. Function + deprecated since 1.0.6 + + Args: + project_name (str): Name of project. + + Returns: + dict[str, dict[str, str]]: Root values by root name by site id. + + """ + con = get_server_api_connection() + return con.get_project_roots_by_site( + project_name=project_name, + )
+ + +
[docs]def get_project_root_overrides_by_site_id( + project_name, + site_id=None, +): + """Root overrides for site. + + If site id is not passed a site set in current api object is used + instead. + + Args: + project_name (str): Name of project. + site_id (Optional[str]): Site id for which want to receive + site overrides. + + Returns: + dict[str, str]: Root values by root name or None if + site does not have overrides. + + """ + con = get_server_api_connection() + return con.get_project_root_overrides_by_site_id( + project_name=project_name, + site_id=site_id, + )
+ + +
[docs]def get_project_roots_for_site( + project_name, + site_id=None, +): + """Root overrides for site. + + If site id is not passed a site set in current api object is used + instead. + + Deprecated: + Use 'get_project_root_overrides_by_site_id' instead. Function + deprecated since 1.0.6 + Args: + project_name (str): Name of project. + site_id (Optional[str]): Site id for which want to receive + site overrides. + + Returns: + dict[str, str]: Root values by root name, root name is not + available if it does not have overrides. + + """ + con = get_server_api_connection() + return con.get_project_roots_for_site( + project_name=project_name, + site_id=site_id, + )
+ + +
[docs]def get_project_roots_by_site_id( + project_name, + site_id=None, +): + """Root values for a site. + + If site id is not passed a site set in current api object is used + instead. If site id is not available, default roots are returned + for current platform. + + Args: + project_name (str): Name of project. + site_id (Optional[str]): Site id for which want to receive + root values. + + Returns: + dict[str, str]: Root values. + + """ + con = get_server_api_connection() + return con.get_project_roots_by_site_id( + project_name=project_name, + site_id=site_id, + )
+ + +
[docs]def get_project_roots_by_platform( + project_name, + platform_name=None, +): + """Root values for a site. + + If platform name is not passed current platform name is used instead. + + This function does return root values without site overrides. It is + possible to use the function to receive default root values. + + Args: + project_name (str): Name of project. + platform_name (Optional[Literal["windows", "linux", "darwin"]]): + Platform name for which want to receive root values. Current + platform name is used if not passed. + + Returns: + dict[str, str]: Root values. + + """ + con = get_server_api_connection() + return con.get_project_roots_by_platform( + project_name=project_name, + platform_name=platform_name, + )
+ + +
[docs]def get_addon_settings_schema( + addon_name, + addon_version, + project_name=None, +): + """Sudio/Project settings schema of an addon. + + Project schema may look differently as some enums are based on project + values. + + Args: + addon_name (str): Name of addon. + addon_version (str): Version of addon. + project_name (Optional[str]): Schema for specific project or + default studio schemas. + + Returns: + dict[str, Any]: Schema of studio/project settings. + + """ + con = get_server_api_connection() + return con.get_addon_settings_schema( + addon_name=addon_name, + addon_version=addon_version, + project_name=project_name, + )
+ + +
[docs]def get_addon_site_settings_schema( + addon_name, + addon_version, +): + """Site settings schema of an addon. + + Args: + addon_name (str): Name of addon. + addon_version (str): Version of addon. + + Returns: + dict[str, Any]: Schema of site settings. + + """ + con = get_server_api_connection() + return con.get_addon_site_settings_schema( + addon_name=addon_name, + addon_version=addon_version, + )
+ + +
[docs]def get_addon_studio_settings( + addon_name, + addon_version, + variant=None, +): + """Addon studio settings. + + Receive studio settings for specific version of an addon. + + Args: + addon_name (str): Name of addon. + addon_version (str): Version of addon. + variant (Optional[Literal['production', 'staging']]): Name of + settings variant. Used 'default_settings_variant' by default. + + Returns: + dict[str, Any]: Addon settings. + + """ + con = get_server_api_connection() + return con.get_addon_studio_settings( + addon_name=addon_name, + addon_version=addon_version, + variant=variant, + )
+ + +
[docs]def get_addon_project_settings( + addon_name, + addon_version, + project_name, + variant=None, + site_id=None, + use_site=True, +): + """Addon project settings. + + Receive project settings for specific version of an addon. The settings + may be with site overrides when enabled. + + Site id is filled with current connection site id if not passed. To + make sure any site id is used set 'use_site' to 'False'. + + Args: + addon_name (str): Name of addon. + addon_version (str): Version of addon. + project_name (str): Name of project for which the settings are + received. + variant (Optional[Literal['production', 'staging']]): Name of + settings variant. Used 'default_settings_variant' by default. + site_id (Optional[str]): Name of site which is used for site + overrides. Is filled with connection 'site_id' attribute + if not passed. + use_site (Optional[bool]): To force disable option of using site + overrides set to 'False'. In that case won't be applied + any site overrides. + + Returns: + dict[str, Any]: Addon settings. + + """ + con = get_server_api_connection() + return con.get_addon_project_settings( + addon_name=addon_name, + addon_version=addon_version, + project_name=project_name, + variant=variant, + site_id=site_id, + use_site=use_site, + )
+ + +
[docs]def get_addon_settings( + addon_name, + addon_version, + project_name=None, + variant=None, + site_id=None, + use_site=True, +): + """Receive addon settings. + + Receive addon settings based on project name value. Some arguments may + be ignored if 'project_name' is set to 'None'. + + Args: + addon_name (str): Name of addon. + addon_version (str): Version of addon. + project_name (Optional[str]): Name of project for which the + settings are received. A studio settings values are received + if is 'None'. + variant (Optional[Literal['production', 'staging']]): Name of + settings variant. Used 'default_settings_variant' by default. + site_id (Optional[str]): Name of site which is used for site + overrides. Is filled with connection 'site_id' attribute + if not passed. + use_site (Optional[bool]): To force disable option of using + site overrides set to 'False'. In that case won't be applied + any site overrides. + + Returns: + dict[str, Any]: Addon settings. + + """ + con = get_server_api_connection() + return con.get_addon_settings( + addon_name=addon_name, + addon_version=addon_version, + project_name=project_name, + variant=variant, + site_id=site_id, + use_site=use_site, + )
+ + +
[docs]def get_addon_site_settings( + addon_name, + addon_version, + site_id=None, +): + """Site settings of an addon. + + If site id is not available an empty dictionary is returned. + + Args: + addon_name (str): Name of addon. + addon_version (str): Version of addon. + site_id (Optional[str]): Name of site for which should be settings + returned. using 'site_id' attribute if not passed. + + Returns: + dict[str, Any]: Site settings. + + """ + con = get_server_api_connection() + return con.get_addon_site_settings( + addon_name=addon_name, + addon_version=addon_version, + site_id=site_id, + )
+ + +
[docs]def get_bundle_settings( + bundle_name=None, + project_name=None, + variant=None, + site_id=None, + use_site=True, +): + """Get complete set of settings for given data. + + If project is not passed then studio settings are returned. If variant + is not passed 'default_settings_variant' is used. If bundle name is + not passed then current production/staging bundle is used, based on + variant value. + + Output contains addon settings and site settings in single dictionary. + + Todos: + - test how it behaves if there is not any bundle. + - test how it behaves if there is not any production/staging + bundle. + + Example output:: + + { + "addons": [ + { + "name": "addon-name", + "version": "addon-version", + "settings": {...}, + "siteSettings": {...} + } + ] + } + + Returns: + dict[str, Any]: All settings for single bundle. + + """ + con = get_server_api_connection() + return con.get_bundle_settings( + bundle_name=bundle_name, + project_name=project_name, + variant=variant, + site_id=site_id, + use_site=use_site, + )
+ + +
[docs]def get_addons_studio_settings( + bundle_name=None, + variant=None, + site_id=None, + use_site=True, + only_values=True, +): + """All addons settings in one bulk. + + Warnings: + Behavior of this function changed with AYON server version 0.3.0. + Structure of output from server changed. If using + 'only_values=True' then output should be same as before. + + Args: + bundle_name (Optional[str]): Name of bundle for which should be + settings received. + variant (Optional[Literal['production', 'staging']]): Name of + settings variant. Used 'default_settings_variant' by default. + site_id (Optional[str]): Site id for which want to receive + site overrides. + use_site (bool): To force disable option of using site overrides + set to 'False'. In that case won't be applied any site + overrides. + only_values (Optional[bool]): Output will contain only settings + values without metadata about addons. + + Returns: + dict[str, Any]: Settings of all addons on server. + + """ + con = get_server_api_connection() + return con.get_addons_studio_settings( + bundle_name=bundle_name, + variant=variant, + site_id=site_id, + use_site=use_site, + only_values=only_values, + )
+ + +
[docs]def get_addons_project_settings( + project_name, + bundle_name=None, + variant=None, + site_id=None, + use_site=True, + only_values=True, +): + """Project settings of all addons. + + Server returns information about used addon versions, so full output + looks like: + + ```json + { + "settings": {...}, + "addons": {...} + } + ``` + + The output can be limited to only values. To do so is 'only_values' + argument which is by default set to 'True'. In that case output + contains only value of 'settings' key. + + Warnings: + Behavior of this function changed with AYON server version 0.3.0. + Structure of output from server changed. If using + 'only_values=True' then output should be same as before. + + Args: + project_name (str): Name of project for which are settings + received. + bundle_name (Optional[str]): Name of bundle for which should be + settings received. + variant (Optional[Literal['production', 'staging']]): Name of + settings variant. Used 'default_settings_variant' by default. + site_id (Optional[str]): Site id for which want to receive + site overrides. + use_site (bool): To force disable option of using site overrides + set to 'False'. In that case won't be applied any site + overrides. + only_values (Optional[bool]): Output will contain only settings + values without metadata about addons. + + Returns: + dict[str, Any]: Settings of all addons on server for passed + project. + + """ + con = get_server_api_connection() + return con.get_addons_project_settings( + project_name=project_name, + bundle_name=bundle_name, + variant=variant, + site_id=site_id, + use_site=use_site, + only_values=only_values, + )
+ + +
[docs]def get_addons_settings( + bundle_name=None, + project_name=None, + variant=None, + site_id=None, + use_site=True, + only_values=True, +): + """Universal function to receive all addon settings. + + Based on 'project_name' will receive studio settings or project + settings. In case project is not passed is 'site_id' ignored. + + Warnings: + Behavior of this function changed with AYON server version 0.3.0. + Structure of output from server changed. If using + 'only_values=True' then output should be same as before. + + Args: + bundle_name (Optional[str]): Name of bundle for which should be + settings received. + project_name (Optional[str]): Name of project for which should be + settings received. + variant (Optional[Literal['production', 'staging']]): Name of + settings variant. Used 'default_settings_variant' by default. + site_id (Optional[str]): Id of site for which want to receive + site overrides. + use_site (Optional[bool]): To force disable option of using site + overrides set to 'False'. In that case won't be applied + any site overrides. + only_values (Optional[bool]): Only settings values will be + returned. By default, is set to 'True'. + + """ + con = get_server_api_connection() + return con.get_addons_settings( + bundle_name=bundle_name, + project_name=project_name, + variant=variant, + site_id=site_id, + use_site=use_site, + only_values=only_values, + )
+ + +
[docs]def get_secrets(): + """Get all secrets. + + Example output:: + + [ + { + "name": "secret_1", + "value": "secret_value_1", + }, + { + "name": "secret_2", + "value": "secret_value_2", + } + ] + + Returns: + list[dict[str, str]]: List of secret entities. + + """ + con = get_server_api_connection() + return con.get_secrets()
+ + +
[docs]def get_secret( + secret_name, +): + """Get secret by name. + + Example output:: + + { + "name": "secret_name", + "value": "secret_value", + } + + Args: + secret_name (str): Name of secret. + + Returns: + dict[str, str]: Secret entity data. + + """ + con = get_server_api_connection() + return con.get_secret( + secret_name=secret_name, + )
+ + +
[docs]def save_secret( + secret_name, + secret_value, +): + """Save secret. + + This endpoint can create and update secret. + + Args: + secret_name (str): Name of secret. + secret_value (str): Value of secret. + + """ + con = get_server_api_connection() + return con.save_secret( + secret_name=secret_name, + secret_value=secret_value, + )
+ + +
[docs]def delete_secret( + secret_name, +): + """Delete secret by name. + + Args: + secret_name (str): Name of secret to delete. + + """ + con = get_server_api_connection() + return con.delete_secret( + secret_name=secret_name, + )
+ + +
[docs]def get_rest_project( + project_name, +): + """Query project by name. + + This call returns project with anatomy data. + + Args: + project_name (str): Name of project. + + Returns: + Union[dict[str, Any], None]: Project entity data or 'None' if + project was not found. + + """ + con = get_server_api_connection() + return con.get_rest_project( + project_name=project_name, + )
+ + +
[docs]def get_rest_projects( + active=True, + library=None, +): + """Query available project entities. + + User must be logged in. + + Args: + active (Optional[bool]): Filter active/inactive projects. Both + are returned if 'None' is passed. + library (Optional[bool]): Filter standard/library projects. Both + are returned if 'None' is passed. + + Returns: + Generator[dict[str, Any]]: Available projects. + + """ + con = get_server_api_connection() + return con.get_rest_projects( + active=active, + library=library, + )
+ + +
[docs]def get_rest_entity_by_id( + project_name, + entity_type, + entity_id, +): + """Get entity using REST on a project by its id. + + Args: + project_name (str): Name of project where entity is. + entity_type (Literal["folder", "task", "product", "version"]): The + entity type which should be received. + entity_id (str): Id of entity. + + Returns: + dict[str, Any]: Received entity data. + + """ + con = get_server_api_connection() + return con.get_rest_entity_by_id( + project_name=project_name, + entity_type=entity_type, + entity_id=entity_id, + )
+ + +
[docs]def get_rest_folder( + project_name, + folder_id, +): + con = get_server_api_connection() + return con.get_rest_folder( + project_name=project_name, + folder_id=folder_id, + )
+ + +
[docs]def get_rest_folders( + project_name, + include_attrib=False, +): + """Get simplified flat list of all project folders. + + Get all project folders in single REST call. This can be faster than + using 'get_folders' method which is using GraphQl, but does not + allow any filtering, and set of fields is defined + by server backend. + + Example:: + + [ + { + "id": "112233445566", + "parentId": "112233445567", + "path": "/root/parent/child", + "parents": ["root", "parent"], + "name": "child", + "label": "Child", + "folderType": "Folder", + "hasTasks": False, + "hasChildren": False, + "taskNames": [ + "Compositing", + ], + "status": "In Progress", + "attrib": {}, + "ownAttrib": [], + "updatedAt": "2023-06-12T15:37:02.420260", + }, + ... + ] + + Args: + project_name (str): Project name. + include_attrib (Optional[bool]): Include attribute values + in output. Slower to query. + + Returns: + list[dict[str, Any]]: List of folder entities. + + """ + con = get_server_api_connection() + return con.get_rest_folders( + project_name=project_name, + include_attrib=include_attrib, + )
+ + +
[docs]def get_rest_task( + project_name, + task_id, +): + con = get_server_api_connection() + return con.get_rest_task( + project_name=project_name, + task_id=task_id, + )
+ + +
[docs]def get_rest_product( + project_name, + product_id, +): + con = get_server_api_connection() + return con.get_rest_product( + project_name=project_name, + product_id=product_id, + )
+ + +
[docs]def get_rest_version( + project_name, + version_id, +): + con = get_server_api_connection() + return con.get_rest_version( + project_name=project_name, + version_id=version_id, + )
+ + +
[docs]def get_rest_representation( + project_name, + representation_id, +): + con = get_server_api_connection() + return con.get_rest_representation( + project_name=project_name, + representation_id=representation_id, + )
+ + +
[docs]def get_project_names( + active=True, + library=None, +): + """Receive available project names. + + User must be logged in. + + Args: + active (Optional[bool]): Filter active/inactive projects. Both + are returned if 'None' is passed. + library (Optional[bool]): Filter standard/library projects. Both + are returned if 'None' is passed. + + Returns: + list[str]: List of available project names. + + """ + con = get_server_api_connection() + return con.get_project_names( + active=active, + library=library, + )
+ + +
[docs]def get_projects( + active=True, + library=None, + fields=None, + own_attributes=False, +): + """Get projects. + + Args: + active (Optional[bool]): Filter active or inactive projects. + Filter is disabled when 'None' is passed. + library (Optional[bool]): Filter library projects. Filter is + disabled when 'None' is passed. + fields (Optional[Iterable[str]]): fields to be queried + for project. + own_attributes (Optional[bool]): Attribute values that are + not explicitly set on entity will have 'None' value. + + Returns: + Generator[dict[str, Any]]: Queried projects. + + """ + con = get_server_api_connection() + return con.get_projects( + active=active, + library=library, + fields=fields, + own_attributes=own_attributes, + )
+ + +
[docs]def get_project( + project_name, + fields=None, + own_attributes=False, +): + """Get project. + + Args: + project_name (str): Name of project. + fields (Optional[Iterable[str]]): fields to be queried + for project. + own_attributes (Optional[bool]): Attribute values that are + not explicitly set on entity will have 'None' value. + + Returns: + Union[dict[str, Any], None]: Project entity data or None + if project was not found. + + """ + con = get_server_api_connection() + return con.get_project( + project_name=project_name, + fields=fields, + own_attributes=own_attributes, + )
+ + +
[docs]def get_folders_hierarchy( + project_name, + search_string=None, + folder_types=None, +): + """Get project hierarchy. + + All folders in project in hierarchy data structure. + + Example output: + { + "hierarchy": [ + { + "id": "...", + "name": "...", + "label": "...", + "status": "...", + "folderType": "...", + "hasTasks": False, + "taskNames": [], + "parents": [], + "parentId": None, + "children": [...children folders...] + }, + ... + ] + } + + Args: + project_name (str): Project where to look for folders. + search_string (Optional[str]): Search string to filter folders. + folder_types (Optional[Iterable[str]]): Folder types to filter. + + Returns: + dict[str, Any]: Response data from server. + + """ + con = get_server_api_connection() + return con.get_folders_hierarchy( + project_name=project_name, + search_string=search_string, + folder_types=folder_types, + )
+ + +
[docs]def get_folders_rest( + project_name, + include_attrib=False, +): + """Get simplified flat list of all project folders. + + Get all project folders in single REST call. This can be faster than + using 'get_folders' method which is using GraphQl, but does not + allow any filtering, and set of fields is defined + by server backend. + + Example:: + + [ + { + "id": "112233445566", + "parentId": "112233445567", + "path": "/root/parent/child", + "parents": ["root", "parent"], + "name": "child", + "label": "Child", + "folderType": "Folder", + "hasTasks": False, + "hasChildren": False, + "taskNames": [ + "Compositing", + ], + "status": "In Progress", + "attrib": {}, + "ownAttrib": [], + "updatedAt": "2023-06-12T15:37:02.420260", + }, + ... + ] + + Deprecated: + Use 'get_rest_folders' instead. Function was renamed to match + other rest functions, like 'get_rest_folder', + 'get_rest_project' etc. . + Will be removed in '1.0.7' or '1.1.0'. + + Args: + project_name (str): Project name. + include_attrib (Optional[bool]): Include attribute values + in output. Slower to query. + + Returns: + list[dict[str, Any]]: List of folder entities. + + """ + con = get_server_api_connection() + return con.get_folders_rest( + project_name=project_name, + include_attrib=include_attrib, + )
+ + +
[docs]def get_folders( + project_name, + folder_ids=None, + folder_paths=None, + folder_names=None, + folder_types=None, + parent_ids=None, + folder_path_regex=None, + has_products=None, + has_tasks=None, + has_children=None, + statuses=None, + assignees_all=None, + tags=None, + active=True, + has_links=None, + fields=None, + own_attributes=False, +): + """Query folders from server. + + Todos: + Folder name won't be unique identifier, so we should add + folder path filtering. + + Notes: + Filter 'active' don't have direct filter in GraphQl. + + Args: + project_name (str): Name of project. + folder_ids (Optional[Iterable[str]]): Folder ids to filter. + folder_paths (Optional[Iterable[str]]): Folder paths used + for filtering. + folder_names (Optional[Iterable[str]]): Folder names used + for filtering. + folder_types (Optional[Iterable[str]]): Folder types used + for filtering. + parent_ids (Optional[Iterable[str]]): Ids of folder parents. + Use 'None' if folder is direct child of project. + folder_path_regex (Optional[str]): Folder path regex used + for filtering. + has_products (Optional[bool]): Filter folders with/without + products. Ignored when None, default behavior. + has_tasks (Optional[bool]): Filter folders with/without + tasks. Ignored when None, default behavior. + has_children (Optional[bool]): Filter folders with/without + children. Ignored when None, default behavior. + statuses (Optional[Iterable[str]]): Folder statuses used + for filtering. + assignees_all (Optional[Iterable[str]]): Filter by assigness + on children tasks. Task must have all of passed assignees. + tags (Optional[Iterable[str]]): Folder tags used + for filtering. + active (Optional[bool]): Filter active/inactive folders. + Both are returned if is set to None. + has_links (Optional[Literal[IN, OUT, ANY]]): Filter + representations with IN/OUT/ANY links. + fields (Optional[Iterable[str]]): Fields to be queried for + folder. All possible folder fields are returned + if 'None' is passed. + own_attributes (Optional[bool]): Attribute values that are + not explicitly set on entity will have 'None' value. + + Returns: + Generator[dict[str, Any]]: Queried folder entities. + + """ + con = get_server_api_connection() + return con.get_folders( + project_name=project_name, + folder_ids=folder_ids, + folder_paths=folder_paths, + folder_names=folder_names, + folder_types=folder_types, + parent_ids=parent_ids, + folder_path_regex=folder_path_regex, + has_products=has_products, + has_tasks=has_tasks, + has_children=has_children, + statuses=statuses, + assignees_all=assignees_all, + tags=tags, + active=active, + has_links=has_links, + fields=fields, + own_attributes=own_attributes, + )
+ + +
[docs]def get_folder_by_id( + project_name, + folder_id, + fields=None, + own_attributes=False, +): + """Query folder entity by id. + + Args: + project_name (str): Name of project where to look for queried + entities. + folder_id (str): Folder id. + fields (Optional[Iterable[str]]): Fields that should be returned. + All fields are returned if 'None' is passed. + own_attributes (Optional[bool]): Attribute values that are + not explicitly set on entity will have 'None' value. + + Returns: + Union[dict, None]: Folder entity data or None if was not found. + + """ + con = get_server_api_connection() + return con.get_folder_by_id( + project_name=project_name, + folder_id=folder_id, + fields=fields, + own_attributes=own_attributes, + )
+ + +
[docs]def get_folder_by_path( + project_name, + folder_path, + fields=None, + own_attributes=False, +): + """Query folder entity by path. + + Folder path is a path to folder with all parent names joined by slash. + + Args: + project_name (str): Name of project where to look for queried + entities. + folder_path (str): Folder path. + fields (Optional[Iterable[str]]): Fields that should be returned. + All fields are returned if 'None' is passed. + own_attributes (Optional[bool]): Attribute values that are + not explicitly set on entity will have 'None' value. + + Returns: + Union[dict, None]: Folder entity data or None if was not found. + + """ + con = get_server_api_connection() + return con.get_folder_by_path( + project_name=project_name, + folder_path=folder_path, + fields=fields, + own_attributes=own_attributes, + )
+ + +
[docs]def get_folder_by_name( + project_name, + folder_name, + fields=None, + own_attributes=False, +): + """Query folder entity by path. + + Warnings: + Folder name is not a unique identifier of a folder. Function is + kept for OpenPype 3 compatibility. + + Args: + project_name (str): Name of project where to look for queried + entities. + folder_name (str): Folder name. + fields (Optional[Iterable[str]]): Fields that should be returned. + All fields are returned if 'None' is passed. + own_attributes (Optional[bool]): Attribute values that are + not explicitly set on entity will have 'None' value. + + Returns: + Union[dict, None]: Folder entity data or None if was not found. + + """ + con = get_server_api_connection() + return con.get_folder_by_name( + project_name=project_name, + folder_name=folder_name, + fields=fields, + own_attributes=own_attributes, + )
+ + +
[docs]def get_folder_ids_with_products( + project_name, + folder_ids=None, +): + """Find folders which have at least one product. + + Folders that have at least one product should be immutable, so they + should not change path -> change of name or name of any parent + is not possible. + + Args: + project_name (str): Name of project. + folder_ids (Optional[Iterable[str]]): Limit folder ids filtering + to a set of folders. If set to None all folders on project are + checked. + + Returns: + set[str]: Folder ids that have at least one product. + + """ + con = get_server_api_connection() + return con.get_folder_ids_with_products( + project_name=project_name, + folder_ids=folder_ids, + )
+ + +
[docs]def create_folder( + project_name, + name, + folder_type=None, + parent_id=None, + label=None, + attrib=None, + data=None, + tags=None, + status=None, + active=None, + thumbnail_id=None, + folder_id=None, +): + """Create new folder. + + Args: + project_name (str): Project name. + name (str): Folder name. + folder_type (Optional[str]): Folder type. + parent_id (Optional[str]): Parent folder id. Parent is project + if is ``None``. + label (Optional[str]): Label of folder. + attrib (Optional[dict[str, Any]]): Folder attributes. + data (Optional[dict[str, Any]]): Folder data. + tags (Optional[Iterable[str]]): Folder tags. + status (Optional[str]): Folder status. + active (Optional[bool]): Folder active state. + thumbnail_id (Optional[str]): Folder thumbnail id. + folder_id (Optional[str]): Folder id. If not passed new id is + generated. + + Returns: + str: Entity id. + + """ + con = get_server_api_connection() + return con.create_folder( + project_name=project_name, + name=name, + folder_type=folder_type, + parent_id=parent_id, + label=label, + attrib=attrib, + data=data, + tags=tags, + status=status, + active=active, + thumbnail_id=thumbnail_id, + folder_id=folder_id, + )
+ + +
[docs]def update_folder( + project_name, + folder_id, + name=None, + folder_type=None, + parent_id=NOT_SET, + label=NOT_SET, + attrib=None, + data=None, + tags=None, + status=None, + active=None, + thumbnail_id=NOT_SET, +): + """Update folder entity on server. + + Do not pass ``parent_id``, ``label`` amd ``thumbnail_id`` if you don't + want to change their values. Value ``None`` would unset + their value. + + Update of ``data`` will override existing value on folder entity. + + Update of ``attrib`` does change only passed attributes. If you want + to unset value, use ``None``. + + Args: + project_name (str): Project name. + folder_id (str): Folder id. + name (Optional[str]): New name. + folder_type (Optional[str]): New folder type. + parent_id (Optional[Union[str, None]]): New parent folder id. + label (Optional[Union[str, None]]): New label. + attrib (Optional[dict[str, Any]]): New attributes. + data (Optional[dict[str, Any]]): New data. + tags (Optional[Iterable[str]]): New tags. + status (Optional[str]): New status. + active (Optional[bool]): New active state. + thumbnail_id (Optional[Union[str, None]]): New thumbnail id. + + """ + con = get_server_api_connection() + return con.update_folder( + project_name=project_name, + folder_id=folder_id, + name=name, + folder_type=folder_type, + parent_id=parent_id, + label=label, + attrib=attrib, + data=data, + tags=tags, + status=status, + active=active, + thumbnail_id=thumbnail_id, + )
+ + +
[docs]def delete_folder( + project_name, + folder_id, + force=False, +): + """Delete folder. + + Args: + project_name (str): Project name. + folder_id (str): Folder id to delete. + force (Optional[bool]): Folder delete folder with all children + folder, products, versions and representations. + + """ + con = get_server_api_connection() + return con.delete_folder( + project_name=project_name, + folder_id=folder_id, + force=force, + )
+ + +
[docs]def get_tasks( + project_name, + task_ids=None, + task_names=None, + task_types=None, + folder_ids=None, + assignees=None, + assignees_all=None, + statuses=None, + tags=None, + active=True, + fields=None, + own_attributes=False, +): + """Query task entities from server. + + Args: + project_name (str): Name of project. + task_ids (Iterable[str]): Task ids to filter. + task_names (Iterable[str]): Task names used for filtering. + task_types (Iterable[str]): Task types used for filtering. + folder_ids (Iterable[str]): Ids of task parents. Use 'None' + if folder is direct child of project. + assignees (Optional[Iterable[str]]): Task assignees used for + filtering. All tasks with any of passed assignees are + returned. + assignees_all (Optional[Iterable[str]]): Task assignees used + for filtering. Task must have all of passed assignees to be + returned. + statuses (Optional[Iterable[str]]): Task statuses used for + filtering. + tags (Optional[Iterable[str]]): Task tags used for + filtering. + active (Optional[bool]): Filter active/inactive tasks. + Both are returned if is set to None. + fields (Optional[Iterable[str]]): Fields to be queried for + folder. All possible folder fields are returned + if 'None' is passed. + own_attributes (Optional[bool]): Attribute values that are + not explicitly set on entity will have 'None' value. + + Returns: + Generator[dict[str, Any]]: Queried task entities. + + """ + con = get_server_api_connection() + return con.get_tasks( + project_name=project_name, + task_ids=task_ids, + task_names=task_names, + task_types=task_types, + folder_ids=folder_ids, + assignees=assignees, + assignees_all=assignees_all, + statuses=statuses, + tags=tags, + active=active, + fields=fields, + own_attributes=own_attributes, + )
+ + +
[docs]def get_task_by_name( + project_name, + folder_id, + task_name, + fields=None, + own_attributes=False, +): + """Query task entity by name and folder id. + + Args: + project_name (str): Name of project where to look for queried + entities. + folder_id (str): Folder id. + task_name (str): Task name + fields (Optional[Iterable[str]]): Fields that should be returned. + All fields are returned if 'None' is passed. + own_attributes (Optional[bool]): Attribute values that are + not explicitly set on entity will have 'None' value. + + Returns: + Union[dict, None]: Task entity data or None if was not found. + + """ + con = get_server_api_connection() + return con.get_task_by_name( + project_name=project_name, + folder_id=folder_id, + task_name=task_name, + fields=fields, + own_attributes=own_attributes, + )
+ + +
[docs]def get_task_by_id( + project_name, + task_id, + fields=None, + own_attributes=False, +): + """Query task entity by id. + + Args: + project_name (str): Name of project where to look for queried + entities. + task_id (str): Task id. + fields (Optional[Iterable[str]]): Fields that should be returned. + All fields are returned if 'None' is passed. + own_attributes (Optional[bool]): Attribute values that are + not explicitly set on entity will have 'None' value. + + Returns: + Union[dict, None]: Task entity data or None if was not found. + + """ + con = get_server_api_connection() + return con.get_task_by_id( + project_name=project_name, + task_id=task_id, + fields=fields, + own_attributes=own_attributes, + )
+ + +
[docs]def get_tasks_by_folder_paths( + project_name, + folder_paths, + task_names=None, + task_types=None, + assignees=None, + assignees_all=None, + statuses=None, + tags=None, + active=True, + fields=None, + own_attributes=False, +): + """Query task entities from server by folder paths. + + Args: + project_name (str): Name of project. + folder_paths (list[str]): Folder paths. + task_names (Iterable[str]): Task names used for filtering. + task_types (Iterable[str]): Task types used for filtering. + assignees (Optional[Iterable[str]]): Task assignees used for + filtering. All tasks with any of passed assignees are + returned. + assignees_all (Optional[Iterable[str]]): Task assignees used + for filtering. Task must have all of passed assignees to be + returned. + statuses (Optional[Iterable[str]]): Task statuses used for + filtering. + tags (Optional[Iterable[str]]): Task tags used for + filtering. + active (Optional[bool]): Filter active/inactive tasks. + Both are returned if is set to None. + fields (Optional[Iterable[str]]): Fields to be queried for + folder. All possible folder fields are returned + if 'None' is passed. + own_attributes (Optional[bool]): Attribute values that are + not explicitly set on entity will have 'None' value. + + Returns: + dict[dict[str, list[dict[str, Any]]]: Task entities by + folder path. + + """ + con = get_server_api_connection() + return con.get_tasks_by_folder_paths( + project_name=project_name, + folder_paths=folder_paths, + task_names=task_names, + task_types=task_types, + assignees=assignees, + assignees_all=assignees_all, + statuses=statuses, + tags=tags, + active=active, + fields=fields, + own_attributes=own_attributes, + )
+ + +
[docs]def get_tasks_by_folder_path( + project_name, + folder_path, + task_names=None, + task_types=None, + assignees=None, + assignees_all=None, + statuses=None, + tags=None, + active=True, + fields=None, + own_attributes=False, +): + """Query task entities from server by folder path. + + Args: + project_name (str): Name of project. + folder_path (str): Folder path. + task_names (Iterable[str]): Task names used for filtering. + task_types (Iterable[str]): Task types used for filtering. + assignees (Optional[Iterable[str]]): Task assignees used for + filtering. All tasks with any of passed assignees are + returned. + assignees_all (Optional[Iterable[str]]): Task assignees used + for filtering. Task must have all of passed assignees to be + returned. + statuses (Optional[Iterable[str]]): Task statuses used for + filtering. + tags (Optional[Iterable[str]]): Task tags used for + filtering. + active (Optional[bool]): Filter active/inactive tasks. + Both are returned if is set to None. + fields (Optional[Iterable[str]]): Fields to be queried for + folder. All possible folder fields are returned + if 'None' is passed. + own_attributes (Optional[bool]): Attribute values that are + not explicitly set on entity will have 'None' value. + + """ + con = get_server_api_connection() + return con.get_tasks_by_folder_path( + project_name=project_name, + folder_path=folder_path, + task_names=task_names, + task_types=task_types, + assignees=assignees, + assignees_all=assignees_all, + statuses=statuses, + tags=tags, + active=active, + fields=fields, + own_attributes=own_attributes, + )
+ + +
[docs]def get_task_by_folder_path( + project_name, + folder_path, + task_name, + fields=None, + own_attributes=False, +): + """Query task entity by folder path and task name. + + Args: + project_name (str): Project name. + folder_path (str): Folder path. + task_name (str): Task name. + fields (Optional[Iterable[str]]): Task fields that should + be returned. + own_attributes (Optional[bool]): Attribute values that are + not explicitly set on entity will have 'None' value. + + Returns: + Union[dict[str, Any], None]: Task entity data or None if was + not found. + + """ + con = get_server_api_connection() + return con.get_task_by_folder_path( + project_name=project_name, + folder_path=folder_path, + task_name=task_name, + fields=fields, + own_attributes=own_attributes, + )
+ + +
[docs]def create_task( + project_name, + name, + task_type, + folder_id, + label=None, + assignees=None, + attrib=None, + data=None, + tags=None, + status=None, + active=None, + thumbnail_id=None, + task_id=None, +): + """Create new task. + + Args: + project_name (str): Project name. + name (str): Folder name. + task_type (str): Task type. + folder_id (str): Parent folder id. + label (Optional[str]): Label of folder. + assignees (Optional[Iterable[str]]): Task assignees. + attrib (Optional[dict[str, Any]]): Task attributes. + data (Optional[dict[str, Any]]): Task data. + tags (Optional[Iterable[str]]): Task tags. + status (Optional[str]): Task status. + active (Optional[bool]): Task active state. + thumbnail_id (Optional[str]): Task thumbnail id. + task_id (Optional[str]): Task id. If not passed new id is + generated. + + Returns: + str: Task id. + + """ + con = get_server_api_connection() + return con.create_task( + project_name=project_name, + name=name, + task_type=task_type, + folder_id=folder_id, + label=label, + assignees=assignees, + attrib=attrib, + data=data, + tags=tags, + status=status, + active=active, + thumbnail_id=thumbnail_id, + task_id=task_id, + )
+ + +
[docs]def update_task( + project_name, + task_id, + name=None, + task_type=None, + folder_id=None, + label=NOT_SET, + assignees=None, + attrib=None, + data=None, + tags=None, + status=None, + active=None, + thumbnail_id=NOT_SET, +): + """Update task entity on server. + + Do not pass ``label`` amd ``thumbnail_id`` if you don't + want to change their values. Value ``None`` would unset + their value. + + Update of ``data`` will override existing value on folder entity. + + Update of ``attrib`` does change only passed attributes. If you want + to unset value, use ``None``. + + Args: + project_name (str): Project name. + task_id (str): Task id. + name (Optional[str]): New name. + task_type (Optional[str]): New task type. + folder_id (Optional[str]): New folder id. + label (Optional[Union[str, None]]): New label. + assignees (Optional[str]): New assignees. + attrib (Optional[dict[str, Any]]): New attributes. + data (Optional[dict[str, Any]]): New data. + tags (Optional[Iterable[str]]): New tags. + status (Optional[str]): New status. + active (Optional[bool]): New active state. + thumbnail_id (Optional[Union[str, None]]): New thumbnail id. + + """ + con = get_server_api_connection() + return con.update_task( + project_name=project_name, + task_id=task_id, + name=name, + task_type=task_type, + folder_id=folder_id, + label=label, + assignees=assignees, + attrib=attrib, + data=data, + tags=tags, + status=status, + active=active, + thumbnail_id=thumbnail_id, + )
+ + +
[docs]def delete_task( + project_name, + task_id, +): + """Delete task. + + Args: + project_name (str): Project name. + task_id (str): Task id to delete. + + """ + con = get_server_api_connection() + return con.delete_task( + project_name=project_name, + task_id=task_id, + )
+ + +
[docs]def get_products( + project_name, + product_ids=None, + product_names=None, + folder_ids=None, + product_types=None, + product_name_regex=None, + product_path_regex=None, + names_by_folder_ids=None, + statuses=None, + tags=None, + active=True, + fields=None, + own_attributes=_PLACEHOLDER, +): + """Query products from server. + + Todos: + Separate 'name_by_folder_ids' filtering to separated method. It + cannot be combined with some other filters. + + Args: + project_name (str): Name of project. + product_ids (Optional[Iterable[str]]): Task ids to filter. + product_names (Optional[Iterable[str]]): Task names used for + filtering. + folder_ids (Optional[Iterable[str]]): Ids of task parents. + Use 'None' if folder is direct child of project. + product_types (Optional[Iterable[str]]): Product types used for + filtering. + product_name_regex (Optional[str]): Filter products by name regex. + product_path_regex (Optional[str]): Filter products by path regex. + Path starts with folder path and ends with product name. + names_by_folder_ids (Optional[dict[str, Iterable[str]]]): Product + name filtering by folder id. + statuses (Optional[Iterable[str]]): Product statuses used + for filtering. + tags (Optional[Iterable[str]]): Product tags used + for filtering. + active (Optional[bool]): Filter active/inactive products. + Both are returned if is set to None. + fields (Optional[Iterable[str]]): Fields to be queried for + folder. All possible folder fields are returned + if 'None' is passed. + own_attributes (Optional[bool]): DEPRECATED: Not supported for + products. + + Returns: + Generator[dict[str, Any]]: Queried product entities. + + """ + con = get_server_api_connection() + return con.get_products( + project_name=project_name, + product_ids=product_ids, + product_names=product_names, + folder_ids=folder_ids, + product_types=product_types, + product_name_regex=product_name_regex, + product_path_regex=product_path_regex, + names_by_folder_ids=names_by_folder_ids, + statuses=statuses, + tags=tags, + active=active, + fields=fields, + own_attributes=own_attributes, + )
+ + +
[docs]def get_product_by_id( + project_name, + product_id, + fields=None, + own_attributes=_PLACEHOLDER, +): + """Query product entity by id. + + Args: + project_name (str): Name of project where to look for queried + entities. + product_id (str): Product id. + fields (Optional[Iterable[str]]): Fields that should be returned. + All fields are returned if 'None' is passed. + own_attributes (Optional[bool]): DEPRECATED: Not supported for + products. + + Returns: + Union[dict, None]: Product entity data or None if was not found. + + """ + con = get_server_api_connection() + return con.get_product_by_id( + project_name=project_name, + product_id=product_id, + fields=fields, + own_attributes=own_attributes, + )
+ + +
[docs]def get_product_by_name( + project_name, + product_name, + folder_id, + fields=None, + own_attributes=_PLACEHOLDER, +): + """Query product entity by name and folder id. + + Args: + project_name (str): Name of project where to look for queried + entities. + product_name (str): Product name. + folder_id (str): Folder id (Folder is a parent of products). + fields (Optional[Iterable[str]]): Fields that should be returned. + All fields are returned if 'None' is passed. + own_attributes (Optional[bool]): DEPRECATED: Not supported for + products. + + Returns: + Union[dict, None]: Product entity data or None if was not found. + + """ + con = get_server_api_connection() + return con.get_product_by_name( + project_name=project_name, + product_name=product_name, + folder_id=folder_id, + fields=fields, + own_attributes=own_attributes, + )
+ + +
[docs]def get_product_types( + fields=None, +): + """Types of products. + + This is server wide information. Product types have 'name', 'icon' and + 'color'. + + Args: + fields (Optional[Iterable[str]]): Product types fields to query. + + Returns: + list[dict[str, Any]]: Product types information. + + """ + con = get_server_api_connection() + return con.get_product_types( + fields=fields, + )
+ + +
[docs]def get_project_product_types( + project_name, + fields=None, +): + """Types of products available on a project. + + Filter only product types available on project. + + Args: + project_name (str): Name of project where to look for + product types. + fields (Optional[Iterable[str]]): Product types fields to query. + + Returns: + list[dict[str, Any]]: Product types information. + + """ + con = get_server_api_connection() + return con.get_project_product_types( + project_name=project_name, + fields=fields, + )
+ + +
[docs]def get_product_type_names( + project_name=None, + product_ids=None, +): + """Product type names. + + Warnings: + This function will be probably removed. Matters if 'products_id' + filter has real use-case. + + Args: + project_name (Optional[str]): Name of project where to look for + queried entities. + product_ids (Optional[Iterable[str]]): Product ids filter. Can be + used only with 'project_name'. + + Returns: + set[str]: Product type names. + + """ + con = get_server_api_connection() + return con.get_product_type_names( + project_name=project_name, + product_ids=product_ids, + )
+ + +
[docs]def create_product( + project_name, + name, + product_type, + folder_id, + attrib=None, + data=None, + tags=None, + status=None, + active=None, + product_id=None, +): + """Create new product. + + Args: + project_name (str): Project name. + name (str): Product name. + product_type (str): Product type. + folder_id (str): Parent folder id. + attrib (Optional[dict[str, Any]]): Product attributes. + data (Optional[dict[str, Any]]): Product data. + tags (Optional[Iterable[str]]): Product tags. + status (Optional[str]): Product status. + active (Optional[bool]): Product active state. + product_id (Optional[str]): Product id. If not passed new id is + generated. + + Returns: + str: Product id. + + """ + con = get_server_api_connection() + return con.create_product( + project_name=project_name, + name=name, + product_type=product_type, + folder_id=folder_id, + attrib=attrib, + data=data, + tags=tags, + status=status, + active=active, + product_id=product_id, + )
+ + +
[docs]def update_product( + project_name, + product_id, + name=None, + folder_id=None, + product_type=None, + attrib=None, + data=None, + tags=None, + status=None, + active=None, +): + """Update product entity on server. + + Update of ``data`` will override existing value on folder entity. + + Update of ``attrib`` does change only passed attributes. If you want + to unset value, use ``None``. + + Args: + project_name (str): Project name. + product_id (str): Product id. + name (Optional[str]): New product name. + folder_id (Optional[str]): New product id. + product_type (Optional[str]): New product type. + attrib (Optional[dict[str, Any]]): New product attributes. + data (Optional[dict[str, Any]]): New product data. + tags (Optional[Iterable[str]]): New product tags. + status (Optional[str]): New product status. + active (Optional[bool]): New product active state. + + """ + con = get_server_api_connection() + return con.update_product( + project_name=project_name, + product_id=product_id, + name=name, + folder_id=folder_id, + product_type=product_type, + attrib=attrib, + data=data, + tags=tags, + status=status, + active=active, + )
+ + +
[docs]def delete_product( + project_name, + product_id, +): + """Delete product. + + Args: + project_name (str): Project name. + product_id (str): Product id to delete. + + """ + con = get_server_api_connection() + return con.delete_product( + project_name=project_name, + product_id=product_id, + )
+ + +
[docs]def get_versions( + project_name, + version_ids=None, + product_ids=None, + task_ids=None, + versions=None, + hero=True, + standard=True, + latest=None, + statuses=None, + tags=None, + active=True, + fields=None, + own_attributes=_PLACEHOLDER, +): + """Get version entities based on passed filters from server. + + Args: + project_name (str): Name of project where to look for versions. + version_ids (Optional[Iterable[str]]): Version ids used for + version filtering. + product_ids (Optional[Iterable[str]]): Product ids used for + version filtering. + task_ids (Optional[Iterable[str]]): Task ids used for + version filtering. + versions (Optional[Iterable[int]]): Versions we're interested in. + hero (Optional[bool]): Skip hero versions when set to False. + standard (Optional[bool]): Skip standard (non-hero) when + set to False. + latest (Optional[bool]): Return only latest version of standard + versions. This can be combined only with 'standard' attribute + set to True. + statuses (Optional[Iterable[str]]): Representation statuses used + for filtering. + tags (Optional[Iterable[str]]): Representation tags used + for filtering. + active (Optional[bool]): Receive active/inactive entities. + Both are returned when 'None' is passed. + fields (Optional[Iterable[str]]): Fields to be queried + for version. All possible folder fields are returned + if 'None' is passed. + own_attributes (Optional[bool]): DEPRECATED: Not supported for + versions. + + Returns: + Generator[dict[str, Any]]: Queried version entities. + + """ + con = get_server_api_connection() + return con.get_versions( + project_name=project_name, + version_ids=version_ids, + product_ids=product_ids, + task_ids=task_ids, + versions=versions, + hero=hero, + standard=standard, + latest=latest, + statuses=statuses, + tags=tags, + active=active, + fields=fields, + own_attributes=own_attributes, + )
+ + +
[docs]def get_version_by_id( + project_name, + version_id, + fields=None, + own_attributes=_PLACEHOLDER, +): + """Query version entity by id. + + Args: + project_name (str): Name of project where to look for queried + entities. + version_id (str): Version id. + fields (Optional[Iterable[str]]): Fields that should be returned. + All fields are returned if 'None' is passed. + own_attributes (Optional[bool]): DEPRECATED: Not supported for + versions. + + Returns: + Union[dict, None]: Version entity data or None if was not found. + + """ + con = get_server_api_connection() + return con.get_version_by_id( + project_name=project_name, + version_id=version_id, + fields=fields, + own_attributes=own_attributes, + )
+ + +
[docs]def get_version_by_name( + project_name, + version, + product_id, + fields=None, + own_attributes=_PLACEHOLDER, +): + """Query version entity by version and product id. + + Args: + project_name (str): Name of project where to look for queried + entities. + version (int): Version of version entity. + product_id (str): Product id. Product is a parent of version. + fields (Optional[Iterable[str]]): Fields that should be returned. + All fields are returned if 'None' is passed. + own_attributes (Optional[bool]): DEPRECATED: Not supported for + versions. + + Returns: + Union[dict, None]: Version entity data or None if was not found. + + """ + con = get_server_api_connection() + return con.get_version_by_name( + project_name=project_name, + version=version, + product_id=product_id, + fields=fields, + own_attributes=own_attributes, + )
+ + +
[docs]def get_hero_version_by_id( + project_name, + version_id, + fields=None, + own_attributes=_PLACEHOLDER, +): + """Query hero version entity by id. + + Args: + project_name (str): Name of project where to look for queried + entities. + version_id (int): Hero version id. + fields (Optional[Iterable[str]]): Fields that should be returned. + All fields are returned if 'None' is passed. + own_attributes (Optional[bool]): DEPRECATED: Not supported for + versions. + + Returns: + Union[dict, None]: Version entity data or None if was not found. + + """ + con = get_server_api_connection() + return con.get_hero_version_by_id( + project_name=project_name, + version_id=version_id, + fields=fields, + own_attributes=own_attributes, + )
+ + +
[docs]def get_hero_version_by_product_id( + project_name, + product_id, + fields=None, + own_attributes=_PLACEHOLDER, +): + """Query hero version entity by product id. + + Only one hero version is available on a product. + + Args: + project_name (str): Name of project where to look for queried + entities. + product_id (int): Product id. + fields (Optional[Iterable[str]]): Fields that should be returned. + All fields are returned if 'None' is passed. + own_attributes (Optional[bool]): DEPRECATED: Not supported for + versions. + + Returns: + Union[dict, None]: Version entity data or None if was not found. + + """ + con = get_server_api_connection() + return con.get_hero_version_by_product_id( + project_name=project_name, + product_id=product_id, + fields=fields, + own_attributes=own_attributes, + )
+ + +
[docs]def get_hero_versions( + project_name, + product_ids=None, + version_ids=None, + active=True, + fields=None, + own_attributes=_PLACEHOLDER, +): + """Query hero versions by multiple filters. + + Only one hero version is available on a product. + + Args: + project_name (str): Name of project where to look for queried + entities. + product_ids (Optional[Iterable[str]]): Product ids. + version_ids (Optional[Iterable[str]]): Version ids. + active (Optional[bool]): Receive active/inactive entities. + Both are returned when 'None' is passed. + fields (Optional[Iterable[str]]): Fields that should be returned. + All fields are returned if 'None' is passed. + own_attributes (Optional[bool]): DEPRECATED: Not supported for + versions. + + Returns: + Union[dict, None]: Version entity data or None if was not found. + + """ + con = get_server_api_connection() + return con.get_hero_versions( + project_name=project_name, + product_ids=product_ids, + version_ids=version_ids, + active=active, + fields=fields, + own_attributes=own_attributes, + )
+ + +
[docs]def get_last_versions( + project_name, + product_ids, + active=True, + fields=None, + own_attributes=_PLACEHOLDER, +): + """Query last version entities by product ids. + + Args: + project_name (str): Project where to look for representation. + product_ids (Iterable[str]): Product ids. + active (Optional[bool]): Receive active/inactive entities. + Both are returned when 'None' is passed. + fields (Optional[Iterable[str]]): fields to be queried + for representations. + own_attributes (Optional[bool]): DEPRECATED: Not supported for + versions. + + Returns: + dict[str, dict[str, Any]]: Last versions by product id. + + """ + con = get_server_api_connection() + return con.get_last_versions( + project_name=project_name, + product_ids=product_ids, + active=active, + fields=fields, + own_attributes=own_attributes, + )
+ + +
[docs]def get_last_version_by_product_id( + project_name, + product_id, + active=True, + fields=None, + own_attributes=_PLACEHOLDER, +): + """Query last version entity by product id. + + Args: + project_name (str): Project where to look for representation. + product_id (str): Product id. + active (Optional[bool]): Receive active/inactive entities. + Both are returned when 'None' is passed. + fields (Optional[Iterable[str]]): fields to be queried + for representations. + own_attributes (Optional[bool]): DEPRECATED: Not supported for + versions. + + Returns: + Union[dict[str, Any], None]: Queried version entity or None. + + """ + con = get_server_api_connection() + return con.get_last_version_by_product_id( + project_name=project_name, + product_id=product_id, + active=active, + fields=fields, + own_attributes=own_attributes, + )
+ + +
[docs]def get_last_version_by_product_name( + project_name, + product_name, + folder_id, + active=True, + fields=None, + own_attributes=_PLACEHOLDER, +): + """Query last version entity by product name and folder id. + + Args: + project_name (str): Project where to look for representation. + product_name (str): Product name. + folder_id (str): Folder id. + active (Optional[bool]): Receive active/inactive entities. + Both are returned when 'None' is passed. + fields (Optional[Iterable[str]]): fields to be queried + for representations. + own_attributes (Optional[bool]): DEPRECATED: Not supported for + representations. + + Returns: + Union[dict[str, Any], None]: Queried version entity or None. + + """ + con = get_server_api_connection() + return con.get_last_version_by_product_name( + project_name=project_name, + product_name=product_name, + folder_id=folder_id, + active=active, + fields=fields, + own_attributes=own_attributes, + )
+ + +
[docs]def version_is_latest( + project_name, + version_id, +): + """Is version latest from a product. + + Args: + project_name (str): Project where to look for representation. + version_id (str): Version id. + + Returns: + bool: Version is latest or not. + + """ + con = get_server_api_connection() + return con.version_is_latest( + project_name=project_name, + version_id=version_id, + )
+ + +
[docs]def create_version( + project_name, + version, + product_id, + task_id=None, + author=None, + attrib=None, + data=None, + tags=None, + status=None, + active=None, + thumbnail_id=None, + version_id=None, +): + """Create new version. + + Args: + project_name (str): Project name. + version (int): Version. + product_id (str): Parent product id. + task_id (Optional[str]): Parent task id. + author (Optional[str]): Version author. + attrib (Optional[dict[str, Any]]): Version attributes. + data (Optional[dict[str, Any]]): Version data. + tags (Optional[Iterable[str]]): Version tags. + status (Optional[str]): Version status. + active (Optional[bool]): Version active state. + thumbnail_id (Optional[str]): Version thumbnail id. + version_id (Optional[str]): Version id. If not passed new id is + generated. + + Returns: + str: Version id. + + """ + con = get_server_api_connection() + return con.create_version( + project_name=project_name, + version=version, + product_id=product_id, + task_id=task_id, + author=author, + attrib=attrib, + data=data, + tags=tags, + status=status, + active=active, + thumbnail_id=thumbnail_id, + version_id=version_id, + )
+ + +
[docs]def update_version( + project_name, + version_id, + version=None, + product_id=None, + task_id=NOT_SET, + author=None, + attrib=None, + data=None, + tags=None, + status=None, + active=None, + thumbnail_id=NOT_SET, +): + """Update version entity on server. + + Do not pass ``task_id`` amd ``thumbnail_id`` if you don't + want to change their values. Value ``None`` would unset + their value. + + Update of ``data`` will override existing value on folder entity. + + Update of ``attrib`` does change only passed attributes. If you want + to unset value, use ``None``. + + Args: + project_name (str): Project name. + version_id (str): Version id. + version (Optional[int]): New version. + product_id (Optional[str]): New product id. + task_id (Optional[Union[str, None]]): New task id. + author (Optional[str]): New author username. + attrib (Optional[dict[str, Any]]): New attributes. + data (Optional[dict[str, Any]]): New data. + tags (Optional[Iterable[str]]): New tags. + status (Optional[str]): New status. + active (Optional[bool]): New active state. + thumbnail_id (Optional[Union[str, None]]): New thumbnail id. + + """ + con = get_server_api_connection() + return con.update_version( + project_name=project_name, + version_id=version_id, + version=version, + product_id=product_id, + task_id=task_id, + author=author, + attrib=attrib, + data=data, + tags=tags, + status=status, + active=active, + thumbnail_id=thumbnail_id, + )
+ + +
[docs]def delete_version( + project_name, + version_id, +): + """Delete version. + + Args: + project_name (str): Project name. + version_id (str): Version id to delete. + + """ + con = get_server_api_connection() + return con.delete_version( + project_name=project_name, + version_id=version_id, + )
+ + +
[docs]def get_representations( + project_name, + representation_ids=None, + representation_names=None, + version_ids=None, + names_by_version_ids=None, + statuses=None, + tags=None, + active=True, + has_links=None, + fields=None, + own_attributes=_PLACEHOLDER, +): + """Get representation entities based on passed filters from server. + + .. todo:: + + Add separated function for 'names_by_version_ids' filtering. + Because can't be combined with others. + + Args: + project_name (str): Name of project where to look for versions. + representation_ids (Optional[Iterable[str]]): Representation ids + used for representation filtering. + representation_names (Optional[Iterable[str]]): Representation + names used for representation filtering. + version_ids (Optional[Iterable[str]]): Version ids used for + representation filtering. Versions are parents of + representations. + names_by_version_ids (Optional[Dict[str, Iterable[str]]): Find + representations by names and version ids. This filter + discards all other filters. + statuses (Optional[Iterable[str]]): Representation statuses used + for filtering. + tags (Optional[Iterable[str]]): Representation tags used + for filtering. + active (Optional[bool]): Receive active/inactive entities. + Both are returned when 'None' is passed. + has_links (Optional[Literal[IN, OUT, ANY]]): Filter + representations with IN/OUT/ANY links. + fields (Optional[Iterable[str]]): Fields to be queried for + representation. All possible fields are returned if 'None' is + passed. + own_attributes (Optional[bool]): DEPRECATED: Not supported for + representations. + + Returns: + Generator[dict[str, Any]]: Queried representation entities. + + """ + con = get_server_api_connection() + return con.get_representations( + project_name=project_name, + representation_ids=representation_ids, + representation_names=representation_names, + version_ids=version_ids, + names_by_version_ids=names_by_version_ids, + statuses=statuses, + tags=tags, + active=active, + has_links=has_links, + fields=fields, + own_attributes=own_attributes, + )
+ + +
[docs]def get_representation_by_id( + project_name, + representation_id, + fields=None, + own_attributes=_PLACEHOLDER, +): + """Query representation entity from server based on id filter. + + Args: + project_name (str): Project where to look for representation. + representation_id (str): Id of representation. + fields (Optional[Iterable[str]]): fields to be queried + for representations. + own_attributes (Optional[bool]): DEPRECATED: Not supported for + representations. + + Returns: + Union[dict[str, Any], None]: Queried representation entity or None. + + """ + con = get_server_api_connection() + return con.get_representation_by_id( + project_name=project_name, + representation_id=representation_id, + fields=fields, + own_attributes=own_attributes, + )
+ + +
[docs]def get_representation_by_name( + project_name, + representation_name, + version_id, + fields=None, + own_attributes=_PLACEHOLDER, +): + """Query representation entity by name and version id. + + Args: + project_name (str): Project where to look for representation. + representation_name (str): Representation name. + version_id (str): Version id. + fields (Optional[Iterable[str]]): fields to be queried + for representations. + own_attributes (Optional[bool]): DEPRECATED: Not supported for + representations. + + Returns: + Union[dict[str, Any], None]: Queried representation entity or None. + + """ + con = get_server_api_connection() + return con.get_representation_by_name( + project_name=project_name, + representation_name=representation_name, + version_id=version_id, + fields=fields, + own_attributes=own_attributes, + )
+ + +
[docs]def get_representations_hierarchy( + project_name, + representation_ids, + project_fields=None, + folder_fields=None, + task_fields=None, + product_fields=None, + version_fields=None, + representation_fields=None, +): + """Find representation with parents by representation id. + + Representation entity with parent entities up to project. + + Default fields are used when any fields are set to `None`. But it is + possible to pass in empty iterable (list, set, tuple) to skip + entity. + + Args: + project_name (str): Project where to look for entities. + representation_ids (Iterable[str]): Representation ids. + project_fields (Optional[Iterable[str]]): Project fields. + folder_fields (Optional[Iterable[str]]): Folder fields. + task_fields (Optional[Iterable[str]]): Task fields. + product_fields (Optional[Iterable[str]]): Product fields. + version_fields (Optional[Iterable[str]]): Version fields. + representation_fields (Optional[Iterable[str]]): Representation + fields. + + Returns: + dict[str, RepresentationHierarchy]: Parent entities by + representation id. + + """ + con = get_server_api_connection() + return con.get_representations_hierarchy( + project_name=project_name, + representation_ids=representation_ids, + project_fields=project_fields, + folder_fields=folder_fields, + task_fields=task_fields, + product_fields=product_fields, + version_fields=version_fields, + representation_fields=representation_fields, + )
+ + +
[docs]def get_representation_hierarchy( + project_name, + representation_id, + project_fields=None, + folder_fields=None, + task_fields=None, + product_fields=None, + version_fields=None, + representation_fields=None, +): + """Find representation parents by representation id. + + Representation parent entities up to project. + + Args: + project_name (str): Project where to look for entities. + representation_id (str): Representation id. + project_fields (Optional[Iterable[str]]): Project fields. + folder_fields (Optional[Iterable[str]]): Folder fields. + task_fields (Optional[Iterable[str]]): Task fields. + product_fields (Optional[Iterable[str]]): Product fields. + version_fields (Optional[Iterable[str]]): Version fields. + representation_fields (Optional[Iterable[str]]): Representation + fields. + + Returns: + RepresentationHierarchy: Representation hierarchy entities. + + """ + con = get_server_api_connection() + return con.get_representation_hierarchy( + project_name=project_name, + representation_id=representation_id, + project_fields=project_fields, + folder_fields=folder_fields, + task_fields=task_fields, + product_fields=product_fields, + version_fields=version_fields, + representation_fields=representation_fields, + )
+ + +
[docs]def get_representations_parents( + project_name, + representation_ids, + project_fields=None, + folder_fields=None, + product_fields=None, + version_fields=None, +): + """Find representations parents by representation id. + + Representation parent entities up to project. + + Args: + project_name (str): Project where to look for entities. + representation_ids (Iterable[str]): Representation ids. + project_fields (Optional[Iterable[str]]): Project fields. + folder_fields (Optional[Iterable[str]]): Folder fields. + product_fields (Optional[Iterable[str]]): Product fields. + version_fields (Optional[Iterable[str]]): Version fields. + + Returns: + dict[str, RepresentationParents]: Parent entities by + representation id. + + """ + con = get_server_api_connection() + return con.get_representations_parents( + project_name=project_name, + representation_ids=representation_ids, + project_fields=project_fields, + folder_fields=folder_fields, + product_fields=product_fields, + version_fields=version_fields, + )
+ + +
[docs]def get_representation_parents( + project_name, + representation_id, + project_fields=None, + folder_fields=None, + product_fields=None, + version_fields=None, +): + """Find representation parents by representation id. + + Representation parent entities up to project. + + Args: + project_name (str): Project where to look for entities. + representation_id (str): Representation id. + project_fields (Optional[Iterable[str]]): Project fields. + folder_fields (Optional[Iterable[str]]): Folder fields. + product_fields (Optional[Iterable[str]]): Product fields. + version_fields (Optional[Iterable[str]]): Version fields. + + Returns: + RepresentationParents: Representation parent entities. + + """ + con = get_server_api_connection() + return con.get_representation_parents( + project_name=project_name, + representation_id=representation_id, + project_fields=project_fields, + folder_fields=folder_fields, + product_fields=product_fields, + version_fields=version_fields, + )
+ + +
[docs]def get_repre_ids_by_context_filters( + project_name, + context_filters, + representation_names=None, + version_ids=None, +): + """Find representation ids which match passed context filters. + + Each representation has context integrated on representation entity in + database. The context may contain project, folder, task name or + product name, product type and many more. This implementation gives + option to quickly filter representation based on representation data + in database. + + Context filters have defined structure. To define filter of nested + subfield use dot '.' as delimiter (For example 'task.name'). + Filter values can be regex filters. String or ``re.Pattern`` can + be used. + + Args: + project_name (str): Project where to look for representations. + context_filters (dict[str, list[str]]): Filters of context fields. + representation_names (Optional[Iterable[str]]): Representation + names, can be used as additional filter for representations + by their names. + version_ids (Optional[Iterable[str]]): Version ids, can be used + as additional filter for representations by their parent ids. + + Returns: + list[str]: Representation ids that match passed filters. + + Example: + The function returns just representation ids so if entities are + required for funtionality they must be queried afterwards by + their ids. + >>> project_name = "testProject" + >>> filters = { + ... "task.name": ["[aA]nimation"], + ... "product": [".*[Mm]ain"] + ... } + >>> repre_ids = get_repre_ids_by_context_filters( + ... project_name, filters) + >>> repres = get_representations(project_name, repre_ids) + + """ + con = get_server_api_connection() + return con.get_repre_ids_by_context_filters( + project_name=project_name, + context_filters=context_filters, + representation_names=representation_names, + version_ids=version_ids, + )
+ + +
[docs]def create_representation( + project_name, + name, + version_id, + files=None, + attrib=None, + data=None, + tags=None, + status=None, + active=None, + representation_id=None, +): + """Create new representation. + + Args: + project_name (str): Project name. + name (str): Representation name. + version_id (str): Parent version id. + files (Optional[list[dict]]): Representation files information. + attrib (Optional[dict[str, Any]]): Representation attributes. + data (Optional[dict[str, Any]]): Representation data. + tags (Optional[Iterable[str]]): Representation tags. + status (Optional[str]): Representation status. + active (Optional[bool]): Representation active state. + representation_id (Optional[str]): Representation id. If not + passed new id is generated. + + Returns: + str: Representation id. + + """ + con = get_server_api_connection() + return con.create_representation( + project_name=project_name, + name=name, + version_id=version_id, + files=files, + attrib=attrib, + data=data, + tags=tags, + status=status, + active=active, + representation_id=representation_id, + )
+ + +
[docs]def update_representation( + project_name, + representation_id, + name=None, + version_id=None, + files=None, + attrib=None, + data=None, + tags=None, + status=None, + active=None, +): + """Update representation entity on server. + + Update of ``data`` will override existing value on folder entity. + + Update of ``attrib`` does change only passed attributes. If you want + to unset value, use ``None``. + + Args: + project_name (str): Project name. + representation_id (str): Representation id. + name (Optional[str]): New name. + version_id (Optional[str]): New version id. + files (Optional[list[dict]]): New files + information. + attrib (Optional[dict[str, Any]]): New attributes. + data (Optional[dict[str, Any]]): New data. + tags (Optional[Iterable[str]]): New tags. + status (Optional[str]): New status. + active (Optional[bool]): New active state. + + """ + con = get_server_api_connection() + return con.update_representation( + project_name=project_name, + representation_id=representation_id, + name=name, + version_id=version_id, + files=files, + attrib=attrib, + data=data, + tags=tags, + status=status, + active=active, + )
+ + +
[docs]def delete_representation( + project_name, + representation_id, +): + """Delete representation. + + Args: + project_name (str): Project name. + representation_id (str): Representation id to delete. + + """ + con = get_server_api_connection() + return con.delete_representation( + project_name=project_name, + representation_id=representation_id, + )
+ + +
[docs]def get_workfiles_info( + project_name, + workfile_ids=None, + task_ids=None, + paths=None, + path_regex=None, + statuses=None, + tags=None, + has_links=None, + fields=None, + own_attributes=_PLACEHOLDER, +): + """Workfile info entities by passed filters. + + Args: + project_name (str): Project under which the entity is located. + workfile_ids (Optional[Iterable[str]]): Workfile ids. + task_ids (Optional[Iterable[str]]): Task ids. + paths (Optional[Iterable[str]]): Rootless workfiles paths. + path_regex (Optional[str]): Regex filter for workfile path. + statuses (Optional[Iterable[str]]): Workfile info statuses used + for filtering. + tags (Optional[Iterable[str]]): Workfile info tags used + for filtering. + has_links (Optional[Literal[IN, OUT, ANY]]): Filter + representations with IN/OUT/ANY links. + fields (Optional[Iterable[str]]): Fields to be queried for + representation. All possible fields are returned if 'None' is + passed. + own_attributes (Optional[bool]): DEPRECATED: Not supported for + workfiles. + + Returns: + Generator[dict[str, Any]]: Queried workfile info entites. + + """ + con = get_server_api_connection() + return con.get_workfiles_info( + project_name=project_name, + workfile_ids=workfile_ids, + task_ids=task_ids, + paths=paths, + path_regex=path_regex, + statuses=statuses, + tags=tags, + has_links=has_links, + fields=fields, + own_attributes=own_attributes, + )
+ + +
[docs]def get_workfile_info( + project_name, + task_id, + path, + fields=None, + own_attributes=_PLACEHOLDER, +): + """Workfile info entity by task id and workfile path. + + Args: + project_name (str): Project under which the entity is located. + task_id (str): Task id. + path (str): Rootless workfile path. + fields (Optional[Iterable[str]]): Fields to be queried for + representation. All possible fields are returned if 'None' is + passed. + own_attributes (Optional[bool]): DEPRECATED: Not supported for + workfiles. + + Returns: + Union[dict[str, Any], None]: Workfile info entity or None. + + """ + con = get_server_api_connection() + return con.get_workfile_info( + project_name=project_name, + task_id=task_id, + path=path, + fields=fields, + own_attributes=own_attributes, + )
+ + +
[docs]def get_workfile_info_by_id( + project_name, + workfile_id, + fields=None, + own_attributes=_PLACEHOLDER, +): + """Workfile info entity by id. + + Args: + project_name (str): Project under which the entity is located. + workfile_id (str): Workfile info id. + fields (Optional[Iterable[str]]): Fields to be queried for + representation. All possible fields are returned if 'None' is + passed. + own_attributes (Optional[bool]): DEPRECATED: Not supported for + workfiles. + + Returns: + Union[dict[str, Any], None]: Workfile info entity or None. + + """ + con = get_server_api_connection() + return con.get_workfile_info_by_id( + project_name=project_name, + workfile_id=workfile_id, + fields=fields, + own_attributes=own_attributes, + )
+ + +
[docs]def get_thumbnail_by_id( + project_name, + thumbnail_id, +): + """Get thumbnail from server by id. + + Permissions of thumbnails are related to entities so thumbnails must + be queried per entity. So an entity type and entity type is required + to be passed. + + Notes: + It is recommended to use one of prepared entity type specific + methods 'get_folder_thumbnail', 'get_version_thumbnail' or + 'get_workfile_thumbnail'. + We do recommend pass thumbnail id if you have access to it. Each + entity that allows thumbnails has 'thumbnailId' field, so it + can be queried. + + Args: + project_name (str): Project under which the entity is located. + thumbnail_id (Optional[str]): DEPRECATED Use + 'get_thumbnail_by_id'. + + Returns: + ThumbnailContent: Thumbnail content wrapper. Does not have to be + valid. + + """ + con = get_server_api_connection() + return con.get_thumbnail_by_id( + project_name=project_name, + thumbnail_id=thumbnail_id, + )
+ + +
[docs]def get_thumbnail( + project_name, + entity_type, + entity_id, + thumbnail_id=None, +): + """Get thumbnail from server. + + Permissions of thumbnails are related to entities so thumbnails must + be queried per entity. So an entity type and entity type is required + to be passed. + + Notes: + It is recommended to use one of prepared entity type specific + methods 'get_folder_thumbnail', 'get_version_thumbnail' or + 'get_workfile_thumbnail'. + We do recommend pass thumbnail id if you have access to it. Each + entity that allows thumbnails has 'thumbnailId' field, so it + can be queried. + + Args: + project_name (str): Project under which the entity is located. + entity_type (str): Entity type which passed entity id represents. + entity_id (str): Entity id for which thumbnail should be returned. + thumbnail_id (Optional[str]): DEPRECATED Use + 'get_thumbnail_by_id'. + + Returns: + ThumbnailContent: Thumbnail content wrapper. Does not have to be + valid. + + """ + con = get_server_api_connection() + return con.get_thumbnail( + project_name=project_name, + entity_type=entity_type, + entity_id=entity_id, + thumbnail_id=thumbnail_id, + )
+ + +
[docs]def get_folder_thumbnail( + project_name, + folder_id, + thumbnail_id=None, +): + """Prepared method to receive thumbnail for folder entity. + + Args: + project_name (str): Project under which the entity is located. + folder_id (str): Folder id for which thumbnail should be returned. + thumbnail_id (Optional[str]): Prepared thumbnail id from entity. + Used only to check if thumbnail was already cached. + + Returns: + Union[str, None]: Path to downloaded thumbnail or none if entity + does not have any (or if user does not have permissions). + + """ + con = get_server_api_connection() + return con.get_folder_thumbnail( + project_name=project_name, + folder_id=folder_id, + thumbnail_id=thumbnail_id, + )
+ + +
[docs]def get_version_thumbnail( + project_name, + version_id, + thumbnail_id=None, +): + """Prepared method to receive thumbnail for version entity. + + Args: + project_name (str): Project under which the entity is located. + version_id (str): Version id for which thumbnail should be + returned. + thumbnail_id (Optional[str]): Prepared thumbnail id from entity. + Used only to check if thumbnail was already cached. + + Returns: + Union[str, None]: Path to downloaded thumbnail or none if entity + does not have any (or if user does not have permissions). + + """ + con = get_server_api_connection() + return con.get_version_thumbnail( + project_name=project_name, + version_id=version_id, + thumbnail_id=thumbnail_id, + )
+ + +
[docs]def get_workfile_thumbnail( + project_name, + workfile_id, + thumbnail_id=None, +): + """Prepared method to receive thumbnail for workfile entity. + + Args: + project_name (str): Project under which the entity is located. + workfile_id (str): Worfile id for which thumbnail should be + returned. + thumbnail_id (Optional[str]): Prepared thumbnail id from entity. + Used only to check if thumbnail was already cached. + + Returns: + Union[str, None]: Path to downloaded thumbnail or none if entity + does not have any (or if user does not have permissions). + + """ + con = get_server_api_connection() + return con.get_workfile_thumbnail( + project_name=project_name, + workfile_id=workfile_id, + thumbnail_id=thumbnail_id, + )
+ + +
[docs]def create_thumbnail( + project_name, + src_filepath, + thumbnail_id=None, +): + """Create new thumbnail on server from passed path. + + Args: + project_name (str): Project where the thumbnail will be created + and can be used. + src_filepath (str): Filepath to thumbnail which should be uploaded. + thumbnail_id (Optional[str]): Prepared if of thumbnail. + + Returns: + str: Created thumbnail id. + + Raises: + ValueError: When thumbnail source cannot be processed. + + """ + con = get_server_api_connection() + return con.create_thumbnail( + project_name=project_name, + src_filepath=src_filepath, + thumbnail_id=thumbnail_id, + )
+ + +
[docs]def update_thumbnail( + project_name, + thumbnail_id, + src_filepath, +): + """Change thumbnail content by id. + + Update can be also used to create new thumbnail. + + Args: + project_name (str): Project where the thumbnail will be created + and can be used. + thumbnail_id (str): Thumbnail id to update. + src_filepath (str): Filepath to thumbnail which should be uploaded. + + Raises: + ValueError: When thumbnail source cannot be processed. + + """ + con = get_server_api_connection() + return con.update_thumbnail( + project_name=project_name, + thumbnail_id=thumbnail_id, + src_filepath=src_filepath, + )
+ + +
[docs]def create_project( + project_name, + project_code, + library_project=False, + preset_name=None, +): + """Create project using AYON settings. + + This project creation function is not validating project entity on + creation. It is because project entity is created blindly with only + minimum required information about project which is name and code. + + Entered project name must be unique and project must not exist yet. + + Note: + This function is here to be OP v4 ready but in v3 has more logic + to do. That's why inner imports are in the body. + + Args: + project_name (str): New project name. Should be unique. + project_code (str): Project's code should be unique too. + library_project (Optional[bool]): Project is library project. + preset_name (Optional[str]): Name of anatomy preset. Default is + used if not passed. + + Raises: + ValueError: When project name already exists. + + Returns: + dict[str, Any]: Created project entity. + + """ + con = get_server_api_connection() + return con.create_project( + project_name=project_name, + project_code=project_code, + library_project=library_project, + preset_name=preset_name, + )
+ + +
[docs]def update_project( + project_name, + library=None, + folder_types=None, + task_types=None, + link_types=None, + statuses=None, + tags=None, + config=None, + attrib=None, + data=None, + active=None, + project_code=None, + **changes, +): + """Update project entity on server. + + Args: + project_name (str): Name of project. + library (Optional[bool]): Change library state. + folder_types (Optional[list[dict[str, Any]]]): Folder type + definitions. + task_types (Optional[list[dict[str, Any]]]): Task type + definitions. + link_types (Optional[list[dict[str, Any]]]): Link type + definitions. + statuses (Optional[list[dict[str, Any]]]): Status definitions. + tags (Optional[list[dict[str, Any]]]): List of tags available to + set on entities. + config (Optional[dict[dict[str, Any]]]): Project anatomy config + with templates and roots. + attrib (Optional[dict[str, Any]]): Project attributes to change. + data (Optional[dict[str, Any]]): Custom data of a project. This + value will 100% override project data. + active (Optional[bool]): Change active state of a project. + project_code (Optional[str]): Change project code. Not recommended + during production. + **changes: Other changed keys based on Rest API documentation. + + """ + con = get_server_api_connection() + return con.update_project( + project_name=project_name, + library=library, + folder_types=folder_types, + task_types=task_types, + link_types=link_types, + statuses=statuses, + tags=tags, + config=config, + attrib=attrib, + data=data, + active=active, + project_code=project_code, + **changes, + )
+ + +
[docs]def delete_project( + project_name, +): + """Delete project from server. + + This will completely remove project from server without any step back. + + Args: + project_name (str): Project name that will be removed. + + """ + con = get_server_api_connection() + return con.delete_project( + project_name=project_name, + )
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
[docs]def send_batch_operations( + project_name, + operations, + can_fail=False, + raise_on_fail=True, +): + """Post multiple CRUD operations to server. + + When multiple changes should be made on server side this is the best + way to go. It is possible to pass multiple operations to process on a + server side and do the changes in a transaction. + + Args: + project_name (str): On which project should be operations + processed. + operations (list[dict[str, Any]]): Operations to be processed. + can_fail (Optional[bool]): Server will try to process all + operations even if one of them fails. + raise_on_fail (Optional[bool]): Raise exception if an operation + fails. You can handle failed operations on your own + when set to 'False'. + + Raises: + ValueError: Operations can't be converted to json string. + FailedOperations: When output does not contain server operations + or 'raise_on_fail' is enabled and any operation fails. + + Returns: + list[dict[str, Any]]: Operations result with process details. + + """ + con = get_server_api_connection() + return con.send_batch_operations( + project_name=project_name, + operations=operations, + can_fail=can_fail, + raise_on_fail=raise_on_fail, + )
+ + +
[docs]def send_activities_batch_operations( + project_name, + operations, + can_fail=False, + raise_on_fail=True, +): + """Post multiple CRUD activities operations to server. + + When multiple changes should be made on server side this is the best + way to go. It is possible to pass multiple operations to process on a + server side and do the changes in a transaction. + + Args: + project_name (str): On which project should be operations + processed. + operations (list[dict[str, Any]]): Operations to be processed. + can_fail (Optional[bool]): Server will try to process all + operations even if one of them fails. + raise_on_fail (Optional[bool]): Raise exception if an operation + fails. You can handle failed operations on your own + when set to 'False'. + + Raises: + ValueError: Operations can't be converted to json string. + FailedOperations: When output does not contain server operations + or 'raise_on_fail' is enabled and any operation fails. + + Returns: + list[dict[str, Any]]: Operations result with process details. + + """ + con = get_server_api_connection() + return con.send_activities_batch_operations( + project_name=project_name, + operations=operations, + can_fail=can_fail, + raise_on_fail=raise_on_fail, + )
+
+ +
+ +
+
+ + +
+

+ + © Copyright 2024, ynput.io +
+ + + + Built with Sphinx using a + + theme + + provided by Revitron. + +

+
+ +
+ +
+
+ +
+ +
+ + + + + + + + + + + + \ No newline at end of file diff --git a/_modules/ayon_api/entity_hub.html b/_modules/ayon_api/entity_hub.html new file mode 100644 index 0000000000..5f432a4b1b --- /dev/null +++ b/_modules/ayon_api/entity_hub.html @@ -0,0 +1,3889 @@ + + + + + + + + + + + ayon_api.entity_hub — ayon-python-api 1.0.11 documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + +
+ + + + + +
+ +
+ +
+ + + +
+
+
+ +

Source code for ayon_api.entity_hub

+import re
+import copy
+import collections
+import warnings
+from abc import ABC, abstractmethod
+import typing
+from typing import Optional, Iterable, Dict, List, Set, Any
+
+from ._api import get_server_api_connection
+from .utils import create_entity_id, convert_entity_id, slugify_string
+
+if typing.TYPE_CHECKING:
+    from typing import Literal, Union
+
+    StatusState = Literal["not_started", "in_progress", "done", "blocked"]
+    EntityType = Literal["project", "folder", "task", "product", "version"]
+
+
+class _CustomNone(object):
+    def __init__(self, name=None):
+        self._name = name or "CustomNone"
+
+    def __repr__(self):
+        return "<{}>".format(self._name)
+
+    def __bool__(self):
+        return False
+
+
+UNKNOWN_VALUE = _CustomNone("UNKNOWN_VALUE")
+PROJECT_PARENT_ID = _CustomNone("PROJECT_PARENT_ID")
+_NOT_SET = _CustomNone("_NOT_SET")
+
+
+
[docs]class EntityHub(object): + """Helper to create, update or remove entities in project. + + The hub is a guide to operation with folder entities and update of project. + Project entity must already exist on server (can be only updated). + + Object is caching entities queried from server. They won't be required once + they were queried, so it is recommended to create new hub or clear cache + frequently. + + Todos: + Listen to server events about entity changes to be able to update + already queried entities. + + Args: + project_name (str): Name of project where changes will happen. + connection (ServerAPI): Connection to server with logged user. + allow_data_changes (bool): This option gives ability to change 'data' + key on entities. This is not recommended as 'data' may be used for + secure information and would also slow down server queries. Content + of 'data' key can't be received only GraphQl. + + """ + + def __init__( + self, project_name, connection=None, allow_data_changes=None + ): + if not connection: + connection = get_server_api_connection() + major, minor, _, _, _ = connection.server_version_tuple + path_start_with_slash = True + if (major, minor) < (0, 6): + path_start_with_slash = False + + if allow_data_changes is None: + allow_data_changes = connection.graphql_allows_data_in_query + + self._connection = connection + self._path_start_with_slash = path_start_with_slash + + self._project_name = project_name + self._entities_by_id = {} + self._entities_by_parent_id = collections.defaultdict(list) + self._project_entity = UNKNOWN_VALUE + + self._allow_data_changes = allow_data_changes + + self._path_reset_queue = None + + @property + def allow_data_changes(self): + """Entity hub allows changes of 'data' key on entities. + + Data are private and not all users may have access to them. + + Older version of AYON server allowed to get 'data' for entity only + using REST api calls, which means to query each entity on-by-one + from server. + + Returns: + bool: Data changes are allowed. + + """ + return self._allow_data_changes + + @property + def path_start_with_slash(self): + """Folder path should start with slash. + + This changed in 0.6.x server version. + + Returns: + bool: Path starts with slash. + + """ + return self._path_start_with_slash + + @property + def project_name(self): + """Project name which is maintained by hub. + + Returns: + str: Name of project. + + """ + return self._project_name + + @property + def project_entity(self): + """Project entity. + + Returns: + ProjectEntity: Project entity. + + """ + if self._project_entity is UNKNOWN_VALUE: + self.fill_project_from_server() + return self._project_entity + +
[docs] def get_attributes_for_type(self, entity_type: "EntityType"): + """Get attributes available for a type. + + Attributes are based on entity types. + + Todos: + Use attribute schema to validate values on entities. + + Args: + entity_type (EntityType): Entity type for which should + be attributes received. + + Returns: + Dict[str, Dict[str, Any]]: Attribute schemas that are available + for entered entity type. + + """ + return self._connection.get_attributes_for_type(entity_type)
+ +
[docs] def get_entity_by_id(self, entity_id: str) -> Optional["BaseEntity"]: + """Receive entity by its id without entity type. + + The entity must be already existing in cached objects. + + Args: + entity_id (str): Id of entity. + + Returns: + Optional[BaseEntity]: Entity object or None. + + """ + return self._entities_by_id.get(entity_id)
+ +
[docs] def get_folder_by_id( + self, + entity_id: str, + allow_fetch: Optional[bool] = True, + ) -> Optional["FolderEntity"]: + """Get folder entity by id. + + Args: + entity_id (str): Folder entity id. + allow_fetch (bool): Try to fetch entity from server if is not + available in cache. + + Returns: + Optional[FolderEntity]: Folder entity object. + + """ + if allow_fetch: + return self.get_or_fetch_entity_by_id(entity_id, ["folder"]) + return self._entities_by_id.get(entity_id)
+ +
[docs] def get_task_by_id( + self, + entity_id: str, + allow_fetch: Optional[bool] = True, + ) -> Optional["TaskEntity"]: + """Get task entity by id. + + Args: + entity_id (str): Id of task entity. + allow_fetch (bool): Try to fetch entity from server if is not + available in cache. + + Returns: + Optional[TaskEntity]: Task entity object or None. + + """ + if allow_fetch: + return self.get_or_fetch_entity_by_id(entity_id, ["task"]) + return self._entities_by_id.get(entity_id)
+ +
[docs] def get_product_by_id( + self, + entity_id: str, + allow_fetch: Optional[bool] = True, + ) -> Optional["ProductEntity"]: + """Get product entity by id. + + Args: + entity_id (str): Product id. + allow_fetch (bool): Try to fetch entity from server if is not + available in cache. + + Returns: + Optional[ProductEntity]: Product entity object or None. + + """ + if allow_fetch: + return self.get_or_fetch_entity_by_id(entity_id, ["product"]) + return self._entities_by_id.get(entity_id)
+ +
[docs] def get_version_by_id( + self, + entity_id: str, + allow_fetch: Optional[bool] = True, + ) -> Optional["VersionEntity"]: + """Get version entity by id. + + Args: + entity_id (str): Version id. + allow_fetch (bool): Try to fetch entity from server if is not + available in cache. + + Returns: + Optional[VersionEntity]: Version entity object or None. + + """ + if allow_fetch: + return self.get_or_fetch_entity_by_id(entity_id, ["version"]) + return self._entities_by_id.get(entity_id)
+ +
[docs] def get_or_fetch_entity_by_id( + self, + entity_id: str, + entity_types: List["EntityType"], + ): + """Get or query entity based on it's id and possible entity types. + + This is a helper function when entity id is known but entity type may + have multiple possible options. + + Args: + entity_id (str): Entity id. + entity_types (Iterable[str]): Possible entity types that can the id + represent. e.g. '["folder", "project"]' + + """ + existing_entity = self._entities_by_id.get(entity_id) + if existing_entity is not None: + return existing_entity + + if not entity_types: + return None + + entity_type = None + entity_data = None + for entity_type in entity_types: + if entity_type == "folder": + entity_data = self._connection.get_folder_by_id( + self.project_name, + entity_id, + fields=self._get_folder_fields(), + own_attributes=True + ) + elif entity_type == "task": + entity_data = self._connection.get_task_by_id( + self.project_name, + entity_id, + fields=self._get_task_fields(), + own_attributes=True + ) + elif entity_type == "product": + entity_data = self._connection.get_product_by_id( + self.project_name, + entity_id, + fields=self._get_product_fields(), + ) + elif entity_type == "version": + entity_data = self._connection.get_version_by_id( + self.project_name, + entity_id, + fields=self._get_version_fields(), + ) + else: + raise ValueError( + "Unknown entity type \"{}\"".format(entity_type) + ) + + if entity_data: + break + + if not entity_data: + return None + + if entity_type == "folder": + folder_entity = self.add_folder(entity_data) + folder_entity.has_published_content = entity_data["hasProducts"] + return folder_entity + + elif entity_type == "task": + return self.add_task(entity_data) + + elif entity_type == "product": + return self.add_product(entity_data) + + elif entity_type == "version": + return self.add_version(entity_data) + + return None
+ +
[docs] def get_or_query_entity_by_id( + self, + entity_id: str, + entity_types: List["EntityType"], + ): + warnings.warn( + "Method 'get_or_query_entity_by_id' is deprecated. " + "Please use 'get_or_fetch_entity_by_id' instead.", + DeprecationWarning + ) + return self.get_or_fetch_entity_by_id(entity_id, entity_types)
+ + @property + def entities(self): + """Iterator over available entities. + + Returns: + Iterator[BaseEntity]: All queried/created entities cached in hub. + + """ + for entity in self._entities_by_id.values(): + yield entity + +
[docs] def add_new_folder( + self, + name: str, + folder_type: str, + parent_id: Optional[str] = UNKNOWN_VALUE, + label: Optional[str] = None, + path: Optional[str] = None, + status: Optional[str] = UNKNOWN_VALUE, + tags: Optional[List[str]] = None, + attribs: Optional[Dict[str, Any]] = UNKNOWN_VALUE, + data: Optional[Dict[str, Any]] = UNKNOWN_VALUE, + thumbnail_id: Optional[str] = UNKNOWN_VALUE, + active: bool = UNKNOWN_VALUE, + entity_id: Optional[str] = None, + created: Optional[bool] = True, + ): + """Create folder object and add it to entity hub. + + Args: + name (str): Name of entity. + folder_type (str): Type of folder. Folder type must be available in + config of project folder types. + parent_id (Union[str, None]): Id of parent entity. + label (Optional[str]): Folder label. + path (Optional[str]): Folder path. Path consist of all parent names + with slash('/') used as separator. + status (Optional[str]): Folder status. + tags (Optional[List[str]]): Folder tags. + attribs (Dict[str, Any]): Attribute values. + data (Dict[str, Any]): Entity data (custom data). + thumbnail_id (Union[str, None]): Id of entity's thumbnail. + active (bool): Is entity active. + entity_id (Optional[str]): Id of the entity. New id is created if + not passed. + created (Optional[bool]): Entity is new. When 'None' is passed the + value is defined based on value of 'entity_id'. + + Returns: + FolderEntity: Added folder entity. + + """ + folder_entity = FolderEntity( + name=name, + folder_type=folder_type, + parent_id=parent_id, + label=label, + path=path, + status=status, + tags=tags, + attribs=attribs, + data=data, + thumbnail_id=thumbnail_id, + active=active, + entity_id=entity_id, + created=created, + entity_hub=self + ) + self.add_entity(folder_entity) + return folder_entity
+ +
[docs] def add_new_task( + self, + name: str, + task_type: str, + folder_id: Optional[str] = UNKNOWN_VALUE, + label: Optional[str] = None, + status: Optional[str] = UNKNOWN_VALUE, + tags: Optional[Iterable[str]] = None, + attribs: Optional[Dict[str, Any]] = UNKNOWN_VALUE, + data: Optional[Dict[str, Any]] = UNKNOWN_VALUE, + assignees: Optional[Iterable[str]] = None, + thumbnail_id: Optional[str] = UNKNOWN_VALUE, + active: Optional[bool] = UNKNOWN_VALUE, + entity_id: Optional[str] = None, + created: Optional[bool] = True, + parent_id: Optional[str] = UNKNOWN_VALUE, + ): + """Create task object and add it to entity hub. + + Args: + name (str): Name of entity. + task_type (str): Type of task. Task type must be available in + config of project task types. + folder_id (Union[str, None]): Parent folder id. + label (Optional[str]): Task label. + status (Optional[str]): Task status. + tags (Optional[Iterable[str]]): Folder tags. + attribs (Dict[str, Any]): Attribute values. + data (Dict[str, Any]): Entity data (custom data). + assignees (Optional[Iterable[str]]): User assignees to the task. + thumbnail_id (Union[str, None]): Id of entity's thumbnail. + active (bool): Is entity active. + entity_id (Optional[str]): Id of the entity. New id is created if + not passed. + created (Optional[bool]): Entity is new. When 'None' is passed the + value is defined based on value of 'entity_id'. + parent_id (Union[str, None]): DEPRECATED Parent folder id. + + Returns: + TaskEntity: Added task entity. + + """ + if parent_id is not UNKNOWN_VALUE: + warnings.warn( + "Used deprecated argument 'parent_id'." + " Use 'folder_id' instead.", + DeprecationWarning + ) + folder_id = parent_id + + task_entity = TaskEntity( + name=name, + task_type=task_type, + folder_id=folder_id, + label=label, + status=status, + tags=tags, + attribs=attribs, + data=data, + assignees=assignees, + thumbnail_id=thumbnail_id, + active=active, + entity_id=entity_id, + created=created, + entity_hub=self, + ) + self.add_entity(task_entity) + return task_entity
+ +
[docs] def add_new_product( + self, + name: str, + product_type: str, + folder_id: Optional["Union[str, _CustomNone]"] = UNKNOWN_VALUE, + tags: Optional[Iterable[str]] = None, + attribs: Optional[Dict[str, Any]] = UNKNOWN_VALUE, + data: Optional[Dict[str, Any]] = UNKNOWN_VALUE, + active: Optional[bool] = UNKNOWN_VALUE, + entity_id: Optional[str] = None, + created: Optional[bool] = True, + ): + """Create task object and add it to entity hub. + + Args: + name (str): Name of entity. + product_type (str): Type of product. + folder_id (Union[str, None]): Parent folder id. + tags (Optional[Iterable[str]]): Folder tags. + attribs (Dict[str, Any]): Attribute values. + data (Dict[str, Any]): Entity data (custom data). + active (bool): Is entity active. + entity_id (Optional[str]): Id of the entity. New id is created if + not passed. + created (Optional[bool]): Entity is new. When 'None' is passed the + value is defined based on value of 'entity_id'. + + Returns: + ProductEntity: Added product entity. + + """ + product_entity = ProductEntity( + name=name, + product_type=product_type, + folder_id=folder_id, + tags=tags, + attribs=attribs, + data=data, + active=active, + entity_id=entity_id, + created=created, + entity_hub=self, + ) + self.add_entity(product_entity) + return product_entity
+ +
[docs] def add_new_version( + self, + version: int, + product_id: Optional["Union[str, _CustomNone]"] = UNKNOWN_VALUE, + task_id: Optional["Union[str, _CustomNone]"] = UNKNOWN_VALUE, + status: Optional[str] = UNKNOWN_VALUE, + tags: Optional[Iterable[str]] = None, + attribs: Optional[Dict[str, Any]] = UNKNOWN_VALUE, + data: Optional[Dict[str, Any]] = UNKNOWN_VALUE, + thumbnail_id: Optional[str] = UNKNOWN_VALUE, + active: Optional[bool] = UNKNOWN_VALUE, + entity_id: Optional[str] = None, + created: Optional[bool] = True, + ): + """Create task object and add it to entity hub. + + Args: + version (int): Version. + product_id (Union[str, None]): Parent product id. + task_id (Union[str, None]): Parent task id. + status (Optional[str]): Task status. + tags (Optional[Iterable[str]]): Folder tags. + attribs (Dict[str, Any]): Attribute values. + data (Dict[str, Any]): Entity data (custom data). + thumbnail_id (Union[str, None]): Id of entity's thumbnail. + active (bool): Is entity active. + entity_id (Optional[str]): Id of the entity. New id is created if + not passed. + created (Optional[bool]): Entity is new. When 'None' is passed the + value is defined based on value of 'entity_id'. + + Returns: + VersionEntity: Added version entity. + + """ + version_entity = VersionEntity( + version=version, + product_id=product_id, + task_id=task_id, + status=status, + tags=tags, + attribs=attribs, + data=data, + thumbnail_id=thumbnail_id, + active=active, + entity_id=entity_id, + created=created, + entity_hub=self, + ) + self.add_entity(version_entity) + return version_entity
+ +
[docs] def add_folder(self, folder): + """Create folder object and add it to entity hub. + + Args: + folder (Dict[str, Any]): Folder entity data. + + Returns: + FolderEntity: Added folder entity. + + """ + folder_entity = FolderEntity.from_entity_data(folder, entity_hub=self) + self.add_entity(folder_entity) + return folder_entity
+ +
[docs] def add_task(self, task): + """Create task object and add it to entity hub. + + Args: + task (Dict[str, Any]): Task entity data. + + Returns: + TaskEntity: Added task entity. + + """ + task_entity = TaskEntity.from_entity_data(task, entity_hub=self) + self.add_entity(task_entity) + return task_entity
+ +
[docs] def add_product(self, product): + """Create version object and add it to entity hub. + + Args: + product (Dict[str, Any]): Version entity data. + + Returns: + ProductEntity: Added version entity. + + """ + product_entity = ProductEntity.from_entity_data( + product, entity_hub=self + ) + self.add_entity(product_entity) + return product_entity
+ +
[docs] def add_version(self, version): + """Create version object and add it to entity hub. + + Args: + version (Dict[str, Any]): Version entity data. + + Returns: + VersionEntity: Added version entity. + + """ + version_entity = VersionEntity.from_entity_data( + version, entity_hub=self + ) + self.add_entity(version_entity) + return version_entity
+ +
[docs] def add_entity(self, entity): + """Add entity to hub cache. + + Args: + entity (BaseEntity): Entity that should be added to hub's cache. + + """ + self._entities_by_id[entity.id] = entity + parent_children = self._entities_by_parent_id[entity.parent_id] + if entity not in parent_children: + parent_children.append(entity) + + if entity.parent_id is PROJECT_PARENT_ID: + return + + parent = self._entities_by_id.get(entity.parent_id) + if parent is not None: + parent.add_child(entity.id)
+ +
[docs] def folder_path_reseted(self, folder_id): + """Method called from 'FolderEntity' on path reset. + + This should reset cache of folder paths on all children entities. + + The path cache is always propagated from top to bottom so if an entity + has not cached path it means that any children can't have it cached. + + """ + if self._path_reset_queue is not None: + self._path_reset_queue.append(folder_id) + return + + self._path_reset_queue = collections.deque() + self._path_reset_queue.append(folder_id) + while self._path_reset_queue: + children = self._entities_by_parent_id[folder_id] + for child in children: + # Get child path but don't trigger cache + path = child.get_path(False) + if path is not None: + # Reset it's path cache if is set + child.reset_path() + else: + self._path_reset_queue.append(child.id) + + self._path_reset_queue = None
+ +
[docs] def unset_entity_parent(self, entity_id, parent_id): + entity = self._entities_by_id.get(entity_id) + parent = self._entities_by_id.get(parent_id) + children_ids = UNKNOWN_VALUE + if parent is not None: + children_ids = parent.get_children_ids(False) + + has_set_parent = False + if entity is not None: + has_set_parent = entity.parent_id == parent_id + + new_parent_id = None + if has_set_parent: + entity.parent_id = new_parent_id + + if children_ids is not UNKNOWN_VALUE and entity_id in children_ids: + parent.remove_child(entity_id) + + if entity is None or not has_set_parent: + self.reset_immutable_for_hierarchy_cache(parent_id) + return + + orig_parent_children = self._entities_by_parent_id[parent_id] + if entity in orig_parent_children: + orig_parent_children.remove(entity) + + new_parent_children = self._entities_by_parent_id[new_parent_id] + if entity not in new_parent_children: + new_parent_children.append(entity) + self.reset_immutable_for_hierarchy_cache(parent_id)
+ +
[docs] def set_entity_parent(self, entity_id, parent_id, orig_parent_id=_NOT_SET): + parent = self._entities_by_id.get(parent_id) + entity = self._entities_by_id.get(entity_id) + if entity is None: + if parent is not None: + children_ids = parent.get_children_ids(False) + if ( + children_ids is not UNKNOWN_VALUE + and entity_id in children_ids + ): + parent.remove_child(entity_id) + self.reset_immutable_for_hierarchy_cache(parent.id) + return + + if orig_parent_id is _NOT_SET: + orig_parent_id = entity.parent_id + if orig_parent_id == parent_id: + return + + orig_parent_children = self._entities_by_parent_id[orig_parent_id] + if entity in orig_parent_children: + orig_parent_children.remove(entity) + self.reset_immutable_for_hierarchy_cache(orig_parent_id) + + orig_parent = self._entities_by_id.get(orig_parent_id) + if orig_parent is not None: + orig_parent.remove_child(entity_id) + + parent_children = self._entities_by_parent_id[parent_id] + if entity not in parent_children: + parent_children.append(entity) + + entity.parent_id = parent_id + if parent is None or parent.get_children_ids(False) is UNKNOWN_VALUE: + return + + parent.add_child(entity_id) + self.reset_immutable_for_hierarchy_cache(parent_id)
+ + def _fetch_entity_children(self, entity): + folder_fields = self._get_folder_fields() + task_fields = self._get_task_fields() + tasks = [] + folders = [] + if entity.entity_type == "project": + folders = list(self._connection.get_folders( + entity["name"], + parent_ids=[entity.id], + fields=folder_fields, + own_attributes=True, + )) + + elif entity.entity_type == "folder": + folders = list(self._connection.get_folders( + self.project_entity["name"], + parent_ids=[entity.id], + fields=folder_fields, + own_attributes=True, + )) + + tasks = list(self._connection.get_tasks( + self.project_entity["name"], + folder_ids=[entity.id], + fields=task_fields, + own_attributes=True, + )) + + children_ids = { + child.id + for child in self._entities_by_parent_id[entity.id] + } + for folder in folders: + folder_entity = self._entities_by_id.get(folder["id"]) + if folder_entity is None: + folder_entity = self.add_folder(folder) + children_ids.add(folder_entity.id) + + elif folder_entity.parent_id == entity.id: + children_ids.add(folder_entity.id) + + folder_entity.has_published_content = folder["hasProducts"] + + for task in tasks: + task_entity = self._entities_by_id.get(task["id"]) + if task_entity is not None: + if task_entity.parent_id == entity.id: + children_ids.add(task_entity.id) + continue + + task_entity = self.add_task(task) + children_ids.add(task_entity.id) + + entity.fill_children_ids(children_ids) + +
[docs] def get_entity_children(self, entity, allow_fetch=True): + children_ids = entity.get_children_ids(allow_fetch=False) + if children_ids is not UNKNOWN_VALUE: + return entity.get_children() + + if children_ids is UNKNOWN_VALUE and not allow_fetch: + return UNKNOWN_VALUE + + self._fetch_entity_children(entity) + + return entity.get_children()
+ +
[docs] def delete_entity(self, entity): + parent_id = entity.parent_id + if parent_id is None: + return + + parent = self._entities_by_id.get(parent_id) + if parent is not None: + parent.remove_child(entity.id) + else: + self.unset_entity_parent(entity.id, parent_id)
+ +
[docs] def reset_immutable_for_hierarchy_cache( + self, entity_id: Optional[str], bottom_to_top: Optional[bool] = True + ): + if bottom_to_top is None or entity_id is None: + return + + reset_queue = collections.deque() + reset_queue.append(entity_id) + if bottom_to_top: + while reset_queue: + entity_id: str = reset_queue.popleft() + entity: Optional["BaseEntity"] = self.get_entity_by_id( + entity_id + ) + if entity is None: + continue + entity.reset_immutable_for_hierarchy_cache(None) + reset_queue.append(entity.parent_id) + else: + while reset_queue: + entity_id: str = reset_queue.popleft() + entity: Optional["BaseEntity"] = self.get_entity_by_id( + entity_id + ) + if entity is None: + continue + entity.reset_immutable_for_hierarchy_cache(None) + for child in self._entities_by_parent_id[entity.id]: + reset_queue.append(child.id)
+ +
[docs] def fill_project_from_server(self): + """Query project data from server and create project entity. + + This method will invalidate previous object of Project entity. + + Returns: + ProjectEntity: Entity that was updated with server data. + + Raises: + ValueError: When project was not found on server. + + """ + project_name = self.project_name + project = self._connection.get_project( + project_name, + own_attributes=True + ) + if not project: + raise ValueError( + "Project \"{}\" was not found.".format(project_name) + ) + major, minor, _, _, _ = self._connection.get_server_version_tuple() + status_scope_supported = True + if (major, minor) < (1, 5): + status_scope_supported = False + self._project_entity = ProjectEntity.from_entity_data( + project, self + ) + self._project_entity.set_status_scope_supported( + status_scope_supported + ) + + self.add_entity(self._project_entity) + return self._project_entity
+ + def _get_folder_fields(self) -> Set[str]: + folder_fields = set( + self._connection.get_default_fields_for_type("folder") + ) + folder_fields.add("hasProducts") + if self._allow_data_changes: + folder_fields.add("data") + return folder_fields + + def _get_task_fields(self) -> Set[str]: + return set( + self._connection.get_default_fields_for_type("task") + ) + + def _get_product_fields(self) -> Set[str]: + return set( + self._connection.get_default_fields_for_type("product") + ) + + def _get_version_fields(self) -> Set[str]: + return set( + self._connection.get_default_fields_for_type("version") + ) + +
[docs] def fetch_hierarchy_entities(self): + """Query whole project at once.""" + project_entity = self.fill_project_from_server() + + folder_fields = self._get_folder_fields() + task_fields = self._get_task_fields() + + folders = self._connection.get_folders( + project_entity.name, + fields=folder_fields, + own_attributes=True, + ) + tasks = self._connection.get_tasks( + project_entity.name, + fields=task_fields, + own_attributes=True, + ) + folders_by_parent_id = collections.defaultdict(list) + for folder in folders: + parent_id = folder["parentId"] + folders_by_parent_id[parent_id].append(folder) + + tasks_by_parent_id = collections.defaultdict(list) + for task in tasks: + parent_id = task["folderId"] + tasks_by_parent_id[parent_id].append(task) + + lock_queue = collections.deque() + hierarchy_queue = collections.deque() + hierarchy_queue.append((None, project_entity)) + while hierarchy_queue: + item = hierarchy_queue.popleft() + parent_id, parent_entity = item + + lock_queue.append(parent_entity) + + children_ids = set() + for folder in folders_by_parent_id[parent_id]: + folder_entity = self.add_folder(folder) + children_ids.add(folder_entity.id) + folder_entity.has_published_content = folder["hasProducts"] + hierarchy_queue.append((folder_entity.id, folder_entity)) + + for task in tasks_by_parent_id[parent_id]: + task_entity = self.add_task(task) + lock_queue.append(task_entity) + children_ids.add(task_entity.id) + + parent_entity.fill_children_ids(children_ids) + + # Lock entities when all are added to hub + # - lock only entities added in this method + while lock_queue: + entity = lock_queue.popleft() + entity.lock()
+ +
[docs] def query_entities_from_server(self): + warnings.warn( + "Method 'query_entities_from_server' is deprecated." + " Please use 'fetch_hierarchy_entities' instead.", + DeprecationWarning + ) + return self.fetch_hierarchy_entities()
+ +
[docs] def lock(self): + if self._project_entity is None: + return + + for entity in self._entities_by_id.values(): + entity.lock()
+ + def _get_top_entities(self): + all_ids = set(self._entities_by_id.keys()) + return [ + entity + for entity in self._entities_by_id.values() + if entity.parent_id not in all_ids + ] + + def _split_entities(self): + top_entities = self._get_top_entities() + entities_queue = collections.deque(top_entities) + removed_entity_ids = [] + created_entity_ids = [] + other_entity_ids = [] + while entities_queue: + entity = entities_queue.popleft() + removed = entity.removed + if removed: + removed_entity_ids.append(entity.id) + elif entity.created: + created_entity_ids.append(entity.id) + else: + other_entity_ids.append(entity.id) + + for child in tuple(self._entities_by_parent_id[entity.id]): + if removed: + self.unset_entity_parent(child.id, entity.id) + entities_queue.append(child) + return created_entity_ids, other_entity_ids, removed_entity_ids + + def _get_update_body(self, entity, changes=None): + if changes is None: + changes = entity.changes + + if not changes: + return None + return { + "type": "update", + "entityType": entity.entity_type, + "entityId": entity.id, + "data": changes + } + + def _get_create_body(self, entity): + return { + "type": "create", + "entityType": entity.entity_type, + "entityId": entity.id, + "data": entity.to_create_body_data() + } + + def _get_delete_body(self, entity): + return { + "type": "delete", + "entityType": entity.entity_type, + "entityId": entity.id + } + + def _pre_commit_types_changes( + self, project_changes, orig_types, changes_key, post_changes + ): + """Compare changes of types on a project. + + Compare old and new types. Change project changes content if some old + types were removed. In that case the final change of types will + happen when all other entities have changed. + + Args: + project_changes (dict[str, Any]): Project changes. + orig_types (list[dict[str, Any]]): Original types. + changes_key (Literal["folderTypes", "taskTypes"]): Key of type + changes in project changes. + post_changes (dict[str, Any]): An object where post changes will + be stored. + + """ + if changes_key not in project_changes: + return + + new_types = project_changes[changes_key] + + orig_types_by_name = { + type_info["name"]: type_info + for type_info in orig_types + } + new_names = { + type_info["name"] + for type_info in new_types + } + diff_names = set(orig_types_by_name) - new_names + if not diff_names: + return + + # Create copy of folder type changes to post changes + # - post changes will be commited at the end + post_changes[changes_key] = copy.deepcopy(new_types) + + for type_name in diff_names: + new_types.append(orig_types_by_name[type_name]) + + def _pre_commit_project(self): + """Some project changes cannot be committed before hierarchy changes. + + It is not possible to change folder types or task types if there are + existing hierarchy items using the removed types. For that purposes + is first committed union of all old and new types and post changes + are prepared when all existing entities are changed. + + Returns: + dict[str, Any]: Changes that will be committed after hierarchy + changes. + + """ + project_changes = self.project_entity.changes + + post_changes = {} + if not project_changes: + return post_changes + + self._pre_commit_types_changes( + project_changes, + self.project_entity.get_orig_folder_types(), + "folderType", + post_changes + ) + self._pre_commit_types_changes( + project_changes, + self.project_entity.get_orig_task_types(), + "taskType", + post_changes + ) + self._connection.update_project(self.project_name, **project_changes) + return post_changes + +
[docs] def commit_changes(self): + """Commit any changes that happened on entities. + + Todo: + Use Operations Session instead of known operations body. + + """ + post_project_changes = self._pre_commit_project() + self.project_entity.lock() + + project_changes = self.project_entity.changes + if project_changes: + response = self._connection.patch( + "projects/{}".format(self.project_name), + **project_changes + ) + response.raise_for_status() + + self.project_entity.lock() + + operations_body = [] + + created_entity_ids, other_entity_ids, removed_entity_ids = ( + self._split_entities() + ) + processed_ids = set() + for entity_id in other_entity_ids: + if entity_id in processed_ids: + continue + + entity = self._entities_by_id[entity_id] + changes = entity.changes + processed_ids.add(entity_id) + if not changes: + continue + + bodies = [self._get_update_body(entity, changes)] + # Parent was created and was not yet added to operations body + parent_queue = collections.deque() + parent_queue.append(entity.parent_id) + while parent_queue: + # Make sure entity's parents are created + parent_id = parent_queue.popleft() + if ( + parent_id is UNKNOWN_VALUE + or parent_id in processed_ids + or parent_id not in created_entity_ids + ): + continue + + parent = self._entities_by_id.get(parent_id) + processed_ids.add(parent.id) + bodies.append(self._get_create_body(parent)) + parent_queue.append(parent.id) + + operations_body.extend(reversed(bodies)) + + for entity_id in created_entity_ids: + if entity_id in processed_ids: + continue + entity = self._entities_by_id[entity_id] + processed_ids.add(entity_id) + operations_body.append(self._get_create_body(entity)) + + for entity_id in reversed(removed_entity_ids): + if entity_id in processed_ids: + continue + + entity = self._entities_by_id.pop(entity_id) + parent_children = self._entities_by_parent_id[entity.parent_id] + if entity in parent_children: + parent_children.remove(entity) + + if not entity.created: + operations_body.append(self._get_delete_body(entity)) + + self._connection.send_batch_operations( + self.project_name, operations_body + ) + if post_project_changes: + self._connection.update_project( + self.project_name, **post_project_changes) + + self.lock()
+ + +
[docs]class AttributeValue(object): + def __init__(self, value): + self._value = value + self._origin_value = copy.deepcopy(value) + +
[docs] def get_value(self): + return self._value
+ +
[docs] def set_value(self, value): + self._value = value
+ + value = property(get_value, set_value) + + @property + def changed(self): + return self._value != self._origin_value + +
[docs] def lock(self): + self._origin_value = copy.deepcopy(self._value)
+ + +
[docs]class Attributes(object): + """Object representing attribs of entity. + + Todos: + This could be enhanced to know attribute schema and validate values + based on the schema. + + Args: + attrib_keys (Iterable[str]): Keys that are available in attribs of the + entity. + values (Optional[Dict[str, Any]]): Values of attributes. + + """ + + def __init__(self, attrib_keys, values=UNKNOWN_VALUE): + if values in (UNKNOWN_VALUE, None): + values = {} + self._attributes = { + key: AttributeValue(values.get(key)) + for key in attrib_keys + } + + def __contains__(self, key): + return key in self._attributes + + def __getitem__(self, key): + return self._attributes[key].value + + def __setitem__(self, key, value): + self._attributes[key].set_value(value) + + def __iter__(self): + for key in self._attributes: + yield key + +
[docs] def keys(self): + return self._attributes.keys()
+ +
[docs] def values(self): + for attribute in self._attributes.values(): + yield attribute.value
+ +
[docs] def items(self): + for key, attribute in self._attributes.items(): + yield key, attribute.value
+ +
[docs] def get(self, key, default=None): + """Get value of attribute. + + Args: + key (str): Attribute name. + default (Any): Default value to return when attribute was not + found. + + """ + attribute = self._attributes.get(key) + if attribute is None: + return default + return attribute.value
+ +
[docs] def set(self, key, value): + """Change value of attribute. + + Args: + key (str): Attribute name. + value (Any): New value of the attribute. + + """ + self[key] = value
+ +
[docs] def get_attribute(self, key): + """Access to attribute object. + + Args: + key (str): Name of attribute. + + Returns: + AttributeValue: Object of attribute value. + + Raises: + KeyError: When attribute is not available. + + """ + return self._attributes[key]
+ +
[docs] def lock(self): + for attribute in self._attributes.values(): + attribute.lock()
+ + @property + def changes(self): + """Attribute value changes. + + Returns: + Dict[str, Any]: Key mapping with new values. + + """ + return { + attr_key: attribute.value + for attr_key, attribute in self._attributes.items() + if attribute.changed + } + +
[docs] def to_dict(self, ignore_none=True): + output = {} + for key, value in self.items(): + if ( + value is UNKNOWN_VALUE + or (ignore_none and value is None) + ): + continue + + output[key] = value + return output
+ + +
[docs]class EntityData(dict): + """Wrapper for 'data' key on entity. + + Data on entity are arbitrary data that are not stored in any deterministic + model. It is possible to store any data that can be parsed to json. + + It is not possible to store 'None' to root key. In that case the key is + not stored, and removed if existed on entity. + To be able to store 'None' value use nested data structure: + + .. highlight:: text + .. code-block:: text + + { + "sceneInfo": { + "description": None, + "camera": "camera1" + } + } + + """ + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + self._orig_data = copy.deepcopy(self) + +
[docs] def get_changes(self): + """Changes in entity data. + + Removed keys have value set to 'None'. + + Returns: + dict[str, Any]: Key mapping with changed values. + + """ + keys = set(self.keys()) | set(self._orig_data.keys()) + output = {} + for key in keys: + if key not in self: + # Key was removed + output[key] = None + elif key not in self._orig_data: + # New value was set + output[key] = self[key] + elif self[key] != self._orig_data[key]: + # Value was changed + output[key] = self[key] + return output
+ +
[docs] def get_new_entity_value(self): + """Value of data for new entity. + + Returns: + dict[str, Any]: Data without None values. + + """ + return { + key: value + for key, value in self.items() + # Ignore 'None' values + if value is not None + }
+ +
[docs] def lock(self): + """Lock changes of entity data.""" + + self._orig_data = copy.deepcopy(self)
+ + +
[docs]class BaseEntity(ABC): + """Object representation of entity from server which is capturing changes. + + All data on created object are expected as "current data" on server entity + unless the entity has set 'created' to 'True'. So if new data should be + stored to server entity then fill entity with server data first and + then change them. + + Calling 'lock' method will mark entity as "saved" and all changes made on + entity are set as "current data" on server. + + Args: + entity_id (Optional[str]): Entity id. New id is created if + not passed. + parent_id (Optional[str]): Parent entity id. + attribs (Optional[Dict[str, Any]]): Attribute values. + data (Optional[Dict[str, Any]]): Entity data (custom data). + thumbnail_id (Optional[str]): Thumbnail id. + active (Optional[bool]): Is entity active. + entity_hub (EntityHub): Object of entity hub which created object of + the entity. + created (Optional[bool]): Entity is new. When 'None' is passed the + value is defined based on value of 'entity_id'. + + """ + _supports_name = False + _supports_label = False + _supports_status = False + _supports_tags = False + _supports_thumbnail = False + + def __init__( + self, + entity_id: Optional[str] = None, + parent_id: Optional["Union[str, _CustomNone]"] = UNKNOWN_VALUE, + attribs: Optional[Dict[str, Any]] = UNKNOWN_VALUE, + data: Optional[Dict[str, Any]] = UNKNOWN_VALUE, + active: Optional[bool] = UNKNOWN_VALUE, + created: Optional[bool] = None, + entity_hub: EntityHub = None, + # Optional arguments + name=None, + label=None, + status: Optional[str] = UNKNOWN_VALUE, + tags: Optional[List[str]] = None, + thumbnail_id: Optional[str] = UNKNOWN_VALUE, + ): + if entity_hub is None: + raise ValueError("Missing required kwarg 'entity_hub'") + + self._entity_hub = entity_hub + + if created is None: + created = entity_id is None + + entity_id = self._prepare_entity_id(entity_id) + + if data is None: + data = EntityData() + + elif data is not UNKNOWN_VALUE: + data = EntityData(data) + + children_ids = UNKNOWN_VALUE + if created: + children_ids = set() + + if not created and parent_id is UNKNOWN_VALUE: + raise ValueError("Existing entity is missing parent id.") + + if tags is None: + tags = [] + else: + tags = list(tags) + + # These are public without any validation at this moment + # may change in future (e.g. name will have regex validation) + self._entity_id = entity_id + + self._parent_id = parent_id + self.active = active + self._created = created + self._attribs = Attributes( + self._get_attributes_for_type(self.entity_type), + attribs + ) + self._data = data + self._children_ids = children_ids + + self._orig_parent_id = parent_id + self._orig_active = active + + # Optional only if supported by entity type + self._name = name + self._label = label + self._status = status + self._tags = copy.deepcopy(tags) + self._thumbnail_id = thumbnail_id + + self._orig_name = name + self._orig_label = label + self._orig_status = status + self._orig_tags = copy.deepcopy(tags) + self._orig_thumbnail_id = thumbnail_id + + self._immutable_for_hierarchy_cache = None + + def __repr__(self): + return "<{} - {}>".format(self.__class__.__name__, self.id) + + def __getitem__(self, item): + return getattr(self, item) + + def __setitem__(self, item, value): + return setattr(self, item, value) + + def _prepare_entity_id(self, entity_id: Any) -> str: + entity_id = convert_entity_id(entity_id) + if entity_id is None: + entity_id = create_entity_id() + return entity_id + + @property + def id(self) -> str: + """Access to entity id under which is entity available on server. + + Returns: + str: Entity id. + + """ + return self._entity_id + + @property + def removed(self) -> bool: + return self._parent_id is None + + @property + def orig_parent_id(self): + return self._orig_parent_id + + @property + def attribs(self): + """Entity attributes based on server configuration. + + Returns: + Attributes: Attributes object handling changes and values of + attributes on entity. + + """ + return self._attribs + + @property + def data(self): + """Entity custom data that are not stored by any deterministic model. + + Be aware that 'data' can't be queried using GraphQl and cannot be + updated partially. + + Returns: + EntityData: Custom data on entity. + + """ + return self._data + + @property + def project_name(self) -> str: + """Quick access to project from entity hub. + + Returns: + str: Name of project under which entity lives. + + """ + return self._entity_hub.project_name + + @property + @abstractmethod + def entity_type(self) -> "EntityType": + """Entity type corresponding to server. + + Returns: + EntityType: Entity type. + + """ + pass + + @property + @abstractmethod + def parent_entity_types(self) -> List[str]: + """Entity type corresponding to server. + + Returns: + List[str]: Possible entity types of parent. + + """ + pass + + @property + @abstractmethod + def changes(self) -> Optional[Dict[str, Any]]: + """Receive entity changes. + + Returns: + Optional[Dict[str, Any]]: All values that have changed on + entity. New entity must return None. + + """ + pass + +
[docs] @classmethod + @abstractmethod + def from_entity_data( + cls, entity_data: Dict[str, Any], entity_hub: EntityHub + ) -> "BaseEntity": + """Create entity based on queried data from server. + + Args: + entity_data (Dict[str, Any]): Entity data from server. + entity_hub (EntityHub): Hub which handle the entity. + + Returns: + BaseEntity: Object of the class. + + """ + pass
+ +
[docs] @abstractmethod + def to_create_body_data(self) -> Dict[str, Any]: + """Convert object of entity to data for server on creation. + + Returns: + Dict[str, Any]: Entity data. + + """ + pass
+ + @property + def immutable_for_hierarchy(self) -> bool: + """Entity is immutable for hierarchy changes. + + Hierarchy changes can be considered as change of name or parents. + + Returns: + bool: Entity is immutable for hierarchy changes. + + """ + if self._immutable_for_hierarchy_cache is not None: + return self._immutable_for_hierarchy_cache + + immutable_for_hierarchy = self._immutable_for_hierarchy + if immutable_for_hierarchy is not None: + self._immutable_for_hierarchy_cache = immutable_for_hierarchy + return self._immutable_for_hierarchy_cache + + for child in self._entity_hub.get_entity_children(self): + if child.immutable_for_hierarchy: + self._immutable_for_hierarchy_cache = True + return self._immutable_for_hierarchy_cache + + self._immutable_for_hierarchy_cache = False + return self._immutable_for_hierarchy_cache + + @property + def _immutable_for_hierarchy(self): + """Override this method to define if entity object is immutable. + + This property was added to define immutable state of Folder entities + which is used in property 'immutable_for_hierarchy'. + + Returns: + Optional[bool]: Bool to explicitly telling if is immutable or + not otherwise None. + + """ + return None + + @property + def has_cached_immutable_hierarchy(self) -> bool: + return self._immutable_for_hierarchy_cache is not None + +
[docs] def reset_immutable_for_hierarchy_cache( + self, bottom_to_top: Optional[bool] = True + ): + """Clear cache of immutable hierarchy property. + + This is used when entity changed parent or a child was added. + + Args: + bottom_to_top (bool): Reset cache from top hierarchy to bottom or + from bottom hierarchy to top. + + """ + self._immutable_for_hierarchy_cache = None + self._entity_hub.reset_immutable_for_hierarchy_cache( + self.id, bottom_to_top + )
+ + def _get_default_changes(self): + """Collect changes of common data on entity. + + Returns: + Dict[str, Any]: Changes on entity. Key and it's new value. + + """ + changes = {} + if ( + self._entity_hub.allow_data_changes + and self._data is not UNKNOWN_VALUE + ): + data_changes = self._data.get_changes() + if data_changes: + changes["data"] = data_changes + + if self._orig_thumbnail_id != self._thumbnail_id: + changes["thumbnailId"] = self._thumbnail_id + + if self._orig_active != self.active: + changes["active"] = self.active + + attrib_changes = self.attribs.changes + if attrib_changes: + changes["attrib"] = attrib_changes + + if self._supports_name and self._orig_name != self._name: + changes["name"] = self._name + + if self._supports_label: + label = self._get_label_value() + if label != self._orig_label: + changes["label"] = label + + if self._supports_status and self._orig_status != self._status: + changes["status"] = self._status + + if self._supports_tags and self._orig_tags != self._tags: + changes["tags"] = self._tags + return changes + + def _get_attributes_for_type(self, entity_type): + return self._entity_hub.get_attributes_for_type(entity_type) + +
[docs] def lock(self): + """Lock entity as 'saved' so all changes are discarded.""" + self._orig_parent_id = self._parent_id + self._orig_name = self._name + self._orig_thumbnail_id = self._thumbnail_id + if isinstance(self._data, EntityData): + self._data.lock() + self._attribs.lock() + + self._immutable_for_hierarchy_cache = None + self._created = False + + if self._supports_label: + self._orig_label = self._get_label_value() + if self._supports_status: + self._orig_status = self._status + if self._supports_tags: + self._orig_tags = copy.deepcopy(self._tags) + if self._supports_thumbnail: + self._orig_thumbnail_id = self._thumbnail_id
+ + def _get_entity_by_id(self, entity_id): + return self._entity_hub.get_entity_by_id(entity_id) + +
[docs] def get_parent_id(self): + """Parent entity id. + + Returns: + Optional[str]: Parent entity id or none if is not set. + + """ + return self._parent_id
+ +
[docs] def set_parent_id(self, parent_id): + """Change parent by id. + + Args: + parent_id (Optional[str]): Id of new parent for entity. + + Raises: + ValueError: If parent was not found by id. + TypeError: If validation of parent does not pass. + + """ + if parent_id != self._parent_id: + orig_parent_id = self._parent_id + self._parent_id = parent_id + self._entity_hub.set_entity_parent( + self.id, parent_id, orig_parent_id + )
+ + parent_id = property(get_parent_id, set_parent_id) + +
[docs] def get_parent(self, allow_fetch=True): + """Parent entity. + + Returns: + Optional[BaseEntity]: Parent object. + + """ + parent = self._entity_hub.get_entity_by_id(self._parent_id) + if parent is not None: + return parent + + if not allow_fetch: + return self._parent_id + + if self._parent_id is UNKNOWN_VALUE: + return self._parent_id + + return self._entity_hub.get_or_fetch_entity_by_id( + self._parent_id, self.parent_entity_types + )
+ +
[docs] def set_parent(self, parent): + """Change parent object. + + Args: + parent (BaseEntity): New parent for entity. + + Raises: + TypeError: If validation of parent does not pass. + + """ + parent_id = None + if parent is not None: + parent_id = parent.id + self._entity_hub.set_entity_parent(self.id, parent_id)
+ + parent = property(get_parent, set_parent) + +
[docs] def get_children_ids(self, allow_fetch=True): + """Access to children objects. + + Todos: + Children should be maybe handled by EntityHub instead of entities + themselves. That would simplify 'set_entity_parent', + 'unset_entity_parent' and other logic related to changing + hierarchy. + + Returns: + Union[List[str], Type[UNKNOWN_VALUE]]: Children iterator. + + """ + if self._children_ids is UNKNOWN_VALUE: + if not allow_fetch: + return self._children_ids + self._entity_hub.get_entity_children(self, True) + return set(self._children_ids)
+ + children_ids = property(get_children_ids) + +
[docs] def get_children(self, allow_fetch=True): + """Access to children objects. + + Returns: + Union[List[BaseEntity], Type[UNKNOWN_VALUE]]: Children iterator. + + """ + if self._children_ids is UNKNOWN_VALUE: + if not allow_fetch: + return self._children_ids + return self._entity_hub.get_entity_children(self, True) + + return [ + self._entity_hub.get_entity_by_id(children_id) + for children_id in self._children_ids + ]
+ + children = property(get_children) + +
[docs] def add_child(self, child): + """Add child entity. + + Args: + child (BaseEntity): Child object to add. + + Raises: + TypeError: When child object has invalid type to be children. + + """ + child_id = child + if isinstance(child_id, BaseEntity): + child_id = child.id + + if self._children_ids is not UNKNOWN_VALUE: + self._children_ids.add(child_id) + + self._entity_hub.set_entity_parent(child_id, self.id)
+ +
[docs] def remove_child(self, child): + """Remove child entity. + + Is ignored if child is not in children. + + Args: + child (Union[str, BaseEntity]): Child object or child id to remove. + + """ + child_id = child + if isinstance(child_id, BaseEntity): + child_id = child.id + + if self._children_ids is not UNKNOWN_VALUE: + self._children_ids.discard(child_id) + self._entity_hub.unset_entity_parent(child_id, self.id)
+ + def get_thumbnail_id(self): + """Thumbnail id of entity. + + Returns: + Optional[str]: Thumbnail id or none if is not set. + + """ + return self._thumbnail_id + + def set_thumbnail_id(self, thumbnail_id): + """Change thumbnail id. + + Args: + thumbnail_id (Union[str, None]): Thumbnail id for entity. + + """ + self._thumbnail_id = thumbnail_id + + thumbnail_id = property(get_thumbnail_id, set_thumbnail_id) + + @property + def created(self): + """Entity is new. + + Returns: + bool: Entity is newly created. + + """ + return self._created + +
[docs] def fill_children_ids(self, children_ids): + """Fill children ids on entity. + + Warning: + This is not an api call but is called from entity hub. + + """ + self._children_ids = set(children_ids)
+ +
[docs] def get_name(self): + if not self._supports_name: + raise NotImplementedError( + f"Name is not supported for '{self.entity_type}'." + ) + return self._name
+ +
[docs] def set_name(self, name): + if not self._supports_name: + raise NotImplementedError( + f"Name is not supported for '{self.entity_type}'." + ) + + if not isinstance(name, str): + raise TypeError("Name must be a string.") + self._name = name
+ + name = property(get_name, set_name) + +
[docs] def get_label(self) -> Optional[str]: + if not self._supports_label: + raise NotImplementedError( + f"Label is not supported for '{self.entity_type}'." + ) + return self._label
+ +
[docs] def set_label(self, label: Optional[str]): + if not self._supports_label: + raise NotImplementedError( + f"Label is not supported for '{self.entity_type}'." + ) + self._label = label
+ + def _get_label_value(self): + """Get label value that will be used for operations. + + Returns: + Optional[str]: Label value. + + """ + label = self._label + if not label or self._name == label: + return None + return label + + label = property(get_label, set_label) + +
[docs] def get_thumbnail_id(self): + """Thumbnail id of entity. + + Returns: + Optional[str]: Thumbnail id or none if is not set. + + """ + if not self._supports_thumbnail: + raise NotImplementedError( + f"Thumbnail is not supported for '{self.entity_type}'." + ) + return self._thumbnail_id
+ +
[docs] def set_thumbnail_id(self, thumbnail_id): + """Change thumbnail id. + + Args: + thumbnail_id (Union[str, None]): Thumbnail id for entity. + + """ + if not self._supports_thumbnail: + raise NotImplementedError( + f"Thumbnail is not supported for '{self.entity_type}'." + ) + self._thumbnail_id = thumbnail_id
+ + thumbnail_id = property(get_thumbnail_id, set_thumbnail_id) + +
[docs] def get_status(self) -> "Union[str, _CustomNone]": + """Folder status. + + Returns: + Union[str, UNKNOWN_VALUE]: Folder status or 'UNKNOWN_VALUE'. + + """ + if not self._supports_status: + raise NotImplementedError( + f"Status is not supported for '{self.entity_type}'." + ) + return self._status
+ +
[docs] def set_status(self, status_name: str): + """Set folder status. + + Args: + status_name (str): Status name. + + """ + if not self._supports_status: + raise NotImplementedError( + f"Status is not supported for '{self.entity_type}'." + ) + project_entity = self._entity_hub.project_entity + status = project_entity.get_status_by_slugified_name(status_name) + if status is None: + raise ValueError( + f"Status {status_name} is not available on project." + ) + + if not status.is_available_for_entity_type(self.entity_type): + raise ValueError( + f"Status {status_name} is not available for folder." + ) + + self._status = status_name
+ + status = property(get_status, set_status) + +
[docs] def get_tags(self): + """Task tags. + + Returns: + list[str]: Task tags. + + """ + if not self._supports_tags: + raise NotImplementedError( + f"Tags are not supported for '{self.entity_type}'." + ) + return self._tags
+ +
[docs] def set_tags(self, tags): + """Change tags. + + Args: + tags (Iterable[str]): Tags. + + """ + if not self._supports_tags: + raise NotImplementedError( + f"Tags are not supported for '{self.entity_type}'." + ) + self._tags = list(tags)
+ + tags = property(get_tags, set_tags)
+ + +
[docs]class ProjectStatus: + """Project status class. + + Args: + name (str): Name of the status. e.g. 'In progress' + short_name (Optional[str]): Short name of the status. e.g. 'IP' + state (Optional[StatusState]): A state of the status. + icon (Optional[str]): Icon of the status. e.g. 'play_arrow'. + color (Optional[str]): Color of the status. e.g. '#eeeeee'. + scope (Optional[Iterable[str]]): Scope of the status. e.g. ['folder']. + index (Optional[int]): Index of the status. + project_statuses (Optional[_ProjectStatuses]): Project statuses + wrapper. + + """ + valid_states = {"not_started", "in_progress", "done", "blocked"} + valid_scope = { + "folder", "task", "product", "version", "representation", "workfile" + } + color_regex = re.compile(r"#([a-f0-9]{6})$") + default_state = "in_progress" + default_color = "#eeeeee" + + def __init__( + self, + name, + short_name=None, + state=None, + icon=None, + color=None, + scope=None, + index=None, + project_statuses=None, + is_new=None, + ): + short_name = short_name or "" + icon = icon or "" + state = state or self.default_state + color = color or self.default_color + if scope is None: + scope = self.valid_scope + scope = set(scope) + self._name = name + self._short_name = short_name + self._icon = icon + self._slugified_name = None + self._state = None + self._color = None + self._scope = scope + self.set_state(state) + self.set_color(color) + + self._original_name = name + self._original_short_name = short_name + self._original_icon = icon + self._original_state = state + self._original_color = color + self._original_scope = set(scope) + self._original_index = index + + self._index = index + self._project_statuses = project_statuses + if is_new is None: + is_new = index is None or project_statuses is None + self._is_new = is_new + + def __str__(self): + short_name = "" + if self.short_name: + short_name = "({})".format(self.short_name) + return "<{} {}{}>".format( + self.__class__.__name__, self.name, short_name + ) + + def __repr__(self): + return str(self) + + def __getitem__(self, key): + if key in { + "name", "short_name", "icon", "state", "color", "slugified_name" + }: + return getattr(self, key) + raise KeyError(key) + + def __setitem__(self, key, value): + if key in {"name", "short_name", "icon", "state", "color"}: + return setattr(self, key, value) + raise KeyError(key) + +
[docs] def lock(self): + """Lock status. + + Changes were commited and current values are now the original values. + + """ + self._is_new = False + self._original_name = self.name + self._original_short_name = self.short_name + self._original_icon = self.icon + self._original_state = self.state + self._original_color = self.color + self._original_scope = self.scope + self._original_index = self.index
+ +
[docs] def is_available_for_entity_type(self, entity_type): + if self._scope is None: + return True + return entity_type in self._scope
+ +
[docs] @staticmethod + def slugify_name(name): + """Slugify status name for name comparison. + + Args: + name (str): Name of the status. + + Returns: + str: Slugified name. + + """ + return slugify_string(name.lower())
+ +
[docs] def get_project_statuses(self): + """Internal logic method. + + Returns: + _ProjectStatuses: Project statuses object. + + """ + return self._project_statuses
+ +
[docs] def set_project_statuses(self, project_statuses): + """Internal logic method to change parent object. + + Args: + project_statuses (_ProjectStatuses): Project statuses object. + + """ + self._project_statuses = project_statuses
+ +
[docs] def unset_project_statuses(self, project_statuses): + """Internal logic method to unset parent object. + + Args: + project_statuses (_ProjectStatuses): Project statuses object. + + """ + if self._project_statuses is project_statuses: + self._project_statuses = None + self._index = None
+ + @property + def changed(self): + """Status has changed. + + Returns: + bool: Status has changed. + + """ + return ( + self._is_new + or self._original_name != self._name + or self._original_short_name != self._short_name + or self._original_index != self._index + or self._original_state != self._state + or self._original_icon != self._icon + or self._original_color != self._color + or self._original_scope != self._scope + ) + +
[docs] def delete(self): + """Remove status from project statuses object.""" + if self._project_statuses is not None: + self._project_statuses.remove(self)
+ +
[docs] def get_index(self): + """Get index of status. + + Returns: + Union[int, None]: Index of status or None if status is not under + project. + + """ + return self._index
+ +
[docs] def set_index(self, index, **kwargs): + """Change status index. + + Returns: + Union[int, None]: Index of status or None if status is not under + project. + + """ + if kwargs.get("from_parent"): + self._index = index + else: + self._project_statuses.set_status_index(self, index)
+ +
[docs] def get_name(self): + """Status name. + + Returns: + str: Status name. + + """ + return self._name
+ +
[docs] def set_name(self, name): + """Change status name. + + Args: + name (str): New status name. + + """ + if not isinstance(name, str): + raise TypeError("Name must be a string.") + if name == self._name: + return + self._name = name + self._slugified_name = None
+ +
[docs] def get_short_name(self): + """Status short name 3 letters tops. + + Returns: + str: Status short name. + + """ + return self._short_name
+ +
[docs] def set_short_name(self, short_name): + """Change status short name. + + Args: + short_name (str): New status short name. 3 letters tops. + + """ + if not isinstance(short_name, str): + raise TypeError("Short name must be a string.") + self._short_name = short_name
+ +
[docs] def get_icon(self): + """Name of icon to use for status. + + Returns: + str: Name of the icon. + + """ + return self._icon
+ +
[docs] def set_icon(self, icon): + """Change status icon name. + + Args: + icon (str): Name of the icon. + + """ + if icon is None: + icon = "" + if not isinstance(icon, str): + raise TypeError("Icon name must be a string.") + self._icon = icon
+ + @property + def slugified_name(self): + """Slugified and lowere status name. + + Can be used for comparison of existing statuses. e.g. 'In Progress' + vs. 'in-progress'. + + Returns: + str: Slugified and lower status name. + + """ + if self._slugified_name is None: + self._slugified_name = self.slugify_name(self.name) + return self._slugified_name + +
[docs] def get_state(self): + """Get state of project status. + + Return: + StatusState: General state of status. + + """ + return self._state
+ +
[docs] def set_state(self, state): + """Set color of project status. + + Args: + state (StatusState): General state of status. + + """ + if state not in self.valid_states: + raise ValueError("Invalid state '{}'".format(str(state))) + self._state = state
+ +
[docs] def get_color(self): + """Get color of project status. + + Returns: + str: Status color. + + """ + return self._color
+ +
[docs] def set_color(self, color): + """Set color of project status. + + Args: + color (str): Color in hex format. Example: '#ff0000'. + + """ + if not isinstance(color, str): + raise TypeError( + "Color must be string got '{}'".format(type(color))) + color = color.lower() + if self.color_regex.fullmatch(color) is None: + raise ValueError("Invalid color value '{}'".format(color)) + self._color = color
+ +
[docs] def get_scope(self): + """Get scope of the status. + + Returns: + Set[str]: Scope of the status. + + """ + return set(self._scope)
+ +
[docs] def set_scope(self, scope): + """Get scope of the status. + + Returns: + scope (Iterable[str]): Scope of the status. + + """ + if not isinstance(scope, (list, set, tuple)): + raise TypeError( + f"Scope must be a list, set, tuple. Got '{type(scope)}'." + ) + + scope = set(scope) + invalid_entity_types = scope - self.valid_scope + if invalid_entity_types: + raise ValueError("Invalid scope values '{}'".format( + ", ".join(invalid_entity_types) + )) + + self._scope = scope
+ + name = property(get_name, set_name) + short_name = property(get_short_name, set_short_name) + project_statuses = property(get_project_statuses, set_project_statuses) + index = property(get_index, set_index) + state = property(get_state, set_state) + color = property(get_color, set_color) + icon = property(get_icon, set_icon) + scope = property(get_scope, set_scope) + + def _validate_other_p_statuses(self, other): + """Validate if other status can be used for move. + + To be able to work with other status, and position them in relation, + they must belong to same existing object of '_ProjectStatuses'. + + Args: + other (ProjectStatus): Other status to validate. + + """ + o_project_statuses = other.project_statuses + m_project_statuses = self.project_statuses + if o_project_statuses is None and m_project_statuses is None: + raise ValueError("Both statuses are not assigned to a project.") + + missing_status = None + if o_project_statuses is None: + missing_status = other + elif m_project_statuses is None: + missing_status = self + if missing_status is not None: + raise ValueError( + "Status '{}' is not assigned to a project.".format( + missing_status.name)) + if m_project_statuses is not o_project_statuses: + raise ValueError( + "Statuse are assigned to different projects." + " Cannot execute move." + ) + +
[docs] def move_before(self, other): + """Move status before other status. + + Args: + other (ProjectStatus): Status to move before. + + """ + self._validate_other_p_statuses(other) + self._project_statuses.set_status_index(self, other.index)
+ +
[docs] def move_after(self, other): + """Move status after other status. + + Args: + other (ProjectStatus): Status to move after. + + """ + self._validate_other_p_statuses(other) + self._project_statuses.set_status_index(self, other.index + 1)
+ +
[docs] def to_data(self): + """Convert status to data. + + Returns: + dict[str, str]: Status data. + + """ + output = { + "name": self.name, + "shortName": self.short_name, + "state": self.state, + "icon": self.icon, + "color": self.color, + "scope": list(self._scope), + } + if ( + not self._is_new + and self._original_name + and self.name != self._original_name + ): + output["original_name"] = self._original_name + return output
+ +
[docs] @classmethod + def from_data(cls, data, index=None, project_statuses=None): + """Create project status from data. + + Args: + data (dict[str, str]): Status data. + index (Optional[int]): Status index. + project_statuses (Optional[ProjectStatuses]): Project statuses + object which wraps the status for a project. + + """ + return cls( + data["name"], + data.get("shortName", data.get("short_name")), + data.get("state"), + data.get("icon"), + data.get("color"), + data.get("scope"), + index=index, + project_statuses=project_statuses + )
+ + +class _ProjectStatuses: + """Wrapper for project statuses. + + Supports basic methods to add, change or remove statuses from a project. + + To add new statuses use 'create' or 'add_status' methods. To change + statuses receive them by one of the getter methods and change their + values. + + Todo: + Validate if statuses are duplicated. + + """ + def __init__(self, statuses): + self._statuses = [ + ProjectStatus.from_data(status, idx, self) + for idx, status in enumerate(statuses) + ] + self._scope_supported = False + self._orig_status_length = len(self._statuses) + self._set_called = False + + def __len__(self): + return len(self._statuses) + + def __iter__(self): + """Iterate over statuses. + + Yields: + ProjectStatus: Project status. + + """ + for status in self._statuses: + yield status + + def create( + self, + name, + short_name=None, + state=None, + icon=None, + color=None, + scope=None, + ): + """Create project status. + + Args: + name (str): Name of the status. e.g. 'In progress' + short_name (Optional[str]): Short name of the status. e.g. 'IP' + state (Optional[StatusState]): A state of the status. + icon (Optional[str]): Icon of the status. e.g. 'play_arrow'. + color (Optional[str]): Color of the status. e.g. '#eeeeee'. + scope (Optional[List[str]]): Scope of the status. e.g. ['folder']. + + Returns: + ProjectStatus: Created project status. + + """ + status = ProjectStatus( + name, short_name, state, icon, color, scope, is_new=True + ) + self.append(status) + return status + + def set_status_scope_supported(self, supported: bool): + self._scope_supported = supported + + def lock(self): + """Lock statuses. + + Changes were commited and current values are now the original values. + + """ + self._orig_status_length = len(self._statuses) + self._set_called = False + for status in self._statuses: + status.lock() + + def to_data(self): + """Convert to project statuses data.""" + output = [ + status.to_data() + for status in self._statuses + ] + # Remove scope if is not supported + if not self._scope_supported: + for item in output: + item.pop("scope") + return output + + def set(self, statuses): + """Explicitly override statuses. + + This method does not handle if statuses changed or not. + + Args: + statuses (list[dict[str, str]]): List of statuses data. + + """ + self._set_called = True + self._statuses = [ + ProjectStatus.from_data(status, idx, self) + for idx, status in enumerate(statuses) + ] + + @property + def changed(self): + """Statuses have changed. + + Returns: + bool: True if statuses changed, False otherwise. + + """ + if self._set_called: + return True + + # Check if status length changed + # - when all statuses are removed it is a changed + if self._orig_status_length != len(self._statuses): + return True + # Go through all statuses and check if any of them changed + for status in self._statuses: + if status.changed: + return True + return False + + def get(self, name, default=None): + """Get status by name. + + Args: + name (str): Status name. + default (Any): Default value of status is not found. + + Returns: + Union[ProjectStatus, Any]: Status or default value. + + """ + return next( + ( + status + for status in self._statuses + if status.name == name + ), + default + ) + + get_status_by_name = get + + def index(self, status, **kwargs): + """Get status index. + + Args: + status (ProjectStatus): Status to get index of. + default (Optional[Any]): Default value if status is not found. + + Returns: + Union[int, Any]: Status index. + + Raises: + ValueError: If status is not found and default value is not + defined. + + """ + output = next( + ( + idx + for idx, st in enumerate(self._statuses) + if st is status + ), + None + ) + if output is not None: + return output + + if "default" in kwargs: + return kwargs["default"] + raise ValueError("Status '{}' not found".format(status.name)) + + def get_status_by_slugified_name(self, name): + """Get status by slugified name. + + Args: + name (str): Status name. Is slugified before search. + + Returns: + Union[ProjectStatus, None]: Status or None if not found. + + """ + slugified_name = ProjectStatus.slugify_name(name) + return next( + ( + status + for status in self._statuses + if status.slugified_name == slugified_name + ), + None + ) + + def remove_by_name(self, name, ignore_missing=False): + """Remove status by name. + + Args: + name (str): Status name. + ignore_missing (Optional[bool]): If True, no error is raised if + status is not found. + + Returns: + ProjectStatus: Removed status. + + """ + matching_status = self.get(name) + if matching_status is None: + if ignore_missing: + return + raise ValueError( + "Status '{}' not found in project".format(name)) + return self.remove(matching_status) + + def remove(self, status, ignore_missing=False): + """Remove status. + + Args: + status (ProjectStatus): Status to remove. + ignore_missing (Optional[bool]): If True, no error is raised if + status is not found. + + Returns: + Union[ProjectStatus, None]: Removed status. + + """ + index = self.index(status, default=None) + if index is None: + if ignore_missing: + return None + raise ValueError("Status '{}' not in project".format(status)) + + return self.pop(index) + + def pop(self, index): + """Remove status by index. + + Args: + index (int): Status index. + + Returns: + ProjectStatus: Removed status. + + """ + status = self._statuses.pop(index) + status.unset_project_statuses(self) + for st in self._statuses[index:]: + st.set_index(st.index - 1, from_parent=True) + return status + + def insert(self, index, status): + """Insert status at index. + + Args: + index (int): Status index. + status (Union[ProjectStatus, dict[str, str]]): Status to insert. + Can be either status object or status data. + + Returns: + ProjectStatus: Inserted status. + + """ + if not isinstance(status, ProjectStatus): + status = ProjectStatus.from_data(status) + + start_index = index + end_index = len(self._statuses) + 1 + matching_index = self.index(status, default=None) + if matching_index is not None: + if matching_index == index: + status.set_index(index, from_parent=True) + return + + self._statuses.pop(matching_index) + if matching_index < index: + start_index = matching_index + end_index = index + 1 + else: + end_index -= 1 + + status.set_project_statuses(self) + self._statuses.insert(index, status) + for idx, st in enumerate(self._statuses[start_index:end_index]): + st.set_index(start_index + idx, from_parent=True) + return status + + def append(self, status): + """Add new status to the end of the list. + + Args: + status (Union[ProjectStatus, dict[str, str]]): Status to insert. + Can be either status object or status data. + + Returns: + ProjectStatus: Inserted status. + + """ + return self.insert(len(self._statuses), status) + + def set_status_index(self, status, index): + """Set status index. + + Args: + status (ProjectStatus): Status to set index. + index (int): New status index. + + """ + return self.insert(index, status) + + +
[docs]class ProjectEntity(BaseEntity): + """Entity representing project on AYON server. + + Args: + name (str): Name of entity. + project_code (str): Project code. + library (bool): Is project library project. + folder_types (list[dict[str, Any]]): Folder types definition. + task_types (list[dict[str, Any]]): Task types definition. + statuses: (list[dict[str, Any]]): Statuses definition. + attribs (Optional[Dict[str, Any]]): Attribute values. + data (Dict[str, Any]): Entity data (custom data). + active (bool): Is entity active. + entity_hub (EntityHub): Object of entity hub which created object of + the entity. + + """ + _supports_name = True + entity_type = "project" + parent_entity_types = [] + # TODO These are hardcoded but maybe should be used from server??? + default_folder_type_icon = "folder" + default_task_type_icon = "task_alt" + + def __init__( + self, + name: str, + project_code: str, + library: bool, + folder_types: List[Dict[str, Any]], + task_types: List[Dict[str, Any]], + statuses: List[Dict[str, Any]], + attribs: Optional[Dict[str, Any]] = UNKNOWN_VALUE, + data: Optional[Dict[str, Any]] = UNKNOWN_VALUE, + active: Optional[bool] = UNKNOWN_VALUE, + entity_hub: EntityHub = None, + ): + super().__init__( + entity_id=name, + parent_id=PROJECT_PARENT_ID, + attribs=attribs, + data=data, + active=active, + created=False, + entity_hub=entity_hub, + name=name, + ) + + self._project_code = project_code + self._library_project = library + self._folder_types = folder_types + self._task_types = task_types + self._statuses_obj = _ProjectStatuses(statuses) + + self._orig_project_code = project_code + self._orig_library_project = library + self._orig_folder_types = copy.deepcopy(folder_types) + self._orig_task_types = copy.deepcopy(task_types) + self._orig_statuses = copy.deepcopy(statuses) + + def _prepare_entity_id(self, entity_id): + if entity_id != self.project_name: + raise ValueError( + "Unexpected entity id value \"{}\". Expected \"{}\"".format( + entity_id, self.project_name)) + return entity_id + +
[docs] def set_name(self, name): + if self._name == name: + return + raise ValueError("It is not allowed to change project name.")
+ +
[docs] def get_parent(self, *args, **kwargs): + return None
+ +
[docs] def set_parent(self, parent): + raise ValueError( + "Parent of project cannot be set to {}".format(parent) + )
+ +
[docs] def set_status_scope_supported(self, supported: bool): + self._statuses_obj.set_status_scope_supported(supported)
+ + parent = property(get_parent, set_parent) + +
[docs] def get_orig_folder_types(self): + return copy.deepcopy(self._orig_folder_types)
+ +
[docs] def get_folder_types(self): + return copy.deepcopy(self._folder_types)
+ +
[docs] def set_folder_types(self, folder_types): + new_folder_types = [] + for folder_type in folder_types: + if "icon" not in folder_type: + folder_type["icon"] = self.default_folder_type_icon + new_folder_types.append(folder_type) + self._folder_types = new_folder_types
+ +
[docs] def get_orig_task_types(self): + return copy.deepcopy(self._orig_task_types)
+ +
[docs] def get_task_types(self): + return copy.deepcopy(self._task_types)
+ +
[docs] def set_task_types(self, task_types): + new_task_types = [] + for task_type in task_types: + if "icon" not in task_type: + task_type["icon"] = self.default_task_type_icon + new_task_types.append(task_type) + self._task_types = new_task_types
+ +
[docs] def get_orig_statuses(self): + return copy.deepcopy(self._orig_statuses)
+ +
[docs] def get_statuses(self): + return self._statuses_obj
+ +
[docs] def set_statuses(self, statuses): + self._statuses_obj.set(statuses)
+ + folder_types = property(get_folder_types, set_folder_types) + task_types = property(get_task_types, set_task_types) + statuses = property(get_statuses, set_statuses) + +
[docs] def get_status_by_slugified_name(self, name): + """Find status by name. + + Args: + name (str): Status name. + + + Returns: + Union[ProjectStatus, None]: Status object or None. + + """ + return self._statuses_obj.get_status_by_slugified_name(name)
+ +
[docs] def lock(self): + super().lock() + self._orig_folder_types = copy.deepcopy(self._folder_types) + self._orig_task_types = copy.deepcopy(self._task_types) + self._statuses_obj.lock()
+ + @property + def changes(self): + changes = self._get_default_changes() + if self._orig_folder_types != self._folder_types: + changes["folderTypes"] = self.get_folder_types() + + if self._orig_task_types != self._task_types: + changes["taskTypes"] = self.get_task_types() + + if self._statuses_obj.changed: + changes["statuses"] = self._statuses_obj.to_data() + + return changes + +
[docs] @classmethod + def from_entity_data(cls, project, entity_hub) -> "ProjectEntity": + return cls( + project["name"], + project["code"], + library=project["library"], + folder_types=project["folderTypes"], + task_types=project["taskTypes"], + statuses=project["statuses"], + attribs=project["ownAttrib"], + data=project["data"], + active=project["active"], + entity_hub=entity_hub, + )
+ +
[docs] def to_create_body_data(self): + raise NotImplementedError( + "ProjectEntity does not support conversion to entity data" + )
+ + +
[docs]class FolderEntity(BaseEntity): + """Entity representing a folder on AYON server. + + Args: + name (str): Name of entity. + folder_type (str): Type of folder. Folder type must be available in + config of project folder types. + parent_id (Union[str, None]): Id of parent entity. + label (Optional[str]): Folder label. + path (Optional[str]): Folder path. Path consist of all parent names + with slash('/') used as separator. + status (Optional[str]): Folder status. + tags (Optional[List[str]]): Folder tags. + attribs (Dict[str, Any]): Attribute values. + data (Dict[str, Any]): Entity data (custom data). + thumbnail_id (Union[str, None]): Id of entity's thumbnail. + active (bool): Is entity active. + entity_id (Union[str, None]): Id of the entity. New id is created if + not passed. + created (Optional[bool]): Entity is new. When 'None' is passed the + value is defined based on value of 'entity_id'. + entity_hub (EntityHub): Object of entity hub which created object of + the entity. + + """ + _supports_name = True + _supports_label = True + _supports_tags = True + _supports_status = True + _supports_thumbnail = True + + entity_type = "folder" + parent_entity_types = ["folder", "project"] + + def __init__( + self, + name: str, + folder_type: str, + parent_id: Optional[str] = UNKNOWN_VALUE, + label: Optional[str] = None, + path: Optional[str] = None, + status: Optional[str] = UNKNOWN_VALUE, + tags: Optional[List[str]] = None, + attribs: Optional[Dict[str, Any]] = UNKNOWN_VALUE, + data: Optional[Dict[str, Any]] = UNKNOWN_VALUE, + thumbnail_id: Optional[str] = UNKNOWN_VALUE, + active: bool = UNKNOWN_VALUE, + entity_id: Optional[str] = None, + created: Optional[bool] = None, + entity_hub: EntityHub = None, + ): + super().__init__( + entity_id=entity_id, + parent_id=parent_id, + attribs=attribs, + data=data, + active=active, + created=created, + entity_hub=entity_hub, + name=name, + label=label, + tags=tags, + status=status, + thumbnail_id=thumbnail_id, + ) + # Autofill project as parent of folder if is not yet set + # - this can be guessed only if folder was just created + if self.created and self._parent_id is UNKNOWN_VALUE: + self._parent_id = self.project_name + + self._folder_type = folder_type + + self._orig_folder_type = folder_type + # Know if folder has any products + # - is used to know if folder allows hierarchy changes + self._has_published_content = False + self._path = path + +
[docs] def get_folder_type(self) -> str: + return self._folder_type
+ +
[docs] def set_folder_type(self, folder_type: str): + self._folder_type = folder_type
+ + folder_type = property(get_folder_type, set_folder_type) + +
[docs] def get_path(self, dynamic_value=True): + if not dynamic_value: + return self._path + + if self._path is None: + parent = self.parent + if parent.entity_type == "folder": + parent_path = parent.path + path = "/".join([parent_path, self.name]) + elif self._entity_hub.path_start_with_slash: + path = "/{}".format(self.name) + else: + path = self.name + self._path = path + return self._path
+ +
[docs] def reset_path(self): + self._path = None + self._entity_hub.folder_path_reseted(self.id)
+ + path = property(get_path) + +
[docs] def get_has_published_content(self): + return self._has_published_content
+ +
[docs] def set_has_published_content(self, has_published_content): + if self._has_published_content is has_published_content: + return + + self._has_published_content = has_published_content + # Reset immutable cache of parents + self._entity_hub.reset_immutable_for_hierarchy_cache(self.id)
+ + has_published_content = property( + get_has_published_content, set_has_published_content + ) + + @property + def _immutable_for_hierarchy(self): + if self.has_published_content: + return True + return None + +
[docs] def lock(self): + super().lock() + self._orig_folder_type = self._folder_type
+ + @property + def changes(self): + changes = self._get_default_changes() + if self._orig_parent_id != self._parent_id: + parent_id = self._parent_id + if parent_id == self.project_name: + parent_id = None + changes["parentId"] = parent_id + + if self._orig_folder_type != self._folder_type: + changes["folderType"] = self._folder_type + + return changes + +
[docs] @classmethod + def from_entity_data(cls, folder, entity_hub) -> "FolderEntity": + parent_id = folder["parentId"] + if parent_id is None: + parent_id = entity_hub.project_entity.id + return cls( + name=folder["name"], + folder_type=folder["folderType"], + parent_id=parent_id, + label=folder["label"], + path=folder["path"], + status=folder["status"], + tags=folder["tags"], + attribs=folder["ownAttrib"], + data=folder.get("data"), + thumbnail_id=folder["thumbnailId"], + active=folder["active"], + entity_id=folder["id"], + created=False, + entity_hub=entity_hub + )
+ +
[docs] def to_create_body_data(self): + parent_id = self._parent_id + if parent_id is UNKNOWN_VALUE: + raise ValueError("Folder does not have set 'parent_id'") + + if parent_id == self.project_name: + parent_id = None + + if not self.name or self.name is UNKNOWN_VALUE: + raise ValueError("Folder does not have set 'name'") + + output = { + "name": self.name, + "folderType": self.folder_type, + "parentId": parent_id, + } + label = self._get_label_value() + if label: + output["label"] = label + + attrib = self.attribs.to_dict() + if attrib: + output["attrib"] = attrib + + # Add tags only if are available + if self.tags: + output["tags"] = list(self.tags) + + if self.status is not UNKNOWN_VALUE: + output["status"] = self.status + + if self.active is not UNKNOWN_VALUE: + output["active"] = self.active + + if self.thumbnail_id is not UNKNOWN_VALUE: + output["thumbnailId"] = self.thumbnail_id + + if ( + self._entity_hub.allow_data_changes + and self._data is not UNKNOWN_VALUE + ): + output["data"] = self._data.get_new_entity_value() + return output
+ + +
[docs]class TaskEntity(BaseEntity): + """Entity representing a task on AYON server. + + Args: + name (str): Name of entity. + task_type (str): Type of task. Task type must be available in config + of project task types. + folder_id (Union[str, None]): Parent folder id. + label (Optional[str]): Task label. + status (Optional[str]): Task status. + tags (Optional[Iterable[str]]): Folder tags. + attribs (Dict[str, Any]): Attribute values. + data (Dict[str, Any]): Entity data (custom data). + assignees (Optional[Iterable[str]]): User assignees to the task. + thumbnail_id (Union[str, None]): Id of entity's thumbnail. + active (bool): Is entity active. + entity_id (Union[str, None]): Id of the entity. New id is created if + not passed. + created (Optional[bool]): Entity is new. When 'None' is passed the + value is defined based on value of 'entity_id'. + entity_hub (EntityHub): Object of entity hub which created object of + the entity. + + """ + _supports_name = True + _supports_label = True + _supports_tags = True + _supports_status = True + _supports_thumbnail = True + entity_type = "task" + parent_entity_types = ["folder"] + + def __init__( + self, + name: str, + task_type: str, + folder_id: Optional[str] = UNKNOWN_VALUE, + label: Optional[str] = None, + status: Optional[str] = UNKNOWN_VALUE, + tags: Optional[Iterable[str]] = None, + attribs: Optional[Dict[str, Any]] = UNKNOWN_VALUE, + data: Optional[Dict[str, Any]] = UNKNOWN_VALUE, + assignees: Optional[Iterable[str]] = None, + thumbnail_id: Optional[str] = UNKNOWN_VALUE, + active: Optional[bool] = UNKNOWN_VALUE, + entity_id: Optional[str] = None, + created: Optional[bool] = None, + entity_hub: EntityHub = None, + ): + super().__init__( + name=name, + parent_id=folder_id, + label=label, + status=status, + tags=tags, + attribs=attribs, + data=data, + thumbnail_id=thumbnail_id, + active=active, + entity_id=entity_id, + created=created, + entity_hub=entity_hub, + ) + if assignees is None: + assignees = [] + else: + assignees = list(assignees) + + self._task_type = task_type + self._assignees = assignees + + self._orig_task_type = task_type + self._orig_assignees = copy.deepcopy(assignees) + + self._children_ids = set() + +
[docs] def lock(self): + super().lock() + self._orig_task_type = self._task_type + self._orig_assignees = copy.deepcopy(self._assignees)
+ +
[docs] def get_folder_id(self): + return self._parent_id
+ +
[docs] def set_folder_id(self, folder_id): + self.set_parent_id(folder_id)
+ + folder_id = property(get_folder_id, set_folder_id) + +
[docs] def get_task_type(self) -> str: + return self._task_type
+ +
[docs] def set_task_type(self, task_type: str): + self._task_type = task_type
+ + task_type = property(get_task_type, set_task_type) + +
[docs] def get_assignees(self): + """Task assignees. + + Returns: + list[str]: Task assignees. + + """ + return self._assignees
+ +
[docs] def set_assignees(self, assignees): + """Change assignees. + + Args: + assignees (Iterable[str]): assignees. + + """ + self._assignees = list(assignees)
+ + assignees = property(get_assignees, set_assignees) + +
[docs] def add_child(self, child): + raise ValueError("Task does not support to add children")
+ + @property + def changes(self): + changes = self._get_default_changes() + + if self._orig_parent_id != self._parent_id: + changes["folderId"] = self._parent_id + + if self._orig_task_type != self._task_type: + changes["taskType"] = self._task_type + + if self._orig_assignees != self._assignees: + changes["assignees"] = self._assignees + + return changes + +
[docs] @classmethod + def from_entity_data(cls, task, entity_hub) -> "TaskEntity": + return cls( + name=task["name"], + task_type=task["taskType"], + folder_id=task["folderId"], + label=task["label"], + status=task["status"], + tags=task["tags"], + attribs=task["ownAttrib"], + data=task.get("data"), + assignees=task["assignees"], + thumbnail_id=task["thumbnailId"], + active=task["active"], + entity_id=task["id"], + created=False, + entity_hub=entity_hub + )
+ +
[docs] def to_create_body_data(self): + if self.parent_id is UNKNOWN_VALUE: + raise ValueError("Task does not have set 'parent_id'") + + output = { + "name": self.name, + "taskType": self.task_type, + "folderId": self.parent_id, + } + label = self._get_label_value() + if label: + output["label"] = label + + attrib = self.attribs.to_dict() + if attrib: + output["attrib"] = attrib + + if self.active is not UNKNOWN_VALUE: + output["active"] = self.active + + if self.status is not UNKNOWN_VALUE: + output["status"] = self.status + + if self.tags: + output["tags"] = self.tags + + if self.assignees: + output["assignees"] = self.assignees + + if ( + self._entity_hub.allow_data_changes + and self._data is not UNKNOWN_VALUE + ): + output["data"] = self._data.get_new_entity_value() + return output
+ + +
[docs]class ProductEntity(BaseEntity): + _supports_name = True + _supports_tags = True + + entity_type = "product" + parent_entity_types = ["folder"] + + def __init__( + self, + name: str, + product_type: str, + folder_id: Optional["Union[str, _CustomNone]"] = UNKNOWN_VALUE, + tags: Optional[Iterable[str]] = None, + attribs: Optional[Dict[str, Any]] = UNKNOWN_VALUE, + data: Optional[Dict[str, Any]] = UNKNOWN_VALUE, + active: Optional[bool] = UNKNOWN_VALUE, + entity_id: Optional[str] = None, + created: Optional[bool] = None, + entity_hub: EntityHub = None, + ): + super().__init__( + name=name, + parent_id=folder_id, + tags=tags, + attribs=attribs, + data=data, + created=created, + entity_id=entity_id, + active=active, + entity_hub=entity_hub, + ) + self._product_type = product_type + + self._orig_product_type = product_type + +
[docs] def get_folder_id(self): + return self._parent_id
+ +
[docs] def set_folder_id(self, folder_id): + self.set_parent_id(folder_id)
+ + folder_id = property(get_folder_id, set_folder_id) + +
[docs] def get_product_type(self): + return self._product_type
+ +
[docs] def set_product_type(self, product_type): + self._product_type = product_type
+ + product_type = property(get_product_type, set_product_type) + +
[docs] def lock(self): + super().lock() + self._orig_product_type = self._product_type
+ + @property + def changes(self): + changes = self._get_default_changes() + + if self._orig_parent_id != self._parent_id: + changes["folderId"] = self._parent_id + + if self._orig_product_type != self._product_type: + changes["productType"] = self._product_type + + return changes + +
[docs] @classmethod + def from_entity_data(cls, product, entity_hub): + return cls( + name=product["name"], + product_type=product["productType"], + folder_id=product["folderId"], + tags=product["tags"], + attribs=product["attrib"], + data=product.get("data"), + active=product["active"], + entity_id=product["id"], + created=False, + entity_hub=entity_hub + )
+ +
[docs] def to_create_body_data(self): + if self.parent_id is UNKNOWN_VALUE: + raise ValueError("Product does not have set 'folder_id'") + + output = { + "name": self.name, + "productType": self.product_type, + "folderId": self.parent_id, + } + + attrib = self.attribs.to_dict() + if attrib: + output["attrib"] = attrib + + if self.active is not UNKNOWN_VALUE: + output["active"] = self.active + + if self.tags: + output["tags"] = self.tags + + if ( + self._entity_hub.allow_data_changes + and self._data is not UNKNOWN_VALUE + ): + output["data"] = self._data.get_new_entity_value() + return output
+ + +
[docs]class VersionEntity(BaseEntity): + _supports_tags = True + _supports_status = True + _supports_thumbnail = True + + entity_type = "version" + parent_entity_types = ["product"] + + def __init__( + self, + version: int, + product_id: Optional["Union[str, _CustomNone]"] = UNKNOWN_VALUE, + task_id: Optional["Union[str, _CustomNone]"] = UNKNOWN_VALUE, + status: Optional[str] = UNKNOWN_VALUE, + tags: Optional[Iterable[str]] = None, + attribs: Optional[Dict[str, Any]] = UNKNOWN_VALUE, + data: Optional[Dict[str, Any]] = UNKNOWN_VALUE, + thumbnail_id: Optional[str] = UNKNOWN_VALUE, + active: Optional[bool] = UNKNOWN_VALUE, + entity_id: Optional[str] = None, + created: Optional[bool] = None, + entity_hub: EntityHub = None, + ): + super().__init__( + parent_id=product_id, + status=status, + tags=tags, + attribs=attribs, + data=data, + thumbnail_id=thumbnail_id, + active=active, + entity_id=entity_id, + created=created, + entity_hub=entity_hub, + ) + self._version = version + self._task_id = task_id + + self._orig_version = version + self._orig_task_id = task_id + +
[docs] def get_version(self): + return self._version
+ +
[docs] def set_version(self, version): + self._version = version
+ + version = property(get_version, set_version) + +
[docs] def get_product_id(self): + return self._parent_id
+ +
[docs] def set_product_id(self, product_id): + self.set_parent_id(product_id)
+ + product_id = property(get_product_id, set_product_id) + +
[docs] def get_task_id(self): + return self._task_id
+ +
[docs] def set_task_id(self, task_id): + self._task_id = task_id
+ + task_id = property(get_task_id, set_task_id) + +
[docs] def lock(self): + super().lock() + self._orig_version = self._version + self._orig_task_id = self._task_id
+ + @property + def changes(self): + changes = self._get_default_changes() + + if self._orig_parent_id != self._parent_id: + changes["productId"] = self._parent_id + + if self._orig_task_id != self._task_id: + changes["taskId"] = self._task_id + + return changes + +
[docs] @classmethod + def from_entity_data(cls, version, entity_hub): + return cls( + version=version["version"], + product_id=version["productId"], + task_id=version["taskId"], + status=version["status"], + tags=version["tags"], + attribs=version["attrib"], + data=version.get("data"), + thumbnail_id=version["thumbnailId"], + active=version["active"], + entity_id=version["id"], + created=False, + entity_hub=entity_hub + )
+ +
[docs] def to_create_body_data(self): + if self.parent_id is UNKNOWN_VALUE: + raise ValueError("Version does not have set 'product_id'") + + output = { + "version": self.version, + "productId": self.parent_id, + } + task_id = self.task_id + if task_id: + output["taskId"] = task_id + + attrib = self.attribs.to_dict() + if attrib: + output["attrib"] = attrib + + if self.active is not UNKNOWN_VALUE: + output["active"] = self.active + + if self.tags: + output["tags"] = self.tags + + if self.status: + output["status"] = self.status + + if ( + self._entity_hub.allow_data_changes + and self._data is not UNKNOWN_VALUE + ): + output["data"] = self._data.get_new_entity_value() + return output
+
+ +
+ +
+
+ + +
+

+ + © Copyright 2024, ynput.io +
+ + + + Built with Sphinx using a + + theme + + provided by Revitron. + +

+
+ +
+ +
+
+ +
+ +
+ + + + + + + + + + + + \ No newline at end of file diff --git a/_modules/ayon_api/events.html b/_modules/ayon_api/events.html new file mode 100644 index 0000000000..010d3a4986 --- /dev/null +++ b/_modules/ayon_api/events.html @@ -0,0 +1,239 @@ + + + + + + + + + + + ayon_api.events — ayon-python-api 1.0.11 documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + +
+ + + + + +
+ +
+ +
+ + + +
+
+
+ +

Source code for ayon_api.events

+import copy
+
+
+
[docs]class ServerEvent(object): + def __init__( + self, + topic, + sender=None, + event_hash=None, + project_name=None, + username=None, + dependencies=None, + description=None, + summary=None, + payload=None, + finished=True, + store=True, + ): + if dependencies is None: + dependencies = [] + if payload is None: + payload = {} + if summary is None: + summary = {} + + self.topic = topic + self.sender = sender + self.event_hash = event_hash + self.project_name = project_name + self.username = username + self.dependencies = dependencies + self.description = description + self.summary = summary + self.payload = payload + self.finished = finished + self.store = store + +
[docs] def to_data(self): + return { + "topic": self.topic, + "sender": self.sender, + "hash": self.event_hash, + "project": self.project_name, + "user": self.username, + "dependencies": copy.deepcopy(self.dependencies), + "description": self.description, + "summary": copy.deepcopy(self.summary), + "payload": self.payload, + "finished": self.finished, + "store": self.store + }
+
+ +
+ +
+
+ + +
+

+ + © Copyright 2024, ynput.io +
+ + + + Built with Sphinx using a + + theme + + provided by Revitron. + +

+
+ +
+ +
+
+ +
+ +
+ + + + + + + + + + + + \ No newline at end of file diff --git a/_modules/ayon_api/exceptions.html b/_modules/ayon_api/exceptions.html new file mode 100644 index 0000000000..928abd9b08 --- /dev/null +++ b/_modules/ayon_api/exceptions.html @@ -0,0 +1,308 @@ + + + + + + + + + + + ayon_api.exceptions — ayon-python-api 1.0.11 documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + +
+ + + + + +
+ +
+ +
+ + + +
+
+
+ +

Source code for ayon_api.exceptions

+import copy
+
+
+
[docs]class UrlError(Exception): + """Url cannot be parsed as url. + + Exception may contain hints of possible fixes of url that can be used in + UI if needed. + """ + + def __init__(self, message, title, hints=None): + if hints is None: + hints = [] + + self.title = title + self.hints = hints + super(UrlError, self).__init__(message)
+ + +
[docs]class ServerError(Exception): + pass
+ + +
[docs]class UnauthorizedError(ServerError): + pass
+ + +
[docs]class AuthenticationError(ServerError): + pass
+ + +
[docs]class ServerNotReached(ServerError): + pass
+ + +
[docs]class UnsupportedServerVersion(ServerError): + """Server version does not support the requested operation. + + This is used for known incompatibilities between the python api and + server. E.g. can be used when endpoint is not available anymore, or + is not yet available on server. + """ + pass
+ + +
[docs]class RequestError(Exception): + def __init__(self, message, response): + self.response = response + super(RequestError, self).__init__(message)
+ + +
[docs]class HTTPRequestError(RequestError): + pass
+ + +
[docs]class GraphQlQueryFailed(Exception): + def __init__(self, errors, query, variables): + if variables is None: + variables = {} + + error_messages = [] + for error in errors: + msg = error["message"] + path = error.get("path") + if path: + msg += " on item '{}'".format("/".join( + # Convert to string + str(x) for x in path + )) + locations = error.get("locations") + if locations: + _locations = [ + "Line {} Column {}".format( + location["line"], location["column"] + ) + for location in locations + ] + + msg += " ({})".format(" and ".join(_locations)) + error_messages.append(msg) + + message = "GraphQl query Failed" + if error_messages: + message = "{}: {}".format(message, " | ".join(error_messages)) + + self.errors = errors + self.query = query + self.variables = copy.deepcopy(variables) + super(GraphQlQueryFailed, self).__init__(message)
+ + +
[docs]class MissingEntityError(Exception): + pass
+ + +
[docs]class ProjectNotFound(MissingEntityError): + def __init__(self, project_name, message=None): + if not message: + message = "Project \"{}\" was not found".format(project_name) + self.project_name = project_name + super(ProjectNotFound, self).__init__(message)
+ + +
[docs]class FolderNotFound(MissingEntityError): + def __init__(self, project_name, folder_id, message=None): + self.project_name = project_name + self.folder_id = folder_id + if not message: + message = ( + "Folder with id \"{}\" was not found in project \"{}\"" + ).format(folder_id, project_name) + super(FolderNotFound, self).__init__(message)
+ + +
[docs]class FailedOperations(Exception): + pass
+ + +
[docs]class FailedServiceInit(Exception): + pass
+
+ +
+ +
+
+ + +
+

+ + © Copyright 2024, ynput.io +
+ + + + Built with Sphinx using a + + theme + + provided by Revitron. + +

+
+ +
+ +
+
+ +
+ +
+ + + + + + + + + + + + \ No newline at end of file diff --git a/_modules/ayon_api/graphql.html b/_modules/ayon_api/graphql.html new file mode 100644 index 0000000000..301e27fce8 --- /dev/null +++ b/_modules/ayon_api/graphql.html @@ -0,0 +1,1250 @@ + + + + + + + + + + + ayon_api.graphql — ayon-python-api 1.0.11 documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + +
+ + + + + +
+ +
+ +
+ + + +
+
+
+ +

Source code for ayon_api.graphql

+import copy
+import numbers
+from abc import ABC, abstractmethod
+from typing import Optional, Iterable
+
+from .exceptions import GraphQlQueryFailed
+from .utils import SortOrder
+
+FIELD_VALUE = object()
+
+
+
[docs]def fields_to_dict(fields): + output = {} + if not fields: + return output + + for field in fields: + hierarchy = field.split(".") + last = hierarchy.pop(-1) + value = output + for part in hierarchy: + if value is FIELD_VALUE: + break + + if part not in value: + value[part] = {} + value = value[part] + + if value is not FIELD_VALUE: + value[last] = FIELD_VALUE + return output
+ + +
[docs]class QueryVariable(object): + """Object representing single varible used in GraphQlQuery. + + Variable definition is in GraphQl query header but it's value is used + in fields. + + Args: + variable_name (str): Name of variable in query. + + """ + + def __init__(self, variable_name): + self._variable_name = variable_name + self._name = "${}".format(variable_name) + + @property + def name(self): + """Name used in field filter.""" + return self._name + + @property + def variable_name(self): + """Name of variable in query definition.""" + return self._variable_name + + def __hash__(self): + return self._name.__hash__() + + def __str__(self): + return self._name + + def __format__(self, *args, **kwargs): + return self._name.__format__(*args, **kwargs)
+ + +
[docs]class GraphQlQuery: + """GraphQl query which can have fields to query. + + Single use object which can be used only for one query. Object and children + objects keep track about paging and progress. + + Args: + name (str): Name of query. + + """ + offset = 2 + + def __init__(self, name, order=None): + self._name = name + self._variables = {} + self._children = [] + self._has_multiple_edge_fields = None + self._order = SortOrder.parse_value(order, SortOrder.ascending) + + @property + def indent(self): + """Indentation for preparation of query string. + + Returns: + int: Ident spaces. + + """ + return 0 + + @property + def child_indent(self): + """Indentation for preparation of query string used by children. + + Returns: + int: Ident spaces for children. + + """ + return self.indent + + @property + def need_query(self): + """Still need query from server. + + Needed for edges which use pagination. + + Returns: + bool: If still need query from server. + + """ + for child in self._children: + if child.need_query: + return True + return False + + @property + def has_multiple_edge_fields(self): + if self._has_multiple_edge_fields is None: + edge_counter = 0 + for child in self._children: + edge_counter += child.sum_edge_fields(2) + if edge_counter > 1: + break + self._has_multiple_edge_fields = edge_counter > 1 + + return self._has_multiple_edge_fields + +
[docs] def add_variable(self, key, value_type, value=None): + """Add variable to query. + + Args: + key (str): Variable name. + value_type (str): Type of expected value in variables. This is + graphql type e.g. "[String!]", "Int", "Boolean", etc. + value (Any): Default value for variable. Can be changed later. + + Returns: + QueryVariable: Created variable object. + + Raises: + KeyError: If variable was already added before. + + """ + if key in self._variables: + raise KeyError( + "Variable \"{}\" was already set with type {}.".format( + key, value_type + ) + ) + + variable = QueryVariable(key) + self._variables[key] = { + "type": value_type, + "variable": variable, + "value": value + } + return variable
+ +
[docs] def get_variable(self, key): + """Variable object. + + Args: + key (str): Variable name added to headers. + + Returns: + QueryVariable: Variable object used in query string. + + """ + return self._variables[key]["variable"]
+ +
[docs] def get_variable_value(self, key, default=None): + """Get Current value of variable. + + Args: + key (str): Variable name. + default (Any): Default value if variable is available. + + Returns: + Any: Variable value. + + """ + variable_item = self._variables.get(key) + if variable_item: + return variable_item["value"] + return default
+ +
[docs] def set_variable_value(self, key, value): + """Set value for variable. + + Args: + key (str): Variable name under which the value is stored. + value (Any): Variable value used in query. Variable is not used + if value is 'None'. + """ + self._variables[key]["value"] = value
+ +
[docs] def get_variable_keys(self): + """Get all variable keys. + + Returns: + set[str]: Variable keys. + + """ + return set(self._variables.keys())
+ +
[docs] def get_variables_values(self): + """Calculate variable values used that should be used in query. + + Variables with value set to 'None' are skipped. + + Returns: + Dict[str, Any]: Variable values by their name. + + """ + output = {} + for key, item in self._variables.items(): + value = item["value"] + if value is not None: + output[key] = item["value"] + + return output
+ +
[docs] def add_obj_field(self, field): + """Add field object to children. + + Args: + field (BaseGraphQlQueryField): Add field to query children. + + """ + if field in self._children: + return + + self._children.append(field) + field.set_parent(self)
+ +
[docs] def add_field_with_edges(self, name): + """Add field with edges to query. + + Args: + name (str): Field name e.g. 'tasks'. + + Returns: + GraphQlQueryEdgeField: Created field object. + + """ + item = GraphQlQueryEdgeField(name, self, self._order) + self.add_obj_field(item) + return item
+ +
[docs] def add_field(self, name): + """Add field to query. + + Args: + name (str): Field name e.g. 'id'. + + Returns: + GraphQlQueryField: Created field object. + + """ + item = GraphQlQueryField(name, self, self._order) + self.add_obj_field(item) + return item
+ +
[docs] def get_field_by_keys( + self, keys: Iterable[str] + ) -> Optional["BaseGraphQlQueryField"]: + keys = list(keys) + if not keys: + return None + + key = keys.pop(0) + for child in self._children: + if child.name == key: + return child.get_field_by_keys(keys) + return None
+ +
[docs] def get_field_by_path( + self, path: str + ) -> Optional["BaseGraphQlQueryField"]: + return self.get_field_by_keys(path.split("/"))
+ +
[docs] def calculate_query(self): + """Calculate query string which is sent to server. + + Returns: + str: GraphQl string with variables and headers. + + Raises: + ValueError: Query has no fiels. + + """ + if not self._children: + raise ValueError("Missing fields to query") + + variables = [] + for item in self._variables.values(): + if item["value"] is None: + continue + + variables.append( + "{}: {}".format(item["variable"], item["type"]) + ) + + variables_str = "" + if variables: + variables_str = "({})".format(",".join(variables)) + header = "query {}{}".format(self._name, variables_str) + + output = [] + output.append(header + " {") + for field in self._children: + output.append(field.calculate_query()) + output.append("}") + + return "\n".join(output)
+ +
[docs] def parse_result(self, data, output, progress_data): + """Parse data from response for output. + + Output is stored to passed 'output' variable. That's because of paging + during which objects must have access to both new and previous values. + + Args: + data (Dict[str, Any]): Data received using calculated query. + output (Dict[str, Any]): Where parsed data are stored. + progress_data (Dict[str, Any]): Data used for paging. + + """ + if not data: + return + + for child in self._children: + child.parse_result(data, output, progress_data)
+ +
[docs] def query(self, con): + """Do a query from server. + + Args: + con (ServerAPI): Connection to server with 'query' method. + + Returns: + Dict[str, Any]: Parsed output from GraphQl query. + + """ + progress_data = {} + output = {} + while self.need_query: + query_str = self.calculate_query() + variables = self.get_variables_values() + response = con.query_graphql( + query_str, + self.get_variables_values() + ) + if response.errors: + raise GraphQlQueryFailed(response.errors, query_str, variables) + self.parse_result(response.data["data"], output, progress_data) + + return output
+ +
[docs] def continuous_query(self, con): + """Do a query from server. + + Args: + con (ServerAPI): Connection to server with 'query' method. + + Returns: + Dict[str, Any]: Parsed output from GraphQl query. + + """ + progress_data = {} + if self.has_multiple_edge_fields: + output = {} + while self.need_query: + query_str = self.calculate_query() + variables = self.get_variables_values() + response = con.query_graphql(query_str, variables) + if response.errors: + raise GraphQlQueryFailed( + response.errors, query_str, variables + ) + self.parse_result(response.data["data"], output, progress_data) + + yield output + + else: + while self.need_query: + output = {} + query_str = self.calculate_query() + variables = self.get_variables_values() + response = con.query_graphql(query_str, variables) + if response.errors: + raise GraphQlQueryFailed( + response.errors, query_str, variables + ) + + self.parse_result(response.data["data"], output, progress_data) + + yield output
+ + +
[docs]class BaseGraphQlQueryField(ABC): + """Field in GraphQl query. + + Args: + name (str): Name of field. + parent (Union[BaseGraphQlQueryField, GraphQlQuery]): Parent object of a + field. + + """ + def __init__(self, name, parent, order): + if isinstance(parent, GraphQlQuery): + query_item = parent + else: + query_item = parent.query_item + + self._name = name + self._parent = parent + + self._filters = {} + + self._children = [] + # Value is changed on first parse of result + self._need_query = True + + self._query_item = query_item + + self._path = None + + self._limit = None + self._order = order + self._fetched_counter = 0 + + def __repr__(self): + return "<{} {}>".format(self.__class__.__name__, self.path) + +
[docs] def get_name(self) -> str: + return self._name
+ + name = property(get_name) + +
[docs] def get_field_by_keys(self, keys: Iterable[str]): + keys = list(keys) + if not keys: + return self + + key = keys.pop(0) + for child in self._children: + if child.name == key: + return child.get_field_by_keys(keys) + return None
+ +
[docs] def set_limit(self, limit: Optional[int]): + self._limit = limit
+ +
[docs] def set_order(self, order): + order = SortOrder.parse_value(order) + if order is None: + raise ValueError( + f"Got invalid value {order}." + f" Expected {SortOrder.ascending} or {SortOrder.descending}" + ) + self._order = order
+ +
[docs] def set_ascending_order(self, enabled=True): + self.set_order( + SortOrder.ascending if enabled else SortOrder.descending + )
+ +
[docs] def set_descending_order(self, enabled=True): + self.set_ascending_order(not enabled)
+ +
[docs] def add_variable(self, key, value_type, value=None): + """Add variable to query. + + Args: + key (str): Variable name. + value_type (str): Type of expected value in variables. This is + graphql type e.g. "[String!]", "Int", "Boolean", etc. + value (Any): Default value for variable. Can be changed later. + + Returns: + QueryVariable: Created variable object. + + Raises: + KeyError: If variable was already added before. + + """ + return self._parent.add_variable(key, value_type, value)
+ +
[docs] def get_variable(self, key): + """Variable object. + + Args: + key (str): Variable name added to headers. + + Returns: + QueryVariable: Variable object used in query string. + + """ + return self._parent.get_variable(key)
+ + @property + def need_query(self): + """Still need query from server. + + Needed for edges which use pagination. Look into children values too. + + Returns: + bool: If still need query from server. + + """ + if self._need_query: + return True + + for child in self._children_iter(): + if child.need_query: + return True + return False + + def _children_iter(self): + """Iterate over all children fields of object. + + Returns: + Iterator[BaseGraphQlQueryField]: Children fields. + + """ + for child in self._children: + yield child + +
[docs] def sum_edge_fields(self, max_limit=None): + """Check how many edge fields query has. + + In case there are multiple edge fields or are nested the query can't + yield mid cursor results. + + Args: + max_limit (int): Skip rest of counting if counter is bigger then + entered number. + + Returns: + int: Counter edge fields + + """ + counter = 0 + if isinstance(self, GraphQlQueryEdgeField): + counter = 1 + + for child in self._children_iter(): + counter += child.sum_edge_fields(max_limit) + if max_limit is not None and counter >= max_limit: + break + return counter
+ + @property + def offset(self): + return self._query_item.offset + + @property + def indent(self): + return self._parent.child_indent + self.offset + + @property + @abstractmethod + def child_indent(self): + pass + + @property + def query_item(self): + return self._query_item + + @property + @abstractmethod + def has_edges(self): + pass + + @property + def child_has_edges(self): + for child in self._children_iter(): + if child.has_edges or child.child_has_edges: + return True + return False + + @property + def path(self): + """Field path for debugging purposes. + + Returns: + str: Field path in query. + + """ + if self._path is None: + if isinstance(self._parent, GraphQlQuery): + path = self._name + else: + path = "/".join((self._parent.path, self._name)) + self._path = path + return self._path + +
[docs] def reset_cursor(self): + for child in self._children_iter(): + child.reset_cursor()
+ +
[docs] def get_variable_value(self, *args, **kwargs): + return self._query_item.get_variable_value(*args, **kwargs)
+ +
[docs] def set_variable_value(self, *args, **kwargs): + return self._query_item.set_variable_value(*args, **kwargs)
+ +
[docs] def set_filter(self, key, value): + self._filters[key] = value
+ +
[docs] def has_filter(self, key): + return key in self._filters
+ +
[docs] def remove_filter(self, key): + self._filters.pop(key, None)
+ +
[docs] def set_parent(self, parent): + if self._parent is parent: + return + self._parent = parent + parent.add_obj_field(self)
+ +
[docs] def add_obj_field(self, field): + if field in self._children: + return + + self._children.append(field) + field.set_parent(self)
+ +
[docs] def add_field_with_edges(self, name): + item = GraphQlQueryEdgeField(name, self, self._order) + self.add_obj_field(item) + return item
+ +
[docs] def add_field(self, name): + item = GraphQlQueryField(name, self, self._order) + self.add_obj_field(item) + return item
+ + def _filter_value_to_str(self, value): + if isinstance(value, QueryVariable): + if self.get_variable_value(value.variable_name) is None: + return None + return str(value) + + if isinstance(value, numbers.Number): + return str(value) + + if isinstance(value, str): + return '"{}"'.format(value) + + if isinstance(value, (list, set, tuple)): + return "[{}]".format( + ", ".join( + self._filter_value_to_str(item) + for item in iter(value) + ) + ) + raise TypeError( + "Unknown type to convert '{}'".format(str(type(value))) + ) + +
[docs] def get_filters(self): + """Receive filters for item. + + By default just use copy of set filters. + + Returns: + Dict[str, Any]: Fields filters. + + """ + return copy.deepcopy(self._filters)
+ + def _filters_to_string(self): + filters = self.get_filters() + if not filters: + return "" + + filter_items = [] + for key, value in filters.items(): + string_value = self._filter_value_to_str(value) + if string_value is None: + continue + + filter_items.append("{}: {}".format(key, string_value)) + + if not filter_items: + return "" + return "({})".format(", ".join(filter_items)) + + def _fake_children_parse(self): + """Mark children as they don't need query.""" + for child in self._children_iter(): + child.parse_result({}, {}, {}) + +
[docs] @abstractmethod + def calculate_query(self): + pass
+ +
[docs] @abstractmethod + def parse_result(self, data, output, progress_data): + pass
+ + +
[docs]class GraphQlQueryField(BaseGraphQlQueryField): + has_edges = False + + @property + def child_indent(self): + return self.indent + +
[docs] def parse_result(self, data, output, progress_data): + if not isinstance(data, dict): + raise TypeError("{} Expected 'dict' type got '{}'".format( + self._name, str(type(data)) + )) + + self._need_query = False + value = data.get(self._name) + if value is None: + self._fake_children_parse() + if self._name in data: + output[self._name] = None + return + + if not self._children: + output[self._name] = value + return + + output_value = output.get(self._name) + if isinstance(value, dict): + if output_value is None: + output_value = {} + output[self._name] = output_value + + for child in self._children: + child.parse_result(value, output_value, progress_data) + return + + if output_value is None: + output_value = [] + output[self._name] = output_value + + if not value: + self._fake_children_parse() + return + + diff = len(value) - len(output_value) + if diff > 0: + for _ in range(diff): + output_value.append({}) + + for idx, item in enumerate(value): + item_value = output_value[idx] + for child in self._children: + child.parse_result(item, item_value, progress_data)
+ +
[docs] def calculate_query(self): + offset = self.indent * " " + header = "{}{}{}".format( + offset, + self._name, + self._filters_to_string() + ) + if not self._children: + return header + + output = [] + output.append(header + " {") + + output.extend([ + field.calculate_query() + for field in self._children + ]) + output.append(offset + "}") + + return "\n".join(output)
+ + +
[docs]class GraphQlQueryEdgeField(BaseGraphQlQueryField): + has_edges = True + + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + self._cursor = None + self._edge_children = [] + + @property + def child_indent(self): + offset = self.offset * 2 + return self.indent + offset + + def _children_iter(self): + for child in super()._children_iter(): + yield child + + for child in self._edge_children: + yield child + +
[docs] def add_obj_field(self, field): + if field in self._edge_children: + return + + super().add_obj_field(field)
+ +
[docs] def add_obj_edge_field(self, field): + if field in self._edge_children or field in self._children: + return + + self._edge_children.append(field) + field.set_parent(self)
+ +
[docs] def add_edge_field(self, name): + item = GraphQlQueryField(name, self, self._order) + self.add_obj_edge_field(item) + return item
+ +
[docs] def reset_cursor(self): + # Reset cursor only for edges + self._cursor = None + self._need_query = True + + super().reset_cursor()
+ +
[docs] def parse_result(self, data, output, progress_data): + if not isinstance(data, dict): + raise TypeError("{} Expected 'dict' type got '{}'".format( + self._name, str(type(data)) + )) + + value = data.get(self._name) + if value is None: + self._fake_children_parse() + self._need_query = False + return + + if self._name in output: + node_values = output[self._name] + else: + node_values = [] + output[self._name] = node_values + + handle_cursors = self.child_has_edges + if handle_cursors: + cursor_key = self._get_cursor_key() + if cursor_key in progress_data: + nodes_by_cursor = progress_data[cursor_key] + else: + nodes_by_cursor = {} + progress_data[cursor_key] = nodes_by_cursor + + page_info = value["pageInfo"] + new_cursor = page_info["endCursor"] + self._need_query = page_info["hasNextPage"] + edges = value["edges"] + # Fake result parse + if not edges: + self._fake_children_parse() + + self._fetched_counter += len(edges) + if self._limit and self._fetched_counter >= self._limit: + self._need_query = False + + for edge in edges: + if not handle_cursors: + edge_value = {} + node_values.append(edge_value) + else: + edge_cursor = edge["cursor"] + edge_value = nodes_by_cursor.get(edge_cursor) + if edge_value is None: + edge_value = {} + nodes_by_cursor[edge_cursor] = edge_value + node_values.append(edge_value) + + for child in self._edge_children: + child.parse_result(edge, edge_value, progress_data) + + for child in self._children: + child.parse_result(edge["node"], edge_value, progress_data) + + if not self._need_query: + return + + change_cursor = True + for child in self._children_iter(): + if child.need_query: + change_cursor = False + + if change_cursor: + for child in self._children_iter(): + child.reset_cursor() + self._cursor = new_cursor
+ + def _get_cursor_key(self): + return "{}/__cursor__".format(self.path) + +
[docs] def get_filters(self): + filters = super().get_filters() + limit_key = "first" + if self._order == SortOrder.descending: + limit_key = "last" + + limit_amount = 300 + if self._limit: + total = self._fetched_counter + limit_amount + if total > self._limit: + limit_amount = self._limit - self._fetched_counter + + filters[limit_key] = limit_amount + + if self._cursor: + filters["after"] = self._cursor + return filters
+ +
[docs] def calculate_query(self): + if not self._children and not self._edge_children: + raise ValueError("Missing child definitions for edges {}".format( + self.path + )) + + offset = self.indent * " " + header = "{}{}{}".format( + offset, + self._name, + self._filters_to_string() + ) + + output = [] + output.append(header + " {") + + edges_offset = offset + self.offset * " " + node_offset = edges_offset + self.offset * " " + output.append(edges_offset + "edges {") + for field in self._edge_children: + output.append(field.calculate_query()) + + if self._children: + output.append(node_offset + "node {") + + for field in self._children: + output.append( + field.calculate_query() + ) + + output.append(node_offset + "}") + if self.child_has_edges: + output.append(node_offset + "cursor") + + output.append(edges_offset + "}") + + # Add page information + output.append(edges_offset + "pageInfo {") + for page_key in ( + "endCursor", + "hasNextPage", + ): + output.append(node_offset + page_key) + output.append(edges_offset + "}") + output.append(offset + "}") + + return "\n".join(output)
+ + +INTROSPECTION_QUERY = """ + query IntrospectionQuery { + __schema { + queryType { name } + mutationType { name } + subscriptionType { name } + types { + ...FullType + } + directives { + name + description + locations + args { + ...InputValue + } + } + } + } + fragment FullType on __Type { + kind + name + description + fields(includeDeprecated: true) { + name + description + args { + ...InputValue + } + type { + ...TypeRef + } + isDeprecated + deprecationReason + } + inputFields { + ...InputValue + } + interfaces { + ...TypeRef + } + enumValues(includeDeprecated: true) { + name + description + isDeprecated + deprecationReason + } + possibleTypes { + ...TypeRef + } + } + fragment InputValue on __InputValue { + name + description + type { ...TypeRef } + defaultValue + } + fragment TypeRef on __Type { + kind + name + ofType { + kind + name + ofType { + kind + name + ofType { + kind + name + ofType { + kind + name + ofType { + kind + name + ofType { + kind + name + ofType { + kind + name + } + } + } + } + } + } + } + } +""" +
+ +
+ +
+
+ + +
+

+ + © Copyright 2024, ynput.io +
+ + + + Built with Sphinx using a + + theme + + provided by Revitron. + +

+
+ +
+ +
+
+ +
+ +
+ + + + + + + + + + + + \ No newline at end of file diff --git a/_modules/ayon_api/graphql_queries.html b/_modules/ayon_api/graphql_queries.html new file mode 100644 index 0000000000..e00b4c619a --- /dev/null +++ b/_modules/ayon_api/graphql_queries.html @@ -0,0 +1,873 @@ + + + + + + + + + + + ayon_api.graphql_queries — ayon-python-api 1.0.11 documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + +
+ + + + + +
+ +
+ +
+ + + +
+
+
+ +

Source code for ayon_api.graphql_queries

+import collections
+
+from .constants import DEFAULT_LINK_FIELDS
+from .graphql import FIELD_VALUE, GraphQlQuery, fields_to_dict
+
+
+
+
+
+
[docs]def project_graphql_query(fields): + query = GraphQlQuery("ProjectQuery") + project_name_var = query.add_variable("projectName", "String!") + project_field = query.add_field("project") + project_field.set_filter("name", project_name_var) + + nested_fields = fields_to_dict(fields) + + query_queue = collections.deque() + for key, value in nested_fields.items(): + query_queue.append((key, value, project_field)) + + while query_queue: + item = query_queue.popleft() + key, value, parent = item + field = parent.add_field(key) + if value is FIELD_VALUE: + continue + + for k, v in value.items(): + query_queue.append((k, v, field)) + return query
+ + +
[docs]def projects_graphql_query(fields): + query = GraphQlQuery("ProjectsQuery") + projects_field = query.add_field_with_edges("projects") + + nested_fields = fields_to_dict(fields) + + query_queue = collections.deque() + for key, value in nested_fields.items(): + query_queue.append((key, value, projects_field)) + + while query_queue: + item = query_queue.popleft() + key, value, parent = item + field = parent.add_field(key) + if value is FIELD_VALUE: + continue + + for k, v in value.items(): + query_queue.append((k, v, field)) + return query
+ + +
[docs]def product_types_query(fields): + query = GraphQlQuery("ProductTypes") + product_types_field = query.add_field("productTypes") + + nested_fields = fields_to_dict(fields) + + query_queue = collections.deque() + for key, value in nested_fields.items(): + query_queue.append((key, value, product_types_field)) + + while query_queue: + item = query_queue.popleft() + key, value, parent = item + field = parent.add_field(key) + if value is FIELD_VALUE: + continue + + for k, v in value.items(): + query_queue.append((k, v, field)) + return query
+ + +
[docs]def project_product_types_query(fields): + query = GraphQlQuery("ProjectProductTypes") + project_query = query.add_field("project") + project_name_var = query.add_variable("projectName", "String!") + project_query.set_filter("name", project_name_var) + product_types_field = project_query.add_field("productTypes") + nested_fields = fields_to_dict(fields) + + query_queue = collections.deque() + for key, value in nested_fields.items(): + query_queue.append((key, value, product_types_field)) + + while query_queue: + item = query_queue.popleft() + key, value, parent = item + field = parent.add_field(key) + if value is FIELD_VALUE: + continue + + for k, v in value.items(): + query_queue.append((k, v, field)) + return query
+ + +
[docs]def folders_graphql_query(fields): + query = GraphQlQuery("FoldersQuery") + project_name_var = query.add_variable("projectName", "String!") + folder_ids_var = query.add_variable("folderIds", "[String!]") + parent_folder_ids_var = query.add_variable("parentFolderIds", "[String!]") + folder_paths_var = query.add_variable("folderPaths", "[String!]") + folder_path_regex_var = query.add_variable("folderPathRegex", "String!") + folder_names_var = query.add_variable("folderNames", "[String!]") + folder_types_var = query.add_variable("folderTypes", "[String!]") + has_products_var = query.add_variable("folderHasProducts", "Boolean!") + has_tasks_var = query.add_variable("folderHasTasks", "Boolean!") + has_links_var = query.add_variable("folderHasLinks", "HasLinksFilter") + has_children_var = query.add_variable("folderHasChildren", "Boolean!") + statuses_var = query.add_variable("folderStatuses", "[String!]") + folder_assignees_all_var = query.add_variable( + "folderAssigneesAll", "[String!]" + ) + tags_var = query.add_variable("folderTags", "[String!]") + + project_field = query.add_field("project") + project_field.set_filter("name", project_name_var) + + folders_field = project_field.add_field_with_edges("folders") + folders_field.set_filter("ids", folder_ids_var) + folders_field.set_filter("parentIds", parent_folder_ids_var) + folders_field.set_filter("names", folder_names_var) + folders_field.set_filter("paths", folder_paths_var) + folders_field.set_filter("pathEx", folder_path_regex_var) + folders_field.set_filter("folderTypes", folder_types_var) + folders_field.set_filter("statuses", statuses_var) + folders_field.set_filter("assignees", folder_assignees_all_var) + folders_field.set_filter("tags", tags_var) + folders_field.set_filter("hasProducts", has_products_var) + folders_field.set_filter("hasTasks", has_tasks_var) + folders_field.set_filter("hasLinks", has_links_var) + folders_field.set_filter("hasChildren", has_children_var) + + nested_fields = fields_to_dict(fields) + add_links_fields(folders_field, nested_fields) + + query_queue = collections.deque() + for key, value in nested_fields.items(): + query_queue.append((key, value, folders_field)) + + while query_queue: + item = query_queue.popleft() + key, value, parent = item + field = parent.add_field(key) + if value is FIELD_VALUE: + continue + + for k, v in value.items(): + query_queue.append((k, v, field)) + return query
+ + +
[docs]def tasks_graphql_query(fields): + query = GraphQlQuery("TasksQuery") + project_name_var = query.add_variable("projectName", "String!") + task_ids_var = query.add_variable("taskIds", "[String!]") + task_names_var = query.add_variable("taskNames", "[String!]") + task_types_var = query.add_variable("taskTypes", "[String!]") + folder_ids_var = query.add_variable("folderIds", "[String!]") + assignees_any_var = query.add_variable("taskAssigneesAny", "[String!]") + assignees_all_var = query.add_variable("taskAssigneesAll", "[String!]") + statuses_var = query.add_variable("taskStatuses", "[String!]") + tags_var = query.add_variable("taskTags", "[String!]") + + project_field = query.add_field("project") + project_field.set_filter("name", project_name_var) + + tasks_field = project_field.add_field_with_edges("tasks") + tasks_field.set_filter("ids", task_ids_var) + # WARNING: At moment when this been created 'names' filter is not supported + tasks_field.set_filter("names", task_names_var) + tasks_field.set_filter("taskTypes", task_types_var) + tasks_field.set_filter("folderIds", folder_ids_var) + tasks_field.set_filter("assigneesAny", assignees_any_var) + tasks_field.set_filter("assignees", assignees_all_var) + tasks_field.set_filter("statuses", statuses_var) + tasks_field.set_filter("tags", tags_var) + + nested_fields = fields_to_dict(fields) + add_links_fields(tasks_field, nested_fields) + + query_queue = collections.deque() + for key, value in nested_fields.items(): + query_queue.append((key, value, tasks_field)) + + while query_queue: + item = query_queue.popleft() + key, value, parent = item + field = parent.add_field(key) + if value is FIELD_VALUE: + continue + + for k, v in value.items(): + query_queue.append((k, v, field)) + return query
+ + +
[docs]def tasks_by_folder_paths_graphql_query(fields): + query = GraphQlQuery("TasksByFolderPathQuery") + project_name_var = query.add_variable("projectName", "String!") + task_names_var = query.add_variable("taskNames", "[String!]") + task_types_var = query.add_variable("taskTypes", "[String!]") + folder_paths_var = query.add_variable("folderPaths", "[String!]") + assignees_any_var = query.add_variable("taskAssigneesAny", "[String!]") + assignees_all_var = query.add_variable("taskAssigneesAll", "[String!]") + statuses_var = query.add_variable("taskStatuses", "[String!]") + tags_var = query.add_variable("taskTags", "[String!]") + + project_field = query.add_field("project") + project_field.set_filter("name", project_name_var) + + folders_field = project_field.add_field_with_edges("folders") + folders_field.add_field("path") + folders_field.set_filter("paths", folder_paths_var) + + tasks_field = folders_field.add_field_with_edges("tasks") + # WARNING: At moment when this been created 'names' filter is not supported + tasks_field.set_filter("names", task_names_var) + tasks_field.set_filter("taskTypes", task_types_var) + tasks_field.set_filter("assigneesAny", assignees_any_var) + tasks_field.set_filter("assignees", assignees_all_var) + tasks_field.set_filter("statuses", statuses_var) + tasks_field.set_filter("tags", tags_var) + + nested_fields = fields_to_dict(fields) + add_links_fields(tasks_field, nested_fields) + + query_queue = collections.deque() + for key, value in nested_fields.items(): + query_queue.append((key, value, tasks_field)) + + while query_queue: + item = query_queue.popleft() + key, value, parent = item + field = parent.add_field(key) + if value is FIELD_VALUE: + continue + + for k, v in value.items(): + query_queue.append((k, v, field)) + return query
+ + +
[docs]def products_graphql_query(fields): + query = GraphQlQuery("ProductsQuery") + + project_name_var = query.add_variable("projectName", "String!") + product_ids_var = query.add_variable("productIds", "[String!]") + product_names_var = query.add_variable("productNames", "[String!]") + folder_ids_var = query.add_variable("folderIds", "[String!]") + product_types_var = query.add_variable("productTypes", "[String!]") + product_name_regex_var = query.add_variable("productNameRegex", "String!") + product_path_regex_var = query.add_variable("productPathRegex", "String!") + statuses_var = query.add_variable("productStatuses.", "[String!]") + tags_var = query.add_variable("productTags.", "[String!]") + + project_field = query.add_field("project") + project_field.set_filter("name", project_name_var) + + products_field = project_field.add_field_with_edges("products") + products_field.set_filter("ids", product_ids_var) + products_field.set_filter("names", product_names_var) + products_field.set_filter("folderIds", folder_ids_var) + products_field.set_filter("productTypes", product_types_var) + products_field.set_filter("statuses", statuses_var) + products_field.set_filter("tags", tags_var) + products_field.set_filter("nameEx", product_name_regex_var) + products_field.set_filter("pathEx", product_path_regex_var) + + nested_fields = fields_to_dict(set(fields)) + add_links_fields(products_field, nested_fields) + + query_queue = collections.deque() + for key, value in nested_fields.items(): + query_queue.append((key, value, products_field)) + + while query_queue: + item = query_queue.popleft() + key, value, parent = item + field = parent.add_field(key) + if value is FIELD_VALUE: + continue + + for k, v in value.items(): + query_queue.append((k, v, field)) + return query
+ + +
[docs]def versions_graphql_query(fields): + query = GraphQlQuery("VersionsQuery") + + project_name_var = query.add_variable("projectName", "String!") + product_ids_var = query.add_variable("productIds", "[String!]") + version_ids_var = query.add_variable("versionIds", "[String!]") + versions_var = query.add_variable("versions", "[Int!]") + hero_only_var = query.add_variable("heroOnly", "Boolean") + latest_only_var = query.add_variable("latestOnly", "Boolean") + hero_or_latest_only_var = query.add_variable( + "heroOrLatestOnly", "Boolean" + ) + statuses_var = query.add_variable("versionStatuses", "[String!]") + tags_var = query.add_variable("versionTags", "[String!]") + + project_field = query.add_field("project") + project_field.set_filter("name", project_name_var) + + versions_field = project_field.add_field_with_edges("versions") + versions_field.set_filter("ids", version_ids_var) + versions_field.set_filter("productIds", product_ids_var) + versions_field.set_filter("versions", versions_var) + versions_field.set_filter("heroOnly", hero_only_var) + versions_field.set_filter("latestOnly", latest_only_var) + versions_field.set_filter("heroOrLatestOnly", hero_or_latest_only_var) + versions_field.set_filter("statuses", statuses_var) + versions_field.set_filter("tags", tags_var) + + nested_fields = fields_to_dict(set(fields)) + add_links_fields(versions_field, nested_fields) + + query_queue = collections.deque() + for key, value in nested_fields.items(): + query_queue.append((key, value, versions_field)) + + while query_queue: + item = query_queue.popleft() + key, value, parent = item + field = parent.add_field(key) + if value is FIELD_VALUE: + continue + + for k, v in value.items(): + query_queue.append((k, v, field)) + return query
+ + +
[docs]def representations_graphql_query(fields): + query = GraphQlQuery("RepresentationsQuery") + + project_name_var = query.add_variable("projectName", "String!") + repre_ids_var = query.add_variable("representationIds", "[String!]") + repre_names_var = query.add_variable("representationNames", "[String!]") + version_ids_var = query.add_variable("versionIds", "[String!]") + has_links_var = query.add_variable( + "representationHasLinks", "HasLinksFilter" + ) + statuses_var = query.add_variable( + "representationStatuses", "[String!]" + ) + tags_var = query.add_variable( + "representationTags", "[String!]" + ) + + project_field = query.add_field("project") + project_field.set_filter("name", project_name_var) + + repres_field = project_field.add_field_with_edges("representations") + repres_field.set_filter("ids", repre_ids_var) + repres_field.set_filter("versionIds", version_ids_var) + repres_field.set_filter("names", repre_names_var) + repres_field.set_filter("hasLinks", has_links_var) + repres_field.set_filter("statuses", statuses_var) + repres_field.set_filter("tags", tags_var) + + nested_fields = fields_to_dict(set(fields)) + add_links_fields(repres_field, nested_fields) + + query_queue = collections.deque() + for key, value in nested_fields.items(): + query_queue.append((key, value, repres_field)) + + while query_queue: + item = query_queue.popleft() + key, value, parent = item + field = parent.add_field(key) + if value is FIELD_VALUE: + continue + + for k, v in value.items(): + query_queue.append((k, v, field)) + return query
+ + +
[docs]def representations_parents_qraphql_query( + version_fields, product_fields, folder_fields +): + query = GraphQlQuery("RepresentationsParentsQuery") + + project_name_var = query.add_variable("projectName", "String!") + repre_ids_var = query.add_variable("representationIds", "[String!]") + + project_field = query.add_field("project") + project_field.set_filter("name", project_name_var) + + repres_field = project_field.add_field_with_edges("representations") + repres_field.add_field("id") + repres_field.set_filter("ids", repre_ids_var) + version_field = repres_field.add_field("version") + + fields_queue = collections.deque() + for key, value in fields_to_dict(version_fields).items(): + fields_queue.append((key, value, version_field)) + + product_field = version_field.add_field("product") + for key, value in fields_to_dict(product_fields).items(): + fields_queue.append((key, value, product_field)) + + folder_field = product_field.add_field("folder") + for key, value in fields_to_dict(folder_fields).items(): + fields_queue.append((key, value, folder_field)) + + while fields_queue: + item = fields_queue.popleft() + key, value, parent = item + field = parent.add_field(key) + if value is FIELD_VALUE: + continue + + for k, v in value.items(): + fields_queue.append((k, v, field)) + + return query
+ + +
[docs]def representations_hierarchy_qraphql_query( + folder_fields, + task_fields, + product_fields, + version_fields, + representation_fields, +): + query = GraphQlQuery("RepresentationsParentsQuery") + + project_name_var = query.add_variable("projectName", "String!") + repre_ids_var = query.add_variable("representationIds", "[String!]") + + project_field = query.add_field("project") + project_field.set_filter("name", project_name_var) + + fields_queue = collections.deque() + + repres_field = project_field.add_field_with_edges("representations") + for key, value in fields_to_dict(representation_fields).items(): + fields_queue.append((key, value, repres_field)) + + repres_field.set_filter("ids", repre_ids_var) + version_field = None + if folder_fields or task_fields or product_fields or version_fields: + version_field = repres_field.add_field("version") + if version_fields: + for key, value in fields_to_dict(version_fields).items(): + fields_queue.append((key, value, version_field)) + + if task_fields: + task_field = version_field.add_field("task") + for key, value in fields_to_dict(task_fields).items(): + fields_queue.append((key, value, task_field)) + + product_field = None + if folder_fields or product_fields: + product_field = version_field.add_field("product") + for key, value in fields_to_dict(product_fields).items(): + fields_queue.append((key, value, product_field)) + + if folder_fields: + folder_field = product_field.add_field("folder") + for key, value in fields_to_dict(folder_fields).items(): + fields_queue.append((key, value, folder_field)) + + while fields_queue: + item = fields_queue.popleft() + key, value, parent = item + field = parent.add_field(key) + if value is FIELD_VALUE: + continue + + for k, v in value.items(): + fields_queue.append((k, v, field)) + + return query
+ + +
[docs]def workfiles_info_graphql_query(fields): + query = GraphQlQuery("WorkfilesInfo") + project_name_var = query.add_variable("projectName", "String!") + workfiles_info_ids = query.add_variable("workfileIds", "[String!]") + task_ids_var = query.add_variable("taskIds", "[String!]") + paths_var = query.add_variable("paths", "[String!]") + path_regex_var = query.add_variable("workfilePathRegex", "String!") + has_links_var = query.add_variable("workfilehasLinks", "HasLinksFilter") + statuses_var = query.add_variable("workfileStatuses", "[String!]") + tags_var = query.add_variable("workfileTags", "[String!]") + + project_field = query.add_field("project") + project_field.set_filter("name", project_name_var) + + workfiles_field = project_field.add_field_with_edges("workfiles") + workfiles_field.set_filter("ids", workfiles_info_ids) + workfiles_field.set_filter("taskIds", task_ids_var) + workfiles_field.set_filter("paths", paths_var) + workfiles_field.set_filter("pathEx", path_regex_var) + workfiles_field.set_filter("hasLinks", has_links_var) + workfiles_field.set_filter("statuses", statuses_var) + workfiles_field.set_filter("tags", tags_var) + + nested_fields = fields_to_dict(set(fields)) + add_links_fields(workfiles_field, nested_fields) + + query_queue = collections.deque() + for key, value in nested_fields.items(): + query_queue.append((key, value, workfiles_field)) + + while query_queue: + item = query_queue.popleft() + key, value, parent = item + field = parent.add_field(key) + if value is FIELD_VALUE: + continue + + for k, v in value.items(): + query_queue.append((k, v, field)) + return query
+ + +
[docs]def events_graphql_query(fields, order, use_states=False): + query = GraphQlQuery("Events", order=order) + topics_var = query.add_variable("eventTopics", "[String!]") + ids_var = query.add_variable("eventIds", "[String!]") + projects_var = query.add_variable("projectNames", "[String!]") + statuses_var = query.add_variable("eventStatuses", "[String!]") + users_var = query.add_variable("eventUsers", "[String!]") + include_logs_var = query.add_variable("includeLogsFilter", "Boolean!") + has_children_var = query.add_variable("hasChildrenFilter", "Boolean!") + newer_than_var = query.add_variable("newerThanFilter", "String!") + older_than_var = query.add_variable("olderThanFilter", "String!") + + statuses_filter_name = "statuses" + if use_states: + statuses_filter_name = "states" + events_field = query.add_field_with_edges("events") + events_field.set_filter("ids", ids_var) + events_field.set_filter("topics", topics_var) + events_field.set_filter("projects", projects_var) + events_field.set_filter(statuses_filter_name, statuses_var) + events_field.set_filter("users", users_var) + events_field.set_filter("includeLogs", include_logs_var) + events_field.set_filter("hasChildren", has_children_var) + events_field.set_filter("newerThan", newer_than_var) + events_field.set_filter("olderThan", older_than_var) + + nested_fields = fields_to_dict(set(fields)) + + query_queue = collections.deque() + for key, value in nested_fields.items(): + query_queue.append((key, value, events_field)) + + while query_queue: + item = query_queue.popleft() + key, value, parent = item + field = parent.add_field(key) + if value is FIELD_VALUE: + continue + + for k, v in value.items(): + query_queue.append((k, v, field)) + return query
+ + +
[docs]def users_graphql_query(fields): + query = GraphQlQuery("Users") + names_var = query.add_variable("userNames", "[String!]") + project_name_var = query.add_variable("projectName", "String!") + + users_field = query.add_field_with_edges("users") + users_field.set_filter("names", names_var) + users_field.set_filter("projectName", project_name_var) + + nested_fields = fields_to_dict(set(fields)) + + query_queue = collections.deque() + for key, value in nested_fields.items(): + query_queue.append((key, value, users_field)) + + while query_queue: + item = query_queue.popleft() + key, value, parent = item + field = parent.add_field(key) + if value is FIELD_VALUE: + continue + + for k, v in value.items(): + query_queue.append((k, v, field)) + return query
+ + +
[docs]def activities_graphql_query(fields, order): + query = GraphQlQuery("Activities", order=order) + project_name_var = query.add_variable("projectName", "String!") + activity_ids_var = query.add_variable("activityIds", "[String!]") + activity_types_var = query.add_variable("activityTypes", "[String!]") + entity_ids_var = query.add_variable("entityIds", "[String!]") + entity_names_var = query.add_variable("entityNames", "[String!]") + entity_type_var = query.add_variable("entityType", "String!") + changed_after_var = query.add_variable("changedAfter", "String!") + changed_before_var = query.add_variable("changedBefore", "String!") + reference_types_var = query.add_variable("referenceTypes", "[String!]") + + project_field = query.add_field("project") + project_field.set_filter("name", project_name_var) + + activities_field = project_field.add_field_with_edges("activities") + activities_field.set_filter("activityIds", activity_ids_var) + activities_field.set_filter("activityTypes", activity_types_var) + activities_field.set_filter("entityIds", entity_ids_var) + activities_field.set_filter("entityNames", entity_names_var) + activities_field.set_filter("entityType", entity_type_var) + activities_field.set_filter("changedAfter", changed_after_var) + activities_field.set_filter("changedBefore", changed_before_var) + activities_field.set_filter("referenceTypes", reference_types_var) + + nested_fields = fields_to_dict(set(fields)) + + query_queue = collections.deque() + for key, value in nested_fields.items(): + query_queue.append((key, value, activities_field)) + + while query_queue: + item = query_queue.popleft() + key, value, parent = item + field = parent.add_field(key) + if value is FIELD_VALUE: + continue + + for k, v in value.items(): + query_queue.append((k, v, field)) + + return query
+
+ +
+ +
+
+ + +
+

+ + © Copyright 2024, ynput.io +
+ + + + Built with Sphinx using a + + theme + + provided by Revitron. + +

+
+ +
+ +
+
+ +
+ +
+ + + + + + + + + + + + \ No newline at end of file diff --git a/_modules/ayon_api/operations.html b/_modules/ayon_api/operations.html new file mode 100644 index 0000000000..46723319e8 --- /dev/null +++ b/_modules/ayon_api/operations.html @@ -0,0 +1,1663 @@ + + + + + + + + + + + ayon_api.operations — ayon-python-api 1.0.11 documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + +
+ + + + + +
+ +
+ +
+ + + +
+
+
+ +

Source code for ayon_api.operations

+import os
+import copy
+import collections
+import uuid
+from abc import ABC, abstractmethod
+
+from ._api import get_server_api_connection
+from .utils import create_entity_id, REMOVED_VALUE, NOT_SET
+
+
+def _create_or_convert_to_id(entity_id=None):
+    if entity_id is None:
+        return create_entity_id()
+
+    # Validate if can be converted to uuid
+    uuid.UUID(entity_id)
+    return entity_id
+
+
+
[docs]def prepare_changes(old_entity, new_entity, entity_type): + """Prepare changes for entity update. + + Notes: + Argument 'entity_type' is not used, yet. But there might be + differences in future. + + Args: + old_entity (dict[str, Any]): Existing entity. + new_entity (dict[str, Any]): New entity. + entity_type (str): Entity type. "project", "folder", "product" etc. + + Returns: + dict[str, Any]: Changes that have new entity. + + """ + changes = {} + for key in set(new_entity.keys()): + if key == "attrib": + continue + + if key in new_entity and new_entity[key] != old_entity.get(key): + changes[key] = new_entity[key] + continue + + attrib_changes = {} + if "attrib" in new_entity: + old_attrib = old_entity.get("attrib") or {} + for key, value in new_entity["attrib"].items(): + if value != old_attrib.get(key): + attrib_changes[key] = value + if attrib_changes: + changes["attrib"] = attrib_changes + return changes
+ + +
[docs]def new_folder_entity( + name, + folder_type, + parent_id=None, + status=None, + tags=None, + attribs=None, + data=None, + thumbnail_id=None, + entity_id=None +): + """Create skeleton data of folder entity. + + Args: + name (str): Is considered as unique identifier of folder in project. + folder_type (str): Type of folder. + parent_id (Optional[str]): Parent folder id. + status (Optional[str]): Product status. + tags (Optional[List[str]]): List of tags. + attribs (Optional[Dict[str, Any]]): Explicitly set attributes + of folder. + data (Optional[Dict[str, Any]]): Custom folder data. Empty dictionary + is used if not passed. + thumbnail_id (Optional[str]): Thumbnail id related to folder. + entity_id (Optional[str]): Predefined id of entity. New id is + created if not passed. + + Returns: + Dict[str, Any]: Skeleton of folder entity. + + """ + if attribs is None: + attribs = {} + + if data is None: + data = {} + + if parent_id is not None: + parent_id = _create_or_convert_to_id(parent_id) + + output = { + "id": _create_or_convert_to_id(entity_id), + "name": name, + # This will be ignored + "folderType": folder_type, + "parentId": parent_id, + "data": data, + "attrib": attribs, + "thumbnailId": thumbnail_id + } + if status: + output["status"] = status + if tags: + output["tags"] = tags + return output
+ + +
[docs]def new_product_entity( + name, + product_type, + folder_id, + status=None, + tags=None, + attribs=None, + data=None, + entity_id=None +): + """Create skeleton data of product entity. + + Args: + name (str): Is considered as unique identifier of + product under folder. + product_type (str): Product type. + folder_id (str): Parent folder id. + status (Optional[str]): Product status. + tags (Optional[List[str]]): List of tags. + attribs (Optional[Dict[str, Any]]): Explicitly set attributes + of product. + data (Optional[Dict[str, Any]]): product entity data. Empty dictionary + is used if not passed. + entity_id (Optional[str]): Predefined id of entity. New id is + created if not passed. + + Returns: + Dict[str, Any]: Skeleton of product entity. + + """ + if attribs is None: + attribs = {} + + if data is None: + data = {} + + output = { + "id": _create_or_convert_to_id(entity_id), + "name": name, + "productType": product_type, + "attrib": attribs, + "data": data, + "folderId": _create_or_convert_to_id(folder_id), + } + if status: + output["status"] = status + if tags: + output["tags"] = tags + return output
+ + +
[docs]def new_version_entity( + version, + product_id, + task_id=None, + thumbnail_id=None, + author=None, + status=None, + tags=None, + attribs=None, + data=None, + entity_id=None +): + """Create skeleton data of version entity. + + Args: + version (int): Is considered as unique identifier of version + under product. + product_id (str): Parent product id. + task_id (Optional[str]): Task id under which product was created. + thumbnail_id (Optional[str]): Thumbnail related to version. + author (Optional[str]): Name of version author. + status (Optional[str]): Version status. + tags (Optional[List[str]]): List of tags. + attribs (Optional[Dict[str, Any]]): Explicitly set attributes + of version. + data (Optional[Dict[str, Any]]): Version entity custom data. + entity_id (Optional[str]): Predefined id of entity. New id is + created if not passed. + + Returns: + Dict[str, Any]: Skeleton of version entity. + + """ + if attribs is None: + attribs = {} + + if data is None: + data = {} + + output = { + "id": _create_or_convert_to_id(entity_id), + "version": int(version), + "productId": _create_or_convert_to_id(product_id), + "attrib": attribs, + "data": data + } + if task_id: + output["taskId"] = task_id + if thumbnail_id: + output["thumbnailId"] = thumbnail_id + if author: + output["author"] = author + if tags: + output["tags"] = tags + if status: + output["status"] = status + return output
+ + +
[docs]def new_hero_version_entity( + version, + product_id, + task_id=None, + thumbnail_id=None, + author=None, + status=None, + tags=None, + attribs=None, + data=None, + entity_id=None +): + """Create skeleton data of hero version entity. + + Args: + version (int): Is considered as unique identifier of version + under product. Should be same as standard version if there is any. + product_id (str): Parent product id. + task_id (Optional[str]): Task id under which product was created. + thumbnail_id (Optional[str]): Thumbnail related to version. + author (Optional[str]): Name of version author. + status (Optional[str]): Version status. + tags (Optional[List[str]]): List of tags. + attribs (Optional[Dict[str, Any]]): Explicitly set attributes + of version. + data (Optional[Dict[str, Any]]): Version entity data. + entity_id (Optional[str]): Predefined id of entity. New id is + created if not passed. + + Returns: + Dict[str, Any]: Skeleton of version entity. + + """ + + return new_version_entity( + -abs(int(version)), + product_id, + task_id, + thumbnail_id, + author, + status, + tags, + attribs, + data, + entity_id + )
+ + +
[docs]def new_representation_entity( + name, + version_id, + files, + status=None, + tags=None, + attribs=None, + data=None, + entity_id=None +): + """Create skeleton data of representation entity. + + Args: + name (str): Representation name considered as unique identifier + of representation under version. + version_id (str): Parent version id. + files (list[dict[str, str]]): List of files in representation. + status (Optional[str]): Representation status. + tags (Optional[List[str]]): List of tags. + attribs (Optional[Dict[str, Any]]): Explicitly set attributes + of representation. + data (Optional[Dict[str, Any]]): Representation entity data. + entity_id (Optional[str]): Predefined id of entity. New id is created + if not passed. + + Returns: + Dict[str, Any]: Skeleton of representation entity. + + """ + if attribs is None: + attribs = {} + + if data is None: + data = {} + + output = { + "id": _create_or_convert_to_id(entity_id), + "versionId": _create_or_convert_to_id(version_id), + "files": files, + "name": name, + "data": data, + "attrib": attribs + } + if tags: + output["tags"] = tags + if status: + output["status"] = status + return output
+ + +
[docs]def new_workfile_info( + filepath, + task_id, + status=None, + tags=None, + attribs=None, + description=None, + data=None, + entity_id=None +): + """Create skeleton data of workfile info entity. + + Workfile entity is at this moment used primarily for artist notes. + + Args: + filepath (str): Rootless workfile filepath. + task_id (str): Task under which was workfile created. + status (Optional[str]): Workfile status. + tags (Optional[List[str]]): Workfile tags. + attribs (Options[dic[str, Any]]): Explicitly set attributes. + description (Optional[str]): Workfile description. + data (Optional[Dict[str, Any]]): Additional metadata. + entity_id (Optional[str]): Predefined id of entity. New id is created + if not passed. + + Returns: + Dict[str, Any]: Skeleton of workfile info entity. + + """ + if attribs is None: + attribs = {} + + if "extension" not in attribs: + attribs["extension"] = os.path.splitext(filepath)[-1] + + if description: + attribs["description"] = description + + if not data: + data = {} + + output = { + "id": _create_or_convert_to_id(entity_id), + "taskId": task_id, + "path": filepath, + "data": data, + "attrib": attribs + } + if status: + output["status"] = status + + if tags: + output["tags"] = tags + return output
+ + +
[docs]class AbstractOperation(ABC): + """Base operation class. + + Opration represent a call into database. The call can create, change or + remove data. + + Args: + project_name (str): On which project operation will happen. + entity_type (str): Type of entity on which change happens. + e.g. 'folder', 'representation' etc. + + """ + def __init__(self, project_name, entity_type, session): + self._project_name = project_name + self._entity_type = entity_type + self._session = session + self._id = str(uuid.uuid4()) + + @property + def project_name(self): + return self._project_name + + @property + def id(self): + """Identifier of operation.""" + return self._id + + @property + def entity_type(self): + return self._entity_type + + @property + @abstractmethod + def operation_name(self): + """Stringified type of operation.""" + pass + +
[docs] def to_data(self): + """Convert opration to data that can be converted to json or others. + + Returns: + Dict[str, Any]: Description of operation. + + """ + return { + "id": self._id, + "entity_type": self.entity_type, + "project_name": self.project_name, + "operation": self.operation_name + }
+ + +
[docs]class CreateOperation(AbstractOperation): + """Opeartion to create an entity. + + Args: + project_name (str): On which project operation will happen. + entity_type (str): Type of entity on which change happens. + e.g. 'folder', 'representation' etc. + data (Dict[str, Any]): Data of entity that will be created. + + """ + operation_name = "create" + + def __init__(self, project_name, entity_type, data, session): + if not data: + data = {} + else: + data = copy.deepcopy(dict(data)) + + if "id" not in data: + data["id"] = create_entity_id() + + self._data = data + super(CreateOperation, self).__init__( + project_name, entity_type, session + ) + + def __setitem__(self, key, value): + self.set_value(key, value) + + def __getitem__(self, key): + return self.data[key] + +
[docs] def set_value(self, key, value): + self.data[key] = value
+ +
[docs] def get(self, key, *args, **kwargs): + return self.data.get(key, *args, **kwargs)
+ + @property + def con(self): + return self.session.con + + @property + def session(self): + return self._session + + @property + def entity_id(self): + return self._data["id"] + + @property + def data(self): + return self._data + +
[docs] def to_data(self): + output = super(CreateOperation, self).to_data() + output["data"] = copy.deepcopy(self.data) + return output
+ +
[docs] def to_server_operation(self): + return { + "id": self.id, + "type": "create", + "entityType": self.entity_type, + "entityId": self.entity_id, + "data": self._data + }
+ + +
[docs]class UpdateOperation(AbstractOperation): + """Operation to update an entity. + + Args: + project_name (str): On which project operation will happen. + entity_type (str): Type of entity on which change happens. + e.g. 'folder', 'representation' etc. + entity_id (str): Identifier of an entity. + update_data (Dict[str, Any]): Key -> value changes that will be set in + database. If value is set to 'REMOVED_VALUE' the key will be + removed. Only first level of dictionary is checked (on purpose). + + """ + operation_name = "update" + + def __init__( + self, project_name, entity_type, entity_id, update_data, session + ): + super(UpdateOperation, self).__init__( + project_name, entity_type, session + ) + + self._entity_id = entity_id + self._update_data = update_data + + @property + def entity_id(self): + return self._entity_id + + @property + def update_data(self): + return self._update_data + + @property + def con(self): + return self.session.con + + @property + def session(self): + return self._session + +
[docs] def to_data(self): + changes = {} + for key, value in self._update_data.items(): + if value is REMOVED_VALUE: + value = None + changes[key] = value + + output = super(UpdateOperation, self).to_data() + output.update({ + "entity_id": self.entity_id, + "changes": changes + }) + return output
+ +
[docs] def to_server_operation(self): + if not self._update_data: + return None + + update_data = {} + for key, value in self._update_data.items(): + if value is REMOVED_VALUE: + value = None + update_data[key] = value + + return { + "id": self.id, + "type": "update", + "entityType": self.entity_type, + "entityId": self.entity_id, + "data": update_data + }
+ + +
[docs]class DeleteOperation(AbstractOperation): + """Opeartion to delete an entity. + + Args: + project_name (str): On which project operation will happen. + entity_type (str): Type of entity on which change happens. + e.g. 'folder', 'representation' etc. + entity_id (str): Entity id that will be removed. + + """ + operation_name = "delete" + + def __init__(self, project_name, entity_type, entity_id, session): + self._entity_id = entity_id + + super(DeleteOperation, self).__init__( + project_name, entity_type, session + ) + + @property + def entity_id(self): + return self._entity_id + + @property + def con(self): + return self.session.con + + @property + def session(self): + return self._session + +
[docs] def to_data(self): + output = super(DeleteOperation, self).to_data() + output["entity_id"] = self.entity_id + return output
+ +
[docs] def to_server_operation(self): + return { + "id": self.id, + "type": self.operation_name, + "entityId": self.entity_id, + "entityType": self.entity_type, + }
+ + +
[docs]class OperationsSession(object): + """Session storing operations that should happen in an order. + + At this moment does not handle anything special can be sonsidered as + stupid list of operations that will happen after each other. If creation + of same entity is there multiple times it's handled in any way and entity + values are not validated. + + All operations must be related to single project. + + Args: + con (Optional[ServerAPI]): Connection to server. Global connection + is used if not passed. + + """ + def __init__(self, con=None): + if con is None: + con = get_server_api_connection() + self._con = con + self._project_cache = {} + self._operations = [] + self._nested_operations = collections.defaultdict(list) + + @property + def con(self): + return self._con + +
[docs] def get_project(self, project_name): + if project_name not in self._project_cache: + self._project_cache[project_name] = self.con.get_project( + project_name) + return copy.deepcopy(self._project_cache[project_name])
+ + def __len__(self): + return len(self._operations) + +
[docs] def add(self, operation): + """Add operation to be processed. + + Args: + operation (BaseOperation): Operation that should be processed. + + """ + if not isinstance( + operation, + (CreateOperation, UpdateOperation, DeleteOperation) + ): + raise TypeError("Expected Operation object got {}".format( + str(type(operation)) + )) + + self._operations.append(operation)
+ +
[docs] def append(self, operation): + """Add operation to be processed. + + Args: + operation (BaseOperation): Operation that should be processed. + + """ + self.add(operation)
+ +
[docs] def extend(self, operations): + """Add operations to be processed. + + Args: + operations (List[BaseOperation]): Operations that should be + processed. + + """ + for operation in operations: + self.add(operation)
+ +
[docs] def remove(self, operation): + """Remove operation.""" + self._operations.remove(operation)
+ +
[docs] def clear(self): + """Clear all registered operations.""" + self._operations = []
+ +
[docs] def to_data(self): + return [ + operation.to_data() + for operation in self._operations + ]
+ +
[docs] def commit(self): + """Commit session operations.""" + operations, self._operations = self._operations, [] + if not operations: + return + + operations_by_project = collections.defaultdict(list) + for operation in operations: + operations_by_project[operation.project_name].append(operation) + + for project_name, operations in operations_by_project.items(): + operations_body = [] + for operation in operations: + body = operation.to_server_operation() + if body is not None: + operations_body.append(body) + + self._con.send_batch_operations( + project_name, operations_body, can_fail=False + )
+ +
[docs] def create_entity(self, project_name, entity_type, data, nested_id=None): + """Fast access to 'CreateOperation'. + + Args: + project_name (str): On which project the creation happens. + entity_type (str): Which entity type will be created. + data (Dicst[str, Any]): Entity data. + nested_id (str): Id of other operation from which is triggered + operation -> Operations can trigger suboperations but they + must be added to operations list after it's parent is added. + + Returns: + CreateOperation: Object of create operation. + + """ + operation = CreateOperation( + project_name, entity_type, data, self + ) + + if nested_id: + self._nested_operations[nested_id].append(operation) + else: + self.add(operation) + if operation.id in self._nested_operations: + self.extend(self._nested_operations.pop(operation.id)) + + return operation
+ +
[docs] def update_entity( + self, project_name, entity_type, entity_id, update_data, nested_id=None + ): + """Fast access to 'UpdateOperation'. + + Returns: + UpdateOperation: Object of update operation. + + """ + operation = UpdateOperation( + project_name, entity_type, entity_id, update_data, self + ) + if nested_id: + self._nested_operations[nested_id].append(operation) + else: + self.add(operation) + if operation.id in self._nested_operations: + self.extend(self._nested_operations.pop(operation.id)) + return operation
+ +
[docs] def delete_entity( + self, project_name, entity_type, entity_id, nested_id=None + ): + """Fast access to 'DeleteOperation'. + + Returns: + DeleteOperation: Object of delete operation. + + """ + operation = DeleteOperation( + project_name, entity_type, entity_id, self + ) + if nested_id: + self._nested_operations[nested_id].append(operation) + else: + self.add(operation) + if operation.id in self._nested_operations: + self.extend(self._nested_operations.pop(operation.id)) + return operation
+ +
[docs] def create_folder( + self, + project_name, + name, + folder_type=None, + parent_id=None, + label=None, + attrib=None, + data=None, + tags=None, + status=None, + active=None, + thumbnail_id=None, + folder_id=None, + ): + """Create new folder. + + Args: + project_name (str): Project name. + name (str): Folder name. + folder_type (Optional[str]): Folder type. + parent_id (Optional[str]): Parent folder id. Parent is project + if is ``None``. + label (Optional[str]): Label of folder. + attrib (Optional[dict[str, Any]]): Folder attributes. + data (Optional[dict[str, Any]]): Folder data. + tags (Optional[Iterable[str]]): Folder tags. + status (Optional[str]): Folder status. + active (Optional[bool]): Folder active state. + thumbnail_id (Optional[str]): Folder thumbnail id. + folder_id (Optional[str]): Folder id. If not passed new id is + generated. + + Returns: + CreateOperation: Object of create operation. + + """ + if not folder_id: + folder_id = create_entity_id() + create_data = { + "id": folder_id, + "name": name, + } + for key, value in ( + ("folderType", folder_type), + ("parentId", parent_id), + ("label", label), + ("attrib", attrib), + ("data", data), + ("tags", tags), + ("status", status), + ("active", active), + ("thumbnailId", thumbnail_id), + ): + if value is not None: + create_data[key] = value + + return self.create_entity( + project_name, "folder", create_data + )
+ +
[docs] def update_folder( + self, + project_name, + folder_id, + name=None, + folder_type=None, + parent_id=NOT_SET, + label=NOT_SET, + attrib=None, + data=None, + tags=None, + status=None, + active=None, + thumbnail_id=NOT_SET, + ): + """Update folder entity on server. + + Do not pass ``parent_id``, ``label`` amd ``thumbnail_id`` if you don't + want to change their values. Value ``None`` would unset + their value. + + Update of ``data`` will override existing value on folder entity. + + Update of ``attrib`` does change only passed attributes. If you want + to unset value, use ``None``. + + Args: + project_name (str): Project name. + folder_id (str): Folder id. + name (Optional[str]): New name. + folder_type (Optional[str]): New folder type. + parent_id (Optional[Union[str, None]]): New parent folder id. + label (Optional[Union[str, None]]): New label. + attrib (Optional[dict[str, Any]]): New attributes. + data (Optional[dict[str, Any]]): New data. + tags (Optional[Iterable[str]]): New tags. + status (Optional[str]): New status. + active (Optional[bool]): New active state. + thumbnail_id (Optional[Union[str, None]]): New thumbnail id. + + Returns: + UpdateOperation: Object of update operation. + + """ + update_data = {} + for key, value in ( + ("name", name), + ("folderType", folder_type), + ("attrib", attrib), + ("data", data), + ("tags", tags), + ("status", status), + ("active", active), + ): + if value is not None: + update_data[key] = value + + for key, value in ( + ("label", label), + ("parentId", parent_id), + ("thumbnailId", thumbnail_id), + ): + if value is not NOT_SET: + update_data[key] = value + + self.update_entity( + project_name, "folder", folder_id, update_data + )
+ +
[docs] def delete_folder(self, project_name, folder_id): + """Delete folder. + + Args: + project_name (str): Project name. + folder_id (str): Folder id to delete. + + Returns: + DeleteOperation: Object of delete operation. + + """ + return self.delete_entity( + project_name, "folder", folder_id + )
+ +
[docs] def create_task( + self, + project_name, + name, + task_type, + folder_id, + label=None, + assignees=None, + attrib=None, + data=None, + tags=None, + status=None, + active=None, + thumbnail_id=None, + task_id=None, + ): + """Create new task. + + Args: + project_name (str): Project name. + name (str): Folder name. + task_type (str): Task type. + folder_id (str): Parent folder id. + label (Optional[str]): Label of folder. + assignees (Optional[Iterable[str]]): Task assignees. + attrib (Optional[dict[str, Any]]): Task attributes. + data (Optional[dict[str, Any]]): Task data. + tags (Optional[Iterable[str]]): Task tags. + status (Optional[str]): Task status. + active (Optional[bool]): Task active state. + thumbnail_id (Optional[str]): Task thumbnail id. + task_id (Optional[str]): Task id. If not passed new id is + generated. + + Returns: + CreateOperation: Object of create operation. + + """ + if not task_id: + task_id = create_entity_id() + create_data = { + "id": task_id, + "name": name, + "taskType": task_type, + "folderId": folder_id, + } + for key, value in ( + ("label", label), + ("attrib", attrib), + ("data", data), + ("tags", tags), + ("status", status), + ("assignees", assignees), + ("active", active), + ("thumbnailId", thumbnail_id), + ): + if value is not None: + create_data[key] = value + + return self.create_entity( + project_name, "task", create_data + )
+ +
[docs] def update_task( + self, + project_name, + task_id, + name=None, + task_type=None, + folder_id=None, + label=NOT_SET, + assignees=None, + attrib=None, + data=None, + tags=None, + status=None, + active=None, + thumbnail_id=NOT_SET, + ): + """Update task entity on server. + + Do not pass ``label`` amd ``thumbnail_id`` if you don't + want to change their values. Value ``None`` would unset + their value. + + Update of ``data`` will override existing value on folder entity. + + Update of ``attrib`` does change only passed attributes. If you want + to unset value, use ``None``. + + Args: + project_name (str): Project name. + task_id (str): Task id. + name (Optional[str]): New name. + task_type (Optional[str]): New task type. + folder_id (Optional[str]): New folder id. + label (Optional[Union[str, None]]): New label. + assignees (Optional[str]): New assignees. + attrib (Optional[dict[str, Any]]): New attributes. + data (Optional[dict[str, Any]]): New data. + tags (Optional[Iterable[str]]): New tags. + status (Optional[str]): New status. + active (Optional[bool]): New active state. + thumbnail_id (Optional[Union[str, None]]): New thumbnail id. + + Returns: + UpdateOperation: Object of update operation. + + """ + update_data = {} + for key, value in ( + ("name", name), + ("taskType", task_type), + ("folderId", folder_id), + ("assignees", assignees), + ("attrib", attrib), + ("data", data), + ("tags", tags), + ("status", status), + ("active", active), + ): + if value is not None: + update_data[key] = value + + for key, value in ( + ("label", label), + ("thumbnailId", thumbnail_id), + ): + if value is not NOT_SET: + update_data[key] = value + + return self.update_entity( + project_name, "task", task_id, update_data + )
+ +
[docs] def delete_task(self, project_name, task_id): + """Delete task. + + Args: + project_name (str): Project name. + task_id (str): Task id to delete. + + Returns: + DeleteOperation: Object of delete operation. + + """ + return self.delete_entity(project_name, "task", task_id)
+ +
[docs] def create_product( + self, + project_name, + name, + product_type, + folder_id, + attrib=None, + data=None, + tags=None, + status=None, + active=None, + product_id=None, + ): + """Create new product. + + Args: + project_name (str): Project name. + name (str): Product name. + product_type (str): Product type. + folder_id (str): Parent folder id. + attrib (Optional[dict[str, Any]]): Product attributes. + data (Optional[dict[str, Any]]): Product data. + tags (Optional[Iterable[str]]): Product tags. + status (Optional[str]): Product status. + active (Optional[bool]): Product active state. + product_id (Optional[str]): Product id. If not passed new id is + generated. + + Returns: + CreateOperation: Object of create operation. + + """ + if not product_id: + product_id = create_entity_id() + create_data = { + "id": product_id, + "name": name, + "productType": product_type, + "folderId": folder_id, + } + for key, value in ( + ("attrib", attrib), + ("data", data), + ("tags", tags), + ("status", status), + ("active", active), + ): + if value is not None: + create_data[key] = value + + return self.create_entity( + project_name, "product", create_data + )
+ +
[docs] def update_product( + self, + project_name, + product_id, + name=None, + folder_id=None, + product_type=None, + attrib=None, + data=None, + tags=None, + status=None, + active=None, + ): + """Update product entity on server. + + Update of ``data`` will override existing value on folder entity. + + Update of ``attrib`` does change only passed attributes. If you want + to unset value, use ``None``. + + Args: + project_name (str): Project name. + product_id (str): Product id. + name (Optional[str]): New product name. + folder_id (Optional[str]): New product id. + product_type (Optional[str]): New product type. + attrib (Optional[dict[str, Any]]): New product attributes. + data (Optional[dict[str, Any]]): New product data. + tags (Optional[Iterable[str]]): New product tags. + status (Optional[str]): New product status. + active (Optional[bool]): New product active state. + + Returns: + UpdateOperation: Object of update operation. + + """ + update_data = {} + for key, value in ( + ("name", name), + ("productType", product_type), + ("folderId", folder_id), + ("attrib", attrib), + ("data", data), + ("tags", tags), + ("status", status), + ("active", active), + ): + if value is not None: + update_data[key] = value + + return self.update_entity( + project_name, "product", update_data + )
+ +
[docs] def delete_product(self, project_name, product_id): + """Delete product. + + Args: + project_name (str): Project name. + product_id (str): Product id to delete. + + Returns: + DeleteOperation: Object of delete operation. + + """ + return self.delete_entity( + project_name, "product", product_id + )
+ +
[docs] def create_version( + self, + project_name, + version, + product_id, + task_id=None, + author=None, + attrib=None, + data=None, + tags=None, + status=None, + active=None, + thumbnail_id=None, + version_id=None, + ): + """Create new version. + + Args: + project_name (str): Project name. + version (int): Version. + product_id (str): Parent product id. + task_id (Optional[str]): Parent task id. + author (Optional[str]): Version author. + attrib (Optional[dict[str, Any]]): Version attributes. + data (Optional[dict[str, Any]]): Version data. + tags (Optional[Iterable[str]]): Version tags. + status (Optional[str]): Version status. + active (Optional[bool]): Version active state. + thumbnail_id (Optional[str]): Version thumbnail id. + version_id (Optional[str]): Version id. If not passed new id is + generated. + + Returns: + CreateOperation: Object of create operation. + + """ + if not version_id: + version_id = create_entity_id() + create_data = { + "id": version_id, + "version": version, + "productId": product_id, + } + for key, value in ( + ("taskId", task_id), + ("author", author), + ("attrib", attrib), + ("data", data), + ("tags", tags), + ("status", status), + ("active", active), + ("thumbnailId", thumbnail_id), + ): + if value is not None: + create_data[key] = value + + return self.create_entity( + project_name, "version", create_data + )
+ +
[docs] def update_version( + self, + project_name, + version_id, + version=None, + product_id=None, + task_id=NOT_SET, + attrib=None, + data=None, + tags=None, + status=None, + active=None, + thumbnail_id=NOT_SET, + ): + """Update version entity on server. + + Do not pass ``task_id`` amd ``thumbnail_id`` if you don't + want to change their values. Value ``None`` would unset + their value. + + Update of ``data`` will override existing value on folder entity. + + Update of ``attrib`` does change only passed attributes. If you want + to unset value, use ``None``. + + Args: + project_name (str): Project name. + version_id (str): Version id. + version (Optional[int]): New version. + product_id (Optional[str]): New product id. + task_id (Optional[Union[str, None]]): New task id. + attrib (Optional[dict[str, Any]]): New attributes. + data (Optional[dict[str, Any]]): New data. + tags (Optional[Iterable[str]]): New tags. + status (Optional[str]): New status. + active (Optional[bool]): New active state. + thumbnail_id (Optional[Union[str, None]]): New thumbnail id. + + Returns: + UpdateOperation: Object of update operation. + + """ + update_data = {} + for key, value in ( + ("version", version), + ("productId", product_id), + ("taskId", task_id), + ("attrib", attrib), + ("data", data), + ("tags", tags), + ("status", status), + ("active", active), + ): + if value is not None: + update_data[key] = value + + for key, value in ( + ("taskId", task_id), + ("thumbnailId", thumbnail_id), + ): + if value is not NOT_SET: + update_data[key] = value + + return self.update_entity( + project_name, "version", version_id, update_data + )
+ +
[docs] def delete_version(self, project_name, version_id): + """Delete version. + + Args: + project_name (str): Project name. + version_id (str): Version id to delete. + + Returns: + DeleteOperation: Object of delete operation. + + """ + return self.delete_entity( + project_name, "version", version_id + )
+ +
[docs] def create_representation( + self, + project_name, + name, + version_id, + files=None, + attrib=None, + data=None, + tags=None, + status=None, + active=None, + representation_id=None, + ): + """Create new representation. + + Args: + project_name (str): Project name. + name (str): Representation name. + version_id (str): Parent version id. + files (Optional[list[dict]]): Representation files information. + attrib (Optional[dict[str, Any]]): Representation attributes. + data (Optional[dict[str, Any]]): Representation data. + tags (Optional[Iterable[str]]): Representation tags. + status (Optional[str]): Representation status. + active (Optional[bool]): Representation active state. + representation_id (Optional[str]): Representation id. If not + passed new id is generated. + + Returns: + CreateOperation: Object of create operation. + + """ + if not representation_id: + representation_id = create_entity_id() + create_data = { + "id": representation_id, + "name": name, + "versionId": version_id, + } + for key, value in ( + ("files", files), + ("attrib", attrib), + ("data", data), + ("tags", tags), + ("status", status), + ("active", active), + ): + if value is not None: + create_data[key] = value + + return self.create_entity( + project_name, + "representation", + create_data + )
+ +
[docs] def update_representation( + self, + project_name, + representation_id, + name=None, + version_id=None, + files=None, + attrib=None, + data=None, + tags=None, + status=None, + active=None, + ): + """Update representation entity on server. + + Update of ``data`` will override existing value on folder entity. + + Update of ``attrib`` does change only passed attributes. If you want + to unset value, use ``None``. + + Args: + project_name (str): Project name. + representation_id (str): Representation id. + name (Optional[str]): New name. + version_id (Optional[str]): New version id. + files (Optional[list[dict]]): New files + information. + attrib (Optional[dict[str, Any]]): New attributes. + data (Optional[dict[str, Any]]): New data. + tags (Optional[Iterable[str]]): New tags. + status (Optional[str]): New status. + active (Optional[bool]): New active state. + + Returns: + UpdateOperation: Object of update operation. + + """ + update_data = {} + for key, value in ( + ("name", name), + ("versionId", version_id), + ("files", files), + ("attrib", attrib), + ("data", data), + ("tags", tags), + ("status", status), + ("active", active), + ): + if value is not None: + update_data[key] = value + + return self.update_entity( + project_name, "representation", update_data + )
+ +
[docs] def delete_representation(self, project_name, representation_id): + """Delete representation. + + Args: + project_name (str): Project name. + representation_id (str): Representation id to delete. + + Returns: + DeleteOperation: Object of delete operation. + + """ + return self.delete_entity( + project_name, "representaion", representation_id + )
+
+ +
+ +
+
+ + +
+

+ + © Copyright 2024, ynput.io +
+ + + + Built with Sphinx using a + + theme + + provided by Revitron. + +

+
+ +
+ +
+
+ +
+ +
+ + + + + + + + + + + + \ No newline at end of file diff --git a/_modules/ayon_api/server_api.html b/_modules/ayon_api/server_api.html new file mode 100644 index 0000000000..eb6657fd65 --- /dev/null +++ b/_modules/ayon_api/server_api.html @@ -0,0 +1,8939 @@ + + + + + + + + + + + ayon_api.server_api — ayon-python-api 1.0.11 documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + +
+ + + + + +
+ +
+ +
+ + + +
+
+
+ +

Source code for ayon_api.server_api

+"""Server API.
+
+Provides access to server API.
+
+"""
+import os
+import re
+import io
+import json
+import time
+import logging
+import collections
+import platform
+import copy
+import uuid
+import warnings
+import itertools
+from contextlib import contextmanager
+import typing
+from typing import Optional, Iterable, Generator, Dict, List, Any
+
+try:
+    from http import HTTPStatus
+except ImportError:
+    HTTPStatus = None
+
+import requests
+try:
+    # This should be used if 'requests' have it available
+    from requests.exceptions import JSONDecodeError as RequestsJSONDecodeError
+except ImportError:
+    # Older versions of 'requests' don't have custom exception for json
+    #   decode error
+    try:
+        from simplejson import JSONDecodeError as RequestsJSONDecodeError
+    except ImportError:
+        from json import JSONDecodeError as RequestsJSONDecodeError
+
+from .constants import (
+    SERVER_RETRIES_ENV_KEY,
+    DEFAULT_FOLDER_TYPE_FIELDS,
+    DEFAULT_TASK_TYPE_FIELDS,
+    DEFAULT_PRODUCT_TYPE_FIELDS,
+    DEFAULT_PROJECT_FIELDS,
+    DEFAULT_FOLDER_FIELDS,
+    DEFAULT_TASK_FIELDS,
+    DEFAULT_PRODUCT_FIELDS,
+    DEFAULT_VERSION_FIELDS,
+    DEFAULT_REPRESENTATION_FIELDS,
+    REPRESENTATION_FILES_FIELDS,
+    DEFAULT_WORKFILE_INFO_FIELDS,
+    DEFAULT_EVENT_FIELDS,
+    DEFAULT_ACTIVITY_FIELDS,
+    DEFAULT_USER_FIELDS,
+    DEFAULT_LINK_FIELDS,
+)
+from .graphql import GraphQlQuery, INTROSPECTION_QUERY
+from .graphql_queries import (
+    project_graphql_query,
+    projects_graphql_query,
+    project_product_types_query,
+    product_types_query,
+    folders_graphql_query,
+    tasks_graphql_query,
+    tasks_by_folder_paths_graphql_query,
+    products_graphql_query,
+    versions_graphql_query,
+    representations_graphql_query,
+    representations_hierarchy_qraphql_query,
+    workfiles_info_graphql_query,
+    events_graphql_query,
+    users_graphql_query,
+    activities_graphql_query,
+)
+from .exceptions import (
+    FailedOperations,
+    UnauthorizedError,
+    AuthenticationError,
+    ServerNotReached,
+    ServerError,
+    HTTPRequestError,
+    UnsupportedServerVersion,
+)
+from .utils import (
+    RepresentationParents,
+    RepresentationHierarchy,
+    prepare_query_string,
+    logout_from_server,
+    create_entity_id,
+    entity_data_json_default,
+    failed_json_default,
+    TransferProgress,
+    ThumbnailContent,
+    get_default_timeout,
+    get_default_settings_variant,
+    get_default_site_id,
+    NOT_SET,
+    get_media_mime_type,
+    SortOrder,
+)
+
+if typing.TYPE_CHECKING:
+    from ._typing import ActivityType, ActivityReferenceType
+
+PatternType = type(re.compile(""))
+JSONDecodeError = getattr(json, "JSONDecodeError", ValueError)
+# This should be collected from server schema
+PROJECT_NAME_ALLOWED_SYMBOLS = "a-zA-Z0-9_"
+PROJECT_NAME_REGEX = re.compile(
+    "^[{}]+$".format(PROJECT_NAME_ALLOWED_SYMBOLS)
+)
+_PLACEHOLDER = object()
+
+VERSION_REGEX = re.compile(
+    r"(?P<major>0|[1-9]\d*)"
+    r"\.(?P<minor>0|[1-9]\d*)"
+    r"\.(?P<patch>0|[1-9]\d*)"
+    r"(?:-(?P<prerelease>[a-zA-Z\d\-.]*))?"
+    r"(?:\+(?P<buildmetadata>[a-zA-Z\d\-.]*))?"
+)
+
+
+def _convert_list_filter_value(value):
+    if value is None:
+        return None
+
+    if isinstance(value, PatternType):
+        return [value.pattern]
+
+    if isinstance(value, (int, float, str, bool)):
+        return [value]
+    return list(set(value))
+
+
+def _prepare_list_filters(output, *args, **kwargs):
+    for key, value in itertools.chain(args, kwargs.items()):
+        value = _convert_list_filter_value(value)
+        if value is None:
+            continue
+        if not value:
+            return False
+        output[key] = value
+    return True
+
+
+def _get_description(response):
+    if HTTPStatus is None:
+        return str(response.orig_response)
+    return HTTPStatus(response.status).description
+
+
+
[docs]class RequestType: + def __init__(self, name): + self.name = name + + def __hash__(self): + return self.name.__hash__()
+ + +
[docs]class RequestTypes: + get = RequestType("GET") + post = RequestType("POST") + put = RequestType("PUT") + patch = RequestType("PATCH") + delete = RequestType("DELETE")
+ + +
[docs]class RestApiResponse(object): + """API Response.""" + + def __init__(self, response, data=None): + if response is None: + status_code = 500 + else: + status_code = response.status_code + self._response = response + self.status = status_code + self._data = data + + @property + def text(self): + if self._response is None: + return self.detail + return self._response.text + + @property + def orig_response(self): + return self._response + + @property + def headers(self): + if self._response is None: + return {} + return self._response.headers + + @property + def data(self): + if self._data is None: + try: + self._data = self.orig_response.json() + except RequestsJSONDecodeError: + self._data = {} + return self._data + + @property + def content(self): + if self._response is None: + return b"" + return self._response.content + + @property + def content_type(self): + return self.headers.get("Content-Type") + + @property + def detail(self): + detail = self.get("detail") + if detail: + return detail + return _get_description(self) + + @property + def status_code(self): + return self.status + +
[docs] def raise_for_status(self, message=None): + if self._response is None: + if self._data and self._data.get("detail"): + raise ServerError(self._data["detail"]) + raise ValueError("Response is not available.") + + if self.status_code == 401: + raise UnauthorizedError("Missing or invalid authentication token") + try: + self._response.raise_for_status() + except requests.exceptions.HTTPError as exc: + if message is None: + message = str(exc) + raise HTTPRequestError(message, exc.response)
+ + def __enter__(self, *args, **kwargs): + return self._response.__enter__(*args, **kwargs) + + def __contains__(self, key): + return key in self.data + + def __repr__(self): + return "<{} [{}]>".format(self.__class__.__name__, self.status) + + def __len__(self): + return int(200 <= self.status < 400) + + def __bool__(self): + return 200 <= self.status < 400 + + def __getitem__(self, key): + return self.data[key] + +
[docs] def get(self, key, default=None): + data = self.data + if isinstance(data, dict): + return self.data.get(key, default) + return default
+ + +
[docs]class GraphQlResponse: + """GraphQl response.""" + + def __init__(self, data): + self.data = data + self.errors = data.get("errors") + + def __len__(self): + if self.errors: + return 0 + return 1 + + def __repr__(self): + if self.errors: + return "<{} errors={}>".format( + self.__class__.__name__, self.errors[0]['message'] + ) + return "<{}>".format(self.__class__.__name__)
+ + +
[docs]def fill_own_attribs(entity): + if not entity or not entity.get("attrib"): + return + + attributes = set(entity["ownAttrib"]) + + own_attrib = {} + entity["ownAttrib"] = own_attrib + + for key, value in entity["attrib"].items(): + if key not in attributes: + own_attrib[key] = None + else: + own_attrib[key] = copy.deepcopy(value)
+ + +class _AsUserStack: + """Handle stack of users used over server api connection in service mode. + + ServerAPI can behave as other users if it is using special API key. + + Examples: + >>> stack = _AsUserStack() + >>> stack.set_default_username("DefaultName") + >>> print(stack.username) + DefaultName + >>> with stack.as_user("Other1"): + ... print(stack.username) + ... with stack.as_user("Other2"): + ... print(stack.username) + ... print(stack.username) + ... stack.clear() + ... print(stack.username) + Other1 + Other2 + Other1 + None + >>> print(stack.username) + None + >>> stack.set_default_username("DefaultName") + >>> print(stack.username) + DefaultName + + """ + def __init__(self): + self._users_by_id = {} + self._user_ids = [] + self._last_user = None + self._default_user = None + + def clear(self): + self._users_by_id = {} + self._user_ids = [] + self._last_user = None + self._default_user = None + + @property + def username(self): + # Use '_user_ids' for boolean check to have ability "unset" + # default user + if self._user_ids: + return self._last_user + return self._default_user + + def get_default_username(self): + return self._default_user + + def set_default_username(self, username=None): + self._default_user = username + + default_username = property(get_default_username, set_default_username) + + @contextmanager + def as_user(self, username): + self._last_user = username + user_id = uuid.uuid4().hex + self._user_ids.append(user_id) + self._users_by_id[user_id] = username + try: + yield + finally: + self._users_by_id.pop(user_id, None) + if not self._user_ids: + return + + # First check if is the user id the last one + was_last = self._user_ids[-1] == user_id + # Remove id from variables + if user_id in self._user_ids: + self._user_ids.remove(user_id) + + if not was_last: + return + + new_last_user = None + if self._user_ids: + new_last_user = self._users_by_id.get(self._user_ids[-1]) + self._last_user = new_last_user + + +
[docs]class ServerAPI(object): + """Base handler of connection to server. + + Requires url to server which is used as base for api and graphql calls. + + Login cause that a session is used + + Args: + base_url (str): Example: http://localhost:5000 + token (Optional[str]): Access token (api key) to server. + site_id (Optional[str]): Unique name of site. Should be the same when + connection is created from the same machine under same user. + client_version (Optional[str]): Version of client application (used in + desktop client application). + default_settings_variant (Optional[Literal["production", "staging"]]): + Settings variant used by default if a method for settings won't + get any (by default is 'production'). + sender_type (Optional[str]): Sender type of requests. Used in server + logs and propagated into events. + sender (Optional[str]): Sender of requests, more specific than + sender type (e.g. machine name). Used in server logs and + propagated into events. + ssl_verify (Union[bool, str, None]): Verify SSL certificate + Looks for env variable value ``AYON_CA_FILE`` by default. If not + available then 'True' is used. + cert (Optional[str]): Path to certificate file. Looks for env + variable value ``AYON_CERT_FILE`` by default. + create_session (Optional[bool]): Create session for connection if + token is available. Default is True. + timeout (Optional[float]): Timeout for requests. + max_retries (Optional[int]): Number of retries for requests. + + """ + _default_max_retries = 3 + # 1 MB chunk by default + # TODO find out if these are reasonable default value + default_download_chunk_size = 1024 * 1024 + default_upload_chunk_size = 1024 * 1024 + + def __init__( + self, + base_url, + token=None, + site_id=NOT_SET, + client_version=None, + default_settings_variant=None, + sender_type=None, + sender=None, + ssl_verify=None, + cert=None, + create_session=True, + timeout=None, + max_retries=None, + ): + if not base_url: + raise ValueError("Invalid server URL {}".format(str(base_url))) + + base_url = base_url.rstrip("/") + self._base_url = base_url + self._rest_url = "{}/api".format(base_url) + self._graphql_url = "{}/graphql".format(base_url) + self._log = None + self._access_token = token + # Allow to have 'site_id' to 'None' + if site_id is NOT_SET: + site_id = get_default_site_id() + self._site_id = site_id + self._client_version = client_version + self._default_settings_variant = ( + default_settings_variant + or get_default_settings_variant() + ) + self._sender = sender + self._sender_type = sender_type + + self._timeout = None + self._max_retries = None + + # Set timeout and max retries based on passed values + self.set_timeout(timeout) + self.set_max_retries(max_retries) + + if ssl_verify is None: + # Custom AYON env variable for CA file or 'True' + # - that should cover most default behaviors in 'requests' + # with 'certifi' + ssl_verify = os.environ.get("AYON_CA_FILE") or True + + if cert is None: + cert = os.environ.get("AYON_CERT_FILE") + + self._ssl_verify = ssl_verify + self._cert = cert + + self._access_token_is_service = None + self._token_is_valid = None + self._token_validation_started = False + self._server_available = None + self._server_version = None + self._server_version_tuple = None + + self._graphql_allows_data_in_query = None + + self._session = None + + self._base_functions_mapping = { + RequestTypes.get: requests.get, + RequestTypes.post: requests.post, + RequestTypes.put: requests.put, + RequestTypes.patch: requests.patch, + RequestTypes.delete: requests.delete + } + self._session_functions_mapping = {} + + # Attributes cache + self._attributes_schema = None + self._entity_type_attributes_cache = {} + + self._as_user_stack = _AsUserStack() + + # Create session + if self._access_token and create_session: + self.validate_server_availability() + self.create_session() + + @property + def log(self): + if self._log is None: + self._log = logging.getLogger(self.__class__.__name__) + return self._log + +
[docs] def get_base_url(self): + return self._base_url
+ +
[docs] def get_rest_url(self): + return self._rest_url
+ + base_url = property(get_base_url) + rest_url = property(get_rest_url) + +
[docs] def get_ssl_verify(self): + """Enable ssl verification. + + Returns: + bool: Current state of ssl verification. + + """ + return self._ssl_verify
+ +
[docs] def set_ssl_verify(self, ssl_verify): + """Change ssl verification state. + + Args: + ssl_verify (Union[bool, str, None]): Enabled/disable + ssl verification, can be a path to file. + + """ + if self._ssl_verify == ssl_verify: + return + self._ssl_verify = ssl_verify + if self._session is not None: + self._session.verify = ssl_verify
+ +
[docs] def get_cert(self): + """Current cert file used for connection to server. + + Returns: + Union[str, None]: Path to cert file. + + """ + return self._cert
+ +
[docs] def set_cert(self, cert): + """Change cert file used for connection to server. + + Args: + cert (Union[str, None]): Path to cert file. + + """ + if cert == self._cert: + return + self._cert = cert + if self._session is not None: + self._session.cert = cert
+ + ssl_verify = property(get_ssl_verify, set_ssl_verify) + cert = property(get_cert, set_cert) + +
[docs] @classmethod + def get_default_timeout(cls): + """Default value for requests timeout. + + Utils function 'get_default_timeout' is used by default. + + Returns: + float: Timeout value in seconds. + + """ + return get_default_timeout()
+ +
[docs] @classmethod + def get_default_max_retries(cls): + """Default value for requests max retries. + + First looks for environment variable SERVER_RETRIES_ENV_KEY, which + can affect max retries value. If not available then use class + attribute '_default_max_retries'. + + Returns: + int: Max retries value. + + """ + try: + return int(os.environ.get(SERVER_RETRIES_ENV_KEY)) + except (ValueError, TypeError): + pass + + return cls._default_max_retries
+ +
[docs] def get_timeout(self): + """Current value for requests timeout. + + Returns: + float: Timeout value in seconds. + + """ + return self._timeout
+ +
[docs] def set_timeout(self, timeout): + """Change timeout value for requests. + + Args: + timeout (Union[float, None]): Timeout value in seconds. + + """ + if timeout is None: + timeout = self.get_default_timeout() + self._timeout = float(timeout)
+ +
[docs] def get_max_retries(self): + """Current value for requests max retries. + + Returns: + int: Max retries value. + + """ + return self._max_retries
+ +
[docs] def set_max_retries(self, max_retries): + """Change max retries value for requests. + + Args: + max_retries (Union[int, None]): Max retries value. + + """ + if max_retries is None: + max_retries = self.get_default_max_retries() + self._max_retries = int(max_retries)
+ + timeout = property(get_timeout, set_timeout) + max_retries = property(get_max_retries, set_max_retries) + + @property + def access_token(self): + """Access token used for authorization to server. + + Returns: + Union[str, None]: Token string or None if not authorized yet. + + """ + return self._access_token + +
[docs] def is_service_user(self): + """Check if connection is using service API key. + + Returns: + bool: Used api key belongs to service user. + + """ + if not self.has_valid_token: + raise ValueError("User is not logged in.") + return bool(self._access_token_is_service)
+ +
[docs] def get_site_id(self): + """Site id used for connection. + + Site id tells server from which machine/site is connection created and + is used for default site overrides when settings are received. + + Returns: + Union[str, None]: Site id value or None if not filled. + + """ + return self._site_id
+ +
[docs] def set_site_id(self, site_id): + """Change site id of connection. + + Behave as specific site for server. It affects default behavior of + settings getter methods. + + Args: + site_id (Union[str, None]): Site id value, or 'None' to unset. + + """ + if self._site_id == site_id: + return + self._site_id = site_id + # Recreate session on machine id change + self._update_session_headers()
+ + site_id = property(get_site_id, set_site_id) + +
[docs] def get_client_version(self): + """Version of client used to connect to server. + + Client version is AYON client build desktop application. + + Returns: + str: Client version string used in connection. + + """ + return self._client_version
+ +
[docs] def set_client_version(self, client_version): + """Set version of client used to connect to server. + + Client version is AYON client build desktop application. + + Args: + client_version (Union[str, None]): Client version string. + + """ + if self._client_version == client_version: + return + + self._client_version = client_version + self._update_session_headers()
+ + client_version = property(get_client_version, set_client_version) + +
[docs] def get_default_settings_variant(self): + """Default variant used for settings. + + Returns: + Union[str, None]: name of variant or None. + + """ + return self._default_settings_variant
+ +
[docs] def set_default_settings_variant(self, variant): + """Change default variant for addon settings. + + Note: + It is recommended to set only 'production' or 'staging' variants + as default variant. + + Args: + variant (str): Settings variant name. It is possible to use + 'production', 'staging' or name of dev bundle. + + """ + self._default_settings_variant = variant
+ + default_settings_variant = property( + get_default_settings_variant, + set_default_settings_variant + ) + +
[docs] def get_sender(self): + """Sender used to send requests. + + Returns: + Union[str, None]: Sender name or None. + + """ + return self._sender
+ +
[docs] def set_sender(self, sender): + """Change sender used for requests. + + Args: + sender (Union[str, None]): Sender name or None. + + """ + if sender == self._sender: + return + self._sender = sender + self._update_session_headers()
+ + sender = property(get_sender, set_sender) + +
[docs] def get_sender_type(self): + """Sender type used to send requests. + + Sender type is supported since AYON server 1.5.5 . + + Returns: + Union[str, None]: Sender type or None. + + """ + return self._sender_type
+ +
[docs] def set_sender_type(self, sender_type): + """Change sender type used for requests. + + Args: + sender_type (Union[str, None]): Sender type or None. + + """ + if sender_type == self._sender_type: + return + self._sender_type = sender_type + self._update_session_headers()
+ + sender_type = property(get_sender_type, set_sender_type) + +
[docs] def get_default_service_username(self): + """Default username used for callbacks when used with service API key. + + Returns: + Union[str, None]: Username if any was filled. + + """ + return self._as_user_stack.get_default_username()
+ +
[docs] def set_default_service_username(self, username=None): + """Service API will work as other user. + + Service API keys can work as other user. It can be temporary using + context manager 'as_user' or it is possible to set default username if + 'as_user' context manager is not entered. + + Args: + username (Optional[str]): Username to work as when service. + + Raises: + ValueError: When connection is not yet authenticated or api key + is not service token. + + """ + current_username = self._as_user_stack.get_default_username() + if current_username == username: + return + + if not self.has_valid_token: + raise ValueError( + "Authentication of connection did not happen yet." + ) + + if not self._access_token_is_service: + raise ValueError( + "Can't set service username. API key is not a service token." + ) + + self._as_user_stack.set_default_username(username) + if self._as_user_stack.username == username: + self._update_session_headers()
+ +
[docs] @contextmanager + def as_username(self, username, ignore_service_error=False): + """Service API will temporarily work as other user. + + This method can be used only if service API key is logged in. + + Args: + username (Union[str, None]): Username to work as when service. + ignore_service_error (Optional[bool]): Ignore error when service + API key is not used. + + Raises: + ValueError: When connection is not yet authenticated or api key + is not service token. + + """ + if not self.has_valid_token: + raise ValueError( + "Authentication of connection did not happen yet." + ) + + if not self._access_token_is_service: + if ignore_service_error: + yield None + return + raise ValueError( + "Can't set service username. API key is not a service token." + ) + + try: + with self._as_user_stack.as_user(username) as o: + self._update_session_headers() + yield o + finally: + self._update_session_headers()
+ + @property + def is_server_available(self): + if self._server_available is None: + response = requests.get( + self._base_url, + cert=self._cert, + verify=self._ssl_verify + ) + self._server_available = response.status_code == 200 + return self._server_available + + @property + def has_valid_token(self): + if self._access_token is None: + return False + + if self._token_is_valid is None: + self.validate_token() + return self._token_is_valid + +
[docs] def validate_server_availability(self): + if not self.is_server_available: + raise ServerNotReached("Server \"{}\" can't be reached".format( + self._base_url + ))
+ +
[docs] def validate_token(self): + try: + self._token_validation_started = True + # TODO add other possible validations + # - existence of 'user' key in info + # - validate that 'site_id' is in 'sites' in info + self.get_info() + self.get_user() + self._token_is_valid = True + + except UnauthorizedError: + self._token_is_valid = False + + finally: + self._token_validation_started = False + return self._token_is_valid
+ +
[docs] def set_token(self, token): + self.reset_token() + self._access_token = token + self.get_user()
+ +
[docs] def reset_token(self): + self._access_token = None + self._token_is_valid = None + self.close_session()
+ +
[docs] def create_session(self, ignore_existing=True, force=False): + """Create a connection session. + + Session helps to keep connection with server without + need to reconnect on each call. + + Args: + ignore_existing (bool): If session already exists, + ignore creation. + force (bool): If session already exists, close it and + create new. + + """ + if force and self._session is not None: + self.close_session() + + if self._session is not None: + if ignore_existing: + return + raise ValueError("Session is already created.") + + self._as_user_stack.clear() + # Validate token before session creation + self.validate_token() + + session = requests.Session() + session.cert = self._cert + session.verify = self._ssl_verify + session.headers.update(self.get_headers()) + + self._session_functions_mapping = { + RequestTypes.get: session.get, + RequestTypes.post: session.post, + RequestTypes.put: session.put, + RequestTypes.patch: session.patch, + RequestTypes.delete: session.delete + } + self._session = session
+ +
[docs] def close_session(self): + if self._session is None: + return + + session = self._session + self._session = None + self._session_functions_mapping = {} + session.close()
+ + def _update_session_headers(self): + if self._session is None: + return + + # Header keys that may change over time + for key, value in ( + ("X-as-user", self._as_user_stack.username), + ("x-ayon-version", self._client_version), + ("x-ayon-site-id", self._site_id), + ("x-sender-type", self._sender_type), + ("x-sender", self._sender), + ): + if value is not None: + self._session.headers[key] = value + elif key in self._session.headers: + self._session.headers.pop(key) + +
[docs] def get_info(self): + """Get information about current used api key. + + By default, the 'info' contains only 'uptime' and 'version'. With + logged user info also contains information about user and machines on + which was logged in. + + Todos: + Use this method for validation of token instead of 'get_user'. + + Returns: + dict[str, Any]: Information from server. + + """ + response = self.get("info") + response.raise_for_status() + return response.data
+ +
[docs] def get_server_version(self): + """Get server version. + + Version should match semantic version (https://semver.org/). + + Returns: + str: Server version. + + """ + if self._server_version is None: + self._server_version = self.get_info()["version"] + return self._server_version
+ +
[docs] def get_server_version_tuple(self): + """Get server version as tuple. + + Version should match semantic version (https://semver.org/). + + This function only returns first three numbers of version. + + Returns: + Tuple[int, int, int, Union[str, None], Union[str, None]]: Server + version. + + """ + if self._server_version_tuple is None: + re_match = VERSION_REGEX.fullmatch( + self.get_server_version()) + self._server_version_tuple = ( + int(re_match.group("major")), + int(re_match.group("minor")), + int(re_match.group("patch")), + re_match.group("prerelease") or "", + re_match.group("buildmetadata") or "", + ) + return self._server_version_tuple
+ + server_version = property(get_server_version) + server_version_tuple = property(get_server_version_tuple) + + @property + def graphql_allows_data_in_query(self): + """GraphQl query can support 'data' field. + + This applies only to project hierarchy entities 'project', 'folder', + 'task', 'product', 'version' and 'representation'. Others like 'user' + still require to use rest api to access 'data'. + + Returns: + bool: True if server supports 'data' field in GraphQl query. + + """ + if self._graphql_allows_data_in_query is None: + major, minor, patch, _, _ = self.server_version_tuple + graphql_allows_data_in_query = True + if (major, minor, patch) < (0, 5, 5): + graphql_allows_data_in_query = False + self._graphql_allows_data_in_query = graphql_allows_data_in_query + return self._graphql_allows_data_in_query + + def _get_user_info(self): + if self._access_token is None: + return None + + if self._access_token_is_service is not None: + response = self.get("users/me") + if response.status == 200: + return response.data + return None + + self._access_token_is_service = False + response = self.get("users/me") + if response.status == 200: + return response.data + + self._access_token_is_service = True + response = self.get("users/me") + if response.status == 200: + return response.data + + self._access_token_is_service = None + return None + +
[docs] def get_users(self, project_name=None, usernames=None, fields=None): + """Get Users. + + Only administrators and managers can fetch all users. For other users + it is required to pass in 'project_name' filter. + + Args: + project_name (Optional[str]): Project name. + usernames (Optional[Iterable[str]]): Filter by usernames. + fields (Optional[Iterable[str]]): Fields to be queried + for users. + + Returns: + Generator[dict[str, Any]]: Queried users. + + """ + filters = {} + if usernames is not None: + usernames = set(usernames) + if not usernames: + return + filters["userNames"] = list(usernames) + + if project_name is not None: + filters["projectName"] = project_name + + if not fields: + fields = self.get_default_fields_for_type("user") + + query = users_graphql_query(set(fields)) + for attr, filter_value in filters.items(): + query.set_variable_value(attr, filter_value) + + for parsed_data in query.continuous_query(self): + for user in parsed_data["users"]: + user["accessGroups"] = json.loads( + user["accessGroups"]) + yield user
+ +
[docs] def get_user_by_name(self, username, project_name=None, fields=None): + """Get user by name using GraphQl. + + Only administrators and managers can fetch all users. For other users + it is required to pass in 'project_name' filter. + + Args: + username (str): Username. + project_name (Optional[str]): Define scope of project. + fields (Optional[Iterable[str]]): Fields to be queried + for users. + + Returns: + Union[dict[str, Any], None]: User info or None if user is not + found. + + """ + if not username: + return None + + for user in self.get_users( + project_name=project_name, + usernames={username}, + fields=fields, + ): + return user + return None
+ +
[docs] def get_user(self, username=None): + """Get user info using REST endpoit. + + Args: + username (Optional[str]): Username. + + Returns: + Union[dict[str, Any], None]: User info or None if user is not + found. + + """ + if username is None: + output = self._get_user_info() + if output is None: + raise UnauthorizedError("User is not authorized.") + return output + + response = self.get("users/{}".format(username)) + response.raise_for_status() + return response.data
+ +
[docs] def get_headers(self, content_type=None): + if content_type is None: + content_type = "application/json" + + headers = { + "Content-Type": content_type, + "x-ayon-platform": platform.system().lower(), + "x-ayon-hostname": platform.node(), + } + if self._site_id is not None: + headers["x-ayon-site-id"] = self._site_id + + if self._client_version is not None: + headers["x-ayon-version"] = self._client_version + + if self._sender_type is not None: + headers["x-sender-type"] = self._sender_type + + if self._sender is not None: + headers["x-sender"] = self._sender + + if self._access_token: + if self._access_token_is_service: + headers["X-Api-Key"] = self._access_token + username = self._as_user_stack.username + if username: + headers["X-as-user"] = username + else: + headers["Authorization"] = "Bearer {}".format( + self._access_token) + return headers
+ +
[docs] def login(self, username, password, create_session=True): + """Login to server. + + Args: + username (str): Username. + password (str): Password. + create_session (Optional[bool]): Create session after login. + Default: True. + + Raises: + AuthenticationError: Login failed. + + """ + if self.has_valid_token: + try: + user_info = self.get_user() + except UnauthorizedError: + user_info = {} + + current_username = user_info.get("name") + if current_username == username: + self.close_session() + if create_session: + self.create_session() + return + + self.reset_token() + + self.validate_server_availability() + + self._token_validation_started = True + + try: + response = self.post( + "auth/login", + name=username, + password=password + ) + if response.status_code != 200: + _detail = response.data.get("detail") + details = "" + if _detail: + details = " {}".format(_detail) + + raise AuthenticationError("Login failed {}".format(details)) + + finally: + self._token_validation_started = False + + self._access_token = response["token"] + + if not self.has_valid_token: + raise AuthenticationError("Invalid credentials") + + if create_session: + self.create_session()
+ +
[docs] def logout(self, soft=False): + if self._access_token: + if not soft: + self._logout() + self.reset_token()
+ + def _logout(self): + logout_from_server(self._base_url, self._access_token) + + def _do_rest_request(self, function, url, **kwargs): + kwargs.setdefault("timeout", self.timeout) + max_retries = kwargs.get("max_retries", self.max_retries) + if max_retries < 1: + max_retries = 1 + if self._session is None: + # Validate token if was not yet validated + # - ignore validation if we're in middle of + # validation + if ( + self._token_is_valid is None + and not self._token_validation_started + ): + self.validate_token() + + if "headers" not in kwargs: + kwargs["headers"] = self.get_headers() + + if isinstance(function, RequestType): + function = self._base_functions_mapping[function] + + elif isinstance(function, RequestType): + function = self._session_functions_mapping[function] + + response = None + new_response = None + for retry_idx in reversed(range(max_retries)): + try: + response = function(url, **kwargs) + break + + except ConnectionRefusedError: + if retry_idx == 0: + self.log.warning( + "Connection error happened.", exc_info=True + ) + + # Server may be restarting + new_response = RestApiResponse( + None, + { + "detail": ( + "Unable to connect the server. Connection refused" + ) + } + ) + + except requests.exceptions.Timeout: + # Connection timed out + new_response = RestApiResponse( + None, + {"detail": "Connection timed out."} + ) + + except requests.exceptions.ConnectionError: + # Log warning only on last attempt + if retry_idx == 0: + self.log.warning( + "Connection error happened.", exc_info=True + ) + + new_response = RestApiResponse( + None, + { + "detail": ( + "Unable to connect the server. Connection error" + ) + } + ) + + time.sleep(0.1) + + if new_response is not None: + return new_response + + content_type = response.headers.get("Content-Type") + if content_type == "application/json": + try: + new_response = RestApiResponse(response) + except JSONDecodeError: + new_response = RestApiResponse( + None, + { + "detail": "The response is not a JSON: {}".format( + response.text) + } + ) + + else: + new_response = RestApiResponse(response) + + self.log.debug("Response {}".format(str(new_response))) + return new_response + +
[docs] def raw_post(self, entrypoint, **kwargs): + url = self._endpoint_to_url(entrypoint) + self.log.debug("Executing [POST] {}".format(url)) + return self._do_rest_request( + RequestTypes.post, + url, + **kwargs + )
+ +
[docs] def raw_put(self, entrypoint, **kwargs): + url = self._endpoint_to_url(entrypoint) + self.log.debug("Executing [PUT] {}".format(url)) + return self._do_rest_request( + RequestTypes.put, + url, + **kwargs + )
+ +
[docs] def raw_patch(self, entrypoint, **kwargs): + url = self._endpoint_to_url(entrypoint) + self.log.debug("Executing [PATCH] {}".format(url)) + return self._do_rest_request( + RequestTypes.patch, + url, + **kwargs + )
+ +
[docs] def raw_get(self, entrypoint, **kwargs): + url = self._endpoint_to_url(entrypoint) + self.log.debug("Executing [GET] {}".format(url)) + return self._do_rest_request( + RequestTypes.get, + url, + **kwargs + )
+ +
[docs] def raw_delete(self, entrypoint, **kwargs): + url = self._endpoint_to_url(entrypoint) + self.log.debug("Executing [DELETE] {}".format(url)) + return self._do_rest_request( + RequestTypes.delete, + url, + **kwargs + )
+ +
[docs] def post(self, entrypoint, **kwargs): + return self.raw_post(entrypoint, json=kwargs)
+ +
[docs] def put(self, entrypoint, **kwargs): + return self.raw_put(entrypoint, json=kwargs)
+ +
[docs] def patch(self, entrypoint, **kwargs): + return self.raw_patch(entrypoint, json=kwargs)
+ +
[docs] def get(self, entrypoint, **kwargs): + return self.raw_get(entrypoint, params=kwargs)
+ +
[docs] def delete(self, entrypoint, **kwargs): + return self.raw_delete(entrypoint, params=kwargs)
+ +
[docs] def get_event(self, event_id): + """Query full event data by id. + + Events received using event server do not contain full information. To + get the full event information is required to receive it explicitly. + + Args: + event_id (str): Event id. + + Returns: + dict[str, Any]: Full event data. + + """ + response = self.get("events/{}".format(event_id)) + response.raise_for_status() + return response.data
+ +
[docs] def get_events( + self, + topics: Optional[Iterable[str]] = None, + event_ids: Optional[Iterable[str]] = None, + project_names: Optional[Iterable[str]] = None, + statuses: Optional[Iterable[str]] = None, + users: Optional[Iterable[str]] = None, + include_logs: Optional[bool] = None, + has_children: Optional[bool] = None, + newer_than: Optional[str] = None, + older_than: Optional[str] = None, + fields: Optional[Iterable[str]] = None, + limit: Optional[int] = None, + order: Optional[SortOrder] = None, + states: Optional[Iterable[str]] = None, + ): + """Get events from server with filtering options. + + Notes: + Not all event happen on a project. + + Args: + topics (Optional[Iterable[str]]): Name of topics. + event_ids (Optional[Iterable[str]]): Event ids. + project_names (Optional[Iterable[str]]): Project on which + event happened. + statuses (Optional[Iterable[str]]): Filtering by statuses. + users (Optional[Iterable[str]]): Filtering by users + who created/triggered an event. + include_logs (Optional[bool]): Query also log events. + has_children (Optional[bool]): Event is with/without children + events. If 'None' then all events are returned, default. + newer_than (Optional[str]): Return only events newer than given + iso datetime string. + older_than (Optional[str]): Return only events older than given + iso datetime string. + fields (Optional[Iterable[str]]): Fields that should be received + for each event. + limit (Optional[int]): Limit number of events to be fetched. + order (Optional[SortOrder]): Order events in ascending + or descending order. It is recommended to set 'limit' + when used descending. + states (Optional[Iterable[str]]): DEPRECATED Filtering by states. + Use 'statuses' instead. + + Returns: + Generator[dict[str, Any]]: Available events matching filters. + + """ + if statuses is None and states is not None: + warnings.warn( + ( + "Used deprecated argument 'states' in 'get_events'." + " Use 'statuses' instead." + ), + DeprecationWarning + ) + statuses = states + + + filters = {} + if not _prepare_list_filters( + filters, + ("eventTopics", topics), + ("eventIds", event_ids), + ("projectNames", project_names), + ("eventStatuses", statuses), + ("eventUsers", users), + ): + return + + if include_logs is None: + include_logs = False + + for filter_key, filter_value in ( + ("includeLogsFilter", include_logs), + ("hasChildrenFilter", has_children), + ("newerThanFilter", newer_than), + ("olderThanFilter", older_than), + ): + if filter_value is not None: + filters[filter_key] = filter_value + + if not fields: + fields = self.get_default_fields_for_type("event") + + major, minor, patch, _, _ = self.server_version_tuple + use_states = (major, minor, patch) <= (1, 5, 6) + + query = events_graphql_query(set(fields), order, use_states) + for attr, filter_value in filters.items(): + query.set_variable_value(attr, filter_value) + + if limit: + events_field = query.get_field_by_path("events") + events_field.set_limit(limit) + + for parsed_data in query.continuous_query(self): + for event in parsed_data["events"]: + yield event
+ +
[docs] def update_event( + self, + event_id, + sender=None, + project_name=None, + username=None, + status=None, + description=None, + summary=None, + payload=None, + progress=None, + retries=None + ): + """Update event data. + + Args: + event_id (str): Event id. + sender (Optional[str]): New sender of event. + project_name (Optional[str]): New project name. + username (Optional[str]): New username. + status (Optional[str]): New event status. Enum: "pending", + "in_progress", "finished", "failed", "aborted", "restarted" + description (Optional[str]): New description. + summary (Optional[dict[str, Any]]): New summary. + payload (Optional[dict[str, Any]]): New payload. + progress (Optional[int]): New progress. Range [0-100]. + retries (Optional[int]): New retries. + + """ + kwargs = { + key: value + for key, value in ( + ("sender", sender), + ("project", project_name), + ("user", username), + ("status", status), + ("description", description), + ("summary", summary), + ("payload", payload), + ("progress", progress), + ("retries", retries), + ) + if value is not None + } + # 'progress' and 'retries' are available since 0.5.x server version + major, minor, _, _, _ = self.server_version_tuple + if (major, minor) < (0, 5): + args = [] + if progress is not None: + args.append("progress") + if retries is not None: + args.append("retries") + fields = ", ".join("'{}'".format(f) for f in args) + ending = "s" if len(args) > 1 else "" + raise ValueError(( + "Your server version '{}' does not support update" + " of {} field{} on event. The fields are supported since" + " server version '0.5'." + ).format(self.get_server_version(), fields, ending)) + + response = self.patch( + "events/{}".format(event_id), + **kwargs + ) + response.raise_for_status()
+ +
[docs] def dispatch_event( + self, + topic, + sender=None, + event_hash=None, + project_name=None, + username=None, + depends_on=None, + description=None, + summary=None, + payload=None, + finished=True, + store=True, + dependencies=None, + ): + """Dispatch event to server. + + Args: + topic (str): Event topic used for filtering of listeners. + sender (Optional[str]): Sender of event. + event_hash (Optional[str]): Event hash. + project_name (Optional[str]): Project name. + depends_on (Optional[str]): Add dependency to another event. + username (Optional[str]): Username which triggered event. + description (Optional[str]): Description of event. + summary (Optional[dict[str, Any]]): Summary of event that can + be used for simple filtering on listeners. + payload (Optional[dict[str, Any]]): Full payload of event data with + all details. + finished (Optional[bool]): Mark event as finished on dispatch. + store (Optional[bool]): Store event in event queue for possible + future processing otherwise is event send only + to active listeners. + dependencies (Optional[list[str]]): Deprecated. + List of event id dependencies. + + Returns: + RestApiResponse: Response from server. + + """ + if summary is None: + summary = {} + if payload is None: + payload = {} + event_data = { + "topic": topic, + "sender": sender, + "hash": event_hash, + "project": project_name, + "user": username, + "description": description, + "summary": summary, + "payload": payload, + "finished": finished, + "store": store, + } + if depends_on: + event_data["dependsOn"] = depends_on + + if dependencies: + warnings.warn( + ( + "Used deprecated argument 'dependencies' in" + " 'dispatch_event'. Use 'depends_on' instead." + ), + DeprecationWarning + ) + + response = self.post("events", **event_data) + response.raise_for_status() + return response
+ +
[docs] def delete_event(self, event_id: str): + """Delete event by id. + + Supported since AYON server 1.6.0. + + Args: + event_id (str): Event id. + + Returns: + RestApiResponse: Response from server. + + """ + response = self.delete(f"events/{event_id}") + response.raise_for_status() + return response
+ + +
[docs] def enroll_event_job( + self, + source_topic, + target_topic, + sender, + description=None, + sequential=None, + events_filter=None, + max_retries=None, + ignore_older_than=None, + ignore_sender_types=None, + ): + """Enroll job based on events. + + Enroll will find first unprocessed event with 'source_topic' and will + create new event with 'target_topic' for it and return the new event + data. + + Use 'sequential' to control that only single target event is created + at same time. Creation of new target events is blocked while there is + at least one unfinished event with target topic, when set to 'True'. + This helps when order of events matter and more than one process using + the same target is running at the same time. + + Make sure the new event has updated status to '"finished"' status + when you're done with logic + + Target topic should not clash with other processes/services. + + Created target event have 'dependsOn' key where is id of source topic. + + Use-case: + - Service 1 is creating events with topic 'my.leech' + - Service 2 process 'my.leech' and uses target topic 'my.process' + - this service can run on 1-n machines + - all events must be processed in a sequence by their creation + time and only one event can be processed at a time + - in this case 'sequential' should be set to 'True' so only + one machine is actually processing events, but if one goes + down there are other that can take place + - Service 3 process 'my.leech' and uses target topic 'my.discover' + - this service can run on 1-n machines + - order of events is not important + - 'sequential' should be 'False' + + Args: + source_topic (str): Source topic to enroll. + target_topic (str): Topic of dependent event. + sender (str): Identifier of sender (e.g. service name or username). + description (Optional[str]): Human readable text shown + in target event. + sequential (Optional[bool]): The source topic must be processed + in sequence. + events_filter (Optional[dict[str, Any]]): Filtering conditions + to filter the source event. For more technical specifications + look to server backed 'ayon_server.sqlfilter.Filter'. + TODO: Add example of filters. + max_retries (Optional[int]): How many times can be event retried. + Default value is based on server (3 at the time of this PR). + ignore_older_than (Optional[int]): Ignore events older than + given number in days. + ignore_sender_types (Optional[List[str]]): Ignore events triggered + by given sender types. + + Returns: + Union[None, dict[str, Any]]: None if there is no event matching + filters. Created event with 'target_topic'. + + """ + kwargs = { + "sourceTopic": source_topic, + "targetTopic": target_topic, + "sender": sender, + } + major, minor, patch, _, _ = self.server_version_tuple + if max_retries is not None: + kwargs["maxRetries"] = max_retries + if sequential is not None: + kwargs["sequential"] = sequential + if description is not None: + kwargs["description"] = description + if events_filter is not None: + kwargs["filter"] = events_filter + if ( + ignore_older_than is not None + and (major, minor, patch) > (1, 5, 1) + ): + kwargs["ignoreOlderThan"] = ignore_older_than + if ignore_sender_types is not None: + if (major, minor, patch) <= (1, 5, 4): + raise ValueError( + "Ignore sender types are not supported for" + f" your version of server {self.server_version}." + ) + kwargs["ignoreSenderTypes"] = list(ignore_sender_types) + + response = self.post("enroll", **kwargs) + if response.status_code == 204: + return None + + if response.status_code == 503: + # Server is busy + self.log.info("Server is busy. Can't enroll event now.") + return None + + if response.status_code >= 400: + self.log.error(response.text) + return None + + return response.data
+ +
[docs] def get_activities( + self, + project_name: str, + activity_ids: Optional[Iterable[str]] = None, + activity_types: Optional[Iterable["ActivityType"]] = None, + entity_ids: Optional[Iterable[str]] = None, + entity_names: Optional[Iterable[str]] = None, + entity_type: Optional[str] = None, + changed_after: Optional[str] = None, + changed_before: Optional[str] = None, + reference_types: Optional[Iterable["ActivityReferenceType"]] = None, + fields: Optional[Iterable[str]] = None, + limit: Optional[int] = None, + order: Optional[SortOrder] = None, + ) -> Generator[Dict[str, Any], None, None]: + """Get activities from server with filtering options. + + Args: + project_name (str): Project on which activities happened. + activity_ids (Optional[Iterable[str]]): Activity ids. + activity_types (Optional[Iterable[ActivityType]]): Activity types. + entity_ids (Optional[Iterable[str]]): Entity ids. + entity_names (Optional[Iterable[str]]): Entity names. + entity_type (Optional[str]): Entity type. + changed_after (Optional[str]): Return only activities changed + after given iso datetime string. + changed_before (Optional[str]): Return only activities changed + before given iso datetime string. + reference_types (Optional[Iterable[ActivityReferenceType]]): + Reference types filter. Defaults to `['origin']`. + fields (Optional[Iterable[str]]): Fields that should be received + for each activity. + limit (Optional[int]): Limit number of activities to be fetched. + order (Optional[SortOrder]): Order activities in ascending + or descending order. It is recommended to set 'limit' + when used descending. + + Returns: + Generator[dict[str, Any]]: Available activities matching filters. + + """ + if not project_name: + return + filters = { + "projectName": project_name, + } + if reference_types is None: + reference_types = {"origin"} + + if not _prepare_list_filters( + filters, + ("activityIds", activity_ids), + ("activityTypes", activity_types), + ("entityIds", entity_ids), + ("entityNames", entity_names), + ("referenceTypes", reference_types), + ): + return + + for filter_key, filter_value in ( + ("entityType", entity_type), + ("changedAfter", changed_after), + ("changedBefore", changed_before), + ): + if filter_value is not None: + filters[filter_key] = filter_value + + if not fields: + fields = self.get_default_fields_for_type("activity") + + query = activities_graphql_query(set(fields), order) + for attr, filter_value in filters.items(): + query.set_variable_value(attr, filter_value) + + if limit: + activities_field = query.get_field_by_path("activities") + activities_field.set_limit(limit) + + for parsed_data in query.continuous_query(self): + for activity in parsed_data["project"]["activities"]: + activity_data = activity.get("activityData") + if isinstance(activity_data, str): + activity["activityData"] = json.loads(activity_data) + yield activity
+ +
[docs] def get_activity_by_id( + self, + project_name: str, + activity_id: str, + reference_types: Optional[Iterable["ActivityReferenceType"]] = None, + fields: Optional[Iterable[str]] = None, + ) -> Optional[Dict[str, Any]]: + """Get activity by id. + + Args: + project_name (str): Project on which activity happened. + activity_id (str): Activity id. + fields (Optional[Iterable[str]]): Fields that should be received + for each activity. + + Returns: + Optional[Dict[str, Any]]: Activity data or None if activity is not + found. + + """ + for activity in self.get_activities( + project_name=project_name, + activity_ids={activity_id}, + reference_types=reference_types, + fields=fields, + ): + return activity + return None
+ +
[docs] def create_activity( + self, + project_name: str, + entity_id: str, + entity_type: str, + activity_type: "ActivityType", + activity_id: Optional[str] = None, + body: Optional[str] = None, + file_ids: Optional[List[str]] = None, + timestamp: Optional[str] = None, + data: Optional[Dict[str, Any]] = None, + ) -> str: + """Create activity on a project. + + Args: + project_name (str): Project on which activity happened. + entity_id (str): Entity id. + entity_type (str): Entity type. + activity_type (ActivityType): Activity type. + activity_id (Optional[str]): Activity id. + body (Optional[str]): Activity body. + file_ids (Optional[List[str]]): List of file ids attached + to activity. + timestamp (Optional[str]): Activity timestamp. + data (Optional[Dict[str, Any]]): Additional data. + + Returns: + str: Activity id. + + """ + post_data = { + "activityType": activity_type, + } + for key, value in ( + ("id", activity_id), + ("body", body), + ("files", file_ids), + ("timestamp", timestamp), + ("data", data), + ): + if value is not None: + post_data[key] = value + + response = self.post( + f"projects/{project_name}/{entity_type}/{entity_id}/activities", + **post_data + ) + response.raise_for_status() + return response.data["id"]
+ +
[docs] def update_activity( + self, + project_name: str, + activity_id: str, + body: Optional[str] = None, + file_ids: Optional[List[str]] = None, + append_file_ids: Optional[bool] = False, + data: Optional[Dict[str, Any]] = None, + ): + """Update activity by id. + + Args: + project_name (str): Project on which activity happened. + activity_id (str): Activity id. + body (str): Activity body. + file_ids (Optional[List[str]]): List of file ids attached + to activity. + append_file_ids (Optional[bool]): Append file ids to existing + list of file ids. + data (Optional[Dict[str, Any]]): Update data in activity. + + """ + update_data = {} + major, minor, patch, _, _ = self.server_version_tuple + new_patch_model = (major, minor, patch) > (1, 5, 6) + if body is None and not new_patch_model: + raise ValueError( + "Update without 'body' is supported" + " after server version 1.5.6." + ) + + if body is not None: + update_data["body"] = body + + if file_ids is not None: + update_data["files"] = file_ids + if new_patch_model: + update_data["appendFiles"] = append_file_ids + elif append_file_ids: + raise ValueError( + "Append file ids is supported after server version 1.5.6." + ) + + if data is not None: + if not new_patch_model: + raise ValueError( + "Update of data is supported after server version 1.5.6." + ) + update_data["data"] = data + + response = self.patch( + f"projects/{project_name}/activities/{activity_id}", + **update_data + ) + response.raise_for_status()
+ +
[docs] def delete_activity(self, project_name: str, activity_id: str): + """Delete activity by id. + + Args: + project_name (str): Project on which activity happened. + activity_id (str): Activity id to remove. + + """ + response = self.delete( + f"projects/{project_name}/activities/{activity_id}" + ) + response.raise_for_status()
+ + def _endpoint_to_url( + self, + endpoint: str, + use_rest: Optional[bool] = True + ): + """Cleanup endpoint and return full url to AYON server. + + If endpoint already starts with server url only slashes are removed. + + Args: + endpoint (str): Endpoint to be cleaned. + use_rest (Optional[bool]): Use only base server url if set to + False, otherwise REST endpoint is used. + + Returns: + str: Full url to AYON server. + + """ + endpoint = endpoint.lstrip("/").rstrip("/") + if endpoint.startswith(self._base_url): + return endpoint + base_url = self._rest_url if use_rest else self._graphql_url + return f"{base_url}/{endpoint}" + + def _download_file_to_stream(self, url, stream, chunk_size, progress): + kwargs = {"stream": True} + if self._session is None: + kwargs["headers"] = self.get_headers() + get_func = self._base_functions_mapping[RequestTypes.get] + else: + get_func = self._session_functions_mapping[RequestTypes.get] + + with get_func(url, **kwargs) as response: + response.raise_for_status() + progress.set_content_size(response.headers["Content-length"]) + for chunk in response.iter_content(chunk_size=chunk_size): + stream.write(chunk) + progress.add_transferred_chunk(len(chunk)) + +
[docs] def download_file_to_stream( + self, endpoint, stream, chunk_size=None, progress=None + ): + """Download file from AYON server to IOStream. + + Endpoint can be full url (must start with 'base_url' of api object). + + Progress object can be used to track download. Can be used when + download happens in thread and other thread want to catch changes over + time. + + Todos: + Use retries and timeout. + Return RestApiResponse. + + Args: + endpoint (str): Endpoint or URL to file that should be downloaded. + stream (Union[io.BytesIO, BinaryIO]): Stream where output will + be stored. + chunk_size (Optional[int]): Size of chunks that are received + in single loop. + progress (Optional[TransferProgress]): Object that gives ability + to track download progress. + + """ + if not chunk_size: + chunk_size = self.default_download_chunk_size + + url = self._endpoint_to_url(endpoint) + + if progress is None: + progress = TransferProgress() + + progress.set_source_url(url) + progress.set_started() + + try: + self._download_file_to_stream( + url, stream, chunk_size, progress + ) + + except Exception as exc: + progress.set_failed(str(exc)) + raise + + finally: + progress.set_transfer_done() + return progress
+ +
[docs] def download_file( + self, endpoint, filepath, chunk_size=None, progress=None + ): + """Download file from AYON server. + + Endpoint can be full url (must start with 'base_url' of api object). + + Progress object can be used to track download. Can be used when + download happens in thread and other thread want to catch changes over + time. + + Todos: + Use retries and timeout. + Return RestApiResponse. + + Args: + endpoint (str): Endpoint or URL to file that should be downloaded. + filepath (str): Path where file will be downloaded. + chunk_size (Optional[int]): Size of chunks that are received + in single loop. + progress (Optional[TransferProgress]): Object that gives ability + to track download progress. + + """ + # Create dummy object so the function does not have to check + # 'progress' variable everywhere + if progress is None: + progress = TransferProgress() + + progress.set_destination_url(filepath) + + dst_directory = os.path.dirname(filepath) + os.makedirs(dst_directory, exist_ok=True) + + try: + with open(filepath, "wb") as stream: + self.download_file_to_stream( + endpoint, stream, chunk_size, progress + ) + + except Exception as exc: + progress.set_failed(str(exc)) + raise + + return progress
+ + @staticmethod + def _upload_chunks_iter(file_stream, progress, chunk_size): + """Generator that yields chunks of file. + + Args: + file_stream (Union[io.BytesIO, BinaryIO]): Byte stream. + progress (TransferProgress): Object to track upload progress. + chunk_size (int): Size of chunks that are uploaded at once. + + Yields: + bytes: Chunk of file. + + """ + # Get size of file + file_stream.seek(0, io.SEEK_END) + size = file_stream.tell() + file_stream.seek(0) + # Set content size to progress object + progress.set_content_size(size) + + while True: + chunk = file_stream.read(chunk_size) + if not chunk: + break + progress.add_transferred_chunk(len(chunk)) + yield chunk + + def _upload_file( + self, + url, + stream, + progress, + request_type=None, + chunk_size=None, + **kwargs + ): + """Upload file to server. + + Args: + url (str): Url where file will be uploaded. + stream (Union[io.BytesIO, BinaryIO]): File stream. + progress (TransferProgress): Object that gives ability to track + progress. + request_type (Optional[RequestType]): Type of request that will + be used. Default is PUT. + chunk_size (Optional[int]): Size of chunks that are uploaded + at once. + **kwargs (Any): Additional arguments that will be passed + to request function. + + Returns: + RestApiResponse: Server response. + + """ + if request_type is None: + request_type = RequestTypes.put + + if self._session is None: + headers = kwargs.setdefault("headers", {}) + for key, value in self.get_headers().items(): + if key not in headers: + headers[key] = value + post_func = self._base_functions_mapping[request_type] + else: + post_func = self._session_functions_mapping[request_type] + + if not chunk_size: + chunk_size = self.default_upload_chunk_size + + response = post_func( + url, + data=self._upload_chunks_iter(stream, progress, chunk_size), + **kwargs + ) + + response.raise_for_status() + return response + +
[docs] def upload_file_from_stream( + self, endpoint, stream, progress=None, request_type=None, **kwargs + ): + """Upload file to server from bytes. + + Todos: + Use retries and timeout. + Return RestApiResponse. + + Args: + endpoint (str): Endpoint or url where file will be uploaded. + stream (Union[io.BytesIO, BinaryIO]): File content stream. + progress (Optional[TransferProgress]): Object that gives ability + to track upload progress. + request_type (Optional[RequestType]): Type of request that will + be used to upload file. + **kwargs (Any): Additional arguments that will be passed + to request function. + + Returns: + requests.Response: Response object + + """ + url = self._endpoint_to_url(endpoint) + + # Create dummy object so the function does not have to check + # 'progress' variable everywhere + if progress is None: + progress = TransferProgress() + + progress.set_destination_url(url) + progress.set_started() + + try: + return self._upload_file( + url, stream, progress, request_type, **kwargs + ) + + except Exception as exc: + progress.set_failed(str(exc)) + raise + + finally: + progress.set_transfer_done()
+ +
[docs] def upload_file( + self, endpoint, filepath, progress=None, request_type=None, **kwargs + ): + """Upload file to server. + + Todos: + Use retries and timeout. + Return RestApiResponse. + + Args: + endpoint (str): Endpoint or url where file will be uploaded. + filepath (str): Source filepath. + progress (Optional[TransferProgress]): Object that gives ability + to track upload progress. + request_type (Optional[RequestType]): Type of request that will + be used to upload file. + **kwargs (Any): Additional arguments that will be passed + to request function. + + Returns: + requests.Response: Response object + + """ + if progress is None: + progress = TransferProgress() + + progress.set_source_url(filepath) + + with open(filepath, "rb") as stream: + return self.upload_file_from_stream( + endpoint, stream, progress, request_type, **kwargs + )
+ +
[docs] def upload_reviewable( + self, + project_name, + version_id, + filepath, + label=None, + content_type=None, + filename=None, + progress=None, + headers=None, + **kwargs + ): + """Upload reviewable file to server. + + Args: + project_name (str): Project name. + version_id (str): Version id. + filepath (str): Reviewable file path to upload. + label (Optional[str]): Reviewable label. Filled automatically + server side with filename. + content_type (Optional[str]): MIME type of the file. + filename (Optional[str]): User as original filename. Filename from + 'filepath' is used when not filled. + progress (Optional[TransferProgress]): Progress. + headers (Optional[Dict[str, Any]]): Headers. + + Returns: + RestApiResponse: Server response. + + """ + if not content_type: + content_type = get_media_mime_type(filepath) + + if not content_type: + raise ValueError( + f"Could not determine MIME type of file '{filepath}'" + ) + + if headers is None: + headers = self.get_headers(content_type) + else: + # Make sure content-type is filled with file content type + content_type_key = next( + ( + key + for key in headers + if key.lower() == "content-type" + ), + "Content-Type" + ) + headers[content_type_key] = content_type + + # Fill original filename if not explicitly defined + if not filename: + filename = os.path.basename(filepath) + headers["x-file-name"] = filename + + query = f"?label={label}" if label else "" + endpoint = ( + f"/projects/{project_name}" + f"/versions/{version_id}/reviewables{query}" + ) + return self.upload_file( + endpoint, + filepath, + progress=progress, + headers=headers, + request_type=RequestTypes.post, + **kwargs + )
+ +
[docs] def trigger_server_restart(self): + """Trigger server restart. + + Restart may be required when a change of specific value happened on + server. + + """ + result = self.post("system/restart") + if result.status_code != 204: + # TODO add better exception + raise ValueError("Failed to restart server")
+ +
[docs] def query_graphql(self, query, variables=None): + """Execute GraphQl query. + + Args: + query (str): GraphQl query string. + variables (Optional[dict[str, Any]): Variables that can be + used in query. + + Returns: + GraphQlResponse: Response from server. + + """ + data = {"query": query, "variables": variables or {}} + response = self._do_rest_request( + RequestTypes.post, + self._graphql_url, + json=data + ) + response.raise_for_status() + return GraphQlResponse(response)
+ +
[docs] def get_graphql_schema(self): + return self.query_graphql(INTROSPECTION_QUERY).data
+ +
[docs] def get_server_schema(self): + """Get server schema with info, url paths, components etc. + + Todos: + Cache schema - How to find out it is outdated? + + Returns: + dict[str, Any]: Full server schema. + + """ + url = "{}/openapi.json".format(self._base_url) + response = self._do_rest_request(RequestTypes.get, url) + if response: + return response.data + return None
+ +
[docs] def get_schemas(self): + """Get components schema. + + Name of components does not match entity type names e.g. 'project' is + under 'ProjectModel'. We should find out some mapping. Also, there + are properties which don't have information about reference to object + e.g. 'config' has just object definition without reference schema. + + Returns: + dict[str, Any]: Component schemas. + + """ + server_schema = self.get_server_schema() + return server_schema["components"]["schemas"]
+ +
[docs] def get_attributes_schema(self, use_cache=True): + if not use_cache: + self.reset_attributes_schema() + + if self._attributes_schema is None: + result = self.get("attributes") + result.raise_for_status() + self._attributes_schema = result.data + return copy.deepcopy(self._attributes_schema)
+ +
[docs] def reset_attributes_schema(self): + self._attributes_schema = None + self._entity_type_attributes_cache = {}
+ +
[docs] def set_attribute_config( + self, attribute_name, data, scope, position=None, builtin=False + ): + if position is None: + attributes = self.get("attributes").data["attributes"] + origin_attr = next( + ( + attr for attr in attributes + if attr["name"] == attribute_name + ), + None + ) + if origin_attr: + position = origin_attr["position"] + else: + position = len(attributes) + + response = self.put( + "attributes/{}".format(attribute_name), + data=data, + scope=scope, + position=position, + builtin=builtin + ) + if response.status_code != 204: + # TODO raise different exception + raise ValueError( + "Attribute \"{}\" was not created/updated. {}".format( + attribute_name, response.detail + ) + ) + + self.reset_attributes_schema()
+ +
[docs] def remove_attribute_config(self, attribute_name): + """Remove attribute from server. + + This can't be un-done, please use carefully. + + Args: + attribute_name (str): Name of attribute to remove. + + """ + response = self.delete("attributes/{}".format(attribute_name)) + response.raise_for_status( + "Attribute \"{}\" was not created/updated. {}".format( + attribute_name, response.detail + ) + ) + + self.reset_attributes_schema()
+ +
[docs] def get_attributes_for_type(self, entity_type): + """Get attribute schemas available for an entity type. + + Example:: + + ``` + # Example attribute schema + { + # Common + "type": "integer", + "title": "Clip Out", + "description": null, + "example": 1, + "default": 1, + # These can be filled based on value of 'type' + "gt": null, + "ge": null, + "lt": null, + "le": null, + "minLength": null, + "maxLength": null, + "minItems": null, + "maxItems": null, + "regex": null, + "enum": null + } + ``` + + Args: + entity_type (str): Entity type for which should be attributes + received. + + Returns: + dict[str, dict[str, Any]]: Attribute schemas that are available + for entered entity type. + + """ + attributes = self._entity_type_attributes_cache.get(entity_type) + if attributes is None: + attributes_schema = self.get_attributes_schema() + attributes = {} + for attr in attributes_schema["attributes"]: + if entity_type not in attr["scope"]: + continue + attr_name = attr["name"] + attributes[attr_name] = attr["data"] + + self._entity_type_attributes_cache[entity_type] = attributes + + return copy.deepcopy(attributes)
+ +
[docs] def get_attributes_fields_for_type(self, entity_type): + """Prepare attribute fields for entity type. + + Returns: + set[str]: Attributes fields for entity type. + + """ + attributes = self.get_attributes_for_type(entity_type) + return { + "attrib.{}".format(attr) + for attr in attributes + }
+ +
[docs] def get_default_fields_for_type(self, entity_type): + """Default fields for entity type. + + Returns most of commonly used fields from server. + + Args: + entity_type (str): Name of entity type. + + Returns: + set[str]: Fields that should be queried from server. + + """ + # Event does not have attributes + if entity_type == "event": + return set(DEFAULT_EVENT_FIELDS) + + if entity_type == "activity": + return set(DEFAULT_ACTIVITY_FIELDS) + + if entity_type == "project": + entity_type_defaults = set(DEFAULT_PROJECT_FIELDS) + if not self.graphql_allows_data_in_query: + entity_type_defaults.discard("data") + + elif entity_type == "folder": + entity_type_defaults = set(DEFAULT_FOLDER_FIELDS) + if not self.graphql_allows_data_in_query: + entity_type_defaults.discard("data") + + elif entity_type == "task": + entity_type_defaults = set(DEFAULT_TASK_FIELDS) + if not self.graphql_allows_data_in_query: + entity_type_defaults.discard("data") + + elif entity_type == "product": + entity_type_defaults = set(DEFAULT_PRODUCT_FIELDS) + if not self.graphql_allows_data_in_query: + entity_type_defaults.discard("data") + + elif entity_type == "version": + entity_type_defaults = set(DEFAULT_VERSION_FIELDS) + if not self.graphql_allows_data_in_query: + entity_type_defaults.discard("data") + + elif entity_type == "representation": + entity_type_defaults = ( + DEFAULT_REPRESENTATION_FIELDS + | REPRESENTATION_FILES_FIELDS + ) + if not self.graphql_allows_data_in_query: + entity_type_defaults.discard("data") + + elif entity_type == "folderType": + entity_type_defaults = set(DEFAULT_FOLDER_TYPE_FIELDS) + + elif entity_type == "taskType": + entity_type_defaults = set(DEFAULT_TASK_TYPE_FIELDS) + + elif entity_type == "productType": + entity_type_defaults = set(DEFAULT_PRODUCT_TYPE_FIELDS) + + elif entity_type == "workfile": + entity_type_defaults = set(DEFAULT_WORKFILE_INFO_FIELDS) + if not self.graphql_allows_data_in_query: + entity_type_defaults.discard("data") + + elif entity_type == "user": + entity_type_defaults = set(DEFAULT_USER_FIELDS) + + else: + raise ValueError("Unknown entity type \"{}\"".format(entity_type)) + return ( + entity_type_defaults + | self.get_attributes_fields_for_type(entity_type) + )
+ +
[docs] def get_addons_info(self, details=True): + """Get information about addons available on server. + + Args: + details (Optional[bool]): Detailed data with information how + to get client code. + + """ + endpoint = "addons" + if details: + endpoint += "?details=1" + response = self.get(endpoint) + response.raise_for_status() + return response.data
+ +
[docs] def get_addon_endpoint(self, addon_name, addon_version, *subpaths): + """Calculate endpoint to addon route. + + Examples: + + >>> api = ServerAPI("https://your.url.com") + >>> api.get_addon_url( + ... "example", "1.0.0", "private", "my.zip") + 'addons/example/1.0.0/private/my.zip' + + Args: + addon_name (str): Name of addon. + addon_version (str): Version of addon. + *subpaths (str): Any amount of subpaths that are added to + addon url. + + Returns: + str: Final url. + + """ + ending = "" + if subpaths: + ending = "/{}".format("/".join(subpaths)) + return "addons/{}/{}{}".format( + addon_name, + addon_version, + ending + )
+ +
[docs] def get_addon_url( + self, addon_name, addon_version, *subpaths, use_rest=True + ): + """Calculate url to addon route. + + Examples: + + >>> api = ServerAPI("https://your.url.com") + >>> api.get_addon_url( + ... "example", "1.0.0", "private", "my.zip") + 'https://your.url.com/api/addons/example/1.0.0/private/my.zip' + + Args: + addon_name (str): Name of addon. + addon_version (str): Version of addon. + *subpaths (str): Any amount of subpaths that are added to + addon url. + use_rest (Optional[bool]): Use rest endpoint. + + Returns: + str: Final url. + + """ + endpoint = self.get_addon_endpoint( + addon_name, addon_version, *subpaths + ) + url_base = self._base_url if use_rest else self._rest_url + return f"{url_base}/{endpoint}"
+ +
[docs] def download_addon_private_file( + self, + addon_name, + addon_version, + filename, + destination_dir, + destination_filename=None, + chunk_size=None, + progress=None, + ): + """Download a file from addon private files. + + This method requires to have authorized token available. Private files + are not under '/api' restpoint. + + Args: + addon_name (str): Addon name. + addon_version (str): Addon version. + filename (str): Filename in private folder on server. + destination_dir (str): Where the file should be downloaded. + destination_filename (Optional[str]): Name of destination + filename. Source filename is used if not passed. + chunk_size (Optional[int]): Download chunk size. + progress (Optional[TransferProgress]): Object that gives ability + to track download progress. + + Returns: + str: Filepath to downloaded file. + + """ + if not destination_filename: + destination_filename = filename + dst_filepath = os.path.join(destination_dir, destination_filename) + # Filename can contain "subfolders" + dst_dirpath = os.path.dirname(dst_filepath) + os.makedirs(dst_dirpath, exist_ok=True) + + endpoint = self.get_addon_endpoint( + addon_name, + addon_version, + "private", + filename + ) + url = f"{self._base_url}/{endpoint}" + self.download_file( + url, dst_filepath, chunk_size=chunk_size, progress=progress + ) + return dst_filepath
+ +
[docs] def get_installers(self, version=None, platform_name=None): + """Information about desktop application installers on server. + + Desktop application installers are helpers to download/update AYON + desktop application for artists. + + Args: + version (Optional[str]): Filter installers by version. + platform_name (Optional[str]): Filter installers by platform name. + + Returns: + list[dict[str, Any]]: + + """ + query_fields = [ + "{}={}".format(key, value) + for key, value in ( + ("version", version), + ("platform", platform_name), + ) + if value + ] + query = "" + if query_fields: + query = "?{}".format(",".join(query_fields)) + + response = self.get("desktop/installers{}".format(query)) + response.raise_for_status() + return response.data
+ +
[docs] def create_installer( + self, + filename, + version, + python_version, + platform_name, + python_modules, + runtime_python_modules, + checksum, + checksum_algorithm, + file_size, + sources=None, + ): + """Create new installer information on server. + + This step will create only metadata. Make sure to upload installer + to the server using 'upload_installer' method. + + Runtime python modules are modules that are required to run AYON + desktop application, but are not added to PYTHONPATH for any + subprocess. + + Args: + filename (str): Installer filename. + version (str): Version of installer. + python_version (str): Version of Python. + platform_name (str): Name of platform. + python_modules (dict[str, str]): Python modules that are available + in installer. + runtime_python_modules (dict[str, str]): Runtime python modules + that are available in installer. + checksum (str): Installer file checksum. + checksum_algorithm (str): Type of checksum used to create checksum. + file_size (int): File size. + sources (Optional[list[dict[str, Any]]]): List of sources that + can be used to download file. + + """ + body = { + "filename": filename, + "version": version, + "pythonVersion": python_version, + "platform": platform_name, + "pythonModules": python_modules, + "runtimePythonModules": runtime_python_modules, + "checksum": checksum, + "checksumAlgorithm": checksum_algorithm, + "size": file_size, + } + if sources: + body["sources"] = sources + + response = self.post("desktop/installers", **body) + response.raise_for_status()
+ +
[docs] def update_installer(self, filename, sources): + """Update installer information on server. + + Args: + filename (str): Installer filename. + sources (list[dict[str, Any]]): List of sources that + can be used to download file. Fully replaces existing sources. + + """ + response = self.patch( + "desktop/installers/{}".format(filename), + sources=sources + ) + response.raise_for_status()
+ +
[docs] def delete_installer(self, filename): + """Delete installer from server. + + Args: + filename (str): Installer filename. + + """ + response = self.delete("desktop/installers/{}".format(filename)) + response.raise_for_status()
+ +
[docs] def download_installer( + self, + filename, + dst_filepath, + chunk_size=None, + progress=None + ): + """Download installer file from server. + + Args: + filename (str): Installer filename. + dst_filepath (str): Destination filepath. + chunk_size (Optional[int]): Download chunk size. + progress (Optional[TransferProgress]): Object that gives ability + to track download progress. + + """ + self.download_file( + "desktop/installers/{}".format(filename), + dst_filepath, + chunk_size=chunk_size, + progress=progress + )
+ +
[docs] def upload_installer(self, src_filepath, dst_filename, progress=None): + """Upload installer file to server. + + Args: + src_filepath (str): Source filepath. + dst_filename (str): Destination filename. + progress (Optional[TransferProgress]): Object that gives ability + to track download progress. + + Returns: + requests.Response: Response object. + + """ + return self.upload_file( + "desktop/installers/{}".format(dst_filename), + src_filepath, + progress=progress + )
+ + def _get_dependency_package_route(self, filename=None): + endpoint = "desktop/dependencyPackages" + if filename: + return "{}/{}".format(endpoint, filename) + return endpoint + +
[docs] def get_dependency_packages(self): + """Information about dependency packages on server. + + To download dependency package, use 'download_dependency_package' + method and pass in 'filename'. + + Example data structure:: + + { + "packages": [ + { + "filename": str, + "platform": str, + "checksum": str, + "checksumAlgorithm": str, + "size": int, + "sources": list[dict[str, Any]], + "supportedAddons": dict[str, str], + "pythonModules": dict[str, str] + } + ] + } + + Returns: + dict[str, Any]: Information about dependency packages known for + server. + + """ + endpoint = self._get_dependency_package_route() + result = self.get(endpoint) + result.raise_for_status() + return result.data
+ +
[docs] def create_dependency_package( + self, + filename, + python_modules, + source_addons, + installer_version, + checksum, + checksum_algorithm, + file_size, + sources=None, + platform_name=None, + ): + """Create dependency package on server. + + The package will be created on a server, it is also required to upload + the package archive file (using :meth:`upload_dependency_package`). + + Args: + filename (str): Filename of dependency package. + python_modules (dict[str, str]): Python modules in dependency + package:: + + {"<module name>": "<module version>", ...} + + source_addons (dict[str, str]): Name of addons for which is + dependency package created:: + + {"<addon name>": "<addon version>", ...} + + installer_version (str): Version of installer for which was + package created. + checksum (str): Checksum of archive file where dependencies are. + checksum_algorithm (str): Algorithm used to calculate checksum. + file_size (Optional[int]): Size of file. + sources (Optional[list[dict[str, Any]]]): Information about + sources from where it is possible to get file. + platform_name (Optional[str]): Name of platform for which is + dependency package targeted. Default value is + current platform. + + """ + post_body = { + "filename": filename, + "pythonModules": python_modules, + "sourceAddons": source_addons, + "installerVersion": installer_version, + "checksum": checksum, + "checksumAlgorithm": checksum_algorithm, + "size": file_size, + "platform": platform_name or platform.system().lower(), + } + if sources: + post_body["sources"] = sources + + route = self._get_dependency_package_route() + response = self.post(route, **post_body) + response.raise_for_status()
+ +
[docs] def update_dependency_package(self, filename, sources): + """Update dependency package metadata on server. + + Args: + filename (str): Filename of dependency package. + sources (list[dict[str, Any]]): Information about + sources from where it is possible to get file. Fully replaces + existing sources. + + """ + response = self.patch( + self._get_dependency_package_route(filename), + sources=sources + ) + response.raise_for_status()
+ +
[docs] def delete_dependency_package(self, filename, platform_name=None): + """Remove dependency package for specific platform. + + Args: + filename (str): Filename of dependency package. + platform_name (Optional[str]): Deprecated. + + """ + if platform_name is not None: + warnings.warn( + ( + "Argument 'platform_name' is deprecated in" + " 'delete_dependency_package'. The argument will be" + " removed, please modify your code accordingly." + ), + DeprecationWarning + ) + + route = self._get_dependency_package_route(filename) + response = self.delete(route) + response.raise_for_status("Failed to delete dependency file") + return response.data
+ +
[docs] def download_dependency_package( + self, + src_filename, + dst_directory, + dst_filename, + platform_name=None, + chunk_size=None, + progress=None, + ): + """Download dependency package from server. + + This method requires to have authorized token available. The package + is only downloaded. + + Args: + src_filename (str): Filename of dependency pacakge. + For server version 0.2.0 and lower it is name of package + to download. + dst_directory (str): Where the file should be downloaded. + dst_filename (str): Name of destination filename. + platform_name (Optional[str]): Deprecated. + chunk_size (Optional[int]): Download chunk size. + progress (Optional[TransferProgress]): Object that gives ability + to track download progress. + + Returns: + str: Filepath to downloaded file. + + """ + if platform_name is not None: + warnings.warn( + ( + "Argument 'platform_name' is deprecated in" + " 'download_dependency_package'. The argument will be" + " removed, please modify your code accordingly." + ), + DeprecationWarning + ) + route = self._get_dependency_package_route(src_filename) + package_filepath = os.path.join(dst_directory, dst_filename) + self.download_file( + route, + package_filepath, + chunk_size=chunk_size, + progress=progress + ) + return package_filepath
+ +
[docs] def upload_dependency_package( + self, src_filepath, dst_filename, platform_name=None, progress=None + ): + """Upload dependency package to server. + + Args: + src_filepath (str): Path to a package file. + dst_filename (str): Dependency package filename or name of package + for server version 0.2.0 or lower. Must be unique. + platform_name (Optional[str]): Deprecated. + progress (Optional[TransferProgress]): Object to keep track about + upload state. + + """ + if platform_name is not None: + warnings.warn( + ( + "Argument 'platform_name' is deprecated in" + " 'upload_dependency_package'. The argument will be" + " removed, please modify your code accordingly." + ), + DeprecationWarning + ) + + route = self._get_dependency_package_route(dst_filename) + self.upload_file(route, src_filepath, progress=progress)
+ +
[docs] def delete_addon(self, addon_name: str, purge: Optional[bool] = None): + """Delete addon from server. + + Delete all versions of addon from server. + + Args: + addon_name (str): Addon name. + purge (Optional[bool]): Purge all data related to the addon. + + """ + query_data = {} + if purge is not None: + query_data["purge"] = "true" if purge else "false" + query = prepare_query_string(query_data) + + response = self.delete(f"addons/{addon_name}{query}") + response.raise_for_status()
+ +
[docs] def delete_addon_version( + self, + addon_name: str, + addon_version: str, + purge: Optional[bool] = None, + ): + """Delete addon version from server. + + Delete all versions of addon from server. + + Args: + addon_name (str): Addon name. + addon_version (str): Addon version. + purge (Optional[bool]): Purge all data related to the addon. + + """ + query_data = {} + if purge is not None: + query_data["purge"] = "true" if purge else "false" + query = prepare_query_string(query_data) + response = self.delete(f"addons/{addon_name}/{addon_version}{query}") + response.raise_for_status()
+ +
[docs] def upload_addon_zip(self, src_filepath, progress=None): + """Upload addon zip file to server. + + File is validated on server. If it is valid, it is installed. It will + create an event job which can be tracked (tracking part is not + implemented yet). + + Example output:: + + {'eventId': 'a1bfbdee27c611eea7580242ac120003'} + + Args: + src_filepath (str): Path to a zip file. + progress (Optional[TransferProgress]): Object to keep track about + upload state. + + Returns: + dict[str, Any]: Response data from server. + + """ + response = self.upload_file( + "addons/install", + src_filepath, + progress=progress, + request_type=RequestTypes.post, + ) + return response.json()
+ +
[docs] def get_bundles(self): + """Server bundles with basic information. + + This is example output:: + + { + "bundles": [ + { + "name": "my_bundle", + "createdAt": "2023-06-12T15:37:02.420260", + "installerVersion": "1.0.0", + "addons": { + "core": "1.2.3" + }, + "dependencyPackages": { + "windows": "a_windows_package123.zip", + "linux": "a_linux_package123.zip", + "darwin": "a_mac_package123.zip" + }, + "isProduction": False, + "isStaging": False + } + ], + "productionBundle": "my_bundle", + "stagingBundle": "test_bundle" + } + + Returns: + dict[str, Any]: Server bundles with basic information. + + """ + response = self.get("bundles") + response.raise_for_status() + return response.data
+ +
[docs] def create_bundle( + self, + name, + addon_versions, + installer_version, + dependency_packages=None, + is_production=None, + is_staging=None, + is_dev=None, + dev_active_user=None, + dev_addons_config=None, + ): + """Create bundle on server. + + Bundle cannot be changed once is created. Only isProduction, isStaging + and dependency packages can change after creation. In case dev bundle + is created, it is possible to change anything, but it is not possible + to mark bundle as dev and production or staging at the same time. + + Development addon config can define custom path to client code. It is + used only for dev bundles. + + Example of 'dev_addons_config':: + + ```json + { + "core": { + "enabled": true, + "path": "/path/to/ayon-core/client" + } + } + ``` + + Args: + name (str): Name of bundle. + addon_versions (dict[str, str]): Addon versions. + installer_version (Union[str, None]): Installer version. + dependency_packages (Optional[dict[str, str]]): Dependency + package names. Keys are platform names and values are name of + packages. + is_production (Optional[bool]): Bundle will be marked as + production. + is_staging (Optional[bool]): Bundle will be marked as staging. + is_dev (Optional[bool]): Bundle will be marked as dev. + dev_active_user (Optional[str]): Username that will be assigned + to dev bundle. Can be used only if 'is_dev' is set to 'True'. + dev_addons_config (Optional[dict[str, Any]]): Configuration for + dev addons. Can be used only if 'is_dev' is set to 'True'. + + """ + body = { + "name": name, + "installerVersion": installer_version, + "addons": addon_versions, + } + + for key, value in ( + ("dependencyPackages", dependency_packages), + ("isProduction", is_production), + ("isStaging", is_staging), + ("isDev", is_dev), + ("activeUser", dev_active_user), + ("addonDevelopment", dev_addons_config), + ): + if value is not None: + body[key] = value + + response = self.post("bundles", **body) + response.raise_for_status()
+ +
[docs] def update_bundle( + self, + bundle_name, + addon_versions=None, + installer_version=None, + dependency_packages=None, + is_production=None, + is_staging=None, + is_dev=None, + dev_active_user=None, + dev_addons_config=None, + ): + """Update bundle on server. + + Dependency packages can be update only for single platform. Others + will be left untouched. Use 'None' value to unset dependency package + from bundle. + + Args: + bundle_name (str): Name of bundle. + addon_versions (Optional[dict[str, str]]): Addon versions, + possible only for dev bundles. + installer_version (Optional[str]): Installer version, possible + only for dev bundles. + dependency_packages (Optional[dict[str, str]]): Dependency pacakge + names that should be used with the bundle. + is_production (Optional[bool]): Bundle will be marked as + production. + is_staging (Optional[bool]): Bundle will be marked as staging. + is_dev (Optional[bool]): Bundle will be marked as dev. + dev_active_user (Optional[str]): Username that will be assigned + to dev bundle. Can be used only for dev bundles. + dev_addons_config (Optional[dict[str, Any]]): Configuration for + dev addons. Can be used only for dev bundles. + + """ + body = { + key: value + for key, value in ( + ("installerVersion", installer_version), + ("addons", addon_versions), + ("dependencyPackages", dependency_packages), + ("isProduction", is_production), + ("isStaging", is_staging), + ("isDev", is_dev), + ("activeUser", dev_active_user), + ("addonDevelopment", dev_addons_config), + ) + if value is not None + } + response = self.patch( + "{}/{}".format("bundles", bundle_name), + **body + ) + response.raise_for_status()
+ +
[docs] def check_bundle_compatibility( + self, + name, + addon_versions, + installer_version, + dependency_packages=None, + is_production=None, + is_staging=None, + is_dev=None, + dev_active_user=None, + dev_addons_config=None, + ): + """Check bundle compatibility. + + Can be used as per-flight validation before creating bundle. + + Args: + name (str): Name of bundle. + addon_versions (dict[str, str]): Addon versions. + installer_version (Union[str, None]): Installer version. + dependency_packages (Optional[dict[str, str]]): Dependency + package names. Keys are platform names and values are name of + packages. + is_production (Optional[bool]): Bundle will be marked as + production. + is_staging (Optional[bool]): Bundle will be marked as staging. + is_dev (Optional[bool]): Bundle will be marked as dev. + dev_active_user (Optional[str]): Username that will be assigned + to dev bundle. Can be used only if 'is_dev' is set to 'True'. + dev_addons_config (Optional[dict[str, Any]]): Configuration for + dev addons. Can be used only if 'is_dev' is set to 'True'. + + Returns: + Dict[str, Any]: Server response, with 'success' and 'issues'. + + """ + body = { + "name": name, + "installerVersion": installer_version, + "addons": addon_versions, + } + + for key, value in ( + ("dependencyPackages", dependency_packages), + ("isProduction", is_production), + ("isStaging", is_staging), + ("isDev", is_dev), + ("activeUser", dev_active_user), + ("addonDevelopment", dev_addons_config), + ): + if value is not None: + body[key] = value + + response = self.post("bundles/check", **body) + response.raise_for_status() + return response.data
+ +
[docs] def delete_bundle(self, bundle_name): + """Delete bundle from server. + + Args: + bundle_name (str): Name of bundle to delete. + + """ + response = self.delete( + "{}/{}".format("bundles", bundle_name) + ) + response.raise_for_status()
+ + # Anatomy presets +
[docs] def get_project_anatomy_presets(self): + """Anatomy presets available on server. + + Content has basic information about presets. Example output:: + + [ + { + "name": "netflix_VFX", + "primary": false, + "version": "1.0.0" + }, + { + ... + }, + ... + ] + + Returns: + list[dict[str, str]]: Anatomy presets available on server. + + """ + result = self.get("anatomy/presets") + result.raise_for_status() + return result.data.get("presets") or []
+ +
[docs] def get_default_anatomy_preset_name(self): + """Name of default anatomy preset. + + Primary preset is used as default preset. But when primary preset is + not set a built-in is used instead. Built-in preset is named '_'. + + Returns: + str: Name of preset that can be used by + 'get_project_anatomy_preset'. + + """ + for preset in self.get_project_anatomy_presets(): + if preset.get("primary"): + return preset["name"] + return "_"
+ +
[docs] def get_project_anatomy_preset(self, preset_name=None): + """Anatomy preset values by name. + + Get anatomy preset values by preset name. Primary preset is returned + if preset name is set to 'None'. + + Args: + preset_name (Optional[str]): Preset name. + + Returns: + dict[str, Any]: Anatomy preset values. + + """ + if preset_name is None: + preset_name = "__primary__" + major, minor, patch, _, _ = self.server_version_tuple + if (major, minor, patch) < (1, 0, 8): + preset_name = self.get_default_anatomy_preset_name() + + result = self.get("anatomy/presets/{}".format(preset_name)) + result.raise_for_status() + return result.data
+ +
[docs] def get_build_in_anatomy_preset(self): + """Get built-in anatomy preset. + + Returns: + dict[str, Any]: Built-in anatomy preset. + + """ + preset_name = "__builtin__" + major, minor, patch, _, _ = self.server_version_tuple + if (major, minor, patch) < (1, 0, 8): + preset_name = "_" + return self.get_project_anatomy_preset(preset_name)
+ +
[docs] def get_project_root_overrides(self, project_name): + """Root overrides per site name. + + Method is based on logged user and can't be received for any other + user on server. + + Output will contain only roots per site id used by logged user. + + Args: + project_name (str): Name of project. + + Returns: + dict[str, dict[str, str]]: Root values by root name by site id. + + """ + result = self.get("projects/{}/roots".format(project_name)) + result.raise_for_status() + return result.data
+ +
[docs] def get_project_roots_by_site(self, project_name): + """Root overrides per site name. + + Method is based on logged user and can't be received for any other + user on server. + + Output will contain only roots per site id used by logged user. + + Deprecated: + Use 'get_project_root_overrides' instead. Function + deprecated since 1.0.6 + + Args: + project_name (str): Name of project. + + Returns: + dict[str, dict[str, str]]: Root values by root name by site id. + + """ + warnings.warn( + ( + "Method 'get_project_roots_by_site' is deprecated." + " Please use 'get_project_root_overrides' instead." + ), + DeprecationWarning + ) + return self.get_project_root_overrides(project_name)
+ +
[docs] def get_project_root_overrides_by_site_id( + self, project_name, site_id=None + ): + """Root overrides for site. + + If site id is not passed a site set in current api object is used + instead. + + Args: + project_name (str): Name of project. + site_id (Optional[str]): Site id for which want to receive + site overrides. + + Returns: + dict[str, str]: Root values by root name or None if + site does not have overrides. + + """ + if site_id is None: + site_id = self.site_id + + if site_id is None: + return {} + roots = self.get_project_root_overrides(project_name) + return roots.get(site_id, {})
+ +
[docs] def get_project_roots_for_site(self, project_name, site_id=None): + """Root overrides for site. + + If site id is not passed a site set in current api object is used + instead. + + Deprecated: + Use 'get_project_root_overrides_by_site_id' instead. Function + deprecated since 1.0.6 + Args: + project_name (str): Name of project. + site_id (Optional[str]): Site id for which want to receive + site overrides. + + Returns: + dict[str, str]: Root values by root name, root name is not + available if it does not have overrides. + + """ + warnings.warn( + ( + "Method 'get_project_roots_for_site' is deprecated." + " Please use 'get_project_root_overrides_by_site_id' instead." + ), + DeprecationWarning + ) + return self.get_project_root_overrides_by_site_id(project_name)
+ + def _get_project_roots_values( + self, project_name, site_id=None, platform_name=None + ): + """Root values for site or platform. + + Helper function that treats 'siteRoots' endpoint. The endpoint + requires to pass exactly one query value of site id + or platform name. + + When using platform name, it does return default project roots without + any site overrides. + + Output should contain all project roots with all filled values. If + value does not have override on a site, it should be filled with + project default value. + + Args: + project_name (str): Project name. + site_id (Optional[str]): Site id for which want to receive + site overrides. + platform_name (Optional[str]): Platform for which want to receive + roots. + + Returns: + dict[str, str]: Root values. + + """ + query_data = {} + if site_id is not None: + query_data["site_id"] = site_id + else: + if platform_name is None: + platform_name = platform.system() + query_data["platform"] = platform_name.lower() + + query = "?{}".format(",".join([ + "{}={}".format(key, value) + for key, value in query_data.items() + ])) + response = self.get( + "projects/{}/siteRoots{}".format(project_name, query) + ) + response.raise_for_status() + return response.data + +
[docs] def get_project_roots_by_site_id(self, project_name, site_id=None): + """Root values for a site. + + If site id is not passed a site set in current api object is used + instead. If site id is not available, default roots are returned + for current platform. + + Args: + project_name (str): Name of project. + site_id (Optional[str]): Site id for which want to receive + root values. + + Returns: + dict[str, str]: Root values. + + """ + if site_id is None: + site_id = self.site_id + + return self._get_project_roots_values(project_name, site_id=site_id)
+ +
[docs] def get_project_roots_by_platform(self, project_name, platform_name=None): + """Root values for a site. + + If platform name is not passed current platform name is used instead. + + This function does return root values without site overrides. It is + possible to use the function to receive default root values. + + Args: + project_name (str): Name of project. + platform_name (Optional[Literal["windows", "linux", "darwin"]]): + Platform name for which want to receive root values. Current + platform name is used if not passed. + + Returns: + dict[str, str]: Root values. + + """ + return self._get_project_roots_values( + project_name, platform_name=platform_name + )
+ +
[docs] def get_addon_settings_schema( + self, addon_name, addon_version, project_name=None + ): + """Sudio/Project settings schema of an addon. + + Project schema may look differently as some enums are based on project + values. + + Args: + addon_name (str): Name of addon. + addon_version (str): Version of addon. + project_name (Optional[str]): Schema for specific project or + default studio schemas. + + Returns: + dict[str, Any]: Schema of studio/project settings. + + """ + args = tuple() + if project_name: + args = (project_name, ) + + endpoint = self.get_addon_endpoint( + addon_name, addon_version, "schema", *args + ) + result = self.get(endpoint) + result.raise_for_status() + return result.data
+ +
[docs] def get_addon_site_settings_schema(self, addon_name, addon_version): + """Site settings schema of an addon. + + Args: + addon_name (str): Name of addon. + addon_version (str): Version of addon. + + Returns: + dict[str, Any]: Schema of site settings. + + """ + result = self.get("addons/{}/{}/siteSettings/schema".format( + addon_name, addon_version + )) + result.raise_for_status() + return result.data
+ +
[docs] def get_addon_studio_settings( + self, + addon_name, + addon_version, + variant=None + ): + """Addon studio settings. + + Receive studio settings for specific version of an addon. + + Args: + addon_name (str): Name of addon. + addon_version (str): Version of addon. + variant (Optional[Literal['production', 'staging']]): Name of + settings variant. Used 'default_settings_variant' by default. + + Returns: + dict[str, Any]: Addon settings. + + """ + if variant is None: + variant = self.default_settings_variant + + query_items = {} + if variant: + query_items["variant"] = variant + query = prepare_query_string(query_items) + + result = self.get( + "addons/{}/{}/settings{}".format(addon_name, addon_version, query) + ) + result.raise_for_status() + return result.data
+ +
[docs] def get_addon_project_settings( + self, + addon_name, + addon_version, + project_name, + variant=None, + site_id=None, + use_site=True + ): + """Addon project settings. + + Receive project settings for specific version of an addon. The settings + may be with site overrides when enabled. + + Site id is filled with current connection site id if not passed. To + make sure any site id is used set 'use_site' to 'False'. + + Args: + addon_name (str): Name of addon. + addon_version (str): Version of addon. + project_name (str): Name of project for which the settings are + received. + variant (Optional[Literal['production', 'staging']]): Name of + settings variant. Used 'default_settings_variant' by default. + site_id (Optional[str]): Name of site which is used for site + overrides. Is filled with connection 'site_id' attribute + if not passed. + use_site (Optional[bool]): To force disable option of using site + overrides set to 'False'. In that case won't be applied + any site overrides. + + Returns: + dict[str, Any]: Addon settings. + + """ + if not use_site: + site_id = None + elif not site_id: + site_id = self.site_id + + query_items = {} + if site_id: + query_items["site"] = site_id + + if variant is None: + variant = self.default_settings_variant + + if variant: + query_items["variant"] = variant + + query = prepare_query_string(query_items) + result = self.get( + "addons/{}/{}/settings/{}{}".format( + addon_name, addon_version, project_name, query + ) + ) + result.raise_for_status() + return result.data
+ +
[docs] def get_addon_settings( + self, + addon_name, + addon_version, + project_name=None, + variant=None, + site_id=None, + use_site=True + ): + """Receive addon settings. + + Receive addon settings based on project name value. Some arguments may + be ignored if 'project_name' is set to 'None'. + + Args: + addon_name (str): Name of addon. + addon_version (str): Version of addon. + project_name (Optional[str]): Name of project for which the + settings are received. A studio settings values are received + if is 'None'. + variant (Optional[Literal['production', 'staging']]): Name of + settings variant. Used 'default_settings_variant' by default. + site_id (Optional[str]): Name of site which is used for site + overrides. Is filled with connection 'site_id' attribute + if not passed. + use_site (Optional[bool]): To force disable option of using + site overrides set to 'False'. In that case won't be applied + any site overrides. + + Returns: + dict[str, Any]: Addon settings. + + """ + if project_name is None: + return self.get_addon_studio_settings( + addon_name, addon_version, variant + ) + return self.get_addon_project_settings( + addon_name, addon_version, project_name, variant, site_id, use_site + )
+ +
[docs] def get_addon_site_settings( + self, addon_name, addon_version, site_id=None + ): + """Site settings of an addon. + + If site id is not available an empty dictionary is returned. + + Args: + addon_name (str): Name of addon. + addon_version (str): Version of addon. + site_id (Optional[str]): Name of site for which should be settings + returned. using 'site_id' attribute if not passed. + + Returns: + dict[str, Any]: Site settings. + + """ + if site_id is None: + site_id = self.site_id + + if not site_id: + return {} + + query = prepare_query_string({"site": site_id}) + result = self.get("addons/{}/{}/siteSettings{}".format( + addon_name, addon_version, query + )) + result.raise_for_status() + return result.data
+ +
[docs] def get_bundle_settings( + self, + bundle_name=None, + project_name=None, + variant=None, + site_id=None, + use_site=True + ): + """Get complete set of settings for given data. + + If project is not passed then studio settings are returned. If variant + is not passed 'default_settings_variant' is used. If bundle name is + not passed then current production/staging bundle is used, based on + variant value. + + Output contains addon settings and site settings in single dictionary. + + Todos: + - test how it behaves if there is not any bundle. + - test how it behaves if there is not any production/staging + bundle. + + Example output:: + + { + "addons": [ + { + "name": "addon-name", + "version": "addon-version", + "settings": {...}, + "siteSettings": {...} + } + ] + } + + Returns: + dict[str, Any]: All settings for single bundle. + + """ + query_values = { + key: value + for key, value in ( + ("project_name", project_name), + ("variant", variant or self.default_settings_variant), + ("bundle_name", bundle_name), + ) + if value + } + if use_site: + if not site_id: + site_id = self.site_id + if site_id: + query_values["site_id"] = site_id + + query = prepare_query_string(query_values) + response = self.get("settings{}".format(query)) + response.raise_for_status() + return response.data
+ +
[docs] def get_addons_studio_settings( + self, + bundle_name=None, + variant=None, + site_id=None, + use_site=True, + only_values=True + ): + """All addons settings in one bulk. + + Warnings: + Behavior of this function changed with AYON server version 0.3.0. + Structure of output from server changed. If using + 'only_values=True' then output should be same as before. + + Args: + bundle_name (Optional[str]): Name of bundle for which should be + settings received. + variant (Optional[Literal['production', 'staging']]): Name of + settings variant. Used 'default_settings_variant' by default. + site_id (Optional[str]): Site id for which want to receive + site overrides. + use_site (bool): To force disable option of using site overrides + set to 'False'. In that case won't be applied any site + overrides. + only_values (Optional[bool]): Output will contain only settings + values without metadata about addons. + + Returns: + dict[str, Any]: Settings of all addons on server. + + """ + output = self.get_bundle_settings( + bundle_name=bundle_name, + variant=variant, + site_id=site_id, + use_site=use_site + ) + if only_values: + output = { + addon["name"]: addon["settings"] + for addon in output["addons"] + } + return output
+ +
[docs] def get_addons_project_settings( + self, + project_name, + bundle_name=None, + variant=None, + site_id=None, + use_site=True, + only_values=True + ): + """Project settings of all addons. + + Server returns information about used addon versions, so full output + looks like: + + ```json + { + "settings": {...}, + "addons": {...} + } + ``` + + The output can be limited to only values. To do so is 'only_values' + argument which is by default set to 'True'. In that case output + contains only value of 'settings' key. + + Warnings: + Behavior of this function changed with AYON server version 0.3.0. + Structure of output from server changed. If using + 'only_values=True' then output should be same as before. + + Args: + project_name (str): Name of project for which are settings + received. + bundle_name (Optional[str]): Name of bundle for which should be + settings received. + variant (Optional[Literal['production', 'staging']]): Name of + settings variant. Used 'default_settings_variant' by default. + site_id (Optional[str]): Site id for which want to receive + site overrides. + use_site (bool): To force disable option of using site overrides + set to 'False'. In that case won't be applied any site + overrides. + only_values (Optional[bool]): Output will contain only settings + values without metadata about addons. + + Returns: + dict[str, Any]: Settings of all addons on server for passed + project. + + """ + if not project_name: + raise ValueError("Project name must be passed.") + + output = self.get_bundle_settings( + project_name=project_name, + bundle_name=bundle_name, + variant=variant, + site_id=site_id, + use_site=use_site + ) + if only_values: + output = { + addon["name"]: addon["settings"] + for addon in output["addons"] + } + return output
+ +
[docs] def get_addons_settings( + self, + bundle_name=None, + project_name=None, + variant=None, + site_id=None, + use_site=True, + only_values=True + ): + """Universal function to receive all addon settings. + + Based on 'project_name' will receive studio settings or project + settings. In case project is not passed is 'site_id' ignored. + + Warnings: + Behavior of this function changed with AYON server version 0.3.0. + Structure of output from server changed. If using + 'only_values=True' then output should be same as before. + + Args: + bundle_name (Optional[str]): Name of bundle for which should be + settings received. + project_name (Optional[str]): Name of project for which should be + settings received. + variant (Optional[Literal['production', 'staging']]): Name of + settings variant. Used 'default_settings_variant' by default. + site_id (Optional[str]): Id of site for which want to receive + site overrides. + use_site (Optional[bool]): To force disable option of using site + overrides set to 'False'. In that case won't be applied + any site overrides. + only_values (Optional[bool]): Only settings values will be + returned. By default, is set to 'True'. + + """ + if project_name is None: + return self.get_addons_studio_settings( + bundle_name=bundle_name, + variant=variant, + site_id=site_id, + use_site=use_site, + only_values=only_values + ) + + return self.get_addons_project_settings( + project_name=project_name, + bundle_name=bundle_name, + variant=variant, + site_id=site_id, + use_site=use_site, + only_values=only_values + )
+ +
[docs] def get_secrets(self): + """Get all secrets. + + Example output:: + + [ + { + "name": "secret_1", + "value": "secret_value_1", + }, + { + "name": "secret_2", + "value": "secret_value_2", + } + ] + + Returns: + list[dict[str, str]]: List of secret entities. + + """ + response = self.get("secrets") + response.raise_for_status() + return response.data
+ +
[docs] def get_secret(self, secret_name): + """Get secret by name. + + Example output:: + + { + "name": "secret_name", + "value": "secret_value", + } + + Args: + secret_name (str): Name of secret. + + Returns: + dict[str, str]: Secret entity data. + + """ + response = self.get("secrets/{}".format(secret_name)) + response.raise_for_status() + return response.data
+ +
[docs] def save_secret(self, secret_name, secret_value): + """Save secret. + + This endpoint can create and update secret. + + Args: + secret_name (str): Name of secret. + secret_value (str): Value of secret. + + """ + response = self.put( + "secrets/{}".format(secret_name), + name=secret_name, + value=secret_value, + ) + response.raise_for_status() + return response.data
+ +
[docs] def delete_secret(self, secret_name): + """Delete secret by name. + + Args: + secret_name (str): Name of secret to delete. + + """ + response = self.delete("secrets/{}".format(secret_name)) + response.raise_for_status() + return response.data
+ + # Entity getters +
[docs] def get_rest_project(self, project_name): + """Query project by name. + + This call returns project with anatomy data. + + Args: + project_name (str): Name of project. + + Returns: + Union[dict[str, Any], None]: Project entity data or 'None' if + project was not found. + + """ + if not project_name: + return None + + response = self.get("projects/{}".format(project_name)) + # TODO ignore only error about not existing project + if response.status != 200: + return None + project = response.data + # Add fake scope to statuses if not available + for status in project["statuses"]: + scope = status.get("scope") + if scope is None: + status["scope"] = [ + "folder", + "task", + "product", + "version", + "representation", + "workfile" + ] + return project
+ +
[docs] def get_rest_projects(self, active=True, library=None): + """Query available project entities. + + User must be logged in. + + Args: + active (Optional[bool]): Filter active/inactive projects. Both + are returned if 'None' is passed. + library (Optional[bool]): Filter standard/library projects. Both + are returned if 'None' is passed. + + Returns: + Generator[dict[str, Any]]: Available projects. + + """ + for project_name in self.get_project_names(active, library): + project = self.get_rest_project(project_name) + if project: + yield project
+ +
[docs] def get_rest_entity_by_id(self, project_name, entity_type, entity_id): + """Get entity using REST on a project by its id. + + Args: + project_name (str): Name of project where entity is. + entity_type (Literal["folder", "task", "product", "version"]): The + entity type which should be received. + entity_id (str): Id of entity. + + Returns: + dict[str, Any]: Received entity data. + + """ + if not all((project_name, entity_type, entity_id)): + return None + + entity_endpoint = "{}s".format(entity_type) + response = self.get("projects/{}/{}/{}".format( + project_name, entity_endpoint, entity_id + )) + if response.status == 200: + return response.data + return None
+ +
[docs] def get_rest_folder(self, project_name, folder_id): + return self.get_rest_entity_by_id(project_name, "folder", folder_id)
+ +
[docs] def get_rest_folders(self, project_name, include_attrib=False): + """Get simplified flat list of all project folders. + + Get all project folders in single REST call. This can be faster than + using 'get_folders' method which is using GraphQl, but does not + allow any filtering, and set of fields is defined + by server backend. + + Example:: + + [ + { + "id": "112233445566", + "parentId": "112233445567", + "path": "/root/parent/child", + "parents": ["root", "parent"], + "name": "child", + "label": "Child", + "folderType": "Folder", + "hasTasks": False, + "hasChildren": False, + "taskNames": [ + "Compositing", + ], + "status": "In Progress", + "attrib": {}, + "ownAttrib": [], + "updatedAt": "2023-06-12T15:37:02.420260", + }, + ... + ] + + Args: + project_name (str): Project name. + include_attrib (Optional[bool]): Include attribute values + in output. Slower to query. + + Returns: + list[dict[str, Any]]: List of folder entities. + + """ + major, minor, patch, _, _ = self.server_version_tuple + if (major, minor, patch) < (1, 0, 8): + raise UnsupportedServerVersion( + "Function 'get_folders_rest' is supported" + " for AYON server 1.0.8 and above." + ) + query = "?attrib={}".format( + "true" if include_attrib else "false" + ) + response = self.get( + "projects/{}/folders{}".format(project_name, query) + ) + response.raise_for_status() + return response.data["folders"]
+ +
[docs] def get_rest_task(self, project_name, task_id): + return self.get_rest_entity_by_id(project_name, "task", task_id)
+ +
[docs] def get_rest_product(self, project_name, product_id): + return self.get_rest_entity_by_id(project_name, "product", product_id)
+ +
[docs] def get_rest_version(self, project_name, version_id): + return self.get_rest_entity_by_id(project_name, "version", version_id)
+ +
[docs] def get_rest_representation(self, project_name, representation_id): + return self.get_rest_entity_by_id( + project_name, "representation", representation_id + )
+ +
[docs] def get_project_names(self, active=True, library=None): + """Receive available project names. + + User must be logged in. + + Args: + active (Optional[bool]): Filter active/inactive projects. Both + are returned if 'None' is passed. + library (Optional[bool]): Filter standard/library projects. Both + are returned if 'None' is passed. + + Returns: + list[str]: List of available project names. + + """ + query_keys = {} + if active is not None: + query_keys["active"] = "true" if active else "false" + + if library is not None: + query_keys["library"] = "true" if library else "false" + query = "" + if query_keys: + query = "?{}".format(",".join([ + "{}={}".format(key, value) + for key, value in query_keys.items() + ])) + + response = self.get("projects{}".format(query), **query_keys) + response.raise_for_status() + data = response.data + project_names = [] + if data: + for project in data["projects"]: + project_names.append(project["name"]) + return project_names
+ + def _should_use_rest_project(self, fields=None): + """Fetch of project must be done using REST endpoint. + + Returns: + bool: REST endpoint must be used to get requested fields. + + """ + if fields is None: + return True + for field in fields: + if field.startswith("config"): + return True + return False + +
[docs] def get_projects( + self, active=True, library=None, fields=None, own_attributes=False + ): + """Get projects. + + Args: + active (Optional[bool]): Filter active or inactive projects. + Filter is disabled when 'None' is passed. + library (Optional[bool]): Filter library projects. Filter is + disabled when 'None' is passed. + fields (Optional[Iterable[str]]): fields to be queried + for project. + own_attributes (Optional[bool]): Attribute values that are + not explicitly set on entity will have 'None' value. + + Returns: + Generator[dict[str, Any]]: Queried projects. + + """ + if fields is not None: + fields = set(fields) + + use_rest = self._should_use_rest_project(fields) + if use_rest: + for project in self.get_rest_projects(active, library): + if own_attributes: + fill_own_attribs(project) + yield project + return + + self._prepare_fields("project", fields, own_attributes) + + query = projects_graphql_query(fields) + for parsed_data in query.continuous_query(self): + for project in parsed_data["projects"]: + if own_attributes: + fill_own_attribs(project) + yield project
+ +
[docs] def get_project(self, project_name, fields=None, own_attributes=False): + """Get project. + + Args: + project_name (str): Name of project. + fields (Optional[Iterable[str]]): fields to be queried + for project. + own_attributes (Optional[bool]): Attribute values that are + not explicitly set on entity will have 'None' value. + + Returns: + Union[dict[str, Any], None]: Project entity data or None + if project was not found. + + """ + if fields is not None: + fields = set(fields) + + use_rest = self._should_use_rest_project(fields) + if use_rest: + project = self.get_rest_project(project_name) + if own_attributes: + fill_own_attribs(project) + return project + + self._prepare_fields("project", fields, own_attributes) + + query = project_graphql_query(fields) + query.set_variable_value("projectName", project_name) + + parsed_data = query.query(self) + + project = parsed_data["project"] + if project is not None: + project["name"] = project_name + if own_attributes: + fill_own_attribs(project) + + return project
+ +
[docs] def get_folders_hierarchy( + self, + project_name, + search_string=None, + folder_types=None + ): + """Get project hierarchy. + + All folders in project in hierarchy data structure. + + Example output: + { + "hierarchy": [ + { + "id": "...", + "name": "...", + "label": "...", + "status": "...", + "folderType": "...", + "hasTasks": False, + "taskNames": [], + "parents": [], + "parentId": None, + "children": [...children folders...] + }, + ... + ] + } + + Args: + project_name (str): Project where to look for folders. + search_string (Optional[str]): Search string to filter folders. + folder_types (Optional[Iterable[str]]): Folder types to filter. + + Returns: + dict[str, Any]: Response data from server. + + """ + if folder_types: + folder_types = ",".join(folder_types) + + query_fields = [ + "{}={}".format(key, value) + for key, value in ( + ("search", search_string), + ("types", folder_types), + ) + if value + ] + query = "" + if query_fields: + query = "?{}".format(",".join(query_fields)) + + response = self.get( + "projects/{}/hierarchy{}".format(project_name, query) + ) + response.raise_for_status() + return response.data
+ +
[docs] def get_folders_rest(self, project_name, include_attrib=False): + """Get simplified flat list of all project folders. + + Get all project folders in single REST call. This can be faster than + using 'get_folders' method which is using GraphQl, but does not + allow any filtering, and set of fields is defined + by server backend. + + Example:: + + [ + { + "id": "112233445566", + "parentId": "112233445567", + "path": "/root/parent/child", + "parents": ["root", "parent"], + "name": "child", + "label": "Child", + "folderType": "Folder", + "hasTasks": False, + "hasChildren": False, + "taskNames": [ + "Compositing", + ], + "status": "In Progress", + "attrib": {}, + "ownAttrib": [], + "updatedAt": "2023-06-12T15:37:02.420260", + }, + ... + ] + + Deprecated: + Use 'get_rest_folders' instead. Function was renamed to match + other rest functions, like 'get_rest_folder', + 'get_rest_project' etc. . + Will be removed in '1.0.7' or '1.1.0'. + + Args: + project_name (str): Project name. + include_attrib (Optional[bool]): Include attribute values + in output. Slower to query. + + Returns: + list[dict[str, Any]]: List of folder entities. + + """ + warnings.warn( + ( + "DEPRECATION: Used deprecated 'get_folders_rest'," + " use 'get_rest_folders' instead." + ), + DeprecationWarning + ) + return self.get_rest_folders(project_name, include_attrib)
+ +
[docs] def get_folders( + self, + project_name, + folder_ids=None, + folder_paths=None, + folder_names=None, + folder_types=None, + parent_ids=None, + folder_path_regex=None, + has_products=None, + has_tasks=None, + has_children=None, + statuses=None, + assignees_all=None, + tags=None, + active=True, + has_links=None, + fields=None, + own_attributes=False + ): + """Query folders from server. + + Todos: + Folder name won't be unique identifier, so we should add + folder path filtering. + + Notes: + Filter 'active' don't have direct filter in GraphQl. + + Args: + project_name (str): Name of project. + folder_ids (Optional[Iterable[str]]): Folder ids to filter. + folder_paths (Optional[Iterable[str]]): Folder paths used + for filtering. + folder_names (Optional[Iterable[str]]): Folder names used + for filtering. + folder_types (Optional[Iterable[str]]): Folder types used + for filtering. + parent_ids (Optional[Iterable[str]]): Ids of folder parents. + Use 'None' if folder is direct child of project. + folder_path_regex (Optional[str]): Folder path regex used + for filtering. + has_products (Optional[bool]): Filter folders with/without + products. Ignored when None, default behavior. + has_tasks (Optional[bool]): Filter folders with/without + tasks. Ignored when None, default behavior. + has_children (Optional[bool]): Filter folders with/without + children. Ignored when None, default behavior. + statuses (Optional[Iterable[str]]): Folder statuses used + for filtering. + assignees_all (Optional[Iterable[str]]): Filter by assigness + on children tasks. Task must have all of passed assignees. + tags (Optional[Iterable[str]]): Folder tags used + for filtering. + active (Optional[bool]): Filter active/inactive folders. + Both are returned if is set to None. + has_links (Optional[Literal[IN, OUT, ANY]]): Filter + representations with IN/OUT/ANY links. + fields (Optional[Iterable[str]]): Fields to be queried for + folder. All possible folder fields are returned + if 'None' is passed. + own_attributes (Optional[bool]): Attribute values that are + not explicitly set on entity will have 'None' value. + + Returns: + Generator[dict[str, Any]]: Queried folder entities. + + """ + if not project_name: + return + + filters = { + "projectName": project_name + } + if not _prepare_list_filters( + filters, + ("folderIds", folder_ids), + ("folderPaths", folder_paths), + ("folderNames", folder_names), + ("folderTypes", folder_types), + ("folderStatuses", statuses), + ("folderTags", tags), + ("folderAssigneesAll", assignees_all), + ): + return + + for filter_key, filter_value in ( + ("folderPathRegex", folder_path_regex), + ("folderHasProducts", has_products), + ("folderHasTasks", has_tasks), + ("folderHasLinks", has_links), + ("folderHasChildren", has_children), + ): + if filter_value is not None: + filters[filter_key] = filter_value + + if parent_ids is not None: + parent_ids = set(parent_ids) + if not parent_ids: + return + if None in parent_ids: + # Replace 'None' with '"root"' which is used during GraphQl + # query for parent ids filter for folders without folder + # parent + parent_ids.remove(None) + parent_ids.add("root") + + if project_name in parent_ids: + # Replace project name with '"root"' which is used during + # GraphQl query for parent ids filter for folders without + # folder parent + parent_ids.remove(project_name) + parent_ids.add("root") + + filters["parentFolderIds"] = list(parent_ids) + + if not fields: + fields = self.get_default_fields_for_type("folder") + else: + fields = set(fields) + self._prepare_fields("folder", fields) + + use_rest = False + if "data" in fields and not self.graphql_allows_data_in_query: + use_rest = True + fields = {"id"} + + if active is not None: + fields.add("active") + + if own_attributes and not use_rest: + fields.add("ownAttrib") + + query = folders_graphql_query(fields) + for attr, filter_value in filters.items(): + query.set_variable_value(attr, filter_value) + + for parsed_data in query.continuous_query(self): + for folder in parsed_data["project"]["folders"]: + if active is not None and active is not folder["active"]: + continue + + if use_rest: + folder = self.get_rest_folder(project_name, folder["id"]) + else: + self._convert_entity_data(folder) + + if own_attributes: + fill_own_attribs(folder) + yield folder
+ +
[docs] def get_folder_by_id( + self, + project_name, + folder_id, + fields=None, + own_attributes=False + ): + """Query folder entity by id. + + Args: + project_name (str): Name of project where to look for queried + entities. + folder_id (str): Folder id. + fields (Optional[Iterable[str]]): Fields that should be returned. + All fields are returned if 'None' is passed. + own_attributes (Optional[bool]): Attribute values that are + not explicitly set on entity will have 'None' value. + + Returns: + Union[dict, None]: Folder entity data or None if was not found. + + """ + folders = self.get_folders( + project_name, + folder_ids=[folder_id], + active=None, + fields=fields, + own_attributes=own_attributes + ) + for folder in folders: + return folder + return None
+ +
[docs] def get_folder_by_path( + self, + project_name, + folder_path, + fields=None, + own_attributes=False + ): + """Query folder entity by path. + + Folder path is a path to folder with all parent names joined by slash. + + Args: + project_name (str): Name of project where to look for queried + entities. + folder_path (str): Folder path. + fields (Optional[Iterable[str]]): Fields that should be returned. + All fields are returned if 'None' is passed. + own_attributes (Optional[bool]): Attribute values that are + not explicitly set on entity will have 'None' value. + + Returns: + Union[dict, None]: Folder entity data or None if was not found. + + """ + folders = self.get_folders( + project_name, + folder_paths=[folder_path], + active=None, + fields=fields, + own_attributes=own_attributes + ) + for folder in folders: + return folder + return None
+ +
[docs] def get_folder_by_name( + self, + project_name, + folder_name, + fields=None, + own_attributes=False + ): + """Query folder entity by path. + + Warnings: + Folder name is not a unique identifier of a folder. Function is + kept for OpenPype 3 compatibility. + + Args: + project_name (str): Name of project where to look for queried + entities. + folder_name (str): Folder name. + fields (Optional[Iterable[str]]): Fields that should be returned. + All fields are returned if 'None' is passed. + own_attributes (Optional[bool]): Attribute values that are + not explicitly set on entity will have 'None' value. + + Returns: + Union[dict, None]: Folder entity data or None if was not found. + + """ + folders = self.get_folders( + project_name, + folder_names=[folder_name], + active=None, + fields=fields, + own_attributes=own_attributes + ) + for folder in folders: + return folder + return None
+ +
[docs] def get_folder_ids_with_products(self, project_name, folder_ids=None): + """Find folders which have at least one product. + + Folders that have at least one product should be immutable, so they + should not change path -> change of name or name of any parent + is not possible. + + Args: + project_name (str): Name of project. + folder_ids (Optional[Iterable[str]]): Limit folder ids filtering + to a set of folders. If set to None all folders on project are + checked. + + Returns: + set[str]: Folder ids that have at least one product. + + """ + if folder_ids is not None: + folder_ids = set(folder_ids) + if not folder_ids: + return set() + + query = folders_graphql_query({"id"}) + query.set_variable_value("projectName", project_name) + query.set_variable_value("folderHasProducts", True) + if folder_ids: + query.set_variable_value("folderIds", list(folder_ids)) + + parsed_data = query.query(self) + folders = parsed_data["project"]["folders"] + return { + folder["id"] + for folder in folders + }
+ +
[docs] def create_folder( + self, + project_name, + name, + folder_type=None, + parent_id=None, + label=None, + attrib=None, + data=None, + tags=None, + status=None, + active=None, + thumbnail_id=None, + folder_id=None, + ): + """Create new folder. + + Args: + project_name (str): Project name. + name (str): Folder name. + folder_type (Optional[str]): Folder type. + parent_id (Optional[str]): Parent folder id. Parent is project + if is ``None``. + label (Optional[str]): Label of folder. + attrib (Optional[dict[str, Any]]): Folder attributes. + data (Optional[dict[str, Any]]): Folder data. + tags (Optional[Iterable[str]]): Folder tags. + status (Optional[str]): Folder status. + active (Optional[bool]): Folder active state. + thumbnail_id (Optional[str]): Folder thumbnail id. + folder_id (Optional[str]): Folder id. If not passed new id is + generated. + + Returns: + str: Entity id. + + """ + if not folder_id: + folder_id = create_entity_id() + create_data = { + "id": folder_id, + "name": name, + } + for key, value in ( + ("folderType", folder_type), + ("parentId", parent_id), + ("label", label), + ("attrib", attrib), + ("data", data), + ("tags", tags), + ("status", status), + ("active", active), + ("thumbnailId", thumbnail_id), + ): + if value is not None: + create_data[key] = value + + response = self.post( + "projects/{}/folders".format(project_name), + **create_data + ) + response.raise_for_status() + return folder_id
+ +
[docs] def update_folder( + self, + project_name, + folder_id, + name=None, + folder_type=None, + parent_id=NOT_SET, + label=NOT_SET, + attrib=None, + data=None, + tags=None, + status=None, + active=None, + thumbnail_id=NOT_SET, + ): + """Update folder entity on server. + + Do not pass ``parent_id``, ``label`` amd ``thumbnail_id`` if you don't + want to change their values. Value ``None`` would unset + their value. + + Update of ``data`` will override existing value on folder entity. + + Update of ``attrib`` does change only passed attributes. If you want + to unset value, use ``None``. + + Args: + project_name (str): Project name. + folder_id (str): Folder id. + name (Optional[str]): New name. + folder_type (Optional[str]): New folder type. + parent_id (Optional[Union[str, None]]): New parent folder id. + label (Optional[Union[str, None]]): New label. + attrib (Optional[dict[str, Any]]): New attributes. + data (Optional[dict[str, Any]]): New data. + tags (Optional[Iterable[str]]): New tags. + status (Optional[str]): New status. + active (Optional[bool]): New active state. + thumbnail_id (Optional[Union[str, None]]): New thumbnail id. + + """ + update_data = {} + for key, value in ( + ("name", name), + ("folderType", folder_type), + ("attrib", attrib), + ("data", data), + ("tags", tags), + ("status", status), + ("active", active), + ): + if value is not None: + update_data[key] = value + + for key, value in ( + ("label", label), + ("parentId", parent_id), + ("thumbnailId", thumbnail_id), + ): + if value is not NOT_SET: + update_data[key] = value + + response = self.patch( + "projects/{}/folders/{}".format(project_name, folder_id), + **update_data + ) + response.raise_for_status()
+ +
[docs] def delete_folder(self, project_name, folder_id, force=False): + """Delete folder. + + Args: + project_name (str): Project name. + folder_id (str): Folder id to delete. + force (Optional[bool]): Folder delete folder with all children + folder, products, versions and representations. + + """ + url = "projects/{}/folders/{}".format(project_name, folder_id) + if force: + url += "?force=true" + response = self.delete(url) + response.raise_for_status()
+ +
[docs] def get_tasks( + self, + project_name, + task_ids=None, + task_names=None, + task_types=None, + folder_ids=None, + assignees=None, + assignees_all=None, + statuses=None, + tags=None, + active=True, + fields=None, + own_attributes=False + ): + """Query task entities from server. + + Args: + project_name (str): Name of project. + task_ids (Iterable[str]): Task ids to filter. + task_names (Iterable[str]): Task names used for filtering. + task_types (Iterable[str]): Task types used for filtering. + folder_ids (Iterable[str]): Ids of task parents. Use 'None' + if folder is direct child of project. + assignees (Optional[Iterable[str]]): Task assignees used for + filtering. All tasks with any of passed assignees are + returned. + assignees_all (Optional[Iterable[str]]): Task assignees used + for filtering. Task must have all of passed assignees to be + returned. + statuses (Optional[Iterable[str]]): Task statuses used for + filtering. + tags (Optional[Iterable[str]]): Task tags used for + filtering. + active (Optional[bool]): Filter active/inactive tasks. + Both are returned if is set to None. + fields (Optional[Iterable[str]]): Fields to be queried for + folder. All possible folder fields are returned + if 'None' is passed. + own_attributes (Optional[bool]): Attribute values that are + not explicitly set on entity will have 'None' value. + + Returns: + Generator[dict[str, Any]]: Queried task entities. + + """ + if not project_name: + return + + filters = { + "projectName": project_name + } + if not _prepare_list_filters( + filters, + ("taskIds", task_ids), + ("taskNames", task_names), + ("taskTypes", task_types), + ("folderIds", folder_ids), + ("taskAssigneesAny", assignees), + ("taskAssigneesAll", assignees_all), + ("taskStatuses", statuses), + ("taskTags", tags), + ): + return + + if not fields: + fields = self.get_default_fields_for_type("task") + else: + fields = set(fields) + self._prepare_fields("task", fields, own_attributes) + + use_rest = False + if "data" in fields and not self.graphql_allows_data_in_query: + use_rest = True + fields = {"id"} + + if active is not None: + fields.add("active") + + query = tasks_graphql_query(fields) + for attr, filter_value in filters.items(): + query.set_variable_value(attr, filter_value) + + for parsed_data in query.continuous_query(self): + for task in parsed_data["project"]["tasks"]: + if active is not None and active is not task["active"]: + continue + + if use_rest: + task = self.get_rest_task(project_name, task["id"]) + else: + self._convert_entity_data(task) + + if own_attributes: + fill_own_attribs(task) + yield task
+ +
[docs] def get_task_by_name( + self, + project_name, + folder_id, + task_name, + fields=None, + own_attributes=False + ): + """Query task entity by name and folder id. + + Args: + project_name (str): Name of project where to look for queried + entities. + folder_id (str): Folder id. + task_name (str): Task name + fields (Optional[Iterable[str]]): Fields that should be returned. + All fields are returned if 'None' is passed. + own_attributes (Optional[bool]): Attribute values that are + not explicitly set on entity will have 'None' value. + + Returns: + Union[dict, None]: Task entity data or None if was not found. + + """ + for task in self.get_tasks( + project_name, + folder_ids=[folder_id], + task_names=[task_name], + active=None, + fields=fields, + own_attributes=own_attributes + ): + return task + return None
+ +
[docs] def get_task_by_id( + self, + project_name, + task_id, + fields=None, + own_attributes=False + ): + """Query task entity by id. + + Args: + project_name (str): Name of project where to look for queried + entities. + task_id (str): Task id. + fields (Optional[Iterable[str]]): Fields that should be returned. + All fields are returned if 'None' is passed. + own_attributes (Optional[bool]): Attribute values that are + not explicitly set on entity will have 'None' value. + + Returns: + Union[dict, None]: Task entity data or None if was not found. + + """ + for task in self.get_tasks( + project_name, + task_ids=[task_id], + active=None, + fields=fields, + own_attributes=own_attributes + ): + return task + return None
+ +
[docs] def get_tasks_by_folder_paths( + self, + project_name, + folder_paths, + task_names=None, + task_types=None, + assignees=None, + assignees_all=None, + statuses=None, + tags=None, + active=True, + fields=None, + own_attributes=False + ): + """Query task entities from server by folder paths. + + Args: + project_name (str): Name of project. + folder_paths (list[str]): Folder paths. + task_names (Iterable[str]): Task names used for filtering. + task_types (Iterable[str]): Task types used for filtering. + assignees (Optional[Iterable[str]]): Task assignees used for + filtering. All tasks with any of passed assignees are + returned. + assignees_all (Optional[Iterable[str]]): Task assignees used + for filtering. Task must have all of passed assignees to be + returned. + statuses (Optional[Iterable[str]]): Task statuses used for + filtering. + tags (Optional[Iterable[str]]): Task tags used for + filtering. + active (Optional[bool]): Filter active/inactive tasks. + Both are returned if is set to None. + fields (Optional[Iterable[str]]): Fields to be queried for + folder. All possible folder fields are returned + if 'None' is passed. + own_attributes (Optional[bool]): Attribute values that are + not explicitly set on entity will have 'None' value. + + Returns: + dict[dict[str, list[dict[str, Any]]]: Task entities by + folder path. + + """ + folder_paths = set(folder_paths) + if not project_name or not folder_paths: + return + + filters = { + "projectName": project_name, + "folderPaths": list(folder_paths), + } + if not _prepare_list_filters( + filters, + ("taskNames", task_names), + ("taskTypes", task_types), + ("taskAssigneesAny", assignees), + ("taskAssigneesAll", assignees_all), + ("taskStatuses", statuses), + ("taskTags", tags), + ): + return + + if not fields: + fields = self.get_default_fields_for_type("task") + else: + fields = set(fields) + self._prepare_fields("task", fields, own_attributes) + + use_rest = False + if "data" in fields and not self.graphql_allows_data_in_query: + use_rest = True + fields = {"id"} + + if active is not None: + fields.add("active") + + query = tasks_by_folder_paths_graphql_query(fields) + for attr, filter_value in filters.items(): + query.set_variable_value(attr, filter_value) + + output = { + folder_path: [] + for folder_path in folder_paths + } + for parsed_data in query.continuous_query(self): + for folder in parsed_data["project"]["folders"]: + folder_path = folder["path"] + for task in folder["tasks"]: + if active is not None and active is not task["active"]: + continue + + if use_rest: + task = self.get_rest_task(project_name, task["id"]) + else: + self._convert_entity_data(task) + + if own_attributes: + fill_own_attribs(task) + output[folder_path].append(task) + return output
+ +
[docs] def get_tasks_by_folder_path( + self, + project_name, + folder_path, + task_names=None, + task_types=None, + assignees=None, + assignees_all=None, + statuses=None, + tags=None, + active=True, + fields=None, + own_attributes=False + ): + """Query task entities from server by folder path. + + Args: + project_name (str): Name of project. + folder_path (str): Folder path. + task_names (Iterable[str]): Task names used for filtering. + task_types (Iterable[str]): Task types used for filtering. + assignees (Optional[Iterable[str]]): Task assignees used for + filtering. All tasks with any of passed assignees are + returned. + assignees_all (Optional[Iterable[str]]): Task assignees used + for filtering. Task must have all of passed assignees to be + returned. + statuses (Optional[Iterable[str]]): Task statuses used for + filtering. + tags (Optional[Iterable[str]]): Task tags used for + filtering. + active (Optional[bool]): Filter active/inactive tasks. + Both are returned if is set to None. + fields (Optional[Iterable[str]]): Fields to be queried for + folder. All possible folder fields are returned + if 'None' is passed. + own_attributes (Optional[bool]): Attribute values that are + not explicitly set on entity will have 'None' value. + + """ + return self.get_tasks_by_folder_paths( + project_name, + [folder_path], + task_names, + task_types=task_types, + assignees=assignees, + assignees_all=assignees_all, + statuses=statuses, + tags=tags, + active=active, + fields=fields, + own_attributes=own_attributes + )[folder_path]
+ +
[docs] def get_task_by_folder_path( + self, + project_name, + folder_path, + task_name, + fields=None, + own_attributes=False + ): + """Query task entity by folder path and task name. + + Args: + project_name (str): Project name. + folder_path (str): Folder path. + task_name (str): Task name. + fields (Optional[Iterable[str]]): Task fields that should + be returned. + own_attributes (Optional[bool]): Attribute values that are + not explicitly set on entity will have 'None' value. + + Returns: + Union[dict[str, Any], None]: Task entity data or None if was + not found. + + """ + for task in self.get_tasks_by_folder_path( + project_name, + folder_path, + active=None, + task_names=[task_name], + fields=fields, + own_attributes=own_attributes, + ): + return task + return None
+ +
[docs] def create_task( + self, + project_name, + name, + task_type, + folder_id, + label=None, + assignees=None, + attrib=None, + data=None, + tags=None, + status=None, + active=None, + thumbnail_id=None, + task_id=None, + ): + """Create new task. + + Args: + project_name (str): Project name. + name (str): Folder name. + task_type (str): Task type. + folder_id (str): Parent folder id. + label (Optional[str]): Label of folder. + assignees (Optional[Iterable[str]]): Task assignees. + attrib (Optional[dict[str, Any]]): Task attributes. + data (Optional[dict[str, Any]]): Task data. + tags (Optional[Iterable[str]]): Task tags. + status (Optional[str]): Task status. + active (Optional[bool]): Task active state. + thumbnail_id (Optional[str]): Task thumbnail id. + task_id (Optional[str]): Task id. If not passed new id is + generated. + + Returns: + str: Task id. + + """ + if not task_id: + task_id = create_entity_id() + create_data = { + "id": task_id, + "name": name, + "taskType": task_type, + "folderId": folder_id, + } + for key, value in ( + ("label", label), + ("attrib", attrib), + ("data", data), + ("tags", tags), + ("status", status), + ("assignees", assignees), + ("active", active), + ("thumbnailId", thumbnail_id), + ): + if value is not None: + create_data[key] = value + + response = self.post( + "projects/{}/tasks".format(project_name), + **create_data + ) + response.raise_for_status() + return task_id
+ +
[docs] def update_task( + self, + project_name, + task_id, + name=None, + task_type=None, + folder_id=None, + label=NOT_SET, + assignees=None, + attrib=None, + data=None, + tags=None, + status=None, + active=None, + thumbnail_id=NOT_SET, + ): + """Update task entity on server. + + Do not pass ``label`` amd ``thumbnail_id`` if you don't + want to change their values. Value ``None`` would unset + their value. + + Update of ``data`` will override existing value on folder entity. + + Update of ``attrib`` does change only passed attributes. If you want + to unset value, use ``None``. + + Args: + project_name (str): Project name. + task_id (str): Task id. + name (Optional[str]): New name. + task_type (Optional[str]): New task type. + folder_id (Optional[str]): New folder id. + label (Optional[Union[str, None]]): New label. + assignees (Optional[str]): New assignees. + attrib (Optional[dict[str, Any]]): New attributes. + data (Optional[dict[str, Any]]): New data. + tags (Optional[Iterable[str]]): New tags. + status (Optional[str]): New status. + active (Optional[bool]): New active state. + thumbnail_id (Optional[Union[str, None]]): New thumbnail id. + + """ + update_data = {} + for key, value in ( + ("name", name), + ("taskType", task_type), + ("folderId", folder_id), + ("assignees", assignees), + ("attrib", attrib), + ("data", data), + ("tags", tags), + ("status", status), + ("active", active), + ): + if value is not None: + update_data[key] = value + + for key, value in ( + ("label", label), + ("thumbnailId", thumbnail_id), + ): + if value is not NOT_SET: + update_data[key] = value + + response = self.patch( + "projects/{}/tasks/{}".format(project_name, task_id), + **update_data + ) + response.raise_for_status()
+ +
[docs] def delete_task(self, project_name, task_id): + """Delete task. + + Args: + project_name (str): Project name. + task_id (str): Task id to delete. + + """ + response = self.delete( + "projects/{}/tasks/{}".format(project_name, task_id) + ) + response.raise_for_status()
+ + def _filter_product( + self, project_name, product, active, use_rest + ): + if active is not None and product["active"] is not active: + return None + + if use_rest: + product = self.get_rest_product(project_name, product["id"]) + else: + self._convert_entity_data(product) + + return product + +
[docs] def get_products( + self, + project_name, + product_ids=None, + product_names=None, + folder_ids=None, + product_types=None, + product_name_regex=None, + product_path_regex=None, + names_by_folder_ids=None, + statuses=None, + tags=None, + active=True, + fields=None, + own_attributes=_PLACEHOLDER + ): + """Query products from server. + + Todos: + Separate 'name_by_folder_ids' filtering to separated method. It + cannot be combined with some other filters. + + Args: + project_name (str): Name of project. + product_ids (Optional[Iterable[str]]): Task ids to filter. + product_names (Optional[Iterable[str]]): Task names used for + filtering. + folder_ids (Optional[Iterable[str]]): Ids of task parents. + Use 'None' if folder is direct child of project. + product_types (Optional[Iterable[str]]): Product types used for + filtering. + product_name_regex (Optional[str]): Filter products by name regex. + product_path_regex (Optional[str]): Filter products by path regex. + Path starts with folder path and ends with product name. + names_by_folder_ids (Optional[dict[str, Iterable[str]]]): Product + name filtering by folder id. + statuses (Optional[Iterable[str]]): Product statuses used + for filtering. + tags (Optional[Iterable[str]]): Product tags used + for filtering. + active (Optional[bool]): Filter active/inactive products. + Both are returned if is set to None. + fields (Optional[Iterable[str]]): Fields to be queried for + folder. All possible folder fields are returned + if 'None' is passed. + own_attributes (Optional[bool]): DEPRECATED: Not supported for + products. + + Returns: + Generator[dict[str, Any]]: Queried product entities. + + """ + if not project_name: + return + + # Prepare these filters before 'name_by_filter_ids' filter + filter_product_names = None + if product_names is not None: + filter_product_names = set(product_names) + if not filter_product_names: + return + + filter_folder_ids = None + if folder_ids is not None: + filter_folder_ids = set(folder_ids) + if not filter_folder_ids: + return + + # This will disable 'folder_ids' and 'product_names' filters + # - maybe could be enhanced in future? + if names_by_folder_ids is not None: + filter_product_names = set() + filter_folder_ids = set() + + for folder_id, names in names_by_folder_ids.items(): + if folder_id and names: + filter_folder_ids.add(folder_id) + filter_product_names |= set(names) + + if not filter_product_names or not filter_folder_ids: + return + + # Convert fields and add minimum required fields + if fields: + fields = set(fields) | {"id"} + self._prepare_fields("product", fields) + else: + fields = self.get_default_fields_for_type("product") + + use_rest = False + if "data" in fields and not self.graphql_allows_data_in_query: + use_rest = True + fields = {"id"} + + if active is not None: + fields.add("active") + + if own_attributes is not _PLACEHOLDER: + warnings.warn( + ( + "'own_attributes' is not supported for products. The" + " argument will be removed from function signature in" + " future (apx. version 1.0.10 or 1.1.0)." + ), + DeprecationWarning + ) + + # Add 'name' and 'folderId' if 'names_by_folder_ids' filter is entered + if names_by_folder_ids: + fields.add("name") + fields.add("folderId") + + # Prepare filters for query + filters = { + "projectName": project_name + } + + if filter_folder_ids: + filters["folderIds"] = list(filter_folder_ids) + + if filter_product_names: + filters["productNames"] = list(filter_product_names) + + if not _prepare_list_filters( + filters, + ("productIds", product_ids), + ("productTypes", product_types), + ("productStatuses", statuses), + ("productTags", tags), + ): + return + + for filter_key, filter_value in ( + ("productNameRegex", product_name_regex), + ("productPathRegex", product_path_regex), + ): + if filter_value: + filters[filter_key] = filter_value + + query = products_graphql_query(fields) + for attr, filter_value in filters.items(): + query.set_variable_value(attr, filter_value) + + parsed_data = query.query(self) + + products = parsed_data.get("project", {}).get("products", []) + # Filter products by 'names_by_folder_ids' + if names_by_folder_ids: + products_by_folder_id = collections.defaultdict(list) + for product in products: + filtered_product = self._filter_product( + project_name, product, active, use_rest + ) + if filtered_product is not None: + folder_id = filtered_product["folderId"] + products_by_folder_id[folder_id].append(filtered_product) + + for folder_id, names in names_by_folder_ids.items(): + for folder_product in products_by_folder_id[folder_id]: + if folder_product["name"] in names: + yield folder_product + + else: + for product in products: + filtered_product = self._filter_product( + project_name, product, active, use_rest + ) + if filtered_product is not None: + yield filtered_product
+ +
[docs] def get_product_by_id( + self, + project_name, + product_id, + fields=None, + own_attributes=_PLACEHOLDER + ): + """Query product entity by id. + + Args: + project_name (str): Name of project where to look for queried + entities. + product_id (str): Product id. + fields (Optional[Iterable[str]]): Fields that should be returned. + All fields are returned if 'None' is passed. + own_attributes (Optional[bool]): DEPRECATED: Not supported for + products. + + Returns: + Union[dict, None]: Product entity data or None if was not found. + + """ + products = self.get_products( + project_name, + product_ids=[product_id], + active=None, + fields=fields, + own_attributes=own_attributes + ) + for product in products: + return product + return None
+ +
[docs] def get_product_by_name( + self, + project_name, + product_name, + folder_id, + fields=None, + own_attributes=_PLACEHOLDER + ): + """Query product entity by name and folder id. + + Args: + project_name (str): Name of project where to look for queried + entities. + product_name (str): Product name. + folder_id (str): Folder id (Folder is a parent of products). + fields (Optional[Iterable[str]]): Fields that should be returned. + All fields are returned if 'None' is passed. + own_attributes (Optional[bool]): DEPRECATED: Not supported for + products. + + Returns: + Union[dict, None]: Product entity data or None if was not found. + + """ + products = self.get_products( + project_name, + product_names=[product_name], + folder_ids=[folder_id], + active=None, + fields=fields, + own_attributes=own_attributes + ) + for product in products: + return product + return None
+ +
[docs] def get_product_types(self, fields=None): + """Types of products. + + This is server wide information. Product types have 'name', 'icon' and + 'color'. + + Args: + fields (Optional[Iterable[str]]): Product types fields to query. + + Returns: + list[dict[str, Any]]: Product types information. + + """ + if not fields: + fields = self.get_default_fields_for_type("productType") + + query = product_types_query(fields) + + parsed_data = query.query(self) + + return parsed_data.get("productTypes", [])
+ +
[docs] def get_project_product_types(self, project_name, fields=None): + """Types of products available on a project. + + Filter only product types available on project. + + Args: + project_name (str): Name of project where to look for + product types. + fields (Optional[Iterable[str]]): Product types fields to query. + + Returns: + list[dict[str, Any]]: Product types information. + + """ + if not fields: + fields = self.get_default_fields_for_type("productType") + + query = project_product_types_query(fields) + query.set_variable_value("projectName", project_name) + + parsed_data = query.query(self) + + return parsed_data.get("project", {}).get("productTypes", [])
+ +
[docs] def get_product_type_names(self, project_name=None, product_ids=None): + """Product type names. + + Warnings: + This function will be probably removed. Matters if 'products_id' + filter has real use-case. + + Args: + project_name (Optional[str]): Name of project where to look for + queried entities. + product_ids (Optional[Iterable[str]]): Product ids filter. Can be + used only with 'project_name'. + + Returns: + set[str]: Product type names. + + """ + if project_name and product_ids: + products = self.get_products( + project_name, + product_ids=product_ids, + fields=["productType"], + active=None, + ) + return { + product["productType"] + for product in products + } + + return { + product_info["name"] + for product_info in self.get_project_product_types( + project_name, fields=["name"] + ) + }
+ +
[docs] def create_product( + self, + project_name, + name, + product_type, + folder_id, + attrib=None, + data=None, + tags=None, + status=None, + active=None, + product_id=None, + ): + """Create new product. + + Args: + project_name (str): Project name. + name (str): Product name. + product_type (str): Product type. + folder_id (str): Parent folder id. + attrib (Optional[dict[str, Any]]): Product attributes. + data (Optional[dict[str, Any]]): Product data. + tags (Optional[Iterable[str]]): Product tags. + status (Optional[str]): Product status. + active (Optional[bool]): Product active state. + product_id (Optional[str]): Product id. If not passed new id is + generated. + + Returns: + str: Product id. + + """ + if not product_id: + product_id = create_entity_id() + create_data = { + "id": product_id, + "name": name, + "productType": product_type, + "folderId": folder_id, + } + for key, value in ( + ("attrib", attrib), + ("data", data), + ("tags", tags), + ("status", status), + ("active", active), + ): + if value is not None: + create_data[key] = value + + response = self.post( + "projects/{}/products".format(project_name), + **create_data + ) + response.raise_for_status() + return product_id
+ +
[docs] def update_product( + self, + project_name, + product_id, + name=None, + folder_id=None, + product_type=None, + attrib=None, + data=None, + tags=None, + status=None, + active=None, + ): + """Update product entity on server. + + Update of ``data`` will override existing value on folder entity. + + Update of ``attrib`` does change only passed attributes. If you want + to unset value, use ``None``. + + Args: + project_name (str): Project name. + product_id (str): Product id. + name (Optional[str]): New product name. + folder_id (Optional[str]): New product id. + product_type (Optional[str]): New product type. + attrib (Optional[dict[str, Any]]): New product attributes. + data (Optional[dict[str, Any]]): New product data. + tags (Optional[Iterable[str]]): New product tags. + status (Optional[str]): New product status. + active (Optional[bool]): New product active state. + + """ + update_data = {} + for key, value in ( + ("name", name), + ("productType", product_type), + ("folderId", folder_id), + ("attrib", attrib), + ("data", data), + ("tags", tags), + ("status", status), + ("active", active), + ): + if value is not None: + update_data[key] = value + + response = self.patch( + "projects/{}/products/{}".format(project_name, product_id), + **update_data + ) + response.raise_for_status()
+ +
[docs] def delete_product(self, project_name, product_id): + """Delete product. + + Args: + project_name (str): Project name. + product_id (str): Product id to delete. + + """ + response = self.delete( + "projects/{}/products/{}".format(project_name, product_id) + ) + response.raise_for_status()
+ +
[docs] def get_versions( + self, + project_name, + version_ids=None, + product_ids=None, + task_ids=None, + versions=None, + hero=True, + standard=True, + latest=None, + statuses=None, + tags=None, + active=True, + fields=None, + own_attributes=_PLACEHOLDER + ): + """Get version entities based on passed filters from server. + + Args: + project_name (str): Name of project where to look for versions. + version_ids (Optional[Iterable[str]]): Version ids used for + version filtering. + product_ids (Optional[Iterable[str]]): Product ids used for + version filtering. + task_ids (Optional[Iterable[str]]): Task ids used for + version filtering. + versions (Optional[Iterable[int]]): Versions we're interested in. + hero (Optional[bool]): Skip hero versions when set to False. + standard (Optional[bool]): Skip standard (non-hero) when + set to False. + latest (Optional[bool]): Return only latest version of standard + versions. This can be combined only with 'standard' attribute + set to True. + statuses (Optional[Iterable[str]]): Representation statuses used + for filtering. + tags (Optional[Iterable[str]]): Representation tags used + for filtering. + active (Optional[bool]): Receive active/inactive entities. + Both are returned when 'None' is passed. + fields (Optional[Iterable[str]]): Fields to be queried + for version. All possible folder fields are returned + if 'None' is passed. + own_attributes (Optional[bool]): DEPRECATED: Not supported for + versions. + + Returns: + Generator[dict[str, Any]]: Queried version entities. + + """ + if not fields: + fields = self.get_default_fields_for_type("version") + else: + fields = set(fields) + self._prepare_fields("version", fields) + + # Make sure fields have minimum required fields + fields |= {"id", "version"} + + use_rest = False + if "data" in fields and not self.graphql_allows_data_in_query: + use_rest = True + fields = {"id"} + + if active is not None: + fields.add("active") + + if own_attributes is not _PLACEHOLDER: + warnings.warn( + ( + "'own_attributes' is not supported for versions. The" + " argument will be removed form function signature in" + " future (apx. version 1.0.10 or 1.1.0)." + ), + DeprecationWarning + ) + + if not hero and not standard: + return + + filters = { + "projectName": project_name + } + if not _prepare_list_filters( + filters, + ("taskIds", task_ids), + ("versionIds", version_ids), + ("productIds", product_ids), + ("taskIds", task_ids), + ("versions", versions), + ("versionStatuses", statuses), + ("versionTags", tags), + ): + return + + queries = [] + # Add filters based on 'hero' and 'standard' + # NOTE: There is not a filter to "ignore" hero versions or to get + # latest and hero version + # - if latest and hero versions should be returned it must be done in + # 2 graphql queries + if standard and not latest: + # This query all versions standard + hero + # - hero must be filtered out if is not enabled during loop + query = versions_graphql_query(fields) + for attr, filter_value in filters.items(): + query.set_variable_value(attr, filter_value) + queries.append(query) + else: + if hero: + # Add hero query if hero is enabled + hero_query = versions_graphql_query(fields) + for attr, filter_value in filters.items(): + hero_query.set_variable_value(attr, filter_value) + + hero_query.set_variable_value("heroOnly", True) + queries.append(hero_query) + + if standard: + standard_query = versions_graphql_query(fields) + for attr, filter_value in filters.items(): + standard_query.set_variable_value(attr, filter_value) + + if latest: + standard_query.set_variable_value("latestOnly", True) + queries.append(standard_query) + + for query in queries: + for parsed_data in query.continuous_query(self): + for version in parsed_data["project"]["versions"]: + if active is not None and version["active"] is not active: + continue + + if not hero and version["version"] < 0: + continue + + if use_rest: + version = self.get_rest_version( + project_name, version["id"] + ) + else: + self._convert_entity_data(version) + + yield version
+ +
[docs] def get_version_by_id( + self, + project_name, + version_id, + fields=None, + own_attributes=_PLACEHOLDER + ): + """Query version entity by id. + + Args: + project_name (str): Name of project where to look for queried + entities. + version_id (str): Version id. + fields (Optional[Iterable[str]]): Fields that should be returned. + All fields are returned if 'None' is passed. + own_attributes (Optional[bool]): DEPRECATED: Not supported for + versions. + + Returns: + Union[dict, None]: Version entity data or None if was not found. + + """ + versions = self.get_versions( + project_name, + version_ids=[version_id], + active=None, + hero=True, + fields=fields, + own_attributes=own_attributes + ) + for version in versions: + return version + return None
+ +
[docs] def get_version_by_name( + self, + project_name, + version, + product_id, + fields=None, + own_attributes=_PLACEHOLDER + ): + """Query version entity by version and product id. + + Args: + project_name (str): Name of project where to look for queried + entities. + version (int): Version of version entity. + product_id (str): Product id. Product is a parent of version. + fields (Optional[Iterable[str]]): Fields that should be returned. + All fields are returned if 'None' is passed. + own_attributes (Optional[bool]): DEPRECATED: Not supported for + versions. + + Returns: + Union[dict, None]: Version entity data or None if was not found. + + """ + versions = self.get_versions( + project_name, + product_ids=[product_id], + versions=[version], + active=None, + fields=fields, + own_attributes=own_attributes + ) + for version in versions: + return version + return None
+ +
[docs] def get_hero_version_by_id( + self, + project_name, + version_id, + fields=None, + own_attributes=_PLACEHOLDER + ): + """Query hero version entity by id. + + Args: + project_name (str): Name of project where to look for queried + entities. + version_id (int): Hero version id. + fields (Optional[Iterable[str]]): Fields that should be returned. + All fields are returned if 'None' is passed. + own_attributes (Optional[bool]): DEPRECATED: Not supported for + versions. + + Returns: + Union[dict, None]: Version entity data or None if was not found. + + """ + versions = self.get_hero_versions( + project_name, + version_ids=[version_id], + fields=fields, + own_attributes=own_attributes + ) + for version in versions: + return version + return None
+ +
[docs] def get_hero_version_by_product_id( + self, + project_name, + product_id, + fields=None, + own_attributes=_PLACEHOLDER + ): + """Query hero version entity by product id. + + Only one hero version is available on a product. + + Args: + project_name (str): Name of project where to look for queried + entities. + product_id (int): Product id. + fields (Optional[Iterable[str]]): Fields that should be returned. + All fields are returned if 'None' is passed. + own_attributes (Optional[bool]): DEPRECATED: Not supported for + versions. + + Returns: + Union[dict, None]: Version entity data or None if was not found. + + """ + versions = self.get_hero_versions( + project_name, + product_ids=[product_id], + fields=fields, + own_attributes=own_attributes + ) + for version in versions: + return version + return None
+ +
[docs] def get_hero_versions( + self, + project_name, + product_ids=None, + version_ids=None, + active=True, + fields=None, + own_attributes=_PLACEHOLDER + ): + """Query hero versions by multiple filters. + + Only one hero version is available on a product. + + Args: + project_name (str): Name of project where to look for queried + entities. + product_ids (Optional[Iterable[str]]): Product ids. + version_ids (Optional[Iterable[str]]): Version ids. + active (Optional[bool]): Receive active/inactive entities. + Both are returned when 'None' is passed. + fields (Optional[Iterable[str]]): Fields that should be returned. + All fields are returned if 'None' is passed. + own_attributes (Optional[bool]): DEPRECATED: Not supported for + versions. + + Returns: + Union[dict, None]: Version entity data or None if was not found. + + """ + return self.get_versions( + project_name, + version_ids=version_ids, + product_ids=product_ids, + hero=True, + standard=False, + active=active, + fields=fields, + own_attributes=own_attributes + )
+ +
[docs] def get_last_versions( + self, + project_name, + product_ids, + active=True, + fields=None, + own_attributes=_PLACEHOLDER + ): + """Query last version entities by product ids. + + Args: + project_name (str): Project where to look for representation. + product_ids (Iterable[str]): Product ids. + active (Optional[bool]): Receive active/inactive entities. + Both are returned when 'None' is passed. + fields (Optional[Iterable[str]]): fields to be queried + for representations. + own_attributes (Optional[bool]): DEPRECATED: Not supported for + versions. + + Returns: + dict[str, dict[str, Any]]: Last versions by product id. + + """ + if fields: + fields = set(fields) + fields.add("productId") + + versions = self.get_versions( + project_name, + product_ids=product_ids, + latest=True, + hero=False, + active=active, + fields=fields, + own_attributes=own_attributes + ) + return { + version["productId"]: version + for version in versions + }
+ +
[docs] def get_last_version_by_product_id( + self, + project_name, + product_id, + active=True, + fields=None, + own_attributes=_PLACEHOLDER + ): + """Query last version entity by product id. + + Args: + project_name (str): Project where to look for representation. + product_id (str): Product id. + active (Optional[bool]): Receive active/inactive entities. + Both are returned when 'None' is passed. + fields (Optional[Iterable[str]]): fields to be queried + for representations. + own_attributes (Optional[bool]): DEPRECATED: Not supported for + versions. + + Returns: + Union[dict[str, Any], None]: Queried version entity or None. + + """ + versions = self.get_versions( + project_name, + product_ids=[product_id], + latest=True, + hero=False, + active=active, + fields=fields, + own_attributes=own_attributes + ) + for version in versions: + return version + return None
+ +
[docs] def get_last_version_by_product_name( + self, + project_name, + product_name, + folder_id, + active=True, + fields=None, + own_attributes=_PLACEHOLDER + ): + """Query last version entity by product name and folder id. + + Args: + project_name (str): Project where to look for representation. + product_name (str): Product name. + folder_id (str): Folder id. + active (Optional[bool]): Receive active/inactive entities. + Both are returned when 'None' is passed. + fields (Optional[Iterable[str]]): fields to be queried + for representations. + own_attributes (Optional[bool]): DEPRECATED: Not supported for + representations. + + Returns: + Union[dict[str, Any], None]: Queried version entity or None. + + """ + if not folder_id: + return None + + product = self.get_product_by_name( + project_name, product_name, folder_id, fields={"id"} + ) + if not product: + return None + return self.get_last_version_by_product_id( + project_name, + product["id"], + active=active, + fields=fields, + own_attributes=own_attributes + )
+ +
[docs] def version_is_latest(self, project_name, version_id): + """Is version latest from a product. + + Args: + project_name (str): Project where to look for representation. + version_id (str): Version id. + + Returns: + bool: Version is latest or not. + + """ + query = GraphQlQuery("VersionIsLatest") + project_name_var = query.add_variable( + "projectName", "String!", project_name + ) + version_id_var = query.add_variable( + "versionId", "String!", version_id + ) + project_query = query.add_field("project") + project_query.set_filter("name", project_name_var) + version_query = project_query.add_field("version") + version_query.set_filter("id", version_id_var) + product_query = version_query.add_field("product") + latest_version_query = product_query.add_field("latestVersion") + latest_version_query.add_field("id") + + parsed_data = query.query(self) + latest_version = ( + parsed_data["project"]["version"]["product"]["latestVersion"] + ) + return latest_version["id"] == version_id
+ +
[docs] def create_version( + self, + project_name, + version, + product_id, + task_id=None, + author=None, + attrib=None, + data=None, + tags=None, + status=None, + active=None, + thumbnail_id=None, + version_id=None, + ): + """Create new version. + + Args: + project_name (str): Project name. + version (int): Version. + product_id (str): Parent product id. + task_id (Optional[str]): Parent task id. + author (Optional[str]): Version author. + attrib (Optional[dict[str, Any]]): Version attributes. + data (Optional[dict[str, Any]]): Version data. + tags (Optional[Iterable[str]]): Version tags. + status (Optional[str]): Version status. + active (Optional[bool]): Version active state. + thumbnail_id (Optional[str]): Version thumbnail id. + version_id (Optional[str]): Version id. If not passed new id is + generated. + + Returns: + str: Version id. + + """ + if not version_id: + version_id = create_entity_id() + create_data = { + "id": version_id, + "version": version, + "productId": product_id, + } + for key, value in ( + ("taskId", task_id), + ("author", author), + ("attrib", attrib), + ("data", data), + ("tags", tags), + ("status", status), + ("active", active), + ("thumbnailId", thumbnail_id), + ): + if value is not None: + create_data[key] = value + + response = self.post( + "projects/{}/versions".format(project_name), + **create_data + ) + response.raise_for_status() + return version_id
+ +
[docs] def update_version( + self, + project_name, + version_id, + version=None, + product_id=None, + task_id=NOT_SET, + author=None, + attrib=None, + data=None, + tags=None, + status=None, + active=None, + thumbnail_id=NOT_SET, + ): + """Update version entity on server. + + Do not pass ``task_id`` amd ``thumbnail_id`` if you don't + want to change their values. Value ``None`` would unset + their value. + + Update of ``data`` will override existing value on folder entity. + + Update of ``attrib`` does change only passed attributes. If you want + to unset value, use ``None``. + + Args: + project_name (str): Project name. + version_id (str): Version id. + version (Optional[int]): New version. + product_id (Optional[str]): New product id. + task_id (Optional[Union[str, None]]): New task id. + author (Optional[str]): New author username. + attrib (Optional[dict[str, Any]]): New attributes. + data (Optional[dict[str, Any]]): New data. + tags (Optional[Iterable[str]]): New tags. + status (Optional[str]): New status. + active (Optional[bool]): New active state. + thumbnail_id (Optional[Union[str, None]]): New thumbnail id. + + """ + update_data = {} + for key, value in ( + ("version", version), + ("productId", product_id), + ("taskId", task_id), + ("attrib", attrib), + ("data", data), + ("tags", tags), + ("status", status), + ("active", active), + ("author", author), + ): + if value is not None: + update_data[key] = value + + for key, value in ( + ("taskId", task_id), + ("thumbnailId", thumbnail_id), + ): + if value is not NOT_SET: + update_data[key] = value + + response = self.patch( + "projects/{}/versions/{}".format(project_name, version_id), + **update_data + ) + response.raise_for_status()
+ +
[docs] def delete_version(self, project_name, version_id): + """Delete version. + + Args: + project_name (str): Project name. + version_id (str): Version id to delete. + + """ + response = self.delete( + "projects/{}/versions/{}".format(project_name, version_id) + ) + response.raise_for_status()
+ + def _representation_conversion(self, representation): + if "context" in representation: + orig_context = representation["context"] + context = {} + if orig_context and orig_context != "null": + context = json.loads(orig_context) + representation["context"] = context + + repre_files = representation.get("files") + if not repre_files: + return + + for repre_file in repre_files: + repre_file_size = repre_file.get("size") + if repre_file_size is not None: + repre_file["size"] = int(repre_file["size"]) + +
[docs] def get_representations( + self, + project_name, + representation_ids=None, + representation_names=None, + version_ids=None, + names_by_version_ids=None, + statuses=None, + tags=None, + active=True, + has_links=None, + fields=None, + own_attributes=_PLACEHOLDER + ): + """Get representation entities based on passed filters from server. + + .. todo:: + + Add separated function for 'names_by_version_ids' filtering. + Because can't be combined with others. + + Args: + project_name (str): Name of project where to look for versions. + representation_ids (Optional[Iterable[str]]): Representation ids + used for representation filtering. + representation_names (Optional[Iterable[str]]): Representation + names used for representation filtering. + version_ids (Optional[Iterable[str]]): Version ids used for + representation filtering. Versions are parents of + representations. + names_by_version_ids (Optional[Dict[str, Iterable[str]]): Find + representations by names and version ids. This filter + discards all other filters. + statuses (Optional[Iterable[str]]): Representation statuses used + for filtering. + tags (Optional[Iterable[str]]): Representation tags used + for filtering. + active (Optional[bool]): Receive active/inactive entities. + Both are returned when 'None' is passed. + has_links (Optional[Literal[IN, OUT, ANY]]): Filter + representations with IN/OUT/ANY links. + fields (Optional[Iterable[str]]): Fields to be queried for + representation. All possible fields are returned if 'None' is + passed. + own_attributes (Optional[bool]): DEPRECATED: Not supported for + representations. + + Returns: + Generator[dict[str, Any]]: Queried representation entities. + + """ + if not fields: + fields = self.get_default_fields_for_type("representation") + else: + fields = set(fields) + self._prepare_fields("representation", fields) + + use_rest = False + if "data" in fields and not self.graphql_allows_data_in_query: + use_rest = True + fields = {"id"} + + if active is not None: + fields.add("active") + + if own_attributes is not _PLACEHOLDER: + warnings.warn( + ( + "'own_attributes' is not supported for representations. " + "The argument will be removed form function signature in " + "future (apx. version 1.0.10 or 1.1.0)." + ), + DeprecationWarning + ) + + if "files" in fields: + fields.discard("files") + fields |= REPRESENTATION_FILES_FIELDS + + filters = { + "projectName": project_name + } + + if representation_ids is not None: + representation_ids = set(representation_ids) + if not representation_ids: + return + filters["representationIds"] = list(representation_ids) + + version_ids_filter = None + representation_names_filter = None + if names_by_version_ids is not None: + version_ids_filter = set() + representation_names_filter = set() + for version_id, names in names_by_version_ids.items(): + version_ids_filter.add(version_id) + representation_names_filter |= set(names) + + if not version_ids_filter or not representation_names_filter: + return + + else: + if representation_names is not None: + representation_names_filter = set(representation_names) + if not representation_names_filter: + return + + if version_ids is not None: + version_ids_filter = set(version_ids) + if not version_ids_filter: + return + + if version_ids_filter: + filters["versionIds"] = list(version_ids_filter) + + if representation_names_filter: + filters["representationNames"] = list(representation_names_filter) + + if statuses is not None: + statuses = set(statuses) + if not statuses: + return + filters["representationStatuses"] = list(statuses) + + if tags is not None: + tags = set(tags) + if not tags: + return + filters["representationTags"] = list(tags) + + if has_links is not None: + filters["representationHasLinks"] = has_links.upper() + + query = representations_graphql_query(fields) + + for attr, filter_value in filters.items(): + query.set_variable_value(attr, filter_value) + + for parsed_data in query.continuous_query(self): + for repre in parsed_data["project"]["representations"]: + if active is not None and active is not repre["active"]: + continue + + if use_rest: + repre = self.get_rest_representation( + project_name, repre["id"] + ) + else: + self._convert_entity_data(repre) + + self._representation_conversion(repre) + + yield repre
+ +
[docs] def get_representation_by_id( + self, + project_name, + representation_id, + fields=None, + own_attributes=_PLACEHOLDER + ): + """Query representation entity from server based on id filter. + + Args: + project_name (str): Project where to look for representation. + representation_id (str): Id of representation. + fields (Optional[Iterable[str]]): fields to be queried + for representations. + own_attributes (Optional[bool]): DEPRECATED: Not supported for + representations. + + Returns: + Union[dict[str, Any], None]: Queried representation entity or None. + + """ + representations = self.get_representations( + project_name, + representation_ids=[representation_id], + active=None, + fields=fields, + own_attributes=own_attributes + ) + for representation in representations: + return representation + return None
+ +
[docs] def get_representation_by_name( + self, + project_name, + representation_name, + version_id, + fields=None, + own_attributes=_PLACEHOLDER + ): + """Query representation entity by name and version id. + + Args: + project_name (str): Project where to look for representation. + representation_name (str): Representation name. + version_id (str): Version id. + fields (Optional[Iterable[str]]): fields to be queried + for representations. + own_attributes (Optional[bool]): DEPRECATED: Not supported for + representations. + + Returns: + Union[dict[str, Any], None]: Queried representation entity or None. + + """ + representations = self.get_representations( + project_name, + representation_names=[representation_name], + version_ids=[version_id], + active=None, + fields=fields, + own_attributes=own_attributes + ) + for representation in representations: + return representation + return None
+ +
[docs] def get_representations_hierarchy( + self, + project_name, + representation_ids, + project_fields=None, + folder_fields=None, + task_fields=None, + product_fields=None, + version_fields=None, + representation_fields=None, + ): + """Find representation with parents by representation id. + + Representation entity with parent entities up to project. + + Default fields are used when any fields are set to `None`. But it is + possible to pass in empty iterable (list, set, tuple) to skip + entity. + + Args: + project_name (str): Project where to look for entities. + representation_ids (Iterable[str]): Representation ids. + project_fields (Optional[Iterable[str]]): Project fields. + folder_fields (Optional[Iterable[str]]): Folder fields. + task_fields (Optional[Iterable[str]]): Task fields. + product_fields (Optional[Iterable[str]]): Product fields. + version_fields (Optional[Iterable[str]]): Version fields. + representation_fields (Optional[Iterable[str]]): Representation + fields. + + Returns: + dict[str, RepresentationHierarchy]: Parent entities by + representation id. + + """ + if not representation_ids: + return {} + + if project_fields is not None: + project_fields = set(project_fields) + self._prepare_fields("project", project_fields) + + project = {} + if project_fields is None: + project = self.get_project(project_name) + + elif project_fields: + # Keep project as empty dictionary if does not have + # filled any fields + project = self.get_project( + project_name, fields=project_fields + ) + + repre_ids = set(representation_ids) + output = { + repre_id: RepresentationHierarchy( + project, None, None, None, None, None + ) + for repre_id in representation_ids + } + + if folder_fields is None: + folder_fields = self.get_default_fields_for_type("folder") + else: + folder_fields = set(folder_fields) + + if task_fields is None: + task_fields = self.get_default_fields_for_type("task") + else: + task_fields = set(task_fields) + + if product_fields is None: + product_fields = self.get_default_fields_for_type("product") + else: + product_fields = set(product_fields) + + if version_fields is None: + version_fields = self.get_default_fields_for_type("version") + else: + version_fields = set(version_fields) + + if representation_fields is None: + representation_fields = self.get_default_fields_for_type( + "representation" + ) + else: + representation_fields = set(representation_fields) + + for (entity_type, fields) in ( + ("folder", folder_fields), + ("task", task_fields), + ("product", product_fields), + ("version", version_fields), + ("representation", representation_fields), + ): + self._prepare_fields(entity_type, fields) + + representation_fields.add("id") + + query = representations_hierarchy_qraphql_query( + folder_fields, + task_fields, + product_fields, + version_fields, + representation_fields, + ) + query.set_variable_value("projectName", project_name) + query.set_variable_value("representationIds", list(repre_ids)) + + parsed_data = query.query(self) + for repre in parsed_data["project"]["representations"]: + repre_id = repre["id"] + version = repre.pop("version", {}) + product = version.pop("product", {}) + task = version.pop("task", None) + folder = product.pop("folder", {}) + self._convert_entity_data(repre) + self._representation_conversion(repre) + self._convert_entity_data(version) + self._convert_entity_data(product) + self._convert_entity_data(folder) + if task: + self._convert_entity_data(task) + + output[repre_id] = RepresentationHierarchy( + project, folder, task, product, version, repre + ) + + return output
+ +
[docs] def get_representation_hierarchy( + self, + project_name, + representation_id, + project_fields=None, + folder_fields=None, + task_fields=None, + product_fields=None, + version_fields=None, + representation_fields=None, + ): + """Find representation parents by representation id. + + Representation parent entities up to project. + + Args: + project_name (str): Project where to look for entities. + representation_id (str): Representation id. + project_fields (Optional[Iterable[str]]): Project fields. + folder_fields (Optional[Iterable[str]]): Folder fields. + task_fields (Optional[Iterable[str]]): Task fields. + product_fields (Optional[Iterable[str]]): Product fields. + version_fields (Optional[Iterable[str]]): Version fields. + representation_fields (Optional[Iterable[str]]): Representation + fields. + + Returns: + RepresentationHierarchy: Representation hierarchy entities. + + """ + if not representation_id: + return None + + parents_by_repre_id = self.get_representations_hierarchy( + project_name, + [representation_id], + project_fields=project_fields, + folder_fields=folder_fields, + task_fields=task_fields, + product_fields=product_fields, + version_fields=version_fields, + representation_fields=representation_fields, + ) + return parents_by_repre_id[representation_id]
+ +
[docs] def get_representations_parents( + self, + project_name, + representation_ids, + project_fields=None, + folder_fields=None, + product_fields=None, + version_fields=None, + ): + """Find representations parents by representation id. + + Representation parent entities up to project. + + Args: + project_name (str): Project where to look for entities. + representation_ids (Iterable[str]): Representation ids. + project_fields (Optional[Iterable[str]]): Project fields. + folder_fields (Optional[Iterable[str]]): Folder fields. + product_fields (Optional[Iterable[str]]): Product fields. + version_fields (Optional[Iterable[str]]): Version fields. + + Returns: + dict[str, RepresentationParents]: Parent entities by + representation id. + + """ + hierarchy_by_repre_id = self.get_representations_hierarchy( + project_name, + representation_ids, + project_fields=project_fields, + folder_fields=folder_fields, + task_fields=set(), + product_fields=product_fields, + version_fields=version_fields, + representation_fields={"id"}, + ) + return { + repre_id: RepresentationParents( + hierarchy.version, + hierarchy.product, + hierarchy.folder, + hierarchy.project, + ) + for repre_id, hierarchy in hierarchy_by_repre_id.items() + }
+ +
[docs] def get_representation_parents( + self, + project_name, + representation_id, + project_fields=None, + folder_fields=None, + product_fields=None, + version_fields=None, + ): + """Find representation parents by representation id. + + Representation parent entities up to project. + + Args: + project_name (str): Project where to look for entities. + representation_id (str): Representation id. + project_fields (Optional[Iterable[str]]): Project fields. + folder_fields (Optional[Iterable[str]]): Folder fields. + product_fields (Optional[Iterable[str]]): Product fields. + version_fields (Optional[Iterable[str]]): Version fields. + + Returns: + RepresentationParents: Representation parent entities. + + """ + if not representation_id: + return None + + parents_by_repre_id = self.get_representations_parents( + project_name, + [representation_id], + project_fields=project_fields, + folder_fields=folder_fields, + product_fields=product_fields, + version_fields=version_fields, + ) + return parents_by_repre_id[representation_id]
+ +
[docs] def get_repre_ids_by_context_filters( + self, + project_name, + context_filters, + representation_names=None, + version_ids=None + ): + """Find representation ids which match passed context filters. + + Each representation has context integrated on representation entity in + database. The context may contain project, folder, task name or + product name, product type and many more. This implementation gives + option to quickly filter representation based on representation data + in database. + + Context filters have defined structure. To define filter of nested + subfield use dot '.' as delimiter (For example 'task.name'). + Filter values can be regex filters. String or ``re.Pattern`` can + be used. + + Args: + project_name (str): Project where to look for representations. + context_filters (dict[str, list[str]]): Filters of context fields. + representation_names (Optional[Iterable[str]]): Representation + names, can be used as additional filter for representations + by their names. + version_ids (Optional[Iterable[str]]): Version ids, can be used + as additional filter for representations by their parent ids. + + Returns: + list[str]: Representation ids that match passed filters. + + Example: + The function returns just representation ids so if entities are + required for funtionality they must be queried afterwards by + their ids. + >>> project_name = "testProject" + >>> filters = { + ... "task.name": ["[aA]nimation"], + ... "product": [".*[Mm]ain"] + ... } + >>> repre_ids = get_repre_ids_by_context_filters( + ... project_name, filters) + >>> repres = get_representations(project_name, repre_ids) + + """ + if not isinstance(context_filters, dict): + raise TypeError( + "Expected 'dict' got {}".format(str(type(context_filters))) + ) + + filter_body = {} + if representation_names is not None: + if not representation_names: + return [] + filter_body["names"] = list(set(representation_names)) + + if version_ids is not None: + if not version_ids: + return [] + filter_body["versionIds"] = list(set(version_ids)) + + body_context_filters = [] + for key, filters in context_filters.items(): + if not isinstance(filters, (set, list, tuple)): + raise TypeError( + "Expected 'set', 'list', 'tuple' got {}".format( + str(type(filters)))) + + new_filters = set() + for filter_value in filters: + if isinstance(filter_value, PatternType): + filter_value = filter_value.pattern + new_filters.add(filter_value) + + body_context_filters.append({ + "key": key, + "values": list(new_filters) + }) + + response = self.post( + "projects/{}/repreContextFilter".format(project_name), + context=body_context_filters, + **filter_body + ) + response.raise_for_status() + return response.data["ids"]
+ +
[docs] def create_representation( + self, + project_name, + name, + version_id, + files=None, + attrib=None, + data=None, + tags=None, + status=None, + active=None, + representation_id=None, + ): + """Create new representation. + + Args: + project_name (str): Project name. + name (str): Representation name. + version_id (str): Parent version id. + files (Optional[list[dict]]): Representation files information. + attrib (Optional[dict[str, Any]]): Representation attributes. + data (Optional[dict[str, Any]]): Representation data. + tags (Optional[Iterable[str]]): Representation tags. + status (Optional[str]): Representation status. + active (Optional[bool]): Representation active state. + representation_id (Optional[str]): Representation id. If not + passed new id is generated. + + Returns: + str: Representation id. + + """ + if not representation_id: + representation_id = create_entity_id() + create_data = { + "id": representation_id, + "name": name, + "versionId": version_id, + } + for key, value in ( + ("files", files), + ("attrib", attrib), + ("data", data), + ("tags", tags), + ("status", status), + ("active", active), + ): + if value is not None: + create_data[key] = value + + response = self.post( + "projects/{}/representations".format(project_name), + **create_data + ) + response.raise_for_status() + return representation_id
+ +
[docs] def update_representation( + self, + project_name, + representation_id, + name=None, + version_id=None, + files=None, + attrib=None, + data=None, + tags=None, + status=None, + active=None, + ): + """Update representation entity on server. + + Update of ``data`` will override existing value on folder entity. + + Update of ``attrib`` does change only passed attributes. If you want + to unset value, use ``None``. + + Args: + project_name (str): Project name. + representation_id (str): Representation id. + name (Optional[str]): New name. + version_id (Optional[str]): New version id. + files (Optional[list[dict]]): New files + information. + attrib (Optional[dict[str, Any]]): New attributes. + data (Optional[dict[str, Any]]): New data. + tags (Optional[Iterable[str]]): New tags. + status (Optional[str]): New status. + active (Optional[bool]): New active state. + + """ + update_data = {} + for key, value in ( + ("name", name), + ("versionId", version_id), + ("files", files), + ("attrib", attrib), + ("data", data), + ("tags", tags), + ("status", status), + ("active", active), + ): + if value is not None: + update_data[key] = value + + response = self.patch( + "projects/{}/representations/{}".format( + project_name, representation_id + ), + **update_data + ) + response.raise_for_status()
+ +
[docs] def delete_representation(self, project_name, representation_id): + """Delete representation. + + Args: + project_name (str): Project name. + representation_id (str): Representation id to delete. + + """ + response = self.delete( + "projects/{}/representation/{}".format( + project_name, representation_id + ) + ) + response.raise_for_status()
+ +
[docs] def get_workfiles_info( + self, + project_name, + workfile_ids=None, + task_ids=None, + paths=None, + path_regex=None, + statuses=None, + tags=None, + has_links=None, + fields=None, + own_attributes=_PLACEHOLDER + ): + """Workfile info entities by passed filters. + + Args: + project_name (str): Project under which the entity is located. + workfile_ids (Optional[Iterable[str]]): Workfile ids. + task_ids (Optional[Iterable[str]]): Task ids. + paths (Optional[Iterable[str]]): Rootless workfiles paths. + path_regex (Optional[str]): Regex filter for workfile path. + statuses (Optional[Iterable[str]]): Workfile info statuses used + for filtering. + tags (Optional[Iterable[str]]): Workfile info tags used + for filtering. + has_links (Optional[Literal[IN, OUT, ANY]]): Filter + representations with IN/OUT/ANY links. + fields (Optional[Iterable[str]]): Fields to be queried for + representation. All possible fields are returned if 'None' is + passed. + own_attributes (Optional[bool]): DEPRECATED: Not supported for + workfiles. + + Returns: + Generator[dict[str, Any]]: Queried workfile info entites. + + """ + filters = {"projectName": project_name} + if task_ids is not None: + task_ids = set(task_ids) + if not task_ids: + return + filters["taskIds"] = list(task_ids) + + if paths is not None: + paths = set(paths) + if not paths: + return + filters["paths"] = list(paths) + + if path_regex is not None: + filters["workfilePathRegex"] = path_regex + + if workfile_ids is not None: + workfile_ids = set(workfile_ids) + if not workfile_ids: + return + filters["workfileIds"] = list(workfile_ids) + + if statuses is not None: + statuses = set(statuses) + if not statuses: + return + filters["workfileStatuses"] = list(statuses) + + if tags is not None: + tags = set(tags) + if not tags: + return + filters["workfileTags"] = list(tags) + + if has_links is not None: + filters["workfilehasLinks"] = has_links.upper() + + if not fields: + fields = self.get_default_fields_for_type("workfile") + else: + fields = set(fields) + self._prepare_fields("workfile", fields) + + if own_attributes is not _PLACEHOLDER: + warnings.warn( + ( + "'own_attributes' is not supported for workfiles. The" + " argument will be removed form function signature in" + " future (apx. version 1.0.10 or 1.1.0)." + ), + DeprecationWarning + ) + + query = workfiles_info_graphql_query(fields) + + for attr, filter_value in filters.items(): + query.set_variable_value(attr, filter_value) + + for parsed_data in query.continuous_query(self): + for workfile_info in parsed_data["project"]["workfiles"]: + self._convert_entity_data(workfile_info) + yield workfile_info
+ +
[docs] def get_workfile_info( + self, + project_name, + task_id, + path, + fields=None, + own_attributes=_PLACEHOLDER + ): + """Workfile info entity by task id and workfile path. + + Args: + project_name (str): Project under which the entity is located. + task_id (str): Task id. + path (str): Rootless workfile path. + fields (Optional[Iterable[str]]): Fields to be queried for + representation. All possible fields are returned if 'None' is + passed. + own_attributes (Optional[bool]): DEPRECATED: Not supported for + workfiles. + + Returns: + Union[dict[str, Any], None]: Workfile info entity or None. + + """ + if not task_id or not path: + return None + + for workfile_info in self.get_workfiles_info( + project_name, + task_ids=[task_id], + paths=[path], + fields=fields, + own_attributes=own_attributes + ): + return workfile_info + return None
+ +
[docs] def get_workfile_info_by_id( + self, + project_name, + workfile_id, + fields=None, + own_attributes=_PLACEHOLDER + ): + """Workfile info entity by id. + + Args: + project_name (str): Project under which the entity is located. + workfile_id (str): Workfile info id. + fields (Optional[Iterable[str]]): Fields to be queried for + representation. All possible fields are returned if 'None' is + passed. + own_attributes (Optional[bool]): DEPRECATED: Not supported for + workfiles. + + Returns: + Union[dict[str, Any], None]: Workfile info entity or None. + + """ + if not workfile_id: + return None + + for workfile_info in self.get_workfiles_info( + project_name, + workfile_ids=[workfile_id], + fields=fields, + own_attributes=own_attributes + ): + return workfile_info + return None
+ + def _prepare_thumbnail_content(self, project_name, response): + content = None + content_type = response.content_type + + # It is expected the response contains thumbnail id otherwise the + # content cannot be cached and filepath returned + thumbnail_id = response.headers.get("X-Thumbnail-Id") + if thumbnail_id is not None: + content = response.content + + return ThumbnailContent( + project_name, thumbnail_id, content, content_type + ) + +
[docs] def get_thumbnail_by_id(self, project_name, thumbnail_id): + """Get thumbnail from server by id. + + Permissions of thumbnails are related to entities so thumbnails must + be queried per entity. So an entity type and entity type is required + to be passed. + + Notes: + It is recommended to use one of prepared entity type specific + methods 'get_folder_thumbnail', 'get_version_thumbnail' or + 'get_workfile_thumbnail'. + We do recommend pass thumbnail id if you have access to it. Each + entity that allows thumbnails has 'thumbnailId' field, so it + can be queried. + + Args: + project_name (str): Project under which the entity is located. + thumbnail_id (Optional[str]): DEPRECATED Use + 'get_thumbnail_by_id'. + + Returns: + ThumbnailContent: Thumbnail content wrapper. Does not have to be + valid. + + """ + response = self.raw_get( + "projects/{}/thumbnails/{}".format( + project_name, + thumbnail_id + ) + ) + return self._prepare_thumbnail_content(project_name, response)
+ +
[docs] def get_thumbnail( + self, project_name, entity_type, entity_id, thumbnail_id=None + ): + """Get thumbnail from server. + + Permissions of thumbnails are related to entities so thumbnails must + be queried per entity. So an entity type and entity type is required + to be passed. + + Notes: + It is recommended to use one of prepared entity type specific + methods 'get_folder_thumbnail', 'get_version_thumbnail' or + 'get_workfile_thumbnail'. + We do recommend pass thumbnail id if you have access to it. Each + entity that allows thumbnails has 'thumbnailId' field, so it + can be queried. + + Args: + project_name (str): Project under which the entity is located. + entity_type (str): Entity type which passed entity id represents. + entity_id (str): Entity id for which thumbnail should be returned. + thumbnail_id (Optional[str]): DEPRECATED Use + 'get_thumbnail_by_id'. + + Returns: + ThumbnailContent: Thumbnail content wrapper. Does not have to be + valid. + + """ + if thumbnail_id: + return self.get_thumbnail_by_id(project_name, thumbnail_id) + + if entity_type in ( + "folder", + "version", + "workfile", + ): + entity_type += "s" + + response = self.raw_get("projects/{}/{}/{}/thumbnail".format( + project_name, + entity_type, + entity_id + )) + return self._prepare_thumbnail_content(project_name, response)
+ +
[docs] def get_folder_thumbnail( + self, project_name, folder_id, thumbnail_id=None + ): + """Prepared method to receive thumbnail for folder entity. + + Args: + project_name (str): Project under which the entity is located. + folder_id (str): Folder id for which thumbnail should be returned. + thumbnail_id (Optional[str]): Prepared thumbnail id from entity. + Used only to check if thumbnail was already cached. + + Returns: + Union[str, None]: Path to downloaded thumbnail or none if entity + does not have any (or if user does not have permissions). + + """ + return self.get_thumbnail( + project_name, "folder", folder_id, thumbnail_id + )
+ +
[docs] def get_version_thumbnail( + self, project_name, version_id, thumbnail_id=None + ): + """Prepared method to receive thumbnail for version entity. + + Args: + project_name (str): Project under which the entity is located. + version_id (str): Version id for which thumbnail should be + returned. + thumbnail_id (Optional[str]): Prepared thumbnail id from entity. + Used only to check if thumbnail was already cached. + + Returns: + Union[str, None]: Path to downloaded thumbnail or none if entity + does not have any (or if user does not have permissions). + + """ + return self.get_thumbnail( + project_name, "version", version_id, thumbnail_id + )
+ +
[docs] def get_workfile_thumbnail( + self, project_name, workfile_id, thumbnail_id=None + ): + """Prepared method to receive thumbnail for workfile entity. + + Args: + project_name (str): Project under which the entity is located. + workfile_id (str): Worfile id for which thumbnail should be + returned. + thumbnail_id (Optional[str]): Prepared thumbnail id from entity. + Used only to check if thumbnail was already cached. + + Returns: + Union[str, None]: Path to downloaded thumbnail or none if entity + does not have any (or if user does not have permissions). + + """ + return self.get_thumbnail( + project_name, "workfile", workfile_id, thumbnail_id + )
+ +
[docs] def create_thumbnail(self, project_name, src_filepath, thumbnail_id=None): + """Create new thumbnail on server from passed path. + + Args: + project_name (str): Project where the thumbnail will be created + and can be used. + src_filepath (str): Filepath to thumbnail which should be uploaded. + thumbnail_id (Optional[str]): Prepared if of thumbnail. + + Returns: + str: Created thumbnail id. + + Raises: + ValueError: When thumbnail source cannot be processed. + + """ + if not os.path.exists(src_filepath): + raise ValueError("Entered filepath does not exist.") + + if thumbnail_id: + self.update_thumbnail( + project_name, + thumbnail_id, + src_filepath + ) + return thumbnail_id + + mime_type = get_media_mime_type(src_filepath) + response = self.upload_file( + "projects/{}/thumbnails".format(project_name), + src_filepath, + request_type=RequestTypes.post, + headers={"Content-Type": mime_type}, + ) + response.raise_for_status() + return response.json()["id"]
+ +
[docs] def update_thumbnail(self, project_name, thumbnail_id, src_filepath): + """Change thumbnail content by id. + + Update can be also used to create new thumbnail. + + Args: + project_name (str): Project where the thumbnail will be created + and can be used. + thumbnail_id (str): Thumbnail id to update. + src_filepath (str): Filepath to thumbnail which should be uploaded. + + Raises: + ValueError: When thumbnail source cannot be processed. + + """ + if not os.path.exists(src_filepath): + raise ValueError("Entered filepath does not exist.") + + mime_type = get_media_mime_type(src_filepath) + response = self.upload_file( + "projects/{}/thumbnails/{}".format(project_name, thumbnail_id), + src_filepath, + request_type=RequestTypes.put, + headers={"Content-Type": mime_type}, + ) + response.raise_for_status()
+ +
[docs] def create_project( + self, + project_name, + project_code, + library_project=False, + preset_name=None + ): + """Create project using AYON settings. + + This project creation function is not validating project entity on + creation. It is because project entity is created blindly with only + minimum required information about project which is name and code. + + Entered project name must be unique and project must not exist yet. + + Note: + This function is here to be OP v4 ready but in v3 has more logic + to do. That's why inner imports are in the body. + + Args: + project_name (str): New project name. Should be unique. + project_code (str): Project's code should be unique too. + library_project (Optional[bool]): Project is library project. + preset_name (Optional[str]): Name of anatomy preset. Default is + used if not passed. + + Raises: + ValueError: When project name already exists. + + Returns: + dict[str, Any]: Created project entity. + + + """ + if self.get_project(project_name): + raise ValueError("Project with name \"{}\" already exists".format( + project_name + )) + + if not PROJECT_NAME_REGEX.match(project_name): + raise ValueError(( + "Project name \"{}\" contain invalid characters" + ).format(project_name)) + + preset = self.get_project_anatomy_preset(preset_name) + + result = self.post( + "projects", + name=project_name, + code=project_code, + anatomy=preset, + library=library_project + ) + + if result.status != 201: + details = "Unknown details ({})".format(result.status) + if result.data: + details = result.data.get("detail") or details + raise ValueError("Failed to create project \"{}\": {}".format( + project_name, details + )) + + return self.get_project(project_name)
+ +
[docs] def update_project( + self, + project_name, + library=None, + folder_types=None, + task_types=None, + link_types=None, + statuses=None, + tags=None, + config=None, + attrib=None, + data=None, + active=None, + project_code=None, + **changes + ): + """Update project entity on server. + + Args: + project_name (str): Name of project. + library (Optional[bool]): Change library state. + folder_types (Optional[list[dict[str, Any]]]): Folder type + definitions. + task_types (Optional[list[dict[str, Any]]]): Task type + definitions. + link_types (Optional[list[dict[str, Any]]]): Link type + definitions. + statuses (Optional[list[dict[str, Any]]]): Status definitions. + tags (Optional[list[dict[str, Any]]]): List of tags available to + set on entities. + config (Optional[dict[dict[str, Any]]]): Project anatomy config + with templates and roots. + attrib (Optional[dict[str, Any]]): Project attributes to change. + data (Optional[dict[str, Any]]): Custom data of a project. This + value will 100% override project data. + active (Optional[bool]): Change active state of a project. + project_code (Optional[str]): Change project code. Not recommended + during production. + **changes: Other changed keys based on Rest API documentation. + + """ + changes.update({ + key: value + for key, value in ( + ("library", library), + ("folderTypes", folder_types), + ("taskTypes", task_types), + ("linkTypes", link_types), + ("statuses", statuses), + ("tags", tags), + ("config", config), + ("attrib", attrib), + ("data", data), + ("active", active), + ("code", project_code), + ) + if value is not None + }) + response = self.patch( + "projects/{}".format(project_name), + **changes + ) + response.raise_for_status()
+ +
[docs] def delete_project(self, project_name): + """Delete project from server. + + This will completely remove project from server without any step back. + + Args: + project_name (str): Project name that will be removed. + + """ + if not self.get_project(project_name): + raise ValueError("Project with name \"{}\" was not found".format( + project_name + )) + + result = self.delete("projects/{}".format(project_name)) + if result.status_code != 204: + raise ValueError( + "Failed to delete project \"{}\". {}".format( + project_name, result.data["detail"] + ) + )
+ + # --- Links --- + + + + + + + + + + + + + + + + + def _prepare_link_filters( + self, filters, link_types, link_direction, link_names, link_name_regex + ): + """Add links filters for GraphQl queries. + + Args: + filters (dict[str, Any]): Object where filters will be added. + link_types (Union[Iterable[str], None]): Link types filters. + link_direction (Union[Literal["in", "out"], None]): Direction of + link "in", "out" or 'None' for both. + link_names (Union[Iterable[str], None]): Link name filters. + link_name_regex (Union[str, None]): Regex filter for link name. + + Returns: + bool: Links are valid, and query from server can happen. + + """ + if link_types is not None: + link_types = set(link_types) + if not link_types: + return False + filters["linkTypes"] = list(link_types) + + if link_names is not None: + link_names = set(link_names) + if not link_names: + return False + filters["linkNames"] = list(link_names) + + if link_direction is not None: + if link_direction not in ("in", "out"): + return False + filters["linkDirection"] = link_direction + + if link_name_regex is not None: + filters["linkNameRegex"] = link_name_regex + return True + + + + + + + + + + + + + + + + + + + + + + + + # --- Batch operations processing --- +
[docs] def send_batch_operations( + self, + project_name, + operations, + can_fail=False, + raise_on_fail=True + ): + """Post multiple CRUD operations to server. + + When multiple changes should be made on server side this is the best + way to go. It is possible to pass multiple operations to process on a + server side and do the changes in a transaction. + + Args: + project_name (str): On which project should be operations + processed. + operations (list[dict[str, Any]]): Operations to be processed. + can_fail (Optional[bool]): Server will try to process all + operations even if one of them fails. + raise_on_fail (Optional[bool]): Raise exception if an operation + fails. You can handle failed operations on your own + when set to 'False'. + + Raises: + ValueError: Operations can't be converted to json string. + FailedOperations: When output does not contain server operations + or 'raise_on_fail' is enabled and any operation fails. + + Returns: + list[dict[str, Any]]: Operations result with process details. + + """ + return self._send_batch_operations( + f"projects/{project_name}/operations", + operations, + can_fail, + raise_on_fail, + )
+ +
[docs] def send_activities_batch_operations( + self, + project_name, + operations, + can_fail=False, + raise_on_fail=True + ): + """Post multiple CRUD activities operations to server. + + When multiple changes should be made on server side this is the best + way to go. It is possible to pass multiple operations to process on a + server side and do the changes in a transaction. + + Args: + project_name (str): On which project should be operations + processed. + operations (list[dict[str, Any]]): Operations to be processed. + can_fail (Optional[bool]): Server will try to process all + operations even if one of them fails. + raise_on_fail (Optional[bool]): Raise exception if an operation + fails. You can handle failed operations on your own + when set to 'False'. + + Raises: + ValueError: Operations can't be converted to json string. + FailedOperations: When output does not contain server operations + or 'raise_on_fail' is enabled and any operation fails. + + Returns: + list[dict[str, Any]]: Operations result with process details. + + """ + return self._send_batch_operations( + f"projects/{project_name}/operations/activities", + operations, + can_fail, + raise_on_fail, + )
+ + def _send_batch_operations( + self, + uri: str, + operations: List[Dict[str, Any]], + can_fail: bool, + raise_on_fail: bool + ): + if not operations: + return [] + + body_by_id = {} + operations_body = [] + for operation in operations: + if not operation: + continue + + op_id = operation.get("id") + if not op_id: + op_id = create_entity_id() + operation["id"] = op_id + + try: + body = json.loads( + json.dumps(operation, default=entity_data_json_default) + ) + except (TypeError, ValueError): + raise ValueError("Couldn't json parse body: {}".format( + json.dumps( + operation, indent=4, default=failed_json_default + ) + )) + + body_by_id[op_id] = body + operations_body.append(body) + + if not operations_body: + return [] + + result = self.post( + uri, + operations=operations_body, + canFail=can_fail + ) + + op_results = result.get("operations") + if op_results is None: + raise FailedOperations( + "Operation failed. Content: {}".format(str(result)) + ) + + if result.get("success") or not raise_on_fail: + return op_results + + for op_result in op_results: + if not op_result["success"]: + operation_id = op_result["id"] + raise FailedOperations(( + "Operation \"{}\" failed with data:\n{}\nDetail: {}." + ).format( + operation_id, + json.dumps(body_by_id[operation_id], indent=4), + op_result["detail"], + )) + return op_results + + def _prepare_fields(self, entity_type, fields, own_attributes=False): + if not fields: + return + + if "attrib" in fields: + fields.remove("attrib") + fields |= self.get_attributes_fields_for_type(entity_type) + + if own_attributes and entity_type in {"project", "folder", "task"}: + fields.add("ownAttrib") + + if entity_type == "project": + if "folderTypes" in fields: + fields.remove("folderTypes") + fields |= { + "folderTypes.{}".format(name) + for name in self.get_default_fields_for_type("folderType") + } + + if "taskTypes" in fields: + fields.remove("taskTypes") + fields |= { + "taskTypes.{}".format(name) + for name in self.get_default_fields_for_type("taskType") + } + + if "productTypes" in fields: + fields.remove("productTypes") + fields |= { + "productTypes.{}".format(name) + for name in self.get_default_fields_for_type( + "productType" + ) + } + + def _convert_entity_data(self, entity): + if not entity or "data" not in entity: + return + + entity_data = entity["data"] or {} + if isinstance(entity_data, str): + entity_data = json.loads(entity_data) + + entity["data"] = entity_data
+
+ +
+ +
+
+ + +
+

+ + © Copyright 2024, ynput.io +
+ + + + Built with Sphinx using a + + theme + + provided by Revitron. + +

+
+ +
+ +
+
+ +
+ +
+ + + + + + + + + + + + \ No newline at end of file diff --git a/_modules/ayon_api/utils.html b/_modules/ayon_api/utils.html new file mode 100644 index 0000000000..7cac06ba99 --- /dev/null +++ b/_modules/ayon_api/utils.html @@ -0,0 +1,1115 @@ + + + + + + + + + + + ayon_api.utils — ayon-python-api 1.0.11 documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + +
+ + + + + +
+ +
+ +
+ + + +
+
+
+ +

Source code for ayon_api.utils

+import os
+import re
+import datetime
+import uuid
+import string
+import platform
+import collections
+from urllib.parse import urlparse, urlencode
+from typing import Optional, Dict, Any
+from enum import IntEnum
+
+import requests
+import unidecode
+
+from .constants import (
+    SERVER_TIMEOUT_ENV_KEY,
+    DEFAULT_VARIANT_ENV_KEY,
+    SITE_ID_ENV_KEY,
+)
+from .exceptions import UrlError
+
+REMOVED_VALUE = object()
+NOT_SET = object()
+SLUGIFY_WHITELIST = string.ascii_letters + string.digits
+SLUGIFY_SEP_WHITELIST = " ,./\\;:!|*^#@~+-_="
+
+RepresentationParents = collections.namedtuple(
+    "RepresentationParents",
+    ("version", "product", "folder", "project")
+)
+
+RepresentationHierarchy = collections.namedtuple(
+    "RepresentationHierarchy",
+    (
+        "project",
+        "folder",
+        "task",
+        "product",
+        "version",
+        "representation",
+    )
+)
+
+
+
[docs]class SortOrder(IntEnum): + """Sort order for GraphQl requests.""" + ascending = 0 + descending = 1 + +
[docs] @classmethod + def parse_value(cls, value, default=None): + if value in (cls.ascending, "ascending", "asc"): + return cls.ascending + if value in (cls.descending, "descending", "desc"): + return cls.descending + return default
+ + +
[docs]def get_default_timeout(): + """Default value for requests timeout. + + First looks for environment variable SERVER_TIMEOUT_ENV_KEY which + can affect timeout value. If not available then use 10.0 s. + + Returns: + float: Timeout value in seconds. + + """ + try: + return float(os.environ.get(SERVER_TIMEOUT_ENV_KEY)) + except (ValueError, TypeError): + pass + return 10.0
+ + +
[docs]def get_default_settings_variant(): + """Default settings variant. + + Returns: + str: Settings variant from environment variable or 'production'. + + """ + return os.environ.get(DEFAULT_VARIANT_ENV_KEY) or "production"
+ + +
[docs]def get_default_site_id(): + """Site id used for server connection. + + Returns: + Union[str, None]: Site id from environment variable or None. + + """ + return os.environ.get(SITE_ID_ENV_KEY)
+ + +
[docs]class ThumbnailContent: + """Wrapper for thumbnail content. + + Args: + project_name (str): Project name. + thumbnail_id (Union[str, None]): Thumbnail id. + content_type (Union[str, None]): Content type e.g. 'image/png'. + content (Union[bytes, None]): Thumbnail content. + + """ + def __init__(self, project_name, thumbnail_id, content, content_type): + self.project_name = project_name + self.thumbnail_id = thumbnail_id + self.content_type = content_type + self.content = content or b"" + + @property + def id(self): + """Wrapper for thumbnail id.""" + return self.thumbnail_id + + @property + def is_valid(self): + """Content of thumbnail is valid. + + Returns: + bool: Content is valid and can be used. + + """ + return ( + self.thumbnail_id is not None + and self.content_type is not None + )
+ + +
[docs]def prepare_query_string(key_values): + """Prepare data to query string. + + If there are any values a query starting with '?' is returned otherwise + an empty string. + + Args: + dict[str, Any]: Query values. + + Returns: + str: Query string. + + """ + if not key_values: + return "" + return "?{}".format(urlencode(key_values))
+ + +
[docs]def create_entity_id(): + return uuid.uuid1().hex
+ + +
[docs]def convert_entity_id(entity_id): + if not entity_id: + return None + + if isinstance(entity_id, uuid.UUID): + return entity_id.hex + + try: + return uuid.UUID(entity_id).hex + + except (TypeError, ValueError, AttributeError): + pass + return None
+ + +
[docs]def convert_or_create_entity_id(entity_id=None): + output = convert_entity_id(entity_id) + if output is None: + output = create_entity_id() + return output
+ + +
[docs]def entity_data_json_default(value): + if isinstance(value, datetime.datetime): + return int(value.timestamp()) + + raise TypeError( + "Object of type {} is not JSON serializable".format(str(type(value))) + )
+ + +
[docs]def slugify_string( + input_string, + separator="_", + slug_whitelist=SLUGIFY_WHITELIST, + split_chars=SLUGIFY_SEP_WHITELIST, + min_length=1, + lower=False, + make_set=False, +): + """Slugify a text string. + + This function removes transliterates input string to ASCII, removes + special characters and use join resulting elements using + specified separator. + + Args: + input_string (str): Input string to slugify + separator (str): A string used to separate returned elements + (default: "_") + slug_whitelist (str): Characters allowed in the output + (default: ascii letters, digits and the separator) + split_chars (str): Set of characters used for word splitting + (there is a sane default) + lower (bool): Convert to lower-case (default: False) + make_set (bool): Return "set" object instead of string. + min_length (int): Minimal length of an element (word). + + Returns: + Union[str, Set[str]]: Based on 'make_set' value returns slugified + string. + + """ + tmp_string = unidecode.unidecode(input_string) + if lower: + tmp_string = tmp_string.lower() + + parts = [ + # Remove all characters that are not in whitelist + re.sub("[^{}]".format(re.escape(slug_whitelist)), "", part) + # Split text into part by split characters + for part in re.split("[{}]".format(re.escape(split_chars)), tmp_string) + ] + # Filter text parts by length + filtered_parts = [ + part + for part in parts + if len(part) >= min_length + ] + if make_set: + return set(filtered_parts) + return separator.join(filtered_parts)
+ + +
[docs]def failed_json_default(value): + return "< Failed value {} > {}".format(type(value), str(value))
+ + +
[docs]def prepare_attribute_changes(old_entity, new_entity, replace=False): + attrib_changes = {} + new_attrib = new_entity.get("attrib") + old_attrib = old_entity.get("attrib") + if new_attrib is None: + if not replace: + return attrib_changes + new_attrib = {} + + if old_attrib is None: + return new_attrib + + for attr, new_attr_value in new_attrib.items(): + old_attr_value = old_attrib.get(attr) + if old_attr_value != new_attr_value: + attrib_changes[attr] = new_attr_value + + if replace: + for attr in old_attrib: + if attr not in new_attrib: + attrib_changes[attr] = REMOVED_VALUE + + return attrib_changes
+ + +
[docs]def prepare_entity_changes(old_entity, new_entity, replace=False): + """Prepare changes of entities.""" + changes = {} + for key, new_value in new_entity.items(): + if key == "attrib": + continue + + old_value = old_entity.get(key) + if old_value != new_value: + changes[key] = new_value + + if replace: + for key in old_entity: + if key not in new_entity: + changes[key] = REMOVED_VALUE + + attr_changes = prepare_attribute_changes(old_entity, new_entity, replace) + if attr_changes: + changes["attrib"] = attr_changes + return changes
+ + +def _try_parse_url(url): + try: + return urlparse(url) + except BaseException: + return None + + +def _try_connect_to_server(url, timeout=None): + if timeout is None: + timeout = get_default_timeout() + try: + # TODO add validation if the url lead to AYON server + # - this won't validate if the url lead to 'google.com' + requests.get(url, timeout=timeout) + + except BaseException: + return False + return True + + +
[docs]def login_to_server(url, username, password, timeout=None): + """Use login to the server to receive token. + + Args: + url (str): Server url. + username (str): User's username. + password (str): User's password. + timeout (Optional[float]): Timeout for request. Value from + 'get_default_timeout' is used if not specified. + + Returns: + Union[str, None]: User's token if login was successfull. + Otherwise 'None'. + + """ + if timeout is None: + timeout = get_default_timeout() + headers = {"Content-Type": "application/json"} + response = requests.post( + "{}/api/auth/login".format(url), + headers=headers, + json={ + "name": username, + "password": password + }, + timeout=timeout, + ) + token = None + # 200 - success + # 401 - invalid credentials + # * - other issues + if response.status_code == 200: + token = response.json()["token"] + return token
+ + +
[docs]def logout_from_server(url, token, timeout=None): + """Logout from server and throw token away. + + Args: + url (str): Url from which should be logged out. + token (str): Token which should be used to log out. + timeout (Optional[float]): Timeout for request. Value from + 'get_default_timeout' is used if not specified. + + """ + if timeout is None: + timeout = get_default_timeout() + headers = { + "Content-Type": "application/json", + "Authorization": "Bearer {}".format(token) + } + requests.post( + url + "/api/auth/logout", + headers=headers, + timeout=timeout, + )
+ + +
[docs]def get_user_by_token(url, token, timeout=None): + """Get user information by url and token. + + Args: + url (str): Server url. + token (str): User's token. + timeout (Optional[float]): Timeout for request. Value from + 'get_default_timeout' is used if not specified. + + Returns: + Optional[Dict[str, Any]]: User information if url and token are valid. + + """ + if timeout is None: + timeout = get_default_timeout() + + base_headers = { + "Content-Type": "application/json", + } + for header_value in ( + {"Authorization": "Bearer {}".format(token)}, + {"X-Api-Key": token}, + ): + headers = base_headers.copy() + headers.update(header_value) + response = requests.get( + "{}/api/users/me".format(url), + headers=headers, + timeout=timeout, + ) + if response.status_code == 200: + return response.json() + return None
+ + +
[docs]def is_token_valid(url, token, timeout=None): + """Check if token is valid. + + Token can be a user token or service api key. + + Args: + url (str): Server url. + token (str): User's token. + timeout (Optional[float]): Timeout for request. Value from + 'get_default_timeout' is used if not specified. + + Returns: + bool: True if token is valid. + + """ + if get_user_by_token(url, token, timeout=timeout): + return True + return False
+ + +
[docs]def validate_url(url, timeout=None): + """Validate url if is valid and server is available. + + Validation checks if can be parsed as url and contains scheme. + + Function will try to autofix url thus will return modified url when + connection to server works. + + .. highlight:: python + .. code-block:: python + + my_url = "my.server.url" + try: + # Store new url + validated_url = validate_url(my_url) + + except UrlError: + # Handle invalid url + ... + + Args: + url (str): Server url. + timeout (Optional[int]): Timeout in seconds for connection to server. + + Returns: + Url which was used to connect to server. + + Raises: + UrlError: Error with short description and hints for user. + + """ + stripperd_url = url.strip() + if not stripperd_url: + raise UrlError( + "Invalid url format. Url is empty.", + title="Invalid url format", + hints=["url seems to be empty"] + ) + + # Not sure if this is good idea? + modified_url = stripperd_url.rstrip("/") + parsed_url = _try_parse_url(modified_url) + universal_hints = [ + "does the url work in browser?" + ] + if parsed_url is None: + raise UrlError( + "Invalid url format. Url cannot be parsed as url \"{}\".".format( + modified_url + ), + title="Invalid url format", + hints=universal_hints + ) + + # Try add 'https://' scheme if is missing + # - this will trigger UrlError if both will crash + if not parsed_url.scheme: + new_url = "https://" + modified_url + if _try_connect_to_server(new_url, timeout=timeout): + return new_url + + if _try_connect_to_server(modified_url, timeout=timeout): + return modified_url + + hints = [] + if "/" in parsed_url.path or not parsed_url.scheme: + new_path = parsed_url.path.split("/")[0] + if not parsed_url.scheme: + new_path = "https://" + new_path + + hints.append( + "did you mean \"{}\"?".format(parsed_url.scheme + new_path) + ) + + raise UrlError( + "Couldn't connect to server on \"{}\"".format(url), + title="Couldn't connect to server", + hints=hints + universal_hints + )
+ + +
[docs]class TransferProgress: + """Object to store progress of download/upload from/to server.""" + + def __init__(self): + self._started = False + self._transfer_done = False + self._transferred = 0 + self._content_size = None + + self._failed = False + self._fail_reason = None + + self._source_url = "N/A" + self._destination_url = "N/A" + +
[docs] def get_content_size(self): + """Content size in bytes. + + Returns: + Union[int, None]: Content size in bytes or None + if is unknown. + + """ + return self._content_size
+ +
[docs] def set_content_size(self, content_size): + """Set content size in bytes. + + Args: + content_size (int): Content size in bytes. + + Raises: + ValueError: If content size was already set. + + """ + if self._content_size is not None: + raise ValueError("Content size was set more then once") + self._content_size = content_size
+ +
[docs] def get_started(self): + """Transfer was started. + + Returns: + bool: True if transfer started. + + """ + return self._started
+ +
[docs] def set_started(self): + """Mark that transfer started. + + Raises: + ValueError: If transfer was already started. + + """ + if self._started: + raise ValueError("Progress already started") + self._started = True
+ +
[docs] def get_transfer_done(self): + """Transfer finished. + + Returns: + bool: Transfer finished. + + """ + return self._transfer_done
+ +
[docs] def set_transfer_done(self): + """Mark progress as transfer finished. + + Raises: + ValueError: If progress was already marked as done + or wasn't started yet. + + """ + if self._transfer_done: + raise ValueError("Progress was already marked as done") + if not self._started: + raise ValueError("Progress didn't start yet") + self._transfer_done = True
+ +
[docs] def get_failed(self): + """Transfer failed. + + Returns: + bool: True if transfer failed. + + """ + return self._failed
+ +
[docs] def get_fail_reason(self): + """Get reason why transfer failed. + + Returns: + Union[str, None]: Reason why transfer + failed or None. + + """ + return self._fail_reason
+ +
[docs] def set_failed(self, reason): + """Mark progress as failed. + + Args: + reason (str): Reason why transfer failed. + + """ + self._fail_reason = reason + self._failed = True
+ +
[docs] def get_transferred_size(self): + """Already transferred size in bytes. + + Returns: + int: Already transferred size in bytes. + + """ + return self._transferred
+ +
[docs] def set_transferred_size(self, transferred): + """Set already transferred size in bytes. + + Args: + transferred (int): Already transferred size in bytes. + + """ + self._transferred = transferred
+ +
[docs] def add_transferred_chunk(self, chunk_size): + """Add transferred chunk size in bytes. + + Args: + chunk_size (int): Add transferred chunk size + in bytes. + + """ + self._transferred += chunk_size
+ +
[docs] def get_source_url(self): + """Source url from where transfer happens. + + Note: + Consider this as title. Must be set using + 'set_source_url' or 'N/A' will be returned. + + Returns: + str: Source url from where transfer happens. + + """ + return self._source_url
+ +
[docs] def set_source_url(self, url): + """Set source url from where transfer happens. + + Args: + url (str): Source url from where transfer happens. + + """ + self._source_url = url
+ +
[docs] def get_destination_url(self): + """Destination url where transfer happens. + + Note: + Consider this as title. Must be set using + 'set_source_url' or 'N/A' will be returned. + + Returns: + str: Destination url where transfer happens. + + """ + return self._destination_url
+ +
[docs] def set_destination_url(self, url): + """Set destination url where transfer happens. + + Args: + url (str): Destination url where transfer happens. + + """ + self._destination_url = url
+ + @property + def is_running(self): + """Check if transfer is running. + + Returns: + bool: True if transfer is running. + + """ + if ( + not self.started + or self.transfer_done + or self.failed + ): + return False + return True + + @property + def transfer_progress(self): + """Get transfer progress in percents. + + Returns: + Union[float, None]: Transfer progress in percents or 'None' + if content size is unknown. + + """ + if self._content_size is None: + return None + return (self._transferred * 100.0) / float(self._content_size) + + content_size = property(get_content_size, set_content_size) + started = property(get_started) + transfer_done = property(get_transfer_done) + failed = property(get_failed) + fail_reason = property(get_fail_reason) + source_url = property(get_source_url, set_source_url) + destination_url = property(get_destination_url, set_destination_url) + transferred_size = property(get_transferred_size, set_transferred_size)
+ + +
[docs]def create_dependency_package_basename(platform_name=None): + """Create basename for dependency package file. + + Args: + platform_name (Optional[str]): Name of platform for which the + bundle is targeted. Default value is current platform. + + Returns: + str: Dependency package name with timestamp and platform. + + """ + if platform_name is None: + platform_name = platform.system().lower() + + now_date = datetime.datetime.now() + time_stamp = now_date.strftime("%y%m%d%H%M") + return "ayon_{}_{}".format(time_stamp, platform_name)
+ + + +def _get_media_mime_type_from_ftyp(content): + if content[8:10] == b"qt" or content[8:12] == b"MSNV": + return "video/quicktime" + + if content[8:12] in (b"3g2a", b"3g2b", b"3g2c", b"KDDI"): + return "video/3gpp2" + + if content[8:12] in ( + b"isom", b"iso2", b"avc1", b"F4V", b"F4P", b"F4A", b"F4B", b"mmp4", + # These might be "video/mp4v" + b"mp41", b"mp42", + # Nero + b"NDSC", b"NDSH", b"NDSM", b"NDSP", b"NDSS", b"NDXC", b"NDXH", + b"NDXM", b"NDXP", b"NDXS", + ): + return "video/mp4" + + if content[8:12] in ( + b"3ge6", b"3ge7", b"3gg6", + b"3gp1", b"3gp2", b"3gp3", b"3gp4", b"3gp5", b"3gp6", b"3gs7", + ): + return "video/3gpp" + + if content[8:11] == b"JP2": + return "image/jp2" + + if content[8:11] == b"jpm": + return "image/jpm" + + if content[8:11] == b"jpx": + return "image/jpx" + + if content[8:12] in (b"M4V\x20", b"M4VH", b"M4VP"): + return "video/x-m4v" + + if content[8:12] in (b"mj2s", b"mjp2"): + return "video/mj2" + return None + + +def _get_media_mime_type_for_content_base(content: bytes) -> Optional[str]: + """Determine Mime-Type of a file. + + Use header of the file to determine mime type (needs 12 bytes). + """ + content_len = len(content) + # Pre-validation (largest definition check) + # - hopefully there cannot be media defined in less than 12 bytes + if content_len < 12: + return None + + # FTYP + if content[4:8] == b"ftyp": + return _get_media_mime_type_from_ftyp(content) + + # BMP + if content[0:2] == b"BM": + return "image/bmp" + + # Tiff + if content[0:2] in (b"MM", b"II"): + return "tiff" + + # PNG + if content[0:4] == b"\211PNG": + return "image/png" + + # JPEG, JFIF or Exif + if ( + content[0:4] == b"\xff\xd8\xff\xdb" + or content[6:10] in (b"JFIF", b"Exif") + ): + return "image/jpeg" + + # Webp + if content[0:4] == b"RIFF" and content[8:12] == b"WEBP": + return "image/webp" + + # Gif + if content[0:6] in (b"GIF87a", b"GIF89a"): + return "gif" + + # Adobe PhotoShop file (8B > Adobe, PS > PhotoShop) + if content[0:4] == b"8BPS": + return "image/vnd.adobe.photoshop" + + # Windows ICO > this might be wild guess as multiple files can start + # with this header + if content[0:4] == b"\x00\x00\x01\x00": + return "image/x-icon" + return None + + +def _get_svg_mime_type(content: bytes) -> Optional[str]: + # SVG + if b'xmlns="http://www.w3.org/2000/svg"' in content: + return "image/svg+xml" + return None + + +
[docs]def get_media_mime_type_for_content(content: bytes) -> Optional[str]: + mime_type = _get_media_mime_type_for_content_base(content) + if mime_type is not None: + return mime_type + return _get_svg_mime_type(content)
+ + +
[docs]def get_media_mime_type_for_stream(stream) -> Optional[str]: + # Read only 12 bytes to determine mime type + content = stream.read(12) + if len(content) < 12: + return None + mime_type = _get_media_mime_type_for_content_base(content) + if mime_type is None: + content += stream.read() + mime_type = _get_svg_mime_type(content) + return mime_type
+ + +
[docs]def get_media_mime_type(filepath: str) -> Optional[str]: + """Determine Mime-Type of a file. + + Args: + filepath (str): Path to file. + + Returns: + Optional[str]: Mime type or None if is unknown mime type. + + """ + if not filepath or not os.path.exists(filepath): + return None + + with open(filepath, "rb") as stream: + return get_media_mime_type_for_stream(stream)
+ + +
[docs]def take_web_action_event( + server_url: str, + action_token: str +) -> Dict[str, Any]: + """Take web action event using action token. + + Action token is generated by AYON server and passed to AYON launcher. + + Args: + server_url (str): AYON server url. + action_token (str): Action token. + + Returns: + Dict[str, Any]: Web action event. + + """ + response = requests.get( + f"{server_url}/api/actions/take/{action_token}" + ) + response.raise_for_status() + return response.json()
+ + +
[docs]def abort_web_action_event( + server_url: str, + action_token: str, + reason: str +): + """Abort web action event using action token. + + A web action event could not be processed for some reason. + + Args: + server_url (str): AYON server url. + action_token (str): Action token. + reason (str): Reason why webaction event was aborted. + + Returns: + requests.Response: Response from server. + + """ + response = requests.post( + f"{server_url}/api/actions/abort/{action_token}", + json={"message": reason}, + ) + response.raise_for_status() + return response
+
+ +
+ +
+
+ + +
+

+ + © Copyright 2024, ynput.io +
+ + + + Built with Sphinx using a + + theme + + provided by Revitron. + +

+
+ +
+ +
+
+ +
+ +
+ + + + + + + + + + + + \ No newline at end of file diff --git a/_modules/index.html b/_modules/index.html new file mode 100644 index 0000000000..7b2ca62905 --- /dev/null +++ b/_modules/index.html @@ -0,0 +1,195 @@ + + + + + + + + + + + Overview: module code — ayon-python-api 1.0.11 documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + +
+ + + + + +
+ +
+ +
+ + + +
+ +
+ + +
+

+ + © Copyright 2024, ynput.io +
+ + + + Built with Sphinx using a + + theme + + provided by Revitron. + +

+
+ +
+ +
+
+ +
+ +
+ + + + + + + + + + + + \ No newline at end of file diff --git a/_sources/ayon_api.constants.rst.txt b/_sources/ayon_api.constants.rst.txt new file mode 100644 index 0000000000..d63b56c656 --- /dev/null +++ b/_sources/ayon_api.constants.rst.txt @@ -0,0 +1,7 @@ +ayon\_api.constants module +========================== + +.. automodule:: ayon_api.constants + :members: + :undoc-members: + :show-inheritance: diff --git a/_sources/ayon_api.entity_hub.rst.txt b/_sources/ayon_api.entity_hub.rst.txt new file mode 100644 index 0000000000..052cd2d4a6 --- /dev/null +++ b/_sources/ayon_api.entity_hub.rst.txt @@ -0,0 +1,7 @@ +ayon\_api.entity\_hub module +============================ + +.. automodule:: ayon_api.entity_hub + :members: + :undoc-members: + :show-inheritance: diff --git a/_sources/ayon_api.events.rst.txt b/_sources/ayon_api.events.rst.txt new file mode 100644 index 0000000000..6eabc0961f --- /dev/null +++ b/_sources/ayon_api.events.rst.txt @@ -0,0 +1,7 @@ +ayon\_api.events module +======================= + +.. automodule:: ayon_api.events + :members: + :undoc-members: + :show-inheritance: diff --git a/_sources/ayon_api.exceptions.rst.txt b/_sources/ayon_api.exceptions.rst.txt new file mode 100644 index 0000000000..ed6f5f3ba0 --- /dev/null +++ b/_sources/ayon_api.exceptions.rst.txt @@ -0,0 +1,7 @@ +ayon\_api.exceptions module +=========================== + +.. automodule:: ayon_api.exceptions + :members: + :undoc-members: + :show-inheritance: diff --git a/_sources/ayon_api.graphql.rst.txt b/_sources/ayon_api.graphql.rst.txt new file mode 100644 index 0000000000..44fd8860af --- /dev/null +++ b/_sources/ayon_api.graphql.rst.txt @@ -0,0 +1,7 @@ +ayon\_api.graphql module +======================== + +.. automodule:: ayon_api.graphql + :members: + :undoc-members: + :show-inheritance: diff --git a/_sources/ayon_api.graphql_queries.rst.txt b/_sources/ayon_api.graphql_queries.rst.txt new file mode 100644 index 0000000000..e660dbb6e0 --- /dev/null +++ b/_sources/ayon_api.graphql_queries.rst.txt @@ -0,0 +1,7 @@ +ayon\_api.graphql\_queries module +================================= + +.. automodule:: ayon_api.graphql_queries + :members: + :undoc-members: + :show-inheritance: diff --git a/_sources/ayon_api.operations.rst.txt b/_sources/ayon_api.operations.rst.txt new file mode 100644 index 0000000000..cf982f50c0 --- /dev/null +++ b/_sources/ayon_api.operations.rst.txt @@ -0,0 +1,7 @@ +ayon\_api.operations module +=========================== + +.. automodule:: ayon_api.operations + :members: + :undoc-members: + :show-inheritance: diff --git a/_sources/ayon_api.rst.txt b/_sources/ayon_api.rst.txt new file mode 100644 index 0000000000..48b1501443 --- /dev/null +++ b/_sources/ayon_api.rst.txt @@ -0,0 +1,24 @@ +ayon\_api package +================= + +.. automodule:: ayon_api + :members: + :undoc-members: + :show-inheritance: + +Submodules +---------- + +.. toctree:: + :maxdepth: 4 + + ayon_api.constants + ayon_api.entity_hub + ayon_api.events + ayon_api.exceptions + ayon_api.graphql + ayon_api.graphql_queries + ayon_api.operations + ayon_api.server_api + ayon_api.utils + ayon_api.version diff --git a/_sources/ayon_api.server_api.rst.txt b/_sources/ayon_api.server_api.rst.txt new file mode 100644 index 0000000000..4f524bafb0 --- /dev/null +++ b/_sources/ayon_api.server_api.rst.txt @@ -0,0 +1,7 @@ +ayon\_api.server\_api module +============================ + +.. automodule:: ayon_api.server_api + :members: + :undoc-members: + :show-inheritance: diff --git a/_sources/ayon_api.utils.rst.txt b/_sources/ayon_api.utils.rst.txt new file mode 100644 index 0000000000..68f7d95ed7 --- /dev/null +++ b/_sources/ayon_api.utils.rst.txt @@ -0,0 +1,7 @@ +ayon\_api.utils module +====================== + +.. automodule:: ayon_api.utils + :members: + :undoc-members: + :show-inheritance: diff --git a/_sources/ayon_api.version.rst.txt b/_sources/ayon_api.version.rst.txt new file mode 100644 index 0000000000..bb54ab1107 --- /dev/null +++ b/_sources/ayon_api.version.rst.txt @@ -0,0 +1,7 @@ +ayon\_api.version module +======================== + +.. automodule:: ayon_api.version + :members: + :undoc-members: + :show-inheritance: diff --git a/_sources/index.rst.txt b/_sources/index.rst.txt new file mode 100644 index 0000000000..ea769b4f61 --- /dev/null +++ b/_sources/index.rst.txt @@ -0,0 +1,68 @@ +Welcome to AYON Python API documentation! +========================================= + +.. container:: .image + + .. image:: ./_static/AYON_blackG_dot.svg + +.. container:: .large + + This is mainly auto-generated documentation for the AYON Python API. + +.. container:: .buttons + + `Python API Reference <./ayon_api.html>`_ + `REST API `_ + `All AYON Docs `_ + + +Getting Started +=============== + +.. code-block:: text + :caption: Install latest version from PyPi + + pip install ayon-python-api + +.. code-block:: text + :caption: Install from Github sources (Alternatively) + + git clone git@github.com:ynput/ayon-python-api.git + cd ayon-python-api + pip install . + +.. code-block:: text + :caption: Ensure installed properly by printing ayon_api version + + python -c "import ayon_api ; print(ayon_api.__version__)" + + +Python API +========== + +* `API Reference <./ayon_api.html>`_ +------------------------------------ + +* `Github Repository `_ +------------------------------------------------------------------ + +Miscellaneous +============= + +* :ref:`genindex` +----------------- + +* :ref:`modindex` +----------------- + +* :ref:`search` +--------------- + + +Summary +======= + +.. toctree:: + :maxdepth: 4 + + modules \ No newline at end of file diff --git a/_sources/modules.rst.txt b/_sources/modules.rst.txt new file mode 100644 index 0000000000..c8961c8681 --- /dev/null +++ b/_sources/modules.rst.txt @@ -0,0 +1,7 @@ +ayon_api +======== + +.. toctree:: + :maxdepth: 4 + + ayon_api diff --git a/_static/AYON_blackG_dot.svg b/_static/AYON_blackG_dot.svg new file mode 100644 index 0000000000..badc20685b --- /dev/null +++ b/_static/AYON_blackG_dot.svg @@ -0,0 +1,41 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/_static/_sphinx_javascript_frameworks_compat.js b/_static/_sphinx_javascript_frameworks_compat.js new file mode 100644 index 0000000000..8549469dc2 --- /dev/null +++ b/_static/_sphinx_javascript_frameworks_compat.js @@ -0,0 +1,134 @@ +/* + * _sphinx_javascript_frameworks_compat.js + * ~~~~~~~~~~ + * + * Compatability shim for jQuery and underscores.js. + * + * WILL BE REMOVED IN Sphinx 6.0 + * xref RemovedInSphinx60Warning + * + */ + +/** + * select a different prefix for underscore + */ +$u = _.noConflict(); + + +/** + * small helper function to urldecode strings + * + * See https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/decodeURIComponent#Decoding_query_parameters_from_a_URL + */ +jQuery.urldecode = function(x) { + if (!x) { + return x + } + return decodeURIComponent(x.replace(/\+/g, ' ')); +}; + +/** + * small helper function to urlencode strings + */ +jQuery.urlencode = encodeURIComponent; + +/** + * This function returns the parsed url parameters of the + * current request. Multiple values per key are supported, + * it will always return arrays of strings for the value parts. + */ +jQuery.getQueryParameters = function(s) { + if (typeof s === 'undefined') + s = document.location.search; + var parts = s.substr(s.indexOf('?') + 1).split('&'); + var result = {}; + for (var i = 0; i < parts.length; i++) { + var tmp = parts[i].split('=', 2); + var key = jQuery.urldecode(tmp[0]); + var value = jQuery.urldecode(tmp[1]); + if (key in result) + result[key].push(value); + else + result[key] = [value]; + } + return result; +}; + +/** + * highlight a given string on a jquery object by wrapping it in + * span elements with the given class name. + */ +jQuery.fn.highlightText = function(text, className) { + function highlight(node, addItems) { + if (node.nodeType === 3) { + var val = node.nodeValue; + var pos = val.toLowerCase().indexOf(text); + if (pos >= 0 && + !jQuery(node.parentNode).hasClass(className) && + !jQuery(node.parentNode).hasClass("nohighlight")) { + var span; + var isInSVG = jQuery(node).closest("body, svg, foreignObject").is("svg"); + if (isInSVG) { + span = document.createElementNS("http://www.w3.org/2000/svg", "tspan"); + } else { + span = document.createElement("span"); + span.className = className; + } + span.appendChild(document.createTextNode(val.substr(pos, text.length))); + node.parentNode.insertBefore(span, node.parentNode.insertBefore( + document.createTextNode(val.substr(pos + text.length)), + node.nextSibling)); + node.nodeValue = val.substr(0, pos); + if (isInSVG) { + var rect = document.createElementNS("http://www.w3.org/2000/svg", "rect"); + var bbox = node.parentElement.getBBox(); + rect.x.baseVal.value = bbox.x; + rect.y.baseVal.value = bbox.y; + rect.width.baseVal.value = bbox.width; + rect.height.baseVal.value = bbox.height; + rect.setAttribute('class', className); + addItems.push({ + "parent": node.parentNode, + "target": rect}); + } + } + } + else if (!jQuery(node).is("button, select, textarea")) { + jQuery.each(node.childNodes, function() { + highlight(this, addItems); + }); + } + } + var addItems = []; + var result = this.each(function() { + highlight(this, addItems); + }); + for (var i = 0; i < addItems.length; ++i) { + jQuery(addItems[i].parent).before(addItems[i].target); + } + return result; +}; + +/* + * backward compatibility for jQuery.browser + * This will be supported until firefox bug is fixed. + */ +if (!jQuery.browser) { + jQuery.uaMatch = function(ua) { + ua = ua.toLowerCase(); + + var match = /(chrome)[ \/]([\w.]+)/.exec(ua) || + /(webkit)[ \/]([\w.]+)/.exec(ua) || + /(opera)(?:.*version|)[ \/]([\w.]+)/.exec(ua) || + /(msie) ([\w.]+)/.exec(ua) || + ua.indexOf("compatible") < 0 && /(mozilla)(?:.*? rv:([\w.]+)|)/.exec(ua) || + []; + + return { + browser: match[ 1 ] || "", + version: match[ 2 ] || "0" + }; + }; + jQuery.browser = {}; + jQuery.browser[jQuery.uaMatch(navigator.userAgent).browser] = true; +} diff --git a/_static/basic.css b/_static/basic.css new file mode 100644 index 0000000000..4e9a9f1fac --- /dev/null +++ b/_static/basic.css @@ -0,0 +1,900 @@ +/* + * basic.css + * ~~~~~~~~~ + * + * Sphinx stylesheet -- basic theme. + * + * :copyright: Copyright 2007-2022 by the Sphinx team, see AUTHORS. + * :license: BSD, see LICENSE for details. + * + */ + +/* -- main layout ----------------------------------------------------------- */ + +div.clearer { + clear: both; +} + +div.section::after { + display: block; + content: ''; + clear: left; +} + +/* -- relbar ---------------------------------------------------------------- */ + +div.related { + width: 100%; + font-size: 90%; +} + +div.related h3 { + display: none; +} + +div.related ul { + margin: 0; + padding: 0 0 0 10px; + list-style: none; +} + +div.related li { + display: inline; +} + +div.related li.right { + float: right; + margin-right: 5px; +} + +/* -- sidebar --------------------------------------------------------------- */ + +div.sphinxsidebarwrapper { + padding: 10px 5px 0 10px; +} + +div.sphinxsidebar { + float: left; + width: 230px; + margin-left: -100%; + font-size: 90%; + word-wrap: break-word; + overflow-wrap : break-word; +} + +div.sphinxsidebar ul { + list-style: none; +} + +div.sphinxsidebar ul ul, +div.sphinxsidebar ul.want-points { + margin-left: 20px; + list-style: square; +} + +div.sphinxsidebar ul ul { + margin-top: 0; + margin-bottom: 0; +} + +div.sphinxsidebar form { + margin-top: 10px; +} + +div.sphinxsidebar input { + border: 1px solid #98dbcc; + font-family: sans-serif; + font-size: 1em; +} + +div.sphinxsidebar #searchbox form.search { + overflow: hidden; +} + +div.sphinxsidebar #searchbox input[type="text"] { + float: left; + width: 80%; + padding: 0.25em; + box-sizing: border-box; +} + +div.sphinxsidebar #searchbox input[type="submit"] { + float: left; + width: 20%; + border-left: none; + padding: 0.25em; + box-sizing: border-box; +} + + +img { + border: 0; + max-width: 100%; +} + +/* -- search page ----------------------------------------------------------- */ + +ul.search { + margin: 10px 0 0 20px; + padding: 0; +} + +ul.search li { + padding: 5px 0 5px 20px; + background-image: url(file.png); + background-repeat: no-repeat; + background-position: 0 7px; +} + +ul.search li a { + font-weight: bold; +} + +ul.search li p.context { + color: #888; + margin: 2px 0 0 30px; + text-align: left; +} + +ul.keywordmatches li.goodmatch a { + font-weight: bold; +} + +/* -- index page ------------------------------------------------------------ */ + +table.contentstable { + width: 90%; + margin-left: auto; + margin-right: auto; +} + +table.contentstable p.biglink { + line-height: 150%; +} + +a.biglink { + font-size: 1.3em; +} + +span.linkdescr { + font-style: italic; + padding-top: 5px; + font-size: 90%; +} + +/* -- general index --------------------------------------------------------- */ + +table.indextable { + width: 100%; +} + +table.indextable td { + text-align: left; + vertical-align: top; +} + +table.indextable ul { + margin-top: 0; + margin-bottom: 0; + list-style-type: none; +} + +table.indextable > tbody > tr > td > ul { + padding-left: 0em; +} + +table.indextable tr.pcap { + height: 10px; +} + +table.indextable tr.cap { + margin-top: 10px; + background-color: #f2f2f2; +} + +img.toggler { + margin-right: 3px; + margin-top: 3px; + cursor: pointer; +} + +div.modindex-jumpbox { + border-top: 1px solid #ddd; + border-bottom: 1px solid #ddd; + margin: 1em 0 1em 0; + padding: 0.4em; +} + +div.genindex-jumpbox { + border-top: 1px solid #ddd; + border-bottom: 1px solid #ddd; + margin: 1em 0 1em 0; + padding: 0.4em; +} + +/* -- domain module index --------------------------------------------------- */ + +table.modindextable td { + padding: 2px; + border-collapse: collapse; +} + +/* -- general body styles --------------------------------------------------- */ + +div.body { + min-width: 360px; + max-width: 800px; +} + +div.body p, div.body dd, div.body li, div.body blockquote { + -moz-hyphens: auto; + -ms-hyphens: auto; + -webkit-hyphens: auto; + hyphens: auto; +} + +a.headerlink { + visibility: hidden; +} + +h1:hover > a.headerlink, +h2:hover > a.headerlink, +h3:hover > a.headerlink, +h4:hover > a.headerlink, +h5:hover > a.headerlink, +h6:hover > a.headerlink, +dt:hover > a.headerlink, +caption:hover > a.headerlink, +p.caption:hover > a.headerlink, +div.code-block-caption:hover > a.headerlink { + visibility: visible; +} + +div.body p.caption { + text-align: inherit; +} + +div.body td { + text-align: left; +} + +.first { + margin-top: 0 !important; +} + +p.rubric { + margin-top: 30px; + font-weight: bold; +} + +img.align-left, figure.align-left, .figure.align-left, object.align-left { + clear: left; + float: left; + margin-right: 1em; +} + +img.align-right, figure.align-right, .figure.align-right, object.align-right { + clear: right; + float: right; + margin-left: 1em; +} + +img.align-center, figure.align-center, .figure.align-center, object.align-center { + display: block; + margin-left: auto; + margin-right: auto; +} + +img.align-default, figure.align-default, .figure.align-default { + display: block; + margin-left: auto; + margin-right: auto; +} + +.align-left { + text-align: left; +} + +.align-center { + text-align: center; +} + +.align-default { + text-align: center; +} + +.align-right { + text-align: right; +} + +/* -- sidebars -------------------------------------------------------------- */ + +div.sidebar, +aside.sidebar { + margin: 0 0 0.5em 1em; + border: 1px solid #ddb; + padding: 7px; + background-color: #ffe; + width: 40%; + float: right; + clear: right; + overflow-x: auto; +} + +p.sidebar-title { + font-weight: bold; +} +nav.contents, +aside.topic, +div.admonition, div.topic, blockquote { + clear: left; +} + +/* -- topics ---------------------------------------------------------------- */ +nav.contents, +aside.topic, +div.topic { + border: 1px solid #ccc; + padding: 7px; + margin: 10px 0 10px 0; +} + +p.topic-title { + font-size: 1.1em; + font-weight: bold; + margin-top: 10px; +} + +/* -- admonitions ----------------------------------------------------------- */ + +div.admonition { + margin-top: 10px; + margin-bottom: 10px; + padding: 7px; +} + +div.admonition dt { + font-weight: bold; +} + +p.admonition-title { + margin: 0px 10px 5px 0px; + font-weight: bold; +} + +div.body p.centered { + text-align: center; + margin-top: 25px; +} + +/* -- content of sidebars/topics/admonitions -------------------------------- */ + +div.sidebar > :last-child, +aside.sidebar > :last-child, +nav.contents > :last-child, +aside.topic > :last-child, +div.topic > :last-child, +div.admonition > :last-child { + margin-bottom: 0; +} + +div.sidebar::after, +aside.sidebar::after, +nav.contents::after, +aside.topic::after, +div.topic::after, +div.admonition::after, +blockquote::after { + display: block; + content: ''; + clear: both; +} + +/* -- tables ---------------------------------------------------------------- */ + +table.docutils { + margin-top: 10px; + margin-bottom: 10px; + border: 0; + border-collapse: collapse; +} + +table.align-center { + margin-left: auto; + margin-right: auto; +} + +table.align-default { + margin-left: auto; + margin-right: auto; +} + +table caption span.caption-number { + font-style: italic; +} + +table caption span.caption-text { +} + +table.docutils td, table.docutils th { + padding: 1px 8px 1px 5px; + border-top: 0; + border-left: 0; + border-right: 0; + border-bottom: 1px solid #aaa; +} + +th { + text-align: left; + padding-right: 5px; +} + +table.citation { + border-left: solid 1px gray; + margin-left: 1px; +} + +table.citation td { + border-bottom: none; +} + +th > :first-child, +td > :first-child { + margin-top: 0px; +} + +th > :last-child, +td > :last-child { + margin-bottom: 0px; +} + +/* -- figures --------------------------------------------------------------- */ + +div.figure, figure { + margin: 0.5em; + padding: 0.5em; +} + +div.figure p.caption, figcaption { + padding: 0.3em; +} + +div.figure p.caption span.caption-number, +figcaption span.caption-number { + font-style: italic; +} + +div.figure p.caption span.caption-text, +figcaption span.caption-text { +} + +/* -- field list styles ----------------------------------------------------- */ + +table.field-list td, table.field-list th { + border: 0 !important; +} + +.field-list ul { + margin: 0; + padding-left: 1em; +} + +.field-list p { + margin: 0; +} + +.field-name { + -moz-hyphens: manual; + -ms-hyphens: manual; + -webkit-hyphens: manual; + hyphens: manual; +} + +/* -- hlist styles ---------------------------------------------------------- */ + +table.hlist { + margin: 1em 0; +} + +table.hlist td { + vertical-align: top; +} + +/* -- object description styles --------------------------------------------- */ + +.sig { + font-family: 'Consolas', 'Menlo', 'DejaVu Sans Mono', 'Bitstream Vera Sans Mono', monospace; +} + +.sig-name, code.descname { + background-color: transparent; + font-weight: bold; +} + +.sig-name { + font-size: 1.1em; +} + +code.descname { + font-size: 1.2em; +} + +.sig-prename, code.descclassname { + background-color: transparent; +} + +.optional { + font-size: 1.3em; +} + +.sig-paren { + font-size: larger; +} + +.sig-param.n { + font-style: italic; +} + +/* C++ specific styling */ + +.sig-inline.c-texpr, +.sig-inline.cpp-texpr { + font-family: unset; +} + +.sig.c .k, .sig.c .kt, +.sig.cpp .k, .sig.cpp .kt { + color: #0033B3; +} + +.sig.c .m, +.sig.cpp .m { + color: #1750EB; +} + +.sig.c .s, .sig.c .sc, +.sig.cpp .s, .sig.cpp .sc { + color: #067D17; +} + + +/* -- other body styles ----------------------------------------------------- */ + +ol.arabic { + list-style: decimal; +} + +ol.loweralpha { + list-style: lower-alpha; +} + +ol.upperalpha { + list-style: upper-alpha; +} + +ol.lowerroman { + list-style: lower-roman; +} + +ol.upperroman { + list-style: upper-roman; +} + +:not(li) > ol > li:first-child > :first-child, +:not(li) > ul > li:first-child > :first-child { + margin-top: 0px; +} + +:not(li) > ol > li:last-child > :last-child, +:not(li) > ul > li:last-child > :last-child { + margin-bottom: 0px; +} + +ol.simple ol p, +ol.simple ul p, +ul.simple ol p, +ul.simple ul p { + margin-top: 0; +} + +ol.simple > li:not(:first-child) > p, +ul.simple > li:not(:first-child) > p { + margin-top: 0; +} + +ol.simple p, +ul.simple p { + margin-bottom: 0; +} +aside.footnote > span, +div.citation > span { + float: left; +} +aside.footnote > span:last-of-type, +div.citation > span:last-of-type { + padding-right: 0.5em; +} +aside.footnote > p { + margin-left: 2em; +} +div.citation > p { + margin-left: 4em; +} +aside.footnote > p:last-of-type, +div.citation > p:last-of-type { + margin-bottom: 0em; +} +aside.footnote > p:last-of-type:after, +div.citation > p:last-of-type:after { + content: ""; + clear: both; +} + +dl.field-list { + display: grid; + grid-template-columns: fit-content(30%) auto; +} + +dl.field-list > dt { + font-weight: bold; + word-break: break-word; + padding-left: 0.5em; + padding-right: 5px; +} + +dl.field-list > dd { + padding-left: 0.5em; + margin-top: 0em; + margin-left: 0em; + margin-bottom: 0em; +} + +dl { + margin-bottom: 15px; +} + +dd > :first-child { + margin-top: 0px; +} + +dd ul, dd table { + margin-bottom: 10px; +} + +dd { + margin-top: 3px; + margin-bottom: 10px; + margin-left: 30px; +} + +dl > dd:last-child, +dl > dd:last-child > :last-child { + margin-bottom: 0; +} + +dt:target, span.highlighted { + background-color: #fbe54e; +} + +rect.highlighted { + fill: #fbe54e; +} + +dl.glossary dt { + font-weight: bold; + font-size: 1.1em; +} + +.versionmodified { + font-style: italic; +} + +.system-message { + background-color: #fda; + padding: 5px; + border: 3px solid red; +} + +.footnote:target { + background-color: #ffa; +} + +.line-block { + display: block; + margin-top: 1em; + margin-bottom: 1em; +} + +.line-block .line-block { + margin-top: 0; + margin-bottom: 0; + margin-left: 1.5em; +} + +.guilabel, .menuselection { + font-family: sans-serif; +} + +.accelerator { + text-decoration: underline; +} + +.classifier { + font-style: oblique; +} + +.classifier:before { + font-style: normal; + margin: 0 0.5em; + content: ":"; + display: inline-block; +} + +abbr, acronym { + border-bottom: dotted 1px; + cursor: help; +} + +/* -- code displays --------------------------------------------------------- */ + +pre { + overflow: auto; + overflow-y: hidden; /* fixes display issues on Chrome browsers */ +} + +pre, div[class*="highlight-"] { + clear: both; +} + +span.pre { + -moz-hyphens: none; + -ms-hyphens: none; + -webkit-hyphens: none; + hyphens: none; + white-space: nowrap; +} + +div[class*="highlight-"] { + margin: 1em 0; +} + +td.linenos pre { + border: 0; + background-color: transparent; + color: #aaa; +} + +table.highlighttable { + display: block; +} + +table.highlighttable tbody { + display: block; +} + +table.highlighttable tr { + display: flex; +} + +table.highlighttable td { + margin: 0; + padding: 0; +} + +table.highlighttable td.linenos { + padding-right: 0.5em; +} + +table.highlighttable td.code { + flex: 1; + overflow: hidden; +} + +.highlight .hll { + display: block; +} + +div.highlight pre, +table.highlighttable pre { + margin: 0; +} + +div.code-block-caption + div { + margin-top: 0; +} + +div.code-block-caption { + margin-top: 1em; + padding: 2px 5px; + font-size: small; +} + +div.code-block-caption code { + background-color: transparent; +} + +table.highlighttable td.linenos, +span.linenos, +div.highlight span.gp { /* gp: Generic.Prompt */ + user-select: none; + -webkit-user-select: text; /* Safari fallback only */ + -webkit-user-select: none; /* Chrome/Safari */ + -moz-user-select: none; /* Firefox */ + -ms-user-select: none; /* IE10+ */ +} + +div.code-block-caption span.caption-number { + padding: 0.1em 0.3em; + font-style: italic; +} + +div.code-block-caption span.caption-text { +} + +div.literal-block-wrapper { + margin: 1em 0; +} + +code.xref, a code { + background-color: transparent; + font-weight: bold; +} + +h1 code, h2 code, h3 code, h4 code, h5 code, h6 code { + background-color: transparent; +} + +.viewcode-link { + float: right; +} + +.viewcode-back { + float: right; + font-family: sans-serif; +} + +div.viewcode-block:target { + margin: -1px -10px; + padding: 0 10px; +} + +/* -- math display ---------------------------------------------------------- */ + +img.math { + vertical-align: middle; +} + +div.body div.math p { + text-align: center; +} + +span.eqno { + float: right; +} + +span.eqno a.headerlink { + position: absolute; + z-index: 1; +} + +div.math:hover a.headerlink { + visibility: visible; +} + +/* -- printout stylesheet --------------------------------------------------- */ + +@media print { + div.document, + div.documentwrapper, + div.bodywrapper { + margin: 0 !important; + width: 100%; + } + + div.sphinxsidebar, + div.related, + div.footer, + #top-link { + display: none; + } +} \ No newline at end of file diff --git a/_static/css/badge_only.css b/_static/css/badge_only.css new file mode 100644 index 0000000000..9ab5eb9501 --- /dev/null +++ b/_static/css/badge_only.css @@ -0,0 +1 @@ +.fa:before{-webkit-font-smoothing:antialiased}.clearfix{*zoom:1}.clearfix:after,.clearfix:before{display:table;content:""}.clearfix:after{clear:both}@font-face{font-family:FontAwesome;font-style:normal;font-weight:400;src:url(fonts/fontawesome-webfont.eot?#iefix) format("embedded-opentype"),url(fonts/fontawesome-webfont.woff2) format("woff2"),url(fonts/fontawesome-webfont.woff) format("woff"),url(fonts/fontawesome-webfont.ttf) format("truetype"),url(fonts/fontawesome-webfont.svg#FontAwesome) format("svg")}.fa:before{font-family:FontAwesome;font-style:normal;font-weight:400;line-height:1}.fa:before,a .fa{text-decoration:inherit}.fa:before,a .fa,li .fa{display:inline-block}li .fa-large:before{width:1.875em}ul.fas{list-style-type:none;margin-left:2em;text-indent:-.8em}ul.fas li .fa{width:.8em}ul.fas li .fa-large:before{vertical-align:baseline}.fa-book:before,.icon-book:before{content:""}.fa-caret-down:before,.icon-caret-down:before{content:""}.fa-caret-up:before,.icon-caret-up:before{content:""}.fa-caret-left:before,.icon-caret-left:before{content:""}.fa-caret-right:before,.icon-caret-right:before{content:""}.rst-versions,.rst-versions.rst-badge{position:fixed;bottom:0!important;left:0!important;width:calc(23rem - 1px)!important;max-width:calc(23rem - 1px)!important;color:var(--nav)!important;background:var(--bg-nav)!important;border-top:1px solid var(--border)!important;font-family:Inter,sans-serif;z-index:400;line-height:30px;box-shadow:1px 0 0 var(--border)}.rst-versions.rst-badge a,.rst-versions a{color:var(--nav)!important;text-decoration:none}.rst-versions .rst-badge-small,.rst-versions.rst-badge .rst-badge-small{display:none}.rst-versions.rst-badge .rst-current-version,.rst-versions .rst-current-version{padding:.4rem .7rem!important;background-color:var(--bg-nav)!important;display:block!important;text-align:right!important;font-size:.9rem!important;cursor:pointer;color:var(--nav)!important;line-height:30px!important;height:auto!important}.rst-versions.rst-badge .rst-current-version:after,.rst-versions .rst-current-version:after{clear:both;content:"";display:block}.rst-versions.rst-badge .rst-current-version .fa,.rst-versions .rst-current-version .fa{color:var(--nav)!important}.rst-versions.rst-badge .rst-current-version .fa-book,.rst-versions .rst-current-version .fa-book{font-size:inherit;line-height:inherit;float:left!important}.rst-versions.rst-badge .rst-current-version.rst-out-of-date,.rst-versions .rst-current-version.rst-out-of-date{background-color:var(--nav)!important;color:var(--bg)!important}.rst-versions.rst-badge .rst-current-version.rst-active-old-version,.rst-versions .rst-current-version.rst-active-old-version{background-color:var(--nav)!important;color:var(--nav)!important}.rst-versions.rst-badge.shift-up,.rst-versions.shift-up{height:auto!important;max-height:100%}.rst-versions.rst-badge.shift-up .rst-other-versions,.rst-versions.shift-up .rst-other-versions{display:block}.rst-versions.rst-badge .rst-other-versions,.rst-versions .rst-other-versions{font-size:.9rem;padding:.4rem .7rem;color:var(--nav)!important;display:none}.rst-versions.rst-badge .rst-other-versions hr,.rst-versions .rst-other-versions hr{display:block;height:1px;border:0;margin:20px 0;padding:0;border-top:1px solid var(--border)!important}.rst-versions.rst-badge .rst-other-versions dd,.rst-versions .rst-other-versions dd{display:inline-block;margin:0}.rst-versions.rst-badge .rst-other-versions dd a,.rst-versions .rst-other-versions dd a{display:inline-block;padding:.4em;line-height:1.5;color:var(--nav)!important}@media screen and (max-width:63.75rem){.rst-versions{display:none}.rst-versions.shift{display:block}} \ No newline at end of file diff --git a/_static/css/fonts/fira-mono-latin-400.woff b/_static/css/fonts/fira-mono-latin-400.woff new file mode 100644 index 0000000000000000000000000000000000000000..0c32bb9bf9adc1ef5d74603d85f44a631081c386 GIT binary patch literal 21024 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 J?42jy007t*hRgr} literal 0 HcmV?d00001 diff --git a/_static/css/fonts/fira-mono-latin-400.woff2 b/_static/css/fonts/fira-mono-latin-400.woff2 new file mode 100644 index 0000000000000000000000000000000000000000..93eeae93bb60daac55bce528b8d0af012bc4f79f GIT binary patch literal 16836 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

>qY6htNCf1<|h9F|xnW{4?tOM*eJc2&8A zWSg2y8Ec+xl>u#XInde)$mMMS(7J2g)+Uqp7w5CfxIDI}IFHMvU#Tn)kH;!4&gV81 z+EMwB?CnyGKckTgx1V!h3aF&QE6qm?2wvB#eIzmm11;b)EO0r%%O;CCpNfHcy`xga z>AL^&Kpw@Ui+FSkLLuc7C}PfTvD9xgd8uQm$dz3R7=59+@`E(w-a}SYo|IG&{V*~q zdR{J0!o`KhXJ#fuKjO#f>kT4971zgt=0MB9adVn1ih@C>C^T6)WOWu*NvBgGPfMrK zn0P$H|G+TthPe3R#dD)`?xS1{Y}-%Y**-WP09%8rDN)UQ^^bo|s7GJ&;b;eMQAbga z`)!r9d+~p1(ErlYpF-20q#xuk^y$v}51MwClBHDr9*jV*K9I^gaF(h(&@l;~*eZ?_ zb5f6SACt+mbowlr{Fpv|0gku;gMW^I`?nI!vgQe8Rvnwzeo9TyV^eQXVxh4KZI|4{ zy3AI1vh*i|=R_SC`2wfuGxeI>qxHO`Pv1UKL7F%n9T3u`_FDcoJqPw8^TYFIsuG-f zZ0`((JWHehMW*~kAG-mE-+;lsfWyCl8B zX>h}m+MQr1u6AgAN!@0!VD7(U{)fMlikPUb3Pjp=;{{O)gcSi&PNoE+s7#3ijME1|*aaI)ZzCkFb(~@=@E`sw|HW>8#eeuO|C9e~k59Wr{(mXYCF?4_pA-M# zzx01X%|Q-wkb}Dr#W;PH@LM!a2sFWa!)~j(ysTTNS zaT2+u?gT8yifapmLLjdIScJuA=gB!lFU((p%`^KVd$VlbrtsZ=pSPZc+V2u#{@ubq zni#bQ6fgS?AO`?7NcXzi1C%yg%K+nOz!>&cd+Kkt<#TZ%j;>r#`(FHH8OG628Eg7j z8r*ahPiymO(aKjNRg3}SXuw#tCsi}?T842nJRHjol-6;2ySDs!IY=G_?JcBVi@6(1 zPimCVBDP|jwAa%JJY97IrHmLS?e#Q1qO~5m5LP5N?k-dV#cv`Mw?#tbcuRXdjk9)p zWYCiJa-22Q(+E6Wib~4hh;f>6NXq5kfzm#kKL3&1)BXGR-aF93{7&LSEveq|lf^u8 zXO9-w;8gTo0{hn6d(qD~+~PQnFBVbq2|U`%qT|#H*$r}!=#-thA2=A?Q`p~Oo7d8T zUPT(Ky_r6;Wh?(~c)|ZewHZ$M-ASJ|IGH0n(BSqR*k3Tf0H&^iqV>9wSZ`RX-YMDV zM}jK(DOB?So0rE^dra;0<|O6|E?5 zwHd(=oZG!W=@SOF-ZdkX5v&r-#u-3WSQ9-F_Mj`Jv=n`tl_g5Mwy%ux9va~$ax6&| z1Y^$LdvCsL0Xj13UjT@&q+11BZ-scR5hu~~TH@Vqu4-P;&Q;2NX!**#>e8V5gfS&v zvv-?4SSXT7todo~k+3T^q*Sk0+5u~OyXU_Yc07S1+C3s2%LexVDAW$D;7ig+Gq7w) z*ua<`)&rjzEow9!fkJYCCz99dy_f#US;@JDZ2PEWnS{x$2BJb3)9hNPB0`whiEso1 zLZwnG`mb*d2B4u9r2b+-4v-E5O@Oe3q&zm_TB@KT+5HM<^nl8CkegqD;S**pl%3N~ zCN@I^a~QxRjg+`~p&|u`!FpD2O_~AV5ZA|B{lMs*=>z=#Ag}s7RIo~;F@wQ~m3Uu? z_?|jpk#mL=n-(asodksLT1+B+)E;gdqsh`#s$GA>LTp}Ed1i)MNifT;2s39}HKU$l zQrTqn?c3on#&75HK8n!sFqhSZm7F5~Tg1SgHSXPl2F!yoI|oMt%EK+t1sP7xe2Y>) zDUCB8@g+-aNpTDc6?+9@J{&}jzDB^goXs=Y{HtAR>9Wp+7Tk#@GKlKFy^RSUcS2)_51bjF$~NUh%h9p zhzhDgl!Wm*OqIoIc>E}O){Mh-X*w$-jd$sR5luqg(MZ+y)9v37kwCT@<2o!#01wy7 zS!9qL5#qpV&;?Q4N*tiT1k!doA6r5dVNx00+eTZbljDqhU9#{F0viyPp4X5W8A&yR zhH7AmT8L1h>`}ta`vXI0Yo0n|Ik<@6*?<|q11pi8D%6~2*5@BJ8?JGEr3NA7Fke5M z%lrPHw>lQ6;?)jk7*RKf4n@yC2ekvC*sjeWUAEw@h6Y%mF0F*jR&kMLrkP8Kh#Kw( z$Lrey23%KXuxS^4y#(D}U(GWxxW9V6ew{Dpi?b7~z_RS5R9)ov!Q_g2lQi)@HY-V2 zE^URmPhF@$5Dz65`8Akk7VfMxdLH6&*oS_i&s4CY>45pHEfQ)MV zc*)qGhTmGiBKZNz4BTpyb2R90i*;5(m6Tzm6o(48Lo}*rZ}z_V=WR!AI-6Q&jS#Dt z7>7%MO?aIhL;zdW2tw}opnUp~73KM@#e-?dZ!#hpy)+_$q zogSuZLDh|mJ2X9up?K%#0S3Xs;;Mts6vl zWCb$w4LSn0qL7ln->-GhM(g6bwtlF&j(6Hb(Fph>-KCI!P#4QYfy`7#bjTZWKOM2&Ou{FMK7T zU=Nh52#a}}Z$g~}8C;7q6|X;3ZiyDcXfdr1GlvwCk0Svx^`m~!0e~|w5`>C(L*b?m zrmrV#S-l4JQcyb|XvJiZ;CIl_G;3J+7D_6V3&k6|P4G;BGbsE2FEj<>jLHCnAt0)B zKTjL?fjPaEfEW?*TcI%(RjdSQ4Qd%ZeCC~Nt0G|$)jq7Dv%Omf3x>I`nFih_#Vyd; z+1_a|%Yr@fhZvdXTH?le?bZyh6kzp?*2^=j7^_6pv62-BY4`S||E*D{Q?6rJ+;XgG zTwJv+C9m=&_r5!0(>&KtoCCM7PgfV`{K)4)!34(9h3{~xfKcLwd5p~cZV_fGj;3~K zcpBCsM+7J1bqR??0?7c7;bUmfp??74$aoe(8Eczj)%)~4B|zJ^7AfWOPM8-r;_RkM z^xG}Oq2wgGkkaMdV_Ut>d<(IM0u}*EX}Vt&PzA`|z}o}eJ?|Nk=`=6W9jg60dS@gi z3d)K&_u5ag_$(l?bNv>eh<~kfltv$@F~HB;snvZ8JxQU&flKngvXBLim)wG8FfcjY zo9dGXajx~lNUlJmba$>{k984}QBKf1uX9KG))Fb?|BFNA5E(76LMtD#7Yr}}C7L+} zNbj6{m7H?w>4NBL-pLp+Y4HelFV2rso<`K}O`iKKM#XKUpj=o>b0NCm)m6antb~Lw zUAT|=x`_hOhLn)8_ZRfZ95fktP6H6^6JZl`>?=svt$Bq9|W)Zx`zSyPR@=| z;igFg8VEuYV4IFTw3|e_Rvb3SN87 z7*vB%OR2Rs_3%OT!@I9?1^CK1gMqzP!}0>*sG5)fONoT0 z6~iePP6wsuJqkI>a2ACJ%GSH0JoncuZJWhr;9fQCc;!-fP^kc5DfX^)0cJZcPd6dhNp8BE;pO)1ZVNcjygGkX zy?%`~z3wtdg=orE6p*aKswyYVl4Y`t69X)O5pgbtqjk33w94Y=gXtZYdq*%GqJp^H zxQ>ni5(^2l4}6_+LaHvNn3`P_RlwL##8!WvH%93botOcd6RMl>&O$ogt$4IC02iA4 zXCn5%vt?vb)0%nGhPZs5KB0Zb=%S~XBR;*PuY30r2{3mG>@pHB&EGIH>zuG$vXjY4 z(@WY>H3xz<d~G`6E1-a*kA)q7?9DlDfHPxrPy$9U982+Qj;lPwc4iWS3mYlGbrXS4 zYMBCbZC@2hd^YTqi|!)U`!v8%%`g%J5+|AwWGwI{kqZH^WxCo1*+v7{a<)2K_E|`v z%r6(}4`f?BVhN5f(`N*81ldZA}oX|`x@a|_ckPh z%0z8A(~Jt9sBu!Vm(fRXpsbptu54hq=F`@B_j9+6K`zEW)JG*TaPOEh@XIn38028M>F8;CRw zEs(=t+GSFCkhk@B;a==6E<5E(4-I{8mt)=xcU#wUVE$Y}(4hELQ9_l)5#VS&f;E`b zg7LP**DbvLu33U|HV4eJzYN9qS6ewHzr5vdLId-@*+Z6mRT z%D2%7p7XHOM)x`_QEd1)YlL=Hq}^_+u8VL9(vG(cp+SU@!FPq(cAqdJHc^^#1cM43 zv>kj5ei5)a!a0}_Mj{eQ?Z!NBBczhqfkX=Y9-+WSLOox=g4L-VR)z}hkc;&qS?lOZ zC<{z{$r@}F!Y*{$_OE=fOMAHjg>iLEa1RUd2zhq~f4$tn<0*oiMM~q21}sllR}^aQ z^$fX(L6sTB&-4>cgk|Vuvs_}#?bGHR2~w{5VVLP3iktVA$YeiZTwFjF?fSGb$_mpX zZyGuX-OciZHd73#$%)f0XO0-kPM-(^cH>yVgB#2F*GhP&yLql_O3l1^H{V{K*Hb+z zK`UCO%siRL!!}M&F2F+JLGHva@14~Sx}PNTDcK& z$>+gY_hE8Y9Pg{|?PA8Plri~fAIo?gn`uu3e<1wl{%ZT|4Yu?O+qco8hgVL-WEq!C zQK?~oh-?VJ7-5%dxjX{bZ^xVS6D+}Euck*i`cI-q_5hi;dA>uIp$Jl>8U(Lm+*8-j ze@L~P2X2n?Or1djvw|qK@z)kBD+%Wez@G>$@qI9z4=KrS&Hq7}ZV6m|8;xK^bAfV5 z@R0OTdq;)%R164TNlPE-r96Ar-d>!|V=sdys8a?N zbT7NTHGTPms>n1r>AtY>2Ivi}iljA`MJl}^7D9O~*XW|6i;*RkOnPSZ>1_lrLhS^E z!?>{{9|qb1Dr39!MdHr>csw+?b#=TvKecl^Y3Zi+Drnf7OojyJvrP7-S*&o{-*n)<(4x5zw+zI*=KL}@ZL3_dxX>#E6ZB@eRvU1oXDgun zy#1IODvAh|Twm>oI}irNHpNpp;WH9bvjfXyDI>vH!#O6)`H<=T24ZZMg$!!T^9v4D z$MQ~mY^X93f<9j+`-(@k^|WrBx}ddEuMi1!1-A>oVWlPz_a#In!iCmt>mg>#9E&5U zIu5W>D4^rjBB-))afORGsw_aToWfKo0|TGYFU>5LO!BiH3QPPF#7|-S?FbSS2J*t+ z|8V(#*@gen;rUAd@$$K176jte=Q#G?mH*ihH$UKk)MJ2vKJmh7kG%VDcP)`8do7pA z4TPh@>%6>IKzW5@y}s%P%Z7^esurmES^|~zFKJ(I-;y>BsA`ZAU#S30B=ooR z4QOx5;p^>RqcP2-4DX3X{8L0g?Q?bsMe-jQca zdFYY{!XR4WLnu}v3KPNB3FO(+#yA2E+JvK;MsPLkk02(Sj$m{S9l@pg>Ifm^^$`+R zK#!0K1amf>*kKGAN9-Nb%g?I@H|bUTRjqPKegXufptM#MR_B$ZSlRh)omyhcwKdbXgPoTqv9xI3lI%(d(uU?Zz zo8+hJaHUx)%`r7w3pEWbol9+Ght48(^tSmS6<8^AQMy@l>(MJ@vjNj8JY{3&uvCpA z{RWK~;^J1U#GJ!wi?-G}W+_tpFopg<|ExXgi&tI;iKb|dmS~N(XpasHBuSPcRho2A zFmMP+8BowL#mRKcVqOV$6E>4E!rDTj;*&(GR5=l`SemH7!pUl%Lkxj$mtD>|@2dYf z>X_rMM39Zxh_mD|twok;bg-N-GNDaG?l;dw(^(t^RW2Gj1}0Wz9>IZ_Hrp(d<`|Q0 z%nO%|dj>G$x+}49$ejV*dr|8sEdcp`J=v@^7Ty}!f{g^~%66hN9CeW*pD-;nwC6bO#F&Uhh zM>#QF)I-E5|K*yiHP-Am0|uP~0R2J%W?N7Z3P0Jepr)^aF z>!>=(Y4|M?6-&2Q&u@qP<=U$Ub7BwKZOomHbG0kkmG7KAS8SDKUZ&ofjLa)4nq0mB z>KhrvRR5o{y!`*YqgI$7u;JdC)YRAhJ!_FPw{~XrXin`3cNg~-ipl~%T;~t=0RT{vonD}2Sw&g~001QS$42D`dfI4B4u;nHc0b(fj|~|B0L>Ie zm4h&0^@9YcDH@yx9-=!?t696Cs`s3IBGrr74v||Z72jd@Kx!xbUgdgNup8@g!KtCne zAN(i4KMyFt?}u9iy#1vAy@URVH9i1HNJL8MXQV&%0RZ3M|D^zc+j>?!NR$>ore9B5 zQzYYxeL=M1#(Hk^6?l`iXMs-Cx^?Ax^TA@P=Zk4QyKu+HHtPcp0+^e7>D8%>wnq_^ z3?8#92ihu7c|~1#R}|{?v$jWw`HGL-37t1bvur5Vy1n1f821JOd_%1+?L<3=_1Rh) zzTOrina*B;*0l?h&wVLfLNwq_342T?4!B>$0Wm)=EIyN|AgnZAi>V|vHGZ=(H>fq9 ztFbTZs$^mO2T&J(yoVyH*G@o5yzDDrqv>uODRf zc7K4jUeXMMBSeLV;=X;wPy=pI4S7UyDXiha8x@w`~1{NS8tShhPmn&poQ{#c^X zpB~bVVM*MK&3m)p`cgkXB`lLar1{ikhGobh^k{{up{Mh!4eeZGH%Vn8d04*}G7m+v zrr(ha2tL?*`QhzTiE)oGv8o-SXT0_-@pjmLHP^&@R_+e^l%a#Yg*(TRJR+ZIWK4IrMES!8wIl`ckE_9 zpm$JHcEK`?5tV2;Ocdk_0MiGfDXX}nDJh8?GcPJhT02+KFKEP8CFXV?Z$J3QdR@cl zzK_TH+&Ad{JC>zxS{Bi~a-LOY-Flkcpk%FbS^out>pV|ivU%U@`105^AiS}Ka5w6# zDE%-<>N<75i2Co6X;N;0I$i_%cTtiA_L)(lpvsz&Dxc2VVTw8uDVm~|IjNSsgtM}e zvNYaN0*K?0==3NdA$9Z8xw#Hde6ngWz zL5=sb7($r|S+f!Xf1;W^s<5urhRk~1&qP^UVwZU8{8!j$-nvlD>MQ5=ZDnnl^(!#& zosI@!qg}W2aoP_s8wsWljx<2R7y+jVpH|AF>?%W-F2j#3L#isp3M@mrYGZ15z*>3W zWEGOMiK5v=b!g-b7W(3z ziSQbR@s=vXRmQ1j3>JxSSiE8=A?r|$QnX403;?r2f zFO{z^RUj`_Y%f(JURhvnm8X7L>~57W{3$VkDN&IsdVwjBu_~pxDl5^kD)ONw0KqXI zCQ}zUO`k(k7hgS>X;b(2dM*zC4u{(ivuoehV?_5c-1ig*?+hb!J_iVQu$L{9-ySmX zfG%VZ2{yU_3!R)kGah%Qlr3^D>d0w$s4glNCJj+JZIpz@#6ihaLP_7^kCBFwp@-H4 ztcg8Z!vWc`5cTn}>0~5l+6=Ibq5kGnerw>6J%;2ye)90YKanG9(-Qb5%ALIy%lEJs z`~1rzI96eM6PGj~#<4GU-6&ulNlH5#xIOcv zHX#TLa@PK5|LH{-_oIvY6=jYB0`eCCGT;k$eDvMTi1+%PCVu^G3W*>*B}xXA%O^U>r z3#12J>0OhD8Y6M@YS9_Vgbs6e9~iTaVbFpb+AtrZr|j+sLre1?#eN5i?8q3r@dZ-K z`PogNg`Bm`IoyW5+E69%);XmlSolr7acTlyieyN0*tgpL_@d~Rxg{GU>p)U0u_&ht zIub3lFg)!b%blP)ca-6{r^p$)tl-iP(oW(-Mr}|-7=TDJJTxUA6g#0oUwtcO>EEEt zK3nse#c-WT6RPz{-Gs5=tzuaTKnX6HsM;dOavkC`wQBGvVy;(M)7^Q1oc#{2x(V6A+Wq1yXR4ShR`trnodAy z2tF1$stHF?6_4ULI!l_B-=^@`QM-(07b0b=UkgZpN0T$pI!i31+Y)d&|^2ioOut)n}z4H=9CB&-z?O)LbIG1MKKn4|lA zr&{FwxC&MGwBg1(M9NGm#>8CYQue${F0bn8OuCjd=!HBFJyLDbJJ(zynefH?Td7yn zUSL&ZdfbRYB8>a!@(crIKMQWOWrt;lYYf#ZEfYP%8Ne4}eV~Y=$gfS|glQF8ZOWN? zg3`iyx>PHvzbTBoQTA7?SxzYP`&0?&o^N($<6`UmzdvI$%juui3*F%9)5I-~(kZm5 zo5=1+T#=nd_w}O_WHn)(2KVKo6=XHxjnX`X_jrF1BD_`(1&Mf>4(p!ECZY}QiYBHE z?vf^Mu!an$zWwgJ^}GetLBP@0ghzfx#Q%=5Trw^V5^ON@;jdj zO|^>Ms`O2kjkLgRQQdC)XQAOE1V9Pi{(U;%mzD6*aD3PI*=YBRx+d~H&kRUWj^lB9 z%f6$TYW4cQky?Xh(fXs#c zw93olKz9c^K3wPCcODw2ZN&$u&pTo*Q+6E4lgmEOu-xeCR1ftVXS=MQj~m+&yh8UZ z&UAGGJJ-pb_T>J8;^T}|b>+j!EB^8f9Iv0-ugc)vq;)L0pGrR)M%wxKDZ=_Gtbp1P z#7E#(rNg$iw!mmCXW=|%zzE*{4djJD*uv%%P(w=|A2=L=8vElYO^#HNEBj|Jn%xI* zz?seZk-?eG6@pn~&*lMXW6u_hLBBKU@qvUe=?N(!Mw#~pflHb9Mv)PxF8G1Lsm=Q( z)ghgvufS#?S}TdqC1>at8eK+3b3A3|J@E^BE5&C=MYEr!<(&x&dnm_mM@4gqJ(e^N^of^%kuhK;`p zRW0{hLWV~rYD&bGxGc`abIsEKw(fkAuxx4TwO|Mdl6thsDmvm-lxq@8sy3sep`zZ| zwjGXR9jOf@@$Q=9#%%JB>0TflO2Bt?jD`sruFpF&Rm>jz2G!Fm&GHgD(LUmnmG_gJ_u*lD}sT%p!& zG@f9bscn1vG99*G^nyl1t)C+sTX1PS$}L`PG}nV237X5Lperu^LrbEH)()-R z*xsA=KH8K7eeo~-I?`>B^`+msX~hR%>*8g2mVsa4d!Lgu?e^U;Q{T2ni{R_}x`woA z#RKkiECNsw)VlA3NUEJwONgvbG$;_eN1xX3-=_l#2;kk-y?m)?PH6WlznE_1>eNAi zIcq1cuctqk<`z<)@($&F+Wwca%YrLK;SF0W0^ z_iMyyPCoB81SH%TFJquLAR@0%tq-x#D|{H(h~f!=lpQCZun)y&gd$X(^#nY7DD2P2 z_Y28yr6k@#SvEe!jp?mbr7Z}D__hC-w8|O>-1P=meXWWWVLC=zt>>;E@q^D<&-$>~ z(bT5=&&4*=?I1Hm4kl3oSz&o{KIM(2Z3W`E^k9hZokscr9aPAmEq_0e70QwW>P?~N+gvM& zjE4IixFxRF%ZTmdaW#2iXJyGPg$-uYvCbG(%q?GSD`7o%M<1O@ta~(>wjFx{^$cU* z9xa&O+>8*kLMR+mfg<_jA|Ubcc|YA|Y7IF)3KINGtD%i)-tSRvThFVQU&LS6A2+6s zExw1FP`Gkr{%pnW?Xe^0^$)IJr6Pf#V05)`XuS2kwin!z> zjw~R~9#etWF%PEwsMbt40bBg{;lH*e4;vcA(P;~y znuPLN)c=BLfML{ob#h4ZSqvY(^&G?8(*3F)a zql~Vrj{XO12NOjJ22ImGE2CkNeTKKds@n@PXM(ZEpEW9dXP*fv0M+=Hf-C!AbcNWY zo+uXgXd$vv>Lv(-m{f8xko=}l=1q-MX(`2(1TCP7%CQ}|FIU9Wy#dtxcm413P+}S7 zr5*c|;RX=P>gq__$RA=JSzez}UWd{Si98O`~bS6GgQ)#>-`)@woJ0x(l zk^e>=8GtDaoo1+^ppe#5k6MHAi1B?BpCjMzjy$jFS$7-}c!q~h{u&y>ik|rl`s{~9 zGNKI%5H!lM!*J{8NjLy`4+<=i0L`XM8?{>>mApBXWwIO2T~BUUO|5c=hCKp@Za?W!X`HbB5QLl};>hY5ICsD?(x z(E%VTt+@?11tWu1a`rE|U20f4rD~c7vP%CYuS{B!BfVY=!kCGC;J2}MjS+etg1kA z96-ytGF+u4*&^LU{nxU}XA+vZ-L3iladOi9%e#>{{`B-x+^O?P@W%JJEY|Bbjj^G@ zRCZ=MJMUpgT%e^d%7*%XmomxEQf9L_Qz3eU7jVAiV-M1+nmey}uU)S7{YxsAt+V1ZUnApSj)Tl9-na{Zzme8*30IJC!ARW-SNUgt&3%?vsYH)5zo8n^Kq+v5rSvyekXl{+qO5oVQO-{>QNZ) zX~75whf42a7Yh#<|5Vhljyn(|R?y+jPV0zH!$9 z9@_qHx8dJY*XHeM%kphS;@7A49>jIqt6)eEV3!0phQSM#8m3lvp53!^!xdgyq*5~P z#EyVx_RbwiV^ICREdJZFKVpoVJNx?CK-cFO6) zrZi|sB=wivu{@f>-8b~Yx>Blz4rR#GrLXWsdUtnx0(W=F*Xmt{%nqI7xf@q}SdDs0 z*!TY2dVsSxGj*2#3(v!2DDUm%nx@H2cH2>ML$4mBUzMb$uIVD35SS+)5Gd&ucb@DiySFpGTu>=4|bxM^X>xaiDxkV<)uf#!8iu^QXI%V7-kw z=~!v3e-Jh6YMbj`SKCXToYS(^*V$Ire{QTc+Aj{328>?T)fye@cGS6-R+t*Cavhz$ zwarWCwdtAR{dOVrruMz-7S)WB9FhHRfoe`OqjOxtNqjb4PGKffUhG-en1@tr$TlR* zo2VKzO34i)HC->xpe2XH9%-s$@zu;Hwg)X`a5a`pDmgw2s=F#|w?lR5Izk}8L+-Q_ z$rU*xrEuyPtiio!1&;na1_e`&fj!2fS~x?3nZuQ4)kLNg^4wpo z+3J29m8;G4KpzmLC|^)^Yd-~ehq!of8YY2vONIO0uhV!dkS|&E+1tH7KdTRnokUV za@q)}4%$HY_c=_S<8w75Kd_Q^Gic+O%9XJ$x;aI;r!E8k#z=*-#oPt7ftVK5t8614 z@q$H{%mL+hd?aUZLIvum|pG>Kb(~avOZv6SS!d)G+-qxxL#de5eXjRkuR0J z9dOe(U_sUq?0Qp#8Q!TEBeg*xNs4$!|y?Qv>I_W$t z!yzr>n;FE~SB*+HNN4ncnwtqiFdiOZ*fu9WGN2DQfe`9>dnjK(zBz(4qki8;vi_2- zI=mxm+w-!urxAln#@@r(LpJlPht0-4+}i*98s82Si~Z;WzX$}r!_%{&fw5u1WhIpr zd_u3OJ(JF{LqZ%iVpEKiK{dc{Gi7!W6CR|IiPa>XVK2mv3%w`{qRXacGB!KI?uqGHnt%gQyI5hI zVLs`4xxB-0F87lAG~B0$_^erzs0nQ_`#WQsgRyw&X;B?xzprR-4KEu+xtw2?Z8ug5 zZc;Ydr=ac^HiF1G7S;gKP(&aX3}tPvxIEHag&e?CGbY8Z$whU)3>A4INvEll!`XExL|J8SQ@$&(1$-?^7;ywpx%~zHK26 z=CL^HEes$~9^jS;`O%R55sk1-?1iVJMJsEpE=sF^K|eOBr^PODP=AF6B1b zJD?maX7-UhSt=IVHge^1f_Rm6X({mIq6gNNdfCp22MX-NKE#v?FpkBAuN zXH=zx2lH{!7Cl@$X$3(n$N?hmp!Qnk4CJqHbeS?ov!J!Yr}}a4_35k=9l zqQj35A$VKpQV$fT(In#t5?Heh>ZK0wR7Ypu9ll7!BTe>q%4q4#n+F5saw-G41hUf~ zI^RTo*voWxaeA-&pa0cN6rWCh zJs&lQ!3d=Z8W3SjN{MNQD-J)d0~cT-D^=U^ z5{|%p8P8^&O0jUO{mXumq?N8T8f}QNH>B&dB=beQNn6`u!ZxWbW(gqSH_1hFxIMNq z?ugVPc0j)|DlqeD z67}~J5J4@{7DK52WwEio?qJ>JFE(HBQ1rPz&Y%f;7KJg!lBWvFW7%>xjDyR3!jpBG*SJX#)NXIGFClQ9D6cpqV&3tmGO)NaDYqP3b ztb{@kBUn1Ln)D)fY0@XCsi8$}@*F62LcKs`NlA6?nyS;-82tJ#MfFR&H0z!j#i0|J z;B=klgQaa3#ssK#*ts!(2eDhRr3W^GEJ#EgGIL_2j?%SYJ;*MG$iiUzz((i7n86 zfeM$XPE9qGl z)okQUNXHAOPW@ZkD@uYQ#f;;gIWsx|p~{m1v#Gd*41Fi2T+_4Z;CaaY6DLFQ`MTn? zW2;v2F~C6mD=z@!@dt_FRUQTq5S2z1m1F@9)?oTy`g2sqM!2N9#TR*mDT)SUg|%iY zKp#^UXpPC{oeD<=DsX(9g(ImHg0!j1u!u04={XEQ zljE79wCW7*dYp8!M5F9Uq(}`42wBlpC$u7?m28wavUDif!rEyu)9~{G3`^;aW7_X) z!0O{62$v4QN{cbSOCbf}7Pb~C$Yl!cXY^sH5GA0|hfG++TN@eoh}Pk-EE2|ytn1|` zT%d$-<}Ab~PoUvQp@a}sX0{FDBMDn3Y^s>gn#!@EEtBHI38`b7D7nq2If@(h^g?R{ z(9UbsypjnALvlepA^ z7Har)+=AxLt9njfRt{Xspp4aIF?|%FK(ljh{mroj*_D#jk^MoK-*u7n?C!ceY?7l-sQx0-R=3r z2PHYL2NlOM+tga}uA9zYU$0&LL&sDK9nU(rL);|34lC+%!6qHZ`XbAb5ZsP9ZWBIGIF!|3-8p=AqDU#}spxNWc9EgpJ z!0SnIXjv)-6GW7i%Gf_pBGu1x?Kpsd!}%2d>_2jPlss59F&gd|k-5lmPmo@lrOD98M^sV;XB`D@bVnvL?~}DFJ8Y`xJ}CU3 zv#KU+L&0KDvzp5=woS+LVpjAW_o7QSemreXgKwy<$!#X74JQP9(Fz}4X3COkK5P>< z(I{#rW$9-SkUZidW$O|ZIVip2IcT#{Rd0kEw|X-1ueSN~p-Pz|V7aBTNpL*5=4H>s zC^x1|7g!1TMTU-=QK~@)vl}S`Z)NkGu%|{WkS%NLG1R(~Z>J@kjV~2xpB$KwlQRF! z?)sNZV{_1%dn6~54gWC>xL=dEP2>Ca6-Iy6WiN<@44&8(!RNJETe(U(}H7R|* z5ROpwvUEx;A?r+)Yjh>=Ema)36~T*0^AV2`#s{<_wI?f8SF_SyZ8#e_i&7q07j;7z z+=yqFRf)XyTCeE56~8T5PVlob^8@K)0PGQQFn4{0sMj8f=3Qm44eU5Im&ZzXqvX?ZG_y-%17F$ zOF$r}ZSh5O4$i%WkA;}b=*$W-*{>~rHVoX#o)q7KcXzHAc#+(LD*x67*m%F%#>BO9 zI=M!;n$;pkU0*9a{W)MevcjA`Eg!`+u2HsU*_(j{}zES%w>&n zHOt7rK&R2_GHce?Gsr0;<>qzWT4#Q^sxCXt-7&6DvyAfI@@B*XxnuFp5?9{X;bmsb ztZ)SMGX>KMvy(!#L^{g#ZPtL}JGG$tLOzBbA`uHUPkIqv8O#du#UH>lTbnZr|F$KD z0%NSwj2>@o@X;dOrqPfutvGyV<|J^A}+pg*v@9`xBD&658r+ z6OQi-u~DwcD)H+kg}|7zXlT&NLtHYMFqIIkSKv4_{&^gj;79TbBo&HoPtamkg}6;A zTd7C}zo83|q1EXWFCW{QY-sfhg@M!4_l*ldeR6!>hZHSBF760&78W6~RvHI=d5m}o zJcTnV(xLz*kK)2b@+1ar&^-T>owA|S7^!ilQPcu#1E~?xVZGbYGFhGH^%Xqg^G{43 zSjb68e9}Mk z@X*Dk9NOm6nhmcU(GJk&yuk!A0P`6LX(O-w@*4n4(k6s`(In=C(kA3Lrj6zXPmGuK zy*(cYt#P|4sbP0!#@2lwB-fezK8D~01c|h(fSJ`WPx@2{@X@O=)uI74!2&r@1_5W0 zX!j1tpBRT)B&hd{f6tP_5_rZW&Lp>`3!q-hcm?QZyiA>3-0ypd|2O22nLdUYM1>xW zNAiaoA>Z4i@=tfO>6|>XkAeAlrfbdg=^kT6RYvi4jkc7t+zy~yTc!8fQ(@TZne!?! zP#7Ypd@U9JDK>NfGN~k>7I%m+4wvsYhO{N+KpS_tl0)D2Ffy!nW7J6F^4DH`f(w&$ z?-?LWm{`AO1<4_V0+A(_Rb#ZmkVy25*AsK>FgaNc506EsE_Xh)v(CcNmyp$S?`!$J zmHwi6GMa-!(;NSs$V1x)&z5v1=FxXkFX3hU4|;`RYk}+Pp{ug;6X)0EUF)XG!(|#H zr{4j_11j;79yrb=(#SOcn2@}-_)sJ?-^|_q1*)acsxe9h8Fe|rTJjvDVK^Ndkg=qo zYFMq`4#ZsOl|WlUH(0d{MX*LKisIR>LMwHBW?^A&ZXr>U(Vf@>uqB?+@sXKfuS&_; z@Rqq)-Mzgf?3k{kd+lf|QJp-z;}dPJf;}Q6c2I}9+$sWBw5DcEv*2#B>1Z0Xqx>ch z`CC_ryW?|KG!ipMwXl|^pR#n*qT$U$#s2T^9^p&_GZhcQkgbs6w~rnH?Wx`h}Hpc@MnU?Tu8`SsLFxP`m1km)z+i4 zeX+Rqvw=5VZ~YZfUZpnE_bXf94>q5X6y2}Y5lowQ3LDDP&J9d!XG?KO@UvSoVmsNOH`qp;TL%%Tm&Mb&qny*_42tA4|i_ZlIGZ0Ga z&EKon2eXkBy}_4H7V4Wa0W^$At%bSh-@kquBP(0SzWmNWnM5^pmGbR$7p?d9ok^-W z>vnL-r&7o{&q;EwTyDmLeNGeCWcA52-|3z87#XnTaoM@P&Am!DRD4rx2(%4*jm0;@ z%-V7Dhx|hjctkS#_Flw?NipwEHNpRD5n4P_axhvg`5SjT7!g~Dd{4(pJPv7Wi zeDZ}xI>++-RmIht>_!Go{#UY4{*mn0_4Lv8^t8_BS?)w#MS(GUnwFGqW*uBp^GuN7 z->SY^VMBBH!G)>lHv-bk;n(crm})W>Uu&Ema*KPm(>^HbwL~akR%k30N=R5aXmkuN z0ihasRxD;r0IW>lfI%_B8Bsz_*n_u5-k;&qMx}|Q{>B#_j<+w(N!Bf$cx@YX2`Ak1 zCRYt{j+N!PWTMy|kI!QW1dn1bRb0&eWA84(P>uT*w=ImSXn2Ij+TbH|H`JtvIfpJd6nYhGB}*Yr8J#!9uR7Vz{gf z0^HOjESxkgr`Fb%%*p5Y^xj@l(jF>Y`LLqjEgPU4sYAFb;iL)(UQoQ0f8=z21gssx z6MdisfrbmuHfyW_Vh{iQ4huRbevF72Sl&Utnm<4gEcQi1+%(%S752h7CS4j!M~t&? z#Hwu?LHXF1m(ikOnZ;v+VPdUs@G6?3`K$1vs^-+0QpQtcn;s1e(@4C|Y#%QuQK4{Z zK@u)jxm@&kOwL~A)kSP(DI>@5tNTK(-{)9|_v^cT9i{~A%w9&-2C4UY@8ruPCMNCb zH?E3ST>e{f5F;@779hxASE-|jsW`Hfh-&_adCdW^YOD5Ws8;%_R(XwCev|e5>$E`%U?3mP!?N)K25@$aCMweX| zQuGnt8>VA@(pt~;16OF{HOrs^(APqzJb6w^2Yq{NDkIX$!x9gs0 z-20kzdZXa&t+36s{u-|&$!XI_JT%q6(PKm1#U++6B^g@lMI|NL93^Etd6#w9jvwtm zJ)I01;EBJ&`p)0dwX9XIT1JTmF{pZnZkbw{-JL6+7++jcws1az z(rz2SUaAm!dtm)!fUlhtC98R&K$rtWKHK+z_<@tVN;e! z7NW%N6$z_n@vUX{;Wv;tN^%o3l2@4>7=~$x zwd^B#C3pdYxq$%i3*u=B2?h*f()SZBmRYJvY^*sw_zpC<1+H#!WXZ>ca^H5dhj%{WE(NW z8azlID#{H!nFbHD*MTE_RUijeu^e<5(wo-qIfFP>daBPfZ_YPh?#VznG>Q(vZC>eJ?HK=Lf_t zJiEUNOH2W8g87NUGX)47-QN;In}jbpV-7an3hj_co2#3O8cpz^&lx$u>J2=s?Y zfybncz<#9X=bGL~H%1=tB)JUcS>T{xfm?jL{tJWA<>X2lU2~`q4Gn=lbgpfpZQ%Y; zitFo-@rrc&(Q?dwz2R4IZ8Zo3vs2nVmw#f#*Ba4^u8oRqr9W9Fu9z_Y^`3}!yz40&Z zMTum^P~3iL-XeeED9wK%L~-d+O!*@b`)#VOimP$;@>@zG>r0&1A14?H1hoki@eQN5 zahbk}n9d~Jog%aK?}c-;S1Vl&_6)stF}jdN<`~i~FT~Of`Geu|G4d@@9t#^8AjI7e zTj1s0@fOuv)s+V%CW()sD+SuA;kjfKO>0qIH!h)mEv7TzOo-geaqOy6_SFO>Xjf*U zACQNhz;eQo!*;+1F0gp?Ot=N;2r{N@$Y^{syurV-zAY|lp6x6u%5o36$4xIyC8AUD zrzocsPKRBtc(FWp)Q@_D4!`n@E$D^PZ3tR5A&N`_CO6;OSg_=oIowAfI3(YpBuA0k0+3^kXHHs%}B@y|5|-e%SuDUf{$D@ zg1|zIi+A3wRE#o!q#7C&u&e{B0*U)onWXfvt4(3nkP2%$)jeD1PuXl6i4mG9v74DEPih~J#Pk)6w#S(qTzRe?^E6W#&>VX!xpRw^Ku=6H$)zMfU z?C@W?zbjQe_st_xbhQGpCv=AZWa=J7BQNaNCD_0V?E(f)0JNZEQB!DY>1p zLB>-h$f9Lz|DsL@u z&nj^yfyWVic07yFC(aERzebrv2s!-pAwz5WZLrtUJ@n2(w>^}P_-PrC-plz#b?V3CdpFUkBb97o!59mVToIRB>z;Ix^Tz9&yKOG5TN% zL`2blMaxlAkyqAfDvRR`RS>cH@Qx@b?C;KxgznR5pK5I!TN?6noB7n=YNPB6+|`sF zvO4Mn8qiCsqc>EF(Jyur-^ibM!JMNm1yqPeE<&XuKwxwG5GHqyABe(bZF09#&_)3t zR)Ii0#VU#wMinH%>cWq(Cc*r;5FqNHY*!c~{GxZP;OD)krvQi~1meF}=eYx?W=QXt znJ+7gp(#X9%mn&!(`=)AHi3RZ zq+ybE7p&Li`wzK?ua}b?q;&L|#2dYaXR`H)&>|fnXJ6-xtKUyVv}7SZY~Pa~JF)4J z7-PEbktmvUCQu@DE#$u94&- zxEpN8fYAi?*@U3ol~lbL~`OQ#cw%u7r037|NAmrq?9Huh>AUAeC3 z+pBKe8(DS@TDCb{Cbpw@`cJ?F$2!uAe-WNYch^!g^6BNj+E7%&o0sEZ!3|UQ)CP2O z>Bw`lAUk|!ZY4XiLC&g{dndneSwPpfSav4&Z@la&K3}2`=?ZwUmneB)|b;* zX)8y{nSZm3P{=W(Qhdy?Iapwdj1rt1+C*=B*?^;VEwx+UnD7DhE25IGFl5qglq75* zWz<_>v9Y9Lx4>bzVU4ij)E797`rT#;#4^WuH6pYAT&idc@nFSx4W0+vRx_v)x4fId z=JG-%)=x_RtnS{fdS2arPzzR^*%mTC({S@fa(OPl>Wc8T8A2jX64@>ajE@+$rsC^1 zH6-|o-b7F-7GFd{gf2llW2Ss3wVNSus1o4n3k>m!#wW-?-sJjI5$!TmP(I9=XwnZEa_VX@&mcZrXX0 z+`;?z`;p6(^JdmqV{>y$O9CZ!-)X8e{3Gj(i3whe`?^IfFZ+(Yp{}Va&C1dm1aPD6 zC9#cu$Vs!vuO#aT@K2r$@fyYXIss;+@;4ro=EM8UB12O3=q8V7?Lm$m0?XYB>=Cvd zY_eZEQY{i^ZZT*yE!THO8W1i7Y_*=HFJgp@Yz^%XnL~LnMAxsgojv|u8c6m|%*oyZ zbu66rjps3%#^S!!vR?^~s=nXd_Z>O09+^o03QT=!wC_uzV2N0xpM*!vp})0U(g~Fq zc$o9GOa#E5{8R~(BR#Xn<>ZK9T&o#W`yJ3!RBYIPLHj-L73lDiM~^eaCM~N@kyA_rhp-BK9?Fh>)_ESywL%2u-8flMxVp`whyVsXN=UrDY zfe|I;deq!!N%9TVdM^%+0!ebOG#7R|%62U60R}&XSuaso#l|La0{EoC8MepX#9P-Z zi_6ashR+!+vXb1G#CYLqWeUh8%2`5$S1)sgWZtymhle~8d<}1tdmP;i|EsFBZt?is z$0D!BCkXprS-jyZarL%d-sfpcrhwlxHNB>HfYG&ec|^J}tvORkh`zf1I~X3>2>zGL z?N5Z;VJaI_J87Gs*KJxH&X&Od&);(^p`+Y#A6$3R?1^bfTU*#CRCiaWjpJ4j*Q7xa)EF9m4XFn|KV924%5KN*F!V!?!Y{oJBap}S93ywPp$JVdOQp8MOI)N#G; zIfCbDzq8-Y#`}`DBmyVJrd9uJn~_%&9QZ|$zy~quo95-+ECt+S4??%tH|NYZp8$~uy5#o% z1(FbL?}PBf99o^PW92UhoBzchYm3t_0>_jiBDfCK4+(=CuU|QzSKWA*FR5f z!~5|u{2;j$1^C@rq9h;1=V@&Vt^Iwm7B_1j!nfo5$gQ;Y58G=2H=Y9^=aTnui?&9= z`6fX5nWk$isA?HNa&FIYv%UIom814V54PcTgBP2;wP*fLlqR3);z6{tJ~n{{?F&68Y8_RFud4OZXX&AY_Q6Yx-ddt$Sp3_@ z8*#S~1OYVJ8bCmnf_zX!t=vO<*}Z@Ne&G#3q?Z4#G^P)0iXH67C-B@?j^epMK)&%F zGQl(Pk}#ef@%spo8Bs}^8Y={aQWyqOPPv{a*FXUX3S>(X2!#M=8OzL*(QUN=p=2l# zkFmTwngHPURTQI~+=ChLY=)ps8fkY>(S=J&_j`Q#wdS6TKUGI$np|pf@qoZ;63)PGl8>m*-MIHwi#9$;lNDyR9uQMaTsG2BP&LNh`2~i-=nUO6kQ6}T^ zt>~KexB=*j9rMI!Cr-D<>nmrbWa)8=%yq;E%=OelZj{cgkj95&vw^OVZwhyN zTZT>g--?=Qh%}Rj#Jp@dbF1S3AdAcch^KkMs86TmdD(1xs8Uk^$wiv?p0oGD%i1oS zLep0(h14tGtHfty7c92#N`U|dSSP*Y8Lo{a;Fhe)jxuG)!OqY?nul7j`+OCs1esjP zbC(rFuHbpS1a@z(5ADoQiO@S*q(ouu>`yEdmQ(`Nbg^5lOtrBltaKAv(C>DNN)Cmk z#$U-5oYK=FwrAaLIh8}oDa31z_w*c}UpUdzdt&~;ks}B8A33r=+*}uQd;RsXK*Zxq z_Khq>M(W8kv$roT-ab2f+tSi)bFFtCyYP-XE}VKVIJgWh-) z|3>9h19U<-!oURk~a?@=DB7K5T|={jbfOt$g0x*BxT7arP4#t z(&A8w*tA)!!FP<;J39TrxGs^H8jO0|)>jUU#GKu}5VbgxQ-k&1b~@0#dUIw(XE1jR zlhf&bgWhd5Sj?KbzV=|ZG1A*NlO8k}#WG8-J&)QMyRk7>IafCDyWC5cTTL}g^iJe z82W55?9KljmC5eU;Q%{ZtCz)TfIu(ak9QW%9vT-(R$m}4Y1gpGy>;}C=QayxVOA|8 zpArUD{0jy}(|9~S9*K-MG>k_)@wkUw(^-5YAW+n8{M!aesi@OI*y33gloMPtJiC_c)b>jk9`BeugG=}^DCr$`h};;@If*O}t!gMv?*JLYv(K9Kypn|x*I%66WQL*9xXCx50bQUxdomWCNM zC4P9q-K-o4eps%+kWI66-i8+P)~P8V@DSOD?-d3(h9^Wl-B3dI(qj5A+2KFgTEDlq zx4Ai&Yp%6eYE2f4Nf_vEXz1x_Xz0#Z&2=_go!QDpdYs%yXTJ)}u#l}X8VI3sof)OW zrRRG9MgC?21?O8ccVR3@lw8=}K?LR=+~UZ9Qr|^WsaFH196FV*Qe&#_(zp2xE^Upj zQdL{irEm9<8*8d7%Z;^;XtS!iqQYQu)Hm{(dydA)-m;DBKKyg**My$Qwsp<}FVOM4 zS>yqhO;q3k1J5Pm)@lNO7`^&r?1n)hkeHrMyaouIBzgRia221Sa5ivOV*d{){k@dB z8HG!fo@W&9vsk>vZ)UyFv3yW?+qSWM(Eh`O5N(@Y0s@bapW*w3&lKs#bauMw{lBG~ zHA*w7TrS0Erq*IL37=s!(-Uv#$=J+wR%@Nv#$}BlP>j7+^lp{X&8yPIze^M|PF=O3 zQeDf4!f&waYxI>W_M_b^2sM-zj3!4kRl|wGNr?gwzD9cRCc(4~lQ&)8yr2C1t?q|; zi=W`l!UwmtxcP?W1H$^9?hgaP)5L)L$X}ouIvy3%qPB~l_O0SaGE6z=^K&^Q@gtqf zfnB~TRiLBJ;&_1XPp=RKl*?$8vphqnQ^en% z-w`Y;fFh{iDgdT02U!4k+KtgObYMXgfZ@BgZQEw7W^LQ{ZWGk2q=`|*R-Z0*(#5D_ z>*YTuH|EUw#-7Qy+Y(LjC~N0l8G;`0a`u?=>(H-fXcgAg!Jz@}!UPX>oW+2D-2qdW!5TMk3a2OE>Z;@%jc7qDZ7xWF zJDl+!ZrbjLKU$vXxcnhwZeuUf5QY2{TQ+z&;i&4mxCuE z9waX^2w8{hM2;Zmky|KV>pbPa4#OjQ}`%+ zDxndzi7CV_k|mpvgUJo#Zt@s;k-S48R2r%l)sGrU?Vt|RQ93Q1gDy;$qifPl==X*? zhB-#aSlzhIM46l>V2YX2nFgBEnVXt-neUljn!i|Zi_OAX8e2wLPFQYPb6N-3oHk&q zVyka!W$S7iV0&nLV;^gO=g8@p>CEgL>;kT;u34_7t_`l;u4AsJZoyr|-O1h8J;J@+ zeaKVVv)YS$HE&z*DIe@B;alQ6>R0@|{A2y|{OkP({2v()Q<_=KTw-&wBiL0O!Ij{u zbKSU&yu_F1xA8~$r~DT{10RTh3?M&f4f=rv;HUrziqJ}!CA5c4=)5rzoYVs8Ma!3vp5A_Kx`Tu3O zR(N+L9BCg}ANdl^A1xkj6CE6#72OfTVjz|^wj%Z*o{X1>H;509Pm3>&Z;4-v|4g_N znG(yAo@AcnxzsQPQ_?B}lzGZ}<%LSCrPMy^Sapj=X*IQ0+E8u3c0rf*CVE$Wn7;oP z#uA#_000000RR923<2N(TL4@D2LJ*9Bmgu3cK`qY%nO$Q0{{Vd+I^5URs>NH01J0= zoE&#c@C4VGb$8#>Y|1S-0cWBD+}?jZQ|$uu5~YK-&OW&XuiVBotygYmn5S3nV3p@r z?)((f#b{gmD|a*5HvY=PthQ})NP?JBR+>ZzGpz*F15$__c^VWkin!Kk9+hPlS@Jvg zN^8t&y)`1@|BM_>Gt$0*`muV2>}%ZwIeA*vf?4LIl_=WvhA@X4=Gjw@wG;`wR?R9B z&Z)gZhHE({*@+@6W;hY+>~LV^L-wWD8t)b6zErrEi2ekZM%K7-BWGbw)twF z8#~9~oB;*^sJn(45RH(jVFLy%2^4&@62W{^JK{!y3mzwvZ6aZWX*hfvVcV_W+8`I z!X`$D|O2Doo*A;WEyO;1s91&jX%um1|t*22T~KC`Gf1dsr)m)$CyeHx)}T zAp~($agq|P8Q7e*xERPVGiZUB+s!$YIRx36IUqboHeEI;Rxpp#X%8O* zCj(0Z12Y3Bkk8|^hrs~^A$(>AmyIfn9f1)W96BO4GN-s~R0HvLaWpV6vN&`kheUvs mL;}HfOAb{IVRmL7tsRX2TQ{)uZe#-LWleDb0PLM7-v9t&k{-qY literal 0 HcmV?d00001 diff --git a/_static/css/fonts/fira-mono-latin-500.woff2 b/_static/css/fonts/fira-mono-latin-500.woff2 new file mode 100644 index 0000000000000000000000000000000000000000..3db53c6bf982aa20b6bd034b436531152309d3f8 GIT binary patch literal 16632 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 HSME^*1vLJc literal 0 HcmV?d00001 diff --git a/_static/css/fonts/fira-mono-latin-700.woff b/_static/css/fonts/fira-mono-latin-700.woff new file mode 100644 index 0000000000000000000000000000000000000000..f9eb91c89d2606b835ee2646cc6f32e6ce9a26f4 GIT binary patch literal 22628 zcmYg%19UDu7w%izwoYx^wr$(CZQC|aZTHl6dupHBy8Zrj*IjqddXnsCb}-3IGMQxD z<;BDRK!6|7FaaR_=U0~e=>KE>)Bb-E7ZDW$0DuC1INbjL1P7!pt{|`c!{z+wg#SUr zO~zGRSykwV>;Az$008=Hw;yOlUYVW=001fYu~GYhf$n~rsiJV?zc2K(j

TmAI4uyy&- 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 Q0_tT=aRC7AohRP_0LIPmF8}}l literal 0 HcmV?d00001 diff --git a/_static/css/fonts/fira-mono-latin-700.woff2 b/_static/css/fonts/fira-mono-latin-700.woff2 new file mode 100644 index 0000000000000000000000000000000000000000..2fd453f3f124ce9c13258cf144fa04f54f53d257 GIT binary patch literal 18236 zcmV)5K*_&%Pew8T0RR9107pCk5dZ)H0IKi+07lyY0RR9100000000000000000000 z0000Q92+1Uh$IGJ0F*Qcff5Oq7ZC^wf`UMS6LIfZMgH#8J91MXV z8#b9GY@1fhZUjruhG_$qgBzbwG2qJ(5ur2~sx9 zDzw8>D0r5t@N~^|Fl-nuub#HaIIxlybXmOBao%rFG)`#;MMdWQK3G_NQl|=&esL$2 zcY_H-XY%bYQ-X1ee@27Y$!-(t>Pb%eV0fFE@ahQz-KU({r(Zb(+eZE*C$B}c*?;xS zE-ZM=2aQ8i&Rm~-IT2M(#FH>U+syr(iq1j>Y=o8yQAB|w%P2&}q>ZSxxpe=pTf5Yy z+kWM~R{8IL@4vVHZ+V_?tlxdJU(~CC=k16lmBy!%RYLVlB2xl~LoB|)|z21G){kHe%dYzT- z0yEkGRpFuww<*dGvpa%+mcP$@@k88oIw{0ZAxNL-;*cETAKmPe_2otpp*?X0Ixpw) zp=!v9S1f^L`Ip=N8^pR{*IkFJ*)M;|FO}XidE#Y_y5+_*DEig$AXJwiq{aW&_D!@# zEXWOS#WIMkXyywld1&TaO}RzMNmt#>iUA?E{g+8n0g6BSs$KKLmHpAJz6awBOB4+R zNdgSAV*mfsoOE8^l-+gZ8ap7#QvK_))__5l3LxzNud03Rdw?iY(gbm$Mn7qO%%_LF z_QqJWd$!sVxbFcEcmN3!kSG$8W+~cKo*9aY<|G23cR)$Uhm!0cqh_=HqxUzGY5$ew zpJZ*wxe6iHzuGO@t+t%D+|+F@yDf@7)AMmL-kEzu$0u}5H_fL&0}8YZ@<8%9;)EzL zPu*4@=UFP3g%DQ+lPpG|=l^K0bEo~;Lr-B}L<$SE6LNs-H~U1{hL#bRfORBT1$hOm zqf*s;=%>DaZ~uQb3Dr?oT12>STK}W(`%}MHWX(eyV+$dS116smf^CeGV4qr zp`uVESnm(ntZq`M(caxVWPonPl3)|!?2G-7f&h8|05FVX5OU2ggaBIMB2n|>YW3hp z0>JAH0Kj_Et>O)~G)P;wqFSd{95-ny`(?tfv_uK_;tO)ca4!uYFu}d@0fX$RQVMeO zjXIUZ>1Sttj186K`zq6rb~+JwpoG!$7rA~;&LBY4b4rW?iRYA=1hG7)idiu0bE^3W zVS7%6uTb{i4>bIQad2Mf(X%_AmTTXW!Y2WKeU=*=+jhfS;omM|q9Phae=$_c6!x>A z4>_+IKr{eOOGke+)et377F8k>xd@S_6(&QjhJaBJKF|z%JP(tMt+*-=*yI@+-1c2R zAQ}+Pshz*60F0I$2QdCzh(7`t5YpUjhPtAmFjtpt5e6d0^1Y-UZ-?n0#MJif7v9=C zT3LbF?qYm@@wEh-ZH~F-nQwuG7FleG!;X0Ejkf}9|Eo69B$G`s)il%1Fw-mt9rD6U zuMm^Lh8Sv?;YJu~l+ni6?|}Ordlq+BrHwoLk_g5UgN@PBgsh6gzC&qn5RLX&=b>=S zrYD*O=GNJkKw88|(LXa;PODLiM@DGg>uW2cfjUdKdw-DWd5J@zwuyYjOC#+g1H1RW zzzbDZAJHk$lNMOFqLBecf+-09Fo3d0%ay^q4FA1+&;P%HzYeGsycPn$hQ~ZW-~b80 z0d{}{>;aAdcAOU215)V(FJuuU!3^B84X{i!M(rz$dZN#t(7`VeDk6ndI7N;qC;EwX z7KCN8Wz-6`29$WcJHS9ll4DV54D$69!6JM&`&;$`)aC_v?ND+6TQq0i0MAFjbH_8_ z+31ljqD;Bc_tZTQAOldx6QEZvXL_`~6YsbUw%O!{r#{<0{f?F4*T2qj zo9(pL4iDY;z!slK^bAaV1PT%?M5HK!m5oCpw_twEzE&Dxyy!5(*9w$U}WTzA_?-xd36pldu%_!i)&=ayOS zw@1EM?T@7fIc2&@l?&%a6YZp~8eSvy0^vEnbW#;$%vbE<+xdUI2Uo zVnQMkayn`nTDdZ1$);4SoK>kxWj@nMy&ARZw6JSM)WsD?opatO&De z@(FOCign#hQQZ>KZK&?RyXziI_Yryk*F)d-NMesAdE#@-mZbIta$j=y6~NcN!S7q* zzC-FVJyWjdD)d6-UaIAlH>&qm6YqS`ppUBdNt-_F=!>t~^-br#>(CE(`>9jE{N=YI zeJl3gN_5K`xc*1l1nQFx0|}%ff-bz+Cs22@&khv!1)z|91(g=vo&XWU5F7(BGH?{c zL`?w~mYBJ-6D(p5g0mo&1ZzO74Xg*T@9v+V5lsMNDYgYGKzsmhwq?l)e3qy1Q;Ov| zfcI@3@+66M6dUeAN%bWDznrC9{p!0&*$!+tc&&egmX(u_R?r8v`I-9n2u_Bz{INzq zK3e!_Q!MNFc}fW&>W8KRuTEgRc}3Oj_rSDc#@xjLQvE>Y)u|#gX}i)f))I}0veKwQ z!ss0d6xG0y0X^`LNhy7=W7K1_+x++=Sg|HG0&BewmZj8yS>{aEog$w`s~%$-s!cWt zNj!;<4wYemG-fOytr6>7Fu2wfGEm0$QB@5oRE0S7{{HRm$blv(dDR1Dz7LemQ)S** ztE9?Nf>^QWWx4Ti(=I?cM_#2-@@&-{k?#F%V&tT;p_D#^_})|>HHcdBr9Op)n@6MJ z6}rC+s<~8y4G>c4YF7{taiSt?25ht`M>!`?4Rp0!$L@(FO)@}Usi>ZAY!qLSf?B;4 zHvOUWnwi>;3IS5OsT=~3XmP(QwbhlZJme!(cBt!cwM(6_R z;jy-_b&FvctP!h=mX;Zi*`Q=Q>!_mPuq;KN%VVqUu2m0MK{SRQtfF9 zeXJ?wKI?bbu#B`PAorc;Axu+h45cwow4TpahBb?2Sfjy)Vtlw(>e;3NL+#FpJu;4e zW?)zvBoYNRsqX)QABM89@^hfcJet&*r`E_Tk4UNy%^Sn8CWTL}vhQSM|0Q!*9sQBv zx8xXrOa_56g5(4%5u!|}D#BC~u0n*G>sX*5J#izB*G4>VPaTON_7Q2K5*aZfEHbQe zY)aUbaj3$nnrIbb)GQLf1{H~NW=EPUlu|UyQPJt(36n1Ef<=#{=l9#i4WJE#^C(KS zB4k!SE@!N6c9ND_(;nS9%evbP_jQ|gvU2n@~>Wkzxtq!asQscmSZe zB`Y~a!$rq5U<2({=ZLcLSCL~nimMxr84_udt_&^DL}s!#gCcl@&AumIq%oh!Jyjue z3_0${e4U}r?xjV?ED(W|d?Uc^so_#TxeVAySzVIseiS1WDVfK5U@NcRv($&M&n`wQ zv(M6Krall(D@3yONlRp?8@4y&)Le8qU>g-{d3Xg#(u;;!i0VpMDRVzlQ zutnyc%(ZDm!-W~5^LPSz*((tU^(du%nZA^`M;hvCX4LgWIdKHaJRm1UlaYfGOe3c%G1KLsjc?)C&bR#F{#LH1G?tgU_hE*6<<)Q+`3Bb!(=u~ zS=B5`Ea)1r%i0-A%n)_y*Ks6Q1}&6Z&dPeay+J-I+BOKAfFQGVLspDE^#Q9Z<>ik= zQ-V-oz)C9*25g)x6*mDcdaB`#i;HSEE%#IDQh7OpN=K&CqKa{u`Jn161U3Z)TFGW0 zrmT|+qa5@M6BKSV)Q~7d7e+zElvouK#}-aO#Fcmz62uolK_rw!6#^2Aq#%+?vIN?3)2sYG26Y2>*D$zZR#dq+x3qmRsRf*Vm79VIrEps9_bxs93&VFj(Wa6(&~ zfc7>49c=T=x<6L3?b=wDXCL-Wv)xqpO4;|AW@BlNay}WlVyiqDhw{0+PkmEX zkG1cq?6>EY_&5DA9{wHI(pJs3r=2Hu!P?Ij(&K3WTqh4NPZ1w3+peByeP`2~uycL( zjIPi9bY99)DW~%>RbTDBbW+-bcH>>9k)3IF1oE_B@bf=b$~|{)c20ibWuJF;cBgd~ zbNluVk6rq=DL#9jWYfUW(}@ZxOm9Tt<^EEC-wnIvm|9(>H_OH3CqGQQ>8<9}Q()t? zbWk^)y}P5mqtR|XbpXW?R`2Q9yP#-ZZtD}I%pv!(0G0hj(w_gIkg+i4)In?0c&TfV zFa!=n6%#*G!1f4Y<#eHDXewPqFs$SHc4reeY?AX(P~;e{IBqBuhJn3llEoZyRQPL^ zsD3y0A&|w2Wz7>jTwN4OwhaRYjqiaD6=8^qLYg(Pug(L3;A_^E8ltx5$l?UJiqtJ+ zV}Hw?#uZu}RImnvG&`V>TDX%(AyVWWy)nwTF~xsd2K#o@rdCdIf@%Kj(HtFLb{qC) zQ%t!UogHMYGRY{f8EV$+jNv8!xhbWW_JT$LXY@A&nQ3KFK4nd|h`<$1^kz>m887Pl z;zKeYY|)H$;xiS`&I19>vCB#IqBh|T4Swf-gn?2B!EgcR&xSfpsMyBm;b*>P0zdc5 z_#)HaSk=_uSw0uY5==3mWSY`HQ%Y7EmPes$uF9H}*t(y;R9(-sb; zJD=F*N%>G1unCjP5LAgOg(7k)NocMd>%$ATnP}HNk*s$|pxMTEW7elLr1J1I<^wJi z)}3K0UUeUyW>_fh^3Kz-Ey3ihZg6@AB=$}A6P$^(YYh~op$Ro&KN6lg6I^L$3(wsM zpoV9|36+1KdG%Nf@|TvRUKKM?uS5mEyWfs6aljs~e9LeYP417rfrOq1E04yrJdHsk z@P|++2#7Io5IBaJt?$DJx#8!x-f)7PMEcU9bgH7y=>+yMSmdyu`*8fq+T31Hkkg>- zUC7kr1sGCBUTF^wU4m(UNj6X39`F_vYiR}^jm~Xk)kp~&Ia($m zwExnMv#4d&=d0GN1>N@Gzn>V?3$zCgIzwhp_v5=K|1DVs@kxb*NNMpM_G(m9Q^H82 z`B6vvBmWG;9j~g2`TDQPgQT=$uZ=w>yRdh2k6&XiL-B%I*!Ct_Tnty%#Kp3RO-zg% zo0DifQ5BMDPlcp$nfnguH2w~c6C;#a8ktX=pk-|ZRzHR~xq!dmh9PjLB^>&ze8kc9 z7^4pq=4MOasuL=Sl%9c(=~N-~ir!u5K-g*fn|Q72ipn)3F*mC3Zni?L5&L+}ky7ls zW}KI_*mskNQ~ZQ{j`&bv7*GhX0L6|C*Rl}-ZKN_2lMql3;Un*2veu)kSPI1mhWcAR zqfQw)%0FH(CXD(Ur+?ZPk4TUBUT^L1R7M9)>z_LO^n$wjx_iOp3Yv8NtyO{NmIw-< zYHqSULJ{eS*EBReZji_$R%|qv^^*~DAqR4LZrnYVIRW;wJRJ$LBYEhd-QvMnPACH} zswysaiWIaQdMMU2#kLwM5RD3z7=CsDC-nA)ACw?NRyU*4jPUUDxO%_p@~^uBTUOcV zrL_H4X#<%KkrKrHe^Udnqz^wNuYXzf*z)i(_D^cn1FD~fUO;X&-niKvACq|~cSn+I zyG@4i&t&zsc@)jDH%C}==-ZIS%C=#wnUSMzZzKzirMaAT&X@-EK-?CO6%&1F%jPj*RQ71776^f28l=#EdJj%!` z3IbH#uXB&vU6lVE;96$q(vz<+6}l-!Nx7f(Dc@kH@z-^%D5n@R0Y9RR>t2A$!H$4k8z*dAcc6qAa&ga6yAg|%StZ!d0c-<|8Uqv-LE%UwW z$1m#DoBUWTx42)NGf5s$ZMUE(e|=lFDu$m$ZbtUm_n}zpljpGScj_$d(0tP9h2c*{ zPzsC!#quB<9F0$q%r2uUc@KNnSi?qH4qeKXK@b&nj+8dAkVz4R6!)OsQSUaiEaPKik_1g#eJ{%I@(=Bz%QD;3tZqUBr@@}mdu zMPOyw@+;9sR>TV6oCZ6mlnIhba*H9rdU&V&*Yi3UggIYkm}SWkFVo4)AHHH(9*}7! zR~r5|4LAMPLSd%HORsrqOsRkAgDp2c=BkWmVstNC5lUPWO$R`A_|s!FZM{X( zsL1t{(zDO{x)Ng718o)Gnj0>-1mc8CZbIC!65>1oKfJ>^ zggiCo0>L(ixad3Tj(J8$P{-x4NvcucN`#;tPM2rae18nAhfCs-GWCsJ}XYHKaDAF=X&%{mZaj z^VfoF!Ieitww^gwW1Jo_=1D7vr!ulv522J9^=4k^XDGb`BhPPRwfIo5&Pi<+6A z@!#t&U$As_>V_*Dnl3f{tZ2Mc9`tAP$huy>CH^_jnsgVJB$7zP_Zk~3x$`sg7v~OM zWj;#OhktIQ1b@mAAu|LS~iZ}d9M+&*@b=f-9(Tw zaIJRLn)&O86QNrMLFuyB^!%giFZBi^RDCIb$=~xJ{2D9JOLN1 zax^&`S2;2APhF9x%3w}K+FkRzzW-jBygNPlDzBaA*(f&#=1Ze>eWXU3 zP=(-jb}by-KXkFLfAGS=eOaLuN3qyrp%q!(L=Ok|erQE@6?L1!{No(*%(x#JG&BEX z!@6?YUfMb?`$HbPh+UU71eCP1vksP)81gh?JM&i@Mj?%8LY0Xg$cUB#nlKoIaMs< z=MQLNZGkegB)bd`EzN;dEg>^U1R-dq07Zj;Pec{}wo=*VCGS97ze(MKN7g~YW0wWQ zWuKz|C1_lUWwhT&>nM}~I3U4Uxc2Fekf^=QdkXM!JWc)8Gs^WgTm2hxNnr_^o6#&5 zv>A;+SF7B_Er53c=1E?|E+bFEX`KF6{z8ku&W#Qck8Akf_L9^RU${N=`=@K0JaQb( zQassfV008p`LY+@yTlY-W`3dqOILlrN(?)Evc4S`S7r+MP^Q$0#pcwUt3$@rzIv5G zR>Ku|)Up(JQCbS&(P?;*jM-R13IzG&@<=oQkkW)?21TXMS}O90076l@ zfZ!Fglue;Cl60jcAmM6FCn(s0%udb2(!@mL#{4xUZX8k0SsO{<(Iwhgm>pv>=u^mX z-yMx2$e3JimW?ddu5cknqW2%h6E2{!Q{*iDqi>glk{pTBpz5E>rDnv!KIm{wJ>3y( zjR5d;43>t+3o#fW-n=kqU$9SbH`yt3LpIA$apW{>6qiWDT7L?auJ;(nX0khB&_)xu zB8g>ILeJato!?D8-4U|?#sYS37IEm=E<^5at_bZyRldwA$ge88u-%?g3GziT75brp zhS*)fG4Bt!jqkOgu$uQr;fVOD4d(P|F?jf3k_JN;tBlJ2scd>iEbK;G&D3DtKWE_J z!`Eebktgs zCM#RaqR~j&>`Y(bdi5l^)#!VvuTYIO3D~Bhl(sCvw{rjuQ{zd%)L&MRwK?YS!bE-m z-9Xmn<*zW~f`)@p>Vcss?9Z#DBq1K+w0x8NH6Wvmrp2716y$Ci>dl=fpnZ%Z8ncnb zODmNPUJ5fjDl}4&$M=^`K_Ke`q=MWi1$?>aCaPd$z(DQHlclSDUhN2Zv9{`|3XLtU zRe$9xff0$CEHhSyTljX#BSs}!pw-^LW33)=?-0 zR@RWTSp_&5jzReTjTpM%kLZY5vKF5Nw8YJoRq84UUs8D?x;_TT67s(Q3V)0QqnS=^ zxKSaLH|9Cov-xTHGNJ+tL+JPDp)MszMlmJr0q-fBbDlg%P z%IjOp1*|e_3ScPe5^cxhw~M-rH^#8tn%#@-=VlMJpKoaq=|i)X|7Wy?R!y$%8zOt? z45DXffb6D4oz94k$%u&&$3%-`MlcPHmm2rHzdlE3|8ih0ud;B_#$xBPlNpuu-F6Eu zr14XCbA3}UJ0DM2CSfB>!m#Ox2`=B#l%!X+u#RRMID#zJ z8)`*edZ>*^T~EZel9a#bjFy76n{k`OBa>d6&Z@ZZX&>$VwXLDM`}c^A&w>9Yd|2#k zHltJ|0bDI@Pe3OLgzN@0q2E@!`@N}KSuA>mRFYcl6e_wqPHn1_9Urjf zXYh&K!WL?ZqRc!03spSE$DJrm5!*2_dD&i7i6x9FPuz+)YQrsCKu&V#7j$t)AJCM< zEvQX0>Z{qwxk)L)?9;_W2`7b{SwR*8<-}wIhecQhe0RoU+pyTJ_+!}p<^%y=OG43T z-nfeC#qRNrzMPY%YDVJV&X%~LGKlUUAO_FQNn%(93W3{`Cd;S(fMxvQwF$~<#S$re zASeEB$TChDvFvkNXUdexGcvWBjBL5gl&PzsML?k(3Yi0iLa6D?I~p8AI;trYxY=ip zU)LkIt=7lNrC15M9UNi|3wyRw!r%%lfPzcZ)=1=3TxwWk+=6 z#^4w_Gk}i+r}{@@BU?KLUs^mf6`Xlt<4tpX@or&|CQD0H5{)0RGgDQCV^7msBVk>^ z!PKzm6xBt)0M!^_)i{{JXES~){VQ7NBaCA(g-j|tgD=yYj8d(&I49-g4QpW^UIxLT z!@R)V&%)Q-IHX4dTYM209{mTpqYh5YO44Z!Oj4Yv>i3(GC@obi)=*HVf>P^!KlAhB zg29P?Ssl-0r0^JKT_f!i^~-&gJ4d20ErV$h;{%DH9$FNmqM1Wqw$DNjpHC() zc7pwWO{e4WwA!>Z0me3Qmp;EvB%UJ>7DCUb1j?s93yH*Ya%=wux!0G-F_3=g`&IP2 z1?yu;OJrW4H35k{1w`{T#J$@-M1<}Gj7+ut-w{5)mlVQ8W0)AU2$kq^{W1%K+J7i0 zj2LbDbYJ*Fd&TWP(<{{AjomOTY1hz=sTGRIE9sT$&~-iFbrzuR30|dg+m)7XLHRce zYxn{(nK4Nqe;1P6i{&Jyl8pao3^Lq`ir5%M`EugTn;X#Rb120BvnA?0+n7JXghq?I z(lcJPoN=8AT`i-MWBKE6-kuW`B(@S2dE@f3X203E(>>7b8!!T z2pXTRK_t5Rzu&he(FX*o&u-3!Mb#mi5ina_XLBd4zfRGtfGvgW`^>uUNiL`6Zq2<7 zoc$%dzr6Zx^*ukv%U#yxgg)!5zE^)YSN3?nY%cg^hn(eI&ORwh@A$Oiy3h7sEa?lM z^ulKky}kCK=Gm-RoDl*6O|%OKxf2e=AQ+;aO}vl?r0BK-ALw%N~$H zq|zyc-iK`nt0gnjQC}P|t{)M5*D&goS!5~v{aaPYfL>FuiD{0TyxU$@mPj<)rbXDsh1!4sH0RS_watD3u%KRvxpUj3wJVRaj!^7xjy$ zZ=7oEHS~(U-7QB=d)s2Xn`5RYp3OS{v91J1_sX&`2$fNvhEm7$t_+RtlV)L&a)U9A zsdqBJTf{L;`6z#6Kh|{Y*vZ7vc8vbfYz-<}Y+Y0@wk#sR)zy73BsSnYa7yg)0skR7 zCps;4_rCD-zw~L9cWSkg3rHuO;-&FxJ!b-Gdv2n4Da%(aL`UXexjNA5l5E=$C@ zmQ{YR`?@ABy)8G{euttqxOih(%2~~ zyEjbApqtb!6&+RUs_>Z+bwoBPA}qq&UF%9%n<>|DDihsla)3LWWXP_(86Xu(6fv<4 zX`jKu2TfP=kj*Apc})y52aQGGeM*HsEH~AlcCa}`X*x~5Jk+}`u7Sc-nhD(KCQgck z!5~s#?F%<{iEj7!V~sIA{!c6Y?9PIsuDWJ6yZ>!>mT0rtESl)f%+2q~1STrGi8uFT z<(7*pM(b)TMk>njtyx=FK3YK%mNf=E*%}hhOmP5+pNRsUP?u9?&MT-?u4{VRDwVXU zRg#w1Hfl|qRNA6aOWQuXX*j|hHp85f!pP5IC3BFb<@+?vto+@^adWa`e%Pm~GxExy z<+7Np@5N9i8qRuLvsfJ4#Ll}aM#>+9DxnH74|h|tLquW8i|~T7Tm&Sy@%x$Bh@36=3iOzwcls0fz21or^u!0=-2%jl z{$l?WDc8=`Wp)x?;a|1-px+vBbQOa^R~zsoq5)4=>KC zgSOfh%NMsVq;8Cy|K!(#MBU>apY*nK)WD8xKKlO(+fp6V>xM0Bhm-id1^2Gt@z(zl z3I8FG|0N*)M=l1rL?8n3h~JT-2;;xbKW5nPf0A1NxHnPjGqTA+cd#JSMM`B-l1iP1 z|2782R;tp%^o_8{rtpZSNLXWpR^9v`u`2{@yrw~m_@F;Zj_Y7?=|`DIQ(^87Vy@;{zD)*jr=Vw{0+Gnl#mc)(Gv$RmyXi@X5`lS zKwf`CYAJ9jW5S5)#6{KoW5q&-yH+?k8KPtmiAsP}~8X^smh9GegHBe&3 z|7T;HO^~AwJ*lJJ!3wELRHCU0(o|`xGWQ1~)_{~~FTv{>T(=pm6=)zl*G zkQPacMXy-@qy4VZB5AR-MD$wC(|SkrB^$+7os6uu+7Tozk`_z6U18Ba3erB(KGHtP z|A-j*f^6)V@6|uwiR^Y03FJc>GbEx>v^)WB)`B&z+ zp!#zWsEBP`VNRi#aU3R7iXBdEr6u)GU}r>$bFhuzCO|RDO0g6xO2BgAM2Z;2j>Wy1 zyx0(8lvu2dXBi1oPZ}>zQ6UM7G6@th%1W`$1gr|6LMcYEW9goldQM3NE0!nkBfxHG zTTmRnhF@O!vi6t|7td(Hm^gT$YK#QBtUxR~d26_KmsV^-{+hZ?9?Nwo{@db~`s_`F z4omJ?*`;?JMr&{J;A1|oOGmdZ3k3>3b@b4%ZoncYQd1qD=O$L15)j?~n;-b9Kik9I zV?EUMHlJ0Cl(20Ru|v_G+D-^;aLg{Rd9emaSAxOY=KyG!w#}x8lEiNBP=S~E0-VB0 z=O55bKCV{G9Jl8$-QX_}<7G6JqzZy*bN%9f{YR_N#H7E00P!>2Z9r7r5vez&tAs~O z*Ek#AEb-jluq08TkH4|KZ#v-INOj2LAtXRN-`RS*N8Pa#yL?`wiiV^3v6)FFk= zjKDeuw=fU*taV+j^GO@6>H;M)i(_xo1$gJDT}* z^WdajfabKEGRl3g1UgiEv-0wBoxE2PP~eCN@8SOW+$8H^wy>p+fX7&ec>;xSN@i-A zp6m-E-#Q$F20xUz?hO(QE>tAFz$qfIp;gRhXMs|8MG`F%^>e^4`txPFuj(eBR!b`o z6`eEG&Ioc(>6Um&Zlh5+xLm@6Vn{?>#X@-gjNG33_>h}egT^A%b&f&{A1n~y0xk&E z#_aN`tx=!r7`=PGZHBpk1wn3LUsU9DE4MG3i)#KujB|kK5i#FZkm{RXWJqv=ff2G! zAsmy~zHCHIR;>NFxm>o$;V{@<$g_XPF@mKx>4B=K@KX@C{e~Q(Ai{?y^rgPg z*ATDraealqm3aAmyewWa8XteL7>u}a%#vv!WDxyRR0zijYe9~|;vYjv7>_TRh@ak- zlExydc{JV)=pQOeA;9+DBfe04f)gG{iia+b^6Hqb(podHZ=uZx)Kxxpk*!Qs7R)w> zJ(o|*EPP_bYz0jlT2>)Q=YuHDCxm4*2klqZlBJ9yX{3&dC_m|KKB|Ab1c|E}=CZMF zWQnl0`I!H)$KCVsg4XtfM1iu*G(^r3*Fz6U11Qsj-p~d`5y_035oLCaxtxNVHe?;r zXX<^wsUlL*QLvC`U$;on-#naaffor91zkQ z4$5vJggxC6(@A`S1(6E1y80yz7Zt{{^D`z2VK?hh(J3sF4yeiacE*vS77*fy9k~&W zp()&su(xQ%KOceyCN|NGA688Hw~M!AXiZQ8)QR)L*X}siOlbtgc36J0Xn;HHmxx>3 z+1{^_zX$Opmp02l-WE;NI-H*p4+!ED3!6EJU1%18N7vx{R*s$QdvWJiHtB^Fqjq4 zWaJ-(RDzQ^9usV$Awq<;~ z5>`uYf|!MF*-Y7<=NG4+sFiVNWrQ6439gyp@4V z9_tA&3V%bZ@7*wfeDHkt!~GA#?QkMW)c;`8`Pyn8T(!xELm!uGKvL%6j+WI(M1A zT5^@3S;tJh%|a&8j53*KLMlAB=%u%ArE!M? zk6*rw05LSR&e|_qEIlx25N!|H!^5SP#EDDr7OvBiYrfOFcTs(MGH^&FTA1tL78|tM z2U6OA46Y5`#!WuKSoMrGVl*)1-3D4$CrvZ@oag35bVd=@5l#OL{{$v&9$V!5l)_Z< z(-?YEIC89+p*9sr}okxY@hieB2p7;xH?kN`8-k?b+G zH;+pmKX=N4n|YExRm?@dBDu^cZ{Vq*L^mAf=}uLitAmJ-lH8Op{y#%ZGy6MZD?g87 zmr zM=37Meni%+l+CEaoh|yuW7ySCo{$)|xq2(sY6_$-NW=~!Ll9u4hVcq5y`LjEP2%XT zP*8vpl44KlO=ExEYNKeh;s*2}3}b-W!V|urJ2Af1359wxXGU~6K=?rbi@qZ~!v+n_vt@N*1x&)~bQo$tVL(oPaR+_ITz=sj?0s(YkE+ z`}fd-Kt#*jfB+%<)yLy-tq4_Au|qzu)>9hx9JR}|>4>46NsKsVdA3($L2`{@tPfzC zGXYS4AaI_D32os(mzBFPbrTVzSFmT03GNc~7#_Ac|8=RUl*2B%r?8~tNZ!EzkXiUI z#gv%wDqXB+ve*1RB}Q7qcwoQ)NULKZ;#u!`n{8TqgW+n6lKhKaiI+W579xo`ja#sV z<_Kou0<+E~tOIQ#3f$n+^zk&CRWj2F4}_xZO&dM1b&(Yhq(|5KOF~Q4E->2zU6Q;evFoe&{?*@(6aqk0rE9BA>ShLq7! z+U`Y3MUl_62pYum95E?J!-QxyEiIAi3Rl$Tsm?kymuV}7oeSo z!N)3KNCsFA2P*(VQ4J*8p{(1|n8fsNjHvpW=3fbQu~*JcG0!JzZnoX>KnH zfq_wzwMkaPqFJd7x`N`6J=1gWG^>$H0QcM%<}4_ZI{!6nY(`wI@*!|vsyc?II2<#^ zMGb*4H=dDN&I_Vz+EJ5W2|>dR5`HT%?D=&h2;PD3w>Q&}#++{RVYO&S;0y``m7!kj+{4Unvx2x6?`2JaX25xg0KEeJU`(x66yRED zKfvtDR&@Wdsi((9GIWmDpYjaDzGg#&H31Z<3;t>V&k;i!V|tUm@5Qm&^QqZ(7i+|HABW!O|807{kxMVKtj9O1acud*0UACgNZ3g zB*7)0vi&@L(<|nADE52Y&)y>9n3@uMo(#YIDmcxS0LEP|*t!R+4*K@K9u1zE! z0CFh7swuP6vT=Yd&k*sY7(F{sp*oy9g2M%7h-wm6Ll$qtX3u`;5ljq0ft3cOFEDm< zN8)(2`whlZm?z41K|2=1tVGN-KV_)r$I0UfV`H`a6Gs?VBS0Y>-0xX<)V0!McukCb zMUsCi%w(={LC`S5-Hz~vjoxB(2c#3+F!M~H%GO;hcBXD_jHPi8yK(=!fTF$P`37UT zt?HFv~+%NSNl|lSh>GFCD3qM|@G3;SC zy$ie>`9@9AVkrqT7bVOm*Dyc9&ILi86+mQeFe~SXDD@5WQbN1_nIvS2OLTQ=^nW4u?Jw63lDM#VY4H$F3U=ysxxP}$vYVr{dq0byhp-ITF&wm{o zows8jHB+HTq6^~;{Uw>9MB<0@({@)-_%q;tay+-6a*zR;ZevGbXXEveJxAdOAdYeC z$$41ox@V~DIbw&7M$INys|(J9UP+^n-jNXJUvCy}+yJH+uO0hfXDoEJEh< z4cOSu1)KWO_xnpE2{czeYCN4P&y zO|RbmCB8Ei)ar;$I3ACy_WQ6#57%B)REF@~!!xE0y^5VidA1Aj7?z7IDs@#B|J2%K zbLPDl09Nq-{9k~-_wR)>;dCfAw4t5feP$!XQBmoL+yw|LHg$^C`fhtAbWK;ro=!l4 zjDrBHDw(N6tdphGu>?RY1V4*@R%X#uKisjWRbA~u&VBs<` z;IyJ|qiDG4A%T&8>G@{WwViL#H?4}o2g2Jb;Cmw|@~s2et+lCtJ_m-ao zb638r1-&2q9KO-_L!4r1InR2@i)10PH2FIOtFRT5{Z*woco44wrCNr1X=uy2b9Njpm=onUXKpgC-M3XKY z(9dH3N}mj)+Z)>O#_0OM_Q$p3C~1CHlx79&A6F5dH3>ak?WX{K z0jO!T%>;{~Sz-fcX$1h!-91K=Sl-adn#xZgkk>?UEZ*Edi7+ubzkTH>0wAX|q9Zyzb}=lb@~mL|z!A_m9xAvv{ILW+JQ6dt?i_ho+A0Cy$ICXty}5qYeA5$OBR8`buo( zioteF#Ejxcjh5I4MD+sJixRw~N$3?kCW*A!vzVs4=wX;~8k9Okk93?DuY1r?8mX^C z@n^5n+Rb02kbarxba{I-)$rARx_7N9Vzvz?G}QiB4AjO^dKbjl1deRzWK?Y9H0`Ku z>7f&RMvR4=8LF^M1)3_bOn1RA;|QQ08tQuKl+m{{fLL{E!Pn?)oNJU)imq@X4byu? z2%dq5x+*21su^wK=89N#T;#hLbR6!j0!GS{mxm&G9w5k?X~=d45Rs{#54*XFujFoR zF=GBjVng|pa7!Y>lVJdZZ(K^|&a6fz1^${eaS~Z?RwK9nq9&>;CDza;+GSZiCwzS4 z9U6KhCy_fr4%gN^ipFLVzf^r!$$}m2-Hiu`dNC_98=cj3!hWNP5!Qo6=s3y{3O>4w zkZX@C6gB1^9rs<~UoCQcq`+CkoB<^ z=*Hbvg!oI=G^Bwa-5?D3t6rv)ZX+A^^0a5Ay*qU_nrUz>eMuEsgZ>l#0w7BVo zVo+pqoc8%R8hWMldTW59#W)c!4~2gTmrB@t=yO6jGzK$PX;8sIjI2G%^sBSa7qV0L zi#kKAZh&q5bG)FRdjSRq`S|Zc63Td=>iVctzR0f*KN?s4h@^MEyD^uv)C=b{N?uNS zgBIecN(PvN02jh#WC&}pMq7-qt|_y=#BFp9ATK=+Pj@%)2CuEr#&`~c>L>+nBoyeu zFB7$kr5M5v0!2uPngIBaaTnMijr=ZIVi^ys=fiHU;wzb@t}qiNzou~4fV|i>lbYZT zZWYnyU_m`+3@5@|Kf-WW=k^pT(KVi5yh(=kh*4ntZcUX8kNnA9>Ljk|l6+ZMqiW$C z*euL)%%xgg66E4OznjOtRrUHAV8r%w-95{r6AIFDmAk-0;ks$aTM?<+rHFN^3cx*c zF`y_`0So)4zGAcLOAKzwSNkk{YWB7j2rpvvj7_zVxvFmLfUiEx_qW&8RE@)_?^=UP zER*Q=@Ia_sD_i{<1YJepEMg{r-qb7@x3Lr)D}=QS1b72J%RG&>{k`h|E8rM4wCo0S zv$Q&bU_7S^bZTNR3`MFKy0%sjbW4E0?=4u-Oq3RL0k}iMb|^{h?!j}2^fSkeFpg|L zMaL&Not>C}k3_-TNbzBzIdF}DUztlRH0d)TQ@%53W8SyM2!rqyvlG$#&`z*UIh&Lc zN942xSjHN&@MnE2^Lo+%3;urRa=ged2W?V&IWlI=Aqx3!Qxv8SE08KjrV&5C04H$N zMzI&gk*iI8@WO0!+dbjHt^YxV-}5ov4``dhVR!Z2Q`nY%bC+Ze1-|;VFCB2b4eHqs zA^-@$cRnM=C62G7#)~^BJ+NC9H@d%N$swFC-#sC1pnt7O{*-1?moMW=lU|7sbuiA}Hd;5$+qW2vqm~ zS1OZ`Faj7ATd6Li_bj$%ovzRJxW`}Tii01G5H;$nBCG{g>Y>aHOHG|Hdsq#d|Djx= zuXFahhJbp=gQm=UF?MPG=7n?JlQ;q>I#`KXGyoQXk3w+GlK5$_kz+QrilyEf=>Y)m&VWhg*$g$~{__ONFAqozLKkg;a~^pGnTR|Rfe5r$?D{~$Vwxt;KXWPouvpYoNH&r z;ELhBXUJ8SiHHza#8x&5A$m4^ozGNiuxlb@0qRwSl(eqpmcq@guaZ<5?l4q?toUKb zj;yC@BPySwtoZ7*9B}RK6I^(QZapEO7 z=e!FpO620sN+e0v0o^5+b#&Kc8*P&6-F$2=mnq9N*LCt>mN|3typbR8Dbd=-u8Nbx zyUIVoxl%u?#GR&{JmeIVR35td^%^3h|4eRvMn#2M^iks<22EPDYWAN0JF2eV)srF9 zkcP6g>onLvIdbL6_p3dcN5Omx`0*DoKOQ=_oPW~p^VK)sqaiKnNKXbbl8MYhfrSYd z0RjppQj};hV#SFE&!EszBSkP-OR|)T5340)T9JH}VP=u0lmPlzARwXIY72AFt+vJi z2OYP~c025JEErh0hS^V@*oK>-wNpta;vAhc5ac`sptk@31vyf{jsgInYzhDc*#jujzIBBXwMUq| ziO~hd4813b8T9_YA>S_2F*EqCUEO8oMb&+%2{9lL83NUyZ-6O@SR(PxQlC_f^wC%y za9NQ37#$q-a{_Iv8E`KqWsx8hjzrL(y#l2tQ!5l{2A)cL3x|es>jxjc57qcM6k!b&^W6gJ$c^`eL1w9;FKLQ>0d(1 zzu=;OGgQSvx%)RqA>XG6y?++R literal 0 HcmV?d00001 diff --git a/_static/css/fonts/fontawesome-webfont.eot b/_static/css/fonts/fontawesome-webfont.eot new file mode 100644 index 0000000000000000000000000000000000000000..e9f60ca953f93e35eab4108bd414bc02ddcf3928 GIT binary patch literal 165742 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 G*yFsbY~cLNXLP73aA74Mq6M9f^&YV`isWW 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

5q!RY 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 + + + +Created by FontForge 20120731 at Mon Oct 24 17:37:40 2016 + By ,,, +Copyright Dave Gandy 2016. All rights reserved. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/_static/css/fonts/fontawesome-webfont.ttf b/_static/css/fonts/fontawesome-webfont.ttf new file mode 100644 index 0000000000000000000000000000000000000000..35acda2fa1196aad98c2adf4378a7611dd713aa3 GIT binary patch literal 165548 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 z zq1_(l4SX+ekaM;bY|YgEqv2RAEE}e-Im8<@oEZ?Z81Y?3(z-@nRbq?!xD9Hyn|7Gx z-NUw`yOor_DJLC1aqkf2(!i=2$ULNfg|s8bV^xB!_rY+bHA;KsWR@aB=!7n&LJq(} z!pqD3Wkvo-Goy zx1edGgnc}u5V8cw&nvWyWU+wXqwinB#x7(uc>H44lXZQkk*w_q#i2O!s_A?a*?`Rx zoZW6Qtj)L1T^4kDeD7;%G5dS816OPqAqPx~(_-jZ`bo-MR_kd&sJv{A^ zs@18qv!kD;U z5Evv$C*bD~m z+x@>Oo>;7%QCxfp-rOkNgx4j-(o*e5`6lW^X^{qpQo~SMWD`Gxyv6)+k)c@o6j`Yd z8c&XSiYbcmoCKe+82}>^CPM+?p@o&i(J*j0zsk}!P?!W%T5`ppk%)?&GxA`%4>0VX zKu?YB6Z)hFtj@u-icb&t5A1}BX!;~SqG5ARpVB>FEWPLW+C+QOf~G-Jj0r`0D6|0w zQUs5sE6PYc)!HWi))NeRvSZB3kWIW|R^A%RfamB2jCbVX(Fn>y%#b1W%}W%qc)XVrwuvM!>Qur!Ooy2`n@?qMe3$`F2vx z9<=L}wP7@diWhCYTD?x)LZ>F6F?z8naL18P%1T9&P_d4p;u=(XW1LO3-< z`{|5@&Y=}7sx3t1Zs zr9ZBmp}YpHLq7lwu?CXL8$Q65$Q29AlDCBJSxu5;p0({^4skD z+4se#9)xg8qnEh|WnPdgQ&+te7@`9WlzAwMit$Julp+d80n+VM1JxwqS5H6*MPKA` zlJ*Z77B;K~;4JkO5eq(@D}tezez*w6g3ZSn?J1d9Z~&MKbf=b6F9;8H22TxRl%y1r z<-6(lJiLAw>r^-=F-AIEd1y|Aq2MggNo&>7Ln)S~iAF1;-4`A*9KlL*vleLO3vhEd(@RsIWp~O@>N4p91SI zb~+*jP?8B~MwmI0W$>ksF8DC*2y8K0o#te?D$z8nrfK{|B1L^TR5hlugr|o=-;>Yn zmL6Yt=NZ2%cAsysPA)D^gkz2Vvh|Z9RJdoH$L$+6a^|>UO=3fBBH0UidA&_JQz9K~ zuo1Z_(cB7CiQ}4loOL3DsdC<+wYysw@&UMl21+LY-(z=6j8fu5%ZQg-z6Bor^M}LX z9hxH}aVC%rodtoGcTh)zEd=yDfCu5mE)qIjw~K+zwn&5c!L-N+E=kwxVEewN#vvx2WGCf^;C9^mmTlYc*kz$NUdQ=gDzLmf z!LXG7{N$Mi3n}?5L&f9TlCzzrgGR*6>MhWBR=lS)qP$&OMAQ2 z`$23{zM%a@9EPdjV|Y1zVVGf?mINO)i-q6;_Ev|n_JQ^Zy&BnUgV>NbY9xba1DlY@ zrg$_Kn?+^_+4V4^xS94tX2oLKAEiuU0<2S#v$WSDt0P^A+d-+M?XlR**u_Xdre&aY zNi~zJk9aLQUqaFZxCNRmu*wnxB_u*M6V0xVCtBhtpGUK)#Dob6DWm-n^~Vy)m~?Yg zO0^+v~`x6Vqtjl4I5;=^o2jyOb~m+ER;lNwO$iN ziH4vk>E`OTRx~v#B|ifef|ceH)%hgqOy|#f=Q|VlN6i{!0CRndN~x8wS6Ppqq7NSH zO5hX{k5T{4ib@&8t)u=V9nY+2RC^75jU%TRix}FDTB%>t;5jpNRv;(KB|%{AI7Jc= zd%t9-AjNUAs?8m40SLOhrjbC_yZoznU$(rnT2);Rr`2e6$k!zwlz!d|sZ3%x@$Nw? zVn?i%t!J+9SF@^ zO&TGun2&?VIygfH5ePk|!e&G3Zm-GUP(imiWzZu$9JU)Wot`}*RHV<-)vUhc6J6{w&PQIaSZ_N<(d>`C$yo#Ly&0Sr5gCkDY(4f@fY5!fLe57sH54#FF4 zg&hda`KjtJ8cTzz;DwFa#{$!}j~g$9zqFBC@To^}i#`b~xhU;p{x{^f1krbEFNqV^ zEq5c!C5XT0o_q{%p&0F@!I;9ejbs#P4q?R!i$?vl3~|GSyq4@q#3=wgsz+zkrIB<< z=HMWEBz?z??GvvT54YsDSnRLcEf!n>^0eKf4(CIT{qs4y$7_4e=JoIkq%~H9$z-r* zZ?`xgwL+DNAJE`VB;S+w#NvBT{3;}{CD&@Ig*Ka2Acx)2Qx zL)V#$n@%vf1Zzms4Th~fS|(DKDT`?BKfX3tkCBvKZLg^hUh|_Gz8?%#d(ANnY`5U1 zo;qjq=5tn!OQ*-JqA&iG-Tg#6Ka|O64eceRrSgggD%%QBX$t=6?hPEK2|lL1{?|>I^Toc>rQU7a_`RSM^EPVl{_&OG-P;|z0?v{3o#pkl zC6Y;&J7;#5N#+H2J-4RqiSK^rj<_Z6t%?`N$A_FUESt{TcayIew5oWi=jxT*aPIP6 z?MG`?k5p%-x>D73irru{R?lu7<54DCT9Q}%=4%@wZij4+M=fzzz`SJ3I%*#AikLUh zn>k=5%IKUP4TrvZ!A{&Oh;BR}6r3t3cpzS(&|cEe&e{MQby|1#X`?17e9?|=i`sPG zL|OOsh`j@PD4sc6&Y3rT`r?-EH0QPR*IobE@_fkB8*(886ZkjkcO{K8Sz$H`^D-8P zjKG9G9A`O!>|!ivAeteRVIcyIGa#O<6I$^O7}9&*8mHd@Gw!WDU*@;*L;SYvlV#p( zzFSsPw&^UdyxO}%i)W8$@f}|84*mz&i2q@SlzMOd%B!BHOJ<(FYUTR(Ui$DuX>?85 zcdzl5m3hzFr2S@c_20C2x&N)|$<=RhzxI!}NN+yS16X^(_mtqY)g*Q%Fux5}bP3q$ zxQD|TB{+4C1gL>zI>g~-ajKMb{2s_cFhN2(I(q^X!$H(GFxpc6oCV9#maj|OhFZaI z;umX6E*fQVTQ@lyZauuv>%E)5z-?zQZne18V5A}}JEQmCz>7^h0r)!zhinBG6 zMQghGt!Do5h%HmAQl~%m+!pr-&wlrcwW;qw)S$6*f}ZvXd;cHw=xm|y~mHbT3yX>?hoYKfy--h+6w9%@_4ukf0Et^zr-DbPwFdyj0VJHi}4bqRetSNR`DoWd( z(%n5>8MQl+>3SeL-DB@IaM{NDwd{{v_HMIO)PKO}v{{##c@ihB0w$aaPTSP4^>n3Z zC8Il%(3dCLLX$-|SwWx1u7KVztXpzNhrOZQ78c$jd{B9lqsNHLr*9h;N9$i+vsrM1 zKzLB_gVdMCfxceejpIZat!MbR)GNZ%^n|fEQo?Xtq#Qa_gEWKTFxSL4b{g}kJNd{QcoQ}HUP-A)Rq;U(***IA*V_0B5mr}Xp$q{YSYs-b2q~DHh z?+muRGn~std!VXuT>P9TL_8Km9G{doqRb-W0B&%d> z^3@hs6y5jaEq%P}dmr(8=f}x~^ z*{I{tkBgYk@Td|Z{csd23pziZlPYt2RJW7D_C#&)OONEWyN`I19_cM;`Aa=y_)ldH z^co(O-xWIN0{y|@?wx@Y!MeVg3Ln%4ORu5~Dl6$h>AGSXrK3!pH%cpM?D|6#*6+A# zlsj;J0_~^?DHIceRC~0iMq)SJ&?R&if{fsdIb>y;H@M4AE`z8~dvz)(e}BqUWK^U~ zFy`PX+z*Bmv9VxAN;%CvMk(#kGBEMP;a-GgGZf~r$(ei(%yGqHa2dS3hxdTT!r>La zUrW2dCTZ!SjD_D(?9$SK02e_#ZOxdAhO%hgVhq54U=2$Hm+1^O^nH<>wS|&<)2TtD zN_MN@O>?A@_&l;U)*GY*5F_a~cgQb_3p`#77ax1iRxIx!r0HkDnA2G*{l|*}g_yI% zZdHt2`Hx^MA#VH7@BEN68Y_;sAcCNgCY7S&dcQsp*$+uW7Dm@$Vl7!YA^51bi} z*Vy8uTj{neIhIL|PhditfC1Jeub(uy}w|wV5 zsQz)04y;BY2$7U4$~P{k)b`hZb>gv1RkD)L#g~$*N^1N1GfNMS)4r|pT*V<&KE1M9 zTh}rzSW#Kcci_#(^qf0gTW3&QN&zsW%VAQ+AZ%-3?E)kMdgL)kY~@mC>l?RH28u;Y zt-@_u^5(W>mDdtqoe){#t;3NA7c@{WoY9bYFNoq+sj&ru;Z`x>4ddY0y*`HRtHFEN% z@mFkp=x0C6zDGgA0s|mP^WNEwE4O}S?%DOtce3At%?ThxRp@`zCH6MyzM)dA9C7IP zI}t;YUV(Jcnw$4LoD4H(EM#!{L-Z|&fhNYnBlKcQ$UScR#HH>scYBTf2u|7Fd8q$R zy5Cbt=Pvf^e}m4?VVL@#Pi3z*q-Q0MG8pGTcbS|eeW%R5bRzKsHSH#G(#$9hj9}0O7lXsC zbZ7#UjJM^FcvdKK3MOEl+Pb-93Px}F$ID&jcvZdJ{d(D)x|*`=vi%1hdg(dd-1E>& zoB4U&a${9!xyxoT%$7gFp{M<_q z9oVnk*Dcp$k#jA#7-pZbXd=L8nDhe<*t_*%gj^Vx>(~KyEY~i&(?@R~L_e^txnUyh z64-dU=Lc;eQ}vPX;g{GitTVZben7||wttapene^dB|oSGB~tmAGqE^`1Jxt$4uXUL zz5?7GEqvmLa{#mgN6la^gYO#}`eXyUJ)lFyTO8*iL~P z$A`A_X^V#!SJyU8Dl%J*6&s9;Jl54CiyfA`ExxmjrZ1P8E%rJ7hFCFo6%{5mRa|LY zk^x76W8M0tQBa1Q(&L`|!e zrczv>+#&b2bt zuD1Bfoe>oW0&!ju$-LI)$URptI!inJ^Dz|<@S1hk+!(n2PWfi-AMb5*F03&_^29MB zgJP7yn#Fw4n&Rod*>LlF+qPx5ZT$80;+m*0X5ffa3d-;F72#5un;L$}RfmR5&xbOf(KNeD|gT1x6bw5t;~j}(oMHcSzkCgcpbd>5UN z7e8CV*di9kpyJAo1YyE9XtfV1Q8^?ViwrKgtK$H60 z%~xgAifVV#>j>4SN10>bP9OV9m`EA-H{bzMimEQ_3@VZH%@KZzjDu` zRCG*Ax6B^%%dyLs2Cw{bePFWM9750@SIoZoff4mJvyxIeIjeZ{tYpbmTk4_{wy!_uygk4J;wwSiK&OpZWguG$O082g z^a3rw)F1Q!*)rNy!Sqz9bk0u-kftk^q{FPl4N+eS@0p1= zhaBFdyShSMz97B%x3GE|Sst~8Le6+?q@g6HwE1hJ#X)o^?{1!x-m`LlQ+4%?^IPIo zHATgqrm-s`+6SW3LjHB>=Pp{i<6FE#j+sX(Vl-kJt6sug<4UG9SH_|( zOb(+Vn|4R4lc8pHa-japR|c0ZAN$KOvzss6bKW^uPM$I$8eTr{EMN2N%{Yrl{Z`Y^ zaQ`-S_6omm((Fih26~Bjf^W$wm1J`8N+(=0ET@KFDy;S%{mF@!2&1UMxk>jTk49;@ z*g#0?*iga;P7abx1bh^d3MoAy*XQp{Hl*t(buU@DamDmvcc;5}`ihM!mvm36|GqRu zn*3}UmnOSUai6mM*y&f#XmqyBo>b=dmra`8;%uC8_33-RpM6;x`Rrc0RM~y9>y~ry zVnGanZLDD_lC%6!F%Jzk##j%?nW>JEaJ#U89t`?mGJS_kO5+5U1Gh;Lb3`{w<-DW; z;USPAm%*aQJ)UeYnLVb2V3MJ2vrxAZ@&#?W$vW)7$+L7~7HSzuF&0V95FC4H6Dy<( z!#o7mJKLMHTNn5)Lyn5l4oh2$s~VI~tlIjn09jE~8C#Ooei=J?K;D+-<8Cb>8RPx8 z-~O0ST{mOeXg+qjG~?}E8@JAo-j?OJjgF3nb^K5v>$yq#-Ybd8lM^jdru2WE-*V6W z>sL(7?%-Qu?&?wZNmmqdn?$FXlE!>2BAa^bWfD69lP0?L3kopYkc4>{m#H6t2dLIEE47|jcI$tEuWzwjmRgqBPkzk zM+(?6)=);W6q<2z95fHMDFKxbhPD-r0IjdX_3EH*BFL|t3))c7d~8v;{wU5p8nHUz9I?>l zVfn$bENo_I3JOh1^^ z+un~MSwCyixbj%C?y{G@G7mSZg_cf~&@djVX_vn8;IF&q?ESd=*AJHOJ(!-hbKPlb zYi-r+me!ezr_eCiQ&SetY;BocRokkbwr=ONGzW2U@X=AUvS^E9eM^w~aztd4h$Q&kF;6EJ1O*M7tJfFi}R1 z6X@asDjL5w+#QEKQE5V48#ASm?H7u5j%nDqi)iO@a1@F z*^R+bGpEOs#pRx9CBZQ}#uQa|dCH5EW%a3Xv1;ye-}5|Yh4g~YH5gI1(b#B|6_ZI; zMkxwTjmkKoZIp~AqhXp+k&SSQ)9C=jCWTKCM?(&MUHex;c3Knl(A%3UgJT_BEixIE zQh!;Q(J<0)C`q0-^|UdaGYzFqr^{vZR~Tk?jyY}gf@H+0RHkZ{OID|x;6>6+g)|BK zs6zLY0U>bcbRd6kU;cgkomCZdBSC8$a1H`pcu;XqH=5 z+$oO3i&T_WpcYnVu*lchi>wxt#iE!!bG#kzjIFqb)`s?|OclRAnzUyW5*Py!P@srDXI}&s2lVYf2ZCG`F`H-9;60 zb<=6weckNk=DC&Q6QxU*uJ9FkaT>}qb##eRS8n%qG`G9WrS>Xm+w)!AXSASfd%5fg z#fqxk(5L9@fM};~Gk^Sgb;7|krF-an$kIROPt4HLqq6+EL+62d@~4Hsy9nIU?=Ue4 zJ69;q+5+73nU|TQu}$>#v(M&Vx1RD=6Lu`d?>zHN?P7J&XWwsvwJt|rr?CZu+l>m4 zTi^VLh6Uu2s392u(5DLaM%)Dr$%h3hRB>V7a9XG`B{ZsWgh4IyTO9R~TAR^h^~>ko z(k|Hy#@bP}7OyN92TKE%qNZfyWL32p-BJf1{jj0QU0V`yj=tRospvSewxGxoC=C|N zve$zAMuSaiyY)QTk9!VmwUK&<#b2fxMl_DX|5x$dKH3>6sdYCQ9@c)^A-Rn9vG?s)0)lCR76kgoR>S;B=kl(v zzM}o+G41dh)%9=ezv$7*a9Mrb+S@13nK-B6D!%vy(}5dzbg$`-UUZJKa`_Z{*$rCu zga2G}o3dTHW|>+P_>c8UOm4Vk-ojaTeAg0-+<4#u-{>pGTYz(%ojZ`0e*nHo=)XZS zpp=$zi4|RBMGJDX{Db?>>fq71rX3t$122E;cJ(9elj+kBXs>3?(tq=s*PeL^<(M$8 zUl;u9e6|EP5Us-A>Lzvr+ln|?*}wt;+gUmd>%?@Wl@m%Qm{>Q0JqTcxtB`ROhd6TB z$VY<7t$^N6IC(s*Z@x2?Gi%eB8%(hYaC zKfY5M-9MeR-@5h zZ?V`qr%%FlPQlW5v_Bp^Q?^)S*%Y#Z$|{!Lpju=$s702T z(P}foXu(uuHN!cJRK*W-8=F*QlYB*zT#WI-SmQ_VYEgKw+>wHhm`ECQS`r3VKw`wi zxlcnn26L*U;F-BC9u{Csy#e%+2uD$He5?mc55)ot>1w`?lr$J zsrI^qGB@!5dglADaHlvWto@|S>kF5>#i#hCNXbp*ZkO$*%P-Sjf3Vc+tuFaJ-^|Ou zW8=}1TOlafUitnrTA2D0<3}&zZz^%y5+t2`Tk`vBI93FqU`W!zY;M%AUoN1V1-I2I zPTVFqaw3Pr-`5HcEFWuD?!8Ybw)Y>g7c0tt=soTHiEBxlY;RlQ`iYY-qdd94zWjyD zFcskM^S{_!E?f3mEh9waR7tb6G&yl%GW%e&Sc5i;y@N)U5ZFLcAsma^K?Cg^%d{PO z=SHQq4a|l`AakzEY;A{n6Rn1u`7v~#ufV*6GZ$`Ef)d2%6apsU6^>QJl0@U& zq|wIBlBAgf0j!YaozAgmhAy0uy;AjRA2%(!`#&e>`V` zg`MfSf5gWvJY#?8%&|`Aj0<@aZ;-q#tCx=-zkGE|_C4)TqKjr-SE6po?cX?Z^B%62 zdA!75;$my<*q)n@eB<^dfFGwRaWB25UL#~PNEV>F^c+e2Be*Df(-rIVBJo2o*an$1*1 zD$bsUC-BvObdmkKlhW<59G9{d=@bAu8a05VWCO=@_~oP=G3SmO91AK_F`#5 zwXLRVay<~JYok|rdQM-~C?dcq?Yfz_*)fIte zkE_g4CeLj1oza=9zH!s!4k%H@-n{6aB&Z;Cs8MK?#Jxl`?wD>^{fTL&eQHAQFtJ_% zNEfs|gGYh+39S{-@#MrPA!XpgWD;NLlne0-Vey1n0?=ww18{L)7G|$1kjI(sjs z@|alUMcx*04*>=BWHv_W-t=rCAy0q6&*;kW&ImkwWTe$lzHJRZJ{-{ zl-mK6+j}V`wobm^^B&2Tl?1r=yWbz;v-F<#y!(CT?-4K(($wWtmD631MN9?trDG zMI7;9U7|UsC;urLP%eH1h%U`LJxT3oM4=gpi%X@lpVR9N6Q(uhJ00RWXeL-Z*V(O8 zsIyyVUvf=RXLBKX`!peifjIMvMs1YT0n$0*B;K^yZf&HN8$N%e=EgOejqihLPBT|< zs)z`nNU}BOdT7wYLy}R10eXUksn9o)jG)&=qteGc|XNI~h5R6UBfaPeIHbA32@*>orZsCB4`Q79}A=z@najfekt-_eTg7a}Mcas^D1ELlN6(y28c{ur|tmueFvIDOQxXs1)_lKrA`L2-^^VNC#miFvO%l6w5uK2bFyu?hyNLCjTCNRRVW^i+GX``giwc&TpV~OHu(yN&o)r2$K$1kjh@>iP z^&`?sCk#?xdFX+ilAb(;I7<$BQ#6j*jKsu%LEhQKe=>ki^ZICepr3#_2#pE`32i4Z zu%eXsgL)3x3Q-^OPPRhm<^!TEPoek6?O^j+qLQ*~#TBw4Aq~M2>U{>{jfojVPADAi zurKpW{7Ii5yqy6_1iXw3$aa!GLn|$~cnvQnv7{LMIFn!&d6K=3kH8+e90Zq5K%6YfdLv}ZdQmTk7SZ7}>rJ9TW)6>NY{uEZ zY^9PI1UqUFm|h0Vqe60Ny=wCFBtKb zXtqOa3M?2OEN=zDX7z}2$Y{2@WJjr?N`auMDVG9kSH~FjfJRNfsR@yJQp4cQ8zaFkT4>5XQqSVt5c}`-A#Z=3-_mGZ^)Hqayei zhJ}wgZ5UDln%)!;Wz@u=m(6C_P@r9*IMPe7Db`CSqad3ky-5-EcG=*v8J&{RtLJ(E zw2h-ghGYcDtqj4Z^nU7ChgEXO0kox=oGaY;0EPqeW89T6htbZg4z!uU1hi;omVj+3 z0B%$+k$`oH5*SeoG`Ay&BAA%nAUjQxsMlNdq8%;SbEAPVC#qm!r7j75W=A)&a6)3% zdQq$fCN;@RqI!KPfl9l=vmBFSFpD1cAxb@~K-$ZIlIL3W}?#3+|2p{|vZVq`YA zMbx|Xl57kJVwoetAo+opiewCkCIO=uBLEaG+!0U$MRdReNsx>+PIJWN6dW)pfeZ(u zQ8ei-Ht69)ZV`qv=vmorhOkF)Squ;)8AUfh<7A_xI8FGHMRW>~%o`1Wt3|8IMrM%& z8)|@=#ssro9=f9HtN0F#O085{Bf6PJnurfzS_yg?qqszmnQIYDP{N=xqPfvl;VNsK^qpoy2&App~Fe(MB7KCI)$p1!&YEB&%$9gTk zmvlt?t7!>_paNt_fYJvw^~LCqX{4opLy!n)md7}<_s?`gytfSAdoScQWTy&Tbr&~( zg9myGVv)l|4-umFBL0)Y(d}Rvt11)(O4ij#zeao~K$vh~JDn0_@3RjP2M0|79T&9+ z?>Vx&M30Sb15&<{RtpeYUf|n7n5GHyc+-FtA=7H$p6Mh=&M0O!so)tze7#WT>pp|x zfWae>0++DfscU2%>|@oiCQj+6O827)1}KsN^a>NSI*4?#ylfG-{q?3MMXX$dUH^S6Ni=Ve1d0(janpz@WqGJ?cG&sewpq294Qa zL{huwuoARdt5F4Dbh#?<2ruzSS{VeDAOtY+52t^xJW=!(0f3P&G3Cs^%~Q~~Wq{YA z!QrEk#>oXK{sc&Z7VB1_>fA1^#YyU1Ff<^9G(!V0!JW`n@EDdj$$2SVK6*7$!BvXP zmAC;h-W75(Nnzpro3CE9eV=~Lp7yS(vXnk@$g3{R`!(UG013==W*Hj{-*F!ujl+np%IX?E0*I&-K^u zY1z1I!`iOu+Ll`UtL|F6Vb?~vk=x9w6}eE^*<)O?pZQ#8YKE#b($x>w$3E*F0Kfk zfnyCo#zOpX1(P2yeHG@fP7}}~GB|&S27%6=@G^V=rmeTB$(w9rC6J@uQmcAMq zQ=Ce?Z0RkF_gu30<;5#jEW32il2?}$-6PZ?au16Y)?kUFy3L?ia1A@%S3G-M`{qn8 ze+|6jh0vqfkhdSb0MvIr!;;*AL}QX^gkc+q0RJ4i9IyOo+qAyHblI+$VuZ3UT7&iIG7640a)fe&>NOVU@xZ*YE`oy!JGMY%j}bGq!= z`R5xY(8TK&AH4b6WoKCo>lPh6vbfu1yYy02g^t9bDbexN!A`*$M5`u&}WqF?+*m?ZoW85&MFmXqQ1J{i;_Oz>3*#0?lWa zf?{tv`_JzP7D3x2gX&ICRn(aR$#>;ciH#pO?<*}!<}cYh_r{hb6*kkXSteV>l9n6i zwx63=u%!9MdE>@2X)3$YXh=DuRh~mN2bQFEH&_nHWfU{q+4=t07pt+Jfj90Or;6JX{BCQrE8bZe&wi3fwEXHRp zz8{VAmxsWU)3nT;;77X7@GCm7_fL1p_xKEG&6G~luO;Bc3ZIa?2b(*uH7qJ!es71c z{Buj4(;Jds$o78u<3df_2~DLq`e9*$SGmrR9p2OoVB5Q(KL3M{1>eq+;+lHK9N?xvyBPHni<#j$sZK{QrKEcdR9+eQD0V? zGPaq!#<-c#a>t4bt+R#Hu_|}dlIGeve@SR!d((u)Ga45+BuhHfA88G0cPrw>>(`ID zZ;aIyn|qmhuDXBthoW{J(WN+`Yud=y(wvd0rm&1*4>6?#8&)Fz z&@V=a0w4)F{^!&W_l6<5xg|-0F!~>aCALbeVsZTd*)M*^tr*!)O8w)mzKThWyQW@X zw%BFs5_@CIic5EPcTJu8=CmynV;``)3}gJ`Vl#VY_3Yib@P-KvBk_%!9OVu#8tG|Nc4I~A>8ch-~X%M@!>yk~ERI|QEcwzgI66IaaY>gx0~lm<@f z5-k^OY#SGC80Yr-tDRP(-FEJ{@_4LHsGJ=)PKZ@`eW75-r0ylN%0Q>&*M;@uZLdJ$ z)rw7Dt5ajr;P;~1P>jID!><(7R;w|Yf}qI&8klT?1dTfc@us5mKEe;qw;YKR(cp-D z6NmUMP8x7cM%~ytE@l*Mp^oN*mCF`gRNhw3gpO1PVi_^JzCJo>#mX(q+iJ(Ts$5=! z13b45gILEULS!=)SmZ{qsC1)$8-4eADGR?v z>~4k_SvdvPHAC}=4(!I^OLgQ@9EMDE7d$PvJbi+K%-HTh`P0#Ea|Jm6zj> z?R)(YWtZoIRx>AqzlG1UjT@6ba>yE z{Wf<5moh^-hu;ptAtPG}`h$4PWcOn>vy`#bH#Ss>OoAEE1gIbQwH#eG8+RHG0~TJ$ z>`C`c7KyM^gqsVNDXxT|1s;nTR&cCg6kd<-msrdE5Ofk=1BGDMlP2!93%0c@rg~4` zq)UFVW%s|`xb>;aR@L^*D>nkSLGNmM?cv)WzHZy3*>+*xAJSX;>))*XRT0r9<#zIpug(}{rSC9T$42@gb zy8eb6)~}wl<=or)2L}4T{vum>-g)QaKjtnp5fyd^;|BxHtx~2W^YbKq1HfB7@>Hw@U5)?b^H=uNOpli?w6O#~V`eG;`irLcC(&Uxz`L_Cl zS8r24e*U71o@dV6Soupo-}Ttu*Dk&EwY`h4KdY-k55DSqR&o7nufO)%>%s-Es^5Q_ z60#cReEy=$4|nW)bLh=|4bxW4j}A?qOle+wjn88oAeYb~!eA+EQ;8Ggp-UldAt$3M z7*E590amz>YB9L(z?Xx&?I37XYw?Os-t+05x6Z4vkzBE6-hrbB=GAB?p{DQXV4CKg zls@_wh*&XC<3R(CEZxg8*Y(6a>cIOq9Nss7{=UQ7Nv%O_WxSyBqnH{@(<>A&2on@z zn57W4Dh*E)o#rJ2#tyxV2;C5#rl8%%As$4qB=IbMt-z|jnWi>>7Ymq37;AW!6Y4nx z1Ogx#!WVdA92mEipgUxzy_?ddg|x)KOCyK)P5v@usc;0sN3{=0slt4CuwaxK@20eO zhdp~Z8iJ7GWrkq_-X`~(eBpthn9|`tZEUCIGiFpJjjxPVE9I)#z3Q$3tw`a69qxjuf+~ z*?v>d5~pcH-AQ~0)8PyIjumD^?SM8!Wb>KZoD7hOlc2nA0_(eG!in>}Ru}>6)>5 z@*}T`Hw{I^-?PS9>(#UFBQpW72* zsfj(2+_9@5x+57aN!`e`f(Mp_I(D>}p8)@&g^g+X1%d{ z%X5boE?hEoj0CiwTh9)#8^?~;|wgor_=Z1BI9_dI{ z&t*f95n?ZgZ5CnQa!v(p|JT?y0%KKgi`Smi9k5r!+!Mkz=&Z$%CFl;?AOzV`YBKrY z0#Y6~J6&dA=m>T@TYb8ukaV4z^Z?VX*MCKcp13-ye1*`gAj_Tm@r{fpm?K!U@Xg2AfndEo6jZN} z=XK0GRNXVLW2c?}B)rH^yR>u}b?|p(W$!TkQTAgu1AIG>MFfNchMQB_^-AQxRE$Th5-E_tBP@v(Cy|ojjP5LEU|JrM8 zVF5;$>Hl^jlHWDPChrTH(vh%bARyj5#TPb>omAs-)4zN z9?9(wybd0$Z5s+}Fiytv}-8U`IC<{6U2_NqEAkv;7lys5Qcq3EKt z0-!^Xy3idllgZ~qX^QTe=i*oGUCJNk>Y26?+9U(Ks|C81S{-v+6ebc`c(yibQbuB% zxM7mk>}dI-TfUi5Jqdu6b`4SqF)y5humuCaHhssdcR(jKf5ZGprx;Oe7VG#G6TA1+ z8oZLl<+ey(L+$Qsck^4fi{I|)p15MX73gHFUU!l${lN{)Ht_Wb%j#UE6cZ9}Wq^>+1wz z9TBA@%f~tby^0YWafmn&8Ppjn1Ng{d;S01WImtMzV<`!zU7;+8e-Xko>qM^OfOZ`Y zEZG#vcm>EGF??&G6+v(3l`X(xMn8ESv=@LdMfdcxFi%g1?0HDPG>blldR`OLlWN80 zz<$t+MM9%1K~JT@#aBZjOu9*G{W$u7cqTM|&a1)0wR8R^*r$<&AhuCq1Z{-aUhc5P zdyaaK{$P=Y6R{40FrWmLbDOCijqB(1PrKlnL)Tm|t=l}toVLAZOXJ*~-dx|_A&o65 zskcpT@bs+d@ia`f)t8ivl{(t%H?O?;=^s3O^GXqopx7E3kz06f^UQq<>gyNmo4Ij; zrOxuzn{WOqP75~PwPXC;3mZ#YW1xy&DEXsl~)u4`-v_{*B%R6xNH3* zJElz8@d#i4`#JV(ko%x;u{LMqLEEDmwD*(ccB9Wp;u*9I?=sC7g>%L{%$4m#zhbjm z)gK{LWQvE1>_yl|4T$nYKNVZ<)vza7FKU5*W~4)KNgN@;SA<9&ERxIfA&UZnB=r%N z5YD4fY$9Mkzy}!G+`KUy>3l(FSi1 zw)t)*w$E4#ZSxfm3cZLC(o3aQQ7uHk>_@fMTHoM0=quh%mfN6%{`O($pyzg0kPf=2 zjA%M7bRl4BhV5{{d4HbnTh`HM&YKw@N~47e7NFGr*9Yzi(7XQl-FJb4hPEKOC!K2x$nWy>8=PJYE)T$=Cqe(n*ChZE zklF{Ms}h0Jd|@o;Gz(~b;9d&c#0O^j{1?tF5dtMj9dG`|j0qZi^aF1r{<7KC5hZ`E zNX2nxJYEr@>u86|tPjTDet;fLn1R+IOm6&3b*}TOyNpIaid@W9c9!jIfiJOgK-aw=xb5Kpb)`E9x%CU82 zEQg_v`e+tWYClJHl=_EsSW?LZO3)o#ox(#2UW9|V7I8fYnz5fRtph`u)dywWL9}UV z*hdU9-BBK5G&}j~O6&dSdWDIpFX;&Or5wNbm^Y+A-x6(K$$Of6JTVl9n0gFY&=T5p zZX?pCxA&w{J)eDSfb?Zh*LT#AdiPlB;A%p|-`Aw6RP2mYTh zLmL~zM^VS0V@*4LkOEG~nQR)HyRB+;*KWli%QqKt&%16HWyMXRhtwdCgyoTm*5#itgp(Wap66 zyr-dgKgjl&t?JLMuw}!Boz)TOa2|37p^FAcPmxX0apWmfp$B1WF_@-dsK+?1F6~yY zEwi!-))Q_CbOP%?p%bx|=d^nLBig-_$e!nh19^Ps`s{SNq{nnW)V-qnz3y+Ipd7HS zsb}z%!+}y8izoy>Nyyj4m_br&8TGFcze#gP4?v*NEdl zzGBLM4qpvdu;5vCFi9^zXU;sW`>pPi|NFD# ze=$xI@7q9B4WPsw4CAO~UJ(S)s@u41E>#9D>!?=*N5m$%^0E` z<0RjkAj02TN9RLX3Js+GArg=Nu>E5z zPa!vMuMV06#7$1dLbwv+VGT(5V_&A~Uy3T^+|y~Q2>lA|=hZZ)ex%G`rhkN54C5gq z>w?qN=A+LgB0-@s{OJs7Da|z%dK)uDH4?m5Y=K(N5KWL)uqDxwBt>QmOk(h~1u6_s z>9x>G_+@bJhBQ;(Rr?20>Tjn}^Y`|rQvI3Ua5$aGq{HFf4BhwAFVk2oHNbk)hmAri zjQ_!g*-c^AKM>A@je&H)i1PsJ5929F<8bLXvONK4;-n6d;Zm7Q=G|k6Fp*AY!b1a`eoS*c zF413z6`x;!NZV1k5)sv;-Dqjt?t&|JLNGSA2yWhU-RYC^oiWI1+idw;6*>m1&Io`^iPgF6c$sN zw9j3KFYs@%*HNz1Jr?F^RiLV%@DyQ^Dnc1h&59pWKhD#AMQV~3k7}>c@gdw=dyRf5 zHGNU7bA_hHWUnI-9SXtjM~LT>U5!uS#{ zKSOhB>l^nUa&S8kEFoAUIDG}(Lr#|uJCGb%29Xr>1S4yk0d)9hoJ7#4xNbi?5Dt?N zBp45evje1L)A;&Smy9J8MJe@1#HwBFoYPv$=k%GOaq!kd58)tzBI~EkGG3Rqy>GOTce-p>jH0rb~c(K z1|9q=$3)Vdgcwyvy&>S3p(f~O;~?XK{)Kch&2!gs=%kNH#-Ee-i}S+a@DNWR(Xnv< zv7kIUUD(c?RS|JmPeXBC6cbxUl6qRxl;fFAiK%!>EzFa zJ$-mz?G%WqC+P-l!DLX&nfxzGAnLaFsOg^Vq~gaW2QQ<(qixj#J=;Y{m`?kHkfO)i zdxQ*`2Jr3iXdj4QE%|AlQ;|Wx~pKrr7xuNnTe=t-AO)iha6xDYpH}>yZ z+FD^H2VS0x4us;Wo_95^kElZ$>j2HW@wyeLi3i%Q28NXxQT7V1{iHY}Llc~!Dkv8* zM><6X$}-pv0N#?+N%W`5%}K0Is%8kCOC~LuR6+;gtHYPi9=dqUoin~Q^MhE;TSIe$6dEI=Xs(`oTlj_C-3c4KT+wJvpu4Kkn_RZVg5jE+RF`XNx?0xmaV~bW?v}wVTXn4{5 zO&2X+*pF%!%qu@3SLRk-npU5?`f_cV9;|pa#ktlD9VuvRx;TK+fWUv_$vC8-@TcO4 zN_-D6?7|-4!VWMEgQ}TUe(c3w4{eyxe8C5t7pS0MFe;X@U&B?sVDIGR;u>?mPyb2F zV5WLiQ2mX&1v=E#B`oe9yk4Y2^CFRk8*rV6k1!uW{m47&7E!m%(ANz&+ixrB^ng(;#RLHnX%tfsjJWM- zyBo5Of=eNl8*;gm`ozE0weGdP7~Iz5$$pI`$C5 z`U46T|8cnpt;J+VO?%~H_`Ph??bcn%Jzu`2`z~tc^PoA?r znJlfFuxIeRC?a>J?C!EC2Bn;dnhn3XeZ}sbjb-10*a7A?aS00$P{m0wm zO_v_`nJOwO*k6S$tHR@xmt`N`;fR%l>^^ZvbfRm}PUBtryK5pTwRdIZgj<#_irORP zr7I?yj7m&+KkD(;PKtLXmF-s9=>`j_AFjI$YN7_w1g7hD(md1~ysZj9;u_Y4i3Ssz zgRH~g_UH9AHR4A!67Z@2zch=Odh*4WzWc2=ekK0-ueW&=xy{z7Gz9CSbv}Pk+4ST# z#ZxnW&!Z1tS0A}`@LT_*wh{sv=f-Dy+2cPoUi{nzYTGjx)eit9s#G5^D0+(|iNBlJ zV$vUX35MrZ8K19VAN|i75_}Z#DO`R~MZQy~2$6gqOvN0Js%d70SzJm|ER&Jy5k>-I z!fh9^fC*zr22w0EG6&Uqo`eqC7_L8gi(#?!A>;y86ak0F7|oHQIhmW!15hHkZ(*|o zF+vd5r!A(imA-b0}qc4-&FS58}j>!?PW$SEg*;W8H~a^e%b?2`O8 z*`i%!x17FmIo=X;^83K2Y3Hja(b_rMns6%ts^>=(bA-9V<9O1I>564?R3a}v1yYtH z*l6T7AY0T66-95WtZgaP8(}|MBGlfNdh@=~Y1m!IA7($BPUtE`qT@h@;M3Hd z;_dtQw^?1x7-WaPK4XDxuqd5+qVz|PQlALGw|x}&MFa4RtVSK`(e|RtFN=u%s&M?) z7+HD3$diG_iYZuX{0ijc(*2C7cTX)p*3LRRtn3r@wq>%<@A9jY)yX*dv zSq7pIH0)jCA$)wa^7RfPVlWXzzoH}vzHmu4?W&f|zEC#fi<;dYS!Z*G+=!O(wLx7} zkfS~!6{@R-(Uw86L(mJl7`6&&tfKDx<)c+WIlqL)3pSX=7*`N5ysyr`8ap$bd^E3w89)ZgPiCBi|f{Ji^U)|AMCk%95n_gVk3|_XmE_Z6(keo8NCgI|@0sfZs3_s1} z$KK|ZCF;AE#cQiOrv*z^HWTBHM`H8Hwdx20FDq8lu^{(Q!@5s%Urrmi_ZX=7)j%7* z2x#|wO+pMI^e#2DpLkU+erWUorFxiNlu1s>XIg^5wIEm|joek2Rd2IsPtNkBRLQTFsnoh4v_<(`f@uV0I_G*I9RD+?L~j{1bx`#0ta zEeZiTNBzhh^|GEN+1vl7{w)Wm!`yhLKAuC&Ve`GhjRo0c|E^`tZXfkQW;&_kBLS|M z7!XYb?!E&&=u`h5Ld{_dyivFMQHW{aI!yVS7oS=ttZ_4U4sb{P=wmO6wCrO3g8Cir zRxN0ht{}^=kNOy`2fdgiLzr_8?$^fWMSdbcHb<)&+4+$`i%$>mB*aF7fv0tiFWhcK zRThLy0Mtx?A6Q34Vn$tJOcHkv?-ldg8_%9Jr8YX#=C;}%u*pWq^?L5VVi61EUkC^@ zTi3LAgna%bC9aB?Qos0?XlUZtnp9cISx)1AbGeO~JGb1<*DpHId@iRrT4e7+!$h07 zWDZ4FAXQ;*hdB%9)8U`#Aq1XW1`G)sm$Ol@ZCv2#2r5~I^BXuYJm%NgOkCQOAufat z)Mo2&C`TDc7EDz1sE;V{`=Bx<#5gYrDb+@@FE3>Yx=pZB79-7UjD-g%Z#qc&td6cl zI`S1u2Q2b!m^1LOg{LEV_eV*@cFW|i{!+a94itA#8 z2;?I%3?C8LQn5B+Ac|?$1Ejde^`AH_B}3`>#H=np*@XDR^y^=fZDd~Fz;wS>e@!M7JaPvv zPU?=U|2$6iw_+;&j{0oiARgl1!2p}_PMTg!Yxs?H%{HmJgU62_ghA}_;}{7x*brZc z@>!rSz|M}1YPdKizI;?B3~2O%LY`8A1SF;-m z+Oxu{+PYOU-V9O}bVd$T!;AU2M<2*KtciMEC29!H9V-u9ZUJ$M-4#Nb$5QVy@LP8HyfiyK->WR(e1g77J;isq@ zxu$>@C(@*mf}RY@L8hJXBrWMOEKDqt3i8iwFSwpR$W>G_j=iMN>(!1>S7GdmXt%UH zpfdn%XxP3S<>d1=1{yBn9c@?(YZkyNN1 zQx^M4-32#mo8SKR;r8t_CV3=RwbSNzS!Jbd%GS0L=qT*0!ERw05x~DzSsUKHYQ||Y zuwKD!+2nux!l3~g>0-F=;qnW{w$F|jqXuhZz#N`4WtzLDj_MYvu(*X@fb3G;s!oPE z?QMW|e7J7#=?C#3QWQRp-~(1;_=?J(Y^}oNmHRoN$^y4Pv2Z8cL)EmwWVNJh@>2ER z)el6y-IQ`!2h2{kx3}jwTf$_!N75)(mi|n=?Ylj_>QzqjfMiO67Wc4{rOcF4JS+{j z&z%duf1`r(U@ZlI{F=sZFnCGJv}cN<(cA|5AP8m+HUK z@vG9%#_zOu)ChxFSxmKsBSSO9XX%g4SU79e4=G!|Cgo(;VeA8dsRxIZ$Eqhj(brh0 z>Jh)P2`<<#u_i^?L>%2jxXAxZX%?<7l073C+~1p!t{Dj_9ZxL$sz|_G{C#{Hv@t=B zP}EsMr62u$;U#=d%MRJHCiNv=5OI3(_o-A=G_9B~AsrRui@pzUDE@tHg#6PmWEuT^ ziPt|@8=kjTNmkqdOlyJS!m{E9I87hqn;%9rT0<0-L99QeURoyK-&OxH^mcao3^t~WeS^K zH`XC|VCLo6*duA78O!ugN@5Elxkhd!CmdSX&*f=utfmDFD9PkBHMk3&aFB&)R8NL4 zD&i)OQLO z(Z_o2Zs~o#^$zu`{XU~$I{T&vAH3;ofJ*ZpJ&JR~s{J0}8cw}`t#a3NvWA?#tMY67 zLG}{Q{#6^CipQ$*V2|W$g2v->Y9+4=(K+K`;I4$BFUb9!Nrk0B*fL+v z_lcdO1uEs@|8I@xoKCB{68@q=)}90JCVF33Lb?M@bC5mog<2~vPXXzk7B$|75Lya& zL)t=%E&Pk`S-PznN<)4iAI;NU!@f0_V&wOND{4!~b@1&pAN$Goqzvq>;o=lr=43Xx{tUtEaN3B>CWZ)Uac%%Y9--wFCA~Ek7aAC_APm}b zpXAnlNOIF+;t%pPlAxIkvv1neXa8*XxNLX6ZDDR(+U5bi-=^>US$+3TyUFaf{gSPI z&A@*!TUbRQ-p-3$KUDc=Hp9j|c+t%)Z{KNid2DyGia&p6lgtpOkDeM{Qy=)H&22V` zFBRKM=Etf98a&;o2pD`R2ctkyWxz`aTDZXBjY52aOspy*2=?xDIZi>&&))8y?Pe*( zt;DkFm|`@cFI!Kx=wFn7fh&cqy-f1RZb2KRCK7JNBsApYHWk=M5J&|wBQOdb+2_^g z*;b(s3o^wX$sWZHhUhNh^+UU2+hPaWw)eN~kHy66akHOp4#cDm_4zDetK1Mqx+sR1`nMz9wwQP*hL>=&Kei3+FtV>|yg%{T(6f`N5BR!MdXj8xHG^3) zqCJiEswQF>ZLP}3Hs3ciKciD63}0Z^MFL6+`V473sGm^=U1^Mx3`Y|Mrl>H0pEcT6 zg^H5MH*WeRUNMs9VN5fcZQ=>}GHBs};LS}+P-y~P#IlYJ0P8ym@R(0L;jYe*1D4ll zwDy~vES0HtyCCI2411OeiC>SA#1wX;8DRXzVihdy^T9BjrZUmN_=b)~n*!R4%Wps~ zkbFH!%W;I*pJZ#8%)c_#RUtKlOksrV!Y3i%vh>?b076sjL-)-NtH_t7E8;OBZOPa@ zAofQ3jdT&<%k!kzaG)7qW3j4HcvQe1&&jd+f8}J3!f+>UDx7H_B8^6hA&r*!PDQ-B za5jys`+BVIUd>7lmgi)Y&fyh!`yosPQAwyIh?7D-h2#b7);pTpdfDrCm->#&W_JPe zRvi?=>OgitOs_62y`!|JbhXf5STOdjJDPjj*#EK7D|Q>bl1&L=hPkN@2)(QE#vP@l zt9uJeTG&n{WG78N)aYu19%#`y%8i44oVsSwNLRxgR6hF`tsw;8VRy)COB4`B4i4SsLAa4`Y(WRazi3X`Vv!fMiDilJX?r1a{9%U3-*f6J-iKJh{i^La~ z$yJ?ASG(MP>=IKImh$g9bD7xJqR}YghlfIHszUwEmoF2yQ`Xet0HgZCGNmYge2TvH z+d^IF=q3{GD`-m8K+R-7AdPA64e{l|c4AofbmD)4hUvwM1bw^%@mXLok{H%R#q;qz z+gU3h@JZH-G^8$-2?T_&a!E51(fhSa5Q$w^j>=mA9b7)O1^G1VKyM1v8fOAgDLfFwlSN7aDkBbh=1Vofi; z{_|sQ`!zOY>fWC264~Y0Y;ZbE!j3Cqv4wlfV?E8SiTe3tr;ceTaXo*JV!Oufp0KT} z!>xB&7aARQo9It=F0Wa;$5j)X(=fKBtv5LhYKFC6eJA)BwZ>zny85O7zI6@a-&ln8 zLF2LorHz$i{9dO!8mb#Jp?&t4L$8*9&!)KTkLxQVHBP8FA!bZwX zC$1xtlqa{pU|8*e#v_V+#E4OT zjwi(7(vGZ$V!mG>tD`=FtRvSqWZ9$*B?GPmVd1ek!0@{$s=gg&_gx>I&W_E$e<7Y+ z5K(_sDS$qH^8rKPSita&*B->#;u88_rMf;Axsguitwh`|=XF8(EVlU^L*PKbu#TN~ zwj8|9X*SENE}$egSAG|3#!^5By}_`$$?RM3+{=QMMid7b`V01GIvvI+&E63R2wQNp zn}sc$*2c&2oUL%!tO4~7wk4n)tpFT)D3<_3R0r=|=}&0KCf!VqIpm|jC(z<~qb-#Q zZxk@2wJZtt%hiN1;J9w_Hzt9B+S-HzVkb8@NIl-+0XLm`=_dDWyDqXB zn&w}0*`hmpYVLH;R9>jKpbgr%Tssmku7 zB4?i;DJ=yE$6)n>a-tiWd=_(RksK=Y6Abz5;b5mLI|>)(FA9o zGzACes-Q@1Vend}5C)iY7*G)}1M%Udge?eW(1HnSXri;yq(~2bXQq`x;Yrz#0k&ke zS%JGlk~lDWC_ny*-Pvc@4#dzy&@`+2PkV%% zOIv<3)+u>drFF184*~^AoZL$_J<;#J>d$8hF1HEz)8d7HT$%mI=(a%Fw_CitukY~T zzCPh-wvU#V(e-YoddEiUO$O~Gr_8a91@$Jc+rpZOpW6;!qTct6s-1GiRv51Kzn!ku z>d;8_q{~ie0yF5Z-59^#vLXATUx*cq!zD=G$XZeu&u5Te*HqWE4IIDJ=3 z;X=s*MnE=AeJ9|E8#P5YEW>Y3>i7+gy{D`72zWgEJ6_;p$$k1u>hqEMJ4WhXT+1`J z2UoHdw1-mEKE?MEYBN#+HGKNk5c-SiJgPNDBrxIO3hq2zQ?Q-Gzn`%I_?VYp&dv2M zvIvf0jiNBnpf1lm=3_A6ApuPS)>4!*8O26GMgpxwaM6T-up7}x$fShgk;qe5v^RIo z>TaB#z4r{2{wUbivuj#sL%^MIIAif88=Zo8VO`(VhtJ#lK)G7`AVbhecjuza-rrB| zo4s>x>$20;IoY}UyhY=kM#Bz+WZSjeUwYHVtw){{#_rt79ybJJr`6`3xa`^N&f)n! zT=yimh90T==dW``)l)vNIle^QUoEWPPd=w1q+I0(zj?aa4;5EaZaQsy5FJ4LeF}5{ z$zg##sP#GwKG2!Ph}IYe2=jqBViZeEZy;=DiXR5O3_2O25Y~Q9y=cg)D}9l1=&&Xw&3l?g{8))$`(k@{a1p3a{ens7utuI^2=vshxrlD-kY-br`D+hAM=))3(PZ zpyB3*357l{^D%K-(OTUkjEoJ4X>x<^UfmPAA7hlXG?QgK21ybCZk1lxS0Sifv<291 zEjcA#Q%-#E!a(4PJtQIWk)#atL{s*GU*JZt07Zc#S!1%fwV7fXkwZu$LI=?Jii9b& z9N7&))d3Vh8fPHy4GD@Ijl7yD&?%NGuJ_OccYXkIaDN7{Ux?ntALbeUyb?sbz03s# zLfJD@r)GcJGkZS!PFErpG3low5RJ#jCL63{qLHqyaMc*AVNejQp_b+{ucvHN$a_^~ zK+n|6Qz^l#n5WiWi;#UEURyWC?C}74{5m0i9bm^jS=(82np)-?!p5j&Hj8-6#y5q$ z-cZx{GVhaJT^!E3OK(B$?9)Oq;h*nmgonr@l}$~5ny#*74^BUz-dtT@>WZ;S_3r_} zQNaQi9BKB}jHzND-dA1Yeacj3_qnU%q4vw$L-Baogt=3ig3Ri*h;4T_HQn8u6~D8% zu3dIGR>z7KUO$}07IDA zm>ULZ#zLtQpB=zl`Xly=k@2w#_&57?*Xi!kJ;wQT>Y(diU_s7c9> zJt9NLo6(QTdY?<&%(7s~gGuhxX6Ia@TxNd)1c%NSn z1vg!?!9F%t+BbteRT}T^ikFtgySn40Y{9CQ#s-^l6%*Z|a#r=PT|QRt>uzZ1KDuU2 z_UG&)_39e07-r|Hmy8d@CawADtYBN~ud`dnC6l4WwkC7cwB?%@#G0C73m(O(B@{A= zKYo4MwAZI+m;dFW_8z_0tM6&w{t;apJRSqCB|8-3|G^xy4{cteem4EFg?KyO^H>jM zvPiWhJ7a++c1XQBBKT_Aev;X1adZCx?O6i7i}=MPVM!{DFhM1no>Vgi=FJObSSzE4 z!cz06q4?jt9&?tl`>Ym||8Lbn@fQ|L_G8v#F`IpVs|l!&x&>B}_z$1B(XGyIsHAWY znA8qOJ=@^)4xPoaU-h^g^}_jK@kTQ7$?aFf|5I6D)sIC2%qiC(coF8shYu$ie*)ue ze%G2{U`NRIn<&=&^cNmI;H`MZjd~?#3I1s@KF{obqiu%g9@l{o^DS=Z{*u!j)-EktzHk%L~ zUeueNeuutfbuxAHnCfe9zB#!P8?xVF){CM-QK}``94{Bxq4Q=lI*@*(t$ z0*llTSuC3*FY_i0Esz=DU(#!`f?@wi{if=Z>r@~3asMrB8H6RvvkTcW)vbP8ZeWX4 zzxps+&i<@^TXl<*)K}C$u*vFs=c>O<uva_OepgZ3^mp(p%~u)K{5Z{k!@f>W^5N zctHJ;`gb-C%!>u<(kED#4A{XPx$+SHa}?%+(O6P8P)JhxL-2PKS-#1p!TbB=d;5nL zMMOs=yP`{Yvn%^wn}ki9e$C!VtI_NeVz`$Lz%L_RchA@F7J^6AM{gFM+M7MOSKOPu ztXH`F#C^w(VO);r;56Hd1-i|6n#b*T>ceqoYd9adu&Oc+x`?PF5k{oi7$_HEV@K2z zymA4)N+`DI{|3bN<-4D@&N)YxIVoqR5q@8N=Kc5COtz?XZfomYb%y==nU^drYn>b!5Ctr?PZ$sZJGC4(Lx<*GmYK3@9};69v2?xCz*86!x1fq z9-^Oe{|eU+0lSwM-%%oRlZiDYBcsgabpN8BFSM>vThx{{TLd#395z2-=dkJ; zUPumj_0A`QOXa%S$dG#HKaV)PHrXJUqTZlMEURp*D&K#c?PX)`>TojQ>yzh(U5ggE z+}3v2ww-mQmrPrgHX82`E)7LZ#9*S)OrYMVHZ2*%Ix2 z-f6n^R()lg_{@W9puD-%bs!$vZY>)VYBn{#u=iUtgZ1U*4oibOw!C4kr;~&cIo+d? zul5rmlh}%uY=)i|^mJ>IyR&mweFZIu_7x~{W-C@zr5Q1cK^!y+OU~frPEZqXZ04#L0$|tY}D-NPT^J>z!>2 zLk;VdDSg7vTYSmLjc%I1lCVSm>+G7BEY6w@(XH|*G{ zSt~)o`-!M-5J4aV2N@%gOd!0FRFIBn|vW}Drt z-eWVGJOi3H9hf$!nudR8+Nmhg011-@!@NC3DA2QVhVsnWtq@_vVUsn7Lgo{)!})lf zHnxUxXX|Z}q6~&9Cutz=WXN1iJCP;&D8)pBPR#N=xfBTp2pd7-lFF5XXBc!;f}%nR z1Ca6zjC^CAo!5Zpsbiu(lgpE2dZaZQmR3Pl1Nu#$p&}HOO1KhD0hr0cDxiUoC%PDR zz2y;b(?1FUenyXAUfrc`fgeIi%?Q>s#3O>1`S`d7)!ab-ztxcdp zi(oNgfzqrSy+Qa-h~$kCFl>tV#u zT0yo>Sj8|%X=Z5eLYl_j3H$wFA3GlQ`NIC8!J3ZtWgQ*Tf>iySj%6K(I%;b=*zAUs z@a=8sq4nu=XBezD!_2jBtet7FSqQn zIF@m`p^X#2_+Y@)f(;Nc7NdxOl%T-$NRFKpzZ*Diiyv-9$byI~Y_VA7@fF$z4H|Dx5g*3@-my-zW{NS^+s=4LU=S;5ULvFYRU7E$thNp8*A(h3CX5s zqQ~5@=c+ot#VX*Ndavjg1ef4*RI#r4+51F`-Xy>#L9~eMYl6w8mrb%>5bZT?ljVD6 ztEdNv0*uOqR@o*xU>7I~%q&O{-x-#ny*Sp3}O21M?Rd(O98C84<|F{P!iYQi+&Y*nsLu5^Ihu$V)k)=GECZL$l#xZCMb z%xz~?w@;eYGR~3+M_}0ce(?P zl902^TxqD4$DQx-Ouql3YC)>Mv?0+^0b7X9MdejK@03cTh{%+U%}ktHqQF-^C6`xw zO``FD0}P~L0z_&PDjancf@m?ZGR0TUYN{lM-RfudpltLzU;yJ{R+GzQ*P|q&zCuzY zP@pguLKr`*Q*oFilK?v&y$CF+j-b`jSz!_lC6mW>m+2px;ND~mcq=BCmMTz-PuXY< zOa5z2j)rQ{(LTN*&~0=Yh5whf_W+NhI=_eaPTAgjUu|FYx>|LuiX}^yT;wh{;oiU% z_p&Z@Y`}m`FN5C~v?rUXJU2@qOB4H#QH{+~N5*}@@#Jm2%V%+B2D zcW!yhdC$u$WMz8Y@Q7Sm;An!nZCaUSSuojY3}>m>9D|bq{)XtxPsx!lnpMKJ$>l0=VE#0Q${LhbVQ?(avB~M5H(A<6VIs~Hmen|XCr57cj;wDg~y7PjIZR* zau8CZLCaPfRJMsKeNi~1P;*LSAkgMF^Q=afBekooDqXYIppZJ`(kv}2%`0n&8lEg` z4=C(+1ET{^|A%kM#z zXK7m|9Wcfc3=~;>1jcJfX#rU|Ppz!j;7pMyJxd%-z##=(QTY&BIZl!@lVSAb*KE2t zsC)F&?X{LH;g7;@GHGHi9oIy36f@s3g3 zRt#I$TBG}b-9;4UrV$&5Ij9vP)Y;Np6VLT3k-c!=P<<;z&y-p^C+_T2?PjhnuA3&) zZg_w4iMx50MTey|GHd-~Qvv|JOonzEpncEx-PZbcYu(#|MF)Yep>~>mY?NK)j*MDlofYp2?IA zdWFjqQYB^@4u{F4kONMK_E=?Xxs$LThk3UpU19S{Nzmr?e_{2qb`9sV2yanqH0d@5 zKGJp8aZ;((RpJ-E(g5Ey-P)#3bab(6W+bgQb9J5E$fs<9fcfNuxIvFo=h1Dgwcy+w zPuTU(HesXi2ZPm;XEiGog3BROSUdQwi5UwQ_J3+1m1G-UYluB@01JOMr|AGf`7CDG z0ig`8Ee4)kL6qbPGy~CNdwL7bt`jNhr{b~f<0Mqx@25+$lS$DH(Vxp|&m0t?&qQTw z7?k*9V*W>p{DU=}4O&dJVTtJY(^>`^lPL~F6O|IFf&j!DWck6E9}tqnNz(gl(B;1+U04#Mx7H@PM!jr;8}`p8X5AFzRgZ z`H&lBbVagpDgs^cAL}3%1zD$XOne$PNmH;OFF;TKQt?TS2u1Xly;A5E%X>i&LS8)c z94WDnS|omqYiN=XeK3B}x+|c@HmfZ(WQ<~YG9AvJ!q|jbd#I*5WUrl&T>ys=H|eYa z=2P;fwY|sZguD`qxdX)M>uI;{{E0Cl55B`!K{}wLHeN|4VH*YnBfJf$tm5E77<2U`gq>@HG1qNC7Hcyb!M;d687pf$B(PUZ=T|xM7)L(EmRVw z;~E{-q~ZvOOr2pdE3KGuy*wmJ%9P@R0*A2yuAhIFS3E2{e{lXEPa&La>y?-W>-8zjMwKGjQ$BzcAdCp)p^-It?U!LP5Hxpchm^Keq$?$57$5a!Z+()BJRD{ z6WgCQN}23z-^iC&TytVqsnMs6p-*RQ(ixw2F8vzfP=&GB|8F?{vwhrLatNCSGk0hY z#-0-r+MT6XGIxqGf<)4vq(!0^mfU%UhXXyCkz}3fmG;0s&`8l>X!W^JfDuz9HUo@{ zuuFqpp>Uv)!psk76{RqQDF$&!v^n_ECT`}V@{zZoqC)oA7_w~`M~N|5Q|_k zJ;Up>vyh*=Kjn%>HQJW}(v6${w!9Z%lq8ZlF>@K=Ek<&|IT4DB~B~Y_O;v9%9bdID;FI$4}a;O}@l!+Yy zZ67)fU;`NEa8WOT7DH7N_&*q17&?q>qwQXMcFgOOnF<0N*-^sEWbzzvC)kr_vv+i5 zgPm2{O*$B>IAd@{>+WUK><(pc@%$Y%QkK)@5Tn}4^Ln|tOsDsh=f>O`Mru?jc?N+S zjv9?oZ;e0J6*s%IG6n*@)S#6c137i!nnDgDIU_YINmjH(${tUCloc<{sdVK)q-C~s z^SX%F!SQCb+A?8SAq-ab;ILesL&}?2F1w-0Zdb;3_7dq1y_J`mAZv20%2Kk(?Wvhm z?BgJojYahs`X@A7)HA9Qm5P}EkW30FIDr{C1ON{u z1g5dIMr=}b5GjQLE~kiOEsekhAqGW;iWew{c8QDP()f-j!!>b}0<_?aiq6~yI>*3B zi`CdXW~Cg76+JS8SL=N!|F26HjVUaAW#N(;&=GruQ@h?1{-Ra%60++(*a{-;SN={& z3m*yJzP9zU)P6F#y&<2IYIRcSWv>_H=QF%ksji&bymFkwB+s?s!OWBD?KvFpwAYaF z6HB9tl5(fq9jdFlXQI1E?Q^gHxncuVOg#lH7*|HYd$Tnnm)HD6gV_v+Ekb4 zp_-m+TC}!*?8^M?Y`$XK{JN&qk1Sq6xYYg&+mlym)o2Awb#46$jTWSN#;OI(jOptu zaCbaIeUAorw`cR3Q9bDuE~l}?)pf9WSllS}RTN5{AmKP8TP%l##64O+ z<9w~)>KD$L^#-v&PKLdn&JjL-V;0%hPd@a%E}(nDen@49b&%5#O-QsX6;-7Ym_{)3 zVl37&u%3X?ma&!7b)K&CFgV2vcWds-QvlU}1h5qyxV^(mlpUfHjzhVqKa?A?iY8<~>_=ad! zk8dO`rvOwQj>Y9oP2*Ot9wKK_hBC~WVtf!r`yU%(p%oD8e+cg4QUi%h2a{}O5}EG* zZ-HLS&Y#FkWd<|*0G}o#4taLmE^k0-iGxUlg8Xl6I@jpH*%~?tx@JuRJn#pu1 z@%_I=rNM%Y&`YFTCG|8jY9=GAaO%H4EqhwG9gJlaZKg1oi{db>rau>VdE^b)^5%>b8}?cL9itw!Y(Bor%WpI?%Pj4J{j!bwjl?n=A z?##%PqWmuA8zS)5vCxk(#bC(9jFU0xQk5C=7R7TRzMFn&JpLe}gI6mL{C!MbWW0*I zJeV8RWO=t%FK{h(m362pOLR55=AN7W`u2&T{v&qlpQUo)8&gl^+xyG^_=H+E&E8{g zDtj>Tm&AiGOuNYD{?mSBc+fDm!jX{TQ=#IZQaQll|>^G`1^D^SV zM+ZBRqk?)b(96%pKAv6kG#;Gx_9RUJOrL=Ch#REmXQRXa?RfD@|1DZPOH<>K-+Z~L-ZeSdCe_=8y zv$DFgjbD+f$Xn5p?QtF#T$_pgT|@$@QGPJGo8D>TeAt8fg6onA*w0M>p@iDdM_^a=-IIAa==ijmLcDs$P+!j}iuEj;;q_SK-hF(6t&u*(3 zU!LE)pqCz!$h##W9aWv*rYjeIUm+JxEFjgC8ezyBN-_G-vS}?09R$E(jR6BMU5U^@ z(V0P0B}3^eADjeW+@$S6T2jX+!gXXQh=c{DMBthD%*Muwk`k2(;0!J{>|O2$aekt_pC0cNlWBQj*NqU$H3%h)ui z?qoV$6o>@NL$D;;M02ATJ{}%ng;dfcXd{fw1p6fDH854f8 zL_5c+rAD;odO-?4m`z)jE@0QsIP#m%s{3yxi%G|qJ9mC592Bk*4$?J5vvrf&4==v> zL*Z%RPT^^~#-wiB-EW#fR>F=Qt#Nm25b;_CbGzR|l<+O7jV3LT3y%tNHaS?@`}o41 zF$uNZFw7Y~77Aa>jb2bAph2cqyb2hF{`0@kc^4I@JroH*5@Ck{3%HA7J ze{=QfTZrXPG(~C3e0zG=<=@}#yeD$(it9e|@}t3Eyl(l}7SBEY4FhdhBIcb^!*gCl znFlPvfq4vU4akQLkM!yPH0F@Xp4CK5WGsrIY#-Z~%66Yny0cS6LL^vZ{#CoPf547v zDOQeSMJf?e5Ldtea!LXg_#yu@^rU^*gZ%^VuaIC)(1`K^c$#TLNtk$0pons6AR0!$ zLUWQKxeJ{spst%xMbvmTKy*u_|1@&<2(Jsb3$Ne98JRk3nUx!DJ=x2tx%A513Tb^+ z6{A$>`g952ZR_y#^#BMQ;Q?NEWr8Kwqc!wGt6zh&EFKrvp{{ zN~{S=Y!iu^0Jos91XK~^De&WAO?3BQ!NF<=uyq~mg=ar(~#oOa0#k@s$PSzc6DGpZY zT%MiJKfg1}p{soS^vIIw;22}*cuMOjV++=yo`T|dD%z@Ov!(S!t0^oRsA=_x^+YR- zRun2H5=~%|fM4gQs|vMD>7n5f8#?tsN@5RaH1W^l8V#@Kb6(2f^@31PSCF5~CtaD} zHvqx#ExV!o0Lk}Jze|zj2?JMi!xC>^ZcUbx|8oD`UrHT5QaV&bC3|pDTvIB|$&v2% z6%>eP4*a&})c8hn-$b+WaF^U1-Y9%4?aZpl@s?;DwsrU3yUt6`1&HKhr(r4L3qt&ZY~Ue$d;q9YOJv}hM+5p1Omb%T%HEakh-=S^t}!cIW|NCt zvYY;N*Q~sC1sQXeEuA^!svEU*$tdANv&&^(v#x9Tve5*SsoPZk-nva@m)o@7>0Un? z!Atj^ZD6Nk^lh>fKMh(sMon0&1|FKqIv6qslh=z6Ed%72Dy!IIOJsI&k(zNe{r5j` zk_^X6`ZxFWKTWP6!%seNfB&|pQNmWNqVSmX-rpQQ`2bN0Cje~8WfmX!`rCUhuDV6| z?tzm(+(*>4Rl?Uf)zvuzW2UIDP+k<|WI}{Ib%x>RC*r31(n%p}+BT+-9GkW+IrRJX zl4DHYwrN6EI=PMW4E<6fuero2mvA4UMJq5i)7)epXyn;=e>z3@9f-LGcf5hMl*Uci zj^i)l8w{96&a4mrQ~GllC9!c~%TH#{M$B;EW?N3ttH6-F_R*bkE z%xs+9eK>1JJlEyUi3|T4SYbBZx6y2}B_?h-TH3hruKPE(H$8SVQM-|~4Xr_@In|BW zVgnhInnHim#YFuiJF;qqG`&6hB@?p%o1y+ku}Y5rxPFzA>{ANaiBNe-q$cmhZ(g6f}5CD+Sf>5JC1{YNhE(3F0!pqbX3(RwM@_N|c zFzw=ol!l+B7sM0Mdy|AsMx{HQl(76 z$#hO*p?1?0eXP0O(<)bIWm(nM?>D&fvK;|!P?al}G1;T~4{9s&3~cWA(L?15m&fK{ z)~>Hj3O^K`+eU6-gO#NfAS4*o;1-7UNR|0&(@~!?n_WwQKqAZxwyrJL|JM&?c06U%ORPS!-dO@oAf`H*?OVR=v)~F4S5z zN+5)YCd&}E8gy1RrguKlTO10oX1m^K%4>6G=~)DM_>yi%EXJsGuk#kUP6`2@0mFH& z*Y7NFja4Y}-Gp?I88a-Qs4d@6Y3k4^;uG$8HkVZ>6{d2Ts(+j_*H>Op!RM>kkox{2 z;Rsw5Iu&f8xr|1}tTY4tlHM>@EiDGFo?bbl;~Fu({1Z6Pa>+DgRgwURk+FuLorv&p zv=R76sC6XM%S1>W=qad%1G_wM3Sh6nDM0zsc0|E!6pSFE;zY!kd0?&wr8l1tn`~l0 zKjN<7P2T10Tav&7>10G6STwUFdt$Ckoo6!J;)Qlku~Vxs*jOESa`jr1$`w?}mAukM zx|OzkuRpal^rsm`;TczAm!Ag(3+p`9y^Z2s;Xjy+&E`xnc2|LnIxpPt&XsPg6uUf-7ft7w~JT& zfw+4o-?d@ch@?j;51V6l_vA4*Mm!^38vC%}t2Q0LXa*LS0U5%JS+ZNQ2IGMa4z4Ku z1XMXlM4({XWT3mXmejMX4KfvQpFUQG=p6zh1P(#hx0TaeK{z8y&FKjo3kEhe;iDcE zfcF9NrmRd+z#75I#zyOzI${$C4z8egkGJ98@%p80)mt99&dA=tEGF*_>L9oaR=CWYsR-P*G_o6S+z$z#(P~a{(6#ymX0~h z+zw|!lNvkPaUB%ja-FB?(Fv**Bgd~HFZW*OO%_;My4Q{$zEnTq*A43HRN?uNFg=hl z(mS>Jp)!boM~Ci|rMz6Z8QFl};xW z+VC;%K?kAOOY{Zm7ozQ4hK7!RFs`B9d6c9mQ-&9ZPv@IOdauhoi;5;SiiX_ zWHK;M)?aq=IP-A2oqKccL$m)pH~*+mz|;ySZZ3~)-BsluH|nc;xl+!#{ao9QcRBNG&Y@@wdtJbh8!GYyZ)Aw zzW!rQ{z;Ot{z+k{O^#r%wLyJLxwd z^XJOJx5eNf7|~5`*>4^z8HR_EXsbFq6_{Qh=&*U_cl%k zwM=iU2Q-PXbe70@^dA>Q@*j7JJAQ6|4-hly6bGu#Guf4I3#=NJmMq+jRMnDLMGTM8 z6FZqoQTr`j5OI0-s_>JgLyrB~1ISJSSW>S5iIM8Fd`kT8G)kmiG74kB5_qw%knBSo z@oyzBOWuPdb_$`9K7a)3Pq%~9W`D>*IUiM@0O!f@)4ww;cr6QD5gESP1B%!6;MicH!*-Y@P77+wB?U{(vm~ z0JN-bp*I7tds}$B|2Yv_ml9GUw621L=mG8zKA?tYOyL8Y$OA*gF20al| zE!BG;U}OpgXwsPQkfX7WgsEmUAWlI(Q%5G%c5JA@ zvU7cnaQC>*j%_XCf?T?a7#|JPH|92fQQw$ue`M)hN67HnNs*fMopiZ@%w_PtA1jc&hb32b{w#B}vxOro)&kk4QYrL#`LlzCOWDbu%nMm`flvZfG|KV$j$ z-FNRE&whE;GvWRhXt!eH;b*Q&eRI=I-{8}UJ`2g|xFh(1d6<`@`9woMA|kP%%i+S5 zK1F0WhSZW`Qt4EZc`V(MZsAXaeCedS(Vb5ELclEaS@QrmjTB5H)0hpPEE5EQNlSt? z21ITlh|EwEWF@giEs@COAQx(+_op}^iJXqHgKDa5asPlpLpVlbgj@6s?#6S zYL9`li=n^zx)AA&B=wJxE3xcTD*N=wh_LiAeKO-y5#$mc`A=Xw@xj(!AZfrCg?F2! z%%%|*5?(3e55O%Be>hdJWqz|Y>@NYc35+My#uxNsQ%rG0cZ281FRKs`l-S?BR7$Qh z-dVrO@Xl=E(CcZ!zjWz~bC~pbD^8Y^*o%J<{*O3DPI*%37d~UUCSH7g{XNT97LQ$? zYDwS3-Mc~fzXjb-ryofsKuafo;|MWb{O%5q#oGdD3s3+{Gu!C$mzxRqo(e`nj_uaPooI_7+V3f_n$&KXNEvegYzVOAmOI2;f z%Txl_vJgS~zx%NlOt`B5A1jvKoKv>6a#W5%cB9YQE}Ng#F-&RRe*ZmNFS`A= zffzY&T}2~NcH;d+T}$M2l)?WJg&c4iEkTi+0V>Z^9RNlas=*@uckms`6J|+}MwkVl zE*N-dTsD!&Rw6C9;`uACcs{*j*L;_2erJQvcU_02%bc~Ubv}FK!A+YVd~oxo2X_nq zIxLJ(Kec`BV~&r=1*4{GtdwIw_4r|;;(YY{D^5OnWS2C@x2K~s>682AHEryBn;yjZ z4?M8>3E?~8cUvB~Zsk;R?@dJv+4DFYRsX`H578avc%LRj22up7SnVaEaV$dP+@Mb2 zq4CIrhOkSI?M#gOW_%ee~$=YyOXUUtta- z@3Q5iMlTbdyK_ZVk=cxE)U2`ldFI@H5%zHXu&HYiR*LHY$S&l*@|^Pwk?pbS!QI|E{fuLT9l>Vn41g5I@&W>ri?f&GFo z2Mvui(Ha1iNH}VO&gaA?EjuED!@2g}wMSvNZckt@^ zbBcT{_aqY7%7ddWm!=M@i%rJXYvdmtmEHZ<%5=2wE#Ya?`{vOxdvUPHUc~Hq)u^&+ zVxd}piz@JUQn_L0+rqRxfv#aS1_Qa)SFTn?$r9m8tB0)&yDHj4Q)OzVO1NO^@T(S# zL(0QB&KiTUe&dAnr^5A~AR?Oh+sP8L@Ls*u%05spT>iM4%=WoC#%#@Vlnc)Y*M>(1 z%>k=bX=I0!#ZUiZtZ{s3P3^i(18oF$Y@`P&pb7q@ zvO&%Rinll&IO>Nvk;2BP83HY%nxOt@^RQ6}1388?OVhV+Wsgs0?25ERVP|+&EE0^` z9;D*zmtfJOHEx^cUSPX*CM%hFt8IaM+BUL@o;Mw^gE?}ONuG9OHsL}9goCExOl6k9 zcBF9hZPPbzo-Rz=Cbo417-4=XMb6q`w5^}k)dn8)rye-Nvy7(}Gh*3HgK@Lu%)3+n z3oI%!*v)_P(IJ#lCcqSZfges}9(VST_vZX!8Iyu_9WRljFOkeF&%DGjD#;zAuOeiL z)kL;tDxm*yaTD@D7Ic(j;`>P;SyBFLyqBneU^?`pM<(c}IK9OD2nZ!U*T9lL1{g;P zQHC5spChCsLWwhCBD+2mm(S2;iqgWTOcCcZWEYknl3hS(8+Jq-!Js3u!vGXFx%%`X z1GZyXL7}pT{gaax|rmpxnPf6C{R0 zTib|2S=j5#k%yaW)!9?dat0A=*X;8^v`SQ&KeDAp3DgrAcLuh@xA;PZBR zg`=d<4p03_tdo51mGomi;T*5W zBR30JjLniAk}JV|c8{b_@+!PN3ED$3pu<0a5gVJRMq0Nr)(md5j3YKqt%Cs={mM&V zt(QUujwTQ>MqnxgM4FbD0^omUM`j%X;ov|kMM@GAVteUvCTv*~XK!V8i8e-rGO=_w zoddypK}UkYEyU(oO|oKfA7hGR%Au_RIi%5mMX8P!NNn^DF#hO?MyUXe5YZ^CBuAyz zAaoLmQ4tEOMf%#4pPP{;jWHM)?Ifp@kt=LAg`7AKI~*z{W3ezw)pVPUQEMy~jk*Wh zTB*WpR!FsEi}0SsqLk?wqmj|el+#Tnl^ko>maAr>%xuC2=oZxEl4o@~9aI9XR%h1D z(rWcqJyENP-l}^|YjhfkRH_Dq0Csag*5}@Ne*Zr;M)&xhr-|1PuRQ|g&-ss8aV zHQ)cOM)PgI#`o!W$Vm6yr&5JrWzH40eATw{n%~Tk@(&l_f~OwphL< zCqVa}HZY$G%oj?XR`mrDRG?uJ%%7|Dde!ITbG2SC$p5Y}8a2z$XEq>ISjNkZ>1)ov zgE4B@ZHNjMe(1B_iMB^&AdI3IXEcx*Chj7 zB70ZAgoM~V!p$$OCVPKo`w;0RGhZ4!{v}p2VcgvrJjUJQ`tKgHL2`y{a5*?8l{pSS zVw`E_9ZV7@{DRZbcUGeBT!b+Rqb4RXao8LXXKXTqpXO606l_ghxNxwE%@d7RW#3 z3UEXjf7lI6*9ic+0Pae`^tPR>QL2SMsL3oEYnGOP$E&ou>S`~7xQVo(=)(GU4qQK3 zr?C@W$tk9f*D9E@M03cl(WrbDVpAIxG#Fl;5L{*BOWVj61YAL>qYM>lvf-j@87tpW z>ZJvtU!o^7M2?;aC>6H~*pz?_@A_f43oiSGu}SQ@oNif|jUiqc=UP!8 z=>_F32*pk3PFPZ*vcpA%CN-p;Wxmn4U-oTG7E0BO+K-oF$b+b15-I&yI4^>TevPA| z*`O%f1ySQ{Y5ZqvdO^$W`%*F%#Lt9hQ~Pdj5nk<{#WM`}1&EZna`}}EkJxL5;b(RK zf@)(^i_(k8hi0cS63J zs|Oki5QJx-ntFo~>>H%pY^E}xqM$b5MkoYvA@~kW?9WyLsNftU=J84%FU=uI1-qz& z1e^PwZW2CepU0^YenL2@YGH@)Zu1jQ{eo)vbm78VWF|Q$<=}w5W#K|%AkIaL_Q^~f zi|eTOp-#ROKBVnH#1e_)P3HY8s08{;dZ}0gP%Po!hLQr;BV~334uMWAl-Bd--#Lr4 zPP?Qdr)gAseNmTiQDw`*c6`PC1Bk z|3&YFAt(-S5J%N3gxme>D{!fPNgp+SjP6|uarzfLH$e)iK6*+D$1m-L*m8QjAGFH^ z!4#H29_}tYGe9>0-gpLnEkFNVf|O((Fhz0>mN{pkLJV{|+nAL!+nm@Nc5q(1;$0 zM^XlI4futW(0Z&+Dmx`;z%>=+F$`--08{c%b07caoO2rfcx&P4E_cI%*(-V`x`@j; zY3;gE`&aF}^~k{oo~)8NnyMR&zN(UV^8aqFW1e}|cCqmFEzbNRLwxxa?}InfKOla<+Aw3N@!C?SkfJo8^8o_ zI-fw6;_#rs8M>Q+4?{*lf6ip$gGD1_2)F*3nIb$OJoLNYv87o1MtGo;=rMVHc^Mg* zzJq)5cfvzNlfHv34fMZg$+Pso7znVXSU~|SIp>ji?}fH(>3^H-I{4m&4?q0ywD-t7 z&`*A`g)pImWS4M#Zu;G9Tl!s%h6&iR8RREo0+8h2rQ~oF4^Cf%UjrF-Vx~<}RSZ*I zE(2MIVn4)+wu!iV_&KCBJ7WozHtAvFJ})oAL?hICnfWHzmC33lUvkOkcX2xQWGg~> z@BaL}sp{L$pV2vjL?679*l!~z{`9L2m(0`GtD8C#ot^Q#F%1oEW0p0nz3W%&ub4Tl zv7>Bsdu8sZhQ_w8CH3p>X8H^MuC2*;raREK{(9zN$DD5BT3H_a=?1Nud0!pn*^pUZupA z00^Tj5tSm3ES7<&%$QX!=9c9_0)sU3X6E^ShyF8t!uA7Cb=}?d)XA@&a=V}EW*W(c zOu_RclPZ>-{Zx1NQ$Vf%1X5Uw9d3Fmy}|)ud-_SSfJENUoGgFpK<0AjCt1h|evE%Z z;>VXe18_1@Fu#N{v}Dy$lYcahh+FBgOa3nO3B5w!-!FNJjDG1I;T;eXh*@fdciwr4 zjDCtq-A8v`@^_NF?=`aGOWz0iLhnbEgMcy@d_;QkKk$7ipcWA}i23ZFsLEMr>E*^m zNiljMCxS`D0CtQRk`;cwZFtH2PC&AwZk-Esg4y{wTFw0ENVACmqI*lPKgx2}QEvCVye^Z; z7cdw4Cy!~hT58(tTvkqTwpOE+DP#Ggikowbz?sCpE1Y-gkZ|y`3z*$+64-JWdFkBM z*Ij#OYe`h^Gw4gVEuZc6IEwvFsdR;*#pxI9Sj47n+C_64wj)Xcy{3t;pT-^ zp1g)@-ZnI(|2o#{s+>8q(rfAp^75*M!p%o28Vqk=(~!6B6Rq}RU(=z=?xM1(WkubU zhnjpJYqg*F8xK`aD#}}&S2U^mP@|C3P(crm1S=Pk9!@{A(q$bR3U-;imDb8&gx;j0 z;T429XfFCd_&s7}e*eKm7kxl#5W7Zh_&9LS%OJK_PssaKWeGE7bk2mF(NjBbZ8CnPRDNY_y0vqvSTwEU)@I|E zO68Zv=36_MNF$?~kh8xcr^0{F%jpBc+=KqI8uz?&m(F%qRQMx)?AV_(LB-(KX^Hq` zc*ZkN%k29pbUyV*rbJ(s3^CW0uoy3ptf1(|FpOf9QHdS+wI<@yAcjwBu(VmQ6c=8m z6b?EH45R20DOnSoM;S*<`PnH@ znU-mbX3h<@cXoy%caE$qshO~gkdgW$q6rpc|}mM zfW4fn2@zHg?ak<`h$MyQiiQ`Lv=lS5hhmgJXsl0?YsZi4E)8$=c$QBnnXh9F&2c*$ zo}1qk)E{n2YI&bMPp&&}lpO)v=eQDNTY=41B&;b>thIE#&z#?7w)+at2l>OB;qvN; zop}qqD&bJPd~C*5L)|+2Gh=x(#-YO)hiLs$8|GplsgTtp7@+wT*fLZpU7J+vUEW}w38eItqmZNf`rIh|C45G*4gvtuv2ThuDXc4 z_`F(~o4xr#n>-TrA-kYAe{7|2#8J7Z{f-(gd;Ga>&c1)lWrqs;pUj`koHIS(pOU_D z^8LS$#%g*dRg)QD^LVnOJea-VNlv(W8>d}4abi{VBvc^g{(<%>=A~8;kSobx+W^dd z&`(FbE}}m!n<$swWH;yBxQ58)FmSG&`4)_se1oQtH6u;oagR#y4*UV% z$RlzEQQ?Bxx~KCmCdnIwnIbM2*apCK_K0`0o;qZC^gB zrnD~peLitnc+7HIOQfYaR@=5i$KjSiQ`sTL}ZLR4Z5zHCAtN>{bMsjN!6PEI-ku9@ESMg(;v}J0-^JMuS7w0b5 znX@cD7-?=8W)2tRaCYfAMyrX35sT!5f6!STjzv9;6_lBvK768%HD@<*NHttQXnIdk z?y7^F`IN{L?uU%rCUVHqK1zo@akLs-EoXkZnBZUz#7i_Tpn#3a5+TYeLYd_#dc{U1 z(h#`k#S*5uBs;gUF*loal*U~7`L0;$=f#;4=AN=BEs2&1-}$2Zg%57C1^v#VI#-t> zJzRMAY0~-3eWdazv*eQV6Mxve+y^*iS4kA#R|fn- zu&3e;qG3vLMn`=l-=NG{P!dW@q#yXDaL&2329-vr{@Uo%C`>lC=j2i0{4mP|q$wR{ zgn!v%CnO%Y0uBjp+Bjf5$TTk4KkHU)cFe@~QB_pz^SCGfJ*?JQKf0@!=#AcW;GQ7N zoi;maX8SBB zw0v&=GnX)%`~NoZ44HYcOdJ!a{DCi*(Pc}iWH`|I(H=k{g-Q{v<}ma?m=r%QWf!J} z8H0%E83q-u1cZqn?7c^L{#>B=FH!3BvbI-O&wt|5F=H-$V*bp7Etk-A)B;d}v8Z?J zB4WCFFCq`qCkDZL$3!R|>lU7)++0^}S32aEDj4OA`8fRuuF~3gDH32)EFsOzy=Bgl zbuV3)$8@b(Z6hmq6?u zdXVtQzxf91Fn&M9rzk%aFfXVsQ6;NGq(q#$=}<**)WJ{ZWib+A-;a)nqTVnf6_5cn z4t)>}4PzEXog;w~#$Z1ki{Lk<(qh}xw}&MofCb9!BjRB5?P=tIsR5L1!lWmvIA=!w|rhUdd}Y5$nj z@Zd2XuQLzdk4WtBzY3^hY>D1*R4J-QL@7{T4h1Gs&|F;1!b2qrcn-4Ri{yl`y@Yd0 z*^pzgBXmX3x!4)Jdgi9aQKc`rW~P=gL~>^9sMO=stc>u zp1E|DPH z1|+>G%%}<4&@;lb7~m`>2842kdFnKRX;3oaB^xJ=tNn^$zN#HJY2(KGHZfn-jm65O zv2|Y|sE=$MDk`P#+f=niuhp-qLb%_?NizMK%8mDJtX!j)P1?vF8!9)6SVmEIG{8bp z2aE9}WF=dHrxwk=qJ>vZKCOv%Yh zo)At7f2FjnBAx2PwiC{psVaa#f^a&N&m&A4FlmWM^^S9%ZFIKlfmIcYLA zle~cwab?#R3c6H?C69~O?j5+5(Ku}I{&=DcPF1X14!C@Ld06RKKXaA|hyZ9WLm+u1 zYU9HRsSL0LRFN&gn`8*8j+(;EIWTVc&J}Lr|J??}oqO%vFY7Pd{Y6}OUwA+M#qNvh zzMOllm$Y2A^8D}4UwIj6VU8R*BHYKNenP=LIsAo_?BrvlN&QmChJE`sbiAY%o;Ws{ zJ^8}+nDF|rXml9KiJ>Kc>Yu7U7@IPDQ1zHiY1R;GVYn5!>kiY=A@hYZ6D5!jXKm9F zjgDUbX@8jR^5dZ3&mH;m`~C4Uo)bA9>NwaLyc_};espuXotf1sT)&St6D)?TGRdDT zPCw<2Figb7ochV#|KTi>N(;hPVQX42l#brCNgD1 zvWp5s5{;f&-4$_d+2V?%|A$k^r5fdYhRjiF3}qc7I;+Crs?HH`C`>$a*KxQcE=)hS z=pzx^E@g3}=pCRZL~ZT#1ON~Xut5lx&eUcc*{uON08|U3d`6q&Pp<)B?F42E1NRRy zJM%GAHH^}96C?Sr?6UqhDb*1YaDnW1aE>TLszQtvMYxNSj>v)_3QAO@Im7ql1+=foE6>vkVT=e zML-E2DW}+g0qxjgNR(UI1)Cq(jDO_2P2H0>Z=T$}>HXxWlfN2Uojavei`8=j+%dd!-BCV*E({dFq=jrOQYQES*I7_41O!tkCj<#5M2QaG8ryvdqK7=gu9TZr8csspKTHAy4i_ol!q6 z<&!|m64QwpObHr;Z$XeC@yn?D)x@T*VtiL!l|DIvw7dzSd8F_dSYno+%Z(I9k_YJj zv|M0aC;$HDo7~;~Dq$pkFC_j<8=icM@OSfRWQ@v%95YffhmKT`I%QJSENWZSf?);l z!poo|oEX;_!8Rr%>f(a^n0^QrUm-z17`_DZ-=T;mxdE-G&1&Sa35xRsy&xnq5mJN0 zK!wb!qvfZ98jkQ>%^p&%D|XmjyV>G3!aoc_lNykvoS^23*1T~x2U{uIUmA95?=I9L z*Jlw~^}!~T5!peeSTkrd+Vf# zRppW?oSGxi$X>^L&`5?#8hsNQ=(QGe0tSE&-C`W$&(dQ$TdnBh+>We?VZv27Gv#S`x zZY2OyBt_P2SMC;6st1M5LWQvTL6yp|2gJf0<7BwUm3uT-o3rxrvdkMw@MpJCqwJhC zsZ*&j?k0Nqf?0WWb$PpuYUTD_yS6LUDAXx#+PCi}1wHVwKmF-3dLTu?Q9A&nV6oSo z@k-UhPdpYrmPL~F=$s-#*jh4}6K)VM{Y!r-HzX`A;+Gyg=WM=6{lGoW=DZ`R5fm3e zUJ!qT%nyqa{2SQ%$wGES$NUcb69&&849DX!S%_!9&{1|m^t$s{#zpXjSU!ThAZ`em zpMkBPEKH+)mURqx;F(k6X~?W8PDi4?A>1LBv62%KdYqIl(To)^r+k4rkHRibtuKrp z+A+}kFuI9BP}DF9=o3}v!~q124L~~#QGm2Yp#;K80}BN8x{HW(2&G>btrLYno+H9@ z35Jh4PFn1&B4`XL_{g>k=KW^r+_+su5K}zr`hwB#F1xI|d$y4oOH{&}z~X<*=X;n5 zfz3sWma*%`tr432PLpt_&gu7BDvm9EuOiIYq6=p1X{ncj7rFYuMO!}UiUBs)BTs*) z1o`Z5JrSoV`*u2pM+f-Tl<-D7;B|slWs{gddl4xwg@uU$RM2QL(h>#HgZf$A;YVLG zl0$wIQT7Opo4-^W&Ft;P9i#4#aYx_(jN}G|+H66>&7adGyzLmnne=3yCCIN}dz^55 z%q53NnLa4o_=l&E4%Pk62f{t%3gK|tBrIdDXQSypVUnQ#)ZYSK&Dbq7n*`JDF?m)27D?iLX(kMOA%T@ zfiG0Ffqf_p6^<=Uz=~9Qb}N=Wa;dfq39?xAiLF(tr0^|+?3lV+4bD}=FZvDP!*|ZV zleuo#==FO+)Lay)iB4#-+S-?Fy@|QJIIp+>9J{11)nNVZ*TGkL-3_oO9~YaG97`l8 z*{J|YePRu82%1q-h4#rUt33k4Y)Nlow(4E0rq3O23t7Bbe$|x$vS#+eW=Ftc^%IBu z#`5&R9&0=M)JgGTyx2DFr|X7BOXMQjAPG%>5=Me~z-OXC8J2#zo#gSvuEokmLq13>Ks;moLJ;z3yyYjIm? zg0+BGvYJ>*qa~#P6T$wBIE>PGX-G8vh!q|}3>8NeL~*NpU@c$^L@~tDK^DVraY>x& z?bc$O#cGkc2@KvrDU$WVlNFHR@nrPQ)cb{S2>N5OmC_7h^vhB+a6Q4DaVe_5(lU!# zw4+1&r_Wz*i%LbWS3HQz&{u#fCNW?^PSAZ(dZ*GecfnPx^t#xIhor9}Uia*q{^*2( zor4b~3k1>VM86!(%Z+PMc6V6DU}B5XdIGL@P}a@}*xZcN_4A&%c+8lK56{0owQc&0 z+cr&|vU&5AsnfR3n7%D_{rtmp-xKq$XXeNZGSNw8Bf?kHe2W-ikXB#O|-cKR7uZ5(TT(GVQ1;IKD*BA^?N;j z@0}ix!ATR1xOEQ{YHbdiSq;J%Z=uHSbC@*_zsJ8-uF;r^io9-jp=FLI67~A6TB9W( zn-kh*Q+vJO4pAtKQNPEeH5!aIo6)4#n%(}Fki*jDi6SSb_5z#QlcAS z@#%&1i23tyME{#Ci!?+UvreNCDv`Mgsb5hG8a^*#cNk6fiCMnPiX-Hp+aBztPl4Oh zyHn6D*0IHn$3DB=tiNbPC^UlpZ*J0?V|6jJJs@Q`rA}qn+Rc8tYS7vYi29IOYhBsd zuG*5FF<(~HWYziASy7zd5#-z)PSo2q#2&G$?fT0GFSTxP_hrrNTFu!t*=E!SBi0Cg z2=SRH$2YzncHm7u96A(;d=Z&(Qi-??nsK-hIGvf`4q1jA~oib#XKO7tb8)6w1$r@c;e$bb_`&F~Ni2jzvZn2Fw$ zz~B)d_)khjggJGS~kwcJ`S$EEhn$FG)b)C?Be?Rg4{?f);@1;dk*(~!#;TB_6ue~koujG{(Beh zUbt{KVXkcLp4__g$fK)QtXTahxoGr)j=G9-8WhCenK&*7rYIphp6F!0FZDa$cKI}A zbC$PH6CR9|P9~in$MVcdqgHQm<%JWmV76W(Ra?!jyjZd}yEEKSQq&abG|$;JC;bSc zi%r_Ko|C*fHU5MMZZ-d!_K;<@%9@Wx|6OFrky`ijgBLxNotf;yC;P z19KdM9L-wjp>Ck8BG5)h!T0r&0%+sf$hTN2Lv zkjxKXirD2~To#O4g3+K1RK6xdDPT%wEeGp9$`BglwrgN{jB|EL-iaRh)`YmW(^uJ7uLBa*m(&$7XGI-Ke zN;nA09{>_C7UNiom=;}hVi~*+tXPQjh2p-!$Alh2G7T7~LDWZk#B@Y`_||eS0j5c8 z+}MXS8)x<*jNC9-9f5cm&Im-bpfa@rDJ#}aeD&mfrlGy%ww*gk?W`wa$f&eubjT!agn2CWzTsF$9FQLv-MyCyzdwe%0(XgSv}M>Fy@F$&>plh^`XnrC<3lF=|wT zxwE#mprEjD7ST?yA%cmit*xpe>+d> ze4^cc(iT%F0-o}GzhxHDd0~0Nw%;391a(%WY$gC>p7cuGwE}l#_6uJTU3%q&Du-Sv z1BNQ6(xHc+GOV2wta51Ju2zM;w9pK?-$vo<7hb5Tx!}@jjIK(9#}tXZhOa3(4AZCt zeR8mWs=yNvM86y>IS;5hz*qP;0}qHi0D~PqBaSeil!iUQlCV3>8lbEi7?siLw38X7Ay0^wp7>Q~U9X90Kmz9u zGh;-Yf!@kam`UQaU~ zKC^g{E;aY>7jX`w7r}f$FY=D2T_qmcXkvb7<8v^QFe+0lBwIdIEMQiJi?iI}QvaG9 zFIlAGEc-(x;`Yw!xJj5VRhrI|!-jRvUkNW&`eTdRs$1-4wL%XTJcV-aZoPtMmT%{l z$~8)|v|`{C&B}j2h3Jt^>K>w12|Y-kXd!bQUbiuM2zE$ z5%+bOo?z+mdio*1I#~xKh1Nl9@bD{9rvijuq<*AxPY@W|#D%3Lf z|LDW95-oJ%uc7PzKjz*$Fsdr;AD?r})J$)wlbIwl6Vlsc5+KPWKp=z?2qjWO?+|(s zVdyBJ6hQ>RtcW5iifb1!x@%WfU2)a5#9eiDS6yFsbs@=IzMtn#5`yBo@BZFDewoaj z+wVE&p7WfiejXa4W`Z0o=tf#%Y#8W@tEJz+IKR>U~HRPH7}){FA_g z2@RTRpp84qzJ|6Tbl~m%2s1O8`iyqZ5(?E!d*MNCf_fBIp0pN>Y$)^p^{g6c-qdT) z2G|`q!rdp`_EOQ1xd-;oeZW1skI7UsOBvE8XfB>qbJ|9n@GEyp#)N$*zuR$;iHTMl zMb6o*mJJixJe)xE3Q6_4>)`+&0VYGZT=+r_+-_y*&qQ=9TDu^?KY|vD9{9zI3DK(5 zME=Du$arMS#9PPZ2`ya}-Oqi0SJ|R6){pAu>P}GuxC!H>S(E&)JRvc zK(%pLIt!%_Ggh;J!P3mN(C&zQ%b!{2zgdp>O3i+p(=nue_40cDaryCg10&jdx17tO z(^oG`_H-m)1cDqwb`64b;Smyx)_@t0hzGhdMCC4<9`|!TD8jm$rK?L{m%e7ES5xX| zjVv*(Fl`#N^Ymjk_TQ;du2gC}db*#$3;ZWOD(u{Xf?=5$H@|z8nKTK#24ycWnW{7M zAKQD&^LZK7DvgHE{3S1zo_>f1NH&P+M;%Csfl8EPu7x`aIkw>Sb*g?XAd3zsX^HUS z;UC1y6~<^aDLl9k{x&4~;8i-HtfOnX;mQ^KYx5>mteILiZ%SkHXs&4RwL5E-R@LO( zM6u}hNxwS1`A=KMZudb^r4d&kLjbo*jB_XUZm7xw()$Npp75WZModdD;0bDHwr`R1 z_{sVCpn^HUU7WwBZ2nzSn$~Q2(Y)xssf8Q^yiQfaGpCL)?csqTYl$*OC+Z@HVq^XB zOye(GF$~=Qgsvvqt>JX}F)?~g{W!WMD}jH~8i`yrp|6CFShk_1l1@(nOjnF*SpCVK zPZ>c(Klp(l_zKcZz|T@YCZ0yA0EZ^D{lW`$b84Z^U^;j-tpQBvB00=t(w>;jRGNw zHbmPcyBkeUMyN*Dp&<=!4Z*9_kr2sB-A2w*DIcMAtDSr>qu8;Cw5OT*sv9K9fcGOK zSm!4y(a2K=dfsK5;!ihJii?WuI$xqIGc`8d;YdoW%gL@wbJ?B#*wjo{qOWdT^k9m- zk==Ptc1~SdlEaZs=lt{%`6zA(m=DT}5dFZ2(yka(5~#H%rX*T@>g=_aAidv5RVz4Y)D3sGFSTS2r^}yJIAKH`4lg%ntx|R z@g|#cj@ugfX#OhfWp`jJqBtUbHkZ4DSHKDHin0O4ELt|2GH9gHaP!L}3}X%RMu9^v zuS(%Jt&VKN;Q3N&Y~gBXg}t%bWVW+k1Gq)5L#s5@ZkEsLIw^XNABqBodZ8Z+V-=0W zNfK@`WLS{B9Hl>p2R#J6Cms(mA4-IIVD5qlOg);Cpn%vztqY4NIw=`LQ{iB&^7#Wa z7a&uV)>V||WdnY{zt5auLkdb=`8s!>hE*dQPt81kI ziO)fk1BII*_SGJx{lTuOLY^sHz={3|Pb?n%Yie4$M&R<(ilKI}PV{R%0}AWba;7QM zlhO+kSbd)<)y`7?fZ^f#8IR88g^8yYJUP*(>zlFUnxzNtoZYl6N1f{El@=@+k}>b# z?4Dj;?9= zS6nw@ob*rWHR+$@M%;ibXjl5MM&Dm&83`?45etEsp3Zfah6&wn{SbZWiSl#g2s8QF z!b4X)kx8BIv0a|9d#)&qO#jKn1JeLSU&g}PO{iQL9$?_n`%N@9{Doli;kV#$3Nk1^ z#U4_1qX>;tNcxH3ovQtK_!)Q;noSJxssaap?qI9Elad>s5bi2j#ytCs3 za>OCS+>#mBw~`ecHs)WC{zzU^cx+5Je#R3lToHj6;g(tCOO%@6wkpq&GX4R1 zbtJ>0R7-sa=3topyX?tUg83mJE@(3F#$*?KY=Y=`;PXg{F}hsA=r60uXOmHR?c0m~v#F!u!V#*&AI! zFCAz1AzPG%yv`L)O!?wt1!(?ra)UJ3BIHo!{9Yy?_5{>Guyf`FChX$Fc_I zzkl<0r)IOI1!D?xv z|1Xy@#d)U%ppGeWtaJ{l2B)wBCoHNdN?uM*O~xylSFjm1X(4SGMWdi;NKxSuf(5t$ z(yq)xWA3qIH}GW;dPcJn8YKu5f;{oiO;wizg-JCFwS~i3j<8^y&6ATjN8`%xe@W3ZTPIsDF&xo?<=iJvK1bU>vQqQpAR2|98e;? zywn>Lli7c4!^k9)D%NBa68o3AL)UnD;d+hQ!;L5&d5@<^J+vey>4Buo;w7UeC9Ww; z>UC`7uuab)c08w7zw+VUfg^7(8}2hqI@xh>QPckSg{{)#cJ`ZoB^^z5>Wnx}rQ)|t zm9Bv?Y4QiD9p9(jwKLujJIq}-HB>Ae=~c1k&Xe~rE;Db4B|o4OT`5J0Rv@-mt!atz zj@X>-1Cp1zVgT55j#C)|HMfmO@q}V#n`2Twx+XYdZTw(Y`5GfTH>Yk!#zc-pZW=AdnU&ctSGLmPRA#Yl%*st2 zE5@3|99PQ)1!p??$QLg?_qS8cq3YGk^9J=x+wtQaLmvIzOJ(X93s+Gg81?GDFTVN4 zi)CtqLG-vQfkdF``vU)J8+thXfiD0dYXo1A1iUiY;}P;M1b7IG9)w;9FLlWY2N_j$6R}D_C#tuFLyR zQg?8Y>?h+f4n;=rDT>*O1&SreUa?-W86MDk6bIlb(X6-=xcVo7u>QE>DaBdEvx-;o zHejCOiI7E?piCY_R(m?>8YV(eH+fkc1o9v@DE}J~P!EEwJy^lDDl0jm&=M6(WjI1} zhsug1OnxZaJWem}2`>S^DmBPMa~QOGSg}|L3CHQ+J#ajM_k+p-7#qsBCaS65;S<0J2iW7)(J59wVcB6%k{?6%EJ!OsS@Utz_$(y8; zY_=t%V?5*DFrIlzZ{ki!YtM2>w{6Pe9$-Sq>~eHS?^dvtrb=lv8>;ST64@AOhk#MC zHzd7!sHq55P!v@j9C-9X0WZ0+LTk2bC|f@z1F_*7DLz zruI=vvH$QnNO|>oNZOsqiluu5BhEgp6xpgOR(aQlPoGxv0hs4a`qNCWlU_c;dVlqi zTDma!WiF=mlT6^9KFbP?yQEJ)%wpTyIW&YF?FBzULCQyRsUJR;KJU0*`iv#~`OnpC z4l-gG(E_)Pgd|FRRmT4(%sYi_RPEM6;$3%-Z%5%{n>c_iJhrLhpPL>N-gq#SBPHg9 zDzo{9P0z5IZB?7kp52`GFuR8^%q3e+zbL)g1bTBFEEJU4yBB)6py1I-C^!=N&1nNd zCbKBK(G8K1;))gUZ+7rVPAR3Vw7t$6-x$fJPaG&+8+m@w#PTMtSUR>8IWwlE8>A1U z(8^i-@18xi?eGFN_%(Z7r8sxBlq5ZS&Db~Cl-F;l9Je^~taR<5acm>kyS*=)&e>K> zn6*kON8)>1LFFjt>#TO+!OahJ(gx)D`j_ncOO%}4G{JPx7gXF@3{UmqLN~)yN9>Bc zpC>`rSsX-oGVPMHLph6`su_njt$XR&Kiz!upPqdwyjDEi%D68N9r}`S(*JBYcVz9o z&$k{p(E9wnYv-(faNH~R-S=Ja_ctH>=)vYCYu{Y{=JESp5mvRUOUK`Q^Y~KX!uq*$ z+wUr^XJ)0&pP$0-5Nl^v=I{ zJj$bjzVt*|k!cGIjUTvd6KyVeA${ty&7gHGB<#Q1y14zTyV}$4`fA-A?XMQk9G1;8 zp5EWF&#>*jJebfrN6kWh2{r0A9OgK6uv*5?N2oX#x;mx`pR@Uo*GrC8yA6OX273VP`NcBT5$Qr0j?G(M{{P7piqRt*) zN=el73s(VL`SV{oUT6>g%o)xA9Yvu3PritOk*PmT7!2X&#aO|Vk=pG~2a{1WGXR_p zgE>l4UMm$H7b0r$wzikJ{oJv(mqs9+QS`6EILDZbuS@=&Z5%$wIA;~Ut2=)?DwiM7V8y|a2de7gte_wyolz2Y5-{hoV zNoufec(7NxJ*CD7ZahunGQ>M#l7ayb)Ka^pQ*2}^2^dYOPAi<uj~;F1rK7F4-`>hvE3z-Vn_W?n%^t`Kao>fq*aO)WY&#u0N+&ig zJ}Q*7oyn@G$P)Y0@>jpY5>F&PG#&KoJ^YRX^+K*%Ss=<$$y_-}L{UXErgc(E5-&jp znr?_BbPwuI#L%IiL?tQGQxhLhEFNIO&2PPbbo8M$OJ>hnvg%;{q2Ii5`}B85i|$0V z!QOX<^!@rRpKN0Z=T@CRx@XJQI$o|_piwYoJ1MS+k z4@{;Nph^J0Rz&vw*R{6pWnO9y>5qG@xbr22mF}0)L#gr~)}4H_qp>6$<~$925GmFS z&0^K?9>3KCfKji9ml=9*)MPGa_6R~d<|%laTO_^BzGM?4)z`l!wMngf1bd$Dc#b>y zn)D5~h>eq4r8agA3&T>^5wi5Qbc9S$4}>iqA?)E5ky+fW9UZ(72IOS8<1gH;@(K&j zloXa+bBDra6BOoL3kUoHL_@>&^ECv-8f4FE#sp1A{n>?AMziib z$qd)|3UYAtV1Drc0u&k(6_1!N+06DIJd)YHfVjlPDl1-ccwBwGrPxwmkM*Bj&`JO9 zczs)T=dI|h&|7Ak>vWhY=o3EevYFqaC&{Tq z)3qak!8J0(ysUS8nYK5}M38q_I^SDc7B9UZ{n3JhIN{&iL_m^m`s*5hGQUi*X#Er` z6bg?OrWdP`5fltDi&4H2EUat@&_IR9LpUa5W4Rg%4tUpe(;Ger9WZ1j`qB}QTf#b^ z3yJPJRD~)R&xINrsUgCROu=#5G1XI4iK;2pV}O@}KOO%07*Vf-`?EeR$EwxqVsv_~ zH78B)v;dStjN$1NIP~7JcXh{s)q6EbIU@q&-f?ixy=5Md=FW1>?>pa>4E#k(Gs<^oc+1PZ8N16fN=wp54FANlzWFAaH=&b{ zfQAnN$J&Hh3yED}MWOIH7)ogV@}!cEsZ;SyN(m5WYD~`QDI`rOS`C|IRmP8uznuy3 z6YU4j3nT_Wj2)#Thq^tT0U!@=r>Blx9f|3`@u^wA`q~sTeE7h|h2DfqiUHkf@F7ED zuYDvW)BRyvr)4E^ilw7Jav_Gs7aQ@|s+U+3X3)W3FWt2JrdKY!z4Sq+^g^o5V&0dV z1qHkqhFbheojd#ItY@|lQRzNyUi9L?d3B#|Oz?MU#uKs^g5D++Bss#_E~hJT&JrXc zz?^emMMC_0k@h`{lHJLW=t%Jn&Ha_?_9*|MfFDXLc--MM6MEpA;3i*GXw={t1haxc zP`O~@;Da)-23idkDiZUq^f)0+6fq@S=PW6PuYLV{sqOpMudQ0PYG8bpASTE6ZY)hl zG*aHwjnBOO%*LsCJTs=3HujEB7KN<%fvc8PNnxb6k3uS-^=bnQO7TWH*Hy)gvgG8l z85Q}%i&JB8E8I|<5bHDvy5v-s&E`r=ju8y8&IB#)g!{#$77yo#OK1lAl0AaH(6h4> z(VSQ$yN2aB^90#@%0m!-u!JJq(ht2_FagGX;(L(h1it7V^eiZib?`=sRIu_INiKC4V|*i)2yOAx9uOS);1I@Ox3+wfauYF3K4 zOuA;4)LOn_QC(VE-J%WUtrDkDYIq@X0)YDCI7@<^#YJY=;(>PkSyL*zZ_nWm%{ET# zC5_}x+2RxIQr_V`A6&?+38kflYBDbn563}g9u_;~*cxbq6e@C1CRBO&B}a9MFmZHg z>&!U}3RApc!IDO{B7B9g^xk`|r1yg^5$eF`>Vbc3h|%r%WXnmGaS946*%m{#AHL;7 z=?R!_dYl?{EfP$pnC0-+&-WUwd!@fx$VwEwO6D^=?VyBEslcEkgpa6}lN3z`4yHZX z0PJK?bdvJ0Fj_W+No&{9n%>9*>{puinPiN$s+-au%71qGl-(Z(C}l zy-X=>xb4;D(X;8Ib!?q{o3`-fx)3Rmbs0h!^KMx*b`G$h3KiVGf3^t&K3Le`N(YJq z`T??m-Xc>Hm9neQeEFW!XjHi*jq+ootM5tgo!)c20)egr?CPwRuUfLyNo8iMvLbTl z7wD>#prGjauD7x7YW3UykBu=V=6-d>2Mvl# zTMd@Tw#(HL(Xa4!u(TMqUOM{n)hmcjWIp^F%XAv5s*(Aoy|L%plHZjaTRM->L;jn( z(Yu2hvm0`_bA)sevFNaIg4T5+6&Jg&Yy|O_8v!qQUC|6pyf#nEG;`oi7ov(2?tsOx zW$u{H1LI1Mvb{(D%T}Up@bb~XA}v#AsS~tIo6y!hUe3Hpod>3stXub!RwUgIXogZk z%z6oQ`n9kwl4ZuhA>I2=`@QF9hzRu%%$g3QTQ>nzmM@SQ5=@t%DGc~QxEVaeP4Jqc zE{Alb9FSjsl+J($zLMM^QvCIE_uhN%b>{Eb2iB!!>8wMCW-XNs%-qH6SFXIC z3q3(Y{R#O1|M$bvH>XTjkfI*9XHkN54q(mprAzIAYmU6KiOt`%2|=Delpg<6>)oYM zq5=0I!8m-lQR)EeDAT#pyIcQs9D(S9f?ZOoh&EIM?{pHpqp#BEz&v%nL&nrW6Gbh|z9nE=Zz&d4Rf@@`|1|q{5LbefQW~ z(y@Na-`H2D*4*%?Z7cqGjog2Fym_fl%A@S)Jyb3{)5Cj6+>5ufz_Gs;=VK3ci$ultSBF&OH3*5JvSrRY&ov&|RRcDKAZ z(cw&Ty~QfLtM*D4J5(^?V^3o8Thg=GgEmxl+BF8F4JW{^@$+qnKJ#x0Zx>;LPPL%3 zDdoN=vwA^5&Z75q_c;@~T)1b`pb6d5zaIJc$>lpxad^4*pst56UgwNs`X^hT+WSqu4jr1Y{0Y7^+WF+oE2$aU?qR7TA!Y3_<4M?r;FMCY> z>^ypYr$&JXSqv) zJkOTO`5Ya&wv_O*k&sroHp^$Wtud4XmQ7u&@r=;Yy;MG736DQB|-Wj=&+b6p7iRe>0zW&L)D!&`j4@G&%F8+)rOvC}XxURy=?4n#mJfM>!i*&PxL}F-W zkK9IO;HJ||)yaiLUj5NCL14o|7!omTpTvmD-|p^AUS5hQg_f_|cA5JFKL-naH`m7n zI=RB=4=O-BzC3o)xxBqV0Xqb!Tu66N_d)rAQ6f+M;=QQ_1*y{N7hRv__Fq%6 zbo;TFUW#~VpBOGkZ9AD-z}0_ob4dyNou+y3yBady!b zsk!m-lN*MHO8omWr)7?;DG;?sk|%t|#pff(gj0?OGPsDT8jDC;_neTvuR;&>6WRxhYVu;z}Q4(tjcOss|yB*Dg8?( z$7qdB>%TlPefo(nCH$-!{@qcKb>@6!)v8ydFK_+LNon%-`Kw;x3K}$`)|2TElxOd4 znm1NGzMq5F+ilxb_8P59T@woAsifhZH^I;PSC4-=bhbE?ZX%tNzIxlhm1xPGGD9ey)#?$3zhFH_?bxWu38Tp`)Pc?nRWaOu>(v7H@ zlDf9o9vj%k|G|rRTJ#G<8O$^XX>W<(?povI(@G+4a&HDuP4}|f?kLjO$)v~`g&X*S zz!hZRIEaPq;YHFl4|uw~M=0fi$Bt7-bx&?hoe~UINb3*u)8{@Rbbc6V9X8E&&~9{n*uB*L8l|I+P0y*hf| zNK4U>ZwhW$9hk9v`s9A;<}&=58;4Mm8R~;!)xYHW6)Fhbu&aL56A>mLqh-iT)S*Hi zVh9wVw0xuvlQ9-lBDsDgKH@D7cZu={LF`@K&_guDLmGUhP(n_=q-cY(TUG*b23?^S5*O33rKQWp`|kc5{)N;`2O~X&znq+_Ev|3VnupxP#M8lT)F{tXa(Ls#n=<(4Vni86uEij zxr*|XIyD@2Vjt;y08EWu4f$gMAVxChP$i+o2Wl3vT ze{-rKhD#EJ@$K`FxbsVGu2WcMOEg|m@UuFOGA&o#{-?NP{RjMKe8)2bxiy?IQ7L@~ zEfdOxcE*?_JT62j^u$+(_uY>$)saQ&N+fmRWYqgDRx#?5Qhg_K4@cvaa~1tzS?^#< zW`Xyt7j(Wa8^}hmNx-38$$rhAWADKLBXMvj6bUJf)Gkm>Ad7i46SLo^49e>yI{B2* zb1>K990uf+PH-K6bk+q9Dnu<+IR{;@1H7{%dPl))ptQ$`M*zGUTr;9ez`u}u>kM>G zdt?g*8%I+e)b4ngzX&&rURUgJB1?hOLAO9)H9pXprr|v~f`#QgMR(BzNda6c;P(@r z03L%p=H<{f(h)kKOoh=j`b@ino(y9E)c&-jn&BEcOpjEmQv41l;wO9}o`;I#a@++C zlTUGFbVU%HM*z_j)J`r69t!#tAQWWU3>5J`RR9)gdB0CAhvqY&gwCAycq!YK3^4~= zgvuc}i__2?MdiRTvCB_ZqTYCjI#r4M&?vJKP&BlM1bzo!Ovr*hl!mHR9HfHCSApxH z_%)>}6=iY?K;_1Ud`+soz)RIq6(jc}KB$j;D-mGp)GFlBi{i77)ILjGfMX*QP^lu7 z&l(5Uruqbjqf|dOC42C;y!70*CHgVZ)g10+)+;q3rPx=LC^ij82I1Ce|5%%_=(-gn zxbM_f6&oKe&TDW)Mnrz=9GeeJT~4&Bm2rjyl}4ACISiqiVXrP|R(u;|{6mGadqmF3^XjRN+iBC;*8a(j{I;}cU z@07mRjC2VJi8lAJ)Hr=VmtN#c3XOwZh76tEVRBtO>l&%?SQ8V{lltr9QoY8)prCou z(8rpVof99&zo$0yyxyFi#bTw_FYdbQi@S>F%w;NV(uQP>AWGk<0n_p}Cn%M=l&#W1 zQ?F8^1u*a8faiGcX6C%>K4w4c0nm)O${1f#2u;08%PBRg8040<3Uf<^7?%ksjlYiN zigUAK)MicZBsK!MG5oz&H;Abliwno-ox*RPpL%?X(#a)jVzRVWpmSMAb2e^;|)N>Gz+l?B(pIZGYpz!&J^?7uV3IA#fDWGz5!-lJEpLB;|`NorHQjTszjmC z-ebKXp;DtqKHLSOI69@rx=>|QXD6fq?ta z-5z8G>m>ry0eLfV$5^$`?5;@f6{yy5`LRZHqQn?YqRFDyXcJv_HU9u$kEVOCO|l9r zGPd;AyA6iW43kmImagUdZ_S_Xj!Uu#)}(89BpZ5f$xs?i(<{xDYZnP<%WLNGe%~&u zMWwcF>dSGPjxSq&{P^-^k`Em*VFd=2jvv(TNui+u&2AetQZ#Ze^;sFGR$5FqCvh8{ z`du#s^Pjs_ZwGu6VGOC*xC{(QwLV`|1K0^SVH%s+ssr4bxwJx~&e7|W($FlC%?8uJ z6}p(fyy8F|$MyZ7qGWMd(e^1woB-f1t5c`f)%Qzz-EQBPpX%Uwdt%=(%Pp?*dDze) z=s&SGi-0^1XD9X9Sv)Tgqgz>RGUTK9NQ_N9Lq83GlELp9$zvM%ysz-gU@o*P>@ot8 zBvrYXgP*h~k1U+C^6S?vCHzG9{bO7&w3J&?jaj zO`h0T?TZV?l6?;3_||BI3Sl44qHHcOwkQ$U=jhB-M2LSD|0j}cLI< z(l?ECuyNw1O%tPQd(WNgxDj3x#L3bUEsH+V89N2YUfIe7UX1~7qNg`14158Zng(zOWHZZB`0%GAORjEQ%lLEDZf_T|T3sl8!I;#U` zLC?`F!N%B3r}6U1%@mY$MVS)1%M?`#QxHb|q%`cV#bNea923nMVrzz3v?}Ns3Lcz1d|VaGZ6{zYv(1C0 z+pqM%ZPX1Mi9n&bNM3gq;|L#;TA-r{g+kJ|O$amzg;)r_FfI5sH8n9)NDQ}1jp0aZ zYk2S8a4Y8yvu1fU+MIZv9M{m5?SZ7OAgFjHo=>Bx?N1NlS0B$s*YYK&MZ+^&$qq(y;2J`Akhi`c2ew>|nRVJ|Sf!+aP6 z1uA_3C6dCF3pjd}fa9HiZMXut9k>Xpb%|a}7jksHyp5k|E3{*c{y2Oi_|PAG zh`OFh4RBc&G$TqC@@WrJis+;irPD*bRt2ROlCzhji^!QyY1+f=I%C1(1tSq(+8Eti zlHSo+GH4`rLZ(DJcgdJa%=4rhKoU48cD#7g_!Jcr?WTl_Jqf3{>OxY?6EV_v%-xQT zUBX^UPkbEd+B+0ok7kMsTAXo&M~7hU^b)=q#~N`GGPzUHO7LiUnVon@I@HOJ-Z=_6 zDirXC>;@!6f{D&`N1+2C+EK9_`LL3i+Z(_!_!&XEfd~XsfPsT%7pdMLl?I|2w}EMg zTKqJ4TXlP~Q?0%AR;}8pcRBf(9XpU=*4aMi(;@xluMTYQmB9vauS}aUf6bctGp6Ou zPE1_?*wn17sgJFn!PktbDh-XS0y`;{vcC6PhqjmsMA(v`xE#REiM-7hCt#Y66{;ft@pA0iz} zSjM^~tb=&Orj}C=FhH${=v%+Jm=XiYNEry&a0^Th zBfXyf>(lt}6&c)%y(v8>eTO@|xAJyoIC4Z9vg7-^8t;(adGcQAk0)o`^A)eWqB?S) zQ*`rc;4Q@;&B8y9Oe4?x%k#91=@+#jfR9jyt@?H-ORah#q_>7ARkh39fB@D3W3KC1 zv&<;a&PF<|bGI<`^2w7}d9$oZp~+O} zUY+{il&BYt2mU@3DjYROmt#gF2W44BEOhDDq81nEf`JhYWw1aXHH381y+hdo+Nrn* zGQlg@BZi7}u929YwicQ7X-uy$NOoFff3r_rJJrtqMjMfes@&YFTw(Xb8~1JAcjLtB zCDUgMmLV2l_Vgvy?TV}I6+)DKArj)lxMkb-GKVQIL>(R~uayoQSSqiWaPQozjwvmWi`5;Z$A2@%HvTz`RJQFbywZnQ^%PNos)tAUBF@Ka(SRW84X)B!CJ#z22<*6 zFILV6JQ&l^M}Q6(c)JH(8`__uVljNax%qswO+r-n#_nxVZllNzLw7H&?od=O-96Om zbXsXk=-Lv)$T_oU?p$e+)PA|jkP`P`MC@VW<$aO9N$Vf_Zu92v9$KHI@}zrIS8hh> zCproGM>Y@@;Nkzjs$nMc*boqi&}q(}iu(OxwOTtA8vYwi|HV6pd_H97;{N}6O{&Vv z+WKw$`|0(`$?H%5eIwCdqWzc4PO((~o43=5~p6-pOh*OVS)S?o$2~{+?jdTqg(ywmH0_V zD%`WDkb2Y=@4*P`b`9v^k4Q=o4#_!czsI0fAd?iXC@_o9#e0#hy+pL-V29`mXdqPPkfAXtkqjNQ(vnVrWf-TBTXy%VpThV+J86Ln zRRp#Xoy1s_v=%@m47R+Ohj8Q$<>ge#i&R$ZM_w6-#oGB=d2fN=puxe)0#QAxvb3tt z?34ue^qu+z%BH$Vc+`C9wIREv=|ts@$wfJXgfPG%Cg$}+WMsYTKKgCVO_kpDSCH5n z*DH-ZoYw0H+U>qBy;99p<%HK14i#CrAf-58b<^}83QMISvAK0k%SW;FnwhQBcCpDD z?E`46QTr&Aji3|xKw?*rVpx`w@f!#AEj1H04z&!L1u};mB|_q9*O}dIf%q}x+2Err znV;|_NIW5zU}}w{6RO-*6RHmRLV;Rx#SL)}rWC7&h}cK_-4AbHnrwAW+coDF^$^2# zBO-Nu7op@XQJ@X$hVgiuNT$^GE*c)VO9#;?@nOf$#J9K zcAdcO&UtQNnXqe`S-EqLWJu4H<`178%;gmQ$ILyD!XBEoODLoI%RG#1>xFj%ydpNI*<~C9GFl(tM$4k0N>uX1e^R$82$DfY?lLM-#^|M8<&5`68_?lI zW}+zONRW(_aFD}MYD}OJQ}BB<$_SQq*+!ufh5XaUDxBptqSQY3z=64ovj&epFgGWg zTZWn7!2B`N{S$6Fe9V^`4k@*!YL~GJViIz;0siMG!tc|X;FCr^q9f8_xFK39z z5-I2WGH22Jku|J7vluFZ*S4ooyO$OX$ni<9gm>i!MAz~GJ}qp4=EO~Pa}SvReqe57 zdczL;XeamLz`=%~C#On#NLyEMNr9EkdUd?r>nI3mnhinTd_i3sNUt)y6hfHK+!rb` zXLcy8qjdwaxZ47?>pc0=yE*06Id8mCouwWT$QWb>#q8{RvOJh3vil}EG_c8|{0VqtyR!Zfb$ zil#aV30s_eQu;?G-UNINjDl>lDw0u-0?ouQGHIr^Rfa<9+R@KVF55$ zL9={*3VN0oWRD^8lK`fee&v8#z7vuJ@%hSBp1jjjG5tlyuC>Q18Vqs$7|RH0l1ZNm zcn$F|c17tRF2fKn^08NkuC~t5i_27NCz>~nt>0*?pJm%vf6W%dgjK3*wLwQ-N`Bm& z1EmF$*nf1suS|32`aPO5UtWmc96wD{?#r#>m#GBxbaj!3do&}3wU^WuVW_?y8pI2s zTz{EnS^NRM;*w%=E!$ICnC)O6Cb%YU*N&b)YlL(syKls-rDL@>OpHyH6sk;-CEeXEy{d`^M~UA#LiWpps$zpKvy!{UCw86PWiw7no zP1=|^!8E%nQV=DC`{xYobKtLT=B9rU^MRz0!mkt$p_Ww?B37WOaq4@$`j(`Z(L4|u z7aU$2XykeahldZ(`+yr@AFJ9n>AhtOq}`zrQ8GB^mQ*fv?g2RGft&C8cD51mja~(1 zv7Mp-OGapv@?00KVgP|-Q5U9UB8o&0sS$u?X_TP|8;v#u+1bLLF4)iOV(`qOG z_+Z!c5$&Z+J^^45xIOwhq5%T9hKM7@C1MbZ>b|+VoTKeK8Y0u@9{9WYz}&h`iDnS0 z1p9#HPkMre!2^Q@b)ZdE4>-K`c(s1Bwkij^n>C^KO7(@AnH4X9D%FNwGE}8QZ=0Ak zKsVaD%RDF}FhZSG{l*(P)#W+TyZN4VwE=#$v*Ot4NfV^|$IL$frkh)qoiq2q_`z9= zi4aTeVofm3b?k6OJ{xI^&#BsGGG$s4rH^Pm&BYomHehAXa>Pbf3|N%&CFdmlC=^Bp zZ+30l--!od%UJJtpe*)(UenI&eMUaJ{~-y3b3542idFMO!6?b2KL*5!Ij$J_G7Sr+|rgT<=t zsL<=Q<``~>G#0^__eLIyF>AF3{@EC_HF6;~L6xdO(3hF2gbH=ySZWa2+&dbFKp^3e zwTe+xxh{U56e!Uk5YTuaB}C^z2aFt77)hW|=r)j$!9=k1^^Cgqj;cXLuOmT+^`K4t z++l9Xd(sZG!DMC& zq&w(71cMWseA~_!yk3%~qR#;naQ4Kj;5Z<%w`pUifwy#_ugmdESS=N;VdElD$UO9S3EG< z^u$wyF14y!M7QiyqR!sd&7JEVJjVu68>}5{r%k;7QkgHVkQADXZ z8=k=_bYU2mRIwLu>Hpw%&){~rumKQyKkbyHtNsA`x-_(n6?TPamdyb`avHBdMaWsO zt54Qu4p-qWPhP7B zf;c!c(gu=82Sjrs^=VKnkxz(6PJYhqfFn&1ZtFo|V{lk7IIP3JxOp-Dg$;}AhA&y% z+%e$T(q+f){QQ`(@z}DZ$FR}yvGhOBT=(|cwQpbd41cdAAGJjgY=W z7F48EVCw|7KC4`_@Q`%j@Rl#?a!2Y$yX(H(a#*@>XrZP&i!IpCZu?U!yMarHK0e6N z(~Bq3GZ!yrav56W2OndfA3OH>F)5v`W5%`T+s>~Qbc+^_KlJwUrEeab1kY#e#%sW1 z1)*?#;Vn+n&4y`=>8%LZ6ul2fRa=XEk^i@E2CN;a!ad zLb7BsK+ZYv2%?eA~Kv}WS~~$IVP{89HcxWKO`4m{y;*=fr#%bZI^yvS|Imm zr2~&|+VuD)mZcZ;>Dm6JFV!%e%N3J6Cb{2B()Y<@u$s(tgI-N9 zYAPLnm)GYB<)v}Ukzx7_?)1Z%r`X|56DMriG+|=o?u6{LUY@ub`ylx)dY7v|{EuBO zy=x5J&t4Pf>6Mn9U~?HP@q!^W-hrIw@fL$io(saV-c6`NQhcNa(eFK6<(5t8fviTe2ViJK=*+{_BKX?>ElzO@@yBqSvF zNz*#g`_dQso>?*!OO31{6cAu<(q3FiE&KoQp620ZwB10gn54_f5&eGl37agIM_uR9RZ^068 zmiYOw@^LW?KR)u|lLbf_jS&FekOCpqT;|9%GQOuQbSsl8$8G;idiH?_rDs3iJ|VBZkLUMlL=mwS2y9+vhCwAg2mVXn)s30E_tpJkl$y z*fSu%FhyERIvs|x90U!RMSV_0WD!gih+;(WMJf=%Jaz-H^c2Xf2DK-8TR^l&9k}3@ za?<-kgq;!0Yef+X4#trn3C^E&f>#~#I zcUa#^@*U$?-+p$_eD}hN*#47Q==?rw`4Z20{bwrngkfNxc=j4&JIW*9d1i5sSO+*FW&%vPA*H>)gG#i^0hLJ*21Q<1YGUj9u$uxPlPzLa=~j;p(&6w0j|L+ zS^q(P!zq4BFh?|wXqPN68A-trBv@WZOt~0*LGpUX%neqUQlCHr0C5Y_z0Fa9fobB% z!=ooNa|I*AKjMjt_oWnoH<+YZzIDfBUOJ{)wRz_x?uOZXVw|AwGx)7Q(WgKmaY(sufE+i9hOTeI~Wzvk|}?8NQ&OYpx(+-~s6w>BC6< z76Z3v6RTLE#1*I8Xj~zV5_+VUWov?40ZdQ`)3ig zD>3e{*bD1=6;7)0mX&HCJ~?{D_r2%3!Ka(|&r8Tu_sbqTJ;Au=dIpjraHH>dSNigj zf@NRW#740JEOVmt7Xxn|v4qS1U0*eLL?(_%RXOvtPxs3lS_1FKLO&<;PUBP-y_%mq zLRXfVTr)E;{?$`HU;V(7Y}}%u(md(;^_LVM+&8V0#-aY0&r)I0R}c{s$Y&EKQGjz| zFc4@EU|0#>8?duTKq@c*n$yrK2BItHr(uKi#^;YecUbyrX6-eCa82z@W;^`c@zv7n z_aqq}kbe8=R^qWALW^|ox{6UHZ0e_fW>ZV+E3cF8L%B&lG2y*^3onlV>?GAh z6;vKl>Hz=(uK@)_A<5SwXz?m}ivrRK(C1|69|uod5tMf1oQo@D2Uq6FA=L|rV*7?a z-aPI80(N)FXVSS7Pu=tBU0-LLC%njPkN=|rsYT;lM#ZIvLbFHb)y}A%J8J&k)vpdH zy!gVDF-vb*^H|PQc7c0WeD|i^f8fTJra!*Haxu&~K& zd3Uj4$PD=Lq^=Jk;J18h({2%8Y6Ds~_sB6=z^7_BUrp?G6 zT%8{iUzO1R?6G4n4fFL1>0@-x+sQbsIx~uaN~w| zd9+gKA|&h41|$UX>Y>0*d5PJCqE~_#2Nb#j&t^)>Yal@%pFk=(qQm9f+!=92Mh841 zSWLm`=&O{olfYx_X7odvtfHF`HL0~aU!x5w1^AiMGf)EHb%IKE6_qZg`_Vx>e6@1% z-b2TZAG~?d;_{3bp{P(~mc)XYQ^T8g-?Sw>MX5E$*wZ9?RfRp#Y}9JXt3<8Q#97o; zRVJ53uT)i5T3iY2#hmOBb?B0DEpqtnIf zHLAHY!Z&Z(kYEAn({H@z&V$$Ml#9zlp^B!ay|cz7s?~{%A2(p_%&EmCB|(%};H_S6 zq+DWcS(Rwwj0TmqvdWZX5vwZAu7trW7S0(_H(^5E$k`rMg4vWftv{>hwl~f?w|Czg zCS5_Hn&*`_&6-g?ux?O;G_7CF)(0oQuxsbeKnjQS=W5Yucy7%YzsSdmLWT!Ev3+G(b#j%Fj>TBSu>f^ zpw__F0smj++=867(&hxO&!GQv`Y@|iXYj4uzI)T`@{)$@R_&ZtU{4vVwD&FQYmwg1 z8n^EB%;|Sbsf>#>R#(-GavA!}UQpRrsZ6q(f+PCnmycgQv6sdOggjw+{)1!E-!je1 zukU5hTC;C;s5Cr)iK5A3InI=)RK>7+lB)_bbh=jWP@7HX=rcB5nOA?)_)$A2*7Qo$ zaO*4G0nXta8BFNAV*bedf|`lLQzA#lGi!P#y-z zl9w(wls=@q58ZI?bE1^#wBlgX7XKVt@AV>*=n26tghev}h|K z49Acbsu>qTZYYI_ssb#nyBT=J<#h&UrmM7CxM&D##>LSSBX0?cmY>wwAlHA`)f=OXtB?`4oRisQZ4=|BwuRxG^w2{Z{!MGYh`{_h${bV>?josn9j zE%O13HdTA$f7dKrUr7PbWp}i_aX0z4k>3ABV~{Kz<$04j=?Dpb;8r?+FhzHU z-72GEc6M{Q9QHYionTo|*EUFRa|#+Hd(T-CE%&e%V`MQsn!8EJj~<3v{KOC(JGYlk zTS+PlJll(L@ke=%@=}~dR0Y*tAx}4P1V41{3Y zb3@UnR7HAX#~FtDqpEy}jiG8i15RE?NGR0)(x9MQ3GA`4H;@>?i%F*Q6un*M8VW`$=60JJjrr3({3V6f+6E?_ zXIK%zv(tMgdB_cUh$2^v;LFJ&wo?b(l~JYZ7aDC@IueOP0qa<er^N)+%bc*@!y_d=@)A1hV&Y`*M#|WlEr?!!7C(z4)c>-EE zpq9Zhrvcs%0%=!;NKYN`75gBWmy6Ja!2^<^UM_akntdtFmX5r6)5ft0u{j5?%`6>I z_8Ob^=9_E;Rk*tL1*t8+QZ&X2yojLM7*3UE?-lFP9eL!k$%uQTM~$PkXW<=RUElQT z;DW~SBP!~LDB9cdLiEuuqtzg9Xc{ra;Tr)D(_ z8f{rHH1A@gRZ519o0R9v4Ahw=+5h5r*Q^hr$K^pAYa45O%)_JW!dBpq#2?hMh1s_ zNS)-d1Kf}l;-q2RVAu!lE@1XRlIuK=%E9l9sZEZXH!m)^HfD0b9gq&V#`}VRPuER2}!z+-;9AM#K$N(^$dr~Cf#Vz za2h}+P~E4?x|v+~@r{7BhipAjgAC%wWFrj7Ir%bpVMBI`Q1V6Rmv&2a(w_6W!t!PHqx-(kdM)E)4Q#Px zP-b~U!`iXZL$g`dAA66kU)FZV*tHD}#*n6!@*Q>d?xtGqR)#);Cnba`p7RTDL z4Q1sG+(W%5$K@2jXmcy{0MJ0?lQJ~u#~R3rEIzM7x^I# zQlrkL(`qx)(=)VMZL%)2K%*(RKo1+c7JY+ElPhpPBBke;u550~+o(>)t6n8i#jmf8nW1XBHhB>5lJLC~XT4=89`r<8QxX zqo(%VG->F%p(XKvpA?60yrrwZ%D(kcH2MUE0zD1Ak!E1(kZ^knV785N)rA@bqOc%O zP!I=&sVE@{{0sZsTw|meq5(^x*bM>FMr&&o+{dHyl3e#>)E@J@7ph2zpCI6rl)!;} zbZJoGMHSW{k6`f>o*oHDoqQ^Sg`fw6_kl9+{lVYw+IM01=shnk-1Oy;KP;4Pf8|%w z`){vX_crtW>O5O4g}6tS!BGCqqg|HrN0IE}_;t7Y8@Ic&W3<^nELwHL?hAVtzPM-f z>iO5*)3WYu>3vWS+~OUsT566+u-JE**QM{jl$JF!1d)`aqi?&xr?lc75>`tm9zoE< z{APq=n1Sfb#C?%N6Zo-hk325iZrd06icOGWI__c90jj(4mX42>@#7+Kjgvd>V#B%h z9UpOM3VF^}hM^NAd+v4UC~`(}NOzE4kg^8SU36W<8;LqX;upt~5M_!Mid`J8y?hPsg=j2!n+uy7P56f~wevR;29`yHc6Wcp z7?p{+Jy{-iw$DD)WbUgnRVP?#tmy^Jq>2%{&!hX8T1}V#BPJFihc&5%`_^P?;+n9K zze*Ja{BAR*{=e$p13ZrE>KosCXJ&hocD1XnRa^D8+FcdfvYO>?%e`AxSrw~V#f@Tt zu?;rW*bdEw&|3&4)Iba*Ku9Pdv_L|PA%!HAkP5cO-|x(fY}t^!$@f0r^MC%fcIM8V z+veVL&pr3tQ@lQ(H{B5hU3cf}4x7V@V;L~v)I?6_*wq6t@dtRqF(&Zxdh`_-87jFo zg{9(bQc^a6km*oxBtb82j0+|3Gt$9d#X?J%2b?W%t;(wOlfeAIqtZ25;A4nbqKVe@ z8qq%asL^OLI8WZ5S?G*P@uv8q)`9n^>;UDX_ULuK%KXB_tZ0`vF~1;IzRt6IISK77 z-|gv)Eyz#wx}viZ3-c>|-7zgy^wCu`W4o?X0{{rKZ1(}3OoJ%xgbRfJ&Tt)B>$;bt~Ya)oH02^A> z?zHL{FI=YWUC4L_u%Zs96<+WowQSBTzrv!*aGs7Lwv$2y=zHr!2B#q>)@n^jG<&zc ze%{XG;hsiMezkXY7Y&E#ncsi?kFPxOhr2$1aeo!7dhU;Gm3R31ubRC%u~1x$o<2R= z8k`#4%yc`wIbK)1ExM;C+7=&Q70n)*)D%-t6q_iRE0U+rIPYg$_ijm?=dI57%-;XT z{{DGazWCW)*MH=B>?8TP-^D$-<^HQvZBbL>I~nhcugb8+Us*55zK~{%u8P0)+2_6; zKQ$`angE(21O97%3H)Kw^?{5e3Q?J>K!-R4#1|JrMzTtP{cS}&H-*?hL0I&l<9B)i z6o@xu<10Ov6^e?+7tRS`%uDbl8>L@f`0%!E4`2B4(2c2kKkj|(ycU=)HYFA;TE8$q z!RSrw$;uu&5M2;nyJlvhWBAIBoSaoVU)Z|&#fw(@lk>v)QC#ne4`vi5x*f|iGwWM( z&Hnlem(96g&CKF7mzmpEY}>YC<+g1 z-E18(f+jMBv@km*uT?$Ws`}>>XgO8h2Io!Cra!F>uk%$gXCXL2%;_N?C)hp_*NI3p zLO*9c^P;nL+SwtN{ng&RU&-&_%08v`D05%sR4GB}+=id{&fc$1=bESTv%dZrXyY0B zl{^}LttWv8RCRvzoLD`v1a|b__0`w<=ggRC@<{)xcgob>IE|eDZEy5ZXQ)H;UvvRJ zdjbx$K;{Ty_n9R3hq1t>(ZxW(1Ldb;KSs(Ir|$s|xUMuAwG~zi!?c^=p=Xxp=9N5eEhR^|KX^olF;(A#aC4bl_-Q$^6);{6eB9CdQM8S1*_Np2I_X^o_%P!ZYABl3X2mGHCDR>zQW zM&Suv;SA%DgXBtCBtD({cutV6nQ`n0z7>Datx)gle30qL!MpT$DK7KGg=;Q}xGrCL zhbpgr$I8oHkxSNCrWGK9?4#dNFioHy99v&Fd2%5?fZ)kv93s_6;?u<(n9`0*t40`| zB(GDt>P$EW@i}5Ty~yEd;=6Jidwh96CF)-;PiHsfms7YL@Sh4?@@vou0_@DgLsq&# zhhK2HffFY(<(4WC=bWG-{d9<+MByX3&V*<_x!eGAnboY! zVK$59QoQ{50z>REr`aUTlM(s=hgAsum~KePrdLx~Ny(-!FvJ~G-=7XqIVNI9;pqII z$6`h} zUU)nZq6Cr^WSIYowj~UDC{{Lwnfvzd-?yE;CcnZ0a`CA(tXe+0Mt6$8THSy5Gk<^P z?*8iW0Q+#?e&O={`%X5q*H{4mUmH89JGBO)3O_&wHUI?r!jI1{DLMbgtO5wHLJg~P zGaEJlV5LoKmoBp`3*P!%#3>-bN!W00}QqoFh(U5 z_I3)fCvSpLkO+H)?~@-H`}}!1@Vqe~6-Nv>$hb*}RUVB()kzcIXv>RX!ILKas?#Y8)jb>rWA^~=6v($U zWv7;bzCwQyw=J5D9yuaR>)f;J%XMt|KlfcEXDhZ1Mq5|NV~=fprP4LWRr$)+$KUT=ltlgu{Ty{aMm#cPR0)3*R$@YWTsR5O zIA6&3uq7mxJGM^9vKoEz&eva;clwN0t5JN%h%MXW@_N4KSGXKsT6H43YU$D{@tvxr ze8cFd?$owzGFd;+so|5iQjSx)d+x!UG@i&t8RFUl2M)N;WFt$Gv>s#A2-r`dRf$Bi z>AxOF>X6ofSS6jCQVeH>63_Bk5f4s)J_ddop~SgAl^4$0uxL_c;p{9-qi0y?N@4$dG>VPyZ;IP+7B1L zH0+AXb|$CfMJ`#pILf$q_uUtd_-ge+T1HGIX8whfFFttPFP~?DOJ@u`aOZFC{&3Uc z#a=jNOyaR{(}54sc%S$VvZg_HCpz$Th0GxOa8#?DCEGdhE2#WZ5~D0D1?v+*oGL@y z5~4St@wFK#p0gJL8!tbqFgW?1{-==hxP0QN{{E++Ft;7OwL)25*Re+~}0H_}6{CX*0oRXs#@+*Y&tIGCWw(8|;cD7%( z`BrA!|Gm`Zm6GqX`1)k_`wVMT-pgz#XJ2RMzOIw+u3x!l?^F9u>>b`S`DOn1hN7`w zU@^4~_>H@!av%5N}n6I9m zvS)bjSNp!dZ_o1HYhK1z(VlUf-X{s&m6#W&542T6n!zXlB-zx%Zsmv@<^mME79>ML zJ3cXrLWL~$buQ;TKC1C5o*G0`w)>7%&%^hp`% zPFq|?O75ft_f)HXp&{OU^dVM<;wBa=KYGqq1O1V8N|07y+)a?xn6F!hKB9F>;pTuu zgG6>AWXypxT=3$F|H{5PfuwtsIfqT6p!g_fblgBT7%}xo@&{5J>HaLZjs@h9%YqV%e4vbA=;aBYfUvbgnw@=pZFuUNz%ud1nDwW_*iEIp78 zsneHMX_ zOssGM6bn=xAm$numq;aA5H6YM&=B$gPUVSqYj_0A35IkspBaRNOlh)^@*l)_*+1`L z!t%(vaBx-6*t5)Kf5+~Ue^q9Vmj4#xvhjRVG@E003zJT~Ab(+ZyY0;SBD;<`5~t*q z`YYmL8HL&7%l&ydRY_6&al}`hiH{qPhcZr+qvu&HZRLV_`A)#~k&iZ*wwh>!m-}4xID_ zG^|!*hXR=*3CtZ5mh)o)CdLgc0m4fdEPG&&LCBw^P{FgO_mH~-?9zsr#KP#mvO2hc zvxrHAjG%kK*wcGJjUx&SASDKl6_f~UxKWN0g>ATjcg2IUFv4DDhIegjnoVz(j4U&g z86~scmKM9#o8d5-jErZ*FY~#vuc(+mH7P|el=%H6I9dNlEq>- zCKQOK&1)^5DOO{2RMC>MI;)}kUHOZ5ySHYo%3v(oXq_V50rfescC*N3;p{hNyS_($ z<_6j1L5esaFF)`iMXdS*)BRx;MfGCI`>FhUYz4v5ql z6V~H?*!H|}6V`n|7DZcb6R+jmIa+B5D*-w%hIi}vUr*BND`6?@Q1GX~hzUw=5E#tG_8d-|q?Y7r{^tJ9yvIzVGg7UAc>DpVJI{$37J zKpTy)c84=_2JI+igw)j%EJDmdjF=*-sZBi{Y5Ne1L-ndKJ{HihqBxqi+G{X96iGlL z|G{@8Be)RJB-ucc0UeJ}_x-rqMQFffI}}py(;M-K+BG>`$TJwnFg_$_(V_dU zLeDGQZ8H51d)NtVcac%BMhudDsp>4h$Wvc*%4@ zB_<3{JjklBxfQ`oWI|$avv5WXcfRUy;5Gb@BO}I239C$V8ZsbNLdEKfQiTN%)(V`vnnc%4~>T=X>a7EQFGF(W|S5SHevO_?5Ko{=$M%3jD)D{ zgRAvU=plb*cVtH$vDiI7+ZVNeOUnF!A*G?{ysNXPic)d*;@O3vp^l7r;epdB;?oO~ z;?y*vF{5l^s_1`H6|*O@bgGM2bJ)b59V$;XrevjsF4pc`iDl90@lh#JtZh-o>?o5d zYIeq=HqH|^8`4>|x5T!IS#D%eZE=RGdGV8`EsjD9(N1%LIS@VjeEBG)kpFh0{8^hP zJw;8yiZf29$oLm!1Gf?ltM2PuuqZx{B-E7iYs@JhQQXAA2mQw3r&xPZW+JwBFm*)p zlny~C5zSLD`3o7iGvs22^zN_>I^cC4q*_4q(FB3rQ`|0j?2=CMIf5W2Km3toWM!vi zlzI=WCm25bfy1AalAaOtuDWsT+2dnRS<|d{TCMtOTt1GUUVG81S8Zwhs0QwPHSlL2 zl6yOPQ0GZmbFeV0cu8}`dWEfdIH$JCpPo~+ymb<0&)DTuEJ{tY>h-wVK8~Ayeb=g2 z!F@Wz4|c=GODFXP0G$2^7||CBNkB(Kevkr?=O9%lQ26Ma(f}5Hq)bnvvkt6}G@~@5 zCpaQkML$Sj9Q}2!bu^*H27(Y&q1#d!Y^YE4CPuN}&a=hXR_)?K$rrKtYxmE(`Pw)p zdhD|ca$}N`J%-q6Dd`n)9m^K(T@j;qNrGi#Z}EI4NT$cmQqCJos0+Lpu)rd9YxVMb z{q|J3!hW7)oXb7OYd+RTUGx2>y@&KXZBekLD7MHKhskO1B-JlWTi&yNZ=+|0$Eu$k z%}m^J@+>tyP^pl4lir0r`Z&<3I4dJT5Q855Kx$qdKm#EG;>&`pqBlw}67LtCL#LKr zP^n6%fyx4~<*FiG1V-UfAAC0&yp#+mgZ~~%Q{JqsuAZojX+>h9)otd^YNv~T;V|kw zjnyf4Jm%1wlZ@WA+aFxF>u}bxu>V$;T3G1A0dHd{&m$Qi&%i$XYT9{E^}!V4#yOG@ zxn-#*#kEy@H8v^5;jNVaaasPNc}0*Xu$t$x(A-sHcNlC;aGKT_T^V~)Ry}at+B+@{ zjds-~GH+I3hCelX>Y9z~a!p)de>>iD{Mjp9Ci%J+`P&&nMU~C)1Hcf&Ir}!q*G++s zxLxQS5{1Pd?SfIV21sPH1yE61Ks!KUYfG?yMm_;z`P__1pOuD?$VxJ=s`*pE`x!CslJ5wr>oJ+y}lyT%s!BB_805*;dH&79sLC)5WEie6Y2K2gqSDZl`=kM z0*kfyQf4Jw$@R<^E!^f19mUqN^*m>9sQUf1+|tZH#@W+S=f*-K_N$nf%=FprKVRyI zNz0rU^-RQ=91A7V@|>)4p(%P_cE#O=ljT-lo>=ZH&xX9AZ*opnkX1|7Iq3zH*P5qh zW)$#snXJ%ufpGPsoaB|xGLx<#c9?O}`6n}NPQ^}BrYr$x(!G2%> zr!KVMK$Rp|rN>f;J5Bo(?6!P5qU|vT%3c)Pch0badE&A0SC%xadgP)DLtKPqj?|r8 z?o4ln3%Y;A8_*G&Kvo5>0)u2`c_B+7F1@WH1_DY3yFQvf#;ko&!`5i?`K#NYoc!vw zZuhEF-$IndWj?=Jt~XTX2><-lWSdk0{(V+nEIZ#~zf4?zEI*C=4Br)kB`oTJhvkp! zW~`O_65UI;CT1r-cp*$5nG6r}itnyY&N8{3ZmY-W6;2F3Z*!TeoxgF(pZq>$PRf

|iJ)rNwdGr)EOmirSOj@aI>%6ZNkal&y#akd%Z!h9PH=pX zunSE4#rHx6xEAD*#{#Db`j(nTHb$rq( z`SIDCw`IE4UK1Cdl({%QKiRpYvTI-Ol)2E3n83%6*X4lQTMw!im@x|=F;1LfZo~Bi zz8NanVFA(DOnN3USPvw4gNFtrRu0qgkpyHaDRvGISd351$@kpw`x|c>3KfXn$u&2; z`YH>)`XD!_1eR6A#F*dni;b15*+r!}i>5Wk&f1YAUQr*cES(1_$e9xt2lm;#X>q1N z^~f!^j11l7%FB=Wh5XVRZ?du2qN$s&8EW$xAD=en{wJ`EcLpk)nsQzwbcYS z`Gd1Uxu1V+O&I5g%~#~+ly9P;rmZu+8N?k8GcAjx>r1RXidKDjVTGVLT0Jn;=%&b4 z;Rg2DM0S{X%2U^#WXLMY%5+<^EuvA1%GkN&g*j1>MX_d^W76@)P`%T0883Go2a({ALKF?KFD>=KXUSYGYYJ3Q7Tk1Ni}n_TnL=PkP}eZH%SJ7V22 zNmh?T@7kRtc?vyJuFI61o{T@EJ6rOw6X){5n9c#d;0Ek*S7H2tlnGpED3z&Cv;vSa zF%Afdu{fd=#`T$~KS;8SP>%}g=rPh(qP!r9DH^uY8h5@~kzlghqids+!c%8YwPtRg zpBPMh53UQm?!}(WIA2w`YGpXMVoJCwB|bBDQB<7UXm}4v=IzL^PMtF~nB=H+N83#a z)$d57Y|nX>TZ*nWBxEG|@?BYpj>LtRrdlofq=r;Wd8SR0(sQyC60&pBCCQOlX-REJ z(p#*)-3yQ~%bk~!kQr~dvUqFdWm_=^&YauN$6lVGU&EvSYZy4!f`Oz{;h+$3V9B;B zaIj;o02H~N=!ESD}J8h-5^cocoYSL{%o5NvbyP58+$p9d*FRvk~X$=Ub z2Ipk}2>f&XbGS231p}FPi6cOn+?AjyX?&<~CXM`ez-!(c^n%-K7h6Hs)HHe)q>mS?`Y}S4F6yJZNv{ z{?h5q!P@gT)#`PHs~cwK7U`ouDNLH`&)28CXumgfp)=WFNSN)*w59lQ;%<@eNHWB( z;4HB)EeiZSeHrV6mm!lQtzc&11LE9u=UrX1aMP?*^-M*vpV|PLc`fWelWZH9{J`%M zerZ`{23RdQ^CPZ4aQlQG&?DU6o%IWH$X3#vA(W62?Na2jp^HF=uF6HqmHu?hmG#yG z`BM*eOqoC5?w{kg&zn`-ad1+}gKuTIj(s9YpMF3I3a1?EsGAAop5<3l9GX)2z?+#d zNRfO{{>!0F?;Kpc`rtd84l&!onPdH9{rnpK!?DR@lcgVy>BxTpA1z3+&zo7_acD}> zgKuYgKKfj*|Ma*k`|StwY7TWyn=#*>3&|$?{F!x~hbaXr|C3(-$p^0Nw;n8-a=5c< z{yck1;SuJ5q2+fsZ+e$3HamFo7?&?%+qlfOefbl1lTgOs9qiBK}bP zSV!N%Eo;293od`*1>x8KkdwXXWuZBXda7=zaJ%IXKYCJFdh$1!Mt*y1V_f6{$v@*z z-^sD2{Vr+7ijV`Y20{@JRSICq&Z6Yl^wHK%S;Vm{VXvZ4>(mBX$~nkA!t_dmJi_9%^0c(_i*qJt=OiWP z+?zc)Cnq^6=Q}yLPaeN9>tgwx`_Fsx>V+|#7jI6UQl9K9!>`YmT%K5B8@Tw&8Bxhi z;p54R9^BjCYLgqPTdJqFP30rAztuAL>ayZh?V%MJ5PlVBFJa!g$(8b_tHeopS^;G! zq^Nvl&&D<3;D%|wtQE757RN>x)b!L&^0>U*EtunDoy)$wG(BO`vPBh=)dq0!I}c{Z zr5BW~6n|e?R8(2?)#AbAyu9SWkZxNYBoUo{l-2Ltox2TJG9myfNxy{BQ);oi>mE`510-d+FPV88sw+UkSx zY%s4{&0kks-^g4k>kNfQ2g^GvF1zW%#X%hGK+&Mk@9w`utges@Qk28R^sz9avHSDn zlE#U9_&CUpkd#0$3$77pXRdG+A+HS>aAHI;VM6I}830cLF{KlU3}L@sKJW|c1&ytj zU*5WAa%a!}Bgc*%x$P%xMQ?8({;}wDNC>_uHRX~yE3SI}s!5SHlCOAu6Q%288_%T< z&>TfyjLy=t@Bnotz!;F60oD&mrd&BL(<{=?pc4Rg1Y{n)uH-wn&Xhk~a_cKcrp_6C zWOUBdr>}2qwLce}yWFzd9q)&}>f^=s;G|;tJJRyFf%;XWqpRu%;_CAqJSUoyvllx1 zUH}AA53Fm5s9PM$y8v{hG1t?dc1>}O1U%O@ z`h1N(y~$h=A4o6sT(IawV+E^xz*Cty$FjQi(2bJMnqZGHvYerTc|{fdQL{pBABPLm z`V_+@>((5s?YLt_#m^EG@^ayI-(yx(4*81yDu%FC@$8S$Z%8YhNJ zp`~;R4$V~dPG`0O5dH>X04mvw4)m}Lj1BP$Kwj7dAV=`I{a_A|5QCH~2C4)D)EmBn z%7evN71PkL^|n5#skpJSF|bBy8&r!3Er2im7X|g ziAS7ZSqK+sje&V{XU$zuyigcCSx8FM!s`x`p)9I0v}Q}AI3qPPGp#{t+_ENA8C7O5 zjotZ!DaJTU5QW~gK%lp&GlZSPC@W}*Gfw$|adKLL$5Z5+O6vvj-PCU_fxmO?zyV75 z8XTSrd1O{!wPc}r1WXntL63%)Wq{-1io(Zc7E&ro4K!}h1ZXDk*sy~@e<2g~7_2r) z&t@3~bKV^nidnhyXJs;$Icr|NU)p>}78;vrOt7qdLz;_UBRLp!(2j`r}o`(yqxwEOv*>ejs@{S*0p2Pb~@x^Hu zH48pp!0Qd9rig1UN>=(tG|jw4tV&5sOQ{l{&o>HVe&NWX@>##-waMw}$+i6U!zBT$ z;p9594|3nhbxNlnDfbVuW+^$nBsR7rJvrmvM-~#e;M_O{Jh?vtuZ+tb#p{w`2gr}T zXh63STn#UnT$x!C^9ork6B>4Sb`wJ$FeC|?tPIxED7q{QNAi%vD0A>E16flmB8hfr zD)>WLegPte{;ct9Sthtuo*0*+=pExF8yjV$%Sxs;Xd{cvY}QL@?|@MdZGj5yrymyo z4MgM=JJ>Q;H1Q7DE||B(Fg6u#apjN2cE@k|*avLHC9e=}a3AMa0Ho1%B?H(n@7TO|ErL3%|m{Y~T!xA+4+ zd+Sec%BAoA?QOR6O*Z|fW5?fOFvE6B<7e}k!z2V7^!(6^>}U6#c<2wee$F>M%O1bw zGKiT=^{mMt6|@=I>tls>ga$z-7bssm@rlIo6pf7EF({ zRm^N|<~R0ScU@2Sb=S%BkJ_V;QFaO0p(3RSeUEBa?L0yGMiV67R^ZeRI|1d44$B%a zmPiy9Ed-#WCc*z)pbEB)=qu0q7VWFFq!Yh9=3JS2QB*&zxNv5X&uN%nJ9e~oKC}iF zgd{^CrXVTDpOaJ&6W|ZIZ0l$ijbG2|1)J*>^ng!P(|ZxKSvVh`+Ko?^A4{7ubH$vT zx{i*z;#KSC2E`PM*MxswO9~S)?G-o8>UCnTP+^1?NR=2@%})+=u1CQyPX$d<1Kq+A z%vs`_k3#@g0Dx=aWuOH7=&5nj+~KJI;aOdBkq8SjGNqmgjW4?p6wyWJG*;+~6Y_I& zbMq65^%add(X*g29bUBK`#W}gUrd`QN+07Gd(jaSu_U1x;E<0H zEa(9dY{_VMYlWETaGOkSN1|BK+C932Po=_l$iJ;7aH9*0Mwu}Vx-iR`*m(q*>n6aY z3Z+oO14HrD=-2vh2YOHi5-^!cm8Gr>YIa=PT`1%{fNk6!M@R#{fA#FbPKml)6~P20 z1`0*f8q`8xKe-Wgv%<12JnQQnyXU{?Qb5p`3iPpcN(X5cJ;>$v=-S#Z(JNZ_zB#(& zYdy@KRJwO;-RX|}^mOn3?R4D907142$qzqz zTB}j9g!`i#Uv|z~v}l&|IamZg&|n@y+5C0C-@AF;Dly%K3Yn4d|@i} zw0S@>)vg&21d}bg6rRfie$4_Ve@V5ydj;9v-77!*8A=y>_n#4K++X|ocGk1~^SiVL z>vbec`N;R6hI!SMe`d3l>?fwb{MAjWtflFCm> zqdjdEvu9U88A1W&6Gxw%8{gnN#=VHsa?*bB4?V>_AimbaQ4Kn53gAksICqyTN5su zJD1&}$mz((kWj;@r>z00&nlWd6UqA4QPPQ1{onQD=~bGSDuBTM6;91O2d7F3(W2s9 zLYn8|T-Uz|(uGlC$j(HT1b)7sgrKj;IXEZj>WT+fM&LD1J_OR4Ls*l*q z(0*St?x?Cn66Xlq2=RBXfAIcmuf0F3!jl#b&CDrGE$O=Fk~`|^*v=7bS7u(Zditi- zwW-ZL2jmZbwQJY=ENTCiKfZAN(wlb|t*M++%RhlqRfYV#{G9wl`NvUtlN<7qoXx9x zBKzeX35|WLYW%Zc^=lYDzVEu5<-IgK1gx>U`KST(A29 z7zKa>5}U&3kmea3T`C7PP8?q(!vL&C%aPcrM^Mg1kzT=ZU_koGHY{==3Tvr$@}meu z(76{7H1?;&I71DJEHUJbY5U7kF&c?($w^%6EDR3)04!Cc>mjVaVxT%7K77Y zh?pqBk>{-y%(hC8Bnm!1{Hf0!vV!feb#LkwVyxaMx5<@y*LL}%dvho98^~G} zG!Mgm12%DxTp%-y23ElgP>F!e<8u@r#M`blW%*7XNs4jC{))30i@_o{144R^Rr8*2 z&`0p*=TzY~ufG2^DI z;q(2Q)BlV7uRm}~M}+kHr>C!dWnn&ErK*Cu zE0x>r%5_Y=!9E*3GS~n^U_5eSLiybZxnwPulF6?oQ?HO%i>G#=8S&=)RljeYeqj9x z@a&1IUpOl(sV3iSmhVvVt^C?Gs8pfKH-G)@yI)IBZS@Byro?W5#*eMGzbgOS`0-~wIj{%qH??L=S2NXR ztHxf1SHsRpw0yA>v zFz!3P#c0_0114N`D=T_$``GdAPi)`*1iPhsjS;ks*I=%!9eIAkj-xhnU5(igD{-f> zshbOzynpf4|Gb7RU)uk6%gU84Z}%;`lj%N}&tEE7O~uhZ@RAp>z+(@yf;-KIp8I}x z!DI5P^955(tf|OqvWk_zW+iuA#iVDpn#>zsli$mvI=7$FZGCgP-e?YHo6X_93;UmF zwmN>eWA&Yr&E}k-$*7<8?giVAU#2(g{Ie=s13AS}aA?3%B=_Db)9(y}j{!}bz<8*~ zJ?g%B6!NI+Chq$f<~O#PjBK3i&fUL_9~G&2j~%7mH(fB+3jam%K`7{~!1cNu7L~(+ zy=h;dw&bj>vBtMm9KnNrBUkX)?+a+$*pYEY0AHsXIp-+-6y9(hF$h$CqJVmdLqK&a zaz)CwldWB7-owEOwgIH1fMZBlS);Sa6aa|k1qDt}&g~oVTYJssk3Tk>_X4fr9*@9T z&wOZNx4r$Zl4;pQ*Tg=hzCoX2Y{;`c@qPYdySUmWO6x80W2*PAyVU04t~7VT^GVy+ zhnU@kPx*$lr}N4$i@LL5fcjI#@d_-FBkZq{^@S`jHYmR$t@{QVp0)EJjtpP>CVHKC zwK@aG`T{8vN%%r}=W%B$ z(_Hb|gBcG?AUFkN5Y~VkE(GrtKO*q7;wN+fJOUo29}*gAigXo;osss59xv!U`MCtT z0Y-7tL3UXoH<G9z{;ZqrR6sUVoNd1cHI&I+7p&q;$?!N3uAwtrmOGDX%no4MwBE zYcw26x2D_tR;zm3LQw{z$I14jT^sfninHcc`?<&9(%S_|Fgz!CeQEma<*PGWbp4^j|Y{)20DOhSxob0p(vRs8Wo6THMV&gai%S?{*q({Z?zGt@82bgi}jd`<0OI%h}?mLwImJ5vIN5RxqA_FrH zs@2572~8G=#8x69z5(NV=>~rmtP)1KN?i~;E|k*J)1YM>DD}XM1K28x)-O3(Ze>l-?J=9$=Cy(7F3C?I= zOiomcQC#KDxT_pC^QMT7w4}n6kv>CmQNZ``#3MQW;Ul8Q=rkAw7UD+1DS2AAFt5=8 zA(0!o*B50lJByg6e69S~^~sLO zw|{F_PIhXxNfa*p$t_zOL`Qkrd0#$!O=hMi9nQo;ugPP(9?98#=>=I?S8aao(^>ZT zhF`y0oHk=sMkaa7nFW=1eN=iTkVoP4?m&{jrHbrYIKMKwrruJ`EsJt?C59YnzC*C! zQE}jx$A82GV{%*XJUltl`DgiwiySp_^I88y9q~t86c=iP4J! zOUleNTViVGPR`iymr8w3ZGBv<)8vY4j&06#i|cM)Q)97u{jKbLX4*CPHTjQ2sg`&c zEnW%xe1QwPR>j9#8~m4DwLLeN$2j6+6B4ZEl*vZl{wrR(WvDeV%`t1Tf8LPXfbq*b zW!1kU{S_xw#h^f!DHf-&ED-(&wMYUV2B-?j z6~eSPWM;Y7&#Oer#)Pmg3sa{oS+olnaA``?^re-%BGFb@dQ7QI$e5a!8S92~PqrcW z%%9*w@2k%r?vR+n>=#QrVX2g@V=IT<{4WbG{r+p;zjT3mV*@q6gZa~+$nVMWBaO)= z(wr-w`rxy_AAe~0qngDl_DX%?Ehd@uOH~qD* zwHg;Z@OSyv7j9++e|`O1ksR-mTZaNy$`}2WEw7hQ^6Gt0{p{86?_I%@+xEVSsR4Ns z&@>7TC3|*7(9tHD?tbWIUj@DF`(gVBa;IdW66dL8xw72&(=`%gnh zzCs1%*%DQD!bmw$!sq|PoyLagim<*d!1{JI(VBo(P%#kG@j!@A$c(}>yt)?AcAAc2 z@J=zY5+y+c4O{4OQ9sO*D%dbC07Zs_2{OW>#H3(>#ID;VMJbP904q|7Nu-?yyrbMn~K9OnSo4Fk@c z)L8C(P5yJcZF;~~_JlV8LqFap?nsI^<-%FC;u!KJ(Ug!T#wSog@j;JP4s(1%Im~fR zISKJ%T7pTGUs8NphLdtl@$8n=Zd<7rjaq-iUuw=|`8UZgd>Wmb;xa~$zD2TtZ;eJ9 zT`9TIpR$UZaXdqZN7Igq5s^!a3Kj~lCj;(!JkeM~M1#cqv_}Ts%8;Hh zH12(EWcaYY~)7fzL!mxZ`r)XYE+ zt0PLtbgAx?I7Pm7M1JY^N97k^h`WTX8fIm;KgP;mi1REbqDk8un00no0QaC}BysLa zx3F|qR+-lT;-vs4*|IY6gBc`0&i*HwK019KPci|*!?%>)e^1Fn^I|@ak*BfZi{;nY zyPtP_#j9P|C%d zIzDS(x!~yqYn5Ecf2Jh9=^Lm*>{(AS!%FC^F4wi_dSGSZB6y*CRQIgzW!*cvk942n z8zGA2hoCFA71%OBmJ$;}uWT`($E@x(gc!ZDg-~`0;6^B1i7*L+hrI!1y{AYTqa2d@@6zTCo1Q!H`o@u428IC!p?{x+;^E?Y0l5?UBS4;X7dxD;~Fnwu*TU^wrhboN7w;8N~lBoLGfs-|Qr^6m6 z2+l;l%xXx>v088$i^-UZMLaqhS4nhP%WM4Bgv6RlriFS|_PQ@RG{wp~{yIG%EZUUo zugVZZ>+5|x4?i${#-&@97wLlyF}@Rnc9YvxVpFd7iqUC_a7yKjN)&H{44Es<7~^)Q zj`cVli3wAjPDi+ket?a>MUOv_72z=D&!M?0i14E< znc=Akr;1+YFkp|BV2duyO}yg#tJ$WZ$8Pq0S2##myV-&$Vlc3FA#2Kmc5Q-#L0 z5dz+Ga;S1VUEFbVF#@!6v5 zh!ce$wCeIJWPazJe&>?M~T7=80Km%%z<$p*1`g0SAVL7MV*HckBHJs zx(s}m8rCDeNedfv-)7sjuu&Jww`gIL&drZ#VT&%8Kcj{1y2*k7-b6p-jkmzhX%}o^ zbi&7&51O0JIJbx(G##NnXf$m>H~1emZ8;TqtN9^B958d9Djx*_BnRC2c=rLL}j zV9Q`vN9VAwzIkKBH@&&9ZHq5ZToNwy)%5iElvhK(!N^c#aATwm85+=@KD43+_=!sE z2Spn}bbsG)&8Emue=i;uBBlfKE3@Y{^Evd%Nyq}q^SR(#-++v4WW;ybv|7X-&TfSF~Z~hqFWjn z9O~-t^92jb3X7GG{Lcz+#D_%iDb#h;r4bw)Q78J)4gJcsQ+e}ELq&O7k#4+U?Z~0# zRP)d?btjcIh&tMkzE|nCZp1Ysmg2jxAdDb1UP>Qw(Nil@5796-_C%V8A{eLk$e?ey z-#6SD@tqmkp-Ag6eRz96UgAwV2Fo`**xVNBZ656QH4hIDcD0NsN&5PSyILbd+CUGY z76PVohI(+=cY3V92^Mu{U`eNd>@YyM5+r&NdQSb`=CjHyRK85tIXpZ7y&h^_vkFUv zUH$(}2}KwwwO9I-(JDgbZz{8>2Orrt6v2Ci#-ZE4`p2Kc8wN^9z$xJ#-EN#QU9GzY zwu1KRu406);cgXD1+m@36aLx@U1YH&13UfBU`{0vPIbGEn!R9GPWFkVOFwLY&BcM z*0Lt-|C(6~@Y!cN8*624EW+AZ2kT^AY(47+^Q{;9l>KagZGa7wAvO$?up8MXcq8A! zwzBiEF}?ueliS!RyNF%PwzEs%c5o-#1xb?2pt`z;UCypxSF)?v)$AI!mtD*DvHk1- z`xcC{UC(Y{H^N8IL0ITM%#N^|*|*s(>{fOgyPe$uPgi%byV*VLUUnb*4!fUymp#B9 zWDl{2+4tBZ>{0d@+^s&ro@C!=PqC-j57<#y<9wDq$9~9u#GYp_uou~n*-Pvv@Id`C zdxgCUBf39hud|=CH`tr(E%r8hhy8-R%id$ZWWQqXvtP4g>;rb3eaJpyzkxN?-@$Xy z$LtU6kL*wE6ZR?ljD61j%)VfMVSix4=7)jl*ytck(D6&0XBhW4MQVc`T3P@jQVi@+1y^3#>Y)@-&{#GdL_q z@GPFqb9gS#c`5L~KH}Q46nYZv( z-o_)m9ZCR% zG2hNF;XC+FzKdVVFXOxU9)3B$f?vt6;#WgcbuYh`@8kRV0sbw19lsuQ|Bd`6evlvH zhxrkHGygWfh2P3=F#jHZgg?q3=tm{3-r4{{cVBpW)B)=lBo#kNETa1^y!cF@K5wg#VPk%wOTJ^4Iv!`0M=V{0;sl ze~Z7(-{HUD@ACKfFZr+d`~27Z82^AD=O6Nq_;2`c`S1Ae`N#YZ{Ez%k{1g5u|BQdm z|IEMOf8l@Sf8&4W|KR`RU-GZ`34W48H>a)ewVPskSv z1n}a7VxdF`2&F<07AV6)nNTiN2$jMlVX`nqs1l|M)k2L>E7S?~!Ze{lm@do^W(u=} z*}@!Qt}suSFEk1ZgoVN)VX?48SSlMn~gl3^dXcgLoh|n%{ z2%SQguwLjEdW2q~Pv{p0gbl)=FeD5MBf>^uldxIXB5W1T6V4YdfD*|zVN|$CxLDXO zTq5icb_%a^VW$O5rNuYT+7TuW+rfPuMRU5WXc`CtNSwAlxY2BpehD z35SIv!p*|Bg2=@!$6&}#-lRA2uhlZryk)f_u z{ZOQNu(i_|>Dw6T=^uzlop>G=hlZO6&2(vs^bQPf5l29^i0xfHy~g3rCQu+95kA~$ zpm5jFFz@fy4@P?XH%1Iw`}=#Fy84XDy?8^<5?BLfsCb@jFMZ?+8dG;e8Y?HX+DiJ;Db zNb|4(OEsvfP9rr%DX^!%wOefOY3?xNW7-Bf`}-n8=8gS5BfXI(w8x?asREN09vRSY z7;Notix^ta9k>g_%^f0sLt;yRf47k?w8BdRgI#^Y`qt*&$Y8Tb%PZdZwCTHso3RjD zh9jGYn>r&z1)7!crmnW(PBY$h^fmQF+J~)b5KHE8WYD5MD3qa14X+;=8t!V}BGR{5 zy87CXPR*xW!>{q|sHvXV|f@z>l%BMx zL8TQ&H9Rt4Rs#w|C|yKwgysx&ZH+XwkM#6dweV1Hb5D;mvbnXVxwrXrv&4?B_F)l( zV>{-^V8j^N0zkuPm?+TN(?1lkqQCmO`Z|=hOX$zOh_SV~C(_r}Jg6VUR-wPw(AwYI zi}BX?Hh1(zhRx&sH8OCzAE|u+_u);E$gmBcJ}^Ku?5h8&g&CfB0W8p zR_fMvbnI}%+=*dqQlVQ3(tI~4p^*WTa;FZ7Qh~GS3`9ns6{8g3I4f#o;OtCP3~+dV zOGLkE5Ocm$8g3ry9?}D&qR&h%gI$sKR%~L-1i9)wkvazZM+Sga`nn|mS5 z$Z!*VDdq_UF-g?`b*n`UDt(1{1I*qxBo6ft0@QF(vKf>RCeQfFMj(PULWMOE?d}J_ zbO8R_uq3tgV~i~tI8#dNIB3%Y;rL;|>o9hC14cmlAjZBK7!f$n4BXxcq&d>lVgz2m zICn(sN*625pry;IKB|yvpry2_x6OjQ!=3#@==_LrXrybHM$AY+MK$VMu~0=KSYi5s zm1(6^mJ|AfmXWR=%$5!#G7r$YV`}b2?ah6y5q)o@t-EX3(oRi6E$bs_dIal0r_%3Y zdvSXts;z$n1J#6f;!2$veO8PLe`iGj{?2-)Q8Ay%Z&8CvMxz=gjH;ARNeyk0p>8Z2 z`kv+ix+#D%Z0+rDq3=>=qg8`<1>VdXM*4@ z*#IiVra)PRWx~p085+Ti#PsbN09cQ-s39aPFSQPgY~4zI*A;1vU;(89iOR8`2@;{B zAL{Ii^t9Q>7aFxSQM5!g0lfl-M!JSN(W8Svb`e^5Hn+9`L20YDf&ml&IV(m5kh7u) zK~2o0AgIpa-ky-yIy6+O2W$dmnpLby9jRc^A*_xrzrj<OOZWXSXNDEchhc(j6pqt1Gw_b9G3NSBax3s%#S zmWaBvX%FIN46}(YO7!V8)R~4hzzv9MpmY#`n|t-`plQ1Yh32+CvAv|M z#NN_1+ycZ7Y^)9gFk#Q2Wmvf>QI4K|RCI=zvQ2m%8JPH%;L17Stvbawfz0jSG-SXu z9qjLFlQ1zxHlvwcEwr`_b#EEKqSik$IJ98|ivq|2fJ(o<9cZ~HBGQEx@ZqijVQ7Sg zHXJt4=B8_7L}(f5;2XQ8O_8paerz22@P`Ct0lV_;m<}rDrnq2?`T^r>aF0rY)2pz( ztsnG&vi;CHzpUK45u`Y%Ql(8uRbFgUS2iW0sh^?(bSb3^ja7MwE@8Tq(WRU&6^4<% zu7;ADV)S)$31TWJQ$;B~Ql<*ZR6&_4C{qPxs;Cf~g2hUX778Ipuo%?@i-T%uwJ0c9 zj7-5|WC|7|Q?Qsal@!y3-j-0N63SG9YJw%GCRjo_N+?GOI4p?)>g>sZ?&8yc6tS?auu2)h})>5rX_)S#0r9Q0P zsqi3`5u{p!RBMoG4Jt1vYf#HNjVcaN#UUy-M43XADMXnfL=X`ohzJoxgo-PqjS=8d1PLTUR91*UB19k&B9I6XNQ4L^ zLIe__5~?IXl>{gU0Yiv@Aw<9sB47v+FoXygLIeyU0)`L)Lx_MOM8FUtU#BTP9k=(tdha0PlBIdGvI7<7av2Mv0N z20es9$AxmxpoeJCLp10i8uSnidWZ%+M1vlpK@ZWOhiK44H0U83^biethz31GgC3$m z4`I-8p&Wz>LWBuIzy$4qvWPN20_EzA3Q$d98u~B|eOSW>fpT>^1*pC-0YI1lAWSGB zOt2KD@ekAZhiUx7H2z^4|1gbzn8rU$;~%E+57YREY5c=9{$U#bFpYnh#y?EsAExmS z)A)x2>a+~hXf3Q!=X{_hptiiGRJ*GaE>NR2wML!!ftoVyeYtiYFRw;>uGQ{!+Pz-8 zPgC!;TD`Sey|r4swOYNkTD`Sey|r4swOYNkTD`Sey|r4swOYNkTD`Sey|r4s8qy5Z zY4z4=_10?v$(?k d0mRO}xo^G_%I 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 SdSQM28?+M|D(2r_;{O0|uQ74} literal 0 HcmV?d00001 diff --git a/_static/css/fonts/fontawesome-webfont.woff2 b/_static/css/fonts/fontawesome-webfont.woff2 new file mode 100644 index 0000000000000000000000000000000000000000..4d13fc60404b91e398a37200c4a77b645cfd9586 GIT binary patch literal 77160 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 literal 0 HcmV?d00001 diff --git a/_static/css/fonts/inter-italic-var.woff2 b/_static/css/fonts/inter-italic-var.woff2 new file mode 100644 index 0000000000000000000000000000000000000000..1f5d92611ad156ca45b0ca8cc90f5aea159aa891 GIT binary patch literal 240688 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

o~ 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≫yrHt3|kTs1c77h7Bnwt1Y0o#S!G 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 Y>VM}A-G4?!Mb9bx+w0+*C;;$(003|+K>z>% literal 0 HcmV?d00001 diff --git a/_static/css/fonts/inter-roman-var.woff2 b/_static/css/fonts/inter-roman-var.woff2 new file mode 100644 index 0000000000000000000000000000000000000000..05621d8d16562a7b5d3f3e9b82176db8098c31d4 GIT binary patch literal 226368 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

xUqr`|Arby8kCCX;(x8Jl^UKtlA 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

=XV7>^437-|ktX z-?G*gvHpq`%B5PFM68tyWd+@pc2PHg_yDmQv0XJAgoz6XU@<5lj#qF{9185AI^6rNMH3=g{(7J7v;IhU(Y0H*A*;Y9#<>~8nOmTDw_ZSSx=aN4vS2MULle1y^`jLp-+VuK_W3N?k7{w{6&ySlCM!;g#C+?h0) z8jZ>APC`Y`A}PJy2=Z2RbZ-)u8PT9MNDvGLt)YR&;PoX{LFCxf=e{ISZ;xPzr ztx^yo*(F9!0?mH1Y46YNrLl_LrLKS3FDAH6hIDNsyYzk{yZrxhv-j)Av)cbgl+;b% z?C&!8YAN?d8TmVzTa{jkwN+IEJ!HolDP=@uPX^$FHiQWPLcs`)G(9jPQEeV4rfM7J zgO!Wg!a@-c$qNd|X($Z$&Tvq>{)6vZU;Vx?tAn=`I6Oh4cGz(Q0vWJ?ceb{c3{Tnk zxIU8H_mau$RUp8ndY)A{`o}GdSep6mUj&bdET3$pgh7nRJkO|XILj9DCoSzb@<+ZCG z$Yp8+aM#~le5#iGe(4gxY%CW?-aOdaGi#YpAmQL;bX2D>H|C0rPQ;()Sf|Ze_q_ZT z5D?5ySi$4jGB~5?>vF-H9-Z*@ZFwP82E(TYLC}R=reVpyI>=QITlg@om`NmxlVF{| zJ$Y09v$E*-%NptLKW3d*%omot=t1GCRK)1gg4lSx43AO~Wpyn#Dq*Zc|9dj3=vK0# z0bUIBnD*>21DvV1+ABknc1wJ^Z_PNxM5Ev-z`y(Gvw9bS!CfYg=ZI=WvIVOk61Dgk zvG8`D@L5l<@ZTo2WED%H%8RZ)rk#V!)WaI>$QZQpa(cV^E3k+1`3u0j-n<#)G1}+p zV(-wUX(7+4Quq`jmgfgaa6t%) z1sG%lDODOqTn{I+iY1?Hj&{^DaToFLH(iqISxoPh8}LC)gcG;w3@;soBhjF+zJ<)h z=VRaj@zKQHgI?!c&eiZZIrWo-l96YhL(k3G?2Z@Um`n){?uSHCcK5s2x}3crtoym^ zX8&v>@N__rZcqK@{JbqDBxujO7aHA?I{|064VaxK(Y`4eT+19aJnDenO`2FHG0c++0bQMe=0*8`~>c!;p zD<)aSZN%m@k)%aHrD_H0bZxq*iA%+oW$<_xx!Wl7CRrt&-zOpYaOrVZatI0PS%zH-L!=V!qq**HC$Ty(`GYhfEvl`E9A)CfhN zSBYaJf#Hwbp+IHqKVhm>IV`D33m+iCmx`v-A>EX3wuWA5kOJdpUEcDMR0sE zi^(ibBh9q`diVD2U+&gn!sO*=G831EAs#y5eoOTJc*6o(#pH4osc1|cUCd}=a8)#P zOnwrRS;j=qny_*%j8kMItNkjLQZBHD^zUw{=BDH~JS2nUq{s59dyOG@Z8jfZ8$OuE zfhh!VxTSlXB4{=_HM=dLgvI2YDPnQX74X9Ofj=3vA|t1CP_Ia{C1)c^`~ezI!@v6O zIr!YNx%Wh6<;idBdIdXvc>PlUFL$iZ&0C_f@g$Y<$J-FMgdr9$`)U%qBQ0Clckh(4 zldF=^>FPpGj36vBl@3WN$p;!WLj#1PgX!E1ZNTdm=Nw5|L*ra(FWe2;1(lws%Ky`I zv+y=nSV^Wz&kLZCZfH|Gizco4j#SymJCljQRAq9xwdq*Q*-ZXhy6jX;jEqHRN?0*5 zl2hOS)a&;n=n?Gv!FTD#&!6Rk*Nqp?s){~0_x9I$r)B+ci+aH-$W9-4Zi+H9 zgl+_=4+o@Q+G$nJ9FsJIdBkpgZdzXKH+ko znO7h1kD-NlGgwx@^Bg#u!{l?vWTN3r&c|jja6*GCw#d%KZd0^fnNtFe>DZ`6`;B{* z;dD1$i4HY=R7y(7QW8uHqQZ11~Hy__T4@GvYm*j=Ow8Sp5}Bd z|G?oaKJ`^dNku#%y%-OCho2!+i(%$8zkrakK&D0O+(My@SIJ7S1Ysybk=m{pR;E`3 zD7q9t>?+{O;D+S!TOFAufjqZ-n~Ia5XUsk>!Ll+_X8tzJvgR^9KkD62M%DtTcOV7Rso`ElDQ>J~HE{U6=?)7k+ibv_m!pcuXW#}-^kdhFuSg(zXyGsErrlt-V(%xEJ%+mrR z9i_NG1Yhs#b^qRM0FX!2PP6^huPZADYnGN`Ilw47WnA@v+G=pPUpK64ZkReBbZcmD zmqAwp5Ce;)&+Nc7L3kVwf}H8uy0b`NUkUy%K~ZrfWiW7n|BBHVs9lc=U06J&8^+CP z&4~}SwtDxc$x{bLRo&6?ssh{zcxZB|q%e6p^zP?YoKs{eK=cM6PCpewG$7 zn#9qASm!Uqv|nPF+UNzOVgPL|x}!>XICEE?YyU>QBR}ar>I7-C%Ew@P7in=vV{?Bf zYi%zsJ{&h-?guCjRY+F^g9spPUZevnjZ4tTX1WfD$e(dRS5-ot{oC;Td_O zKDTc9jE>&&^%)*TEA4pXwzgL3{VLnNLR2i#EB55= zGXSFALvXgmZ3#kQLUkMUO?Cuz#D_eOSl*t(-iwA?Xx2m8(qo(@t`vz?u>=p3~p07qx? z!<(&^ZIBvOoyMdnxLShN@k z8e9;nCc{-6J!jBVtsWeNXpd~$ne|Rc4rxg-CL@W#;g!C*DI|KRNd790x2Ku1Ye6S5 zB_?pgD6^6hBAOIaEG;cT5D_FPsFkx^RtzUO;6VhUMk2ao@nXIUQDzbF!B2u7<8dV7 zeDDv)DMU9%H%Bt@@o@$Q20pQlKf&WACIcyg^_>ITyJdg5H`tll7FN4a`?yeA{>Xiz z9T$eBHVjHAr<}<4m4>B#+1lWa3U2W z8yg#pjg%bJs-Ykte^h$Eu<*WAhD!Gnr0W;gZ}n3$cFI5in3Y)R<;9n+Fhyp53Qkfw z5vC*;1ep|8IDenlA-R7^^AUo7B8m@RU$@{#o!}$j3~rFA_+kAWHY#%i0xmm|i9kdI z7^!h&!qP0FXETEcfncI_#hGG#=~i~lURi`-?n0V6bg+N0sU3;?7-@z7YvHRfdC_>5q*PNuxQ-{% z1gVx<>95_K6&#U@TtKK;#nijhJD^gRS_;Y%#gJhh4G-`)^04$h(hHgH5$hgvx9%;F z@USo|ToCFnqZgDHpg!>IIKuLzTw2tQpM^oeu03X;{QeA(^j-jzW$t1YXMJo@VGFa- zK-841d$-^Kc^S)1a~bbmaN)SusNG5uS=cl-EHnhaUGP_Xn)zZk%abUGx`hZ8_5un4 zRYFUmD3`%K`aj6q3y?I#2KO>#JQ~GbmSrF<-@;h8rjhN@EwB#+c)Z{N*203pfCYDK z7NGj6m0kaJv>ZFu+y0-}E`R|m{@1>X)7!V+rQBo(91yW>Jw0t8%m0}29zY}6h}!A{ zENFba4`R?7*EQI*?=Ovs-#0$we1yq=k(n65KY6{XI)%Qzx-EL8hycr8Eb7mbMB$iShL z7jMaiw{OKAo_Vd3-XV#ed(HL%$a}ej z7g_mVG3|3=&Li%dh@0mki}9F_!5_zR247la-1uG0zHjO-!kdr}zgiP_u%XgznYmu+ z5Q+K&{q{rGXkW|fQ~zfC5* z`DTzlu&feNllPMjQ7ANs1dHCQX*FO$Fn3w_;?CR0N2ohc{^Re!{TXh9kmmrhfE>*9 zy+cM~6M~sJeQo5>5)e)pWUYKzn3}rpUt#2sqhhz~?=dd;ZdIT7v9ZEfnm%;q8f?kI zDXTfU)-ack=U$f^%Z+~Xq`p|oQ3by|oOc)R%L*a6wG8@}Q$bRwc0pwA$T&^+s5uD* z{-P$3y(j?l;!^!-V*EI}AH%$o#e~Z{)hB**qRF|kV-#p8C_m3LwY+#NX> z56k7#MgQF$9FJGVNlwj0#J~9mDg|>i01PH+{E;7{^Or$@CGtmu&3)~N`;ONhA6h#7 zkO5Nj%}frN{;=9}&nr_3kLIU#dNQ8xl#D6#EN^OCAkn{cwFR+J9#3pwVNGH1$&}bb zu@9!Hv-+v&l=O_tG}5k+`W9XEqJ_hM;}$q!I^h;bGN+~F1$ERlsRf!^#^rPhEqzuW zg0bez_7cl?sSSRbeICUX?&R{`9_d0kiTcW4gp$7HLo=nW6AcrkuJo@pSvG(1iKIq? zAPkyTrM($)Bxl}OvQlHB()38&JOc9|-u`v_K@nb|&h4>0wVjvw&0j)l-<;W=>$G4` z8#wN~Aa(T6YMc5$C1rnDtK4AsMSN~c_KmOeb?u`uL!Y&$!KwI{27ST&*!Ev4uT>;4 z2T)uy&#psRYDyMs8FMKqa~ZUh`MK1Lf~Sm&&&e67bMq-_-#sa@QLz+~r}t%2Y}Dmg z2oyHdRJC#9=LhGl+Soc{f`_-XEUwPXZqF=wD_^)Fls8k-Nt-28C#ZtdBWRuFi2*$M3gMJ2t5g_>KDkTv931}T!7x~*_lZ<6KyTOo>L6FE?Xt*n|kO7 zS#v!2Lfs{vN8Fji1_1;Me-5>?)56u_a3JA`SA631h1%o6o;CUjx;md9Tkan3VVdhG zaU}sQhJGhWDGPbMO<@FjUn=Mqsq}|+(qwUobk-Z0t98p-yh2wld7{`=Yi6lWvZ!nw zb4^^D)_cFax}9mgmP)2suC-Ug8BLr&kN?--j}+Qpnzr;GO^%7-Gaa-!v$Hy!ItM<6 zRk9O3HmYRRt8hDoL?vB}la{b?!{oZGcID+Vy7F9Cb1{t-U+hn{Wos4DDw$kGrHC#&CAfQm$$Cv1wv1!|pt-Fwx?clph+4xKURrFErihzM_nVXFh+!U%*7Z$K79%5j(%f+8JOz0`U?R+0``>DC z`NnUElobm=63O?A@!l?ByRhgdt2xT~_wR#tNJh%sTuKIQJ|%T7gO)l!pGwP^OHG-d z^Yo-d#YRzLJ-si-M#Wx^0)Wq_XBJR2aT3k4FKRqxb^hK0U&}*L>-6la2{E6ku*mh2 z`J)ENG==je`6{wzV zGWBH=T~Q$`9@E$(h0|<>9X$86L=KKpAQK95S^TcH(X@pw#d=M-ES7g|T)9()Uic-6P!u&!fGU{i z6^|{Lq_cO=piGN*<(L=~PMyuTQ00Yqb^LU`a`O_wsi26!wWIDHKbS%W!&42_olM)0 zRBEdA$IfadeWXs7EH0JKdLxbWvdaW@;sxvD&ErA)GzcnwdF z2Xjm}bHsdEYo^c@AnPhxv)n6DbZs4bON{XPxa@Bbr_7WQu)QL$5)*cr7#e zYNAaPH^0;;Sx~l)sSuaW>>a{d#YZZQYWZ<};F0>Yj#`JTM`lx0Mg9KBr9aF#mzrJW zmSg4|deIbO zA z^-=-8=i%qraiUuF+73N-8NT-zzK89mgzjp>dJPKv4K01q=p3TDd9->=TYTSlCtFIO zfRaN2*>keweK3EP3TT2*B?QR*Mkfd#c$z*^F9DxXrQoi(^9{T z6Ex#1y*!xm!S;uG{j?|h>Qqk4gZZ6+4Ey>&e6B}O__=pF;g%*Pxs(nTWj>F~lO>>m zsHE6iS_KF zjqI73udd+|Ua$%?eFq3YP~_1J(zo*Ryo}2E6I3YASeIB}xQmVta(?&Z*($#A+S=|e zS2FyJP%tb$lI^beKc>k3b8~S)Qe%{VuVS$24z~Zz#yDzrbD`s>Q|a0O)X;#!{<6qe zz$}%zZyD(u{+V$Vgt5(CKIc54uv>b(41HBU^QpBkn_3&;*Q*k0+Ag4fZ(~#vqpkSt zXDEryfm~BkSyNImqO&dfvBNxfnUUf9B0Ys!p7Q0x&+l|sX1u_B?rUf1ovz0Ovduy4 z7SdvMU32}HiY>w{WV?%w-!1wBUw-R?DC#1S$zY0&nVbJRJF9wFJR|#eac<<&*K8iP%RC~*-Q*fR|H@2v>b3j% zoj&L=G`KN>m?Kv7Fs!Xj9v7Qs%Ag<~G(ZTO|Io-1)tkhjSgP#6NMw{mretoK*SjTjjXcis)k7~=-*lsSzvyNWPGR>s z-&D1{69LBc_E(M5PHODQ@rW6AY(dn#P*&zvCyn#%GmN{FLoIl9Ey~1&=gP#o7E4}e zvPrjnhDqE>D;slfl$x=1$k8KaN3_Pt;}!N-jq3wWsMY$W_MC(7SC`vF(uSK?EY7nBG=Dd#SGNjq`}BsSJQZ^ zwio3jboxc%B!pbBvhel_m*bn#*aZLPAUvV@UNER|yx`XwUcK1ooQI~hq_v#M>QbeO z@~yqRuS~DcEmz2m_r_lh{|YZ6Ty#4dnoIxQedk!N=rm+Nyk>7`&4+v ze~-K25Bx7D4oDAFf6BS?PqpstivRy-CN(Y;M*UmOaG7_L3n%3!C)4|G>XtXQr{fbZ z{99jW?D8xXh-7SN^rSuN<%bxlc{S+_#J*u=`RpWWB#xyD)=xGx7#_Mf{c-~N9Nq@0 z`^x)n6&p#){F05Ft$}Me9J6rJ=P$>Gr^i&Mr-zj1>+<#TUPaKJH#TIv5|Hz<{|w5! zoqBQdm)9@&{Rcn@HuzxOciR*4Z?|`U|J;uD^z?}r_&C-7qf;7k7w|Sg4nZsU{DzIE z1^%(_4VW!rhC`HgAwbq;y&}8)dj6p9f%fzlG4$RR z;+e&D({(2^l_$y<TAY8#q3Pm)Y9;9gKBKtpq-qYAxbT`ZeQE(Recwg#Y~Ke z$EEY4@k_RYBr*!ZSktu;ks}@?6}yASaemv7 zyLZag@zDKw{!aA#K9p_2Q$Szf8xe9?W>q64T}7k(${HG1RpRo_1L2=&Ze|KyXT;(=h(K88`KTt z=m;n-rc45wSrHb9UZ{4M^V$LH49(=(>`Q%)rzU24rtWkVv8b1&SGAK*XH4mak@J_P zt+EDC!sL++OFYyk7kyzOiXM7c%QZPibYFOTYTNzzhgk$hdSrIr_bucHk2!3`e1}Ct z^Sg308uh|Oddk_W8Z6OY!1#?Gjq^No9&ljkn)BK=B?DNj#?Xr*D_q!G`xRCa%JD^H z%Y7yG|jGcc73>7RNWM_c&Ik3PN@ zcH>5Xy}@~RLE^Wgr9HR)eWM`a@l=aoGV zmng5nC^W)N-Np?g_>ZfgNHNHTO{4&aoZXIcBimYOj|p=lm{yKf6Q6dW9NQk0a~7+d zC=Bd4{N7f}18Lf%5Vq3|i6=C|yCEncB!0VK59(MtKZIy!d&}YJRTBU$jk$f+QF-Y3 zYuq^_-VprjQCWj*gkDWczS@{j+p&ChjPH2;K*$rDW?Pv`XwBTow{ zkD~8!z+O4kvk#)10WeH-Pv07Pv1{sA_r@(C-#7MG5PS_&t;;~+{u8(8}$_TBFhtpD@!#K4WCpiSc}04ilOTg9U; zLeh~#Zl=CBiroeqr#vu#rs4xrlf9$V#s2?PPS~`mQDFq3c7w1aCXj}?tk77vj-+5= zR2h69H>^aT!2$E-aMij+dS_MRx4#=fgXJf1-j46xC-v^ug2b6;**)2%+8v3$%fSpY zN*D%xhWIpKhOqKs8jb2+m)2~?ukNg?RwVp|mo?!(uDl?Eq_jcXy5)~Qd+Hiluv%+o z!C`O)>NS^Ad0@Hr9K_i82ia=IU`s}_lUk(kJb%8L6nrL!-5U&Cf_VVB(E=bhK6P(m z(Qz?+TV%0eF;G=yoJAns=NS1$XVZbbQ>80JM>AE3@{OYzvbA@NvjQMl`n!5mAN!hG zxWm-`=STI0_=|U3Dfg_(kEijFr}G4p$e6r+ru6TP1EfERHlHUtI#=IYR(Sp7H@pY! zDIwoM5Bf|<3*fgTC0$Y611Z*v=^e-Igv#8y!srR^&w-sz2W$3U32nFkIG?Y`73tW1 ziH9()J-A&_k`nmMLJ(o6)k>e6qp&)$_hq)K~B8A;ct z^Lkm&U27vLbGg3P4#Fj=X@0Q8OmR_vrEons zUTyi}!9!*vSHYnQa;hCw%IE((_Z9j)6sT?IjcEvAOjdx$kp?XUot^ zqS8WSH1$;1VaTn5g>IvbldnDiGp=31P>9V(PuVxS{I~t4Ij7{47Zkf!5&q}De!vs& zOM%|>si#t}d?M8SO~3iCK>1%DR{IbK4ZX?r7Xs~X{+IlD4k> z^eiKy5^6&W9Ck5#=SEF$Nlyob9vn?NA8R^^PsT^?)99Rt+9~%POu7KkJM_l zgOkXicC~j^*Q5ZvJbvxkg|j{{$6lT}v|4k%p>ik>?KG7>s~75@sHBh9HrA31m6b%T zt1#u!ACu4pC}0v`yW2JrRzb{Ltb|+s?)%{N;GHk7{nF+s^n9kfv@5E5`P=}p+7($V7-!d1UJ>}yyp_20Kl2GNd|#64Ic#Sn^U^a;<-ps z72BY%Rfr7Y7wOUWoUPtqz2*BssRBJW4SK|epMAb+C;!&ZUu-}>@TSnY9;pvw*M_C!9_`H`YRHDm$zq%yH?r$e z^Zo}oPi&%FXyk+fg*&t#9pFofk7rd)R4q=9b^3PR9eWNkG0@u<9kctxxD^u|6!Fl( zoS;Tv_DuBrcsH1ZL!^ExpgJri*SFFgXp-}cdZf?tp@>g(8ARIdyR`OfNJe)hH#P)38&hfFuo{65Q1UGk7h;P6_H1QBBkMe%1V3n@DO8u=7 z_n>+IuG1q%J>sr9#uOawlMpWw@Aq)arvS{|l2cf9t6hEQ$?iTC9Xq|e11C3(L8YAU zZmY1Jpcq_^B>~BuUn@6i1U-|2w@UUaU8?}Z74#uzWUcI%Z##OireIrFHBt~4qT;)i z=h&^8nfdm&lssG8mK?`nN>+uSHff>`?DB|k5__iiLBJ3+IE*wyF|wosiWIcy`Iv;H zi~^q&1IJT`V@sf7ou!JpULDsPXXVMhv{%0>S)cqBWonymBLJ4&t<^`i{ql$Jw>~f& zBL(=A0v`<_#AjhOp3KSyafLQpCC~m=W`3*S9J^Z?Z=}Ju?O;V#2q;;O!!0>rC)A8= z>Ujn%UInpQ#v!|R7H=3L5QU80*dOVF7cr0nF@iqS@#V5c;Uls>=QcS-d?_|Qj5|m< z{#4Q+njzoG`I^jOX=6bg(H7IKofe&usZP;qWfQX3@MFkerSg>|zyaA-st2ecHYhix z+J!(;z|_OG67^s!C<6h^e}SSgH*>`ZG#0Ig_hnr*YXwN+U_65)V1dw+@E$^u^KgnH zqRD=-fRzJQ#zZOiqLL~y3Glpuh%qwYe(LWJELf9!uwN<#kXn#j+#%y@@8n|q<$#Qw zD*=UgQu5NuA;!5YUza~Wd0O@2q|mCm0)j-1zSr{d=gt$e69OJ9zjy2Kuid{tSYNr^ z-jc2nGp9=DOOqdbUWiIlP5DllP9?#7@;e|Vhn~ji^&EqHxX7;zwT=!` z!aYd;O9w`TGS+wZQ#3vIDyZKTNhqJ)QtUf)Uxl4p?gtEMK5-5J-dln#FrUr|5Q+B3 zTD{0D1lnQFwu(-S3hf?LvB77p$^)A>TVJZ=4Xrw;enWVY9>XNnIb}8IQ4O1!=S?^| z{C*_4I6e8aA+^JA?!@7)%g@?0eShm0fB({A@ampRZkp+(-OnedeH;kU!%)8M4MKw}i^Xe{WSBRskNRJ&gEB)k=l)>{w} z%`B@qkoK7kFKyH34P>T$a~3vOC$&S=O`CwnewQad`s|g%vLe$0&@l~-W~IS5%2UAH zbm_;0gT6FhoBA|FD0 zt6e`4f7tT5f@ON=e^FyGHAI3feJj}5o4Gcz*RZzsI+7~|5MxajRoS)?`Cx5KGEI-1 zD|iiJOKXX2(c2ADi#pBN5WSXidqCJ#;jN(Nbc8opmIjd;@KpFMM%YyDR|I5-AetRL zCj*OzZV^*?7@Z<`?XC~dSA>3;-vFmSxT&qGMxB!yAa%Z;57_4p^A8UW z2j~NUws1P}&{#weqO87#sp;JCJCR5liT zTtTmZ{{Ba+lW&D`TzOBj+zOx+#8~ZE*f+3=+6nNA-q)yeRTnDN-Hpi2LMJuo9vW`d zj9c=@D~3BQB8B4?;JCYJ5jou1Hf}Ml)C{*;!1+Mw%&V*9aT+UmS&ii!UR}AIAA~{$ z`NgAxg5ptrcD^7ItPR`n32kaJH8$BZc9oR~AGPq0t~p1et;JdbVDBKHvrym>c>t%> z7dj7hr|3QOAamScn|*WC6viEMlR7a7uSf)fbq?_L`PDXvfGQex!?g+GQ^rK;x>DVy z+HnduV5!1wl9_10-i)qib+qCLcrG4V>$zFsKMU`3 zN$}=>7Q4@>VgCWHqxaD=Uuk6Lyxi-Dz4@QvqjTjEz%rOu4f{LqB}< zqIFAd3oKPUj22u$)DrX6DN^#E{>{hvGt!TO#zFet$@OovxJ0e@E!xTPXJ^(ld z9};>SW%6E)L;)it#$YKZFtEKWm4=fkuo!Y;xI)sak;{4;0lRzF!N=07jcMAnqO2@t z5y_aF-glc-futh4ax8_2p==96pn^yfx5bPwDkw%42W1(3z7GA}PZLFs7 zDzA?&*FWF@5nI}m0@rw!!r+aN7zKpJFp7MQ-9Bu0T#|-TGc?U4RZ#YOJNC&?xo4CjY4l zLahO{+QC&_qjnu^HEP~UjHI?beh2n@_|yE;LZV{n3O`2Afujcw9$_5eg|eGPU?)!@ z8%F0!4uu0D)!5G4I%ReMm$^yc(Wr2`6R=x(locu|rju|ztsoelb7b1aOeVm*J{d}P zXKK`*M>^LGm5OG1#s=a%!`5|jmXxh#P{Wfj4Tk;ael|}?mYR+YOQhx>D}YNjKVYd= zkN8{^HHGh=;vXN5M+OXA#uEzE(NtWHpTI9ZBmqtwwZW0{k{y02oipoxyh>O_km_86lCg%>woEBIF35z|2`bpwNB%l@e{LLD-K_e+s3+*Ku9W2W zhPIAO0vmYXKxbKRXBGn#_yq5yeLRJ0rQw#Tkrv!`lGK-H-9Ni0+;57zzoN}&W8jy>H@|(JH`H)U7 z`=YzN@W5eLIbTo#Zj*Lo%c-VDm@y(v{@|yuZVmyjw`eK-;&}cLP z!jwXQ^4=q-<3$UOhFgen!sx~K3I3=X10;Wtj9qlsS7U1%btd*#pCZ{S--mH66mTpX zo6pdLd4N%ZTm7Nu*1x#Xn&Wi#Na+!>lY(1MaHBN3?^>JNIFej+n(N%)ecn6SxrEzB z1Tc$Z%xEG{W7hrTasmVPhRZO95hoh|zUUL9>+_EOM|`IdL{;>c79&7u$LPWh3#F1! z6SUD0q>clj4#Kkaji7a$5<^PFOUxpXid7E*H4rf!Lrlby@TgFF1QkSa6O2R`>C+Y$ zhvDR{UXP?FPhvDe02kKMuNkjwbgyf?mrJ`y`xzFW4)uzmS*md zc@{Dj1M~cYV)fC+(xBLG>{t6hasJE7?v7&sYkw1fGEU~8LlE>IRZ?L;;tm27(#<>5 zrvtoN!3nCX*#N8MJP$B-;cS~l>-z8`fQ$GoTia$K-I|U@I;YkE`BKeN>2#}caC2xA zXrwc=D%49AO94);j+2``n}Eyy(&Lod1kqaGR?#KGESd!G4EE7B(KsQDQaJ0l)|s3U zR=U!O!Q44~(gf!gM2R%PmfMIw#RKyV1_W~GhGWGstPxerknnaH>@5A^fN7^TMGL}7 z%7mIuJay0`nCe6J3Gj{${!fQLJ6OS|QE}x#;-Cn4?$KyBc_i?n176>Fdk5m9x5*HN zVe#2GrudA6o5lCcb?x@@ZubtE%P8d5R&ZFwMLb?^{=NhU)OK*()=}FLr)u)Ec$fQ= zc-PmKIZURp`N9Yz$swqbvUiXrog7dV)v%a3LOPd~!t{RK?=A>sIROkUM2w6$)V#9i zRK~}(?I=j!Q4rTw8P93x`|*bD)xMvH8>y5Ep{_i|6 zn;_V@6e^V&E9yaOgT~(@Ycm)0n8fQYGGV z^|;Loc560%F(+cc;T#9+5JP3b11elS_kX@0YY!u3#(3Vm;IaSmAl4pE;>5(}Y;eNb zz)9))$<7u0`KM?$Vg%q#{c3CtWmVw+wsYo(ZrhtPkL$bcKfY;uagj36|x?CY-oeATwUZ?S<_NkX<;2+{@g@OjcjH)7d|&;+eDF@uIB z(Ud})99jkNqlHb*9I+EdBmJD!SI&tYcf4zOv38^gbW=9e(P7Nk2ZlX+vrGM*Mpn~2 zaBpOa6qbr*ktF~T*48XHu_J57IoS82Wp{_dC+|Nlhc1sid0!}cuU__XNU0g;MuO-{ zH}B$rpp)KSn4lmG*34TQ)PJ646>>5Fx0HvNp=Y*k665GW_(X zV_PY0#xU}o#(&~{qI<7;BR=NeZ30VzQA<2Ud@hR2gpL~H$6$n$p_K8al#-IgsGf_76bsKv9a<+AjP<% z0w@IvJ$L!RRv88cfK(gfWE{+b3qA{tLI$!>paM+oh57Y)I2L}P4RU1j=NeBWMVHPe z=I?@u#xSC>rIj)r_p2{>RnG^60oefp5L};u$8vlBT*w#O;kj46b5N~~UQU#z3onz} zNCP{P%)z_)u-!k|r zdn*_&F& zJ)BPLFAyk54LUU7Iz;Nut!(0j;#{VqUHtr_P(e~xZkdMXe{w(aNZfuOzZjI#mxHI= zq(n-QZHFZ2bg)m>bLP|0R&0|aBPWn>lf5gyCfE^%#o&`j_=KbwSOOitHbz%Q>c1}N z*AIF^hd!F5`wp-?-ygblj`XMkcWzn3kQ`MazpkhNkw9+0Bba#|qP~jAQD?V~abTC~ zl{@O^$x&bzTD=Lr!)-KeKxsAx6MYfb46hF)wcohV50MPNI8B2b0^KGxQ% zqR!0pETt3d=eDc%qGUp+=_CX=$%DqR4Ld<#He4OZg%={QjuXM^N7@l#YoG{zDiDOB zq)()2P^&r`L~S8(5(W#S2AO=M5w~4Xt2GzIns68yi-{zE8Y4v=4Jx&!L8J+Rk+A3} z(r#1bHQZ#lAQlrb7>I$DcL`ix6_`R`i9SZ)*&npe7)AAElRBEHtN%Y*lSAtE;{rrc-l1jc(4IO`y%uc_Lso#NT^a(q8zqv=PbeuML zCR4Txm1@UFlXNZ!QrM<9O-?=^Mn33>LGR3b*X=A$zJ4V&JH9S7IiIFwkdDEk2*(d) zVqJ1*G8P(h!u(0C>{drrTot1V9JX!ge~CZhIWtnM?G8q&u6t1nM-{d~Fc>zSM3&2( zv(m&YEG_UScCt+k7kG~lei-?nFzkIY_!!eTCJ3-NjAAR>{%tFSG~=AM!!p4RNC7YJ zF4bhm52QP{54;#{>FzPl9q`4FF%bz-UVMaej2Axv76Hdb)ee|vjI{$VS;4x6@P*}2 zKdU+h5uIn}+vT-w{SzY-q_EWZRI&ub@d?bx2f9P%1#80s=lBSX@`)y3{NnalT7(+} zDw$iuaMU?{wDTn9^DvsSC)FOnV}x+iG3wE=v5TfTzc@NTa!iv|**T$8^w-t*uZ)bMVfbBpKsB)S{P*o150qnFLl_C&gI?Lv( z?Eo+hWo^|pXH+Rw(ecI90azRy2Add+8mrcwQ8QK17~j|iqg+aOEeN+4tD_)dNM1J_ z66O*vk6m}-y2Mp;Bj4tecr8n32>Xk4KnMk-RxWWCo)R(PoWsXxX7DdEi{HE>uB0Xp z6xtRge{qGK4J(g0eC?{y)ftobJp~=)DA;~_EjZ1~D`-Re#>5nnBo^#|kONM~{^C-Q z>sg}#wBQrD+nsx7-vg=s@QY3aP4WKIW1c0z(30Tl%HNOPGnpOe#~v zqH^U$aTl|`y;7EQ)eXX@O+(`zS%70@41Ne67Dq4mSm+bP<=_%y?fbOr;>_y4r~=pO zJMgb|XR(@7$&AzOC^)=?$Mse+BI{X6NkNW;v?L~rxv|*_sY-Cn;&=Tx);Q3(FxJ_n zYlH}T)=wUzb5l;03wfj*F(%1nciun~2Ul>90wPCFU`8-%LY!!P+P<%%kg|i)OOY$R5-#D!1xG(>yqf5{F$$)O_57x$;)UTGuiCp$%&XCsP9p- z%WtIVAcKuziJ8JXLwR8W(j3wpbeTflHA%U8td^^*MTTI*bza`_UZ~x*?w8ZD#CP(` zvzt#63Zit|qQ!n8F-r;n8mX+U5;=Z(++NxjywUXYA#lwgO^2K8msc;QSG^T6U`>!F z5Nh!ohh{siM!tB}!YN@R`YZ&#I}W0*mQaq@vrXfzIxzdHH3uK8>-=C3X@mNecmCL1K7?D2eu^L(B zm#3>?=`ef(3Kzx1pkbnT!M{cpiUf7ta1gJK4Xh7Xs}b)R3!nPnI~N*Z$p94WHXUiw z_N6&1wJ5%>Fl-DayBpXBR|H1Fi44AD12i}4+er<9E$}Y_f1``AGsn0=s`c6sw2;gA zp>thw>Fz_@hnMb28b+_(K&_$l4gdSEOsm~uT!cQhoqs8}s!@vd^~T!Q0G+peOc3O| z{Czo2*>!Z|t7~<#j(ZPL4?FIHB-70J&)s|XHM#8j?8hH>ZhevvL|SY`$q76n?POSM zQBz@9TUvam!Wd%XKVo51Q6HLi5`iy0fvHGi`O`wVcx9HKxiszjY_D)!gq=Cs#H&XN zPcaT*VPgo{L|{8y5eRFOIk^4Cc~gC0^ZY?wlTnTXYq+u z%vx-HN}18Ds#;N2^{omdFgM>?-^QmSAK0+dAGnUIR0jgPaprA>1D5Jw-=piHzlZqL zjKBrDy!Ow#Zm+c8RgLE9^0H3G@>WQx!E#_F#>Vo-01O2*KXX{^fD;*559#%iA0v&nx@mbfn@0_pP9 z6O&U6@EOCzDPSX3F6B^|V$gDZ9My-t+ya;-UZdOgz5NE(&YFN?ww<8Mk9m>cre;clquuWujWTih+@li$#PrJ z{o;+=ZONdSmpcr-By>#1;Qh|~lEp012QG+Gwzoc=&O%-%S%eRRoQ z&H%==I>REL+KZjJK0U!f*@7T-<%+=oXcrw$PZ4G@Q|wb4ZC1Wl0HIVjDLCvTc+v+? zwC#25$vKRhsmti)5lpR#R)MkR%aFWj2=#Et1PA4?)RkA+%!^M7A_T$wqA-eW1Bb>&`%A#@TkEQOs$_u>sq(DL-Op($#d10);93x21=Inq~(=`Tc zz;+4^*NvDv5351PnN>I8gXNQ%mCWMO*&C^?VK@i#QP-NL*PWJsOEShot_|^ zybiX=Wk9XP)PG&MbNi<&<%{sd6dn)31m=Y4VA?i@OZJ{ve1K_L@bKFxI=&PY4*^yd zY>0~JOFj2xhuojy-azjG)~JAd0y^jVW}+)b?@~FAi2wTCG(u&|f7E9M??x@#|3sSu zn`N>OfHzA=$dG$4Z-p3U5E4{ zX%6$o4oJmkC}b0`wf2cE{>8{KQ57(lRIxAeq3tdEl{gn=*g@S>DE3g3fiNdFL%8KA z!HqCuH$rY+D9epDE$j`?d0iFNCd>;RW4*4t6;P{vwQnUX`^fi3v$#^zbi4{S5CvXZ zAQLLD4b?^s-&J9&kre!*Gygjy{8@TU392_uiVfzJVBYEhi=C(oDVWi|gc<6YJ)P-H zXFAiFP8gp{Jb13W(>q6_UcSKCSB{~!0)E!VG;scPZ&Q4lpGU~4p9TFtDNkRkNL3PZS89p5s%m21Rz301dOQT7;#PfDdipG=+VEUH=~1hb^pzbzO)=*j zir;_%w(~L5>vdBfmyvn2h`pHvny^2lpIeahTJJi~Lcl^Fv9&xBn}2I0vHQsury{=q zV{>*M+cBgBZ>zluY6?>hW=SxMhFN6HVqseJ2#xG?WGM$l*;KtD>Y|a@iLuLxM&}o% zHU(N@86<;LB+1uCoVW*?Xu_=n%g7Y6=@E?uD-ZG|?7z#8@ zMmgW;tTEUp4ZXFujA_$mZW!QmFe9pmqGl!h2~Pf*d|pXb6=My%q9Q$uDtdits+R2# zmDH$2W1M_0mcy=eRGIl;Uku4_CMpO_6Ty6morW z!N4g8vs9S3TMsphwQxFK*p^mC1`*8Z?8EGG26RkO6df!K(aE6fdJig9IPhg*A`H1R z{Y0ss_lG7!`t6sfP{2@WopX{h(~YeQDF-Dx=Cs?Y3~g)h=n7xt8qtHcI;Ejzm?FKu z_?!K06SeR`a4_sn5wH}>NObtjY@5UOX4)d_OJULw0WLuVIuS%5nJFq>C2w6<;JU8( zb={t&t~N{CU&+v4QZQSmnOl66hOBJP#iQhlFH%f!%fH*DKxkJ!^re8P`LVy(q3(AB zIaA+sm0YNlTMfCcx=@QnTryP;zZxu2 zEHt?puo%aHz+|yCfCnDg1=>n{x?aC z1Y;Hatd2OF;`Fzfq9oO^Ng3Q(30_dqUeM4mO7Vi@66m1fP-BMp0`#_QsanW$!HnG4 zq=?TNZUj{{taC8b(dT%U1{;DJ3`jYNWFAyq*lXcIlL7YymxP;Y+5RBcitMZj+9yKd zYk77}r8&;J+++cN5HK5Psy!7IqqgRYu9o%I;?-LVJEzr%2$IiS_XkYk@FZ@mo81GX zxnNdZm$4(A*0LsoKCi0xZf>}+^>F=|@YDisODlH|^4hrTJD@4M4hu{!U6TaL0uNK`Y9AWG;4sGAfPX>RpY`<42d-ONv zNL`{+R8vmXh7BJLRZJQaDJ{QhCUDCg%o~pdhP0`NBIf1c$Db90nLW|A{B;leAc!OD z)y}2JpJlgk*zY7}?{bDS$9hQ+t`nk2aW&;=7Z1G1?66C6KXUP?vc`dB;i2XxWLCY3QVvC+`O-A1SF=L9of`KP zvN$0{`v&GGg8QD&EgE7Q+K48!y|cj>1{!zFUUFiNj?dU>+BU}P>@e(iGH@e9K&>Gp ziV#BtTF5TszaLw=cxP7r6)Ft@_ifu4QLllc+cpMvc}$l#rn(D@y-$zWAp`aV0sl-8 zvMXVH!#H-^#+Z|zfs`QZEJMf4vCJEF##3@gy)wN#Yor}s>gKj~4rVxE6FKHJj08W6 zou>kN)32sfTVTNm&h*n7`Z?>kmylBRbGNj@Ad9|Rm*Z29!E>`F>9ErQqhTH2*}I$X zv;GXM1=!*>V*ipI2afp*n@BMv_qqIaFIZAp7c#W1m8%B+E(eLqE>-nun{%3$iUiAH zyraZ{B6d(0qj0%)0q2L0%umfOAAp9gne$u^sO>9$K@sdtN7Y!7(z$j`k4tYuPkCHAz&}jGB&c zJy-rFyMCCrn0_&QynDg0*J-XJ<#zrdy>%hIZ7n=WUH*!;H7|R#t;vM1s#Bg)zj4c> zIlI%`**knn-)yaxl3X9}i*`(w`0~=l1(}47YhN8DZ26eIDOWmN-e^?j;7BD1x}1VG zU@-0QQTSADg23E&4th(TC;c;X5*4=|J=CIDPF%>cr~2p z$o{FNsv(C6`z@`eRabRRPOF}(I!{e^o+@Di3Z6)p*8A=?(ZPIiJ7}P{e{5`T(b5}f z@M5q<>6(Ci7PfzE?uP&to%W@1Mf@N8)Jquw>i6XFr-Rz zNtrCzx{%V5M0ShK(6(0oAi+Nn)GGnbox?Z7VV0G{Oool^8hpFb!}BQuR4s8+OI^W5 zwF9|er&y4En1Iy~u+c6NkZB0GW4lDu%bazhxHeJtI{P!>k&j|aG@0}>m8oY8G9<1wi^y~Ze{ra>% ztxxOI`e)Op^~SWbb?F+Jo|^S*_BO&6ys&c%YI|Dq{MQh_$*)_slS zaD#p4TP%NUA>(83!7@Emcf2fokX$#98@sDf?U*ddPnTTE9R9mwvj7hznE#7Tr)FSp%ikDwC*}&B=lDthu!qc zJDOFQBaU6eJA>h1Bf@88@{N_c`Q=%>0N2;I?S_K7 zO;`PBp6K?#y6OCQ;@(o3`Zn*C7oWO&$L*d!=hPaWm>Ll|@8q*0Z(7AKE{5&Y;K#pK zjRf!P31IKiQs$IU-4HSLzK`ZGa+3TQxF_@y1Oo;P7={zz-z}W-c(g>HF-#$U^-xd` zC556wS)s5{S|~16t}o{7u+cr-x^Lktve&B|uNo4MDLAn}w~IIw^ENA3piCsOX-^W^ zS&Ewu41d$fNbDkI-0@*|a9gLM|{{e!(989N2KBtbre#Fkf*z}}bQ zrlTMHO=rqrSD%{hv{IU$L>!jW5M@&vxaK%e4BpnOI`Vrpb-A|sWZ~f1UQ8S)GgTh` zTsy{}P_2gQHjvVl(vi}U5~f5^Dk;g7SW3c_Hqns+5=WOKgt$u8@71VR*55ChNVB>~ zB(?&0V!Hu$=-PA|2D?mby6uqC^q9k8+4e-)$M*qzM+$f-K^39OKw+V@R57Z&^Z@IC z*q&!Zn{syYgB>?LfLn0O~CVaczhq)2_JS#sDzXQ$JMP-(?E zI9trVw!Rdk?M-$U0`9m7(=ijXF$Z$DUH*(DRwS3F)7c7k%U21mMw*qf7p_4)uEll1 z`Z-S*YUJg0xq-vYXy%YXvo{F-{hH*X1-SIIWHe?l*vp;uV2Q$*do|`M`Q2phiJo}Z z`_O(Mn)eU4{R_D59>62C1147hd6{yFB0n4nxefZkCWmy<){S70d4m_g3qD(5*iANQ zkEzr{d+-?b%m8?7{=6^{cmyrRAPmMAH_S^H-mipc73zZ5jtX<3@FDpB#w}uSvgF-F zE8Ab$O}BDXs8pp|jaqf;HE7hNS&LR}+I8sk$6x<+>DGg;SD$_Z1`QcDV$_&%6DCcW zzGoiD%WUsI8$v$20;EC?PSk0*ei510>CwpfUY~6`03qbFD?lpb;6xqUdm<6?*%cra za&Rt~rydW%U~>pm1S@_L;^*n>p`G={5&p)L$`MS;%g;qSh zIn82fb8{Y=*qhXJH0<`-6P}Kh_d~HYnE0p9DwkSXDq}ND`}c={QZ)+BZI3PB$@*I% zpulrpCC1%;Z5xA&fvd~S6)O%+yaY*-rTFTb@7>@>g&_K`mFYl$r*vSb1^0i)jp>p) z;vtfDk0v))%84dU$KHelruG~&*`&EjoaIW8gZ$bQ8FG{poOa}< zVl6L_+cRas^LB4qOZ86S>Wd$L!eAL=qtlIp)-@hl%J`@>6QIwT2z}DT=vtGYFHWSf z6PY%XS5i~Bq$$g#sXS_;7IOyByp`BWe&E5linI41Np?7ncG0;c1QSd!!2}abFu?>9OfbO&6HG9{1QSd!!2}ab zFu?1r67Ea)A!6?A5>?ps{;PQUuZ6wl2EHl)C^_U*Kr{HFS@Z!uY#VR8c_f?R52+P- z@L@HuE@fkwO_|V6FfEO*rTsMYPiukE%NkB!JDB_ghcy~-*Dd%hA~+(oAwcD?E?V;g zFaI>2$5P#&j9V_p&O>Gi^&izqNOOvJapys|lgrC50oL%oPKmn~>eo9$*UUHu+ww4r zH+eT$v-v^T81tnGFabf~Gv~-)?&-b)9G1WDCK`kN^&VU)>(9$>_n<3RVn4D z{E%@AV_E*xCoNBg?Ei_3s%i8728N9?cR+lf{b@o?;_nt(2mY;EV7Yk>er<8s!0xqm+UkfVQwvE!W)862@{LG#3 zSX1Kwk6A|VO&en}bqzi%+86$D!0zW#6L{a$NP8pg?q-uW@2nl$&)-VRcEFRZXHVcL z`!eN}cKoeQ^TsDHFno_R%Lg<5HSd*AwY}dT^fb~@Sv;D0JDFL0Wt#ap)TS?wGoD$r z+F-C3Y7*Z1uG_R{4)>nT+XPP=gE^F2P_~xi0bz{RHNnJWda815^lXb^CT(2lOeBxcN;nE1Qj9cx zWt~_n-RzHg=_d~~TdEcI{uOD{9hho9Id1eRi{1SK`2HDuNnf^l&L4xHz&!SRY|d_* zVZOsT(G_-}A(#G#gfzRc<WK<<%~Y$kJu|AF zs#+7tT>(z^y*7r$?}OjF&Gu(8qpI=q*>6B96mPaoVRJ=IpFr1g9lPb`fFA#{oTHHH zC8g+pcTGgZe1~E2j-aUM1UBmT4zvyIY)MGtbGN1>KvC&BYW{Y+yabH~;oXuaUbyqO zJyv^y7vA{bXa6p3vf7UQrzdo9ZCdU?E~B+cV<@#7p~}TqJA+{xj^ke=QM>KtWf^KS zd0^g@c@lT@VHKqJK0Bb@?Kw<7Ge?5IGqNedS)B&913h$vd4KZc*GBJ|wf=Xjg`cdg zQtx}t!e*CU>x$|UzSOfi`OlC4b^7Fq&iwx@b94Gy$mHLzRzLlCYaF3(FGX>AN5SUl zD}D!`S$qDETmM)8{PEcj`o7ULf{BZ2=7W~8DRZmQILF;f`pNvy#<%eOPG0vnya|TL z{uQ@6*OTpkx4t$)mdG_Qq=o)}U;*Z`OFwH%^!CDF^<-v%`wYN78Ug-s zwP}JFQ9l55{`S2#+Qr`4rg|r|(wrR6CM&Tj-Z>Pr*e;PXIY9m%fanhA#&a@7M;EV% z)7la6z&AxJ59_F8g%~ZcfNHqAf>SLU-pJ`zky+8NCZ))Wvtc`H5CF+CE)y5Jlf?MY zXAFW(3BqOwi78d}POq^zZ`4~@mhap+R}Axa`v;Wz?Al>23Gm(d;LEdl=VldpFD6=^ zFckSii=meI=$Zl(=ESZIs>3Y;g6V&yaLuuBu)Mr;lL zO#nL|23I!qB3?430rr<>4m25badok(N-Qe45bHztAs`%R8fa8}HycZ-$|>E`?FGS! z8&)481)Zj-(`jfK!IdHv1b`R?yO}fQvmR1&XD?uMQx7i)wyY1zL9tP*FvEMBt0fbD zvWCur*h|eH4pq^oH9hW3N10?(6|5Oh%WJVa1R!ecNh`){mb3kxjY+RK#3of+oES{? z&Nm^6Z(?jq)fT7j^IwgH5Kj$N69Y)Tb9?3VA|vzD

&nKZ;W-wL**tscvKm91#T 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

7zENjRe8z~V{^&7qA{GK;6;+kmYgS&0U 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.caption,h2,h3,p{orphans:3;widows:3}.rst-content .toctree-wrapper>p.caption,h2,h3{page-break-after:avoid}}.btn,.fa:before,.icon:before,.rst-content .admonition-title:before,.rst-content .code-block-caption .headerlink:before,.rst-content code.download span:first-child:before,.rst-content dl dt .headerlink:before,.rst-content h1 .headerlink:before,.rst-content h2 .headerlink:before,.rst-content h3 .headerlink:before,.rst-content h4 .headerlink:before,.rst-content h5 .headerlink:before,.rst-content h6 .headerlink:before,.rst-content p.caption .headerlink:before,.rst-content table>caption .headerlink:before,.rst-content tt.download span:first-child:before,.wy-alert,.wy-dropdown .caret:before,.wy-inline-validate.wy-inline-validate-danger .wy-input-context:before,.wy-inline-validate.wy-inline-validate-info .wy-input-context:before,.wy-inline-validate.wy-inline-validate-success .wy-input-context:before,.wy-inline-validate.wy-inline-validate-warning .wy-input-context:before,.wy-menu-vertical li.current>a,.wy-menu-vertical li.current>a span.toctree-expand:before,.wy-menu-vertical li.on a,.wy-menu-vertical li.on a span.toctree-expand:before,.wy-menu-vertical li span.toctree-expand:before,.wy-nav-top a,.wy-side-nav-search .brand>a,input[type=color],input[type=date],input[type=datetime-local],input[type=datetime],input[type=email],input[type=month],input[type=number],input[type=password],input[type=search],input[type=tel],input[type=text],input[type=time],input[type=url],input[type=week],select,textarea{-webkit-font-smoothing:antialiased}.clearfix{*zoom:1}.clearfix:after,.clearfix:before{display:table;content:""}.clearfix:after{clear:both}/*! + * Font Awesome 4.7.0 by @davegandy - http://fontawesome.io - @fontawesome + * License - http://fontawesome.io/license (Font: SIL OFL 1.1, CSS: MIT License) + */@font-face{font-family:FontAwesome;src:url(fonts/fontawesome-webfont.eot);src:url(fonts/fontawesome-webfont.eot?#iefix&v=4.7.0) format("embedded-opentype"),url(fonts/fontawesome-webfont.woff2) format("woff2"),url(fonts/fontawesome-webfont.woff) format("woff"),url(fonts/fontawesome-webfont.ttf) format("truetype"),url(fonts/fontawesome-webfont.svg#fontawesomeregular) format("svg");font-weight:400;font-style:normal}.fa,.icon,.rst-content .admonition-title,.rst-content .code-block-caption .headerlink,.rst-content code.download span:first-child,.rst-content dl dt .headerlink,.rst-content h1 .headerlink,.rst-content h2 .headerlink,.rst-content h3 .headerlink,.rst-content h4 .headerlink,.rst-content h5 .headerlink,.rst-content h6 .headerlink,.rst-content p.caption .headerlink,.rst-content table>caption .headerlink,.rst-content tt.download span:first-child,.wy-menu-vertical li.current>a span.toctree-expand,.wy-menu-vertical li.on a span.toctree-expand,.wy-menu-vertical li span.toctree-expand{display:inline-block;font:normal normal normal 14px/1 FontAwesome;font-size:inherit;text-rendering:auto;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.fa-lg{font-size:1.3333333333em;line-height:.75em;vertical-align:-15%}.fa-2x{font-size:2em}.fa-3x{font-size:3em}.fa-4x{font-size:4em}.fa-5x{font-size:5em}.fa-fw{width:1.2857142857em;text-align:center}.fa-ul{padding-left:0;margin-left:2.1428571429em;list-style-type:none}.fa-ul>li{position:relative}.fa-li{position:absolute;left:-2.1428571429em;width:2.1428571429em;top:.1428571429em;text-align:center}.fa-li.fa-lg{left:-1.8571428571em}.fa-border{padding:.2em .25em .15em;border:.08em solid #eee;border-radius:.1em}.fa-pull-left{float:left}.fa-pull-right{float:right}.fa-pull-left.icon,.fa.fa-pull-left,.rst-content .code-block-caption .fa-pull-left.headerlink,.rst-content .fa-pull-left.admonition-title,.rst-content code.download span.fa-pull-left:first-child,.rst-content dl dt .fa-pull-left.headerlink,.rst-content h1 .fa-pull-left.headerlink,.rst-content h2 .fa-pull-left.headerlink,.rst-content h3 .fa-pull-left.headerlink,.rst-content h4 .fa-pull-left.headerlink,.rst-content h5 .fa-pull-left.headerlink,.rst-content h6 .fa-pull-left.headerlink,.rst-content p.caption .fa-pull-left.headerlink,.rst-content table>caption .fa-pull-left.headerlink,.rst-content tt.download span.fa-pull-left:first-child,.wy-menu-vertical li.current>a span.fa-pull-left.toctree-expand,.wy-menu-vertical li.on a span.fa-pull-left.toctree-expand,.wy-menu-vertical li span.fa-pull-left.toctree-expand{margin-right:.3em}.fa-pull-right.icon,.fa.fa-pull-right,.rst-content .code-block-caption .fa-pull-right.headerlink,.rst-content .fa-pull-right.admonition-title,.rst-content code.download span.fa-pull-right:first-child,.rst-content dl dt .fa-pull-right.headerlink,.rst-content h1 .fa-pull-right.headerlink,.rst-content h2 .fa-pull-right.headerlink,.rst-content h3 .fa-pull-right.headerlink,.rst-content h4 .fa-pull-right.headerlink,.rst-content h5 .fa-pull-right.headerlink,.rst-content h6 .fa-pull-right.headerlink,.rst-content p.caption .fa-pull-right.headerlink,.rst-content table>caption .fa-pull-right.headerlink,.rst-content tt.download span.fa-pull-right:first-child,.wy-menu-vertical li.current>a span.fa-pull-right.toctree-expand,.wy-menu-vertical li.on a span.fa-pull-right.toctree-expand,.wy-menu-vertical li span.fa-pull-right.toctree-expand{margin-left:.3em}.pull-right{float:right}.pull-left{float:left}.fa.pull-left,.pull-left.icon,.rst-content .code-block-caption .pull-left.headerlink,.rst-content .pull-left.admonition-title,.rst-content code.download span.pull-left:first-child,.rst-content dl dt .pull-left.headerlink,.rst-content h1 .pull-left.headerlink,.rst-content h2 .pull-left.headerlink,.rst-content h3 .pull-left.headerlink,.rst-content h4 .pull-left.headerlink,.rst-content h5 .pull-left.headerlink,.rst-content h6 .pull-left.headerlink,.rst-content p.caption .pull-left.headerlink,.rst-content table>caption .pull-left.headerlink,.rst-content tt.download span.pull-left:first-child,.wy-menu-vertical li.current>a span.pull-left.toctree-expand,.wy-menu-vertical li.on a span.pull-left.toctree-expand,.wy-menu-vertical li span.pull-left.toctree-expand{margin-right:.3em}.fa.pull-right,.pull-right.icon,.rst-content .code-block-caption .pull-right.headerlink,.rst-content .pull-right.admonition-title,.rst-content code.download span.pull-right:first-child,.rst-content dl dt .pull-right.headerlink,.rst-content h1 .pull-right.headerlink,.rst-content h2 .pull-right.headerlink,.rst-content h3 .pull-right.headerlink,.rst-content h4 .pull-right.headerlink,.rst-content h5 .pull-right.headerlink,.rst-content h6 .pull-right.headerlink,.rst-content p.caption .pull-right.headerlink,.rst-content table>caption .pull-right.headerlink,.rst-content tt.download span.pull-right:first-child,.wy-menu-vertical li.current>a span.pull-right.toctree-expand,.wy-menu-vertical li.on a span.pull-right.toctree-expand,.wy-menu-vertical li span.pull-right.toctree-expand{margin-left:.3em}.fa-spin{-webkit-animation:fa-spin 2s linear infinite;animation:fa-spin 2s linear infinite}.fa-pulse{-webkit-animation:fa-spin 1s steps(8) infinite;animation:fa-spin 1s steps(8) infinite}@-webkit-keyframes fa-spin{0%{-webkit-transform:rotate(0deg);transform:rotate(0deg)}to{-webkit-transform:rotate(359deg);transform:rotate(359deg)}}@keyframes fa-spin{0%{-webkit-transform:rotate(0deg);transform:rotate(0deg)}to{-webkit-transform:rotate(359deg);transform:rotate(359deg)}}.fa-rotate-90{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=1)";-webkit-transform:rotate(90deg);-ms-transform:rotate(90deg);transform:rotate(90deg)}.fa-rotate-180{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=2)";-webkit-transform:rotate(180deg);-ms-transform:rotate(180deg);transform:rotate(180deg)}.fa-rotate-270{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=3)";-webkit-transform:rotate(270deg);-ms-transform:rotate(270deg);transform:rotate(270deg)}.fa-flip-horizontal{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=0, mirror=1)";-webkit-transform:scaleX(-1);-ms-transform:scaleX(-1);transform:scaleX(-1)}.fa-flip-vertical{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=2, mirror=1)";-webkit-transform:scaleY(-1);-ms-transform:scaleY(-1);transform:scaleY(-1)}:root .fa-flip-horizontal,:root .fa-flip-vertical,:root .fa-rotate-90,:root .fa-rotate-180,:root .fa-rotate-270{filter:none}.fa-stack{position:relative;display:inline-block;width:2em;height:2em;line-height:2em;vertical-align:middle}.fa-stack-1x,.fa-stack-2x{position:absolute;left:0;width:100%;text-align:center}.fa-stack-1x{line-height:inherit}.fa-stack-2x{font-size:2em}.fa-inverse{color:#fff}.fa-glass:before{content:""}.fa-music:before{content:""}.fa-search:before,.icon-search:before{content:""}.fa-envelope-o:before{content:""}.fa-heart:before{content:""}.fa-star:before{content:""}.fa-star-o:before{content:""}.fa-user:before{content:""}.fa-film:before{content:""}.fa-th-large:before{content:""}.fa-th:before{content:""}.fa-th-list:before{content:""}.fa-check:before{content:""}.fa-close:before,.fa-remove:before,.fa-times:before{content:""}.fa-search-plus:before{content:""}.fa-search-minus:before{content:""}.fa-power-off:before{content:""}.fa-signal:before{content:""}.fa-cog:before,.fa-gear:before{content:""}.fa-trash-o:before{content:""}.fa-home:before,.icon-home:before{content:""}.fa-file-o:before{content:""}.fa-clock-o:before{content:""}.fa-road:before{content:""}.fa-download:before,.rst-content code.download span:first-child:before,.rst-content tt.download span:first-child:before{content:""}.fa-arrow-circle-o-down:before{content:""}.fa-arrow-circle-o-up:before{content:""}.fa-inbox:before{content:""}.fa-play-circle-o:before{content:""}.fa-repeat:before,.fa-rotate-right:before{content:""}.fa-refresh:before{content:""}.fa-list-alt:before{content:""}.fa-lock:before{content:""}.fa-flag:before{content:""}.fa-headphones:before{content:""}.fa-volume-off:before{content:""}.fa-volume-down:before{content:""}.fa-volume-up:before{content:""}.fa-qrcode:before{content:""}.fa-barcode:before{content:""}.fa-tag:before{content:""}.fa-tags:before{content:""}.fa-book:before,.icon-book:before{content:""}.fa-bookmark:before{content:""}.fa-print:before{content:""}.fa-camera:before{content:""}.fa-font:before{content:""}.fa-bold:before{content:""}.fa-italic:before{content:""}.fa-text-height:before{content:""}.fa-text-width:before{content:""}.fa-align-left:before{content:""}.fa-align-center:before{content:""}.fa-align-right:before{content:""}.fa-align-justify:before{content:""}.fa-list:before{content:""}.fa-dedent:before,.fa-outdent:before{content:""}.fa-indent:before{content:""}.fa-video-camera:before{content:""}.fa-image:before,.fa-photo:before,.fa-picture-o:before{content:""}.fa-pencil:before{content:""}.fa-map-marker:before{content:""}.fa-adjust:before{content:""}.fa-tint:before{content:""}.fa-edit:before,.fa-pencil-square-o:before{content:""}.fa-share-square-o:before{content:""}.fa-check-square-o:before{content:""}.fa-arrows:before{content:""}.fa-step-backward:before{content:""}.fa-fast-backward:before{content:""}.fa-backward:before{content:""}.fa-play:before{content:""}.fa-pause:before{content:""}.fa-stop:before{content:""}.fa-forward:before{content:""}.fa-fast-forward:before{content:""}.fa-step-forward:before{content:""}.fa-eject:before{content:""}.fa-chevron-left:before{content:""}.fa-chevron-right:before{content:""}.fa-plus-circle:before{content:""}.fa-minus-circle:before{content:""}.fa-times-circle:before,.wy-inline-validate.wy-inline-validate-danger .wy-input-context:before{content:""}.fa-check-circle:before,.wy-inline-validate.wy-inline-validate-success .wy-input-context:before{content:""}.fa-question-circle:before{content:""}.fa-info-circle:before{content:""}.fa-crosshairs:before{content:""}.fa-times-circle-o:before{content:""}.fa-check-circle-o:before{content:""}.fa-ban:before{content:""}.fa-arrow-left:before{content:""}.fa-arrow-right:before{content:""}.fa-arrow-up:before{content:""}.fa-arrow-down:before{content:""}.fa-mail-forward:before,.fa-share:before{content:""}.fa-expand:before{content:""}.fa-compress:before{content:""}.fa-plus:before{content:""}.fa-minus:before{content:""}.fa-asterisk:before{content:""}.fa-exclamation-circle:before,.wy-inline-validate.wy-inline-validate-info .wy-input-context:before,.wy-inline-validate.wy-inline-validate-warning .wy-input-context:before{content:""}.fa-gift:before{content:""}.fa-leaf:before{content:""}.fa-fire:before,.icon-fire:before,.rst-content .admonition-title:before,.rst-content .danger .admonition-title:before,.rst-content .error .admonition-title:before,.rst-content .hint .admonition-title:before,.rst-content .important .admonition-title:before,.rst-content .note .admonition-title:before,.rst-content .seealso .admonition-title:before,.rst-content .tip .admonition-title:before{content:""}.fa-eye:before{content:""}.fa-eye-slash:before{content:""}.fa-exclamation-triangle:before,.fa-warning:before{content:""}.fa-plane:before{content:""}.fa-calendar:before{content:""}.fa-random:before{content:""}.fa-comment:before{content:""}.fa-magnet:before{content:""}.fa-chevron-up:before{content:""}.fa-chevron-down:before{content:""}.fa-retweet:before{content:""}.fa-shopping-cart:before{content:""}.fa-folder:before{content:""}.fa-folder-open:before{content:""}.fa-arrows-v:before{content:""}.fa-arrows-h:before{content:""}.fa-bar-chart-o:before,.fa-bar-chart:before{content:""}.fa-twitter-square:before{content:""}.fa-facebook-square:before{content:""}.fa-camera-retro:before{content:""}.fa-key:before{content:""}.fa-cogs:before,.fa-gears:before{content:""}.fa-comments:before{content:""}.fa-thumbs-o-up:before{content:""}.fa-thumbs-o-down:before{content:""}.fa-star-half:before{content:""}.fa-heart-o:before{content:""}.fa-sign-out:before{content:""}.fa-linkedin-square:before{content:""}.fa-thumb-tack:before{content:""}.fa-external-link:before{content:""}.fa-sign-in:before{content:""}.fa-trophy:before{content:""}.fa-github-square:before{content:""}.fa-upload:before{content:""}.fa-lemon-o:before{content:""}.fa-phone:before{content:""}.fa-square-o:before{content:""}.fa-bookmark-o:before{content:""}.fa-phone-square:before{content:""}.fa-twitter:before{content:""}.fa-facebook-f:before,.fa-facebook:before{content:""}.fa-github:before,.icon-github:before{content:""}.fa-unlock:before{content:""}.fa-credit-card:before{content:""}.fa-feed:before,.fa-rss:before{content:""}.fa-hdd-o:before{content:""}.fa-bullhorn:before{content:""}.fa-bell:before{content:""}.fa-certificate:before{content:""}.fa-hand-o-right:before{content:""}.fa-hand-o-left:before{content:""}.fa-hand-o-up:before{content:""}.fa-hand-o-down:before{content:""}.fa-arrow-circle-left:before,.icon-circle-arrow-left:before{content:""}.fa-arrow-circle-right:before,.icon-circle-arrow-right:before{content:""}.fa-arrow-circle-up:before{content:""}.fa-arrow-circle-down:before{content:""}.fa-globe:before{content:""}.fa-wrench:before{content:""}.fa-tasks:before{content:""}.fa-filter:before{content:""}.fa-briefcase:before{content:""}.fa-arrows-alt:before{content:""}.fa-group:before,.fa-users:before{content:""}.fa-chain:before,.fa-link:before,.icon-link:before{content:""}.fa-cloud:before{content:""}.fa-flask:before{content:""}.fa-cut:before,.fa-scissors:before{content:""}.fa-copy:before,.fa-files-o:before{content:""}.fa-paperclip:before{content:""}.fa-floppy-o:before,.fa-save:before{content:""}.fa-square:before{content:""}.fa-bars:before,.fa-navicon:before,.fa-reorder:before{content:""}.fa-list-ul:before{content:""}.fa-list-ol:before{content:""}.fa-strikethrough:before{content:""}.fa-underline:before{content:""}.fa-table:before{content:""}.fa-magic:before{content:""}.fa-truck:before{content:""}.fa-pinterest:before{content:""}.fa-pinterest-square:before{content:""}.fa-google-plus-square:before{content:""}.fa-google-plus:before{content:""}.fa-money:before{content:""}.fa-caret-down:before,.icon-caret-down:before,.wy-dropdown .caret:before{content:""}.fa-caret-up:before{content:""}.fa-caret-left:before{content:""}.fa-caret-right:before{content:""}.fa-columns:before{content:""}.fa-sort:before,.fa-unsorted:before{content:""}.fa-sort-desc:before,.fa-sort-down:before{content:""}.fa-sort-asc:before,.fa-sort-up:before{content:""}.fa-envelope:before{content:""}.fa-linkedin:before{content:""}.fa-rotate-left:before,.fa-undo:before{content:""}.fa-gavel:before,.fa-legal:before{content:""}.fa-dashboard:before,.fa-tachometer:before{content:""}.fa-comment-o:before{content:""}.fa-comments-o:before{content:""}.fa-bolt:before,.fa-flash:before,.rst-content .admonition-todo .admonition-title:before,.rst-content .attention .admonition-title:before,.rst-content .caution .admonition-title:before,.rst-content .warning .admonition-title:before{content:""}.fa-sitemap:before{content:""}.fa-umbrella:before{content:""}.fa-clipboard:before,.fa-paste:before{content:""}.fa-lightbulb-o:before{content:""}.fa-exchange:before{content:""}.fa-cloud-download:before{content:""}.fa-cloud-upload:before{content:""}.fa-user-md:before{content:""}.fa-stethoscope:before{content:""}.fa-suitcase:before{content:""}.fa-bell-o:before{content:""}.fa-coffee:before{content:""}.fa-cutlery:before{content:""}.fa-file-text-o:before{content:""}.fa-building-o:before{content:""}.fa-hospital-o:before{content:""}.fa-ambulance:before{content:""}.fa-medkit:before{content:""}.fa-fighter-jet:before{content:""}.fa-beer:before{content:""}.fa-h-square:before{content:""}.fa-plus-square:before{content:""}.fa-angle-double-left:before{content:""}.fa-angle-double-right:before{content:""}.fa-angle-double-up:before{content:""}.fa-angle-double-down:before{content:""}.fa-angle-left:before{content:""}.fa-angle-right:before,.wy-menu-vertical li span.toctree-expand:before{content:""}.fa-angle-up:before{content:""}.fa-angle-down:before,.wy-menu-vertical li.current>a span.toctree-expand:before,.wy-menu-vertical li.on a span.toctree-expand:before{content:""}.fa-desktop:before{content:""}.fa-laptop:before{content:""}.fa-tablet:before{content:""}.fa-mobile-phone:before,.fa-mobile:before{content:""}.fa-circle-o:before{content:""}.fa-quote-left:before{content:""}.fa-quote-right:before{content:""}.fa-spinner:before{content:""}.fa-circle:before{content:""}.fa-mail-reply:before,.fa-reply:before{content:""}.fa-github-alt:before{content:""}.fa-folder-o:before{content:""}.fa-folder-open-o:before{content:""}.fa-smile-o:before{content:""}.fa-frown-o:before{content:""}.fa-meh-o:before{content:""}.fa-gamepad:before{content:""}.fa-keyboard-o:before{content:""}.fa-flag-o:before{content:""}.fa-flag-checkered:before{content:""}.fa-terminal:before{content:""}.fa-code:before{content:""}.fa-mail-reply-all:before,.fa-reply-all:before{content:""}.fa-star-half-empty:before,.fa-star-half-full:before,.fa-star-half-o:before{content:""}.fa-location-arrow:before{content:""}.fa-crop:before{content:""}.fa-code-fork:before{content:""}.fa-chain-broken:before,.fa-unlink:before{content:""}.fa-question:before{content:""}.fa-info:before{content:""}.fa-exclamation:before{content:""}.fa-superscript:before{content:""}.fa-subscript:before{content:""}.fa-eraser:before{content:""}.fa-puzzle-piece:before{content:""}.fa-microphone:before{content:""}.fa-microphone-slash:before{content:""}.fa-shield:before{content:""}.fa-calendar-o:before{content:""}.fa-fire-extinguisher:before{content:""}.fa-rocket:before{content:""}.fa-maxcdn:before{content:""}.fa-chevron-circle-left:before{content:""}.fa-chevron-circle-right:before{content:""}.fa-chevron-circle-up:before{content:""}.fa-chevron-circle-down:before{content:""}.fa-html5:before{content:""}.fa-css3:before{content:""}.fa-anchor:before{content:""}.fa-unlock-alt:before{content:""}.fa-bullseye:before{content:""}.fa-ellipsis-h:before{content:""}.fa-ellipsis-v:before{content:""}.fa-rss-square:before{content:""}.fa-play-circle:before{content:""}.fa-ticket:before{content:""}.fa-minus-square:before{content:""}.fa-minus-square-o:before{content:""}.fa-level-up:before{content:""}.fa-level-down:before{content:""}.fa-check-square:before{content:""}.fa-pencil-square:before{content:""}.fa-external-link-square:before{content:""}.fa-share-square:before{content:""}.fa-compass:before{content:""}.fa-caret-square-o-down:before,.fa-toggle-down:before{content:""}.fa-caret-square-o-up:before,.fa-toggle-up:before{content:""}.fa-caret-square-o-right:before,.fa-toggle-right:before{content:""}.fa-eur:before,.fa-euro:before{content:""}.fa-gbp:before{content:""}.fa-dollar:before,.fa-usd:before{content:""}.fa-inr:before,.fa-rupee:before{content:""}.fa-cny:before,.fa-jpy:before,.fa-rmb:before,.fa-yen:before{content:""}.fa-rouble:before,.fa-rub:before,.fa-ruble:before{content:""}.fa-krw:before,.fa-won:before{content:""}.fa-bitcoin:before,.fa-btc:before{content:""}.fa-file:before{content:""}.fa-file-text:before{content:""}.fa-sort-alpha-asc:before{content:""}.fa-sort-alpha-desc:before{content:""}.fa-sort-amount-asc:before{content:""}.fa-sort-amount-desc:before{content:""}.fa-sort-numeric-asc:before{content:""}.fa-sort-numeric-desc:before{content:""}.fa-thumbs-up:before{content:""}.fa-thumbs-down:before{content:""}.fa-youtube-square:before{content:""}.fa-youtube:before{content:""}.fa-xing:before{content:""}.fa-xing-square:before{content:""}.fa-youtube-play:before{content:""}.fa-dropbox:before{content:""}.fa-stack-overflow:before{content:""}.fa-instagram:before{content:""}.fa-flickr:before{content:""}.fa-adn:before{content:""}.fa-bitbucket:before,.icon-bitbucket:before{content:""}.fa-bitbucket-square:before{content:""}.fa-tumblr:before{content:""}.fa-tumblr-square:before{content:""}.fa-long-arrow-down:before{content:""}.fa-long-arrow-up:before{content:""}.fa-long-arrow-left:before{content:""}.fa-long-arrow-right:before{content:""}.fa-apple:before{content:""}.fa-windows:before{content:""}.fa-android:before{content:""}.fa-linux:before{content:""}.fa-dribbble:before{content:""}.fa-skype:before{content:""}.fa-foursquare:before{content:""}.fa-trello:before{content:""}.fa-female:before{content:""}.fa-male:before{content:""}.fa-gittip:before,.fa-gratipay:before{content:""}.fa-sun-o:before{content:""}.fa-moon-o:before{content:""}.fa-archive:before{content:""}.fa-bug:before{content:""}.fa-vk:before{content:""}.fa-weibo:before{content:""}.fa-renren:before{content:""}.fa-pagelines:before{content:""}.fa-stack-exchange:before{content:""}.fa-arrow-circle-o-right:before{content:""}.fa-arrow-circle-o-left:before{content:""}.fa-caret-square-o-left:before,.fa-toggle-left:before{content:""}.fa-dot-circle-o:before{content:""}.fa-wheelchair:before{content:""}.fa-vimeo-square:before{content:""}.fa-try:before,.fa-turkish-lira:before{content:""}.fa-plus-square-o:before{content:""}.fa-space-shuttle:before{content:""}.fa-slack:before{content:""}.fa-envelope-square:before{content:""}.fa-wordpress:before{content:""}.fa-openid:before{content:""}.fa-bank:before,.fa-institution:before,.fa-university:before{content:""}.fa-graduation-cap:before,.fa-mortar-board:before{content:""}.fa-yahoo:before{content:""}.fa-google:before{content:""}.fa-reddit:before{content:""}.fa-reddit-square:before{content:""}.fa-stumbleupon-circle:before{content:""}.fa-stumbleupon:before{content:""}.fa-delicious:before{content:""}.fa-digg:before{content:""}.fa-pied-piper-pp:before{content:""}.fa-pied-piper-alt:before{content:""}.fa-drupal:before{content:""}.fa-joomla:before{content:""}.fa-language:before{content:""}.fa-fax:before{content:""}.fa-building:before{content:""}.fa-child:before{content:""}.fa-paw:before{content:""}.fa-spoon:before{content:""}.fa-cube:before{content:""}.fa-cubes:before{content:""}.fa-behance:before{content:""}.fa-behance-square:before{content:""}.fa-steam:before{content:""}.fa-steam-square:before{content:""}.fa-recycle:before{content:""}.fa-automobile:before,.fa-car:before{content:""}.fa-cab:before,.fa-taxi:before{content:""}.fa-tree:before{content:""}.fa-spotify:before{content:""}.fa-deviantart:before{content:""}.fa-soundcloud:before{content:""}.fa-database:before{content:""}.fa-file-pdf-o:before{content:""}.fa-file-word-o:before{content:""}.fa-file-excel-o:before{content:""}.fa-file-powerpoint-o:before{content:""}.fa-file-image-o:before,.fa-file-photo-o:before,.fa-file-picture-o:before{content:""}.fa-file-archive-o:before,.fa-file-zip-o:before{content:""}.fa-file-audio-o:before,.fa-file-sound-o:before{content:""}.fa-file-movie-o:before,.fa-file-video-o:before{content:""}.fa-file-code-o:before{content:""}.fa-vine:before{content:""}.fa-codepen:before{content:""}.fa-jsfiddle:before{content:""}.fa-life-bouy:before,.fa-life-buoy:before,.fa-life-ring:before,.fa-life-saver:before,.fa-support:before{content:""}.fa-circle-o-notch:before{content:""}.fa-ra:before,.fa-rebel:before,.fa-resistance:before{content:""}.fa-empire:before,.fa-ge:before{content:""}.fa-git-square:before{content:""}.fa-git:before{content:""}.fa-hacker-news:before,.fa-y-combinator-square:before,.fa-yc-square:before{content:""}.fa-tencent-weibo:before{content:""}.fa-qq:before{content:""}.fa-wechat:before,.fa-weixin:before{content:""}.fa-paper-plane:before,.fa-send:before{content:""}.fa-paper-plane-o:before,.fa-send-o:before{content:""}.fa-history:before{content:""}.fa-circle-thin:before{content:""}.fa-header:before{content:""}.fa-paragraph:before{content:""}.fa-sliders:before{content:""}.fa-share-alt:before{content:""}.fa-share-alt-square:before{content:""}.fa-bomb:before{content:""}.fa-futbol-o:before,.fa-soccer-ball-o:before{content:""}.fa-tty:before{content:""}.fa-binoculars:before{content:""}.fa-plug:before{content:""}.fa-slideshare:before{content:""}.fa-twitch:before{content:""}.fa-yelp:before{content:""}.fa-newspaper-o:before{content:""}.fa-wifi:before{content:""}.fa-calculator:before{content:""}.fa-paypal:before{content:""}.fa-google-wallet:before{content:""}.fa-cc-visa:before{content:""}.fa-cc-mastercard:before{content:""}.fa-cc-discover:before{content:""}.fa-cc-amex:before{content:""}.fa-cc-paypal:before{content:""}.fa-cc-stripe:before{content:""}.fa-bell-slash:before{content:""}.fa-bell-slash-o:before{content:""}.fa-trash:before{content:""}.fa-copyright:before{content:""}.fa-at:before{content:""}.fa-eyedropper:before{content:""}.fa-paint-brush:before{content:""}.fa-birthday-cake:before{content:""}.fa-area-chart:before{content:""}.fa-pie-chart:before{content:""}.fa-line-chart:before{content:""}.fa-lastfm:before{content:""}.fa-lastfm-square:before{content:""}.fa-toggle-off:before{content:""}.fa-toggle-on:before{content:""}.fa-bicycle:before{content:""}.fa-bus:before{content:""}.fa-ioxhost:before{content:""}.fa-angellist:before{content:""}.fa-cc:before{content:""}.fa-ils:before,.fa-shekel:before,.fa-sheqel:before{content:""}.fa-meanpath:before{content:""}.fa-buysellads:before{content:""}.fa-connectdevelop:before{content:""}.fa-dashcube:before{content:""}.fa-forumbee:before{content:""}.fa-leanpub:before{content:""}.fa-sellsy:before{content:""}.fa-shirtsinbulk:before{content:""}.fa-simplybuilt:before{content:""}.fa-skyatlas:before{content:""}.fa-cart-plus:before{content:""}.fa-cart-arrow-down:before{content:""}.fa-diamond:before{content:""}.fa-ship:before{content:""}.fa-user-secret:before{content:""}.fa-motorcycle:before{content:""}.fa-street-view:before{content:""}.fa-heartbeat:before{content:""}.fa-venus:before{content:""}.fa-mars:before{content:""}.fa-mercury:before{content:""}.fa-intersex:before,.fa-transgender:before{content:""}.fa-transgender-alt:before{content:""}.fa-venus-double:before{content:""}.fa-mars-double:before{content:""}.fa-venus-mars:before{content:""}.fa-mars-stroke:before{content:""}.fa-mars-stroke-v:before{content:""}.fa-mars-stroke-h:before{content:""}.fa-neuter:before{content:""}.fa-genderless:before{content:""}.fa-facebook-official:before{content:""}.fa-pinterest-p:before{content:""}.fa-whatsapp:before{content:""}.fa-server:before{content:""}.fa-user-plus:before{content:""}.fa-user-times:before{content:""}.fa-bed:before,.fa-hotel:before{content:""}.fa-viacoin:before{content:""}.fa-train:before{content:""}.fa-subway:before{content:""}.fa-medium:before{content:""}.fa-y-combinator:before,.fa-yc:before{content:""}.fa-optin-monster:before{content:""}.fa-opencart:before{content:""}.fa-expeditedssl:before{content:""}.fa-battery-4:before,.fa-battery-full:before,.fa-battery:before{content:""}.fa-battery-3:before,.fa-battery-three-quarters:before{content:""}.fa-battery-2:before,.fa-battery-half:before{content:""}.fa-battery-1:before,.fa-battery-quarter:before{content:""}.fa-battery-0:before,.fa-battery-empty:before{content:""}.fa-mouse-pointer:before{content:""}.fa-i-cursor:before{content:""}.fa-object-group:before{content:""}.fa-object-ungroup:before{content:""}.fa-sticky-note:before{content:""}.fa-sticky-note-o:before{content:""}.fa-cc-jcb:before{content:""}.fa-cc-diners-club:before{content:""}.fa-clone:before{content:""}.fa-balance-scale:before{content:""}.fa-hourglass-o:before{content:""}.fa-hourglass-1:before,.fa-hourglass-start:before{content:""}.fa-hourglass-2:before,.fa-hourglass-half:before{content:""}.fa-hourglass-3:before,.fa-hourglass-end:before{content:""}.fa-hourglass:before{content:""}.fa-hand-grab-o:before,.fa-hand-rock-o:before{content:""}.fa-hand-paper-o:before,.fa-hand-stop-o:before{content:""}.fa-hand-scissors-o:before{content:""}.fa-hand-lizard-o:before{content:""}.fa-hand-spock-o:before{content:""}.fa-hand-pointer-o:before{content:""}.fa-hand-peace-o:before{content:""}.fa-trademark:before{content:""}.fa-registered:before{content:""}.fa-creative-commons:before{content:""}.fa-gg:before{content:""}.fa-gg-circle:before{content:""}.fa-tripadvisor:before{content:""}.fa-odnoklassniki:before{content:""}.fa-odnoklassniki-square:before{content:""}.fa-get-pocket:before{content:""}.fa-wikipedia-w:before{content:""}.fa-safari:before{content:""}.fa-chrome:before{content:""}.fa-firefox:before{content:""}.fa-opera:before{content:""}.fa-internet-explorer:before{content:""}.fa-television:before,.fa-tv:before{content:""}.fa-contao:before{content:""}.fa-500px:before{content:""}.fa-amazon:before{content:""}.fa-calendar-plus-o:before{content:""}.fa-calendar-minus-o:before{content:""}.fa-calendar-times-o:before{content:""}.fa-calendar-check-o:before{content:""}.fa-industry:before{content:""}.fa-map-pin:before{content:""}.fa-map-signs:before{content:""}.fa-map-o:before{content:""}.fa-map:before{content:""}.fa-commenting:before{content:""}.fa-commenting-o:before{content:""}.fa-houzz:before{content:""}.fa-vimeo:before{content:""}.fa-black-tie:before{content:""}.fa-fonticons:before{content:""}.fa-reddit-alien:before{content:""}.fa-edge:before{content:""}.fa-credit-card-alt:before{content:""}.fa-codiepie:before{content:""}.fa-modx:before{content:""}.fa-fort-awesome:before{content:""}.fa-usb:before{content:""}.fa-product-hunt:before{content:""}.fa-mixcloud:before{content:""}.fa-scribd:before{content:""}.fa-pause-circle:before{content:""}.fa-pause-circle-o:before{content:""}.fa-stop-circle:before{content:""}.fa-stop-circle-o:before{content:""}.fa-shopping-bag:before{content:""}.fa-shopping-basket:before{content:""}.fa-hashtag:before{content:""}.fa-bluetooth:before{content:""}.fa-bluetooth-b:before{content:""}.fa-percent:before{content:""}.fa-gitlab:before,.icon-gitlab:before{content:""}.fa-wpbeginner:before{content:""}.fa-wpforms:before{content:""}.fa-envira:before{content:""}.fa-universal-access:before{content:""}.fa-wheelchair-alt:before{content:""}.fa-question-circle-o:before{content:""}.fa-blind:before{content:""}.fa-audio-description:before{content:""}.fa-volume-control-phone:before{content:""}.fa-braille:before{content:""}.fa-assistive-listening-systems:before{content:""}.fa-american-sign-language-interpreting:before,.fa-asl-interpreting:before{content:""}.fa-deaf:before,.fa-deafness:before,.fa-hard-of-hearing:before{content:""}.fa-glide:before{content:""}.fa-glide-g:before{content:""}.fa-sign-language:before,.fa-signing:before{content:""}.fa-low-vision:before{content:""}.fa-viadeo:before{content:""}.fa-viadeo-square:before{content:""}.fa-snapchat:before{content:""}.fa-snapchat-ghost:before{content:""}.fa-snapchat-square:before{content:""}.fa-pied-piper:before{content:""}.fa-first-order:before{content:""}.fa-yoast:before{content:""}.fa-themeisle:before{content:""}.fa-google-plus-circle:before,.fa-google-plus-official:before{content:""}.fa-fa:before,.fa-font-awesome:before{content:""}.fa-handshake-o:before{content:""}.fa-envelope-open:before{content:""}.fa-envelope-open-o:before{content:""}.fa-linode:before{content:""}.fa-address-book:before{content:""}.fa-address-book-o:before{content:""}.fa-address-card:before,.fa-vcard:before{content:""}.fa-address-card-o:before,.fa-vcard-o:before{content:""}.fa-user-circle:before{content:""}.fa-user-circle-o:before{content:""}.fa-user-o:before{content:""}.fa-id-badge:before{content:""}.fa-drivers-license:before,.fa-id-card:before{content:""}.fa-drivers-license-o:before,.fa-id-card-o:before{content:""}.fa-quora:before{content:""}.fa-free-code-camp:before{content:""}.fa-telegram:before{content:""}.fa-thermometer-4:before,.fa-thermometer-full:before,.fa-thermometer:before{content:""}.fa-thermometer-3:before,.fa-thermometer-three-quarters:before{content:""}.fa-thermometer-2:before,.fa-thermometer-half:before{content:""}.fa-thermometer-1:before,.fa-thermometer-quarter:before{content:""}.fa-thermometer-0:before,.fa-thermometer-empty:before{content:""}.fa-shower:before{content:""}.fa-bath:before,.fa-bathtub:before,.fa-s15:before{content:""}.fa-podcast:before{content:""}.fa-window-maximize:before{content:""}.fa-window-minimize:before{content:""}.fa-window-restore:before{content:""}.fa-times-rectangle:before,.fa-window-close:before{content:""}.fa-times-rectangle-o:before,.fa-window-close-o:before{content:""}.fa-bandcamp:before{content:""}.fa-grav:before{content:""}.fa-etsy:before{content:""}.fa-imdb:before{content:""}.fa-ravelry:before{content:""}.fa-eercast:before{content:""}.fa-microchip:before{content:""}.fa-snowflake-o:before{content:""}.fa-superpowers:before{content:""}.fa-wpexplorer:before{content:""}.fa-meetup:before{content:""}.sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);border:0}.sr-only-focusable:active,.sr-only-focusable:focus{position:static;width:auto;height:auto;margin:0;overflow:visible;clip:auto}.fa,.icon,.rst-content .admonition-title,.rst-content .code-block-caption .headerlink,.rst-content code.download span:first-child,.rst-content dl dt .headerlink,.rst-content h1 .headerlink,.rst-content h2 .headerlink,.rst-content h3 .headerlink,.rst-content h4 .headerlink,.rst-content h5 .headerlink,.rst-content h6 .headerlink,.rst-content p.caption .headerlink,.rst-content table>caption .headerlink,.rst-content tt.download span:first-child,.wy-dropdown .caret,.wy-inline-validate.wy-inline-validate-danger .wy-input-context,.wy-inline-validate.wy-inline-validate-info .wy-input-context,.wy-inline-validate.wy-inline-validate-success .wy-input-context,.wy-inline-validate.wy-inline-validate-warning .wy-input-context,.wy-menu-vertical li.current>a span.toctree-expand,.wy-menu-vertical li.on a span.toctree-expand,.wy-menu-vertical li span.toctree-expand{font-family:inherit}.fa:before,.icon:before,.rst-content .admonition-title:before,.rst-content .code-block-caption .headerlink:before,.rst-content code.download span:first-child:before,.rst-content dl dt .headerlink:before,.rst-content h1 .headerlink:before,.rst-content h2 .headerlink:before,.rst-content h3 .headerlink:before,.rst-content h4 .headerlink:before,.rst-content h5 .headerlink:before,.rst-content h6 .headerlink:before,.rst-content p.caption .headerlink:before,.rst-content table>caption .headerlink:before,.rst-content tt.download span:first-child:before,.wy-dropdown .caret:before,.wy-inline-validate.wy-inline-validate-danger .wy-input-context:before,.wy-inline-validate.wy-inline-validate-info .wy-input-context:before,.wy-inline-validate.wy-inline-validate-success .wy-input-context:before,.wy-inline-validate.wy-inline-validate-warning .wy-input-context:before,.wy-menu-vertical li.current>a span.toctree-expand:before,.wy-menu-vertical li.on a span.toctree-expand:before,.wy-menu-vertical li span.toctree-expand:before{font-family:FontAwesome;display:inline-block;font-style:normal;font-weight:400;line-height:1;text-decoration:inherit}.rst-content .code-block-caption a .headerlink,.rst-content a .admonition-title,.rst-content code.download a span:first-child,.rst-content dl dt a .headerlink,.rst-content h1 a .headerlink,.rst-content h2 a .headerlink,.rst-content h3 a .headerlink,.rst-content h4 a .headerlink,.rst-content h5 a .headerlink,.rst-content h6 a .headerlink,.rst-content p.caption a .headerlink,.rst-content table>caption a .headerlink,.rst-content tt.download a span:first-child,.wy-menu-vertical li.current>a span.toctree-expand,.wy-menu-vertical li.on a span.toctree-expand,.wy-menu-vertical li a span.toctree-expand,a .fa,a .icon,a .rst-content .admonition-title,a .rst-content .code-block-caption .headerlink,a .rst-content code.download span:first-child,a .rst-content dl dt .headerlink,a .rst-content h1 .headerlink,a .rst-content h2 .headerlink,a .rst-content h3 .headerlink,a .rst-content h4 .headerlink,a .rst-content h5 .headerlink,a .rst-content h6 .headerlink,a .rst-content p.caption .headerlink,a .rst-content table>caption .headerlink,a .rst-content tt.download span:first-child,a .wy-menu-vertical li span.toctree-expand{display:inline-block;text-decoration:inherit}.btn .fa,.btn .icon,.btn .rst-content .admonition-title,.btn .rst-content .code-block-caption .headerlink,.btn .rst-content code.download span:first-child,.btn .rst-content dl dt .headerlink,.btn .rst-content h1 .headerlink,.btn .rst-content h2 .headerlink,.btn .rst-content h3 .headerlink,.btn .rst-content h4 .headerlink,.btn .rst-content h5 .headerlink,.btn .rst-content h6 .headerlink,.btn .rst-content p.caption .headerlink,.btn .rst-content table>caption .headerlink,.btn .rst-content tt.download span:first-child,.btn .wy-menu-vertical li.current>a span.toctree-expand,.btn .wy-menu-vertical li.on a span.toctree-expand,.btn .wy-menu-vertical li span.toctree-expand,.nav .fa,.nav .icon,.nav .rst-content .admonition-title,.nav .rst-content .code-block-caption .headerlink,.nav .rst-content code.download span:first-child,.nav .rst-content dl dt .headerlink,.nav .rst-content h1 .headerlink,.nav .rst-content h2 .headerlink,.nav .rst-content h3 .headerlink,.nav .rst-content h4 .headerlink,.nav .rst-content h5 .headerlink,.nav .rst-content h6 .headerlink,.nav .rst-content p.caption .headerlink,.nav .rst-content table>caption .headerlink,.nav .rst-content tt.download span:first-child,.nav .wy-menu-vertical li.current>a span.toctree-expand,.nav .wy-menu-vertical li.on a span.toctree-expand,.nav .wy-menu-vertical li span.toctree-expand,.rst-content .btn .admonition-title,.rst-content .code-block-caption .btn .headerlink,.rst-content .code-block-caption .nav .headerlink,.rst-content .nav .admonition-title,.rst-content code.download .btn span:first-child,.rst-content code.download .nav span:first-child,.rst-content dl dt .btn .headerlink,.rst-content dl dt .nav .headerlink,.rst-content h1 .btn .headerlink,.rst-content h1 .nav .headerlink,.rst-content h2 .btn .headerlink,.rst-content h2 .nav .headerlink,.rst-content h3 .btn .headerlink,.rst-content h3 .nav .headerlink,.rst-content h4 .btn .headerlink,.rst-content h4 .nav .headerlink,.rst-content h5 .btn .headerlink,.rst-content h5 .nav .headerlink,.rst-content h6 .btn .headerlink,.rst-content h6 .nav .headerlink,.rst-content p.caption .btn .headerlink,.rst-content p.caption .nav .headerlink,.rst-content table>caption .btn .headerlink,.rst-content table>caption .nav .headerlink,.rst-content tt.download .btn span:first-child,.rst-content tt.download .nav span:first-child,.wy-menu-vertical li .btn span.toctree-expand,.wy-menu-vertical li.current>a .btn span.toctree-expand,.wy-menu-vertical li.current>a .nav span.toctree-expand,.wy-menu-vertical li .nav span.toctree-expand,.wy-menu-vertical li.on a .btn span.toctree-expand,.wy-menu-vertical li.on a .nav span.toctree-expand{display:inline}.btn .fa-large.icon,.btn .fa.fa-large,.btn .rst-content .code-block-caption .fa-large.headerlink,.btn .rst-content .fa-large.admonition-title,.btn .rst-content code.download span.fa-large:first-child,.btn .rst-content dl dt .fa-large.headerlink,.btn .rst-content h1 .fa-large.headerlink,.btn .rst-content h2 .fa-large.headerlink,.btn .rst-content h3 .fa-large.headerlink,.btn .rst-content h4 .fa-large.headerlink,.btn .rst-content h5 .fa-large.headerlink,.btn .rst-content h6 .fa-large.headerlink,.btn .rst-content p.caption .fa-large.headerlink,.btn .rst-content table>caption .fa-large.headerlink,.btn .rst-content tt.download span.fa-large:first-child,.btn .wy-menu-vertical li span.fa-large.toctree-expand,.nav .fa-large.icon,.nav .fa.fa-large,.nav .rst-content .code-block-caption .fa-large.headerlink,.nav .rst-content .fa-large.admonition-title,.nav .rst-content code.download span.fa-large:first-child,.nav .rst-content dl dt .fa-large.headerlink,.nav .rst-content h1 .fa-large.headerlink,.nav .rst-content h2 .fa-large.headerlink,.nav .rst-content h3 .fa-large.headerlink,.nav .rst-content h4 .fa-large.headerlink,.nav .rst-content h5 .fa-large.headerlink,.nav .rst-content h6 .fa-large.headerlink,.nav .rst-content p.caption .fa-large.headerlink,.nav .rst-content table>caption .fa-large.headerlink,.nav .rst-content tt.download span.fa-large:first-child,.nav .wy-menu-vertical li span.fa-large.toctree-expand,.rst-content .btn .fa-large.admonition-title,.rst-content .code-block-caption .btn .fa-large.headerlink,.rst-content .code-block-caption .nav .fa-large.headerlink,.rst-content .nav .fa-large.admonition-title,.rst-content code.download .btn span.fa-large:first-child,.rst-content code.download .nav span.fa-large:first-child,.rst-content dl dt .btn .fa-large.headerlink,.rst-content dl dt .nav .fa-large.headerlink,.rst-content h1 .btn .fa-large.headerlink,.rst-content h1 .nav .fa-large.headerlink,.rst-content h2 .btn .fa-large.headerlink,.rst-content h2 .nav .fa-large.headerlink,.rst-content h3 .btn .fa-large.headerlink,.rst-content h3 .nav .fa-large.headerlink,.rst-content h4 .btn .fa-large.headerlink,.rst-content h4 .nav .fa-large.headerlink,.rst-content h5 .btn .fa-large.headerlink,.rst-content h5 .nav .fa-large.headerlink,.rst-content h6 .btn .fa-large.headerlink,.rst-content h6 .nav .fa-large.headerlink,.rst-content p.caption .btn .fa-large.headerlink,.rst-content p.caption .nav .fa-large.headerlink,.rst-content table>caption .btn .fa-large.headerlink,.rst-content table>caption .nav .fa-large.headerlink,.rst-content tt.download .btn span.fa-large:first-child,.rst-content tt.download .nav span.fa-large:first-child,.wy-menu-vertical li .btn span.fa-large.toctree-expand,.wy-menu-vertical li .nav span.fa-large.toctree-expand{line-height:.9em}.btn .fa-spin.icon,.btn .fa.fa-spin,.btn .rst-content .code-block-caption .fa-spin.headerlink,.btn .rst-content .fa-spin.admonition-title,.btn .rst-content code.download span.fa-spin:first-child,.btn .rst-content dl dt .fa-spin.headerlink,.btn .rst-content h1 .fa-spin.headerlink,.btn .rst-content h2 .fa-spin.headerlink,.btn .rst-content h3 .fa-spin.headerlink,.btn .rst-content h4 .fa-spin.headerlink,.btn .rst-content h5 .fa-spin.headerlink,.btn .rst-content h6 .fa-spin.headerlink,.btn .rst-content p.caption .fa-spin.headerlink,.btn .rst-content table>caption .fa-spin.headerlink,.btn .rst-content tt.download span.fa-spin:first-child,.btn .wy-menu-vertical li span.fa-spin.toctree-expand,.nav .fa-spin.icon,.nav .fa.fa-spin,.nav .rst-content .code-block-caption .fa-spin.headerlink,.nav .rst-content .fa-spin.admonition-title,.nav .rst-content code.download span.fa-spin:first-child,.nav .rst-content dl dt .fa-spin.headerlink,.nav .rst-content h1 .fa-spin.headerlink,.nav .rst-content h2 .fa-spin.headerlink,.nav .rst-content h3 .fa-spin.headerlink,.nav .rst-content h4 .fa-spin.headerlink,.nav .rst-content h5 .fa-spin.headerlink,.nav .rst-content h6 .fa-spin.headerlink,.nav .rst-content p.caption .fa-spin.headerlink,.nav .rst-content table>caption .fa-spin.headerlink,.nav .rst-content tt.download span.fa-spin:first-child,.nav .wy-menu-vertical li span.fa-spin.toctree-expand,.rst-content .btn .fa-spin.admonition-title,.rst-content .code-block-caption .btn .fa-spin.headerlink,.rst-content .code-block-caption .nav .fa-spin.headerlink,.rst-content .nav .fa-spin.admonition-title,.rst-content code.download .btn span.fa-spin:first-child,.rst-content code.download .nav span.fa-spin:first-child,.rst-content dl dt .btn .fa-spin.headerlink,.rst-content dl dt .nav .fa-spin.headerlink,.rst-content h1 .btn .fa-spin.headerlink,.rst-content h1 .nav .fa-spin.headerlink,.rst-content h2 .btn .fa-spin.headerlink,.rst-content h2 .nav .fa-spin.headerlink,.rst-content h3 .btn .fa-spin.headerlink,.rst-content h3 .nav .fa-spin.headerlink,.rst-content h4 .btn .fa-spin.headerlink,.rst-content h4 .nav .fa-spin.headerlink,.rst-content h5 .btn .fa-spin.headerlink,.rst-content h5 .nav .fa-spin.headerlink,.rst-content h6 .btn .fa-spin.headerlink,.rst-content h6 .nav .fa-spin.headerlink,.rst-content p.caption .btn .fa-spin.headerlink,.rst-content p.caption .nav .fa-spin.headerlink,.rst-content table>caption .btn .fa-spin.headerlink,.rst-content table>caption .nav .fa-spin.headerlink,.rst-content tt.download .btn span.fa-spin:first-child,.rst-content tt.download .nav span.fa-spin:first-child,.wy-menu-vertical li .btn span.fa-spin.toctree-expand,.wy-menu-vertical li .nav span.fa-spin.toctree-expand{display:inline-block}.btn.fa:before,.btn.icon:before,.rst-content .btn.admonition-title:before,.rst-content .code-block-caption .btn.headerlink:before,.rst-content code.download span.btn:first-child:before,.rst-content dl dt .btn.headerlink:before,.rst-content h1 .btn.headerlink:before,.rst-content h2 .btn.headerlink:before,.rst-content h3 .btn.headerlink:before,.rst-content h4 .btn.headerlink:before,.rst-content h5 .btn.headerlink:before,.rst-content h6 .btn.headerlink:before,.rst-content p.caption .btn.headerlink:before,.rst-content table>caption .btn.headerlink:before,.rst-content tt.download span.btn:first-child:before,.wy-menu-vertical li span.btn.toctree-expand:before{opacity:.5;-webkit-transition:opacity .05s ease-in;-moz-transition:opacity .05s ease-in;transition:opacity .05s ease-in}.btn.fa:hover:before,.btn.icon:hover:before,.rst-content .btn.admonition-title:hover:before,.rst-content .code-block-caption .btn.headerlink:hover:before,.rst-content code.download span.btn:hover:first-child:before,.rst-content dl dt .btn.headerlink:hover:before,.rst-content h1 .btn.headerlink:hover:before,.rst-content h2 .btn.headerlink:hover:before,.rst-content h3 .btn.headerlink:hover:before,.rst-content h4 .btn.headerlink:hover:before,.rst-content h5 .btn.headerlink:hover:before,.rst-content h6 .btn.headerlink:hover:before,.rst-content p.caption .btn.headerlink:hover:before,.rst-content table>caption .btn.headerlink:hover:before,.rst-content tt.download span.btn:hover:first-child:before,.wy-menu-vertical li span.btn.toctree-expand:hover:before{opacity:1}.btn-mini .fa:before,.btn-mini .icon:before,.btn-mini .rst-content .admonition-title:before,.btn-mini .rst-content .code-block-caption .headerlink:before,.btn-mini .rst-content code.download span:first-child:before,.btn-mini .rst-content dl dt .headerlink:before,.btn-mini .rst-content h1 .headerlink:before,.btn-mini .rst-content h2 .headerlink:before,.btn-mini .rst-content h3 .headerlink:before,.btn-mini .rst-content h4 .headerlink:before,.btn-mini .rst-content h5 .headerlink:before,.btn-mini .rst-content h6 .headerlink:before,.btn-mini .rst-content p.caption .headerlink:before,.btn-mini .rst-content table>caption .headerlink:before,.btn-mini .rst-content tt.download span:first-child:before,.btn-mini .wy-menu-vertical li span.toctree-expand:before,.rst-content .btn-mini .admonition-title:before,.rst-content .code-block-caption .btn-mini .headerlink:before,.rst-content code.download .btn-mini span:first-child:before,.rst-content dl dt .btn-mini .headerlink:before,.rst-content h1 .btn-mini .headerlink:before,.rst-content h2 .btn-mini .headerlink:before,.rst-content h3 .btn-mini .headerlink:before,.rst-content h4 .btn-mini .headerlink:before,.rst-content h5 .btn-mini .headerlink:before,.rst-content h6 .btn-mini .headerlink:before,.rst-content p.caption .btn-mini .headerlink:before,.rst-content table>caption .btn-mini .headerlink:before,.rst-content tt.download .btn-mini span:first-child:before,.wy-menu-vertical li .btn-mini span.toctree-expand:before{font-size:14px;vertical-align:-15%}.wy-alert{padding:.8em;line-height:1.6em;margin-bottom:1.6em;background:#e7f2fa}.wy-alert-title{font-weight:700;display:block;color:#fff;background:#6ab0de;padding:.4em .8em;margin:-.8em -.8em .8em}.wy-alert.wy-alert-danger{background:#fdf3f2}.wy-alert.wy-alert-danger .wy-alert-title{background:#f29f97}.wy-alert.wy-alert-warning{background:#ffedcc}.wy-alert.wy-alert-warning .wy-alert-title{background:#f0b37e}.wy-alert.wy-alert-info{background:#e7f2fa}.wy-alert.wy-alert-info .wy-alert-title{background:#6ab0de}.wy-alert.wy-alert-success{background:#dbfaf4}.wy-alert.wy-alert-success .wy-alert-title{background:#1abc9c}.wy-alert.wy-alert-neutral{background:var(--bg-panel)}.wy-alert.wy-alert-neutral .wy-alert-title{color:var(--text);background:var(--border)}.wy-alert.wy-alert-neutral a{color:var(--link)}.wy-alert p:last-child{margin-bottom:0}.wy-tray-container{position:fixed;bottom:0;left:0;z-index:600}.wy-tray-container li{display:block;width:23rem;background:transparent;color:#fff;text-align:center;box-shadow:0 5px 5px 0 rgba(0,0,0,.1);padding:0 1.6em;min-width:20%;opacity:0;height:0;line-height:56px;overflow:hidden;-webkit-transition:all .3s ease-in;-moz-transition:all .3s ease-in;transition:all .3s ease-in}.wy-tray-container li.wy-tray-item-success{background:#27ae60}.wy-tray-container li.wy-tray-item-info{background:#2980b9}.wy-tray-container li.wy-tray-item-warning{background:#e67e22}.wy-tray-container li.wy-tray-item-danger{background:#e74c3c}.wy-tray-container li.on{opacity:1;height:56px}@media screen and (max-width:768px){.wy-tray-container{bottom:auto;top:0;width:100%}.wy-tray-container li{width:100%}}.wy-dropdown{position:relative;display:inline-block}.wy-dropdown-active .wy-dropdown-menu{display:block}.wy-dropdown-menu{position:absolute;left:0;display:none;float:left;top:100%;min-width:100%;background:#fcfcfc;z-index:100;border:1px solid #cfd7dd;box-shadow:0 2px 2px 0 rgba(0,0,0,.1);padding:.8em}.wy-dropdown-menu>dd>a{display:block;clear:both;color:var(--text);white-space:nowrap;font-size:90%;padding:0 .8em;cursor:pointer}.wy-dropdown-menu>dd>a:hover{background:var(--link);color:#fff}.wy-dropdown-menu>dd.divider{border-top:1px solid #cfd7dd;margin:.4em 0}.wy-dropdown-menu>dd.search{padding-bottom:.8em}.wy-dropdown-menu>dd.search input[type=search]{width:100%}.wy-dropdown-menu>dd.call-to-action{background:#e3e3e3;text-transform:uppercase;font-weight:500;font-size:80%}.wy-dropdown-menu>dd.call-to-action:hover{background:#e3e3e3}.wy-dropdown-menu>dd.call-to-action .btn{color:#fff}.wy-dropdown.wy-dropdown-up .wy-dropdown-menu{bottom:100%;top:auto;left:auto;right:0}.wy-dropdown.wy-dropdown-bubble .wy-dropdown-menu{background:var(--bg);margin-top:2px}.wy-dropdown.wy-dropdown-bubble .wy-dropdown-menu a{padding:.4em .8em}.wy-dropdown.wy-dropdown-bubble .wy-dropdown-menu a:hover{background:var(--link);color:#fff}.wy-dropdown.wy-dropdown-left .wy-dropdown-menu{right:0;left:auto;text-align:right}.wy-dropdown-arrow:before{content:" ";border-bottom:5px solid #f5f5f5;border-left:5px solid transparent;border-right:5px solid transparent;position:absolute;display:block;top:-4px;left:50%;margin-left:-3px}.wy-dropdown-arrow.wy-dropdown-arrow-left:before{left:11px}@media screen and (max-width:768px){.tablet-hide{display:none}}@media screen and (max-width:480px){.mobile-hide{display:none}}.float-left{float:left}.float-right{float:right}.full-width{width:100%}.rst-content table.docutils,.rst-content table.field-list,.wy-table{border-collapse:collapse;border-spacing:0;empty-cells:show;margin-bottom:1.6em}.rst-content table.docutils caption,.rst-content table.field-list caption,.wy-table caption{color:#000;font:italic 85%/1 arial,sans-serif;padding:1em 0;text-align:center}.rst-content table.docutils td,.rst-content table.docutils th,.rst-content table.field-list td,.rst-content table.field-list th,.wy-table td,.wy-table th{font-size:90%;margin:0;overflow:visible;padding:8px 16px}.rst-content table.docutils td:first-child,.rst-content table.docutils th:first-child,.rst-content table.field-list td:first-child,.rst-content table.field-list th:first-child,.wy-table td:first-child,.wy-table th:first-child{border-left-width:0}.rst-content table.docutils thead,.rst-content table.field-list thead,.wy-table thead{color:#000;text-align:left;vertical-align:bottom;white-space:nowrap}.rst-content table.docutils thead th,.rst-content table.field-list thead th,.wy-table thead th{font-weight:700;border-bottom:2px solid var(--border)}.rst-content table.docutils td,.rst-content table.field-list td,.wy-table td{background-color:transparent;vertical-align:middle}.rst-content table.docutils td p,.rst-content table.field-list td p,.wy-table td p{line-height:1.2em}.rst-content table.docutils td p:last-child,.rst-content table.field-list td p:last-child,.wy-table td p:last-child{margin-bottom:0}.rst-content table.docutils .wy-table-cell-min,.rst-content table.field-list .wy-table-cell-min,.wy-table .wy-table-cell-min{width:1%;padding-right:0}.rst-content table.docutils .wy-table-cell-min input[type=checkbox],.rst-content table.field-list .wy-table-cell-min input[type=checkbox],.wy-table .wy-table-cell-min input[type=checkbox]{margin:0}.wy-table-secondary{color:grey;font-size:90%}.wy-table-tertiary{color:grey;font-size:80%}.wy-table-backed,.wy-table-odd td,.wy-table-striped tr:nth-child(2n-1) td{background-color:var(--bg-panel)}.rst-content table.docutils,.wy-table-bordered-all{border:1px solid var(--border)}.rst-content table.docutils td,.wy-table-bordered-all td{border-bottom:1px solid var(--border);border-left:1px solid var(--border)}.rst-content table.docutils tbody>tr:last-child td,.wy-table-bordered-all tbody>tr:last-child td{border-bottom-width:0}.wy-table-bordered{border:1px solid var(--border)}.wy-table-bordered-rows td{border-bottom:1px solid var(--border)}.wy-table-bordered-rows tbody>tr:last-child td{border-bottom-width:0}.wy-table-horizontal td,.wy-table-horizontal th{border-width:0 0 1px;border-bottom:1px solid var(--border)}.wy-table-horizontal tbody>tr:last-child td{border-bottom-width:0}.wy-table-responsive{margin-bottom:1.6em;max-width:100%;overflow:auto}.wy-table-responsive table{margin-bottom:0!important}.wy-table-responsive table td,.wy-table-responsive table th{white-space:nowrap}button{font-size:100%;margin:0;vertical-align:baseline;*vertical-align:middle;cursor:pointer;line-height:normal;-webkit-appearance:button;*overflow:visible}button::-moz-focus-inner,input::-moz-focus-inner{border:0;padding:0}button[disabled]{cursor:default}.btn{display:inline-block;border-radius:2px;line-height:normal;white-space:nowrap;text-align:center;cursor:pointer;font-size:100%;padding:.4em .8em 2.4em;border:1px solid rgba(0,0,0,.1);background-color:#27ae60;text-decoration:none;font-weight:400;font-family:Inter,sans-serif;box-shadow:inset 0 1px 2px -1px hsla(0,0%,100%,.5),inset 0 -2px 0 0 rgba(0,0,0,.1);outline-none:false;vertical-align:middle;*display:inline;zoom:1;-webkit-user-drag:none;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;-webkit-transition:all .1s linear;-moz-transition:all .1s linear;transition:all .1s linear}.btn,.btn-hover,.btn:hover{color:#fff}.btn:focus{outline:0}.btn:active{box-shadow:inset 0 -1px 0 0 rgba(0,0,0,.05),inset 0 2px 0 0 rgba(0,0,0,.1);padding:2.4em .8em .4em}.btn:visited{color:#fff}.btn:disabled{background-image:none;filter:progid:DXImageTransform.Microsoft.gradient(enabled = false);filter:alpha(opacity=40);opacity:.4;cursor:not-allowed;box-shadow:none}.btn::-moz-focus-inner{padding:0;border:0}.btn-small{font-size:80%}.btn-neutral{color:var(--text)!important}.btn-neutral,.btn-neutral:hover{background-color:var(--bg)!important}.btn-neutral:hover{color:var(--text)}.btn-neutral:visited{color:var(--text)!important}.wy-btn-group .btn,.wy-control .btn{vertical-align:middle}.wy-btn-group{margin-bottom:1.6em;*zoom:1}.wy-btn-group:after,.wy-btn-group:before{display:table;content:""}.wy-btn-group:after{clear:both}.wy-form-stacked select{display:block}.wy-form-aligned .wy-help-inline,.wy-form-aligned input,.wy-form-aligned label,.wy-form-aligned select,.wy-form-aligned textarea{display:inline-block;*display:inline;*zoom:1;vertical-align:middle}.wy-form-aligned .wy-control-group>label{display:inline-block;vertical-align:middle;width:10em;margin:.4em .8em 0 0;float:left}.wy-form-aligned .wy-control{float:left}.wy-form-aligned .wy-control label{display:block}.wy-form-aligned .wy-control select{margin-top:.4em}fieldset{margin:0}fieldset,legend{border:0;padding:0}legend{width:100%;white-space:normal;margin-bottom:1.6em;font-size:150%;*margin-left:-7px}label,legend{display:block}label{margin:0 0 .3125em;color:#333;font-size:90%}input,select,textarea{font-size:100%;margin:0;vertical-align:baseline;*vertical-align:middle}.wy-control-group{margin-bottom:1.6em;max-width:1200px;margin-left:auto;margin-right:auto;*zoom:1}.wy-control-group:after,.wy-control-group:before{display:table;content:""}.wy-control-group:after{clear:both}.wy-control-group.wy-control-group-required>label:after{content:" *";color:#e74c3c}.wy-control-group .wy-form-full,.wy-control-group .wy-form-halves,.wy-control-group .wy-form-thirds{padding-bottom:.8em}.wy-control-group .wy-form-full input[type=color],.wy-control-group .wy-form-full input[type=date],.wy-control-group .wy-form-full input[type=datetime-local],.wy-control-group .wy-form-full input[type=datetime],.wy-control-group .wy-form-full input[type=email],.wy-control-group .wy-form-full input[type=month],.wy-control-group .wy-form-full input[type=number],.wy-control-group .wy-form-full input[type=password],.wy-control-group .wy-form-full input[type=search],.wy-control-group .wy-form-full input[type=tel],.wy-control-group .wy-form-full input[type=text],.wy-control-group .wy-form-full input[type=time],.wy-control-group .wy-form-full input[type=url],.wy-control-group .wy-form-full input[type=week],.wy-control-group .wy-form-full select,.wy-control-group .wy-form-halves input[type=color],.wy-control-group .wy-form-halves input[type=date],.wy-control-group .wy-form-halves input[type=datetime-local],.wy-control-group .wy-form-halves input[type=datetime],.wy-control-group .wy-form-halves input[type=email],.wy-control-group .wy-form-halves input[type=month],.wy-control-group .wy-form-halves input[type=number],.wy-control-group .wy-form-halves input[type=password],.wy-control-group .wy-form-halves input[type=search],.wy-control-group .wy-form-halves input[type=tel],.wy-control-group .wy-form-halves input[type=text],.wy-control-group .wy-form-halves input[type=time],.wy-control-group .wy-form-halves input[type=url],.wy-control-group .wy-form-halves input[type=week],.wy-control-group .wy-form-halves select,.wy-control-group .wy-form-thirds input[type=color],.wy-control-group .wy-form-thirds input[type=date],.wy-control-group .wy-form-thirds input[type=datetime-local],.wy-control-group .wy-form-thirds input[type=datetime],.wy-control-group .wy-form-thirds input[type=email],.wy-control-group .wy-form-thirds input[type=month],.wy-control-group .wy-form-thirds input[type=number],.wy-control-group .wy-form-thirds input[type=password],.wy-control-group .wy-form-thirds input[type=search],.wy-control-group .wy-form-thirds input[type=tel],.wy-control-group .wy-form-thirds input[type=text],.wy-control-group .wy-form-thirds input[type=time],.wy-control-group .wy-form-thirds input[type=url],.wy-control-group .wy-form-thirds input[type=week],.wy-control-group .wy-form-thirds select{width:100%}.wy-control-group .wy-form-full{float:left;display:block;width:100%;margin-right:0}.wy-control-group .wy-form-full:last-child{margin-right:0}.wy-control-group .wy-form-halves{float:left;display:block;margin-right:3.191641474%;width:48.404179263%}.wy-control-group .wy-form-halves:last-child,.wy-control-group .wy-form-halves:nth-of-type(2n){margin-right:0}.wy-control-group .wy-form-halves:nth-of-type(odd){clear:left}.wy-control-group .wy-form-thirds{float:left;display:block;margin-right:3.191641474%;width:31.2055723507%}.wy-control-group .wy-form-thirds:last-child,.wy-control-group .wy-form-thirds:nth-of-type(3n){margin-right:0}.wy-control-group .wy-form-thirds:nth-of-type(3n+1){clear:left}.wy-control-group.wy-control-group-no-input .wy-control,.wy-control-no-input{margin:.4em 0 0;font-size:90%}.wy-control-no-input{display:inline-block}.wy-control-group.fluid-input input[type=color],.wy-control-group.fluid-input input[type=date],.wy-control-group.fluid-input input[type=datetime-local],.wy-control-group.fluid-input input[type=datetime],.wy-control-group.fluid-input input[type=email],.wy-control-group.fluid-input input[type=month],.wy-control-group.fluid-input input[type=number],.wy-control-group.fluid-input input[type=password],.wy-control-group.fluid-input input[type=search],.wy-control-group.fluid-input input[type=tel],.wy-control-group.fluid-input input[type=text],.wy-control-group.fluid-input input[type=time],.wy-control-group.fluid-input input[type=url],.wy-control-group.fluid-input input[type=week]{width:100%}.wy-form-message-inline{padding-left:.3em;color:#666;font-size:90%}.wy-form-message{display:block;color:#999;font-size:70%;margin-top:.3125em;font-style:italic}.wy-form-message p{font-size:inherit;font-style:italic;margin-bottom:.4em}.wy-form-message p:last-child{margin-bottom:0}input{line-height:normal}input[type=button],input[type=reset],input[type=submit]{-webkit-appearance:button;cursor:pointer;font-family:Inter,sans-serif;*overflow:visible}input[type=color],input[type=date],input[type=datetime-local],input[type=datetime],input[type=email],input[type=month],input[type=number],input[type=password],input[type=search],input[type=tel],input[type=text],input[type=time],input[type=url],input[type=week]{-webkit-appearance:none;padding:.4em;display:inline-block;border:1px solid #ccc;font-size:80%;font-family:Inter,sans-serif;box-shadow:inset 0 1px 3px #ddd;border-radius:0;-webkit-transition:border .3s linear;-moz-transition:border .3s linear;transition:border .3s linear}input[type=datetime-local]{padding:.34375em .625em}input[disabled]{cursor:default}input[type=checkbox],input[type=radio]{padding:0;margin-right:.3125em;*height:13px;*width:13px}input[type=checkbox],input[type=radio],input[type=search]{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}input[type=search]::-webkit-search-cancel-button,input[type=search]::-webkit-search-decoration{-webkit-appearance:none}input[type=color]:focus,input[type=date]:focus,input[type=datetime-local]:focus,input[type=datetime]:focus,input[type=email]:focus,input[type=month]:focus,input[type=number]:focus,input[type=password]:focus,input[type=search]:focus,input[type=tel]:focus,input[type=text]:focus,input[type=time]:focus,input[type=url]:focus,input[type=week]:focus{outline:0;outline:thin dotted;border-color:#333}input.no-focus:focus{border-color:#ccc!important}input[type=checkbox]:focus,input[type=file]:focus,input[type=radio]:focus{outline:thin dotted #333;outline:1px auto #129fea}input[type=color][disabled],input[type=date][disabled],input[type=datetime-local][disabled],input[type=datetime][disabled],input[type=email][disabled],input[type=month][disabled],input[type=number][disabled],input[type=password][disabled],input[type=search][disabled],input[type=tel][disabled],input[type=text][disabled],input[type=time][disabled],input[type=url][disabled],input[type=week][disabled]{cursor:not-allowed;background-color:var(--bg)}input:focus:invalid,select:focus:invalid,textarea:focus:invalid{color:#e74c3c;border:1px solid #e74c3c}input:focus:invalid:focus,select:focus:invalid:focus,textarea:focus:invalid:focus{border-color:#e74c3c}input[type=checkbox]:focus:invalid:focus,input[type=file]:focus:invalid:focus,input[type=radio]:focus:invalid:focus{outline-color:#e74c3c}input.wy-input-large{padding:.8em;font-size:100%}textarea{overflow:auto;vertical-align:top;width:100%;font-family:Inter,sans-serif}select,textarea{padding:.5em .625em;display:inline-block;border:1px solid #ccc;font-size:80%;box-shadow:inset 0 1px 3px #ddd;-webkit-transition:border .3s linear;-moz-transition:border .3s linear;transition:border .3s linear}select{border:1px solid #ccc;background-color:var(--bg)}select[multiple]{height:auto}select:focus,textarea:focus{outline:0}input[readonly],select[disabled],select[readonly],textarea[disabled],textarea[readonly]{cursor:not-allowed;background-color:var(--bg)}input[type=checkbox][disabled],input[type=radio][disabled]{cursor:not-allowed}.wy-checkbox,.wy-radio{margin:.4em 0;color:var(--text);display:block}.wy-checkbox input,.wy-radio input{vertical-align:baseline}.wy-form-message-inline{display:inline-block;*display:inline;*zoom:1;vertical-align:middle}.wy-input-prefix,.wy-input-suffix{white-space:nowrap;padding:.4em}.wy-input-prefix .wy-input-context,.wy-input-suffix .wy-input-context{line-height:27px;padding:0 8px;display:inline-block;font-size:80%;background-color:var(--bg-panel);border:1px solid #ccc;color:#999}.wy-input-suffix .wy-input-context{border-left:0}.wy-input-prefix .wy-input-context{border-right:0}.wy-switch{position:relative;display:block;height:1.6em;margin-top:.8em;cursor:pointer}.wy-switch:before{left:0;top:0;width:2.4em;height:.8em;background:#ccc}.wy-switch:after,.wy-switch:before{position:absolute;content:"";display:block;border-radius:4px;-webkit-transition:all .2s ease-in-out;-moz-transition:all .2s ease-in-out;transition:all .2s ease-in-out}.wy-switch:after{width:18px;height:18px;background:#999;left:-3px;top:-3px}.wy-switch span{position:absolute;left:3.2em;display:block;font-size:12px;color:#ccc;line-height:1}.wy-switch.active:before{background:#1e8449}.wy-switch.active:after{left:1.6em;background:#27ae60}.wy-switch.disabled{cursor:not-allowed;opacity:.8}.wy-control-group.wy-control-group-error .wy-form-message,.wy-control-group.wy-control-group-error>label{color:#e74c3c}.wy-control-group.wy-control-group-error input[type=color],.wy-control-group.wy-control-group-error input[type=date],.wy-control-group.wy-control-group-error input[type=datetime-local],.wy-control-group.wy-control-group-error input[type=datetime],.wy-control-group.wy-control-group-error input[type=email],.wy-control-group.wy-control-group-error input[type=month],.wy-control-group.wy-control-group-error input[type=number],.wy-control-group.wy-control-group-error input[type=password],.wy-control-group.wy-control-group-error input[type=search],.wy-control-group.wy-control-group-error input[type=tel],.wy-control-group.wy-control-group-error input[type=text],.wy-control-group.wy-control-group-error input[type=time],.wy-control-group.wy-control-group-error input[type=url],.wy-control-group.wy-control-group-error input[type=week],.wy-control-group.wy-control-group-error textarea{border:1px solid #e74c3c}.wy-inline-validate{white-space:nowrap}.wy-inline-validate .wy-input-context{padding:.5em .625em;display:inline-block;font-size:80%}.wy-inline-validate.wy-inline-validate-success .wy-input-context{color:#27ae60}.wy-inline-validate.wy-inline-validate-danger .wy-input-context{color:#e74c3c}.wy-inline-validate.wy-inline-validate-warning .wy-input-context{color:#e67e22}.wy-inline-validate.wy-inline-validate-info .wy-input-context{color:#2980b9}.rotate-90{-webkit-transform:rotate(90deg);-moz-transform:rotate(90deg);-ms-transform:rotate(90deg);-o-transform:rotate(90deg);transform:rotate(90deg)}.rotate-180{-webkit-transform:rotate(180deg);-moz-transform:rotate(180deg);-ms-transform:rotate(180deg);-o-transform:rotate(180deg);transform:rotate(180deg)}.rotate-270{-webkit-transform:rotate(270deg);-moz-transform:rotate(270deg);-ms-transform:rotate(270deg);-o-transform:rotate(270deg);transform:rotate(270deg)}.mirror{-webkit-transform:scaleX(-1);-moz-transform:scaleX(-1);-ms-transform:scaleX(-1);-o-transform:scaleX(-1);transform:scaleX(-1)}.mirror.rotate-90{-webkit-transform:scaleX(-1) rotate(90deg);-moz-transform:scaleX(-1) rotate(90deg);-ms-transform:scaleX(-1) rotate(90deg);-o-transform:scaleX(-1) rotate(90deg);transform:scaleX(-1) rotate(90deg)}.mirror.rotate-180{-webkit-transform:scaleX(-1) rotate(180deg);-moz-transform:scaleX(-1) rotate(180deg);-ms-transform:scaleX(-1) rotate(180deg);-o-transform:scaleX(-1) rotate(180deg);transform:scaleX(-1) rotate(180deg)}.mirror.rotate-270{-webkit-transform:scaleX(-1) rotate(270deg);-moz-transform:scaleX(-1) rotate(270deg);-ms-transform:scaleX(-1) rotate(270deg);-o-transform:scaleX(-1) rotate(270deg);transform:scaleX(-1) rotate(270deg)}@media only screen and (max-width:480px){.wy-form button[type=submit]{margin:.7em 0 0}.wy-form input[type=color],.wy-form input[type=date],.wy-form input[type=datetime-local],.wy-form input[type=datetime],.wy-form input[type=email],.wy-form input[type=month],.wy-form input[type=number],.wy-form input[type=password],.wy-form input[type=search],.wy-form input[type=tel],.wy-form input[type=text],.wy-form input[type=time],.wy-form input[type=url],.wy-form input[type=week],.wy-form label{margin-bottom:.3em;display:block}.wy-form input[type=color],.wy-form input[type=date],.wy-form input[type=datetime-local],.wy-form input[type=datetime],.wy-form input[type=email],.wy-form input[type=month],.wy-form input[type=number],.wy-form input[type=password],.wy-form input[type=search],.wy-form input[type=tel],.wy-form input[type=time],.wy-form input[type=url],.wy-form input[type=week]{margin-bottom:0}.wy-form-aligned .wy-control-group label{margin-bottom:.3em;text-align:left;display:block;width:100%}.wy-form-aligned .wy-control{margin:1.5em 0 0}.wy-form-message,.wy-form-message-inline,.wy-form .wy-help-inline{display:block;font-size:80%;padding:.4em 0}}a{color:var(--link);text-decoration:none;cursor:pointer}a:hover{color:var(--text)}a:visited{color:var(--link)}html{height:100%}body,html{overflow-x:hidden}body{font-family:Inter,sans-serif;font-weight:400;color:var(--text);min-height:100%;background:var(--bg)}.wy-text-left{text-align:left}.wy-text-center{text-align:center}.wy-text-right{text-align:right}.wy-text-large{font-size:120%}.wy-text-normal{font-size:100%}.wy-text-small,small{font-size:80%}.wy-text-strike{text-decoration:line-through}.wy-text-warning{color:#e67e22!important}a.wy-text-warning:hover{color:#eb9950!important}.wy-text-info{color:#2980b9!important}a.wy-text-info:hover{color:#409ad5!important}.wy-text-success{color:#27ae60!important}a.wy-text-success:hover{color:#36d278!important}.wy-text-danger{color:#e74c3c!important}a.wy-text-danger:hover{color:#ed7669!important}.wy-text-neutral{color:var(--text)!important}.rst-content .toctree-wrapper>p.caption,h1,h2,h3,h4,h5,h6,legend{margin-top:0;font-weight:700;font-family:Inter,sans-serif}p{line-height:1.6em;font-size:1rem;margin:0 0 1.6em}h1{font-size:175%}.rst-content .toctree-wrapper>p.caption,h2{font-size:150%}h3{font-size:125%}h4{font-size:115%}h5{font-size:110%}h6{font-size:100%}hr{display:block;height:1px;border:0;border-top:1px solid var(--border);margin:1.6em 0;padding:0}.rst-content code,.rst-content tt,code{white-space:nowrap;max-width:100%;background:var(--bg);border:1px solid var(--bg);font-size:75%;padding:0 5px;font-family:Fira Mono,monospace;color:var(--code);overflow-x:auto}.rst-content code.code-large,.rst-content tt.code-large,code.code-large{font-size:90%}.rst-content .section ul,.rst-content .toctree-wrapper ul,.wy-plain-list-disc,article ul{list-style:disc;line-height:1.6em;margin-bottom:1.6em}.rst-content .section ul li,.rst-content .toctree-wrapper ul li,.wy-plain-list-disc li,article ul li{list-style:disc;margin-left:1.6em}.rst-content .section ul li p:last-child,.rst-content .section ul li ul,.rst-content .toctree-wrapper ul li p:last-child,.rst-content .toctree-wrapper ul li ul,.wy-plain-list-disc li p:last-child,.wy-plain-list-disc li ul,article ul li p:last-child,article ul li ul{margin-bottom:0}.rst-content .section ul li li,.rst-content .toctree-wrapper ul li li,.wy-plain-list-disc li li,article ul li li{list-style:circle}.rst-content .section ul li li li,.rst-content .toctree-wrapper ul li li li,.wy-plain-list-disc li li li,article ul li li li{list-style:square}.rst-content .section ul li ol li,.rst-content .toctree-wrapper ul li ol li,.wy-plain-list-disc li ol li,article ul li ol li{list-style:decimal}.rst-content .section ol,.rst-content ol.arabic,.wy-plain-list-decimal,article ol{list-style:decimal;line-height:1.6em;margin-bottom:1.6em}.rst-content .section ol li,.rst-content ol.arabic li,.wy-plain-list-decimal li,article ol li{list-style:decimal;margin-left:1.6em}.rst-content .section ol li p:last-child,.rst-content .section ol li ul,.rst-content ol.arabic li p:last-child,.rst-content ol.arabic li ul,.wy-plain-list-decimal li p:last-child,.wy-plain-list-decimal li ul,article ol li p:last-child,article ol li ul{margin-bottom:0}.rst-content .section ol li ul li,.rst-content ol.arabic li ul li,.wy-plain-list-decimal li ul li,article ol li ul li{list-style:disc}.wy-breadcrumbs{*zoom:1;padding:.5rem 0}.wy-breadcrumbs:after,.wy-breadcrumbs:before{display:table;content:""}.wy-breadcrumbs:after{clear:both}.wy-breadcrumbs li{display:inline-block;line-height:1.8;color:var(--nav)}.wy-breadcrumbs li.wy-breadcrumbs-aside{float:right}.wy-breadcrumbs li>a{display:inline-block;padding:0 .4rem;color:var(--nav);text-decoration:none!important}.wy-breadcrumbs li>a:active,.wy-breadcrumbs li>a:hover{color:var(--muted)!important}.wy-breadcrumbs li>a:first-child{padding-left:0}.rst-content .wy-breadcrumbs li code,.rst-content .wy-breadcrumbs li tt,.wy-breadcrumbs li .rst-content code,.wy-breadcrumbs li .rst-content tt,.wy-breadcrumbs li code{padding:5px;border:none;background:none}.rst-content .wy-breadcrumbs li tt.literal,.wy-breadcrumbs li .rst-content tt.literal,.wy-breadcrumbs li code.literal{color:var(--text)}.wy-breadcrumbs-extra{margin-bottom:0;color:var(--text);font-size:80%;display:inline-block}.rst-content .code-block-caption .wy-breadcrumbs-aside .headerlink,.rst-content .wy-breadcrumbs-aside .admonition-title,.rst-content code.download .wy-breadcrumbs-aside span:first-child,.rst-content dl dt .wy-breadcrumbs-aside .headerlink,.rst-content h1 .wy-breadcrumbs-aside .headerlink,.rst-content h2 .wy-breadcrumbs-aside .headerlink,.rst-content h3 .wy-breadcrumbs-aside .headerlink,.rst-content h4 .wy-breadcrumbs-aside .headerlink,.rst-content h5 .wy-breadcrumbs-aside .headerlink,.rst-content h6 .wy-breadcrumbs-aside .headerlink,.rst-content p.caption .wy-breadcrumbs-aside .headerlink,.rst-content table>caption .wy-breadcrumbs-aside .headerlink,.rst-content tt.download .wy-breadcrumbs-aside span:first-child,.wy-breadcrumbs-aside .fa,.wy-breadcrumbs-aside .icon,.wy-breadcrumbs-aside .rst-content .admonition-title,.wy-breadcrumbs-aside .rst-content .code-block-caption .headerlink,.wy-breadcrumbs-aside .rst-content code.download span:first-child,.wy-breadcrumbs-aside .rst-content dl dt .headerlink,.wy-breadcrumbs-aside .rst-content h1 .headerlink,.wy-breadcrumbs-aside .rst-content h2 .headerlink,.wy-breadcrumbs-aside .rst-content h3 .headerlink,.wy-breadcrumbs-aside .rst-content h4 .headerlink,.wy-breadcrumbs-aside .rst-content h5 .headerlink,.wy-breadcrumbs-aside .rst-content h6 .headerlink,.wy-breadcrumbs-aside .rst-content p.caption .headerlink,.wy-breadcrumbs-aside .rst-content table>caption .headerlink,.wy-breadcrumbs-aside .rst-content tt.download span:first-child,.wy-breadcrumbs-aside .wy-menu-vertical li.current>a span.toctree-expand,.wy-breadcrumbs-aside .wy-menu-vertical li.on a span.toctree-expand,.wy-breadcrumbs-aside .wy-menu-vertical li span.toctree-expand,.wy-menu-vertical li.current>a .wy-breadcrumbs-aside span.toctree-expand,.wy-menu-vertical li.on a .wy-breadcrumbs-aside span.toctree-expand,.wy-menu-vertical li .wy-breadcrumbs-aside span.toctree-expand{font-size:1.2rem}@media screen and (max-width:480px){.wy-breadcrumbs-extra,.wy-breadcrumbs li.wy-breadcrumbs-aside{display:none}}@media print{.wy-breadcrumbs li.wy-breadcrumbs-aside{display:none}}html{font-size:14px;background-color:var(--bg)}@media screen and (min-width:480px){html{font-size:15px}}@media screen and (min-width:1400px){html{font-size:16px}}@media screen and (min-width:1700px){html{font-size:17px}}@media screen and (min-width:1900px){html{font-size:18px}}body{background-color:var(--bg);font-weight:calc(450*var(--font-weight-factor))}.wy-affix{position:fixed;top:2.5em}[data-menu-wrap]{-webkit-transition:all .2s ease-in;-moz-transition:all .2s ease-in;transition:all .2s ease-in;position:absolute;opacity:1;width:100%;opacity:0}[data-menu-wrap].move-center{left:0;right:auto;opacity:1}[data-menu-wrap].move-left{right:auto;left:-100%;opacity:0}[data-menu-wrap].move-right{right:-100%;left:auto;opacity:0}.wy-body-for-nav{background:var(--bg)}.wy-grid-for-nav{position:absolute;width:100%;height:100%}.wy-nav-side{position:fixed;top:0;bottom:0;left:0;padding-bottom:0!important;width:23rem;overflow-x:hidden;overflow-y:hidden;min-height:100%;color:var(--text);background:var(--bg-nav);z-index:200}.wy-side-scroll{width:24.5rem;position:relative;overflow-x:hidden;overflow-y:scroll;height:100%}.wy-side-scroll:before{content:"";position:fixed;display:none;width:23rem;height:100%;top:0;left:0;pointer-events:none;border-right:1px solid var(--border)}.shift .wy-side-scroll:before{display:block}@media screen and (min-width:63.75rem){.wy-side-scroll:before{display:block}}.wy-nav-top{display:none;background:var(--bg-nav);color:var(--nav);padding:0 1.25em;border-bottom:1px solid var(--border);position:relative;line-height:4rem;text-align:left;font-size:100%;*zoom:1}.wy-nav-top:after,.wy-nav-top:before{display:table;content:""}.wy-nav-top:after{clear:both}.wy-nav-top a{color:inherit;font-weight:calc(700*var(--font-weight-factor));margin:1rem 1.25em}.wy-nav-top img{height:2rem}.wy-nav-top i{font-size:1.25rem;line-height:inherit;float:left;cursor:pointer;padding-top:inherit;padding-right:inherit;border-right:1px solid var(--border)}.wy-nav-content-wrap{margin-left:23rem;background:var(--bg);min-height:100%}.wy-nav-content{padding:1.75rem 2.5em 2.5em;height:100%;max-width:52rem;margin:auto}.wy-body-mask{position:fixed;width:100%;height:100%;background:rgba(0,0,0,.2);display:none;z-index:499}.wy-body-mask.on{display:block}footer{margin-top:5rem;color:var(--muted)}footer a{text-decoration:underline}footer a,footer a:visited{color:inherit}footer a:hover{color:inherit;text-decoration:none}footer p{margin-bottom:.8em}.rst-content footer span.commit code,.rst-content footer span.commit tt,footer span.commit .rst-content code,footer span.commit .rst-content tt,footer span.commit code{padding:0;font-family:Fira Mono,monospace;font-size:1em;background:none;border:none;color:var(--muted)}.rst-footer-buttons{margin:5rem 0;*zoom:1}.rst-footer-buttons:after,.rst-footer-buttons:before{width:100%;display:table;content:""}.rst-footer-buttons:after{clear:both}.rst-footer-buttons .btn{padding:0;line-height:2.5rem;box-shadow:none;border:none;font-weight:calc(700*var(--font-weight-factor))}.rst-footer-buttons .btn:hover{color:var(--muted)!important;background-color:var(--bg)!important}.rst-breadcrumbs-buttons{margin-top:12px;*zoom:1}.rst-breadcrumbs-buttons:after,.rst-breadcrumbs-buttons:before{display:table;content:""}.rst-breadcrumbs-buttons:after{clear:both}#search-results .search li{margin-bottom:1.5rem}#search-results .search li a{text-decoration:none}#search-results .context{color:var(--text);line-height:1.6em}.genindextable li>ul{margin-left:1.6em}@media screen and (max-width:63.75rem){.wy-body-for-nav{background:var(--bg)}.wy-nav-top{display:block}.wy-nav-side{left:-23rem}.wy-nav-side.shift{width:23rem;left:0}.wy-nav-content-wrap{margin-left:0}.wy-nav-content-wrap .wy-nav-content{padding:.75rem 1.25em 2.5em}.wy-nav-content-wrap.shift{position:fixed;min-width:100%;left:23rem;top:0;height:100%;overflow:hidden}}@media screen and (min-width:63.75rem){.wy-nav-content{margin:0;background:var(--bg)}}@media screen and (min-width:109.5rem){.wy-nav-content-wrap{margin-left:0}.wy-nav-content{margin-left:auto;margin-right:auto}}@media print{.rst-versions,.wy-nav-side,footer{display:none}.wy-nav-content-wrap{margin-left:0}}.ethical-rtd{padding-left:2.5em!important;padding-bottom:4rem!important}.ethical-sidebar{background-color:var(--bg)!important;border:1px solid var(--border)!important;padding:1rem!important}.ethical-dark-theme .ethical-text>a,.ethical-sidebar{color:var(--text)!important}.rst-versions,.rst-versions.rst-badge{position:fixed;bottom:0!important;left:0!important;width:calc(23rem - 1px)!important;max-width:calc(23rem - 1px)!important;color:var(--nav)!important;background:var(--bg-nav)!important;border-top:1px solid var(--border)!important;font-family:Inter,sans-serif;z-index:400;line-height:30px;box-shadow:1px 0 0 var(--border)}.rst-versions.rst-badge a,.rst-versions a{color:var(--nav)!important;text-decoration:none}.rst-versions .rst-badge-small,.rst-versions.rst-badge .rst-badge-small{display:none}.rst-versions.rst-badge .rst-current-version,.rst-versions .rst-current-version{padding:.4rem .7rem!important;background-color:var(--bg-nav)!important;display:block!important;text-align:right!important;font-size:.9rem!important;cursor:pointer;color:var(--nav)!important;line-height:30px!important;height:auto!important;*zoom:1}.rst-versions.rst-badge .rst-current-version:after,.rst-versions.rst-badge .rst-current-version:before,.rst-versions .rst-current-version:after,.rst-versions .rst-current-version:before{display:table;content:""}.rst-versions.rst-badge .rst-current-version:after,.rst-versions .rst-current-version:after{clear:both}.rst-content .code-block-caption .rst-versions .rst-current-version .headerlink,.rst-content .rst-versions .rst-current-version .admonition-title,.rst-content code.download .rst-versions .rst-current-version span:first-child,.rst-content dl dt .rst-versions .rst-current-version .headerlink,.rst-content h1 .rst-versions .rst-current-version .headerlink,.rst-content h2 .rst-versions .rst-current-version .headerlink,.rst-content h3 .rst-versions .rst-current-version .headerlink,.rst-content h4 .rst-versions .rst-current-version .headerlink,.rst-content h5 .rst-versions .rst-current-version .headerlink,.rst-content h6 .rst-versions .rst-current-version .headerlink,.rst-content p.caption .rst-versions .rst-current-version .headerlink,.rst-content table>caption .rst-versions .rst-current-version .headerlink,.rst-content tt.download .rst-versions .rst-current-version span:first-child,.rst-versions.rst-badge .rst-current-version .fa,.rst-versions .rst-current-version .fa,.rst-versions .rst-current-version .icon,.rst-versions .rst-current-version .rst-content .admonition-title,.rst-versions .rst-current-version .rst-content .code-block-caption .headerlink,.rst-versions .rst-current-version .rst-content code.download span:first-child,.rst-versions .rst-current-version .rst-content dl dt .headerlink,.rst-versions .rst-current-version .rst-content h1 .headerlink,.rst-versions .rst-current-version .rst-content h2 .headerlink,.rst-versions .rst-current-version .rst-content h3 .headerlink,.rst-versions .rst-current-version .rst-content h4 .headerlink,.rst-versions .rst-current-version .rst-content h5 .headerlink,.rst-versions .rst-current-version .rst-content h6 .headerlink,.rst-versions .rst-current-version .rst-content p.caption .headerlink,.rst-versions .rst-current-version .rst-content table>caption .headerlink,.rst-versions .rst-current-version .rst-content tt.download span:first-child,.rst-versions .rst-current-version .wy-menu-vertical li span.toctree-expand,.wy-menu-vertical li .rst-versions .rst-current-version span.toctree-expand{color:var(--nav)!important}.rst-versions.rst-badge .rst-current-version .fa-book,.rst-versions .rst-current-version .fa-book,.rst-versions .rst-current-version .icon-book{font-size:inherit;line-height:inherit;float:left!important}.rst-versions.rst-badge .rst-current-version.rst-out-of-date,.rst-versions .rst-current-version.rst-out-of-date{background-color:var(--nav)!important;color:var(--bg)!important}.rst-versions.rst-badge .rst-current-version.rst-active-old-version,.rst-versions .rst-current-version.rst-active-old-version{background-color:var(--nav)!important;color:var(--nav)!important}.rst-versions.rst-badge.shift-up,.rst-versions.shift-up{height:auto!important;max-height:100%}.rst-versions.rst-badge.shift-up .rst-other-versions,.rst-versions.shift-up .rst-other-versions{display:block}.rst-versions.rst-badge .rst-other-versions,.rst-versions .rst-other-versions{font-size:.9rem;padding:.4rem .7rem;color:var(--nav)!important;display:none}.rst-versions.rst-badge .rst-other-versions hr,.rst-versions .rst-other-versions hr{display:block;height:1px;border:0;margin:20px 0;padding:0;border-top:1px solid var(--border)!important}.rst-versions.rst-badge .rst-other-versions dd,.rst-versions .rst-other-versions dd{display:inline-block;margin:0}.rst-versions.rst-badge .rst-other-versions dd a,.rst-versions .rst-other-versions dd a{display:inline-block;padding:.4em;line-height:1.5;color:var(--nav)!important}@media screen and (max-width:63.75rem){.rst-versions{display:none}.rst-versions.shift{display:block}}.rst-content #search-results .toctree-wrapper>p.caption,.rst-content #search-results h2,.rst-content h1{font-size:2.6rem;line-height:1.2;letter-spacing:-.02em;margin-top:6.4rem;margin-bottom:2.68rem;font-weight:calc(700*var(--font-weight-factor));color:var(--headline);max-width:100%;overflow-wrap:break-word}.rst-content .toctree-wrapper>p.caption,.rst-content h2{font-size:1.8rem;line-height:1.25;letter-spacing:-.01em;margin-top:3.125rem;margin-bottom:1.5rem!important;font-weight:calc(680*var(--font-weight-factor))}.rst-content h3,.rst-content ul.search a{font-size:1.5rem;line-height:1.3;letter-spacing:-.01em;margin-top:2.5rem;margin-bottom:1.5rem!important;font-weight:calc(620*var(--font-weight-factor));color:var(--headline)}.rst-content h3:after,.rst-content ul.search a:after{border:none!important}.rst-content ul.search li a{display:inline-block;margin:1rem 0 .75rem!important}.rst-content h4{font-size:1.3rem;line-height:1.4;letter-spacing:-.01em}.rst-content h4,.rst-content h5{margin-top:1.5rem;margin-bottom:1.5rem!important;font-weight:calc(450*var(--font-weight-factor));color:var(--headline)}.rst-content h5{font-size:1.15rem;line-height:1.5}.rst-content hr{margin:2.5rem 0;border-top:1px solid var(--border)}.rst-content a{-webkit-transition:color .2s;-moz-transition:color .2s;transition:color .2s}.rst-content .document dt a,.rst-content .document li a,.rst-content .document p a,.rst-content .large p>a,.rst-content footer a:not([class]){position:relative;color:var(--link);font-weight:calc(580*var(--font-weight-factor));text-decoration:none}.rst-content .document dt a:visited,.rst-content .document li a:visited,.rst-content .document p a:visited,.rst-content .large p>a:visited,.rst-content footer a:not([class]):visited{color:var(--link)}.rst-content .document dt a:hover,.rst-content .document li a:hover,.rst-content .document p a:hover,.rst-content .large p>a:hover,.rst-content footer a:not([class]):hover{text-decoration:none}.rst-content .document dt a:hover:after,.rst-content .document li a:hover:after,.rst-content .document p a:hover:after,.rst-content .large p>a:hover:after,.rst-content footer a:not([class]):hover:after{opacity:.8}.rst-content .document dt a:after,.rst-content .document li a:after,.rst-content .document p a:after,.rst-content .large p>a:after,.rst-content footer a:not([class]):after{content:"";position:absolute;left:0;right:0;bottom:-3px;border-bottom:1px solid;opacity:.3;-webkit-transition:opacity .2s;-moz-transition:opacity .2s;transition:opacity .2s}.rst-content p{margin-bottom:1.5rem}.rst-content img{max-width:100%;height:auto}.rst-content div.figure{margin-bottom:1.5rem}.rst-content div.figure p.caption{font-style:italic}.rst-content div.figure p:last-child.caption{margin-bottom:0}.rst-content div.figure.align-center{text-align:center}.rst-content .section>a>img,.rst-content .section>img{margin-bottom:1.5rem}.rst-content abbr[title]{text-decoration:none}.rst-content.style-external-links a.reference.external:after{font-family:FontAwesome;content:"";color:var(--link);vertical-align:super;font-size:60%;margin:0 .2em}.rst-content blockquote{margin-left:1.5rem;line-height:1.6em;margin-bottom:1.5rem}.rst-content pre.literal-block{white-space:pre;margin:0;padding:1.5rem;font-family:Fira Mono,monospace;display:block;overflow:auto}.rst-content div[class^=highlight],.rst-content pre.literal-block{border:1px solid var(--border);overflow-x:auto;margin:1px 0 1.5rem}.rst-content div[class^=highlight] div[class^=highlight],.rst-content pre.literal-block div[class^=highlight]{padding:0;border:none;margin:0}.rst-content div[class^=highlight] td.code{width:100%}.rst-content .linenodiv pre{border-right:1px solid var(--border);margin:0;padding:1rem;font-family:Fira Mono,monospace;user-select:none;pointer-events:none;color:var(--muted)}.rst-content div[class^=highlight] pre{white-space:pre;margin:0;padding:1rem;display:block;overflow:auto}.rst-content div[class^=highlight] pre .hll{display:block;margin:0 -1rem;padding:0 1rem}.rst-content .linenodiv pre,.rst-content div[class^=highlight] pre,.rst-content pre.literal-block{font-family:Fira Mono,monospace;font-size:12px;line-height:1.4}.rst-content div.highlight .gp{user-select:none;pointer-events:none}.rst-content .code-block-caption{font-style:italic;font-size:85%;line-height:1;padding:1em 0;text-align:center}@media print{.rst-content .codeblock,.rst-content div[class^=highlight],.rst-content div[class^=highlight] pre{white-space:pre-wrap}}.rst-content .admonition{margin:1.5rem 0;padding:1.25rem;clear:both;background-color:var(--info-bg);color:var(--info-text);font-weight:calc(450*var(--font-weight-factor))}.rst-content .admonition>*{margin:0}.rst-content .admonition-title{display:block;margin:0 0 1.25rem;color:var(--info-text)}.rst-content .admonition-title:before{margin-right:1rem;font-size:120%}.rst-content .hint,.rst-content .important,.rst-content .note,.rst-content .seealso,.rst-content .tip{color:var(--info-text);background-color:var(--info-bg)}.rst-content .hint .admonition-title,.rst-content .important .admonition-title,.rst-content .note .admonition-title,.rst-content .seealso .admonition-title,.rst-content .tip .admonition-title{color:inherit;background-color:transparent;font-weight:calc(450*var(--font-weight-factor))}.rst-content .danger,.rst-content .error{color:var(--danger-text);background-color:var(--danger-bg)}.rst-content .danger .admonition-title,.rst-content .error .admonition-title{color:inherit;background-color:transparent;font-weight:calc(450*var(--font-weight-factor))}.rst-content .admonition-todo,.rst-content .attention,.rst-content .caution,.rst-content .warning{color:var(--warning-text);background-color:var(--warning-bg)}.rst-content .admonition-todo .admonition-title,.rst-content .attention .admonition-title,.rst-content .caution .admonition-title,.rst-content .warning .admonition-title{color:inherit;background-color:transparent;font-weight:calc(450*var(--font-weight-factor))}.rst-content .admonition table{border-color:var(--border)}.rst-content .admonition table td,.rst-content .admonition table th{background:transparent!important;border-color:var(--border)!important}.rst-content .section ol.loweralpha,.rst-content .section ol.loweralpha>li{list-style:lower-alpha}.rst-content .section ol.upperalpha,.rst-content .section ol.upperalpha>li{list-style:upper-alpha}.rst-content .section ol li>*,.rst-content .section ul li>*{margin-top:1rem;margin-bottom:1rem}.rst-content .section ol li>:first-child,.rst-content .section ul li>:first-child{margin-top:0}.rst-content .section ol li>p,.rst-content .section ol li>p:last-child,.rst-content .section ul li>p,.rst-content .section ul li>p:last-child{margin-bottom:1rem}.rst-content .section ol li>p:only-child,.rst-content .section ol li>p:only-child:last-child,.rst-content .section ul li>p:only-child,.rst-content .section ul li>p:only-child:last-child{margin-bottom:0}.rst-content .section ol li>ol,.rst-content .section ol li>ul,.rst-content .section ul li>ol,.rst-content .section ul li>ul{margin-bottom:1rem}.rst-content .section ol,.rst-content .section ul,.rst-content .wy-plain-list-decimal,.rst-content ol.arabic{margin-bottom:1.5rem}.rst-content .section ol>li,.rst-content .section ul>li,.rst-content .wy-plain-list-decimal>li,.rst-content ol.arabic>li{margin-left:2rem;padding-left:.3rem}.rst-content .section ol.simple li>*,.rst-content .section ol.simple li ol,.rst-content .section ol.simple li ul,.rst-content .section ul.simple li>*,.rst-content .section ul.simple li ol,.rst-content .section ul.simple li ul{margin-top:0;margin-bottom:0}.rst-content .line-block{margin-left:0;margin-bottom:1.5rem;line-height:1.6em}.rst-content .line-block .line-block{margin-left:1.5rem;margin-bottom:0}.rst-content .topic-title{font-weight:700;margin-bottom:1rem}.rst-content .toc-backref{color:var(--text)}.rst-content .align-right{float:right;margin:0 0 1.5rem 1.5rem}.rst-content .align-left{float:left;margin:0 1.5rem 1.5rem 0}.rst-content .align-center{margin:auto}.rst-content .align-center:not(table){display:block}.rst-content .code-block-caption .headerlink,.rst-content dl dt .headerlink,.rst-content h1 .headerlink,.rst-content h2 .headerlink,.rst-content h3 .headerlink,.rst-content h4 .headerlink,.rst-content h5 .headerlink,.rst-content h6 .headerlink,.rst-content p.caption .headerlink,.rst-content table>caption .headerlink{visibility:hidden;font-size:1rem}.rst-content .code-block-caption .headerlink:after,.rst-content dl dt .headerlink:after,.rst-content h1 .headerlink:after,.rst-content h2 .headerlink:after,.rst-content h3 .headerlink:after,.rst-content h4 .headerlink:after,.rst-content h5 .headerlink:after,.rst-content h6 .headerlink:after,.rst-content p.caption .headerlink:after,.rst-content table>caption .headerlink:after{content:"";font-family:FontAwesome}.rst-content .code-block-caption:hover .headerlink:after,.rst-content dl dt:hover .headerlink:after,.rst-content h1:hover .headerlink:after,.rst-content h2:hover .headerlink:after,.rst-content h3:hover .headerlink:after,.rst-content h4:hover .headerlink:after,.rst-content h5:hover .headerlink:after,.rst-content h6:hover .headerlink:after,.rst-content p.caption:hover .headerlink:after,.rst-content table>caption:hover .headerlink:after{visibility:visible;color:var(--muted)!important;text-decoration:none}.rst-content table>caption .headerlink:after{font-size:12px}.rst-content .centered{text-align:center}.rst-content .sidebar{float:right;width:40%;display:block;margin:0 0 1.5rem 1.5rem;padding:1.5rem;background:var(--bg);border:1px solid var(--border)}.rst-content .sidebar dl,.rst-content .sidebar p,.rst-content .sidebar ul{font-size:90%}.rst-content .sidebar .last,.rst-content .sidebar>:last-child{margin-bottom:0}.rst-content .sidebar .sidebar-title{display:block;font-family:Inter,sans-serif;font-weight:calc(700*var(--font-weight-factor));background:var(--bg-panel);padding:.5rem 1rem;margin:-1.5rem -1.5rem 1.5rem;font-size:100%}.rst-content .highlighted{color:var(--text)!important;background:var(--bg-highlight)!important;box-shadow:0 0 0 1px var(--bg-highlight);display:inline;font-weight:calc(700*var(--font-weight-factor))}.rst-content .citation-reference,.rst-content .footnote-reference{vertical-align:baseline;position:relative;top:-.4em;line-height:0;font-size:90%}.rst-content .hlist{width:100%}html.writer-html4 .rst-content table.docutils.citation,html.writer-html4 .rst-content table.docutils.footnote{background:none;border:none}html.writer-html4 .rst-content table.docutils.citation td,html.writer-html4 .rst-content table.docutils.citation tr,html.writer-html4 .rst-content table.docutils.footnote td,html.writer-html4 .rst-content table.docutils.footnote tr{border:none;background-color:transparent!important;white-space:normal}html.writer-html4 .rst-content table.docutils.citation td.label,html.writer-html4 .rst-content table.docutils.footnote td.label{padding-left:0;padding-right:0;vertical-align:top}html.writer-html5 .rst-content dl dt span.classifier:before{content:" : "}html.writer-html5 .rst-content dl.field-list,html.writer-html5 .rst-content dl.footnote{display:grid;grid-row-gap:.7rem;grid-column-gap:1.5rem;grid-template-columns:max-content auto;align-items:start;margin:1rem 0}html.writer-html5 .rst-content dl.field-list>dt,html.writer-html5 .rst-content dl.footnote>dt{padding-right:1rem}html.writer-html5 .rst-content dl.field-list>dt:after,html.writer-html5 .rst-content dl.footnote>dt:after{content:":"}html.writer-html5 .rst-content dl.field-list>dd,html.writer-html5 .rst-content dl.field-list>dt,html.writer-html5 .rst-content dl.footnote>dd,html.writer-html5 .rst-content dl.footnote>dt{margin:0;padding:0}html.writer-html5 .rst-content dl.field-list>dd>p,html.writer-html5 .rst-content dl.field-list>dd>ul,html.writer-html5 .rst-content dl.field-list>dt>p,html.writer-html5 .rst-content dl.field-list>dt>ul,html.writer-html5 .rst-content dl.footnote>dd>p,html.writer-html5 .rst-content dl.footnote>dd>ul,html.writer-html5 .rst-content dl.footnote>dt>p,html.writer-html5 .rst-content dl.footnote>dt>ul{margin:0}html.writer-html5 .rst-content dl.field-list>dd>p>li,html.writer-html5 .rst-content dl.field-list>dd>ul>li,html.writer-html5 .rst-content dl.field-list>dt>p>li,html.writer-html5 .rst-content dl.field-list>dt>ul>li,html.writer-html5 .rst-content dl.footnote>dd>p>li,html.writer-html5 .rst-content dl.footnote>dd>ul>li,html.writer-html5 .rst-content dl.footnote>dt>p>li,html.writer-html5 .rst-content dl.footnote>dt>ul>li{margin-left:1.2rem;padding-left:0}html.writer-html5 .rst-content dl.field-list>dd code,html.writer-html5 .rst-content dl.field-list>dd tt,html.writer-html5 .rst-content dl.field-list>dt code,html.writer-html5 .rst-content dl.field-list>dt tt,html.writer-html5 .rst-content dl.footnote>dd code,html.writer-html5 .rst-content dl.footnote>dd tt,html.writer-html5 .rst-content dl.footnote>dt code,html.writer-html5 .rst-content dl.footnote>dt tt{padding:0}html.writer-html5 .rst-content dl.footnote{font-size:.9rem}html.writer-html5 .rst-content dl.footnote>dt{margin:0 .5rem .5rem 0;line-height:1.2rem;word-break:break-all;font-weight:calc(450*var(--font-weight-factor))}html.writer-html5 .rst-content dl.footnote>dt>span.brackets{margin-right:.5rem}html.writer-html5 .rst-content dl.footnote>dt>span.brackets:before{content:"["}html.writer-html5 .rst-content dl.footnote>dt>span.brackets:after{content:"]"}html.writer-html5 .rst-content dl.footnote>dt>span.brackets>a{text-decoration:none}html.writer-html5 .rst-content dl.footnote>dt>span.fn-backref{font-style:italic}html.writer-html5 .rst-content dl.footnote>dd{margin:0 0 1.5rem;line-height:1.2rem}html.writer-html5 .rst-content dl.footnote>dd p,html.writer-html5 .rst-content dl.option-list kbd{font-size:.9rem}.rst-content table.docutils.footnote,.rst-content table.docutils.footnote code,.rst-content table.docutils.footnote tt,html.writer-html4 .rst-content table.docutils.citation,html.writer-html4 .rst-content table.docutils.citation code,html.writer-html4 .rst-content table.docutils.citation tt,html.writer-html5 .rst-content dl.footnote,html.writer-html5 .rst-content dl.footnote code,html.writer-html5 .rst-content dl.footnote tt{color:var(--muted)}.rst-content .wy-table-responsive.citation,.rst-content .wy-table-responsive.footnote{margin-bottom:0}.rst-content .wy-table-responsive.citation+:not(.citation),.rst-content .wy-table-responsive.footnote+:not(.footnote){margin-top:1.5rem}.rst-content .wy-table-responsive.citation:last-child,.rst-content .wy-table-responsive.footnote:last-child{margin-bottom:1.5rem}.rst-content table.docutils{width:100%;max-width:100%!important}.rst-content table.docutils th{border-color:var(--border)}html.writer-html5 .rst-content table.docutils th{border:1px solid var(--border)}html.writer-html5 .rst-content table.docutils td,html.writer-html5 .rst-content table.docutils th{vertical-align:top;padding:.7rem .85rem}html.writer-html5 .rst-content table.docutils td>p,html.writer-html5 .rst-content table.docutils th>p{line-height:1.6em;margin-bottom:0;font-size:.9rem;white-space:normal}.rst-content table.docutils td .last,.rst-content table.docutils td .last>:last-child{margin-bottom:0}.rst-content table.field-list,.rst-content table.field-list td{border:none}.rst-content table.field-list td p{font-size:inherit;line-height:inherit}.rst-content table.field-list td>strong{display:inline-block}.rst-content table.field-list .field-name{padding-right:10px;text-align:left;white-space:nowrap}.rst-content table.field-list .field-body{text-align:left}.rst-content code,.rst-content tt{color:var(--code);font-family:Fira Mono,monospace;padding:2px 5px}.rst-content code big,.rst-content code em,.rst-content tt big,.rst-content tt em{font-size:100%!important;line-height:normal}.rst-content code.literal,.rst-content tt.literal{color:var(--code)}.rst-content code.xref,.rst-content tt.xref,a .rst-content code,a .rst-content tt{font-weight:calc(700*var(--font-weight-factor));color:var(--code)}.rst-content kbd,.rst-content pre,.rst-content samp{font-family:Fira Mono,monospace}.rst-content a code,.rst-content a tt{color:var(--link)}.rst-content dl{margin-bottom:1.5rem}.rst-content dl dt{font-weight:calc(500*var(--font-weight-factor));margin-bottom:1rem}.rst-content dl>dd>[class*=highlight],.rst-content dl ol,.rst-content dl p,.rst-content dl table,.rst-content dl ul{margin-bottom:1rem}.rst-content dl dd{margin:0 0 1rem 1.5rem;line-height:1.6em}html.writer-html4 .rst-content dl:not(.docutils),html.writer-html5 .rst-content dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.glossary){margin-bottom:1.5rem}html.writer-html4 .rst-content dl:not(.docutils)>dt,html.writer-html5 .rst-content dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.glossary)>dt{display:table;margin:1.5rem 0 1rem!important;font-size:1rem;line-height:1.6;background:var(--bg-code);color:var(--code);padding:.4rem .8rem;position:relative}html.writer-html4 .rst-content dl:not(.docutils)>dt em.property,html.writer-html5 .rst-content dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.glossary)>dt em.property{display:inline!important}html.writer-html4 .rst-content dl:not(.docutils)>dt .headerlink,html.writer-html5 .rst-content dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.glossary)>dt .headerlink{color:var(--text);font-size:100%!important;text-decoration:none!important}html.writer-html4 .rst-content dl:not(.docutils)>dt a,html.writer-html5 .rst-content dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.glossary)>dt a{margin-left:.5rem}html.writer-html4 .rst-content dl:not(.docutils)>dt a:after,html.writer-html5 .rst-content dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.glossary)>dt a:after{border-bottom:none!important}html.writer-html4 .rst-content dl:not(.docutils).class>dt,html.writer-html5 .rst-content dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.glossary).class>dt{padding:.6rem 1.2rem;font-size:1rem;line-height:1.6;background:var(--info-bg);color:var(--info-text)}html.writer-html4 .rst-content dl:not(.docutils).class>dt *,html.writer-html5 .rst-content dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.glossary).class>dt *{color:inherit}html.writer-html4 .rst-content dl:not(.docutils) dl:not(.field-list)>dt a:after,html.writer-html5 .rst-content dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.glossary) dl:not(.field-list)>dt a:after{border-bottom:none!important}html.writer-html4 .rst-content dl:not(.docutils) dl:not(.field-list)>dt .headerlink,html.writer-html5 .rst-content dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.glossary) dl:not(.field-list)>dt .headerlink{color:var(--text);font-size:100%!important}html.writer-html4 .rst-content dl:not(.docutils)>dt:first-child,html.writer-html5 .rst-content dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.glossary)>dt:first-child{margin-top:0}html.writer-html4 .rst-content dl:not(.docutils) code,html.writer-html4 .rst-content dl:not(.docutils) tt,html.writer-html5 .rst-content dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.glossary) code,html.writer-html5 .rst-content dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.glossary) tt{font-weight:calc(500*var(--font-weight-factor))}html.writer-html4 .rst-content dl:not(.docutils) code.descclassname,html.writer-html4 .rst-content dl:not(.docutils) code.descname,html.writer-html4 .rst-content dl:not(.docutils) tt.descclassname,html.writer-html4 .rst-content dl:not(.docutils) tt.descname,html.writer-html5 .rst-content dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.glossary) code.descclassname,html.writer-html5 .rst-content dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.glossary) code.descname,html.writer-html5 .rst-content dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.glossary) tt.descclassname,html.writer-html5 .rst-content dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.glossary) tt.descname{color:inherit;background-color:inherit!important;border:none;padding:0;font-size:100%!important}html.writer-html4 .rst-content dl:not(.docutils) code.descname,html.writer-html4 .rst-content dl:not(.docutils) tt.descname,html.writer-html5 .rst-content dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.glossary) code.descname,html.writer-html5 .rst-content dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.glossary) tt.descname{font-weight:calc(500*var(--font-weight-factor))}html.writer-html4 .rst-content dl:not(.docutils) .optional,html.writer-html5 .rst-content dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.glossary) .optional{display:inline-block;padding:0 4px;color:var(--text);font-weight:calc(500*var(--font-weight-factor))}html.writer-html4 .rst-content dl:not(.docutils) .property,html.writer-html5 .rst-content dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.glossary) .property{display:inline-block;padding-right:8px}.rst-content .viewcode-back,.rst-content .viewcode-link{display:inline-block;color:inherit;font-size:80%;opacity:.6}.rst-content .viewcode-back{display:block;float:right}.rst-content p.rubric{margin-bottom:1.5rem;font-weight:calc(500*var(--font-weight-factor))}.rst-content code.download,.rst-content tt.download{background:inherit;padding:inherit;font-weight:400;font-family:inherit;font-size:inherit;color:inherit;border:inherit;white-space:inherit}.rst-content code.download span:first-child,.rst-content tt.download span:first-child{-webkit-font-smoothing:subpixel-antialiased}.rst-content code.download span:first-child:before,.rst-content tt.download span:first-child:before{margin-right:4px}.rst-content .guilabel{border:1px solid var(--border);background:var(--bg-panel);font-size:80%;font-weight:calc(500*var(--font-weight-factor));padding:.2rem .3rem;margin:auto .15rem}.rst-content .versionmodified{font-style:italic}@media screen and (max-width:480px){.rst-content .sidebar{width:100%}}@media screen and (max-width:63.75rem){#search-results .rst-content .toctree-wrapper>p.caption,#search-results h2,.rst-content #search-results .toctree-wrapper>p.caption,html:not(.landing) h1{margin-top:2.5rem!important}}span[id*=MathJax-Span]{color:var(--text)}.math{text-align:center}@font-face{font-family:Inter;font-style:normal;font-weight:200 800;font-display:swap;src:url(fonts/inter-roman-var.woff2) format("woff2")}@font-face{font-family:Inter;font-style:italic;font-weight:200 800;font-display:swap;src:url(fonts/inter-italic-var.woff2) format("woff2")}@font-face{font-family:Fira Mono;font-style:normal;font-display:swap;font-weight:400;src:local("Fira Mono Regular "),local("Fira Mono-Regular"),url(fonts/fira-mono-latin-400.woff2) format("woff2")}@font-face{font-family:Fira Mono;font-style:normal;font-display:swap;font-weight:500;src:local("Fira Mono Medium "),local("Fira Mono-Medium"),url(fonts/fira-mono-latin-500.woff2) format("woff2")}@font-face{font-family:Fira Mono;font-style:normal;font-display:swap;font-weight:700;src:local("Fira Mono Bold "),local("Fira Mono-Bold"),url(fonts/fira-mono-latin-700.woff2) format("woff2"),url(fonts/fira-mono-latin-700.woff) format("woff")}.highlight-python,.literal-block,.rst-content code,.rst-content tt,[class^=highlight],code,pre{border:none!important;background-color:var(--bg-code)!important}.highlight-python,.rst-content code,.rst-content tt,code,pre{font-weight:400;font-size:1rem!important;line-height:1.6!important}pre{padding:1.0625rem 1.25rem!important}.rst-content pre code,.rst-content pre tt,pre .rst-content code,pre .rst-content tt,pre code{padding:0}.highlight .hll{margin-left:-1.25rem!important;margin-right:-1.25rem!important;padding-left:1.25rem!important;padding-right:1.25rem!important;font-weight:500;background-color:var(--bg-code-highlight)!important}.linenos pre{padding-right:.4rem!important}.linenos+.code .highlight pre{padding-left:0!important}.wy-menu a:hover{text-decoration:none}.wy-menu-vertical{width:23rem;padding-top:2.225rem;padding-bottom:5rem;border-right:1px solid var(--border)}.wy-menu-vertical header,.wy-menu-vertical p.caption{color:var(--nav);line-height:1.6em;padding:.4em 2.5em;margin:1.25rem 0 0;display:block;font-weight:calc(700*var(--font-weight-factor))}.wy-menu-vertical>:first-child{margin-top:0!important}.wy-menu-vertical li.current{background-color:var(--bg-nav-current-list)!important}.wy-menu-vertical li a{color:var(--nav);-webkit-transition:background-color .2s;-moz-transition:background-color .2s;transition:background-color .2s}.wy-menu-vertical li a:hover{background:var(--bg-nav-hover)!important}.rst-content .wy-menu-vertical li code,.rst-content .wy-menu-vertical li tt,.wy-menu-vertical li .rst-content code,.wy-menu-vertical li .rst-content tt,.wy-menu-vertical li code{border:none;background:inherit;color:inherit;padding-left:0;padding-right:0}.wy-menu-vertical li span.toctree-expand{float:left;margin-left:-1.25rem}.wy-menu-vertical li a.reference{line-height:1.6em}.wy-menu-vertical li a.reference.current,.wy-menu-vertical li a.reference.current:hover{color:var(--nav-current);background-color:var(--bg-nav-current)!important;box-shadow:1px 0 0 var(--bg-nav-current)}.wy-menu-vertical li a.reference.current *,.wy-menu-vertical li a.reference.current:hover *{background-color:inherit!important}.wy-menu-vertical li.current>a,.wy-menu-vertical li.on a{color:var(--nav);padding:.4em 2.5em;font-weight:inherit;position:relative;border:none}.wy-menu-vertical li.current>a:hover,.wy-menu-vertical li.on a:hover{background:var(--bg-nav-hover)}.wy-menu-vertical li.current>a:hover span.toctree-expand,.wy-menu-vertical li.on a:hover span.toctree-expand{color:inherit}.wy-menu-vertical li.current>a span.toctree-expand,.wy-menu-vertical li.on a span.toctree-expand,.wy-menu-vertical li span.toctree-expand{display:inline-block;font-size:1.1em;line-height:inherit;color:inherit}.wy-menu-vertical .toctree-l1.current .toctree-l2>ul,.wy-menu-vertical .toctree-l2.current .toctree-l3>ul,.wy-menu-vertical .toctree-l3.current .toctree-l4>ul,.wy-menu-vertical .toctree-l4.current .toctree-l5>ul,.wy-menu-vertical .toctree-l5.current .toctree-l6>ul,.wy-menu-vertical .toctree-l6.current .toctree-l7>ul,.wy-menu-vertical .toctree-l7.current .toctree-l8>ul,.wy-menu-vertical .toctree-l8.current .toctree-l9>ul,.wy-menu-vertical .toctree-l9.current .toctree-l10>ul,.wy-menu-vertical .toctree-l10.current .toctree-l11>ul{display:none}.wy-menu-vertical .toctree-l1.current .current.toctree-l2>ul,.wy-menu-vertical .toctree-l2.current .current.toctree-l3>ul,.wy-menu-vertical .toctree-l3.current .current.toctree-l4>ul,.wy-menu-vertical .toctree-l4.current .current.toctree-l5>ul,.wy-menu-vertical .toctree-l5.current .current.toctree-l6>ul,.wy-menu-vertical .toctree-l6.current .current.toctree-l7>ul,.wy-menu-vertical .toctree-l7.current .current.toctree-l8>ul,.wy-menu-vertical .toctree-l8.current .current.toctree-l9>ul,.wy-menu-vertical .toctree-l9.current .current.toctree-l10>ul,.wy-menu-vertical .toctree-l10.current .current.toctree-l11>ul{display:block}.wy-menu-vertical li.toctree-l3,.wy-menu-vertical li.toctree-l4{font-size:.9rem}.wy-menu-vertical li.toctree-l2>a,.wy-menu-vertical li.toctree-l3>a,.wy-menu-vertical li.toctree-l4>a,.wy-menu-vertical li.toctree-l5>a,.wy-menu-vertical li.toctree-l6>a,.wy-menu-vertical li.toctree-l7>a,.wy-menu-vertical li.toctree-l8>a,.wy-menu-vertical li.toctree-l9>a,.wy-menu-vertical li.toctree-l10>a{color:var(--nav)}.wy-menu-vertical li.toctree-l2>a{padding-left:3.75em}.wy-menu-vertical li.toctree-l2 a:hover span.toctree-expand{color:inherit}.wy-menu-vertical li.toctree-l3>a{padding-left:5em}.wy-menu-vertical li.toctree-l3 a:hover span.toctree-expand{color:inherit}.wy-menu-vertical li.toctree-l4>a{padding-left:6.25em}.wy-menu-vertical li.toctree-l4 a:hover span.toctree-expand{color:inherit}.wy-menu-vertical li.toctree-l5>a{padding-left:7.5em}.wy-menu-vertical li.toctree-l5 a:hover span.toctree-expand{color:inherit}.wy-menu-vertical li.toctree-l6>a{padding-left:8.75em}.wy-menu-vertical li.toctree-l6 a:hover span.toctree-expand{color:inherit}.wy-menu-vertical li.toctree-l7>a{padding-left:10em}.wy-menu-vertical li.toctree-l7 a:hover span.toctree-expand{color:inherit}.wy-menu-vertical li.toctree-l8>a{padding-left:11.25em}.wy-menu-vertical li.toctree-l8 a:hover span.toctree-expand{color:inherit}.wy-menu-vertical li.toctree-l9>a{padding-left:12.5em}.wy-menu-vertical li.toctree-l9 a:hover span.toctree-expand{color:inherit}.wy-menu-vertical li.toctree-l10>a{padding-left:13.75em}.wy-menu-vertical li.toctree-l10 a:hover span.toctree-expand{color:inherit}.wy-menu-vertical li.current ul{display:block}.wy-menu-vertical li ul{margin-bottom:0;display:none}.wy-menu-vertical li ul li a{margin-bottom:0;color:var(--nav);font-weight:calc(450*var(--font-weight-factor))}.wy-menu-vertical a{line-height:18px;padding:.4em 2.5em;display:block;position:relative;color:var(--nav)}.wy-menu-vertical a:hover{background-color:var(--bg-nav-hover);cursor:pointer}.wy-menu-vertical a:hover span.toctree-expand{color:inherit}.wy-menu-vertical a:active{background-color:var(--bg-nav-hover);cursor:pointer}.wy-side-nav-search{display:block;width:23rem;box-sizing:border-box;padding:1.75rem 2.5em 0;margin:0!important;z-index:200;background-color:var(--bg-nav);text-align:left;color:var(--nav)}.wy-side-nav-search input[type=text]{display:block;width:90%;position:relative;border:2px solid var(--border);background-color:transparent!important;-webkit-appearance:none!important;padding:0 1.1rem;margin:0 calc(-1.1rem - 2px);line-height:2.6rem;font-weight:calc(450*var(--font-weight-factor));box-shadow:none;font-size:inherit;color:var(--text);border-radius:100px}.wy-side-nav-search input[type=text]:focus{border-color:var(--text)}.wy-side-nav-search input[type=text]:focus::placeholder{color:transparent}.wy-side-nav-search input[type=text]:focus:-ms-input-placeholder{color:transparent}.wy-side-nav-search input[type=text]:focus::-ms-input-placeholder{color:transparent}.wy-side-nav-search ::placeholder{color:var(--nav)}.wy-side-nav-search :-ms-input-placeholder{color:var(--nav)}.wy-side-nav-search ::-ms-input-placeholder{color:var(--nav)}.wy-side-nav-search .brand{min-height:9.5rem;height:9.5rem;max-height:9.5rem}.wy-side-nav-search .brand>a{color:var(--text);font-size:100%;font-weight:calc(700*var(--font-weight-factor));display:inline-block;padding:5px 0 0}.wy-side-nav-search .brand>a img.logo{display:block;margin:0;padding:0;height:4.25rem!important;max-height:4.25rem;width:auto;border-radius:0;max-width:100%;background:transparent}.wy-nav .wy-menu-vertical a,.wy-nav .wy-menu-vertical header{color:var(--text)}.wy-nav .wy-menu-vertical a:hover{background-color:var(--link);color:var(--bg)}.landing .navbar ul{display:flex;width:100%;max-width:52rem;margin:0 auto;box-sizing:border-box;padding:0 1.75em;overflow:auto}.landing .navbar ul .github a,.landing .navbar ul li a{display:inline-block;padding:0 .75em!important;font-size:1em;line-height:3.5rem;color:var(--nav);-webkit-transition:all .2s;-moz-transition:all .2s;transition:all .2s}.landing .navbar ul .github a:hover,.landing .navbar ul li a:hover{color:var(--muted)}.landing .navbar ul li.github{flex-grow:2;text-align:right}.landing .navbar ul li.github a{font-size:1.2em}.landing body>.container{max-width:52rem;padding:0 2.5em;margin:5rem auto}.landing body>.container .container{margin:2.5rem 0}.landing .rst-content h1{margin-top:3.65rem;margin-bottom:1.8rem;font-size:3.1rem;font-weight:calc(700*var(--font-weight-factor));letter-spacing:-.02em}.landing .rst-content h1 .headerlink{display:none!important}.landing footer{margin-top:7.5rem}.landing .rst-versions.rst-badge{display:none!important}@media screen and (max-width:63.75rem){.landing .navbar ul{padding:0 .9166666667em}.landing body>.container{margin:4.5rem auto;padding:0 1.6666666667em}}@media screen and (min-width:63.75rem){.landing .navbar{padding-top:1.4rem}}.rst-content .container{margin:1.5rem 0}.rst-content .large p{font-size:160%;line-height:1.5;font-weight:calc(450*var(--font-weight-factor))}.rst-content .buttons a{display:inline-block;font-size:1.35rem;line-height:2.5;margin-right:.5em;padding:0 1.25em;border:2px solid var(--border);text-decoration:none;font-weight:calc(620*var(--font-weight-factor));border-radius:100px;color:var(--text);-webkit-transition:all .2s;-moz-transition:all .2s;transition:all .2s}.rst-content .buttons a:hover{border-color:var(--text)}.rst-content .buttons a:first-child{color:var(--bg);background-color:var(--text);border-color:var(--text)}.rst-content .buttons a:first-child:hover{color:var(--text);background-color:var(--bg)}.rst-content .image{background-color:var(--bg-panel);text-align:center;padding:2.5rem}.rst-content .image img{box-shadow:0 5px 2.5rem rgba(0,0,0,.2)}:root{--hue:215;--sat:6%;--bg:hsl(var(--hue),var(--sat),100%);--bg-nav:var(--bg);--bg-nav-current:hsl(var(--hue),var(--sat),22%);--bg-nav-current-list:hsl(var(--hue),var(--sat),96.5%);--bg-nav-hover:hsl(var(--hue),var(--sat),94%);--bg-code:hsl(var(--hue),var(--sat),96.5%);--bg-code-highlight:hsl(var(--hue),var(--sat),93%);--bg-panel:var(--bg-code);--text:hsl(var(--hue),var(--sat),22%);--link:inherit;--nav:var(--text);--nav-current:var(--bg);--code:hsl(var(--hue),var(--sat),26%);--headline:var(--text);--border:hsl(var(--hue),var(--sat),89%);--muted:hsl(var(--hue),var(--sat),72%);--info-text:var(--bg);--info-bg:hsl(var(--hue),var(--sat),22%);--danger-text:#fdf2f1;--danger-bg:#ee5f4f;--warning-text:#504f1b;--warning-bg:#f9f776;--bg-highlight:#fff9a3;--font-weight-factor:1.12}:root *{-webkit-font-smoothing:antialiased!important;-moz-osx-font-smoothing:grayscale!important}html.dark{--hue:215;--sat:6%;--bg:hsl(var(--hue),var(--sat),14%);--bg-nav:var(--bg);--bg-nav-current:hsl(var(--hue),var(--sat),26%);--bg-nav-current-list:hsl(var(--hue),var(--sat),17%);--bg-nav-hover:hsl(var(--hue),var(--sat),20%);--bg-code:hsl(var(--hue),var(--sat),17%);--bg-code-highlight:hsl(var(--hue),var(--sat),21%);--bg-panel:var(--bg-code);--text:hsl(var(--hue),var(--sat),94%);--link:inherit;--nav:hsl(var(--hue),var(--sat),52%);--code:hsl(var(--hue),var(--sat),86%);--headline:var(--text);--nav-current:var(--text);--border:hsl(var(--hue),var(--sat),20%);--muted:hsl(var(--hue),var(--sat),38%);--info-text:var(--text);--info-bg:hsl(var(--hue),var(--sat),26%);--danger-text:#f28f88;--danger-bg:#362b2b;--warning-text:#ebdc8e;--warning-bg:#323129;--bg-highlight:#2e6eb8;--font-weight-factor:0.95}@media screen and (min-width:768px){::-webkit-scrollbar{width:1.4rem;height:1.4rem;background-color:transparent}.wy-side-scroll::-webkit-scrollbar{display:none}::-webkit-scrollbar-track:vertical{border-left:1px solid var(--border)}::-webkit-scrollbar-track:horizontal{border-top:1px solid var(--border)}::-webkit-scrollbar-thumb{min-width:3rem;min-height:3rem;background-color:var(--border);border:.4rem solid transparent;-moz-background-clip:content;-webkit-background-clip:content;background-clip:content-box;border-radius:1rem}::-webkit-scrollbar-thumb:hover{background-color:var(--bg-nav-hover)}::-webkit-scrollbar-corner{background-color:transparent;border-left:1px solid var(--border);border-top:1px solid var(--border)}*{scrollbar-width:thin;scrollbar-color:var(--bg-nav) var(--text)}}.highlight *{color:var(--code)!important;font-weight:400}.highlight .c,.highlight .c1,.highlight .cm{color:var(--muted)!important;font-style:italic}.highlight .err{color:var(--code)!important}.highlight .k,.highlight .kc,.highlight .kd,.highlight .kn,.highlight .kp,.highlight .kr,.highlight .kt{font-weight:500!important;opacity:.67}.highlight .l,.highlight .ld,.highlight .s,.highlight .s1,.highlight .s2,.highlight .sb,.highlight .sc,.highlight .sd,.highlight .se,.highlight .sh,.highlight .si,.highlight .sx{font-style:italic;font-weight:500!important;color:var(--nav)!important}.highlight .bp,.highlight .nb,.highlight .nc,.highlight .nd,.highlight .ne,.highlight .ni,.highlight .nl,.highlight .no,.highlight .nv,.highlight .py,.highlight .vc,.highlight .vg{font-weight:700!important;color:var(--nav)!important}.highlight .n,.highlight .nf,.highlight .nn,.highlight .o,.highlight .p,.highlight nx{opacity:1}.highlight .il,.highlight .m,.highlight .mf,.highlight .mh,.highlight .mi,.highlight .mo,.highlight .na{opacity:.8}.highlight .sr,.highlight .ss{color:var(--nav)!important}.highlight .ge{font-style:italic}.highlight .gs{font-weight:700}.highlight .nt,.highlight .vi{font-style:italic}.highlight .ow{font-weight:700}.highlight .gu{opacity:.5}.highlight .gd,.highlight .gi,.highlight .w{font-weight:500} \ No newline at end of file diff --git a/_static/doctools.js b/_static/doctools.js new file mode 100644 index 0000000000..527b876ca6 --- /dev/null +++ b/_static/doctools.js @@ -0,0 +1,156 @@ +/* + * doctools.js + * ~~~~~~~~~~~ + * + * Base JavaScript utilities for all Sphinx HTML documentation. + * + * :copyright: Copyright 2007-2022 by the Sphinx team, see AUTHORS. + * :license: BSD, see LICENSE for details. + * + */ +"use strict"; + +const BLACKLISTED_KEY_CONTROL_ELEMENTS = new Set([ + "TEXTAREA", + "INPUT", + "SELECT", + "BUTTON", +]); + +const _ready = (callback) => { + if (document.readyState !== "loading") { + callback(); + } else { + document.addEventListener("DOMContentLoaded", callback); + } +}; + +/** + * Small JavaScript module for the documentation. + */ +const Documentation = { + init: () => { + Documentation.initDomainIndexTable(); + Documentation.initOnKeyListeners(); + }, + + /** + * i18n support + */ + TRANSLATIONS: {}, + PLURAL_EXPR: (n) => (n === 1 ? 0 : 1), + LOCALE: "unknown", + + // gettext and ngettext don't access this so that the functions + // can safely bound to a different name (_ = Documentation.gettext) + gettext: (string) => { + const translated = Documentation.TRANSLATIONS[string]; + switch (typeof translated) { + case "undefined": + return string; // no translation + case "string": + return translated; // translation exists + default: + return translated[0]; // (singular, plural) translation tuple exists + } + }, + + ngettext: (singular, plural, n) => { + const translated = Documentation.TRANSLATIONS[singular]; + if (typeof translated !== "undefined") + return translated[Documentation.PLURAL_EXPR(n)]; + return n === 1 ? singular : plural; + }, + + addTranslations: (catalog) => { + Object.assign(Documentation.TRANSLATIONS, catalog.messages); + Documentation.PLURAL_EXPR = new Function( + "n", + `return (${catalog.plural_expr})` + ); + Documentation.LOCALE = catalog.locale; + }, + + /** + * helper function to focus on search bar + */ + focusSearchBar: () => { + document.querySelectorAll("input[name=q]")[0]?.focus(); + }, + + /** + * Initialise the domain index toggle buttons + */ + initDomainIndexTable: () => { + const toggler = (el) => { + const idNumber = el.id.substr(7); + const toggledRows = document.querySelectorAll(`tr.cg-${idNumber}`); + if (el.src.substr(-9) === "minus.png") { + el.src = `${el.src.substr(0, el.src.length - 9)}plus.png`; + toggledRows.forEach((el) => (el.style.display = "none")); + } else { + el.src = `${el.src.substr(0, el.src.length - 8)}minus.png`; + toggledRows.forEach((el) => (el.style.display = "")); + } + }; + + const togglerElements = document.querySelectorAll("img.toggler"); + togglerElements.forEach((el) => + el.addEventListener("click", (event) => toggler(event.currentTarget)) + ); + togglerElements.forEach((el) => (el.style.display = "")); + if (DOCUMENTATION_OPTIONS.COLLAPSE_INDEX) togglerElements.forEach(toggler); + }, + + initOnKeyListeners: () => { + // only install a listener if it is really needed + if ( + !DOCUMENTATION_OPTIONS.NAVIGATION_WITH_KEYS && + !DOCUMENTATION_OPTIONS.ENABLE_SEARCH_SHORTCUTS + ) + return; + + document.addEventListener("keydown", (event) => { + // bail for input elements + if (BLACKLISTED_KEY_CONTROL_ELEMENTS.has(document.activeElement.tagName)) return; + // bail with special keys + if (event.altKey || event.ctrlKey || event.metaKey) return; + + if (!event.shiftKey) { + switch (event.key) { + case "ArrowLeft": + if (!DOCUMENTATION_OPTIONS.NAVIGATION_WITH_KEYS) break; + + const prevLink = document.querySelector('link[rel="prev"]'); + if (prevLink && prevLink.href) { + window.location.href = prevLink.href; + event.preventDefault(); + } + break; + case "ArrowRight": + if (!DOCUMENTATION_OPTIONS.NAVIGATION_WITH_KEYS) break; + + const nextLink = document.querySelector('link[rel="next"]'); + if (nextLink && nextLink.href) { + window.location.href = nextLink.href; + event.preventDefault(); + } + break; + } + } + + // some keyboard layouts may need Shift to get / + switch (event.key) { + case "/": + if (!DOCUMENTATION_OPTIONS.ENABLE_SEARCH_SHORTCUTS) break; + Documentation.focusSearchBar(); + event.preventDefault(); + } + }); + }, +}; + +// quick alias for translations +const _ = Documentation.gettext; + +_ready(Documentation.init); diff --git a/_static/documentation_options.js b/_static/documentation_options.js new file mode 100644 index 0000000000..044ee2a9a2 --- /dev/null +++ b/_static/documentation_options.js @@ -0,0 +1,14 @@ +var DOCUMENTATION_OPTIONS = { + URL_ROOT: document.getElementById("documentation_options").getAttribute('data-url_root'), + VERSION: '1.0.11', + LANGUAGE: 'en', + COLLAPSE_INDEX: false, + BUILDER: 'html', + FILE_SUFFIX: '.html', + LINK_SUFFIX: '.html', + HAS_SOURCE: true, + SOURCELINK_SUFFIX: '.txt', + NAVIGATION_WITH_KEYS: false, + SHOW_SEARCH_SUMMARY: true, + ENABLE_SEARCH_SHORTCUTS: true, +}; \ No newline at end of file diff --git a/_static/favicon.ico b/_static/favicon.ico new file mode 100644 index 0000000000000000000000000000000000000000..978df914816ca191c1824b38f921b8c09944119c GIT binary patch literal 15086 zcmeHO3vg7`8UB|b_=cjbZP6yxKCEbK%jnc;HEo?bWstU1+tH4r9bc_&p$}WJ!`Q@u zf!0AsgYrkAP!@pobbGBE;0KDx5FmR1(k(mUYwqaja6L1*{C2LnQZ&cd$I*ox{_ z7kg+`sfVsM>pL(%0J(k;r-S^R>sVdEla*<)8O1eFQcJ(p_W|vC&q^l1Z9RHmPZz3=B)8)Y5mySME_%Gsr z)a7XOlK+$GdcO_gyZhBd-1>Tvm;ROx{F$%;Y|rVjnw4Ml(gpC{oC*J9th|{Vr>msbR_d4lANo^MIKK@It49txCOjwIo_%G(YEep8R zdzPh)sZS4i)VInCb7DKz*f+pd*wkJEt{q2mxxMdwZMk0;Dz(Js?RZX;>FW3Kf12i6 z$|d^5^YDX2e0J*SLwTYteZ4YpDGOfau-d9<<$pDP+kkCL!&Mv(59s8Wn@_*aTN-8= zWc5B!-f9P{nGDuRJR zZePJ0eA5g%@Y|0u{E`_qyKT_Vi{p23)v=%QsWXiR9DReWc(#AXG7sYN#=YnpP4l21 zG|_J}=;J&4gk79ga+OVV4)6rG1!rUY-ZuCh3ZG$5$1_~+yD%wkeV^8Q&*7R67xBQ* zDTBTC-gkM&k(e!?RZTo8YdCi4i_1m)^qa~10POir@i>*Qbkh{zwA}AM{<0Cf;1_zdBq{!; z(gSp!Q#>P)$2iTi`VH3XC!RdT*oQ3qCM*A!6#tzg!7@@E;NM}@hdUM-@y}n76#H(8 zYj9$GS< z7?b(dav{jkTJ-_rUM%xHnT?OaJ>Zvo>?`;^>cAxr{wkjNTR#feCNNBlfPMrbn|^FA zv@6a-B82Emqi}pBtAu0bu)54x}z7zk&wJw(Yxll>%q}aT$X~Q%o1>M(d>O1wPm$!^56}ssv_#a4xp~NK4 zZq{aEX9(x2u2a9e8HdiZCVCqF%D`u}yrSer5@qT_#oTmi8_qR+-f&OJ+KK5zV=rw= zN4Vsb-fMjfd8O{?D>uwr8@?9vv$_7u$kYsJ_Xfi~$RRG#a*28yDBF(7;GMWo=|=K$ z4eC|TUHyJ7*Qf3){@T%-xufr3Ob+K;k@tQu{%m3oXExquZM$~SC2tH|V(C}; zSG>YML(3hiys|g6dZ}4IQ?>a*YrI>7U$^x8)Gp#DcUph9Uv0RCy`5`oFAmTZ z=#%NWpC3LH$vr@q=iFzBVbn1N%$X?fOY3hjq$*s_%;5@kk^eB))I|gTc3~ZGMx?rjvWJPJ`cSm; znfS&UQ|7P}AE{(9mbTBW0N*XC?2Y6;>{h70TXHXL9*J!`I%NU66n;-z`o_5qtf%o` zGnu=h`xMi65`LrQne!a6Eax`h=Y5kgaF11n5LL-Xs31zRIM>Q(cY(4R5%v(0aEB1W9oZ7%5JE^pC~sIuL&6tA621@$A#5Sk zm(47pi|8eB3JFW(fMRiqw7$7u7*{l}xgXDqVV{1mZF#S|eTBs64foKtUt1S@Xd}v| z^X^dF-`U;%lw&L;4cBi2Xky6_NP2K{a`8!7z9-bt7%C{k>X2)}JM%)NFC6)$yF`EIu^;ovGj-hB^NzMwF7us> zvqHOZ2CV@$Z_{kxJ#&vvIdef3Q-$nO3sSE zaELB}Y?sL2zZ|qIrrT5Mrq5aF>D!_Em#9DGm+}31OfQqHY}j|>eJa0kGM&VG@IM<= ui{HwdYHydQ>!dC*&V1cT2q_CyAr{;?H9{f8m4*7U5$DZ~P&iL}X6XOhHQJg0 literal 0 HcmV?d00001 diff --git a/_static/file.png b/_static/file.png new file mode 100644 index 0000000000000000000000000000000000000000..a858a410e4faa62ce324d814e4b816fff83a6fb3 GIT binary patch literal 286 zcmV+(0pb3MP)s`hMrGg#P~ix$^RISR_I47Y|r1 z_CyJOe}D1){SET-^Amu_i71Lt6eYfZjRyw@I6OQAIXXHDfiX^GbOlHe=Ae4>0m)d(f|Me07*qoM6N<$f}vM^LjV8( literal 0 HcmV?d00001 diff --git a/_static/jquery-3.6.0.js b/_static/jquery-3.6.0.js new file mode 100644 index 0000000000..fc6c299b73 --- /dev/null +++ b/_static/jquery-3.6.0.js @@ -0,0 +1,10881 @@ +/*! + * jQuery JavaScript Library v3.6.0 + * https://jquery.com/ + * + * Includes Sizzle.js + * https://sizzlejs.com/ + * + * Copyright OpenJS Foundation and other contributors + * Released under the MIT license + * https://jquery.org/license + * + * Date: 2021-03-02T17:08Z + */ +( function( global, factory ) { + + "use strict"; + + if ( typeof module === "object" && typeof module.exports === "object" ) { + + // For CommonJS and CommonJS-like environments where a proper `window` + // is present, execute the factory and get jQuery. + // For environments that do not have a `window` with a `document` + // (such as Node.js), expose a factory as module.exports. + // This accentuates the need for the creation of a real `window`. + // e.g. var jQuery = require("jquery")(window); + // See ticket #14549 for more info. + module.exports = global.document ? + factory( global, true ) : + function( w ) { + if ( !w.document ) { + throw new Error( "jQuery requires a window with a document" ); + } + return factory( w ); + }; + } else { + factory( global ); + } + +// Pass this if window is not defined yet +} )( typeof window !== "undefined" ? window : this, function( window, noGlobal ) { + +// Edge <= 12 - 13+, Firefox <=18 - 45+, IE 10 - 11, Safari 5.1 - 9+, iOS 6 - 9.1 +// throw exceptions when non-strict code (e.g., ASP.NET 4.5) accesses strict mode +// arguments.callee.caller (trac-13335). But as of jQuery 3.0 (2016), strict mode should be common +// enough that all such attempts are guarded in a try block. +"use strict"; + +var arr = []; + +var getProto = Object.getPrototypeOf; + +var slice = arr.slice; + +var flat = arr.flat ? function( array ) { + return arr.flat.call( array ); +} : function( array ) { + return arr.concat.apply( [], array ); +}; + + +var push = arr.push; + +var indexOf = arr.indexOf; + +var class2type = {}; + +var toString = class2type.toString; + +var hasOwn = class2type.hasOwnProperty; + +var fnToString = hasOwn.toString; + +var ObjectFunctionString = fnToString.call( Object ); + +var support = {}; + +var isFunction = function isFunction( obj ) { + + // Support: Chrome <=57, Firefox <=52 + // In some browsers, typeof returns "function" for HTML elements + // (i.e., `typeof document.createElement( "object" ) === "function"`). + // We don't want to classify *any* DOM node as a function. + // Support: QtWeb <=3.8.5, WebKit <=534.34, wkhtmltopdf tool <=0.12.5 + // Plus for old WebKit, typeof returns "function" for HTML collections + // (e.g., `typeof document.getElementsByTagName("div") === "function"`). (gh-4756) + return typeof obj === "function" && typeof obj.nodeType !== "number" && + typeof obj.item !== "function"; + }; + + +var isWindow = function isWindow( obj ) { + return obj != null && obj === obj.window; + }; + + +var document = window.document; + + + + var preservedScriptAttributes = { + type: true, + src: true, + nonce: true, + noModule: true + }; + + function DOMEval( code, node, doc ) { + doc = doc || document; + + var i, val, + script = doc.createElement( "script" ); + + script.text = code; + if ( node ) { + for ( i in preservedScriptAttributes ) { + + // Support: Firefox 64+, Edge 18+ + // Some browsers don't support the "nonce" property on scripts. + // On the other hand, just using `getAttribute` is not enough as + // the `nonce` attribute is reset to an empty string whenever it + // becomes browsing-context connected. + // See https://github.com/whatwg/html/issues/2369 + // See https://html.spec.whatwg.org/#nonce-attributes + // The `node.getAttribute` check was added for the sake of + // `jQuery.globalEval` so that it can fake a nonce-containing node + // via an object. + val = node[ i ] || node.getAttribute && node.getAttribute( i ); + if ( val ) { + script.setAttribute( i, val ); + } + } + } + doc.head.appendChild( script ).parentNode.removeChild( script ); + } + + +function toType( obj ) { + if ( obj == null ) { + return obj + ""; + } + + // Support: Android <=2.3 only (functionish RegExp) + return typeof obj === "object" || typeof obj === "function" ? + class2type[ toString.call( obj ) ] || "object" : + typeof obj; +} +/* global Symbol */ +// Defining this global in .eslintrc.json would create a danger of using the global +// unguarded in another place, it seems safer to define global only for this module + + + +var + version = "3.6.0", + + // Define a local copy of jQuery + jQuery = function( selector, context ) { + + // The jQuery object is actually just the init constructor 'enhanced' + // Need init if jQuery is called (just allow error to be thrown if not included) + return new jQuery.fn.init( selector, context ); + }; + +jQuery.fn = jQuery.prototype = { + + // The current version of jQuery being used + jquery: version, + + constructor: jQuery, + + // The default length of a jQuery object is 0 + length: 0, + + toArray: function() { + return slice.call( this ); + }, + + // Get the Nth element in the matched element set OR + // Get the whole matched element set as a clean array + get: function( num ) { + + // Return all the elements in a clean array + if ( num == null ) { + return slice.call( this ); + } + + // Return just the one element from the set + return num < 0 ? this[ num + this.length ] : this[ num ]; + }, + + // Take an array of elements and push it onto the stack + // (returning the new matched element set) + pushStack: function( elems ) { + + // Build a new jQuery matched element set + var ret = jQuery.merge( this.constructor(), elems ); + + // Add the old object onto the stack (as a reference) + ret.prevObject = this; + + // Return the newly-formed element set + return ret; + }, + + // Execute a callback for every element in the matched set. + each: function( callback ) { + return jQuery.each( this, callback ); + }, + + map: function( callback ) { + return this.pushStack( jQuery.map( this, function( elem, i ) { + return callback.call( elem, i, elem ); + } ) ); + }, + + slice: function() { + return this.pushStack( slice.apply( this, arguments ) ); + }, + + first: function() { + return this.eq( 0 ); + }, + + last: function() { + return this.eq( -1 ); + }, + + even: function() { + return this.pushStack( jQuery.grep( this, function( _elem, i ) { + return ( i + 1 ) % 2; + } ) ); + }, + + odd: function() { + return this.pushStack( jQuery.grep( this, function( _elem, i ) { + return i % 2; + } ) ); + }, + + eq: function( i ) { + var len = this.length, + j = +i + ( i < 0 ? len : 0 ); + return this.pushStack( j >= 0 && j < len ? [ this[ j ] ] : [] ); + }, + + end: function() { + return this.prevObject || this.constructor(); + }, + + // For internal use only. + // Behaves like an Array's method, not like a jQuery method. + push: push, + sort: arr.sort, + splice: arr.splice +}; + +jQuery.extend = jQuery.fn.extend = function() { + var options, name, src, copy, copyIsArray, clone, + target = arguments[ 0 ] || {}, + i = 1, + length = arguments.length, + deep = false; + + // Handle a deep copy situation + if ( typeof target === "boolean" ) { + deep = target; + + // Skip the boolean and the target + target = arguments[ i ] || {}; + i++; + } + + // Handle case when target is a string or something (possible in deep copy) + if ( typeof target !== "object" && !isFunction( target ) ) { + target = {}; + } + + // Extend jQuery itself if only one argument is passed + if ( i === length ) { + target = this; + i--; + } + + for ( ; i < length; i++ ) { + + // Only deal with non-null/undefined values + if ( ( options = arguments[ i ] ) != null ) { + + // Extend the base object + for ( name in options ) { + copy = options[ name ]; + + // Prevent Object.prototype pollution + // Prevent never-ending loop + if ( name === "__proto__" || target === copy ) { + continue; + } + + // Recurse if we're merging plain objects or arrays + if ( deep && copy && ( jQuery.isPlainObject( copy ) || + ( copyIsArray = Array.isArray( copy ) ) ) ) { + src = target[ name ]; + + // Ensure proper type for the source value + if ( copyIsArray && !Array.isArray( src ) ) { + clone = []; + } else if ( !copyIsArray && !jQuery.isPlainObject( src ) ) { + clone = {}; + } else { + clone = src; + } + copyIsArray = false; + + // Never move original objects, clone them + target[ name ] = jQuery.extend( deep, clone, copy ); + + // Don't bring in undefined values + } else if ( copy !== undefined ) { + target[ name ] = copy; + } + } + } + } + + // Return the modified object + return target; +}; + +jQuery.extend( { + + // Unique for each copy of jQuery on the page + expando: "jQuery" + ( version + Math.random() ).replace( /\D/g, "" ), + + // Assume jQuery is ready without the ready module + isReady: true, + + error: function( msg ) { + throw new Error( msg ); + }, + + noop: function() {}, + + isPlainObject: function( obj ) { + var proto, Ctor; + + // Detect obvious negatives + // Use toString instead of jQuery.type to catch host objects + if ( !obj || toString.call( obj ) !== "[object Object]" ) { + return false; + } + + proto = getProto( obj ); + + // Objects with no prototype (e.g., `Object.create( null )`) are plain + if ( !proto ) { + return true; + } + + // Objects with prototype are plain iff they were constructed by a global Object function + Ctor = hasOwn.call( proto, "constructor" ) && proto.constructor; + return typeof Ctor === "function" && fnToString.call( Ctor ) === ObjectFunctionString; + }, + + isEmptyObject: function( obj ) { + var name; + + for ( name in obj ) { + return false; + } + return true; + }, + + // Evaluates a script in a provided context; falls back to the global one + // if not specified. + globalEval: function( code, options, doc ) { + DOMEval( code, { nonce: options && options.nonce }, doc ); + }, + + each: function( obj, callback ) { + var length, i = 0; + + if ( isArrayLike( obj ) ) { + length = obj.length; + for ( ; i < length; i++ ) { + if ( callback.call( obj[ i ], i, obj[ i ] ) === false ) { + break; + } + } + } else { + for ( i in obj ) { + if ( callback.call( obj[ i ], i, obj[ i ] ) === false ) { + break; + } + } + } + + return obj; + }, + + // results is for internal usage only + makeArray: function( arr, results ) { + var ret = results || []; + + if ( arr != null ) { + if ( isArrayLike( Object( arr ) ) ) { + jQuery.merge( ret, + typeof arr === "string" ? + [ arr ] : arr + ); + } else { + push.call( ret, arr ); + } + } + + return ret; + }, + + inArray: function( elem, arr, i ) { + return arr == null ? -1 : indexOf.call( arr, elem, i ); + }, + + // Support: Android <=4.0 only, PhantomJS 1 only + // push.apply(_, arraylike) throws on ancient WebKit + merge: function( first, second ) { + var len = +second.length, + j = 0, + i = first.length; + + for ( ; j < len; j++ ) { + first[ i++ ] = second[ j ]; + } + + first.length = i; + + return first; + }, + + grep: function( elems, callback, invert ) { + var callbackInverse, + matches = [], + i = 0, + length = elems.length, + callbackExpect = !invert; + + // Go through the array, only saving the items + // that pass the validator function + for ( ; i < length; i++ ) { + callbackInverse = !callback( elems[ i ], i ); + if ( callbackInverse !== callbackExpect ) { + matches.push( elems[ i ] ); + } + } + + return matches; + }, + + // arg is for internal usage only + map: function( elems, callback, arg ) { + var length, value, + i = 0, + ret = []; + + // Go through the array, translating each of the items to their new values + if ( isArrayLike( elems ) ) { + length = elems.length; + for ( ; i < length; i++ ) { + value = callback( elems[ i ], i, arg ); + + if ( value != null ) { + ret.push( value ); + } + } + + // Go through every key on the object, + } else { + for ( i in elems ) { + value = callback( elems[ i ], i, arg ); + + if ( value != null ) { + ret.push( value ); + } + } + } + + // Flatten any nested arrays + return flat( ret ); + }, + + // A global GUID counter for objects + guid: 1, + + // jQuery.support is not used in Core but other projects attach their + // properties to it so it needs to exist. + support: support +} ); + +if ( typeof Symbol === "function" ) { + jQuery.fn[ Symbol.iterator ] = arr[ Symbol.iterator ]; +} + +// Populate the class2type map +jQuery.each( "Boolean Number String Function Array Date RegExp Object Error Symbol".split( " " ), + function( _i, name ) { + class2type[ "[object " + name + "]" ] = name.toLowerCase(); + } ); + +function isArrayLike( obj ) { + + // Support: real iOS 8.2 only (not reproducible in simulator) + // `in` check used to prevent JIT error (gh-2145) + // hasOwn isn't used here due to false negatives + // regarding Nodelist length in IE + var length = !!obj && "length" in obj && obj.length, + type = toType( obj ); + + if ( isFunction( obj ) || isWindow( obj ) ) { + return false; + } + + return type === "array" || length === 0 || + typeof length === "number" && length > 0 && ( length - 1 ) in obj; +} +var Sizzle = +/*! + * Sizzle CSS Selector Engine v2.3.6 + * https://sizzlejs.com/ + * + * Copyright JS Foundation and other contributors + * Released under the MIT license + * https://js.foundation/ + * + * Date: 2021-02-16 + */ +( function( window ) { +var i, + support, + Expr, + getText, + isXML, + tokenize, + compile, + select, + outermostContext, + sortInput, + hasDuplicate, + + // Local document vars + setDocument, + document, + docElem, + documentIsHTML, + rbuggyQSA, + rbuggyMatches, + matches, + contains, + + // Instance-specific data + expando = "sizzle" + 1 * new Date(), + preferredDoc = window.document, + dirruns = 0, + done = 0, + classCache = createCache(), + tokenCache = createCache(), + compilerCache = createCache(), + nonnativeSelectorCache = createCache(), + sortOrder = function( a, b ) { + if ( a === b ) { + hasDuplicate = true; + } + return 0; + }, + + // Instance methods + hasOwn = ( {} ).hasOwnProperty, + arr = [], + pop = arr.pop, + pushNative = arr.push, + push = arr.push, + slice = arr.slice, + + // Use a stripped-down indexOf as it's faster than native + // https://jsperf.com/thor-indexof-vs-for/5 + indexOf = function( list, elem ) { + var i = 0, + len = list.length; + for ( ; i < len; i++ ) { + if ( list[ i ] === elem ) { + return i; + } + } + return -1; + }, + + booleans = "checked|selected|async|autofocus|autoplay|controls|defer|disabled|hidden|" + + "ismap|loop|multiple|open|readonly|required|scoped", + + // Regular expressions + + // http://www.w3.org/TR/css3-selectors/#whitespace + whitespace = "[\\x20\\t\\r\\n\\f]", + + // https://www.w3.org/TR/css-syntax-3/#ident-token-diagram + identifier = "(?:\\\\[\\da-fA-F]{1,6}" + whitespace + + "?|\\\\[^\\r\\n\\f]|[\\w-]|[^\0-\\x7f])+", + + // Attribute selectors: http://www.w3.org/TR/selectors/#attribute-selectors + attributes = "\\[" + whitespace + "*(" + identifier + ")(?:" + whitespace + + + // Operator (capture 2) + "*([*^$|!~]?=)" + whitespace + + + // "Attribute values must be CSS identifiers [capture 5] + // or strings [capture 3 or capture 4]" + "*(?:'((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\"|(" + identifier + "))|)" + + whitespace + "*\\]", + + pseudos = ":(" + identifier + ")(?:\\((" + + + // To reduce the number of selectors needing tokenize in the preFilter, prefer arguments: + // 1. quoted (capture 3; capture 4 or capture 5) + "('((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\")|" + + + // 2. simple (capture 6) + "((?:\\\\.|[^\\\\()[\\]]|" + attributes + ")*)|" + + + // 3. anything else (capture 2) + ".*" + + ")\\)|)", + + // Leading and non-escaped trailing whitespace, capturing some non-whitespace characters preceding the latter + rwhitespace = new RegExp( whitespace + "+", "g" ), + rtrim = new RegExp( "^" + whitespace + "+|((?:^|[^\\\\])(?:\\\\.)*)" + + whitespace + "+$", "g" ), + + rcomma = new RegExp( "^" + whitespace + "*," + whitespace + "*" ), + rcombinators = new RegExp( "^" + whitespace + "*([>+~]|" + whitespace + ")" + whitespace + + "*" ), + rdescend = new RegExp( whitespace + "|>" ), + + rpseudo = new RegExp( pseudos ), + ridentifier = new RegExp( "^" + identifier + "$" ), + + matchExpr = { + "ID": new RegExp( "^#(" + identifier + ")" ), + "CLASS": new RegExp( "^\\.(" + identifier + ")" ), + "TAG": new RegExp( "^(" + identifier + "|[*])" ), + "ATTR": new RegExp( "^" + attributes ), + "PSEUDO": new RegExp( "^" + pseudos ), + "CHILD": new RegExp( "^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\(" + + whitespace + "*(even|odd|(([+-]|)(\\d*)n|)" + whitespace + "*(?:([+-]|)" + + whitespace + "*(\\d+)|))" + whitespace + "*\\)|)", "i" ), + "bool": new RegExp( "^(?:" + booleans + ")$", "i" ), + + // For use in libraries implementing .is() + // We use this for POS matching in `select` + "needsContext": new RegExp( "^" + whitespace + + "*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\(" + whitespace + + "*((?:-\\d)?\\d*)" + whitespace + "*\\)|)(?=[^-]|$)", "i" ) + }, + + rhtml = /HTML$/i, + rinputs = /^(?:input|select|textarea|button)$/i, + rheader = /^h\d$/i, + + rnative = /^[^{]+\{\s*\[native \w/, + + // Easily-parseable/retrievable ID or TAG or CLASS selectors + rquickExpr = /^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/, + + rsibling = /[+~]/, + + // CSS escapes + // http://www.w3.org/TR/CSS21/syndata.html#escaped-characters + runescape = new RegExp( "\\\\[\\da-fA-F]{1,6}" + whitespace + "?|\\\\([^\\r\\n\\f])", "g" ), + funescape = function( escape, nonHex ) { + var high = "0x" + escape.slice( 1 ) - 0x10000; + + return nonHex ? + + // Strip the backslash prefix from a non-hex escape sequence + nonHex : + + // Replace a hexadecimal escape sequence with the encoded Unicode code point + // Support: IE <=11+ + // For values outside the Basic Multilingual Plane (BMP), manually construct a + // surrogate pair + high < 0 ? + String.fromCharCode( high + 0x10000 ) : + String.fromCharCode( high >> 10 | 0xD800, high & 0x3FF | 0xDC00 ); + }, + + // CSS string/identifier serialization + // https://drafts.csswg.org/cssom/#common-serializing-idioms + rcssescape = /([\0-\x1f\x7f]|^-?\d)|^-$|[^\0-\x1f\x7f-\uFFFF\w-]/g, + fcssescape = function( ch, asCodePoint ) { + if ( asCodePoint ) { + + // U+0000 NULL becomes U+FFFD REPLACEMENT CHARACTER + if ( ch === "\0" ) { + return "\uFFFD"; + } + + // Control characters and (dependent upon position) numbers get escaped as code points + return ch.slice( 0, -1 ) + "\\" + + ch.charCodeAt( ch.length - 1 ).toString( 16 ) + " "; + } + + // Other potentially-special ASCII characters get backslash-escaped + return "\\" + ch; + }, + + // Used for iframes + // See setDocument() + // Removing the function wrapper causes a "Permission Denied" + // error in IE + unloadHandler = function() { + setDocument(); + }, + + inDisabledFieldset = addCombinator( + function( elem ) { + return elem.disabled === true && elem.nodeName.toLowerCase() === "fieldset"; + }, + { dir: "parentNode", next: "legend" } + ); + +// Optimize for push.apply( _, NodeList ) +try { + push.apply( + ( arr = slice.call( preferredDoc.childNodes ) ), + preferredDoc.childNodes + ); + + // Support: Android<4.0 + // Detect silently failing push.apply + // eslint-disable-next-line no-unused-expressions + arr[ preferredDoc.childNodes.length ].nodeType; +} catch ( e ) { + push = { apply: arr.length ? + + // Leverage slice if possible + function( target, els ) { + pushNative.apply( target, slice.call( els ) ); + } : + + // Support: IE<9 + // Otherwise append directly + function( target, els ) { + var j = target.length, + i = 0; + + // Can't trust NodeList.length + while ( ( target[ j++ ] = els[ i++ ] ) ) {} + target.length = j - 1; + } + }; +} + +function Sizzle( selector, context, results, seed ) { + var m, i, elem, nid, match, groups, newSelector, + newContext = context && context.ownerDocument, + + // nodeType defaults to 9, since context defaults to document + nodeType = context ? context.nodeType : 9; + + results = results || []; + + // Return early from calls with invalid selector or context + if ( typeof selector !== "string" || !selector || + nodeType !== 1 && nodeType !== 9 && nodeType !== 11 ) { + + return results; + } + + // Try to shortcut find operations (as opposed to filters) in HTML documents + if ( !seed ) { + setDocument( context ); + context = context || document; + + if ( documentIsHTML ) { + + // If the selector is sufficiently simple, try using a "get*By*" DOM method + // (excepting DocumentFragment context, where the methods don't exist) + if ( nodeType !== 11 && ( match = rquickExpr.exec( selector ) ) ) { + + // ID selector + if ( ( m = match[ 1 ] ) ) { + + // Document context + if ( nodeType === 9 ) { + if ( ( elem = context.getElementById( m ) ) ) { + + // Support: IE, Opera, Webkit + // TODO: identify versions + // getElementById can match elements by name instead of ID + if ( elem.id === m ) { + results.push( elem ); + return results; + } + } else { + return results; + } + + // Element context + } else { + + // Support: IE, Opera, Webkit + // TODO: identify versions + // getElementById can match elements by name instead of ID + if ( newContext && ( elem = newContext.getElementById( m ) ) && + contains( context, elem ) && + elem.id === m ) { + + results.push( elem ); + return results; + } + } + + // Type selector + } else if ( match[ 2 ] ) { + push.apply( results, context.getElementsByTagName( selector ) ); + return results; + + // Class selector + } else if ( ( m = match[ 3 ] ) && support.getElementsByClassName && + context.getElementsByClassName ) { + + push.apply( results, context.getElementsByClassName( m ) ); + return results; + } + } + + // Take advantage of querySelectorAll + if ( support.qsa && + !nonnativeSelectorCache[ selector + " " ] && + ( !rbuggyQSA || !rbuggyQSA.test( selector ) ) && + + // Support: IE 8 only + // Exclude object elements + ( nodeType !== 1 || context.nodeName.toLowerCase() !== "object" ) ) { + + newSelector = selector; + newContext = context; + + // qSA considers elements outside a scoping root when evaluating child or + // descendant combinators, which is not what we want. + // In such cases, we work around the behavior by prefixing every selector in the + // list with an ID selector referencing the scope context. + // The technique has to be used as well when a leading combinator is used + // as such selectors are not recognized by querySelectorAll. + // Thanks to Andrew Dupont for this technique. + if ( nodeType === 1 && + ( rdescend.test( selector ) || rcombinators.test( selector ) ) ) { + + // Expand context for sibling selectors + newContext = rsibling.test( selector ) && testContext( context.parentNode ) || + context; + + // We can use :scope instead of the ID hack if the browser + // supports it & if we're not changing the context. + if ( newContext !== context || !support.scope ) { + + // Capture the context ID, setting it first if necessary + if ( ( nid = context.getAttribute( "id" ) ) ) { + nid = nid.replace( rcssescape, fcssescape ); + } else { + context.setAttribute( "id", ( nid = expando ) ); + } + } + + // Prefix every selector in the list + groups = tokenize( selector ); + i = groups.length; + while ( i-- ) { + groups[ i ] = ( nid ? "#" + nid : ":scope" ) + " " + + toSelector( groups[ i ] ); + } + newSelector = groups.join( "," ); + } + + try { + push.apply( results, + newContext.querySelectorAll( newSelector ) + ); + return results; + } catch ( qsaError ) { + nonnativeSelectorCache( selector, true ); + } finally { + if ( nid === expando ) { + context.removeAttribute( "id" ); + } + } + } + } + } + + // All others + return select( selector.replace( rtrim, "$1" ), context, results, seed ); +} + +/** + * Create key-value caches of limited size + * @returns {function(string, object)} Returns the Object data after storing it on itself with + * property name the (space-suffixed) string and (if the cache is larger than Expr.cacheLength) + * deleting the oldest entry + */ +function createCache() { + var keys = []; + + function cache( key, value ) { + + // Use (key + " ") to avoid collision with native prototype properties (see Issue #157) + if ( keys.push( key + " " ) > Expr.cacheLength ) { + + // Only keep the most recent entries + delete cache[ keys.shift() ]; + } + return ( cache[ key + " " ] = value ); + } + return cache; +} + +/** + * Mark a function for special use by Sizzle + * @param {Function} fn The function to mark + */ +function markFunction( fn ) { + fn[ expando ] = true; + return fn; +} + +/** + * Support testing using an element + * @param {Function} fn Passed the created element and returns a boolean result + */ +function assert( fn ) { + var el = document.createElement( "fieldset" ); + + try { + return !!fn( el ); + } catch ( e ) { + return false; + } finally { + + // Remove from its parent by default + if ( el.parentNode ) { + el.parentNode.removeChild( el ); + } + + // release memory in IE + el = null; + } +} + +/** + * Adds the same handler for all of the specified attrs + * @param {String} attrs Pipe-separated list of attributes + * @param {Function} handler The method that will be applied + */ +function addHandle( attrs, handler ) { + var arr = attrs.split( "|" ), + i = arr.length; + + while ( i-- ) { + Expr.attrHandle[ arr[ i ] ] = handler; + } +} + +/** + * Checks document order of two siblings + * @param {Element} a + * @param {Element} b + * @returns {Number} Returns less than 0 if a precedes b, greater than 0 if a follows b + */ +function siblingCheck( a, b ) { + var cur = b && a, + diff = cur && a.nodeType === 1 && b.nodeType === 1 && + a.sourceIndex - b.sourceIndex; + + // Use IE sourceIndex if available on both nodes + if ( diff ) { + return diff; + } + + // Check if b follows a + if ( cur ) { + while ( ( cur = cur.nextSibling ) ) { + if ( cur === b ) { + return -1; + } + } + } + + return a ? 1 : -1; +} + +/** + * Returns a function to use in pseudos for input types + * @param {String} type + */ +function createInputPseudo( type ) { + return function( elem ) { + var name = elem.nodeName.toLowerCase(); + return name === "input" && elem.type === type; + }; +} + +/** + * Returns a function to use in pseudos for buttons + * @param {String} type + */ +function createButtonPseudo( type ) { + return function( elem ) { + var name = elem.nodeName.toLowerCase(); + return ( name === "input" || name === "button" ) && elem.type === type; + }; +} + +/** + * Returns a function to use in pseudos for :enabled/:disabled + * @param {Boolean} disabled true for :disabled; false for :enabled + */ +function createDisabledPseudo( disabled ) { + + // Known :disabled false positives: fieldset[disabled] > legend:nth-of-type(n+2) :can-disable + return function( elem ) { + + // Only certain elements can match :enabled or :disabled + // https://html.spec.whatwg.org/multipage/scripting.html#selector-enabled + // https://html.spec.whatwg.org/multipage/scripting.html#selector-disabled + if ( "form" in elem ) { + + // Check for inherited disabledness on relevant non-disabled elements: + // * listed form-associated elements in a disabled fieldset + // https://html.spec.whatwg.org/multipage/forms.html#category-listed + // https://html.spec.whatwg.org/multipage/forms.html#concept-fe-disabled + // * option elements in a disabled optgroup + // https://html.spec.whatwg.org/multipage/forms.html#concept-option-disabled + // All such elements have a "form" property. + if ( elem.parentNode && elem.disabled === false ) { + + // Option elements defer to a parent optgroup if present + if ( "label" in elem ) { + if ( "label" in elem.parentNode ) { + return elem.parentNode.disabled === disabled; + } else { + return elem.disabled === disabled; + } + } + + // Support: IE 6 - 11 + // Use the isDisabled shortcut property to check for disabled fieldset ancestors + return elem.isDisabled === disabled || + + // Where there is no isDisabled, check manually + /* jshint -W018 */ + elem.isDisabled !== !disabled && + inDisabledFieldset( elem ) === disabled; + } + + return elem.disabled === disabled; + + // Try to winnow out elements that can't be disabled before trusting the disabled property. + // Some victims get caught in our net (label, legend, menu, track), but it shouldn't + // even exist on them, let alone have a boolean value. + } else if ( "label" in elem ) { + return elem.disabled === disabled; + } + + // Remaining elements are neither :enabled nor :disabled + return false; + }; +} + +/** + * Returns a function to use in pseudos for positionals + * @param {Function} fn + */ +function createPositionalPseudo( fn ) { + return markFunction( function( argument ) { + argument = +argument; + return markFunction( function( seed, matches ) { + var j, + matchIndexes = fn( [], seed.length, argument ), + i = matchIndexes.length; + + // Match elements found at the specified indexes + while ( i-- ) { + if ( seed[ ( j = matchIndexes[ i ] ) ] ) { + seed[ j ] = !( matches[ j ] = seed[ j ] ); + } + } + } ); + } ); +} + +/** + * Checks a node for validity as a Sizzle context + * @param {Element|Object=} context + * @returns {Element|Object|Boolean} The input node if acceptable, otherwise a falsy value + */ +function testContext( context ) { + return context && typeof context.getElementsByTagName !== "undefined" && context; +} + +// Expose support vars for convenience +support = Sizzle.support = {}; + +/** + * Detects XML nodes + * @param {Element|Object} elem An element or a document + * @returns {Boolean} True iff elem is a non-HTML XML node + */ +isXML = Sizzle.isXML = function( elem ) { + var namespace = elem && elem.namespaceURI, + docElem = elem && ( elem.ownerDocument || elem ).documentElement; + + // Support: IE <=8 + // Assume HTML when documentElement doesn't yet exist, such as inside loading iframes + // https://bugs.jquery.com/ticket/4833 + return !rhtml.test( namespace || docElem && docElem.nodeName || "HTML" ); +}; + +/** + * Sets document-related variables once based on the current document + * @param {Element|Object} [doc] An element or document object to use to set the document + * @returns {Object} Returns the current document + */ +setDocument = Sizzle.setDocument = function( node ) { + var hasCompare, subWindow, + doc = node ? node.ownerDocument || node : preferredDoc; + + // Return early if doc is invalid or already selected + // Support: IE 11+, Edge 17 - 18+ + // IE/Edge sometimes throw a "Permission denied" error when strict-comparing + // two documents; shallow comparisons work. + // eslint-disable-next-line eqeqeq + if ( doc == document || doc.nodeType !== 9 || !doc.documentElement ) { + return document; + } + + // Update global variables + document = doc; + docElem = document.documentElement; + documentIsHTML = !isXML( document ); + + // Support: IE 9 - 11+, Edge 12 - 18+ + // Accessing iframe documents after unload throws "permission denied" errors (jQuery #13936) + // Support: IE 11+, Edge 17 - 18+ + // IE/Edge sometimes throw a "Permission denied" error when strict-comparing + // two documents; shallow comparisons work. + // eslint-disable-next-line eqeqeq + if ( preferredDoc != document && + ( subWindow = document.defaultView ) && subWindow.top !== subWindow ) { + + // Support: IE 11, Edge + if ( subWindow.addEventListener ) { + subWindow.addEventListener( "unload", unloadHandler, false ); + + // Support: IE 9 - 10 only + } else if ( subWindow.attachEvent ) { + subWindow.attachEvent( "onunload", unloadHandler ); + } + } + + // Support: IE 8 - 11+, Edge 12 - 18+, Chrome <=16 - 25 only, Firefox <=3.6 - 31 only, + // Safari 4 - 5 only, Opera <=11.6 - 12.x only + // IE/Edge & older browsers don't support the :scope pseudo-class. + // Support: Safari 6.0 only + // Safari 6.0 supports :scope but it's an alias of :root there. + support.scope = assert( function( el ) { + docElem.appendChild( el ).appendChild( document.createElement( "div" ) ); + return typeof el.querySelectorAll !== "undefined" && + !el.querySelectorAll( ":scope fieldset div" ).length; + } ); + + /* Attributes + ---------------------------------------------------------------------- */ + + // Support: IE<8 + // Verify that getAttribute really returns attributes and not properties + // (excepting IE8 booleans) + support.attributes = assert( function( el ) { + el.className = "i"; + return !el.getAttribute( "className" ); + } ); + + /* getElement(s)By* + ---------------------------------------------------------------------- */ + + // Check if getElementsByTagName("*") returns only elements + support.getElementsByTagName = assert( function( el ) { + el.appendChild( document.createComment( "" ) ); + return !el.getElementsByTagName( "*" ).length; + } ); + + // Support: IE<9 + support.getElementsByClassName = rnative.test( document.getElementsByClassName ); + + // Support: IE<10 + // Check if getElementById returns elements by name + // The broken getElementById methods don't pick up programmatically-set names, + // so use a roundabout getElementsByName test + support.getById = assert( function( el ) { + docElem.appendChild( el ).id = expando; + return !document.getElementsByName || !document.getElementsByName( expando ).length; + } ); + + // ID filter and find + if ( support.getById ) { + Expr.filter[ "ID" ] = function( id ) { + var attrId = id.replace( runescape, funescape ); + return function( elem ) { + return elem.getAttribute( "id" ) === attrId; + }; + }; + Expr.find[ "ID" ] = function( id, context ) { + if ( typeof context.getElementById !== "undefined" && documentIsHTML ) { + var elem = context.getElementById( id ); + return elem ? [ elem ] : []; + } + }; + } else { + Expr.filter[ "ID" ] = function( id ) { + var attrId = id.replace( runescape, funescape ); + return function( elem ) { + var node = typeof elem.getAttributeNode !== "undefined" && + elem.getAttributeNode( "id" ); + return node && node.value === attrId; + }; + }; + + // Support: IE 6 - 7 only + // getElementById is not reliable as a find shortcut + Expr.find[ "ID" ] = function( id, context ) { + if ( typeof context.getElementById !== "undefined" && documentIsHTML ) { + var node, i, elems, + elem = context.getElementById( id ); + + if ( elem ) { + + // Verify the id attribute + node = elem.getAttributeNode( "id" ); + if ( node && node.value === id ) { + return [ elem ]; + } + + // Fall back on getElementsByName + elems = context.getElementsByName( id ); + i = 0; + while ( ( elem = elems[ i++ ] ) ) { + node = elem.getAttributeNode( "id" ); + if ( node && node.value === id ) { + return [ elem ]; + } + } + } + + return []; + } + }; + } + + // Tag + Expr.find[ "TAG" ] = support.getElementsByTagName ? + function( tag, context ) { + if ( typeof context.getElementsByTagName !== "undefined" ) { + return context.getElementsByTagName( tag ); + + // DocumentFragment nodes don't have gEBTN + } else if ( support.qsa ) { + return context.querySelectorAll( tag ); + } + } : + + function( tag, context ) { + var elem, + tmp = [], + i = 0, + + // By happy coincidence, a (broken) gEBTN appears on DocumentFragment nodes too + results = context.getElementsByTagName( tag ); + + // Filter out possible comments + if ( tag === "*" ) { + while ( ( elem = results[ i++ ] ) ) { + if ( elem.nodeType === 1 ) { + tmp.push( elem ); + } + } + + return tmp; + } + return results; + }; + + // Class + Expr.find[ "CLASS" ] = support.getElementsByClassName && function( className, context ) { + if ( typeof context.getElementsByClassName !== "undefined" && documentIsHTML ) { + return context.getElementsByClassName( className ); + } + }; + + /* QSA/matchesSelector + ---------------------------------------------------------------------- */ + + // QSA and matchesSelector support + + // matchesSelector(:active) reports false when true (IE9/Opera 11.5) + rbuggyMatches = []; + + // qSa(:focus) reports false when true (Chrome 21) + // We allow this because of a bug in IE8/9 that throws an error + // whenever `document.activeElement` is accessed on an iframe + // So, we allow :focus to pass through QSA all the time to avoid the IE error + // See https://bugs.jquery.com/ticket/13378 + rbuggyQSA = []; + + if ( ( support.qsa = rnative.test( document.querySelectorAll ) ) ) { + + // Build QSA regex + // Regex strategy adopted from Diego Perini + assert( function( el ) { + + var input; + + // Select is set to empty string on purpose + // This is to test IE's treatment of not explicitly + // setting a boolean content attribute, + // since its presence should be enough + // https://bugs.jquery.com/ticket/12359 + docElem.appendChild( el ).innerHTML = "" + + ""; + + // Support: IE8, Opera 11-12.16 + // Nothing should be selected when empty strings follow ^= or $= or *= + // The test attribute must be unknown in Opera but "safe" for WinRT + // https://msdn.microsoft.com/en-us/library/ie/hh465388.aspx#attribute_section + if ( el.querySelectorAll( "[msallowcapture^='']" ).length ) { + rbuggyQSA.push( "[*^$]=" + whitespace + "*(?:''|\"\")" ); + } + + // Support: IE8 + // Boolean attributes and "value" are not treated correctly + if ( !el.querySelectorAll( "[selected]" ).length ) { + rbuggyQSA.push( "\\[" + whitespace + "*(?:value|" + booleans + ")" ); + } + + // Support: Chrome<29, Android<4.4, Safari<7.0+, iOS<7.0+, PhantomJS<1.9.8+ + if ( !el.querySelectorAll( "[id~=" + expando + "-]" ).length ) { + rbuggyQSA.push( "~=" ); + } + + // Support: IE 11+, Edge 15 - 18+ + // IE 11/Edge don't find elements on a `[name='']` query in some cases. + // Adding a temporary attribute to the document before the selection works + // around the issue. + // Interestingly, IE 10 & older don't seem to have the issue. + input = document.createElement( "input" ); + input.setAttribute( "name", "" ); + el.appendChild( input ); + if ( !el.querySelectorAll( "[name='']" ).length ) { + rbuggyQSA.push( "\\[" + whitespace + "*name" + whitespace + "*=" + + whitespace + "*(?:''|\"\")" ); + } + + // Webkit/Opera - :checked should return selected option elements + // http://www.w3.org/TR/2011/REC-css3-selectors-20110929/#checked + // IE8 throws error here and will not see later tests + if ( !el.querySelectorAll( ":checked" ).length ) { + rbuggyQSA.push( ":checked" ); + } + + // Support: Safari 8+, iOS 8+ + // https://bugs.webkit.org/show_bug.cgi?id=136851 + // In-page `selector#id sibling-combinator selector` fails + if ( !el.querySelectorAll( "a#" + expando + "+*" ).length ) { + rbuggyQSA.push( ".#.+[+~]" ); + } + + // Support: Firefox <=3.6 - 5 only + // Old Firefox doesn't throw on a badly-escaped identifier. + el.querySelectorAll( "\\\f" ); + rbuggyQSA.push( "[\\r\\n\\f]" ); + } ); + + assert( function( el ) { + el.innerHTML = "" + + ""; + + // Support: Windows 8 Native Apps + // The type and name attributes are restricted during .innerHTML assignment + var input = document.createElement( "input" ); + input.setAttribute( "type", "hidden" ); + el.appendChild( input ).setAttribute( "name", "D" ); + + // Support: IE8 + // Enforce case-sensitivity of name attribute + if ( el.querySelectorAll( "[name=d]" ).length ) { + rbuggyQSA.push( "name" + whitespace + "*[*^$|!~]?=" ); + } + + // FF 3.5 - :enabled/:disabled and hidden elements (hidden elements are still enabled) + // IE8 throws error here and will not see later tests + if ( el.querySelectorAll( ":enabled" ).length !== 2 ) { + rbuggyQSA.push( ":enabled", ":disabled" ); + } + + // Support: IE9-11+ + // IE's :disabled selector does not pick up the children of disabled fieldsets + docElem.appendChild( el ).disabled = true; + if ( el.querySelectorAll( ":disabled" ).length !== 2 ) { + rbuggyQSA.push( ":enabled", ":disabled" ); + } + + // Support: Opera 10 - 11 only + // Opera 10-11 does not throw on post-comma invalid pseudos + el.querySelectorAll( "*,:x" ); + rbuggyQSA.push( ",.*:" ); + } ); + } + + if ( ( support.matchesSelector = rnative.test( ( matches = docElem.matches || + docElem.webkitMatchesSelector || + docElem.mozMatchesSelector || + docElem.oMatchesSelector || + docElem.msMatchesSelector ) ) ) ) { + + assert( function( el ) { + + // Check to see if it's possible to do matchesSelector + // on a disconnected node (IE 9) + support.disconnectedMatch = matches.call( el, "*" ); + + // This should fail with an exception + // Gecko does not error, returns false instead + matches.call( el, "[s!='']:x" ); + rbuggyMatches.push( "!=", pseudos ); + } ); + } + + rbuggyQSA = rbuggyQSA.length && new RegExp( rbuggyQSA.join( "|" ) ); + rbuggyMatches = rbuggyMatches.length && new RegExp( rbuggyMatches.join( "|" ) ); + + /* Contains + ---------------------------------------------------------------------- */ + hasCompare = rnative.test( docElem.compareDocumentPosition ); + + // Element contains another + // Purposefully self-exclusive + // As in, an element does not contain itself + contains = hasCompare || rnative.test( docElem.contains ) ? + function( a, b ) { + var adown = a.nodeType === 9 ? a.documentElement : a, + bup = b && b.parentNode; + return a === bup || !!( bup && bup.nodeType === 1 && ( + adown.contains ? + adown.contains( bup ) : + a.compareDocumentPosition && a.compareDocumentPosition( bup ) & 16 + ) ); + } : + function( a, b ) { + if ( b ) { + while ( ( b = b.parentNode ) ) { + if ( b === a ) { + return true; + } + } + } + return false; + }; + + /* Sorting + ---------------------------------------------------------------------- */ + + // Document order sorting + sortOrder = hasCompare ? + function( a, b ) { + + // Flag for duplicate removal + if ( a === b ) { + hasDuplicate = true; + return 0; + } + + // Sort on method existence if only one input has compareDocumentPosition + var compare = !a.compareDocumentPosition - !b.compareDocumentPosition; + if ( compare ) { + return compare; + } + + // Calculate position if both inputs belong to the same document + // Support: IE 11+, Edge 17 - 18+ + // IE/Edge sometimes throw a "Permission denied" error when strict-comparing + // two documents; shallow comparisons work. + // eslint-disable-next-line eqeqeq + compare = ( a.ownerDocument || a ) == ( b.ownerDocument || b ) ? + a.compareDocumentPosition( b ) : + + // Otherwise we know they are disconnected + 1; + + // Disconnected nodes + if ( compare & 1 || + ( !support.sortDetached && b.compareDocumentPosition( a ) === compare ) ) { + + // Choose the first element that is related to our preferred document + // Support: IE 11+, Edge 17 - 18+ + // IE/Edge sometimes throw a "Permission denied" error when strict-comparing + // two documents; shallow comparisons work. + // eslint-disable-next-line eqeqeq + if ( a == document || a.ownerDocument == preferredDoc && + contains( preferredDoc, a ) ) { + return -1; + } + + // Support: IE 11+, Edge 17 - 18+ + // IE/Edge sometimes throw a "Permission denied" error when strict-comparing + // two documents; shallow comparisons work. + // eslint-disable-next-line eqeqeq + if ( b == document || b.ownerDocument == preferredDoc && + contains( preferredDoc, b ) ) { + return 1; + } + + // Maintain original order + return sortInput ? + ( indexOf( sortInput, a ) - indexOf( sortInput, b ) ) : + 0; + } + + return compare & 4 ? -1 : 1; + } : + function( a, b ) { + + // Exit early if the nodes are identical + if ( a === b ) { + hasDuplicate = true; + return 0; + } + + var cur, + i = 0, + aup = a.parentNode, + bup = b.parentNode, + ap = [ a ], + bp = [ b ]; + + // Parentless nodes are either documents or disconnected + if ( !aup || !bup ) { + + // Support: IE 11+, Edge 17 - 18+ + // IE/Edge sometimes throw a "Permission denied" error when strict-comparing + // two documents; shallow comparisons work. + /* eslint-disable eqeqeq */ + return a == document ? -1 : + b == document ? 1 : + /* eslint-enable eqeqeq */ + aup ? -1 : + bup ? 1 : + sortInput ? + ( indexOf( sortInput, a ) - indexOf( sortInput, b ) ) : + 0; + + // If the nodes are siblings, we can do a quick check + } else if ( aup === bup ) { + return siblingCheck( a, b ); + } + + // Otherwise we need full lists of their ancestors for comparison + cur = a; + while ( ( cur = cur.parentNode ) ) { + ap.unshift( cur ); + } + cur = b; + while ( ( cur = cur.parentNode ) ) { + bp.unshift( cur ); + } + + // Walk down the tree looking for a discrepancy + while ( ap[ i ] === bp[ i ] ) { + i++; + } + + return i ? + + // Do a sibling check if the nodes have a common ancestor + siblingCheck( ap[ i ], bp[ i ] ) : + + // Otherwise nodes in our document sort first + // Support: IE 11+, Edge 17 - 18+ + // IE/Edge sometimes throw a "Permission denied" error when strict-comparing + // two documents; shallow comparisons work. + /* eslint-disable eqeqeq */ + ap[ i ] == preferredDoc ? -1 : + bp[ i ] == preferredDoc ? 1 : + /* eslint-enable eqeqeq */ + 0; + }; + + return document; +}; + +Sizzle.matches = function( expr, elements ) { + return Sizzle( expr, null, null, elements ); +}; + +Sizzle.matchesSelector = function( elem, expr ) { + setDocument( elem ); + + if ( support.matchesSelector && documentIsHTML && + !nonnativeSelectorCache[ expr + " " ] && + ( !rbuggyMatches || !rbuggyMatches.test( expr ) ) && + ( !rbuggyQSA || !rbuggyQSA.test( expr ) ) ) { + + try { + var ret = matches.call( elem, expr ); + + // IE 9's matchesSelector returns false on disconnected nodes + if ( ret || support.disconnectedMatch || + + // As well, disconnected nodes are said to be in a document + // fragment in IE 9 + elem.document && elem.document.nodeType !== 11 ) { + return ret; + } + } catch ( e ) { + nonnativeSelectorCache( expr, true ); + } + } + + return Sizzle( expr, document, null, [ elem ] ).length > 0; +}; + +Sizzle.contains = function( context, elem ) { + + // Set document vars if needed + // Support: IE 11+, Edge 17 - 18+ + // IE/Edge sometimes throw a "Permission denied" error when strict-comparing + // two documents; shallow comparisons work. + // eslint-disable-next-line eqeqeq + if ( ( context.ownerDocument || context ) != document ) { + setDocument( context ); + } + return contains( context, elem ); +}; + +Sizzle.attr = function( elem, name ) { + + // Set document vars if needed + // Support: IE 11+, Edge 17 - 18+ + // IE/Edge sometimes throw a "Permission denied" error when strict-comparing + // two documents; shallow comparisons work. + // eslint-disable-next-line eqeqeq + if ( ( elem.ownerDocument || elem ) != document ) { + setDocument( elem ); + } + + var fn = Expr.attrHandle[ name.toLowerCase() ], + + // Don't get fooled by Object.prototype properties (jQuery #13807) + val = fn && hasOwn.call( Expr.attrHandle, name.toLowerCase() ) ? + fn( elem, name, !documentIsHTML ) : + undefined; + + return val !== undefined ? + val : + support.attributes || !documentIsHTML ? + elem.getAttribute( name ) : + ( val = elem.getAttributeNode( name ) ) && val.specified ? + val.value : + null; +}; + +Sizzle.escape = function( sel ) { + return ( sel + "" ).replace( rcssescape, fcssescape ); +}; + +Sizzle.error = function( msg ) { + throw new Error( "Syntax error, unrecognized expression: " + msg ); +}; + +/** + * Document sorting and removing duplicates + * @param {ArrayLike} results + */ +Sizzle.uniqueSort = function( results ) { + var elem, + duplicates = [], + j = 0, + i = 0; + + // Unless we *know* we can detect duplicates, assume their presence + hasDuplicate = !support.detectDuplicates; + sortInput = !support.sortStable && results.slice( 0 ); + results.sort( sortOrder ); + + if ( hasDuplicate ) { + while ( ( elem = results[ i++ ] ) ) { + if ( elem === results[ i ] ) { + j = duplicates.push( i ); + } + } + while ( j-- ) { + results.splice( duplicates[ j ], 1 ); + } + } + + // Clear input after sorting to release objects + // See https://github.com/jquery/sizzle/pull/225 + sortInput = null; + + return results; +}; + +/** + * Utility function for retrieving the text value of an array of DOM nodes + * @param {Array|Element} elem + */ +getText = Sizzle.getText = function( elem ) { + var node, + ret = "", + i = 0, + nodeType = elem.nodeType; + + if ( !nodeType ) { + + // If no nodeType, this is expected to be an array + while ( ( node = elem[ i++ ] ) ) { + + // Do not traverse comment nodes + ret += getText( node ); + } + } else if ( nodeType === 1 || nodeType === 9 || nodeType === 11 ) { + + // Use textContent for elements + // innerText usage removed for consistency of new lines (jQuery #11153) + if ( typeof elem.textContent === "string" ) { + return elem.textContent; + } else { + + // Traverse its children + for ( elem = elem.firstChild; elem; elem = elem.nextSibling ) { + ret += getText( elem ); + } + } + } else if ( nodeType === 3 || nodeType === 4 ) { + return elem.nodeValue; + } + + // Do not include comment or processing instruction nodes + + return ret; +}; + +Expr = Sizzle.selectors = { + + // Can be adjusted by the user + cacheLength: 50, + + createPseudo: markFunction, + + match: matchExpr, + + attrHandle: {}, + + find: {}, + + relative: { + ">": { dir: "parentNode", first: true }, + " ": { dir: "parentNode" }, + "+": { dir: "previousSibling", first: true }, + "~": { dir: "previousSibling" } + }, + + preFilter: { + "ATTR": function( match ) { + match[ 1 ] = match[ 1 ].replace( runescape, funescape ); + + // Move the given value to match[3] whether quoted or unquoted + match[ 3 ] = ( match[ 3 ] || match[ 4 ] || + match[ 5 ] || "" ).replace( runescape, funescape ); + + if ( match[ 2 ] === "~=" ) { + match[ 3 ] = " " + match[ 3 ] + " "; + } + + return match.slice( 0, 4 ); + }, + + "CHILD": function( match ) { + + /* matches from matchExpr["CHILD"] + 1 type (only|nth|...) + 2 what (child|of-type) + 3 argument (even|odd|\d*|\d*n([+-]\d+)?|...) + 4 xn-component of xn+y argument ([+-]?\d*n|) + 5 sign of xn-component + 6 x of xn-component + 7 sign of y-component + 8 y of y-component + */ + match[ 1 ] = match[ 1 ].toLowerCase(); + + if ( match[ 1 ].slice( 0, 3 ) === "nth" ) { + + // nth-* requires argument + if ( !match[ 3 ] ) { + Sizzle.error( match[ 0 ] ); + } + + // numeric x and y parameters for Expr.filter.CHILD + // remember that false/true cast respectively to 0/1 + match[ 4 ] = +( match[ 4 ] ? + match[ 5 ] + ( match[ 6 ] || 1 ) : + 2 * ( match[ 3 ] === "even" || match[ 3 ] === "odd" ) ); + match[ 5 ] = +( ( match[ 7 ] + match[ 8 ] ) || match[ 3 ] === "odd" ); + + // other types prohibit arguments + } else if ( match[ 3 ] ) { + Sizzle.error( match[ 0 ] ); + } + + return match; + }, + + "PSEUDO": function( match ) { + var excess, + unquoted = !match[ 6 ] && match[ 2 ]; + + if ( matchExpr[ "CHILD" ].test( match[ 0 ] ) ) { + return null; + } + + // Accept quoted arguments as-is + if ( match[ 3 ] ) { + match[ 2 ] = match[ 4 ] || match[ 5 ] || ""; + + // Strip excess characters from unquoted arguments + } else if ( unquoted && rpseudo.test( unquoted ) && + + // Get excess from tokenize (recursively) + ( excess = tokenize( unquoted, true ) ) && + + // advance to the next closing parenthesis + ( excess = unquoted.indexOf( ")", unquoted.length - excess ) - unquoted.length ) ) { + + // excess is a negative index + match[ 0 ] = match[ 0 ].slice( 0, excess ); + match[ 2 ] = unquoted.slice( 0, excess ); + } + + // Return only captures needed by the pseudo filter method (type and argument) + return match.slice( 0, 3 ); + } + }, + + filter: { + + "TAG": function( nodeNameSelector ) { + var nodeName = nodeNameSelector.replace( runescape, funescape ).toLowerCase(); + return nodeNameSelector === "*" ? + function() { + return true; + } : + function( elem ) { + return elem.nodeName && elem.nodeName.toLowerCase() === nodeName; + }; + }, + + "CLASS": function( className ) { + var pattern = classCache[ className + " " ]; + + return pattern || + ( pattern = new RegExp( "(^|" + whitespace + + ")" + className + "(" + whitespace + "|$)" ) ) && classCache( + className, function( elem ) { + return pattern.test( + typeof elem.className === "string" && elem.className || + typeof elem.getAttribute !== "undefined" && + elem.getAttribute( "class" ) || + "" + ); + } ); + }, + + "ATTR": function( name, operator, check ) { + return function( elem ) { + var result = Sizzle.attr( elem, name ); + + if ( result == null ) { + return operator === "!="; + } + if ( !operator ) { + return true; + } + + result += ""; + + /* eslint-disable max-len */ + + return operator === "=" ? result === check : + operator === "!=" ? result !== check : + operator === "^=" ? check && result.indexOf( check ) === 0 : + operator === "*=" ? check && result.indexOf( check ) > -1 : + operator === "$=" ? check && result.slice( -check.length ) === check : + operator === "~=" ? ( " " + result.replace( rwhitespace, " " ) + " " ).indexOf( check ) > -1 : + operator === "|=" ? result === check || result.slice( 0, check.length + 1 ) === check + "-" : + false; + /* eslint-enable max-len */ + + }; + }, + + "CHILD": function( type, what, _argument, first, last ) { + var simple = type.slice( 0, 3 ) !== "nth", + forward = type.slice( -4 ) !== "last", + ofType = what === "of-type"; + + return first === 1 && last === 0 ? + + // Shortcut for :nth-*(n) + function( elem ) { + return !!elem.parentNode; + } : + + function( elem, _context, xml ) { + var cache, uniqueCache, outerCache, node, nodeIndex, start, + dir = simple !== forward ? "nextSibling" : "previousSibling", + parent = elem.parentNode, + name = ofType && elem.nodeName.toLowerCase(), + useCache = !xml && !ofType, + diff = false; + + if ( parent ) { + + // :(first|last|only)-(child|of-type) + if ( simple ) { + while ( dir ) { + node = elem; + while ( ( node = node[ dir ] ) ) { + if ( ofType ? + node.nodeName.toLowerCase() === name : + node.nodeType === 1 ) { + + return false; + } + } + + // Reverse direction for :only-* (if we haven't yet done so) + start = dir = type === "only" && !start && "nextSibling"; + } + return true; + } + + start = [ forward ? parent.firstChild : parent.lastChild ]; + + // non-xml :nth-child(...) stores cache data on `parent` + if ( forward && useCache ) { + + // Seek `elem` from a previously-cached index + + // ...in a gzip-friendly way + node = parent; + outerCache = node[ expando ] || ( node[ expando ] = {} ); + + // Support: IE <9 only + // Defend against cloned attroperties (jQuery gh-1709) + uniqueCache = outerCache[ node.uniqueID ] || + ( outerCache[ node.uniqueID ] = {} ); + + cache = uniqueCache[ type ] || []; + nodeIndex = cache[ 0 ] === dirruns && cache[ 1 ]; + diff = nodeIndex && cache[ 2 ]; + node = nodeIndex && parent.childNodes[ nodeIndex ]; + + while ( ( node = ++nodeIndex && node && node[ dir ] || + + // Fallback to seeking `elem` from the start + ( diff = nodeIndex = 0 ) || start.pop() ) ) { + + // When found, cache indexes on `parent` and break + if ( node.nodeType === 1 && ++diff && node === elem ) { + uniqueCache[ type ] = [ dirruns, nodeIndex, diff ]; + break; + } + } + + } else { + + // Use previously-cached element index if available + if ( useCache ) { + + // ...in a gzip-friendly way + node = elem; + outerCache = node[ expando ] || ( node[ expando ] = {} ); + + // Support: IE <9 only + // Defend against cloned attroperties (jQuery gh-1709) + uniqueCache = outerCache[ node.uniqueID ] || + ( outerCache[ node.uniqueID ] = {} ); + + cache = uniqueCache[ type ] || []; + nodeIndex = cache[ 0 ] === dirruns && cache[ 1 ]; + diff = nodeIndex; + } + + // xml :nth-child(...) + // or :nth-last-child(...) or :nth(-last)?-of-type(...) + if ( diff === false ) { + + // Use the same loop as above to seek `elem` from the start + while ( ( node = ++nodeIndex && node && node[ dir ] || + ( diff = nodeIndex = 0 ) || start.pop() ) ) { + + if ( ( ofType ? + node.nodeName.toLowerCase() === name : + node.nodeType === 1 ) && + ++diff ) { + + // Cache the index of each encountered element + if ( useCache ) { + outerCache = node[ expando ] || + ( node[ expando ] = {} ); + + // Support: IE <9 only + // Defend against cloned attroperties (jQuery gh-1709) + uniqueCache = outerCache[ node.uniqueID ] || + ( outerCache[ node.uniqueID ] = {} ); + + uniqueCache[ type ] = [ dirruns, diff ]; + } + + if ( node === elem ) { + break; + } + } + } + } + } + + // Incorporate the offset, then check against cycle size + diff -= last; + return diff === first || ( diff % first === 0 && diff / first >= 0 ); + } + }; + }, + + "PSEUDO": function( pseudo, argument ) { + + // pseudo-class names are case-insensitive + // http://www.w3.org/TR/selectors/#pseudo-classes + // Prioritize by case sensitivity in case custom pseudos are added with uppercase letters + // Remember that setFilters inherits from pseudos + var args, + fn = Expr.pseudos[ pseudo ] || Expr.setFilters[ pseudo.toLowerCase() ] || + Sizzle.error( "unsupported pseudo: " + pseudo ); + + // The user may use createPseudo to indicate that + // arguments are needed to create the filter function + // just as Sizzle does + if ( fn[ expando ] ) { + return fn( argument ); + } + + // But maintain support for old signatures + if ( fn.length > 1 ) { + args = [ pseudo, pseudo, "", argument ]; + return Expr.setFilters.hasOwnProperty( pseudo.toLowerCase() ) ? + markFunction( function( seed, matches ) { + var idx, + matched = fn( seed, argument ), + i = matched.length; + while ( i-- ) { + idx = indexOf( seed, matched[ i ] ); + seed[ idx ] = !( matches[ idx ] = matched[ i ] ); + } + } ) : + function( elem ) { + return fn( elem, 0, args ); + }; + } + + return fn; + } + }, + + pseudos: { + + // Potentially complex pseudos + "not": markFunction( function( selector ) { + + // Trim the selector passed to compile + // to avoid treating leading and trailing + // spaces as combinators + var input = [], + results = [], + matcher = compile( selector.replace( rtrim, "$1" ) ); + + return matcher[ expando ] ? + markFunction( function( seed, matches, _context, xml ) { + var elem, + unmatched = matcher( seed, null, xml, [] ), + i = seed.length; + + // Match elements unmatched by `matcher` + while ( i-- ) { + if ( ( elem = unmatched[ i ] ) ) { + seed[ i ] = !( matches[ i ] = elem ); + } + } + } ) : + function( elem, _context, xml ) { + input[ 0 ] = elem; + matcher( input, null, xml, results ); + + // Don't keep the element (issue #299) + input[ 0 ] = null; + return !results.pop(); + }; + } ), + + "has": markFunction( function( selector ) { + return function( elem ) { + return Sizzle( selector, elem ).length > 0; + }; + } ), + + "contains": markFunction( function( text ) { + text = text.replace( runescape, funescape ); + return function( elem ) { + return ( elem.textContent || getText( elem ) ).indexOf( text ) > -1; + }; + } ), + + // "Whether an element is represented by a :lang() selector + // is based solely on the element's language value + // being equal to the identifier C, + // or beginning with the identifier C immediately followed by "-". + // The matching of C against the element's language value is performed case-insensitively. + // The identifier C does not have to be a valid language name." + // http://www.w3.org/TR/selectors/#lang-pseudo + "lang": markFunction( function( lang ) { + + // lang value must be a valid identifier + if ( !ridentifier.test( lang || "" ) ) { + Sizzle.error( "unsupported lang: " + lang ); + } + lang = lang.replace( runescape, funescape ).toLowerCase(); + return function( elem ) { + var elemLang; + do { + if ( ( elemLang = documentIsHTML ? + elem.lang : + elem.getAttribute( "xml:lang" ) || elem.getAttribute( "lang" ) ) ) { + + elemLang = elemLang.toLowerCase(); + return elemLang === lang || elemLang.indexOf( lang + "-" ) === 0; + } + } while ( ( elem = elem.parentNode ) && elem.nodeType === 1 ); + return false; + }; + } ), + + // Miscellaneous + "target": function( elem ) { + var hash = window.location && window.location.hash; + return hash && hash.slice( 1 ) === elem.id; + }, + + "root": function( elem ) { + return elem === docElem; + }, + + "focus": function( elem ) { + return elem === document.activeElement && + ( !document.hasFocus || document.hasFocus() ) && + !!( elem.type || elem.href || ~elem.tabIndex ); + }, + + // Boolean properties + "enabled": createDisabledPseudo( false ), + "disabled": createDisabledPseudo( true ), + + "checked": function( elem ) { + + // In CSS3, :checked should return both checked and selected elements + // http://www.w3.org/TR/2011/REC-css3-selectors-20110929/#checked + var nodeName = elem.nodeName.toLowerCase(); + return ( nodeName === "input" && !!elem.checked ) || + ( nodeName === "option" && !!elem.selected ); + }, + + "selected": function( elem ) { + + // Accessing this property makes selected-by-default + // options in Safari work properly + if ( elem.parentNode ) { + // eslint-disable-next-line no-unused-expressions + elem.parentNode.selectedIndex; + } + + return elem.selected === true; + }, + + // Contents + "empty": function( elem ) { + + // http://www.w3.org/TR/selectors/#empty-pseudo + // :empty is negated by element (1) or content nodes (text: 3; cdata: 4; entity ref: 5), + // but not by others (comment: 8; processing instruction: 7; etc.) + // nodeType < 6 works because attributes (2) do not appear as children + for ( elem = elem.firstChild; elem; elem = elem.nextSibling ) { + if ( elem.nodeType < 6 ) { + return false; + } + } + return true; + }, + + "parent": function( elem ) { + return !Expr.pseudos[ "empty" ]( elem ); + }, + + // Element/input types + "header": function( elem ) { + return rheader.test( elem.nodeName ); + }, + + "input": function( elem ) { + return rinputs.test( elem.nodeName ); + }, + + "button": function( elem ) { + var name = elem.nodeName.toLowerCase(); + return name === "input" && elem.type === "button" || name === "button"; + }, + + "text": function( elem ) { + var attr; + return elem.nodeName.toLowerCase() === "input" && + elem.type === "text" && + + // Support: IE<8 + // New HTML5 attribute values (e.g., "search") appear with elem.type === "text" + ( ( attr = elem.getAttribute( "type" ) ) == null || + attr.toLowerCase() === "text" ); + }, + + // Position-in-collection + "first": createPositionalPseudo( function() { + return [ 0 ]; + } ), + + "last": createPositionalPseudo( function( _matchIndexes, length ) { + return [ length - 1 ]; + } ), + + "eq": createPositionalPseudo( function( _matchIndexes, length, argument ) { + return [ argument < 0 ? argument + length : argument ]; + } ), + + "even": createPositionalPseudo( function( matchIndexes, length ) { + var i = 0; + for ( ; i < length; i += 2 ) { + matchIndexes.push( i ); + } + return matchIndexes; + } ), + + "odd": createPositionalPseudo( function( matchIndexes, length ) { + var i = 1; + for ( ; i < length; i += 2 ) { + matchIndexes.push( i ); + } + return matchIndexes; + } ), + + "lt": createPositionalPseudo( function( matchIndexes, length, argument ) { + var i = argument < 0 ? + argument + length : + argument > length ? + length : + argument; + for ( ; --i >= 0; ) { + matchIndexes.push( i ); + } + return matchIndexes; + } ), + + "gt": createPositionalPseudo( function( matchIndexes, length, argument ) { + var i = argument < 0 ? argument + length : argument; + for ( ; ++i < length; ) { + matchIndexes.push( i ); + } + return matchIndexes; + } ) + } +}; + +Expr.pseudos[ "nth" ] = Expr.pseudos[ "eq" ]; + +// Add button/input type pseudos +for ( i in { radio: true, checkbox: true, file: true, password: true, image: true } ) { + Expr.pseudos[ i ] = createInputPseudo( i ); +} +for ( i in { submit: true, reset: true } ) { + Expr.pseudos[ i ] = createButtonPseudo( i ); +} + +// Easy API for creating new setFilters +function setFilters() {} +setFilters.prototype = Expr.filters = Expr.pseudos; +Expr.setFilters = new setFilters(); + +tokenize = Sizzle.tokenize = function( selector, parseOnly ) { + var matched, match, tokens, type, + soFar, groups, preFilters, + cached = tokenCache[ selector + " " ]; + + if ( cached ) { + return parseOnly ? 0 : cached.slice( 0 ); + } + + soFar = selector; + groups = []; + preFilters = Expr.preFilter; + + while ( soFar ) { + + // Comma and first run + if ( !matched || ( match = rcomma.exec( soFar ) ) ) { + if ( match ) { + + // Don't consume trailing commas as valid + soFar = soFar.slice( match[ 0 ].length ) || soFar; + } + groups.push( ( tokens = [] ) ); + } + + matched = false; + + // Combinators + if ( ( match = rcombinators.exec( soFar ) ) ) { + matched = match.shift(); + tokens.push( { + value: matched, + + // Cast descendant combinators to space + type: match[ 0 ].replace( rtrim, " " ) + } ); + soFar = soFar.slice( matched.length ); + } + + // Filters + for ( type in Expr.filter ) { + if ( ( match = matchExpr[ type ].exec( soFar ) ) && ( !preFilters[ type ] || + ( match = preFilters[ type ]( match ) ) ) ) { + matched = match.shift(); + tokens.push( { + value: matched, + type: type, + matches: match + } ); + soFar = soFar.slice( matched.length ); + } + } + + if ( !matched ) { + break; + } + } + + // Return the length of the invalid excess + // if we're just parsing + // Otherwise, throw an error or return tokens + return parseOnly ? + soFar.length : + soFar ? + Sizzle.error( selector ) : + + // Cache the tokens + tokenCache( selector, groups ).slice( 0 ); +}; + +function toSelector( tokens ) { + var i = 0, + len = tokens.length, + selector = ""; + for ( ; i < len; i++ ) { + selector += tokens[ i ].value; + } + return selector; +} + +function addCombinator( matcher, combinator, base ) { + var dir = combinator.dir, + skip = combinator.next, + key = skip || dir, + checkNonElements = base && key === "parentNode", + doneName = done++; + + return combinator.first ? + + // Check against closest ancestor/preceding element + function( elem, context, xml ) { + while ( ( elem = elem[ dir ] ) ) { + if ( elem.nodeType === 1 || checkNonElements ) { + return matcher( elem, context, xml ); + } + } + return false; + } : + + // Check against all ancestor/preceding elements + function( elem, context, xml ) { + var oldCache, uniqueCache, outerCache, + newCache = [ dirruns, doneName ]; + + // We can't set arbitrary data on XML nodes, so they don't benefit from combinator caching + if ( xml ) { + while ( ( elem = elem[ dir ] ) ) { + if ( elem.nodeType === 1 || checkNonElements ) { + if ( matcher( elem, context, xml ) ) { + return true; + } + } + } + } else { + while ( ( elem = elem[ dir ] ) ) { + if ( elem.nodeType === 1 || checkNonElements ) { + outerCache = elem[ expando ] || ( elem[ expando ] = {} ); + + // Support: IE <9 only + // Defend against cloned attroperties (jQuery gh-1709) + uniqueCache = outerCache[ elem.uniqueID ] || + ( outerCache[ elem.uniqueID ] = {} ); + + if ( skip && skip === elem.nodeName.toLowerCase() ) { + elem = elem[ dir ] || elem; + } else if ( ( oldCache = uniqueCache[ key ] ) && + oldCache[ 0 ] === dirruns && oldCache[ 1 ] === doneName ) { + + // Assign to newCache so results back-propagate to previous elements + return ( newCache[ 2 ] = oldCache[ 2 ] ); + } else { + + // Reuse newcache so results back-propagate to previous elements + uniqueCache[ key ] = newCache; + + // A match means we're done; a fail means we have to keep checking + if ( ( newCache[ 2 ] = matcher( elem, context, xml ) ) ) { + return true; + } + } + } + } + } + return false; + }; +} + +function elementMatcher( matchers ) { + return matchers.length > 1 ? + function( elem, context, xml ) { + var i = matchers.length; + while ( i-- ) { + if ( !matchers[ i ]( elem, context, xml ) ) { + return false; + } + } + return true; + } : + matchers[ 0 ]; +} + +function multipleContexts( selector, contexts, results ) { + var i = 0, + len = contexts.length; + for ( ; i < len; i++ ) { + Sizzle( selector, contexts[ i ], results ); + } + return results; +} + +function condense( unmatched, map, filter, context, xml ) { + var elem, + newUnmatched = [], + i = 0, + len = unmatched.length, + mapped = map != null; + + for ( ; i < len; i++ ) { + if ( ( elem = unmatched[ i ] ) ) { + if ( !filter || filter( elem, context, xml ) ) { + newUnmatched.push( elem ); + if ( mapped ) { + map.push( i ); + } + } + } + } + + return newUnmatched; +} + +function setMatcher( preFilter, selector, matcher, postFilter, postFinder, postSelector ) { + if ( postFilter && !postFilter[ expando ] ) { + postFilter = setMatcher( postFilter ); + } + if ( postFinder && !postFinder[ expando ] ) { + postFinder = setMatcher( postFinder, postSelector ); + } + return markFunction( function( seed, results, context, xml ) { + var temp, i, elem, + preMap = [], + postMap = [], + preexisting = results.length, + + // Get initial elements from seed or context + elems = seed || multipleContexts( + selector || "*", + context.nodeType ? [ context ] : context, + [] + ), + + // Prefilter to get matcher input, preserving a map for seed-results synchronization + matcherIn = preFilter && ( seed || !selector ) ? + condense( elems, preMap, preFilter, context, xml ) : + elems, + + matcherOut = matcher ? + + // If we have a postFinder, or filtered seed, or non-seed postFilter or preexisting results, + postFinder || ( seed ? preFilter : preexisting || postFilter ) ? + + // ...intermediate processing is necessary + [] : + + // ...otherwise use results directly + results : + matcherIn; + + // Find primary matches + if ( matcher ) { + matcher( matcherIn, matcherOut, context, xml ); + } + + // Apply postFilter + if ( postFilter ) { + temp = condense( matcherOut, postMap ); + postFilter( temp, [], context, xml ); + + // Un-match failing elements by moving them back to matcherIn + i = temp.length; + while ( i-- ) { + if ( ( elem = temp[ i ] ) ) { + matcherOut[ postMap[ i ] ] = !( matcherIn[ postMap[ i ] ] = elem ); + } + } + } + + if ( seed ) { + if ( postFinder || preFilter ) { + if ( postFinder ) { + + // Get the final matcherOut by condensing this intermediate into postFinder contexts + temp = []; + i = matcherOut.length; + while ( i-- ) { + if ( ( elem = matcherOut[ i ] ) ) { + + // Restore matcherIn since elem is not yet a final match + temp.push( ( matcherIn[ i ] = elem ) ); + } + } + postFinder( null, ( matcherOut = [] ), temp, xml ); + } + + // Move matched elements from seed to results to keep them synchronized + i = matcherOut.length; + while ( i-- ) { + if ( ( elem = matcherOut[ i ] ) && + ( temp = postFinder ? indexOf( seed, elem ) : preMap[ i ] ) > -1 ) { + + seed[ temp ] = !( results[ temp ] = elem ); + } + } + } + + // Add elements to results, through postFinder if defined + } else { + matcherOut = condense( + matcherOut === results ? + matcherOut.splice( preexisting, matcherOut.length ) : + matcherOut + ); + if ( postFinder ) { + postFinder( null, results, matcherOut, xml ); + } else { + push.apply( results, matcherOut ); + } + } + } ); +} + +function matcherFromTokens( tokens ) { + var checkContext, matcher, j, + len = tokens.length, + leadingRelative = Expr.relative[ tokens[ 0 ].type ], + implicitRelative = leadingRelative || Expr.relative[ " " ], + i = leadingRelative ? 1 : 0, + + // The foundational matcher ensures that elements are reachable from top-level context(s) + matchContext = addCombinator( function( elem ) { + return elem === checkContext; + }, implicitRelative, true ), + matchAnyContext = addCombinator( function( elem ) { + return indexOf( checkContext, elem ) > -1; + }, implicitRelative, true ), + matchers = [ function( elem, context, xml ) { + var ret = ( !leadingRelative && ( xml || context !== outermostContext ) ) || ( + ( checkContext = context ).nodeType ? + matchContext( elem, context, xml ) : + matchAnyContext( elem, context, xml ) ); + + // Avoid hanging onto element (issue #299) + checkContext = null; + return ret; + } ]; + + for ( ; i < len; i++ ) { + if ( ( matcher = Expr.relative[ tokens[ i ].type ] ) ) { + matchers = [ addCombinator( elementMatcher( matchers ), matcher ) ]; + } else { + matcher = Expr.filter[ tokens[ i ].type ].apply( null, tokens[ i ].matches ); + + // Return special upon seeing a positional matcher + if ( matcher[ expando ] ) { + + // Find the next relative operator (if any) for proper handling + j = ++i; + for ( ; j < len; j++ ) { + if ( Expr.relative[ tokens[ j ].type ] ) { + break; + } + } + return setMatcher( + i > 1 && elementMatcher( matchers ), + i > 1 && toSelector( + + // If the preceding token was a descendant combinator, insert an implicit any-element `*` + tokens + .slice( 0, i - 1 ) + .concat( { value: tokens[ i - 2 ].type === " " ? "*" : "" } ) + ).replace( rtrim, "$1" ), + matcher, + i < j && matcherFromTokens( tokens.slice( i, j ) ), + j < len && matcherFromTokens( ( tokens = tokens.slice( j ) ) ), + j < len && toSelector( tokens ) + ); + } + matchers.push( matcher ); + } + } + + return elementMatcher( matchers ); +} + +function matcherFromGroupMatchers( elementMatchers, setMatchers ) { + var bySet = setMatchers.length > 0, + byElement = elementMatchers.length > 0, + superMatcher = function( seed, context, xml, results, outermost ) { + var elem, j, matcher, + matchedCount = 0, + i = "0", + unmatched = seed && [], + setMatched = [], + contextBackup = outermostContext, + + // We must always have either seed elements or outermost context + elems = seed || byElement && Expr.find[ "TAG" ]( "*", outermost ), + + // Use integer dirruns iff this is the outermost matcher + dirrunsUnique = ( dirruns += contextBackup == null ? 1 : Math.random() || 0.1 ), + len = elems.length; + + if ( outermost ) { + + // Support: IE 11+, Edge 17 - 18+ + // IE/Edge sometimes throw a "Permission denied" error when strict-comparing + // two documents; shallow comparisons work. + // eslint-disable-next-line eqeqeq + outermostContext = context == document || context || outermost; + } + + // Add elements passing elementMatchers directly to results + // Support: IE<9, Safari + // Tolerate NodeList properties (IE: "length"; Safari: ) matching elements by id + for ( ; i !== len && ( elem = elems[ i ] ) != null; i++ ) { + if ( byElement && elem ) { + j = 0; + + // Support: IE 11+, Edge 17 - 18+ + // IE/Edge sometimes throw a "Permission denied" error when strict-comparing + // two documents; shallow comparisons work. + // eslint-disable-next-line eqeqeq + if ( !context && elem.ownerDocument != document ) { + setDocument( elem ); + xml = !documentIsHTML; + } + while ( ( matcher = elementMatchers[ j++ ] ) ) { + if ( matcher( elem, context || document, xml ) ) { + results.push( elem ); + break; + } + } + if ( outermost ) { + dirruns = dirrunsUnique; + } + } + + // Track unmatched elements for set filters + if ( bySet ) { + + // They will have gone through all possible matchers + if ( ( elem = !matcher && elem ) ) { + matchedCount--; + } + + // Lengthen the array for every element, matched or not + if ( seed ) { + unmatched.push( elem ); + } + } + } + + // `i` is now the count of elements visited above, and adding it to `matchedCount` + // makes the latter nonnegative. + matchedCount += i; + + // Apply set filters to unmatched elements + // NOTE: This can be skipped if there are no unmatched elements (i.e., `matchedCount` + // equals `i`), unless we didn't visit _any_ elements in the above loop because we have + // no element matchers and no seed. + // Incrementing an initially-string "0" `i` allows `i` to remain a string only in that + // case, which will result in a "00" `matchedCount` that differs from `i` but is also + // numerically zero. + if ( bySet && i !== matchedCount ) { + j = 0; + while ( ( matcher = setMatchers[ j++ ] ) ) { + matcher( unmatched, setMatched, context, xml ); + } + + if ( seed ) { + + // Reintegrate element matches to eliminate the need for sorting + if ( matchedCount > 0 ) { + while ( i-- ) { + if ( !( unmatched[ i ] || setMatched[ i ] ) ) { + setMatched[ i ] = pop.call( results ); + } + } + } + + // Discard index placeholder values to get only actual matches + setMatched = condense( setMatched ); + } + + // Add matches to results + push.apply( results, setMatched ); + + // Seedless set matches succeeding multiple successful matchers stipulate sorting + if ( outermost && !seed && setMatched.length > 0 && + ( matchedCount + setMatchers.length ) > 1 ) { + + Sizzle.uniqueSort( results ); + } + } + + // Override manipulation of globals by nested matchers + if ( outermost ) { + dirruns = dirrunsUnique; + outermostContext = contextBackup; + } + + return unmatched; + }; + + return bySet ? + markFunction( superMatcher ) : + superMatcher; +} + +compile = Sizzle.compile = function( selector, match /* Internal Use Only */ ) { + var i, + setMatchers = [], + elementMatchers = [], + cached = compilerCache[ selector + " " ]; + + if ( !cached ) { + + // Generate a function of recursive functions that can be used to check each element + if ( !match ) { + match = tokenize( selector ); + } + i = match.length; + while ( i-- ) { + cached = matcherFromTokens( match[ i ] ); + if ( cached[ expando ] ) { + setMatchers.push( cached ); + } else { + elementMatchers.push( cached ); + } + } + + // Cache the compiled function + cached = compilerCache( + selector, + matcherFromGroupMatchers( elementMatchers, setMatchers ) + ); + + // Save selector and tokenization + cached.selector = selector; + } + return cached; +}; + +/** + * A low-level selection function that works with Sizzle's compiled + * selector functions + * @param {String|Function} selector A selector or a pre-compiled + * selector function built with Sizzle.compile + * @param {Element} context + * @param {Array} [results] + * @param {Array} [seed] A set of elements to match against + */ +select = Sizzle.select = function( selector, context, results, seed ) { + var i, tokens, token, type, find, + compiled = typeof selector === "function" && selector, + match = !seed && tokenize( ( selector = compiled.selector || selector ) ); + + results = results || []; + + // Try to minimize operations if there is only one selector in the list and no seed + // (the latter of which guarantees us context) + if ( match.length === 1 ) { + + // Reduce context if the leading compound selector is an ID + tokens = match[ 0 ] = match[ 0 ].slice( 0 ); + if ( tokens.length > 2 && ( token = tokens[ 0 ] ).type === "ID" && + context.nodeType === 9 && documentIsHTML && Expr.relative[ tokens[ 1 ].type ] ) { + + context = ( Expr.find[ "ID" ]( token.matches[ 0 ] + .replace( runescape, funescape ), context ) || [] )[ 0 ]; + if ( !context ) { + return results; + + // Precompiled matchers will still verify ancestry, so step up a level + } else if ( compiled ) { + context = context.parentNode; + } + + selector = selector.slice( tokens.shift().value.length ); + } + + // Fetch a seed set for right-to-left matching + i = matchExpr[ "needsContext" ].test( selector ) ? 0 : tokens.length; + while ( i-- ) { + token = tokens[ i ]; + + // Abort if we hit a combinator + if ( Expr.relative[ ( type = token.type ) ] ) { + break; + } + if ( ( find = Expr.find[ type ] ) ) { + + // Search, expanding context for leading sibling combinators + if ( ( seed = find( + token.matches[ 0 ].replace( runescape, funescape ), + rsibling.test( tokens[ 0 ].type ) && testContext( context.parentNode ) || + context + ) ) ) { + + // If seed is empty or no tokens remain, we can return early + tokens.splice( i, 1 ); + selector = seed.length && toSelector( tokens ); + if ( !selector ) { + push.apply( results, seed ); + return results; + } + + break; + } + } + } + } + + // Compile and execute a filtering function if one is not provided + // Provide `match` to avoid retokenization if we modified the selector above + ( compiled || compile( selector, match ) )( + seed, + context, + !documentIsHTML, + results, + !context || rsibling.test( selector ) && testContext( context.parentNode ) || context + ); + return results; +}; + +// One-time assignments + +// Sort stability +support.sortStable = expando.split( "" ).sort( sortOrder ).join( "" ) === expando; + +// Support: Chrome 14-35+ +// Always assume duplicates if they aren't passed to the comparison function +support.detectDuplicates = !!hasDuplicate; + +// Initialize against the default document +setDocument(); + +// Support: Webkit<537.32 - Safari 6.0.3/Chrome 25 (fixed in Chrome 27) +// Detached nodes confoundingly follow *each other* +support.sortDetached = assert( function( el ) { + + // Should return 1, but returns 4 (following) + return el.compareDocumentPosition( document.createElement( "fieldset" ) ) & 1; +} ); + +// Support: IE<8 +// Prevent attribute/property "interpolation" +// https://msdn.microsoft.com/en-us/library/ms536429%28VS.85%29.aspx +if ( !assert( function( el ) { + el.innerHTML = ""; + return el.firstChild.getAttribute( "href" ) === "#"; +} ) ) { + addHandle( "type|href|height|width", function( elem, name, isXML ) { + if ( !isXML ) { + return elem.getAttribute( name, name.toLowerCase() === "type" ? 1 : 2 ); + } + } ); +} + +// Support: IE<9 +// Use defaultValue in place of getAttribute("value") +if ( !support.attributes || !assert( function( el ) { + el.innerHTML = ""; + el.firstChild.setAttribute( "value", "" ); + return el.firstChild.getAttribute( "value" ) === ""; +} ) ) { + addHandle( "value", function( elem, _name, isXML ) { + if ( !isXML && elem.nodeName.toLowerCase() === "input" ) { + return elem.defaultValue; + } + } ); +} + +// Support: IE<9 +// Use getAttributeNode to fetch booleans when getAttribute lies +if ( !assert( function( el ) { + return el.getAttribute( "disabled" ) == null; +} ) ) { + addHandle( booleans, function( elem, name, isXML ) { + var val; + if ( !isXML ) { + return elem[ name ] === true ? name.toLowerCase() : + ( val = elem.getAttributeNode( name ) ) && val.specified ? + val.value : + null; + } + } ); +} + +return Sizzle; + +} )( window ); + + + +jQuery.find = Sizzle; +jQuery.expr = Sizzle.selectors; + +// Deprecated +jQuery.expr[ ":" ] = jQuery.expr.pseudos; +jQuery.uniqueSort = jQuery.unique = Sizzle.uniqueSort; +jQuery.text = Sizzle.getText; +jQuery.isXMLDoc = Sizzle.isXML; +jQuery.contains = Sizzle.contains; +jQuery.escapeSelector = Sizzle.escape; + + + + +var dir = function( elem, dir, until ) { + var matched = [], + truncate = until !== undefined; + + while ( ( elem = elem[ dir ] ) && elem.nodeType !== 9 ) { + if ( elem.nodeType === 1 ) { + if ( truncate && jQuery( elem ).is( until ) ) { + break; + } + matched.push( elem ); + } + } + return matched; +}; + + +var siblings = function( n, elem ) { + var matched = []; + + for ( ; n; n = n.nextSibling ) { + if ( n.nodeType === 1 && n !== elem ) { + matched.push( n ); + } + } + + return matched; +}; + + +var rneedsContext = jQuery.expr.match.needsContext; + + + +function nodeName( elem, name ) { + + return elem.nodeName && elem.nodeName.toLowerCase() === name.toLowerCase(); + +} +var rsingleTag = ( /^<([a-z][^\/\0>:\x20\t\r\n\f]*)[\x20\t\r\n\f]*\/?>(?:<\/\1>|)$/i ); + + + +// Implement the identical functionality for filter and not +function winnow( elements, qualifier, not ) { + if ( isFunction( qualifier ) ) { + return jQuery.grep( elements, function( elem, i ) { + return !!qualifier.call( elem, i, elem ) !== not; + } ); + } + + // Single element + if ( qualifier.nodeType ) { + return jQuery.grep( elements, function( elem ) { + return ( elem === qualifier ) !== not; + } ); + } + + // Arraylike of elements (jQuery, arguments, Array) + if ( typeof qualifier !== "string" ) { + return jQuery.grep( elements, function( elem ) { + return ( indexOf.call( qualifier, elem ) > -1 ) !== not; + } ); + } + + // Filtered directly for both simple and complex selectors + return jQuery.filter( qualifier, elements, not ); +} + +jQuery.filter = function( expr, elems, not ) { + var elem = elems[ 0 ]; + + if ( not ) { + expr = ":not(" + expr + ")"; + } + + if ( elems.length === 1 && elem.nodeType === 1 ) { + return jQuery.find.matchesSelector( elem, expr ) ? [ elem ] : []; + } + + return jQuery.find.matches( expr, jQuery.grep( elems, function( elem ) { + return elem.nodeType === 1; + } ) ); +}; + +jQuery.fn.extend( { + find: function( selector ) { + var i, ret, + len = this.length, + self = this; + + if ( typeof selector !== "string" ) { + return this.pushStack( jQuery( selector ).filter( function() { + for ( i = 0; i < len; i++ ) { + if ( jQuery.contains( self[ i ], this ) ) { + return true; + } + } + } ) ); + } + + ret = this.pushStack( [] ); + + for ( i = 0; i < len; i++ ) { + jQuery.find( selector, self[ i ], ret ); + } + + return len > 1 ? jQuery.uniqueSort( ret ) : ret; + }, + filter: function( selector ) { + return this.pushStack( winnow( this, selector || [], false ) ); + }, + not: function( selector ) { + return this.pushStack( winnow( this, selector || [], true ) ); + }, + is: function( selector ) { + return !!winnow( + this, + + // If this is a positional/relative selector, check membership in the returned set + // so $("p:first").is("p:last") won't return true for a doc with two "p". + typeof selector === "string" && rneedsContext.test( selector ) ? + jQuery( selector ) : + selector || [], + false + ).length; + } +} ); + + +// Initialize a jQuery object + + +// A central reference to the root jQuery(document) +var rootjQuery, + + // A simple way to check for HTML strings + // Prioritize #id over to avoid XSS via location.hash (#9521) + // Strict HTML recognition (#11290: must start with <) + // Shortcut simple #id case for speed + rquickExpr = /^(?:\s*(<[\w\W]+>)[^>]*|#([\w-]+))$/, + + init = jQuery.fn.init = function( selector, context, root ) { + var match, elem; + + // HANDLE: $(""), $(null), $(undefined), $(false) + if ( !selector ) { + return this; + } + + // Method init() accepts an alternate rootjQuery + // so migrate can support jQuery.sub (gh-2101) + root = root || rootjQuery; + + // Handle HTML strings + if ( typeof selector === "string" ) { + if ( selector[ 0 ] === "<" && + selector[ selector.length - 1 ] === ">" && + selector.length >= 3 ) { + + // Assume that strings that start and end with <> are HTML and skip the regex check + match = [ null, selector, null ]; + + } else { + match = rquickExpr.exec( selector ); + } + + // Match html or make sure no context is specified for #id + if ( match && ( match[ 1 ] || !context ) ) { + + // HANDLE: $(html) -> $(array) + if ( match[ 1 ] ) { + context = context instanceof jQuery ? context[ 0 ] : context; + + // Option to run scripts is true for back-compat + // Intentionally let the error be thrown if parseHTML is not present + jQuery.merge( this, jQuery.parseHTML( + match[ 1 ], + context && context.nodeType ? context.ownerDocument || context : document, + true + ) ); + + // HANDLE: $(html, props) + if ( rsingleTag.test( match[ 1 ] ) && jQuery.isPlainObject( context ) ) { + for ( match in context ) { + + // Properties of context are called as methods if possible + if ( isFunction( this[ match ] ) ) { + this[ match ]( context[ match ] ); + + // ...and otherwise set as attributes + } else { + this.attr( match, context[ match ] ); + } + } + } + + return this; + + // HANDLE: $(#id) + } else { + elem = document.getElementById( match[ 2 ] ); + + if ( elem ) { + + // Inject the element directly into the jQuery object + this[ 0 ] = elem; + this.length = 1; + } + return this; + } + + // HANDLE: $(expr, $(...)) + } else if ( !context || context.jquery ) { + return ( context || root ).find( selector ); + + // HANDLE: $(expr, context) + // (which is just equivalent to: $(context).find(expr) + } else { + return this.constructor( context ).find( selector ); + } + + // HANDLE: $(DOMElement) + } else if ( selector.nodeType ) { + this[ 0 ] = selector; + this.length = 1; + return this; + + // HANDLE: $(function) + // Shortcut for document ready + } else if ( isFunction( selector ) ) { + return root.ready !== undefined ? + root.ready( selector ) : + + // Execute immediately if ready is not present + selector( jQuery ); + } + + return jQuery.makeArray( selector, this ); + }; + +// Give the init function the jQuery prototype for later instantiation +init.prototype = jQuery.fn; + +// Initialize central reference +rootjQuery = jQuery( document ); + + +var rparentsprev = /^(?:parents|prev(?:Until|All))/, + + // Methods guaranteed to produce a unique set when starting from a unique set + guaranteedUnique = { + children: true, + contents: true, + next: true, + prev: true + }; + +jQuery.fn.extend( { + has: function( target ) { + var targets = jQuery( target, this ), + l = targets.length; + + return this.filter( function() { + var i = 0; + for ( ; i < l; i++ ) { + if ( jQuery.contains( this, targets[ i ] ) ) { + return true; + } + } + } ); + }, + + closest: function( selectors, context ) { + var cur, + i = 0, + l = this.length, + matched = [], + targets = typeof selectors !== "string" && jQuery( selectors ); + + // Positional selectors never match, since there's no _selection_ context + if ( !rneedsContext.test( selectors ) ) { + for ( ; i < l; i++ ) { + for ( cur = this[ i ]; cur && cur !== context; cur = cur.parentNode ) { + + // Always skip document fragments + if ( cur.nodeType < 11 && ( targets ? + targets.index( cur ) > -1 : + + // Don't pass non-elements to Sizzle + cur.nodeType === 1 && + jQuery.find.matchesSelector( cur, selectors ) ) ) { + + matched.push( cur ); + break; + } + } + } + } + + return this.pushStack( matched.length > 1 ? jQuery.uniqueSort( matched ) : matched ); + }, + + // Determine the position of an element within the set + index: function( elem ) { + + // No argument, return index in parent + if ( !elem ) { + return ( this[ 0 ] && this[ 0 ].parentNode ) ? this.first().prevAll().length : -1; + } + + // Index in selector + if ( typeof elem === "string" ) { + return indexOf.call( jQuery( elem ), this[ 0 ] ); + } + + // Locate the position of the desired element + return indexOf.call( this, + + // If it receives a jQuery object, the first element is used + elem.jquery ? elem[ 0 ] : elem + ); + }, + + add: function( selector, context ) { + return this.pushStack( + jQuery.uniqueSort( + jQuery.merge( this.get(), jQuery( selector, context ) ) + ) + ); + }, + + addBack: function( selector ) { + return this.add( selector == null ? + this.prevObject : this.prevObject.filter( selector ) + ); + } +} ); + +function sibling( cur, dir ) { + while ( ( cur = cur[ dir ] ) && cur.nodeType !== 1 ) {} + return cur; +} + +jQuery.each( { + parent: function( elem ) { + var parent = elem.parentNode; + return parent && parent.nodeType !== 11 ? parent : null; + }, + parents: function( elem ) { + return dir( elem, "parentNode" ); + }, + parentsUntil: function( elem, _i, until ) { + return dir( elem, "parentNode", until ); + }, + next: function( elem ) { + return sibling( elem, "nextSibling" ); + }, + prev: function( elem ) { + return sibling( elem, "previousSibling" ); + }, + nextAll: function( elem ) { + return dir( elem, "nextSibling" ); + }, + prevAll: function( elem ) { + return dir( elem, "previousSibling" ); + }, + nextUntil: function( elem, _i, until ) { + return dir( elem, "nextSibling", until ); + }, + prevUntil: function( elem, _i, until ) { + return dir( elem, "previousSibling", until ); + }, + siblings: function( elem ) { + return siblings( ( elem.parentNode || {} ).firstChild, elem ); + }, + children: function( elem ) { + return siblings( elem.firstChild ); + }, + contents: function( elem ) { + if ( elem.contentDocument != null && + + // Support: IE 11+ + // elements with no `data` attribute has an object + // `contentDocument` with a `null` prototype. + getProto( elem.contentDocument ) ) { + + return elem.contentDocument; + } + + // Support: IE 9 - 11 only, iOS 7 only, Android Browser <=4.3 only + // Treat the template element as a regular one in browsers that + // don't support it. + if ( nodeName( elem, "template" ) ) { + elem = elem.content || elem; + } + + return jQuery.merge( [], elem.childNodes ); + } +}, function( name, fn ) { + jQuery.fn[ name ] = function( until, selector ) { + var matched = jQuery.map( this, fn, until ); + + if ( name.slice( -5 ) !== "Until" ) { + selector = until; + } + + if ( selector && typeof selector === "string" ) { + matched = jQuery.filter( selector, matched ); + } + + if ( this.length > 1 ) { + + // Remove duplicates + if ( !guaranteedUnique[ name ] ) { + jQuery.uniqueSort( matched ); + } + + // Reverse order for parents* and prev-derivatives + if ( rparentsprev.test( name ) ) { + matched.reverse(); + } + } + + return this.pushStack( matched ); + }; +} ); +var rnothtmlwhite = ( /[^\x20\t\r\n\f]+/g ); + + + +// Convert String-formatted options into Object-formatted ones +function createOptions( options ) { + var object = {}; + jQuery.each( options.match( rnothtmlwhite ) || [], function( _, flag ) { + object[ flag ] = true; + } ); + return object; +} + +/* + * Create a callback list using the following parameters: + * + * options: an optional list of space-separated options that will change how + * the callback list behaves or a more traditional option object + * + * By default a callback list will act like an event callback list and can be + * "fired" multiple times. + * + * Possible options: + * + * once: will ensure the callback list can only be fired once (like a Deferred) + * + * memory: will keep track of previous values and will call any callback added + * after the list has been fired right away with the latest "memorized" + * values (like a Deferred) + * + * unique: will ensure a callback can only be added once (no duplicate in the list) + * + * stopOnFalse: interrupt callings when a callback returns false + * + */ +jQuery.Callbacks = function( options ) { + + // Convert options from String-formatted to Object-formatted if needed + // (we check in cache first) + options = typeof options === "string" ? + createOptions( options ) : + jQuery.extend( {}, options ); + + var // Flag to know if list is currently firing + firing, + + // Last fire value for non-forgettable lists + memory, + + // Flag to know if list was already fired + fired, + + // Flag to prevent firing + locked, + + // Actual callback list + list = [], + + // Queue of execution data for repeatable lists + queue = [], + + // Index of currently firing callback (modified by add/remove as needed) + firingIndex = -1, + + // Fire callbacks + fire = function() { + + // Enforce single-firing + locked = locked || options.once; + + // Execute callbacks for all pending executions, + // respecting firingIndex overrides and runtime changes + fired = firing = true; + for ( ; queue.length; firingIndex = -1 ) { + memory = queue.shift(); + while ( ++firingIndex < list.length ) { + + // Run callback and check for early termination + if ( list[ firingIndex ].apply( memory[ 0 ], memory[ 1 ] ) === false && + options.stopOnFalse ) { + + // Jump to end and forget the data so .add doesn't re-fire + firingIndex = list.length; + memory = false; + } + } + } + + // Forget the data if we're done with it + if ( !options.memory ) { + memory = false; + } + + firing = false; + + // Clean up if we're done firing for good + if ( locked ) { + + // Keep an empty list if we have data for future add calls + if ( memory ) { + list = []; + + // Otherwise, this object is spent + } else { + list = ""; + } + } + }, + + // Actual Callbacks object + self = { + + // Add a callback or a collection of callbacks to the list + add: function() { + if ( list ) { + + // If we have memory from a past run, we should fire after adding + if ( memory && !firing ) { + firingIndex = list.length - 1; + queue.push( memory ); + } + + ( function add( args ) { + jQuery.each( args, function( _, arg ) { + if ( isFunction( arg ) ) { + if ( !options.unique || !self.has( arg ) ) { + list.push( arg ); + } + } else if ( arg && arg.length && toType( arg ) !== "string" ) { + + // Inspect recursively + add( arg ); + } + } ); + } )( arguments ); + + if ( memory && !firing ) { + fire(); + } + } + return this; + }, + + // Remove a callback from the list + remove: function() { + jQuery.each( arguments, function( _, arg ) { + var index; + while ( ( index = jQuery.inArray( arg, list, index ) ) > -1 ) { + list.splice( index, 1 ); + + // Handle firing indexes + if ( index <= firingIndex ) { + firingIndex--; + } + } + } ); + return this; + }, + + // Check if a given callback is in the list. + // If no argument is given, return whether or not list has callbacks attached. + has: function( fn ) { + return fn ? + jQuery.inArray( fn, list ) > -1 : + list.length > 0; + }, + + // Remove all callbacks from the list + empty: function() { + if ( list ) { + list = []; + } + return this; + }, + + // Disable .fire and .add + // Abort any current/pending executions + // Clear all callbacks and values + disable: function() { + locked = queue = []; + list = memory = ""; + return this; + }, + disabled: function() { + return !list; + }, + + // Disable .fire + // Also disable .add unless we have memory (since it would have no effect) + // Abort any pending executions + lock: function() { + locked = queue = []; + if ( !memory && !firing ) { + list = memory = ""; + } + return this; + }, + locked: function() { + return !!locked; + }, + + // Call all callbacks with the given context and arguments + fireWith: function( context, args ) { + if ( !locked ) { + args = args || []; + args = [ context, args.slice ? args.slice() : args ]; + queue.push( args ); + if ( !firing ) { + fire(); + } + } + return this; + }, + + // Call all the callbacks with the given arguments + fire: function() { + self.fireWith( this, arguments ); + return this; + }, + + // To know if the callbacks have already been called at least once + fired: function() { + return !!fired; + } + }; + + return self; +}; + + +function Identity( v ) { + return v; +} +function Thrower( ex ) { + throw ex; +} + +function adoptValue( value, resolve, reject, noValue ) { + var method; + + try { + + // Check for promise aspect first to privilege synchronous behavior + if ( value && isFunction( ( method = value.promise ) ) ) { + method.call( value ).done( resolve ).fail( reject ); + + // Other thenables + } else if ( value && isFunction( ( method = value.then ) ) ) { + method.call( value, resolve, reject ); + + // Other non-thenables + } else { + + // Control `resolve` arguments by letting Array#slice cast boolean `noValue` to integer: + // * false: [ value ].slice( 0 ) => resolve( value ) + // * true: [ value ].slice( 1 ) => resolve() + resolve.apply( undefined, [ value ].slice( noValue ) ); + } + + // For Promises/A+, convert exceptions into rejections + // Since jQuery.when doesn't unwrap thenables, we can skip the extra checks appearing in + // Deferred#then to conditionally suppress rejection. + } catch ( value ) { + + // Support: Android 4.0 only + // Strict mode functions invoked without .call/.apply get global-object context + reject.apply( undefined, [ value ] ); + } +} + +jQuery.extend( { + + Deferred: function( func ) { + var tuples = [ + + // action, add listener, callbacks, + // ... .then handlers, argument index, [final state] + [ "notify", "progress", jQuery.Callbacks( "memory" ), + jQuery.Callbacks( "memory" ), 2 ], + [ "resolve", "done", jQuery.Callbacks( "once memory" ), + jQuery.Callbacks( "once memory" ), 0, "resolved" ], + [ "reject", "fail", jQuery.Callbacks( "once memory" ), + jQuery.Callbacks( "once memory" ), 1, "rejected" ] + ], + state = "pending", + promise = { + state: function() { + return state; + }, + always: function() { + deferred.done( arguments ).fail( arguments ); + return this; + }, + "catch": function( fn ) { + return promise.then( null, fn ); + }, + + // Keep pipe for back-compat + pipe: function( /* fnDone, fnFail, fnProgress */ ) { + var fns = arguments; + + return jQuery.Deferred( function( newDefer ) { + jQuery.each( tuples, function( _i, tuple ) { + + // Map tuples (progress, done, fail) to arguments (done, fail, progress) + var fn = isFunction( fns[ tuple[ 4 ] ] ) && fns[ tuple[ 4 ] ]; + + // deferred.progress(function() { bind to newDefer or newDefer.notify }) + // deferred.done(function() { bind to newDefer or newDefer.resolve }) + // deferred.fail(function() { bind to newDefer or newDefer.reject }) + deferred[ tuple[ 1 ] ]( function() { + var returned = fn && fn.apply( this, arguments ); + if ( returned && isFunction( returned.promise ) ) { + returned.promise() + .progress( newDefer.notify ) + .done( newDefer.resolve ) + .fail( newDefer.reject ); + } else { + newDefer[ tuple[ 0 ] + "With" ]( + this, + fn ? [ returned ] : arguments + ); + } + } ); + } ); + fns = null; + } ).promise(); + }, + then: function( onFulfilled, onRejected, onProgress ) { + var maxDepth = 0; + function resolve( depth, deferred, handler, special ) { + return function() { + var that = this, + args = arguments, + mightThrow = function() { + var returned, then; + + // Support: Promises/A+ section 2.3.3.3.3 + // https://promisesaplus.com/#point-59 + // Ignore double-resolution attempts + if ( depth < maxDepth ) { + return; + } + + returned = handler.apply( that, args ); + + // Support: Promises/A+ section 2.3.1 + // https://promisesaplus.com/#point-48 + if ( returned === deferred.promise() ) { + throw new TypeError( "Thenable self-resolution" ); + } + + // Support: Promises/A+ sections 2.3.3.1, 3.5 + // https://promisesaplus.com/#point-54 + // https://promisesaplus.com/#point-75 + // Retrieve `then` only once + then = returned && + + // Support: Promises/A+ section 2.3.4 + // https://promisesaplus.com/#point-64 + // Only check objects and functions for thenability + ( typeof returned === "object" || + typeof returned === "function" ) && + returned.then; + + // Handle a returned thenable + if ( isFunction( then ) ) { + + // Special processors (notify) just wait for resolution + if ( special ) { + then.call( + returned, + resolve( maxDepth, deferred, Identity, special ), + resolve( maxDepth, deferred, Thrower, special ) + ); + + // Normal processors (resolve) also hook into progress + } else { + + // ...and disregard older resolution values + maxDepth++; + + then.call( + returned, + resolve( maxDepth, deferred, Identity, special ), + resolve( maxDepth, deferred, Thrower, special ), + resolve( maxDepth, deferred, Identity, + deferred.notifyWith ) + ); + } + + // Handle all other returned values + } else { + + // Only substitute handlers pass on context + // and multiple values (non-spec behavior) + if ( handler !== Identity ) { + that = undefined; + args = [ returned ]; + } + + // Process the value(s) + // Default process is resolve + ( special || deferred.resolveWith )( that, args ); + } + }, + + // Only normal processors (resolve) catch and reject exceptions + process = special ? + mightThrow : + function() { + try { + mightThrow(); + } catch ( e ) { + + if ( jQuery.Deferred.exceptionHook ) { + jQuery.Deferred.exceptionHook( e, + process.stackTrace ); + } + + // Support: Promises/A+ section 2.3.3.3.4.1 + // https://promisesaplus.com/#point-61 + // Ignore post-resolution exceptions + if ( depth + 1 >= maxDepth ) { + + // Only substitute handlers pass on context + // and multiple values (non-spec behavior) + if ( handler !== Thrower ) { + that = undefined; + args = [ e ]; + } + + deferred.rejectWith( that, args ); + } + } + }; + + // Support: Promises/A+ section 2.3.3.3.1 + // https://promisesaplus.com/#point-57 + // Re-resolve promises immediately to dodge false rejection from + // subsequent errors + if ( depth ) { + process(); + } else { + + // Call an optional hook to record the stack, in case of exception + // since it's otherwise lost when execution goes async + if ( jQuery.Deferred.getStackHook ) { + process.stackTrace = jQuery.Deferred.getStackHook(); + } + window.setTimeout( process ); + } + }; + } + + return jQuery.Deferred( function( newDefer ) { + + // progress_handlers.add( ... ) + tuples[ 0 ][ 3 ].add( + resolve( + 0, + newDefer, + isFunction( onProgress ) ? + onProgress : + Identity, + newDefer.notifyWith + ) + ); + + // fulfilled_handlers.add( ... ) + tuples[ 1 ][ 3 ].add( + resolve( + 0, + newDefer, + isFunction( onFulfilled ) ? + onFulfilled : + Identity + ) + ); + + // rejected_handlers.add( ... ) + tuples[ 2 ][ 3 ].add( + resolve( + 0, + newDefer, + isFunction( onRejected ) ? + onRejected : + Thrower + ) + ); + } ).promise(); + }, + + // Get a promise for this deferred + // If obj is provided, the promise aspect is added to the object + promise: function( obj ) { + return obj != null ? jQuery.extend( obj, promise ) : promise; + } + }, + deferred = {}; + + // Add list-specific methods + jQuery.each( tuples, function( i, tuple ) { + var list = tuple[ 2 ], + stateString = tuple[ 5 ]; + + // promise.progress = list.add + // promise.done = list.add + // promise.fail = list.add + promise[ tuple[ 1 ] ] = list.add; + + // Handle state + if ( stateString ) { + list.add( + function() { + + // state = "resolved" (i.e., fulfilled) + // state = "rejected" + state = stateString; + }, + + // rejected_callbacks.disable + // fulfilled_callbacks.disable + tuples[ 3 - i ][ 2 ].disable, + + // rejected_handlers.disable + // fulfilled_handlers.disable + tuples[ 3 - i ][ 3 ].disable, + + // progress_callbacks.lock + tuples[ 0 ][ 2 ].lock, + + // progress_handlers.lock + tuples[ 0 ][ 3 ].lock + ); + } + + // progress_handlers.fire + // fulfilled_handlers.fire + // rejected_handlers.fire + list.add( tuple[ 3 ].fire ); + + // deferred.notify = function() { deferred.notifyWith(...) } + // deferred.resolve = function() { deferred.resolveWith(...) } + // deferred.reject = function() { deferred.rejectWith(...) } + deferred[ tuple[ 0 ] ] = function() { + deferred[ tuple[ 0 ] + "With" ]( this === deferred ? undefined : this, arguments ); + return this; + }; + + // deferred.notifyWith = list.fireWith + // deferred.resolveWith = list.fireWith + // deferred.rejectWith = list.fireWith + deferred[ tuple[ 0 ] + "With" ] = list.fireWith; + } ); + + // Make the deferred a promise + promise.promise( deferred ); + + // Call given func if any + if ( func ) { + func.call( deferred, deferred ); + } + + // All done! + return deferred; + }, + + // Deferred helper + when: function( singleValue ) { + var + + // count of uncompleted subordinates + remaining = arguments.length, + + // count of unprocessed arguments + i = remaining, + + // subordinate fulfillment data + resolveContexts = Array( i ), + resolveValues = slice.call( arguments ), + + // the primary Deferred + primary = jQuery.Deferred(), + + // subordinate callback factory + updateFunc = function( i ) { + return function( value ) { + resolveContexts[ i ] = this; + resolveValues[ i ] = arguments.length > 1 ? slice.call( arguments ) : value; + if ( !( --remaining ) ) { + primary.resolveWith( resolveContexts, resolveValues ); + } + }; + }; + + // Single- and empty arguments are adopted like Promise.resolve + if ( remaining <= 1 ) { + adoptValue( singleValue, primary.done( updateFunc( i ) ).resolve, primary.reject, + !remaining ); + + // Use .then() to unwrap secondary thenables (cf. gh-3000) + if ( primary.state() === "pending" || + isFunction( resolveValues[ i ] && resolveValues[ i ].then ) ) { + + return primary.then(); + } + } + + // Multiple arguments are aggregated like Promise.all array elements + while ( i-- ) { + adoptValue( resolveValues[ i ], updateFunc( i ), primary.reject ); + } + + return primary.promise(); + } +} ); + + +// These usually indicate a programmer mistake during development, +// warn about them ASAP rather than swallowing them by default. +var rerrorNames = /^(Eval|Internal|Range|Reference|Syntax|Type|URI)Error$/; + +jQuery.Deferred.exceptionHook = function( error, stack ) { + + // Support: IE 8 - 9 only + // Console exists when dev tools are open, which can happen at any time + if ( window.console && window.console.warn && error && rerrorNames.test( error.name ) ) { + window.console.warn( "jQuery.Deferred exception: " + error.message, error.stack, stack ); + } +}; + + + + +jQuery.readyException = function( error ) { + window.setTimeout( function() { + throw error; + } ); +}; + + + + +// The deferred used on DOM ready +var readyList = jQuery.Deferred(); + +jQuery.fn.ready = function( fn ) { + + readyList + .then( fn ) + + // Wrap jQuery.readyException in a function so that the lookup + // happens at the time of error handling instead of callback + // registration. + .catch( function( error ) { + jQuery.readyException( error ); + } ); + + return this; +}; + +jQuery.extend( { + + // Is the DOM ready to be used? Set to true once it occurs. + isReady: false, + + // A counter to track how many items to wait for before + // the ready event fires. See #6781 + readyWait: 1, + + // Handle when the DOM is ready + ready: function( wait ) { + + // Abort if there are pending holds or we're already ready + if ( wait === true ? --jQuery.readyWait : jQuery.isReady ) { + return; + } + + // Remember that the DOM is ready + jQuery.isReady = true; + + // If a normal DOM Ready event fired, decrement, and wait if need be + if ( wait !== true && --jQuery.readyWait > 0 ) { + return; + } + + // If there are functions bound, to execute + readyList.resolveWith( document, [ jQuery ] ); + } +} ); + +jQuery.ready.then = readyList.then; + +// The ready event handler and self cleanup method +function completed() { + document.removeEventListener( "DOMContentLoaded", completed ); + window.removeEventListener( "load", completed ); + jQuery.ready(); +} + +// Catch cases where $(document).ready() is called +// after the browser event has already occurred. +// Support: IE <=9 - 10 only +// Older IE sometimes signals "interactive" too soon +if ( document.readyState === "complete" || + ( document.readyState !== "loading" && !document.documentElement.doScroll ) ) { + + // Handle it asynchronously to allow scripts the opportunity to delay ready + window.setTimeout( jQuery.ready ); + +} else { + + // Use the handy event callback + document.addEventListener( "DOMContentLoaded", completed ); + + // A fallback to window.onload, that will always work + window.addEventListener( "load", completed ); +} + + + + +// Multifunctional method to get and set values of a collection +// The value/s can optionally be executed if it's a function +var access = function( elems, fn, key, value, chainable, emptyGet, raw ) { + var i = 0, + len = elems.length, + bulk = key == null; + + // Sets many values + if ( toType( key ) === "object" ) { + chainable = true; + for ( i in key ) { + access( elems, fn, i, key[ i ], true, emptyGet, raw ); + } + + // Sets one value + } else if ( value !== undefined ) { + chainable = true; + + if ( !isFunction( value ) ) { + raw = true; + } + + if ( bulk ) { + + // Bulk operations run against the entire set + if ( raw ) { + fn.call( elems, value ); + fn = null; + + // ...except when executing function values + } else { + bulk = fn; + fn = function( elem, _key, value ) { + return bulk.call( jQuery( elem ), value ); + }; + } + } + + if ( fn ) { + for ( ; i < len; i++ ) { + fn( + elems[ i ], key, raw ? + value : + value.call( elems[ i ], i, fn( elems[ i ], key ) ) + ); + } + } + } + + if ( chainable ) { + return elems; + } + + // Gets + if ( bulk ) { + return fn.call( elems ); + } + + return len ? fn( elems[ 0 ], key ) : emptyGet; +}; + + +// Matches dashed string for camelizing +var rmsPrefix = /^-ms-/, + rdashAlpha = /-([a-z])/g; + +// Used by camelCase as callback to replace() +function fcamelCase( _all, letter ) { + return letter.toUpperCase(); +} + +// Convert dashed to camelCase; used by the css and data modules +// Support: IE <=9 - 11, Edge 12 - 15 +// Microsoft forgot to hump their vendor prefix (#9572) +function camelCase( string ) { + return string.replace( rmsPrefix, "ms-" ).replace( rdashAlpha, fcamelCase ); +} +var acceptData = function( owner ) { + + // Accepts only: + // - Node + // - Node.ELEMENT_NODE + // - Node.DOCUMENT_NODE + // - Object + // - Any + return owner.nodeType === 1 || owner.nodeType === 9 || !( +owner.nodeType ); +}; + + + + +function Data() { + this.expando = jQuery.expando + Data.uid++; +} + +Data.uid = 1; + +Data.prototype = { + + cache: function( owner ) { + + // Check if the owner object already has a cache + var value = owner[ this.expando ]; + + // If not, create one + if ( !value ) { + value = {}; + + // We can accept data for non-element nodes in modern browsers, + // but we should not, see #8335. + // Always return an empty object. + if ( acceptData( owner ) ) { + + // If it is a node unlikely to be stringify-ed or looped over + // use plain assignment + if ( owner.nodeType ) { + owner[ this.expando ] = value; + + // Otherwise secure it in a non-enumerable property + // configurable must be true to allow the property to be + // deleted when data is removed + } else { + Object.defineProperty( owner, this.expando, { + value: value, + configurable: true + } ); + } + } + } + + return value; + }, + set: function( owner, data, value ) { + var prop, + cache = this.cache( owner ); + + // Handle: [ owner, key, value ] args + // Always use camelCase key (gh-2257) + if ( typeof data === "string" ) { + cache[ camelCase( data ) ] = value; + + // Handle: [ owner, { properties } ] args + } else { + + // Copy the properties one-by-one to the cache object + for ( prop in data ) { + cache[ camelCase( prop ) ] = data[ prop ]; + } + } + return cache; + }, + get: function( owner, key ) { + return key === undefined ? + this.cache( owner ) : + + // Always use camelCase key (gh-2257) + owner[ this.expando ] && owner[ this.expando ][ camelCase( key ) ]; + }, + access: function( owner, key, value ) { + + // In cases where either: + // + // 1. No key was specified + // 2. A string key was specified, but no value provided + // + // Take the "read" path and allow the get method to determine + // which value to return, respectively either: + // + // 1. The entire cache object + // 2. The data stored at the key + // + if ( key === undefined || + ( ( key && typeof key === "string" ) && value === undefined ) ) { + + return this.get( owner, key ); + } + + // When the key is not a string, or both a key and value + // are specified, set or extend (existing objects) with either: + // + // 1. An object of properties + // 2. A key and value + // + this.set( owner, key, value ); + + // Since the "set" path can have two possible entry points + // return the expected data based on which path was taken[*] + return value !== undefined ? value : key; + }, + remove: function( owner, key ) { + var i, + cache = owner[ this.expando ]; + + if ( cache === undefined ) { + return; + } + + if ( key !== undefined ) { + + // Support array or space separated string of keys + if ( Array.isArray( key ) ) { + + // If key is an array of keys... + // We always set camelCase keys, so remove that. + key = key.map( camelCase ); + } else { + key = camelCase( key ); + + // If a key with the spaces exists, use it. + // Otherwise, create an array by matching non-whitespace + key = key in cache ? + [ key ] : + ( key.match( rnothtmlwhite ) || [] ); + } + + i = key.length; + + while ( i-- ) { + delete cache[ key[ i ] ]; + } + } + + // Remove the expando if there's no more data + if ( key === undefined || jQuery.isEmptyObject( cache ) ) { + + // Support: Chrome <=35 - 45 + // Webkit & Blink performance suffers when deleting properties + // from DOM nodes, so set to undefined instead + // https://bugs.chromium.org/p/chromium/issues/detail?id=378607 (bug restricted) + if ( owner.nodeType ) { + owner[ this.expando ] = undefined; + } else { + delete owner[ this.expando ]; + } + } + }, + hasData: function( owner ) { + var cache = owner[ this.expando ]; + return cache !== undefined && !jQuery.isEmptyObject( cache ); + } +}; +var dataPriv = new Data(); + +var dataUser = new Data(); + + + +// Implementation Summary +// +// 1. Enforce API surface and semantic compatibility with 1.9.x branch +// 2. Improve the module's maintainability by reducing the storage +// paths to a single mechanism. +// 3. Use the same single mechanism to support "private" and "user" data. +// 4. _Never_ expose "private" data to user code (TODO: Drop _data, _removeData) +// 5. Avoid exposing implementation details on user objects (eg. expando properties) +// 6. Provide a clear path for implementation upgrade to WeakMap in 2014 + +var rbrace = /^(?:\{[\w\W]*\}|\[[\w\W]*\])$/, + rmultiDash = /[A-Z]/g; + +function getData( data ) { + if ( data === "true" ) { + return true; + } + + if ( data === "false" ) { + return false; + } + + if ( data === "null" ) { + return null; + } + + // Only convert to a number if it doesn't change the string + if ( data === +data + "" ) { + return +data; + } + + if ( rbrace.test( data ) ) { + return JSON.parse( data ); + } + + return data; +} + +function dataAttr( elem, key, data ) { + var name; + + // If nothing was found internally, try to fetch any + // data from the HTML5 data-* attribute + if ( data === undefined && elem.nodeType === 1 ) { + name = "data-" + key.replace( rmultiDash, "-$&" ).toLowerCase(); + data = elem.getAttribute( name ); + + if ( typeof data === "string" ) { + try { + data = getData( data ); + } catch ( e ) {} + + // Make sure we set the data so it isn't changed later + dataUser.set( elem, key, data ); + } else { + data = undefined; + } + } + return data; +} + +jQuery.extend( { + hasData: function( elem ) { + return dataUser.hasData( elem ) || dataPriv.hasData( elem ); + }, + + data: function( elem, name, data ) { + return dataUser.access( elem, name, data ); + }, + + removeData: function( elem, name ) { + dataUser.remove( elem, name ); + }, + + // TODO: Now that all calls to _data and _removeData have been replaced + // with direct calls to dataPriv methods, these can be deprecated. + _data: function( elem, name, data ) { + return dataPriv.access( elem, name, data ); + }, + + _removeData: function( elem, name ) { + dataPriv.remove( elem, name ); + } +} ); + +jQuery.fn.extend( { + data: function( key, value ) { + var i, name, data, + elem = this[ 0 ], + attrs = elem && elem.attributes; + + // Gets all values + if ( key === undefined ) { + if ( this.length ) { + data = dataUser.get( elem ); + + if ( elem.nodeType === 1 && !dataPriv.get( elem, "hasDataAttrs" ) ) { + i = attrs.length; + while ( i-- ) { + + // Support: IE 11 only + // The attrs elements can be null (#14894) + if ( attrs[ i ] ) { + name = attrs[ i ].name; + if ( name.indexOf( "data-" ) === 0 ) { + name = camelCase( name.slice( 5 ) ); + dataAttr( elem, name, data[ name ] ); + } + } + } + dataPriv.set( elem, "hasDataAttrs", true ); + } + } + + return data; + } + + // Sets multiple values + if ( typeof key === "object" ) { + return this.each( function() { + dataUser.set( this, key ); + } ); + } + + return access( this, function( value ) { + var data; + + // The calling jQuery object (element matches) is not empty + // (and therefore has an element appears at this[ 0 ]) and the + // `value` parameter was not undefined. An empty jQuery object + // will result in `undefined` for elem = this[ 0 ] which will + // throw an exception if an attempt to read a data cache is made. + if ( elem && value === undefined ) { + + // Attempt to get data from the cache + // The key will always be camelCased in Data + data = dataUser.get( elem, key ); + if ( data !== undefined ) { + return data; + } + + // Attempt to "discover" the data in + // HTML5 custom data-* attrs + data = dataAttr( elem, key ); + if ( data !== undefined ) { + return data; + } + + // We tried really hard, but the data doesn't exist. + return; + } + + // Set the data... + this.each( function() { + + // We always store the camelCased key + dataUser.set( this, key, value ); + } ); + }, null, value, arguments.length > 1, null, true ); + }, + + removeData: function( key ) { + return this.each( function() { + dataUser.remove( this, key ); + } ); + } +} ); + + +jQuery.extend( { + queue: function( elem, type, data ) { + var queue; + + if ( elem ) { + type = ( type || "fx" ) + "queue"; + queue = dataPriv.get( elem, type ); + + // Speed up dequeue by getting out quickly if this is just a lookup + if ( data ) { + if ( !queue || Array.isArray( data ) ) { + queue = dataPriv.access( elem, type, jQuery.makeArray( data ) ); + } else { + queue.push( data ); + } + } + return queue || []; + } + }, + + dequeue: function( elem, type ) { + type = type || "fx"; + + var queue = jQuery.queue( elem, type ), + startLength = queue.length, + fn = queue.shift(), + hooks = jQuery._queueHooks( elem, type ), + next = function() { + jQuery.dequeue( elem, type ); + }; + + // If the fx queue is dequeued, always remove the progress sentinel + if ( fn === "inprogress" ) { + fn = queue.shift(); + startLength--; + } + + if ( fn ) { + + // Add a progress sentinel to prevent the fx queue from being + // automatically dequeued + if ( type === "fx" ) { + queue.unshift( "inprogress" ); + } + + // Clear up the last queue stop function + delete hooks.stop; + fn.call( elem, next, hooks ); + } + + if ( !startLength && hooks ) { + hooks.empty.fire(); + } + }, + + // Not public - generate a queueHooks object, or return the current one + _queueHooks: function( elem, type ) { + var key = type + "queueHooks"; + return dataPriv.get( elem, key ) || dataPriv.access( elem, key, { + empty: jQuery.Callbacks( "once memory" ).add( function() { + dataPriv.remove( elem, [ type + "queue", key ] ); + } ) + } ); + } +} ); + +jQuery.fn.extend( { + queue: function( type, data ) { + var setter = 2; + + if ( typeof type !== "string" ) { + data = type; + type = "fx"; + setter--; + } + + if ( arguments.length < setter ) { + return jQuery.queue( this[ 0 ], type ); + } + + return data === undefined ? + this : + this.each( function() { + var queue = jQuery.queue( this, type, data ); + + // Ensure a hooks for this queue + jQuery._queueHooks( this, type ); + + if ( type === "fx" && queue[ 0 ] !== "inprogress" ) { + jQuery.dequeue( this, type ); + } + } ); + }, + dequeue: function( type ) { + return this.each( function() { + jQuery.dequeue( this, type ); + } ); + }, + clearQueue: function( type ) { + return this.queue( type || "fx", [] ); + }, + + // Get a promise resolved when queues of a certain type + // are emptied (fx is the type by default) + promise: function( type, obj ) { + var tmp, + count = 1, + defer = jQuery.Deferred(), + elements = this, + i = this.length, + resolve = function() { + if ( !( --count ) ) { + defer.resolveWith( elements, [ elements ] ); + } + }; + + if ( typeof type !== "string" ) { + obj = type; + type = undefined; + } + type = type || "fx"; + + while ( i-- ) { + tmp = dataPriv.get( elements[ i ], type + "queueHooks" ); + if ( tmp && tmp.empty ) { + count++; + tmp.empty.add( resolve ); + } + } + resolve(); + return defer.promise( obj ); + } +} ); +var pnum = ( /[+-]?(?:\d*\.|)\d+(?:[eE][+-]?\d+|)/ ).source; + +var rcssNum = new RegExp( "^(?:([+-])=|)(" + pnum + ")([a-z%]*)$", "i" ); + + +var cssExpand = [ "Top", "Right", "Bottom", "Left" ]; + +var documentElement = document.documentElement; + + + + var isAttached = function( elem ) { + return jQuery.contains( elem.ownerDocument, elem ); + }, + composed = { composed: true }; + + // Support: IE 9 - 11+, Edge 12 - 18+, iOS 10.0 - 10.2 only + // Check attachment across shadow DOM boundaries when possible (gh-3504) + // Support: iOS 10.0-10.2 only + // Early iOS 10 versions support `attachShadow` but not `getRootNode`, + // leading to errors. We need to check for `getRootNode`. + if ( documentElement.getRootNode ) { + isAttached = function( elem ) { + return jQuery.contains( elem.ownerDocument, elem ) || + elem.getRootNode( composed ) === elem.ownerDocument; + }; + } +var isHiddenWithinTree = function( elem, el ) { + + // isHiddenWithinTree might be called from jQuery#filter function; + // in that case, element will be second argument + elem = el || elem; + + // Inline style trumps all + return elem.style.display === "none" || + elem.style.display === "" && + + // Otherwise, check computed style + // Support: Firefox <=43 - 45 + // Disconnected elements can have computed display: none, so first confirm that elem is + // in the document. + isAttached( elem ) && + + jQuery.css( elem, "display" ) === "none"; + }; + + + +function adjustCSS( elem, prop, valueParts, tween ) { + var adjusted, scale, + maxIterations = 20, + currentValue = tween ? + function() { + return tween.cur(); + } : + function() { + return jQuery.css( elem, prop, "" ); + }, + initial = currentValue(), + unit = valueParts && valueParts[ 3 ] || ( jQuery.cssNumber[ prop ] ? "" : "px" ), + + // Starting value computation is required for potential unit mismatches + initialInUnit = elem.nodeType && + ( jQuery.cssNumber[ prop ] || unit !== "px" && +initial ) && + rcssNum.exec( jQuery.css( elem, prop ) ); + + if ( initialInUnit && initialInUnit[ 3 ] !== unit ) { + + // Support: Firefox <=54 + // Halve the iteration target value to prevent interference from CSS upper bounds (gh-2144) + initial = initial / 2; + + // Trust units reported by jQuery.css + unit = unit || initialInUnit[ 3 ]; + + // Iteratively approximate from a nonzero starting point + initialInUnit = +initial || 1; + + while ( maxIterations-- ) { + + // Evaluate and update our best guess (doubling guesses that zero out). + // Finish if the scale equals or crosses 1 (making the old*new product non-positive). + jQuery.style( elem, prop, initialInUnit + unit ); + if ( ( 1 - scale ) * ( 1 - ( scale = currentValue() / initial || 0.5 ) ) <= 0 ) { + maxIterations = 0; + } + initialInUnit = initialInUnit / scale; + + } + + initialInUnit = initialInUnit * 2; + jQuery.style( elem, prop, initialInUnit + unit ); + + // Make sure we update the tween properties later on + valueParts = valueParts || []; + } + + if ( valueParts ) { + initialInUnit = +initialInUnit || +initial || 0; + + // Apply relative offset (+=/-=) if specified + adjusted = valueParts[ 1 ] ? + initialInUnit + ( valueParts[ 1 ] + 1 ) * valueParts[ 2 ] : + +valueParts[ 2 ]; + if ( tween ) { + tween.unit = unit; + tween.start = initialInUnit; + tween.end = adjusted; + } + } + return adjusted; +} + + +var defaultDisplayMap = {}; + +function getDefaultDisplay( elem ) { + var temp, + doc = elem.ownerDocument, + nodeName = elem.nodeName, + display = defaultDisplayMap[ nodeName ]; + + if ( display ) { + return display; + } + + temp = doc.body.appendChild( doc.createElement( nodeName ) ); + display = jQuery.css( temp, "display" ); + + temp.parentNode.removeChild( temp ); + + if ( display === "none" ) { + display = "block"; + } + defaultDisplayMap[ nodeName ] = display; + + return display; +} + +function showHide( elements, show ) { + var display, elem, + values = [], + index = 0, + length = elements.length; + + // Determine new display value for elements that need to change + for ( ; index < length; index++ ) { + elem = elements[ index ]; + if ( !elem.style ) { + continue; + } + + display = elem.style.display; + if ( show ) { + + // Since we force visibility upon cascade-hidden elements, an immediate (and slow) + // check is required in this first loop unless we have a nonempty display value (either + // inline or about-to-be-restored) + if ( display === "none" ) { + values[ index ] = dataPriv.get( elem, "display" ) || null; + if ( !values[ index ] ) { + elem.style.display = ""; + } + } + if ( elem.style.display === "" && isHiddenWithinTree( elem ) ) { + values[ index ] = getDefaultDisplay( elem ); + } + } else { + if ( display !== "none" ) { + values[ index ] = "none"; + + // Remember what we're overwriting + dataPriv.set( elem, "display", display ); + } + } + } + + // Set the display of the elements in a second loop to avoid constant reflow + for ( index = 0; index < length; index++ ) { + if ( values[ index ] != null ) { + elements[ index ].style.display = values[ index ]; + } + } + + return elements; +} + +jQuery.fn.extend( { + show: function() { + return showHide( this, true ); + }, + hide: function() { + return showHide( this ); + }, + toggle: function( state ) { + if ( typeof state === "boolean" ) { + return state ? this.show() : this.hide(); + } + + return this.each( function() { + if ( isHiddenWithinTree( this ) ) { + jQuery( this ).show(); + } else { + jQuery( this ).hide(); + } + } ); + } +} ); +var rcheckableType = ( /^(?:checkbox|radio)$/i ); + +var rtagName = ( /<([a-z][^\/\0>\x20\t\r\n\f]*)/i ); + +var rscriptType = ( /^$|^module$|\/(?:java|ecma)script/i ); + + + +( function() { + var fragment = document.createDocumentFragment(), + div = fragment.appendChild( document.createElement( "div" ) ), + input = document.createElement( "input" ); + + // Support: Android 4.0 - 4.3 only + // Check state lost if the name is set (#11217) + // Support: Windows Web Apps (WWA) + // `name` and `type` must use .setAttribute for WWA (#14901) + input.setAttribute( "type", "radio" ); + input.setAttribute( "checked", "checked" ); + input.setAttribute( "name", "t" ); + + div.appendChild( input ); + + // Support: Android <=4.1 only + // Older WebKit doesn't clone checked state correctly in fragments + support.checkClone = div.cloneNode( true ).cloneNode( true ).lastChild.checked; + + // Support: IE <=11 only + // Make sure textarea (and checkbox) defaultValue is properly cloned + div.innerHTML = ""; + support.noCloneChecked = !!div.cloneNode( true ).lastChild.defaultValue; + + // Support: IE <=9 only + // IE <=9 replaces "; + support.option = !!div.lastChild; +} )(); + + +// We have to close these tags to support XHTML (#13200) +var wrapMap = { + + // XHTML parsers do not magically insert elements in the + // same way that tag soup parsers do. So we cannot shorten + // this by omitting or other required elements. + thead: [ 1, "", "
" ], + col: [ 2, "", "
" ], + tr: [ 2, "", "
" ], + td: [ 3, "", "
" ], + + _default: [ 0, "", "" ] +}; + +wrapMap.tbody = wrapMap.tfoot = wrapMap.colgroup = wrapMap.caption = wrapMap.thead; +wrapMap.th = wrapMap.td; + +// Support: IE <=9 only +if ( !support.option ) { + wrapMap.optgroup = wrapMap.option = [ 1, "" ]; +} + + +function getAll( context, tag ) { + + // Support: IE <=9 - 11 only + // Use typeof to avoid zero-argument method invocation on host objects (#15151) + var ret; + + if ( typeof context.getElementsByTagName !== "undefined" ) { + ret = context.getElementsByTagName( tag || "*" ); + + } else if ( typeof context.querySelectorAll !== "undefined" ) { + ret = context.querySelectorAll( tag || "*" ); + + } else { + ret = []; + } + + if ( tag === undefined || tag && nodeName( context, tag ) ) { + return jQuery.merge( [ context ], ret ); + } + + return ret; +} + + +// Mark scripts as having already been evaluated +function setGlobalEval( elems, refElements ) { + var i = 0, + l = elems.length; + + for ( ; i < l; i++ ) { + dataPriv.set( + elems[ i ], + "globalEval", + !refElements || dataPriv.get( refElements[ i ], "globalEval" ) + ); + } +} + + +var rhtml = /<|&#?\w+;/; + +function buildFragment( elems, context, scripts, selection, ignored ) { + var elem, tmp, tag, wrap, attached, j, + fragment = context.createDocumentFragment(), + nodes = [], + i = 0, + l = elems.length; + + for ( ; i < l; i++ ) { + elem = elems[ i ]; + + if ( elem || elem === 0 ) { + + // Add nodes directly + if ( toType( elem ) === "object" ) { + + // Support: Android <=4.0 only, PhantomJS 1 only + // push.apply(_, arraylike) throws on ancient WebKit + jQuery.merge( nodes, elem.nodeType ? [ elem ] : elem ); + + // Convert non-html into a text node + } else if ( !rhtml.test( elem ) ) { + nodes.push( context.createTextNode( elem ) ); + + // Convert html into DOM nodes + } else { + tmp = tmp || fragment.appendChild( context.createElement( "div" ) ); + + // Deserialize a standard representation + tag = ( rtagName.exec( elem ) || [ "", "" ] )[ 1 ].toLowerCase(); + wrap = wrapMap[ tag ] || wrapMap._default; + tmp.innerHTML = wrap[ 1 ] + jQuery.htmlPrefilter( elem ) + wrap[ 2 ]; + + // Descend through wrappers to the right content + j = wrap[ 0 ]; + while ( j-- ) { + tmp = tmp.lastChild; + } + + // Support: Android <=4.0 only, PhantomJS 1 only + // push.apply(_, arraylike) throws on ancient WebKit + jQuery.merge( nodes, tmp.childNodes ); + + // Remember the top-level container + tmp = fragment.firstChild; + + // Ensure the created nodes are orphaned (#12392) + tmp.textContent = ""; + } + } + } + + // Remove wrapper from fragment + fragment.textContent = ""; + + i = 0; + while ( ( elem = nodes[ i++ ] ) ) { + + // Skip elements already in the context collection (trac-4087) + if ( selection && jQuery.inArray( elem, selection ) > -1 ) { + if ( ignored ) { + ignored.push( elem ); + } + continue; + } + + attached = isAttached( elem ); + + // Append to fragment + tmp = getAll( fragment.appendChild( elem ), "script" ); + + // Preserve script evaluation history + if ( attached ) { + setGlobalEval( tmp ); + } + + // Capture executables + if ( scripts ) { + j = 0; + while ( ( elem = tmp[ j++ ] ) ) { + if ( rscriptType.test( elem.type || "" ) ) { + scripts.push( elem ); + } + } + } + } + + return fragment; +} + + +var rtypenamespace = /^([^.]*)(?:\.(.+)|)/; + +function returnTrue() { + return true; +} + +function returnFalse() { + return false; +} + +// Support: IE <=9 - 11+ +// focus() and blur() are asynchronous, except when they are no-op. +// So expect focus to be synchronous when the element is already active, +// and blur to be synchronous when the element is not already active. +// (focus and blur are always synchronous in other supported browsers, +// this just defines when we can count on it). +function expectSync( elem, type ) { + return ( elem === safeActiveElement() ) === ( type === "focus" ); +} + +// Support: IE <=9 only +// Accessing document.activeElement can throw unexpectedly +// https://bugs.jquery.com/ticket/13393 +function safeActiveElement() { + try { + return document.activeElement; + } catch ( err ) { } +} + +function on( elem, types, selector, data, fn, one ) { + var origFn, type; + + // Types can be a map of types/handlers + if ( typeof types === "object" ) { + + // ( types-Object, selector, data ) + if ( typeof selector !== "string" ) { + + // ( types-Object, data ) + data = data || selector; + selector = undefined; + } + for ( type in types ) { + on( elem, type, selector, data, types[ type ], one ); + } + return elem; + } + + if ( data == null && fn == null ) { + + // ( types, fn ) + fn = selector; + data = selector = undefined; + } else if ( fn == null ) { + if ( typeof selector === "string" ) { + + // ( types, selector, fn ) + fn = data; + data = undefined; + } else { + + // ( types, data, fn ) + fn = data; + data = selector; + selector = undefined; + } + } + if ( fn === false ) { + fn = returnFalse; + } else if ( !fn ) { + return elem; + } + + if ( one === 1 ) { + origFn = fn; + fn = function( event ) { + + // Can use an empty set, since event contains the info + jQuery().off( event ); + return origFn.apply( this, arguments ); + }; + + // Use same guid so caller can remove using origFn + fn.guid = origFn.guid || ( origFn.guid = jQuery.guid++ ); + } + return elem.each( function() { + jQuery.event.add( this, types, fn, data, selector ); + } ); +} + +/* + * Helper functions for managing events -- not part of the public interface. + * Props to Dean Edwards' addEvent library for many of the ideas. + */ +jQuery.event = { + + global: {}, + + add: function( elem, types, handler, data, selector ) { + + var handleObjIn, eventHandle, tmp, + events, t, handleObj, + special, handlers, type, namespaces, origType, + elemData = dataPriv.get( elem ); + + // Only attach events to objects that accept data + if ( !acceptData( elem ) ) { + return; + } + + // Caller can pass in an object of custom data in lieu of the handler + if ( handler.handler ) { + handleObjIn = handler; + handler = handleObjIn.handler; + selector = handleObjIn.selector; + } + + // Ensure that invalid selectors throw exceptions at attach time + // Evaluate against documentElement in case elem is a non-element node (e.g., document) + if ( selector ) { + jQuery.find.matchesSelector( documentElement, selector ); + } + + // Make sure that the handler has a unique ID, used to find/remove it later + if ( !handler.guid ) { + handler.guid = jQuery.guid++; + } + + // Init the element's event structure and main handler, if this is the first + if ( !( events = elemData.events ) ) { + events = elemData.events = Object.create( null ); + } + if ( !( eventHandle = elemData.handle ) ) { + eventHandle = elemData.handle = function( e ) { + + // Discard the second event of a jQuery.event.trigger() and + // when an event is called after a page has unloaded + return typeof jQuery !== "undefined" && jQuery.event.triggered !== e.type ? + jQuery.event.dispatch.apply( elem, arguments ) : undefined; + }; + } + + // Handle multiple events separated by a space + types = ( types || "" ).match( rnothtmlwhite ) || [ "" ]; + t = types.length; + while ( t-- ) { + tmp = rtypenamespace.exec( types[ t ] ) || []; + type = origType = tmp[ 1 ]; + namespaces = ( tmp[ 2 ] || "" ).split( "." ).sort(); + + // There *must* be a type, no attaching namespace-only handlers + if ( !type ) { + continue; + } + + // If event changes its type, use the special event handlers for the changed type + special = jQuery.event.special[ type ] || {}; + + // If selector defined, determine special event api type, otherwise given type + type = ( selector ? special.delegateType : special.bindType ) || type; + + // Update special based on newly reset type + special = jQuery.event.special[ type ] || {}; + + // handleObj is passed to all event handlers + handleObj = jQuery.extend( { + type: type, + origType: origType, + data: data, + handler: handler, + guid: handler.guid, + selector: selector, + needsContext: selector && jQuery.expr.match.needsContext.test( selector ), + namespace: namespaces.join( "." ) + }, handleObjIn ); + + // Init the event handler queue if we're the first + if ( !( handlers = events[ type ] ) ) { + handlers = events[ type ] = []; + handlers.delegateCount = 0; + + // Only use addEventListener if the special events handler returns false + if ( !special.setup || + special.setup.call( elem, data, namespaces, eventHandle ) === false ) { + + if ( elem.addEventListener ) { + elem.addEventListener( type, eventHandle ); + } + } + } + + if ( special.add ) { + special.add.call( elem, handleObj ); + + if ( !handleObj.handler.guid ) { + handleObj.handler.guid = handler.guid; + } + } + + // Add to the element's handler list, delegates in front + if ( selector ) { + handlers.splice( handlers.delegateCount++, 0, handleObj ); + } else { + handlers.push( handleObj ); + } + + // Keep track of which events have ever been used, for event optimization + jQuery.event.global[ type ] = true; + } + + }, + + // Detach an event or set of events from an element + remove: function( elem, types, handler, selector, mappedTypes ) { + + var j, origCount, tmp, + events, t, handleObj, + special, handlers, type, namespaces, origType, + elemData = dataPriv.hasData( elem ) && dataPriv.get( elem ); + + if ( !elemData || !( events = elemData.events ) ) { + return; + } + + // Once for each type.namespace in types; type may be omitted + types = ( types || "" ).match( rnothtmlwhite ) || [ "" ]; + t = types.length; + while ( t-- ) { + tmp = rtypenamespace.exec( types[ t ] ) || []; + type = origType = tmp[ 1 ]; + namespaces = ( tmp[ 2 ] || "" ).split( "." ).sort(); + + // Unbind all events (on this namespace, if provided) for the element + if ( !type ) { + for ( type in events ) { + jQuery.event.remove( elem, type + types[ t ], handler, selector, true ); + } + continue; + } + + special = jQuery.event.special[ type ] || {}; + type = ( selector ? special.delegateType : special.bindType ) || type; + handlers = events[ type ] || []; + tmp = tmp[ 2 ] && + new RegExp( "(^|\\.)" + namespaces.join( "\\.(?:.*\\.|)" ) + "(\\.|$)" ); + + // Remove matching events + origCount = j = handlers.length; + while ( j-- ) { + handleObj = handlers[ j ]; + + if ( ( mappedTypes || origType === handleObj.origType ) && + ( !handler || handler.guid === handleObj.guid ) && + ( !tmp || tmp.test( handleObj.namespace ) ) && + ( !selector || selector === handleObj.selector || + selector === "**" && handleObj.selector ) ) { + handlers.splice( j, 1 ); + + if ( handleObj.selector ) { + handlers.delegateCount--; + } + if ( special.remove ) { + special.remove.call( elem, handleObj ); + } + } + } + + // Remove generic event handler if we removed something and no more handlers exist + // (avoids potential for endless recursion during removal of special event handlers) + if ( origCount && !handlers.length ) { + if ( !special.teardown || + special.teardown.call( elem, namespaces, elemData.handle ) === false ) { + + jQuery.removeEvent( elem, type, elemData.handle ); + } + + delete events[ type ]; + } + } + + // Remove data and the expando if it's no longer used + if ( jQuery.isEmptyObject( events ) ) { + dataPriv.remove( elem, "handle events" ); + } + }, + + dispatch: function( nativeEvent ) { + + var i, j, ret, matched, handleObj, handlerQueue, + args = new Array( arguments.length ), + + // Make a writable jQuery.Event from the native event object + event = jQuery.event.fix( nativeEvent ), + + handlers = ( + dataPriv.get( this, "events" ) || Object.create( null ) + )[ event.type ] || [], + special = jQuery.event.special[ event.type ] || {}; + + // Use the fix-ed jQuery.Event rather than the (read-only) native event + args[ 0 ] = event; + + for ( i = 1; i < arguments.length; i++ ) { + args[ i ] = arguments[ i ]; + } + + event.delegateTarget = this; + + // Call the preDispatch hook for the mapped type, and let it bail if desired + if ( special.preDispatch && special.preDispatch.call( this, event ) === false ) { + return; + } + + // Determine handlers + handlerQueue = jQuery.event.handlers.call( this, event, handlers ); + + // Run delegates first; they may want to stop propagation beneath us + i = 0; + while ( ( matched = handlerQueue[ i++ ] ) && !event.isPropagationStopped() ) { + event.currentTarget = matched.elem; + + j = 0; + while ( ( handleObj = matched.handlers[ j++ ] ) && + !event.isImmediatePropagationStopped() ) { + + // If the event is namespaced, then each handler is only invoked if it is + // specially universal or its namespaces are a superset of the event's. + if ( !event.rnamespace || handleObj.namespace === false || + event.rnamespace.test( handleObj.namespace ) ) { + + event.handleObj = handleObj; + event.data = handleObj.data; + + ret = ( ( jQuery.event.special[ handleObj.origType ] || {} ).handle || + handleObj.handler ).apply( matched.elem, args ); + + if ( ret !== undefined ) { + if ( ( event.result = ret ) === false ) { + event.preventDefault(); + event.stopPropagation(); + } + } + } + } + } + + // Call the postDispatch hook for the mapped type + if ( special.postDispatch ) { + special.postDispatch.call( this, event ); + } + + return event.result; + }, + + handlers: function( event, handlers ) { + var i, handleObj, sel, matchedHandlers, matchedSelectors, + handlerQueue = [], + delegateCount = handlers.delegateCount, + cur = event.target; + + // Find delegate handlers + if ( delegateCount && + + // Support: IE <=9 + // Black-hole SVG instance trees (trac-13180) + cur.nodeType && + + // Support: Firefox <=42 + // Suppress spec-violating clicks indicating a non-primary pointer button (trac-3861) + // https://www.w3.org/TR/DOM-Level-3-Events/#event-type-click + // Support: IE 11 only + // ...but not arrow key "clicks" of radio inputs, which can have `button` -1 (gh-2343) + !( event.type === "click" && event.button >= 1 ) ) { + + for ( ; cur !== this; cur = cur.parentNode || this ) { + + // Don't check non-elements (#13208) + // Don't process clicks on disabled elements (#6911, #8165, #11382, #11764) + if ( cur.nodeType === 1 && !( event.type === "click" && cur.disabled === true ) ) { + matchedHandlers = []; + matchedSelectors = {}; + for ( i = 0; i < delegateCount; i++ ) { + handleObj = handlers[ i ]; + + // Don't conflict with Object.prototype properties (#13203) + sel = handleObj.selector + " "; + + if ( matchedSelectors[ sel ] === undefined ) { + matchedSelectors[ sel ] = handleObj.needsContext ? + jQuery( sel, this ).index( cur ) > -1 : + jQuery.find( sel, this, null, [ cur ] ).length; + } + if ( matchedSelectors[ sel ] ) { + matchedHandlers.push( handleObj ); + } + } + if ( matchedHandlers.length ) { + handlerQueue.push( { elem: cur, handlers: matchedHandlers } ); + } + } + } + } + + // Add the remaining (directly-bound) handlers + cur = this; + if ( delegateCount < handlers.length ) { + handlerQueue.push( { elem: cur, handlers: handlers.slice( delegateCount ) } ); + } + + return handlerQueue; + }, + + addProp: function( name, hook ) { + Object.defineProperty( jQuery.Event.prototype, name, { + enumerable: true, + configurable: true, + + get: isFunction( hook ) ? + function() { + if ( this.originalEvent ) { + return hook( this.originalEvent ); + } + } : + function() { + if ( this.originalEvent ) { + return this.originalEvent[ name ]; + } + }, + + set: function( value ) { + Object.defineProperty( this, name, { + enumerable: true, + configurable: true, + writable: true, + value: value + } ); + } + } ); + }, + + fix: function( originalEvent ) { + return originalEvent[ jQuery.expando ] ? + originalEvent : + new jQuery.Event( originalEvent ); + }, + + special: { + load: { + + // Prevent triggered image.load events from bubbling to window.load + noBubble: true + }, + click: { + + // Utilize native event to ensure correct state for checkable inputs + setup: function( data ) { + + // For mutual compressibility with _default, replace `this` access with a local var. + // `|| data` is dead code meant only to preserve the variable through minification. + var el = this || data; + + // Claim the first handler + if ( rcheckableType.test( el.type ) && + el.click && nodeName( el, "input" ) ) { + + // dataPriv.set( el, "click", ... ) + leverageNative( el, "click", returnTrue ); + } + + // Return false to allow normal processing in the caller + return false; + }, + trigger: function( data ) { + + // For mutual compressibility with _default, replace `this` access with a local var. + // `|| data` is dead code meant only to preserve the variable through minification. + var el = this || data; + + // Force setup before triggering a click + if ( rcheckableType.test( el.type ) && + el.click && nodeName( el, "input" ) ) { + + leverageNative( el, "click" ); + } + + // Return non-false to allow normal event-path propagation + return true; + }, + + // For cross-browser consistency, suppress native .click() on links + // Also prevent it if we're currently inside a leveraged native-event stack + _default: function( event ) { + var target = event.target; + return rcheckableType.test( target.type ) && + target.click && nodeName( target, "input" ) && + dataPriv.get( target, "click" ) || + nodeName( target, "a" ); + } + }, + + beforeunload: { + postDispatch: function( event ) { + + // Support: Firefox 20+ + // Firefox doesn't alert if the returnValue field is not set. + if ( event.result !== undefined && event.originalEvent ) { + event.originalEvent.returnValue = event.result; + } + } + } + } +}; + +// Ensure the presence of an event listener that handles manually-triggered +// synthetic events by interrupting progress until reinvoked in response to +// *native* events that it fires directly, ensuring that state changes have +// already occurred before other listeners are invoked. +function leverageNative( el, type, expectSync ) { + + // Missing expectSync indicates a trigger call, which must force setup through jQuery.event.add + if ( !expectSync ) { + if ( dataPriv.get( el, type ) === undefined ) { + jQuery.event.add( el, type, returnTrue ); + } + return; + } + + // Register the controller as a special universal handler for all event namespaces + dataPriv.set( el, type, false ); + jQuery.event.add( el, type, { + namespace: false, + handler: function( event ) { + var notAsync, result, + saved = dataPriv.get( this, type ); + + if ( ( event.isTrigger & 1 ) && this[ type ] ) { + + // Interrupt processing of the outer synthetic .trigger()ed event + // Saved data should be false in such cases, but might be a leftover capture object + // from an async native handler (gh-4350) + if ( !saved.length ) { + + // Store arguments for use when handling the inner native event + // There will always be at least one argument (an event object), so this array + // will not be confused with a leftover capture object. + saved = slice.call( arguments ); + dataPriv.set( this, type, saved ); + + // Trigger the native event and capture its result + // Support: IE <=9 - 11+ + // focus() and blur() are asynchronous + notAsync = expectSync( this, type ); + this[ type ](); + result = dataPriv.get( this, type ); + if ( saved !== result || notAsync ) { + dataPriv.set( this, type, false ); + } else { + result = {}; + } + if ( saved !== result ) { + + // Cancel the outer synthetic event + event.stopImmediatePropagation(); + event.preventDefault(); + + // Support: Chrome 86+ + // In Chrome, if an element having a focusout handler is blurred by + // clicking outside of it, it invokes the handler synchronously. If + // that handler calls `.remove()` on the element, the data is cleared, + // leaving `result` undefined. We need to guard against this. + return result && result.value; + } + + // If this is an inner synthetic event for an event with a bubbling surrogate + // (focus or blur), assume that the surrogate already propagated from triggering the + // native event and prevent that from happening again here. + // This technically gets the ordering wrong w.r.t. to `.trigger()` (in which the + // bubbling surrogate propagates *after* the non-bubbling base), but that seems + // less bad than duplication. + } else if ( ( jQuery.event.special[ type ] || {} ).delegateType ) { + event.stopPropagation(); + } + + // If this is a native event triggered above, everything is now in order + // Fire an inner synthetic event with the original arguments + } else if ( saved.length ) { + + // ...and capture the result + dataPriv.set( this, type, { + value: jQuery.event.trigger( + + // Support: IE <=9 - 11+ + // Extend with the prototype to reset the above stopImmediatePropagation() + jQuery.extend( saved[ 0 ], jQuery.Event.prototype ), + saved.slice( 1 ), + this + ) + } ); + + // Abort handling of the native event + event.stopImmediatePropagation(); + } + } + } ); +} + +jQuery.removeEvent = function( elem, type, handle ) { + + // This "if" is needed for plain objects + if ( elem.removeEventListener ) { + elem.removeEventListener( type, handle ); + } +}; + +jQuery.Event = function( src, props ) { + + // Allow instantiation without the 'new' keyword + if ( !( this instanceof jQuery.Event ) ) { + return new jQuery.Event( src, props ); + } + + // Event object + if ( src && src.type ) { + this.originalEvent = src; + this.type = src.type; + + // Events bubbling up the document may have been marked as prevented + // by a handler lower down the tree; reflect the correct value. + this.isDefaultPrevented = src.defaultPrevented || + src.defaultPrevented === undefined && + + // Support: Android <=2.3 only + src.returnValue === false ? + returnTrue : + returnFalse; + + // Create target properties + // Support: Safari <=6 - 7 only + // Target should not be a text node (#504, #13143) + this.target = ( src.target && src.target.nodeType === 3 ) ? + src.target.parentNode : + src.target; + + this.currentTarget = src.currentTarget; + this.relatedTarget = src.relatedTarget; + + // Event type + } else { + this.type = src; + } + + // Put explicitly provided properties onto the event object + if ( props ) { + jQuery.extend( this, props ); + } + + // Create a timestamp if incoming event doesn't have one + this.timeStamp = src && src.timeStamp || Date.now(); + + // Mark it as fixed + this[ jQuery.expando ] = true; +}; + +// jQuery.Event is based on DOM3 Events as specified by the ECMAScript Language Binding +// https://www.w3.org/TR/2003/WD-DOM-Level-3-Events-20030331/ecma-script-binding.html +jQuery.Event.prototype = { + constructor: jQuery.Event, + isDefaultPrevented: returnFalse, + isPropagationStopped: returnFalse, + isImmediatePropagationStopped: returnFalse, + isSimulated: false, + + preventDefault: function() { + var e = this.originalEvent; + + this.isDefaultPrevented = returnTrue; + + if ( e && !this.isSimulated ) { + e.preventDefault(); + } + }, + stopPropagation: function() { + var e = this.originalEvent; + + this.isPropagationStopped = returnTrue; + + if ( e && !this.isSimulated ) { + e.stopPropagation(); + } + }, + stopImmediatePropagation: function() { + var e = this.originalEvent; + + this.isImmediatePropagationStopped = returnTrue; + + if ( e && !this.isSimulated ) { + e.stopImmediatePropagation(); + } + + this.stopPropagation(); + } +}; + +// Includes all common event props including KeyEvent and MouseEvent specific props +jQuery.each( { + altKey: true, + bubbles: true, + cancelable: true, + changedTouches: true, + ctrlKey: true, + detail: true, + eventPhase: true, + metaKey: true, + pageX: true, + pageY: true, + shiftKey: true, + view: true, + "char": true, + code: true, + charCode: true, + key: true, + keyCode: true, + button: true, + buttons: true, + clientX: true, + clientY: true, + offsetX: true, + offsetY: true, + pointerId: true, + pointerType: true, + screenX: true, + screenY: true, + targetTouches: true, + toElement: true, + touches: true, + which: true +}, jQuery.event.addProp ); + +jQuery.each( { focus: "focusin", blur: "focusout" }, function( type, delegateType ) { + jQuery.event.special[ type ] = { + + // Utilize native event if possible so blur/focus sequence is correct + setup: function() { + + // Claim the first handler + // dataPriv.set( this, "focus", ... ) + // dataPriv.set( this, "blur", ... ) + leverageNative( this, type, expectSync ); + + // Return false to allow normal processing in the caller + return false; + }, + trigger: function() { + + // Force setup before trigger + leverageNative( this, type ); + + // Return non-false to allow normal event-path propagation + return true; + }, + + // Suppress native focus or blur as it's already being fired + // in leverageNative. + _default: function() { + return true; + }, + + delegateType: delegateType + }; +} ); + +// Create mouseenter/leave events using mouseover/out and event-time checks +// so that event delegation works in jQuery. +// Do the same for pointerenter/pointerleave and pointerover/pointerout +// +// Support: Safari 7 only +// Safari sends mouseenter too often; see: +// https://bugs.chromium.org/p/chromium/issues/detail?id=470258 +// for the description of the bug (it existed in older Chrome versions as well). +jQuery.each( { + mouseenter: "mouseover", + mouseleave: "mouseout", + pointerenter: "pointerover", + pointerleave: "pointerout" +}, function( orig, fix ) { + jQuery.event.special[ orig ] = { + delegateType: fix, + bindType: fix, + + handle: function( event ) { + var ret, + target = this, + related = event.relatedTarget, + handleObj = event.handleObj; + + // For mouseenter/leave call the handler if related is outside the target. + // NB: No relatedTarget if the mouse left/entered the browser window + if ( !related || ( related !== target && !jQuery.contains( target, related ) ) ) { + event.type = handleObj.origType; + ret = handleObj.handler.apply( this, arguments ); + event.type = fix; + } + return ret; + } + }; +} ); + +jQuery.fn.extend( { + + on: function( types, selector, data, fn ) { + return on( this, types, selector, data, fn ); + }, + one: function( types, selector, data, fn ) { + return on( this, types, selector, data, fn, 1 ); + }, + off: function( types, selector, fn ) { + var handleObj, type; + if ( types && types.preventDefault && types.handleObj ) { + + // ( event ) dispatched jQuery.Event + handleObj = types.handleObj; + jQuery( types.delegateTarget ).off( + handleObj.namespace ? + handleObj.origType + "." + handleObj.namespace : + handleObj.origType, + handleObj.selector, + handleObj.handler + ); + return this; + } + if ( typeof types === "object" ) { + + // ( types-object [, selector] ) + for ( type in types ) { + this.off( type, selector, types[ type ] ); + } + return this; + } + if ( selector === false || typeof selector === "function" ) { + + // ( types [, fn] ) + fn = selector; + selector = undefined; + } + if ( fn === false ) { + fn = returnFalse; + } + return this.each( function() { + jQuery.event.remove( this, types, fn, selector ); + } ); + } +} ); + + +var + + // Support: IE <=10 - 11, Edge 12 - 13 only + // In IE/Edge using regex groups here causes severe slowdowns. + // See https://connect.microsoft.com/IE/feedback/details/1736512/ + rnoInnerhtml = /\s*$/g; + +// Prefer a tbody over its parent table for containing new rows +function manipulationTarget( elem, content ) { + if ( nodeName( elem, "table" ) && + nodeName( content.nodeType !== 11 ? content : content.firstChild, "tr" ) ) { + + return jQuery( elem ).children( "tbody" )[ 0 ] || elem; + } + + return elem; +} + +// Replace/restore the type attribute of script elements for safe DOM manipulation +function disableScript( elem ) { + elem.type = ( elem.getAttribute( "type" ) !== null ) + "/" + elem.type; + return elem; +} +function restoreScript( elem ) { + if ( ( elem.type || "" ).slice( 0, 5 ) === "true/" ) { + elem.type = elem.type.slice( 5 ); + } else { + elem.removeAttribute( "type" ); + } + + return elem; +} + +function cloneCopyEvent( src, dest ) { + var i, l, type, pdataOld, udataOld, udataCur, events; + + if ( dest.nodeType !== 1 ) { + return; + } + + // 1. Copy private data: events, handlers, etc. + if ( dataPriv.hasData( src ) ) { + pdataOld = dataPriv.get( src ); + events = pdataOld.events; + + if ( events ) { + dataPriv.remove( dest, "handle events" ); + + for ( type in events ) { + for ( i = 0, l = events[ type ].length; i < l; i++ ) { + jQuery.event.add( dest, type, events[ type ][ i ] ); + } + } + } + } + + // 2. Copy user data + if ( dataUser.hasData( src ) ) { + udataOld = dataUser.access( src ); + udataCur = jQuery.extend( {}, udataOld ); + + dataUser.set( dest, udataCur ); + } +} + +// Fix IE bugs, see support tests +function fixInput( src, dest ) { + var nodeName = dest.nodeName.toLowerCase(); + + // Fails to persist the checked state of a cloned checkbox or radio button. + if ( nodeName === "input" && rcheckableType.test( src.type ) ) { + dest.checked = src.checked; + + // Fails to return the selected option to the default selected state when cloning options + } else if ( nodeName === "input" || nodeName === "textarea" ) { + dest.defaultValue = src.defaultValue; + } +} + +function domManip( collection, args, callback, ignored ) { + + // Flatten any nested arrays + args = flat( args ); + + var fragment, first, scripts, hasScripts, node, doc, + i = 0, + l = collection.length, + iNoClone = l - 1, + value = args[ 0 ], + valueIsFunction = isFunction( value ); + + // We can't cloneNode fragments that contain checked, in WebKit + if ( valueIsFunction || + ( l > 1 && typeof value === "string" && + !support.checkClone && rchecked.test( value ) ) ) { + return collection.each( function( index ) { + var self = collection.eq( index ); + if ( valueIsFunction ) { + args[ 0 ] = value.call( this, index, self.html() ); + } + domManip( self, args, callback, ignored ); + } ); + } + + if ( l ) { + fragment = buildFragment( args, collection[ 0 ].ownerDocument, false, collection, ignored ); + first = fragment.firstChild; + + if ( fragment.childNodes.length === 1 ) { + fragment = first; + } + + // Require either new content or an interest in ignored elements to invoke the callback + if ( first || ignored ) { + scripts = jQuery.map( getAll( fragment, "script" ), disableScript ); + hasScripts = scripts.length; + + // Use the original fragment for the last item + // instead of the first because it can end up + // being emptied incorrectly in certain situations (#8070). + for ( ; i < l; i++ ) { + node = fragment; + + if ( i !== iNoClone ) { + node = jQuery.clone( node, true, true ); + + // Keep references to cloned scripts for later restoration + if ( hasScripts ) { + + // Support: Android <=4.0 only, PhantomJS 1 only + // push.apply(_, arraylike) throws on ancient WebKit + jQuery.merge( scripts, getAll( node, "script" ) ); + } + } + + callback.call( collection[ i ], node, i ); + } + + if ( hasScripts ) { + doc = scripts[ scripts.length - 1 ].ownerDocument; + + // Reenable scripts + jQuery.map( scripts, restoreScript ); + + // Evaluate executable scripts on first document insertion + for ( i = 0; i < hasScripts; i++ ) { + node = scripts[ i ]; + if ( rscriptType.test( node.type || "" ) && + !dataPriv.access( node, "globalEval" ) && + jQuery.contains( doc, node ) ) { + + if ( node.src && ( node.type || "" ).toLowerCase() !== "module" ) { + + // Optional AJAX dependency, but won't run scripts if not present + if ( jQuery._evalUrl && !node.noModule ) { + jQuery._evalUrl( node.src, { + nonce: node.nonce || node.getAttribute( "nonce" ) + }, doc ); + } + } else { + DOMEval( node.textContent.replace( rcleanScript, "" ), node, doc ); + } + } + } + } + } + } + + return collection; +} + +function remove( elem, selector, keepData ) { + var node, + nodes = selector ? jQuery.filter( selector, elem ) : elem, + i = 0; + + for ( ; ( node = nodes[ i ] ) != null; i++ ) { + if ( !keepData && node.nodeType === 1 ) { + jQuery.cleanData( getAll( node ) ); + } + + if ( node.parentNode ) { + if ( keepData && isAttached( node ) ) { + setGlobalEval( getAll( node, "script" ) ); + } + node.parentNode.removeChild( node ); + } + } + + return elem; +} + +jQuery.extend( { + htmlPrefilter: function( html ) { + return html; + }, + + clone: function( elem, dataAndEvents, deepDataAndEvents ) { + var i, l, srcElements, destElements, + clone = elem.cloneNode( true ), + inPage = isAttached( elem ); + + // Fix IE cloning issues + if ( !support.noCloneChecked && ( elem.nodeType === 1 || elem.nodeType === 11 ) && + !jQuery.isXMLDoc( elem ) ) { + + // We eschew Sizzle here for performance reasons: https://jsperf.com/getall-vs-sizzle/2 + destElements = getAll( clone ); + srcElements = getAll( elem ); + + for ( i = 0, l = srcElements.length; i < l; i++ ) { + fixInput( srcElements[ i ], destElements[ i ] ); + } + } + + // Copy the events from the original to the clone + if ( dataAndEvents ) { + if ( deepDataAndEvents ) { + srcElements = srcElements || getAll( elem ); + destElements = destElements || getAll( clone ); + + for ( i = 0, l = srcElements.length; i < l; i++ ) { + cloneCopyEvent( srcElements[ i ], destElements[ i ] ); + } + } else { + cloneCopyEvent( elem, clone ); + } + } + + // Preserve script evaluation history + destElements = getAll( clone, "script" ); + if ( destElements.length > 0 ) { + setGlobalEval( destElements, !inPage && getAll( elem, "script" ) ); + } + + // Return the cloned set + return clone; + }, + + cleanData: function( elems ) { + var data, elem, type, + special = jQuery.event.special, + i = 0; + + for ( ; ( elem = elems[ i ] ) !== undefined; i++ ) { + if ( acceptData( elem ) ) { + if ( ( data = elem[ dataPriv.expando ] ) ) { + if ( data.events ) { + for ( type in data.events ) { + if ( special[ type ] ) { + jQuery.event.remove( elem, type ); + + // This is a shortcut to avoid jQuery.event.remove's overhead + } else { + jQuery.removeEvent( elem, type, data.handle ); + } + } + } + + // Support: Chrome <=35 - 45+ + // Assign undefined instead of using delete, see Data#remove + elem[ dataPriv.expando ] = undefined; + } + if ( elem[ dataUser.expando ] ) { + + // Support: Chrome <=35 - 45+ + // Assign undefined instead of using delete, see Data#remove + elem[ dataUser.expando ] = undefined; + } + } + } + } +} ); + +jQuery.fn.extend( { + detach: function( selector ) { + return remove( this, selector, true ); + }, + + remove: function( selector ) { + return remove( this, selector ); + }, + + text: function( value ) { + return access( this, function( value ) { + return value === undefined ? + jQuery.text( this ) : + this.empty().each( function() { + if ( this.nodeType === 1 || this.nodeType === 11 || this.nodeType === 9 ) { + this.textContent = value; + } + } ); + }, null, value, arguments.length ); + }, + + append: function() { + return domManip( this, arguments, function( elem ) { + if ( this.nodeType === 1 || this.nodeType === 11 || this.nodeType === 9 ) { + var target = manipulationTarget( this, elem ); + target.appendChild( elem ); + } + } ); + }, + + prepend: function() { + return domManip( this, arguments, function( elem ) { + if ( this.nodeType === 1 || this.nodeType === 11 || this.nodeType === 9 ) { + var target = manipulationTarget( this, elem ); + target.insertBefore( elem, target.firstChild ); + } + } ); + }, + + before: function() { + return domManip( this, arguments, function( elem ) { + if ( this.parentNode ) { + this.parentNode.insertBefore( elem, this ); + } + } ); + }, + + after: function() { + return domManip( this, arguments, function( elem ) { + if ( this.parentNode ) { + this.parentNode.insertBefore( elem, this.nextSibling ); + } + } ); + }, + + empty: function() { + var elem, + i = 0; + + for ( ; ( elem = this[ i ] ) != null; i++ ) { + if ( elem.nodeType === 1 ) { + + // Prevent memory leaks + jQuery.cleanData( getAll( elem, false ) ); + + // Remove any remaining nodes + elem.textContent = ""; + } + } + + return this; + }, + + clone: function( dataAndEvents, deepDataAndEvents ) { + dataAndEvents = dataAndEvents == null ? false : dataAndEvents; + deepDataAndEvents = deepDataAndEvents == null ? dataAndEvents : deepDataAndEvents; + + return this.map( function() { + return jQuery.clone( this, dataAndEvents, deepDataAndEvents ); + } ); + }, + + html: function( value ) { + return access( this, function( value ) { + var elem = this[ 0 ] || {}, + i = 0, + l = this.length; + + if ( value === undefined && elem.nodeType === 1 ) { + return elem.innerHTML; + } + + // See if we can take a shortcut and just use innerHTML + if ( typeof value === "string" && !rnoInnerhtml.test( value ) && + !wrapMap[ ( rtagName.exec( value ) || [ "", "" ] )[ 1 ].toLowerCase() ] ) { + + value = jQuery.htmlPrefilter( value ); + + try { + for ( ; i < l; i++ ) { + elem = this[ i ] || {}; + + // Remove element nodes and prevent memory leaks + if ( elem.nodeType === 1 ) { + jQuery.cleanData( getAll( elem, false ) ); + elem.innerHTML = value; + } + } + + elem = 0; + + // If using innerHTML throws an exception, use the fallback method + } catch ( e ) {} + } + + if ( elem ) { + this.empty().append( value ); + } + }, null, value, arguments.length ); + }, + + replaceWith: function() { + var ignored = []; + + // Make the changes, replacing each non-ignored context element with the new content + return domManip( this, arguments, function( elem ) { + var parent = this.parentNode; + + if ( jQuery.inArray( this, ignored ) < 0 ) { + jQuery.cleanData( getAll( this ) ); + if ( parent ) { + parent.replaceChild( elem, this ); + } + } + + // Force callback invocation + }, ignored ); + } +} ); + +jQuery.each( { + appendTo: "append", + prependTo: "prepend", + insertBefore: "before", + insertAfter: "after", + replaceAll: "replaceWith" +}, function( name, original ) { + jQuery.fn[ name ] = function( selector ) { + var elems, + ret = [], + insert = jQuery( selector ), + last = insert.length - 1, + i = 0; + + for ( ; i <= last; i++ ) { + elems = i === last ? this : this.clone( true ); + jQuery( insert[ i ] )[ original ]( elems ); + + // Support: Android <=4.0 only, PhantomJS 1 only + // .get() because push.apply(_, arraylike) throws on ancient WebKit + push.apply( ret, elems.get() ); + } + + return this.pushStack( ret ); + }; +} ); +var rnumnonpx = new RegExp( "^(" + pnum + ")(?!px)[a-z%]+$", "i" ); + +var getStyles = function( elem ) { + + // Support: IE <=11 only, Firefox <=30 (#15098, #14150) + // IE throws on elements created in popups + // FF meanwhile throws on frame elements through "defaultView.getComputedStyle" + var view = elem.ownerDocument.defaultView; + + if ( !view || !view.opener ) { + view = window; + } + + return view.getComputedStyle( elem ); + }; + +var swap = function( elem, options, callback ) { + var ret, name, + old = {}; + + // Remember the old values, and insert the new ones + for ( name in options ) { + old[ name ] = elem.style[ name ]; + elem.style[ name ] = options[ name ]; + } + + ret = callback.call( elem ); + + // Revert the old values + for ( name in options ) { + elem.style[ name ] = old[ name ]; + } + + return ret; +}; + + +var rboxStyle = new RegExp( cssExpand.join( "|" ), "i" ); + + + +( function() { + + // Executing both pixelPosition & boxSizingReliable tests require only one layout + // so they're executed at the same time to save the second computation. + function computeStyleTests() { + + // This is a singleton, we need to execute it only once + if ( !div ) { + return; + } + + container.style.cssText = "position:absolute;left:-11111px;width:60px;" + + "margin-top:1px;padding:0;border:0"; + div.style.cssText = + "position:relative;display:block;box-sizing:border-box;overflow:scroll;" + + "margin:auto;border:1px;padding:1px;" + + "width:60%;top:1%"; + documentElement.appendChild( container ).appendChild( div ); + + var divStyle = window.getComputedStyle( div ); + pixelPositionVal = divStyle.top !== "1%"; + + // Support: Android 4.0 - 4.3 only, Firefox <=3 - 44 + reliableMarginLeftVal = roundPixelMeasures( divStyle.marginLeft ) === 12; + + // Support: Android 4.0 - 4.3 only, Safari <=9.1 - 10.1, iOS <=7.0 - 9.3 + // Some styles come back with percentage values, even though they shouldn't + div.style.right = "60%"; + pixelBoxStylesVal = roundPixelMeasures( divStyle.right ) === 36; + + // Support: IE 9 - 11 only + // Detect misreporting of content dimensions for box-sizing:border-box elements + boxSizingReliableVal = roundPixelMeasures( divStyle.width ) === 36; + + // Support: IE 9 only + // Detect overflow:scroll screwiness (gh-3699) + // Support: Chrome <=64 + // Don't get tricked when zoom affects offsetWidth (gh-4029) + div.style.position = "absolute"; + scrollboxSizeVal = roundPixelMeasures( div.offsetWidth / 3 ) === 12; + + documentElement.removeChild( container ); + + // Nullify the div so it wouldn't be stored in the memory and + // it will also be a sign that checks already performed + div = null; + } + + function roundPixelMeasures( measure ) { + return Math.round( parseFloat( measure ) ); + } + + var pixelPositionVal, boxSizingReliableVal, scrollboxSizeVal, pixelBoxStylesVal, + reliableTrDimensionsVal, reliableMarginLeftVal, + container = document.createElement( "div" ), + div = document.createElement( "div" ); + + // Finish early in limited (non-browser) environments + if ( !div.style ) { + return; + } + + // Support: IE <=9 - 11 only + // Style of cloned element affects source element cloned (#8908) + div.style.backgroundClip = "content-box"; + div.cloneNode( true ).style.backgroundClip = ""; + support.clearCloneStyle = div.style.backgroundClip === "content-box"; + + jQuery.extend( support, { + boxSizingReliable: function() { + computeStyleTests(); + return boxSizingReliableVal; + }, + pixelBoxStyles: function() { + computeStyleTests(); + return pixelBoxStylesVal; + }, + pixelPosition: function() { + computeStyleTests(); + return pixelPositionVal; + }, + reliableMarginLeft: function() { + computeStyleTests(); + return reliableMarginLeftVal; + }, + scrollboxSize: function() { + computeStyleTests(); + return scrollboxSizeVal; + }, + + // Support: IE 9 - 11+, Edge 15 - 18+ + // IE/Edge misreport `getComputedStyle` of table rows with width/height + // set in CSS while `offset*` properties report correct values. + // Behavior in IE 9 is more subtle than in newer versions & it passes + // some versions of this test; make sure not to make it pass there! + // + // Support: Firefox 70+ + // Only Firefox includes border widths + // in computed dimensions. (gh-4529) + reliableTrDimensions: function() { + var table, tr, trChild, trStyle; + if ( reliableTrDimensionsVal == null ) { + table = document.createElement( "table" ); + tr = document.createElement( "tr" ); + trChild = document.createElement( "div" ); + + table.style.cssText = "position:absolute;left:-11111px;border-collapse:separate"; + tr.style.cssText = "border:1px solid"; + + // Support: Chrome 86+ + // Height set through cssText does not get applied. + // Computed height then comes back as 0. + tr.style.height = "1px"; + trChild.style.height = "9px"; + + // Support: Android 8 Chrome 86+ + // In our bodyBackground.html iframe, + // display for all div elements is set to "inline", + // which causes a problem only in Android 8 Chrome 86. + // Ensuring the div is display: block + // gets around this issue. + trChild.style.display = "block"; + + documentElement + .appendChild( table ) + .appendChild( tr ) + .appendChild( trChild ); + + trStyle = window.getComputedStyle( tr ); + reliableTrDimensionsVal = ( parseInt( trStyle.height, 10 ) + + parseInt( trStyle.borderTopWidth, 10 ) + + parseInt( trStyle.borderBottomWidth, 10 ) ) === tr.offsetHeight; + + documentElement.removeChild( table ); + } + return reliableTrDimensionsVal; + } + } ); +} )(); + + +function curCSS( elem, name, computed ) { + var width, minWidth, maxWidth, ret, + + // Support: Firefox 51+ + // Retrieving style before computed somehow + // fixes an issue with getting wrong values + // on detached elements + style = elem.style; + + computed = computed || getStyles( elem ); + + // getPropertyValue is needed for: + // .css('filter') (IE 9 only, #12537) + // .css('--customProperty) (#3144) + if ( computed ) { + ret = computed.getPropertyValue( name ) || computed[ name ]; + + if ( ret === "" && !isAttached( elem ) ) { + ret = jQuery.style( elem, name ); + } + + // A tribute to the "awesome hack by Dean Edwards" + // Android Browser returns percentage for some values, + // but width seems to be reliably pixels. + // This is against the CSSOM draft spec: + // https://drafts.csswg.org/cssom/#resolved-values + if ( !support.pixelBoxStyles() && rnumnonpx.test( ret ) && rboxStyle.test( name ) ) { + + // Remember the original values + width = style.width; + minWidth = style.minWidth; + maxWidth = style.maxWidth; + + // Put in the new values to get a computed value out + style.minWidth = style.maxWidth = style.width = ret; + ret = computed.width; + + // Revert the changed values + style.width = width; + style.minWidth = minWidth; + style.maxWidth = maxWidth; + } + } + + return ret !== undefined ? + + // Support: IE <=9 - 11 only + // IE returns zIndex value as an integer. + ret + "" : + ret; +} + + +function addGetHookIf( conditionFn, hookFn ) { + + // Define the hook, we'll check on the first run if it's really needed. + return { + get: function() { + if ( conditionFn() ) { + + // Hook not needed (or it's not possible to use it due + // to missing dependency), remove it. + delete this.get; + return; + } + + // Hook needed; redefine it so that the support test is not executed again. + return ( this.get = hookFn ).apply( this, arguments ); + } + }; +} + + +var cssPrefixes = [ "Webkit", "Moz", "ms" ], + emptyStyle = document.createElement( "div" ).style, + vendorProps = {}; + +// Return a vendor-prefixed property or undefined +function vendorPropName( name ) { + + // Check for vendor prefixed names + var capName = name[ 0 ].toUpperCase() + name.slice( 1 ), + i = cssPrefixes.length; + + while ( i-- ) { + name = cssPrefixes[ i ] + capName; + if ( name in emptyStyle ) { + return name; + } + } +} + +// Return a potentially-mapped jQuery.cssProps or vendor prefixed property +function finalPropName( name ) { + var final = jQuery.cssProps[ name ] || vendorProps[ name ]; + + if ( final ) { + return final; + } + if ( name in emptyStyle ) { + return name; + } + return vendorProps[ name ] = vendorPropName( name ) || name; +} + + +var + + // Swappable if display is none or starts with table + // except "table", "table-cell", or "table-caption" + // See here for display values: https://developer.mozilla.org/en-US/docs/CSS/display + rdisplayswap = /^(none|table(?!-c[ea]).+)/, + rcustomProp = /^--/, + cssShow = { position: "absolute", visibility: "hidden", display: "block" }, + cssNormalTransform = { + letterSpacing: "0", + fontWeight: "400" + }; + +function setPositiveNumber( _elem, value, subtract ) { + + // Any relative (+/-) values have already been + // normalized at this point + var matches = rcssNum.exec( value ); + return matches ? + + // Guard against undefined "subtract", e.g., when used as in cssHooks + Math.max( 0, matches[ 2 ] - ( subtract || 0 ) ) + ( matches[ 3 ] || "px" ) : + value; +} + +function boxModelAdjustment( elem, dimension, box, isBorderBox, styles, computedVal ) { + var i = dimension === "width" ? 1 : 0, + extra = 0, + delta = 0; + + // Adjustment may not be necessary + if ( box === ( isBorderBox ? "border" : "content" ) ) { + return 0; + } + + for ( ; i < 4; i += 2 ) { + + // Both box models exclude margin + if ( box === "margin" ) { + delta += jQuery.css( elem, box + cssExpand[ i ], true, styles ); + } + + // If we get here with a content-box, we're seeking "padding" or "border" or "margin" + if ( !isBorderBox ) { + + // Add padding + delta += jQuery.css( elem, "padding" + cssExpand[ i ], true, styles ); + + // For "border" or "margin", add border + if ( box !== "padding" ) { + delta += jQuery.css( elem, "border" + cssExpand[ i ] + "Width", true, styles ); + + // But still keep track of it otherwise + } else { + extra += jQuery.css( elem, "border" + cssExpand[ i ] + "Width", true, styles ); + } + + // If we get here with a border-box (content + padding + border), we're seeking "content" or + // "padding" or "margin" + } else { + + // For "content", subtract padding + if ( box === "content" ) { + delta -= jQuery.css( elem, "padding" + cssExpand[ i ], true, styles ); + } + + // For "content" or "padding", subtract border + if ( box !== "margin" ) { + delta -= jQuery.css( elem, "border" + cssExpand[ i ] + "Width", true, styles ); + } + } + } + + // Account for positive content-box scroll gutter when requested by providing computedVal + if ( !isBorderBox && computedVal >= 0 ) { + + // offsetWidth/offsetHeight is a rounded sum of content, padding, scroll gutter, and border + // Assuming integer scroll gutter, subtract the rest and round down + delta += Math.max( 0, Math.ceil( + elem[ "offset" + dimension[ 0 ].toUpperCase() + dimension.slice( 1 ) ] - + computedVal - + delta - + extra - + 0.5 + + // If offsetWidth/offsetHeight is unknown, then we can't determine content-box scroll gutter + // Use an explicit zero to avoid NaN (gh-3964) + ) ) || 0; + } + + return delta; +} + +function getWidthOrHeight( elem, dimension, extra ) { + + // Start with computed style + var styles = getStyles( elem ), + + // To avoid forcing a reflow, only fetch boxSizing if we need it (gh-4322). + // Fake content-box until we know it's needed to know the true value. + boxSizingNeeded = !support.boxSizingReliable() || extra, + isBorderBox = boxSizingNeeded && + jQuery.css( elem, "boxSizing", false, styles ) === "border-box", + valueIsBorderBox = isBorderBox, + + val = curCSS( elem, dimension, styles ), + offsetProp = "offset" + dimension[ 0 ].toUpperCase() + dimension.slice( 1 ); + + // Support: Firefox <=54 + // Return a confounding non-pixel value or feign ignorance, as appropriate. + if ( rnumnonpx.test( val ) ) { + if ( !extra ) { + return val; + } + val = "auto"; + } + + + // Support: IE 9 - 11 only + // Use offsetWidth/offsetHeight for when box sizing is unreliable. + // In those cases, the computed value can be trusted to be border-box. + if ( ( !support.boxSizingReliable() && isBorderBox || + + // Support: IE 10 - 11+, Edge 15 - 18+ + // IE/Edge misreport `getComputedStyle` of table rows with width/height + // set in CSS while `offset*` properties report correct values. + // Interestingly, in some cases IE 9 doesn't suffer from this issue. + !support.reliableTrDimensions() && nodeName( elem, "tr" ) || + + // Fall back to offsetWidth/offsetHeight when value is "auto" + // This happens for inline elements with no explicit setting (gh-3571) + val === "auto" || + + // Support: Android <=4.1 - 4.3 only + // Also use offsetWidth/offsetHeight for misreported inline dimensions (gh-3602) + !parseFloat( val ) && jQuery.css( elem, "display", false, styles ) === "inline" ) && + + // Make sure the element is visible & connected + elem.getClientRects().length ) { + + isBorderBox = jQuery.css( elem, "boxSizing", false, styles ) === "border-box"; + + // Where available, offsetWidth/offsetHeight approximate border box dimensions. + // Where not available (e.g., SVG), assume unreliable box-sizing and interpret the + // retrieved value as a content box dimension. + valueIsBorderBox = offsetProp in elem; + if ( valueIsBorderBox ) { + val = elem[ offsetProp ]; + } + } + + // Normalize "" and auto + val = parseFloat( val ) || 0; + + // Adjust for the element's box model + return ( val + + boxModelAdjustment( + elem, + dimension, + extra || ( isBorderBox ? "border" : "content" ), + valueIsBorderBox, + styles, + + // Provide the current computed size to request scroll gutter calculation (gh-3589) + val + ) + ) + "px"; +} + +jQuery.extend( { + + // Add in style property hooks for overriding the default + // behavior of getting and setting a style property + cssHooks: { + opacity: { + get: function( elem, computed ) { + if ( computed ) { + + // We should always get a number back from opacity + var ret = curCSS( elem, "opacity" ); + return ret === "" ? "1" : ret; + } + } + } + }, + + // Don't automatically add "px" to these possibly-unitless properties + cssNumber: { + "animationIterationCount": true, + "columnCount": true, + "fillOpacity": true, + "flexGrow": true, + "flexShrink": true, + "fontWeight": true, + "gridArea": true, + "gridColumn": true, + "gridColumnEnd": true, + "gridColumnStart": true, + "gridRow": true, + "gridRowEnd": true, + "gridRowStart": true, + "lineHeight": true, + "opacity": true, + "order": true, + "orphans": true, + "widows": true, + "zIndex": true, + "zoom": true + }, + + // Add in properties whose names you wish to fix before + // setting or getting the value + cssProps: {}, + + // Get and set the style property on a DOM Node + style: function( elem, name, value, extra ) { + + // Don't set styles on text and comment nodes + if ( !elem || elem.nodeType === 3 || elem.nodeType === 8 || !elem.style ) { + return; + } + + // Make sure that we're working with the right name + var ret, type, hooks, + origName = camelCase( name ), + isCustomProp = rcustomProp.test( name ), + style = elem.style; + + // Make sure that we're working with the right name. We don't + // want to query the value if it is a CSS custom property + // since they are user-defined. + if ( !isCustomProp ) { + name = finalPropName( origName ); + } + + // Gets hook for the prefixed version, then unprefixed version + hooks = jQuery.cssHooks[ name ] || jQuery.cssHooks[ origName ]; + + // Check if we're setting a value + if ( value !== undefined ) { + type = typeof value; + + // Convert "+=" or "-=" to relative numbers (#7345) + if ( type === "string" && ( ret = rcssNum.exec( value ) ) && ret[ 1 ] ) { + value = adjustCSS( elem, name, ret ); + + // Fixes bug #9237 + type = "number"; + } + + // Make sure that null and NaN values aren't set (#7116) + if ( value == null || value !== value ) { + return; + } + + // If a number was passed in, add the unit (except for certain CSS properties) + // The isCustomProp check can be removed in jQuery 4.0 when we only auto-append + // "px" to a few hardcoded values. + if ( type === "number" && !isCustomProp ) { + value += ret && ret[ 3 ] || ( jQuery.cssNumber[ origName ] ? "" : "px" ); + } + + // background-* props affect original clone's values + if ( !support.clearCloneStyle && value === "" && name.indexOf( "background" ) === 0 ) { + style[ name ] = "inherit"; + } + + // If a hook was provided, use that value, otherwise just set the specified value + if ( !hooks || !( "set" in hooks ) || + ( value = hooks.set( elem, value, extra ) ) !== undefined ) { + + if ( isCustomProp ) { + style.setProperty( name, value ); + } else { + style[ name ] = value; + } + } + + } else { + + // If a hook was provided get the non-computed value from there + if ( hooks && "get" in hooks && + ( ret = hooks.get( elem, false, extra ) ) !== undefined ) { + + return ret; + } + + // Otherwise just get the value from the style object + return style[ name ]; + } + }, + + css: function( elem, name, extra, styles ) { + var val, num, hooks, + origName = camelCase( name ), + isCustomProp = rcustomProp.test( name ); + + // Make sure that we're working with the right name. We don't + // want to modify the value if it is a CSS custom property + // since they are user-defined. + if ( !isCustomProp ) { + name = finalPropName( origName ); + } + + // Try prefixed name followed by the unprefixed name + hooks = jQuery.cssHooks[ name ] || jQuery.cssHooks[ origName ]; + + // If a hook was provided get the computed value from there + if ( hooks && "get" in hooks ) { + val = hooks.get( elem, true, extra ); + } + + // Otherwise, if a way to get the computed value exists, use that + if ( val === undefined ) { + val = curCSS( elem, name, styles ); + } + + // Convert "normal" to computed value + if ( val === "normal" && name in cssNormalTransform ) { + val = cssNormalTransform[ name ]; + } + + // Make numeric if forced or a qualifier was provided and val looks numeric + if ( extra === "" || extra ) { + num = parseFloat( val ); + return extra === true || isFinite( num ) ? num || 0 : val; + } + + return val; + } +} ); + +jQuery.each( [ "height", "width" ], function( _i, dimension ) { + jQuery.cssHooks[ dimension ] = { + get: function( elem, computed, extra ) { + if ( computed ) { + + // Certain elements can have dimension info if we invisibly show them + // but it must have a current display style that would benefit + return rdisplayswap.test( jQuery.css( elem, "display" ) ) && + + // Support: Safari 8+ + // Table columns in Safari have non-zero offsetWidth & zero + // getBoundingClientRect().width unless display is changed. + // Support: IE <=11 only + // Running getBoundingClientRect on a disconnected node + // in IE throws an error. + ( !elem.getClientRects().length || !elem.getBoundingClientRect().width ) ? + swap( elem, cssShow, function() { + return getWidthOrHeight( elem, dimension, extra ); + } ) : + getWidthOrHeight( elem, dimension, extra ); + } + }, + + set: function( elem, value, extra ) { + var matches, + styles = getStyles( elem ), + + // Only read styles.position if the test has a chance to fail + // to avoid forcing a reflow. + scrollboxSizeBuggy = !support.scrollboxSize() && + styles.position === "absolute", + + // To avoid forcing a reflow, only fetch boxSizing if we need it (gh-3991) + boxSizingNeeded = scrollboxSizeBuggy || extra, + isBorderBox = boxSizingNeeded && + jQuery.css( elem, "boxSizing", false, styles ) === "border-box", + subtract = extra ? + boxModelAdjustment( + elem, + dimension, + extra, + isBorderBox, + styles + ) : + 0; + + // Account for unreliable border-box dimensions by comparing offset* to computed and + // faking a content-box to get border and padding (gh-3699) + if ( isBorderBox && scrollboxSizeBuggy ) { + subtract -= Math.ceil( + elem[ "offset" + dimension[ 0 ].toUpperCase() + dimension.slice( 1 ) ] - + parseFloat( styles[ dimension ] ) - + boxModelAdjustment( elem, dimension, "border", false, styles ) - + 0.5 + ); + } + + // Convert to pixels if value adjustment is needed + if ( subtract && ( matches = rcssNum.exec( value ) ) && + ( matches[ 3 ] || "px" ) !== "px" ) { + + elem.style[ dimension ] = value; + value = jQuery.css( elem, dimension ); + } + + return setPositiveNumber( elem, value, subtract ); + } + }; +} ); + +jQuery.cssHooks.marginLeft = addGetHookIf( support.reliableMarginLeft, + function( elem, computed ) { + if ( computed ) { + return ( parseFloat( curCSS( elem, "marginLeft" ) ) || + elem.getBoundingClientRect().left - + swap( elem, { marginLeft: 0 }, function() { + return elem.getBoundingClientRect().left; + } ) + ) + "px"; + } + } +); + +// These hooks are used by animate to expand properties +jQuery.each( { + margin: "", + padding: "", + border: "Width" +}, function( prefix, suffix ) { + jQuery.cssHooks[ prefix + suffix ] = { + expand: function( value ) { + var i = 0, + expanded = {}, + + // Assumes a single number if not a string + parts = typeof value === "string" ? value.split( " " ) : [ value ]; + + for ( ; i < 4; i++ ) { + expanded[ prefix + cssExpand[ i ] + suffix ] = + parts[ i ] || parts[ i - 2 ] || parts[ 0 ]; + } + + return expanded; + } + }; + + if ( prefix !== "margin" ) { + jQuery.cssHooks[ prefix + suffix ].set = setPositiveNumber; + } +} ); + +jQuery.fn.extend( { + css: function( name, value ) { + return access( this, function( elem, name, value ) { + var styles, len, + map = {}, + i = 0; + + if ( Array.isArray( name ) ) { + styles = getStyles( elem ); + len = name.length; + + for ( ; i < len; i++ ) { + map[ name[ i ] ] = jQuery.css( elem, name[ i ], false, styles ); + } + + return map; + } + + return value !== undefined ? + jQuery.style( elem, name, value ) : + jQuery.css( elem, name ); + }, name, value, arguments.length > 1 ); + } +} ); + + +function Tween( elem, options, prop, end, easing ) { + return new Tween.prototype.init( elem, options, prop, end, easing ); +} +jQuery.Tween = Tween; + +Tween.prototype = { + constructor: Tween, + init: function( elem, options, prop, end, easing, unit ) { + this.elem = elem; + this.prop = prop; + this.easing = easing || jQuery.easing._default; + this.options = options; + this.start = this.now = this.cur(); + this.end = end; + this.unit = unit || ( jQuery.cssNumber[ prop ] ? "" : "px" ); + }, + cur: function() { + var hooks = Tween.propHooks[ this.prop ]; + + return hooks && hooks.get ? + hooks.get( this ) : + Tween.propHooks._default.get( this ); + }, + run: function( percent ) { + var eased, + hooks = Tween.propHooks[ this.prop ]; + + if ( this.options.duration ) { + this.pos = eased = jQuery.easing[ this.easing ]( + percent, this.options.duration * percent, 0, 1, this.options.duration + ); + } else { + this.pos = eased = percent; + } + this.now = ( this.end - this.start ) * eased + this.start; + + if ( this.options.step ) { + this.options.step.call( this.elem, this.now, this ); + } + + if ( hooks && hooks.set ) { + hooks.set( this ); + } else { + Tween.propHooks._default.set( this ); + } + return this; + } +}; + +Tween.prototype.init.prototype = Tween.prototype; + +Tween.propHooks = { + _default: { + get: function( tween ) { + var result; + + // Use a property on the element directly when it is not a DOM element, + // or when there is no matching style property that exists. + if ( tween.elem.nodeType !== 1 || + tween.elem[ tween.prop ] != null && tween.elem.style[ tween.prop ] == null ) { + return tween.elem[ tween.prop ]; + } + + // Passing an empty string as a 3rd parameter to .css will automatically + // attempt a parseFloat and fallback to a string if the parse fails. + // Simple values such as "10px" are parsed to Float; + // complex values such as "rotate(1rad)" are returned as-is. + result = jQuery.css( tween.elem, tween.prop, "" ); + + // Empty strings, null, undefined and "auto" are converted to 0. + return !result || result === "auto" ? 0 : result; + }, + set: function( tween ) { + + // Use step hook for back compat. + // Use cssHook if its there. + // Use .style if available and use plain properties where available. + if ( jQuery.fx.step[ tween.prop ] ) { + jQuery.fx.step[ tween.prop ]( tween ); + } else if ( tween.elem.nodeType === 1 && ( + jQuery.cssHooks[ tween.prop ] || + tween.elem.style[ finalPropName( tween.prop ) ] != null ) ) { + jQuery.style( tween.elem, tween.prop, tween.now + tween.unit ); + } else { + tween.elem[ tween.prop ] = tween.now; + } + } + } +}; + +// Support: IE <=9 only +// Panic based approach to setting things on disconnected nodes +Tween.propHooks.scrollTop = Tween.propHooks.scrollLeft = { + set: function( tween ) { + if ( tween.elem.nodeType && tween.elem.parentNode ) { + tween.elem[ tween.prop ] = tween.now; + } + } +}; + +jQuery.easing = { + linear: function( p ) { + return p; + }, + swing: function( p ) { + return 0.5 - Math.cos( p * Math.PI ) / 2; + }, + _default: "swing" +}; + +jQuery.fx = Tween.prototype.init; + +// Back compat <1.8 extension point +jQuery.fx.step = {}; + + + + +var + fxNow, inProgress, + rfxtypes = /^(?:toggle|show|hide)$/, + rrun = /queueHooks$/; + +function schedule() { + if ( inProgress ) { + if ( document.hidden === false && window.requestAnimationFrame ) { + window.requestAnimationFrame( schedule ); + } else { + window.setTimeout( schedule, jQuery.fx.interval ); + } + + jQuery.fx.tick(); + } +} + +// Animations created synchronously will run synchronously +function createFxNow() { + window.setTimeout( function() { + fxNow = undefined; + } ); + return ( fxNow = Date.now() ); +} + +// Generate parameters to create a standard animation +function genFx( type, includeWidth ) { + var which, + i = 0, + attrs = { height: type }; + + // If we include width, step value is 1 to do all cssExpand values, + // otherwise step value is 2 to skip over Left and Right + includeWidth = includeWidth ? 1 : 0; + for ( ; i < 4; i += 2 - includeWidth ) { + which = cssExpand[ i ]; + attrs[ "margin" + which ] = attrs[ "padding" + which ] = type; + } + + if ( includeWidth ) { + attrs.opacity = attrs.width = type; + } + + return attrs; +} + +function createTween( value, prop, animation ) { + var tween, + collection = ( Animation.tweeners[ prop ] || [] ).concat( Animation.tweeners[ "*" ] ), + index = 0, + length = collection.length; + for ( ; index < length; index++ ) { + if ( ( tween = collection[ index ].call( animation, prop, value ) ) ) { + + // We're done with this property + return tween; + } + } +} + +function defaultPrefilter( elem, props, opts ) { + var prop, value, toggle, hooks, oldfire, propTween, restoreDisplay, display, + isBox = "width" in props || "height" in props, + anim = this, + orig = {}, + style = elem.style, + hidden = elem.nodeType && isHiddenWithinTree( elem ), + dataShow = dataPriv.get( elem, "fxshow" ); + + // Queue-skipping animations hijack the fx hooks + if ( !opts.queue ) { + hooks = jQuery._queueHooks( elem, "fx" ); + if ( hooks.unqueued == null ) { + hooks.unqueued = 0; + oldfire = hooks.empty.fire; + hooks.empty.fire = function() { + if ( !hooks.unqueued ) { + oldfire(); + } + }; + } + hooks.unqueued++; + + anim.always( function() { + + // Ensure the complete handler is called before this completes + anim.always( function() { + hooks.unqueued--; + if ( !jQuery.queue( elem, "fx" ).length ) { + hooks.empty.fire(); + } + } ); + } ); + } + + // Detect show/hide animations + for ( prop in props ) { + value = props[ prop ]; + if ( rfxtypes.test( value ) ) { + delete props[ prop ]; + toggle = toggle || value === "toggle"; + if ( value === ( hidden ? "hide" : "show" ) ) { + + // Pretend to be hidden if this is a "show" and + // there is still data from a stopped show/hide + if ( value === "show" && dataShow && dataShow[ prop ] !== undefined ) { + hidden = true; + + // Ignore all other no-op show/hide data + } else { + continue; + } + } + orig[ prop ] = dataShow && dataShow[ prop ] || jQuery.style( elem, prop ); + } + } + + // Bail out if this is a no-op like .hide().hide() + propTween = !jQuery.isEmptyObject( props ); + if ( !propTween && jQuery.isEmptyObject( orig ) ) { + return; + } + + // Restrict "overflow" and "display" styles during box animations + if ( isBox && elem.nodeType === 1 ) { + + // Support: IE <=9 - 11, Edge 12 - 15 + // Record all 3 overflow attributes because IE does not infer the shorthand + // from identically-valued overflowX and overflowY and Edge just mirrors + // the overflowX value there. + opts.overflow = [ style.overflow, style.overflowX, style.overflowY ]; + + // Identify a display type, preferring old show/hide data over the CSS cascade + restoreDisplay = dataShow && dataShow.display; + if ( restoreDisplay == null ) { + restoreDisplay = dataPriv.get( elem, "display" ); + } + display = jQuery.css( elem, "display" ); + if ( display === "none" ) { + if ( restoreDisplay ) { + display = restoreDisplay; + } else { + + // Get nonempty value(s) by temporarily forcing visibility + showHide( [ elem ], true ); + restoreDisplay = elem.style.display || restoreDisplay; + display = jQuery.css( elem, "display" ); + showHide( [ elem ] ); + } + } + + // Animate inline elements as inline-block + if ( display === "inline" || display === "inline-block" && restoreDisplay != null ) { + if ( jQuery.css( elem, "float" ) === "none" ) { + + // Restore the original display value at the end of pure show/hide animations + if ( !propTween ) { + anim.done( function() { + style.display = restoreDisplay; + } ); + if ( restoreDisplay == null ) { + display = style.display; + restoreDisplay = display === "none" ? "" : display; + } + } + style.display = "inline-block"; + } + } + } + + if ( opts.overflow ) { + style.overflow = "hidden"; + anim.always( function() { + style.overflow = opts.overflow[ 0 ]; + style.overflowX = opts.overflow[ 1 ]; + style.overflowY = opts.overflow[ 2 ]; + } ); + } + + // Implement show/hide animations + propTween = false; + for ( prop in orig ) { + + // General show/hide setup for this element animation + if ( !propTween ) { + if ( dataShow ) { + if ( "hidden" in dataShow ) { + hidden = dataShow.hidden; + } + } else { + dataShow = dataPriv.access( elem, "fxshow", { display: restoreDisplay } ); + } + + // Store hidden/visible for toggle so `.stop().toggle()` "reverses" + if ( toggle ) { + dataShow.hidden = !hidden; + } + + // Show elements before animating them + if ( hidden ) { + showHide( [ elem ], true ); + } + + /* eslint-disable no-loop-func */ + + anim.done( function() { + + /* eslint-enable no-loop-func */ + + // The final step of a "hide" animation is actually hiding the element + if ( !hidden ) { + showHide( [ elem ] ); + } + dataPriv.remove( elem, "fxshow" ); + for ( prop in orig ) { + jQuery.style( elem, prop, orig[ prop ] ); + } + } ); + } + + // Per-property setup + propTween = createTween( hidden ? dataShow[ prop ] : 0, prop, anim ); + if ( !( prop in dataShow ) ) { + dataShow[ prop ] = propTween.start; + if ( hidden ) { + propTween.end = propTween.start; + propTween.start = 0; + } + } + } +} + +function propFilter( props, specialEasing ) { + var index, name, easing, value, hooks; + + // camelCase, specialEasing and expand cssHook pass + for ( index in props ) { + name = camelCase( index ); + easing = specialEasing[ name ]; + value = props[ index ]; + if ( Array.isArray( value ) ) { + easing = value[ 1 ]; + value = props[ index ] = value[ 0 ]; + } + + if ( index !== name ) { + props[ name ] = value; + delete props[ index ]; + } + + hooks = jQuery.cssHooks[ name ]; + if ( hooks && "expand" in hooks ) { + value = hooks.expand( value ); + delete props[ name ]; + + // Not quite $.extend, this won't overwrite existing keys. + // Reusing 'index' because we have the correct "name" + for ( index in value ) { + if ( !( index in props ) ) { + props[ index ] = value[ index ]; + specialEasing[ index ] = easing; + } + } + } else { + specialEasing[ name ] = easing; + } + } +} + +function Animation( elem, properties, options ) { + var result, + stopped, + index = 0, + length = Animation.prefilters.length, + deferred = jQuery.Deferred().always( function() { + + // Don't match elem in the :animated selector + delete tick.elem; + } ), + tick = function() { + if ( stopped ) { + return false; + } + var currentTime = fxNow || createFxNow(), + remaining = Math.max( 0, animation.startTime + animation.duration - currentTime ), + + // Support: Android 2.3 only + // Archaic crash bug won't allow us to use `1 - ( 0.5 || 0 )` (#12497) + temp = remaining / animation.duration || 0, + percent = 1 - temp, + index = 0, + length = animation.tweens.length; + + for ( ; index < length; index++ ) { + animation.tweens[ index ].run( percent ); + } + + deferred.notifyWith( elem, [ animation, percent, remaining ] ); + + // If there's more to do, yield + if ( percent < 1 && length ) { + return remaining; + } + + // If this was an empty animation, synthesize a final progress notification + if ( !length ) { + deferred.notifyWith( elem, [ animation, 1, 0 ] ); + } + + // Resolve the animation and report its conclusion + deferred.resolveWith( elem, [ animation ] ); + return false; + }, + animation = deferred.promise( { + elem: elem, + props: jQuery.extend( {}, properties ), + opts: jQuery.extend( true, { + specialEasing: {}, + easing: jQuery.easing._default + }, options ), + originalProperties: properties, + originalOptions: options, + startTime: fxNow || createFxNow(), + duration: options.duration, + tweens: [], + createTween: function( prop, end ) { + var tween = jQuery.Tween( elem, animation.opts, prop, end, + animation.opts.specialEasing[ prop ] || animation.opts.easing ); + animation.tweens.push( tween ); + return tween; + }, + stop: function( gotoEnd ) { + var index = 0, + + // If we are going to the end, we want to run all the tweens + // otherwise we skip this part + length = gotoEnd ? animation.tweens.length : 0; + if ( stopped ) { + return this; + } + stopped = true; + for ( ; index < length; index++ ) { + animation.tweens[ index ].run( 1 ); + } + + // Resolve when we played the last frame; otherwise, reject + if ( gotoEnd ) { + deferred.notifyWith( elem, [ animation, 1, 0 ] ); + deferred.resolveWith( elem, [ animation, gotoEnd ] ); + } else { + deferred.rejectWith( elem, [ animation, gotoEnd ] ); + } + return this; + } + } ), + props = animation.props; + + propFilter( props, animation.opts.specialEasing ); + + for ( ; index < length; index++ ) { + result = Animation.prefilters[ index ].call( animation, elem, props, animation.opts ); + if ( result ) { + if ( isFunction( result.stop ) ) { + jQuery._queueHooks( animation.elem, animation.opts.queue ).stop = + result.stop.bind( result ); + } + return result; + } + } + + jQuery.map( props, createTween, animation ); + + if ( isFunction( animation.opts.start ) ) { + animation.opts.start.call( elem, animation ); + } + + // Attach callbacks from options + animation + .progress( animation.opts.progress ) + .done( animation.opts.done, animation.opts.complete ) + .fail( animation.opts.fail ) + .always( animation.opts.always ); + + jQuery.fx.timer( + jQuery.extend( tick, { + elem: elem, + anim: animation, + queue: animation.opts.queue + } ) + ); + + return animation; +} + +jQuery.Animation = jQuery.extend( Animation, { + + tweeners: { + "*": [ function( prop, value ) { + var tween = this.createTween( prop, value ); + adjustCSS( tween.elem, prop, rcssNum.exec( value ), tween ); + return tween; + } ] + }, + + tweener: function( props, callback ) { + if ( isFunction( props ) ) { + callback = props; + props = [ "*" ]; + } else { + props = props.match( rnothtmlwhite ); + } + + var prop, + index = 0, + length = props.length; + + for ( ; index < length; index++ ) { + prop = props[ index ]; + Animation.tweeners[ prop ] = Animation.tweeners[ prop ] || []; + Animation.tweeners[ prop ].unshift( callback ); + } + }, + + prefilters: [ defaultPrefilter ], + + prefilter: function( callback, prepend ) { + if ( prepend ) { + Animation.prefilters.unshift( callback ); + } else { + Animation.prefilters.push( callback ); + } + } +} ); + +jQuery.speed = function( speed, easing, fn ) { + var opt = speed && typeof speed === "object" ? jQuery.extend( {}, speed ) : { + complete: fn || !fn && easing || + isFunction( speed ) && speed, + duration: speed, + easing: fn && easing || easing && !isFunction( easing ) && easing + }; + + // Go to the end state if fx are off + if ( jQuery.fx.off ) { + opt.duration = 0; + + } else { + if ( typeof opt.duration !== "number" ) { + if ( opt.duration in jQuery.fx.speeds ) { + opt.duration = jQuery.fx.speeds[ opt.duration ]; + + } else { + opt.duration = jQuery.fx.speeds._default; + } + } + } + + // Normalize opt.queue - true/undefined/null -> "fx" + if ( opt.queue == null || opt.queue === true ) { + opt.queue = "fx"; + } + + // Queueing + opt.old = opt.complete; + + opt.complete = function() { + if ( isFunction( opt.old ) ) { + opt.old.call( this ); + } + + if ( opt.queue ) { + jQuery.dequeue( this, opt.queue ); + } + }; + + return opt; +}; + +jQuery.fn.extend( { + fadeTo: function( speed, to, easing, callback ) { + + // Show any hidden elements after setting opacity to 0 + return this.filter( isHiddenWithinTree ).css( "opacity", 0 ).show() + + // Animate to the value specified + .end().animate( { opacity: to }, speed, easing, callback ); + }, + animate: function( prop, speed, easing, callback ) { + var empty = jQuery.isEmptyObject( prop ), + optall = jQuery.speed( speed, easing, callback ), + doAnimation = function() { + + // Operate on a copy of prop so per-property easing won't be lost + var anim = Animation( this, jQuery.extend( {}, prop ), optall ); + + // Empty animations, or finishing resolves immediately + if ( empty || dataPriv.get( this, "finish" ) ) { + anim.stop( true ); + } + }; + + doAnimation.finish = doAnimation; + + return empty || optall.queue === false ? + this.each( doAnimation ) : + this.queue( optall.queue, doAnimation ); + }, + stop: function( type, clearQueue, gotoEnd ) { + var stopQueue = function( hooks ) { + var stop = hooks.stop; + delete hooks.stop; + stop( gotoEnd ); + }; + + if ( typeof type !== "string" ) { + gotoEnd = clearQueue; + clearQueue = type; + type = undefined; + } + if ( clearQueue ) { + this.queue( type || "fx", [] ); + } + + return this.each( function() { + var dequeue = true, + index = type != null && type + "queueHooks", + timers = jQuery.timers, + data = dataPriv.get( this ); + + if ( index ) { + if ( data[ index ] && data[ index ].stop ) { + stopQueue( data[ index ] ); + } + } else { + for ( index in data ) { + if ( data[ index ] && data[ index ].stop && rrun.test( index ) ) { + stopQueue( data[ index ] ); + } + } + } + + for ( index = timers.length; index--; ) { + if ( timers[ index ].elem === this && + ( type == null || timers[ index ].queue === type ) ) { + + timers[ index ].anim.stop( gotoEnd ); + dequeue = false; + timers.splice( index, 1 ); + } + } + + // Start the next in the queue if the last step wasn't forced. + // Timers currently will call their complete callbacks, which + // will dequeue but only if they were gotoEnd. + if ( dequeue || !gotoEnd ) { + jQuery.dequeue( this, type ); + } + } ); + }, + finish: function( type ) { + if ( type !== false ) { + type = type || "fx"; + } + return this.each( function() { + var index, + data = dataPriv.get( this ), + queue = data[ type + "queue" ], + hooks = data[ type + "queueHooks" ], + timers = jQuery.timers, + length = queue ? queue.length : 0; + + // Enable finishing flag on private data + data.finish = true; + + // Empty the queue first + jQuery.queue( this, type, [] ); + + if ( hooks && hooks.stop ) { + hooks.stop.call( this, true ); + } + + // Look for any active animations, and finish them + for ( index = timers.length; index--; ) { + if ( timers[ index ].elem === this && timers[ index ].queue === type ) { + timers[ index ].anim.stop( true ); + timers.splice( index, 1 ); + } + } + + // Look for any animations in the old queue and finish them + for ( index = 0; index < length; index++ ) { + if ( queue[ index ] && queue[ index ].finish ) { + queue[ index ].finish.call( this ); + } + } + + // Turn off finishing flag + delete data.finish; + } ); + } +} ); + +jQuery.each( [ "toggle", "show", "hide" ], function( _i, name ) { + var cssFn = jQuery.fn[ name ]; + jQuery.fn[ name ] = function( speed, easing, callback ) { + return speed == null || typeof speed === "boolean" ? + cssFn.apply( this, arguments ) : + this.animate( genFx( name, true ), speed, easing, callback ); + }; +} ); + +// Generate shortcuts for custom animations +jQuery.each( { + slideDown: genFx( "show" ), + slideUp: genFx( "hide" ), + slideToggle: genFx( "toggle" ), + fadeIn: { opacity: "show" }, + fadeOut: { opacity: "hide" }, + fadeToggle: { opacity: "toggle" } +}, function( name, props ) { + jQuery.fn[ name ] = function( speed, easing, callback ) { + return this.animate( props, speed, easing, callback ); + }; +} ); + +jQuery.timers = []; +jQuery.fx.tick = function() { + var timer, + i = 0, + timers = jQuery.timers; + + fxNow = Date.now(); + + for ( ; i < timers.length; i++ ) { + timer = timers[ i ]; + + // Run the timer and safely remove it when done (allowing for external removal) + if ( !timer() && timers[ i ] === timer ) { + timers.splice( i--, 1 ); + } + } + + if ( !timers.length ) { + jQuery.fx.stop(); + } + fxNow = undefined; +}; + +jQuery.fx.timer = function( timer ) { + jQuery.timers.push( timer ); + jQuery.fx.start(); +}; + +jQuery.fx.interval = 13; +jQuery.fx.start = function() { + if ( inProgress ) { + return; + } + + inProgress = true; + schedule(); +}; + +jQuery.fx.stop = function() { + inProgress = null; +}; + +jQuery.fx.speeds = { + slow: 600, + fast: 200, + + // Default speed + _default: 400 +}; + + +// Based off of the plugin by Clint Helfers, with permission. +// https://web.archive.org/web/20100324014747/http://blindsignals.com/index.php/2009/07/jquery-delay/ +jQuery.fn.delay = function( time, type ) { + time = jQuery.fx ? jQuery.fx.speeds[ time ] || time : time; + type = type || "fx"; + + return this.queue( type, function( next, hooks ) { + var timeout = window.setTimeout( next, time ); + hooks.stop = function() { + window.clearTimeout( timeout ); + }; + } ); +}; + + +( function() { + var input = document.createElement( "input" ), + select = document.createElement( "select" ), + opt = select.appendChild( document.createElement( "option" ) ); + + input.type = "checkbox"; + + // Support: Android <=4.3 only + // Default value for a checkbox should be "on" + support.checkOn = input.value !== ""; + + // Support: IE <=11 only + // Must access selectedIndex to make default options select + support.optSelected = opt.selected; + + // Support: IE <=11 only + // An input loses its value after becoming a radio + input = document.createElement( "input" ); + input.value = "t"; + input.type = "radio"; + support.radioValue = input.value === "t"; +} )(); + + +var boolHook, + attrHandle = jQuery.expr.attrHandle; + +jQuery.fn.extend( { + attr: function( name, value ) { + return access( this, jQuery.attr, name, value, arguments.length > 1 ); + }, + + removeAttr: function( name ) { + return this.each( function() { + jQuery.removeAttr( this, name ); + } ); + } +} ); + +jQuery.extend( { + attr: function( elem, name, value ) { + var ret, hooks, + nType = elem.nodeType; + + // Don't get/set attributes on text, comment and attribute nodes + if ( nType === 3 || nType === 8 || nType === 2 ) { + return; + } + + // Fallback to prop when attributes are not supported + if ( typeof elem.getAttribute === "undefined" ) { + return jQuery.prop( elem, name, value ); + } + + // Attribute hooks are determined by the lowercase version + // Grab necessary hook if one is defined + if ( nType !== 1 || !jQuery.isXMLDoc( elem ) ) { + hooks = jQuery.attrHooks[ name.toLowerCase() ] || + ( jQuery.expr.match.bool.test( name ) ? boolHook : undefined ); + } + + if ( value !== undefined ) { + if ( value === null ) { + jQuery.removeAttr( elem, name ); + return; + } + + if ( hooks && "set" in hooks && + ( ret = hooks.set( elem, value, name ) ) !== undefined ) { + return ret; + } + + elem.setAttribute( name, value + "" ); + return value; + } + + if ( hooks && "get" in hooks && ( ret = hooks.get( elem, name ) ) !== null ) { + return ret; + } + + ret = jQuery.find.attr( elem, name ); + + // Non-existent attributes return null, we normalize to undefined + return ret == null ? undefined : ret; + }, + + attrHooks: { + type: { + set: function( elem, value ) { + if ( !support.radioValue && value === "radio" && + nodeName( elem, "input" ) ) { + var val = elem.value; + elem.setAttribute( "type", value ); + if ( val ) { + elem.value = val; + } + return value; + } + } + } + }, + + removeAttr: function( elem, value ) { + var name, + i = 0, + + // Attribute names can contain non-HTML whitespace characters + // https://html.spec.whatwg.org/multipage/syntax.html#attributes-2 + attrNames = value && value.match( rnothtmlwhite ); + + if ( attrNames && elem.nodeType === 1 ) { + while ( ( name = attrNames[ i++ ] ) ) { + elem.removeAttribute( name ); + } + } + } +} ); + +// Hooks for boolean attributes +boolHook = { + set: function( elem, value, name ) { + if ( value === false ) { + + // Remove boolean attributes when set to false + jQuery.removeAttr( elem, name ); + } else { + elem.setAttribute( name, name ); + } + return name; + } +}; + +jQuery.each( jQuery.expr.match.bool.source.match( /\w+/g ), function( _i, name ) { + var getter = attrHandle[ name ] || jQuery.find.attr; + + attrHandle[ name ] = function( elem, name, isXML ) { + var ret, handle, + lowercaseName = name.toLowerCase(); + + if ( !isXML ) { + + // Avoid an infinite loop by temporarily removing this function from the getter + handle = attrHandle[ lowercaseName ]; + attrHandle[ lowercaseName ] = ret; + ret = getter( elem, name, isXML ) != null ? + lowercaseName : + null; + attrHandle[ lowercaseName ] = handle; + } + return ret; + }; +} ); + + + + +var rfocusable = /^(?:input|select|textarea|button)$/i, + rclickable = /^(?:a|area)$/i; + +jQuery.fn.extend( { + prop: function( name, value ) { + return access( this, jQuery.prop, name, value, arguments.length > 1 ); + }, + + removeProp: function( name ) { + return this.each( function() { + delete this[ jQuery.propFix[ name ] || name ]; + } ); + } +} ); + +jQuery.extend( { + prop: function( elem, name, value ) { + var ret, hooks, + nType = elem.nodeType; + + // Don't get/set properties on text, comment and attribute nodes + if ( nType === 3 || nType === 8 || nType === 2 ) { + return; + } + + if ( nType !== 1 || !jQuery.isXMLDoc( elem ) ) { + + // Fix name and attach hooks + name = jQuery.propFix[ name ] || name; + hooks = jQuery.propHooks[ name ]; + } + + if ( value !== undefined ) { + if ( hooks && "set" in hooks && + ( ret = hooks.set( elem, value, name ) ) !== undefined ) { + return ret; + } + + return ( elem[ name ] = value ); + } + + if ( hooks && "get" in hooks && ( ret = hooks.get( elem, name ) ) !== null ) { + return ret; + } + + return elem[ name ]; + }, + + propHooks: { + tabIndex: { + get: function( elem ) { + + // Support: IE <=9 - 11 only + // elem.tabIndex doesn't always return the + // correct value when it hasn't been explicitly set + // https://web.archive.org/web/20141116233347/http://fluidproject.org/blog/2008/01/09/getting-setting-and-removing-tabindex-values-with-javascript/ + // Use proper attribute retrieval(#12072) + var tabindex = jQuery.find.attr( elem, "tabindex" ); + + if ( tabindex ) { + return parseInt( tabindex, 10 ); + } + + if ( + rfocusable.test( elem.nodeName ) || + rclickable.test( elem.nodeName ) && + elem.href + ) { + return 0; + } + + return -1; + } + } + }, + + propFix: { + "for": "htmlFor", + "class": "className" + } +} ); + +// Support: IE <=11 only +// Accessing the selectedIndex property +// forces the browser to respect setting selected +// on the option +// The getter ensures a default option is selected +// when in an optgroup +// eslint rule "no-unused-expressions" is disabled for this code +// since it considers such accessions noop +if ( !support.optSelected ) { + jQuery.propHooks.selected = { + get: function( elem ) { + + /* eslint no-unused-expressions: "off" */ + + var parent = elem.parentNode; + if ( parent && parent.parentNode ) { + parent.parentNode.selectedIndex; + } + return null; + }, + set: function( elem ) { + + /* eslint no-unused-expressions: "off" */ + + var parent = elem.parentNode; + if ( parent ) { + parent.selectedIndex; + + if ( parent.parentNode ) { + parent.parentNode.selectedIndex; + } + } + } + }; +} + +jQuery.each( [ + "tabIndex", + "readOnly", + "maxLength", + "cellSpacing", + "cellPadding", + "rowSpan", + "colSpan", + "useMap", + "frameBorder", + "contentEditable" +], function() { + jQuery.propFix[ this.toLowerCase() ] = this; +} ); + + + + + // Strip and collapse whitespace according to HTML spec + // https://infra.spec.whatwg.org/#strip-and-collapse-ascii-whitespace + function stripAndCollapse( value ) { + var tokens = value.match( rnothtmlwhite ) || []; + return tokens.join( " " ); + } + + +function getClass( elem ) { + return elem.getAttribute && elem.getAttribute( "class" ) || ""; +} + +function classesToArray( value ) { + if ( Array.isArray( value ) ) { + return value; + } + if ( typeof value === "string" ) { + return value.match( rnothtmlwhite ) || []; + } + return []; +} + +jQuery.fn.extend( { + addClass: function( value ) { + var classes, elem, cur, curValue, clazz, j, finalValue, + i = 0; + + if ( isFunction( value ) ) { + return this.each( function( j ) { + jQuery( this ).addClass( value.call( this, j, getClass( this ) ) ); + } ); + } + + classes = classesToArray( value ); + + if ( classes.length ) { + while ( ( elem = this[ i++ ] ) ) { + curValue = getClass( elem ); + cur = elem.nodeType === 1 && ( " " + stripAndCollapse( curValue ) + " " ); + + if ( cur ) { + j = 0; + while ( ( clazz = classes[ j++ ] ) ) { + if ( cur.indexOf( " " + clazz + " " ) < 0 ) { + cur += clazz + " "; + } + } + + // Only assign if different to avoid unneeded rendering. + finalValue = stripAndCollapse( cur ); + if ( curValue !== finalValue ) { + elem.setAttribute( "class", finalValue ); + } + } + } + } + + return this; + }, + + removeClass: function( value ) { + var classes, elem, cur, curValue, clazz, j, finalValue, + i = 0; + + if ( isFunction( value ) ) { + return this.each( function( j ) { + jQuery( this ).removeClass( value.call( this, j, getClass( this ) ) ); + } ); + } + + if ( !arguments.length ) { + return this.attr( "class", "" ); + } + + classes = classesToArray( value ); + + if ( classes.length ) { + while ( ( elem = this[ i++ ] ) ) { + curValue = getClass( elem ); + + // This expression is here for better compressibility (see addClass) + cur = elem.nodeType === 1 && ( " " + stripAndCollapse( curValue ) + " " ); + + if ( cur ) { + j = 0; + while ( ( clazz = classes[ j++ ] ) ) { + + // Remove *all* instances + while ( cur.indexOf( " " + clazz + " " ) > -1 ) { + cur = cur.replace( " " + clazz + " ", " " ); + } + } + + // Only assign if different to avoid unneeded rendering. + finalValue = stripAndCollapse( cur ); + if ( curValue !== finalValue ) { + elem.setAttribute( "class", finalValue ); + } + } + } + } + + return this; + }, + + toggleClass: function( value, stateVal ) { + var type = typeof value, + isValidValue = type === "string" || Array.isArray( value ); + + if ( typeof stateVal === "boolean" && isValidValue ) { + return stateVal ? this.addClass( value ) : this.removeClass( value ); + } + + if ( isFunction( value ) ) { + return this.each( function( i ) { + jQuery( this ).toggleClass( + value.call( this, i, getClass( this ), stateVal ), + stateVal + ); + } ); + } + + return this.each( function() { + var className, i, self, classNames; + + if ( isValidValue ) { + + // Toggle individual class names + i = 0; + self = jQuery( this ); + classNames = classesToArray( value ); + + while ( ( className = classNames[ i++ ] ) ) { + + // Check each className given, space separated list + if ( self.hasClass( className ) ) { + self.removeClass( className ); + } else { + self.addClass( className ); + } + } + + // Toggle whole class name + } else if ( value === undefined || type === "boolean" ) { + className = getClass( this ); + if ( className ) { + + // Store className if set + dataPriv.set( this, "__className__", className ); + } + + // If the element has a class name or if we're passed `false`, + // then remove the whole classname (if there was one, the above saved it). + // Otherwise bring back whatever was previously saved (if anything), + // falling back to the empty string if nothing was stored. + if ( this.setAttribute ) { + this.setAttribute( "class", + className || value === false ? + "" : + dataPriv.get( this, "__className__" ) || "" + ); + } + } + } ); + }, + + hasClass: function( selector ) { + var className, elem, + i = 0; + + className = " " + selector + " "; + while ( ( elem = this[ i++ ] ) ) { + if ( elem.nodeType === 1 && + ( " " + stripAndCollapse( getClass( elem ) ) + " " ).indexOf( className ) > -1 ) { + return true; + } + } + + return false; + } +} ); + + + + +var rreturn = /\r/g; + +jQuery.fn.extend( { + val: function( value ) { + var hooks, ret, valueIsFunction, + elem = this[ 0 ]; + + if ( !arguments.length ) { + if ( elem ) { + hooks = jQuery.valHooks[ elem.type ] || + jQuery.valHooks[ elem.nodeName.toLowerCase() ]; + + if ( hooks && + "get" in hooks && + ( ret = hooks.get( elem, "value" ) ) !== undefined + ) { + return ret; + } + + ret = elem.value; + + // Handle most common string cases + if ( typeof ret === "string" ) { + return ret.replace( rreturn, "" ); + } + + // Handle cases where value is null/undef or number + return ret == null ? "" : ret; + } + + return; + } + + valueIsFunction = isFunction( value ); + + return this.each( function( i ) { + var val; + + if ( this.nodeType !== 1 ) { + return; + } + + if ( valueIsFunction ) { + val = value.call( this, i, jQuery( this ).val() ); + } else { + val = value; + } + + // Treat null/undefined as ""; convert numbers to string + if ( val == null ) { + val = ""; + + } else if ( typeof val === "number" ) { + val += ""; + + } else if ( Array.isArray( val ) ) { + val = jQuery.map( val, function( value ) { + return value == null ? "" : value + ""; + } ); + } + + hooks = jQuery.valHooks[ this.type ] || jQuery.valHooks[ this.nodeName.toLowerCase() ]; + + // If set returns undefined, fall back to normal setting + if ( !hooks || !( "set" in hooks ) || hooks.set( this, val, "value" ) === undefined ) { + this.value = val; + } + } ); + } +} ); + +jQuery.extend( { + valHooks: { + option: { + get: function( elem ) { + + var val = jQuery.find.attr( elem, "value" ); + return val != null ? + val : + + // Support: IE <=10 - 11 only + // option.text throws exceptions (#14686, #14858) + // Strip and collapse whitespace + // https://html.spec.whatwg.org/#strip-and-collapse-whitespace + stripAndCollapse( jQuery.text( elem ) ); + } + }, + select: { + get: function( elem ) { + var value, option, i, + options = elem.options, + index = elem.selectedIndex, + one = elem.type === "select-one", + values = one ? null : [], + max = one ? index + 1 : options.length; + + if ( index < 0 ) { + i = max; + + } else { + i = one ? index : 0; + } + + // Loop through all the selected options + for ( ; i < max; i++ ) { + option = options[ i ]; + + // Support: IE <=9 only + // IE8-9 doesn't update selected after form reset (#2551) + if ( ( option.selected || i === index ) && + + // Don't return options that are disabled or in a disabled optgroup + !option.disabled && + ( !option.parentNode.disabled || + !nodeName( option.parentNode, "optgroup" ) ) ) { + + // Get the specific value for the option + value = jQuery( option ).val(); + + // We don't need an array for one selects + if ( one ) { + return value; + } + + // Multi-Selects return an array + values.push( value ); + } + } + + return values; + }, + + set: function( elem, value ) { + var optionSet, option, + options = elem.options, + values = jQuery.makeArray( value ), + i = options.length; + + while ( i-- ) { + option = options[ i ]; + + /* eslint-disable no-cond-assign */ + + if ( option.selected = + jQuery.inArray( jQuery.valHooks.option.get( option ), values ) > -1 + ) { + optionSet = true; + } + + /* eslint-enable no-cond-assign */ + } + + // Force browsers to behave consistently when non-matching value is set + if ( !optionSet ) { + elem.selectedIndex = -1; + } + return values; + } + } + } +} ); + +// Radios and checkboxes getter/setter +jQuery.each( [ "radio", "checkbox" ], function() { + jQuery.valHooks[ this ] = { + set: function( elem, value ) { + if ( Array.isArray( value ) ) { + return ( elem.checked = jQuery.inArray( jQuery( elem ).val(), value ) > -1 ); + } + } + }; + if ( !support.checkOn ) { + jQuery.valHooks[ this ].get = function( elem ) { + return elem.getAttribute( "value" ) === null ? "on" : elem.value; + }; + } +} ); + + + + +// Return jQuery for attributes-only inclusion + + +support.focusin = "onfocusin" in window; + + +var rfocusMorph = /^(?:focusinfocus|focusoutblur)$/, + stopPropagationCallback = function( e ) { + e.stopPropagation(); + }; + +jQuery.extend( jQuery.event, { + + trigger: function( event, data, elem, onlyHandlers ) { + + var i, cur, tmp, bubbleType, ontype, handle, special, lastElement, + eventPath = [ elem || document ], + type = hasOwn.call( event, "type" ) ? event.type : event, + namespaces = hasOwn.call( event, "namespace" ) ? event.namespace.split( "." ) : []; + + cur = lastElement = tmp = elem = elem || document; + + // Don't do events on text and comment nodes + if ( elem.nodeType === 3 || elem.nodeType === 8 ) { + return; + } + + // focus/blur morphs to focusin/out; ensure we're not firing them right now + if ( rfocusMorph.test( type + jQuery.event.triggered ) ) { + return; + } + + if ( type.indexOf( "." ) > -1 ) { + + // Namespaced trigger; create a regexp to match event type in handle() + namespaces = type.split( "." ); + type = namespaces.shift(); + namespaces.sort(); + } + ontype = type.indexOf( ":" ) < 0 && "on" + type; + + // Caller can pass in a jQuery.Event object, Object, or just an event type string + event = event[ jQuery.expando ] ? + event : + new jQuery.Event( type, typeof event === "object" && event ); + + // Trigger bitmask: & 1 for native handlers; & 2 for jQuery (always true) + event.isTrigger = onlyHandlers ? 2 : 3; + event.namespace = namespaces.join( "." ); + event.rnamespace = event.namespace ? + new RegExp( "(^|\\.)" + namespaces.join( "\\.(?:.*\\.|)" ) + "(\\.|$)" ) : + null; + + // Clean up the event in case it is being reused + event.result = undefined; + if ( !event.target ) { + event.target = elem; + } + + // Clone any incoming data and prepend the event, creating the handler arg list + data = data == null ? + [ event ] : + jQuery.makeArray( data, [ event ] ); + + // Allow special events to draw outside the lines + special = jQuery.event.special[ type ] || {}; + if ( !onlyHandlers && special.trigger && special.trigger.apply( elem, data ) === false ) { + return; + } + + // Determine event propagation path in advance, per W3C events spec (#9951) + // Bubble up to document, then to window; watch for a global ownerDocument var (#9724) + if ( !onlyHandlers && !special.noBubble && !isWindow( elem ) ) { + + bubbleType = special.delegateType || type; + if ( !rfocusMorph.test( bubbleType + type ) ) { + cur = cur.parentNode; + } + for ( ; cur; cur = cur.parentNode ) { + eventPath.push( cur ); + tmp = cur; + } + + // Only add window if we got to document (e.g., not plain obj or detached DOM) + if ( tmp === ( elem.ownerDocument || document ) ) { + eventPath.push( tmp.defaultView || tmp.parentWindow || window ); + } + } + + // Fire handlers on the event path + i = 0; + while ( ( cur = eventPath[ i++ ] ) && !event.isPropagationStopped() ) { + lastElement = cur; + event.type = i > 1 ? + bubbleType : + special.bindType || type; + + // jQuery handler + handle = ( dataPriv.get( cur, "events" ) || Object.create( null ) )[ event.type ] && + dataPriv.get( cur, "handle" ); + if ( handle ) { + handle.apply( cur, data ); + } + + // Native handler + handle = ontype && cur[ ontype ]; + if ( handle && handle.apply && acceptData( cur ) ) { + event.result = handle.apply( cur, data ); + if ( event.result === false ) { + event.preventDefault(); + } + } + } + event.type = type; + + // If nobody prevented the default action, do it now + if ( !onlyHandlers && !event.isDefaultPrevented() ) { + + if ( ( !special._default || + special._default.apply( eventPath.pop(), data ) === false ) && + acceptData( elem ) ) { + + // Call a native DOM method on the target with the same name as the event. + // Don't do default actions on window, that's where global variables be (#6170) + if ( ontype && isFunction( elem[ type ] ) && !isWindow( elem ) ) { + + // Don't re-trigger an onFOO event when we call its FOO() method + tmp = elem[ ontype ]; + + if ( tmp ) { + elem[ ontype ] = null; + } + + // Prevent re-triggering of the same event, since we already bubbled it above + jQuery.event.triggered = type; + + if ( event.isPropagationStopped() ) { + lastElement.addEventListener( type, stopPropagationCallback ); + } + + elem[ type ](); + + if ( event.isPropagationStopped() ) { + lastElement.removeEventListener( type, stopPropagationCallback ); + } + + jQuery.event.triggered = undefined; + + if ( tmp ) { + elem[ ontype ] = tmp; + } + } + } + } + + return event.result; + }, + + // Piggyback on a donor event to simulate a different one + // Used only for `focus(in | out)` events + simulate: function( type, elem, event ) { + var e = jQuery.extend( + new jQuery.Event(), + event, + { + type: type, + isSimulated: true + } + ); + + jQuery.event.trigger( e, null, elem ); + } + +} ); + +jQuery.fn.extend( { + + trigger: function( type, data ) { + return this.each( function() { + jQuery.event.trigger( type, data, this ); + } ); + }, + triggerHandler: function( type, data ) { + var elem = this[ 0 ]; + if ( elem ) { + return jQuery.event.trigger( type, data, elem, true ); + } + } +} ); + + +// Support: Firefox <=44 +// Firefox doesn't have focus(in | out) events +// Related ticket - https://bugzilla.mozilla.org/show_bug.cgi?id=687787 +// +// Support: Chrome <=48 - 49, Safari <=9.0 - 9.1 +// focus(in | out) events fire after focus & blur events, +// which is spec violation - http://www.w3.org/TR/DOM-Level-3-Events/#events-focusevent-event-order +// Related ticket - https://bugs.chromium.org/p/chromium/issues/detail?id=449857 +if ( !support.focusin ) { + jQuery.each( { focus: "focusin", blur: "focusout" }, function( orig, fix ) { + + // Attach a single capturing handler on the document while someone wants focusin/focusout + var handler = function( event ) { + jQuery.event.simulate( fix, event.target, jQuery.event.fix( event ) ); + }; + + jQuery.event.special[ fix ] = { + setup: function() { + + // Handle: regular nodes (via `this.ownerDocument`), window + // (via `this.document`) & document (via `this`). + var doc = this.ownerDocument || this.document || this, + attaches = dataPriv.access( doc, fix ); + + if ( !attaches ) { + doc.addEventListener( orig, handler, true ); + } + dataPriv.access( doc, fix, ( attaches || 0 ) + 1 ); + }, + teardown: function() { + var doc = this.ownerDocument || this.document || this, + attaches = dataPriv.access( doc, fix ) - 1; + + if ( !attaches ) { + doc.removeEventListener( orig, handler, true ); + dataPriv.remove( doc, fix ); + + } else { + dataPriv.access( doc, fix, attaches ); + } + } + }; + } ); +} +var location = window.location; + +var nonce = { guid: Date.now() }; + +var rquery = ( /\?/ ); + + + +// Cross-browser xml parsing +jQuery.parseXML = function( data ) { + var xml, parserErrorElem; + if ( !data || typeof data !== "string" ) { + return null; + } + + // Support: IE 9 - 11 only + // IE throws on parseFromString with invalid input. + try { + xml = ( new window.DOMParser() ).parseFromString( data, "text/xml" ); + } catch ( e ) {} + + parserErrorElem = xml && xml.getElementsByTagName( "parsererror" )[ 0 ]; + if ( !xml || parserErrorElem ) { + jQuery.error( "Invalid XML: " + ( + parserErrorElem ? + jQuery.map( parserErrorElem.childNodes, function( el ) { + return el.textContent; + } ).join( "\n" ) : + data + ) ); + } + return xml; +}; + + +var + rbracket = /\[\]$/, + rCRLF = /\r?\n/g, + rsubmitterTypes = /^(?:submit|button|image|reset|file)$/i, + rsubmittable = /^(?:input|select|textarea|keygen)/i; + +function buildParams( prefix, obj, traditional, add ) { + var name; + + if ( Array.isArray( obj ) ) { + + // Serialize array item. + jQuery.each( obj, function( i, v ) { + if ( traditional || rbracket.test( prefix ) ) { + + // Treat each array item as a scalar. + add( prefix, v ); + + } else { + + // Item is non-scalar (array or object), encode its numeric index. + buildParams( + prefix + "[" + ( typeof v === "object" && v != null ? i : "" ) + "]", + v, + traditional, + add + ); + } + } ); + + } else if ( !traditional && toType( obj ) === "object" ) { + + // Serialize object item. + for ( name in obj ) { + buildParams( prefix + "[" + name + "]", obj[ name ], traditional, add ); + } + + } else { + + // Serialize scalar item. + add( prefix, obj ); + } +} + +// Serialize an array of form elements or a set of +// key/values into a query string +jQuery.param = function( a, traditional ) { + var prefix, + s = [], + add = function( key, valueOrFunction ) { + + // If value is a function, invoke it and use its return value + var value = isFunction( valueOrFunction ) ? + valueOrFunction() : + valueOrFunction; + + s[ s.length ] = encodeURIComponent( key ) + "=" + + encodeURIComponent( value == null ? "" : value ); + }; + + if ( a == null ) { + return ""; + } + + // If an array was passed in, assume that it is an array of form elements. + if ( Array.isArray( a ) || ( a.jquery && !jQuery.isPlainObject( a ) ) ) { + + // Serialize the form elements + jQuery.each( a, function() { + add( this.name, this.value ); + } ); + + } else { + + // If traditional, encode the "old" way (the way 1.3.2 or older + // did it), otherwise encode params recursively. + for ( prefix in a ) { + buildParams( prefix, a[ prefix ], traditional, add ); + } + } + + // Return the resulting serialization + return s.join( "&" ); +}; + +jQuery.fn.extend( { + serialize: function() { + return jQuery.param( this.serializeArray() ); + }, + serializeArray: function() { + return this.map( function() { + + // Can add propHook for "elements" to filter or add form elements + var elements = jQuery.prop( this, "elements" ); + return elements ? jQuery.makeArray( elements ) : this; + } ).filter( function() { + var type = this.type; + + // Use .is( ":disabled" ) so that fieldset[disabled] works + return this.name && !jQuery( this ).is( ":disabled" ) && + rsubmittable.test( this.nodeName ) && !rsubmitterTypes.test( type ) && + ( this.checked || !rcheckableType.test( type ) ); + } ).map( function( _i, elem ) { + var val = jQuery( this ).val(); + + if ( val == null ) { + return null; + } + + if ( Array.isArray( val ) ) { + return jQuery.map( val, function( val ) { + return { name: elem.name, value: val.replace( rCRLF, "\r\n" ) }; + } ); + } + + return { name: elem.name, value: val.replace( rCRLF, "\r\n" ) }; + } ).get(); + } +} ); + + +var + r20 = /%20/g, + rhash = /#.*$/, + rantiCache = /([?&])_=[^&]*/, + rheaders = /^(.*?):[ \t]*([^\r\n]*)$/mg, + + // #7653, #8125, #8152: local protocol detection + rlocalProtocol = /^(?:about|app|app-storage|.+-extension|file|res|widget):$/, + rnoContent = /^(?:GET|HEAD)$/, + rprotocol = /^\/\//, + + /* Prefilters + * 1) They are useful to introduce custom dataTypes (see ajax/jsonp.js for an example) + * 2) These are called: + * - BEFORE asking for a transport + * - AFTER param serialization (s.data is a string if s.processData is true) + * 3) key is the dataType + * 4) the catchall symbol "*" can be used + * 5) execution will start with transport dataType and THEN continue down to "*" if needed + */ + prefilters = {}, + + /* Transports bindings + * 1) key is the dataType + * 2) the catchall symbol "*" can be used + * 3) selection will start with transport dataType and THEN go to "*" if needed + */ + transports = {}, + + // Avoid comment-prolog char sequence (#10098); must appease lint and evade compression + allTypes = "*/".concat( "*" ), + + // Anchor tag for parsing the document origin + originAnchor = document.createElement( "a" ); + +originAnchor.href = location.href; + +// Base "constructor" for jQuery.ajaxPrefilter and jQuery.ajaxTransport +function addToPrefiltersOrTransports( structure ) { + + // dataTypeExpression is optional and defaults to "*" + return function( dataTypeExpression, func ) { + + if ( typeof dataTypeExpression !== "string" ) { + func = dataTypeExpression; + dataTypeExpression = "*"; + } + + var dataType, + i = 0, + dataTypes = dataTypeExpression.toLowerCase().match( rnothtmlwhite ) || []; + + if ( isFunction( func ) ) { + + // For each dataType in the dataTypeExpression + while ( ( dataType = dataTypes[ i++ ] ) ) { + + // Prepend if requested + if ( dataType[ 0 ] === "+" ) { + dataType = dataType.slice( 1 ) || "*"; + ( structure[ dataType ] = structure[ dataType ] || [] ).unshift( func ); + + // Otherwise append + } else { + ( structure[ dataType ] = structure[ dataType ] || [] ).push( func ); + } + } + } + }; +} + +// Base inspection function for prefilters and transports +function inspectPrefiltersOrTransports( structure, options, originalOptions, jqXHR ) { + + var inspected = {}, + seekingTransport = ( structure === transports ); + + function inspect( dataType ) { + var selected; + inspected[ dataType ] = true; + jQuery.each( structure[ dataType ] || [], function( _, prefilterOrFactory ) { + var dataTypeOrTransport = prefilterOrFactory( options, originalOptions, jqXHR ); + if ( typeof dataTypeOrTransport === "string" && + !seekingTransport && !inspected[ dataTypeOrTransport ] ) { + + options.dataTypes.unshift( dataTypeOrTransport ); + inspect( dataTypeOrTransport ); + return false; + } else if ( seekingTransport ) { + return !( selected = dataTypeOrTransport ); + } + } ); + return selected; + } + + return inspect( options.dataTypes[ 0 ] ) || !inspected[ "*" ] && inspect( "*" ); +} + +// A special extend for ajax options +// that takes "flat" options (not to be deep extended) +// Fixes #9887 +function ajaxExtend( target, src ) { + var key, deep, + flatOptions = jQuery.ajaxSettings.flatOptions || {}; + + for ( key in src ) { + if ( src[ key ] !== undefined ) { + ( flatOptions[ key ] ? target : ( deep || ( deep = {} ) ) )[ key ] = src[ key ]; + } + } + if ( deep ) { + jQuery.extend( true, target, deep ); + } + + return target; +} + +/* Handles responses to an ajax request: + * - finds the right dataType (mediates between content-type and expected dataType) + * - returns the corresponding response + */ +function ajaxHandleResponses( s, jqXHR, responses ) { + + var ct, type, finalDataType, firstDataType, + contents = s.contents, + dataTypes = s.dataTypes; + + // Remove auto dataType and get content-type in the process + while ( dataTypes[ 0 ] === "*" ) { + dataTypes.shift(); + if ( ct === undefined ) { + ct = s.mimeType || jqXHR.getResponseHeader( "Content-Type" ); + } + } + + // Check if we're dealing with a known content-type + if ( ct ) { + for ( type in contents ) { + if ( contents[ type ] && contents[ type ].test( ct ) ) { + dataTypes.unshift( type ); + break; + } + } + } + + // Check to see if we have a response for the expected dataType + if ( dataTypes[ 0 ] in responses ) { + finalDataType = dataTypes[ 0 ]; + } else { + + // Try convertible dataTypes + for ( type in responses ) { + if ( !dataTypes[ 0 ] || s.converters[ type + " " + dataTypes[ 0 ] ] ) { + finalDataType = type; + break; + } + if ( !firstDataType ) { + firstDataType = type; + } + } + + // Or just use first one + finalDataType = finalDataType || firstDataType; + } + + // If we found a dataType + // We add the dataType to the list if needed + // and return the corresponding response + if ( finalDataType ) { + if ( finalDataType !== dataTypes[ 0 ] ) { + dataTypes.unshift( finalDataType ); + } + return responses[ finalDataType ]; + } +} + +/* Chain conversions given the request and the original response + * Also sets the responseXXX fields on the jqXHR instance + */ +function ajaxConvert( s, response, jqXHR, isSuccess ) { + var conv2, current, conv, tmp, prev, + converters = {}, + + // Work with a copy of dataTypes in case we need to modify it for conversion + dataTypes = s.dataTypes.slice(); + + // Create converters map with lowercased keys + if ( dataTypes[ 1 ] ) { + for ( conv in s.converters ) { + converters[ conv.toLowerCase() ] = s.converters[ conv ]; + } + } + + current = dataTypes.shift(); + + // Convert to each sequential dataType + while ( current ) { + + if ( s.responseFields[ current ] ) { + jqXHR[ s.responseFields[ current ] ] = response; + } + + // Apply the dataFilter if provided + if ( !prev && isSuccess && s.dataFilter ) { + response = s.dataFilter( response, s.dataType ); + } + + prev = current; + current = dataTypes.shift(); + + if ( current ) { + + // There's only work to do if current dataType is non-auto + if ( current === "*" ) { + + current = prev; + + // Convert response if prev dataType is non-auto and differs from current + } else if ( prev !== "*" && prev !== current ) { + + // Seek a direct converter + conv = converters[ prev + " " + current ] || converters[ "* " + current ]; + + // If none found, seek a pair + if ( !conv ) { + for ( conv2 in converters ) { + + // If conv2 outputs current + tmp = conv2.split( " " ); + if ( tmp[ 1 ] === current ) { + + // If prev can be converted to accepted input + conv = converters[ prev + " " + tmp[ 0 ] ] || + converters[ "* " + tmp[ 0 ] ]; + if ( conv ) { + + // Condense equivalence converters + if ( conv === true ) { + conv = converters[ conv2 ]; + + // Otherwise, insert the intermediate dataType + } else if ( converters[ conv2 ] !== true ) { + current = tmp[ 0 ]; + dataTypes.unshift( tmp[ 1 ] ); + } + break; + } + } + } + } + + // Apply converter (if not an equivalence) + if ( conv !== true ) { + + // Unless errors are allowed to bubble, catch and return them + if ( conv && s.throws ) { + response = conv( response ); + } else { + try { + response = conv( response ); + } catch ( e ) { + return { + state: "parsererror", + error: conv ? e : "No conversion from " + prev + " to " + current + }; + } + } + } + } + } + } + + return { state: "success", data: response }; +} + +jQuery.extend( { + + // Counter for holding the number of active queries + active: 0, + + // Last-Modified header cache for next request + lastModified: {}, + etag: {}, + + ajaxSettings: { + url: location.href, + type: "GET", + isLocal: rlocalProtocol.test( location.protocol ), + global: true, + processData: true, + async: true, + contentType: "application/x-www-form-urlencoded; charset=UTF-8", + + /* + timeout: 0, + data: null, + dataType: null, + username: null, + password: null, + cache: null, + throws: false, + traditional: false, + headers: {}, + */ + + accepts: { + "*": allTypes, + text: "text/plain", + html: "text/html", + xml: "application/xml, text/xml", + json: "application/json, text/javascript" + }, + + contents: { + xml: /\bxml\b/, + html: /\bhtml/, + json: /\bjson\b/ + }, + + responseFields: { + xml: "responseXML", + text: "responseText", + json: "responseJSON" + }, + + // Data converters + // Keys separate source (or catchall "*") and destination types with a single space + converters: { + + // Convert anything to text + "* text": String, + + // Text to html (true = no transformation) + "text html": true, + + // Evaluate text as a json expression + "text json": JSON.parse, + + // Parse text as xml + "text xml": jQuery.parseXML + }, + + // For options that shouldn't be deep extended: + // you can add your own custom options here if + // and when you create one that shouldn't be + // deep extended (see ajaxExtend) + flatOptions: { + url: true, + context: true + } + }, + + // Creates a full fledged settings object into target + // with both ajaxSettings and settings fields. + // If target is omitted, writes into ajaxSettings. + ajaxSetup: function( target, settings ) { + return settings ? + + // Building a settings object + ajaxExtend( ajaxExtend( target, jQuery.ajaxSettings ), settings ) : + + // Extending ajaxSettings + ajaxExtend( jQuery.ajaxSettings, target ); + }, + + ajaxPrefilter: addToPrefiltersOrTransports( prefilters ), + ajaxTransport: addToPrefiltersOrTransports( transports ), + + // Main method + ajax: function( url, options ) { + + // If url is an object, simulate pre-1.5 signature + if ( typeof url === "object" ) { + options = url; + url = undefined; + } + + // Force options to be an object + options = options || {}; + + var transport, + + // URL without anti-cache param + cacheURL, + + // Response headers + responseHeadersString, + responseHeaders, + + // timeout handle + timeoutTimer, + + // Url cleanup var + urlAnchor, + + // Request state (becomes false upon send and true upon completion) + completed, + + // To know if global events are to be dispatched + fireGlobals, + + // Loop variable + i, + + // uncached part of the url + uncached, + + // Create the final options object + s = jQuery.ajaxSetup( {}, options ), + + // Callbacks context + callbackContext = s.context || s, + + // Context for global events is callbackContext if it is a DOM node or jQuery collection + globalEventContext = s.context && + ( callbackContext.nodeType || callbackContext.jquery ) ? + jQuery( callbackContext ) : + jQuery.event, + + // Deferreds + deferred = jQuery.Deferred(), + completeDeferred = jQuery.Callbacks( "once memory" ), + + // Status-dependent callbacks + statusCode = s.statusCode || {}, + + // Headers (they are sent all at once) + requestHeaders = {}, + requestHeadersNames = {}, + + // Default abort message + strAbort = "canceled", + + // Fake xhr + jqXHR = { + readyState: 0, + + // Builds headers hashtable if needed + getResponseHeader: function( key ) { + var match; + if ( completed ) { + if ( !responseHeaders ) { + responseHeaders = {}; + while ( ( match = rheaders.exec( responseHeadersString ) ) ) { + responseHeaders[ match[ 1 ].toLowerCase() + " " ] = + ( responseHeaders[ match[ 1 ].toLowerCase() + " " ] || [] ) + .concat( match[ 2 ] ); + } + } + match = responseHeaders[ key.toLowerCase() + " " ]; + } + return match == null ? null : match.join( ", " ); + }, + + // Raw string + getAllResponseHeaders: function() { + return completed ? responseHeadersString : null; + }, + + // Caches the header + setRequestHeader: function( name, value ) { + if ( completed == null ) { + name = requestHeadersNames[ name.toLowerCase() ] = + requestHeadersNames[ name.toLowerCase() ] || name; + requestHeaders[ name ] = value; + } + return this; + }, + + // Overrides response content-type header + overrideMimeType: function( type ) { + if ( completed == null ) { + s.mimeType = type; + } + return this; + }, + + // Status-dependent callbacks + statusCode: function( map ) { + var code; + if ( map ) { + if ( completed ) { + + // Execute the appropriate callbacks + jqXHR.always( map[ jqXHR.status ] ); + } else { + + // Lazy-add the new callbacks in a way that preserves old ones + for ( code in map ) { + statusCode[ code ] = [ statusCode[ code ], map[ code ] ]; + } + } + } + return this; + }, + + // Cancel the request + abort: function( statusText ) { + var finalText = statusText || strAbort; + if ( transport ) { + transport.abort( finalText ); + } + done( 0, finalText ); + return this; + } + }; + + // Attach deferreds + deferred.promise( jqXHR ); + + // Add protocol if not provided (prefilters might expect it) + // Handle falsy url in the settings object (#10093: consistency with old signature) + // We also use the url parameter if available + s.url = ( ( url || s.url || location.href ) + "" ) + .replace( rprotocol, location.protocol + "//" ); + + // Alias method option to type as per ticket #12004 + s.type = options.method || options.type || s.method || s.type; + + // Extract dataTypes list + s.dataTypes = ( s.dataType || "*" ).toLowerCase().match( rnothtmlwhite ) || [ "" ]; + + // A cross-domain request is in order when the origin doesn't match the current origin. + if ( s.crossDomain == null ) { + urlAnchor = document.createElement( "a" ); + + // Support: IE <=8 - 11, Edge 12 - 15 + // IE throws exception on accessing the href property if url is malformed, + // e.g. http://example.com:80x/ + try { + urlAnchor.href = s.url; + + // Support: IE <=8 - 11 only + // Anchor's host property isn't correctly set when s.url is relative + urlAnchor.href = urlAnchor.href; + s.crossDomain = originAnchor.protocol + "//" + originAnchor.host !== + urlAnchor.protocol + "//" + urlAnchor.host; + } catch ( e ) { + + // If there is an error parsing the URL, assume it is crossDomain, + // it can be rejected by the transport if it is invalid + s.crossDomain = true; + } + } + + // Convert data if not already a string + if ( s.data && s.processData && typeof s.data !== "string" ) { + s.data = jQuery.param( s.data, s.traditional ); + } + + // Apply prefilters + inspectPrefiltersOrTransports( prefilters, s, options, jqXHR ); + + // If request was aborted inside a prefilter, stop there + if ( completed ) { + return jqXHR; + } + + // We can fire global events as of now if asked to + // Don't fire events if jQuery.event is undefined in an AMD-usage scenario (#15118) + fireGlobals = jQuery.event && s.global; + + // Watch for a new set of requests + if ( fireGlobals && jQuery.active++ === 0 ) { + jQuery.event.trigger( "ajaxStart" ); + } + + // Uppercase the type + s.type = s.type.toUpperCase(); + + // Determine if request has content + s.hasContent = !rnoContent.test( s.type ); + + // Save the URL in case we're toying with the If-Modified-Since + // and/or If-None-Match header later on + // Remove hash to simplify url manipulation + cacheURL = s.url.replace( rhash, "" ); + + // More options handling for requests with no content + if ( !s.hasContent ) { + + // Remember the hash so we can put it back + uncached = s.url.slice( cacheURL.length ); + + // If data is available and should be processed, append data to url + if ( s.data && ( s.processData || typeof s.data === "string" ) ) { + cacheURL += ( rquery.test( cacheURL ) ? "&" : "?" ) + s.data; + + // #9682: remove data so that it's not used in an eventual retry + delete s.data; + } + + // Add or update anti-cache param if needed + if ( s.cache === false ) { + cacheURL = cacheURL.replace( rantiCache, "$1" ); + uncached = ( rquery.test( cacheURL ) ? "&" : "?" ) + "_=" + ( nonce.guid++ ) + + uncached; + } + + // Put hash and anti-cache on the URL that will be requested (gh-1732) + s.url = cacheURL + uncached; + + // Change '%20' to '+' if this is encoded form body content (gh-2658) + } else if ( s.data && s.processData && + ( s.contentType || "" ).indexOf( "application/x-www-form-urlencoded" ) === 0 ) { + s.data = s.data.replace( r20, "+" ); + } + + // Set the If-Modified-Since and/or If-None-Match header, if in ifModified mode. + if ( s.ifModified ) { + if ( jQuery.lastModified[ cacheURL ] ) { + jqXHR.setRequestHeader( "If-Modified-Since", jQuery.lastModified[ cacheURL ] ); + } + if ( jQuery.etag[ cacheURL ] ) { + jqXHR.setRequestHeader( "If-None-Match", jQuery.etag[ cacheURL ] ); + } + } + + // Set the correct header, if data is being sent + if ( s.data && s.hasContent && s.contentType !== false || options.contentType ) { + jqXHR.setRequestHeader( "Content-Type", s.contentType ); + } + + // Set the Accepts header for the server, depending on the dataType + jqXHR.setRequestHeader( + "Accept", + s.dataTypes[ 0 ] && s.accepts[ s.dataTypes[ 0 ] ] ? + s.accepts[ s.dataTypes[ 0 ] ] + + ( s.dataTypes[ 0 ] !== "*" ? ", " + allTypes + "; q=0.01" : "" ) : + s.accepts[ "*" ] + ); + + // Check for headers option + for ( i in s.headers ) { + jqXHR.setRequestHeader( i, s.headers[ i ] ); + } + + // Allow custom headers/mimetypes and early abort + if ( s.beforeSend && + ( s.beforeSend.call( callbackContext, jqXHR, s ) === false || completed ) ) { + + // Abort if not done already and return + return jqXHR.abort(); + } + + // Aborting is no longer a cancellation + strAbort = "abort"; + + // Install callbacks on deferreds + completeDeferred.add( s.complete ); + jqXHR.done( s.success ); + jqXHR.fail( s.error ); + + // Get transport + transport = inspectPrefiltersOrTransports( transports, s, options, jqXHR ); + + // If no transport, we auto-abort + if ( !transport ) { + done( -1, "No Transport" ); + } else { + jqXHR.readyState = 1; + + // Send global event + if ( fireGlobals ) { + globalEventContext.trigger( "ajaxSend", [ jqXHR, s ] ); + } + + // If request was aborted inside ajaxSend, stop there + if ( completed ) { + return jqXHR; + } + + // Timeout + if ( s.async && s.timeout > 0 ) { + timeoutTimer = window.setTimeout( function() { + jqXHR.abort( "timeout" ); + }, s.timeout ); + } + + try { + completed = false; + transport.send( requestHeaders, done ); + } catch ( e ) { + + // Rethrow post-completion exceptions + if ( completed ) { + throw e; + } + + // Propagate others as results + done( -1, e ); + } + } + + // Callback for when everything is done + function done( status, nativeStatusText, responses, headers ) { + var isSuccess, success, error, response, modified, + statusText = nativeStatusText; + + // Ignore repeat invocations + if ( completed ) { + return; + } + + completed = true; + + // Clear timeout if it exists + if ( timeoutTimer ) { + window.clearTimeout( timeoutTimer ); + } + + // Dereference transport for early garbage collection + // (no matter how long the jqXHR object will be used) + transport = undefined; + + // Cache response headers + responseHeadersString = headers || ""; + + // Set readyState + jqXHR.readyState = status > 0 ? 4 : 0; + + // Determine if successful + isSuccess = status >= 200 && status < 300 || status === 304; + + // Get response data + if ( responses ) { + response = ajaxHandleResponses( s, jqXHR, responses ); + } + + // Use a noop converter for missing script but not if jsonp + if ( !isSuccess && + jQuery.inArray( "script", s.dataTypes ) > -1 && + jQuery.inArray( "json", s.dataTypes ) < 0 ) { + s.converters[ "text script" ] = function() {}; + } + + // Convert no matter what (that way responseXXX fields are always set) + response = ajaxConvert( s, response, jqXHR, isSuccess ); + + // If successful, handle type chaining + if ( isSuccess ) { + + // Set the If-Modified-Since and/or If-None-Match header, if in ifModified mode. + if ( s.ifModified ) { + modified = jqXHR.getResponseHeader( "Last-Modified" ); + if ( modified ) { + jQuery.lastModified[ cacheURL ] = modified; + } + modified = jqXHR.getResponseHeader( "etag" ); + if ( modified ) { + jQuery.etag[ cacheURL ] = modified; + } + } + + // if no content + if ( status === 204 || s.type === "HEAD" ) { + statusText = "nocontent"; + + // if not modified + } else if ( status === 304 ) { + statusText = "notmodified"; + + // If we have data, let's convert it + } else { + statusText = response.state; + success = response.data; + error = response.error; + isSuccess = !error; + } + } else { + + // Extract error from statusText and normalize for non-aborts + error = statusText; + if ( status || !statusText ) { + statusText = "error"; + if ( status < 0 ) { + status = 0; + } + } + } + + // Set data for the fake xhr object + jqXHR.status = status; + jqXHR.statusText = ( nativeStatusText || statusText ) + ""; + + // Success/Error + if ( isSuccess ) { + deferred.resolveWith( callbackContext, [ success, statusText, jqXHR ] ); + } else { + deferred.rejectWith( callbackContext, [ jqXHR, statusText, error ] ); + } + + // Status-dependent callbacks + jqXHR.statusCode( statusCode ); + statusCode = undefined; + + if ( fireGlobals ) { + globalEventContext.trigger( isSuccess ? "ajaxSuccess" : "ajaxError", + [ jqXHR, s, isSuccess ? success : error ] ); + } + + // Complete + completeDeferred.fireWith( callbackContext, [ jqXHR, statusText ] ); + + if ( fireGlobals ) { + globalEventContext.trigger( "ajaxComplete", [ jqXHR, s ] ); + + // Handle the global AJAX counter + if ( !( --jQuery.active ) ) { + jQuery.event.trigger( "ajaxStop" ); + } + } + } + + return jqXHR; + }, + + getJSON: function( url, data, callback ) { + return jQuery.get( url, data, callback, "json" ); + }, + + getScript: function( url, callback ) { + return jQuery.get( url, undefined, callback, "script" ); + } +} ); + +jQuery.each( [ "get", "post" ], function( _i, method ) { + jQuery[ method ] = function( url, data, callback, type ) { + + // Shift arguments if data argument was omitted + if ( isFunction( data ) ) { + type = type || callback; + callback = data; + data = undefined; + } + + // The url can be an options object (which then must have .url) + return jQuery.ajax( jQuery.extend( { + url: url, + type: method, + dataType: type, + data: data, + success: callback + }, jQuery.isPlainObject( url ) && url ) ); + }; +} ); + +jQuery.ajaxPrefilter( function( s ) { + var i; + for ( i in s.headers ) { + if ( i.toLowerCase() === "content-type" ) { + s.contentType = s.headers[ i ] || ""; + } + } +} ); + + +jQuery._evalUrl = function( url, options, doc ) { + return jQuery.ajax( { + url: url, + + // Make this explicit, since user can override this through ajaxSetup (#11264) + type: "GET", + dataType: "script", + cache: true, + async: false, + global: false, + + // Only evaluate the response if it is successful (gh-4126) + // dataFilter is not invoked for failure responses, so using it instead + // of the default converter is kludgy but it works. + converters: { + "text script": function() {} + }, + dataFilter: function( response ) { + jQuery.globalEval( response, options, doc ); + } + } ); +}; + + +jQuery.fn.extend( { + wrapAll: function( html ) { + var wrap; + + if ( this[ 0 ] ) { + if ( isFunction( html ) ) { + html = html.call( this[ 0 ] ); + } + + // The elements to wrap the target around + wrap = jQuery( html, this[ 0 ].ownerDocument ).eq( 0 ).clone( true ); + + if ( this[ 0 ].parentNode ) { + wrap.insertBefore( this[ 0 ] ); + } + + wrap.map( function() { + var elem = this; + + while ( elem.firstElementChild ) { + elem = elem.firstElementChild; + } + + return elem; + } ).append( this ); + } + + return this; + }, + + wrapInner: function( html ) { + if ( isFunction( html ) ) { + return this.each( function( i ) { + jQuery( this ).wrapInner( html.call( this, i ) ); + } ); + } + + return this.each( function() { + var self = jQuery( this ), + contents = self.contents(); + + if ( contents.length ) { + contents.wrapAll( html ); + + } else { + self.append( html ); + } + } ); + }, + + wrap: function( html ) { + var htmlIsFunction = isFunction( html ); + + return this.each( function( i ) { + jQuery( this ).wrapAll( htmlIsFunction ? html.call( this, i ) : html ); + } ); + }, + + unwrap: function( selector ) { + this.parent( selector ).not( "body" ).each( function() { + jQuery( this ).replaceWith( this.childNodes ); + } ); + return this; + } +} ); + + +jQuery.expr.pseudos.hidden = function( elem ) { + return !jQuery.expr.pseudos.visible( elem ); +}; +jQuery.expr.pseudos.visible = function( elem ) { + return !!( elem.offsetWidth || elem.offsetHeight || elem.getClientRects().length ); +}; + + + + +jQuery.ajaxSettings.xhr = function() { + try { + return new window.XMLHttpRequest(); + } catch ( e ) {} +}; + +var xhrSuccessStatus = { + + // File protocol always yields status code 0, assume 200 + 0: 200, + + // Support: IE <=9 only + // #1450: sometimes IE returns 1223 when it should be 204 + 1223: 204 + }, + xhrSupported = jQuery.ajaxSettings.xhr(); + +support.cors = !!xhrSupported && ( "withCredentials" in xhrSupported ); +support.ajax = xhrSupported = !!xhrSupported; + +jQuery.ajaxTransport( function( options ) { + var callback, errorCallback; + + // Cross domain only allowed if supported through XMLHttpRequest + if ( support.cors || xhrSupported && !options.crossDomain ) { + return { + send: function( headers, complete ) { + var i, + xhr = options.xhr(); + + xhr.open( + options.type, + options.url, + options.async, + options.username, + options.password + ); + + // Apply custom fields if provided + if ( options.xhrFields ) { + for ( i in options.xhrFields ) { + xhr[ i ] = options.xhrFields[ i ]; + } + } + + // Override mime type if needed + if ( options.mimeType && xhr.overrideMimeType ) { + xhr.overrideMimeType( options.mimeType ); + } + + // X-Requested-With header + // For cross-domain requests, seeing as conditions for a preflight are + // akin to a jigsaw puzzle, we simply never set it to be sure. + // (it can always be set on a per-request basis or even using ajaxSetup) + // For same-domain requests, won't change header if already provided. + if ( !options.crossDomain && !headers[ "X-Requested-With" ] ) { + headers[ "X-Requested-With" ] = "XMLHttpRequest"; + } + + // Set headers + for ( i in headers ) { + xhr.setRequestHeader( i, headers[ i ] ); + } + + // Callback + callback = function( type ) { + return function() { + if ( callback ) { + callback = errorCallback = xhr.onload = + xhr.onerror = xhr.onabort = xhr.ontimeout = + xhr.onreadystatechange = null; + + if ( type === "abort" ) { + xhr.abort(); + } else if ( type === "error" ) { + + // Support: IE <=9 only + // On a manual native abort, IE9 throws + // errors on any property access that is not readyState + if ( typeof xhr.status !== "number" ) { + complete( 0, "error" ); + } else { + complete( + + // File: protocol always yields status 0; see #8605, #14207 + xhr.status, + xhr.statusText + ); + } + } else { + complete( + xhrSuccessStatus[ xhr.status ] || xhr.status, + xhr.statusText, + + // Support: IE <=9 only + // IE9 has no XHR2 but throws on binary (trac-11426) + // For XHR2 non-text, let the caller handle it (gh-2498) + ( xhr.responseType || "text" ) !== "text" || + typeof xhr.responseText !== "string" ? + { binary: xhr.response } : + { text: xhr.responseText }, + xhr.getAllResponseHeaders() + ); + } + } + }; + }; + + // Listen to events + xhr.onload = callback(); + errorCallback = xhr.onerror = xhr.ontimeout = callback( "error" ); + + // Support: IE 9 only + // Use onreadystatechange to replace onabort + // to handle uncaught aborts + if ( xhr.onabort !== undefined ) { + xhr.onabort = errorCallback; + } else { + xhr.onreadystatechange = function() { + + // Check readyState before timeout as it changes + if ( xhr.readyState === 4 ) { + + // Allow onerror to be called first, + // but that will not handle a native abort + // Also, save errorCallback to a variable + // as xhr.onerror cannot be accessed + window.setTimeout( function() { + if ( callback ) { + errorCallback(); + } + } ); + } + }; + } + + // Create the abort callback + callback = callback( "abort" ); + + try { + + // Do send the request (this may raise an exception) + xhr.send( options.hasContent && options.data || null ); + } catch ( e ) { + + // #14683: Only rethrow if this hasn't been notified as an error yet + if ( callback ) { + throw e; + } + } + }, + + abort: function() { + if ( callback ) { + callback(); + } + } + }; + } +} ); + + + + +// Prevent auto-execution of scripts when no explicit dataType was provided (See gh-2432) +jQuery.ajaxPrefilter( function( s ) { + if ( s.crossDomain ) { + s.contents.script = false; + } +} ); + +// Install script dataType +jQuery.ajaxSetup( { + accepts: { + script: "text/javascript, application/javascript, " + + "application/ecmascript, application/x-ecmascript" + }, + contents: { + script: /\b(?:java|ecma)script\b/ + }, + converters: { + "text script": function( text ) { + jQuery.globalEval( text ); + return text; + } + } +} ); + +// Handle cache's special case and crossDomain +jQuery.ajaxPrefilter( "script", function( s ) { + if ( s.cache === undefined ) { + s.cache = false; + } + if ( s.crossDomain ) { + s.type = "GET"; + } +} ); + +// Bind script tag hack transport +jQuery.ajaxTransport( "script", function( s ) { + + // This transport only deals with cross domain or forced-by-attrs requests + if ( s.crossDomain || s.scriptAttrs ) { + var script, callback; + return { + send: function( _, complete ) { + script = jQuery( " + + + + + + + + + + + + + + + + + + + +
+ + + +
+ + + + + +
+ +
+ +
+ + + +
+
+
+ +
+

ayon_api.constants module

+
+ + +
+ +
+ + +
+
+ +
+ +
+ + + + + + + + + + + + \ No newline at end of file diff --git a/ayon_api.entity_hub.html b/ayon_api.entity_hub.html new file mode 100644 index 0000000000..6ff8fc96fd --- /dev/null +++ b/ayon_api.entity_hub.html @@ -0,0 +1,3062 @@ + + + + + + + + + + + + ayon_api.entity_hub module — ayon-python-api 1.0.11 documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + +
+ + + + + +
+ +
+ +
+ + + +
+
+
+ +
+

ayon_api.entity_hub module

+
+
+class AttributeValue(value)[source]
+

Bases: object

+
+
+property changed
+
+ +
+
+get_value()[source]
+
+ +
+
+lock()[source]
+
+ +
+
+set_value(value)[source]
+
+ +
+
+property value
+
+ +
+ +
+
+class Attributes(attrib_keys, values=<UNKNOWN_VALUE>)[source]
+

Bases: object

+

Object representing attribs of entity.

+
+
Todos:

This could be enhanced to know attribute schema and validate values +based on the schema.

+
+
+
+
Parameters:
+
    +
  • attrib_keys (Iterable[str]) – Keys that are available in attribs of the +entity.

  • +
  • values (Optional[Dict[str, Any]]) – Values of attributes.

  • +
+
+
+
+
+property changes
+

Attribute value changes.

+
+
Returns:
+

Key mapping with new values.

+
+
Return type:
+

Dict[str, Any]

+
+
+
+ +
+
+get(key, default=None)[source]
+

Get value of attribute.

+
+
Parameters:
+
    +
  • key (str) – Attribute name.

  • +
  • default (Any) – Default value to return when attribute was not +found.

  • +
+
+
+
+ +
+
+get_attribute(key)[source]
+

Access to attribute object.

+
+
Parameters:
+

key (str) – Name of attribute.

+
+
Returns:
+

Object of attribute value.

+
+
Return type:
+

AttributeValue

+
+
Raises:
+

KeyError – When attribute is not available.

+
+
+
+ +
+
+items()[source]
+
+ +
+
+keys()[source]
+
+ +
+
+lock()[source]
+
+ +
+
+set(key, value)[source]
+

Change value of attribute.

+
+
Parameters:
+
    +
  • key (str) – Attribute name.

  • +
  • value (Any) – New value of the attribute.

  • +
+
+
+
+ +
+
+to_dict(ignore_none=True)[source]
+
+ +
+
+values()[source]
+
+ +
+ +
+
+class BaseEntity(entity_id: ~typing.Optional[str] = None, parent_id: ~typing.Optional[Union[str, _CustomNone]] = <UNKNOWN_VALUE>, attribs: ~typing.Optional[~typing.Dict[str, ~typing.Any]] = <UNKNOWN_VALUE>, data: ~typing.Optional[~typing.Dict[str, ~typing.Any]] = <UNKNOWN_VALUE>, active: ~typing.Optional[bool] = <UNKNOWN_VALUE>, created: ~typing.Optional[bool] = None, entity_hub: ~ayon_api.entity_hub.EntityHub = None, name=None, label=None, status: ~typing.Optional[str] = <UNKNOWN_VALUE>, tags: ~typing.Optional[~typing.List[str]] = None, thumbnail_id: ~typing.Optional[str] = <UNKNOWN_VALUE>)[source]
+

Bases: ABC

+

Object representation of entity from server which is capturing changes.

+

All data on created object are expected as “current data” on server entity +unless the entity has set ‘created’ to ‘True’. So if new data should be +stored to server entity then fill entity with server data first and +then change them.

+

Calling ‘lock’ method will mark entity as “saved” and all changes made on +entity are set as “current data” on server.

+
+
Parameters:
+
    +
  • entity_id (Optional[str]) – Entity id. New id is created if +not passed.

  • +
  • parent_id (Optional[str]) – Parent entity id.

  • +
  • attribs (Optional[Dict[str, Any]]) – Attribute values.

  • +
  • data (Optional[Dict[str, Any]]) – Entity data (custom data).

  • +
  • thumbnail_id (Optional[str]) – Thumbnail id.

  • +
  • active (Optional[bool]) – Is entity active.

  • +
  • entity_hub (EntityHub) – Object of entity hub which created object of +the entity.

  • +
  • created (Optional[bool]) – Entity is new. When ‘None’ is passed the +value is defined based on value of ‘entity_id’.

  • +
+
+
+
+
+add_child(child)[source]
+

Add child entity.

+
+
Parameters:
+

child (BaseEntity) – Child object to add.

+
+
Raises:
+

TypeError – When child object has invalid type to be children.

+
+
+
+ +
+
+property attribs
+

Entity attributes based on server configuration.

+
+
Returns:
+

+
Attributes object handling changes and values of

attributes on entity.

+
+
+

+
+
Return type:
+

Attributes

+
+
+
+ +
+
+abstract property changes: Optional[Dict[str, Any]]
+

Receive entity changes.

+
+
Returns:
+

+
All values that have changed on

entity. New entity must return None.

+
+
+

+
+
Return type:
+

Optional[Dict[str, Any]]

+
+
+
+ +
+
+property children
+

Access to children objects.

+
+
Returns:
+

Children iterator.

+
+
Return type:
+

Union[List[BaseEntity], Type[UNKNOWN_VALUE]]

+
+
+
+ +
+
+property children_ids
+

Access to children objects.

+
+
Todos:
+
Children should be maybe handled by EntityHub instead of entities

themselves. That would simplify ‘set_entity_parent’, +‘unset_entity_parent’ and other logic related to changing +hierarchy.

+
+
+
+
+
+
Returns:
+

Children iterator.

+
+
Return type:
+

Union[List[str], Type[UNKNOWN_VALUE]]

+
+
+
+ +
+
+property created
+

Entity is new.

+
+
Returns:
+

Entity is newly created.

+
+
Return type:
+

bool

+
+
+
+ +
+
+property data
+

Entity custom data that are not stored by any deterministic model.

+
+
Be aware that ‘data’ can’t be queried using GraphQl and cannot be

updated partially.

+
+
+
+
Returns:
+

Custom data on entity.

+
+
Return type:
+

EntityData

+
+
+
+ +
+
+abstract property entity_type: EntityType
+

Entity type corresponding to server.

+
+
Returns:
+

Entity type.

+
+
Return type:
+

EntityType

+
+
+
+ +
+
+fill_children_ids(children_ids)[source]
+

Fill children ids on entity.

+
+

Warning

+

This is not an api call but is called from entity hub.

+
+
+ +
+
+abstract classmethod from_entity_data(entity_data: Dict[str, Any], entity_hub: EntityHub) BaseEntity[source]
+

Create entity based on queried data from server.

+
+
Parameters:
+
    +
  • entity_data (Dict[str, Any]) – Entity data from server.

  • +
  • entity_hub (EntityHub) – Hub which handle the entity.

  • +
+
+
Returns:
+

Object of the class.

+
+
Return type:
+

BaseEntity

+
+
+
+ +
+
+get_children(allow_fetch=True)[source]
+

Access to children objects.

+
+
Returns:
+

Children iterator.

+
+
Return type:
+

Union[List[BaseEntity], Type[UNKNOWN_VALUE]]

+
+
+
+ +
+
+get_children_ids(allow_fetch=True)[source]
+

Access to children objects.

+
+
Todos:
+
Children should be maybe handled by EntityHub instead of entities

themselves. That would simplify ‘set_entity_parent’, +‘unset_entity_parent’ and other logic related to changing +hierarchy.

+
+
+
+
+
+
Returns:
+

Children iterator.

+
+
Return type:
+

Union[List[str], Type[UNKNOWN_VALUE]]

+
+
+
+ +
+
+get_label() Optional[str][source]
+
+ +
+
+get_name()[source]
+
+ +
+
+get_parent(allow_fetch=True)[source]
+

Parent entity.

+
+
Returns:
+

Parent object.

+
+
Return type:
+

Optional[BaseEntity]

+
+
+
+ +
+
+get_parent_id()[source]
+

Parent entity id.

+
+
Returns:
+

Parent entity id or none if is not set.

+
+
Return type:
+

Optional[str]

+
+
+
+ +
+
+get_status() Union[str, _CustomNone][source]
+

Folder status.

+
+
Returns:
+

Folder status or ‘UNKNOWN_VALUE’.

+
+
Return type:
+

Union[str, UNKNOWN_VALUE]

+
+
+
+ +
+
+get_tags()[source]
+

Task tags.

+
+
Returns:
+

Task tags.

+
+
Return type:
+

list[str]

+
+
+
+ +
+
+get_thumbnail_id()[source]
+

Thumbnail id of entity.

+
+
Returns:
+

Thumbnail id or none if is not set.

+
+
Return type:
+

Optional[str]

+
+
+
+ +
+
+property has_cached_immutable_hierarchy: bool
+
+ +
+
+property id: str
+

Access to entity id under which is entity available on server.

+
+
Returns:
+

Entity id.

+
+
Return type:
+

str

+
+
+
+ +
+
+property immutable_for_hierarchy: bool
+

Entity is immutable for hierarchy changes.

+

Hierarchy changes can be considered as change of name or parents.

+
+
Returns:
+

Entity is immutable for hierarchy changes.

+
+
Return type:
+

bool

+
+
+
+ +
+
+property label: Optional[str]
+
+ +
+
+lock()[source]
+

Lock entity as ‘saved’ so all changes are discarded.

+
+ +
+
+property name
+
+ +
+
+property orig_parent_id
+
+ +
+
+property parent
+

Parent entity.

+
+
Returns:
+

Parent object.

+
+
Return type:
+

Optional[BaseEntity]

+
+
+
+ +
+
+abstract property parent_entity_types: List[str]
+

Entity type corresponding to server.

+
+
Returns:
+

Possible entity types of parent.

+
+
Return type:
+

List[str]

+
+
+
+ +
+
+property parent_id
+

Parent entity id.

+
+
Returns:
+

Parent entity id or none if is not set.

+
+
Return type:
+

Optional[str]

+
+
+
+ +
+
+property project_name: str
+

Quick access to project from entity hub.

+
+
Returns:
+

Name of project under which entity lives.

+
+
Return type:
+

str

+
+
+
+ +
+
+remove_child(child)[source]
+

Remove child entity.

+

Is ignored if child is not in children.

+
+
Parameters:
+

child (Union[str, BaseEntity]) – Child object or child id to remove.

+
+
+
+ +
+
+property removed: bool
+
+ +
+
+reset_immutable_for_hierarchy_cache(bottom_to_top: Optional[bool] = True)[source]
+

Clear cache of immutable hierarchy property.

+

This is used when entity changed parent or a child was added.

+
+
Parameters:
+

bottom_to_top (bool) – Reset cache from top hierarchy to bottom or +from bottom hierarchy to top.

+
+
+
+ +
+
+set_label(label: Optional[str])[source]
+
+ +
+
+set_name(name)[source]
+
+ +
+
+set_parent(parent)[source]
+

Change parent object.

+
+
Parameters:
+

parent (BaseEntity) – New parent for entity.

+
+
Raises:
+

TypeError – If validation of parent does not pass.

+
+
+
+ +
+
+set_parent_id(parent_id)[source]
+

Change parent by id.

+
+
Parameters:
+

parent_id (Optional[str]) – Id of new parent for entity.

+
+
Raises:
+
    +
  • ValueError – If parent was not found by id.

  • +
  • TypeError – If validation of parent does not pass.

  • +
+
+
+
+ +
+
+set_status(status_name: str)[source]
+

Set folder status.

+
+
Parameters:
+

status_name (str) – Status name.

+
+
+
+ +
+
+set_tags(tags)[source]
+

Change tags.

+
+
Parameters:
+

tags (Iterable[str]) – Tags.

+
+
+
+ +
+
+set_thumbnail_id(thumbnail_id)[source]
+

Change thumbnail id.

+
+
Parameters:
+

thumbnail_id (Union[str, None]) – Thumbnail id for entity.

+
+
+
+ +
+
+property status: Union[str, _CustomNone]
+

Folder status.

+
+
Returns:
+

Folder status or ‘UNKNOWN_VALUE’.

+
+
Return type:
+

Union[str, UNKNOWN_VALUE]

+
+
+
+ +
+
+property tags
+

Task tags.

+
+
Returns:
+

Task tags.

+
+
Return type:
+

list[str]

+
+
+
+ +
+
+property thumbnail_id
+

Thumbnail id of entity.

+
+
Returns:
+

Thumbnail id or none if is not set.

+
+
Return type:
+

Optional[str]

+
+
+
+ +
+
+abstract to_create_body_data() Dict[str, Any][source]
+

Convert object of entity to data for server on creation.

+
+
Returns:
+

Entity data.

+
+
Return type:
+

Dict[str, Any]

+
+
+
+ +
+ +
+
+class EntityData(*args, **kwargs)[source]
+

Bases: dict

+

Wrapper for ‘data’ key on entity.

+

Data on entity are arbitrary data that are not stored in any deterministic +model. It is possible to store any data that can be parsed to json.

+

It is not possible to store ‘None’ to root key. In that case the key is +not stored, and removed if existed on entity. +To be able to store ‘None’ value use nested data structure:

+
{
+    "sceneInfo": {
+        "description": None,
+        "camera": "camera1"
+    }
+}
+
+
+
+
+get_changes()[source]
+

Changes in entity data.

+

Removed keys have value set to ‘None’.

+
+
Returns:
+

Key mapping with changed values.

+
+
Return type:
+

dict[str, Any]

+
+
+
+ +
+
+get_new_entity_value()[source]
+

Value of data for new entity.

+
+
Returns:
+

Data without None values.

+
+
Return type:
+

dict[str, Any]

+
+
+
+ +
+
+lock()[source]
+

Lock changes of entity data.

+
+ +
+ +
+
+class EntityHub(project_name, connection=None, allow_data_changes=None)[source]
+

Bases: object

+

Helper to create, update or remove entities in project.

+

The hub is a guide to operation with folder entities and update of project. +Project entity must already exist on server (can be only updated).

+

Object is caching entities queried from server. They won’t be required once +they were queried, so it is recommended to create new hub or clear cache +frequently.

+
+
Todos:
+
Listen to server events about entity changes to be able to update

already queried entities.

+
+
+
+
+
+
Parameters:
+
    +
  • project_name (str) – Name of project where changes will happen.

  • +
  • connection (ServerAPI) – Connection to server with logged user.

  • +
  • allow_data_changes (bool) – This option gives ability to change ‘data’ +key on entities. This is not recommended as ‘data’ may be used for +secure information and would also slow down server queries. Content +of ‘data’ key can’t be received only GraphQl.

  • +
+
+
+
+
+add_entity(entity)[source]
+

Add entity to hub cache.

+
+
Parameters:
+

entity (BaseEntity) – Entity that should be added to hub’s cache.

+
+
+
+ +
+
+add_folder(folder)[source]
+

Create folder object and add it to entity hub.

+
+
Parameters:
+

folder (Dict[str, Any]) – Folder entity data.

+
+
Returns:
+

Added folder entity.

+
+
Return type:
+

FolderEntity

+
+
+
+ +
+
+add_new_folder(name: str, folder_type: str, parent_id: ~typing.Optional[str] = <UNKNOWN_VALUE>, label: ~typing.Optional[str] = None, path: ~typing.Optional[str] = None, status: ~typing.Optional[str] = <UNKNOWN_VALUE>, tags: ~typing.Optional[~typing.List[str]] = None, attribs: ~typing.Optional[~typing.Dict[str, ~typing.Any]] = <UNKNOWN_VALUE>, data: ~typing.Optional[~typing.Dict[str, ~typing.Any]] = <UNKNOWN_VALUE>, thumbnail_id: ~typing.Optional[str] = <UNKNOWN_VALUE>, active: bool = <UNKNOWN_VALUE>, entity_id: ~typing.Optional[str] = None, created: ~typing.Optional[bool] = True)[source]
+

Create folder object and add it to entity hub.

+
+
Parameters:
+
    +
  • name (str) – Name of entity.

  • +
  • folder_type (str) – Type of folder. Folder type must be available in +config of project folder types.

  • +
  • parent_id (Union[str, None]) – Id of parent entity.

  • +
  • label (Optional[str]) – Folder label.

  • +
  • path (Optional[str]) – Folder path. Path consist of all parent names +with slash(‘/’) used as separator.

  • +
  • status (Optional[str]) – Folder status.

  • +
  • tags (Optional[List[str]]) – Folder tags.

  • +
  • attribs (Dict[str, Any]) – Attribute values.

  • +
  • data (Dict[str, Any]) – Entity data (custom data).

  • +
  • thumbnail_id (Union[str, None]) – Id of entity’s thumbnail.

  • +
  • active (bool) – Is entity active.

  • +
  • entity_id (Optional[str]) – Id of the entity. New id is created if +not passed.

  • +
  • created (Optional[bool]) – Entity is new. When ‘None’ is passed the +value is defined based on value of ‘entity_id’.

  • +
+
+
Returns:
+

Added folder entity.

+
+
Return type:
+

FolderEntity

+
+
+
+ +
+
+add_new_product(name: str, product_type: str, folder_id: ~typing.Optional[Union[str, _CustomNone]] = <UNKNOWN_VALUE>, tags: ~typing.Optional[~typing.Iterable[str]] = None, attribs: ~typing.Optional[~typing.Dict[str, ~typing.Any]] = <UNKNOWN_VALUE>, data: ~typing.Optional[~typing.Dict[str, ~typing.Any]] = <UNKNOWN_VALUE>, active: ~typing.Optional[bool] = <UNKNOWN_VALUE>, entity_id: ~typing.Optional[str] = None, created: ~typing.Optional[bool] = True)[source]
+

Create task object and add it to entity hub.

+
+
Parameters:
+
    +
  • name (str) – Name of entity.

  • +
  • product_type (str) – Type of product.

  • +
  • folder_id (Union[str, None]) – Parent folder id.

  • +
  • tags (Optional[Iterable[str]]) – Folder tags.

  • +
  • attribs (Dict[str, Any]) – Attribute values.

  • +
  • data (Dict[str, Any]) – Entity data (custom data).

  • +
  • active (bool) – Is entity active.

  • +
  • entity_id (Optional[str]) – Id of the entity. New id is created if +not passed.

  • +
  • created (Optional[bool]) – Entity is new. When ‘None’ is passed the +value is defined based on value of ‘entity_id’.

  • +
+
+
Returns:
+

Added product entity.

+
+
Return type:
+

ProductEntity

+
+
+
+ +
+
+add_new_task(name: str, task_type: str, folder_id: ~typing.Optional[str] = <UNKNOWN_VALUE>, label: ~typing.Optional[str] = None, status: ~typing.Optional[str] = <UNKNOWN_VALUE>, tags: ~typing.Optional[~typing.Iterable[str]] = None, attribs: ~typing.Optional[~typing.Dict[str, ~typing.Any]] = <UNKNOWN_VALUE>, data: ~typing.Optional[~typing.Dict[str, ~typing.Any]] = <UNKNOWN_VALUE>, assignees: ~typing.Optional[~typing.Iterable[str]] = None, thumbnail_id: ~typing.Optional[str] = <UNKNOWN_VALUE>, active: ~typing.Optional[bool] = <UNKNOWN_VALUE>, entity_id: ~typing.Optional[str] = None, created: ~typing.Optional[bool] = True, parent_id: ~typing.Optional[str] = <UNKNOWN_VALUE>)[source]
+

Create task object and add it to entity hub.

+
+
Parameters:
+
    +
  • name (str) – Name of entity.

  • +
  • task_type (str) – Type of task. Task type must be available in +config of project task types.

  • +
  • folder_id (Union[str, None]) – Parent folder id.

  • +
  • label (Optional[str]) – Task label.

  • +
  • status (Optional[str]) – Task status.

  • +
  • tags (Optional[Iterable[str]]) – Folder tags.

  • +
  • attribs (Dict[str, Any]) – Attribute values.

  • +
  • data (Dict[str, Any]) – Entity data (custom data).

  • +
  • assignees (Optional[Iterable[str]]) – User assignees to the task.

  • +
  • thumbnail_id (Union[str, None]) – Id of entity’s thumbnail.

  • +
  • active (bool) – Is entity active.

  • +
  • entity_id (Optional[str]) – Id of the entity. New id is created if +not passed.

  • +
  • created (Optional[bool]) – Entity is new. When ‘None’ is passed the +value is defined based on value of ‘entity_id’.

  • +
  • parent_id (Union[str, None]) – DEPRECATED Parent folder id.

  • +
+
+
Returns:
+

Added task entity.

+
+
Return type:
+

TaskEntity

+
+
+
+ +
+
+add_new_version(version: int, product_id: ~typing.Optional[Union[str, _CustomNone]] = <UNKNOWN_VALUE>, task_id: ~typing.Optional[Union[str, _CustomNone]] = <UNKNOWN_VALUE>, status: ~typing.Optional[str] = <UNKNOWN_VALUE>, tags: ~typing.Optional[~typing.Iterable[str]] = None, attribs: ~typing.Optional[~typing.Dict[str, ~typing.Any]] = <UNKNOWN_VALUE>, data: ~typing.Optional[~typing.Dict[str, ~typing.Any]] = <UNKNOWN_VALUE>, thumbnail_id: ~typing.Optional[str] = <UNKNOWN_VALUE>, active: ~typing.Optional[bool] = <UNKNOWN_VALUE>, entity_id: ~typing.Optional[str] = None, created: ~typing.Optional[bool] = True)[source]
+

Create task object and add it to entity hub.

+
+
Parameters:
+
    +
  • version (int) – Version.

  • +
  • product_id (Union[str, None]) – Parent product id.

  • +
  • task_id (Union[str, None]) – Parent task id.

  • +
  • status (Optional[str]) – Task status.

  • +
  • tags (Optional[Iterable[str]]) – Folder tags.

  • +
  • attribs (Dict[str, Any]) – Attribute values.

  • +
  • data (Dict[str, Any]) – Entity data (custom data).

  • +
  • thumbnail_id (Union[str, None]) – Id of entity’s thumbnail.

  • +
  • active (bool) – Is entity active.

  • +
  • entity_id (Optional[str]) – Id of the entity. New id is created if +not passed.

  • +
  • created (Optional[bool]) – Entity is new. When ‘None’ is passed the +value is defined based on value of ‘entity_id’.

  • +
+
+
Returns:
+

Added version entity.

+
+
Return type:
+

VersionEntity

+
+
+
+ +
+
+add_product(product)[source]
+

Create version object and add it to entity hub.

+
+
Parameters:
+

product (Dict[str, Any]) – Version entity data.

+
+
Returns:
+

Added version entity.

+
+
Return type:
+

ProductEntity

+
+
+
+ +
+
+add_task(task)[source]
+

Create task object and add it to entity hub.

+
+
Parameters:
+

task (Dict[str, Any]) – Task entity data.

+
+
Returns:
+

Added task entity.

+
+
Return type:
+

TaskEntity

+
+
+
+ +
+
+add_version(version)[source]
+

Create version object and add it to entity hub.

+
+
Parameters:
+

version (Dict[str, Any]) – Version entity data.

+
+
Returns:
+

Added version entity.

+
+
Return type:
+

VersionEntity

+
+
+
+ +
+
+property allow_data_changes
+

Entity hub allows changes of ‘data’ key on entities.

+

Data are private and not all users may have access to them.

+

Older version of AYON server allowed to get ‘data’ for entity only +using REST api calls, which means to query each entity on-by-one +from server.

+
+
Returns:
+

Data changes are allowed.

+
+
Return type:
+

bool

+
+
+
+ +
+
+commit_changes()[source]
+

Commit any changes that happened on entities.

+
+ +
+
+delete_entity(entity)[source]
+
+ +
+
+property entities
+

Iterator over available entities.

+
+
Returns:
+

All queried/created entities cached in hub.

+
+
Return type:
+

Iterator[BaseEntity]

+
+
+
+ +
+
+fetch_hierarchy_entities()[source]
+

Query whole project at once.

+
+ +
+
+fill_project_from_server()[source]
+

Query project data from server and create project entity.

+

This method will invalidate previous object of Project entity.

+
+
Returns:
+

Entity that was updated with server data.

+
+
Return type:
+

ProjectEntity

+
+
Raises:
+

ValueError – When project was not found on server.

+
+
+
+ +
+
+folder_path_reseted(folder_id)[source]
+

Method called from ‘FolderEntity’ on path reset.

+

This should reset cache of folder paths on all children entities.

+

The path cache is always propagated from top to bottom so if an entity +has not cached path it means that any children can’t have it cached.

+
+ +
+
+get_attributes_for_type(entity_type: EntityType)[source]
+

Get attributes available for a type.

+

Attributes are based on entity types.

+
+
Todos:

Use attribute schema to validate values on entities.

+
+
+
+
Parameters:
+

entity_type (EntityType) – Entity type for which should +be attributes received.

+
+
Returns:
+

+
Attribute schemas that are available

for entered entity type.

+
+
+

+
+
Return type:
+

Dict[str, Dict[str, Any]]

+
+
+
+ +
+
+get_entity_by_id(entity_id: str) Optional[BaseEntity][source]
+

Receive entity by its id without entity type.

+

The entity must be already existing in cached objects.

+
+
Parameters:
+

entity_id (str) – Id of entity.

+
+
Returns:
+

Entity object or None.

+
+
Return type:
+

Optional[BaseEntity]

+
+
+
+ +
+
+get_entity_children(entity, allow_fetch=True)[source]
+
+ +
+
+get_folder_by_id(entity_id: str, allow_fetch: Optional[bool] = True) Optional[FolderEntity][source]
+

Get folder entity by id.

+
+
Parameters:
+
    +
  • entity_id (str) – Folder entity id.

  • +
  • allow_fetch (bool) – Try to fetch entity from server if is not +available in cache.

  • +
+
+
Returns:
+

Folder entity object.

+
+
Return type:
+

Optional[FolderEntity]

+
+
+
+ +
+
+get_or_fetch_entity_by_id(entity_id: str, entity_types: List[EntityType])[source]
+

Get or query entity based on it’s id and possible entity types.

+

This is a helper function when entity id is known but entity type may +have multiple possible options.

+
+
Parameters:
+
    +
  • entity_id (str) – Entity id.

  • +
  • entity_types (Iterable[str]) – Possible entity types that can the id +represent. e.g. ‘[“folder”, “project”]’

  • +
+
+
+
+ +
+
+get_or_query_entity_by_id(entity_id: str, entity_types: List[EntityType])[source]
+
+ +
+
+get_product_by_id(entity_id: str, allow_fetch: Optional[bool] = True) Optional[ProductEntity][source]
+

Get product entity by id.

+
+
Parameters:
+
    +
  • entity_id (str) – Product id.

  • +
  • allow_fetch (bool) – Try to fetch entity from server if is not +available in cache.

  • +
+
+
Returns:
+

Product entity object or None.

+
+
Return type:
+

Optional[ProductEntity]

+
+
+
+ +
+
+get_task_by_id(entity_id: str, allow_fetch: Optional[bool] = True) Optional[TaskEntity][source]
+

Get task entity by id.

+
+
Parameters:
+
    +
  • entity_id (str) – Id of task entity.

  • +
  • allow_fetch (bool) – Try to fetch entity from server if is not +available in cache.

  • +
+
+
Returns:
+

Task entity object or None.

+
+
Return type:
+

Optional[TaskEntity]

+
+
+
+ +
+
+get_version_by_id(entity_id: str, allow_fetch: Optional[bool] = True) Optional[VersionEntity][source]
+

Get version entity by id.

+
+
Parameters:
+
    +
  • entity_id (str) – Version id.

  • +
  • allow_fetch (bool) – Try to fetch entity from server if is not +available in cache.

  • +
+
+
Returns:
+

Version entity object or None.

+
+
Return type:
+

Optional[VersionEntity]

+
+
+
+ +
+
+lock()[source]
+
+ +
+
+property path_start_with_slash
+

Folder path should start with slash.

+

This changed in 0.6.x server version.

+
+
Returns:
+

Path starts with slash.

+
+
Return type:
+

bool

+
+
+
+ +
+
+property project_entity
+

Project entity.

+
+
Returns:
+

Project entity.

+
+
Return type:
+

ProjectEntity

+
+
+
+ +
+
+property project_name
+

Project name which is maintained by hub.

+
+
Returns:
+

Name of project.

+
+
Return type:
+

str

+
+
+
+ +
+
+query_entities_from_server()[source]
+
+ +
+
+reset_immutable_for_hierarchy_cache(entity_id: Optional[str], bottom_to_top: Optional[bool] = True)[source]
+
+ +
+
+set_entity_parent(entity_id, parent_id, orig_parent_id=<_NOT_SET>)[source]
+
+ +
+
+unset_entity_parent(entity_id, parent_id)[source]
+
+ +
+ +
+
+class FolderEntity(name: str, folder_type: str, parent_id: ~typing.Optional[str] = <UNKNOWN_VALUE>, label: ~typing.Optional[str] = None, path: ~typing.Optional[str] = None, status: ~typing.Optional[str] = <UNKNOWN_VALUE>, tags: ~typing.Optional[~typing.List[str]] = None, attribs: ~typing.Optional[~typing.Dict[str, ~typing.Any]] = <UNKNOWN_VALUE>, data: ~typing.Optional[~typing.Dict[str, ~typing.Any]] = <UNKNOWN_VALUE>, thumbnail_id: ~typing.Optional[str] = <UNKNOWN_VALUE>, active: bool = <UNKNOWN_VALUE>, entity_id: ~typing.Optional[str] = None, created: ~typing.Optional[bool] = None, entity_hub: ~typing.Optional[~ayon_api.entity_hub.EntityHub] = None)[source]
+

Bases: BaseEntity

+

Entity representing a folder on AYON server.

+
+
Parameters:
+
    +
  • name (str) – Name of entity.

  • +
  • folder_type (str) – Type of folder. Folder type must be available in +config of project folder types.

  • +
  • parent_id (Union[str, None]) – Id of parent entity.

  • +
  • label (Optional[str]) – Folder label.

  • +
  • path (Optional[str]) – Folder path. Path consist of all parent names +with slash(‘/’) used as separator.

  • +
  • status (Optional[str]) – Folder status.

  • +
  • tags (Optional[List[str]]) – Folder tags.

  • +
  • attribs (Dict[str, Any]) – Attribute values.

  • +
  • data (Dict[str, Any]) – Entity data (custom data).

  • +
  • thumbnail_id (Union[str, None]) – Id of entity’s thumbnail.

  • +
  • active (bool) – Is entity active.

  • +
  • entity_id (Union[str, None]) – Id of the entity. New id is created if +not passed.

  • +
  • created (Optional[bool]) – Entity is new. When ‘None’ is passed the +value is defined based on value of ‘entity_id’.

  • +
  • entity_hub (EntityHub) – Object of entity hub which created object of +the entity.

  • +
+
+
+
+
+property changes
+

Receive entity changes.

+
+
Returns:
+

+
All values that have changed on

entity. New entity must return None.

+
+
+

+
+
Return type:
+

Optional[Dict[str, Any]]

+
+
+
+ +
+
+entity_type = 'folder'
+
+ +
+
+property folder_type: str
+
+ +
+
+classmethod from_entity_data(folder, entity_hub) FolderEntity[source]
+

Create entity based on queried data from server.

+
+
Parameters:
+
    +
  • entity_data (Dict[str, Any]) – Entity data from server.

  • +
  • entity_hub (EntityHub) – Hub which handle the entity.

  • +
+
+
Returns:
+

Object of the class.

+
+
Return type:
+

BaseEntity

+
+
+
+ +
+
+get_folder_type() str[source]
+
+ +
+
+get_has_published_content()[source]
+
+ +
+
+get_path(dynamic_value=True)[source]
+
+ +
+
+property has_published_content
+
+ +
+
+lock()[source]
+

Lock entity as ‘saved’ so all changes are discarded.

+
+ +
+
+parent_entity_types = ['folder', 'project']
+
+ +
+
+property path
+
+ +
+
+reset_path()[source]
+
+ +
+
+set_folder_type(folder_type: str)[source]
+
+ +
+
+set_has_published_content(has_published_content)[source]
+
+ +
+
+to_create_body_data()[source]
+

Convert object of entity to data for server on creation.

+
+
Returns:
+

Entity data.

+
+
Return type:
+

Dict[str, Any]

+
+
+
+ +
+ +
+
+class ProductEntity(name: str, product_type: str, folder_id: ~typing.Optional[Union[str, _CustomNone]] = <UNKNOWN_VALUE>, tags: ~typing.Optional[~typing.Iterable[str]] = None, attribs: ~typing.Optional[~typing.Dict[str, ~typing.Any]] = <UNKNOWN_VALUE>, data: ~typing.Optional[~typing.Dict[str, ~typing.Any]] = <UNKNOWN_VALUE>, active: ~typing.Optional[bool] = <UNKNOWN_VALUE>, entity_id: ~typing.Optional[str] = None, created: ~typing.Optional[bool] = None, entity_hub: ~ayon_api.entity_hub.EntityHub = None)[source]
+

Bases: BaseEntity

+
+
+property changes
+

Receive entity changes.

+
+
Returns:
+

+
All values that have changed on

entity. New entity must return None.

+
+
+

+
+
Return type:
+

Optional[Dict[str, Any]]

+
+
+
+ +
+
+entity_type = 'product'
+
+ +
+
+property folder_id
+
+ +
+
+classmethod from_entity_data(product, entity_hub)[source]
+

Create entity based on queried data from server.

+
+
Parameters:
+
    +
  • entity_data (Dict[str, Any]) – Entity data from server.

  • +
  • entity_hub (EntityHub) – Hub which handle the entity.

  • +
+
+
Returns:
+

Object of the class.

+
+
Return type:
+

BaseEntity

+
+
+
+ +
+
+get_folder_id()[source]
+
+ +
+
+get_product_type()[source]
+
+ +
+
+lock()[source]
+

Lock entity as ‘saved’ so all changes are discarded.

+
+ +
+
+parent_entity_types = ['folder']
+
+ +
+
+property product_type
+
+ +
+
+set_folder_id(folder_id)[source]
+
+ +
+
+set_product_type(product_type)[source]
+
+ +
+
+to_create_body_data()[source]
+

Convert object of entity to data for server on creation.

+
+
Returns:
+

Entity data.

+
+
Return type:
+

Dict[str, Any]

+
+
+
+ +
+ +
+
+class ProjectEntity(name: str, project_code: str, library: bool, folder_types: ~typing.List[~typing.Dict[str, ~typing.Any]], task_types: ~typing.List[~typing.Dict[str, ~typing.Any]], statuses: ~typing.List[~typing.Dict[str, ~typing.Any]], attribs: ~typing.Optional[~typing.Dict[str, ~typing.Any]] = <UNKNOWN_VALUE>, data: ~typing.Optional[~typing.Dict[str, ~typing.Any]] = <UNKNOWN_VALUE>, active: ~typing.Optional[bool] = <UNKNOWN_VALUE>, entity_hub: ~typing.Optional[~ayon_api.entity_hub.EntityHub] = None)[source]
+

Bases: BaseEntity

+

Entity representing project on AYON server.

+
+
Parameters:
+
    +
  • name (str) – Name of entity.

  • +
  • project_code (str) – Project code.

  • +
  • library (bool) – Is project library project.

  • +
  • folder_types (list[dict[str, Any]]) – Folder types definition.

  • +
  • task_types (list[dict[str, Any]]) – Task types definition.

  • +
  • statuses – (list[dict[str, Any]]): Statuses definition.

  • +
  • attribs (Optional[Dict[str, Any]]) – Attribute values.

  • +
  • data (Dict[str, Any]) – Entity data (custom data).

  • +
  • active (bool) – Is entity active.

  • +
  • entity_hub (EntityHub) – Object of entity hub which created object of +the entity.

  • +
+
+
+
+
+property changes
+

Receive entity changes.

+
+
Returns:
+

+
All values that have changed on

entity. New entity must return None.

+
+
+

+
+
Return type:
+

Optional[Dict[str, Any]]

+
+
+
+ +
+
+default_folder_type_icon = 'folder'
+
+ +
+
+default_task_type_icon = 'task_alt'
+
+ +
+
+entity_type = 'project'
+
+ +
+
+property folder_types
+
+ +
+
+classmethod from_entity_data(project, entity_hub) ProjectEntity[source]
+

Create entity based on queried data from server.

+
+
Parameters:
+
    +
  • entity_data (Dict[str, Any]) – Entity data from server.

  • +
  • entity_hub (EntityHub) – Hub which handle the entity.

  • +
+
+
Returns:
+

Object of the class.

+
+
Return type:
+

BaseEntity

+
+
+
+ +
+
+get_folder_types()[source]
+
+ +
+
+get_orig_folder_types()[source]
+
+ +
+
+get_orig_statuses()[source]
+
+ +
+
+get_orig_task_types()[source]
+
+ +
+
+get_parent(*args, **kwargs)[source]
+

Parent entity.

+
+
Returns:
+

Parent object.

+
+
Return type:
+

Optional[BaseEntity]

+
+
+
+ +
+
+get_status_by_slugified_name(name)[source]
+

Find status by name.

+
+
Parameters:
+

name (str) – Status name.

+
+
Returns:
+

Status object or None.

+
+
Return type:
+

Union[ProjectStatus, None]

+
+
+
+ +
+
+get_statuses()[source]
+
+ +
+
+get_task_types()[source]
+
+ +
+
+lock()[source]
+

Lock entity as ‘saved’ so all changes are discarded.

+
+ +
+
+property parent
+

Parent entity.

+
+
Returns:
+

Parent object.

+
+
Return type:
+

Optional[BaseEntity]

+
+
+
+ +
+
+parent_entity_types = []
+
+ +
+
+set_folder_types(folder_types)[source]
+
+ +
+
+set_name(name)[source]
+
+ +
+
+set_parent(parent)[source]
+

Change parent object.

+
+
Parameters:
+

parent (BaseEntity) – New parent for entity.

+
+
Raises:
+

TypeError – If validation of parent does not pass.

+
+
+
+ +
+
+set_status_scope_supported(supported: bool)[source]
+
+ +
+
+set_statuses(statuses)[source]
+
+ +
+
+set_task_types(task_types)[source]
+
+ +
+
+property statuses
+
+ +
+
+property task_types
+
+ +
+
+to_create_body_data()[source]
+

Convert object of entity to data for server on creation.

+
+
Returns:
+

Entity data.

+
+
Return type:
+

Dict[str, Any]

+
+
+
+ +
+ +
+
+class ProjectStatus(name, short_name=None, state=None, icon=None, color=None, scope=None, index=None, project_statuses=None, is_new=None)[source]
+

Bases: object

+

Project status class.

+
+
Parameters:
+
    +
  • name (str) – Name of the status. e.g. ‘In progress’

  • +
  • short_name (Optional[str]) – Short name of the status. e.g. ‘IP’

  • +
  • state (Optional[StatusState]) – A state of the status.

  • +
  • icon (Optional[str]) – Icon of the status. e.g. ‘play_arrow’.

  • +
  • color (Optional[str]) – Color of the status. e.g. ‘#eeeeee’.

  • +
  • scope (Optional[Iterable[str]]) – Scope of the status. e.g. [‘folder’].

  • +
  • index (Optional[int]) – Index of the status.

  • +
  • project_statuses (Optional[_ProjectStatuses]) – Project statuses +wrapper.

  • +
+
+
+
+
+property changed
+

Status has changed.

+
+
Returns:
+

Status has changed.

+
+
Return type:
+

bool

+
+
+
+ +
+
+property color
+

Get color of project status.

+
+
Returns:
+

Status color.

+
+
Return type:
+

str

+
+
+
+ +
+
+color_regex = re.compile('#([a-f0-9]{6})$')
+
+ +
+
+default_color = '#eeeeee'
+
+ +
+
+default_state = 'in_progress'
+
+ +
+
+delete()[source]
+

Remove status from project statuses object.

+
+ +
+
+classmethod from_data(data, index=None, project_statuses=None)[source]
+

Create project status from data.

+
+
Parameters:
+
    +
  • data (dict[str, str]) – Status data.

  • +
  • index (Optional[int]) – Status index.

  • +
  • project_statuses (Optional[ProjectStatuses]) – Project statuses +object which wraps the status for a project.

  • +
+
+
+
+ +
+
+get_color()[source]
+

Get color of project status.

+
+
Returns:
+

Status color.

+
+
Return type:
+

str

+
+
+
+ +
+
+get_icon()[source]
+

Name of icon to use for status.

+
+
Returns:
+

Name of the icon.

+
+
Return type:
+

str

+
+
+
+ +
+
+get_index()[source]
+

Get index of status.

+
+
Returns:
+

+
Index of status or None if status is not under

project.

+
+
+

+
+
Return type:
+

Union[int, None]

+
+
+
+ +
+
+get_name()[source]
+

Status name.

+
+
Returns:
+

Status name.

+
+
Return type:
+

str

+
+
+
+ +
+
+get_project_statuses()[source]
+

Internal logic method.

+
+
Returns:
+

Project statuses object.

+
+
Return type:
+

_ProjectStatuses

+
+
+
+ +
+
+get_scope()[source]
+

Get scope of the status.

+
+
Returns:
+

Scope of the status.

+
+
Return type:
+

Set[str]

+
+
+
+ +
+
+get_short_name()[source]
+

Status short name 3 letters tops.

+
+
Returns:
+

Status short name.

+
+
Return type:
+

str

+
+
+
+ +
+
+get_state()[source]
+

Get state of project status.

+
+
Returns:
+

General state of status.

+
+
Return type:
+

StatusState

+
+
+
+ +
+
+property icon
+

Name of icon to use for status.

+
+
Returns:
+

Name of the icon.

+
+
Return type:
+

str

+
+
+
+ +
+
+property index
+

Get index of status.

+
+
Returns:
+

+
Index of status or None if status is not under

project.

+
+
+

+
+
Return type:
+

Union[int, None]

+
+
+
+ +
+
+is_available_for_entity_type(entity_type)[source]
+
+ +
+
+lock()[source]
+

Lock status.

+

Changes were commited and current values are now the original values.

+
+ +
+
+move_after(other)[source]
+

Move status after other status.

+
+
Parameters:
+

other (ProjectStatus) – Status to move after.

+
+
+
+ +
+
+move_before(other)[source]
+

Move status before other status.

+
+
Parameters:
+

other (ProjectStatus) – Status to move before.

+
+
+
+ +
+
+property name
+

Status name.

+
+
Returns:
+

Status name.

+
+
Return type:
+

str

+
+
+
+ +
+
+property project_statuses
+

Internal logic method.

+
+
Returns:
+

Project statuses object.

+
+
Return type:
+

_ProjectStatuses

+
+
+
+ +
+
+property scope
+

Get scope of the status.

+
+
Returns:
+

Scope of the status.

+
+
Return type:
+

Set[str]

+
+
+
+ +
+
+set_color(color)[source]
+

Set color of project status.

+
+
Parameters:
+

color (str) – Color in hex format. Example: ‘#ff0000’.

+
+
+
+ +
+
+set_icon(icon)[source]
+

Change status icon name.

+
+
Parameters:
+

icon (str) – Name of the icon.

+
+
+
+ +
+
+set_index(index, **kwargs)[source]
+

Change status index.

+
+
Returns:
+

+
Index of status or None if status is not under

project.

+
+
+

+
+
Return type:
+

Union[int, None]

+
+
+
+ +
+
+set_name(name)[source]
+

Change status name.

+
+
Parameters:
+

name (str) – New status name.

+
+
+
+ +
+
+set_project_statuses(project_statuses)[source]
+

Internal logic method to change parent object.

+
+
Parameters:
+

project_statuses (_ProjectStatuses) – Project statuses object.

+
+
+
+ +
+
+set_scope(scope)[source]
+

Get scope of the status.

+
+
Returns:
+

Scope of the status.

+
+
Return type:
+

scope (Iterable[str])

+
+
+
+ +
+
+set_short_name(short_name)[source]
+

Change status short name.

+
+
Parameters:
+

short_name (str) – New status short name. 3 letters tops.

+
+
+
+ +
+
+set_state(state)[source]
+

Set color of project status.

+
+
Parameters:
+

state (StatusState) – General state of status.

+
+
+
+ +
+
+property short_name
+

Status short name 3 letters tops.

+
+
Returns:
+

Status short name.

+
+
Return type:
+

str

+
+
+
+ +
+
+property slugified_name
+

Slugified and lowere status name.

+
+
Can be used for comparison of existing statuses. e.g. ‘In Progress’

vs. ‘in-progress’.

+
+
+
+
Returns:
+

Slugified and lower status name.

+
+
Return type:
+

str

+
+
+
+ +
+
+static slugify_name(name)[source]
+

Slugify status name for name comparison.

+
+
Parameters:
+

name (str) – Name of the status.

+
+
Returns:
+

Slugified name.

+
+
Return type:
+

str

+
+
+
+ +
+
+property state
+

Get state of project status.

+
+
Returns:
+

General state of status.

+
+
Return type:
+

StatusState

+
+
+
+ +
+
+to_data()[source]
+

Convert status to data.

+
+
Returns:
+

Status data.

+
+
Return type:
+

dict[str, str]

+
+
+
+ +
+
+unset_project_statuses(project_statuses)[source]
+

Internal logic method to unset parent object.

+
+
Parameters:
+

project_statuses (_ProjectStatuses) – Project statuses object.

+
+
+
+ +
+
+valid_scope = {'folder', 'product', 'representation', 'task', 'version', 'workfile'}
+
+ +
+
+valid_states = {'blocked', 'done', 'in_progress', 'not_started'}
+
+ +
+ +
+
+class TaskEntity(name: str, task_type: str, folder_id: ~typing.Optional[str] = <UNKNOWN_VALUE>, label: ~typing.Optional[str] = None, status: ~typing.Optional[str] = <UNKNOWN_VALUE>, tags: ~typing.Optional[~typing.Iterable[str]] = None, attribs: ~typing.Optional[~typing.Dict[str, ~typing.Any]] = <UNKNOWN_VALUE>, data: ~typing.Optional[~typing.Dict[str, ~typing.Any]] = <UNKNOWN_VALUE>, assignees: ~typing.Optional[~typing.Iterable[str]] = None, thumbnail_id: ~typing.Optional[str] = <UNKNOWN_VALUE>, active: ~typing.Optional[bool] = <UNKNOWN_VALUE>, entity_id: ~typing.Optional[str] = None, created: ~typing.Optional[bool] = None, entity_hub: ~typing.Optional[~ayon_api.entity_hub.EntityHub] = None)[source]
+

Bases: BaseEntity

+

Entity representing a task on AYON server.

+
+
Parameters:
+
    +
  • name (str) – Name of entity.

  • +
  • task_type (str) – Type of task. Task type must be available in config +of project task types.

  • +
  • folder_id (Union[str, None]) – Parent folder id.

  • +
  • label (Optional[str]) – Task label.

  • +
  • status (Optional[str]) – Task status.

  • +
  • tags (Optional[Iterable[str]]) – Folder tags.

  • +
  • attribs (Dict[str, Any]) – Attribute values.

  • +
  • data (Dict[str, Any]) – Entity data (custom data).

  • +
  • assignees (Optional[Iterable[str]]) – User assignees to the task.

  • +
  • thumbnail_id (Union[str, None]) – Id of entity’s thumbnail.

  • +
  • active (bool) – Is entity active.

  • +
  • entity_id (Union[str, None]) – Id of the entity. New id is created if +not passed.

  • +
  • created (Optional[bool]) – Entity is new. When ‘None’ is passed the +value is defined based on value of ‘entity_id’.

  • +
  • entity_hub (EntityHub) – Object of entity hub which created object of +the entity.

  • +
+
+
+
+
+add_child(child)[source]
+

Add child entity.

+
+
Parameters:
+

child (BaseEntity) – Child object to add.

+
+
Raises:
+

TypeError – When child object has invalid type to be children.

+
+
+
+ +
+
+property assignees
+

Task assignees.

+
+
Returns:
+

Task assignees.

+
+
Return type:
+

list[str]

+
+
+
+ +
+
+property changes
+

Receive entity changes.

+
+
Returns:
+

+
All values that have changed on

entity. New entity must return None.

+
+
+

+
+
Return type:
+

Optional[Dict[str, Any]]

+
+
+
+ +
+
+entity_type = 'task'
+
+ +
+
+property folder_id
+
+ +
+
+classmethod from_entity_data(task, entity_hub) TaskEntity[source]
+

Create entity based on queried data from server.

+
+
Parameters:
+
    +
  • entity_data (Dict[str, Any]) – Entity data from server.

  • +
  • entity_hub (EntityHub) – Hub which handle the entity.

  • +
+
+
Returns:
+

Object of the class.

+
+
Return type:
+

BaseEntity

+
+
+
+ +
+
+get_assignees()[source]
+

Task assignees.

+
+
Returns:
+

Task assignees.

+
+
Return type:
+

list[str]

+
+
+
+ +
+
+get_folder_id()[source]
+
+ +
+
+get_task_type() str[source]
+
+ +
+
+lock()[source]
+

Lock entity as ‘saved’ so all changes are discarded.

+
+ +
+
+parent_entity_types = ['folder']
+
+ +
+
+set_assignees(assignees)[source]
+

Change assignees.

+
+
Parameters:
+

assignees (Iterable[str]) – assignees.

+
+
+
+ +
+
+set_folder_id(folder_id)[source]
+
+ +
+
+set_task_type(task_type: str)[source]
+
+ +
+
+property task_type: str
+
+ +
+
+to_create_body_data()[source]
+

Convert object of entity to data for server on creation.

+
+
Returns:
+

Entity data.

+
+
Return type:
+

Dict[str, Any]

+
+
+
+ +
+ +
+
+class VersionEntity(version: int, product_id: ~typing.Optional[Union[str, _CustomNone]] = <UNKNOWN_VALUE>, task_id: ~typing.Optional[Union[str, _CustomNone]] = <UNKNOWN_VALUE>, status: ~typing.Optional[str] = <UNKNOWN_VALUE>, tags: ~typing.Optional[~typing.Iterable[str]] = None, attribs: ~typing.Optional[~typing.Dict[str, ~typing.Any]] = <UNKNOWN_VALUE>, data: ~typing.Optional[~typing.Dict[str, ~typing.Any]] = <UNKNOWN_VALUE>, thumbnail_id: ~typing.Optional[str] = <UNKNOWN_VALUE>, active: ~typing.Optional[bool] = <UNKNOWN_VALUE>, entity_id: ~typing.Optional[str] = None, created: ~typing.Optional[bool] = None, entity_hub: ~ayon_api.entity_hub.EntityHub = None)[source]
+

Bases: BaseEntity

+
+
+property changes
+

Receive entity changes.

+
+
Returns:
+

+
All values that have changed on

entity. New entity must return None.

+
+
+

+
+
Return type:
+

Optional[Dict[str, Any]]

+
+
+
+ +
+
+entity_type = 'version'
+
+ +
+
+classmethod from_entity_data(version, entity_hub)[source]
+

Create entity based on queried data from server.

+
+
Parameters:
+
    +
  • entity_data (Dict[str, Any]) – Entity data from server.

  • +
  • entity_hub (EntityHub) – Hub which handle the entity.

  • +
+
+
Returns:
+

Object of the class.

+
+
Return type:
+

BaseEntity

+
+
+
+ +
+
+get_product_id()[source]
+
+ +
+
+get_task_id()[source]
+
+ +
+
+get_version()[source]
+
+ +
+
+lock()[source]
+

Lock entity as ‘saved’ so all changes are discarded.

+
+ +
+
+parent_entity_types = ['product']
+
+ +
+
+property product_id
+
+ +
+
+set_product_id(product_id)[source]
+
+ +
+
+set_task_id(task_id)[source]
+
+ +
+
+set_version(version)[source]
+
+ +
+
+property task_id
+
+ +
+
+to_create_body_data()[source]
+

Convert object of entity to data for server on creation.

+
+
Returns:
+

Entity data.

+
+
Return type:
+

Dict[str, Any]

+
+
+
+ +
+
+property version
+
+ +
+ +
+ + +
+ +
+ + +
+
+ +
+ +
+ + + + + + + + + + + + \ No newline at end of file diff --git a/ayon_api.events.html b/ayon_api.events.html new file mode 100644 index 0000000000..eaa81b5a8e --- /dev/null +++ b/ayon_api.events.html @@ -0,0 +1,469 @@ + + + + + + + + + + + + ayon_api.events module — ayon-python-api 1.0.11 documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + +
+ + + + + +
+ +
+ +
+ + + +
+
+
+ +
+

ayon_api.events module

+
+
+class ServerEvent(topic, sender=None, event_hash=None, project_name=None, username=None, dependencies=None, description=None, summary=None, payload=None, finished=True, store=True)[source]
+

Bases: object

+
+
+to_data()[source]
+
+ +
+ +
+ + +
+ +
+ + +
+
+ +
+ +
+ + + + + + + + + + + + \ No newline at end of file diff --git a/ayon_api.exceptions.html b/ayon_api.exceptions.html new file mode 100644 index 0000000000..b7713c52e8 --- /dev/null +++ b/ayon_api.exceptions.html @@ -0,0 +1,551 @@ + + + + + + + + + + + + ayon_api.exceptions module — ayon-python-api 1.0.11 documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + +
+ + + + + +
+ +
+ +
+ + + +
+
+
+ +
+

ayon_api.exceptions module

+
+
+exception AuthenticationError[source]
+

Bases: ServerError

+
+ +
+
+exception FailedOperations[source]
+

Bases: Exception

+
+ +
+
+exception FailedServiceInit[source]
+

Bases: Exception

+
+ +
+
+exception FolderNotFound(project_name, folder_id, message=None)[source]
+

Bases: MissingEntityError

+
+ +
+
+exception GraphQlQueryFailed(errors, query, variables)[source]
+

Bases: Exception

+
+ +
+
+exception HTTPRequestError(message, response)[source]
+

Bases: RequestError

+
+ +
+
+exception MissingEntityError[source]
+

Bases: Exception

+
+ +
+
+exception ProjectNotFound(project_name, message=None)[source]
+

Bases: MissingEntityError

+
+ +
+
+exception RequestError(message, response)[source]
+

Bases: Exception

+
+ +
+
+exception ServerError[source]
+

Bases: Exception

+
+ +
+
+exception ServerNotReached[source]
+

Bases: ServerError

+
+ +
+
+exception UnauthorizedError[source]
+

Bases: ServerError

+
+ +
+
+exception UnsupportedServerVersion[source]
+

Bases: ServerError

+

Server version does not support the requested operation.

+
+
This is used for known incompatibilities between the python api and

server. E.g. can be used when endpoint is not available anymore, or +is not yet available on server.

+
+
+
+ +
+
+exception UrlError(message, title, hints=None)[source]
+

Bases: Exception

+

Url cannot be parsed as url.

+

Exception may contain hints of possible fixes of url that can be used in +UI if needed.

+
+ +
+ + +
+ +
+ + +
+
+ +
+ +
+ + + + + + + + + + + + \ No newline at end of file diff --git a/ayon_api.graphql.html b/ayon_api.graphql.html new file mode 100644 index 0000000000..ab7e9bd510 --- /dev/null +++ b/ayon_api.graphql.html @@ -0,0 +1,1119 @@ + + + + + + + + + + + + ayon_api.graphql module — ayon-python-api 1.0.11 documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + +
+ + + + + +
+ +
+ +
+ + + +
+
+
+ +
+

ayon_api.graphql module

+
+
+class BaseGraphQlQueryField(name, parent, order)[source]
+

Bases: ABC

+

Field in GraphQl query.

+
+
Parameters:
+
    +
  • name (str) – Name of field.

  • +
  • parent (Union[BaseGraphQlQueryField, GraphQlQuery]) – Parent object of a +field.

  • +
+
+
+
+
+add_field(name)[source]
+
+ +
+
+add_field_with_edges(name)[source]
+
+ +
+
+add_obj_field(field)[source]
+
+ +
+
+add_variable(key, value_type, value=None)[source]
+

Add variable to query.

+
+
Parameters:
+
    +
  • key (str) – Variable name.

  • +
  • value_type (str) – Type of expected value in variables. This is +graphql type e.g. “[String!]”, “Int”, “Boolean”, etc.

  • +
  • value (Any) – Default value for variable. Can be changed later.

  • +
+
+
Returns:
+

Created variable object.

+
+
Return type:
+

QueryVariable

+
+
Raises:
+

KeyError – If variable was already added before.

+
+
+
+ +
+
+abstract calculate_query()[source]
+
+ +
+
+property child_has_edges
+
+ +
+
+abstract property child_indent
+
+ +
+
+get_field_by_keys(keys: Iterable[str])[source]
+
+ +
+
+get_filters()[source]
+

Receive filters for item.

+

By default just use copy of set filters.

+
+
Returns:
+

Fields filters.

+
+
Return type:
+

Dict[str, Any]

+
+
+
+ +
+
+get_name() str[source]
+
+ +
+
+get_variable(key)[source]
+

Variable object.

+
+
Parameters:
+

key (str) – Variable name added to headers.

+
+
Returns:
+

Variable object used in query string.

+
+
Return type:
+

QueryVariable

+
+
+
+ +
+
+get_variable_value(*args, **kwargs)[source]
+
+ +
+
+abstract property has_edges
+
+ +
+
+has_filter(key)[source]
+
+ +
+
+property indent
+
+ +
+
+property name: str
+
+ +
+
+property need_query
+

Still need query from server.

+

Needed for edges which use pagination. Look into children values too.

+
+
Returns:
+

If still need query from server.

+
+
Return type:
+

bool

+
+
+
+ +
+
+property offset
+
+ +
+
+abstract parse_result(data, output, progress_data)[source]
+
+ +
+
+property path
+

Field path for debugging purposes.

+
+
Returns:
+

Field path in query.

+
+
Return type:
+

str

+
+
+
+ +
+
+property query_item
+
+ +
+
+remove_filter(key)[source]
+
+ +
+
+reset_cursor()[source]
+
+ +
+
+set_ascending_order(enabled=True)[source]
+
+ +
+
+set_descending_order(enabled=True)[source]
+
+ +
+
+set_filter(key, value)[source]
+
+ +
+
+set_limit(limit: Optional[int])[source]
+
+ +
+
+set_order(order)[source]
+
+ +
+
+set_parent(parent)[source]
+
+ +
+
+set_variable_value(*args, **kwargs)[source]
+
+ +
+
+sum_edge_fields(max_limit=None)[source]
+

Check how many edge fields query has.

+

In case there are multiple edge fields or are nested the query can’t +yield mid cursor results.

+
+
Parameters:
+

max_limit (int) – Skip rest of counting if counter is bigger then +entered number.

+
+
Returns:
+

Counter edge fields

+
+
Return type:
+

int

+
+
+
+ +
+ +
+
+class GraphQlQuery(name, order=None)[source]
+

Bases: object

+

GraphQl query which can have fields to query.

+

Single use object which can be used only for one query. Object and children +objects keep track about paging and progress.

+
+
Parameters:
+

name (str) – Name of query.

+
+
+
+
+add_field(name)[source]
+

Add field to query.

+
+
Parameters:
+

name (str) – Field name e.g. ‘id’.

+
+
Returns:
+

Created field object.

+
+
Return type:
+

GraphQlQueryField

+
+
+
+ +
+
+add_field_with_edges(name)[source]
+

Add field with edges to query.

+
+
Parameters:
+

name (str) – Field name e.g. ‘tasks’.

+
+
Returns:
+

Created field object.

+
+
Return type:
+

GraphQlQueryEdgeField

+
+
+
+ +
+
+add_obj_field(field)[source]
+

Add field object to children.

+
+
Parameters:
+

field (BaseGraphQlQueryField) – Add field to query children.

+
+
+
+ +
+
+add_variable(key, value_type, value=None)[source]
+

Add variable to query.

+
+
Parameters:
+
    +
  • key (str) – Variable name.

  • +
  • value_type (str) – Type of expected value in variables. This is +graphql type e.g. “[String!]”, “Int”, “Boolean”, etc.

  • +
  • value (Any) – Default value for variable. Can be changed later.

  • +
+
+
Returns:
+

Created variable object.

+
+
Return type:
+

QueryVariable

+
+
Raises:
+

KeyError – If variable was already added before.

+
+
+
+ +
+
+calculate_query()[source]
+

Calculate query string which is sent to server.

+
+
Returns:
+

GraphQl string with variables and headers.

+
+
Return type:
+

str

+
+
Raises:
+

ValueError – Query has no fiels.

+
+
+
+ +
+
+property child_indent
+

Indentation for preparation of query string used by children.

+
+
Returns:
+

Ident spaces for children.

+
+
Return type:
+

int

+
+
+
+ +
+
+continuous_query(con)[source]
+

Do a query from server.

+
+
Parameters:
+

con (ServerAPI) – Connection to server with ‘query’ method.

+
+
Returns:
+

Parsed output from GraphQl query.

+
+
Return type:
+

Dict[str, Any]

+
+
+
+ +
+
+get_field_by_keys(keys: Iterable[str]) Optional[BaseGraphQlQueryField][source]
+
+ +
+
+get_field_by_path(path: str) Optional[BaseGraphQlQueryField][source]
+
+ +
+
+get_variable(key)[source]
+

Variable object.

+
+
Parameters:
+

key (str) – Variable name added to headers.

+
+
Returns:
+

Variable object used in query string.

+
+
Return type:
+

QueryVariable

+
+
+
+ +
+
+get_variable_keys()[source]
+

Get all variable keys.

+
+
Returns:
+

Variable keys.

+
+
Return type:
+

set[str]

+
+
+
+ +
+
+get_variable_value(key, default=None)[source]
+

Get Current value of variable.

+
+
Parameters:
+
    +
  • key (str) – Variable name.

  • +
  • default (Any) – Default value if variable is available.

  • +
+
+
Returns:
+

Variable value.

+
+
Return type:
+

Any

+
+
+
+ +
+
+get_variables_values()[source]
+

Calculate variable values used that should be used in query.

+

Variables with value set to ‘None’ are skipped.

+
+
Returns:
+

Variable values by their name.

+
+
Return type:
+

Dict[str, Any]

+
+
+
+ +
+
+property has_multiple_edge_fields
+
+ +
+
+property indent
+

Indentation for preparation of query string.

+
+
Returns:
+

Ident spaces.

+
+
Return type:
+

int

+
+
+
+ +
+
+property need_query
+

Still need query from server.

+

Needed for edges which use pagination.

+
+
Returns:
+

If still need query from server.

+
+
Return type:
+

bool

+
+
+
+ +
+
+offset = 2
+
+ +
+
+parse_result(data, output, progress_data)[source]
+

Parse data from response for output.

+

Output is stored to passed ‘output’ variable. That’s because of paging +during which objects must have access to both new and previous values.

+
+
Parameters:
+
    +
  • data (Dict[str, Any]) – Data received using calculated query.

  • +
  • output (Dict[str, Any]) – Where parsed data are stored.

  • +
  • progress_data (Dict[str, Any]) – Data used for paging.

  • +
+
+
+
+ +
+
+query(con)[source]
+

Do a query from server.

+
+
Parameters:
+

con (ServerAPI) – Connection to server with ‘query’ method.

+
+
Returns:
+

Parsed output from GraphQl query.

+
+
Return type:
+

Dict[str, Any]

+
+
+
+ +
+
+set_variable_value(key, value)[source]
+

Set value for variable.

+
+
Parameters:
+
    +
  • key (str) – Variable name under which the value is stored.

  • +
  • value (Any) – Variable value used in query. Variable is not used +if value is ‘None’.

  • +
+
+
+
+ +
+ +
+
+class GraphQlQueryEdgeField(*args, **kwargs)[source]
+

Bases: BaseGraphQlQueryField

+
+
+add_edge_field(name)[source]
+
+ +
+
+add_obj_edge_field(field)[source]
+
+ +
+
+add_obj_field(field)[source]
+
+ +
+
+calculate_query()[source]
+
+ +
+
+property child_indent
+
+ +
+
+get_filters()[source]
+

Receive filters for item.

+

By default just use copy of set filters.

+
+
Returns:
+

Fields filters.

+
+
Return type:
+

Dict[str, Any]

+
+
+
+ +
+
+has_edges = True
+
+ +
+
+parse_result(data, output, progress_data)[source]
+
+ +
+
+reset_cursor()[source]
+
+ +
+ +
+
+class GraphQlQueryField(name, parent, order)[source]
+

Bases: BaseGraphQlQueryField

+
+
+calculate_query()[source]
+
+ +
+
+property child_indent
+
+ +
+
+has_edges = False
+
+ +
+
+parse_result(data, output, progress_data)[source]
+
+ +
+ +
+
+class QueryVariable(variable_name)[source]
+

Bases: object

+

Object representing single varible used in GraphQlQuery.

+

Variable definition is in GraphQl query header but it’s value is used +in fields.

+
+
Parameters:
+

variable_name (str) – Name of variable in query.

+
+
+
+
+property name
+

Name used in field filter.

+
+ +
+
+property variable_name
+

Name of variable in query definition.

+
+ +
+ +
+
+fields_to_dict(fields)[source]
+
+ +
+ + +
+ +
+ + +
+
+ +
+ +
+ + + + + + + + + + + + \ No newline at end of file diff --git a/ayon_api.graphql_queries.html b/ayon_api.graphql_queries.html new file mode 100644 index 0000000000..fb2635c62a --- /dev/null +++ b/ayon_api.graphql_queries.html @@ -0,0 +1,543 @@ + + + + + + + + + + + + ayon_api.graphql_queries module — ayon-python-api 1.0.11 documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + +
+ + + + + +
+ +
+ +
+ + + +
+
+
+ +
+

ayon_api.graphql_queries module

+
+
+activities_graphql_query(fields, order)[source]
+
+ +
+ +
+ +
+
+events_graphql_query(fields, order, use_states=False)[source]
+
+ +
+
+folders_graphql_query(fields)[source]
+
+ +
+
+product_types_query(fields)[source]
+
+ +
+
+products_graphql_query(fields)[source]
+
+ +
+
+project_graphql_query(fields)[source]
+
+ +
+
+project_product_types_query(fields)[source]
+
+ +
+
+projects_graphql_query(fields)[source]
+
+ +
+
+representations_graphql_query(fields)[source]
+
+ +
+
+representations_hierarchy_qraphql_query(folder_fields, task_fields, product_fields, version_fields, representation_fields)[source]
+
+ +
+
+representations_parents_qraphql_query(version_fields, product_fields, folder_fields)[source]
+
+ +
+
+tasks_by_folder_paths_graphql_query(fields)[source]
+
+ +
+
+tasks_graphql_query(fields)[source]
+
+ +
+
+users_graphql_query(fields)[source]
+
+ +
+
+versions_graphql_query(fields)[source]
+
+ +
+
+workfiles_info_graphql_query(fields)[source]
+
+ +
+ + +
+ +
+ + +
+
+ +
+ +
+ + + + + + + + + + + + \ No newline at end of file diff --git a/ayon_api.html b/ayon_api.html new file mode 100644 index 0000000000..d6d37c5a4f --- /dev/null +++ b/ayon_api.html @@ -0,0 +1,13059 @@ + + + + + + + + + + + + ayon_api package — ayon-python-api 1.0.11 documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + +
+ + + + + +
+ +
+ +
+ + + +
+
+
+ +
+

ayon_api package

+
+
+class GlobalServerAPI(site_id=None, client_version=None, default_settings_variant=None, ssl_verify=None, cert=None)[source]
+

Bases: ServerAPI

+

Extended server api which also handles storing tokens and url.

+

Created object expect to have set environment variables +‘AYON_SERVER_URL’. Also is expecting filled ‘AYON_API_KEY’ +but that can be filled afterwards with calling ‘login’ method.

+
+
+static get_token()[source]
+
+ +
+
+static get_url()[source]
+
+ +
+
+login(username, password)[source]
+

Login to the server or change user.

+

If user is the same as current user and token is available the +login is skipped.

+
+ +
+
+static set_environments(url, token)[source]
+

Change url and token environemnts in currently running process.

+
+
Parameters:
+
    +
  • url (str) – New server url.

  • +
  • token (str) – User’s token.

  • +
+
+
+
+ +
+ +
+
+class RequestTypes[source]
+

Bases: object

+
+
+delete = <ayon_api.server_api.RequestType object>
+
+ +
+
+get = <ayon_api.server_api.RequestType object>
+
+ +
+
+patch = <ayon_api.server_api.RequestType object>
+
+ +
+
+post = <ayon_api.server_api.RequestType object>
+
+ +
+
+put = <ayon_api.server_api.RequestType object>
+
+ +
+ +
+
+class ServerAPI(base_url, token=None, site_id=<object object>, client_version=None, default_settings_variant=None, sender_type=None, sender=None, ssl_verify=None, cert=None, create_session=True, timeout=None, max_retries=None)[source]
+

Bases: object

+

Base handler of connection to server.

+

Requires url to server which is used as base for api and graphql calls.

+

Login cause that a session is used

+
+
Parameters:
+
    +
  • base_url (str) – Example: http://localhost:5000

  • +
  • token (Optional[str]) – Access token (api key) to server.

  • +
  • site_id (Optional[str]) – Unique name of site. Should be the same when +connection is created from the same machine under same user.

  • +
  • client_version (Optional[str]) – Version of client application (used in +desktop client application).

  • +
  • default_settings_variant (Optional[Literal["production", "staging"]]) – Settings variant used by default if a method for settings won’t +get any (by default is ‘production’).

  • +
  • sender_type (Optional[str]) – Sender type of requests. Used in server +logs and propagated into events.

  • +
  • sender (Optional[str]) – Sender of requests, more specific than +sender type (e.g. machine name). Used in server logs and +propagated into events.

  • +
  • ssl_verify (Union[bool, str, None]) – Verify SSL certificate +Looks for env variable value AYON_CA_FILE by default. If not +available then ‘True’ is used.

  • +
  • cert (Optional[str]) – Path to certificate file. Looks for env +variable value AYON_CERT_FILE by default.

  • +
  • create_session (Optional[bool]) – Create session for connection if +token is available. Default is True.

  • +
  • timeout (Optional[float]) – Timeout for requests.

  • +
  • max_retries (Optional[int]) – Number of retries for requests.

  • +
+
+
+
+
+property access_token
+

Access token used for authorization to server.

+
+
Returns:
+

Token string or None if not authorized yet.

+
+
Return type:
+

Union[str, None]

+
+
+
+ +
+
+as_username(username, ignore_service_error=False)[source]
+

Service API will temporarily work as other user.

+

This method can be used only if service API key is logged in.

+
+
Parameters:
+
    +
  • username (Union[str, None]) – Username to work as when service.

  • +
  • ignore_service_error (Optional[bool]) – Ignore error when service +API key is not used.

  • +
+
+
Raises:
+

ValueError – When connection is not yet authenticated or api key + is not service token.

+
+
+
+ +
+
+property base_url
+
+ +
+
+property cert
+

Current cert file used for connection to server.

+
+
Returns:
+

Path to cert file.

+
+
Return type:
+

Union[str, None]

+
+
+
+ +
+
+check_bundle_compatibility(name, addon_versions, installer_version, dependency_packages=None, is_production=None, is_staging=None, is_dev=None, dev_active_user=None, dev_addons_config=None)[source]
+

Check bundle compatibility.

+

Can be used as per-flight validation before creating bundle.

+
+
Parameters:
+
    +
  • name (str) – Name of bundle.

  • +
  • addon_versions (dict[str, str]) – Addon versions.

  • +
  • installer_version (Union[str, None]) – Installer version.

  • +
  • dependency_packages (Optional[dict[str, str]]) – Dependency +package names. Keys are platform names and values are name of +packages.

  • +
  • is_production (Optional[bool]) – Bundle will be marked as +production.

  • +
  • is_staging (Optional[bool]) – Bundle will be marked as staging.

  • +
  • is_dev (Optional[bool]) – Bundle will be marked as dev.

  • +
  • dev_active_user (Optional[str]) – Username that will be assigned +to dev bundle. Can be used only if ‘is_dev’ is set to ‘True’.

  • +
  • dev_addons_config (Optional[dict[str, Any]]) – Configuration for +dev addons. Can be used only if ‘is_dev’ is set to ‘True’.

  • +
+
+
Returns:
+

Server response, with ‘success’ and ‘issues’.

+
+
Return type:
+

Dict[str, Any]

+
+
+
+ +
+
+property client_version
+

Version of client used to connect to server.

+

Client version is AYON client build desktop application.

+
+
Returns:
+

Client version string used in connection.

+
+
Return type:
+

str

+
+
+
+ +
+
+close_session()[source]
+
+ +
+
+create_activity(project_name: str, entity_id: str, entity_type: str, activity_type: ActivityType, activity_id: Optional[str] = None, body: Optional[str] = None, file_ids: Optional[List[str]] = None, timestamp: Optional[str] = None, data: Optional[Dict[str, Any]] = None) str[source]
+

Create activity on a project.

+
+
Parameters:
+
    +
  • project_name (str) – Project on which activity happened.

  • +
  • entity_id (str) – Entity id.

  • +
  • entity_type (str) – Entity type.

  • +
  • activity_type (ActivityType) – Activity type.

  • +
  • activity_id (Optional[str]) – Activity id.

  • +
  • body (Optional[str]) – Activity body.

  • +
  • file_ids (Optional[List[str]]) – List of file ids attached +to activity.

  • +
  • timestamp (Optional[str]) – Activity timestamp.

  • +
  • data (Optional[Dict[str, Any]]) – Additional data.

  • +
+
+
Returns:
+

Activity id.

+
+
Return type:
+

str

+
+
+
+ +
+
+create_bundle(name, addon_versions, installer_version, dependency_packages=None, is_production=None, is_staging=None, is_dev=None, dev_active_user=None, dev_addons_config=None)[source]
+

Create bundle on server.

+

Bundle cannot be changed once is created. Only isProduction, isStaging +and dependency packages can change after creation. In case dev bundle +is created, it is possible to change anything, but it is not possible +to mark bundle as dev and production or staging at the same time.

+

Development addon config can define custom path to client code. It is +used only for dev bundles.

+

Example of ‘dev_addons_config’:

+
```json
+{
+    "core": {
+        "enabled": true,
+        "path": "/path/to/ayon-core/client"
+    }
+}
+```
+
+
+
+
Parameters:
+
    +
  • name (str) – Name of bundle.

  • +
  • addon_versions (dict[str, str]) – Addon versions.

  • +
  • installer_version (Union[str, None]) – Installer version.

  • +
  • dependency_packages (Optional[dict[str, str]]) – Dependency +package names. Keys are platform names and values are name of +packages.

  • +
  • is_production (Optional[bool]) – Bundle will be marked as +production.

  • +
  • is_staging (Optional[bool]) – Bundle will be marked as staging.

  • +
  • is_dev (Optional[bool]) – Bundle will be marked as dev.

  • +
  • dev_active_user (Optional[str]) – Username that will be assigned +to dev bundle. Can be used only if ‘is_dev’ is set to ‘True’.

  • +
  • dev_addons_config (Optional[dict[str, Any]]) – Configuration for +dev addons. Can be used only if ‘is_dev’ is set to ‘True’.

  • +
+
+
+
+ +
+
+create_dependency_package(filename, python_modules, source_addons, installer_version, checksum, checksum_algorithm, file_size, sources=None, platform_name=None)[source]
+

Create dependency package on server.

+

The package will be created on a server, it is also required to upload +the package archive file (using upload_dependency_package()).

+
+
Parameters:
+
    +
  • filename (str) – Filename of dependency package.

  • +
  • python_modules (dict[str, str]) –

    Python modules in dependency +package:

    +
    {"<module name>": "<module version>", ...}
    +
    +
    +

  • +
  • source_addons (dict[str, str]) –

    Name of addons for which is +dependency package created:

    +
    {"<addon name>": "<addon version>", ...}
    +
    +
    +

  • +
  • installer_version (str) – Version of installer for which was +package created.

  • +
  • checksum (str) – Checksum of archive file where dependencies are.

  • +
  • checksum_algorithm (str) – Algorithm used to calculate checksum.

  • +
  • file_size (Optional[int]) – Size of file.

  • +
  • sources (Optional[list[dict[str, Any]]]) – Information about +sources from where it is possible to get file.

  • +
  • platform_name (Optional[str]) – Name of platform for which is +dependency package targeted. Default value is +current platform.

  • +
+
+
+
+ +
+
+create_folder(project_name, name, folder_type=None, parent_id=None, label=None, attrib=None, data=None, tags=None, status=None, active=None, thumbnail_id=None, folder_id=None)[source]
+

Create new folder.

+
+
Parameters:
+
    +
  • project_name (str) – Project name.

  • +
  • name (str) – Folder name.

  • +
  • folder_type (Optional[str]) – Folder type.

  • +
  • parent_id (Optional[str]) – Parent folder id. Parent is project +if is None.

  • +
  • label (Optional[str]) – Label of folder.

  • +
  • attrib (Optional[dict[str, Any]]) – Folder attributes.

  • +
  • data (Optional[dict[str, Any]]) – Folder data.

  • +
  • tags (Optional[Iterable[str]]) – Folder tags.

  • +
  • status (Optional[str]) – Folder status.

  • +
  • active (Optional[bool]) – Folder active state.

  • +
  • thumbnail_id (Optional[str]) – Folder thumbnail id.

  • +
  • folder_id (Optional[str]) – Folder id. If not passed new id is +generated.

  • +
+
+
Returns:
+

Entity id.

+
+
Return type:
+

str

+
+
+
+ +
+
+create_installer(filename, version, python_version, platform_name, python_modules, runtime_python_modules, checksum, checksum_algorithm, file_size, sources=None)[source]
+

Create new installer information on server.

+
+
This step will create only metadata. Make sure to upload installer

to the server using ‘upload_installer’ method.

+
+
Runtime python modules are modules that are required to run AYON

desktop application, but are not added to PYTHONPATH for any +subprocess.

+
+
+
+
Parameters:
+
    +
  • filename (str) – Installer filename.

  • +
  • version (str) – Version of installer.

  • +
  • python_version (str) – Version of Python.

  • +
  • platform_name (str) – Name of platform.

  • +
  • python_modules (dict[str, str]) – Python modules that are available +in installer.

  • +
  • runtime_python_modules (dict[str, str]) – Runtime python modules +that are available in installer.

  • +
  • checksum (str) – Installer file checksum.

  • +
  • checksum_algorithm (str) – Type of checksum used to create checksum.

  • +
  • file_size (int) – File size.

  • +
  • sources (Optional[list[dict[str, Any]]]) – List of sources that +can be used to download file.

  • +
+
+
+
+ +
+ +

Create link between 2 entities.

+

Link has a type which must already exists on a project.

+

Example output:

+
{
+    "id": "59a212c0d2e211eda0e20242ac120002"
+}
+
+
+
+
Parameters:
+
    +
  • project_name (str) – Project where the link is created.

  • +
  • link_type_name (str) – Type of link.

  • +
  • input_id (str) – Input entity id.

  • +
  • input_type (str) – Entity type of input entity.

  • +
  • output_id (str) – Output entity id.

  • +
  • output_type (str) – Entity type of output entity.

  • +
  • link_name (Optional[str]) – Name of link. +Available from server version ‘1.0.0-rc.6’.

  • +
+
+
Returns:
+

Information about link.

+
+
Return type:
+

dict[str, str]

+
+
Raises:
+

HTTPRequestError – Server error happened.

+
+
+
+ +
+ +

Create or update link type on server.

+
+

Warning

+

Because PUT is used for creation it is also used for update.

+
+
+
Parameters:
+
    +
  • project_name (str) – Project where link type is created.

  • +
  • link_type_name (str) – Name of link type.

  • +
  • input_type (str) – Input entity type of link.

  • +
  • output_type (str) – Output entity type of link.

  • +
  • data (Optional[dict[str, Any]]) – Additional data related to link.

  • +
+
+
Raises:
+

HTTPRequestError – Server error happened.

+
+
+
+ +
+
+create_product(project_name, name, product_type, folder_id, attrib=None, data=None, tags=None, status=None, active=None, product_id=None)[source]
+

Create new product.

+
+
Parameters:
+
    +
  • project_name (str) – Project name.

  • +
  • name (str) – Product name.

  • +
  • product_type (str) – Product type.

  • +
  • folder_id (str) – Parent folder id.

  • +
  • attrib (Optional[dict[str, Any]]) – Product attributes.

  • +
  • data (Optional[dict[str, Any]]) – Product data.

  • +
  • tags (Optional[Iterable[str]]) – Product tags.

  • +
  • status (Optional[str]) – Product status.

  • +
  • active (Optional[bool]) – Product active state.

  • +
  • product_id (Optional[str]) – Product id. If not passed new id is +generated.

  • +
+
+
Returns:
+

Product id.

+
+
Return type:
+

str

+
+
+
+ +
+
+create_project(project_name, project_code, library_project=False, preset_name=None)[source]
+

Create project using AYON settings.

+

This project creation function is not validating project entity on +creation. It is because project entity is created blindly with only +minimum required information about project which is name and code.

+

Entered project name must be unique and project must not exist yet.

+
+

Note

+
+
This function is here to be OP v4 ready but in v3 has more logic

to do. That’s why inner imports are in the body.

+
+
+
+
+
Parameters:
+
    +
  • project_name (str) – New project name. Should be unique.

  • +
  • project_code (str) – Project’s code should be unique too.

  • +
  • library_project (Optional[bool]) – Project is library project.

  • +
  • preset_name (Optional[str]) – Name of anatomy preset. Default is +used if not passed.

  • +
+
+
Raises:
+

ValueError – When project name already exists.

+
+
Returns:
+

Created project entity.

+
+
Return type:
+

dict[str, Any]

+
+
+
+ +
+
+create_representation(project_name, name, version_id, files=None, attrib=None, data=None, tags=None, status=None, active=None, representation_id=None)[source]
+

Create new representation.

+
+
Parameters:
+
    +
  • project_name (str) – Project name.

  • +
  • name (str) – Representation name.

  • +
  • version_id (str) – Parent version id.

  • +
  • files (Optional[list[dict]]) – Representation files information.

  • +
  • attrib (Optional[dict[str, Any]]) – Representation attributes.

  • +
  • data (Optional[dict[str, Any]]) – Representation data.

  • +
  • tags (Optional[Iterable[str]]) – Representation tags.

  • +
  • status (Optional[str]) – Representation status.

  • +
  • active (Optional[bool]) – Representation active state.

  • +
  • representation_id (Optional[str]) – Representation id. If not +passed new id is generated.

  • +
+
+
Returns:
+

Representation id.

+
+
Return type:
+

str

+
+
+
+ +
+
+create_session(ignore_existing=True, force=False)[source]
+

Create a connection session.

+

Session helps to keep connection with server without +need to reconnect on each call.

+
+
Parameters:
+
    +
  • ignore_existing (bool) – If session already exists, +ignore creation.

  • +
  • force (bool) – If session already exists, close it and +create new.

  • +
+
+
+
+ +
+
+create_task(project_name, name, task_type, folder_id, label=None, assignees=None, attrib=None, data=None, tags=None, status=None, active=None, thumbnail_id=None, task_id=None)[source]
+

Create new task.

+
+
Parameters:
+
    +
  • project_name (str) – Project name.

  • +
  • name (str) – Folder name.

  • +
  • task_type (str) – Task type.

  • +
  • folder_id (str) – Parent folder id.

  • +
  • label (Optional[str]) – Label of folder.

  • +
  • assignees (Optional[Iterable[str]]) – Task assignees.

  • +
  • attrib (Optional[dict[str, Any]]) – Task attributes.

  • +
  • data (Optional[dict[str, Any]]) – Task data.

  • +
  • tags (Optional[Iterable[str]]) – Task tags.

  • +
  • status (Optional[str]) – Task status.

  • +
  • active (Optional[bool]) – Task active state.

  • +
  • thumbnail_id (Optional[str]) – Task thumbnail id.

  • +
  • task_id (Optional[str]) – Task id. If not passed new id is +generated.

  • +
+
+
Returns:
+

Task id.

+
+
Return type:
+

str

+
+
+
+ +
+
+create_thumbnail(project_name, src_filepath, thumbnail_id=None)[source]
+

Create new thumbnail on server from passed path.

+
+
Parameters:
+
    +
  • project_name (str) – Project where the thumbnail will be created +and can be used.

  • +
  • src_filepath (str) – Filepath to thumbnail which should be uploaded.

  • +
  • thumbnail_id (Optional[str]) – Prepared if of thumbnail.

  • +
+
+
Returns:
+

Created thumbnail id.

+
+
Return type:
+

str

+
+
Raises:
+

ValueError – When thumbnail source cannot be processed.

+
+
+
+ +
+
+create_version(project_name, version, product_id, task_id=None, author=None, attrib=None, data=None, tags=None, status=None, active=None, thumbnail_id=None, version_id=None)[source]
+

Create new version.

+
+
Parameters:
+
    +
  • project_name (str) – Project name.

  • +
  • version (int) – Version.

  • +
  • product_id (str) – Parent product id.

  • +
  • task_id (Optional[str]) – Parent task id.

  • +
  • author (Optional[str]) – Version author.

  • +
  • attrib (Optional[dict[str, Any]]) – Version attributes.

  • +
  • data (Optional[dict[str, Any]]) – Version data.

  • +
  • tags (Optional[Iterable[str]]) – Version tags.

  • +
  • status (Optional[str]) – Version status.

  • +
  • active (Optional[bool]) – Version active state.

  • +
  • thumbnail_id (Optional[str]) – Version thumbnail id.

  • +
  • version_id (Optional[str]) – Version id. If not passed new id is +generated.

  • +
+
+
Returns:
+

Version id.

+
+
Return type:
+

str

+
+
+
+ +
+
+default_download_chunk_size = 1048576
+
+ +
+
+property default_settings_variant
+

Default variant used for settings.

+
+
Returns:
+

name of variant or None.

+
+
Return type:
+

Union[str, None]

+
+
+
+ +
+
+default_upload_chunk_size = 1048576
+
+ +
+
+delete(entrypoint, **kwargs)[source]
+
+ +
+
+delete_activity(project_name: str, activity_id: str)[source]
+

Delete activity by id.

+
+
Parameters:
+
    +
  • project_name (str) – Project on which activity happened.

  • +
  • activity_id (str) – Activity id to remove.

  • +
+
+
+
+ +
+
+delete_addon(addon_name: str, purge: Optional[bool] = None)[source]
+

Delete addon from server.

+

Delete all versions of addon from server.

+
+
Parameters:
+
    +
  • addon_name (str) – Addon name.

  • +
  • purge (Optional[bool]) – Purge all data related to the addon.

  • +
+
+
+
+ +
+
+delete_addon_version(addon_name: str, addon_version: str, purge: Optional[bool] = None)[source]
+

Delete addon version from server.

+

Delete all versions of addon from server.

+
+
Parameters:
+
    +
  • addon_name (str) – Addon name.

  • +
  • addon_version (str) – Addon version.

  • +
  • purge (Optional[bool]) – Purge all data related to the addon.

  • +
+
+
+
+ +
+
+delete_bundle(bundle_name)[source]
+

Delete bundle from server.

+
+
Parameters:
+

bundle_name (str) – Name of bundle to delete.

+
+
+
+ +
+
+delete_dependency_package(filename, platform_name=None)[source]
+

Remove dependency package for specific platform.

+
+
Parameters:
+
    +
  • filename (str) – Filename of dependency package.

  • +
  • platform_name (Optional[str]) – Deprecated.

  • +
+
+
+
+ +
+
+delete_event(event_id: str)[source]
+

Delete event by id.

+

Supported since AYON server 1.6.0.

+
+
Parameters:
+

event_id (str) – Event id.

+
+
Returns:
+

Response from server.

+
+
Return type:
+

RestApiResponse

+
+
+
+ +
+
+delete_folder(project_name, folder_id, force=False)[source]
+

Delete folder.

+
+
Parameters:
+
    +
  • project_name (str) – Project name.

  • +
  • folder_id (str) – Folder id to delete.

  • +
  • force (Optional[bool]) – Folder delete folder with all children +folder, products, versions and representations.

  • +
+
+
+
+ +
+
+delete_installer(filename)[source]
+

Delete installer from server.

+
+
Parameters:
+

filename (str) – Installer filename.

+
+
+
+ +
+ +

Remove link by id.

+
+
Parameters:
+
    +
  • project_name (str) – Project where link exists.

  • +
  • link_id (str) – Id of link.

  • +
+
+
Raises:
+

HTTPRequestError – Server error happened.

+
+
+
+ +
+ +

Remove link type from project.

+
+
Parameters:
+
    +
  • project_name (str) – Project where link type is created.

  • +
  • link_type_name (str) – Name of link type.

  • +
  • input_type (str) – Input entity type of link.

  • +
  • output_type (str) – Output entity type of link.

  • +
+
+
Raises:
+

HTTPRequestError – Server error happened.

+
+
+
+ +
+
+delete_product(project_name, product_id)[source]
+

Delete product.

+
+
Parameters:
+
    +
  • project_name (str) – Project name.

  • +
  • product_id (str) – Product id to delete.

  • +
+
+
+
+ +
+
+delete_project(project_name)[source]
+

Delete project from server.

+

This will completely remove project from server without any step back.

+
+
Parameters:
+

project_name (str) – Project name that will be removed.

+
+
+
+ +
+
+delete_representation(project_name, representation_id)[source]
+

Delete representation.

+
+
Parameters:
+
    +
  • project_name (str) – Project name.

  • +
  • representation_id (str) – Representation id to delete.

  • +
+
+
+
+ +
+
+delete_secret(secret_name)[source]
+

Delete secret by name.

+
+
Parameters:
+

secret_name (str) – Name of secret to delete.

+
+
+
+ +
+
+delete_task(project_name, task_id)[source]
+

Delete task.

+
+
Parameters:
+
    +
  • project_name (str) – Project name.

  • +
  • task_id (str) – Task id to delete.

  • +
+
+
+
+ +
+
+delete_version(project_name, version_id)[source]
+

Delete version.

+
+
Parameters:
+
    +
  • project_name (str) – Project name.

  • +
  • version_id (str) – Version id to delete.

  • +
+
+
+
+ +
+
+dispatch_event(topic, sender=None, event_hash=None, project_name=None, username=None, depends_on=None, description=None, summary=None, payload=None, finished=True, store=True, dependencies=None)[source]
+

Dispatch event to server.

+
+
Parameters:
+
    +
  • topic (str) – Event topic used for filtering of listeners.

  • +
  • sender (Optional[str]) – Sender of event.

  • +
  • event_hash (Optional[str]) – Event hash.

  • +
  • project_name (Optional[str]) – Project name.

  • +
  • depends_on (Optional[str]) – Add dependency to another event.

  • +
  • username (Optional[str]) – Username which triggered event.

  • +
  • description (Optional[str]) – Description of event.

  • +
  • summary (Optional[dict[str, Any]]) – Summary of event that can +be used for simple filtering on listeners.

  • +
  • payload (Optional[dict[str, Any]]) – Full payload of event data with +all details.

  • +
  • finished (Optional[bool]) – Mark event as finished on dispatch.

  • +
  • store (Optional[bool]) – Store event in event queue for possible +future processing otherwise is event send only +to active listeners.

  • +
  • dependencies (Optional[list[str]]) – Deprecated. +List of event id dependencies.

  • +
+
+
Returns:
+

Response from server.

+
+
Return type:
+

RestApiResponse

+
+
+
+ +
+
+download_addon_private_file(addon_name, addon_version, filename, destination_dir, destination_filename=None, chunk_size=None, progress=None)[source]
+

Download a file from addon private files.

+

This method requires to have authorized token available. Private files +are not under ‘/api’ restpoint.

+
+
Parameters:
+
    +
  • addon_name (str) – Addon name.

  • +
  • addon_version (str) – Addon version.

  • +
  • filename (str) – Filename in private folder on server.

  • +
  • destination_dir (str) – Where the file should be downloaded.

  • +
  • destination_filename (Optional[str]) – Name of destination +filename. Source filename is used if not passed.

  • +
  • chunk_size (Optional[int]) – Download chunk size.

  • +
  • progress (Optional[TransferProgress]) – Object that gives ability +to track download progress.

  • +
+
+
Returns:
+

Filepath to downloaded file.

+
+
Return type:
+

str

+
+
+
+ +
+
+download_dependency_package(src_filename, dst_directory, dst_filename, platform_name=None, chunk_size=None, progress=None)[source]
+

Download dependency package from server.

+

This method requires to have authorized token available. The package +is only downloaded.

+
+
Parameters:
+
    +
  • src_filename (str) – Filename of dependency pacakge. +For server version 0.2.0 and lower it is name of package +to download.

  • +
  • dst_directory (str) – Where the file should be downloaded.

  • +
  • dst_filename (str) – Name of destination filename.

  • +
  • platform_name (Optional[str]) – Deprecated.

  • +
  • chunk_size (Optional[int]) – Download chunk size.

  • +
  • progress (Optional[TransferProgress]) – Object that gives ability +to track download progress.

  • +
+
+
Returns:
+

Filepath to downloaded file.

+
+
Return type:
+

str

+
+
+
+ +
+
+download_file(endpoint, filepath, chunk_size=None, progress=None)[source]
+

Download file from AYON server.

+

Endpoint can be full url (must start with ‘base_url’ of api object).

+

Progress object can be used to track download. Can be used when +download happens in thread and other thread want to catch changes over +time.

+
+
Todos:

Use retries and timeout. +Return RestApiResponse.

+
+
+
+
Parameters:
+
    +
  • endpoint (str) – Endpoint or URL to file that should be downloaded.

  • +
  • filepath (str) – Path where file will be downloaded.

  • +
  • chunk_size (Optional[int]) – Size of chunks that are received +in single loop.

  • +
  • progress (Optional[TransferProgress]) – Object that gives ability +to track download progress.

  • +
+
+
+
+ +
+
+download_file_to_stream(endpoint, stream, chunk_size=None, progress=None)[source]
+

Download file from AYON server to IOStream.

+

Endpoint can be full url (must start with ‘base_url’ of api object).

+

Progress object can be used to track download. Can be used when +download happens in thread and other thread want to catch changes over +time.

+
+
Todos:

Use retries and timeout. +Return RestApiResponse.

+
+
+
+
Parameters:
+
    +
  • endpoint (str) – Endpoint or URL to file that should be downloaded.

  • +
  • stream (Union[io.BytesIO, BinaryIO]) – Stream where output will +be stored.

  • +
  • chunk_size (Optional[int]) – Size of chunks that are received +in single loop.

  • +
  • progress (Optional[TransferProgress]) – Object that gives ability +to track download progress.

  • +
+
+
+
+ +
+
+download_installer(filename, dst_filepath, chunk_size=None, progress=None)[source]
+

Download installer file from server.

+
+
Parameters:
+
    +
  • filename (str) – Installer filename.

  • +
  • dst_filepath (str) – Destination filepath.

  • +
  • chunk_size (Optional[int]) – Download chunk size.

  • +
  • progress (Optional[TransferProgress]) – Object that gives ability +to track download progress.

  • +
+
+
+
+ +
+
+enroll_event_job(source_topic, target_topic, sender, description=None, sequential=None, events_filter=None, max_retries=None, ignore_older_than=None, ignore_sender_types=None)[source]
+

Enroll job based on events.

+

Enroll will find first unprocessed event with ‘source_topic’ and will +create new event with ‘target_topic’ for it and return the new event +data.

+

Use ‘sequential’ to control that only single target event is created +at same time. Creation of new target events is blocked while there is +at least one unfinished event with target topic, when set to ‘True’. +This helps when order of events matter and more than one process using +the same target is running at the same time.

+

Make sure the new event has updated status to ‘“finished”’ status +when you’re done with logic

+

Target topic should not clash with other processes/services.

+

Created target event have ‘dependsOn’ key where is id of source topic.

+
+
Use-case:
    +
  • Service 1 is creating events with topic ‘my.leech’

  • +
  • +
    Service 2 process ‘my.leech’ and uses target topic ‘my.process’
      +
    • this service can run on 1-n machines

    • +
    • +
      all events must be processed in a sequence by their creation

      time and only one event can be processed at a time

      +
      +
      +
    • +
    • +
      in this case ‘sequential’ should be set to ‘True’ so only

      one machine is actually processing events, but if one goes +down there are other that can take place

      +
      +
      +
    • +
    +
    +
    +
  • +
  • +
    Service 3 process ‘my.leech’ and uses target topic ‘my.discover’
      +
    • this service can run on 1-n machines

    • +
    • order of events is not important

    • +
    • ‘sequential’ should be ‘False’

    • +
    +
    +
    +
  • +
+
+
+
+
Parameters:
+
    +
  • source_topic (str) – Source topic to enroll.

  • +
  • target_topic (str) – Topic of dependent event.

  • +
  • sender (str) – Identifier of sender (e.g. service name or username).

  • +
  • description (Optional[str]) – Human readable text shown +in target event.

  • +
  • sequential (Optional[bool]) – The source topic must be processed +in sequence.

  • +
  • events_filter (Optional[dict[str, Any]]) – Filtering conditions +to filter the source event. For more technical specifications +look to server backed ‘ayon_server.sqlfilter.Filter’. +TODO: Add example of filters.

  • +
  • max_retries (Optional[int]) – How many times can be event retried. +Default value is based on server (3 at the time of this PR).

  • +
  • ignore_older_than (Optional[int]) – Ignore events older than +given number in days.

  • +
  • ignore_sender_types (Optional[List[str]]) – Ignore events triggered +by given sender types.

  • +
+
+
Returns:
+

+
None if there is no event matching

filters. Created event with ‘target_topic’.

+
+
+

+
+
Return type:
+

Union[None, dict[str, Any]]

+
+
+
+ +
+
+get(entrypoint, **kwargs)[source]
+
+ +
+
+get_activities(project_name: str, activity_ids: Optional[Iterable[str]] = None, activity_types: Optional[Iterable[ActivityType]] = None, entity_ids: Optional[Iterable[str]] = None, entity_names: Optional[Iterable[str]] = None, entity_type: Optional[str] = None, changed_after: Optional[str] = None, changed_before: Optional[str] = None, reference_types: Optional[Iterable[ActivityReferenceType]] = None, fields: Optional[Iterable[str]] = None, limit: Optional[int] = None, order: Optional[SortOrder] = None) Generator[Dict[str, Any], None, None][source]
+

Get activities from server with filtering options.

+
+
Parameters:
+
    +
  • project_name (str) – Project on which activities happened.

  • +
  • activity_ids (Optional[Iterable[str]]) – Activity ids.

  • +
  • activity_types (Optional[Iterable[ActivityType]]) – Activity types.

  • +
  • entity_ids (Optional[Iterable[str]]) – Entity ids.

  • +
  • entity_names (Optional[Iterable[str]]) – Entity names.

  • +
  • entity_type (Optional[str]) – Entity type.

  • +
  • changed_after (Optional[str]) – Return only activities changed +after given iso datetime string.

  • +
  • changed_before (Optional[str]) – Return only activities changed +before given iso datetime string.

  • +
  • reference_types (Optional[Iterable[ActivityReferenceType]]) – Reference types filter. Defaults to [‘origin’].

  • +
  • fields (Optional[Iterable[str]]) – Fields that should be received +for each activity.

  • +
  • limit (Optional[int]) – Limit number of activities to be fetched.

  • +
  • order (Optional[SortOrder]) – Order activities in ascending +or descending order. It is recommended to set ‘limit’ +when used descending.

  • +
+
+
Returns:
+

Available activities matching filters.

+
+
Return type:
+

Generator[dict[str, Any]]

+
+
+
+ +
+
+get_activity_by_id(project_name: str, activity_id: str, reference_types: Optional[Iterable[ActivityReferenceType]] = None, fields: Optional[Iterable[str]] = None) Optional[Dict[str, Any]][source]
+

Get activity by id.

+
+
Parameters:
+
    +
  • project_name (str) – Project on which activity happened.

  • +
  • activity_id (str) – Activity id.

  • +
  • fields (Optional[Iterable[str]]) – Fields that should be received +for each activity.

  • +
+
+
Returns:
+

+
Activity data or None if activity is not

found.

+
+
+

+
+
Return type:
+

Optional[Dict[str, Any]]

+
+
+
+ +
+
+get_addon_endpoint(addon_name, addon_version, *subpaths)[source]
+

Calculate endpoint to addon route.

+
+

Examples

+
>>> api = ServerAPI("https://your.url.com")
+>>> api.get_addon_url(
+...     "example", "1.0.0", "private", "my.zip")
+'addons/example/1.0.0/private/my.zip'
+
+
+
+
+
Parameters:
+
    +
  • addon_name (str) – Name of addon.

  • +
  • addon_version (str) – Version of addon.

  • +
  • *subpaths (str) – Any amount of subpaths that are added to +addon url.

  • +
+
+
Returns:
+

Final url.

+
+
Return type:
+

str

+
+
+
+ +
+
+get_addon_project_settings(addon_name, addon_version, project_name, variant=None, site_id=None, use_site=True)[source]
+

Addon project settings.

+

Receive project settings for specific version of an addon. The settings +may be with site overrides when enabled.

+

Site id is filled with current connection site id if not passed. To +make sure any site id is used set ‘use_site’ to ‘False’.

+
+
Parameters:
+
    +
  • addon_name (str) – Name of addon.

  • +
  • addon_version (str) – Version of addon.

  • +
  • project_name (str) – Name of project for which the settings are +received.

  • +
  • variant (Optional[Literal['production', 'staging']]) – Name of +settings variant. Used ‘default_settings_variant’ by default.

  • +
  • site_id (Optional[str]) – Name of site which is used for site +overrides. Is filled with connection ‘site_id’ attribute +if not passed.

  • +
  • use_site (Optional[bool]) – To force disable option of using site +overrides set to ‘False’. In that case won’t be applied +any site overrides.

  • +
+
+
Returns:
+

Addon settings.

+
+
Return type:
+

dict[str, Any]

+
+
+
+ +
+
+get_addon_settings(addon_name, addon_version, project_name=None, variant=None, site_id=None, use_site=True)[source]
+

Receive addon settings.

+

Receive addon settings based on project name value. Some arguments may +be ignored if ‘project_name’ is set to ‘None’.

+
+
Parameters:
+
    +
  • addon_name (str) – Name of addon.

  • +
  • addon_version (str) – Version of addon.

  • +
  • project_name (Optional[str]) – Name of project for which the +settings are received. A studio settings values are received +if is ‘None’.

  • +
  • variant (Optional[Literal['production', 'staging']]) – Name of +settings variant. Used ‘default_settings_variant’ by default.

  • +
  • site_id (Optional[str]) – Name of site which is used for site +overrides. Is filled with connection ‘site_id’ attribute +if not passed.

  • +
  • use_site (Optional[bool]) – To force disable option of using +site overrides set to ‘False’. In that case won’t be applied +any site overrides.

  • +
+
+
Returns:
+

Addon settings.

+
+
Return type:
+

dict[str, Any]

+
+
+
+ +
+
+get_addon_settings_schema(addon_name, addon_version, project_name=None)[source]
+

Sudio/Project settings schema of an addon.

+

Project schema may look differently as some enums are based on project +values.

+
+
Parameters:
+
    +
  • addon_name (str) – Name of addon.

  • +
  • addon_version (str) – Version of addon.

  • +
  • project_name (Optional[str]) – Schema for specific project or +default studio schemas.

  • +
+
+
Returns:
+

Schema of studio/project settings.

+
+
Return type:
+

dict[str, Any]

+
+
+
+ +
+
+get_addon_site_settings(addon_name, addon_version, site_id=None)[source]
+

Site settings of an addon.

+

If site id is not available an empty dictionary is returned.

+
+
Parameters:
+
    +
  • addon_name (str) – Name of addon.

  • +
  • addon_version (str) – Version of addon.

  • +
  • site_id (Optional[str]) – Name of site for which should be settings +returned. using ‘site_id’ attribute if not passed.

  • +
+
+
Returns:
+

Site settings.

+
+
Return type:
+

dict[str, Any]

+
+
+
+ +
+
+get_addon_site_settings_schema(addon_name, addon_version)[source]
+

Site settings schema of an addon.

+
+
Parameters:
+
    +
  • addon_name (str) – Name of addon.

  • +
  • addon_version (str) – Version of addon.

  • +
+
+
Returns:
+

Schema of site settings.

+
+
Return type:
+

dict[str, Any]

+
+
+
+ +
+
+get_addon_studio_settings(addon_name, addon_version, variant=None)[source]
+

Addon studio settings.

+

Receive studio settings for specific version of an addon.

+
+
Parameters:
+
    +
  • addon_name (str) – Name of addon.

  • +
  • addon_version (str) – Version of addon.

  • +
  • variant (Optional[Literal['production', 'staging']]) – Name of +settings variant. Used ‘default_settings_variant’ by default.

  • +
+
+
Returns:
+

Addon settings.

+
+
Return type:
+

dict[str, Any]

+
+
+
+ +
+
+get_addon_url(addon_name, addon_version, *subpaths, use_rest=True)[source]
+

Calculate url to addon route.

+
+

Examples

+
>>> api = ServerAPI("https://your.url.com")
+>>> api.get_addon_url(
+...     "example", "1.0.0", "private", "my.zip")
+'https://your.url.com/api/addons/example/1.0.0/private/my.zip'
+
+
+
+
+
Parameters:
+
    +
  • addon_name (str) – Name of addon.

  • +
  • addon_version (str) – Version of addon.

  • +
  • *subpaths (str) – Any amount of subpaths that are added to +addon url.

  • +
  • use_rest (Optional[bool]) – Use rest endpoint.

  • +
+
+
Returns:
+

Final url.

+
+
Return type:
+

str

+
+
+
+ +
+
+get_addons_info(details=True)[source]
+

Get information about addons available on server.

+
+
Parameters:
+

details (Optional[bool]) – Detailed data with information how +to get client code.

+
+
+
+ +
+
+get_addons_project_settings(project_name, bundle_name=None, variant=None, site_id=None, use_site=True, only_values=True)[source]
+

Project settings of all addons.

+

Server returns information about used addon versions, so full output +looks like:

+
+
```json
+
{

“settings”: {…}, +“addons”: {…}

+
+
+

}

+
+
+

```

+

The output can be limited to only values. To do so is ‘only_values’ +argument which is by default set to ‘True’. In that case output +contains only value of ‘settings’ key.

+
+

Warning

+
+
Behavior of this function changed with AYON server version 0.3.0.

Structure of output from server changed. If using +‘only_values=True’ then output should be same as before.

+
+
+
+
+
Parameters:
+
    +
  • project_name (str) – Name of project for which are settings +received.

  • +
  • bundle_name (Optional[str]) – Name of bundle for which should be +settings received.

  • +
  • variant (Optional[Literal['production', 'staging']]) – Name of +settings variant. Used ‘default_settings_variant’ by default.

  • +
  • site_id (Optional[str]) – Site id for which want to receive +site overrides.

  • +
  • use_site (bool) – To force disable option of using site overrides +set to ‘False’. In that case won’t be applied any site +overrides.

  • +
  • only_values (Optional[bool]) – Output will contain only settings +values without metadata about addons.

  • +
+
+
Returns:
+

+
Settings of all addons on server for passed

project.

+
+
+

+
+
Return type:
+

dict[str, Any]

+
+
+
+ +
+
+get_addons_settings(bundle_name=None, project_name=None, variant=None, site_id=None, use_site=True, only_values=True)[source]
+

Universal function to receive all addon settings.

+

Based on ‘project_name’ will receive studio settings or project +settings. In case project is not passed is ‘site_id’ ignored.

+
+

Warning

+
+
Behavior of this function changed with AYON server version 0.3.0.

Structure of output from server changed. If using +‘only_values=True’ then output should be same as before.

+
+
+
+
+
Parameters:
+
    +
  • bundle_name (Optional[str]) – Name of bundle for which should be +settings received.

  • +
  • project_name (Optional[str]) – Name of project for which should be +settings received.

  • +
  • variant (Optional[Literal['production', 'staging']]) – Name of +settings variant. Used ‘default_settings_variant’ by default.

  • +
  • site_id (Optional[str]) – Id of site for which want to receive +site overrides.

  • +
  • use_site (Optional[bool]) – To force disable option of using site +overrides set to ‘False’. In that case won’t be applied +any site overrides.

  • +
  • only_values (Optional[bool]) – Only settings values will be +returned. By default, is set to ‘True’.

  • +
+
+
+
+ +
+
+get_addons_studio_settings(bundle_name=None, variant=None, site_id=None, use_site=True, only_values=True)[source]
+

All addons settings in one bulk.

+
+

Warning

+
+
Behavior of this function changed with AYON server version 0.3.0.

Structure of output from server changed. If using +‘only_values=True’ then output should be same as before.

+
+
+
+
+
Parameters:
+
    +
  • bundle_name (Optional[str]) – Name of bundle for which should be +settings received.

  • +
  • variant (Optional[Literal['production', 'staging']]) – Name of +settings variant. Used ‘default_settings_variant’ by default.

  • +
  • site_id (Optional[str]) – Site id for which want to receive +site overrides.

  • +
  • use_site (bool) – To force disable option of using site overrides +set to ‘False’. In that case won’t be applied any site +overrides.

  • +
  • only_values (Optional[bool]) – Output will contain only settings +values without metadata about addons.

  • +
+
+
Returns:
+

Settings of all addons on server.

+
+
Return type:
+

dict[str, Any]

+
+
+
+ +
+
+get_attributes_fields_for_type(entity_type)[source]
+

Prepare attribute fields for entity type.

+
+
Returns:
+

Attributes fields for entity type.

+
+
Return type:
+

set[str]

+
+
+
+ +
+
+get_attributes_for_type(entity_type)[source]
+

Get attribute schemas available for an entity type.

+

Example:

+
```
+# Example attribute schema
+{
+    # Common
+    "type": "integer",
+    "title": "Clip Out",
+    "description": null,
+    "example": 1,
+    "default": 1,
+    # These can be filled based on value of 'type'
+    "gt": null,
+    "ge": null,
+    "lt": null,
+    "le": null,
+    "minLength": null,
+    "maxLength": null,
+    "minItems": null,
+    "maxItems": null,
+    "regex": null,
+    "enum": null
+}
+```
+
+
+
+
Parameters:
+

entity_type (str) – Entity type for which should be attributes +received.

+
+
Returns:
+

+
Attribute schemas that are available

for entered entity type.

+
+
+

+
+
Return type:
+

dict[str, dict[str, Any]]

+
+
+
+ +
+
+get_attributes_schema(use_cache=True)[source]
+
+ +
+
+get_base_url()[source]
+
+ +
+
+get_build_in_anatomy_preset()[source]
+

Get built-in anatomy preset.

+
+
Returns:
+

Built-in anatomy preset.

+
+
Return type:
+

dict[str, Any]

+
+
+
+ +
+
+get_bundle_settings(bundle_name=None, project_name=None, variant=None, site_id=None, use_site=True)[source]
+

Get complete set of settings for given data.

+

If project is not passed then studio settings are returned. If variant +is not passed ‘default_settings_variant’ is used. If bundle name is +not passed then current production/staging bundle is used, based on +variant value.

+

Output contains addon settings and site settings in single dictionary.

+
+
Todos:
    +
  • test how it behaves if there is not any bundle.

  • +
  • +
    test how it behaves if there is not any production/staging

    bundle.

    +
    +
    +
  • +
+
+
+

Example output:

+
{
+    "addons": [
+        {
+            "name": "addon-name",
+            "version": "addon-version",
+            "settings": {...},
+            "siteSettings": {...}
+        }
+    ]
+}
+
+
+
+
Returns:
+

All settings for single bundle.

+
+
Return type:
+

dict[str, Any]

+
+
+
+ +
+
+get_bundles()[source]
+

Server bundles with basic information.

+

This is example output:

+
{
+    "bundles": [
+        {
+            "name": "my_bundle",
+            "createdAt": "2023-06-12T15:37:02.420260",
+            "installerVersion": "1.0.0",
+            "addons": {
+                "core": "1.2.3"
+            },
+            "dependencyPackages": {
+                "windows": "a_windows_package123.zip",
+                "linux": "a_linux_package123.zip",
+                "darwin": "a_mac_package123.zip"
+            },
+            "isProduction": False,
+            "isStaging": False
+        }
+    ],
+    "productionBundle": "my_bundle",
+    "stagingBundle": "test_bundle"
+}
+
+
+
+
Returns:
+

Server bundles with basic information.

+
+
Return type:
+

dict[str, Any]

+
+
+
+ +
+
+get_cert()[source]
+

Current cert file used for connection to server.

+
+
Returns:
+

Path to cert file.

+
+
Return type:
+

Union[str, None]

+
+
+
+ +
+
+get_client_version()[source]
+

Version of client used to connect to server.

+

Client version is AYON client build desktop application.

+
+
Returns:
+

Client version string used in connection.

+
+
Return type:
+

str

+
+
+
+ +
+
+get_default_anatomy_preset_name()[source]
+

Name of default anatomy preset.

+

Primary preset is used as default preset. But when primary preset is +not set a built-in is used instead. Built-in preset is named ‘_’.

+
+
Returns:
+

+
Name of preset that can be used by

’get_project_anatomy_preset’.

+
+
+

+
+
Return type:
+

str

+
+
+
+ +
+
+get_default_fields_for_type(entity_type)[source]
+

Default fields for entity type.

+

Returns most of commonly used fields from server.

+
+
Parameters:
+

entity_type (str) – Name of entity type.

+
+
Returns:
+

Fields that should be queried from server.

+
+
Return type:
+

set[str]

+
+
+
+ +
+
+classmethod get_default_max_retries()[source]
+

Default value for requests max retries.

+

First looks for environment variable SERVER_RETRIES_ENV_KEY, which +can affect max retries value. If not available then use class +attribute ‘_default_max_retries’.

+
+
Returns:
+

Max retries value.

+
+
Return type:
+

int

+
+
+
+ +
+
+get_default_service_username()[source]
+

Default username used for callbacks when used with service API key.

+
+
Returns:
+

Username if any was filled.

+
+
Return type:
+

Union[str, None]

+
+
+
+ +
+
+get_default_settings_variant()[source]
+

Default variant used for settings.

+
+
Returns:
+

name of variant or None.

+
+
Return type:
+

Union[str, None]

+
+
+
+ +
+
+classmethod get_default_timeout()[source]
+

Default value for requests timeout.

+

Utils function ‘get_default_timeout’ is used by default.

+
+
Returns:
+

Timeout value in seconds.

+
+
Return type:
+

float

+
+
+
+ +
+
+get_dependency_packages()[source]
+

Information about dependency packages on server.

+

To download dependency package, use ‘download_dependency_package’ +method and pass in ‘filename’.

+

Example data structure:

+
{
+    "packages": [
+        {
+            "filename": str,
+            "platform": str,
+            "checksum": str,
+            "checksumAlgorithm": str,
+            "size": int,
+            "sources": list[dict[str, Any]],
+            "supportedAddons": dict[str, str],
+            "pythonModules": dict[str, str]
+        }
+    ]
+}
+
+
+
+
Returns:
+

+
Information about dependency packages known for

server.

+
+
+

+
+
Return type:
+

dict[str, Any]

+
+
+
+ +
+ +

Helper method to get links from server for entity types.

+
Example output:
+{
+    "59a212c0d2e211eda0e20242ac120001": [
+        {
+            "id": "59a212c0d2e211eda0e20242ac120002",
+            "linkType": "reference",
+            "description": "reference link between folders",
+            "projectName": "my_project",
+            "author": "frantadmin",
+            "entityId": "b1df109676db11ed8e8c6c9466b19aa8",
+            "entityType": "folder",
+            "direction": "out"
+        },
+        ...
+    ],
+    ...
+}
+
+
+
+
Parameters:
+
    +
  • project_name (str) – Project where links are.

  • +
  • (Literal["folder" (entity_type) –

  • +
  • "task"

  • +
  • "product"

  • +
+
+
+

:param : +:param | “version”: Entity type. +:param “representations”]): Entity type. +:param entity_ids: Ids of entities for which +:type entity_ids: Optional[Iterable[str]] +:param | links should be received.: +:param link_types: Link type filters. +:type link_types: Optional[Iterable[str]] +:param link_direction: Link direction +:type link_direction: Optional[Literal["in", "out"]] +:param | filter.: +:param link_names: Link name filters. +:type link_names: Optional[Iterable[str]] +:param link_name_regex: Regex filter for link name. +:type link_name_regex: Optional[str]

+
+
Returns:
+

Link info by entity ids.

+
+
Return type:
+

dict[str, list[dict[str, Any]]]

+
+
+
+ +
+
+get_event(event_id)[source]
+

Query full event data by id.

+

Events received using event server do not contain full information. To +get the full event information is required to receive it explicitly.

+
+
Parameters:
+

event_id (str) – Event id.

+
+
Returns:
+

Full event data.

+
+
Return type:
+

dict[str, Any]

+
+
+
+ +
+
+get_events(topics: Optional[Iterable[str]] = None, event_ids: Optional[Iterable[str]] = None, project_names: Optional[Iterable[str]] = None, statuses: Optional[Iterable[str]] = None, users: Optional[Iterable[str]] = None, include_logs: Optional[bool] = None, has_children: Optional[bool] = None, newer_than: Optional[str] = None, older_than: Optional[str] = None, fields: Optional[Iterable[str]] = None, limit: Optional[int] = None, order: Optional[SortOrder] = None, states: Optional[Iterable[str]] = None)[source]
+

Get events from server with filtering options.

+
+

Notes

+

Not all event happen on a project.

+
+
+
Parameters:
+
    +
  • topics (Optional[Iterable[str]]) – Name of topics.

  • +
  • event_ids (Optional[Iterable[str]]) – Event ids.

  • +
  • project_names (Optional[Iterable[str]]) – Project on which +event happened.

  • +
  • statuses (Optional[Iterable[str]]) – Filtering by statuses.

  • +
  • users (Optional[Iterable[str]]) – Filtering by users +who created/triggered an event.

  • +
  • include_logs (Optional[bool]) – Query also log events.

  • +
  • has_children (Optional[bool]) – Event is with/without children +events. If ‘None’ then all events are returned, default.

  • +
  • newer_than (Optional[str]) – Return only events newer than given +iso datetime string.

  • +
  • older_than (Optional[str]) – Return only events older than given +iso datetime string.

  • +
  • fields (Optional[Iterable[str]]) – Fields that should be received +for each event.

  • +
  • limit (Optional[int]) – Limit number of events to be fetched.

  • +
  • order (Optional[SortOrder]) – Order events in ascending +or descending order. It is recommended to set ‘limit’ +when used descending.

  • +
  • states (Optional[Iterable[str]]) – DEPRECATED Filtering by states. +Use ‘statuses’ instead.

  • +
+
+
Returns:
+

Available events matching filters.

+
+
Return type:
+

Generator[dict[str, Any]]

+
+
+
+ +
+
+get_folder_by_id(project_name, folder_id, fields=None, own_attributes=False)[source]
+

Query folder entity by id.

+
+
Parameters:
+
    +
  • project_name (str) – Name of project where to look for queried +entities.

  • +
  • folder_id (str) – Folder id.

  • +
  • fields (Optional[Iterable[str]]) – Fields that should be returned. +All fields are returned if ‘None’ is passed.

  • +
  • own_attributes (Optional[bool]) – Attribute values that are +not explicitly set on entity will have ‘None’ value.

  • +
+
+
Returns:
+

Folder entity data or None if was not found.

+
+
Return type:
+

Union[dict, None]

+
+
+
+ +
+
+get_folder_by_name(project_name, folder_name, fields=None, own_attributes=False)[source]
+

Query folder entity by path.

+
+

Warning

+
+
Folder name is not a unique identifier of a folder. Function is

kept for OpenPype 3 compatibility.

+
+
+
+
+
Parameters:
+
    +
  • project_name (str) – Name of project where to look for queried +entities.

  • +
  • folder_name (str) – Folder name.

  • +
  • fields (Optional[Iterable[str]]) – Fields that should be returned. +All fields are returned if ‘None’ is passed.

  • +
  • own_attributes (Optional[bool]) – Attribute values that are +not explicitly set on entity will have ‘None’ value.

  • +
+
+
Returns:
+

Folder entity data or None if was not found.

+
+
Return type:
+

Union[dict, None]

+
+
+
+ +
+
+get_folder_by_path(project_name, folder_path, fields=None, own_attributes=False)[source]
+

Query folder entity by path.

+

Folder path is a path to folder with all parent names joined by slash.

+
+
Parameters:
+
    +
  • project_name (str) – Name of project where to look for queried +entities.

  • +
  • folder_path (str) – Folder path.

  • +
  • fields (Optional[Iterable[str]]) – Fields that should be returned. +All fields are returned if ‘None’ is passed.

  • +
  • own_attributes (Optional[bool]) – Attribute values that are +not explicitly set on entity will have ‘None’ value.

  • +
+
+
Returns:
+

Folder entity data or None if was not found.

+
+
Return type:
+

Union[dict, None]

+
+
+
+ +
+
+get_folder_ids_with_products(project_name, folder_ids=None)[source]
+

Find folders which have at least one product.

+

Folders that have at least one product should be immutable, so they +should not change path -> change of name or name of any parent +is not possible.

+
+
Parameters:
+
    +
  • project_name (str) – Name of project.

  • +
  • folder_ids (Optional[Iterable[str]]) – Limit folder ids filtering +to a set of folders. If set to None all folders on project are +checked.

  • +
+
+
Returns:
+

Folder ids that have at least one product.

+
+
Return type:
+

set[str]

+
+
+
+ +
+ +

Query folder links from server.

+
+
Parameters:
+
    +
  • project_name (str) – Project where links are.

  • +
  • folder_id (str) – Folder id for which links should be received.

  • +
  • link_types (Optional[Iterable[str]]) – Link type filters.

  • +
  • link_direction (Optional[Literal["in", "out"]]) – Link direction +filter.

  • +
+
+
Returns:
+

Link info of folder.

+
+
Return type:
+

list[dict[str, Any]]

+
+
+
+ +
+
+get_folder_thumbnail(project_name, folder_id, thumbnail_id=None)[source]
+

Prepared method to receive thumbnail for folder entity.

+
+
Parameters:
+
    +
  • project_name (str) – Project under which the entity is located.

  • +
  • folder_id (str) – Folder id for which thumbnail should be returned.

  • +
  • thumbnail_id (Optional[str]) – Prepared thumbnail id from entity. +Used only to check if thumbnail was already cached.

  • +
+
+
Returns:
+

+
Path to downloaded thumbnail or none if entity

does not have any (or if user does not have permissions).

+
+
+

+
+
Return type:
+

Union[str, None]

+
+
+
+ +
+
+get_folders(project_name, folder_ids=None, folder_paths=None, folder_names=None, folder_types=None, parent_ids=None, folder_path_regex=None, has_products=None, has_tasks=None, has_children=None, statuses=None, assignees_all=None, tags=None, active=True, has_links=None, fields=None, own_attributes=False)[source]
+

Query folders from server.

+
+
Todos:
+
Folder name won’t be unique identifier, so we should add

folder path filtering.

+
+
+
+
+
+

Notes

+

Filter ‘active’ don’t have direct filter in GraphQl.

+
+
+
Parameters:
+
    +
  • project_name (str) – Name of project.

  • +
  • folder_ids (Optional[Iterable[str]]) – Folder ids to filter.

  • +
  • folder_paths (Optional[Iterable[str]]) – Folder paths used +for filtering.

  • +
  • folder_names (Optional[Iterable[str]]) – Folder names used +for filtering.

  • +
  • folder_types (Optional[Iterable[str]]) – Folder types used +for filtering.

  • +
  • parent_ids (Optional[Iterable[str]]) – Ids of folder parents. +Use ‘None’ if folder is direct child of project.

  • +
  • folder_path_regex (Optional[str]) – Folder path regex used +for filtering.

  • +
  • has_products (Optional[bool]) – Filter folders with/without +products. Ignored when None, default behavior.

  • +
  • has_tasks (Optional[bool]) – Filter folders with/without +tasks. Ignored when None, default behavior.

  • +
  • has_children (Optional[bool]) – Filter folders with/without +children. Ignored when None, default behavior.

  • +
  • statuses (Optional[Iterable[str]]) – Folder statuses used +for filtering.

  • +
  • assignees_all (Optional[Iterable[str]]) – Filter by assigness +on children tasks. Task must have all of passed assignees.

  • +
  • tags (Optional[Iterable[str]]) – Folder tags used +for filtering.

  • +
  • active (Optional[bool]) – Filter active/inactive folders. +Both are returned if is set to None.

  • +
  • has_links (Optional[Literal[IN, OUT, ANY]]) – Filter +representations with IN/OUT/ANY links.

  • +
  • fields (Optional[Iterable[str]]) – Fields to be queried for +folder. All possible folder fields are returned +if ‘None’ is passed.

  • +
  • own_attributes (Optional[bool]) – Attribute values that are +not explicitly set on entity will have ‘None’ value.

  • +
+
+
Returns:
+

Queried folder entities.

+
+
Return type:
+

Generator[dict[str, Any]]

+
+
+
+ +
+
+get_folders_hierarchy(project_name, search_string=None, folder_types=None)[source]
+

Get project hierarchy.

+

All folders in project in hierarchy data structure.

+
+
Example output:
+
{
+
“hierarchy”: [
+
{

“id”: “…”, +“name”: “…”, +“label”: “…”, +“status”: “…”, +“folderType”: “…”, +“hasTasks”: False, +“taskNames”: [], +“parents”: [], +“parentId”: None, +“children”: […children folders…]

+
+
+
+
+

]

+
+
+

}

+
+
+
+
Parameters:
+
    +
  • project_name (str) – Project where to look for folders.

  • +
  • search_string (Optional[str]) – Search string to filter folders.

  • +
  • folder_types (Optional[Iterable[str]]) – Folder types to filter.

  • +
+
+
Returns:
+

Response data from server.

+
+
Return type:
+

dict[str, Any]

+
+
+
+ +
+ +

Query folders links from server.

+
+
Parameters:
+
    +
  • project_name (str) – Project where links are.

  • +
  • folder_ids (Optional[Iterable[str]]) – Ids of folders for which +links should be received.

  • +
  • link_types (Optional[Iterable[str]]) – Link type filters.

  • +
  • link_direction (Optional[Literal["in", "out"]]) – Link direction +filter.

  • +
+
+
Returns:
+

Link info by folder ids.

+
+
Return type:
+

dict[str, list[dict[str, Any]]]

+
+
+
+ +
+
+get_folders_rest(project_name, include_attrib=False)[source]
+

Get simplified flat list of all project folders.

+
+
Get all project folders in single REST call. This can be faster than

using ‘get_folders’ method which is using GraphQl, but does not +allow any filtering, and set of fields is defined +by server backend.

+
+
+

Example:

+
[
+    {
+        "id": "112233445566",
+        "parentId": "112233445567",
+        "path": "/root/parent/child",
+        "parents": ["root", "parent"],
+        "name": "child",
+        "label": "Child",
+        "folderType": "Folder",
+        "hasTasks": False,
+        "hasChildren": False,
+        "taskNames": [
+            "Compositing",
+        ],
+        "status": "In Progress",
+        "attrib": {},
+        "ownAttrib": [],
+        "updatedAt": "2023-06-12T15:37:02.420260",
+    },
+    ...
+]
+
+
+
+
Deprecated:
+
Use ‘get_rest_folders’ instead. Function was renamed to match

other rest functions, like ‘get_rest_folder’, +‘get_rest_project’ etc. .

+
+
+

Will be removed in ‘1.0.7’ or ‘1.1.0’.

+
+
+
+
Parameters:
+
    +
  • project_name (str) – Project name.

  • +
  • include_attrib (Optional[bool]) – Include attribute values +in output. Slower to query.

  • +
+
+
Returns:
+

List of folder entities.

+
+
Return type:
+

list[dict[str, Any]]

+
+
+
+ +
+ +

Calculate full link type name used for query from server.

+
+
Parameters:
+
    +
  • link_type_name (str) – Type of link.

  • +
  • input_type (str) – Input entity type of link.

  • +
  • output_type (str) – Output entity type of link.

  • +
+
+
Returns:
+

Full name of link type used for query from server.

+
+
Return type:
+

str

+
+
+
+ +
+
+get_graphql_schema()[source]
+
+ +
+
+get_headers(content_type=None)[source]
+
+ +
+
+get_hero_version_by_id(project_name, version_id, fields=None, own_attributes=<object object>)[source]
+

Query hero version entity by id.

+
+
Parameters:
+
    +
  • project_name (str) – Name of project where to look for queried +entities.

  • +
  • version_id (int) – Hero version id.

  • +
  • fields (Optional[Iterable[str]]) – Fields that should be returned. +All fields are returned if ‘None’ is passed.

  • +
  • own_attributes (Optional[bool]) – DEPRECATED: Not supported for +versions.

  • +
+
+
Returns:
+

Version entity data or None if was not found.

+
+
Return type:
+

Union[dict, None]

+
+
+
+ +
+
+get_hero_version_by_product_id(project_name, product_id, fields=None, own_attributes=<object object>)[source]
+

Query hero version entity by product id.

+

Only one hero version is available on a product.

+
+
Parameters:
+
    +
  • project_name (str) – Name of project where to look for queried +entities.

  • +
  • product_id (int) – Product id.

  • +
  • fields (Optional[Iterable[str]]) – Fields that should be returned. +All fields are returned if ‘None’ is passed.

  • +
  • own_attributes (Optional[bool]) – DEPRECATED: Not supported for +versions.

  • +
+
+
Returns:
+

Version entity data or None if was not found.

+
+
Return type:
+

Union[dict, None]

+
+
+
+ +
+
+get_hero_versions(project_name, product_ids=None, version_ids=None, active=True, fields=None, own_attributes=<object object>)[source]
+

Query hero versions by multiple filters.

+

Only one hero version is available on a product.

+
+
Parameters:
+
    +
  • project_name (str) – Name of project where to look for queried +entities.

  • +
  • product_ids (Optional[Iterable[str]]) – Product ids.

  • +
  • version_ids (Optional[Iterable[str]]) – Version ids.

  • +
  • active (Optional[bool]) – Receive active/inactive entities. +Both are returned when ‘None’ is passed.

  • +
  • fields (Optional[Iterable[str]]) – Fields that should be returned. +All fields are returned if ‘None’ is passed.

  • +
  • own_attributes (Optional[bool]) – DEPRECATED: Not supported for +versions.

  • +
+
+
Returns:
+

Version entity data or None if was not found.

+
+
Return type:
+

Union[dict, None]

+
+
+
+ +
+
+get_info()[source]
+

Get information about current used api key.

+

By default, the ‘info’ contains only ‘uptime’ and ‘version’. With +logged user info also contains information about user and machines on +which was logged in.

+
+
Todos:

Use this method for validation of token instead of ‘get_user’.

+
+
+
+
Returns:
+

Information from server.

+
+
Return type:
+

dict[str, Any]

+
+
+
+ +
+
+get_installers(version=None, platform_name=None)[source]
+

Information about desktop application installers on server.

+

Desktop application installers are helpers to download/update AYON +desktop application for artists.

+
+
Parameters:
+
    +
  • version (Optional[str]) – Filter installers by version.

  • +
  • platform_name (Optional[str]) – Filter installers by platform name.

  • +
+
+
Return type:
+

list[dict[str, Any]]

+
+
+
+ +
+
+get_last_version_by_product_id(project_name, product_id, active=True, fields=None, own_attributes=<object object>)[source]
+

Query last version entity by product id.

+
+
Parameters:
+
    +
  • project_name (str) – Project where to look for representation.

  • +
  • product_id (str) – Product id.

  • +
  • active (Optional[bool]) – Receive active/inactive entities. +Both are returned when ‘None’ is passed.

  • +
  • fields (Optional[Iterable[str]]) – fields to be queried +for representations.

  • +
  • own_attributes (Optional[bool]) – DEPRECATED: Not supported for +versions.

  • +
+
+
Returns:
+

Queried version entity or None.

+
+
Return type:
+

Union[dict[str, Any], None]

+
+
+
+ +
+
+get_last_version_by_product_name(project_name, product_name, folder_id, active=True, fields=None, own_attributes=<object object>)[source]
+

Query last version entity by product name and folder id.

+
+
Parameters:
+
    +
  • project_name (str) – Project where to look for representation.

  • +
  • product_name (str) – Product name.

  • +
  • folder_id (str) – Folder id.

  • +
  • active (Optional[bool]) – Receive active/inactive entities. +Both are returned when ‘None’ is passed.

  • +
  • fields (Optional[Iterable[str]]) – fields to be queried +for representations.

  • +
  • own_attributes (Optional[bool]) – DEPRECATED: Not supported for +representations.

  • +
+
+
Returns:
+

Queried version entity or None.

+
+
Return type:
+

Union[dict[str, Any], None]

+
+
+
+ +
+
+get_last_versions(project_name, product_ids, active=True, fields=None, own_attributes=<object object>)[source]
+

Query last version entities by product ids.

+
+
Parameters:
+
    +
  • project_name (str) – Project where to look for representation.

  • +
  • product_ids (Iterable[str]) – Product ids.

  • +
  • active (Optional[bool]) – Receive active/inactive entities. +Both are returned when ‘None’ is passed.

  • +
  • fields (Optional[Iterable[str]]) – fields to be queried +for representations.

  • +
  • own_attributes (Optional[bool]) – DEPRECATED: Not supported for +versions.

  • +
+
+
Returns:
+

Last versions by product id.

+
+
Return type:
+

dict[str, dict[str, Any]]

+
+
+
+ +
+ +

Get link type data.

+

There is not dedicated REST endpoint to get single link type, +so method ‘get_link_types’ is used.

+
+
Example output:
+
{

“name”: “reference|folder|folder”, +“link_type”: “reference”, +“input_type”: “folder”, +“output_type”: “folder”, +“data”: {}

+
+
+

}

+
+
+
+
Parameters:
+
    +
  • project_name (str) – Project where link type is available.

  • +
  • link_type_name (str) – Name of link type.

  • +
  • input_type (str) – Input entity type of link.

  • +
  • output_type (str) – Output entity type of link.

  • +
+
+
Returns:
+

Link type information.

+
+
Return type:
+

Union[None, dict[str, Any]]

+
+
+
+ +
+ +

All link types available on a project.

+
+
Example output:
+
[
+
{

“name”: “reference|folder|folder”, +“link_type”: “reference”, +“input_type”: “folder”, +“output_type”: “folder”, +“data”: {}

+
+
+

}

+
+
+

]

+
+
+
+
Parameters:
+

project_name (str) – Name of project where to look for link types.

+
+
Returns:
+

Link types available on project.

+
+
Return type:
+

list[dict[str, Any]]

+
+
+
+ +
+
+get_max_retries()[source]
+

Current value for requests max retries.

+
+
Returns:
+

Max retries value.

+
+
Return type:
+

int

+
+
+
+ +
+
+get_product_by_id(project_name, product_id, fields=None, own_attributes=<object object>)[source]
+

Query product entity by id.

+
+
Parameters:
+
    +
  • project_name (str) – Name of project where to look for queried +entities.

  • +
  • product_id (str) – Product id.

  • +
  • fields (Optional[Iterable[str]]) – Fields that should be returned. +All fields are returned if ‘None’ is passed.

  • +
  • own_attributes (Optional[bool]) – DEPRECATED: Not supported for +products.

  • +
+
+
Returns:
+

Product entity data or None if was not found.

+
+
Return type:
+

Union[dict, None]

+
+
+
+ +
+
+get_product_by_name(project_name, product_name, folder_id, fields=None, own_attributes=<object object>)[source]
+

Query product entity by name and folder id.

+
+
Parameters:
+
    +
  • project_name (str) – Name of project where to look for queried +entities.

  • +
  • product_name (str) – Product name.

  • +
  • folder_id (str) – Folder id (Folder is a parent of products).

  • +
  • fields (Optional[Iterable[str]]) – Fields that should be returned. +All fields are returned if ‘None’ is passed.

  • +
  • own_attributes (Optional[bool]) – DEPRECATED: Not supported for +products.

  • +
+
+
Returns:
+

Product entity data or None if was not found.

+
+
Return type:
+

Union[dict, None]

+
+
+
+ +
+ +

Query product links from server.

+
+
Parameters:
+
    +
  • project_name (str) – Project where links are.

  • +
  • product_id (str) – Product id for which links should be received.

  • +
  • link_types (Optional[Iterable[str]]) – Link type filters.

  • +
  • link_direction (Optional[Literal["in", "out"]]) – Link direction +filter.

  • +
+
+
Returns:
+

Link info of product.

+
+
Return type:
+

list[dict[str, Any]]

+
+
+
+ +
+
+get_product_type_names(project_name=None, product_ids=None)[source]
+

Product type names.

+
+

Warning

+
+
This function will be probably removed. Matters if ‘products_id’

filter has real use-case.

+
+
+
+
+
Parameters:
+
    +
  • project_name (Optional[str]) – Name of project where to look for +queried entities.

  • +
  • product_ids (Optional[Iterable[str]]) – Product ids filter. Can be +used only with ‘project_name’.

  • +
+
+
Returns:
+

Product type names.

+
+
Return type:
+

set[str]

+
+
+
+ +
+
+get_product_types(fields=None)[source]
+

Types of products.

+
+
This is server wide information. Product types have ‘name’, ‘icon’ and

‘color’.

+
+
+
+
Parameters:
+

fields (Optional[Iterable[str]]) – Product types fields to query.

+
+
Returns:
+

Product types information.

+
+
Return type:
+

list[dict[str, Any]]

+
+
+
+ +
+
+get_products(project_name, product_ids=None, product_names=None, folder_ids=None, product_types=None, product_name_regex=None, product_path_regex=None, names_by_folder_ids=None, statuses=None, tags=None, active=True, fields=None, own_attributes=<object object>)[source]
+

Query products from server.

+
+
Todos:
+
Separate ‘name_by_folder_ids’ filtering to separated method. It

cannot be combined with some other filters.

+
+
+
+
+
+
Parameters:
+
    +
  • project_name (str) – Name of project.

  • +
  • product_ids (Optional[Iterable[str]]) – Task ids to filter.

  • +
  • product_names (Optional[Iterable[str]]) – Task names used for +filtering.

  • +
  • folder_ids (Optional[Iterable[str]]) – Ids of task parents. +Use ‘None’ if folder is direct child of project.

  • +
  • product_types (Optional[Iterable[str]]) – Product types used for +filtering.

  • +
  • product_name_regex (Optional[str]) – Filter products by name regex.

  • +
  • product_path_regex (Optional[str]) – Filter products by path regex. +Path starts with folder path and ends with product name.

  • +
  • names_by_folder_ids (Optional[dict[str, Iterable[str]]]) – Product +name filtering by folder id.

  • +
  • statuses (Optional[Iterable[str]]) – Product statuses used +for filtering.

  • +
  • tags (Optional[Iterable[str]]) – Product tags used +for filtering.

  • +
  • active (Optional[bool]) – Filter active/inactive products. +Both are returned if is set to None.

  • +
  • fields (Optional[Iterable[str]]) – Fields to be queried for +folder. All possible folder fields are returned +if ‘None’ is passed.

  • +
  • own_attributes (Optional[bool]) – DEPRECATED: Not supported for +products.

  • +
+
+
Returns:
+

Queried product entities.

+
+
Return type:
+

Generator[dict[str, Any]]

+
+
+
+ +
+ +

Query products links from server.

+
+
Parameters:
+
    +
  • project_name (str) – Project where links are.

  • +
  • product_ids (Optional[Iterable[str]]) – Ids of products for which +links should be received.

  • +
  • link_types (Optional[Iterable[str]]) – Link type filters.

  • +
  • link_direction (Optional[Literal["in", "out"]]) – Link direction +filter.

  • +
+
+
Returns:
+

Link info by product ids.

+
+
Return type:
+

dict[str, list[dict[str, Any]]]

+
+
+
+ +
+
+get_project(project_name, fields=None, own_attributes=False)[source]
+

Get project.

+
+
Parameters:
+
    +
  • project_name (str) – Name of project.

  • +
  • fields (Optional[Iterable[str]]) – fields to be queried +for project.

  • +
  • own_attributes (Optional[bool]) – Attribute values that are +not explicitly set on entity will have ‘None’ value.

  • +
+
+
Returns:
+

+
Project entity data or None

if project was not found.

+
+
+

+
+
Return type:
+

Union[dict[str, Any], None]

+
+
+
+ +
+
+get_project_anatomy_preset(preset_name=None)[source]
+

Anatomy preset values by name.

+

Get anatomy preset values by preset name. Primary preset is returned +if preset name is set to ‘None’.

+
+
Parameters:
+

preset_name (Optional[str]) – Preset name.

+
+
Returns:
+

Anatomy preset values.

+
+
Return type:
+

dict[str, Any]

+
+
+
+ +
+
+get_project_anatomy_presets()[source]
+

Anatomy presets available on server.

+

Content has basic information about presets. Example output:

+
[
+    {
+        "name": "netflix_VFX",
+        "primary": false,
+        "version": "1.0.0"
+    },
+    {
+        ...
+    },
+    ...
+]
+
+
+
+
Returns:
+

Anatomy presets available on server.

+
+
Return type:
+

list[dict[str, str]]

+
+
+
+ +
+
+get_project_names(active=True, library=None)[source]
+

Receive available project names.

+

User must be logged in.

+
+
Parameters:
+
    +
  • active (Optional[bool]) – Filter active/inactive projects. Both +are returned if ‘None’ is passed.

  • +
  • library (Optional[bool]) – Filter standard/library projects. Both +are returned if ‘None’ is passed.

  • +
+
+
Returns:
+

List of available project names.

+
+
Return type:
+

list[str]

+
+
+
+ +
+
+get_project_product_types(project_name, fields=None)[source]
+

Types of products available on a project.

+

Filter only product types available on project.

+
+
Parameters:
+
    +
  • project_name (str) – Name of project where to look for +product types.

  • +
  • fields (Optional[Iterable[str]]) – Product types fields to query.

  • +
+
+
Returns:
+

Product types information.

+
+
Return type:
+

list[dict[str, Any]]

+
+
+
+ +
+
+get_project_root_overrides(project_name)[source]
+

Root overrides per site name.

+
+
Method is based on logged user and can’t be received for any other

user on server.

+
+
+

Output will contain only roots per site id used by logged user.

+
+
Parameters:
+

project_name (str) – Name of project.

+
+
Returns:
+

Root values by root name by site id.

+
+
Return type:
+

dict[str, dict[str, str]]

+
+
+
+ +
+
+get_project_root_overrides_by_site_id(project_name, site_id=None)[source]
+

Root overrides for site.

+

If site id is not passed a site set in current api object is used +instead.

+
+
Parameters:
+
    +
  • project_name (str) – Name of project.

  • +
  • site_id (Optional[str]) – Site id for which want to receive +site overrides.

  • +
+
+
Returns:
+

+
Root values by root name or None if

site does not have overrides.

+
+
+

+
+
Return type:
+

dict[str, str]

+
+
+
+ +
+
+get_project_roots_by_platform(project_name, platform_name=None)[source]
+

Root values for a site.

+

If platform name is not passed current platform name is used instead.

+
+
This function does return root values without site overrides. It is

possible to use the function to receive default root values.

+
+
+
+
Parameters:
+
    +
  • project_name (str) – Name of project.

  • +
  • platform_name (Optional[Literal["windows", "linux", "darwin"]]) – Platform name for which want to receive root values. Current +platform name is used if not passed.

  • +
+
+
Returns:
+

Root values.

+
+
Return type:
+

dict[str, str]

+
+
+
+ +
+
+get_project_roots_by_site(project_name)[source]
+

Root overrides per site name.

+

Method is based on logged user and can’t be received for any other +user on server.

+

Output will contain only roots per site id used by logged user.

+
+
Deprecated:
+
Use ‘get_project_root_overrides’ instead. Function

deprecated since 1.0.6

+
+
+
+
+
+
Parameters:
+

project_name (str) – Name of project.

+
+
Returns:
+

Root values by root name by site id.

+
+
Return type:
+

dict[str, dict[str, str]]

+
+
+
+ +
+
+get_project_roots_by_site_id(project_name, site_id=None)[source]
+

Root values for a site.

+

If site id is not passed a site set in current api object is used +instead. If site id is not available, default roots are returned +for current platform.

+
+
Parameters:
+
    +
  • project_name (str) – Name of project.

  • +
  • site_id (Optional[str]) – Site id for which want to receive +root values.

  • +
+
+
Returns:
+

Root values.

+
+
Return type:
+

dict[str, str]

+
+
+
+ +
+
+get_project_roots_for_site(project_name, site_id=None)[source]
+

Root overrides for site.

+

If site id is not passed a site set in current api object is used +instead.

+
+
Deprecated:
+
Use ‘get_project_root_overrides_by_site_id’ instead. Function

deprecated since 1.0.6

+
+
+
+
+
+
Parameters:
+
    +
  • project_name (str) – Name of project.

  • +
  • site_id (Optional[str]) – Site id for which want to receive +site overrides.

  • +
+
+
Returns:
+

+
Root values by root name, root name is not

available if it does not have overrides.

+
+
+

+
+
Return type:
+

dict[str, str]

+
+
+
+ +
+
+get_projects(active=True, library=None, fields=None, own_attributes=False)[source]
+

Get projects.

+
+
Parameters:
+
    +
  • active (Optional[bool]) – Filter active or inactive projects. +Filter is disabled when ‘None’ is passed.

  • +
  • library (Optional[bool]) – Filter library projects. Filter is +disabled when ‘None’ is passed.

  • +
  • fields (Optional[Iterable[str]]) – fields to be queried +for project.

  • +
  • own_attributes (Optional[bool]) – Attribute values that are +not explicitly set on entity will have ‘None’ value.

  • +
+
+
Returns:
+

Queried projects.

+
+
Return type:
+

Generator[dict[str, Any]]

+
+
+
+ +
+
+get_repre_ids_by_context_filters(project_name, context_filters, representation_names=None, version_ids=None)[source]
+

Find representation ids which match passed context filters.

+

Each representation has context integrated on representation entity in +database. The context may contain project, folder, task name or +product name, product type and many more. This implementation gives +option to quickly filter representation based on representation data +in database.

+
+
Context filters have defined structure. To define filter of nested

subfield use dot ‘.’ as delimiter (For example ‘task.name’).

+
+
Filter values can be regex filters. String or re.Pattern can

be used.

+
+
+
+
Parameters:
+
    +
  • project_name (str) – Project where to look for representations.

  • +
  • context_filters (dict[str, list[str]]) – Filters of context fields.

  • +
  • representation_names (Optional[Iterable[str]]) – Representation +names, can be used as additional filter for representations +by their names.

  • +
  • version_ids (Optional[Iterable[str]]) – Version ids, can be used +as additional filter for representations by their parent ids.

  • +
+
+
Returns:
+

Representation ids that match passed filters.

+
+
Return type:
+

list[str]

+
+
+
+

Example

+
+
The function returns just representation ids so if entities are

required for funtionality they must be queried afterwards by +their ids.

+
+
+
>>> project_name = "testProject"
+>>> filters = {
+...     "task.name": ["[aA]nimation"],
+...     "product": [".*[Mm]ain"]
+... }
+>>> repre_ids = get_repre_ids_by_context_filters(
+...     project_name, filters)
+>>> repres = get_representations(project_name, repre_ids)
+
+
+
+
+ +
+
+get_representation_by_id(project_name, representation_id, fields=None, own_attributes=<object object>)[source]
+

Query representation entity from server based on id filter.

+
+
Parameters:
+
    +
  • project_name (str) – Project where to look for representation.

  • +
  • representation_id (str) – Id of representation.

  • +
  • fields (Optional[Iterable[str]]) – fields to be queried +for representations.

  • +
  • own_attributes (Optional[bool]) – DEPRECATED: Not supported for +representations.

  • +
+
+
Returns:
+

Queried representation entity or None.

+
+
Return type:
+

Union[dict[str, Any], None]

+
+
+
+ +
+
+get_representation_by_name(project_name, representation_name, version_id, fields=None, own_attributes=<object object>)[source]
+

Query representation entity by name and version id.

+
+
Parameters:
+
    +
  • project_name (str) – Project where to look for representation.

  • +
  • representation_name (str) – Representation name.

  • +
  • version_id (str) – Version id.

  • +
  • fields (Optional[Iterable[str]]) – fields to be queried +for representations.

  • +
  • own_attributes (Optional[bool]) – DEPRECATED: Not supported for +representations.

  • +
+
+
Returns:
+

Queried representation entity or None.

+
+
Return type:
+

Union[dict[str, Any], None]

+
+
+
+ +
+
+get_representation_hierarchy(project_name, representation_id, project_fields=None, folder_fields=None, task_fields=None, product_fields=None, version_fields=None, representation_fields=None)[source]
+

Find representation parents by representation id.

+

Representation parent entities up to project.

+
+
Parameters:
+
    +
  • project_name (str) – Project where to look for entities.

  • +
  • representation_id (str) – Representation id.

  • +
  • project_fields (Optional[Iterable[str]]) – Project fields.

  • +
  • folder_fields (Optional[Iterable[str]]) – Folder fields.

  • +
  • task_fields (Optional[Iterable[str]]) – Task fields.

  • +
  • product_fields (Optional[Iterable[str]]) – Product fields.

  • +
  • version_fields (Optional[Iterable[str]]) – Version fields.

  • +
  • representation_fields (Optional[Iterable[str]]) – Representation +fields.

  • +
+
+
Returns:
+

Representation hierarchy entities.

+
+
Return type:
+

RepresentationHierarchy

+
+
+
+ +
+ +

Query representation links from server.

+
+
Parameters:
+
    +
  • project_name (str) – Project where links are.

  • +
  • representation_id (str) – Representation id for which links +should be received.

  • +
  • link_types (Optional[Iterable[str]]) – Link type filters.

  • +
  • link_direction (Optional[Literal["in", "out"]]) – Link direction +filter.

  • +
+
+
Returns:
+

Link info of representation.

+
+
Return type:
+

list[dict[str, Any]]

+
+
+
+ +
+
+get_representation_parents(project_name, representation_id, project_fields=None, folder_fields=None, product_fields=None, version_fields=None)[source]
+

Find representation parents by representation id.

+

Representation parent entities up to project.

+
+
Parameters:
+
    +
  • project_name (str) – Project where to look for entities.

  • +
  • representation_id (str) – Representation id.

  • +
  • project_fields (Optional[Iterable[str]]) – Project fields.

  • +
  • folder_fields (Optional[Iterable[str]]) – Folder fields.

  • +
  • product_fields (Optional[Iterable[str]]) – Product fields.

  • +
  • version_fields (Optional[Iterable[str]]) – Version fields.

  • +
+
+
Returns:
+

Representation parent entities.

+
+
Return type:
+

RepresentationParents

+
+
+
+ +
+
+get_representations(project_name, representation_ids=None, representation_names=None, version_ids=None, names_by_version_ids=None, statuses=None, tags=None, active=True, has_links=None, fields=None, own_attributes=<object object>)[source]
+

Get representation entities based on passed filters from server.

+
+
Parameters:
+
    +
  • project_name (str) – Name of project where to look for versions.

  • +
  • representation_ids (Optional[Iterable[str]]) – Representation ids +used for representation filtering.

  • +
  • representation_names (Optional[Iterable[str]]) – Representation +names used for representation filtering.

  • +
  • version_ids (Optional[Iterable[str]]) – Version ids used for +representation filtering. Versions are parents of +representations.

  • +
  • names_by_version_ids (Optional[Dict[str, Iterable[str]]) – Find +representations by names and version ids. This filter +discards all other filters.

  • +
  • statuses (Optional[Iterable[str]]) – Representation statuses used +for filtering.

  • +
  • tags (Optional[Iterable[str]]) – Representation tags used +for filtering.

  • +
  • active (Optional[bool]) – Receive active/inactive entities. +Both are returned when ‘None’ is passed.

  • +
  • has_links (Optional[Literal[IN, OUT, ANY]]) – Filter +representations with IN/OUT/ANY links.

  • +
  • fields (Optional[Iterable[str]]) – Fields to be queried for +representation. All possible fields are returned if ‘None’ is +passed.

  • +
  • own_attributes (Optional[bool]) – DEPRECATED: Not supported for +representations.

  • +
+
+
Returns:
+

Queried representation entities.

+
+
Return type:
+

Generator[dict[str, Any]]

+
+
+
+ +
+
+get_representations_hierarchy(project_name, representation_ids, project_fields=None, folder_fields=None, task_fields=None, product_fields=None, version_fields=None, representation_fields=None)[source]
+

Find representation with parents by representation id.

+

Representation entity with parent entities up to project.

+
+
Default fields are used when any fields are set to None. But it is

possible to pass in empty iterable (list, set, tuple) to skip +entity.

+
+
+
+
Parameters:
+
    +
  • project_name (str) – Project where to look for entities.

  • +
  • representation_ids (Iterable[str]) – Representation ids.

  • +
  • project_fields (Optional[Iterable[str]]) – Project fields.

  • +
  • folder_fields (Optional[Iterable[str]]) – Folder fields.

  • +
  • task_fields (Optional[Iterable[str]]) – Task fields.

  • +
  • product_fields (Optional[Iterable[str]]) – Product fields.

  • +
  • version_fields (Optional[Iterable[str]]) – Version fields.

  • +
  • representation_fields (Optional[Iterable[str]]) – Representation +fields.

  • +
+
+
Returns:
+

+
Parent entities by

representation id.

+
+
+

+
+
Return type:
+

dict[str, RepresentationHierarchy]

+
+
+
+ +
+ +

Query representations links from server.

+
+
Parameters:
+
    +
  • project_name (str) – Project where links are.

  • +
  • representation_ids (Optional[Iterable[str]]) – Ids of +representations for which links should be received.

  • +
  • link_types (Optional[Iterable[str]]) – Link type filters.

  • +
  • link_direction (Optional[Literal["in", "out"]]) – Link direction +filter.

  • +
+
+
Returns:
+

Link info by representation ids.

+
+
Return type:
+

dict[str, list[dict[str, Any]]]

+
+
+
+ +
+
+get_representations_parents(project_name, representation_ids, project_fields=None, folder_fields=None, product_fields=None, version_fields=None)[source]
+

Find representations parents by representation id.

+

Representation parent entities up to project.

+
+
Parameters:
+
    +
  • project_name (str) – Project where to look for entities.

  • +
  • representation_ids (Iterable[str]) – Representation ids.

  • +
  • project_fields (Optional[Iterable[str]]) – Project fields.

  • +
  • folder_fields (Optional[Iterable[str]]) – Folder fields.

  • +
  • product_fields (Optional[Iterable[str]]) – Product fields.

  • +
  • version_fields (Optional[Iterable[str]]) – Version fields.

  • +
+
+
Returns:
+

+
Parent entities by

representation id.

+
+
+

+
+
Return type:
+

dict[str, RepresentationParents]

+
+
+
+ +
+
+get_rest_entity_by_id(project_name, entity_type, entity_id)[source]
+

Get entity using REST on a project by its id.

+
+
Parameters:
+
    +
  • project_name (str) – Name of project where entity is.

  • +
  • entity_type (Literal["folder", "task", "product", "version"]) – The +entity type which should be received.

  • +
  • entity_id (str) – Id of entity.

  • +
+
+
Returns:
+

Received entity data.

+
+
Return type:
+

dict[str, Any]

+
+
+
+ +
+
+get_rest_folder(project_name, folder_id)[source]
+
+ +
+
+get_rest_folders(project_name, include_attrib=False)[source]
+

Get simplified flat list of all project folders.

+
+
Get all project folders in single REST call. This can be faster than

using ‘get_folders’ method which is using GraphQl, but does not +allow any filtering, and set of fields is defined +by server backend.

+
+
+

Example:

+
[
+    {
+        "id": "112233445566",
+        "parentId": "112233445567",
+        "path": "/root/parent/child",
+        "parents": ["root", "parent"],
+        "name": "child",
+        "label": "Child",
+        "folderType": "Folder",
+        "hasTasks": False,
+        "hasChildren": False,
+        "taskNames": [
+            "Compositing",
+        ],
+        "status": "In Progress",
+        "attrib": {},
+        "ownAttrib": [],
+        "updatedAt": "2023-06-12T15:37:02.420260",
+    },
+    ...
+]
+
+
+
+
Parameters:
+
    +
  • project_name (str) – Project name.

  • +
  • include_attrib (Optional[bool]) – Include attribute values +in output. Slower to query.

  • +
+
+
Returns:
+

List of folder entities.

+
+
Return type:
+

list[dict[str, Any]]

+
+
+
+ +
+
+get_rest_product(project_name, product_id)[source]
+
+ +
+
+get_rest_project(project_name)[source]
+

Query project by name.

+

This call returns project with anatomy data.

+
+
Parameters:
+

project_name (str) – Name of project.

+
+
Returns:
+

+
Project entity data or ‘None’ if

project was not found.

+
+
+

+
+
Return type:
+

Union[dict[str, Any], None]

+
+
+
+ +
+
+get_rest_projects(active=True, library=None)[source]
+

Query available project entities.

+

User must be logged in.

+
+
Parameters:
+
    +
  • active (Optional[bool]) – Filter active/inactive projects. Both +are returned if ‘None’ is passed.

  • +
  • library (Optional[bool]) – Filter standard/library projects. Both +are returned if ‘None’ is passed.

  • +
+
+
Returns:
+

Available projects.

+
+
Return type:
+

Generator[dict[str, Any]]

+
+
+
+ +
+
+get_rest_representation(project_name, representation_id)[source]
+
+ +
+
+get_rest_task(project_name, task_id)[source]
+
+ +
+
+get_rest_url()[source]
+
+ +
+
+get_rest_version(project_name, version_id)[source]
+
+ +
+
+get_schemas()[source]
+

Get components schema.

+

Name of components does not match entity type names e.g. ‘project’ is +under ‘ProjectModel’. We should find out some mapping. Also, there +are properties which don’t have information about reference to object +e.g. ‘config’ has just object definition without reference schema.

+
+
Returns:
+

Component schemas.

+
+
Return type:
+

dict[str, Any]

+
+
+
+ +
+
+get_secret(secret_name)[source]
+

Get secret by name.

+

Example output:

+
{
+    "name": "secret_name",
+    "value": "secret_value",
+}
+
+
+
+
Parameters:
+

secret_name (str) – Name of secret.

+
+
Returns:
+

Secret entity data.

+
+
Return type:
+

dict[str, str]

+
+
+
+ +
+
+get_secrets()[source]
+

Get all secrets.

+

Example output:

+
[
+    {
+        "name": "secret_1",
+        "value": "secret_value_1",
+    },
+    {
+        "name": "secret_2",
+        "value": "secret_value_2",
+    }
+]
+
+
+
+
Returns:
+

List of secret entities.

+
+
Return type:
+

list[dict[str, str]]

+
+
+
+ +
+
+get_sender()[source]
+

Sender used to send requests.

+
+
Returns:
+

Sender name or None.

+
+
Return type:
+

Union[str, None]

+
+
+
+ +
+
+get_sender_type()[source]
+

Sender type used to send requests.

+

Sender type is supported since AYON server 1.5.5 .

+
+
Returns:
+

Sender type or None.

+
+
Return type:
+

Union[str, None]

+
+
+
+ +
+
+get_server_schema()[source]
+

Get server schema with info, url paths, components etc.

+
+
Todos:

Cache schema - How to find out it is outdated?

+
+
+
+
Returns:
+

Full server schema.

+
+
Return type:
+

dict[str, Any]

+
+
+
+ +
+
+get_server_version()[source]
+

Get server version.

+

Version should match semantic version (https://semver.org/).

+
+
Returns:
+

Server version.

+
+
Return type:
+

str

+
+
+
+ +
+
+get_server_version_tuple()[source]
+

Get server version as tuple.

+

Version should match semantic version (https://semver.org/).

+

This function only returns first three numbers of version.

+
+
Returns:
+

+
Server

version.

+
+
+

+
+
Return type:
+

Tuple[int, int, int, Union[str, None], Union[str, None]]

+
+
+
+ +
+
+get_site_id()[source]
+

Site id used for connection.

+

Site id tells server from which machine/site is connection created and +is used for default site overrides when settings are received.

+
+
Returns:
+

Site id value or None if not filled.

+
+
Return type:
+

Union[str, None]

+
+
+
+ +
+
+get_ssl_verify()[source]
+

Enable ssl verification.

+
+
Returns:
+

Current state of ssl verification.

+
+
Return type:
+

bool

+
+
+
+ +
+
+get_task_by_folder_path(project_name, folder_path, task_name, fields=None, own_attributes=False)[source]
+

Query task entity by folder path and task name.

+
+
Parameters:
+
    +
  • project_name (str) – Project name.

  • +
  • folder_path (str) – Folder path.

  • +
  • task_name (str) – Task name.

  • +
  • fields (Optional[Iterable[str]]) – Task fields that should +be returned.

  • +
  • own_attributes (Optional[bool]) – Attribute values that are +not explicitly set on entity will have ‘None’ value.

  • +
+
+
Returns:
+

+
Task entity data or None if was

not found.

+
+
+

+
+
Return type:
+

Union[dict[str, Any], None]

+
+
+
+ +
+
+get_task_by_id(project_name, task_id, fields=None, own_attributes=False)[source]
+

Query task entity by id.

+
+
Parameters:
+
    +
  • project_name (str) – Name of project where to look for queried +entities.

  • +
  • task_id (str) – Task id.

  • +
  • fields (Optional[Iterable[str]]) – Fields that should be returned. +All fields are returned if ‘None’ is passed.

  • +
  • own_attributes (Optional[bool]) – Attribute values that are +not explicitly set on entity will have ‘None’ value.

  • +
+
+
Returns:
+

Task entity data or None if was not found.

+
+
Return type:
+

Union[dict, None]

+
+
+
+ +
+
+get_task_by_name(project_name, folder_id, task_name, fields=None, own_attributes=False)[source]
+

Query task entity by name and folder id.

+
+
Parameters:
+
    +
  • project_name (str) – Name of project where to look for queried +entities.

  • +
  • folder_id (str) – Folder id.

  • +
  • task_name (str) – Task name

  • +
  • fields (Optional[Iterable[str]]) – Fields that should be returned. +All fields are returned if ‘None’ is passed.

  • +
  • own_attributes (Optional[bool]) – Attribute values that are +not explicitly set on entity will have ‘None’ value.

  • +
+
+
Returns:
+

Task entity data or None if was not found.

+
+
Return type:
+

Union[dict, None]

+
+
+
+ +
+ +

Query task links from server.

+
+
Parameters:
+
    +
  • project_name (str) – Project where links are.

  • +
  • task_id (str) – Task id for which links should be received.

  • +
  • link_types (Optional[Iterable[str]]) – Link type filters.

  • +
  • link_direction (Optional[Literal["in", "out"]]) – Link direction +filter.

  • +
+
+
Returns:
+

Link info of task.

+
+
Return type:
+

list[dict[str, Any]]

+
+
+
+ +
+
+get_tasks(project_name, task_ids=None, task_names=None, task_types=None, folder_ids=None, assignees=None, assignees_all=None, statuses=None, tags=None, active=True, fields=None, own_attributes=False)[source]
+

Query task entities from server.

+
+
Parameters:
+
    +
  • project_name (str) – Name of project.

  • +
  • task_ids (Iterable[str]) – Task ids to filter.

  • +
  • task_names (Iterable[str]) – Task names used for filtering.

  • +
  • task_types (Iterable[str]) – Task types used for filtering.

  • +
  • folder_ids (Iterable[str]) – Ids of task parents. Use ‘None’ +if folder is direct child of project.

  • +
  • assignees (Optional[Iterable[str]]) – Task assignees used for +filtering. All tasks with any of passed assignees are +returned.

  • +
  • assignees_all (Optional[Iterable[str]]) – Task assignees used +for filtering. Task must have all of passed assignees to be +returned.

  • +
  • statuses (Optional[Iterable[str]]) – Task statuses used for +filtering.

  • +
  • tags (Optional[Iterable[str]]) – Task tags used for +filtering.

  • +
  • active (Optional[bool]) – Filter active/inactive tasks. +Both are returned if is set to None.

  • +
  • fields (Optional[Iterable[str]]) – Fields to be queried for +folder. All possible folder fields are returned +if ‘None’ is passed.

  • +
  • own_attributes (Optional[bool]) – Attribute values that are +not explicitly set on entity will have ‘None’ value.

  • +
+
+
Returns:
+

Queried task entities.

+
+
Return type:
+

Generator[dict[str, Any]]

+
+
+
+ +
+
+get_tasks_by_folder_path(project_name, folder_path, task_names=None, task_types=None, assignees=None, assignees_all=None, statuses=None, tags=None, active=True, fields=None, own_attributes=False)[source]
+

Query task entities from server by folder path.

+
+
Parameters:
+
    +
  • project_name (str) – Name of project.

  • +
  • folder_path (str) – Folder path.

  • +
  • task_names (Iterable[str]) – Task names used for filtering.

  • +
  • task_types (Iterable[str]) – Task types used for filtering.

  • +
  • assignees (Optional[Iterable[str]]) – Task assignees used for +filtering. All tasks with any of passed assignees are +returned.

  • +
  • assignees_all (Optional[Iterable[str]]) – Task assignees used +for filtering. Task must have all of passed assignees to be +returned.

  • +
  • statuses (Optional[Iterable[str]]) – Task statuses used for +filtering.

  • +
  • tags (Optional[Iterable[str]]) – Task tags used for +filtering.

  • +
  • active (Optional[bool]) – Filter active/inactive tasks. +Both are returned if is set to None.

  • +
  • fields (Optional[Iterable[str]]) – Fields to be queried for +folder. All possible folder fields are returned +if ‘None’ is passed.

  • +
  • own_attributes (Optional[bool]) – Attribute values that are +not explicitly set on entity will have ‘None’ value.

  • +
+
+
+
+ +
+
+get_tasks_by_folder_paths(project_name, folder_paths, task_names=None, task_types=None, assignees=None, assignees_all=None, statuses=None, tags=None, active=True, fields=None, own_attributes=False)[source]
+

Query task entities from server by folder paths.

+
+
Parameters:
+
    +
  • project_name (str) – Name of project.

  • +
  • folder_paths (list[str]) – Folder paths.

  • +
  • task_names (Iterable[str]) – Task names used for filtering.

  • +
  • task_types (Iterable[str]) – Task types used for filtering.

  • +
  • assignees (Optional[Iterable[str]]) – Task assignees used for +filtering. All tasks with any of passed assignees are +returned.

  • +
  • assignees_all (Optional[Iterable[str]]) – Task assignees used +for filtering. Task must have all of passed assignees to be +returned.

  • +
  • statuses (Optional[Iterable[str]]) – Task statuses used for +filtering.

  • +
  • tags (Optional[Iterable[str]]) – Task tags used for +filtering.

  • +
  • active (Optional[bool]) – Filter active/inactive tasks. +Both are returned if is set to None.

  • +
  • fields (Optional[Iterable[str]]) – Fields to be queried for +folder. All possible folder fields are returned +if ‘None’ is passed.

  • +
  • own_attributes (Optional[bool]) – Attribute values that are +not explicitly set on entity will have ‘None’ value.

  • +
+
+
Returns:
+

+
Task entities by

folder path.

+
+
+

+
+
Return type:
+

dict[dict[str, list[dict[str, Any]]]

+
+
+
+ +
+ +

Query tasks links from server.

+
+
Parameters:
+
    +
  • project_name (str) – Project where links are.

  • +
  • task_ids (Optional[Iterable[str]]) – Ids of tasks for which +links should be received.

  • +
  • link_types (Optional[Iterable[str]]) – Link type filters.

  • +
  • link_direction (Optional[Literal["in", "out"]]) – Link direction +filter.

  • +
+
+
Returns:
+

Link info by task ids.

+
+
Return type:
+

dict[str, list[dict[str, Any]]]

+
+
+
+ +
+
+get_thumbnail(project_name, entity_type, entity_id, thumbnail_id=None)[source]
+

Get thumbnail from server.

+

Permissions of thumbnails are related to entities so thumbnails must +be queried per entity. So an entity type and entity type is required +to be passed.

+
+

Notes

+
+
It is recommended to use one of prepared entity type specific

methods ‘get_folder_thumbnail’, ‘get_version_thumbnail’ or +‘get_workfile_thumbnail’.

+
+
We do recommend pass thumbnail id if you have access to it. Each

entity that allows thumbnails has ‘thumbnailId’ field, so it +can be queried.

+
+
+
+
+
Parameters:
+
    +
  • project_name (str) – Project under which the entity is located.

  • +
  • entity_type (str) – Entity type which passed entity id represents.

  • +
  • entity_id (str) – Entity id for which thumbnail should be returned.

  • +
  • thumbnail_id (Optional[str]) – DEPRECATED Use +‘get_thumbnail_by_id’.

  • +
+
+
Returns:
+

+
Thumbnail content wrapper. Does not have to be

valid.

+
+
+

+
+
Return type:
+

ThumbnailContent

+
+
+
+ +
+
+get_thumbnail_by_id(project_name, thumbnail_id)[source]
+

Get thumbnail from server by id.

+

Permissions of thumbnails are related to entities so thumbnails must +be queried per entity. So an entity type and entity type is required +to be passed.

+
+

Notes

+
+
It is recommended to use one of prepared entity type specific

methods ‘get_folder_thumbnail’, ‘get_version_thumbnail’ or +‘get_workfile_thumbnail’.

+
+
We do recommend pass thumbnail id if you have access to it. Each

entity that allows thumbnails has ‘thumbnailId’ field, so it +can be queried.

+
+
+
+
+
Parameters:
+
    +
  • project_name (str) – Project under which the entity is located.

  • +
  • thumbnail_id (Optional[str]) – DEPRECATED Use +‘get_thumbnail_by_id’.

  • +
+
+
Returns:
+

+
Thumbnail content wrapper. Does not have to be

valid.

+
+
+

+
+
Return type:
+

ThumbnailContent

+
+
+
+ +
+
+get_timeout()[source]
+

Current value for requests timeout.

+
+
Returns:
+

Timeout value in seconds.

+
+
Return type:
+

float

+
+
+
+ +
+
+get_user(username=None)[source]
+

Get user info using REST endpoit.

+
+
Parameters:
+

username (Optional[str]) – Username.

+
+
Returns:
+

+
User info or None if user is not

found.

+
+
+

+
+
Return type:
+

Union[dict[str, Any], None]

+
+
+
+ +
+
+get_user_by_name(username, project_name=None, fields=None)[source]
+

Get user by name using GraphQl.

+
+
Only administrators and managers can fetch all users. For other users

it is required to pass in ‘project_name’ filter.

+
+
+
+
Parameters:
+
    +
  • username (str) – Username.

  • +
  • project_name (Optional[str]) – Define scope of project.

  • +
  • fields (Optional[Iterable[str]]) – Fields to be queried +for users.

  • +
+
+
Returns:
+

+
User info or None if user is not

found.

+
+
+

+
+
Return type:
+

Union[dict[str, Any], None]

+
+
+
+ +
+
+get_users(project_name=None, usernames=None, fields=None)[source]
+

Get Users.

+
+
Only administrators and managers can fetch all users. For other users

it is required to pass in ‘project_name’ filter.

+
+
+
+
Parameters:
+
    +
  • project_name (Optional[str]) – Project name.

  • +
  • usernames (Optional[Iterable[str]]) – Filter by usernames.

  • +
  • fields (Optional[Iterable[str]]) – Fields to be queried +for users.

  • +
+
+
Returns:
+

Queried users.

+
+
Return type:
+

Generator[dict[str, Any]]

+
+
+
+ +
+
+get_version_by_id(project_name, version_id, fields=None, own_attributes=<object object>)[source]
+

Query version entity by id.

+
+
Parameters:
+
    +
  • project_name (str) – Name of project where to look for queried +entities.

  • +
  • version_id (str) – Version id.

  • +
  • fields (Optional[Iterable[str]]) – Fields that should be returned. +All fields are returned if ‘None’ is passed.

  • +
  • own_attributes (Optional[bool]) – DEPRECATED: Not supported for +versions.

  • +
+
+
Returns:
+

Version entity data or None if was not found.

+
+
Return type:
+

Union[dict, None]

+
+
+
+ +
+
+get_version_by_name(project_name, version, product_id, fields=None, own_attributes=<object object>)[source]
+

Query version entity by version and product id.

+
+
Parameters:
+
    +
  • project_name (str) – Name of project where to look for queried +entities.

  • +
  • version (int) – Version of version entity.

  • +
  • product_id (str) – Product id. Product is a parent of version.

  • +
  • fields (Optional[Iterable[str]]) – Fields that should be returned. +All fields are returned if ‘None’ is passed.

  • +
  • own_attributes (Optional[bool]) – DEPRECATED: Not supported for +versions.

  • +
+
+
Returns:
+

Version entity data or None if was not found.

+
+
Return type:
+

Union[dict, None]

+
+
+
+ +
+ +

Query version links from server.

+
+
Parameters:
+
    +
  • project_name (str) – Project where links are.

  • +
  • version_id (str) – Version id for which links should be received.

  • +
  • link_types (Optional[Iterable[str]]) – Link type filters.

  • +
  • link_direction (Optional[Literal["in", "out"]]) – Link direction +filter.

  • +
+
+
Returns:
+

Link info of version.

+
+
Return type:
+

list[dict[str, Any]]

+
+
+
+ +
+
+get_version_thumbnail(project_name, version_id, thumbnail_id=None)[source]
+

Prepared method to receive thumbnail for version entity.

+
+
Parameters:
+
    +
  • project_name (str) – Project under which the entity is located.

  • +
  • version_id (str) – Version id for which thumbnail should be +returned.

  • +
  • thumbnail_id (Optional[str]) – Prepared thumbnail id from entity. +Used only to check if thumbnail was already cached.

  • +
+
+
Returns:
+

+
Path to downloaded thumbnail or none if entity

does not have any (or if user does not have permissions).

+
+
+

+
+
Return type:
+

Union[str, None]

+
+
+
+ +
+
+get_versions(project_name, version_ids=None, product_ids=None, task_ids=None, versions=None, hero=True, standard=True, latest=None, statuses=None, tags=None, active=True, fields=None, own_attributes=<object object>)[source]
+

Get version entities based on passed filters from server.

+
+
Parameters:
+
    +
  • project_name (str) – Name of project where to look for versions.

  • +
  • version_ids (Optional[Iterable[str]]) – Version ids used for +version filtering.

  • +
  • product_ids (Optional[Iterable[str]]) – Product ids used for +version filtering.

  • +
  • task_ids (Optional[Iterable[str]]) – Task ids used for +version filtering.

  • +
  • versions (Optional[Iterable[int]]) – Versions we’re interested in.

  • +
  • hero (Optional[bool]) – Skip hero versions when set to False.

  • +
  • standard (Optional[bool]) – Skip standard (non-hero) when +set to False.

  • +
  • latest (Optional[bool]) – Return only latest version of standard +versions. This can be combined only with ‘standard’ attribute +set to True.

  • +
  • statuses (Optional[Iterable[str]]) – Representation statuses used +for filtering.

  • +
  • tags (Optional[Iterable[str]]) – Representation tags used +for filtering.

  • +
  • active (Optional[bool]) – Receive active/inactive entities. +Both are returned when ‘None’ is passed.

  • +
  • fields (Optional[Iterable[str]]) – Fields to be queried +for version. All possible folder fields are returned +if ‘None’ is passed.

  • +
  • own_attributes (Optional[bool]) – DEPRECATED: Not supported for +versions.

  • +
+
+
Returns:
+

Queried version entities.

+
+
Return type:
+

Generator[dict[str, Any]]

+
+
+
+ +
+ +

Query versions links from server.

+
+
Parameters:
+
    +
  • project_name (str) – Project where links are.

  • +
  • version_ids (Optional[Iterable[str]]) – Ids of versions for which +links should be received.

  • +
  • link_types (Optional[Iterable[str]]) – Link type filters.

  • +
  • link_direction (Optional[Literal["in", "out"]]) – Link direction +filter.

  • +
+
+
Returns:
+

Link info by version ids.

+
+
Return type:
+

dict[str, list[dict[str, Any]]]

+
+
+
+ +
+
+get_workfile_info(project_name, task_id, path, fields=None, own_attributes=<object object>)[source]
+

Workfile info entity by task id and workfile path.

+
+
Parameters:
+
    +
  • project_name (str) – Project under which the entity is located.

  • +
  • task_id (str) – Task id.

  • +
  • path (str) – Rootless workfile path.

  • +
  • fields (Optional[Iterable[str]]) – Fields to be queried for +representation. All possible fields are returned if ‘None’ is +passed.

  • +
  • own_attributes (Optional[bool]) – DEPRECATED: Not supported for +workfiles.

  • +
+
+
Returns:
+

Workfile info entity or None.

+
+
Return type:
+

Union[dict[str, Any], None]

+
+
+
+ +
+
+get_workfile_info_by_id(project_name, workfile_id, fields=None, own_attributes=<object object>)[source]
+

Workfile info entity by id.

+
+
Parameters:
+
    +
  • project_name (str) – Project under which the entity is located.

  • +
  • workfile_id (str) – Workfile info id.

  • +
  • fields (Optional[Iterable[str]]) – Fields to be queried for +representation. All possible fields are returned if ‘None’ is +passed.

  • +
  • own_attributes (Optional[bool]) – DEPRECATED: Not supported for +workfiles.

  • +
+
+
Returns:
+

Workfile info entity or None.

+
+
Return type:
+

Union[dict[str, Any], None]

+
+
+
+ +
+
+get_workfile_thumbnail(project_name, workfile_id, thumbnail_id=None)[source]
+

Prepared method to receive thumbnail for workfile entity.

+
+
Parameters:
+
    +
  • project_name (str) – Project under which the entity is located.

  • +
  • workfile_id (str) – Worfile id for which thumbnail should be +returned.

  • +
  • thumbnail_id (Optional[str]) – Prepared thumbnail id from entity. +Used only to check if thumbnail was already cached.

  • +
+
+
Returns:
+

+
Path to downloaded thumbnail or none if entity

does not have any (or if user does not have permissions).

+
+
+

+
+
Return type:
+

Union[str, None]

+
+
+
+ +
+
+get_workfiles_info(project_name, workfile_ids=None, task_ids=None, paths=None, path_regex=None, statuses=None, tags=None, has_links=None, fields=None, own_attributes=<object object>)[source]
+

Workfile info entities by passed filters.

+
+
Parameters:
+
    +
  • project_name (str) – Project under which the entity is located.

  • +
  • workfile_ids (Optional[Iterable[str]]) – Workfile ids.

  • +
  • task_ids (Optional[Iterable[str]]) – Task ids.

  • +
  • paths (Optional[Iterable[str]]) – Rootless workfiles paths.

  • +
  • path_regex (Optional[str]) – Regex filter for workfile path.

  • +
  • statuses (Optional[Iterable[str]]) – Workfile info statuses used +for filtering.

  • +
  • tags (Optional[Iterable[str]]) – Workfile info tags used +for filtering.

  • +
  • has_links (Optional[Literal[IN, OUT, ANY]]) – Filter +representations with IN/OUT/ANY links.

  • +
  • fields (Optional[Iterable[str]]) – Fields to be queried for +representation. All possible fields are returned if ‘None’ is +passed.

  • +
  • own_attributes (Optional[bool]) – DEPRECATED: Not supported for +workfiles.

  • +
+
+
Returns:
+

Queried workfile info entites.

+
+
Return type:
+

Generator[dict[str, Any]]

+
+
+
+ +
+
+property graphql_allows_data_in_query
+

GraphQl query can support ‘data’ field.

+

This applies only to project hierarchy entities ‘project’, ‘folder’, +‘task’, ‘product’, ‘version’ and ‘representation’. Others like ‘user’ +still require to use rest api to access ‘data’.

+
+
Returns:
+

True if server supports ‘data’ field in GraphQl query.

+
+
Return type:
+

bool

+
+
+
+ +
+
+property has_valid_token
+
+ +
+
+property is_server_available
+
+ +
+
+is_service_user()[source]
+

Check if connection is using service API key.

+
+
Returns:
+

Used api key belongs to service user.

+
+
Return type:
+

bool

+
+
+
+ +
+
+property log
+
+ +
+
+login(username, password, create_session=True)[source]
+

Login to server.

+
+
Parameters:
+
    +
  • username (str) – Username.

  • +
  • password (str) – Password.

  • +
  • create_session (Optional[bool]) – Create session after login. +Default: True.

  • +
+
+
Raises:
+

AuthenticationError – Login failed.

+
+
+
+ +
+
+logout(soft=False)[source]
+
+ +
+ +

Make sure link type exists on a project.

+
+
Parameters:
+
    +
  • project_name (str) – Name of project.

  • +
  • link_type_name (str) – Name of link type.

  • +
  • input_type (str) – Input entity type of link.

  • +
  • output_type (str) – Output entity type of link.

  • +
  • data (Optional[dict[str, Any]]) – Link type related data.

  • +
+
+
+
+ +
+
+property max_retries
+

Current value for requests max retries.

+
+
Returns:
+

Max retries value.

+
+
Return type:
+

int

+
+
+
+ +
+
+patch(entrypoint, **kwargs)[source]
+
+ +
+
+post(entrypoint, **kwargs)[source]
+
+ +
+
+put(entrypoint, **kwargs)[source]
+
+ +
+
+query_graphql(query, variables=None)[source]
+

Execute GraphQl query.

+
+
Parameters:
+
    +
  • query (str) – GraphQl query string.

  • +
  • variables (Optional[dict[str, Any]) – Variables that can be +used in query.

  • +
+
+
Returns:
+

Response from server.

+
+
Return type:
+

GraphQlResponse

+
+
+
+ +
+
+raw_delete(entrypoint, **kwargs)[source]
+
+ +
+
+raw_get(entrypoint, **kwargs)[source]
+
+ +
+
+raw_patch(entrypoint, **kwargs)[source]
+
+ +
+
+raw_post(entrypoint, **kwargs)[source]
+
+ +
+
+raw_put(entrypoint, **kwargs)[source]
+
+ +
+
+remove_attribute_config(attribute_name)[source]
+

Remove attribute from server.

+

This can’t be un-done, please use carefully.

+
+
Parameters:
+

attribute_name (str) – Name of attribute to remove.

+
+
+
+ +
+
+reset_attributes_schema()[source]
+
+ +
+
+reset_token()[source]
+
+ +
+
+property rest_url
+
+ +
+
+save_secret(secret_name, secret_value)[source]
+

Save secret.

+

This endpoint can create and update secret.

+
+
Parameters:
+
    +
  • secret_name (str) – Name of secret.

  • +
  • secret_value (str) – Value of secret.

  • +
+
+
+
+ +
+
+send_activities_batch_operations(project_name, operations, can_fail=False, raise_on_fail=True)[source]
+

Post multiple CRUD activities operations to server.

+

When multiple changes should be made on server side this is the best +way to go. It is possible to pass multiple operations to process on a +server side and do the changes in a transaction.

+
+
Parameters:
+
    +
  • project_name (str) – On which project should be operations +processed.

  • +
  • operations (list[dict[str, Any]]) – Operations to be processed.

  • +
  • can_fail (Optional[bool]) – Server will try to process all +operations even if one of them fails.

  • +
  • raise_on_fail (Optional[bool]) – Raise exception if an operation +fails. You can handle failed operations on your own +when set to ‘False’.

  • +
+
+
Raises:
+
    +
  • ValueError – Operations can’t be converted to json string.

  • +
  • FailedOperations – When output does not contain server operations + or ‘raise_on_fail’ is enabled and any operation fails.

  • +
+
+
Returns:
+

Operations result with process details.

+
+
Return type:
+

list[dict[str, Any]]

+
+
+
+ +
+
+send_batch_operations(project_name, operations, can_fail=False, raise_on_fail=True)[source]
+

Post multiple CRUD operations to server.

+

When multiple changes should be made on server side this is the best +way to go. It is possible to pass multiple operations to process on a +server side and do the changes in a transaction.

+
+
Parameters:
+
    +
  • project_name (str) – On which project should be operations +processed.

  • +
  • operations (list[dict[str, Any]]) – Operations to be processed.

  • +
  • can_fail (Optional[bool]) – Server will try to process all +operations even if one of them fails.

  • +
  • raise_on_fail (Optional[bool]) – Raise exception if an operation +fails. You can handle failed operations on your own +when set to ‘False’.

  • +
+
+
Raises:
+
    +
  • ValueError – Operations can’t be converted to json string.

  • +
  • FailedOperations – When output does not contain server operations + or ‘raise_on_fail’ is enabled and any operation fails.

  • +
+
+
Returns:
+

Operations result with process details.

+
+
Return type:
+

list[dict[str, Any]]

+
+
+
+ +
+
+property sender
+

Sender used to send requests.

+
+
Returns:
+

Sender name or None.

+
+
Return type:
+

Union[str, None]

+
+
+
+ +
+
+property sender_type
+

Sender type used to send requests.

+

Sender type is supported since AYON server 1.5.5 .

+
+
Returns:
+

Sender type or None.

+
+
Return type:
+

Union[str, None]

+
+
+
+ +
+
+property server_version
+

Get server version.

+

Version should match semantic version (https://semver.org/).

+
+
Returns:
+

Server version.

+
+
Return type:
+

str

+
+
+
+ +
+
+property server_version_tuple
+

Get server version as tuple.

+

Version should match semantic version (https://semver.org/).

+

This function only returns first three numbers of version.

+
+
Returns:
+

+
Server

version.

+
+
+

+
+
Return type:
+

Tuple[int, int, int, Union[str, None], Union[str, None]]

+
+
+
+ +
+
+set_attribute_config(attribute_name, data, scope, position=None, builtin=False)[source]
+
+ +
+
+set_cert(cert)[source]
+

Change cert file used for connection to server.

+
+
Parameters:
+

cert (Union[str, None]) – Path to cert file.

+
+
+
+ +
+
+set_client_version(client_version)[source]
+

Set version of client used to connect to server.

+

Client version is AYON client build desktop application.

+
+
Parameters:
+

client_version (Union[str, None]) – Client version string.

+
+
+
+ +
+
+set_default_service_username(username=None)[source]
+

Service API will work as other user.

+

Service API keys can work as other user. It can be temporary using +context manager ‘as_user’ or it is possible to set default username if +‘as_user’ context manager is not entered.

+
+
Parameters:
+

username (Optional[str]) – Username to work as when service.

+
+
Raises:
+

ValueError – When connection is not yet authenticated or api key + is not service token.

+
+
+
+ +
+
+set_default_settings_variant(variant)[source]
+

Change default variant for addon settings.

+
+

Note

+
+
It is recommended to set only ‘production’ or ‘staging’ variants

as default variant.

+
+
+
+
+
Parameters:
+

variant (str) – Settings variant name. It is possible to use +‘production’, ‘staging’ or name of dev bundle.

+
+
+
+ +
+
+set_max_retries(max_retries)[source]
+

Change max retries value for requests.

+
+
Parameters:
+

max_retries (Union[int, None]) – Max retries value.

+
+
+
+ +
+
+set_sender(sender)[source]
+

Change sender used for requests.

+
+
Parameters:
+

sender (Union[str, None]) – Sender name or None.

+
+
+
+ +
+
+set_sender_type(sender_type)[source]
+

Change sender type used for requests.

+
+
Parameters:
+

sender_type (Union[str, None]) – Sender type or None.

+
+
+
+ +
+
+set_site_id(site_id)[source]
+

Change site id of connection.

+

Behave as specific site for server. It affects default behavior of +settings getter methods.

+
+
Parameters:
+

site_id (Union[str, None]) – Site id value, or ‘None’ to unset.

+
+
+
+ +
+
+set_ssl_verify(ssl_verify)[source]
+

Change ssl verification state.

+
+
Parameters:
+

ssl_verify (Union[bool, str, None]) – Enabled/disable +ssl verification, can be a path to file.

+
+
+
+ +
+
+set_timeout(timeout)[source]
+

Change timeout value for requests.

+
+
Parameters:
+

timeout (Union[float, None]) – Timeout value in seconds.

+
+
+
+ +
+
+set_token(token)[source]
+
+ +
+
+property site_id
+

Site id used for connection.

+

Site id tells server from which machine/site is connection created and +is used for default site overrides when settings are received.

+
+
Returns:
+

Site id value or None if not filled.

+
+
Return type:
+

Union[str, None]

+
+
+
+ +
+
+property ssl_verify
+

Enable ssl verification.

+
+
Returns:
+

Current state of ssl verification.

+
+
Return type:
+

bool

+
+
+
+ +
+
+property timeout
+

Current value for requests timeout.

+
+
Returns:
+

Timeout value in seconds.

+
+
Return type:
+

float

+
+
+
+ +
+
+trigger_server_restart()[source]
+

Trigger server restart.

+

Restart may be required when a change of specific value happened on +server.

+
+ +
+
+update_activity(project_name: str, activity_id: str, body: Optional[str] = None, file_ids: Optional[List[str]] = None, append_file_ids: Optional[bool] = False, data: Optional[Dict[str, Any]] = None)[source]
+

Update activity by id.

+
+
Parameters:
+
    +
  • project_name (str) – Project on which activity happened.

  • +
  • activity_id (str) – Activity id.

  • +
  • body (str) – Activity body.

  • +
  • file_ids (Optional[List[str]]) – List of file ids attached +to activity.

  • +
  • append_file_ids (Optional[bool]) – Append file ids to existing +list of file ids.

  • +
  • data (Optional[Dict[str, Any]]) – Update data in activity.

  • +
+
+
+
+ +
+
+update_bundle(bundle_name, addon_versions=None, installer_version=None, dependency_packages=None, is_production=None, is_staging=None, is_dev=None, dev_active_user=None, dev_addons_config=None)[source]
+

Update bundle on server.

+

Dependency packages can be update only for single platform. Others +will be left untouched. Use ‘None’ value to unset dependency package +from bundle.

+
+
Parameters:
+
    +
  • bundle_name (str) – Name of bundle.

  • +
  • addon_versions (Optional[dict[str, str]]) – Addon versions, +possible only for dev bundles.

  • +
  • installer_version (Optional[str]) – Installer version, possible +only for dev bundles.

  • +
  • dependency_packages (Optional[dict[str, str]]) – Dependency pacakge +names that should be used with the bundle.

  • +
  • is_production (Optional[bool]) – Bundle will be marked as +production.

  • +
  • is_staging (Optional[bool]) – Bundle will be marked as staging.

  • +
  • is_dev (Optional[bool]) – Bundle will be marked as dev.

  • +
  • dev_active_user (Optional[str]) – Username that will be assigned +to dev bundle. Can be used only for dev bundles.

  • +
  • dev_addons_config (Optional[dict[str, Any]]) – Configuration for +dev addons. Can be used only for dev bundles.

  • +
+
+
+
+ +
+
+update_dependency_package(filename, sources)[source]
+

Update dependency package metadata on server.

+
+
Parameters:
+
    +
  • filename (str) – Filename of dependency package.

  • +
  • sources (list[dict[str, Any]]) – Information about +sources from where it is possible to get file. Fully replaces +existing sources.

  • +
+
+
+
+ +
+
+update_event(event_id, sender=None, project_name=None, username=None, status=None, description=None, summary=None, payload=None, progress=None, retries=None)[source]
+

Update event data.

+
+
Parameters:
+
    +
  • event_id (str) – Event id.

  • +
  • sender (Optional[str]) – New sender of event.

  • +
  • project_name (Optional[str]) – New project name.

  • +
  • username (Optional[str]) – New username.

  • +
  • status (Optional[str]) – New event status. Enum: “pending”, +“in_progress”, “finished”, “failed”, “aborted”, “restarted”

  • +
  • description (Optional[str]) – New description.

  • +
  • summary (Optional[dict[str, Any]]) – New summary.

  • +
  • payload (Optional[dict[str, Any]]) – New payload.

  • +
  • progress (Optional[int]) – New progress. Range [0-100].

  • +
  • retries (Optional[int]) – New retries.

  • +
+
+
+
+ +
+
+update_folder(project_name, folder_id, name=None, folder_type=None, parent_id=<object object>, label=<object object>, attrib=None, data=None, tags=None, status=None, active=None, thumbnail_id=<object object>)[source]
+

Update folder entity on server.

+
+
Do not pass parent_id, label amd thumbnail_id if you don’t

want to change their values. Value None would unset +their value.

+
+
+

Update of data will override existing value on folder entity.

+
+
Update of attrib does change only passed attributes. If you want

to unset value, use None.

+
+
+
+
Parameters:
+
    +
  • project_name (str) – Project name.

  • +
  • folder_id (str) – Folder id.

  • +
  • name (Optional[str]) – New name.

  • +
  • folder_type (Optional[str]) – New folder type.

  • +
  • parent_id (Optional[Union[str, None]]) – New parent folder id.

  • +
  • label (Optional[Union[str, None]]) – New label.

  • +
  • attrib (Optional[dict[str, Any]]) – New attributes.

  • +
  • data (Optional[dict[str, Any]]) – New data.

  • +
  • tags (Optional[Iterable[str]]) – New tags.

  • +
  • status (Optional[str]) – New status.

  • +
  • active (Optional[bool]) – New active state.

  • +
  • thumbnail_id (Optional[Union[str, None]]) – New thumbnail id.

  • +
+
+
+
+ +
+
+update_installer(filename, sources)[source]
+

Update installer information on server.

+
+
Parameters:
+
    +
  • filename (str) – Installer filename.

  • +
  • sources (list[dict[str, Any]]) – List of sources that +can be used to download file. Fully replaces existing sources.

  • +
+
+
+
+ +
+
+update_product(project_name, product_id, name=None, folder_id=None, product_type=None, attrib=None, data=None, tags=None, status=None, active=None)[source]
+

Update product entity on server.

+

Update of data will override existing value on folder entity.

+
+
Update of attrib does change only passed attributes. If you want

to unset value, use None.

+
+
+
+
Parameters:
+
    +
  • project_name (str) – Project name.

  • +
  • product_id (str) – Product id.

  • +
  • name (Optional[str]) – New product name.

  • +
  • folder_id (Optional[str]) – New product id.

  • +
  • product_type (Optional[str]) – New product type.

  • +
  • attrib (Optional[dict[str, Any]]) – New product attributes.

  • +
  • data (Optional[dict[str, Any]]) – New product data.

  • +
  • tags (Optional[Iterable[str]]) – New product tags.

  • +
  • status (Optional[str]) – New product status.

  • +
  • active (Optional[bool]) – New product active state.

  • +
+
+
+
+ +
+
+update_project(project_name, library=None, folder_types=None, task_types=None, link_types=None, statuses=None, tags=None, config=None, attrib=None, data=None, active=None, project_code=None, **changes)[source]
+

Update project entity on server.

+
+
Parameters:
+
    +
  • project_name (str) – Name of project.

  • +
  • library (Optional[bool]) – Change library state.

  • +
  • folder_types (Optional[list[dict[str, Any]]]) – Folder type +definitions.

  • +
  • task_types (Optional[list[dict[str, Any]]]) – Task type +definitions.

  • +
  • link_types (Optional[list[dict[str, Any]]]) – Link type +definitions.

  • +
  • statuses (Optional[list[dict[str, Any]]]) – Status definitions.

  • +
  • tags (Optional[list[dict[str, Any]]]) – List of tags available to +set on entities.

  • +
  • config (Optional[dict[dict[str, Any]]]) – Project anatomy config +with templates and roots.

  • +
  • attrib (Optional[dict[str, Any]]) – Project attributes to change.

  • +
  • data (Optional[dict[str, Any]]) – Custom data of a project. This +value will 100% override project data.

  • +
  • active (Optional[bool]) – Change active state of a project.

  • +
  • project_code (Optional[str]) – Change project code. Not recommended +during production.

  • +
  • **changes – Other changed keys based on Rest API documentation.

  • +
+
+
+
+ +
+
+update_representation(project_name, representation_id, name=None, version_id=None, files=None, attrib=None, data=None, tags=None, status=None, active=None)[source]
+

Update representation entity on server.

+

Update of data will override existing value on folder entity.

+
+
Update of attrib does change only passed attributes. If you want

to unset value, use None.

+
+
+
+
Parameters:
+
    +
  • project_name (str) – Project name.

  • +
  • representation_id (str) – Representation id.

  • +
  • name (Optional[str]) – New name.

  • +
  • version_id (Optional[str]) – New version id.

  • +
  • files (Optional[list[dict]]) – New files +information.

  • +
  • attrib (Optional[dict[str, Any]]) – New attributes.

  • +
  • data (Optional[dict[str, Any]]) – New data.

  • +
  • tags (Optional[Iterable[str]]) – New tags.

  • +
  • status (Optional[str]) – New status.

  • +
  • active (Optional[bool]) – New active state.

  • +
+
+
+
+ +
+
+update_task(project_name, task_id, name=None, task_type=None, folder_id=None, label=<object object>, assignees=None, attrib=None, data=None, tags=None, status=None, active=None, thumbnail_id=<object object>)[source]
+

Update task entity on server.

+
+
Do not pass label amd thumbnail_id if you don’t

want to change their values. Value None would unset +their value.

+
+
+

Update of data will override existing value on folder entity.

+
+
Update of attrib does change only passed attributes. If you want

to unset value, use None.

+
+
+
+
Parameters:
+
    +
  • project_name (str) – Project name.

  • +
  • task_id (str) – Task id.

  • +
  • name (Optional[str]) – New name.

  • +
  • task_type (Optional[str]) – New task type.

  • +
  • folder_id (Optional[str]) – New folder id.

  • +
  • label (Optional[Union[str, None]]) – New label.

  • +
  • assignees (Optional[str]) – New assignees.

  • +
  • attrib (Optional[dict[str, Any]]) – New attributes.

  • +
  • data (Optional[dict[str, Any]]) – New data.

  • +
  • tags (Optional[Iterable[str]]) – New tags.

  • +
  • status (Optional[str]) – New status.

  • +
  • active (Optional[bool]) – New active state.

  • +
  • thumbnail_id (Optional[Union[str, None]]) – New thumbnail id.

  • +
+
+
+
+ +
+
+update_thumbnail(project_name, thumbnail_id, src_filepath)[source]
+

Change thumbnail content by id.

+

Update can be also used to create new thumbnail.

+
+
Parameters:
+
    +
  • project_name (str) – Project where the thumbnail will be created +and can be used.

  • +
  • thumbnail_id (str) – Thumbnail id to update.

  • +
  • src_filepath (str) – Filepath to thumbnail which should be uploaded.

  • +
+
+
Raises:
+

ValueError – When thumbnail source cannot be processed.

+
+
+
+ +
+
+update_version(project_name, version_id, version=None, product_id=None, task_id=<object object>, author=None, attrib=None, data=None, tags=None, status=None, active=None, thumbnail_id=<object object>)[source]
+

Update version entity on server.

+
+
Do not pass task_id amd thumbnail_id if you don’t

want to change their values. Value None would unset +their value.

+
+
+

Update of data will override existing value on folder entity.

+
+
Update of attrib does change only passed attributes. If you want

to unset value, use None.

+
+
+
+
Parameters:
+
    +
  • project_name (str) – Project name.

  • +
  • version_id (str) – Version id.

  • +
  • version (Optional[int]) – New version.

  • +
  • product_id (Optional[str]) – New product id.

  • +
  • task_id (Optional[Union[str, None]]) – New task id.

  • +
  • author (Optional[str]) – New author username.

  • +
  • attrib (Optional[dict[str, Any]]) – New attributes.

  • +
  • data (Optional[dict[str, Any]]) – New data.

  • +
  • tags (Optional[Iterable[str]]) – New tags.

  • +
  • status (Optional[str]) – New status.

  • +
  • active (Optional[bool]) – New active state.

  • +
  • thumbnail_id (Optional[Union[str, None]]) – New thumbnail id.

  • +
+
+
+
+ +
+
+upload_addon_zip(src_filepath, progress=None)[source]
+

Upload addon zip file to server.

+
+
File is validated on server. If it is valid, it is installed. It will

create an event job which can be tracked (tracking part is not +implemented yet).

+
+
+

Example output:

+
{'eventId': 'a1bfbdee27c611eea7580242ac120003'}
+
+
+
+
Parameters:
+
    +
  • src_filepath (str) – Path to a zip file.

  • +
  • progress (Optional[TransferProgress]) – Object to keep track about +upload state.

  • +
+
+
Returns:
+

Response data from server.

+
+
Return type:
+

dict[str, Any]

+
+
+
+ +
+
+upload_dependency_package(src_filepath, dst_filename, platform_name=None, progress=None)[source]
+

Upload dependency package to server.

+
+
Parameters:
+
    +
  • src_filepath (str) – Path to a package file.

  • +
  • dst_filename (str) – Dependency package filename or name of package +for server version 0.2.0 or lower. Must be unique.

  • +
  • platform_name (Optional[str]) – Deprecated.

  • +
  • progress (Optional[TransferProgress]) – Object to keep track about +upload state.

  • +
+
+
+
+ +
+
+upload_file(endpoint, filepath, progress=None, request_type=None, **kwargs)[source]
+

Upload file to server.

+
+
Todos:

Use retries and timeout. +Return RestApiResponse.

+
+
+
+
Parameters:
+
    +
  • endpoint (str) – Endpoint or url where file will be uploaded.

  • +
  • filepath (str) – Source filepath.

  • +
  • progress (Optional[TransferProgress]) – Object that gives ability +to track upload progress.

  • +
  • request_type (Optional[RequestType]) – Type of request that will +be used to upload file.

  • +
  • **kwargs (Any) – Additional arguments that will be passed +to request function.

  • +
+
+
Returns:
+

Response object

+
+
Return type:
+

requests.Response

+
+
+
+ +
+
+upload_file_from_stream(endpoint, stream, progress=None, request_type=None, **kwargs)[source]
+

Upload file to server from bytes.

+
+
Todos:

Use retries and timeout. +Return RestApiResponse.

+
+
+
+
Parameters:
+
    +
  • endpoint (str) – Endpoint or url where file will be uploaded.

  • +
  • stream (Union[io.BytesIO, BinaryIO]) – File content stream.

  • +
  • progress (Optional[TransferProgress]) – Object that gives ability +to track upload progress.

  • +
  • request_type (Optional[RequestType]) – Type of request that will +be used to upload file.

  • +
  • **kwargs (Any) – Additional arguments that will be passed +to request function.

  • +
+
+
Returns:
+

Response object

+
+
Return type:
+

requests.Response

+
+
+
+ +
+
+upload_installer(src_filepath, dst_filename, progress=None)[source]
+

Upload installer file to server.

+
+
Parameters:
+
    +
  • src_filepath (str) – Source filepath.

  • +
  • dst_filename (str) – Destination filename.

  • +
  • progress (Optional[TransferProgress]) – Object that gives ability +to track download progress.

  • +
+
+
Returns:
+

Response object.

+
+
Return type:
+

requests.Response

+
+
+
+ +
+
+upload_reviewable(project_name, version_id, filepath, label=None, content_type=None, filename=None, progress=None, headers=None, **kwargs)[source]
+

Upload reviewable file to server.

+
+
Parameters:
+
    +
  • project_name (str) – Project name.

  • +
  • version_id (str) – Version id.

  • +
  • filepath (str) – Reviewable file path to upload.

  • +
  • label (Optional[str]) – Reviewable label. Filled automatically +server side with filename.

  • +
  • content_type (Optional[str]) – MIME type of the file.

  • +
  • filename (Optional[str]) – User as original filename. Filename from +‘filepath’ is used when not filled.

  • +
  • progress (Optional[TransferProgress]) – Progress.

  • +
  • headers (Optional[Dict[str, Any]]) – Headers.

  • +
+
+
Returns:
+

Server response.

+
+
Return type:
+

RestApiResponse

+
+
+
+ +
+
+validate_server_availability()[source]
+
+ +
+
+validate_token()[source]
+
+ +
+
+version_is_latest(project_name, version_id)[source]
+

Is version latest from a product.

+
+
Parameters:
+
    +
  • project_name (str) – Project where to look for representation.

  • +
  • version_id (str) – Version id.

  • +
+
+
Returns:
+

Version is latest or not.

+
+
Return type:
+

bool

+
+
+
+ +
+ +
+
+class ServiceContext[source]
+

Bases: object

+

Helper for services running under server.

+

When service is running from server the process receives information about +connection from environment variables. This class helps to initialize the +values without knowing environment variables (that may change over time).

+

All what must be done is to call ‘init_service’ function/method. The +arguments are for cases when the service is running in specific environment +and their values are e.g. loaded from private file or for testing purposes.

+
+
+addon_name = None
+
+ +
+
+addon_version = None
+
+ +
+
+classmethod init_service(token=None, server_url=None, addon_name=None, addon_version=None, service_name=None, connect=True)[source]
+
+ +
+
+server_url = None
+
+ +
+
+service_name = None
+
+ +
+
+token = None
+
+ +
+ +
+
+class SortOrder(value)[source]
+

Bases: IntEnum

+

Sort order for GraphQl requests.

+
+
+ascending = 0
+
+ +
+
+descending = 1
+
+ +
+
+classmethod parse_value(value, default=None)[source]
+
+ +
+ +
+
+class TransferProgress[source]
+

Bases: object

+

Object to store progress of download/upload from/to server.

+
+
+add_transferred_chunk(chunk_size)[source]
+

Add transferred chunk size in bytes.

+
+
Parameters:
+

chunk_size (int) – Add transferred chunk size +in bytes.

+
+
+
+ +
+
+property content_size
+

Content size in bytes.

+
+
Returns:
+

+
Content size in bytes or None

if is unknown.

+
+
+

+
+
Return type:
+

Union[int, None]

+
+
+
+ +
+
+property destination_url
+

Destination url where transfer happens.

+
+

Note

+
+
Consider this as title. Must be set using

‘set_source_url’ or ‘N/A’ will be returned.

+
+
+
+
+
Returns:
+

Destination url where transfer happens.

+
+
Return type:
+

str

+
+
+
+ +
+
+property fail_reason
+

Get reason why transfer failed.

+
+
Returns:
+

+
Reason why transfer

failed or None.

+
+
+

+
+
Return type:
+

Union[str, None]

+
+
+
+ +
+
+property failed
+

Transfer failed.

+
+
Returns:
+

True if transfer failed.

+
+
Return type:
+

bool

+
+
+
+ +
+
+get_content_size()[source]
+

Content size in bytes.

+
+
Returns:
+

+
Content size in bytes or None

if is unknown.

+
+
+

+
+
Return type:
+

Union[int, None]

+
+
+
+ +
+
+get_destination_url()[source]
+

Destination url where transfer happens.

+
+

Note

+
+
Consider this as title. Must be set using

‘set_source_url’ or ‘N/A’ will be returned.

+
+
+
+
+
Returns:
+

Destination url where transfer happens.

+
+
Return type:
+

str

+
+
+
+ +
+
+get_fail_reason()[source]
+

Get reason why transfer failed.

+
+
Returns:
+

+
Reason why transfer

failed or None.

+
+
+

+
+
Return type:
+

Union[str, None]

+
+
+
+ +
+
+get_failed()[source]
+

Transfer failed.

+
+
Returns:
+

True if transfer failed.

+
+
Return type:
+

bool

+
+
+
+ +
+
+get_source_url()[source]
+

Source url from where transfer happens.

+
+

Note

+
+
Consider this as title. Must be set using

‘set_source_url’ or ‘N/A’ will be returned.

+
+
+
+
+
Returns:
+

Source url from where transfer happens.

+
+
Return type:
+

str

+
+
+
+ +
+
+get_started()[source]
+

Transfer was started.

+
+
Returns:
+

True if transfer started.

+
+
Return type:
+

bool

+
+
+
+ +
+
+get_transfer_done()[source]
+

Transfer finished.

+
+
Returns:
+

Transfer finished.

+
+
Return type:
+

bool

+
+
+
+ +
+
+get_transferred_size()[source]
+

Already transferred size in bytes.

+
+
Returns:
+

Already transferred size in bytes.

+
+
Return type:
+

int

+
+
+
+ +
+
+property is_running
+

Check if transfer is running.

+
+
Returns:
+

True if transfer is running.

+
+
Return type:
+

bool

+
+
+
+ +
+
+set_content_size(content_size)[source]
+

Set content size in bytes.

+
+
Parameters:
+

content_size (int) – Content size in bytes.

+
+
Raises:
+

ValueError – If content size was already set.

+
+
+
+ +
+
+set_destination_url(url)[source]
+

Set destination url where transfer happens.

+
+
Parameters:
+

url (str) – Destination url where transfer happens.

+
+
+
+ +
+
+set_failed(reason)[source]
+

Mark progress as failed.

+
+
Parameters:
+

reason (str) – Reason why transfer failed.

+
+
+
+ +
+
+set_source_url(url)[source]
+

Set source url from where transfer happens.

+
+
Parameters:
+

url (str) – Source url from where transfer happens.

+
+
+
+ +
+
+set_started()[source]
+

Mark that transfer started.

+
+
Raises:
+

ValueError – If transfer was already started.

+
+
+
+ +
+
+set_transfer_done()[source]
+

Mark progress as transfer finished.

+
+
Raises:
+

ValueError – If progress was already marked as done + or wasn’t started yet.

+
+
+
+ +
+
+set_transferred_size(transferred)[source]
+

Set already transferred size in bytes.

+
+
Parameters:
+

transferred (int) – Already transferred size in bytes.

+
+
+
+ +
+
+property source_url
+

Source url from where transfer happens.

+
+

Note

+
+
Consider this as title. Must be set using

‘set_source_url’ or ‘N/A’ will be returned.

+
+
+
+
+
Returns:
+

Source url from where transfer happens.

+
+
Return type:
+

str

+
+
+
+ +
+
+property started
+

Transfer was started.

+
+
Returns:
+

True if transfer started.

+
+
Return type:
+

bool

+
+
+
+ +
+
+property transfer_done
+

Transfer finished.

+
+
Returns:
+

Transfer finished.

+
+
Return type:
+

bool

+
+
+
+ +
+
+property transfer_progress
+

Get transfer progress in percents.

+
+
Returns:
+

+
Transfer progress in percents or ‘None’

if content size is unknown.

+
+
+

+
+
Return type:
+

Union[float, None]

+
+
+
+ +
+
+property transferred_size
+

Already transferred size in bytes.

+
+
Returns:
+

Already transferred size in bytes.

+
+
Return type:
+

int

+
+
+
+ +
+ +
+
+abort_web_action_event(server_url: str, action_token: str, reason: str)[source]
+

Abort web action event using action token.

+

A web action event could not be processed for some reason.

+
+
Parameters:
+
    +
  • server_url (str) – AYON server url.

  • +
  • action_token (str) – Action token.

  • +
  • reason (str) – Reason why webaction event was aborted.

  • +
+
+
Returns:
+

Response from server.

+
+
Return type:
+

requests.Response

+
+
+
+ +
+
+change_token(url, token)[source]
+

Change connection token for url.

+

This function can be also used to change url.

+
+
Parameters:
+
    +
  • url (str) – Server url.

  • +
  • token (str) – API key token.

  • +
+
+
+
+ +
+
+check_bundle_compatibility(name, addon_versions, installer_version, dependency_packages=None, is_production=None, is_staging=None, is_dev=None, dev_active_user=None, dev_addons_config=None)[source]
+

Check bundle compatibility.

+

Can be used as per-flight validation before creating bundle.

+
+
Parameters:
+
    +
  • name (str) – Name of bundle.

  • +
  • addon_versions (dict[str, str]) – Addon versions.

  • +
  • installer_version (Union[str, None]) – Installer version.

  • +
  • dependency_packages (Optional[dict[str, str]]) – Dependency +package names. Keys are platform names and values are name of +packages.

  • +
  • is_production (Optional[bool]) – Bundle will be marked as +production.

  • +
  • is_staging (Optional[bool]) – Bundle will be marked as staging.

  • +
  • is_dev (Optional[bool]) – Bundle will be marked as dev.

  • +
  • dev_active_user (Optional[str]) – Username that will be assigned +to dev bundle. Can be used only if ‘is_dev’ is set to ‘True’.

  • +
  • dev_addons_config (Optional[dict[str, Any]]) – Configuration for +dev addons. Can be used only if ‘is_dev’ is set to ‘True’.

  • +
+
+
Returns:
+

Server response, with ‘success’ and ‘issues’.

+
+
Return type:
+

Dict[str, Any]

+
+
+
+ +
+
+close_connection()[source]
+

Close global connection if is connected.

+
+ +
+
+create_activity(project_name: str, entity_id: str, entity_type: str, activity_type: ActivityType, activity_id: Optional[str] = None, body: Optional[str] = None, file_ids: Optional[List[str]] = None, timestamp: Optional[str] = None, data: Optional[Dict[str, Any]] = None) str[source]
+

Create activity on a project.

+
+
Parameters:
+
    +
  • project_name (str) – Project on which activity happened.

  • +
  • entity_id (str) – Entity id.

  • +
  • entity_type (str) – Entity type.

  • +
  • activity_type (ActivityType) – Activity type.

  • +
  • activity_id (Optional[str]) – Activity id.

  • +
  • body (Optional[str]) – Activity body.

  • +
  • file_ids (Optional[List[str]]) – List of file ids attached +to activity.

  • +
  • timestamp (Optional[str]) – Activity timestamp.

  • +
  • data (Optional[Dict[str, Any]]) – Additional data.

  • +
+
+
Returns:
+

Activity id.

+
+
Return type:
+

str

+
+
+
+ +
+
+create_bundle(name, addon_versions, installer_version, dependency_packages=None, is_production=None, is_staging=None, is_dev=None, dev_active_user=None, dev_addons_config=None)[source]
+

Create bundle on server.

+

Bundle cannot be changed once is created. Only isProduction, isStaging +and dependency packages can change after creation. In case dev bundle +is created, it is possible to change anything, but it is not possible +to mark bundle as dev and production or staging at the same time.

+

Development addon config can define custom path to client code. It is +used only for dev bundles.

+

Example of ‘dev_addons_config’:

+
```json
+{
+    "core": {
+        "enabled": true,
+        "path": "/path/to/ayon-core/client"
+    }
+}
+```
+
+
+
+
Parameters:
+
    +
  • name (str) – Name of bundle.

  • +
  • addon_versions (dict[str, str]) – Addon versions.

  • +
  • installer_version (Union[str, None]) – Installer version.

  • +
  • dependency_packages (Optional[dict[str, str]]) – Dependency +package names. Keys are platform names and values are name of +packages.

  • +
  • is_production (Optional[bool]) – Bundle will be marked as +production.

  • +
  • is_staging (Optional[bool]) – Bundle will be marked as staging.

  • +
  • is_dev (Optional[bool]) – Bundle will be marked as dev.

  • +
  • dev_active_user (Optional[str]) – Username that will be assigned +to dev bundle. Can be used only if ‘is_dev’ is set to ‘True’.

  • +
  • dev_addons_config (Optional[dict[str, Any]]) – Configuration for +dev addons. Can be used only if ‘is_dev’ is set to ‘True’.

  • +
+
+
+
+ +
+
+create_connection(site_id=None, client_version=None)[source]
+

Create global connection.

+
+
Parameters:
+
    +
  • site_id (str) – Machine site id/name.

  • +
  • client_version (str) – Desktop app version.

  • +
+
+
Returns:
+

Created connection.

+
+
Return type:
+

GlobalServerAPI

+
+
+
+ +
+
+create_dependency_package(filename, python_modules, source_addons, installer_version, checksum, checksum_algorithm, file_size, sources=None, platform_name=None)[source]
+

Create dependency package on server.

+

The package will be created on a server, it is also required to upload +the package archive file (using upload_dependency_package()).

+
+
Parameters:
+
    +
  • filename (str) – Filename of dependency package.

  • +
  • python_modules (dict[str, str]) –

    Python modules in dependency +package:

    +
    {"<module name>": "<module version>", ...}
    +
    +
    +

  • +
  • source_addons (dict[str, str]) –

    Name of addons for which is +dependency package created:

    +
    {"<addon name>": "<addon version>", ...}
    +
    +
    +

  • +
  • installer_version (str) – Version of installer for which was +package created.

  • +
  • checksum (str) – Checksum of archive file where dependencies are.

  • +
  • checksum_algorithm (str) – Algorithm used to calculate checksum.

  • +
  • file_size (Optional[int]) – Size of file.

  • +
  • sources (Optional[list[dict[str, Any]]]) – Information about +sources from where it is possible to get file.

  • +
  • platform_name (Optional[str]) – Name of platform for which is +dependency package targeted. Default value is +current platform.

  • +
+
+
+
+ +
+
+create_dependency_package_basename(platform_name=None)[source]
+

Create basename for dependency package file.

+
+
Parameters:
+

platform_name (Optional[str]) – Name of platform for which the +bundle is targeted. Default value is current platform.

+
+
Returns:
+

Dependency package name with timestamp and platform.

+
+
Return type:
+

str

+
+
+
+ +
+
+create_folder(project_name, name, folder_type=None, parent_id=None, label=None, attrib=None, data=None, tags=None, status=None, active=None, thumbnail_id=None, folder_id=None)[source]
+

Create new folder.

+
+
Parameters:
+
    +
  • project_name (str) – Project name.

  • +
  • name (str) – Folder name.

  • +
  • folder_type (Optional[str]) – Folder type.

  • +
  • parent_id (Optional[str]) – Parent folder id. Parent is project +if is None.

  • +
  • label (Optional[str]) – Label of folder.

  • +
  • attrib (Optional[dict[str, Any]]) – Folder attributes.

  • +
  • data (Optional[dict[str, Any]]) – Folder data.

  • +
  • tags (Optional[Iterable[str]]) – Folder tags.

  • +
  • status (Optional[str]) – Folder status.

  • +
  • active (Optional[bool]) – Folder active state.

  • +
  • thumbnail_id (Optional[str]) – Folder thumbnail id.

  • +
  • folder_id (Optional[str]) – Folder id. If not passed new id is +generated.

  • +
+
+
Returns:
+

Entity id.

+
+
Return type:
+

str

+
+
+
+ +
+
+create_installer(filename, version, python_version, platform_name, python_modules, runtime_python_modules, checksum, checksum_algorithm, file_size, sources=None)[source]
+

Create new installer information on server.

+
+
This step will create only metadata. Make sure to upload installer

to the server using ‘upload_installer’ method.

+
+
Runtime python modules are modules that are required to run AYON

desktop application, but are not added to PYTHONPATH for any +subprocess.

+
+
+
+
Parameters:
+
    +
  • filename (str) – Installer filename.

  • +
  • version (str) – Version of installer.

  • +
  • python_version (str) – Version of Python.

  • +
  • platform_name (str) – Name of platform.

  • +
  • python_modules (dict[str, str]) – Python modules that are available +in installer.

  • +
  • runtime_python_modules (dict[str, str]) – Runtime python modules +that are available in installer.

  • +
  • checksum (str) – Installer file checksum.

  • +
  • checksum_algorithm (str) – Type of checksum used to create checksum.

  • +
  • file_size (int) – File size.

  • +
  • sources (Optional[list[dict[str, Any]]]) – List of sources that +can be used to download file.

  • +
+
+
+
+ +
+ +

Create link between 2 entities.

+

Link has a type which must already exists on a project.

+

Example output:

+
{
+    "id": "59a212c0d2e211eda0e20242ac120002"
+}
+
+
+
+
Parameters:
+
    +
  • project_name (str) – Project where the link is created.

  • +
  • link_type_name (str) – Type of link.

  • +
  • input_id (str) – Input entity id.

  • +
  • input_type (str) – Entity type of input entity.

  • +
  • output_id (str) – Output entity id.

  • +
  • output_type (str) – Entity type of output entity.

  • +
  • link_name (Optional[str]) – Name of link. +Available from server version ‘1.0.0-rc.6’.

  • +
+
+
Returns:
+

Information about link.

+
+
Return type:
+

dict[str, str]

+
+
Raises:
+

HTTPRequestError – Server error happened.

+
+
+
+ +
+ +

Create or update link type on server.

+
+

Warning

+

Because PUT is used for creation it is also used for update.

+
+
+
Parameters:
+
    +
  • project_name (str) – Project where link type is created.

  • +
  • link_type_name (str) – Name of link type.

  • +
  • input_type (str) – Input entity type of link.

  • +
  • output_type (str) – Output entity type of link.

  • +
  • data (Optional[dict[str, Any]]) – Additional data related to link.

  • +
+
+
Raises:
+

HTTPRequestError – Server error happened.

+
+
+
+ +
+
+create_product(project_name, name, product_type, folder_id, attrib=None, data=None, tags=None, status=None, active=None, product_id=None)[source]
+

Create new product.

+
+
Parameters:
+
    +
  • project_name (str) – Project name.

  • +
  • name (str) – Product name.

  • +
  • product_type (str) – Product type.

  • +
  • folder_id (str) – Parent folder id.

  • +
  • attrib (Optional[dict[str, Any]]) – Product attributes.

  • +
  • data (Optional[dict[str, Any]]) – Product data.

  • +
  • tags (Optional[Iterable[str]]) – Product tags.

  • +
  • status (Optional[str]) – Product status.

  • +
  • active (Optional[bool]) – Product active state.

  • +
  • product_id (Optional[str]) – Product id. If not passed new id is +generated.

  • +
+
+
Returns:
+

Product id.

+
+
Return type:
+

str

+
+
+
+ +
+
+create_project(project_name, project_code, library_project=False, preset_name=None)[source]
+

Create project using AYON settings.

+

This project creation function is not validating project entity on +creation. It is because project entity is created blindly with only +minimum required information about project which is name and code.

+

Entered project name must be unique and project must not exist yet.

+
+

Note

+
+
This function is here to be OP v4 ready but in v3 has more logic

to do. That’s why inner imports are in the body.

+
+
+
+
+
Parameters:
+
    +
  • project_name (str) – New project name. Should be unique.

  • +
  • project_code (str) – Project’s code should be unique too.

  • +
  • library_project (Optional[bool]) – Project is library project.

  • +
  • preset_name (Optional[str]) – Name of anatomy preset. Default is +used if not passed.

  • +
+
+
Raises:
+

ValueError – When project name already exists.

+
+
Returns:
+

Created project entity.

+
+
Return type:
+

dict[str, Any]

+
+
+
+ +
+
+create_representation(project_name, name, version_id, files=None, attrib=None, data=None, tags=None, status=None, active=None, representation_id=None)[source]
+

Create new representation.

+
+
Parameters:
+
    +
  • project_name (str) – Project name.

  • +
  • name (str) – Representation name.

  • +
  • version_id (str) – Parent version id.

  • +
  • files (Optional[list[dict]]) – Representation files information.

  • +
  • attrib (Optional[dict[str, Any]]) – Representation attributes.

  • +
  • data (Optional[dict[str, Any]]) – Representation data.

  • +
  • tags (Optional[Iterable[str]]) – Representation tags.

  • +
  • status (Optional[str]) – Representation status.

  • +
  • active (Optional[bool]) – Representation active state.

  • +
  • representation_id (Optional[str]) – Representation id. If not +passed new id is generated.

  • +
+
+
Returns:
+

Representation id.

+
+
Return type:
+

str

+
+
+
+ +
+
+create_task(project_name, name, task_type, folder_id, label=None, assignees=None, attrib=None, data=None, tags=None, status=None, active=None, thumbnail_id=None, task_id=None)[source]
+

Create new task.

+
+
Parameters:
+
    +
  • project_name (str) – Project name.

  • +
  • name (str) – Folder name.

  • +
  • task_type (str) – Task type.

  • +
  • folder_id (str) – Parent folder id.

  • +
  • label (Optional[str]) – Label of folder.

  • +
  • assignees (Optional[Iterable[str]]) – Task assignees.

  • +
  • attrib (Optional[dict[str, Any]]) – Task attributes.

  • +
  • data (Optional[dict[str, Any]]) – Task data.

  • +
  • tags (Optional[Iterable[str]]) – Task tags.

  • +
  • status (Optional[str]) – Task status.

  • +
  • active (Optional[bool]) – Task active state.

  • +
  • thumbnail_id (Optional[str]) – Task thumbnail id.

  • +
  • task_id (Optional[str]) – Task id. If not passed new id is +generated.

  • +
+
+
Returns:
+

Task id.

+
+
Return type:
+

str

+
+
+
+ +
+
+create_thumbnail(project_name, src_filepath, thumbnail_id=None)[source]
+

Create new thumbnail on server from passed path.

+
+
Parameters:
+
    +
  • project_name (str) – Project where the thumbnail will be created +and can be used.

  • +
  • src_filepath (str) – Filepath to thumbnail which should be uploaded.

  • +
  • thumbnail_id (Optional[str]) – Prepared if of thumbnail.

  • +
+
+
Returns:
+

Created thumbnail id.

+
+
Return type:
+

str

+
+
Raises:
+

ValueError – When thumbnail source cannot be processed.

+
+
+
+ +
+
+create_version(project_name, version, product_id, task_id=None, author=None, attrib=None, data=None, tags=None, status=None, active=None, thumbnail_id=None, version_id=None)[source]
+

Create new version.

+
+
Parameters:
+
    +
  • project_name (str) – Project name.

  • +
  • version (int) – Version.

  • +
  • product_id (str) – Parent product id.

  • +
  • task_id (Optional[str]) – Parent task id.

  • +
  • author (Optional[str]) – Version author.

  • +
  • attrib (Optional[dict[str, Any]]) – Version attributes.

  • +
  • data (Optional[dict[str, Any]]) – Version data.

  • +
  • tags (Optional[Iterable[str]]) – Version tags.

  • +
  • status (Optional[str]) – Version status.

  • +
  • active (Optional[bool]) – Version active state.

  • +
  • thumbnail_id (Optional[str]) – Version thumbnail id.

  • +
  • version_id (Optional[str]) – Version id. If not passed new id is +generated.

  • +
+
+
Returns:
+

Version id.

+
+
Return type:
+

str

+
+
+
+ +
+
+delete(entrypoint, **kwargs)[source]
+
+ +
+
+delete_activity(project_name: str, activity_id: str)[source]
+

Delete activity by id.

+
+
Parameters:
+
    +
  • project_name (str) – Project on which activity happened.

  • +
  • activity_id (str) – Activity id to remove.

  • +
+
+
+
+ +
+
+delete_addon(addon_name: str, purge: Optional[bool] = None)[source]
+

Delete addon from server.

+

Delete all versions of addon from server.

+
+
Parameters:
+
    +
  • addon_name (str) – Addon name.

  • +
  • purge (Optional[bool]) – Purge all data related to the addon.

  • +
+
+
+
+ +
+
+delete_addon_version(addon_name: str, addon_version: str, purge: Optional[bool] = None)[source]
+

Delete addon version from server.

+

Delete all versions of addon from server.

+
+
Parameters:
+
    +
  • addon_name (str) – Addon name.

  • +
  • addon_version (str) – Addon version.

  • +
  • purge (Optional[bool]) – Purge all data related to the addon.

  • +
+
+
+
+ +
+
+delete_bundle(bundle_name)[source]
+

Delete bundle from server.

+
+
Parameters:
+

bundle_name (str) – Name of bundle to delete.

+
+
+
+ +
+
+delete_dependency_package(filename, platform_name=None)[source]
+

Remove dependency package for specific platform.

+
+
Parameters:
+
    +
  • filename (str) – Filename of dependency package.

  • +
  • platform_name (Optional[str]) – Deprecated.

  • +
+
+
+
+ +
+
+delete_event(event_id: str)[source]
+

Delete event by id.

+

Supported since AYON server 1.6.0.

+
+
Parameters:
+

event_id (str) – Event id.

+
+
Returns:
+

Response from server.

+
+
Return type:
+

RestApiResponse

+
+
+
+ +
+
+delete_folder(project_name, folder_id, force=False)[source]
+

Delete folder.

+
+
Parameters:
+
    +
  • project_name (str) – Project name.

  • +
  • folder_id (str) – Folder id to delete.

  • +
  • force (Optional[bool]) – Folder delete folder with all children +folder, products, versions and representations.

  • +
+
+
+
+ +
+
+delete_installer(filename)[source]
+

Delete installer from server.

+
+
Parameters:
+

filename (str) – Installer filename.

+
+
+
+ +
+ +

Remove link by id.

+
+
Parameters:
+
    +
  • project_name (str) – Project where link exists.

  • +
  • link_id (str) – Id of link.

  • +
+
+
Raises:
+

HTTPRequestError – Server error happened.

+
+
+
+ +
+ +

Remove link type from project.

+
+
Parameters:
+
    +
  • project_name (str) – Project where link type is created.

  • +
  • link_type_name (str) – Name of link type.

  • +
  • input_type (str) – Input entity type of link.

  • +
  • output_type (str) – Output entity type of link.

  • +
+
+
Raises:
+

HTTPRequestError – Server error happened.

+
+
+
+ +
+
+delete_product(project_name, product_id)[source]
+

Delete product.

+
+
Parameters:
+
    +
  • project_name (str) – Project name.

  • +
  • product_id (str) – Product id to delete.

  • +
+
+
+
+ +
+
+delete_project(project_name)[source]
+

Delete project from server.

+

This will completely remove project from server without any step back.

+
+
Parameters:
+

project_name (str) – Project name that will be removed.

+
+
+
+ +
+
+delete_representation(project_name, representation_id)[source]
+

Delete representation.

+
+
Parameters:
+
    +
  • project_name (str) – Project name.

  • +
  • representation_id (str) – Representation id to delete.

  • +
+
+
+
+ +
+
+delete_secret(secret_name)[source]
+

Delete secret by name.

+
+
Parameters:
+

secret_name (str) – Name of secret to delete.

+
+
+
+ +
+
+delete_task(project_name, task_id)[source]
+

Delete task.

+
+
Parameters:
+
    +
  • project_name (str) – Project name.

  • +
  • task_id (str) – Task id to delete.

  • +
+
+
+
+ +
+
+delete_version(project_name, version_id)[source]
+

Delete version.

+
+
Parameters:
+
    +
  • project_name (str) – Project name.

  • +
  • version_id (str) – Version id to delete.

  • +
+
+
+
+ +
+
+dispatch_event(topic, sender=None, event_hash=None, project_name=None, username=None, depends_on=None, description=None, summary=None, payload=None, finished=True, store=True, dependencies=None)[source]
+

Dispatch event to server.

+
+
Parameters:
+
    +
  • topic (str) – Event topic used for filtering of listeners.

  • +
  • sender (Optional[str]) – Sender of event.

  • +
  • event_hash (Optional[str]) – Event hash.

  • +
  • project_name (Optional[str]) – Project name.

  • +
  • depends_on (Optional[str]) – Add dependency to another event.

  • +
  • username (Optional[str]) – Username which triggered event.

  • +
  • description (Optional[str]) – Description of event.

  • +
  • summary (Optional[dict[str, Any]]) – Summary of event that can +be used for simple filtering on listeners.

  • +
  • payload (Optional[dict[str, Any]]) – Full payload of event data with +all details.

  • +
  • finished (Optional[bool]) – Mark event as finished on dispatch.

  • +
  • store (Optional[bool]) – Store event in event queue for possible +future processing otherwise is event send only +to active listeners.

  • +
  • dependencies (Optional[list[str]]) – Deprecated. +List of event id dependencies.

  • +
+
+
Returns:
+

Response from server.

+
+
Return type:
+

RestApiResponse

+
+
+
+ +
+
+download_addon_private_file(addon_name, addon_version, filename, destination_dir, destination_filename=None, chunk_size=None, progress=None)[source]
+

Download a file from addon private files.

+

This method requires to have authorized token available. Private files +are not under ‘/api’ restpoint.

+
+
Parameters:
+
    +
  • addon_name (str) – Addon name.

  • +
  • addon_version (str) – Addon version.

  • +
  • filename (str) – Filename in private folder on server.

  • +
  • destination_dir (str) – Where the file should be downloaded.

  • +
  • destination_filename (Optional[str]) – Name of destination +filename. Source filename is used if not passed.

  • +
  • chunk_size (Optional[int]) – Download chunk size.

  • +
  • progress (Optional[TransferProgress]) – Object that gives ability +to track download progress.

  • +
+
+
Returns:
+

Filepath to downloaded file.

+
+
Return type:
+

str

+
+
+
+ +
+
+download_dependency_package(src_filename, dst_directory, dst_filename, platform_name=None, chunk_size=None, progress=None)[source]
+

Download dependency package from server.

+

This method requires to have authorized token available. The package +is only downloaded.

+
+
Parameters:
+
    +
  • src_filename (str) – Filename of dependency pacakge. +For server version 0.2.0 and lower it is name of package +to download.

  • +
  • dst_directory (str) – Where the file should be downloaded.

  • +
  • dst_filename (str) – Name of destination filename.

  • +
  • platform_name (Optional[str]) – Deprecated.

  • +
  • chunk_size (Optional[int]) – Download chunk size.

  • +
  • progress (Optional[TransferProgress]) – Object that gives ability +to track download progress.

  • +
+
+
Returns:
+

Filepath to downloaded file.

+
+
Return type:
+

str

+
+
+
+ +
+
+download_file(endpoint, filepath, chunk_size=None, progress=None)[source]
+

Download file from AYON server.

+

Endpoint can be full url (must start with ‘base_url’ of api object).

+

Progress object can be used to track download. Can be used when +download happens in thread and other thread want to catch changes over +time.

+
+
Todos:

Use retries and timeout. +Return RestApiResponse.

+
+
+
+
Parameters:
+
    +
  • endpoint (str) – Endpoint or URL to file that should be downloaded.

  • +
  • filepath (str) – Path where file will be downloaded.

  • +
  • chunk_size (Optional[int]) – Size of chunks that are received +in single loop.

  • +
  • progress (Optional[TransferProgress]) – Object that gives ability +to track download progress.

  • +
+
+
+
+ +
+
+download_file_to_stream(endpoint, stream, chunk_size=None, progress=None)[source]
+

Download file from AYON server to IOStream.

+

Endpoint can be full url (must start with ‘base_url’ of api object).

+

Progress object can be used to track download. Can be used when +download happens in thread and other thread want to catch changes over +time.

+
+
Todos:

Use retries and timeout. +Return RestApiResponse.

+
+
+
+
Parameters:
+
    +
  • endpoint (str) – Endpoint or URL to file that should be downloaded.

  • +
  • stream (Union[io.BytesIO, BinaryIO]) – Stream where output will +be stored.

  • +
  • chunk_size (Optional[int]) – Size of chunks that are received +in single loop.

  • +
  • progress (Optional[TransferProgress]) – Object that gives ability +to track download progress.

  • +
+
+
+
+ +
+
+download_installer(filename, dst_filepath, chunk_size=None, progress=None)[source]
+

Download installer file from server.

+
+
Parameters:
+
    +
  • filename (str) – Installer filename.

  • +
  • dst_filepath (str) – Destination filepath.

  • +
  • chunk_size (Optional[int]) – Download chunk size.

  • +
  • progress (Optional[TransferProgress]) – Object that gives ability +to track download progress.

  • +
+
+
+
+ +
+
+enroll_event_job(source_topic, target_topic, sender, description=None, sequential=None, events_filter=None, max_retries=None, ignore_older_than=None, ignore_sender_types=None)[source]
+

Enroll job based on events.

+

Enroll will find first unprocessed event with ‘source_topic’ and will +create new event with ‘target_topic’ for it and return the new event +data.

+

Use ‘sequential’ to control that only single target event is created +at same time. Creation of new target events is blocked while there is +at least one unfinished event with target topic, when set to ‘True’. +This helps when order of events matter and more than one process using +the same target is running at the same time.

+

Make sure the new event has updated status to ‘“finished”’ status +when you’re done with logic

+

Target topic should not clash with other processes/services.

+

Created target event have ‘dependsOn’ key where is id of source topic.

+
+
Use-case:
    +
  • Service 1 is creating events with topic ‘my.leech’

  • +
  • +
    Service 2 process ‘my.leech’ and uses target topic ‘my.process’
      +
    • this service can run on 1-n machines

    • +
    • +
      all events must be processed in a sequence by their creation

      time and only one event can be processed at a time

      +
      +
      +
    • +
    • +
      in this case ‘sequential’ should be set to ‘True’ so only

      one machine is actually processing events, but if one goes +down there are other that can take place

      +
      +
      +
    • +
    +
    +
    +
  • +
  • +
    Service 3 process ‘my.leech’ and uses target topic ‘my.discover’
      +
    • this service can run on 1-n machines

    • +
    • order of events is not important

    • +
    • ‘sequential’ should be ‘False’

    • +
    +
    +
    +
  • +
+
+
+
+
Parameters:
+
    +
  • source_topic (str) – Source topic to enroll.

  • +
  • target_topic (str) – Topic of dependent event.

  • +
  • sender (str) – Identifier of sender (e.g. service name or username).

  • +
  • description (Optional[str]) – Human readable text shown +in target event.

  • +
  • sequential (Optional[bool]) – The source topic must be processed +in sequence.

  • +
  • events_filter (Optional[dict[str, Any]]) – Filtering conditions +to filter the source event. For more technical specifications +look to server backed ‘ayon_server.sqlfilter.Filter’. +TODO: Add example of filters.

  • +
  • max_retries (Optional[int]) – How many times can be event retried. +Default value is based on server (3 at the time of this PR).

  • +
  • ignore_older_than (Optional[int]) – Ignore events older than +given number in days.

  • +
  • ignore_sender_types (Optional[List[str]]) – Ignore events triggered +by given sender types.

  • +
+
+
Returns:
+

+
None if there is no event matching

filters. Created event with ‘target_topic’.

+
+
+

+
+
Return type:
+

Union[None, dict[str, Any]]

+
+
+
+ +
+
+get(entrypoint, **kwargs)[source]
+
+ +
+
+get_activities(project_name: str, activity_ids: Optional[Iterable[str]] = None, activity_types: Optional[Iterable[ActivityType]] = None, entity_ids: Optional[Iterable[str]] = None, entity_names: Optional[Iterable[str]] = None, entity_type: Optional[str] = None, changed_after: Optional[str] = None, changed_before: Optional[str] = None, reference_types: Optional[Iterable[ActivityReferenceType]] = None, fields: Optional[Iterable[str]] = None, limit: Optional[int] = None, order: Optional[SortOrder] = None) Generator[Dict[str, Any], None, None][source]
+

Get activities from server with filtering options.

+
+
Parameters:
+
    +
  • project_name (str) – Project on which activities happened.

  • +
  • activity_ids (Optional[Iterable[str]]) – Activity ids.

  • +
  • activity_types (Optional[Iterable[ActivityType]]) – Activity types.

  • +
  • entity_ids (Optional[Iterable[str]]) – Entity ids.

  • +
  • entity_names (Optional[Iterable[str]]) – Entity names.

  • +
  • entity_type (Optional[str]) – Entity type.

  • +
  • changed_after (Optional[str]) – Return only activities changed +after given iso datetime string.

  • +
  • changed_before (Optional[str]) – Return only activities changed +before given iso datetime string.

  • +
  • reference_types (Optional[Iterable[ActivityReferenceType]]) – Reference types filter. Defaults to [‘origin’].

  • +
  • fields (Optional[Iterable[str]]) – Fields that should be received +for each activity.

  • +
  • limit (Optional[int]) – Limit number of activities to be fetched.

  • +
  • order (Optional[SortOrder]) – Order activities in ascending +or descending order. It is recommended to set ‘limit’ +when used descending.

  • +
+
+
Returns:
+

Available activities matching filters.

+
+
Return type:
+

Generator[dict[str, Any]]

+
+
+
+ +
+
+get_activity_by_id(project_name: str, activity_id: str, reference_types: Optional[Iterable[ActivityReferenceType]] = None, fields: Optional[Iterable[str]] = None) Optional[Dict[str, Any]][source]
+

Get activity by id.

+
+
Parameters:
+
    +
  • project_name (str) – Project on which activity happened.

  • +
  • activity_id (str) – Activity id.

  • +
  • fields (Optional[Iterable[str]]) – Fields that should be received +for each activity.

  • +
+
+
Returns:
+

+
Activity data or None if activity is not

found.

+
+
+

+
+
Return type:
+

Optional[Dict[str, Any]]

+
+
+
+ +
+
+get_addon_endpoint(addon_name, addon_version, *subpaths)[source]
+

Calculate endpoint to addon route.

+
+

Examples

+
>>> api = ServerAPI("https://your.url.com")
+>>> api.get_addon_url(
+...     "example", "1.0.0", "private", "my.zip")
+'addons/example/1.0.0/private/my.zip'
+
+
+
+
+
Parameters:
+
    +
  • addon_name (str) – Name of addon.

  • +
  • addon_version (str) – Version of addon.

  • +
  • *subpaths (str) – Any amount of subpaths that are added to +addon url.

  • +
+
+
Returns:
+

Final url.

+
+
Return type:
+

str

+
+
+
+ +
+
+get_addon_project_settings(addon_name, addon_version, project_name, variant=None, site_id=None, use_site=True)[source]
+

Addon project settings.

+

Receive project settings for specific version of an addon. The settings +may be with site overrides when enabled.

+

Site id is filled with current connection site id if not passed. To +make sure any site id is used set ‘use_site’ to ‘False’.

+
+
Parameters:
+
    +
  • addon_name (str) – Name of addon.

  • +
  • addon_version (str) – Version of addon.

  • +
  • project_name (str) – Name of project for which the settings are +received.

  • +
  • variant (Optional[Literal['production', 'staging']]) – Name of +settings variant. Used ‘default_settings_variant’ by default.

  • +
  • site_id (Optional[str]) – Name of site which is used for site +overrides. Is filled with connection ‘site_id’ attribute +if not passed.

  • +
  • use_site (Optional[bool]) – To force disable option of using site +overrides set to ‘False’. In that case won’t be applied +any site overrides.

  • +
+
+
Returns:
+

Addon settings.

+
+
Return type:
+

dict[str, Any]

+
+
+
+ +
+
+get_addon_settings(addon_name, addon_version, project_name=None, variant=None, site_id=None, use_site=True)[source]
+

Receive addon settings.

+

Receive addon settings based on project name value. Some arguments may +be ignored if ‘project_name’ is set to ‘None’.

+
+
Parameters:
+
    +
  • addon_name (str) – Name of addon.

  • +
  • addon_version (str) – Version of addon.

  • +
  • project_name (Optional[str]) – Name of project for which the +settings are received. A studio settings values are received +if is ‘None’.

  • +
  • variant (Optional[Literal['production', 'staging']]) – Name of +settings variant. Used ‘default_settings_variant’ by default.

  • +
  • site_id (Optional[str]) – Name of site which is used for site +overrides. Is filled with connection ‘site_id’ attribute +if not passed.

  • +
  • use_site (Optional[bool]) – To force disable option of using +site overrides set to ‘False’. In that case won’t be applied +any site overrides.

  • +
+
+
Returns:
+

Addon settings.

+
+
Return type:
+

dict[str, Any]

+
+
+
+ +
+
+get_addon_settings_schema(addon_name, addon_version, project_name=None)[source]
+

Sudio/Project settings schema of an addon.

+

Project schema may look differently as some enums are based on project +values.

+
+
Parameters:
+
    +
  • addon_name (str) – Name of addon.

  • +
  • addon_version (str) – Version of addon.

  • +
  • project_name (Optional[str]) – Schema for specific project or +default studio schemas.

  • +
+
+
Returns:
+

Schema of studio/project settings.

+
+
Return type:
+

dict[str, Any]

+
+
+
+ +
+
+get_addon_site_settings(addon_name, addon_version, site_id=None)[source]
+

Site settings of an addon.

+

If site id is not available an empty dictionary is returned.

+
+
Parameters:
+
    +
  • addon_name (str) – Name of addon.

  • +
  • addon_version (str) – Version of addon.

  • +
  • site_id (Optional[str]) – Name of site for which should be settings +returned. using ‘site_id’ attribute if not passed.

  • +
+
+
Returns:
+

Site settings.

+
+
Return type:
+

dict[str, Any]

+
+
+
+ +
+
+get_addon_site_settings_schema(addon_name, addon_version)[source]
+

Site settings schema of an addon.

+
+
Parameters:
+
    +
  • addon_name (str) – Name of addon.

  • +
  • addon_version (str) – Version of addon.

  • +
+
+
Returns:
+

Schema of site settings.

+
+
Return type:
+

dict[str, Any]

+
+
+
+ +
+
+get_addon_studio_settings(addon_name, addon_version, variant=None)[source]
+

Addon studio settings.

+

Receive studio settings for specific version of an addon.

+
+
Parameters:
+
    +
  • addon_name (str) – Name of addon.

  • +
  • addon_version (str) – Version of addon.

  • +
  • variant (Optional[Literal['production', 'staging']]) – Name of +settings variant. Used ‘default_settings_variant’ by default.

  • +
+
+
Returns:
+

Addon settings.

+
+
Return type:
+

dict[str, Any]

+
+
+
+ +
+
+get_addon_url(addon_name, addon_version, *subpaths, use_rest=True)[source]
+

Calculate url to addon route.

+
+

Examples

+
>>> api = ServerAPI("https://your.url.com")
+>>> api.get_addon_url(
+...     "example", "1.0.0", "private", "my.zip")
+'https://your.url.com/api/addons/example/1.0.0/private/my.zip'
+
+
+
+
+
Parameters:
+
    +
  • addon_name (str) – Name of addon.

  • +
  • addon_version (str) – Version of addon.

  • +
  • *subpaths (str) – Any amount of subpaths that are added to +addon url.

  • +
  • use_rest (Optional[bool]) – Use rest endpoint.

  • +
+
+
Returns:
+

Final url.

+
+
Return type:
+

str

+
+
+
+ +
+
+get_addons_info(details=True)[source]
+

Get information about addons available on server.

+
+
Parameters:
+

details (Optional[bool]) – Detailed data with information how +to get client code.

+
+
+
+ +
+
+get_addons_project_settings(project_name, bundle_name=None, variant=None, site_id=None, use_site=True, only_values=True)[source]
+

Project settings of all addons.

+

Server returns information about used addon versions, so full output +looks like:

+
+
```json
+
{

“settings”: {…}, +“addons”: {…}

+
+
+

}

+
+
+

```

+

The output can be limited to only values. To do so is ‘only_values’ +argument which is by default set to ‘True’. In that case output +contains only value of ‘settings’ key.

+
+

Warning

+
+
Behavior of this function changed with AYON server version 0.3.0.

Structure of output from server changed. If using +‘only_values=True’ then output should be same as before.

+
+
+
+
+
Parameters:
+
    +
  • project_name (str) – Name of project for which are settings +received.

  • +
  • bundle_name (Optional[str]) – Name of bundle for which should be +settings received.

  • +
  • variant (Optional[Literal['production', 'staging']]) – Name of +settings variant. Used ‘default_settings_variant’ by default.

  • +
  • site_id (Optional[str]) – Site id for which want to receive +site overrides.

  • +
  • use_site (bool) – To force disable option of using site overrides +set to ‘False’. In that case won’t be applied any site +overrides.

  • +
  • only_values (Optional[bool]) – Output will contain only settings +values without metadata about addons.

  • +
+
+
Returns:
+

+
Settings of all addons on server for passed

project.

+
+
+

+
+
Return type:
+

dict[str, Any]

+
+
+
+ +
+
+get_addons_settings(bundle_name=None, project_name=None, variant=None, site_id=None, use_site=True, only_values=True)[source]
+

Universal function to receive all addon settings.

+

Based on ‘project_name’ will receive studio settings or project +settings. In case project is not passed is ‘site_id’ ignored.

+
+

Warning

+
+
Behavior of this function changed with AYON server version 0.3.0.

Structure of output from server changed. If using +‘only_values=True’ then output should be same as before.

+
+
+
+
+
Parameters:
+
    +
  • bundle_name (Optional[str]) – Name of bundle for which should be +settings received.

  • +
  • project_name (Optional[str]) – Name of project for which should be +settings received.

  • +
  • variant (Optional[Literal['production', 'staging']]) – Name of +settings variant. Used ‘default_settings_variant’ by default.

  • +
  • site_id (Optional[str]) – Id of site for which want to receive +site overrides.

  • +
  • use_site (Optional[bool]) – To force disable option of using site +overrides set to ‘False’. In that case won’t be applied +any site overrides.

  • +
  • only_values (Optional[bool]) – Only settings values will be +returned. By default, is set to ‘True’.

  • +
+
+
+
+ +
+
+get_addons_studio_settings(bundle_name=None, variant=None, site_id=None, use_site=True, only_values=True)[source]
+

All addons settings in one bulk.

+
+

Warning

+
+
Behavior of this function changed with AYON server version 0.3.0.

Structure of output from server changed. If using +‘only_values=True’ then output should be same as before.

+
+
+
+
+
Parameters:
+
    +
  • bundle_name (Optional[str]) – Name of bundle for which should be +settings received.

  • +
  • variant (Optional[Literal['production', 'staging']]) – Name of +settings variant. Used ‘default_settings_variant’ by default.

  • +
  • site_id (Optional[str]) – Site id for which want to receive +site overrides.

  • +
  • use_site (bool) – To force disable option of using site overrides +set to ‘False’. In that case won’t be applied any site +overrides.

  • +
  • only_values (Optional[bool]) – Output will contain only settings +values without metadata about addons.

  • +
+
+
Returns:
+

Settings of all addons on server.

+
+
Return type:
+

dict[str, Any]

+
+
+
+ +
+
+get_attributes_fields_for_type(entity_type)[source]
+

Prepare attribute fields for entity type.

+
+
Returns:
+

Attributes fields for entity type.

+
+
Return type:
+

set[str]

+
+
+
+ +
+
+get_attributes_for_type(entity_type)[source]
+

Get attribute schemas available for an entity type.

+

Example:

+
```
+# Example attribute schema
+{
+    # Common
+    "type": "integer",
+    "title": "Clip Out",
+    "description": null,
+    "example": 1,
+    "default": 1,
+    # These can be filled based on value of 'type'
+    "gt": null,
+    "ge": null,
+    "lt": null,
+    "le": null,
+    "minLength": null,
+    "maxLength": null,
+    "minItems": null,
+    "maxItems": null,
+    "regex": null,
+    "enum": null
+}
+```
+
+
+
+
Parameters:
+

entity_type (str) – Entity type for which should be attributes +received.

+
+
Returns:
+

+
Attribute schemas that are available

for entered entity type.

+
+
+

+
+
Return type:
+

dict[str, dict[str, Any]]

+
+
+
+ +
+
+get_attributes_schema(use_cache=True)[source]
+
+ +
+
+get_base_url()[source]
+
+ +
+
+get_build_in_anatomy_preset()[source]
+

Get built-in anatomy preset.

+
+
Returns:
+

Built-in anatomy preset.

+
+
Return type:
+

dict[str, Any]

+
+
+
+ +
+
+get_bundle_settings(bundle_name=None, project_name=None, variant=None, site_id=None, use_site=True)[source]
+

Get complete set of settings for given data.

+

If project is not passed then studio settings are returned. If variant +is not passed ‘default_settings_variant’ is used. If bundle name is +not passed then current production/staging bundle is used, based on +variant value.

+

Output contains addon settings and site settings in single dictionary.

+
+
Todos:
    +
  • test how it behaves if there is not any bundle.

  • +
  • +
    test how it behaves if there is not any production/staging

    bundle.

    +
    +
    +
  • +
+
+
+

Example output:

+
{
+    "addons": [
+        {
+            "name": "addon-name",
+            "version": "addon-version",
+            "settings": {...},
+            "siteSettings": {...}
+        }
+    ]
+}
+
+
+
+
Returns:
+

All settings for single bundle.

+
+
Return type:
+

dict[str, Any]

+
+
+
+ +
+
+get_bundles()[source]
+

Server bundles with basic information.

+

This is example output:

+
{
+    "bundles": [
+        {
+            "name": "my_bundle",
+            "createdAt": "2023-06-12T15:37:02.420260",
+            "installerVersion": "1.0.0",
+            "addons": {
+                "core": "1.2.3"
+            },
+            "dependencyPackages": {
+                "windows": "a_windows_package123.zip",
+                "linux": "a_linux_package123.zip",
+                "darwin": "a_mac_package123.zip"
+            },
+            "isProduction": False,
+            "isStaging": False
+        }
+    ],
+    "productionBundle": "my_bundle",
+    "stagingBundle": "test_bundle"
+}
+
+
+
+
Returns:
+

Server bundles with basic information.

+
+
Return type:
+

dict[str, Any]

+
+
+
+ +
+
+get_cert()[source]
+

Current cert file used for connection to server.

+
+
Returns:
+

Path to cert file.

+
+
Return type:
+

Union[str, None]

+
+
+
+ +
+
+get_client_version()[source]
+

Version of client used to connect to server.

+

Client version is AYON client build desktop application.

+
+
Returns:
+

Client version string used in connection.

+
+
Return type:
+

str

+
+
+
+ +
+
+get_default_anatomy_preset_name()[source]
+

Name of default anatomy preset.

+

Primary preset is used as default preset. But when primary preset is +not set a built-in is used instead. Built-in preset is named ‘_’.

+
+
Returns:
+

+
Name of preset that can be used by

’get_project_anatomy_preset’.

+
+
+

+
+
Return type:
+

str

+
+
+
+ +
+
+get_default_fields_for_type(entity_type)[source]
+

Default fields for entity type.

+

Returns most of commonly used fields from server.

+
+
Parameters:
+

entity_type (str) – Name of entity type.

+
+
Returns:
+

Fields that should be queried from server.

+
+
Return type:
+

set[str]

+
+
+
+ +
+
+get_default_settings_variant()[source]
+

Default variant used for settings.

+
+
Returns:
+

name of variant or None.

+
+
Return type:
+

Union[str, None]

+
+
+
+ +
+
+get_dependency_packages()[source]
+

Information about dependency packages on server.

+

To download dependency package, use ‘download_dependency_package’ +method and pass in ‘filename’.

+

Example data structure:

+
{
+    "packages": [
+        {
+            "filename": str,
+            "platform": str,
+            "checksum": str,
+            "checksumAlgorithm": str,
+            "size": int,
+            "sources": list[dict[str, Any]],
+            "supportedAddons": dict[str, str],
+            "pythonModules": dict[str, str]
+        }
+    ]
+}
+
+
+
+
Returns:
+

+
Information about dependency packages known for

server.

+
+
+

+
+
Return type:
+

dict[str, Any]

+
+
+
+ +
+ +

Helper method to get links from server for entity types.

+
Example output:
+{
+    "59a212c0d2e211eda0e20242ac120001": [
+        {
+            "id": "59a212c0d2e211eda0e20242ac120002",
+            "linkType": "reference",
+            "description": "reference link between folders",
+            "projectName": "my_project",
+            "author": "frantadmin",
+            "entityId": "b1df109676db11ed8e8c6c9466b19aa8",
+            "entityType": "folder",
+            "direction": "out"
+        },
+        ...
+    ],
+    ...
+}
+
+
+
+
Parameters:
+
    +
  • project_name (str) – Project where links are.

  • +
  • (Literal["folder" (entity_type) –

  • +
  • "task"

  • +
  • "product"

  • +
+
+
+

:param : +:param | “version”: Entity type. +:param “representations”]): Entity type. +:param entity_ids: Ids of entities for which +:type entity_ids: Optional[Iterable[str]] +:param | links should be received.: +:param link_types: Link type filters. +:type link_types: Optional[Iterable[str]] +:param link_direction: Link direction +:type link_direction: Optional[Literal["in", "out"]] +:param | filter.: +:param link_names: Link name filters. +:type link_names: Optional[Iterable[str]] +:param link_name_regex: Regex filter for link name. +:type link_name_regex: Optional[str]

+
+
Returns:
+

Link info by entity ids.

+
+
Return type:
+

dict[str, list[dict[str, Any]]]

+
+
+
+ +
+
+get_event(event_id)[source]
+

Query full event data by id.

+

Events received using event server do not contain full information. To +get the full event information is required to receive it explicitly.

+
+
Parameters:
+

event_id (str) – Event id.

+
+
Returns:
+

Full event data.

+
+
Return type:
+

dict[str, Any]

+
+
+
+ +
+
+get_events(topics: Optional[Iterable[str]] = None, event_ids: Optional[Iterable[str]] = None, project_names: Optional[Iterable[str]] = None, statuses: Optional[Iterable[str]] = None, users: Optional[Iterable[str]] = None, include_logs: Optional[bool] = None, has_children: Optional[bool] = None, newer_than: Optional[str] = None, older_than: Optional[str] = None, fields: Optional[Iterable[str]] = None, limit: Optional[int] = None, order: Optional[SortOrder] = None, states: Optional[Iterable[str]] = None)[source]
+

Get events from server with filtering options.

+
+

Notes

+

Not all event happen on a project.

+
+
+
Parameters:
+
    +
  • topics (Optional[Iterable[str]]) – Name of topics.

  • +
  • event_ids (Optional[Iterable[str]]) – Event ids.

  • +
  • project_names (Optional[Iterable[str]]) – Project on which +event happened.

  • +
  • statuses (Optional[Iterable[str]]) – Filtering by statuses.

  • +
  • users (Optional[Iterable[str]]) – Filtering by users +who created/triggered an event.

  • +
  • include_logs (Optional[bool]) – Query also log events.

  • +
  • has_children (Optional[bool]) – Event is with/without children +events. If ‘None’ then all events are returned, default.

  • +
  • newer_than (Optional[str]) – Return only events newer than given +iso datetime string.

  • +
  • older_than (Optional[str]) – Return only events older than given +iso datetime string.

  • +
  • fields (Optional[Iterable[str]]) – Fields that should be received +for each event.

  • +
  • limit (Optional[int]) – Limit number of events to be fetched.

  • +
  • order (Optional[SortOrder]) – Order events in ascending +or descending order. It is recommended to set ‘limit’ +when used descending.

  • +
  • states (Optional[Iterable[str]]) – DEPRECATED Filtering by states. +Use ‘statuses’ instead.

  • +
+
+
Returns:
+

Available events matching filters.

+
+
Return type:
+

Generator[dict[str, Any]]

+
+
+
+ +
+
+get_folder_by_id(project_name, folder_id, fields=None, own_attributes=False)[source]
+

Query folder entity by id.

+
+
Parameters:
+
    +
  • project_name (str) – Name of project where to look for queried +entities.

  • +
  • folder_id (str) – Folder id.

  • +
  • fields (Optional[Iterable[str]]) – Fields that should be returned. +All fields are returned if ‘None’ is passed.

  • +
  • own_attributes (Optional[bool]) – Attribute values that are +not explicitly set on entity will have ‘None’ value.

  • +
+
+
Returns:
+

Folder entity data or None if was not found.

+
+
Return type:
+

Union[dict, None]

+
+
+
+ +
+
+get_folder_by_name(project_name, folder_name, fields=None, own_attributes=False)[source]
+

Query folder entity by path.

+
+

Warning

+
+
Folder name is not a unique identifier of a folder. Function is

kept for OpenPype 3 compatibility.

+
+
+
+
+
Parameters:
+
    +
  • project_name (str) – Name of project where to look for queried +entities.

  • +
  • folder_name (str) – Folder name.

  • +
  • fields (Optional[Iterable[str]]) – Fields that should be returned. +All fields are returned if ‘None’ is passed.

  • +
  • own_attributes (Optional[bool]) – Attribute values that are +not explicitly set on entity will have ‘None’ value.

  • +
+
+
Returns:
+

Folder entity data or None if was not found.

+
+
Return type:
+

Union[dict, None]

+
+
+
+ +
+
+get_folder_by_path(project_name, folder_path, fields=None, own_attributes=False)[source]
+

Query folder entity by path.

+

Folder path is a path to folder with all parent names joined by slash.

+
+
Parameters:
+
    +
  • project_name (str) – Name of project where to look for queried +entities.

  • +
  • folder_path (str) – Folder path.

  • +
  • fields (Optional[Iterable[str]]) – Fields that should be returned. +All fields are returned if ‘None’ is passed.

  • +
  • own_attributes (Optional[bool]) – Attribute values that are +not explicitly set on entity will have ‘None’ value.

  • +
+
+
Returns:
+

Folder entity data or None if was not found.

+
+
Return type:
+

Union[dict, None]

+
+
+
+ +
+
+get_folder_ids_with_products(project_name, folder_ids=None)[source]
+

Find folders which have at least one product.

+

Folders that have at least one product should be immutable, so they +should not change path -> change of name or name of any parent +is not possible.

+
+
Parameters:
+
    +
  • project_name (str) – Name of project.

  • +
  • folder_ids (Optional[Iterable[str]]) – Limit folder ids filtering +to a set of folders. If set to None all folders on project are +checked.

  • +
+
+
Returns:
+

Folder ids that have at least one product.

+
+
Return type:
+

set[str]

+
+
+
+ +
+ +

Query folder links from server.

+
+
Parameters:
+
    +
  • project_name (str) – Project where links are.

  • +
  • folder_id (str) – Folder id for which links should be received.

  • +
  • link_types (Optional[Iterable[str]]) – Link type filters.

  • +
  • link_direction (Optional[Literal["in", "out"]]) – Link direction +filter.

  • +
+
+
Returns:
+

Link info of folder.

+
+
Return type:
+

list[dict[str, Any]]

+
+
+
+ +
+
+get_folder_thumbnail(project_name, folder_id, thumbnail_id=None)[source]
+

Prepared method to receive thumbnail for folder entity.

+
+
Parameters:
+
    +
  • project_name (str) – Project under which the entity is located.

  • +
  • folder_id (str) – Folder id for which thumbnail should be returned.

  • +
  • thumbnail_id (Optional[str]) – Prepared thumbnail id from entity. +Used only to check if thumbnail was already cached.

  • +
+
+
Returns:
+

+
Path to downloaded thumbnail or none if entity

does not have any (or if user does not have permissions).

+
+
+

+
+
Return type:
+

Union[str, None]

+
+
+
+ +
+
+get_folders(project_name, folder_ids=None, folder_paths=None, folder_names=None, folder_types=None, parent_ids=None, folder_path_regex=None, has_products=None, has_tasks=None, has_children=None, statuses=None, assignees_all=None, tags=None, active=True, has_links=None, fields=None, own_attributes=False)[source]
+

Query folders from server.

+
+
Todos:
+
Folder name won’t be unique identifier, so we should add

folder path filtering.

+
+
+
+
+
+

Notes

+

Filter ‘active’ don’t have direct filter in GraphQl.

+
+
+
Parameters:
+
    +
  • project_name (str) – Name of project.

  • +
  • folder_ids (Optional[Iterable[str]]) – Folder ids to filter.

  • +
  • folder_paths (Optional[Iterable[str]]) – Folder paths used +for filtering.

  • +
  • folder_names (Optional[Iterable[str]]) – Folder names used +for filtering.

  • +
  • folder_types (Optional[Iterable[str]]) – Folder types used +for filtering.

  • +
  • parent_ids (Optional[Iterable[str]]) – Ids of folder parents. +Use ‘None’ if folder is direct child of project.

  • +
  • folder_path_regex (Optional[str]) – Folder path regex used +for filtering.

  • +
  • has_products (Optional[bool]) – Filter folders with/without +products. Ignored when None, default behavior.

  • +
  • has_tasks (Optional[bool]) – Filter folders with/without +tasks. Ignored when None, default behavior.

  • +
  • has_children (Optional[bool]) – Filter folders with/without +children. Ignored when None, default behavior.

  • +
  • statuses (Optional[Iterable[str]]) – Folder statuses used +for filtering.

  • +
  • assignees_all (Optional[Iterable[str]]) – Filter by assigness +on children tasks. Task must have all of passed assignees.

  • +
  • tags (Optional[Iterable[str]]) – Folder tags used +for filtering.

  • +
  • active (Optional[bool]) – Filter active/inactive folders. +Both are returned if is set to None.

  • +
  • has_links (Optional[Literal[IN, OUT, ANY]]) – Filter +representations with IN/OUT/ANY links.

  • +
  • fields (Optional[Iterable[str]]) – Fields to be queried for +folder. All possible folder fields are returned +if ‘None’ is passed.

  • +
  • own_attributes (Optional[bool]) – Attribute values that are +not explicitly set on entity will have ‘None’ value.

  • +
+
+
Returns:
+

Queried folder entities.

+
+
Return type:
+

Generator[dict[str, Any]]

+
+
+
+ +
+
+get_folders_hierarchy(project_name, search_string=None, folder_types=None)[source]
+

Get project hierarchy.

+

All folders in project in hierarchy data structure.

+
+
Example output:
+
{
+
“hierarchy”: [
+
{

“id”: “…”, +“name”: “…”, +“label”: “…”, +“status”: “…”, +“folderType”: “…”, +“hasTasks”: False, +“taskNames”: [], +“parents”: [], +“parentId”: None, +“children”: […children folders…]

+
+
+
+
+

]

+
+
+

}

+
+
+
+
Parameters:
+
    +
  • project_name (str) – Project where to look for folders.

  • +
  • search_string (Optional[str]) – Search string to filter folders.

  • +
  • folder_types (Optional[Iterable[str]]) – Folder types to filter.

  • +
+
+
Returns:
+

Response data from server.

+
+
Return type:
+

dict[str, Any]

+
+
+
+ +
+ +

Query folders links from server.

+
+
Parameters:
+
    +
  • project_name (str) – Project where links are.

  • +
  • folder_ids (Optional[Iterable[str]]) – Ids of folders for which +links should be received.

  • +
  • link_types (Optional[Iterable[str]]) – Link type filters.

  • +
  • link_direction (Optional[Literal["in", "out"]]) – Link direction +filter.

  • +
+
+
Returns:
+

Link info by folder ids.

+
+
Return type:
+

dict[str, list[dict[str, Any]]]

+
+
+
+ +
+
+get_folders_rest(project_name, include_attrib=False)[source]
+

Get simplified flat list of all project folders.

+
+
Get all project folders in single REST call. This can be faster than

using ‘get_folders’ method which is using GraphQl, but does not +allow any filtering, and set of fields is defined +by server backend.

+
+
+

Example:

+
[
+    {
+        "id": "112233445566",
+        "parentId": "112233445567",
+        "path": "/root/parent/child",
+        "parents": ["root", "parent"],
+        "name": "child",
+        "label": "Child",
+        "folderType": "Folder",
+        "hasTasks": False,
+        "hasChildren": False,
+        "taskNames": [
+            "Compositing",
+        ],
+        "status": "In Progress",
+        "attrib": {},
+        "ownAttrib": [],
+        "updatedAt": "2023-06-12T15:37:02.420260",
+    },
+    ...
+]
+
+
+
+
Deprecated:
+
Use ‘get_rest_folders’ instead. Function was renamed to match

other rest functions, like ‘get_rest_folder’, +‘get_rest_project’ etc. .

+
+
+

Will be removed in ‘1.0.7’ or ‘1.1.0’.

+
+
+
+
Parameters:
+
    +
  • project_name (str) – Project name.

  • +
  • include_attrib (Optional[bool]) – Include attribute values +in output. Slower to query.

  • +
+
+
Returns:
+

List of folder entities.

+
+
Return type:
+

list[dict[str, Any]]

+
+
+
+ +
+ +

Calculate full link type name used for query from server.

+
+
Parameters:
+
    +
  • link_type_name (str) – Type of link.

  • +
  • input_type (str) – Input entity type of link.

  • +
  • output_type (str) – Output entity type of link.

  • +
+
+
Returns:
+

Full name of link type used for query from server.

+
+
Return type:
+

str

+
+
+
+ +
+
+get_graphql_schema()[source]
+
+ +
+
+get_hero_version_by_id(project_name, version_id, fields=None, own_attributes=<object object>)[source]
+

Query hero version entity by id.

+
+
Parameters:
+
    +
  • project_name (str) – Name of project where to look for queried +entities.

  • +
  • version_id (int) – Hero version id.

  • +
  • fields (Optional[Iterable[str]]) – Fields that should be returned. +All fields are returned if ‘None’ is passed.

  • +
  • own_attributes (Optional[bool]) – DEPRECATED: Not supported for +versions.

  • +
+
+
Returns:
+

Version entity data or None if was not found.

+
+
Return type:
+

Union[dict, None]

+
+
+
+ +
+
+get_hero_version_by_product_id(project_name, product_id, fields=None, own_attributes=<object object>)[source]
+

Query hero version entity by product id.

+

Only one hero version is available on a product.

+
+
Parameters:
+
    +
  • project_name (str) – Name of project where to look for queried +entities.

  • +
  • product_id (int) – Product id.

  • +
  • fields (Optional[Iterable[str]]) – Fields that should be returned. +All fields are returned if ‘None’ is passed.

  • +
  • own_attributes (Optional[bool]) – DEPRECATED: Not supported for +versions.

  • +
+
+
Returns:
+

Version entity data or None if was not found.

+
+
Return type:
+

Union[dict, None]

+
+
+
+ +
+
+get_hero_versions(project_name, product_ids=None, version_ids=None, active=True, fields=None, own_attributes=<object object>)[source]
+

Query hero versions by multiple filters.

+

Only one hero version is available on a product.

+
+
Parameters:
+
    +
  • project_name (str) – Name of project where to look for queried +entities.

  • +
  • product_ids (Optional[Iterable[str]]) – Product ids.

  • +
  • version_ids (Optional[Iterable[str]]) – Version ids.

  • +
  • active (Optional[bool]) – Receive active/inactive entities. +Both are returned when ‘None’ is passed.

  • +
  • fields (Optional[Iterable[str]]) – Fields that should be returned. +All fields are returned if ‘None’ is passed.

  • +
  • own_attributes (Optional[bool]) – DEPRECATED: Not supported for +versions.

  • +
+
+
Returns:
+

Version entity data or None if was not found.

+
+
Return type:
+

Union[dict, None]

+
+
+
+ +
+
+get_info()[source]
+

Get information about current used api key.

+

By default, the ‘info’ contains only ‘uptime’ and ‘version’. With +logged user info also contains information about user and machines on +which was logged in.

+
+
Todos:

Use this method for validation of token instead of ‘get_user’.

+
+
+
+
Returns:
+

Information from server.

+
+
Return type:
+

dict[str, Any]

+
+
+
+ +
+
+get_installers(version=None, platform_name=None)[source]
+

Information about desktop application installers on server.

+

Desktop application installers are helpers to download/update AYON +desktop application for artists.

+
+
Parameters:
+
    +
  • version (Optional[str]) – Filter installers by version.

  • +
  • platform_name (Optional[str]) – Filter installers by platform name.

  • +
+
+
Return type:
+

list[dict[str, Any]]

+
+
+
+ +
+
+get_last_version_by_product_id(project_name, product_id, active=True, fields=None, own_attributes=<object object>)[source]
+

Query last version entity by product id.

+
+
Parameters:
+
    +
  • project_name (str) – Project where to look for representation.

  • +
  • product_id (str) – Product id.

  • +
  • active (Optional[bool]) – Receive active/inactive entities. +Both are returned when ‘None’ is passed.

  • +
  • fields (Optional[Iterable[str]]) – fields to be queried +for representations.

  • +
  • own_attributes (Optional[bool]) – DEPRECATED: Not supported for +versions.

  • +
+
+
Returns:
+

Queried version entity or None.

+
+
Return type:
+

Union[dict[str, Any], None]

+
+
+
+ +
+
+get_last_version_by_product_name(project_name, product_name, folder_id, active=True, fields=None, own_attributes=<object object>)[source]
+

Query last version entity by product name and folder id.

+
+
Parameters:
+
    +
  • project_name (str) – Project where to look for representation.

  • +
  • product_name (str) – Product name.

  • +
  • folder_id (str) – Folder id.

  • +
  • active (Optional[bool]) – Receive active/inactive entities. +Both are returned when ‘None’ is passed.

  • +
  • fields (Optional[Iterable[str]]) – fields to be queried +for representations.

  • +
  • own_attributes (Optional[bool]) – DEPRECATED: Not supported for +representations.

  • +
+
+
Returns:
+

Queried version entity or None.

+
+
Return type:
+

Union[dict[str, Any], None]

+
+
+
+ +
+
+get_last_versions(project_name, product_ids, active=True, fields=None, own_attributes=<object object>)[source]
+

Query last version entities by product ids.

+
+
Parameters:
+
    +
  • project_name (str) – Project where to look for representation.

  • +
  • product_ids (Iterable[str]) – Product ids.

  • +
  • active (Optional[bool]) – Receive active/inactive entities. +Both are returned when ‘None’ is passed.

  • +
  • fields (Optional[Iterable[str]]) – fields to be queried +for representations.

  • +
  • own_attributes (Optional[bool]) – DEPRECATED: Not supported for +versions.

  • +
+
+
Returns:
+

Last versions by product id.

+
+
Return type:
+

dict[str, dict[str, Any]]

+
+
+
+ +
+ +

Get link type data.

+

There is not dedicated REST endpoint to get single link type, +so method ‘get_link_types’ is used.

+
+
Example output:
+
{

“name”: “reference|folder|folder”, +“link_type”: “reference”, +“input_type”: “folder”, +“output_type”: “folder”, +“data”: {}

+
+
+

}

+
+
+
+
Parameters:
+
    +
  • project_name (str) – Project where link type is available.

  • +
  • link_type_name (str) – Name of link type.

  • +
  • input_type (str) – Input entity type of link.

  • +
  • output_type (str) – Output entity type of link.

  • +
+
+
Returns:
+

Link type information.

+
+
Return type:
+

Union[None, dict[str, Any]]

+
+
+
+ +
+ +

All link types available on a project.

+
+
Example output:
+
[
+
{

“name”: “reference|folder|folder”, +“link_type”: “reference”, +“input_type”: “folder”, +“output_type”: “folder”, +“data”: {}

+
+
+

}

+
+
+

]

+
+
+
+
Parameters:
+

project_name (str) – Name of project where to look for link types.

+
+
Returns:
+

Link types available on project.

+
+
Return type:
+

list[dict[str, Any]]

+
+
+
+ +
+
+get_max_retries()[source]
+

Current value for requests max retries.

+
+
Returns:
+

Max retries value.

+
+
Return type:
+

int

+
+
+
+ +
+
+get_product_by_id(project_name, product_id, fields=None, own_attributes=<object object>)[source]
+

Query product entity by id.

+
+
Parameters:
+
    +
  • project_name (str) – Name of project where to look for queried +entities.

  • +
  • product_id (str) – Product id.

  • +
  • fields (Optional[Iterable[str]]) – Fields that should be returned. +All fields are returned if ‘None’ is passed.

  • +
  • own_attributes (Optional[bool]) – DEPRECATED: Not supported for +products.

  • +
+
+
Returns:
+

Product entity data or None if was not found.

+
+
Return type:
+

Union[dict, None]

+
+
+
+ +
+
+get_product_by_name(project_name, product_name, folder_id, fields=None, own_attributes=<object object>)[source]
+

Query product entity by name and folder id.

+
+
Parameters:
+
    +
  • project_name (str) – Name of project where to look for queried +entities.

  • +
  • product_name (str) – Product name.

  • +
  • folder_id (str) – Folder id (Folder is a parent of products).

  • +
  • fields (Optional[Iterable[str]]) – Fields that should be returned. +All fields are returned if ‘None’ is passed.

  • +
  • own_attributes (Optional[bool]) – DEPRECATED: Not supported for +products.

  • +
+
+
Returns:
+

Product entity data or None if was not found.

+
+
Return type:
+

Union[dict, None]

+
+
+
+ +
+ +

Query product links from server.

+
+
Parameters:
+
    +
  • project_name (str) – Project where links are.

  • +
  • product_id (str) – Product id for which links should be received.

  • +
  • link_types (Optional[Iterable[str]]) – Link type filters.

  • +
  • link_direction (Optional[Literal["in", "out"]]) – Link direction +filter.

  • +
+
+
Returns:
+

Link info of product.

+
+
Return type:
+

list[dict[str, Any]]

+
+
+
+ +
+
+get_product_type_names(project_name=None, product_ids=None)[source]
+

Product type names.

+
+

Warning

+
+
This function will be probably removed. Matters if ‘products_id’

filter has real use-case.

+
+
+
+
+
Parameters:
+
    +
  • project_name (Optional[str]) – Name of project where to look for +queried entities.

  • +
  • product_ids (Optional[Iterable[str]]) – Product ids filter. Can be +used only with ‘project_name’.

  • +
+
+
Returns:
+

Product type names.

+
+
Return type:
+

set[str]

+
+
+
+ +
+
+get_product_types(fields=None)[source]
+

Types of products.

+
+
This is server wide information. Product types have ‘name’, ‘icon’ and

‘color’.

+
+
+
+
Parameters:
+

fields (Optional[Iterable[str]]) – Product types fields to query.

+
+
Returns:
+

Product types information.

+
+
Return type:
+

list[dict[str, Any]]

+
+
+
+ +
+
+get_products(project_name, product_ids=None, product_names=None, folder_ids=None, product_types=None, product_name_regex=None, product_path_regex=None, names_by_folder_ids=None, statuses=None, tags=None, active=True, fields=None, own_attributes=<object object>)[source]
+

Query products from server.

+
+
Todos:
+
Separate ‘name_by_folder_ids’ filtering to separated method. It

cannot be combined with some other filters.

+
+
+
+
+
+
Parameters:
+
    +
  • project_name (str) – Name of project.

  • +
  • product_ids (Optional[Iterable[str]]) – Task ids to filter.

  • +
  • product_names (Optional[Iterable[str]]) – Task names used for +filtering.

  • +
  • folder_ids (Optional[Iterable[str]]) – Ids of task parents. +Use ‘None’ if folder is direct child of project.

  • +
  • product_types (Optional[Iterable[str]]) – Product types used for +filtering.

  • +
  • product_name_regex (Optional[str]) – Filter products by name regex.

  • +
  • product_path_regex (Optional[str]) – Filter products by path regex. +Path starts with folder path and ends with product name.

  • +
  • names_by_folder_ids (Optional[dict[str, Iterable[str]]]) – Product +name filtering by folder id.

  • +
  • statuses (Optional[Iterable[str]]) – Product statuses used +for filtering.

  • +
  • tags (Optional[Iterable[str]]) – Product tags used +for filtering.

  • +
  • active (Optional[bool]) – Filter active/inactive products. +Both are returned if is set to None.

  • +
  • fields (Optional[Iterable[str]]) – Fields to be queried for +folder. All possible folder fields are returned +if ‘None’ is passed.

  • +
  • own_attributes (Optional[bool]) – DEPRECATED: Not supported for +products.

  • +
+
+
Returns:
+

Queried product entities.

+
+
Return type:
+

Generator[dict[str, Any]]

+
+
+
+ +
+ +

Query products links from server.

+
+
Parameters:
+
    +
  • project_name (str) – Project where links are.

  • +
  • product_ids (Optional[Iterable[str]]) – Ids of products for which +links should be received.

  • +
  • link_types (Optional[Iterable[str]]) – Link type filters.

  • +
  • link_direction (Optional[Literal["in", "out"]]) – Link direction +filter.

  • +
+
+
Returns:
+

Link info by product ids.

+
+
Return type:
+

dict[str, list[dict[str, Any]]]

+
+
+
+ +
+
+get_project(project_name, fields=None, own_attributes=False)[source]
+

Get project.

+
+
Parameters:
+
    +
  • project_name (str) – Name of project.

  • +
  • fields (Optional[Iterable[str]]) – fields to be queried +for project.

  • +
  • own_attributes (Optional[bool]) – Attribute values that are +not explicitly set on entity will have ‘None’ value.

  • +
+
+
Returns:
+

+
Project entity data or None

if project was not found.

+
+
+

+
+
Return type:
+

Union[dict[str, Any], None]

+
+
+
+ +
+
+get_project_anatomy_preset(preset_name=None)[source]
+

Anatomy preset values by name.

+

Get anatomy preset values by preset name. Primary preset is returned +if preset name is set to ‘None’.

+
+
Parameters:
+

preset_name (Optional[str]) – Preset name.

+
+
Returns:
+

Anatomy preset values.

+
+
Return type:
+

dict[str, Any]

+
+
+
+ +
+
+get_project_anatomy_presets()[source]
+

Anatomy presets available on server.

+

Content has basic information about presets. Example output:

+
[
+    {
+        "name": "netflix_VFX",
+        "primary": false,
+        "version": "1.0.0"
+    },
+    {
+        ...
+    },
+    ...
+]
+
+
+
+
Returns:
+

Anatomy presets available on server.

+
+
Return type:
+

list[dict[str, str]]

+
+
+
+ +
+
+get_project_names(active=True, library=None)[source]
+

Receive available project names.

+

User must be logged in.

+
+
Parameters:
+
    +
  • active (Optional[bool]) – Filter active/inactive projects. Both +are returned if ‘None’ is passed.

  • +
  • library (Optional[bool]) – Filter standard/library projects. Both +are returned if ‘None’ is passed.

  • +
+
+
Returns:
+

List of available project names.

+
+
Return type:
+

list[str]

+
+
+
+ +
+
+get_project_product_types(project_name, fields=None)[source]
+

Types of products available on a project.

+

Filter only product types available on project.

+
+
Parameters:
+
    +
  • project_name (str) – Name of project where to look for +product types.

  • +
  • fields (Optional[Iterable[str]]) – Product types fields to query.

  • +
+
+
Returns:
+

Product types information.

+
+
Return type:
+

list[dict[str, Any]]

+
+
+
+ +
+
+get_project_root_overrides(project_name)[source]
+

Root overrides per site name.

+
+
Method is based on logged user and can’t be received for any other

user on server.

+
+
+

Output will contain only roots per site id used by logged user.

+
+
Parameters:
+

project_name (str) – Name of project.

+
+
Returns:
+

Root values by root name by site id.

+
+
Return type:
+

dict[str, dict[str, str]]

+
+
+
+ +
+
+get_project_root_overrides_by_site_id(project_name, site_id=None)[source]
+

Root overrides for site.

+

If site id is not passed a site set in current api object is used +instead.

+
+
Parameters:
+
    +
  • project_name (str) – Name of project.

  • +
  • site_id (Optional[str]) – Site id for which want to receive +site overrides.

  • +
+
+
Returns:
+

+
Root values by root name or None if

site does not have overrides.

+
+
+

+
+
Return type:
+

dict[str, str]

+
+
+
+ +
+
+get_project_roots_by_platform(project_name, platform_name=None)[source]
+

Root values for a site.

+

If platform name is not passed current platform name is used instead.

+
+
This function does return root values without site overrides. It is

possible to use the function to receive default root values.

+
+
+
+
Parameters:
+
    +
  • project_name (str) – Name of project.

  • +
  • platform_name (Optional[Literal["windows", "linux", "darwin"]]) – Platform name for which want to receive root values. Current +platform name is used if not passed.

  • +
+
+
Returns:
+

Root values.

+
+
Return type:
+

dict[str, str]

+
+
+
+ +
+
+get_project_roots_by_site(project_name)[source]
+

Root overrides per site name.

+

Method is based on logged user and can’t be received for any other +user on server.

+

Output will contain only roots per site id used by logged user.

+
+
Deprecated:
+
Use ‘get_project_root_overrides’ instead. Function

deprecated since 1.0.6

+
+
+
+
+
+
Parameters:
+

project_name (str) – Name of project.

+
+
Returns:
+

Root values by root name by site id.

+
+
Return type:
+

dict[str, dict[str, str]]

+
+
+
+ +
+
+get_project_roots_by_site_id(project_name, site_id=None)[source]
+

Root values for a site.

+

If site id is not passed a site set in current api object is used +instead. If site id is not available, default roots are returned +for current platform.

+
+
Parameters:
+
    +
  • project_name (str) – Name of project.

  • +
  • site_id (Optional[str]) – Site id for which want to receive +root values.

  • +
+
+
Returns:
+

Root values.

+
+
Return type:
+

dict[str, str]

+
+
+
+ +
+
+get_project_roots_for_site(project_name, site_id=None)[source]
+

Root overrides for site.

+

If site id is not passed a site set in current api object is used +instead.

+
+
Deprecated:
+
Use ‘get_project_root_overrides_by_site_id’ instead. Function

deprecated since 1.0.6

+
+
+
+
+
+
Parameters:
+
    +
  • project_name (str) – Name of project.

  • +
  • site_id (Optional[str]) – Site id for which want to receive +site overrides.

  • +
+
+
Returns:
+

+
Root values by root name, root name is not

available if it does not have overrides.

+
+
+

+
+
Return type:
+

dict[str, str]

+
+
+
+ +
+
+get_projects(active=True, library=None, fields=None, own_attributes=False)[source]
+

Get projects.

+
+
Parameters:
+
    +
  • active (Optional[bool]) – Filter active or inactive projects. +Filter is disabled when ‘None’ is passed.

  • +
  • library (Optional[bool]) – Filter library projects. Filter is +disabled when ‘None’ is passed.

  • +
  • fields (Optional[Iterable[str]]) – fields to be queried +for project.

  • +
  • own_attributes (Optional[bool]) – Attribute values that are +not explicitly set on entity will have ‘None’ value.

  • +
+
+
Returns:
+

Queried projects.

+
+
Return type:
+

Generator[dict[str, Any]]

+
+
+
+ +
+
+get_repre_ids_by_context_filters(project_name, context_filters, representation_names=None, version_ids=None)[source]
+

Find representation ids which match passed context filters.

+

Each representation has context integrated on representation entity in +database. The context may contain project, folder, task name or +product name, product type and many more. This implementation gives +option to quickly filter representation based on representation data +in database.

+
+
Context filters have defined structure. To define filter of nested

subfield use dot ‘.’ as delimiter (For example ‘task.name’).

+
+
Filter values can be regex filters. String or re.Pattern can

be used.

+
+
+
+
Parameters:
+
    +
  • project_name (str) – Project where to look for representations.

  • +
  • context_filters (dict[str, list[str]]) – Filters of context fields.

  • +
  • representation_names (Optional[Iterable[str]]) – Representation +names, can be used as additional filter for representations +by their names.

  • +
  • version_ids (Optional[Iterable[str]]) – Version ids, can be used +as additional filter for representations by their parent ids.

  • +
+
+
Returns:
+

Representation ids that match passed filters.

+
+
Return type:
+

list[str]

+
+
+
+

Example

+
+
The function returns just representation ids so if entities are

required for funtionality they must be queried afterwards by +their ids.

+
+
+
>>> project_name = "testProject"
+>>> filters = {
+...     "task.name": ["[aA]nimation"],
+...     "product": [".*[Mm]ain"]
+... }
+>>> repre_ids = get_repre_ids_by_context_filters(
+...     project_name, filters)
+>>> repres = get_representations(project_name, repre_ids)
+
+
+
+
+ +
+
+get_representation_by_id(project_name, representation_id, fields=None, own_attributes=<object object>)[source]
+

Query representation entity from server based on id filter.

+
+
Parameters:
+
    +
  • project_name (str) – Project where to look for representation.

  • +
  • representation_id (str) – Id of representation.

  • +
  • fields (Optional[Iterable[str]]) – fields to be queried +for representations.

  • +
  • own_attributes (Optional[bool]) – DEPRECATED: Not supported for +representations.

  • +
+
+
Returns:
+

Queried representation entity or None.

+
+
Return type:
+

Union[dict[str, Any], None]

+
+
+
+ +
+
+get_representation_by_name(project_name, representation_name, version_id, fields=None, own_attributes=<object object>)[source]
+

Query representation entity by name and version id.

+
+
Parameters:
+
    +
  • project_name (str) – Project where to look for representation.

  • +
  • representation_name (str) – Representation name.

  • +
  • version_id (str) – Version id.

  • +
  • fields (Optional[Iterable[str]]) – fields to be queried +for representations.

  • +
  • own_attributes (Optional[bool]) – DEPRECATED: Not supported for +representations.

  • +
+
+
Returns:
+

Queried representation entity or None.

+
+
Return type:
+

Union[dict[str, Any], None]

+
+
+
+ +
+
+get_representation_hierarchy(project_name, representation_id, project_fields=None, folder_fields=None, task_fields=None, product_fields=None, version_fields=None, representation_fields=None)[source]
+

Find representation parents by representation id.

+

Representation parent entities up to project.

+
+
Parameters:
+
    +
  • project_name (str) – Project where to look for entities.

  • +
  • representation_id (str) – Representation id.

  • +
  • project_fields (Optional[Iterable[str]]) – Project fields.

  • +
  • folder_fields (Optional[Iterable[str]]) – Folder fields.

  • +
  • task_fields (Optional[Iterable[str]]) – Task fields.

  • +
  • product_fields (Optional[Iterable[str]]) – Product fields.

  • +
  • version_fields (Optional[Iterable[str]]) – Version fields.

  • +
  • representation_fields (Optional[Iterable[str]]) – Representation +fields.

  • +
+
+
Returns:
+

Representation hierarchy entities.

+
+
Return type:
+

RepresentationHierarchy

+
+
+
+ +
+ +

Query representation links from server.

+
+
Parameters:
+
    +
  • project_name (str) – Project where links are.

  • +
  • representation_id (str) – Representation id for which links +should be received.

  • +
  • link_types (Optional[Iterable[str]]) – Link type filters.

  • +
  • link_direction (Optional[Literal["in", "out"]]) – Link direction +filter.

  • +
+
+
Returns:
+

Link info of representation.

+
+
Return type:
+

list[dict[str, Any]]

+
+
+
+ +
+
+get_representation_parents(project_name, representation_id, project_fields=None, folder_fields=None, product_fields=None, version_fields=None)[source]
+

Find representation parents by representation id.

+

Representation parent entities up to project.

+
+
Parameters:
+
    +
  • project_name (str) – Project where to look for entities.

  • +
  • representation_id (str) – Representation id.

  • +
  • project_fields (Optional[Iterable[str]]) – Project fields.

  • +
  • folder_fields (Optional[Iterable[str]]) – Folder fields.

  • +
  • product_fields (Optional[Iterable[str]]) – Product fields.

  • +
  • version_fields (Optional[Iterable[str]]) – Version fields.

  • +
+
+
Returns:
+

Representation parent entities.

+
+
Return type:
+

RepresentationParents

+
+
+
+ +
+
+get_representations(project_name, representation_ids=None, representation_names=None, version_ids=None, names_by_version_ids=None, statuses=None, tags=None, active=True, has_links=None, fields=None, own_attributes=<object object>)[source]
+

Get representation entities based on passed filters from server.

+
+
Parameters:
+
    +
  • project_name (str) – Name of project where to look for versions.

  • +
  • representation_ids (Optional[Iterable[str]]) – Representation ids +used for representation filtering.

  • +
  • representation_names (Optional[Iterable[str]]) – Representation +names used for representation filtering.

  • +
  • version_ids (Optional[Iterable[str]]) – Version ids used for +representation filtering. Versions are parents of +representations.

  • +
  • names_by_version_ids (Optional[Dict[str, Iterable[str]]) – Find +representations by names and version ids. This filter +discards all other filters.

  • +
  • statuses (Optional[Iterable[str]]) – Representation statuses used +for filtering.

  • +
  • tags (Optional[Iterable[str]]) – Representation tags used +for filtering.

  • +
  • active (Optional[bool]) – Receive active/inactive entities. +Both are returned when ‘None’ is passed.

  • +
  • has_links (Optional[Literal[IN, OUT, ANY]]) – Filter +representations with IN/OUT/ANY links.

  • +
  • fields (Optional[Iterable[str]]) – Fields to be queried for +representation. All possible fields are returned if ‘None’ is +passed.

  • +
  • own_attributes (Optional[bool]) – DEPRECATED: Not supported for +representations.

  • +
+
+
Returns:
+

Queried representation entities.

+
+
Return type:
+

Generator[dict[str, Any]]

+
+
+
+ +
+
+get_representations_hierarchy(project_name, representation_ids, project_fields=None, folder_fields=None, task_fields=None, product_fields=None, version_fields=None, representation_fields=None)[source]
+

Find representation with parents by representation id.

+

Representation entity with parent entities up to project.

+
+
Default fields are used when any fields are set to None. But it is

possible to pass in empty iterable (list, set, tuple) to skip +entity.

+
+
+
+
Parameters:
+
    +
  • project_name (str) – Project where to look for entities.

  • +
  • representation_ids (Iterable[str]) – Representation ids.

  • +
  • project_fields (Optional[Iterable[str]]) – Project fields.

  • +
  • folder_fields (Optional[Iterable[str]]) – Folder fields.

  • +
  • task_fields (Optional[Iterable[str]]) – Task fields.

  • +
  • product_fields (Optional[Iterable[str]]) – Product fields.

  • +
  • version_fields (Optional[Iterable[str]]) – Version fields.

  • +
  • representation_fields (Optional[Iterable[str]]) – Representation +fields.

  • +
+
+
Returns:
+

+
Parent entities by

representation id.

+
+
+

+
+
Return type:
+

dict[str, RepresentationHierarchy]

+
+
+
+ +
+ +

Query representations links from server.

+
+
Parameters:
+
    +
  • project_name (str) – Project where links are.

  • +
  • representation_ids (Optional[Iterable[str]]) – Ids of +representations for which links should be received.

  • +
  • link_types (Optional[Iterable[str]]) – Link type filters.

  • +
  • link_direction (Optional[Literal["in", "out"]]) – Link direction +filter.

  • +
+
+
Returns:
+

Link info by representation ids.

+
+
Return type:
+

dict[str, list[dict[str, Any]]]

+
+
+
+ +
+
+get_representations_parents(project_name, representation_ids, project_fields=None, folder_fields=None, product_fields=None, version_fields=None)[source]
+

Find representations parents by representation id.

+

Representation parent entities up to project.

+
+
Parameters:
+
    +
  • project_name (str) – Project where to look for entities.

  • +
  • representation_ids (Iterable[str]) – Representation ids.

  • +
  • project_fields (Optional[Iterable[str]]) – Project fields.

  • +
  • folder_fields (Optional[Iterable[str]]) – Folder fields.

  • +
  • product_fields (Optional[Iterable[str]]) – Product fields.

  • +
  • version_fields (Optional[Iterable[str]]) – Version fields.

  • +
+
+
Returns:
+

+
Parent entities by

representation id.

+
+
+

+
+
Return type:
+

dict[str, RepresentationParents]

+
+
+
+ +
+
+get_rest_entity_by_id(project_name, entity_type, entity_id)[source]
+

Get entity using REST on a project by its id.

+
+
Parameters:
+
    +
  • project_name (str) – Name of project where entity is.

  • +
  • entity_type (Literal["folder", "task", "product", "version"]) – The +entity type which should be received.

  • +
  • entity_id (str) – Id of entity.

  • +
+
+
Returns:
+

Received entity data.

+
+
Return type:
+

dict[str, Any]

+
+
+
+ +
+
+get_rest_folder(project_name, folder_id)[source]
+
+ +
+
+get_rest_folders(project_name, include_attrib=False)[source]
+

Get simplified flat list of all project folders.

+
+
Get all project folders in single REST call. This can be faster than

using ‘get_folders’ method which is using GraphQl, but does not +allow any filtering, and set of fields is defined +by server backend.

+
+
+

Example:

+
[
+    {
+        "id": "112233445566",
+        "parentId": "112233445567",
+        "path": "/root/parent/child",
+        "parents": ["root", "parent"],
+        "name": "child",
+        "label": "Child",
+        "folderType": "Folder",
+        "hasTasks": False,
+        "hasChildren": False,
+        "taskNames": [
+            "Compositing",
+        ],
+        "status": "In Progress",
+        "attrib": {},
+        "ownAttrib": [],
+        "updatedAt": "2023-06-12T15:37:02.420260",
+    },
+    ...
+]
+
+
+
+
Parameters:
+
    +
  • project_name (str) – Project name.

  • +
  • include_attrib (Optional[bool]) – Include attribute values +in output. Slower to query.

  • +
+
+
Returns:
+

List of folder entities.

+
+
Return type:
+

list[dict[str, Any]]

+
+
+
+ +
+
+get_rest_product(project_name, product_id)[source]
+
+ +
+
+get_rest_project(project_name)[source]
+

Query project by name.

+

This call returns project with anatomy data.

+
+
Parameters:
+

project_name (str) – Name of project.

+
+
Returns:
+

+
Project entity data or ‘None’ if

project was not found.

+
+
+

+
+
Return type:
+

Union[dict[str, Any], None]

+
+
+
+ +
+
+get_rest_projects(active=True, library=None)[source]
+

Query available project entities.

+

User must be logged in.

+
+
Parameters:
+
    +
  • active (Optional[bool]) – Filter active/inactive projects. Both +are returned if ‘None’ is passed.

  • +
  • library (Optional[bool]) – Filter standard/library projects. Both +are returned if ‘None’ is passed.

  • +
+
+
Returns:
+

Available projects.

+
+
Return type:
+

Generator[dict[str, Any]]

+
+
+
+ +
+
+get_rest_representation(project_name, representation_id)[source]
+
+ +
+
+get_rest_task(project_name, task_id)[source]
+
+ +
+
+get_rest_url()[source]
+
+ +
+
+get_rest_version(project_name, version_id)[source]
+
+ +
+
+get_schemas()[source]
+

Get components schema.

+

Name of components does not match entity type names e.g. ‘project’ is +under ‘ProjectModel’. We should find out some mapping. Also, there +are properties which don’t have information about reference to object +e.g. ‘config’ has just object definition without reference schema.

+
+
Returns:
+

Component schemas.

+
+
Return type:
+

dict[str, Any]

+
+
+
+ +
+
+get_secret(secret_name)[source]
+

Get secret by name.

+

Example output:

+
{
+    "name": "secret_name",
+    "value": "secret_value",
+}
+
+
+
+
Parameters:
+

secret_name (str) – Name of secret.

+
+
Returns:
+

Secret entity data.

+
+
Return type:
+

dict[str, str]

+
+
+
+ +
+
+get_secrets()[source]
+

Get all secrets.

+

Example output:

+
[
+    {
+        "name": "secret_1",
+        "value": "secret_value_1",
+    },
+    {
+        "name": "secret_2",
+        "value": "secret_value_2",
+    }
+]
+
+
+
+
Returns:
+

List of secret entities.

+
+
Return type:
+

list[dict[str, str]]

+
+
+
+ +
+
+get_sender()[source]
+

Sender used to send requests.

+
+
Returns:
+

Sender name or None.

+
+
Return type:
+

Union[str, None]

+
+
+
+ +
+
+get_sender_type()[source]
+

Sender type used to send requests.

+

Sender type is supported since AYON server 1.5.5 .

+
+
Returns:
+

Sender type or None.

+
+
Return type:
+

Union[str, None]

+
+
+
+ +
+
+get_server_api_connection()[source]
+

Access to global scope object of GlobalServerAPI.

+

This access expect to have set environment variables ‘AYON_SERVER_URL’ +and ‘AYON_API_KEY’.

+
+
Returns:
+

Object of connection to server.

+
+
Return type:
+

GlobalServerAPI

+
+
+
+ +
+
+get_server_schema()[source]
+

Get server schema with info, url paths, components etc.

+
+
Todos:

Cache schema - How to find out it is outdated?

+
+
+
+
Returns:
+

Full server schema.

+
+
Return type:
+

dict[str, Any]

+
+
+
+ +
+
+get_server_version()[source]
+

Get server version.

+

Version should match semantic version (https://semver.org/).

+
+
Returns:
+

Server version.

+
+
Return type:
+

str

+
+
+
+ +
+
+get_server_version_tuple()[source]
+

Get server version as tuple.

+

Version should match semantic version (https://semver.org/).

+

This function only returns first three numbers of version.

+
+
Returns:
+

+
Server

version.

+
+
+

+
+
Return type:
+

Tuple[int, int, int, Union[str, None], Union[str, None]]

+
+
+
+ +
+
+get_service_addon_name()[source]
+

Name of addon which initialized service connection.

+

Service context must be initialized to be able to use this function. Call +‘init_service’ on you service start to do so.

+
+
Returns:
+

Name of addon or None.

+
+
Return type:
+

Union[str, None]

+
+
+
+ +
+
+get_service_addon_settings(project_name=None)[source]
+

Addon settings of service which initialized service.

+

Service context must be initialized to be able to use this function. Call +‘init_service’ on you service start to do so.

+
+
Parameters:
+

project_name (Optional[str]) – Project name.

+
+
Returns:
+

Addon settings.

+
+
Return type:
+

Dict[str, Any]

+
+
Raises:
+

ValueError – When service was not initialized.

+
+
+
+ +
+
+get_service_addon_version()[source]
+

Version of addon which initialized service connection.

+

Service context must be initialized to be able to use this function. Call +‘init_service’ on you service start to do so.

+
+
Returns:
+

Version of addon or None.

+
+
Return type:
+

Union[str, None]

+
+
+
+ +
+
+get_service_name()[source]
+

Name of service.

+

Service context must be initialized to be able to use this function. Call +‘init_service’ on you service start to do so.

+
+
Returns:
+

Name of service if service was registered.

+
+
Return type:
+

Union[str, None]

+
+
+
+ +
+
+get_site_id()[source]
+

Site id used for connection.

+

Site id tells server from which machine/site is connection created and +is used for default site overrides when settings are received.

+
+
Returns:
+

Site id value or None if not filled.

+
+
Return type:
+

Union[str, None]

+
+
+
+ +
+
+get_ssl_verify()[source]
+

Enable ssl verification.

+
+
Returns:
+

Current state of ssl verification.

+
+
Return type:
+

bool

+
+
+
+ +
+
+get_task_by_folder_path(project_name, folder_path, task_name, fields=None, own_attributes=False)[source]
+

Query task entity by folder path and task name.

+
+
Parameters:
+
    +
  • project_name (str) – Project name.

  • +
  • folder_path (str) – Folder path.

  • +
  • task_name (str) – Task name.

  • +
  • fields (Optional[Iterable[str]]) – Task fields that should +be returned.

  • +
  • own_attributes (Optional[bool]) – Attribute values that are +not explicitly set on entity will have ‘None’ value.

  • +
+
+
Returns:
+

+
Task entity data or None if was

not found.

+
+
+

+
+
Return type:
+

Union[dict[str, Any], None]

+
+
+
+ +
+
+get_task_by_id(project_name, task_id, fields=None, own_attributes=False)[source]
+

Query task entity by id.

+
+
Parameters:
+
    +
  • project_name (str) – Name of project where to look for queried +entities.

  • +
  • task_id (str) – Task id.

  • +
  • fields (Optional[Iterable[str]]) – Fields that should be returned. +All fields are returned if ‘None’ is passed.

  • +
  • own_attributes (Optional[bool]) – Attribute values that are +not explicitly set on entity will have ‘None’ value.

  • +
+
+
Returns:
+

Task entity data or None if was not found.

+
+
Return type:
+

Union[dict, None]

+
+
+
+ +
+
+get_task_by_name(project_name, folder_id, task_name, fields=None, own_attributes=False)[source]
+

Query task entity by name and folder id.

+
+
Parameters:
+
    +
  • project_name (str) – Name of project where to look for queried +entities.

  • +
  • folder_id (str) – Folder id.

  • +
  • task_name (str) – Task name

  • +
  • fields (Optional[Iterable[str]]) – Fields that should be returned. +All fields are returned if ‘None’ is passed.

  • +
  • own_attributes (Optional[bool]) – Attribute values that are +not explicitly set on entity will have ‘None’ value.

  • +
+
+
Returns:
+

Task entity data or None if was not found.

+
+
Return type:
+

Union[dict, None]

+
+
+
+ +
+ +

Query task links from server.

+
+
Parameters:
+
    +
  • project_name (str) – Project where links are.

  • +
  • task_id (str) – Task id for which links should be received.

  • +
  • link_types (Optional[Iterable[str]]) – Link type filters.

  • +
  • link_direction (Optional[Literal["in", "out"]]) – Link direction +filter.

  • +
+
+
Returns:
+

Link info of task.

+
+
Return type:
+

list[dict[str, Any]]

+
+
+
+ +
+
+get_tasks(project_name, task_ids=None, task_names=None, task_types=None, folder_ids=None, assignees=None, assignees_all=None, statuses=None, tags=None, active=True, fields=None, own_attributes=False)[source]
+

Query task entities from server.

+
+
Parameters:
+
    +
  • project_name (str) – Name of project.

  • +
  • task_ids (Iterable[str]) – Task ids to filter.

  • +
  • task_names (Iterable[str]) – Task names used for filtering.

  • +
  • task_types (Iterable[str]) – Task types used for filtering.

  • +
  • folder_ids (Iterable[str]) – Ids of task parents. Use ‘None’ +if folder is direct child of project.

  • +
  • assignees (Optional[Iterable[str]]) – Task assignees used for +filtering. All tasks with any of passed assignees are +returned.

  • +
  • assignees_all (Optional[Iterable[str]]) – Task assignees used +for filtering. Task must have all of passed assignees to be +returned.

  • +
  • statuses (Optional[Iterable[str]]) – Task statuses used for +filtering.

  • +
  • tags (Optional[Iterable[str]]) – Task tags used for +filtering.

  • +
  • active (Optional[bool]) – Filter active/inactive tasks. +Both are returned if is set to None.

  • +
  • fields (Optional[Iterable[str]]) – Fields to be queried for +folder. All possible folder fields are returned +if ‘None’ is passed.

  • +
  • own_attributes (Optional[bool]) – Attribute values that are +not explicitly set on entity will have ‘None’ value.

  • +
+
+
Returns:
+

Queried task entities.

+
+
Return type:
+

Generator[dict[str, Any]]

+
+
+
+ +
+
+get_tasks_by_folder_path(project_name, folder_path, task_names=None, task_types=None, assignees=None, assignees_all=None, statuses=None, tags=None, active=True, fields=None, own_attributes=False)[source]
+

Query task entities from server by folder path.

+
+
Parameters:
+
    +
  • project_name (str) – Name of project.

  • +
  • folder_path (str) – Folder path.

  • +
  • task_names (Iterable[str]) – Task names used for filtering.

  • +
  • task_types (Iterable[str]) – Task types used for filtering.

  • +
  • assignees (Optional[Iterable[str]]) – Task assignees used for +filtering. All tasks with any of passed assignees are +returned.

  • +
  • assignees_all (Optional[Iterable[str]]) – Task assignees used +for filtering. Task must have all of passed assignees to be +returned.

  • +
  • statuses (Optional[Iterable[str]]) – Task statuses used for +filtering.

  • +
  • tags (Optional[Iterable[str]]) – Task tags used for +filtering.

  • +
  • active (Optional[bool]) – Filter active/inactive tasks. +Both are returned if is set to None.

  • +
  • fields (Optional[Iterable[str]]) – Fields to be queried for +folder. All possible folder fields are returned +if ‘None’ is passed.

  • +
  • own_attributes (Optional[bool]) – Attribute values that are +not explicitly set on entity will have ‘None’ value.

  • +
+
+
+
+ +
+
+get_tasks_by_folder_paths(project_name, folder_paths, task_names=None, task_types=None, assignees=None, assignees_all=None, statuses=None, tags=None, active=True, fields=None, own_attributes=False)[source]
+

Query task entities from server by folder paths.

+
+
Parameters:
+
    +
  • project_name (str) – Name of project.

  • +
  • folder_paths (list[str]) – Folder paths.

  • +
  • task_names (Iterable[str]) – Task names used for filtering.

  • +
  • task_types (Iterable[str]) – Task types used for filtering.

  • +
  • assignees (Optional[Iterable[str]]) – Task assignees used for +filtering. All tasks with any of passed assignees are +returned.

  • +
  • assignees_all (Optional[Iterable[str]]) – Task assignees used +for filtering. Task must have all of passed assignees to be +returned.

  • +
  • statuses (Optional[Iterable[str]]) – Task statuses used for +filtering.

  • +
  • tags (Optional[Iterable[str]]) – Task tags used for +filtering.

  • +
  • active (Optional[bool]) – Filter active/inactive tasks. +Both are returned if is set to None.

  • +
  • fields (Optional[Iterable[str]]) – Fields to be queried for +folder. All possible folder fields are returned +if ‘None’ is passed.

  • +
  • own_attributes (Optional[bool]) – Attribute values that are +not explicitly set on entity will have ‘None’ value.

  • +
+
+
Returns:
+

+
Task entities by

folder path.

+
+
+

+
+
Return type:
+

dict[dict[str, list[dict[str, Any]]]

+
+
+
+ +
+ +

Query tasks links from server.

+
+
Parameters:
+
    +
  • project_name (str) – Project where links are.

  • +
  • task_ids (Optional[Iterable[str]]) – Ids of tasks for which +links should be received.

  • +
  • link_types (Optional[Iterable[str]]) – Link type filters.

  • +
  • link_direction (Optional[Literal["in", "out"]]) – Link direction +filter.

  • +
+
+
Returns:
+

Link info by task ids.

+
+
Return type:
+

dict[str, list[dict[str, Any]]]

+
+
+
+ +
+
+get_thumbnail(project_name, entity_type, entity_id, thumbnail_id=None)[source]
+

Get thumbnail from server.

+

Permissions of thumbnails are related to entities so thumbnails must +be queried per entity. So an entity type and entity type is required +to be passed.

+
+

Notes

+
+
It is recommended to use one of prepared entity type specific

methods ‘get_folder_thumbnail’, ‘get_version_thumbnail’ or +‘get_workfile_thumbnail’.

+
+
We do recommend pass thumbnail id if you have access to it. Each

entity that allows thumbnails has ‘thumbnailId’ field, so it +can be queried.

+
+
+
+
+
Parameters:
+
    +
  • project_name (str) – Project under which the entity is located.

  • +
  • entity_type (str) – Entity type which passed entity id represents.

  • +
  • entity_id (str) – Entity id for which thumbnail should be returned.

  • +
  • thumbnail_id (Optional[str]) – DEPRECATED Use +‘get_thumbnail_by_id’.

  • +
+
+
Returns:
+

+
Thumbnail content wrapper. Does not have to be

valid.

+
+
+

+
+
Return type:
+

ThumbnailContent

+
+
+
+ +
+
+get_thumbnail_by_id(project_name, thumbnail_id)[source]
+

Get thumbnail from server by id.

+

Permissions of thumbnails are related to entities so thumbnails must +be queried per entity. So an entity type and entity type is required +to be passed.

+
+

Notes

+
+
It is recommended to use one of prepared entity type specific

methods ‘get_folder_thumbnail’, ‘get_version_thumbnail’ or +‘get_workfile_thumbnail’.

+
+
We do recommend pass thumbnail id if you have access to it. Each

entity that allows thumbnails has ‘thumbnailId’ field, so it +can be queried.

+
+
+
+
+
Parameters:
+
    +
  • project_name (str) – Project under which the entity is located.

  • +
  • thumbnail_id (Optional[str]) – DEPRECATED Use +‘get_thumbnail_by_id’.

  • +
+
+
Returns:
+

+
Thumbnail content wrapper. Does not have to be

valid.

+
+
+

+
+
Return type:
+

ThumbnailContent

+
+
+
+ +
+
+get_timeout()[source]
+

Current value for requests timeout.

+
+
Returns:
+

Timeout value in seconds.

+
+
Return type:
+

float

+
+
+
+ +
+
+get_user(username=None)[source]
+

Get user info using REST endpoit.

+
+
Parameters:
+

username (Optional[str]) – Username.

+
+
Returns:
+

+
User info or None if user is not

found.

+
+
+

+
+
Return type:
+

Union[dict[str, Any], None]

+
+
+
+ +
+
+get_user_by_name(username, project_name=None, fields=None)[source]
+

Get user by name using GraphQl.

+
+
Only administrators and managers can fetch all users. For other users

it is required to pass in ‘project_name’ filter.

+
+
+
+
Parameters:
+
    +
  • username (str) – Username.

  • +
  • project_name (Optional[str]) – Define scope of project.

  • +
  • fields (Optional[Iterable[str]]) – Fields to be queried +for users.

  • +
+
+
Returns:
+

+
User info or None if user is not

found.

+
+
+

+
+
Return type:
+

Union[dict[str, Any], None]

+
+
+
+ +
+
+get_user_by_token(url, token, timeout=None)[source]
+

Get user information by url and token.

+
+
Parameters:
+
    +
  • url (str) – Server url.

  • +
  • token (str) – User’s token.

  • +
  • timeout (Optional[float]) – Timeout for request. Value from +‘get_default_timeout’ is used if not specified.

  • +
+
+
Returns:
+

User information if url and token are valid.

+
+
Return type:
+

Optional[Dict[str, Any]]

+
+
+
+ +
+
+get_users(project_name=None, usernames=None, fields=None)[source]
+

Get Users.

+
+
Only administrators and managers can fetch all users. For other users

it is required to pass in ‘project_name’ filter.

+
+
+
+
Parameters:
+
    +
  • project_name (Optional[str]) – Project name.

  • +
  • usernames (Optional[Iterable[str]]) – Filter by usernames.

  • +
  • fields (Optional[Iterable[str]]) – Fields to be queried +for users.

  • +
+
+
Returns:
+

Queried users.

+
+
Return type:
+

Generator[dict[str, Any]]

+
+
+
+ +
+
+get_version_by_id(project_name, version_id, fields=None, own_attributes=<object object>)[source]
+

Query version entity by id.

+
+
Parameters:
+
    +
  • project_name (str) – Name of project where to look for queried +entities.

  • +
  • version_id (str) – Version id.

  • +
  • fields (Optional[Iterable[str]]) – Fields that should be returned. +All fields are returned if ‘None’ is passed.

  • +
  • own_attributes (Optional[bool]) – DEPRECATED: Not supported for +versions.

  • +
+
+
Returns:
+

Version entity data or None if was not found.

+
+
Return type:
+

Union[dict, None]

+
+
+
+ +
+
+get_version_by_name(project_name, version, product_id, fields=None, own_attributes=<object object>)[source]
+

Query version entity by version and product id.

+
+
Parameters:
+
    +
  • project_name (str) – Name of project where to look for queried +entities.

  • +
  • version (int) – Version of version entity.

  • +
  • product_id (str) – Product id. Product is a parent of version.

  • +
  • fields (Optional[Iterable[str]]) – Fields that should be returned. +All fields are returned if ‘None’ is passed.

  • +
  • own_attributes (Optional[bool]) – DEPRECATED: Not supported for +versions.

  • +
+
+
Returns:
+

Version entity data or None if was not found.

+
+
Return type:
+

Union[dict, None]

+
+
+
+ +
+ +

Query version links from server.

+
+
Parameters:
+
    +
  • project_name (str) – Project where links are.

  • +
  • version_id (str) – Version id for which links should be received.

  • +
  • link_types (Optional[Iterable[str]]) – Link type filters.

  • +
  • link_direction (Optional[Literal["in", "out"]]) – Link direction +filter.

  • +
+
+
Returns:
+

Link info of version.

+
+
Return type:
+

list[dict[str, Any]]

+
+
+
+ +
+
+get_version_thumbnail(project_name, version_id, thumbnail_id=None)[source]
+

Prepared method to receive thumbnail for version entity.

+
+
Parameters:
+
    +
  • project_name (str) – Project under which the entity is located.

  • +
  • version_id (str) – Version id for which thumbnail should be +returned.

  • +
  • thumbnail_id (Optional[str]) – Prepared thumbnail id from entity. +Used only to check if thumbnail was already cached.

  • +
+
+
Returns:
+

+
Path to downloaded thumbnail or none if entity

does not have any (or if user does not have permissions).

+
+
+

+
+
Return type:
+

Union[str, None]

+
+
+
+ +
+
+get_versions(project_name, version_ids=None, product_ids=None, task_ids=None, versions=None, hero=True, standard=True, latest=None, statuses=None, tags=None, active=True, fields=None, own_attributes=<object object>)[source]
+

Get version entities based on passed filters from server.

+
+
Parameters:
+
    +
  • project_name (str) – Name of project where to look for versions.

  • +
  • version_ids (Optional[Iterable[str]]) – Version ids used for +version filtering.

  • +
  • product_ids (Optional[Iterable[str]]) – Product ids used for +version filtering.

  • +
  • task_ids (Optional[Iterable[str]]) – Task ids used for +version filtering.

  • +
  • versions (Optional[Iterable[int]]) – Versions we’re interested in.

  • +
  • hero (Optional[bool]) – Skip hero versions when set to False.

  • +
  • standard (Optional[bool]) – Skip standard (non-hero) when +set to False.

  • +
  • latest (Optional[bool]) – Return only latest version of standard +versions. This can be combined only with ‘standard’ attribute +set to True.

  • +
  • statuses (Optional[Iterable[str]]) – Representation statuses used +for filtering.

  • +
  • tags (Optional[Iterable[str]]) – Representation tags used +for filtering.

  • +
  • active (Optional[bool]) – Receive active/inactive entities. +Both are returned when ‘None’ is passed.

  • +
  • fields (Optional[Iterable[str]]) – Fields to be queried +for version. All possible folder fields are returned +if ‘None’ is passed.

  • +
  • own_attributes (Optional[bool]) – DEPRECATED: Not supported for +versions.

  • +
+
+
Returns:
+

Queried version entities.

+
+
Return type:
+

Generator[dict[str, Any]]

+
+
+
+ +
+ +

Query versions links from server.

+
+
Parameters:
+
    +
  • project_name (str) – Project where links are.

  • +
  • version_ids (Optional[Iterable[str]]) – Ids of versions for which +links should be received.

  • +
  • link_types (Optional[Iterable[str]]) – Link type filters.

  • +
  • link_direction (Optional[Literal["in", "out"]]) – Link direction +filter.

  • +
+
+
Returns:
+

Link info by version ids.

+
+
Return type:
+

dict[str, list[dict[str, Any]]]

+
+
+
+ +
+
+get_workfile_info(project_name, task_id, path, fields=None, own_attributes=<object object>)[source]
+

Workfile info entity by task id and workfile path.

+
+
Parameters:
+
    +
  • project_name (str) – Project under which the entity is located.

  • +
  • task_id (str) – Task id.

  • +
  • path (str) – Rootless workfile path.

  • +
  • fields (Optional[Iterable[str]]) – Fields to be queried for +representation. All possible fields are returned if ‘None’ is +passed.

  • +
  • own_attributes (Optional[bool]) – DEPRECATED: Not supported for +workfiles.

  • +
+
+
Returns:
+

Workfile info entity or None.

+
+
Return type:
+

Union[dict[str, Any], None]

+
+
+
+ +
+
+get_workfile_info_by_id(project_name, workfile_id, fields=None, own_attributes=<object object>)[source]
+

Workfile info entity by id.

+
+
Parameters:
+
    +
  • project_name (str) – Project under which the entity is located.

  • +
  • workfile_id (str) – Workfile info id.

  • +
  • fields (Optional[Iterable[str]]) – Fields to be queried for +representation. All possible fields are returned if ‘None’ is +passed.

  • +
  • own_attributes (Optional[bool]) – DEPRECATED: Not supported for +workfiles.

  • +
+
+
Returns:
+

Workfile info entity or None.

+
+
Return type:
+

Union[dict[str, Any], None]

+
+
+
+ +
+
+get_workfile_thumbnail(project_name, workfile_id, thumbnail_id=None)[source]
+

Prepared method to receive thumbnail for workfile entity.

+
+
Parameters:
+
    +
  • project_name (str) – Project under which the entity is located.

  • +
  • workfile_id (str) – Worfile id for which thumbnail should be +returned.

  • +
  • thumbnail_id (Optional[str]) – Prepared thumbnail id from entity. +Used only to check if thumbnail was already cached.

  • +
+
+
Returns:
+

+
Path to downloaded thumbnail or none if entity

does not have any (or if user does not have permissions).

+
+
+

+
+
Return type:
+

Union[str, None]

+
+
+
+ +
+
+get_workfiles_info(project_name, workfile_ids=None, task_ids=None, paths=None, path_regex=None, statuses=None, tags=None, has_links=None, fields=None, own_attributes=<object object>)[source]
+

Workfile info entities by passed filters.

+
+
Parameters:
+
    +
  • project_name (str) – Project under which the entity is located.

  • +
  • workfile_ids (Optional[Iterable[str]]) – Workfile ids.

  • +
  • task_ids (Optional[Iterable[str]]) – Task ids.

  • +
  • paths (Optional[Iterable[str]]) – Rootless workfiles paths.

  • +
  • path_regex (Optional[str]) – Regex filter for workfile path.

  • +
  • statuses (Optional[Iterable[str]]) – Workfile info statuses used +for filtering.

  • +
  • tags (Optional[Iterable[str]]) – Workfile info tags used +for filtering.

  • +
  • has_links (Optional[Literal[IN, OUT, ANY]]) – Filter +representations with IN/OUT/ANY links.

  • +
  • fields (Optional[Iterable[str]]) – Fields to be queried for +representation. All possible fields are returned if ‘None’ is +passed.

  • +
  • own_attributes (Optional[bool]) – DEPRECATED: Not supported for +workfiles.

  • +
+
+
Returns:
+

Queried workfile info entites.

+
+
Return type:
+

Generator[dict[str, Any]]

+
+
+
+ +
+
+init_service(*args, **kwargs)[source]
+

Initialize current connection from service.

+

The service expect specific environment variables. The variables must all +be set to make the connection work as a service.

+
+ +
+
+is_connection_created()[source]
+

Is global connection created.

+
+
Returns:
+

True if connection was connected.

+
+
Return type:
+

bool

+
+
+
+ +
+
+is_service_user()[source]
+

Check if connection is using service API key.

+
+
Returns:
+

Used api key belongs to service user.

+
+
Return type:
+

bool

+
+
+
+ +
+
+is_token_valid(url, token, timeout=None)[source]
+

Check if token is valid.

+

Token can be a user token or service api key.

+
+
Parameters:
+
    +
  • url (str) – Server url.

  • +
  • token (str) – User’s token.

  • +
  • timeout (Optional[float]) – Timeout for request. Value from +‘get_default_timeout’ is used if not specified.

  • +
+
+
Returns:
+

True if token is valid.

+
+
Return type:
+

bool

+
+
+
+ +
+
+login_to_server(url, username, password, timeout=None)[source]
+

Use login to the server to receive token.

+
+
Parameters:
+
    +
  • url (str) – Server url.

  • +
  • username (str) – User’s username.

  • +
  • password (str) – User’s password.

  • +
  • timeout (Optional[float]) – Timeout for request. Value from +‘get_default_timeout’ is used if not specified.

  • +
+
+
Returns:
+

+
User’s token if login was successfull.

Otherwise ‘None’.

+
+
+

+
+
Return type:
+

Union[str, None]

+
+
+
+ +
+ +

Make sure link type exists on a project.

+
+
Parameters:
+
    +
  • project_name (str) – Name of project.

  • +
  • link_type_name (str) – Name of link type.

  • +
  • input_type (str) – Input entity type of link.

  • +
  • output_type (str) – Output entity type of link.

  • +
  • data (Optional[dict[str, Any]]) – Link type related data.

  • +
+
+
+
+ +
+
+patch(entrypoint, **kwargs)[source]
+
+ +
+
+post(entrypoint, **kwargs)[source]
+
+ +
+
+put(entrypoint, **kwargs)[source]
+
+ +
+
+query_graphql(query, variables=None)[source]
+

Execute GraphQl query.

+
+
Parameters:
+
    +
  • query (str) – GraphQl query string.

  • +
  • variables (Optional[dict[str, Any]) – Variables that can be +used in query.

  • +
+
+
Returns:
+

Response from server.

+
+
Return type:
+

GraphQlResponse

+
+
+
+ +
+
+raw_delete(entrypoint, **kwargs)[source]
+
+ +
+
+raw_get(entrypoint, **kwargs)[source]
+
+ +
+
+raw_patch(entrypoint, **kwargs)[source]
+
+ +
+
+raw_post(entrypoint, **kwargs)[source]
+
+ +
+
+raw_put(entrypoint, **kwargs)[source]
+
+ +
+
+remove_attribute_config(attribute_name)[source]
+

Remove attribute from server.

+

This can’t be un-done, please use carefully.

+
+
Parameters:
+

attribute_name (str) – Name of attribute to remove.

+
+
+
+ +
+
+reset_attributes_schema()[source]
+
+ +
+
+save_secret(secret_name, secret_value)[source]
+

Save secret.

+

This endpoint can create and update secret.

+
+
Parameters:
+
    +
  • secret_name (str) – Name of secret.

  • +
  • secret_value (str) – Value of secret.

  • +
+
+
+
+ +
+
+send_activities_batch_operations(project_name, operations, can_fail=False, raise_on_fail=True)[source]
+

Post multiple CRUD activities operations to server.

+

When multiple changes should be made on server side this is the best +way to go. It is possible to pass multiple operations to process on a +server side and do the changes in a transaction.

+
+
Parameters:
+
    +
  • project_name (str) – On which project should be operations +processed.

  • +
  • operations (list[dict[str, Any]]) – Operations to be processed.

  • +
  • can_fail (Optional[bool]) – Server will try to process all +operations even if one of them fails.

  • +
  • raise_on_fail (Optional[bool]) – Raise exception if an operation +fails. You can handle failed operations on your own +when set to ‘False’.

  • +
+
+
Raises:
+
    +
  • ValueError – Operations can’t be converted to json string.

  • +
  • FailedOperations – When output does not contain server operations + or ‘raise_on_fail’ is enabled and any operation fails.

  • +
+
+
Returns:
+

Operations result with process details.

+
+
Return type:
+

list[dict[str, Any]]

+
+
+
+ +
+
+send_batch_operations(project_name, operations, can_fail=False, raise_on_fail=True)[source]
+

Post multiple CRUD operations to server.

+

When multiple changes should be made on server side this is the best +way to go. It is possible to pass multiple operations to process on a +server side and do the changes in a transaction.

+
+
Parameters:
+
    +
  • project_name (str) – On which project should be operations +processed.

  • +
  • operations (list[dict[str, Any]]) – Operations to be processed.

  • +
  • can_fail (Optional[bool]) – Server will try to process all +operations even if one of them fails.

  • +
  • raise_on_fail (Optional[bool]) – Raise exception if an operation +fails. You can handle failed operations on your own +when set to ‘False’.

  • +
+
+
Raises:
+
    +
  • ValueError – Operations can’t be converted to json string.

  • +
  • FailedOperations – When output does not contain server operations + or ‘raise_on_fail’ is enabled and any operation fails.

  • +
+
+
Returns:
+

Operations result with process details.

+
+
Return type:
+

list[dict[str, Any]]

+
+
+
+ +
+
+set_attribute_config(attribute_name, data, scope, position=None, builtin=False)[source]
+
+ +
+
+set_cert(cert)[source]
+

Change cert file used for connection to server.

+
+
Parameters:
+

cert (Union[str, None]) – Path to cert file.

+
+
+
+ +
+
+set_client_version(client_version)[source]
+

Set version of client used to connect to server.

+

Client version is AYON client build desktop application.

+
+
Parameters:
+

client_version (Union[str, None]) – Client version string.

+
+
+
+ +
+
+set_default_settings_variant(variant)[source]
+

Change default variant for addon settings.

+
+

Note

+
+
It is recommended to set only ‘production’ or ‘staging’ variants

as default variant.

+
+
+
+
+
Parameters:
+

variant (str) – Settings variant name. It is possible to use +‘production’, ‘staging’ or name of dev bundle.

+
+
+
+ +
+
+set_environments(url, token)[source]
+

Set global environments for global connection.

+
+
Parameters:
+
    +
  • url (Union[str, None]) – Url to server or None to unset environments.

  • +
  • token (Union[str, None]) – API key token to be used for connection.

  • +
+
+
+
+ +
+
+set_max_retries(max_retries)[source]
+

Change max retries value for requests.

+
+
Parameters:
+

max_retries (Union[int, None]) – Max retries value.

+
+
+
+ +
+
+set_sender(sender)[source]
+

Change sender used for requests.

+
+
Parameters:
+

sender (Union[str, None]) – Sender name or None.

+
+
+
+ +
+
+set_sender_type(sender_type)[source]
+

Change sender type used for requests.

+
+
Parameters:
+

sender_type (Union[str, None]) – Sender type or None.

+
+
+
+ +
+
+set_site_id(site_id)[source]
+

Change site id of connection.

+

Behave as specific site for server. It affects default behavior of +settings getter methods.

+
+
Parameters:
+

site_id (Union[str, None]) – Site id value, or ‘None’ to unset.

+
+
+
+ +
+
+set_ssl_verify(ssl_verify)[source]
+

Change ssl verification state.

+
+
Parameters:
+

ssl_verify (Union[bool, str, None]) – Enabled/disable +ssl verification, can be a path to file.

+
+
+
+ +
+
+set_timeout(timeout)[source]
+

Change timeout value for requests.

+
+
Parameters:
+

timeout (Union[float, None]) – Timeout value in seconds.

+
+
+
+ +
+
+slugify_string(input_string, separator='_', slug_whitelist='abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789', split_chars=' ,./\\;:!|*^#@~+-_=', min_length=1, lower=False, make_set=False)[source]
+

Slugify a text string.

+

This function removes transliterates input string to ASCII, removes +special characters and use join resulting elements using +specified separator.

+
+
Parameters:
+
    +
  • input_string (str) – Input string to slugify

  • +
  • separator (str) – A string used to separate returned elements +(default: “_”)

  • +
  • slug_whitelist (str) – Characters allowed in the output +(default: ascii letters, digits and the separator)

  • +
  • split_chars (str) – Set of characters used for word splitting +(there is a sane default)

  • +
  • lower (bool) – Convert to lower-case (default: False)

  • +
  • make_set (bool) – Return “set” object instead of string.

  • +
  • min_length (int) – Minimal length of an element (word).

  • +
+
+
Returns:
+

+
Based on ‘make_set’ value returns slugified

string.

+
+
+

+
+
Return type:
+

Union[str, Set[str]]

+
+
+
+ +
+
+take_web_action_event(server_url: str, action_token: str) Dict[str, Any][source]
+

Take web action event using action token.

+

Action token is generated by AYON server and passed to AYON launcher.

+
+
Parameters:
+
    +
  • server_url (str) – AYON server url.

  • +
  • action_token (str) – Action token.

  • +
+
+
Returns:
+

Web action event.

+
+
Return type:
+

Dict[str, Any]

+
+
+
+ +
+
+trigger_server_restart()[source]
+

Trigger server restart.

+

Restart may be required when a change of specific value happened on +server.

+
+ +
+
+update_activity(project_name: str, activity_id: str, body: Optional[str] = None, file_ids: Optional[List[str]] = None, append_file_ids: Optional[bool] = False, data: Optional[Dict[str, Any]] = None)[source]
+

Update activity by id.

+
+
Parameters:
+
    +
  • project_name (str) – Project on which activity happened.

  • +
  • activity_id (str) – Activity id.

  • +
  • body (str) – Activity body.

  • +
  • file_ids (Optional[List[str]]) – List of file ids attached +to activity.

  • +
  • append_file_ids (Optional[bool]) – Append file ids to existing +list of file ids.

  • +
  • data (Optional[Dict[str, Any]]) – Update data in activity.

  • +
+
+
+
+ +
+
+update_bundle(bundle_name, addon_versions=None, installer_version=None, dependency_packages=None, is_production=None, is_staging=None, is_dev=None, dev_active_user=None, dev_addons_config=None)[source]
+

Update bundle on server.

+

Dependency packages can be update only for single platform. Others +will be left untouched. Use ‘None’ value to unset dependency package +from bundle.

+
+
Parameters:
+
    +
  • bundle_name (str) – Name of bundle.

  • +
  • addon_versions (Optional[dict[str, str]]) – Addon versions, +possible only for dev bundles.

  • +
  • installer_version (Optional[str]) – Installer version, possible +only for dev bundles.

  • +
  • dependency_packages (Optional[dict[str, str]]) – Dependency pacakge +names that should be used with the bundle.

  • +
  • is_production (Optional[bool]) – Bundle will be marked as +production.

  • +
  • is_staging (Optional[bool]) – Bundle will be marked as staging.

  • +
  • is_dev (Optional[bool]) – Bundle will be marked as dev.

  • +
  • dev_active_user (Optional[str]) – Username that will be assigned +to dev bundle. Can be used only for dev bundles.

  • +
  • dev_addons_config (Optional[dict[str, Any]]) – Configuration for +dev addons. Can be used only for dev bundles.

  • +
+
+
+
+ +
+
+update_dependency_package(filename, sources)[source]
+

Update dependency package metadata on server.

+
+
Parameters:
+
    +
  • filename (str) – Filename of dependency package.

  • +
  • sources (list[dict[str, Any]]) – Information about +sources from where it is possible to get file. Fully replaces +existing sources.

  • +
+
+
+
+ +
+
+update_event(event_id, sender=None, project_name=None, username=None, status=None, description=None, summary=None, payload=None, progress=None, retries=None)[source]
+

Update event data.

+
+
Parameters:
+
    +
  • event_id (str) – Event id.

  • +
  • sender (Optional[str]) – New sender of event.

  • +
  • project_name (Optional[str]) – New project name.

  • +
  • username (Optional[str]) – New username.

  • +
  • status (Optional[str]) – New event status. Enum: “pending”, +“in_progress”, “finished”, “failed”, “aborted”, “restarted”

  • +
  • description (Optional[str]) – New description.

  • +
  • summary (Optional[dict[str, Any]]) – New summary.

  • +
  • payload (Optional[dict[str, Any]]) – New payload.

  • +
  • progress (Optional[int]) – New progress. Range [0-100].

  • +
  • retries (Optional[int]) – New retries.

  • +
+
+
+
+ +
+
+update_folder(project_name, folder_id, name=None, folder_type=None, parent_id=<object object>, label=<object object>, attrib=None, data=None, tags=None, status=None, active=None, thumbnail_id=<object object>)[source]
+

Update folder entity on server.

+
+
Do not pass parent_id, label amd thumbnail_id if you don’t

want to change their values. Value None would unset +their value.

+
+
+

Update of data will override existing value on folder entity.

+
+
Update of attrib does change only passed attributes. If you want

to unset value, use None.

+
+
+
+
Parameters:
+
    +
  • project_name (str) – Project name.

  • +
  • folder_id (str) – Folder id.

  • +
  • name (Optional[str]) – New name.

  • +
  • folder_type (Optional[str]) – New folder type.

  • +
  • parent_id (Optional[Union[str, None]]) – New parent folder id.

  • +
  • label (Optional[Union[str, None]]) – New label.

  • +
  • attrib (Optional[dict[str, Any]]) – New attributes.

  • +
  • data (Optional[dict[str, Any]]) – New data.

  • +
  • tags (Optional[Iterable[str]]) – New tags.

  • +
  • status (Optional[str]) – New status.

  • +
  • active (Optional[bool]) – New active state.

  • +
  • thumbnail_id (Optional[Union[str, None]]) – New thumbnail id.

  • +
+
+
+
+ +
+
+update_installer(filename, sources)[source]
+

Update installer information on server.

+
+
Parameters:
+
    +
  • filename (str) – Installer filename.

  • +
  • sources (list[dict[str, Any]]) – List of sources that +can be used to download file. Fully replaces existing sources.

  • +
+
+
+
+ +
+
+update_product(project_name, product_id, name=None, folder_id=None, product_type=None, attrib=None, data=None, tags=None, status=None, active=None)[source]
+

Update product entity on server.

+

Update of data will override existing value on folder entity.

+
+
Update of attrib does change only passed attributes. If you want

to unset value, use None.

+
+
+
+
Parameters:
+
    +
  • project_name (str) – Project name.

  • +
  • product_id (str) – Product id.

  • +
  • name (Optional[str]) – New product name.

  • +
  • folder_id (Optional[str]) – New product id.

  • +
  • product_type (Optional[str]) – New product type.

  • +
  • attrib (Optional[dict[str, Any]]) – New product attributes.

  • +
  • data (Optional[dict[str, Any]]) – New product data.

  • +
  • tags (Optional[Iterable[str]]) – New product tags.

  • +
  • status (Optional[str]) – New product status.

  • +
  • active (Optional[bool]) – New product active state.

  • +
+
+
+
+ +
+
+update_project(project_name, library=None, folder_types=None, task_types=None, link_types=None, statuses=None, tags=None, config=None, attrib=None, data=None, active=None, project_code=None, **changes)[source]
+

Update project entity on server.

+
+
Parameters:
+
    +
  • project_name (str) – Name of project.

  • +
  • library (Optional[bool]) – Change library state.

  • +
  • folder_types (Optional[list[dict[str, Any]]]) – Folder type +definitions.

  • +
  • task_types (Optional[list[dict[str, Any]]]) – Task type +definitions.

  • +
  • link_types (Optional[list[dict[str, Any]]]) – Link type +definitions.

  • +
  • statuses (Optional[list[dict[str, Any]]]) – Status definitions.

  • +
  • tags (Optional[list[dict[str, Any]]]) – List of tags available to +set on entities.

  • +
  • config (Optional[dict[dict[str, Any]]]) – Project anatomy config +with templates and roots.

  • +
  • attrib (Optional[dict[str, Any]]) – Project attributes to change.

  • +
  • data (Optional[dict[str, Any]]) – Custom data of a project. This +value will 100% override project data.

  • +
  • active (Optional[bool]) – Change active state of a project.

  • +
  • project_code (Optional[str]) – Change project code. Not recommended +during production.

  • +
  • **changes – Other changed keys based on Rest API documentation.

  • +
+
+
+
+ +
+
+update_representation(project_name, representation_id, name=None, version_id=None, files=None, attrib=None, data=None, tags=None, status=None, active=None)[source]
+

Update representation entity on server.

+

Update of data will override existing value on folder entity.

+
+
Update of attrib does change only passed attributes. If you want

to unset value, use None.

+
+
+
+
Parameters:
+
    +
  • project_name (str) – Project name.

  • +
  • representation_id (str) – Representation id.

  • +
  • name (Optional[str]) – New name.

  • +
  • version_id (Optional[str]) – New version id.

  • +
  • files (Optional[list[dict]]) – New files +information.

  • +
  • attrib (Optional[dict[str, Any]]) – New attributes.

  • +
  • data (Optional[dict[str, Any]]) – New data.

  • +
  • tags (Optional[Iterable[str]]) – New tags.

  • +
  • status (Optional[str]) – New status.

  • +
  • active (Optional[bool]) – New active state.

  • +
+
+
+
+ +
+
+update_task(project_name, task_id, name=None, task_type=None, folder_id=None, label=<object object>, assignees=None, attrib=None, data=None, tags=None, status=None, active=None, thumbnail_id=<object object>)[source]
+

Update task entity on server.

+
+
Do not pass label amd thumbnail_id if you don’t

want to change their values. Value None would unset +their value.

+
+
+

Update of data will override existing value on folder entity.

+
+
Update of attrib does change only passed attributes. If you want

to unset value, use None.

+
+
+
+
Parameters:
+
    +
  • project_name (str) – Project name.

  • +
  • task_id (str) – Task id.

  • +
  • name (Optional[str]) – New name.

  • +
  • task_type (Optional[str]) – New task type.

  • +
  • folder_id (Optional[str]) – New folder id.

  • +
  • label (Optional[Union[str, None]]) – New label.

  • +
  • assignees (Optional[str]) – New assignees.

  • +
  • attrib (Optional[dict[str, Any]]) – New attributes.

  • +
  • data (Optional[dict[str, Any]]) – New data.

  • +
  • tags (Optional[Iterable[str]]) – New tags.

  • +
  • status (Optional[str]) – New status.

  • +
  • active (Optional[bool]) – New active state.

  • +
  • thumbnail_id (Optional[Union[str, None]]) – New thumbnail id.

  • +
+
+
+
+ +
+
+update_thumbnail(project_name, thumbnail_id, src_filepath)[source]
+

Change thumbnail content by id.

+

Update can be also used to create new thumbnail.

+
+
Parameters:
+
    +
  • project_name (str) – Project where the thumbnail will be created +and can be used.

  • +
  • thumbnail_id (str) – Thumbnail id to update.

  • +
  • src_filepath (str) – Filepath to thumbnail which should be uploaded.

  • +
+
+
Raises:
+

ValueError – When thumbnail source cannot be processed.

+
+
+
+ +
+
+update_version(project_name, version_id, version=None, product_id=None, task_id=<object object>, author=None, attrib=None, data=None, tags=None, status=None, active=None, thumbnail_id=<object object>)[source]
+

Update version entity on server.

+
+
Do not pass task_id amd thumbnail_id if you don’t

want to change their values. Value None would unset +their value.

+
+
+

Update of data will override existing value on folder entity.

+
+
Update of attrib does change only passed attributes. If you want

to unset value, use None.

+
+
+
+
Parameters:
+
    +
  • project_name (str) – Project name.

  • +
  • version_id (str) – Version id.

  • +
  • version (Optional[int]) – New version.

  • +
  • product_id (Optional[str]) – New product id.

  • +
  • task_id (Optional[Union[str, None]]) – New task id.

  • +
  • author (Optional[str]) – New author username.

  • +
  • attrib (Optional[dict[str, Any]]) – New attributes.

  • +
  • data (Optional[dict[str, Any]]) – New data.

  • +
  • tags (Optional[Iterable[str]]) – New tags.

  • +
  • status (Optional[str]) – New status.

  • +
  • active (Optional[bool]) – New active state.

  • +
  • thumbnail_id (Optional[Union[str, None]]) – New thumbnail id.

  • +
+
+
+
+ +
+
+upload_addon_zip(src_filepath, progress=None)[source]
+

Upload addon zip file to server.

+
+
File is validated on server. If it is valid, it is installed. It will

create an event job which can be tracked (tracking part is not +implemented yet).

+
+
+

Example output:

+
{'eventId': 'a1bfbdee27c611eea7580242ac120003'}
+
+
+
+
Parameters:
+
    +
  • src_filepath (str) – Path to a zip file.

  • +
  • progress (Optional[TransferProgress]) – Object to keep track about +upload state.

  • +
+
+
Returns:
+

Response data from server.

+
+
Return type:
+

dict[str, Any]

+
+
+
+ +
+
+upload_dependency_package(src_filepath, dst_filename, platform_name=None, progress=None)[source]
+

Upload dependency package to server.

+
+
Parameters:
+
    +
  • src_filepath (str) – Path to a package file.

  • +
  • dst_filename (str) – Dependency package filename or name of package +for server version 0.2.0 or lower. Must be unique.

  • +
  • platform_name (Optional[str]) – Deprecated.

  • +
  • progress (Optional[TransferProgress]) – Object to keep track about +upload state.

  • +
+
+
+
+ +
+
+upload_file(endpoint, filepath, progress=None, request_type=None, **kwargs)[source]
+

Upload file to server.

+
+
Todos:

Use retries and timeout. +Return RestApiResponse.

+
+
+
+
Parameters:
+
    +
  • endpoint (str) – Endpoint or url where file will be uploaded.

  • +
  • filepath (str) – Source filepath.

  • +
  • progress (Optional[TransferProgress]) – Object that gives ability +to track upload progress.

  • +
  • request_type (Optional[RequestType]) – Type of request that will +be used to upload file.

  • +
  • **kwargs (Any) – Additional arguments that will be passed +to request function.

  • +
+
+
Returns:
+

Response object

+
+
Return type:
+

requests.Response

+
+
+
+ +
+
+upload_file_from_stream(endpoint, stream, progress, request_type, **kwargs)[source]
+

Upload file to server from bytes.

+
+
Todos:

Use retries and timeout. +Return RestApiResponse.

+
+
+
+
Parameters:
+
    +
  • endpoint (str) – Endpoint or url where file will be uploaded.

  • +
  • stream (Union[io.BytesIO, BinaryIO]) – File content stream.

  • +
  • progress (Optional[TransferProgress]) – Object that gives ability +to track upload progress.

  • +
  • request_type (Optional[RequestType]) – Type of request that will +be used to upload file.

  • +
  • **kwargs (Any) – Additional arguments that will be passed +to request function.

  • +
+
+
Returns:
+

Response object

+
+
Return type:
+

requests.Response

+
+
+
+ +
+
+upload_installer(src_filepath, dst_filename, progress=None)[source]
+

Upload installer file to server.

+
+
Parameters:
+
    +
  • src_filepath (str) – Source filepath.

  • +
  • dst_filename (str) – Destination filename.

  • +
  • progress (Optional[TransferProgress]) – Object that gives ability +to track download progress.

  • +
+
+
Returns:
+

Response object.

+
+
Return type:
+

requests.Response

+
+
+
+ +
+
+upload_reviewable(project_name, version_id, filepath, label=None, content_type=None, filename=None, progress=None, headers=None, **kwargs)[source]
+

Upload reviewable file to server.

+
+
Parameters:
+
    +
  • project_name (str) – Project name.

  • +
  • version_id (str) – Version id.

  • +
  • filepath (str) – Reviewable file path to upload.

  • +
  • label (Optional[str]) – Reviewable label. Filled automatically +server side with filename.

  • +
  • content_type (Optional[str]) – MIME type of the file.

  • +
  • filename (Optional[str]) – User as original filename. Filename from +‘filepath’ is used when not filled.

  • +
  • progress (Optional[TransferProgress]) – Progress.

  • +
  • headers (Optional[Dict[str, Any]]) – Headers.

  • +
+
+
Returns:
+

Server response.

+
+
Return type:
+

RestApiResponse

+
+
+
+ +
+
+validate_url(url, timeout=None)[source]
+

Validate url if is valid and server is available.

+

Validation checks if can be parsed as url and contains scheme.

+

Function will try to autofix url thus will return modified url when +connection to server works.

+
my_url = "my.server.url"
+try:
+    # Store new url
+    validated_url = validate_url(my_url)
+
+except UrlError:
+    # Handle invalid url
+    ...
+
+
+
+
Parameters:
+
    +
  • url (str) – Server url.

  • +
  • timeout (Optional[int]) – Timeout in seconds for connection to server.

  • +
+
+
Returns:
+

Url which was used to connect to server.

+
+
Raises:
+

UrlError – Error with short description and hints for user.

+
+
+
+ +
+
+version_is_latest(project_name, version_id)[source]
+

Is version latest from a product.

+
+
Parameters:
+
    +
  • project_name (str) – Project where to look for representation.

  • +
  • version_id (str) – Version id.

  • +
+
+
Returns:
+

Version is latest or not.

+
+
Return type:
+

bool

+
+
+
+ +
+

Submodules

+
+ +
+
+
+ + +
+ +
+ + +
+
+ +
+ +
+ + + + + + + + + + + + \ No newline at end of file diff --git a/ayon_api.operations.html b/ayon_api.operations.html new file mode 100644 index 0000000000..2045303ee7 --- /dev/null +++ b/ayon_api.operations.html @@ -0,0 +1,1487 @@ + + + + + + + + + + + + ayon_api.operations module — ayon-python-api 1.0.11 documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + +
+ + + + + +
+ +
+ +
+ + + +
+
+
+ +
+

ayon_api.operations module

+
+
+class AbstractOperation(project_name, entity_type, session)[source]
+

Bases: ABC

+

Base operation class.

+

Opration represent a call into database. The call can create, change or +remove data.

+
+
Parameters:
+
    +
  • project_name (str) – On which project operation will happen.

  • +
  • entity_type (str) – Type of entity on which change happens. +e.g. ‘folder’, ‘representation’ etc.

  • +
+
+
+
+
+property entity_type
+
+ +
+
+property id
+

Identifier of operation.

+
+ +
+
+abstract property operation_name
+

Stringified type of operation.

+
+ +
+
+property project_name
+
+ +
+
+to_data()[source]
+

Convert opration to data that can be converted to json or others.

+
+
Returns:
+

Description of operation.

+
+
Return type:
+

Dict[str, Any]

+
+
+
+ +
+ +
+
+class CreateOperation(project_name, entity_type, data, session)[source]
+

Bases: AbstractOperation

+

Opeartion to create an entity.

+
+
Parameters:
+
    +
  • project_name (str) – On which project operation will happen.

  • +
  • entity_type (str) – Type of entity on which change happens. +e.g. ‘folder’, ‘representation’ etc.

  • +
  • data (Dict[str, Any]) – Data of entity that will be created.

  • +
+
+
+
+
+property con
+
+ +
+
+property data
+
+ +
+
+property entity_id
+
+ +
+
+get(key, *args, **kwargs)[source]
+
+ +
+
+operation_name = 'create'
+
+ +
+
+property session
+
+ +
+
+set_value(key, value)[source]
+
+ +
+
+to_data()[source]
+

Convert opration to data that can be converted to json or others.

+
+
Returns:
+

Description of operation.

+
+
Return type:
+

Dict[str, Any]

+
+
+
+ +
+
+to_server_operation()[source]
+
+ +
+ +
+
+class DeleteOperation(project_name, entity_type, entity_id, session)[source]
+

Bases: AbstractOperation

+

Opeartion to delete an entity.

+
+
Parameters:
+
    +
  • project_name (str) – On which project operation will happen.

  • +
  • entity_type (str) – Type of entity on which change happens. +e.g. ‘folder’, ‘representation’ etc.

  • +
  • entity_id (str) – Entity id that will be removed.

  • +
+
+
+
+
+property con
+
+ +
+
+property entity_id
+
+ +
+
+operation_name = 'delete'
+
+ +
+
+property session
+
+ +
+
+to_data()[source]
+

Convert opration to data that can be converted to json or others.

+
+
Returns:
+

Description of operation.

+
+
Return type:
+

Dict[str, Any]

+
+
+
+ +
+
+to_server_operation()[source]
+
+ +
+ +
+
+class OperationsSession(con=None)[source]
+

Bases: object

+

Session storing operations that should happen in an order.

+

At this moment does not handle anything special can be sonsidered as +stupid list of operations that will happen after each other. If creation +of same entity is there multiple times it’s handled in any way and entity +values are not validated.

+

All operations must be related to single project.

+
+
Parameters:
+

con (Optional[ServerAPI]) – Connection to server. Global connection +is used if not passed.

+
+
+
+
+add(operation)[source]
+

Add operation to be processed.

+
+
Parameters:
+

operation (BaseOperation) – Operation that should be processed.

+
+
+
+ +
+
+append(operation)[source]
+

Add operation to be processed.

+
+
Parameters:
+

operation (BaseOperation) – Operation that should be processed.

+
+
+
+ +
+
+clear()[source]
+

Clear all registered operations.

+
+ +
+
+commit()[source]
+

Commit session operations.

+
+ +
+
+property con
+
+ +
+
+create_entity(project_name, entity_type, data, nested_id=None)[source]
+

Fast access to ‘CreateOperation’.

+
+
Parameters:
+
    +
  • project_name (str) – On which project the creation happens.

  • +
  • entity_type (str) – Which entity type will be created.

  • +
  • data (Dicst[str, Any]) – Entity data.

  • +
  • nested_id (str) – Id of other operation from which is triggered +operation -> Operations can trigger suboperations but they +must be added to operations list after it’s parent is added.

  • +
+
+
Returns:
+

Object of create operation.

+
+
Return type:
+

CreateOperation

+
+
+
+ +
+
+create_folder(project_name, name, folder_type=None, parent_id=None, label=None, attrib=None, data=None, tags=None, status=None, active=None, thumbnail_id=None, folder_id=None)[source]
+

Create new folder.

+
+
Parameters:
+
    +
  • project_name (str) – Project name.

  • +
  • name (str) – Folder name.

  • +
  • folder_type (Optional[str]) – Folder type.

  • +
  • parent_id (Optional[str]) – Parent folder id. Parent is project +if is None.

  • +
  • label (Optional[str]) – Label of folder.

  • +
  • attrib (Optional[dict[str, Any]]) – Folder attributes.

  • +
  • data (Optional[dict[str, Any]]) – Folder data.

  • +
  • tags (Optional[Iterable[str]]) – Folder tags.

  • +
  • status (Optional[str]) – Folder status.

  • +
  • active (Optional[bool]) – Folder active state.

  • +
  • thumbnail_id (Optional[str]) – Folder thumbnail id.

  • +
  • folder_id (Optional[str]) – Folder id. If not passed new id is +generated.

  • +
+
+
Returns:
+

Object of create operation.

+
+
Return type:
+

CreateOperation

+
+
+
+ +
+
+create_product(project_name, name, product_type, folder_id, attrib=None, data=None, tags=None, status=None, active=None, product_id=None)[source]
+

Create new product.

+
+
Parameters:
+
    +
  • project_name (str) – Project name.

  • +
  • name (str) – Product name.

  • +
  • product_type (str) – Product type.

  • +
  • folder_id (str) – Parent folder id.

  • +
  • attrib (Optional[dict[str, Any]]) – Product attributes.

  • +
  • data (Optional[dict[str, Any]]) – Product data.

  • +
  • tags (Optional[Iterable[str]]) – Product tags.

  • +
  • status (Optional[str]) – Product status.

  • +
  • active (Optional[bool]) – Product active state.

  • +
  • product_id (Optional[str]) – Product id. If not passed new id is +generated.

  • +
+
+
Returns:
+

Object of create operation.

+
+
Return type:
+

CreateOperation

+
+
+
+ +
+
+create_representation(project_name, name, version_id, files=None, attrib=None, data=None, tags=None, status=None, active=None, representation_id=None)[source]
+

Create new representation.

+
+
Parameters:
+
    +
  • project_name (str) – Project name.

  • +
  • name (str) – Representation name.

  • +
  • version_id (str) – Parent version id.

  • +
  • files (Optional[list[dict]]) – Representation files information.

  • +
  • attrib (Optional[dict[str, Any]]) – Representation attributes.

  • +
  • data (Optional[dict[str, Any]]) – Representation data.

  • +
  • tags (Optional[Iterable[str]]) – Representation tags.

  • +
  • status (Optional[str]) – Representation status.

  • +
  • active (Optional[bool]) – Representation active state.

  • +
  • representation_id (Optional[str]) – Representation id. If not +passed new id is generated.

  • +
+
+
Returns:
+

Object of create operation.

+
+
Return type:
+

CreateOperation

+
+
+
+ +
+
+create_task(project_name, name, task_type, folder_id, label=None, assignees=None, attrib=None, data=None, tags=None, status=None, active=None, thumbnail_id=None, task_id=None)[source]
+

Create new task.

+
+
Parameters:
+
    +
  • project_name (str) – Project name.

  • +
  • name (str) – Folder name.

  • +
  • task_type (str) – Task type.

  • +
  • folder_id (str) – Parent folder id.

  • +
  • label (Optional[str]) – Label of folder.

  • +
  • assignees (Optional[Iterable[str]]) – Task assignees.

  • +
  • attrib (Optional[dict[str, Any]]) – Task attributes.

  • +
  • data (Optional[dict[str, Any]]) – Task data.

  • +
  • tags (Optional[Iterable[str]]) – Task tags.

  • +
  • status (Optional[str]) – Task status.

  • +
  • active (Optional[bool]) – Task active state.

  • +
  • thumbnail_id (Optional[str]) – Task thumbnail id.

  • +
  • task_id (Optional[str]) – Task id. If not passed new id is +generated.

  • +
+
+
Returns:
+

Object of create operation.

+
+
Return type:
+

CreateOperation

+
+
+
+ +
+
+create_version(project_name, version, product_id, task_id=None, author=None, attrib=None, data=None, tags=None, status=None, active=None, thumbnail_id=None, version_id=None)[source]
+

Create new version.

+
+
Parameters:
+
    +
  • project_name (str) – Project name.

  • +
  • version (int) – Version.

  • +
  • product_id (str) – Parent product id.

  • +
  • task_id (Optional[str]) – Parent task id.

  • +
  • author (Optional[str]) – Version author.

  • +
  • attrib (Optional[dict[str, Any]]) – Version attributes.

  • +
  • data (Optional[dict[str, Any]]) – Version data.

  • +
  • tags (Optional[Iterable[str]]) – Version tags.

  • +
  • status (Optional[str]) – Version status.

  • +
  • active (Optional[bool]) – Version active state.

  • +
  • thumbnail_id (Optional[str]) – Version thumbnail id.

  • +
  • version_id (Optional[str]) – Version id. If not passed new id is +generated.

  • +
+
+
Returns:
+

Object of create operation.

+
+
Return type:
+

CreateOperation

+
+
+
+ +
+
+delete_entity(project_name, entity_type, entity_id, nested_id=None)[source]
+

Fast access to ‘DeleteOperation’.

+
+
Returns:
+

Object of delete operation.

+
+
Return type:
+

DeleteOperation

+
+
+
+ +
+
+delete_folder(project_name, folder_id)[source]
+

Delete folder.

+
+
Parameters:
+
    +
  • project_name (str) – Project name.

  • +
  • folder_id (str) – Folder id to delete.

  • +
+
+
Returns:
+

Object of delete operation.

+
+
Return type:
+

DeleteOperation

+
+
+
+ +
+
+delete_product(project_name, product_id)[source]
+

Delete product.

+
+
Parameters:
+
    +
  • project_name (str) – Project name.

  • +
  • product_id (str) – Product id to delete.

  • +
+
+
Returns:
+

Object of delete operation.

+
+
Return type:
+

DeleteOperation

+
+
+
+ +
+
+delete_representation(project_name, representation_id)[source]
+

Delete representation.

+
+
Parameters:
+
    +
  • project_name (str) – Project name.

  • +
  • representation_id (str) – Representation id to delete.

  • +
+
+
Returns:
+

Object of delete operation.

+
+
Return type:
+

DeleteOperation

+
+
+
+ +
+
+delete_task(project_name, task_id)[source]
+

Delete task.

+
+
Parameters:
+
    +
  • project_name (str) – Project name.

  • +
  • task_id (str) – Task id to delete.

  • +
+
+
Returns:
+

Object of delete operation.

+
+
Return type:
+

DeleteOperation

+
+
+
+ +
+
+delete_version(project_name, version_id)[source]
+

Delete version.

+
+
Parameters:
+
    +
  • project_name (str) – Project name.

  • +
  • version_id (str) – Version id to delete.

  • +
+
+
Returns:
+

Object of delete operation.

+
+
Return type:
+

DeleteOperation

+
+
+
+ +
+
+extend(operations)[source]
+

Add operations to be processed.

+
+
Parameters:
+

operations (List[BaseOperation]) – Operations that should be +processed.

+
+
+
+ +
+
+get_project(project_name)[source]
+
+ +
+
+remove(operation)[source]
+

Remove operation.

+
+ +
+
+to_data()[source]
+
+ +
+
+update_entity(project_name, entity_type, entity_id, update_data, nested_id=None)[source]
+

Fast access to ‘UpdateOperation’.

+
+
Returns:
+

Object of update operation.

+
+
Return type:
+

UpdateOperation

+
+
+
+ +
+
+update_folder(project_name, folder_id, name=None, folder_type=None, parent_id=<object object>, label=<object object>, attrib=None, data=None, tags=None, status=None, active=None, thumbnail_id=<object object>)[source]
+

Update folder entity on server.

+
+
Do not pass parent_id, label amd thumbnail_id if you don’t

want to change their values. Value None would unset +their value.

+
+
+

Update of data will override existing value on folder entity.

+
+
Update of attrib does change only passed attributes. If you want

to unset value, use None.

+
+
+
+
Parameters:
+
    +
  • project_name (str) – Project name.

  • +
  • folder_id (str) – Folder id.

  • +
  • name (Optional[str]) – New name.

  • +
  • folder_type (Optional[str]) – New folder type.

  • +
  • parent_id (Optional[Union[str, None]]) – New parent folder id.

  • +
  • label (Optional[Union[str, None]]) – New label.

  • +
  • attrib (Optional[dict[str, Any]]) – New attributes.

  • +
  • data (Optional[dict[str, Any]]) – New data.

  • +
  • tags (Optional[Iterable[str]]) – New tags.

  • +
  • status (Optional[str]) – New status.

  • +
  • active (Optional[bool]) – New active state.

  • +
  • thumbnail_id (Optional[Union[str, None]]) – New thumbnail id.

  • +
+
+
Returns:
+

Object of update operation.

+
+
Return type:
+

UpdateOperation

+
+
+
+ +
+
+update_product(project_name, product_id, name=None, folder_id=None, product_type=None, attrib=None, data=None, tags=None, status=None, active=None)[source]
+

Update product entity on server.

+

Update of data will override existing value on folder entity.

+
+
Update of attrib does change only passed attributes. If you want

to unset value, use None.

+
+
+
+
Parameters:
+
    +
  • project_name (str) – Project name.

  • +
  • product_id (str) – Product id.

  • +
  • name (Optional[str]) – New product name.

  • +
  • folder_id (Optional[str]) – New product id.

  • +
  • product_type (Optional[str]) – New product type.

  • +
  • attrib (Optional[dict[str, Any]]) – New product attributes.

  • +
  • data (Optional[dict[str, Any]]) – New product data.

  • +
  • tags (Optional[Iterable[str]]) – New product tags.

  • +
  • status (Optional[str]) – New product status.

  • +
  • active (Optional[bool]) – New product active state.

  • +
+
+
Returns:
+

Object of update operation.

+
+
Return type:
+

UpdateOperation

+
+
+
+ +
+
+update_representation(project_name, representation_id, name=None, version_id=None, files=None, attrib=None, data=None, tags=None, status=None, active=None)[source]
+

Update representation entity on server.

+

Update of data will override existing value on folder entity.

+
+
Update of attrib does change only passed attributes. If you want

to unset value, use None.

+
+
+
+
Parameters:
+
    +
  • project_name (str) – Project name.

  • +
  • representation_id (str) – Representation id.

  • +
  • name (Optional[str]) – New name.

  • +
  • version_id (Optional[str]) – New version id.

  • +
  • files (Optional[list[dict]]) – New files +information.

  • +
  • attrib (Optional[dict[str, Any]]) – New attributes.

  • +
  • data (Optional[dict[str, Any]]) – New data.

  • +
  • tags (Optional[Iterable[str]]) – New tags.

  • +
  • status (Optional[str]) – New status.

  • +
  • active (Optional[bool]) – New active state.

  • +
+
+
Returns:
+

Object of update operation.

+
+
Return type:
+

UpdateOperation

+
+
+
+ +
+
+update_task(project_name, task_id, name=None, task_type=None, folder_id=None, label=<object object>, assignees=None, attrib=None, data=None, tags=None, status=None, active=None, thumbnail_id=<object object>)[source]
+

Update task entity on server.

+
+
Do not pass label amd thumbnail_id if you don’t

want to change their values. Value None would unset +their value.

+
+
+

Update of data will override existing value on folder entity.

+
+
Update of attrib does change only passed attributes. If you want

to unset value, use None.

+
+
+
+
Parameters:
+
    +
  • project_name (str) – Project name.

  • +
  • task_id (str) – Task id.

  • +
  • name (Optional[str]) – New name.

  • +
  • task_type (Optional[str]) – New task type.

  • +
  • folder_id (Optional[str]) – New folder id.

  • +
  • label (Optional[Union[str, None]]) – New label.

  • +
  • assignees (Optional[str]) – New assignees.

  • +
  • attrib (Optional[dict[str, Any]]) – New attributes.

  • +
  • data (Optional[dict[str, Any]]) – New data.

  • +
  • tags (Optional[Iterable[str]]) – New tags.

  • +
  • status (Optional[str]) – New status.

  • +
  • active (Optional[bool]) – New active state.

  • +
  • thumbnail_id (Optional[Union[str, None]]) – New thumbnail id.

  • +
+
+
Returns:
+

Object of update operation.

+
+
Return type:
+

UpdateOperation

+
+
+
+ +
+
+update_version(project_name, version_id, version=None, product_id=None, task_id=<object object>, attrib=None, data=None, tags=None, status=None, active=None, thumbnail_id=<object object>)[source]
+

Update version entity on server.

+
+
Do not pass task_id amd thumbnail_id if you don’t

want to change their values. Value None would unset +their value.

+
+
+

Update of data will override existing value on folder entity.

+
+
Update of attrib does change only passed attributes. If you want

to unset value, use None.

+
+
+
+
Parameters:
+
    +
  • project_name (str) – Project name.

  • +
  • version_id (str) – Version id.

  • +
  • version (Optional[int]) – New version.

  • +
  • product_id (Optional[str]) – New product id.

  • +
  • task_id (Optional[Union[str, None]]) – New task id.

  • +
  • attrib (Optional[dict[str, Any]]) – New attributes.

  • +
  • data (Optional[dict[str, Any]]) – New data.

  • +
  • tags (Optional[Iterable[str]]) – New tags.

  • +
  • status (Optional[str]) – New status.

  • +
  • active (Optional[bool]) – New active state.

  • +
  • thumbnail_id (Optional[Union[str, None]]) – New thumbnail id.

  • +
+
+
Returns:
+

Object of update operation.

+
+
Return type:
+

UpdateOperation

+
+
+
+ +
+ +
+
+class UpdateOperation(project_name, entity_type, entity_id, update_data, session)[source]
+

Bases: AbstractOperation

+

Operation to update an entity.

+
+
Parameters:
+
    +
  • project_name (str) – On which project operation will happen.

  • +
  • entity_type (str) – Type of entity on which change happens. +e.g. ‘folder’, ‘representation’ etc.

  • +
  • entity_id (str) – Identifier of an entity.

  • +
  • update_data (Dict[str, Any]) – Key -> value changes that will be set in +database. If value is set to ‘REMOVED_VALUE’ the key will be +removed. Only first level of dictionary is checked (on purpose).

  • +
+
+
+
+
+property con
+
+ +
+
+property entity_id
+
+ +
+
+operation_name = 'update'
+
+ +
+
+property session
+
+ +
+
+to_data()[source]
+

Convert opration to data that can be converted to json or others.

+
+
Returns:
+

Description of operation.

+
+
Return type:
+

Dict[str, Any]

+
+
+
+ +
+
+to_server_operation()[source]
+
+ +
+
+property update_data
+
+ +
+ +
+
+new_folder_entity(name, folder_type, parent_id=None, status=None, tags=None, attribs=None, data=None, thumbnail_id=None, entity_id=None)[source]
+

Create skeleton data of folder entity.

+
+
Parameters:
+
    +
  • name (str) – Is considered as unique identifier of folder in project.

  • +
  • folder_type (str) – Type of folder.

  • +
  • parent_id (Optional[str]) – Parent folder id.

  • +
  • status (Optional[str]) – Product status.

  • +
  • tags (Optional[List[str]]) – List of tags.

  • +
  • attribs (Optional[Dict[str, Any]]) – Explicitly set attributes +of folder.

  • +
  • data (Optional[Dict[str, Any]]) – Custom folder data. Empty dictionary +is used if not passed.

  • +
  • thumbnail_id (Optional[str]) – Thumbnail id related to folder.

  • +
  • entity_id (Optional[str]) – Predefined id of entity. New id is +created if not passed.

  • +
+
+
Returns:
+

Skeleton of folder entity.

+
+
Return type:
+

Dict[str, Any]

+
+
+
+ +
+
+new_hero_version_entity(version, product_id, task_id=None, thumbnail_id=None, author=None, status=None, tags=None, attribs=None, data=None, entity_id=None)[source]
+

Create skeleton data of hero version entity.

+
+
Parameters:
+
    +
  • version (int) – Is considered as unique identifier of version +under product. Should be same as standard version if there is any.

  • +
  • product_id (str) – Parent product id.

  • +
  • task_id (Optional[str]) – Task id under which product was created.

  • +
  • thumbnail_id (Optional[str]) – Thumbnail related to version.

  • +
  • author (Optional[str]) – Name of version author.

  • +
  • status (Optional[str]) – Version status.

  • +
  • tags (Optional[List[str]]) – List of tags.

  • +
  • attribs (Optional[Dict[str, Any]]) – Explicitly set attributes +of version.

  • +
  • data (Optional[Dict[str, Any]]) – Version entity data.

  • +
  • entity_id (Optional[str]) – Predefined id of entity. New id is +created if not passed.

  • +
+
+
Returns:
+

Skeleton of version entity.

+
+
Return type:
+

Dict[str, Any]

+
+
+
+ +
+
+new_product_entity(name, product_type, folder_id, status=None, tags=None, attribs=None, data=None, entity_id=None)[source]
+

Create skeleton data of product entity.

+
+
Parameters:
+
    +
  • name (str) – Is considered as unique identifier of +product under folder.

  • +
  • product_type (str) – Product type.

  • +
  • folder_id (str) – Parent folder id.

  • +
  • status (Optional[str]) – Product status.

  • +
  • tags (Optional[List[str]]) – List of tags.

  • +
  • attribs (Optional[Dict[str, Any]]) – Explicitly set attributes +of product.

  • +
  • data (Optional[Dict[str, Any]]) – product entity data. Empty dictionary +is used if not passed.

  • +
  • entity_id (Optional[str]) – Predefined id of entity. New id is +created if not passed.

  • +
+
+
Returns:
+

Skeleton of product entity.

+
+
Return type:
+

Dict[str, Any]

+
+
+
+ +
+
+new_representation_entity(name, version_id, files, status=None, tags=None, attribs=None, data=None, entity_id=None)[source]
+

Create skeleton data of representation entity.

+
+
Parameters:
+
    +
  • name (str) – Representation name considered as unique identifier +of representation under version.

  • +
  • version_id (str) – Parent version id.

  • +
  • files (list[dict[str, str]]) – List of files in representation.

  • +
  • status (Optional[str]) – Representation status.

  • +
  • tags (Optional[List[str]]) – List of tags.

  • +
  • attribs (Optional[Dict[str, Any]]) – Explicitly set attributes +of representation.

  • +
  • data (Optional[Dict[str, Any]]) – Representation entity data.

  • +
  • entity_id (Optional[str]) – Predefined id of entity. New id is created +if not passed.

  • +
+
+
Returns:
+

Skeleton of representation entity.

+
+
Return type:
+

Dict[str, Any]

+
+
+
+ +
+
+new_version_entity(version, product_id, task_id=None, thumbnail_id=None, author=None, status=None, tags=None, attribs=None, data=None, entity_id=None)[source]
+

Create skeleton data of version entity.

+
+
Parameters:
+
    +
  • version (int) – Is considered as unique identifier of version +under product.

  • +
  • product_id (str) – Parent product id.

  • +
  • task_id (Optional[str]) – Task id under which product was created.

  • +
  • thumbnail_id (Optional[str]) – Thumbnail related to version.

  • +
  • author (Optional[str]) – Name of version author.

  • +
  • status (Optional[str]) – Version status.

  • +
  • tags (Optional[List[str]]) – List of tags.

  • +
  • attribs (Optional[Dict[str, Any]]) – Explicitly set attributes +of version.

  • +
  • data (Optional[Dict[str, Any]]) – Version entity custom data.

  • +
  • entity_id (Optional[str]) – Predefined id of entity. New id is +created if not passed.

  • +
+
+
Returns:
+

Skeleton of version entity.

+
+
Return type:
+

Dict[str, Any]

+
+
+
+ +
+
+new_workfile_info(filepath, task_id, status=None, tags=None, attribs=None, description=None, data=None, entity_id=None)[source]
+

Create skeleton data of workfile info entity.

+

Workfile entity is at this moment used primarily for artist notes.

+
+
Parameters:
+
    +
  • filepath (str) – Rootless workfile filepath.

  • +
  • task_id (str) – Task under which was workfile created.

  • +
  • status (Optional[str]) – Workfile status.

  • +
  • tags (Optional[List[str]]) – Workfile tags.

  • +
  • attribs (Options[dic[str, Any]]) – Explicitly set attributes.

  • +
  • description (Optional[str]) – Workfile description.

  • +
  • data (Optional[Dict[str, Any]]) – Additional metadata.

  • +
  • entity_id (Optional[str]) – Predefined id of entity. New id is created +if not passed.

  • +
+
+
Returns:
+

Skeleton of workfile info entity.

+
+
Return type:
+

Dict[str, Any]

+
+
+
+ +
+
+prepare_changes(old_entity, new_entity, entity_type)[source]
+

Prepare changes for entity update.

+
+

Notes

+
+
Argument ‘entity_type’ is not used, yet. But there might be

differences in future.

+
+
+
+
+
Parameters:
+
    +
  • old_entity (dict[str, Any]) – Existing entity.

  • +
  • new_entity (dict[str, Any]) – New entity.

  • +
  • entity_type (str) – Entity type. “project”, “folder”, “product” etc.

  • +
+
+
Returns:
+

Changes that have new entity.

+
+
Return type:
+

dict[str, Any]

+
+
+
+ +
+ + +
+ +
+ + +
+
+ +
+ +
+ + + + + + + + + + + + \ No newline at end of file diff --git a/ayon_api.server_api.html b/ayon_api.server_api.html new file mode 100644 index 0000000000..894d8c06c4 --- /dev/null +++ b/ayon_api.server_api.html @@ -0,0 +1,6064 @@ + + + + + + + + + + + + ayon_api.server_api module — ayon-python-api 1.0.11 documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + +
+ + + + + +
+ +
+ +
+ + + +
+
+
+ +
+

ayon_api.server_api module

+

Server API.

+

Provides access to server API.

+
+
+class GraphQlResponse(data)[source]
+

Bases: object

+

GraphQl response.

+
+ +
+
+class RequestType(name)[source]
+

Bases: object

+
+ +
+
+class RequestTypes[source]
+

Bases: object

+
+
+delete = <ayon_api.server_api.RequestType object>
+
+ +
+
+get = <ayon_api.server_api.RequestType object>
+
+ +
+
+patch = <ayon_api.server_api.RequestType object>
+
+ +
+
+post = <ayon_api.server_api.RequestType object>
+
+ +
+
+put = <ayon_api.server_api.RequestType object>
+
+ +
+ +
+
+class RestApiResponse(response, data=None)[source]
+

Bases: object

+

API Response.

+
+
+property content
+
+ +
+
+property content_type
+
+ +
+
+property data
+
+ +
+
+property detail
+
+ +
+
+get(key, default=None)[source]
+
+ +
+
+property headers
+
+ +
+
+property orig_response
+
+ +
+
+raise_for_status(message=None)[source]
+
+ +
+
+property status_code
+
+ +
+
+property text
+
+ +
+ +
+
+class ServerAPI(base_url, token=None, site_id=<object object>, client_version=None, default_settings_variant=None, sender_type=None, sender=None, ssl_verify=None, cert=None, create_session=True, timeout=None, max_retries=None)[source]
+

Bases: object

+

Base handler of connection to server.

+

Requires url to server which is used as base for api and graphql calls.

+

Login cause that a session is used

+
+
Parameters:
+
    +
  • base_url (str) – Example: http://localhost:5000

  • +
  • token (Optional[str]) – Access token (api key) to server.

  • +
  • site_id (Optional[str]) – Unique name of site. Should be the same when +connection is created from the same machine under same user.

  • +
  • client_version (Optional[str]) – Version of client application (used in +desktop client application).

  • +
  • default_settings_variant (Optional[Literal["production", "staging"]]) – Settings variant used by default if a method for settings won’t +get any (by default is ‘production’).

  • +
  • sender_type (Optional[str]) – Sender type of requests. Used in server +logs and propagated into events.

  • +
  • sender (Optional[str]) – Sender of requests, more specific than +sender type (e.g. machine name). Used in server logs and +propagated into events.

  • +
  • ssl_verify (Union[bool, str, None]) – Verify SSL certificate +Looks for env variable value AYON_CA_FILE by default. If not +available then ‘True’ is used.

  • +
  • cert (Optional[str]) – Path to certificate file. Looks for env +variable value AYON_CERT_FILE by default.

  • +
  • create_session (Optional[bool]) – Create session for connection if +token is available. Default is True.

  • +
  • timeout (Optional[float]) – Timeout for requests.

  • +
  • max_retries (Optional[int]) – Number of retries for requests.

  • +
+
+
+
+
+property access_token
+

Access token used for authorization to server.

+
+
Returns:
+

Token string or None if not authorized yet.

+
+
Return type:
+

Union[str, None]

+
+
+
+ +
+
+as_username(username, ignore_service_error=False)[source]
+

Service API will temporarily work as other user.

+

This method can be used only if service API key is logged in.

+
+
Parameters:
+
    +
  • username (Union[str, None]) – Username to work as when service.

  • +
  • ignore_service_error (Optional[bool]) – Ignore error when service +API key is not used.

  • +
+
+
Raises:
+

ValueError – When connection is not yet authenticated or api key + is not service token.

+
+
+
+ +
+
+property base_url
+
+ +
+
+property cert
+

Current cert file used for connection to server.

+
+
Returns:
+

Path to cert file.

+
+
Return type:
+

Union[str, None]

+
+
+
+ +
+
+check_bundle_compatibility(name, addon_versions, installer_version, dependency_packages=None, is_production=None, is_staging=None, is_dev=None, dev_active_user=None, dev_addons_config=None)[source]
+

Check bundle compatibility.

+

Can be used as per-flight validation before creating bundle.

+
+
Parameters:
+
    +
  • name (str) – Name of bundle.

  • +
  • addon_versions (dict[str, str]) – Addon versions.

  • +
  • installer_version (Union[str, None]) – Installer version.

  • +
  • dependency_packages (Optional[dict[str, str]]) – Dependency +package names. Keys are platform names and values are name of +packages.

  • +
  • is_production (Optional[bool]) – Bundle will be marked as +production.

  • +
  • is_staging (Optional[bool]) – Bundle will be marked as staging.

  • +
  • is_dev (Optional[bool]) – Bundle will be marked as dev.

  • +
  • dev_active_user (Optional[str]) – Username that will be assigned +to dev bundle. Can be used only if ‘is_dev’ is set to ‘True’.

  • +
  • dev_addons_config (Optional[dict[str, Any]]) – Configuration for +dev addons. Can be used only if ‘is_dev’ is set to ‘True’.

  • +
+
+
Returns:
+

Server response, with ‘success’ and ‘issues’.

+
+
Return type:
+

Dict[str, Any]

+
+
+
+ +
+
+property client_version
+

Version of client used to connect to server.

+

Client version is AYON client build desktop application.

+
+
Returns:
+

Client version string used in connection.

+
+
Return type:
+

str

+
+
+
+ +
+
+close_session()[source]
+
+ +
+
+create_activity(project_name: str, entity_id: str, entity_type: str, activity_type: ActivityType, activity_id: Optional[str] = None, body: Optional[str] = None, file_ids: Optional[List[str]] = None, timestamp: Optional[str] = None, data: Optional[Dict[str, Any]] = None) str[source]
+

Create activity on a project.

+
+
Parameters:
+
    +
  • project_name (str) – Project on which activity happened.

  • +
  • entity_id (str) – Entity id.

  • +
  • entity_type (str) – Entity type.

  • +
  • activity_type (ActivityType) – Activity type.

  • +
  • activity_id (Optional[str]) – Activity id.

  • +
  • body (Optional[str]) – Activity body.

  • +
  • file_ids (Optional[List[str]]) – List of file ids attached +to activity.

  • +
  • timestamp (Optional[str]) – Activity timestamp.

  • +
  • data (Optional[Dict[str, Any]]) – Additional data.

  • +
+
+
Returns:
+

Activity id.

+
+
Return type:
+

str

+
+
+
+ +
+
+create_bundle(name, addon_versions, installer_version, dependency_packages=None, is_production=None, is_staging=None, is_dev=None, dev_active_user=None, dev_addons_config=None)[source]
+

Create bundle on server.

+

Bundle cannot be changed once is created. Only isProduction, isStaging +and dependency packages can change after creation. In case dev bundle +is created, it is possible to change anything, but it is not possible +to mark bundle as dev and production or staging at the same time.

+

Development addon config can define custom path to client code. It is +used only for dev bundles.

+

Example of ‘dev_addons_config’:

+
```json
+{
+    "core": {
+        "enabled": true,
+        "path": "/path/to/ayon-core/client"
+    }
+}
+```
+
+
+
+
Parameters:
+
    +
  • name (str) – Name of bundle.

  • +
  • addon_versions (dict[str, str]) – Addon versions.

  • +
  • installer_version (Union[str, None]) – Installer version.

  • +
  • dependency_packages (Optional[dict[str, str]]) – Dependency +package names. Keys are platform names and values are name of +packages.

  • +
  • is_production (Optional[bool]) – Bundle will be marked as +production.

  • +
  • is_staging (Optional[bool]) – Bundle will be marked as staging.

  • +
  • is_dev (Optional[bool]) – Bundle will be marked as dev.

  • +
  • dev_active_user (Optional[str]) – Username that will be assigned +to dev bundle. Can be used only if ‘is_dev’ is set to ‘True’.

  • +
  • dev_addons_config (Optional[dict[str, Any]]) – Configuration for +dev addons. Can be used only if ‘is_dev’ is set to ‘True’.

  • +
+
+
+
+ +
+
+create_dependency_package(filename, python_modules, source_addons, installer_version, checksum, checksum_algorithm, file_size, sources=None, platform_name=None)[source]
+

Create dependency package on server.

+

The package will be created on a server, it is also required to upload +the package archive file (using upload_dependency_package()).

+
+
Parameters:
+
    +
  • filename (str) – Filename of dependency package.

  • +
  • python_modules (dict[str, str]) –

    Python modules in dependency +package:

    +
    {"<module name>": "<module version>", ...}
    +
    +
    +

  • +
  • source_addons (dict[str, str]) –

    Name of addons for which is +dependency package created:

    +
    {"<addon name>": "<addon version>", ...}
    +
    +
    +

  • +
  • installer_version (str) – Version of installer for which was +package created.

  • +
  • checksum (str) – Checksum of archive file where dependencies are.

  • +
  • checksum_algorithm (str) – Algorithm used to calculate checksum.

  • +
  • file_size (Optional[int]) – Size of file.

  • +
  • sources (Optional[list[dict[str, Any]]]) – Information about +sources from where it is possible to get file.

  • +
  • platform_name (Optional[str]) – Name of platform for which is +dependency package targeted. Default value is +current platform.

  • +
+
+
+
+ +
+
+create_folder(project_name, name, folder_type=None, parent_id=None, label=None, attrib=None, data=None, tags=None, status=None, active=None, thumbnail_id=None, folder_id=None)[source]
+

Create new folder.

+
+
Parameters:
+
    +
  • project_name (str) – Project name.

  • +
  • name (str) – Folder name.

  • +
  • folder_type (Optional[str]) – Folder type.

  • +
  • parent_id (Optional[str]) – Parent folder id. Parent is project +if is None.

  • +
  • label (Optional[str]) – Label of folder.

  • +
  • attrib (Optional[dict[str, Any]]) – Folder attributes.

  • +
  • data (Optional[dict[str, Any]]) – Folder data.

  • +
  • tags (Optional[Iterable[str]]) – Folder tags.

  • +
  • status (Optional[str]) – Folder status.

  • +
  • active (Optional[bool]) – Folder active state.

  • +
  • thumbnail_id (Optional[str]) – Folder thumbnail id.

  • +
  • folder_id (Optional[str]) – Folder id. If not passed new id is +generated.

  • +
+
+
Returns:
+

Entity id.

+
+
Return type:
+

str

+
+
+
+ +
+
+create_installer(filename, version, python_version, platform_name, python_modules, runtime_python_modules, checksum, checksum_algorithm, file_size, sources=None)[source]
+

Create new installer information on server.

+
+
This step will create only metadata. Make sure to upload installer

to the server using ‘upload_installer’ method.

+
+
Runtime python modules are modules that are required to run AYON

desktop application, but are not added to PYTHONPATH for any +subprocess.

+
+
+
+
Parameters:
+
    +
  • filename (str) – Installer filename.

  • +
  • version (str) – Version of installer.

  • +
  • python_version (str) – Version of Python.

  • +
  • platform_name (str) – Name of platform.

  • +
  • python_modules (dict[str, str]) – Python modules that are available +in installer.

  • +
  • runtime_python_modules (dict[str, str]) – Runtime python modules +that are available in installer.

  • +
  • checksum (str) – Installer file checksum.

  • +
  • checksum_algorithm (str) – Type of checksum used to create checksum.

  • +
  • file_size (int) – File size.

  • +
  • sources (Optional[list[dict[str, Any]]]) – List of sources that +can be used to download file.

  • +
+
+
+
+ +
+ +

Create link between 2 entities.

+

Link has a type which must already exists on a project.

+

Example output:

+
{
+    "id": "59a212c0d2e211eda0e20242ac120002"
+}
+
+
+
+
Parameters:
+
    +
  • project_name (str) – Project where the link is created.

  • +
  • link_type_name (str) – Type of link.

  • +
  • input_id (str) – Input entity id.

  • +
  • input_type (str) – Entity type of input entity.

  • +
  • output_id (str) – Output entity id.

  • +
  • output_type (str) – Entity type of output entity.

  • +
  • link_name (Optional[str]) – Name of link. +Available from server version ‘1.0.0-rc.6’.

  • +
+
+
Returns:
+

Information about link.

+
+
Return type:
+

dict[str, str]

+
+
Raises:
+

HTTPRequestError – Server error happened.

+
+
+
+ +
+ +

Create or update link type on server.

+
+

Warning

+

Because PUT is used for creation it is also used for update.

+
+
+
Parameters:
+
    +
  • project_name (str) – Project where link type is created.

  • +
  • link_type_name (str) – Name of link type.

  • +
  • input_type (str) – Input entity type of link.

  • +
  • output_type (str) – Output entity type of link.

  • +
  • data (Optional[dict[str, Any]]) – Additional data related to link.

  • +
+
+
Raises:
+

HTTPRequestError – Server error happened.

+
+
+
+ +
+
+create_product(project_name, name, product_type, folder_id, attrib=None, data=None, tags=None, status=None, active=None, product_id=None)[source]
+

Create new product.

+
+
Parameters:
+
    +
  • project_name (str) – Project name.

  • +
  • name (str) – Product name.

  • +
  • product_type (str) – Product type.

  • +
  • folder_id (str) – Parent folder id.

  • +
  • attrib (Optional[dict[str, Any]]) – Product attributes.

  • +
  • data (Optional[dict[str, Any]]) – Product data.

  • +
  • tags (Optional[Iterable[str]]) – Product tags.

  • +
  • status (Optional[str]) – Product status.

  • +
  • active (Optional[bool]) – Product active state.

  • +
  • product_id (Optional[str]) – Product id. If not passed new id is +generated.

  • +
+
+
Returns:
+

Product id.

+
+
Return type:
+

str

+
+
+
+ +
+
+create_project(project_name, project_code, library_project=False, preset_name=None)[source]
+

Create project using AYON settings.

+

This project creation function is not validating project entity on +creation. It is because project entity is created blindly with only +minimum required information about project which is name and code.

+

Entered project name must be unique and project must not exist yet.

+
+

Note

+
+
This function is here to be OP v4 ready but in v3 has more logic

to do. That’s why inner imports are in the body.

+
+
+
+
+
Parameters:
+
    +
  • project_name (str) – New project name. Should be unique.

  • +
  • project_code (str) – Project’s code should be unique too.

  • +
  • library_project (Optional[bool]) – Project is library project.

  • +
  • preset_name (Optional[str]) – Name of anatomy preset. Default is +used if not passed.

  • +
+
+
Raises:
+

ValueError – When project name already exists.

+
+
Returns:
+

Created project entity.

+
+
Return type:
+

dict[str, Any]

+
+
+
+ +
+
+create_representation(project_name, name, version_id, files=None, attrib=None, data=None, tags=None, status=None, active=None, representation_id=None)[source]
+

Create new representation.

+
+
Parameters:
+
    +
  • project_name (str) – Project name.

  • +
  • name (str) – Representation name.

  • +
  • version_id (str) – Parent version id.

  • +
  • files (Optional[list[dict]]) – Representation files information.

  • +
  • attrib (Optional[dict[str, Any]]) – Representation attributes.

  • +
  • data (Optional[dict[str, Any]]) – Representation data.

  • +
  • tags (Optional[Iterable[str]]) – Representation tags.

  • +
  • status (Optional[str]) – Representation status.

  • +
  • active (Optional[bool]) – Representation active state.

  • +
  • representation_id (Optional[str]) – Representation id. If not +passed new id is generated.

  • +
+
+
Returns:
+

Representation id.

+
+
Return type:
+

str

+
+
+
+ +
+
+create_session(ignore_existing=True, force=False)[source]
+

Create a connection session.

+

Session helps to keep connection with server without +need to reconnect on each call.

+
+
Parameters:
+
    +
  • ignore_existing (bool) – If session already exists, +ignore creation.

  • +
  • force (bool) – If session already exists, close it and +create new.

  • +
+
+
+
+ +
+
+create_task(project_name, name, task_type, folder_id, label=None, assignees=None, attrib=None, data=None, tags=None, status=None, active=None, thumbnail_id=None, task_id=None)[source]
+

Create new task.

+
+
Parameters:
+
    +
  • project_name (str) – Project name.

  • +
  • name (str) – Folder name.

  • +
  • task_type (str) – Task type.

  • +
  • folder_id (str) – Parent folder id.

  • +
  • label (Optional[str]) – Label of folder.

  • +
  • assignees (Optional[Iterable[str]]) – Task assignees.

  • +
  • attrib (Optional[dict[str, Any]]) – Task attributes.

  • +
  • data (Optional[dict[str, Any]]) – Task data.

  • +
  • tags (Optional[Iterable[str]]) – Task tags.

  • +
  • status (Optional[str]) – Task status.

  • +
  • active (Optional[bool]) – Task active state.

  • +
  • thumbnail_id (Optional[str]) – Task thumbnail id.

  • +
  • task_id (Optional[str]) – Task id. If not passed new id is +generated.

  • +
+
+
Returns:
+

Task id.

+
+
Return type:
+

str

+
+
+
+ +
+
+create_thumbnail(project_name, src_filepath, thumbnail_id=None)[source]
+

Create new thumbnail on server from passed path.

+
+
Parameters:
+
    +
  • project_name (str) – Project where the thumbnail will be created +and can be used.

  • +
  • src_filepath (str) – Filepath to thumbnail which should be uploaded.

  • +
  • thumbnail_id (Optional[str]) – Prepared if of thumbnail.

  • +
+
+
Returns:
+

Created thumbnail id.

+
+
Return type:
+

str

+
+
Raises:
+

ValueError – When thumbnail source cannot be processed.

+
+
+
+ +
+
+create_version(project_name, version, product_id, task_id=None, author=None, attrib=None, data=None, tags=None, status=None, active=None, thumbnail_id=None, version_id=None)[source]
+

Create new version.

+
+
Parameters:
+
    +
  • project_name (str) – Project name.

  • +
  • version (int) – Version.

  • +
  • product_id (str) – Parent product id.

  • +
  • task_id (Optional[str]) – Parent task id.

  • +
  • author (Optional[str]) – Version author.

  • +
  • attrib (Optional[dict[str, Any]]) – Version attributes.

  • +
  • data (Optional[dict[str, Any]]) – Version data.

  • +
  • tags (Optional[Iterable[str]]) – Version tags.

  • +
  • status (Optional[str]) – Version status.

  • +
  • active (Optional[bool]) – Version active state.

  • +
  • thumbnail_id (Optional[str]) – Version thumbnail id.

  • +
  • version_id (Optional[str]) – Version id. If not passed new id is +generated.

  • +
+
+
Returns:
+

Version id.

+
+
Return type:
+

str

+
+
+
+ +
+
+default_download_chunk_size = 1048576
+
+ +
+
+property default_settings_variant
+

Default variant used for settings.

+
+
Returns:
+

name of variant or None.

+
+
Return type:
+

Union[str, None]

+
+
+
+ +
+
+default_upload_chunk_size = 1048576
+
+ +
+
+delete(entrypoint, **kwargs)[source]
+
+ +
+
+delete_activity(project_name: str, activity_id: str)[source]
+

Delete activity by id.

+
+
Parameters:
+
    +
  • project_name (str) – Project on which activity happened.

  • +
  • activity_id (str) – Activity id to remove.

  • +
+
+
+
+ +
+
+delete_addon(addon_name: str, purge: Optional[bool] = None)[source]
+

Delete addon from server.

+

Delete all versions of addon from server.

+
+
Parameters:
+
    +
  • addon_name (str) – Addon name.

  • +
  • purge (Optional[bool]) – Purge all data related to the addon.

  • +
+
+
+
+ +
+
+delete_addon_version(addon_name: str, addon_version: str, purge: Optional[bool] = None)[source]
+

Delete addon version from server.

+

Delete all versions of addon from server.

+
+
Parameters:
+
    +
  • addon_name (str) – Addon name.

  • +
  • addon_version (str) – Addon version.

  • +
  • purge (Optional[bool]) – Purge all data related to the addon.

  • +
+
+
+
+ +
+
+delete_bundle(bundle_name)[source]
+

Delete bundle from server.

+
+
Parameters:
+

bundle_name (str) – Name of bundle to delete.

+
+
+
+ +
+
+delete_dependency_package(filename, platform_name=None)[source]
+

Remove dependency package for specific platform.

+
+
Parameters:
+
    +
  • filename (str) – Filename of dependency package.

  • +
  • platform_name (Optional[str]) – Deprecated.

  • +
+
+
+
+ +
+
+delete_event(event_id: str)[source]
+

Delete event by id.

+

Supported since AYON server 1.6.0.

+
+
Parameters:
+

event_id (str) – Event id.

+
+
Returns:
+

Response from server.

+
+
Return type:
+

RestApiResponse

+
+
+
+ +
+
+delete_folder(project_name, folder_id, force=False)[source]
+

Delete folder.

+
+
Parameters:
+
    +
  • project_name (str) – Project name.

  • +
  • folder_id (str) – Folder id to delete.

  • +
  • force (Optional[bool]) – Folder delete folder with all children +folder, products, versions and representations.

  • +
+
+
+
+ +
+
+delete_installer(filename)[source]
+

Delete installer from server.

+
+
Parameters:
+

filename (str) – Installer filename.

+
+
+
+ +
+ +

Remove link by id.

+
+
Parameters:
+
    +
  • project_name (str) – Project where link exists.

  • +
  • link_id (str) – Id of link.

  • +
+
+
Raises:
+

HTTPRequestError – Server error happened.

+
+
+
+ +
+ +

Remove link type from project.

+
+
Parameters:
+
    +
  • project_name (str) – Project where link type is created.

  • +
  • link_type_name (str) – Name of link type.

  • +
  • input_type (str) – Input entity type of link.

  • +
  • output_type (str) – Output entity type of link.

  • +
+
+
Raises:
+

HTTPRequestError – Server error happened.

+
+
+
+ +
+
+delete_product(project_name, product_id)[source]
+

Delete product.

+
+
Parameters:
+
    +
  • project_name (str) – Project name.

  • +
  • product_id (str) – Product id to delete.

  • +
+
+
+
+ +
+
+delete_project(project_name)[source]
+

Delete project from server.

+

This will completely remove project from server without any step back.

+
+
Parameters:
+

project_name (str) – Project name that will be removed.

+
+
+
+ +
+
+delete_representation(project_name, representation_id)[source]
+

Delete representation.

+
+
Parameters:
+
    +
  • project_name (str) – Project name.

  • +
  • representation_id (str) – Representation id to delete.

  • +
+
+
+
+ +
+
+delete_secret(secret_name)[source]
+

Delete secret by name.

+
+
Parameters:
+

secret_name (str) – Name of secret to delete.

+
+
+
+ +
+
+delete_task(project_name, task_id)[source]
+

Delete task.

+
+
Parameters:
+
    +
  • project_name (str) – Project name.

  • +
  • task_id (str) – Task id to delete.

  • +
+
+
+
+ +
+
+delete_version(project_name, version_id)[source]
+

Delete version.

+
+
Parameters:
+
    +
  • project_name (str) – Project name.

  • +
  • version_id (str) – Version id to delete.

  • +
+
+
+
+ +
+
+dispatch_event(topic, sender=None, event_hash=None, project_name=None, username=None, depends_on=None, description=None, summary=None, payload=None, finished=True, store=True, dependencies=None)[source]
+

Dispatch event to server.

+
+
Parameters:
+
    +
  • topic (str) – Event topic used for filtering of listeners.

  • +
  • sender (Optional[str]) – Sender of event.

  • +
  • event_hash (Optional[str]) – Event hash.

  • +
  • project_name (Optional[str]) – Project name.

  • +
  • depends_on (Optional[str]) – Add dependency to another event.

  • +
  • username (Optional[str]) – Username which triggered event.

  • +
  • description (Optional[str]) – Description of event.

  • +
  • summary (Optional[dict[str, Any]]) – Summary of event that can +be used for simple filtering on listeners.

  • +
  • payload (Optional[dict[str, Any]]) – Full payload of event data with +all details.

  • +
  • finished (Optional[bool]) – Mark event as finished on dispatch.

  • +
  • store (Optional[bool]) – Store event in event queue for possible +future processing otherwise is event send only +to active listeners.

  • +
  • dependencies (Optional[list[str]]) – Deprecated. +List of event id dependencies.

  • +
+
+
Returns:
+

Response from server.

+
+
Return type:
+

RestApiResponse

+
+
+
+ +
+
+download_addon_private_file(addon_name, addon_version, filename, destination_dir, destination_filename=None, chunk_size=None, progress=None)[source]
+

Download a file from addon private files.

+

This method requires to have authorized token available. Private files +are not under ‘/api’ restpoint.

+
+
Parameters:
+
    +
  • addon_name (str) – Addon name.

  • +
  • addon_version (str) – Addon version.

  • +
  • filename (str) – Filename in private folder on server.

  • +
  • destination_dir (str) – Where the file should be downloaded.

  • +
  • destination_filename (Optional[str]) – Name of destination +filename. Source filename is used if not passed.

  • +
  • chunk_size (Optional[int]) – Download chunk size.

  • +
  • progress (Optional[TransferProgress]) – Object that gives ability +to track download progress.

  • +
+
+
Returns:
+

Filepath to downloaded file.

+
+
Return type:
+

str

+
+
+
+ +
+
+download_dependency_package(src_filename, dst_directory, dst_filename, platform_name=None, chunk_size=None, progress=None)[source]
+

Download dependency package from server.

+

This method requires to have authorized token available. The package +is only downloaded.

+
+
Parameters:
+
    +
  • src_filename (str) – Filename of dependency pacakge. +For server version 0.2.0 and lower it is name of package +to download.

  • +
  • dst_directory (str) – Where the file should be downloaded.

  • +
  • dst_filename (str) – Name of destination filename.

  • +
  • platform_name (Optional[str]) – Deprecated.

  • +
  • chunk_size (Optional[int]) – Download chunk size.

  • +
  • progress (Optional[TransferProgress]) – Object that gives ability +to track download progress.

  • +
+
+
Returns:
+

Filepath to downloaded file.

+
+
Return type:
+

str

+
+
+
+ +
+
+download_file(endpoint, filepath, chunk_size=None, progress=None)[source]
+

Download file from AYON server.

+

Endpoint can be full url (must start with ‘base_url’ of api object).

+

Progress object can be used to track download. Can be used when +download happens in thread and other thread want to catch changes over +time.

+
+
Todos:

Use retries and timeout. +Return RestApiResponse.

+
+
+
+
Parameters:
+
    +
  • endpoint (str) – Endpoint or URL to file that should be downloaded.

  • +
  • filepath (str) – Path where file will be downloaded.

  • +
  • chunk_size (Optional[int]) – Size of chunks that are received +in single loop.

  • +
  • progress (Optional[TransferProgress]) – Object that gives ability +to track download progress.

  • +
+
+
+
+ +
+
+download_file_to_stream(endpoint, stream, chunk_size=None, progress=None)[source]
+

Download file from AYON server to IOStream.

+

Endpoint can be full url (must start with ‘base_url’ of api object).

+

Progress object can be used to track download. Can be used when +download happens in thread and other thread want to catch changes over +time.

+
+
Todos:

Use retries and timeout. +Return RestApiResponse.

+
+
+
+
Parameters:
+
    +
  • endpoint (str) – Endpoint or URL to file that should be downloaded.

  • +
  • stream (Union[io.BytesIO, BinaryIO]) – Stream where output will +be stored.

  • +
  • chunk_size (Optional[int]) – Size of chunks that are received +in single loop.

  • +
  • progress (Optional[TransferProgress]) – Object that gives ability +to track download progress.

  • +
+
+
+
+ +
+
+download_installer(filename, dst_filepath, chunk_size=None, progress=None)[source]
+

Download installer file from server.

+
+
Parameters:
+
    +
  • filename (str) – Installer filename.

  • +
  • dst_filepath (str) – Destination filepath.

  • +
  • chunk_size (Optional[int]) – Download chunk size.

  • +
  • progress (Optional[TransferProgress]) – Object that gives ability +to track download progress.

  • +
+
+
+
+ +
+
+enroll_event_job(source_topic, target_topic, sender, description=None, sequential=None, events_filter=None, max_retries=None, ignore_older_than=None, ignore_sender_types=None)[source]
+

Enroll job based on events.

+

Enroll will find first unprocessed event with ‘source_topic’ and will +create new event with ‘target_topic’ for it and return the new event +data.

+

Use ‘sequential’ to control that only single target event is created +at same time. Creation of new target events is blocked while there is +at least one unfinished event with target topic, when set to ‘True’. +This helps when order of events matter and more than one process using +the same target is running at the same time.

+

Make sure the new event has updated status to ‘“finished”’ status +when you’re done with logic

+

Target topic should not clash with other processes/services.

+

Created target event have ‘dependsOn’ key where is id of source topic.

+
+
Use-case:
    +
  • Service 1 is creating events with topic ‘my.leech’

  • +
  • +
    Service 2 process ‘my.leech’ and uses target topic ‘my.process’
      +
    • this service can run on 1-n machines

    • +
    • +
      all events must be processed in a sequence by their creation

      time and only one event can be processed at a time

      +
      +
      +
    • +
    • +
      in this case ‘sequential’ should be set to ‘True’ so only

      one machine is actually processing events, but if one goes +down there are other that can take place

      +
      +
      +
    • +
    +
    +
    +
  • +
  • +
    Service 3 process ‘my.leech’ and uses target topic ‘my.discover’
      +
    • this service can run on 1-n machines

    • +
    • order of events is not important

    • +
    • ‘sequential’ should be ‘False’

    • +
    +
    +
    +
  • +
+
+
+
+
Parameters:
+
    +
  • source_topic (str) – Source topic to enroll.

  • +
  • target_topic (str) – Topic of dependent event.

  • +
  • sender (str) – Identifier of sender (e.g. service name or username).

  • +
  • description (Optional[str]) – Human readable text shown +in target event.

  • +
  • sequential (Optional[bool]) – The source topic must be processed +in sequence.

  • +
  • events_filter (Optional[dict[str, Any]]) – Filtering conditions +to filter the source event. For more technical specifications +look to server backed ‘ayon_server.sqlfilter.Filter’. +TODO: Add example of filters.

  • +
  • max_retries (Optional[int]) – How many times can be event retried. +Default value is based on server (3 at the time of this PR).

  • +
  • ignore_older_than (Optional[int]) – Ignore events older than +given number in days.

  • +
  • ignore_sender_types (Optional[List[str]]) – Ignore events triggered +by given sender types.

  • +
+
+
Returns:
+

+
None if there is no event matching

filters. Created event with ‘target_topic’.

+
+
+

+
+
Return type:
+

Union[None, dict[str, Any]]

+
+
+
+ +
+
+get(entrypoint, **kwargs)[source]
+
+ +
+
+get_activities(project_name: str, activity_ids: Optional[Iterable[str]] = None, activity_types: Optional[Iterable[ActivityType]] = None, entity_ids: Optional[Iterable[str]] = None, entity_names: Optional[Iterable[str]] = None, entity_type: Optional[str] = None, changed_after: Optional[str] = None, changed_before: Optional[str] = None, reference_types: Optional[Iterable[ActivityReferenceType]] = None, fields: Optional[Iterable[str]] = None, limit: Optional[int] = None, order: Optional[SortOrder] = None) Generator[Dict[str, Any], None, None][source]
+

Get activities from server with filtering options.

+
+
Parameters:
+
    +
  • project_name (str) – Project on which activities happened.

  • +
  • activity_ids (Optional[Iterable[str]]) – Activity ids.

  • +
  • activity_types (Optional[Iterable[ActivityType]]) – Activity types.

  • +
  • entity_ids (Optional[Iterable[str]]) – Entity ids.

  • +
  • entity_names (Optional[Iterable[str]]) – Entity names.

  • +
  • entity_type (Optional[str]) – Entity type.

  • +
  • changed_after (Optional[str]) – Return only activities changed +after given iso datetime string.

  • +
  • changed_before (Optional[str]) – Return only activities changed +before given iso datetime string.

  • +
  • reference_types (Optional[Iterable[ActivityReferenceType]]) – Reference types filter. Defaults to [‘origin’].

  • +
  • fields (Optional[Iterable[str]]) – Fields that should be received +for each activity.

  • +
  • limit (Optional[int]) – Limit number of activities to be fetched.

  • +
  • order (Optional[SortOrder]) – Order activities in ascending +or descending order. It is recommended to set ‘limit’ +when used descending.

  • +
+
+
Returns:
+

Available activities matching filters.

+
+
Return type:
+

Generator[dict[str, Any]]

+
+
+
+ +
+
+get_activity_by_id(project_name: str, activity_id: str, reference_types: Optional[Iterable[ActivityReferenceType]] = None, fields: Optional[Iterable[str]] = None) Optional[Dict[str, Any]][source]
+

Get activity by id.

+
+
Parameters:
+
    +
  • project_name (str) – Project on which activity happened.

  • +
  • activity_id (str) – Activity id.

  • +
  • fields (Optional[Iterable[str]]) – Fields that should be received +for each activity.

  • +
+
+
Returns:
+

+
Activity data or None if activity is not

found.

+
+
+

+
+
Return type:
+

Optional[Dict[str, Any]]

+
+
+
+ +
+
+get_addon_endpoint(addon_name, addon_version, *subpaths)[source]
+

Calculate endpoint to addon route.

+
+

Examples

+
>>> api = ServerAPI("https://your.url.com")
+>>> api.get_addon_url(
+...     "example", "1.0.0", "private", "my.zip")
+'addons/example/1.0.0/private/my.zip'
+
+
+
+
+
Parameters:
+
    +
  • addon_name (str) – Name of addon.

  • +
  • addon_version (str) – Version of addon.

  • +
  • *subpaths (str) – Any amount of subpaths that are added to +addon url.

  • +
+
+
Returns:
+

Final url.

+
+
Return type:
+

str

+
+
+
+ +
+
+get_addon_project_settings(addon_name, addon_version, project_name, variant=None, site_id=None, use_site=True)[source]
+

Addon project settings.

+

Receive project settings for specific version of an addon. The settings +may be with site overrides when enabled.

+

Site id is filled with current connection site id if not passed. To +make sure any site id is used set ‘use_site’ to ‘False’.

+
+
Parameters:
+
    +
  • addon_name (str) – Name of addon.

  • +
  • addon_version (str) – Version of addon.

  • +
  • project_name (str) – Name of project for which the settings are +received.

  • +
  • variant (Optional[Literal['production', 'staging']]) – Name of +settings variant. Used ‘default_settings_variant’ by default.

  • +
  • site_id (Optional[str]) – Name of site which is used for site +overrides. Is filled with connection ‘site_id’ attribute +if not passed.

  • +
  • use_site (Optional[bool]) – To force disable option of using site +overrides set to ‘False’. In that case won’t be applied +any site overrides.

  • +
+
+
Returns:
+

Addon settings.

+
+
Return type:
+

dict[str, Any]

+
+
+
+ +
+
+get_addon_settings(addon_name, addon_version, project_name=None, variant=None, site_id=None, use_site=True)[source]
+

Receive addon settings.

+

Receive addon settings based on project name value. Some arguments may +be ignored if ‘project_name’ is set to ‘None’.

+
+
Parameters:
+
    +
  • addon_name (str) – Name of addon.

  • +
  • addon_version (str) – Version of addon.

  • +
  • project_name (Optional[str]) – Name of project for which the +settings are received. A studio settings values are received +if is ‘None’.

  • +
  • variant (Optional[Literal['production', 'staging']]) – Name of +settings variant. Used ‘default_settings_variant’ by default.

  • +
  • site_id (Optional[str]) – Name of site which is used for site +overrides. Is filled with connection ‘site_id’ attribute +if not passed.

  • +
  • use_site (Optional[bool]) – To force disable option of using +site overrides set to ‘False’. In that case won’t be applied +any site overrides.

  • +
+
+
Returns:
+

Addon settings.

+
+
Return type:
+

dict[str, Any]

+
+
+
+ +
+
+get_addon_settings_schema(addon_name, addon_version, project_name=None)[source]
+

Sudio/Project settings schema of an addon.

+

Project schema may look differently as some enums are based on project +values.

+
+
Parameters:
+
    +
  • addon_name (str) – Name of addon.

  • +
  • addon_version (str) – Version of addon.

  • +
  • project_name (Optional[str]) – Schema for specific project or +default studio schemas.

  • +
+
+
Returns:
+

Schema of studio/project settings.

+
+
Return type:
+

dict[str, Any]

+
+
+
+ +
+
+get_addon_site_settings(addon_name, addon_version, site_id=None)[source]
+

Site settings of an addon.

+

If site id is not available an empty dictionary is returned.

+
+
Parameters:
+
    +
  • addon_name (str) – Name of addon.

  • +
  • addon_version (str) – Version of addon.

  • +
  • site_id (Optional[str]) – Name of site for which should be settings +returned. using ‘site_id’ attribute if not passed.

  • +
+
+
Returns:
+

Site settings.

+
+
Return type:
+

dict[str, Any]

+
+
+
+ +
+
+get_addon_site_settings_schema(addon_name, addon_version)[source]
+

Site settings schema of an addon.

+
+
Parameters:
+
    +
  • addon_name (str) – Name of addon.

  • +
  • addon_version (str) – Version of addon.

  • +
+
+
Returns:
+

Schema of site settings.

+
+
Return type:
+

dict[str, Any]

+
+
+
+ +
+
+get_addon_studio_settings(addon_name, addon_version, variant=None)[source]
+

Addon studio settings.

+

Receive studio settings for specific version of an addon.

+
+
Parameters:
+
    +
  • addon_name (str) – Name of addon.

  • +
  • addon_version (str) – Version of addon.

  • +
  • variant (Optional[Literal['production', 'staging']]) – Name of +settings variant. Used ‘default_settings_variant’ by default.

  • +
+
+
Returns:
+

Addon settings.

+
+
Return type:
+

dict[str, Any]

+
+
+
+ +
+
+get_addon_url(addon_name, addon_version, *subpaths, use_rest=True)[source]
+

Calculate url to addon route.

+
+

Examples

+
>>> api = ServerAPI("https://your.url.com")
+>>> api.get_addon_url(
+...     "example", "1.0.0", "private", "my.zip")
+'https://your.url.com/api/addons/example/1.0.0/private/my.zip'
+
+
+
+
+
Parameters:
+
    +
  • addon_name (str) – Name of addon.

  • +
  • addon_version (str) – Version of addon.

  • +
  • *subpaths (str) – Any amount of subpaths that are added to +addon url.

  • +
  • use_rest (Optional[bool]) – Use rest endpoint.

  • +
+
+
Returns:
+

Final url.

+
+
Return type:
+

str

+
+
+
+ +
+
+get_addons_info(details=True)[source]
+

Get information about addons available on server.

+
+
Parameters:
+

details (Optional[bool]) – Detailed data with information how +to get client code.

+
+
+
+ +
+
+get_addons_project_settings(project_name, bundle_name=None, variant=None, site_id=None, use_site=True, only_values=True)[source]
+

Project settings of all addons.

+

Server returns information about used addon versions, so full output +looks like:

+
+
```json
+
{

“settings”: {…}, +“addons”: {…}

+
+
+

}

+
+
+

```

+

The output can be limited to only values. To do so is ‘only_values’ +argument which is by default set to ‘True’. In that case output +contains only value of ‘settings’ key.

+
+

Warning

+
+
Behavior of this function changed with AYON server version 0.3.0.

Structure of output from server changed. If using +‘only_values=True’ then output should be same as before.

+
+
+
+
+
Parameters:
+
    +
  • project_name (str) – Name of project for which are settings +received.

  • +
  • bundle_name (Optional[str]) – Name of bundle for which should be +settings received.

  • +
  • variant (Optional[Literal['production', 'staging']]) – Name of +settings variant. Used ‘default_settings_variant’ by default.

  • +
  • site_id (Optional[str]) – Site id for which want to receive +site overrides.

  • +
  • use_site (bool) – To force disable option of using site overrides +set to ‘False’. In that case won’t be applied any site +overrides.

  • +
  • only_values (Optional[bool]) – Output will contain only settings +values without metadata about addons.

  • +
+
+
Returns:
+

+
Settings of all addons on server for passed

project.

+
+
+

+
+
Return type:
+

dict[str, Any]

+
+
+
+ +
+
+get_addons_settings(bundle_name=None, project_name=None, variant=None, site_id=None, use_site=True, only_values=True)[source]
+

Universal function to receive all addon settings.

+

Based on ‘project_name’ will receive studio settings or project +settings. In case project is not passed is ‘site_id’ ignored.

+
+

Warning

+
+
Behavior of this function changed with AYON server version 0.3.0.

Structure of output from server changed. If using +‘only_values=True’ then output should be same as before.

+
+
+
+
+
Parameters:
+
    +
  • bundle_name (Optional[str]) – Name of bundle for which should be +settings received.

  • +
  • project_name (Optional[str]) – Name of project for which should be +settings received.

  • +
  • variant (Optional[Literal['production', 'staging']]) – Name of +settings variant. Used ‘default_settings_variant’ by default.

  • +
  • site_id (Optional[str]) – Id of site for which want to receive +site overrides.

  • +
  • use_site (Optional[bool]) – To force disable option of using site +overrides set to ‘False’. In that case won’t be applied +any site overrides.

  • +
  • only_values (Optional[bool]) – Only settings values will be +returned. By default, is set to ‘True’.

  • +
+
+
+
+ +
+
+get_addons_studio_settings(bundle_name=None, variant=None, site_id=None, use_site=True, only_values=True)[source]
+

All addons settings in one bulk.

+
+

Warning

+
+
Behavior of this function changed with AYON server version 0.3.0.

Structure of output from server changed. If using +‘only_values=True’ then output should be same as before.

+
+
+
+
+
Parameters:
+
    +
  • bundle_name (Optional[str]) – Name of bundle for which should be +settings received.

  • +
  • variant (Optional[Literal['production', 'staging']]) – Name of +settings variant. Used ‘default_settings_variant’ by default.

  • +
  • site_id (Optional[str]) – Site id for which want to receive +site overrides.

  • +
  • use_site (bool) – To force disable option of using site overrides +set to ‘False’. In that case won’t be applied any site +overrides.

  • +
  • only_values (Optional[bool]) – Output will contain only settings +values without metadata about addons.

  • +
+
+
Returns:
+

Settings of all addons on server.

+
+
Return type:
+

dict[str, Any]

+
+
+
+ +
+
+get_attributes_fields_for_type(entity_type)[source]
+

Prepare attribute fields for entity type.

+
+
Returns:
+

Attributes fields for entity type.

+
+
Return type:
+

set[str]

+
+
+
+ +
+
+get_attributes_for_type(entity_type)[source]
+

Get attribute schemas available for an entity type.

+

Example:

+
```
+# Example attribute schema
+{
+    # Common
+    "type": "integer",
+    "title": "Clip Out",
+    "description": null,
+    "example": 1,
+    "default": 1,
+    # These can be filled based on value of 'type'
+    "gt": null,
+    "ge": null,
+    "lt": null,
+    "le": null,
+    "minLength": null,
+    "maxLength": null,
+    "minItems": null,
+    "maxItems": null,
+    "regex": null,
+    "enum": null
+}
+```
+
+
+
+
Parameters:
+

entity_type (str) – Entity type for which should be attributes +received.

+
+
Returns:
+

+
Attribute schemas that are available

for entered entity type.

+
+
+

+
+
Return type:
+

dict[str, dict[str, Any]]

+
+
+
+ +
+
+get_attributes_schema(use_cache=True)[source]
+
+ +
+
+get_base_url()[source]
+
+ +
+
+get_build_in_anatomy_preset()[source]
+

Get built-in anatomy preset.

+
+
Returns:
+

Built-in anatomy preset.

+
+
Return type:
+

dict[str, Any]

+
+
+
+ +
+
+get_bundle_settings(bundle_name=None, project_name=None, variant=None, site_id=None, use_site=True)[source]
+

Get complete set of settings for given data.

+

If project is not passed then studio settings are returned. If variant +is not passed ‘default_settings_variant’ is used. If bundle name is +not passed then current production/staging bundle is used, based on +variant value.

+

Output contains addon settings and site settings in single dictionary.

+
+
Todos:
    +
  • test how it behaves if there is not any bundle.

  • +
  • +
    test how it behaves if there is not any production/staging

    bundle.

    +
    +
    +
  • +
+
+
+

Example output:

+
{
+    "addons": [
+        {
+            "name": "addon-name",
+            "version": "addon-version",
+            "settings": {...},
+            "siteSettings": {...}
+        }
+    ]
+}
+
+
+
+
Returns:
+

All settings for single bundle.

+
+
Return type:
+

dict[str, Any]

+
+
+
+ +
+
+get_bundles()[source]
+

Server bundles with basic information.

+

This is example output:

+
{
+    "bundles": [
+        {
+            "name": "my_bundle",
+            "createdAt": "2023-06-12T15:37:02.420260",
+            "installerVersion": "1.0.0",
+            "addons": {
+                "core": "1.2.3"
+            },
+            "dependencyPackages": {
+                "windows": "a_windows_package123.zip",
+                "linux": "a_linux_package123.zip",
+                "darwin": "a_mac_package123.zip"
+            },
+            "isProduction": False,
+            "isStaging": False
+        }
+    ],
+    "productionBundle": "my_bundle",
+    "stagingBundle": "test_bundle"
+}
+
+
+
+
Returns:
+

Server bundles with basic information.

+
+
Return type:
+

dict[str, Any]

+
+
+
+ +
+
+get_cert()[source]
+

Current cert file used for connection to server.

+
+
Returns:
+

Path to cert file.

+
+
Return type:
+

Union[str, None]

+
+
+
+ +
+
+get_client_version()[source]
+

Version of client used to connect to server.

+

Client version is AYON client build desktop application.

+
+
Returns:
+

Client version string used in connection.

+
+
Return type:
+

str

+
+
+
+ +
+
+get_default_anatomy_preset_name()[source]
+

Name of default anatomy preset.

+

Primary preset is used as default preset. But when primary preset is +not set a built-in is used instead. Built-in preset is named ‘_’.

+
+
Returns:
+

+
Name of preset that can be used by

’get_project_anatomy_preset’.

+
+
+

+
+
Return type:
+

str

+
+
+
+ +
+
+get_default_fields_for_type(entity_type)[source]
+

Default fields for entity type.

+

Returns most of commonly used fields from server.

+
+
Parameters:
+

entity_type (str) – Name of entity type.

+
+
Returns:
+

Fields that should be queried from server.

+
+
Return type:
+

set[str]

+
+
+
+ +
+
+classmethod get_default_max_retries()[source]
+

Default value for requests max retries.

+

First looks for environment variable SERVER_RETRIES_ENV_KEY, which +can affect max retries value. If not available then use class +attribute ‘_default_max_retries’.

+
+
Returns:
+

Max retries value.

+
+
Return type:
+

int

+
+
+
+ +
+
+get_default_service_username()[source]
+

Default username used for callbacks when used with service API key.

+
+
Returns:
+

Username if any was filled.

+
+
Return type:
+

Union[str, None]

+
+
+
+ +
+
+get_default_settings_variant()[source]
+

Default variant used for settings.

+
+
Returns:
+

name of variant or None.

+
+
Return type:
+

Union[str, None]

+
+
+
+ +
+
+classmethod get_default_timeout()[source]
+

Default value for requests timeout.

+

Utils function ‘get_default_timeout’ is used by default.

+
+
Returns:
+

Timeout value in seconds.

+
+
Return type:
+

float

+
+
+
+ +
+
+get_dependency_packages()[source]
+

Information about dependency packages on server.

+

To download dependency package, use ‘download_dependency_package’ +method and pass in ‘filename’.

+

Example data structure:

+
{
+    "packages": [
+        {
+            "filename": str,
+            "platform": str,
+            "checksum": str,
+            "checksumAlgorithm": str,
+            "size": int,
+            "sources": list[dict[str, Any]],
+            "supportedAddons": dict[str, str],
+            "pythonModules": dict[str, str]
+        }
+    ]
+}
+
+
+
+
Returns:
+

+
Information about dependency packages known for

server.

+
+
+

+
+
Return type:
+

dict[str, Any]

+
+
+
+ +
+ +

Helper method to get links from server for entity types.

+
Example output:
+{
+    "59a212c0d2e211eda0e20242ac120001": [
+        {
+            "id": "59a212c0d2e211eda0e20242ac120002",
+            "linkType": "reference",
+            "description": "reference link between folders",
+            "projectName": "my_project",
+            "author": "frantadmin",
+            "entityId": "b1df109676db11ed8e8c6c9466b19aa8",
+            "entityType": "folder",
+            "direction": "out"
+        },
+        ...
+    ],
+    ...
+}
+
+
+
+
Parameters:
+
    +
  • project_name (str) – Project where links are.

  • +
  • (Literal["folder" (entity_type) –

  • +
  • "task"

  • +
  • "product"

  • +
+
+
+

:param : +:param | “version”: Entity type. +:param “representations”]): Entity type. +:param entity_ids: Ids of entities for which +:type entity_ids: Optional[Iterable[str]] +:param | links should be received.: +:param link_types: Link type filters. +:type link_types: Optional[Iterable[str]] +:param link_direction: Link direction +:type link_direction: Optional[Literal["in", "out"]] +:param | filter.: +:param link_names: Link name filters. +:type link_names: Optional[Iterable[str]] +:param link_name_regex: Regex filter for link name. +:type link_name_regex: Optional[str]

+
+
Returns:
+

Link info by entity ids.

+
+
Return type:
+

dict[str, list[dict[str, Any]]]

+
+
+
+ +
+
+get_event(event_id)[source]
+

Query full event data by id.

+

Events received using event server do not contain full information. To +get the full event information is required to receive it explicitly.

+
+
Parameters:
+

event_id (str) – Event id.

+
+
Returns:
+

Full event data.

+
+
Return type:
+

dict[str, Any]

+
+
+
+ +
+
+get_events(topics: Optional[Iterable[str]] = None, event_ids: Optional[Iterable[str]] = None, project_names: Optional[Iterable[str]] = None, statuses: Optional[Iterable[str]] = None, users: Optional[Iterable[str]] = None, include_logs: Optional[bool] = None, has_children: Optional[bool] = None, newer_than: Optional[str] = None, older_than: Optional[str] = None, fields: Optional[Iterable[str]] = None, limit: Optional[int] = None, order: Optional[SortOrder] = None, states: Optional[Iterable[str]] = None)[source]
+

Get events from server with filtering options.

+
+

Notes

+

Not all event happen on a project.

+
+
+
Parameters:
+
    +
  • topics (Optional[Iterable[str]]) – Name of topics.

  • +
  • event_ids (Optional[Iterable[str]]) – Event ids.

  • +
  • project_names (Optional[Iterable[str]]) – Project on which +event happened.

  • +
  • statuses (Optional[Iterable[str]]) – Filtering by statuses.

  • +
  • users (Optional[Iterable[str]]) – Filtering by users +who created/triggered an event.

  • +
  • include_logs (Optional[bool]) – Query also log events.

  • +
  • has_children (Optional[bool]) – Event is with/without children +events. If ‘None’ then all events are returned, default.

  • +
  • newer_than (Optional[str]) – Return only events newer than given +iso datetime string.

  • +
  • older_than (Optional[str]) – Return only events older than given +iso datetime string.

  • +
  • fields (Optional[Iterable[str]]) – Fields that should be received +for each event.

  • +
  • limit (Optional[int]) – Limit number of events to be fetched.

  • +
  • order (Optional[SortOrder]) – Order events in ascending +or descending order. It is recommended to set ‘limit’ +when used descending.

  • +
  • states (Optional[Iterable[str]]) – DEPRECATED Filtering by states. +Use ‘statuses’ instead.

  • +
+
+
Returns:
+

Available events matching filters.

+
+
Return type:
+

Generator[dict[str, Any]]

+
+
+
+ +
+
+get_folder_by_id(project_name, folder_id, fields=None, own_attributes=False)[source]
+

Query folder entity by id.

+
+
Parameters:
+
    +
  • project_name (str) – Name of project where to look for queried +entities.

  • +
  • folder_id (str) – Folder id.

  • +
  • fields (Optional[Iterable[str]]) – Fields that should be returned. +All fields are returned if ‘None’ is passed.

  • +
  • own_attributes (Optional[bool]) – Attribute values that are +not explicitly set on entity will have ‘None’ value.

  • +
+
+
Returns:
+

Folder entity data or None if was not found.

+
+
Return type:
+

Union[dict, None]

+
+
+
+ +
+
+get_folder_by_name(project_name, folder_name, fields=None, own_attributes=False)[source]
+

Query folder entity by path.

+
+

Warning

+
+
Folder name is not a unique identifier of a folder. Function is

kept for OpenPype 3 compatibility.

+
+
+
+
+
Parameters:
+
    +
  • project_name (str) – Name of project where to look for queried +entities.

  • +
  • folder_name (str) – Folder name.

  • +
  • fields (Optional[Iterable[str]]) – Fields that should be returned. +All fields are returned if ‘None’ is passed.

  • +
  • own_attributes (Optional[bool]) – Attribute values that are +not explicitly set on entity will have ‘None’ value.

  • +
+
+
Returns:
+

Folder entity data or None if was not found.

+
+
Return type:
+

Union[dict, None]

+
+
+
+ +
+
+get_folder_by_path(project_name, folder_path, fields=None, own_attributes=False)[source]
+

Query folder entity by path.

+

Folder path is a path to folder with all parent names joined by slash.

+
+
Parameters:
+
    +
  • project_name (str) – Name of project where to look for queried +entities.

  • +
  • folder_path (str) – Folder path.

  • +
  • fields (Optional[Iterable[str]]) – Fields that should be returned. +All fields are returned if ‘None’ is passed.

  • +
  • own_attributes (Optional[bool]) – Attribute values that are +not explicitly set on entity will have ‘None’ value.

  • +
+
+
Returns:
+

Folder entity data or None if was not found.

+
+
Return type:
+

Union[dict, None]

+
+
+
+ +
+
+get_folder_ids_with_products(project_name, folder_ids=None)[source]
+

Find folders which have at least one product.

+

Folders that have at least one product should be immutable, so they +should not change path -> change of name or name of any parent +is not possible.

+
+
Parameters:
+
    +
  • project_name (str) – Name of project.

  • +
  • folder_ids (Optional[Iterable[str]]) – Limit folder ids filtering +to a set of folders. If set to None all folders on project are +checked.

  • +
+
+
Returns:
+

Folder ids that have at least one product.

+
+
Return type:
+

set[str]

+
+
+
+ +
+ +

Query folder links from server.

+
+
Parameters:
+
    +
  • project_name (str) – Project where links are.

  • +
  • folder_id (str) – Folder id for which links should be received.

  • +
  • link_types (Optional[Iterable[str]]) – Link type filters.

  • +
  • link_direction (Optional[Literal["in", "out"]]) – Link direction +filter.

  • +
+
+
Returns:
+

Link info of folder.

+
+
Return type:
+

list[dict[str, Any]]

+
+
+
+ +
+
+get_folder_thumbnail(project_name, folder_id, thumbnail_id=None)[source]
+

Prepared method to receive thumbnail for folder entity.

+
+
Parameters:
+
    +
  • project_name (str) – Project under which the entity is located.

  • +
  • folder_id (str) – Folder id for which thumbnail should be returned.

  • +
  • thumbnail_id (Optional[str]) – Prepared thumbnail id from entity. +Used only to check if thumbnail was already cached.

  • +
+
+
Returns:
+

+
Path to downloaded thumbnail or none if entity

does not have any (or if user does not have permissions).

+
+
+

+
+
Return type:
+

Union[str, None]

+
+
+
+ +
+
+get_folders(project_name, folder_ids=None, folder_paths=None, folder_names=None, folder_types=None, parent_ids=None, folder_path_regex=None, has_products=None, has_tasks=None, has_children=None, statuses=None, assignees_all=None, tags=None, active=True, has_links=None, fields=None, own_attributes=False)[source]
+

Query folders from server.

+
+
Todos:
+
Folder name won’t be unique identifier, so we should add

folder path filtering.

+
+
+
+
+
+

Notes

+

Filter ‘active’ don’t have direct filter in GraphQl.

+
+
+
Parameters:
+
    +
  • project_name (str) – Name of project.

  • +
  • folder_ids (Optional[Iterable[str]]) – Folder ids to filter.

  • +
  • folder_paths (Optional[Iterable[str]]) – Folder paths used +for filtering.

  • +
  • folder_names (Optional[Iterable[str]]) – Folder names used +for filtering.

  • +
  • folder_types (Optional[Iterable[str]]) – Folder types used +for filtering.

  • +
  • parent_ids (Optional[Iterable[str]]) – Ids of folder parents. +Use ‘None’ if folder is direct child of project.

  • +
  • folder_path_regex (Optional[str]) – Folder path regex used +for filtering.

  • +
  • has_products (Optional[bool]) – Filter folders with/without +products. Ignored when None, default behavior.

  • +
  • has_tasks (Optional[bool]) – Filter folders with/without +tasks. Ignored when None, default behavior.

  • +
  • has_children (Optional[bool]) – Filter folders with/without +children. Ignored when None, default behavior.

  • +
  • statuses (Optional[Iterable[str]]) – Folder statuses used +for filtering.

  • +
  • assignees_all (Optional[Iterable[str]]) – Filter by assigness +on children tasks. Task must have all of passed assignees.

  • +
  • tags (Optional[Iterable[str]]) – Folder tags used +for filtering.

  • +
  • active (Optional[bool]) – Filter active/inactive folders. +Both are returned if is set to None.

  • +
  • has_links (Optional[Literal[IN, OUT, ANY]]) – Filter +representations with IN/OUT/ANY links.

  • +
  • fields (Optional[Iterable[str]]) – Fields to be queried for +folder. All possible folder fields are returned +if ‘None’ is passed.

  • +
  • own_attributes (Optional[bool]) – Attribute values that are +not explicitly set on entity will have ‘None’ value.

  • +
+
+
Returns:
+

Queried folder entities.

+
+
Return type:
+

Generator[dict[str, Any]]

+
+
+
+ +
+
+get_folders_hierarchy(project_name, search_string=None, folder_types=None)[source]
+

Get project hierarchy.

+

All folders in project in hierarchy data structure.

+
+
Example output:
+
{
+
“hierarchy”: [
+
{

“id”: “…”, +“name”: “…”, +“label”: “…”, +“status”: “…”, +“folderType”: “…”, +“hasTasks”: False, +“taskNames”: [], +“parents”: [], +“parentId”: None, +“children”: […children folders…]

+
+
+
+
+

]

+
+
+

}

+
+
+
+
Parameters:
+
    +
  • project_name (str) – Project where to look for folders.

  • +
  • search_string (Optional[str]) – Search string to filter folders.

  • +
  • folder_types (Optional[Iterable[str]]) – Folder types to filter.

  • +
+
+
Returns:
+

Response data from server.

+
+
Return type:
+

dict[str, Any]

+
+
+
+ +
+ +

Query folders links from server.

+
+
Parameters:
+
    +
  • project_name (str) – Project where links are.

  • +
  • folder_ids (Optional[Iterable[str]]) – Ids of folders for which +links should be received.

  • +
  • link_types (Optional[Iterable[str]]) – Link type filters.

  • +
  • link_direction (Optional[Literal["in", "out"]]) – Link direction +filter.

  • +
+
+
Returns:
+

Link info by folder ids.

+
+
Return type:
+

dict[str, list[dict[str, Any]]]

+
+
+
+ +
+
+get_folders_rest(project_name, include_attrib=False)[source]
+

Get simplified flat list of all project folders.

+
+
Get all project folders in single REST call. This can be faster than

using ‘get_folders’ method which is using GraphQl, but does not +allow any filtering, and set of fields is defined +by server backend.

+
+
+

Example:

+
[
+    {
+        "id": "112233445566",
+        "parentId": "112233445567",
+        "path": "/root/parent/child",
+        "parents": ["root", "parent"],
+        "name": "child",
+        "label": "Child",
+        "folderType": "Folder",
+        "hasTasks": False,
+        "hasChildren": False,
+        "taskNames": [
+            "Compositing",
+        ],
+        "status": "In Progress",
+        "attrib": {},
+        "ownAttrib": [],
+        "updatedAt": "2023-06-12T15:37:02.420260",
+    },
+    ...
+]
+
+
+
+
Deprecated:
+
Use ‘get_rest_folders’ instead. Function was renamed to match

other rest functions, like ‘get_rest_folder’, +‘get_rest_project’ etc. .

+
+
+

Will be removed in ‘1.0.7’ or ‘1.1.0’.

+
+
+
+
Parameters:
+
    +
  • project_name (str) – Project name.

  • +
  • include_attrib (Optional[bool]) – Include attribute values +in output. Slower to query.

  • +
+
+
Returns:
+

List of folder entities.

+
+
Return type:
+

list[dict[str, Any]]

+
+
+
+ +
+ +

Calculate full link type name used for query from server.

+
+
Parameters:
+
    +
  • link_type_name (str) – Type of link.

  • +
  • input_type (str) – Input entity type of link.

  • +
  • output_type (str) – Output entity type of link.

  • +
+
+
Returns:
+

Full name of link type used for query from server.

+
+
Return type:
+

str

+
+
+
+ +
+
+get_graphql_schema()[source]
+
+ +
+
+get_headers(content_type=None)[source]
+
+ +
+
+get_hero_version_by_id(project_name, version_id, fields=None, own_attributes=<object object>)[source]
+

Query hero version entity by id.

+
+
Parameters:
+
    +
  • project_name (str) – Name of project where to look for queried +entities.

  • +
  • version_id (int) – Hero version id.

  • +
  • fields (Optional[Iterable[str]]) – Fields that should be returned. +All fields are returned if ‘None’ is passed.

  • +
  • own_attributes (Optional[bool]) – DEPRECATED: Not supported for +versions.

  • +
+
+
Returns:
+

Version entity data or None if was not found.

+
+
Return type:
+

Union[dict, None]

+
+
+
+ +
+
+get_hero_version_by_product_id(project_name, product_id, fields=None, own_attributes=<object object>)[source]
+

Query hero version entity by product id.

+

Only one hero version is available on a product.

+
+
Parameters:
+
    +
  • project_name (str) – Name of project where to look for queried +entities.

  • +
  • product_id (int) – Product id.

  • +
  • fields (Optional[Iterable[str]]) – Fields that should be returned. +All fields are returned if ‘None’ is passed.

  • +
  • own_attributes (Optional[bool]) – DEPRECATED: Not supported for +versions.

  • +
+
+
Returns:
+

Version entity data or None if was not found.

+
+
Return type:
+

Union[dict, None]

+
+
+
+ +
+
+get_hero_versions(project_name, product_ids=None, version_ids=None, active=True, fields=None, own_attributes=<object object>)[source]
+

Query hero versions by multiple filters.

+

Only one hero version is available on a product.

+
+
Parameters:
+
    +
  • project_name (str) – Name of project where to look for queried +entities.

  • +
  • product_ids (Optional[Iterable[str]]) – Product ids.

  • +
  • version_ids (Optional[Iterable[str]]) – Version ids.

  • +
  • active (Optional[bool]) – Receive active/inactive entities. +Both are returned when ‘None’ is passed.

  • +
  • fields (Optional[Iterable[str]]) – Fields that should be returned. +All fields are returned if ‘None’ is passed.

  • +
  • own_attributes (Optional[bool]) – DEPRECATED: Not supported for +versions.

  • +
+
+
Returns:
+

Version entity data or None if was not found.

+
+
Return type:
+

Union[dict, None]

+
+
+
+ +
+
+get_info()[source]
+

Get information about current used api key.

+

By default, the ‘info’ contains only ‘uptime’ and ‘version’. With +logged user info also contains information about user and machines on +which was logged in.

+
+
Todos:

Use this method for validation of token instead of ‘get_user’.

+
+
+
+
Returns:
+

Information from server.

+
+
Return type:
+

dict[str, Any]

+
+
+
+ +
+
+get_installers(version=None, platform_name=None)[source]
+

Information about desktop application installers on server.

+

Desktop application installers are helpers to download/update AYON +desktop application for artists.

+
+
Parameters:
+
    +
  • version (Optional[str]) – Filter installers by version.

  • +
  • platform_name (Optional[str]) – Filter installers by platform name.

  • +
+
+
Return type:
+

list[dict[str, Any]]

+
+
+
+ +
+
+get_last_version_by_product_id(project_name, product_id, active=True, fields=None, own_attributes=<object object>)[source]
+

Query last version entity by product id.

+
+
Parameters:
+
    +
  • project_name (str) – Project where to look for representation.

  • +
  • product_id (str) – Product id.

  • +
  • active (Optional[bool]) – Receive active/inactive entities. +Both are returned when ‘None’ is passed.

  • +
  • fields (Optional[Iterable[str]]) – fields to be queried +for representations.

  • +
  • own_attributes (Optional[bool]) – DEPRECATED: Not supported for +versions.

  • +
+
+
Returns:
+

Queried version entity or None.

+
+
Return type:
+

Union[dict[str, Any], None]

+
+
+
+ +
+
+get_last_version_by_product_name(project_name, product_name, folder_id, active=True, fields=None, own_attributes=<object object>)[source]
+

Query last version entity by product name and folder id.

+
+
Parameters:
+
    +
  • project_name (str) – Project where to look for representation.

  • +
  • product_name (str) – Product name.

  • +
  • folder_id (str) – Folder id.

  • +
  • active (Optional[bool]) – Receive active/inactive entities. +Both are returned when ‘None’ is passed.

  • +
  • fields (Optional[Iterable[str]]) – fields to be queried +for representations.

  • +
  • own_attributes (Optional[bool]) – DEPRECATED: Not supported for +representations.

  • +
+
+
Returns:
+

Queried version entity or None.

+
+
Return type:
+

Union[dict[str, Any], None]

+
+
+
+ +
+
+get_last_versions(project_name, product_ids, active=True, fields=None, own_attributes=<object object>)[source]
+

Query last version entities by product ids.

+
+
Parameters:
+
    +
  • project_name (str) – Project where to look for representation.

  • +
  • product_ids (Iterable[str]) – Product ids.

  • +
  • active (Optional[bool]) – Receive active/inactive entities. +Both are returned when ‘None’ is passed.

  • +
  • fields (Optional[Iterable[str]]) – fields to be queried +for representations.

  • +
  • own_attributes (Optional[bool]) – DEPRECATED: Not supported for +versions.

  • +
+
+
Returns:
+

Last versions by product id.

+
+
Return type:
+

dict[str, dict[str, Any]]

+
+
+
+ +
+ +

Get link type data.

+

There is not dedicated REST endpoint to get single link type, +so method ‘get_link_types’ is used.

+
+
Example output:
+
{

“name”: “reference|folder|folder”, +“link_type”: “reference”, +“input_type”: “folder”, +“output_type”: “folder”, +“data”: {}

+
+
+

}

+
+
+
+
Parameters:
+
    +
  • project_name (str) – Project where link type is available.

  • +
  • link_type_name (str) – Name of link type.

  • +
  • input_type (str) – Input entity type of link.

  • +
  • output_type (str) – Output entity type of link.

  • +
+
+
Returns:
+

Link type information.

+
+
Return type:
+

Union[None, dict[str, Any]]

+
+
+
+ +
+ +

All link types available on a project.

+
+
Example output:
+
[
+
{

“name”: “reference|folder|folder”, +“link_type”: “reference”, +“input_type”: “folder”, +“output_type”: “folder”, +“data”: {}

+
+
+

}

+
+
+

]

+
+
+
+
Parameters:
+

project_name (str) – Name of project where to look for link types.

+
+
Returns:
+

Link types available on project.

+
+
Return type:
+

list[dict[str, Any]]

+
+
+
+ +
+
+get_max_retries()[source]
+

Current value for requests max retries.

+
+
Returns:
+

Max retries value.

+
+
Return type:
+

int

+
+
+
+ +
+
+get_product_by_id(project_name, product_id, fields=None, own_attributes=<object object>)[source]
+

Query product entity by id.

+
+
Parameters:
+
    +
  • project_name (str) – Name of project where to look for queried +entities.

  • +
  • product_id (str) – Product id.

  • +
  • fields (Optional[Iterable[str]]) – Fields that should be returned. +All fields are returned if ‘None’ is passed.

  • +
  • own_attributes (Optional[bool]) – DEPRECATED: Not supported for +products.

  • +
+
+
Returns:
+

Product entity data or None if was not found.

+
+
Return type:
+

Union[dict, None]

+
+
+
+ +
+
+get_product_by_name(project_name, product_name, folder_id, fields=None, own_attributes=<object object>)[source]
+

Query product entity by name and folder id.

+
+
Parameters:
+
    +
  • project_name (str) – Name of project where to look for queried +entities.

  • +
  • product_name (str) – Product name.

  • +
  • folder_id (str) – Folder id (Folder is a parent of products).

  • +
  • fields (Optional[Iterable[str]]) – Fields that should be returned. +All fields are returned if ‘None’ is passed.

  • +
  • own_attributes (Optional[bool]) – DEPRECATED: Not supported for +products.

  • +
+
+
Returns:
+

Product entity data or None if was not found.

+
+
Return type:
+

Union[dict, None]

+
+
+
+ +
+ +

Query product links from server.

+
+
Parameters:
+
    +
  • project_name (str) – Project where links are.

  • +
  • product_id (str) – Product id for which links should be received.

  • +
  • link_types (Optional[Iterable[str]]) – Link type filters.

  • +
  • link_direction (Optional[Literal["in", "out"]]) – Link direction +filter.

  • +
+
+
Returns:
+

Link info of product.

+
+
Return type:
+

list[dict[str, Any]]

+
+
+
+ +
+
+get_product_type_names(project_name=None, product_ids=None)[source]
+

Product type names.

+
+

Warning

+
+
This function will be probably removed. Matters if ‘products_id’

filter has real use-case.

+
+
+
+
+
Parameters:
+
    +
  • project_name (Optional[str]) – Name of project where to look for +queried entities.

  • +
  • product_ids (Optional[Iterable[str]]) – Product ids filter. Can be +used only with ‘project_name’.

  • +
+
+
Returns:
+

Product type names.

+
+
Return type:
+

set[str]

+
+
+
+ +
+
+get_product_types(fields=None)[source]
+

Types of products.

+
+
This is server wide information. Product types have ‘name’, ‘icon’ and

‘color’.

+
+
+
+
Parameters:
+

fields (Optional[Iterable[str]]) – Product types fields to query.

+
+
Returns:
+

Product types information.

+
+
Return type:
+

list[dict[str, Any]]

+
+
+
+ +
+
+get_products(project_name, product_ids=None, product_names=None, folder_ids=None, product_types=None, product_name_regex=None, product_path_regex=None, names_by_folder_ids=None, statuses=None, tags=None, active=True, fields=None, own_attributes=<object object>)[source]
+

Query products from server.

+
+
Todos:
+
Separate ‘name_by_folder_ids’ filtering to separated method. It

cannot be combined with some other filters.

+
+
+
+
+
+
Parameters:
+
    +
  • project_name (str) – Name of project.

  • +
  • product_ids (Optional[Iterable[str]]) – Task ids to filter.

  • +
  • product_names (Optional[Iterable[str]]) – Task names used for +filtering.

  • +
  • folder_ids (Optional[Iterable[str]]) – Ids of task parents. +Use ‘None’ if folder is direct child of project.

  • +
  • product_types (Optional[Iterable[str]]) – Product types used for +filtering.

  • +
  • product_name_regex (Optional[str]) – Filter products by name regex.

  • +
  • product_path_regex (Optional[str]) – Filter products by path regex. +Path starts with folder path and ends with product name.

  • +
  • names_by_folder_ids (Optional[dict[str, Iterable[str]]]) – Product +name filtering by folder id.

  • +
  • statuses (Optional[Iterable[str]]) – Product statuses used +for filtering.

  • +
  • tags (Optional[Iterable[str]]) – Product tags used +for filtering.

  • +
  • active (Optional[bool]) – Filter active/inactive products. +Both are returned if is set to None.

  • +
  • fields (Optional[Iterable[str]]) – Fields to be queried for +folder. All possible folder fields are returned +if ‘None’ is passed.

  • +
  • own_attributes (Optional[bool]) – DEPRECATED: Not supported for +products.

  • +
+
+
Returns:
+

Queried product entities.

+
+
Return type:
+

Generator[dict[str, Any]]

+
+
+
+ +
+ +

Query products links from server.

+
+
Parameters:
+
    +
  • project_name (str) – Project where links are.

  • +
  • product_ids (Optional[Iterable[str]]) – Ids of products for which +links should be received.

  • +
  • link_types (Optional[Iterable[str]]) – Link type filters.

  • +
  • link_direction (Optional[Literal["in", "out"]]) – Link direction +filter.

  • +
+
+
Returns:
+

Link info by product ids.

+
+
Return type:
+

dict[str, list[dict[str, Any]]]

+
+
+
+ +
+
+get_project(project_name, fields=None, own_attributes=False)[source]
+

Get project.

+
+
Parameters:
+
    +
  • project_name (str) – Name of project.

  • +
  • fields (Optional[Iterable[str]]) – fields to be queried +for project.

  • +
  • own_attributes (Optional[bool]) – Attribute values that are +not explicitly set on entity will have ‘None’ value.

  • +
+
+
Returns:
+

+
Project entity data or None

if project was not found.

+
+
+

+
+
Return type:
+

Union[dict[str, Any], None]

+
+
+
+ +
+
+get_project_anatomy_preset(preset_name=None)[source]
+

Anatomy preset values by name.

+

Get anatomy preset values by preset name. Primary preset is returned +if preset name is set to ‘None’.

+
+
Parameters:
+

preset_name (Optional[str]) – Preset name.

+
+
Returns:
+

Anatomy preset values.

+
+
Return type:
+

dict[str, Any]

+
+
+
+ +
+
+get_project_anatomy_presets()[source]
+

Anatomy presets available on server.

+

Content has basic information about presets. Example output:

+
[
+    {
+        "name": "netflix_VFX",
+        "primary": false,
+        "version": "1.0.0"
+    },
+    {
+        ...
+    },
+    ...
+]
+
+
+
+
Returns:
+

Anatomy presets available on server.

+
+
Return type:
+

list[dict[str, str]]

+
+
+
+ +
+
+get_project_names(active=True, library=None)[source]
+

Receive available project names.

+

User must be logged in.

+
+
Parameters:
+
    +
  • active (Optional[bool]) – Filter active/inactive projects. Both +are returned if ‘None’ is passed.

  • +
  • library (Optional[bool]) – Filter standard/library projects. Both +are returned if ‘None’ is passed.

  • +
+
+
Returns:
+

List of available project names.

+
+
Return type:
+

list[str]

+
+
+
+ +
+
+get_project_product_types(project_name, fields=None)[source]
+

Types of products available on a project.

+

Filter only product types available on project.

+
+
Parameters:
+
    +
  • project_name (str) – Name of project where to look for +product types.

  • +
  • fields (Optional[Iterable[str]]) – Product types fields to query.

  • +
+
+
Returns:
+

Product types information.

+
+
Return type:
+

list[dict[str, Any]]

+
+
+
+ +
+
+get_project_root_overrides(project_name)[source]
+

Root overrides per site name.

+
+
Method is based on logged user and can’t be received for any other

user on server.

+
+
+

Output will contain only roots per site id used by logged user.

+
+
Parameters:
+

project_name (str) – Name of project.

+
+
Returns:
+

Root values by root name by site id.

+
+
Return type:
+

dict[str, dict[str, str]]

+
+
+
+ +
+
+get_project_root_overrides_by_site_id(project_name, site_id=None)[source]
+

Root overrides for site.

+

If site id is not passed a site set in current api object is used +instead.

+
+
Parameters:
+
    +
  • project_name (str) – Name of project.

  • +
  • site_id (Optional[str]) – Site id for which want to receive +site overrides.

  • +
+
+
Returns:
+

+
Root values by root name or None if

site does not have overrides.

+
+
+

+
+
Return type:
+

dict[str, str]

+
+
+
+ +
+
+get_project_roots_by_platform(project_name, platform_name=None)[source]
+

Root values for a site.

+

If platform name is not passed current platform name is used instead.

+
+
This function does return root values without site overrides. It is

possible to use the function to receive default root values.

+
+
+
+
Parameters:
+
    +
  • project_name (str) – Name of project.

  • +
  • platform_name (Optional[Literal["windows", "linux", "darwin"]]) – Platform name for which want to receive root values. Current +platform name is used if not passed.

  • +
+
+
Returns:
+

Root values.

+
+
Return type:
+

dict[str, str]

+
+
+
+ +
+
+get_project_roots_by_site(project_name)[source]
+

Root overrides per site name.

+

Method is based on logged user and can’t be received for any other +user on server.

+

Output will contain only roots per site id used by logged user.

+
+
Deprecated:
+
Use ‘get_project_root_overrides’ instead. Function

deprecated since 1.0.6

+
+
+
+
+
+
Parameters:
+

project_name (str) – Name of project.

+
+
Returns:
+

Root values by root name by site id.

+
+
Return type:
+

dict[str, dict[str, str]]

+
+
+
+ +
+
+get_project_roots_by_site_id(project_name, site_id=None)[source]
+

Root values for a site.

+

If site id is not passed a site set in current api object is used +instead. If site id is not available, default roots are returned +for current platform.

+
+
Parameters:
+
    +
  • project_name (str) – Name of project.

  • +
  • site_id (Optional[str]) – Site id for which want to receive +root values.

  • +
+
+
Returns:
+

Root values.

+
+
Return type:
+

dict[str, str]

+
+
+
+ +
+
+get_project_roots_for_site(project_name, site_id=None)[source]
+

Root overrides for site.

+

If site id is not passed a site set in current api object is used +instead.

+
+
Deprecated:
+
Use ‘get_project_root_overrides_by_site_id’ instead. Function

deprecated since 1.0.6

+
+
+
+
+
+
Parameters:
+
    +
  • project_name (str) – Name of project.

  • +
  • site_id (Optional[str]) – Site id for which want to receive +site overrides.

  • +
+
+
Returns:
+

+
Root values by root name, root name is not

available if it does not have overrides.

+
+
+

+
+
Return type:
+

dict[str, str]

+
+
+
+ +
+
+get_projects(active=True, library=None, fields=None, own_attributes=False)[source]
+

Get projects.

+
+
Parameters:
+
    +
  • active (Optional[bool]) – Filter active or inactive projects. +Filter is disabled when ‘None’ is passed.

  • +
  • library (Optional[bool]) – Filter library projects. Filter is +disabled when ‘None’ is passed.

  • +
  • fields (Optional[Iterable[str]]) – fields to be queried +for project.

  • +
  • own_attributes (Optional[bool]) – Attribute values that are +not explicitly set on entity will have ‘None’ value.

  • +
+
+
Returns:
+

Queried projects.

+
+
Return type:
+

Generator[dict[str, Any]]

+
+
+
+ +
+
+get_repre_ids_by_context_filters(project_name, context_filters, representation_names=None, version_ids=None)[source]
+

Find representation ids which match passed context filters.

+

Each representation has context integrated on representation entity in +database. The context may contain project, folder, task name or +product name, product type and many more. This implementation gives +option to quickly filter representation based on representation data +in database.

+
+
Context filters have defined structure. To define filter of nested

subfield use dot ‘.’ as delimiter (For example ‘task.name’).

+
+
Filter values can be regex filters. String or re.Pattern can

be used.

+
+
+
+
Parameters:
+
    +
  • project_name (str) – Project where to look for representations.

  • +
  • context_filters (dict[str, list[str]]) – Filters of context fields.

  • +
  • representation_names (Optional[Iterable[str]]) – Representation +names, can be used as additional filter for representations +by their names.

  • +
  • version_ids (Optional[Iterable[str]]) – Version ids, can be used +as additional filter for representations by their parent ids.

  • +
+
+
Returns:
+

Representation ids that match passed filters.

+
+
Return type:
+

list[str]

+
+
+
+

Example

+
+
The function returns just representation ids so if entities are

required for funtionality they must be queried afterwards by +their ids.

+
+
+
>>> project_name = "testProject"
+>>> filters = {
+...     "task.name": ["[aA]nimation"],
+...     "product": [".*[Mm]ain"]
+... }
+>>> repre_ids = get_repre_ids_by_context_filters(
+...     project_name, filters)
+>>> repres = get_representations(project_name, repre_ids)
+
+
+
+
+ +
+
+get_representation_by_id(project_name, representation_id, fields=None, own_attributes=<object object>)[source]
+

Query representation entity from server based on id filter.

+
+
Parameters:
+
    +
  • project_name (str) – Project where to look for representation.

  • +
  • representation_id (str) – Id of representation.

  • +
  • fields (Optional[Iterable[str]]) – fields to be queried +for representations.

  • +
  • own_attributes (Optional[bool]) – DEPRECATED: Not supported for +representations.

  • +
+
+
Returns:
+

Queried representation entity or None.

+
+
Return type:
+

Union[dict[str, Any], None]

+
+
+
+ +
+
+get_representation_by_name(project_name, representation_name, version_id, fields=None, own_attributes=<object object>)[source]
+

Query representation entity by name and version id.

+
+
Parameters:
+
    +
  • project_name (str) – Project where to look for representation.

  • +
  • representation_name (str) – Representation name.

  • +
  • version_id (str) – Version id.

  • +
  • fields (Optional[Iterable[str]]) – fields to be queried +for representations.

  • +
  • own_attributes (Optional[bool]) – DEPRECATED: Not supported for +representations.

  • +
+
+
Returns:
+

Queried representation entity or None.

+
+
Return type:
+

Union[dict[str, Any], None]

+
+
+
+ +
+
+get_representation_hierarchy(project_name, representation_id, project_fields=None, folder_fields=None, task_fields=None, product_fields=None, version_fields=None, representation_fields=None)[source]
+

Find representation parents by representation id.

+

Representation parent entities up to project.

+
+
Parameters:
+
    +
  • project_name (str) – Project where to look for entities.

  • +
  • representation_id (str) – Representation id.

  • +
  • project_fields (Optional[Iterable[str]]) – Project fields.

  • +
  • folder_fields (Optional[Iterable[str]]) – Folder fields.

  • +
  • task_fields (Optional[Iterable[str]]) – Task fields.

  • +
  • product_fields (Optional[Iterable[str]]) – Product fields.

  • +
  • version_fields (Optional[Iterable[str]]) – Version fields.

  • +
  • representation_fields (Optional[Iterable[str]]) – Representation +fields.

  • +
+
+
Returns:
+

Representation hierarchy entities.

+
+
Return type:
+

RepresentationHierarchy

+
+
+
+ +
+ +

Query representation links from server.

+
+
Parameters:
+
    +
  • project_name (str) – Project where links are.

  • +
  • representation_id (str) – Representation id for which links +should be received.

  • +
  • link_types (Optional[Iterable[str]]) – Link type filters.

  • +
  • link_direction (Optional[Literal["in", "out"]]) – Link direction +filter.

  • +
+
+
Returns:
+

Link info of representation.

+
+
Return type:
+

list[dict[str, Any]]

+
+
+
+ +
+
+get_representation_parents(project_name, representation_id, project_fields=None, folder_fields=None, product_fields=None, version_fields=None)[source]
+

Find representation parents by representation id.

+

Representation parent entities up to project.

+
+
Parameters:
+
    +
  • project_name (str) – Project where to look for entities.

  • +
  • representation_id (str) – Representation id.

  • +
  • project_fields (Optional[Iterable[str]]) – Project fields.

  • +
  • folder_fields (Optional[Iterable[str]]) – Folder fields.

  • +
  • product_fields (Optional[Iterable[str]]) – Product fields.

  • +
  • version_fields (Optional[Iterable[str]]) – Version fields.

  • +
+
+
Returns:
+

Representation parent entities.

+
+
Return type:
+

RepresentationParents

+
+
+
+ +
+
+get_representations(project_name, representation_ids=None, representation_names=None, version_ids=None, names_by_version_ids=None, statuses=None, tags=None, active=True, has_links=None, fields=None, own_attributes=<object object>)[source]
+

Get representation entities based on passed filters from server.

+
+
Parameters:
+
    +
  • project_name (str) – Name of project where to look for versions.

  • +
  • representation_ids (Optional[Iterable[str]]) – Representation ids +used for representation filtering.

  • +
  • representation_names (Optional[Iterable[str]]) – Representation +names used for representation filtering.

  • +
  • version_ids (Optional[Iterable[str]]) – Version ids used for +representation filtering. Versions are parents of +representations.

  • +
  • names_by_version_ids (Optional[Dict[str, Iterable[str]]) – Find +representations by names and version ids. This filter +discards all other filters.

  • +
  • statuses (Optional[Iterable[str]]) – Representation statuses used +for filtering.

  • +
  • tags (Optional[Iterable[str]]) – Representation tags used +for filtering.

  • +
  • active (Optional[bool]) – Receive active/inactive entities. +Both are returned when ‘None’ is passed.

  • +
  • has_links (Optional[Literal[IN, OUT, ANY]]) – Filter +representations with IN/OUT/ANY links.

  • +
  • fields (Optional[Iterable[str]]) – Fields to be queried for +representation. All possible fields are returned if ‘None’ is +passed.

  • +
  • own_attributes (Optional[bool]) – DEPRECATED: Not supported for +representations.

  • +
+
+
Returns:
+

Queried representation entities.

+
+
Return type:
+

Generator[dict[str, Any]]

+
+
+
+ +
+
+get_representations_hierarchy(project_name, representation_ids, project_fields=None, folder_fields=None, task_fields=None, product_fields=None, version_fields=None, representation_fields=None)[source]
+

Find representation with parents by representation id.

+

Representation entity with parent entities up to project.

+
+
Default fields are used when any fields are set to None. But it is

possible to pass in empty iterable (list, set, tuple) to skip +entity.

+
+
+
+
Parameters:
+
    +
  • project_name (str) – Project where to look for entities.

  • +
  • representation_ids (Iterable[str]) – Representation ids.

  • +
  • project_fields (Optional[Iterable[str]]) – Project fields.

  • +
  • folder_fields (Optional[Iterable[str]]) – Folder fields.

  • +
  • task_fields (Optional[Iterable[str]]) – Task fields.

  • +
  • product_fields (Optional[Iterable[str]]) – Product fields.

  • +
  • version_fields (Optional[Iterable[str]]) – Version fields.

  • +
  • representation_fields (Optional[Iterable[str]]) – Representation +fields.

  • +
+
+
Returns:
+

+
Parent entities by

representation id.

+
+
+

+
+
Return type:
+

dict[str, RepresentationHierarchy]

+
+
+
+ +
+ +

Query representations links from server.

+
+
Parameters:
+
    +
  • project_name (str) – Project where links are.

  • +
  • representation_ids (Optional[Iterable[str]]) – Ids of +representations for which links should be received.

  • +
  • link_types (Optional[Iterable[str]]) – Link type filters.

  • +
  • link_direction (Optional[Literal["in", "out"]]) – Link direction +filter.

  • +
+
+
Returns:
+

Link info by representation ids.

+
+
Return type:
+

dict[str, list[dict[str, Any]]]

+
+
+
+ +
+
+get_representations_parents(project_name, representation_ids, project_fields=None, folder_fields=None, product_fields=None, version_fields=None)[source]
+

Find representations parents by representation id.

+

Representation parent entities up to project.

+
+
Parameters:
+
    +
  • project_name (str) – Project where to look for entities.

  • +
  • representation_ids (Iterable[str]) – Representation ids.

  • +
  • project_fields (Optional[Iterable[str]]) – Project fields.

  • +
  • folder_fields (Optional[Iterable[str]]) – Folder fields.

  • +
  • product_fields (Optional[Iterable[str]]) – Product fields.

  • +
  • version_fields (Optional[Iterable[str]]) – Version fields.

  • +
+
+
Returns:
+

+
Parent entities by

representation id.

+
+
+

+
+
Return type:
+

dict[str, RepresentationParents]

+
+
+
+ +
+
+get_rest_entity_by_id(project_name, entity_type, entity_id)[source]
+

Get entity using REST on a project by its id.

+
+
Parameters:
+
    +
  • project_name (str) – Name of project where entity is.

  • +
  • entity_type (Literal["folder", "task", "product", "version"]) – The +entity type which should be received.

  • +
  • entity_id (str) – Id of entity.

  • +
+
+
Returns:
+

Received entity data.

+
+
Return type:
+

dict[str, Any]

+
+
+
+ +
+
+get_rest_folder(project_name, folder_id)[source]
+
+ +
+
+get_rest_folders(project_name, include_attrib=False)[source]
+

Get simplified flat list of all project folders.

+
+
Get all project folders in single REST call. This can be faster than

using ‘get_folders’ method which is using GraphQl, but does not +allow any filtering, and set of fields is defined +by server backend.

+
+
+

Example:

+
[
+    {
+        "id": "112233445566",
+        "parentId": "112233445567",
+        "path": "/root/parent/child",
+        "parents": ["root", "parent"],
+        "name": "child",
+        "label": "Child",
+        "folderType": "Folder",
+        "hasTasks": False,
+        "hasChildren": False,
+        "taskNames": [
+            "Compositing",
+        ],
+        "status": "In Progress",
+        "attrib": {},
+        "ownAttrib": [],
+        "updatedAt": "2023-06-12T15:37:02.420260",
+    },
+    ...
+]
+
+
+
+
Parameters:
+
    +
  • project_name (str) – Project name.

  • +
  • include_attrib (Optional[bool]) – Include attribute values +in output. Slower to query.

  • +
+
+
Returns:
+

List of folder entities.

+
+
Return type:
+

list[dict[str, Any]]

+
+
+
+ +
+
+get_rest_product(project_name, product_id)[source]
+
+ +
+
+get_rest_project(project_name)[source]
+

Query project by name.

+

This call returns project with anatomy data.

+
+
Parameters:
+

project_name (str) – Name of project.

+
+
Returns:
+

+
Project entity data or ‘None’ if

project was not found.

+
+
+

+
+
Return type:
+

Union[dict[str, Any], None]

+
+
+
+ +
+
+get_rest_projects(active=True, library=None)[source]
+

Query available project entities.

+

User must be logged in.

+
+
Parameters:
+
    +
  • active (Optional[bool]) – Filter active/inactive projects. Both +are returned if ‘None’ is passed.

  • +
  • library (Optional[bool]) – Filter standard/library projects. Both +are returned if ‘None’ is passed.

  • +
+
+
Returns:
+

Available projects.

+
+
Return type:
+

Generator[dict[str, Any]]

+
+
+
+ +
+
+get_rest_representation(project_name, representation_id)[source]
+
+ +
+
+get_rest_task(project_name, task_id)[source]
+
+ +
+
+get_rest_url()[source]
+
+ +
+
+get_rest_version(project_name, version_id)[source]
+
+ +
+
+get_schemas()[source]
+

Get components schema.

+

Name of components does not match entity type names e.g. ‘project’ is +under ‘ProjectModel’. We should find out some mapping. Also, there +are properties which don’t have information about reference to object +e.g. ‘config’ has just object definition without reference schema.

+
+
Returns:
+

Component schemas.

+
+
Return type:
+

dict[str, Any]

+
+
+
+ +
+
+get_secret(secret_name)[source]
+

Get secret by name.

+

Example output:

+
{
+    "name": "secret_name",
+    "value": "secret_value",
+}
+
+
+
+
Parameters:
+

secret_name (str) – Name of secret.

+
+
Returns:
+

Secret entity data.

+
+
Return type:
+

dict[str, str]

+
+
+
+ +
+
+get_secrets()[source]
+

Get all secrets.

+

Example output:

+
[
+    {
+        "name": "secret_1",
+        "value": "secret_value_1",
+    },
+    {
+        "name": "secret_2",
+        "value": "secret_value_2",
+    }
+]
+
+
+
+
Returns:
+

List of secret entities.

+
+
Return type:
+

list[dict[str, str]]

+
+
+
+ +
+
+get_sender()[source]
+

Sender used to send requests.

+
+
Returns:
+

Sender name or None.

+
+
Return type:
+

Union[str, None]

+
+
+
+ +
+
+get_sender_type()[source]
+

Sender type used to send requests.

+

Sender type is supported since AYON server 1.5.5 .

+
+
Returns:
+

Sender type or None.

+
+
Return type:
+

Union[str, None]

+
+
+
+ +
+
+get_server_schema()[source]
+

Get server schema with info, url paths, components etc.

+
+
Todos:

Cache schema - How to find out it is outdated?

+
+
+
+
Returns:
+

Full server schema.

+
+
Return type:
+

dict[str, Any]

+
+
+
+ +
+
+get_server_version()[source]
+

Get server version.

+

Version should match semantic version (https://semver.org/).

+
+
Returns:
+

Server version.

+
+
Return type:
+

str

+
+
+
+ +
+
+get_server_version_tuple()[source]
+

Get server version as tuple.

+

Version should match semantic version (https://semver.org/).

+

This function only returns first three numbers of version.

+
+
Returns:
+

+
Server

version.

+
+
+

+
+
Return type:
+

Tuple[int, int, int, Union[str, None], Union[str, None]]

+
+
+
+ +
+
+get_site_id()[source]
+

Site id used for connection.

+

Site id tells server from which machine/site is connection created and +is used for default site overrides when settings are received.

+
+
Returns:
+

Site id value or None if not filled.

+
+
Return type:
+

Union[str, None]

+
+
+
+ +
+
+get_ssl_verify()[source]
+

Enable ssl verification.

+
+
Returns:
+

Current state of ssl verification.

+
+
Return type:
+

bool

+
+
+
+ +
+
+get_task_by_folder_path(project_name, folder_path, task_name, fields=None, own_attributes=False)[source]
+

Query task entity by folder path and task name.

+
+
Parameters:
+
    +
  • project_name (str) – Project name.

  • +
  • folder_path (str) – Folder path.

  • +
  • task_name (str) – Task name.

  • +
  • fields (Optional[Iterable[str]]) – Task fields that should +be returned.

  • +
  • own_attributes (Optional[bool]) – Attribute values that are +not explicitly set on entity will have ‘None’ value.

  • +
+
+
Returns:
+

+
Task entity data or None if was

not found.

+
+
+

+
+
Return type:
+

Union[dict[str, Any], None]

+
+
+
+ +
+
+get_task_by_id(project_name, task_id, fields=None, own_attributes=False)[source]
+

Query task entity by id.

+
+
Parameters:
+
    +
  • project_name (str) – Name of project where to look for queried +entities.

  • +
  • task_id (str) – Task id.

  • +
  • fields (Optional[Iterable[str]]) – Fields that should be returned. +All fields are returned if ‘None’ is passed.

  • +
  • own_attributes (Optional[bool]) – Attribute values that are +not explicitly set on entity will have ‘None’ value.

  • +
+
+
Returns:
+

Task entity data or None if was not found.

+
+
Return type:
+

Union[dict, None]

+
+
+
+ +
+
+get_task_by_name(project_name, folder_id, task_name, fields=None, own_attributes=False)[source]
+

Query task entity by name and folder id.

+
+
Parameters:
+
    +
  • project_name (str) – Name of project where to look for queried +entities.

  • +
  • folder_id (str) – Folder id.

  • +
  • task_name (str) – Task name

  • +
  • fields (Optional[Iterable[str]]) – Fields that should be returned. +All fields are returned if ‘None’ is passed.

  • +
  • own_attributes (Optional[bool]) – Attribute values that are +not explicitly set on entity will have ‘None’ value.

  • +
+
+
Returns:
+

Task entity data or None if was not found.

+
+
Return type:
+

Union[dict, None]

+
+
+
+ +
+ +

Query task links from server.

+
+
Parameters:
+
    +
  • project_name (str) – Project where links are.

  • +
  • task_id (str) – Task id for which links should be received.

  • +
  • link_types (Optional[Iterable[str]]) – Link type filters.

  • +
  • link_direction (Optional[Literal["in", "out"]]) – Link direction +filter.

  • +
+
+
Returns:
+

Link info of task.

+
+
Return type:
+

list[dict[str, Any]]

+
+
+
+ +
+
+get_tasks(project_name, task_ids=None, task_names=None, task_types=None, folder_ids=None, assignees=None, assignees_all=None, statuses=None, tags=None, active=True, fields=None, own_attributes=False)[source]
+

Query task entities from server.

+
+
Parameters:
+
    +
  • project_name (str) – Name of project.

  • +
  • task_ids (Iterable[str]) – Task ids to filter.

  • +
  • task_names (Iterable[str]) – Task names used for filtering.

  • +
  • task_types (Iterable[str]) – Task types used for filtering.

  • +
  • folder_ids (Iterable[str]) – Ids of task parents. Use ‘None’ +if folder is direct child of project.

  • +
  • assignees (Optional[Iterable[str]]) – Task assignees used for +filtering. All tasks with any of passed assignees are +returned.

  • +
  • assignees_all (Optional[Iterable[str]]) – Task assignees used +for filtering. Task must have all of passed assignees to be +returned.

  • +
  • statuses (Optional[Iterable[str]]) – Task statuses used for +filtering.

  • +
  • tags (Optional[Iterable[str]]) – Task tags used for +filtering.

  • +
  • active (Optional[bool]) – Filter active/inactive tasks. +Both are returned if is set to None.

  • +
  • fields (Optional[Iterable[str]]) – Fields to be queried for +folder. All possible folder fields are returned +if ‘None’ is passed.

  • +
  • own_attributes (Optional[bool]) – Attribute values that are +not explicitly set on entity will have ‘None’ value.

  • +
+
+
Returns:
+

Queried task entities.

+
+
Return type:
+

Generator[dict[str, Any]]

+
+
+
+ +
+
+get_tasks_by_folder_path(project_name, folder_path, task_names=None, task_types=None, assignees=None, assignees_all=None, statuses=None, tags=None, active=True, fields=None, own_attributes=False)[source]
+

Query task entities from server by folder path.

+
+
Parameters:
+
    +
  • project_name (str) – Name of project.

  • +
  • folder_path (str) – Folder path.

  • +
  • task_names (Iterable[str]) – Task names used for filtering.

  • +
  • task_types (Iterable[str]) – Task types used for filtering.

  • +
  • assignees (Optional[Iterable[str]]) – Task assignees used for +filtering. All tasks with any of passed assignees are +returned.

  • +
  • assignees_all (Optional[Iterable[str]]) – Task assignees used +for filtering. Task must have all of passed assignees to be +returned.

  • +
  • statuses (Optional[Iterable[str]]) – Task statuses used for +filtering.

  • +
  • tags (Optional[Iterable[str]]) – Task tags used for +filtering.

  • +
  • active (Optional[bool]) – Filter active/inactive tasks. +Both are returned if is set to None.

  • +
  • fields (Optional[Iterable[str]]) – Fields to be queried for +folder. All possible folder fields are returned +if ‘None’ is passed.

  • +
  • own_attributes (Optional[bool]) – Attribute values that are +not explicitly set on entity will have ‘None’ value.

  • +
+
+
+
+ +
+
+get_tasks_by_folder_paths(project_name, folder_paths, task_names=None, task_types=None, assignees=None, assignees_all=None, statuses=None, tags=None, active=True, fields=None, own_attributes=False)[source]
+

Query task entities from server by folder paths.

+
+
Parameters:
+
    +
  • project_name (str) – Name of project.

  • +
  • folder_paths (list[str]) – Folder paths.

  • +
  • task_names (Iterable[str]) – Task names used for filtering.

  • +
  • task_types (Iterable[str]) – Task types used for filtering.

  • +
  • assignees (Optional[Iterable[str]]) – Task assignees used for +filtering. All tasks with any of passed assignees are +returned.

  • +
  • assignees_all (Optional[Iterable[str]]) – Task assignees used +for filtering. Task must have all of passed assignees to be +returned.

  • +
  • statuses (Optional[Iterable[str]]) – Task statuses used for +filtering.

  • +
  • tags (Optional[Iterable[str]]) – Task tags used for +filtering.

  • +
  • active (Optional[bool]) – Filter active/inactive tasks. +Both are returned if is set to None.

  • +
  • fields (Optional[Iterable[str]]) – Fields to be queried for +folder. All possible folder fields are returned +if ‘None’ is passed.

  • +
  • own_attributes (Optional[bool]) – Attribute values that are +not explicitly set on entity will have ‘None’ value.

  • +
+
+
Returns:
+

+
Task entities by

folder path.

+
+
+

+
+
Return type:
+

dict[dict[str, list[dict[str, Any]]]

+
+
+
+ +
+ +

Query tasks links from server.

+
+
Parameters:
+
    +
  • project_name (str) – Project where links are.

  • +
  • task_ids (Optional[Iterable[str]]) – Ids of tasks for which +links should be received.

  • +
  • link_types (Optional[Iterable[str]]) – Link type filters.

  • +
  • link_direction (Optional[Literal["in", "out"]]) – Link direction +filter.

  • +
+
+
Returns:
+

Link info by task ids.

+
+
Return type:
+

dict[str, list[dict[str, Any]]]

+
+
+
+ +
+
+get_thumbnail(project_name, entity_type, entity_id, thumbnail_id=None)[source]
+

Get thumbnail from server.

+

Permissions of thumbnails are related to entities so thumbnails must +be queried per entity. So an entity type and entity type is required +to be passed.

+
+

Notes

+
+
It is recommended to use one of prepared entity type specific

methods ‘get_folder_thumbnail’, ‘get_version_thumbnail’ or +‘get_workfile_thumbnail’.

+
+
We do recommend pass thumbnail id if you have access to it. Each

entity that allows thumbnails has ‘thumbnailId’ field, so it +can be queried.

+
+
+
+
+
Parameters:
+
    +
  • project_name (str) – Project under which the entity is located.

  • +
  • entity_type (str) – Entity type which passed entity id represents.

  • +
  • entity_id (str) – Entity id for which thumbnail should be returned.

  • +
  • thumbnail_id (Optional[str]) – DEPRECATED Use +‘get_thumbnail_by_id’.

  • +
+
+
Returns:
+

+
Thumbnail content wrapper. Does not have to be

valid.

+
+
+

+
+
Return type:
+

ThumbnailContent

+
+
+
+ +
+
+get_thumbnail_by_id(project_name, thumbnail_id)[source]
+

Get thumbnail from server by id.

+

Permissions of thumbnails are related to entities so thumbnails must +be queried per entity. So an entity type and entity type is required +to be passed.

+
+

Notes

+
+
It is recommended to use one of prepared entity type specific

methods ‘get_folder_thumbnail’, ‘get_version_thumbnail’ or +‘get_workfile_thumbnail’.

+
+
We do recommend pass thumbnail id if you have access to it. Each

entity that allows thumbnails has ‘thumbnailId’ field, so it +can be queried.

+
+
+
+
+
Parameters:
+
    +
  • project_name (str) – Project under which the entity is located.

  • +
  • thumbnail_id (Optional[str]) – DEPRECATED Use +‘get_thumbnail_by_id’.

  • +
+
+
Returns:
+

+
Thumbnail content wrapper. Does not have to be

valid.

+
+
+

+
+
Return type:
+

ThumbnailContent

+
+
+
+ +
+
+get_timeout()[source]
+

Current value for requests timeout.

+
+
Returns:
+

Timeout value in seconds.

+
+
Return type:
+

float

+
+
+
+ +
+
+get_user(username=None)[source]
+

Get user info using REST endpoit.

+
+
Parameters:
+

username (Optional[str]) – Username.

+
+
Returns:
+

+
User info or None if user is not

found.

+
+
+

+
+
Return type:
+

Union[dict[str, Any], None]

+
+
+
+ +
+
+get_user_by_name(username, project_name=None, fields=None)[source]
+

Get user by name using GraphQl.

+
+
Only administrators and managers can fetch all users. For other users

it is required to pass in ‘project_name’ filter.

+
+
+
+
Parameters:
+
    +
  • username (str) – Username.

  • +
  • project_name (Optional[str]) – Define scope of project.

  • +
  • fields (Optional[Iterable[str]]) – Fields to be queried +for users.

  • +
+
+
Returns:
+

+
User info or None if user is not

found.

+
+
+

+
+
Return type:
+

Union[dict[str, Any], None]

+
+
+
+ +
+
+get_users(project_name=None, usernames=None, fields=None)[source]
+

Get Users.

+
+
Only administrators and managers can fetch all users. For other users

it is required to pass in ‘project_name’ filter.

+
+
+
+
Parameters:
+
    +
  • project_name (Optional[str]) – Project name.

  • +
  • usernames (Optional[Iterable[str]]) – Filter by usernames.

  • +
  • fields (Optional[Iterable[str]]) – Fields to be queried +for users.

  • +
+
+
Returns:
+

Queried users.

+
+
Return type:
+

Generator[dict[str, Any]]

+
+
+
+ +
+
+get_version_by_id(project_name, version_id, fields=None, own_attributes=<object object>)[source]
+

Query version entity by id.

+
+
Parameters:
+
    +
  • project_name (str) – Name of project where to look for queried +entities.

  • +
  • version_id (str) – Version id.

  • +
  • fields (Optional[Iterable[str]]) – Fields that should be returned. +All fields are returned if ‘None’ is passed.

  • +
  • own_attributes (Optional[bool]) – DEPRECATED: Not supported for +versions.

  • +
+
+
Returns:
+

Version entity data or None if was not found.

+
+
Return type:
+

Union[dict, None]

+
+
+
+ +
+
+get_version_by_name(project_name, version, product_id, fields=None, own_attributes=<object object>)[source]
+

Query version entity by version and product id.

+
+
Parameters:
+
    +
  • project_name (str) – Name of project where to look for queried +entities.

  • +
  • version (int) – Version of version entity.

  • +
  • product_id (str) – Product id. Product is a parent of version.

  • +
  • fields (Optional[Iterable[str]]) – Fields that should be returned. +All fields are returned if ‘None’ is passed.

  • +
  • own_attributes (Optional[bool]) – DEPRECATED: Not supported for +versions.

  • +
+
+
Returns:
+

Version entity data or None if was not found.

+
+
Return type:
+

Union[dict, None]

+
+
+
+ +
+ +

Query version links from server.

+
+
Parameters:
+
    +
  • project_name (str) – Project where links are.

  • +
  • version_id (str) – Version id for which links should be received.

  • +
  • link_types (Optional[Iterable[str]]) – Link type filters.

  • +
  • link_direction (Optional[Literal["in", "out"]]) – Link direction +filter.

  • +
+
+
Returns:
+

Link info of version.

+
+
Return type:
+

list[dict[str, Any]]

+
+
+
+ +
+
+get_version_thumbnail(project_name, version_id, thumbnail_id=None)[source]
+

Prepared method to receive thumbnail for version entity.

+
+
Parameters:
+
    +
  • project_name (str) – Project under which the entity is located.

  • +
  • version_id (str) – Version id for which thumbnail should be +returned.

  • +
  • thumbnail_id (Optional[str]) – Prepared thumbnail id from entity. +Used only to check if thumbnail was already cached.

  • +
+
+
Returns:
+

+
Path to downloaded thumbnail or none if entity

does not have any (or if user does not have permissions).

+
+
+

+
+
Return type:
+

Union[str, None]

+
+
+
+ +
+
+get_versions(project_name, version_ids=None, product_ids=None, task_ids=None, versions=None, hero=True, standard=True, latest=None, statuses=None, tags=None, active=True, fields=None, own_attributes=<object object>)[source]
+

Get version entities based on passed filters from server.

+
+
Parameters:
+
    +
  • project_name (str) – Name of project where to look for versions.

  • +
  • version_ids (Optional[Iterable[str]]) – Version ids used for +version filtering.

  • +
  • product_ids (Optional[Iterable[str]]) – Product ids used for +version filtering.

  • +
  • task_ids (Optional[Iterable[str]]) – Task ids used for +version filtering.

  • +
  • versions (Optional[Iterable[int]]) – Versions we’re interested in.

  • +
  • hero (Optional[bool]) – Skip hero versions when set to False.

  • +
  • standard (Optional[bool]) – Skip standard (non-hero) when +set to False.

  • +
  • latest (Optional[bool]) – Return only latest version of standard +versions. This can be combined only with ‘standard’ attribute +set to True.

  • +
  • statuses (Optional[Iterable[str]]) – Representation statuses used +for filtering.

  • +
  • tags (Optional[Iterable[str]]) – Representation tags used +for filtering.

  • +
  • active (Optional[bool]) – Receive active/inactive entities. +Both are returned when ‘None’ is passed.

  • +
  • fields (Optional[Iterable[str]]) – Fields to be queried +for version. All possible folder fields are returned +if ‘None’ is passed.

  • +
  • own_attributes (Optional[bool]) – DEPRECATED: Not supported for +versions.

  • +
+
+
Returns:
+

Queried version entities.

+
+
Return type:
+

Generator[dict[str, Any]]

+
+
+
+ +
+ +

Query versions links from server.

+
+
Parameters:
+
    +
  • project_name (str) – Project where links are.

  • +
  • version_ids (Optional[Iterable[str]]) – Ids of versions for which +links should be received.

  • +
  • link_types (Optional[Iterable[str]]) – Link type filters.

  • +
  • link_direction (Optional[Literal["in", "out"]]) – Link direction +filter.

  • +
+
+
Returns:
+

Link info by version ids.

+
+
Return type:
+

dict[str, list[dict[str, Any]]]

+
+
+
+ +
+
+get_workfile_info(project_name, task_id, path, fields=None, own_attributes=<object object>)[source]
+

Workfile info entity by task id and workfile path.

+
+
Parameters:
+
    +
  • project_name (str) – Project under which the entity is located.

  • +
  • task_id (str) – Task id.

  • +
  • path (str) – Rootless workfile path.

  • +
  • fields (Optional[Iterable[str]]) – Fields to be queried for +representation. All possible fields are returned if ‘None’ is +passed.

  • +
  • own_attributes (Optional[bool]) – DEPRECATED: Not supported for +workfiles.

  • +
+
+
Returns:
+

Workfile info entity or None.

+
+
Return type:
+

Union[dict[str, Any], None]

+
+
+
+ +
+
+get_workfile_info_by_id(project_name, workfile_id, fields=None, own_attributes=<object object>)[source]
+

Workfile info entity by id.

+
+
Parameters:
+
    +
  • project_name (str) – Project under which the entity is located.

  • +
  • workfile_id (str) – Workfile info id.

  • +
  • fields (Optional[Iterable[str]]) – Fields to be queried for +representation. All possible fields are returned if ‘None’ is +passed.

  • +
  • own_attributes (Optional[bool]) – DEPRECATED: Not supported for +workfiles.

  • +
+
+
Returns:
+

Workfile info entity or None.

+
+
Return type:
+

Union[dict[str, Any], None]

+
+
+
+ +
+
+get_workfile_thumbnail(project_name, workfile_id, thumbnail_id=None)[source]
+

Prepared method to receive thumbnail for workfile entity.

+
+
Parameters:
+
    +
  • project_name (str) – Project under which the entity is located.

  • +
  • workfile_id (str) – Worfile id for which thumbnail should be +returned.

  • +
  • thumbnail_id (Optional[str]) – Prepared thumbnail id from entity. +Used only to check if thumbnail was already cached.

  • +
+
+
Returns:
+

+
Path to downloaded thumbnail or none if entity

does not have any (or if user does not have permissions).

+
+
+

+
+
Return type:
+

Union[str, None]

+
+
+
+ +
+
+get_workfiles_info(project_name, workfile_ids=None, task_ids=None, paths=None, path_regex=None, statuses=None, tags=None, has_links=None, fields=None, own_attributes=<object object>)[source]
+

Workfile info entities by passed filters.

+
+
Parameters:
+
    +
  • project_name (str) – Project under which the entity is located.

  • +
  • workfile_ids (Optional[Iterable[str]]) – Workfile ids.

  • +
  • task_ids (Optional[Iterable[str]]) – Task ids.

  • +
  • paths (Optional[Iterable[str]]) – Rootless workfiles paths.

  • +
  • path_regex (Optional[str]) – Regex filter for workfile path.

  • +
  • statuses (Optional[Iterable[str]]) – Workfile info statuses used +for filtering.

  • +
  • tags (Optional[Iterable[str]]) – Workfile info tags used +for filtering.

  • +
  • has_links (Optional[Literal[IN, OUT, ANY]]) – Filter +representations with IN/OUT/ANY links.

  • +
  • fields (Optional[Iterable[str]]) – Fields to be queried for +representation. All possible fields are returned if ‘None’ is +passed.

  • +
  • own_attributes (Optional[bool]) – DEPRECATED: Not supported for +workfiles.

  • +
+
+
Returns:
+

Queried workfile info entites.

+
+
Return type:
+

Generator[dict[str, Any]]

+
+
+
+ +
+
+property graphql_allows_data_in_query
+

GraphQl query can support ‘data’ field.

+

This applies only to project hierarchy entities ‘project’, ‘folder’, +‘task’, ‘product’, ‘version’ and ‘representation’. Others like ‘user’ +still require to use rest api to access ‘data’.

+
+
Returns:
+

True if server supports ‘data’ field in GraphQl query.

+
+
Return type:
+

bool

+
+
+
+ +
+
+property has_valid_token
+
+ +
+
+property is_server_available
+
+ +
+
+is_service_user()[source]
+

Check if connection is using service API key.

+
+
Returns:
+

Used api key belongs to service user.

+
+
Return type:
+

bool

+
+
+
+ +
+
+property log
+
+ +
+
+login(username, password, create_session=True)[source]
+

Login to server.

+
+
Parameters:
+
    +
  • username (str) – Username.

  • +
  • password (str) – Password.

  • +
  • create_session (Optional[bool]) – Create session after login. +Default: True.

  • +
+
+
Raises:
+

AuthenticationError – Login failed.

+
+
+
+ +
+
+logout(soft=False)[source]
+
+ +
+ +

Make sure link type exists on a project.

+
+
Parameters:
+
    +
  • project_name (str) – Name of project.

  • +
  • link_type_name (str) – Name of link type.

  • +
  • input_type (str) – Input entity type of link.

  • +
  • output_type (str) – Output entity type of link.

  • +
  • data (Optional[dict[str, Any]]) – Link type related data.

  • +
+
+
+
+ +
+
+property max_retries
+

Current value for requests max retries.

+
+
Returns:
+

Max retries value.

+
+
Return type:
+

int

+
+
+
+ +
+
+patch(entrypoint, **kwargs)[source]
+
+ +
+
+post(entrypoint, **kwargs)[source]
+
+ +
+
+put(entrypoint, **kwargs)[source]
+
+ +
+
+query_graphql(query, variables=None)[source]
+

Execute GraphQl query.

+
+
Parameters:
+
    +
  • query (str) – GraphQl query string.

  • +
  • variables (Optional[dict[str, Any]) – Variables that can be +used in query.

  • +
+
+
Returns:
+

Response from server.

+
+
Return type:
+

GraphQlResponse

+
+
+
+ +
+
+raw_delete(entrypoint, **kwargs)[source]
+
+ +
+
+raw_get(entrypoint, **kwargs)[source]
+
+ +
+
+raw_patch(entrypoint, **kwargs)[source]
+
+ +
+
+raw_post(entrypoint, **kwargs)[source]
+
+ +
+
+raw_put(entrypoint, **kwargs)[source]
+
+ +
+
+remove_attribute_config(attribute_name)[source]
+

Remove attribute from server.

+

This can’t be un-done, please use carefully.

+
+
Parameters:
+

attribute_name (str) – Name of attribute to remove.

+
+
+
+ +
+
+reset_attributes_schema()[source]
+
+ +
+
+reset_token()[source]
+
+ +
+
+property rest_url
+
+ +
+
+save_secret(secret_name, secret_value)[source]
+

Save secret.

+

This endpoint can create and update secret.

+
+
Parameters:
+
    +
  • secret_name (str) – Name of secret.

  • +
  • secret_value (str) – Value of secret.

  • +
+
+
+
+ +
+
+send_activities_batch_operations(project_name, operations, can_fail=False, raise_on_fail=True)[source]
+

Post multiple CRUD activities operations to server.

+

When multiple changes should be made on server side this is the best +way to go. It is possible to pass multiple operations to process on a +server side and do the changes in a transaction.

+
+
Parameters:
+
    +
  • project_name (str) – On which project should be operations +processed.

  • +
  • operations (list[dict[str, Any]]) – Operations to be processed.

  • +
  • can_fail (Optional[bool]) – Server will try to process all +operations even if one of them fails.

  • +
  • raise_on_fail (Optional[bool]) – Raise exception if an operation +fails. You can handle failed operations on your own +when set to ‘False’.

  • +
+
+
Raises:
+
    +
  • ValueError – Operations can’t be converted to json string.

  • +
  • FailedOperations – When output does not contain server operations + or ‘raise_on_fail’ is enabled and any operation fails.

  • +
+
+
Returns:
+

Operations result with process details.

+
+
Return type:
+

list[dict[str, Any]]

+
+
+
+ +
+
+send_batch_operations(project_name, operations, can_fail=False, raise_on_fail=True)[source]
+

Post multiple CRUD operations to server.

+

When multiple changes should be made on server side this is the best +way to go. It is possible to pass multiple operations to process on a +server side and do the changes in a transaction.

+
+
Parameters:
+
    +
  • project_name (str) – On which project should be operations +processed.

  • +
  • operations (list[dict[str, Any]]) – Operations to be processed.

  • +
  • can_fail (Optional[bool]) – Server will try to process all +operations even if one of them fails.

  • +
  • raise_on_fail (Optional[bool]) – Raise exception if an operation +fails. You can handle failed operations on your own +when set to ‘False’.

  • +
+
+
Raises:
+
    +
  • ValueError – Operations can’t be converted to json string.

  • +
  • FailedOperations – When output does not contain server operations + or ‘raise_on_fail’ is enabled and any operation fails.

  • +
+
+
Returns:
+

Operations result with process details.

+
+
Return type:
+

list[dict[str, Any]]

+
+
+
+ +
+
+property sender
+

Sender used to send requests.

+
+
Returns:
+

Sender name or None.

+
+
Return type:
+

Union[str, None]

+
+
+
+ +
+
+property sender_type
+

Sender type used to send requests.

+

Sender type is supported since AYON server 1.5.5 .

+
+
Returns:
+

Sender type or None.

+
+
Return type:
+

Union[str, None]

+
+
+
+ +
+
+property server_version
+

Get server version.

+

Version should match semantic version (https://semver.org/).

+
+
Returns:
+

Server version.

+
+
Return type:
+

str

+
+
+
+ +
+
+property server_version_tuple
+

Get server version as tuple.

+

Version should match semantic version (https://semver.org/).

+

This function only returns first three numbers of version.

+
+
Returns:
+

+
Server

version.

+
+
+

+
+
Return type:
+

Tuple[int, int, int, Union[str, None], Union[str, None]]

+
+
+
+ +
+
+set_attribute_config(attribute_name, data, scope, position=None, builtin=False)[source]
+
+ +
+
+set_cert(cert)[source]
+

Change cert file used for connection to server.

+
+
Parameters:
+

cert (Union[str, None]) – Path to cert file.

+
+
+
+ +
+
+set_client_version(client_version)[source]
+

Set version of client used to connect to server.

+

Client version is AYON client build desktop application.

+
+
Parameters:
+

client_version (Union[str, None]) – Client version string.

+
+
+
+ +
+
+set_default_service_username(username=None)[source]
+

Service API will work as other user.

+

Service API keys can work as other user. It can be temporary using +context manager ‘as_user’ or it is possible to set default username if +‘as_user’ context manager is not entered.

+
+
Parameters:
+

username (Optional[str]) – Username to work as when service.

+
+
Raises:
+

ValueError – When connection is not yet authenticated or api key + is not service token.

+
+
+
+ +
+
+set_default_settings_variant(variant)[source]
+

Change default variant for addon settings.

+
+

Note

+
+
It is recommended to set only ‘production’ or ‘staging’ variants

as default variant.

+
+
+
+
+
Parameters:
+

variant (str) – Settings variant name. It is possible to use +‘production’, ‘staging’ or name of dev bundle.

+
+
+
+ +
+
+set_max_retries(max_retries)[source]
+

Change max retries value for requests.

+
+
Parameters:
+

max_retries (Union[int, None]) – Max retries value.

+
+
+
+ +
+
+set_sender(sender)[source]
+

Change sender used for requests.

+
+
Parameters:
+

sender (Union[str, None]) – Sender name or None.

+
+
+
+ +
+
+set_sender_type(sender_type)[source]
+

Change sender type used for requests.

+
+
Parameters:
+

sender_type (Union[str, None]) – Sender type or None.

+
+
+
+ +
+
+set_site_id(site_id)[source]
+

Change site id of connection.

+

Behave as specific site for server. It affects default behavior of +settings getter methods.

+
+
Parameters:
+

site_id (Union[str, None]) – Site id value, or ‘None’ to unset.

+
+
+
+ +
+
+set_ssl_verify(ssl_verify)[source]
+

Change ssl verification state.

+
+
Parameters:
+

ssl_verify (Union[bool, str, None]) – Enabled/disable +ssl verification, can be a path to file.

+
+
+
+ +
+
+set_timeout(timeout)[source]
+

Change timeout value for requests.

+
+
Parameters:
+

timeout (Union[float, None]) – Timeout value in seconds.

+
+
+
+ +
+
+set_token(token)[source]
+
+ +
+
+property site_id
+

Site id used for connection.

+

Site id tells server from which machine/site is connection created and +is used for default site overrides when settings are received.

+
+
Returns:
+

Site id value or None if not filled.

+
+
Return type:
+

Union[str, None]

+
+
+
+ +
+
+property ssl_verify
+

Enable ssl verification.

+
+
Returns:
+

Current state of ssl verification.

+
+
Return type:
+

bool

+
+
+
+ +
+
+property timeout
+

Current value for requests timeout.

+
+
Returns:
+

Timeout value in seconds.

+
+
Return type:
+

float

+
+
+
+ +
+
+trigger_server_restart()[source]
+

Trigger server restart.

+

Restart may be required when a change of specific value happened on +server.

+
+ +
+
+update_activity(project_name: str, activity_id: str, body: Optional[str] = None, file_ids: Optional[List[str]] = None, append_file_ids: Optional[bool] = False, data: Optional[Dict[str, Any]] = None)[source]
+

Update activity by id.

+
+
Parameters:
+
    +
  • project_name (str) – Project on which activity happened.

  • +
  • activity_id (str) – Activity id.

  • +
  • body (str) – Activity body.

  • +
  • file_ids (Optional[List[str]]) – List of file ids attached +to activity.

  • +
  • append_file_ids (Optional[bool]) – Append file ids to existing +list of file ids.

  • +
  • data (Optional[Dict[str, Any]]) – Update data in activity.

  • +
+
+
+
+ +
+
+update_bundle(bundle_name, addon_versions=None, installer_version=None, dependency_packages=None, is_production=None, is_staging=None, is_dev=None, dev_active_user=None, dev_addons_config=None)[source]
+

Update bundle on server.

+

Dependency packages can be update only for single platform. Others +will be left untouched. Use ‘None’ value to unset dependency package +from bundle.

+
+
Parameters:
+
    +
  • bundle_name (str) – Name of bundle.

  • +
  • addon_versions (Optional[dict[str, str]]) – Addon versions, +possible only for dev bundles.

  • +
  • installer_version (Optional[str]) – Installer version, possible +only for dev bundles.

  • +
  • dependency_packages (Optional[dict[str, str]]) – Dependency pacakge +names that should be used with the bundle.

  • +
  • is_production (Optional[bool]) – Bundle will be marked as +production.

  • +
  • is_staging (Optional[bool]) – Bundle will be marked as staging.

  • +
  • is_dev (Optional[bool]) – Bundle will be marked as dev.

  • +
  • dev_active_user (Optional[str]) – Username that will be assigned +to dev bundle. Can be used only for dev bundles.

  • +
  • dev_addons_config (Optional[dict[str, Any]]) – Configuration for +dev addons. Can be used only for dev bundles.

  • +
+
+
+
+ +
+
+update_dependency_package(filename, sources)[source]
+

Update dependency package metadata on server.

+
+
Parameters:
+
    +
  • filename (str) – Filename of dependency package.

  • +
  • sources (list[dict[str, Any]]) – Information about +sources from where it is possible to get file. Fully replaces +existing sources.

  • +
+
+
+
+ +
+
+update_event(event_id, sender=None, project_name=None, username=None, status=None, description=None, summary=None, payload=None, progress=None, retries=None)[source]
+

Update event data.

+
+
Parameters:
+
    +
  • event_id (str) – Event id.

  • +
  • sender (Optional[str]) – New sender of event.

  • +
  • project_name (Optional[str]) – New project name.

  • +
  • username (Optional[str]) – New username.

  • +
  • status (Optional[str]) – New event status. Enum: “pending”, +“in_progress”, “finished”, “failed”, “aborted”, “restarted”

  • +
  • description (Optional[str]) – New description.

  • +
  • summary (Optional[dict[str, Any]]) – New summary.

  • +
  • payload (Optional[dict[str, Any]]) – New payload.

  • +
  • progress (Optional[int]) – New progress. Range [0-100].

  • +
  • retries (Optional[int]) – New retries.

  • +
+
+
+
+ +
+
+update_folder(project_name, folder_id, name=None, folder_type=None, parent_id=<object object>, label=<object object>, attrib=None, data=None, tags=None, status=None, active=None, thumbnail_id=<object object>)[source]
+

Update folder entity on server.

+
+
Do not pass parent_id, label amd thumbnail_id if you don’t

want to change their values. Value None would unset +their value.

+
+
+

Update of data will override existing value on folder entity.

+
+
Update of attrib does change only passed attributes. If you want

to unset value, use None.

+
+
+
+
Parameters:
+
    +
  • project_name (str) – Project name.

  • +
  • folder_id (str) – Folder id.

  • +
  • name (Optional[str]) – New name.

  • +
  • folder_type (Optional[str]) – New folder type.

  • +
  • parent_id (Optional[Union[str, None]]) – New parent folder id.

  • +
  • label (Optional[Union[str, None]]) – New label.

  • +
  • attrib (Optional[dict[str, Any]]) – New attributes.

  • +
  • data (Optional[dict[str, Any]]) – New data.

  • +
  • tags (Optional[Iterable[str]]) – New tags.

  • +
  • status (Optional[str]) – New status.

  • +
  • active (Optional[bool]) – New active state.

  • +
  • thumbnail_id (Optional[Union[str, None]]) – New thumbnail id.

  • +
+
+
+
+ +
+
+update_installer(filename, sources)[source]
+

Update installer information on server.

+
+
Parameters:
+
    +
  • filename (str) – Installer filename.

  • +
  • sources (list[dict[str, Any]]) – List of sources that +can be used to download file. Fully replaces existing sources.

  • +
+
+
+
+ +
+
+update_product(project_name, product_id, name=None, folder_id=None, product_type=None, attrib=None, data=None, tags=None, status=None, active=None)[source]
+

Update product entity on server.

+

Update of data will override existing value on folder entity.

+
+
Update of attrib does change only passed attributes. If you want

to unset value, use None.

+
+
+
+
Parameters:
+
    +
  • project_name (str) – Project name.

  • +
  • product_id (str) – Product id.

  • +
  • name (Optional[str]) – New product name.

  • +
  • folder_id (Optional[str]) – New product id.

  • +
  • product_type (Optional[str]) – New product type.

  • +
  • attrib (Optional[dict[str, Any]]) – New product attributes.

  • +
  • data (Optional[dict[str, Any]]) – New product data.

  • +
  • tags (Optional[Iterable[str]]) – New product tags.

  • +
  • status (Optional[str]) – New product status.

  • +
  • active (Optional[bool]) – New product active state.

  • +
+
+
+
+ +
+
+update_project(project_name, library=None, folder_types=None, task_types=None, link_types=None, statuses=None, tags=None, config=None, attrib=None, data=None, active=None, project_code=None, **changes)[source]
+

Update project entity on server.

+
+
Parameters:
+
    +
  • project_name (str) – Name of project.

  • +
  • library (Optional[bool]) – Change library state.

  • +
  • folder_types (Optional[list[dict[str, Any]]]) – Folder type +definitions.

  • +
  • task_types (Optional[list[dict[str, Any]]]) – Task type +definitions.

  • +
  • link_types (Optional[list[dict[str, Any]]]) – Link type +definitions.

  • +
  • statuses (Optional[list[dict[str, Any]]]) – Status definitions.

  • +
  • tags (Optional[list[dict[str, Any]]]) – List of tags available to +set on entities.

  • +
  • config (Optional[dict[dict[str, Any]]]) – Project anatomy config +with templates and roots.

  • +
  • attrib (Optional[dict[str, Any]]) – Project attributes to change.

  • +
  • data (Optional[dict[str, Any]]) – Custom data of a project. This +value will 100% override project data.

  • +
  • active (Optional[bool]) – Change active state of a project.

  • +
  • project_code (Optional[str]) – Change project code. Not recommended +during production.

  • +
  • **changes – Other changed keys based on Rest API documentation.

  • +
+
+
+
+ +
+
+update_representation(project_name, representation_id, name=None, version_id=None, files=None, attrib=None, data=None, tags=None, status=None, active=None)[source]
+

Update representation entity on server.

+

Update of data will override existing value on folder entity.

+
+
Update of attrib does change only passed attributes. If you want

to unset value, use None.

+
+
+
+
Parameters:
+
    +
  • project_name (str) – Project name.

  • +
  • representation_id (str) – Representation id.

  • +
  • name (Optional[str]) – New name.

  • +
  • version_id (Optional[str]) – New version id.

  • +
  • files (Optional[list[dict]]) – New files +information.

  • +
  • attrib (Optional[dict[str, Any]]) – New attributes.

  • +
  • data (Optional[dict[str, Any]]) – New data.

  • +
  • tags (Optional[Iterable[str]]) – New tags.

  • +
  • status (Optional[str]) – New status.

  • +
  • active (Optional[bool]) – New active state.

  • +
+
+
+
+ +
+
+update_task(project_name, task_id, name=None, task_type=None, folder_id=None, label=<object object>, assignees=None, attrib=None, data=None, tags=None, status=None, active=None, thumbnail_id=<object object>)[source]
+

Update task entity on server.

+
+
Do not pass label amd thumbnail_id if you don’t

want to change their values. Value None would unset +their value.

+
+
+

Update of data will override existing value on folder entity.

+
+
Update of attrib does change only passed attributes. If you want

to unset value, use None.

+
+
+
+
Parameters:
+
    +
  • project_name (str) – Project name.

  • +
  • task_id (str) – Task id.

  • +
  • name (Optional[str]) – New name.

  • +
  • task_type (Optional[str]) – New task type.

  • +
  • folder_id (Optional[str]) – New folder id.

  • +
  • label (Optional[Union[str, None]]) – New label.

  • +
  • assignees (Optional[str]) – New assignees.

  • +
  • attrib (Optional[dict[str, Any]]) – New attributes.

  • +
  • data (Optional[dict[str, Any]]) – New data.

  • +
  • tags (Optional[Iterable[str]]) – New tags.

  • +
  • status (Optional[str]) – New status.

  • +
  • active (Optional[bool]) – New active state.

  • +
  • thumbnail_id (Optional[Union[str, None]]) – New thumbnail id.

  • +
+
+
+
+ +
+
+update_thumbnail(project_name, thumbnail_id, src_filepath)[source]
+

Change thumbnail content by id.

+

Update can be also used to create new thumbnail.

+
+
Parameters:
+
    +
  • project_name (str) – Project where the thumbnail will be created +and can be used.

  • +
  • thumbnail_id (str) – Thumbnail id to update.

  • +
  • src_filepath (str) – Filepath to thumbnail which should be uploaded.

  • +
+
+
Raises:
+

ValueError – When thumbnail source cannot be processed.

+
+
+
+ +
+
+update_version(project_name, version_id, version=None, product_id=None, task_id=<object object>, author=None, attrib=None, data=None, tags=None, status=None, active=None, thumbnail_id=<object object>)[source]
+

Update version entity on server.

+
+
Do not pass task_id amd thumbnail_id if you don’t

want to change their values. Value None would unset +their value.

+
+
+

Update of data will override existing value on folder entity.

+
+
Update of attrib does change only passed attributes. If you want

to unset value, use None.

+
+
+
+
Parameters:
+
    +
  • project_name (str) – Project name.

  • +
  • version_id (str) – Version id.

  • +
  • version (Optional[int]) – New version.

  • +
  • product_id (Optional[str]) – New product id.

  • +
  • task_id (Optional[Union[str, None]]) – New task id.

  • +
  • author (Optional[str]) – New author username.

  • +
  • attrib (Optional[dict[str, Any]]) – New attributes.

  • +
  • data (Optional[dict[str, Any]]) – New data.

  • +
  • tags (Optional[Iterable[str]]) – New tags.

  • +
  • status (Optional[str]) – New status.

  • +
  • active (Optional[bool]) – New active state.

  • +
  • thumbnail_id (Optional[Union[str, None]]) – New thumbnail id.

  • +
+
+
+
+ +
+
+upload_addon_zip(src_filepath, progress=None)[source]
+

Upload addon zip file to server.

+
+
File is validated on server. If it is valid, it is installed. It will

create an event job which can be tracked (tracking part is not +implemented yet).

+
+
+

Example output:

+
{'eventId': 'a1bfbdee27c611eea7580242ac120003'}
+
+
+
+
Parameters:
+
    +
  • src_filepath (str) – Path to a zip file.

  • +
  • progress (Optional[TransferProgress]) – Object to keep track about +upload state.

  • +
+
+
Returns:
+

Response data from server.

+
+
Return type:
+

dict[str, Any]

+
+
+
+ +
+
+upload_dependency_package(src_filepath, dst_filename, platform_name=None, progress=None)[source]
+

Upload dependency package to server.

+
+
Parameters:
+
    +
  • src_filepath (str) – Path to a package file.

  • +
  • dst_filename (str) – Dependency package filename or name of package +for server version 0.2.0 or lower. Must be unique.

  • +
  • platform_name (Optional[str]) – Deprecated.

  • +
  • progress (Optional[TransferProgress]) – Object to keep track about +upload state.

  • +
+
+
+
+ +
+
+upload_file(endpoint, filepath, progress=None, request_type=None, **kwargs)[source]
+

Upload file to server.

+
+
Todos:

Use retries and timeout. +Return RestApiResponse.

+
+
+
+
Parameters:
+
    +
  • endpoint (str) – Endpoint or url where file will be uploaded.

  • +
  • filepath (str) – Source filepath.

  • +
  • progress (Optional[TransferProgress]) – Object that gives ability +to track upload progress.

  • +
  • request_type (Optional[RequestType]) – Type of request that will +be used to upload file.

  • +
  • **kwargs (Any) – Additional arguments that will be passed +to request function.

  • +
+
+
Returns:
+

Response object

+
+
Return type:
+

requests.Response

+
+
+
+ +
+
+upload_file_from_stream(endpoint, stream, progress=None, request_type=None, **kwargs)[source]
+

Upload file to server from bytes.

+
+
Todos:

Use retries and timeout. +Return RestApiResponse.

+
+
+
+
Parameters:
+
    +
  • endpoint (str) – Endpoint or url where file will be uploaded.

  • +
  • stream (Union[io.BytesIO, BinaryIO]) – File content stream.

  • +
  • progress (Optional[TransferProgress]) – Object that gives ability +to track upload progress.

  • +
  • request_type (Optional[RequestType]) – Type of request that will +be used to upload file.

  • +
  • **kwargs (Any) – Additional arguments that will be passed +to request function.

  • +
+
+
Returns:
+

Response object

+
+
Return type:
+

requests.Response

+
+
+
+ +
+
+upload_installer(src_filepath, dst_filename, progress=None)[source]
+

Upload installer file to server.

+
+
Parameters:
+
    +
  • src_filepath (str) – Source filepath.

  • +
  • dst_filename (str) – Destination filename.

  • +
  • progress (Optional[TransferProgress]) – Object that gives ability +to track download progress.

  • +
+
+
Returns:
+

Response object.

+
+
Return type:
+

requests.Response

+
+
+
+ +
+
+upload_reviewable(project_name, version_id, filepath, label=None, content_type=None, filename=None, progress=None, headers=None, **kwargs)[source]
+

Upload reviewable file to server.

+
+
Parameters:
+
    +
  • project_name (str) – Project name.

  • +
  • version_id (str) – Version id.

  • +
  • filepath (str) – Reviewable file path to upload.

  • +
  • label (Optional[str]) – Reviewable label. Filled automatically +server side with filename.

  • +
  • content_type (Optional[str]) – MIME type of the file.

  • +
  • filename (Optional[str]) – User as original filename. Filename from +‘filepath’ is used when not filled.

  • +
  • progress (Optional[TransferProgress]) – Progress.

  • +
  • headers (Optional[Dict[str, Any]]) – Headers.

  • +
+
+
Returns:
+

Server response.

+
+
Return type:
+

RestApiResponse

+
+
+
+ +
+
+validate_server_availability()[source]
+
+ +
+
+validate_token()[source]
+
+ +
+
+version_is_latest(project_name, version_id)[source]
+

Is version latest from a product.

+
+
Parameters:
+
    +
  • project_name (str) – Project where to look for representation.

  • +
  • version_id (str) – Version id.

  • +
+
+
Returns:
+

Version is latest or not.

+
+
Return type:
+

bool

+
+
+
+ +
+ +
+
+fill_own_attribs(entity)[source]
+
+ +
+ + +
+ +
+ + +
+
+ +
+ +
+ + + + + + + + + + + + \ No newline at end of file diff --git a/ayon_api.utils.html b/ayon_api.utils.html new file mode 100644 index 0000000000..1a04cb8e3c --- /dev/null +++ b/ayon_api.utils.html @@ -0,0 +1,1335 @@ + + + + + + + + + + + + ayon_api.utils module — ayon-python-api 1.0.11 documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + +
+ + + + + +
+ +
+ +
+ + + +
+
+
+ +
+

ayon_api.utils module

+
+
+class RepresentationHierarchy(project, folder, task, product, version, representation)
+

Bases: tuple

+
+
+folder
+

Alias for field number 1

+
+ +
+
+product
+

Alias for field number 3

+
+ +
+
+project
+

Alias for field number 0

+
+ +
+
+representation
+

Alias for field number 5

+
+ +
+
+task
+

Alias for field number 2

+
+ +
+
+version
+

Alias for field number 4

+
+ +
+ +
+
+class RepresentationParents(version, product, folder, project)
+

Bases: tuple

+
+
+folder
+

Alias for field number 2

+
+ +
+
+product
+

Alias for field number 1

+
+ +
+
+project
+

Alias for field number 3

+
+ +
+
+version
+

Alias for field number 0

+
+ +
+ +
+
+class SortOrder(value)[source]
+

Bases: IntEnum

+

Sort order for GraphQl requests.

+
+
+ascending = 0
+
+ +
+
+descending = 1
+
+ +
+
+classmethod parse_value(value, default=None)[source]
+
+ +
+ +
+
+class ThumbnailContent(project_name, thumbnail_id, content, content_type)[source]
+

Bases: object

+

Wrapper for thumbnail content.

+
+
Parameters:
+
    +
  • project_name (str) – Project name.

  • +
  • thumbnail_id (Union[str, None]) – Thumbnail id.

  • +
  • content_type (Union[str, None]) – Content type e.g. ‘image/png’.

  • +
  • content (Union[bytes, None]) – Thumbnail content.

  • +
+
+
+
+
+property id
+

Wrapper for thumbnail id.

+
+ +
+
+property is_valid
+

Content of thumbnail is valid.

+
+
Returns:
+

Content is valid and can be used.

+
+
Return type:
+

bool

+
+
+
+ +
+ +
+
+class TransferProgress[source]
+

Bases: object

+

Object to store progress of download/upload from/to server.

+
+
+add_transferred_chunk(chunk_size)[source]
+

Add transferred chunk size in bytes.

+
+
Parameters:
+

chunk_size (int) – Add transferred chunk size +in bytes.

+
+
+
+ +
+
+property content_size
+

Content size in bytes.

+
+
Returns:
+

+
Content size in bytes or None

if is unknown.

+
+
+

+
+
Return type:
+

Union[int, None]

+
+
+
+ +
+
+property destination_url
+

Destination url where transfer happens.

+
+

Note

+
+
Consider this as title. Must be set using

‘set_source_url’ or ‘N/A’ will be returned.

+
+
+
+
+
Returns:
+

Destination url where transfer happens.

+
+
Return type:
+

str

+
+
+
+ +
+
+property fail_reason
+

Get reason why transfer failed.

+
+
Returns:
+

+
Reason why transfer

failed or None.

+
+
+

+
+
Return type:
+

Union[str, None]

+
+
+
+ +
+
+property failed
+

Transfer failed.

+
+
Returns:
+

True if transfer failed.

+
+
Return type:
+

bool

+
+
+
+ +
+
+get_content_size()[source]
+

Content size in bytes.

+
+
Returns:
+

+
Content size in bytes or None

if is unknown.

+
+
+

+
+
Return type:
+

Union[int, None]

+
+
+
+ +
+
+get_destination_url()[source]
+

Destination url where transfer happens.

+
+

Note

+
+
Consider this as title. Must be set using

‘set_source_url’ or ‘N/A’ will be returned.

+
+
+
+
+
Returns:
+

Destination url where transfer happens.

+
+
Return type:
+

str

+
+
+
+ +
+
+get_fail_reason()[source]
+

Get reason why transfer failed.

+
+
Returns:
+

+
Reason why transfer

failed or None.

+
+
+

+
+
Return type:
+

Union[str, None]

+
+
+
+ +
+
+get_failed()[source]
+

Transfer failed.

+
+
Returns:
+

True if transfer failed.

+
+
Return type:
+

bool

+
+
+
+ +
+
+get_source_url()[source]
+

Source url from where transfer happens.

+
+

Note

+
+
Consider this as title. Must be set using

‘set_source_url’ or ‘N/A’ will be returned.

+
+
+
+
+
Returns:
+

Source url from where transfer happens.

+
+
Return type:
+

str

+
+
+
+ +
+
+get_started()[source]
+

Transfer was started.

+
+
Returns:
+

True if transfer started.

+
+
Return type:
+

bool

+
+
+
+ +
+
+get_transfer_done()[source]
+

Transfer finished.

+
+
Returns:
+

Transfer finished.

+
+
Return type:
+

bool

+
+
+
+ +
+
+get_transferred_size()[source]
+

Already transferred size in bytes.

+
+
Returns:
+

Already transferred size in bytes.

+
+
Return type:
+

int

+
+
+
+ +
+
+property is_running
+

Check if transfer is running.

+
+
Returns:
+

True if transfer is running.

+
+
Return type:
+

bool

+
+
+
+ +
+
+set_content_size(content_size)[source]
+

Set content size in bytes.

+
+
Parameters:
+

content_size (int) – Content size in bytes.

+
+
Raises:
+

ValueError – If content size was already set.

+
+
+
+ +
+
+set_destination_url(url)[source]
+

Set destination url where transfer happens.

+
+
Parameters:
+

url (str) – Destination url where transfer happens.

+
+
+
+ +
+
+set_failed(reason)[source]
+

Mark progress as failed.

+
+
Parameters:
+

reason (str) – Reason why transfer failed.

+
+
+
+ +
+
+set_source_url(url)[source]
+

Set source url from where transfer happens.

+
+
Parameters:
+

url (str) – Source url from where transfer happens.

+
+
+
+ +
+
+set_started()[source]
+

Mark that transfer started.

+
+
Raises:
+

ValueError – If transfer was already started.

+
+
+
+ +
+
+set_transfer_done()[source]
+

Mark progress as transfer finished.

+
+
Raises:
+

ValueError – If progress was already marked as done + or wasn’t started yet.

+
+
+
+ +
+
+set_transferred_size(transferred)[source]
+

Set already transferred size in bytes.

+
+
Parameters:
+

transferred (int) – Already transferred size in bytes.

+
+
+
+ +
+
+property source_url
+

Source url from where transfer happens.

+
+

Note

+
+
Consider this as title. Must be set using

‘set_source_url’ or ‘N/A’ will be returned.

+
+
+
+
+
Returns:
+

Source url from where transfer happens.

+
+
Return type:
+

str

+
+
+
+ +
+
+property started
+

Transfer was started.

+
+
Returns:
+

True if transfer started.

+
+
Return type:
+

bool

+
+
+
+ +
+
+property transfer_done
+

Transfer finished.

+
+
Returns:
+

Transfer finished.

+
+
Return type:
+

bool

+
+
+
+ +
+
+property transfer_progress
+

Get transfer progress in percents.

+
+
Returns:
+

+
Transfer progress in percents or ‘None’

if content size is unknown.

+
+
+

+
+
Return type:
+

Union[float, None]

+
+
+
+ +
+
+property transferred_size
+

Already transferred size in bytes.

+
+
Returns:
+

Already transferred size in bytes.

+
+
Return type:
+

int

+
+
+
+ +
+ +
+
+abort_web_action_event(server_url: str, action_token: str, reason: str)[source]
+

Abort web action event using action token.

+

A web action event could not be processed for some reason.

+
+
Parameters:
+
    +
  • server_url (str) – AYON server url.

  • +
  • action_token (str) – Action token.

  • +
  • reason (str) – Reason why webaction event was aborted.

  • +
+
+
Returns:
+

Response from server.

+
+
Return type:
+

requests.Response

+
+
+
+ +
+
+convert_entity_id(entity_id)[source]
+
+ +
+
+convert_or_create_entity_id(entity_id=None)[source]
+
+ +
+
+create_dependency_package_basename(platform_name=None)[source]
+

Create basename for dependency package file.

+
+
Parameters:
+

platform_name (Optional[str]) – Name of platform for which the +bundle is targeted. Default value is current platform.

+
+
Returns:
+

Dependency package name with timestamp and platform.

+
+
Return type:
+

str

+
+
+
+ +
+
+create_entity_id()[source]
+
+ +
+
+entity_data_json_default(value)[source]
+
+ +
+
+failed_json_default(value)[source]
+
+ +
+
+get_default_settings_variant()[source]
+

Default settings variant.

+
+
Returns:
+

Settings variant from environment variable or ‘production’.

+
+
Return type:
+

str

+
+
+
+ +
+
+get_default_site_id()[source]
+

Site id used for server connection.

+
+
Returns:
+

Site id from environment variable or None.

+
+
Return type:
+

Union[str, None]

+
+
+
+ +
+
+get_default_timeout()[source]
+

Default value for requests timeout.

+

First looks for environment variable SERVER_TIMEOUT_ENV_KEY which +can affect timeout value. If not available then use 10.0 s.

+
+
Returns:
+

Timeout value in seconds.

+
+
Return type:
+

float

+
+
+
+ +
+
+get_media_mime_type(filepath: str) Optional[str][source]
+

Determine Mime-Type of a file.

+
+
Parameters:
+

filepath (str) – Path to file.

+
+
Returns:
+

Mime type or None if is unknown mime type.

+
+
Return type:
+

Optional[str]

+
+
+
+ +
+
+get_media_mime_type_for_content(content: bytes) Optional[str][source]
+
+ +
+
+get_media_mime_type_for_stream(stream) Optional[str][source]
+
+ +
+
+get_user_by_token(url, token, timeout=None)[source]
+

Get user information by url and token.

+
+
Parameters:
+
    +
  • url (str) – Server url.

  • +
  • token (str) – User’s token.

  • +
  • timeout (Optional[float]) – Timeout for request. Value from +‘get_default_timeout’ is used if not specified.

  • +
+
+
Returns:
+

User information if url and token are valid.

+
+
Return type:
+

Optional[Dict[str, Any]]

+
+
+
+ +
+
+is_token_valid(url, token, timeout=None)[source]
+

Check if token is valid.

+

Token can be a user token or service api key.

+
+
Parameters:
+
    +
  • url (str) – Server url.

  • +
  • token (str) – User’s token.

  • +
  • timeout (Optional[float]) – Timeout for request. Value from +‘get_default_timeout’ is used if not specified.

  • +
+
+
Returns:
+

True if token is valid.

+
+
Return type:
+

bool

+
+
+
+ +
+
+login_to_server(url, username, password, timeout=None)[source]
+

Use login to the server to receive token.

+
+
Parameters:
+
    +
  • url (str) – Server url.

  • +
  • username (str) – User’s username.

  • +
  • password (str) – User’s password.

  • +
  • timeout (Optional[float]) – Timeout for request. Value from +‘get_default_timeout’ is used if not specified.

  • +
+
+
Returns:
+

+
User’s token if login was successfull.

Otherwise ‘None’.

+
+
+

+
+
Return type:
+

Union[str, None]

+
+
+
+ +
+
+logout_from_server(url, token, timeout=None)[source]
+

Logout from server and throw token away.

+
+
Parameters:
+
    +
  • url (str) – Url from which should be logged out.

  • +
  • token (str) – Token which should be used to log out.

  • +
  • timeout (Optional[float]) – Timeout for request. Value from +‘get_default_timeout’ is used if not specified.

  • +
+
+
+
+ +
+
+prepare_attribute_changes(old_entity, new_entity, replace=False)[source]
+
+ +
+
+prepare_entity_changes(old_entity, new_entity, replace=False)[source]
+

Prepare changes of entities.

+
+ +
+
+prepare_query_string(key_values)[source]
+

Prepare data to query string.

+

If there are any values a query starting with ‘?’ is returned otherwise +an empty string.

+
+
Parameters:
+
    +
  • dict[str – Query values.

  • +
  • Any] – Query values.

  • +
+
+
Returns:
+

Query string.

+
+
Return type:
+

str

+
+
+
+ +
+
+slugify_string(input_string, separator='_', slug_whitelist='abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789', split_chars=' ,./\\;:!|*^#@~+-_=', min_length=1, lower=False, make_set=False)[source]
+

Slugify a text string.

+

This function removes transliterates input string to ASCII, removes +special characters and use join resulting elements using +specified separator.

+
+
Parameters:
+
    +
  • input_string (str) – Input string to slugify

  • +
  • separator (str) – A string used to separate returned elements +(default: “_”)

  • +
  • slug_whitelist (str) – Characters allowed in the output +(default: ascii letters, digits and the separator)

  • +
  • split_chars (str) – Set of characters used for word splitting +(there is a sane default)

  • +
  • lower (bool) – Convert to lower-case (default: False)

  • +
  • make_set (bool) – Return “set” object instead of string.

  • +
  • min_length (int) – Minimal length of an element (word).

  • +
+
+
Returns:
+

+
Based on ‘make_set’ value returns slugified

string.

+
+
+

+
+
Return type:
+

Union[str, Set[str]]

+
+
+
+ +
+
+take_web_action_event(server_url: str, action_token: str) Dict[str, Any][source]
+

Take web action event using action token.

+

Action token is generated by AYON server and passed to AYON launcher.

+
+
Parameters:
+
    +
  • server_url (str) – AYON server url.

  • +
  • action_token (str) – Action token.

  • +
+
+
Returns:
+

Web action event.

+
+
Return type:
+

Dict[str, Any]

+
+
+
+ +
+
+validate_url(url, timeout=None)[source]
+

Validate url if is valid and server is available.

+

Validation checks if can be parsed as url and contains scheme.

+

Function will try to autofix url thus will return modified url when +connection to server works.

+
my_url = "my.server.url"
+try:
+    # Store new url
+    validated_url = validate_url(my_url)
+
+except UrlError:
+    # Handle invalid url
+    ...
+
+
+
+
Parameters:
+
    +
  • url (str) – Server url.

  • +
  • timeout (Optional[int]) – Timeout in seconds for connection to server.

  • +
+
+
Returns:
+

Url which was used to connect to server.

+
+
Raises:
+

UrlError – Error with short description and hints for user.

+
+
+
+ +
+ + +
+ +
+ + +
+
+ +
+ +
+ + + + + + + + + + + + \ No newline at end of file diff --git a/ayon_api.version.html b/ayon_api.version.html new file mode 100644 index 0000000000..96d5e75c53 --- /dev/null +++ b/ayon_api.version.html @@ -0,0 +1,456 @@ + + + + + + + + + + + + ayon_api.version module — ayon-python-api 1.0.11 documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + +
+ + + + + +
+ +
+ +
+ + + +
+
+
+ +
+

ayon_api.version module

+

Package declaring Python API for AYON server.

+
+ + +
+ +
+ + +
+
+ +
+ +
+ + + + + + + + + + + + \ No newline at end of file diff --git a/genindex.html b/genindex.html new file mode 100644 index 0000000000..d66a1286fe --- /dev/null +++ b/genindex.html @@ -0,0 +1,3007 @@ + + + + + + + + + + + Index — ayon-python-api 1.0.11 documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + +
+ + + + + +
+ +
+ +
+ + + +
+
+
+ + +

Index

+ +
+ A + | B + | C + | D + | E + | F + | G + | H + | I + | K + | L + | M + | N + | O + | P + | Q + | R + | S + | T + | U + | V + | W + +
+

A

+ + + +
+ +

B

+ + + +
+ +

C

+ + + +
+ +

D

+ + + +
+ +

E

+ + + +
+ +

F

+ + + +
+ +

G

+ + + +
+ +

H

+ + + +
+ +

I

+ + + +
+ +

K

+ + +
+ +

L

+ + + +
+ +

M

+ + + +
+ +

N

+ + + +
+ +

O

+ + + +
+ +

P

+ + + +
+ +

Q

+ + + +
+ +

R

+ + + +
+ +

S

+ + + +
+ +

T

+ + + +
+ +

U

+ + + +
+ +

V

+ + + +
+ +

W

+ + +
+ + + +
+ +
+
+ + +
+

+ + © Copyright 2024, ynput.io +
+ + + + Built with Sphinx using a + + theme + + provided by Revitron. + +

+
+ +
+ +
+
+ +
+ +
+ + + + + + + + + + + + \ No newline at end of file diff --git a/index.html b/index.html new file mode 100644 index 0000000000..a36184593e --- /dev/null +++ b/index.html @@ -0,0 +1,713 @@ + + + + + + + + + + + + Welcome to AYON Python API documentation! — ayon-python-api 1.0.11 documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ +
+

Welcome to AYON Python API documentation!

+
+_images/AYON_blackG_dot.svg
+
+

This is mainly auto-generated documentation for the AYON Python API.

+
+ +
+
+

Getting Started

+
+
Install latest version from PyPi
+
    pip install ayon-python-api
+
+
+
+
+
Install from Github sources (Alternatively)
+
    git clone git@github.com:ynput/ayon-python-api.git
+    cd ayon-python-api
+    pip install .
+
+
+
+
+
Ensure installed properly by printing ayon_api version
+
    python -c "import ayon_api ; print(ayon_api.__version__)"
+
+
+
+
+
+

Python API

+ +
+ +
+
+
+

Miscellaneous

+ +
+ +
+ +
+
+
+

Summary

+
+ +
+
+ + +
+ + +
+

+ + © Copyright 2024, ynput.io +
+ + + + Built with Sphinx using a + + theme + + provided by Revitron. + +

+
+ +
+ +
+
+ + + + \ No newline at end of file diff --git a/modules.html b/modules.html new file mode 100644 index 0000000000..00a4e4e269 --- /dev/null +++ b/modules.html @@ -0,0 +1,871 @@ + + + + + + + + + + + + ayon_api — ayon-python-api 1.0.11 documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + +
+ + + + + +
+ +
+ +
+ + + +
+
+
+ +
+

ayon_api

+
+ +
+
+ + +
+ +
+ + +
+
+ +
+ +
+ + + + + + + + + + + + \ No newline at end of file diff --git a/objects.inv b/objects.inv new file mode 100644 index 0000000000000000000000000000000000000000..83567470509685b281675f59fe38eb227aa9f64a GIT binary patch literal 7628 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 literal 0 HcmV?d00001 diff --git a/py-modindex.html b/py-modindex.html new file mode 100644 index 0000000000..0a2ecf9882 --- /dev/null +++ b/py-modindex.html @@ -0,0 +1,256 @@ + + + + + + + + + + + Python Module Index — ayon-python-api 1.0.11 documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + +
+ + + + + +
+ +
+ +
+ + + +
+
+
+ + +

Python Module Index

+ +
+ a +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
 
+ a
+ ayon_api +
    + ayon_api.constants +
    + ayon_api.entity_hub +
    + ayon_api.events +
    + ayon_api.exceptions +
    + ayon_api.graphql +
    + ayon_api.graphql_queries +
    + ayon_api.operations +
    + ayon_api.server_api +
    + ayon_api.utils +
    + ayon_api.version +
+ + +
+ +
+
+ + +
+

+ + © Copyright 2024, ynput.io +
+ + + + Built with Sphinx using a + + theme + + provided by Revitron. + +

+
+ +
+ +
+
+ +
+ +
+ + + + + + + + + + + + \ No newline at end of file diff --git a/search.html b/search.html new file mode 100644 index 0000000000..f2c4d2573f --- /dev/null +++ b/search.html @@ -0,0 +1,205 @@ + + + + + + + + + + + Search — ayon-python-api 1.0.11 documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + +
+ + + + + +
+ +
+ +
+ + + +
+
+
+ + + + +
+ +
+ +
+ +
+
+ + +
+

+ + © Copyright 2024, ynput.io +
+ + + + Built with Sphinx using a + + theme + + provided by Revitron. + +

+
+ +
+ +
+
+ +
+ +
+ + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/searchindex.js b/searchindex.js new file mode 100644 index 0000000000..40596a5599 --- /dev/null +++ b/searchindex.js @@ -0,0 +1 @@ +Search.setIndex({"docnames": ["ayon_api", "ayon_api.constants", "ayon_api.entity_hub", "ayon_api.events", "ayon_api.exceptions", "ayon_api.graphql", "ayon_api.graphql_queries", "ayon_api.operations", "ayon_api.server_api", "ayon_api.utils", "ayon_api.version", "index", "modules"], "filenames": ["ayon_api.rst", "ayon_api.constants.rst", "ayon_api.entity_hub.rst", "ayon_api.events.rst", "ayon_api.exceptions.rst", "ayon_api.graphql.rst", "ayon_api.graphql_queries.rst", "ayon_api.operations.rst", "ayon_api.server_api.rst", "ayon_api.utils.rst", "ayon_api.version.rst", "index.rst", "modules.rst"], "titles": ["ayon_api package", "ayon_api.constants module", "ayon_api.entity_hub module", "ayon_api.events module", "ayon_api.exceptions module", "ayon_api.graphql module", "ayon_api.graphql_queries module", "ayon_api.operations module", "ayon_api.server_api module", "ayon_api.utils module", "ayon_api.version module", "Welcome to AYON Python API documentation!", "ayon_api"], "terms": {"class": [0, 2, 3, 5, 7, 8, 9], "globalserverapi": [0, 11, 12], "site_id": [0, 8, 11, 12], "none": [0, 2, 3, 4, 5, 7, 8, 9], "client_vers": [0, 8, 11, 12], "default_settings_vari": [0, 8, 11, 12], "ssl_verifi": [0, 8, 11, 12], "cert": [0, 8, 11, 12], "sourc": [0, 2, 3, 4, 5, 6, 7, 8, 9, 11], "base": [0, 2, 3, 4, 5, 7, 8, 9], "serverapi": [0, 2, 5, 7, 8, 11, 12], "extend": [0, 7], "server": [0, 2, 4, 5, 7, 8, 9, 10], "api": [0, 2, 4, 8, 9, 10], "which": [0, 2, 5, 7, 8, 9], "also": [0, 2, 8], "handl": [0, 2, 7, 8, 9], "store": [0, 2, 3, 5, 7, 8, 9], "token": [0, 8, 9, 11, 12], "url": [0, 4, 8, 9], "creat": [0, 2, 5, 7, 8, 9], "object": [0, 2, 3, 5, 7, 8, 9], "expect": [0, 2, 5], "have": [0, 2, 5, 7, 8], "set": [0, 2, 5, 7, 8, 9], "environ": [0, 8, 9], "variabl": [0, 4, 5, 8, 9], "ayon_server_url": 0, "i": [0, 2, 4, 5, 7, 8, 9, 11], "fill": [0, 2, 8], "ayon_api_kei": 0, "can": [0, 2, 4, 5, 7, 8, 9], "afterward": [0, 8], "call": [0, 2, 7, 8], "login": [0, 8, 9, 11, 12], "method": [0, 2, 5, 8], "static": [0, 2], "get_token": [0, 11, 12], "get_url": [0, 11, 12], "usernam": [0, 3, 8, 9], "password": [0, 8, 9], "chang": [0, 2, 5, 7, 8, 9], "user": [0, 2, 8, 9], "If": [0, 2, 5, 7, 8, 9], "same": [0, 7, 8], "current": [0, 2, 5, 8, 9], "avail": [0, 2, 4, 5, 8, 9], "skip": [0, 5, 8], "set_environ": [0, 11, 12], "environemnt": 0, "run": [0, 8, 9], "process": [0, 7, 8, 9], "paramet": [0, 2, 5, 7, 8, 9], "str": [0, 2, 5, 7, 8, 9], "new": [0, 2, 5, 7, 8, 9], "": [0, 2, 5, 7, 8, 9], "requesttyp": [0, 8, 11, 12], "delet": [0, 2, 7, 8, 11, 12], "server_api": [0, 11, 12], "get": [0, 2, 5, 7, 8, 9, 12], "patch": [0, 8, 11, 12], "post": [0, 8, 11, 12], "put": [0, 8, 11, 12], "base_url": [0, 8, 11, 12], "sender_typ": [0, 8, 11, 12], "sender": [0, 3, 8, 11, 12], "create_sess": [0, 8, 11, 12], "true": [0, 2, 3, 5, 8, 9], "timeout": [0, 8, 9, 11, 12], "max_retri": [0, 8, 11, 12], "handler": [0, 8], "connect": [0, 2, 5, 7, 8, 9], "requir": [0, 2, 8], "us": [0, 2, 4, 5, 7, 8, 9], "graphql": [0, 2, 8, 9, 11, 12], "caus": [0, 8], "session": [0, 2, 7, 8], "http": [0, 8], "localhost": [0, 8], "5000": [0, 8], "option": [0, 2, 5, 7, 8, 9], "access": [0, 2, 5, 7, 8], "kei": [0, 2, 5, 7, 8, 9], "uniqu": [0, 7, 8], "name": [0, 2, 5, 7, 8, 9], "site": [0, 8, 9], "should": [0, 2, 5, 7, 8, 9], "when": [0, 2, 4, 8, 9], "from": [0, 2, 5, 7, 8, 9, 11], "machin": [0, 8], "under": [0, 2, 5, 7, 8], "version": [0, 2, 4, 7, 8, 9, 11, 12], "client": [0, 8], "applic": [0, 8], "desktop": [0, 8], "liter": [0, 8], "product": [0, 2, 7, 8, 9], "stage": [0, 8], "variant": [0, 8, 9], "default": [0, 2, 5, 8, 9], "won": [0, 2, 8], "t": [0, 2, 5, 7, 8, 9], "ani": [0, 2, 5, 7, 8, 9], "type": [0, 2, 5, 7, 8, 9], "request": [0, 4, 8, 9], "log": [0, 2, 8, 9, 11, 12], "propag": [0, 2, 8], "event": [0, 2, 8, 9, 11, 12], "more": [0, 8], "specif": [0, 8], "than": [0, 8], "e": [0, 2, 4, 5, 7, 8, 9], "g": [0, 2, 4, 5, 7, 8, 9], "union": [0, 2, 5, 7, 8, 9], "bool": [0, 2, 5, 7, 8, 9], "verifi": [0, 8], "ssl": [0, 8], "certif": [0, 8], "look": [0, 5, 8, 9], "env": [0, 8], "valu": [0, 2, 5, 7, 8, 9], "ayon_ca_fil": [0, 8], "path": [0, 2, 5, 8, 9], "file": [0, 7, 8, 9], "ayon_cert_fil": [0, 8], "float": [0, 8, 9], "int": [0, 2, 5, 7, 8, 9], "number": [0, 5, 8, 9], "retri": [0, 8], "properti": [0, 2, 5, 7, 8, 9], "access_token": [0, 8, 11, 12], "author": [0, 7, 8], "return": [0, 2, 5, 7, 8, 9], "string": [0, 5, 8, 9], "yet": [0, 4, 7, 8, 9], "as_usernam": [0, 8, 11, 12], "ignore_service_error": [0, 8], "fals": [0, 5, 6, 8, 9], "servic": [0, 8, 9], "temporarili": [0, 8], "work": [0, 8, 9], "other": [0, 2, 7, 8], "thi": [0, 2, 4, 5, 7, 8, 9, 11], "onli": [0, 2, 5, 7, 8], "ignor": [0, 2, 8], "error": [0, 4, 8, 9], "rais": [0, 2, 5, 8, 9], "valueerror": [0, 2, 5, 8, 9], "authent": [0, 8], "check_bundle_compat": [0, 8, 11, 12], "addon_vers": [0, 8, 11, 12], "installer_vers": [0, 8], "dependency_packag": [0, 8], "is_product": [0, 8], "is_stag": [0, 8], "is_dev": [0, 8], "dev_active_us": [0, 8], "dev_addons_config": [0, 8], "check": [0, 5, 7, 8, 9], "bundl": [0, 8, 9], "compat": [0, 8], "per": [0, 8], "flight": [0, 8], "valid": [0, 2, 7, 8, 9], "befor": [0, 2, 5, 8], "dict": [0, 2, 5, 7, 8, 9], "addon": [0, 8], "instal": [0, 8, 11], "depend": [0, 3, 8, 9], "ar": [0, 2, 5, 7, 8, 9], "platform": [0, 8, 9], "mark": [0, 2, 8, 9], "dev": [0, 8], "assign": [0, 8], "configur": [0, 2, 8], "respons": [0, 4, 5, 8, 9], "success": [0, 8], "issu": [0, 8], "ayon": [0, 2, 8, 9, 10], "build": [0, 8], "close_sess": [0, 8, 11, 12], "create_act": [0, 8, 11, 12], "project_nam": [0, 2, 3, 4, 7, 8, 9], "entity_id": [0, 2, 7, 8, 9], "entity_typ": [0, 2, 7, 8], "activity_typ": [0, 8], "activitytyp": [0, 8], "activity_id": [0, 8], "bodi": [0, 2, 8], "file_id": [0, 8], "list": [0, 2, 7, 8], "timestamp": [0, 8, 9], "data": [0, 2, 5, 7, 8, 9], "activ": [0, 2, 7, 8], "project": [0, 2, 7, 8, 9], "happen": [0, 2, 7, 8, 9], "entiti": [0, 2, 7, 8, 9], "id": [0, 2, 5, 7, 8, 9], "attach": [0, 8], "addit": [0, 7, 8], "create_bundl": [0, 8, 11, 12], "cannot": [0, 2, 4, 8], "onc": [0, 2, 8], "isproduct": [0, 8], "isstag": [0, 8], "after": [0, 2, 7, 8], "creation": [0, 2, 7, 8], "In": [0, 2, 5, 8], "case": [0, 2, 5, 8, 9], "possibl": [0, 2, 4, 8], "anyth": [0, 7, 8], "time": [0, 7, 8], "develop": [0, 8], "config": [0, 2, 8], "defin": [0, 2, 8], "custom": [0, 2, 7, 8], "code": [0, 2, 8], "It": [0, 2, 8], "json": [0, 2, 7, 8], "core": [0, 8], "enabl": [0, 5, 8], "create_dependency_packag": [0, 8, 11, 12], "filenam": [0, 8], "python_modul": [0, 8], "source_addon": [0, 8], "checksum": [0, 8], "checksum_algorithm": [0, 8], "file_s": [0, 8], "platform_nam": [0, 8, 9], "The": [0, 2, 7, 8], "upload": [0, 8, 9], "archiv": [0, 8], "upload_dependency_packag": [0, 8, 11, 12], "python": [0, 4, 8, 10], "modul": [0, 11, 12], "wa": [0, 2, 5, 7, 8, 9], "where": [0, 2, 5, 8, 9], "algorithm": [0, 8], "calcul": [0, 5, 8], "size": [0, 8, 9], "inform": [0, 2, 7, 8, 9], "about": [0, 2, 5, 8], "target": [0, 8, 9], "create_fold": [0, 7, 8, 11, 12], "folder_typ": [0, 2, 7, 8], "parent_id": [0, 2, 7, 8], "label": [0, 2, 7, 8], "attrib": [0, 2, 7, 8], "tag": [0, 2, 7, 8], "statu": [0, 2, 7, 8], "thumbnail_id": [0, 2, 7, 8, 9], "folder_id": [0, 2, 4, 7, 8], "folder": [0, 2, 7, 8, 9], "parent": [0, 2, 5, 7, 8], "attribut": [0, 2, 7, 8, 12], "iter": [0, 2, 5, 7, 8], "state": [0, 2, 7, 8], "thumbnail": [0, 2, 7, 8, 9], "pass": [0, 2, 5, 7, 8, 9], "gener": [0, 2, 7, 8, 9, 11], "create_instal": [0, 8, 11, 12], "python_vers": [0, 8], "runtime_python_modul": [0, 8], "step": [0, 8], "metadata": [0, 7, 8], "make": [0, 8], "sure": [0, 8], "upload_instal": [0, 8, 11, 12], "runtim": [0, 8], "ad": [0, 2, 5, 7, 8], "pythonpath": [0, 8], "subprocess": [0, 8], "download": [0, 8, 9], "create_link": [0, 8, 11, 12], "link_type_nam": [0, 8], "input_id": [0, 8], "input_typ": [0, 8], "output_id": [0, 8], "output_typ": [0, 8], "link_nam": [0, 8], "link": [0, 8], "between": [0, 4, 8], "2": [0, 5, 8, 9], "ha": [0, 2, 5, 8], "must": [0, 2, 5, 7, 8, 9], "alreadi": [0, 2, 5, 8, 9], "exist": [0, 2, 7, 8], "output": [0, 5, 8, 9], "59a212c0d2e211eda0e20242ac120002": [0, 8], "input": [0, 8, 9], "1": [0, 8, 9], "0": [0, 2, 8, 9], "rc": [0, 8], "6": [0, 2, 8], "httprequesterror": [0, 4, 8, 12], "create_link_typ": [0, 8, 11, 12], "updat": [0, 2, 7, 8], "becaus": [0, 5, 8], "relat": [0, 2, 7, 8], "create_product": [0, 7, 8, 11, 12], "product_typ": [0, 2, 7, 8], "product_id": [0, 2, 7, 8], "create_project": [0, 8, 11, 12], "project_cod": [0, 2, 8], "library_project": [0, 8], "preset_nam": [0, 8], "function": [0, 2, 8, 9], "blindli": [0, 8], "minimum": [0, 8], "enter": [0, 2, 5, 8], "here": [0, 8], "op": [0, 8], "v4": [0, 8], "readi": [0, 8], "v3": [0, 8], "logic": [0, 2, 8], "do": [0, 5, 7, 8], "That": [0, 2, 5, 8], "why": [0, 8, 9], "inner": [0, 8], "import": [0, 8, 11], "too": [0, 5, 8], "librari": [0, 2, 8], "anatomi": [0, 8], "preset": [0, 8], "create_represent": [0, 7, 8, 11, 12], "version_id": [0, 7, 8], "representation_id": [0, 7, 8], "represent": [0, 2, 7, 8, 9], "ignore_exist": [0, 8], "forc": [0, 8], "help": [0, 8], "keep": [0, 5, 8], "without": [0, 2, 8], "need": [0, 4, 5, 8], "reconnect": [0, 8], "each": [0, 2, 7, 8], "close": [0, 8], "create_task": [0, 7, 8, 11, 12], "task_typ": [0, 2, 7, 8], "assigne": [0, 2, 7, 8], "task_id": [0, 2, 7, 8], "task": [0, 2, 5, 7, 8, 9], "create_thumbnail": [0, 8, 11, 12], "src_filepath": [0, 8], "filepath": [0, 7, 8, 9], "prepar": [0, 5, 7, 8, 9], "create_vers": [0, 7, 8, 11, 12], "default_download_chunk_s": [0, 8, 11, 12], "1048576": [0, 8], "default_upload_chunk_s": [0, 8, 11, 12], "entrypoint": [0, 8], "kwarg": [0, 2, 5, 7, 8], "delete_act": [0, 8, 11, 12], "remov": [0, 2, 7, 8, 9], "delete_addon": [0, 8, 11, 12], "addon_nam": [0, 8, 11, 12], "purg": [0, 8], "all": [0, 2, 5, 7, 8, 11], "delete_addon_vers": [0, 8, 11, 12], "delete_bundl": [0, 8, 11, 12], "bundle_nam": [0, 8], "delete_dependency_packag": [0, 8, 11, 12], "deprec": [0, 2, 8], "delete_ev": [0, 8, 11, 12], "event_id": [0, 8], "support": [0, 2, 4, 8], "sinc": [0, 8], "restapirespons": [0, 8, 12], "delete_fold": [0, 7, 8, 11, 12], "children": [0, 2, 5, 8], "delete_instal": [0, 8, 11, 12], "delete_link": [0, 8, 11, 12], "link_id": [0, 8], "delete_link_typ": [0, 8, 11, 12], "delete_product": [0, 7, 8, 11, 12], "delete_project": [0, 8, 11, 12], "complet": [0, 8], "back": [0, 8], "delete_represent": [0, 7, 8, 11, 12], "delete_secret": [0, 8, 11, 12], "secret_nam": [0, 8], "secret": [0, 8], "delete_task": [0, 7, 8, 11, 12], "delete_vers": [0, 7, 8, 11, 12], "dispatch_ev": [0, 8, 11, 12], "topic": [0, 3, 8], "event_hash": [0, 3, 8], "depends_on": [0, 8], "descript": [0, 2, 3, 7, 8, 9], "summari": [0, 3, 8], "payload": [0, 3, 8], "finish": [0, 3, 8, 9], "dispatch": [0, 8], "filter": [0, 5, 8], "listen": [0, 2, 8], "hash": [0, 8], "add": [0, 2, 5, 7, 8, 9], "anoth": [0, 8], "trigger": [0, 7, 8], "simpl": [0, 8], "full": [0, 8], "detail": [0, 8], "queue": [0, 8], "futur": [0, 7, 8], "otherwis": [0, 8, 9], "send": [0, 8], "download_addon_private_fil": [0, 8, 11, 12], "destination_dir": [0, 8], "destination_filenam": [0, 8], "chunk_siz": [0, 8, 9], "progress": [0, 2, 5, 8, 9], "privat": [0, 2, 8], "restpoint": [0, 8], "destin": [0, 8, 9], "chunk": [0, 8, 9], "transferprogress": [0, 8, 9, 11, 12], "give": [0, 2, 8], "abil": [0, 2, 8], "track": [0, 5, 8], "download_dependency_packag": [0, 8, 11, 12], "src_filenam": [0, 8], "dst_directori": [0, 8], "dst_filenam": [0, 8], "pacakg": [0, 8], "For": [0, 8], "lower": [0, 2, 8, 9], "download_fil": [0, 8, 11, 12], "endpoint": [0, 4, 8], "start": [0, 2, 8, 9, 12], "thread": [0, 8], "want": [0, 7, 8], "catch": [0, 8], "over": [0, 2, 8], "receiv": [0, 2, 5, 8, 9], "singl": [0, 5, 7, 8], "loop": [0, 8], "download_file_to_stream": [0, 8, 11, 12], "stream": [0, 8, 9], "iostream": [0, 8], "io": [0, 8], "bytesio": [0, 8], "binaryio": [0, 8], "download_instal": [0, 8, 11, 12], "dst_filepath": [0, 8], "enroll_event_job": [0, 8, 11, 12], "source_top": [0, 8], "target_top": [0, 8], "sequenti": [0, 8], "events_filt": [0, 8], "ignore_older_than": [0, 8], "ignore_sender_typ": [0, 8], "enrol": [0, 8], "job": [0, 8], "find": [0, 2, 8], "first": [0, 2, 7, 8, 9], "unprocess": [0, 8], "control": [0, 8], "block": [0, 2, 8], "while": [0, 8], "least": [0, 8], "one": [0, 2, 5, 8], "unfinish": [0, 8], "order": [0, 5, 6, 7, 8, 9], "matter": [0, 8], "you": [0, 7, 8], "re": [0, 2, 8], "done": [0, 2, 8, 9], "clash": [0, 8], "dependson": [0, 8], "my": [0, 8, 9], "leech": [0, 8], "n": [0, 8, 9], "sequenc": [0, 8], "so": [0, 2, 8], "actual": [0, 8], "goe": [0, 8], "down": [0, 2, 8], "take": [0, 8, 9], "place": [0, 8], "3": [0, 2, 8, 9], "discov": [0, 8], "identifi": [0, 7, 8], "human": [0, 8], "readabl": [0, 8], "text": [0, 8, 9], "shown": [0, 8], "condit": [0, 8], "technic": [0, 8], "ayon_serv": [0, 8], "sqlfilter": [0, 8], "how": [0, 5, 8], "mani": [0, 5, 8], "pr": [0, 8], "older": [0, 2, 8], "given": [0, 8], "dai": [0, 8], "match": [0, 8], "get_act": [0, 8, 11, 12], "entity_nam": [0, 8], "changed_aft": [0, 8], "changed_befor": [0, 8], "reference_typ": [0, 8], "activityreferencetyp": [0, 8], "field": [0, 5, 6, 8, 9], "limit": [0, 5, 8], "sortord": [0, 8, 9, 11, 12], "iso": [0, 8], "datetim": [0, 8], "refer": [0, 8, 11], "origin": [0, 2, 8], "fetch": [0, 2, 8], "ascend": [0, 8, 9, 11, 12], "descend": [0, 8, 9, 11, 12], "recommend": [0, 2, 8], "get_activity_by_id": [0, 8, 11, 12], "found": [0, 2, 8], "get_addon_endpoint": [0, 8, 11, 12], "subpath": [0, 8], "rout": [0, 8], "your": [0, 8], "com": [0, 8, 11], "get_addon_url": [0, 8, 11, 12], "zip": [0, 8], "amount": [0, 8], "final": [0, 8], "get_addon_project_set": [0, 8, 11, 12], "use_sit": [0, 8], "an": [0, 2, 7, 8, 9], "mai": [0, 2, 4, 8], "overrid": [0, 7, 8], "To": [0, 2, 8], "disabl": [0, 8], "appli": [0, 8], "get_addon_set": [0, 8, 11, 12], "some": [0, 8, 9], "argument": [0, 7, 8], "A": [0, 2, 8, 9], "studio": [0, 8], "get_addon_settings_schema": [0, 8, 11, 12], "sudio": [0, 8], "schema": [0, 2, 8], "differ": [0, 7, 8], "enum": [0, 8], "get_addon_site_set": [0, 8, 11, 12], "empti": [0, 7, 8, 9], "dictionari": [0, 7, 8], "get_addon_site_settings_schema": [0, 8, 11, 12], "get_addon_studio_set": [0, 8, 11, 12], "use_rest": [0, 8], "rest": [0, 2, 5, 8, 11], "get_addons_info": [0, 8, 11, 12], "get_addons_project_set": [0, 8, 11, 12], "only_valu": [0, 8], "like": [0, 8], "contain": [0, 4, 8, 9], "behavior": [0, 8], "structur": [0, 2, 8], "get_addons_set": [0, 8, 11, 12], "univers": [0, 8], "By": [0, 5, 8], "get_addons_studio_set": [0, 8, 11, 12], "bulk": [0, 8], "get_attributes_fields_for_typ": [0, 8, 11, 12], "get_attributes_for_typ": [0, 2, 8, 11, 12], "common": [0, 8], "integ": [0, 8], "titl": [0, 4, 8, 9], "clip": [0, 8], "out": [0, 8, 9], "null": [0, 8], "These": [0, 8], "gt": [0, 8], "ge": [0, 8], "lt": [0, 8], "le": [0, 8], "minlength": [0, 8], "maxlength": [0, 8], "minitem": [0, 8], "maxitem": [0, 8], "regex": [0, 8], "get_attributes_schema": [0, 8, 11, 12], "use_cach": [0, 8], "get_base_url": [0, 8, 11, 12], "get_build_in_anatomy_preset": [0, 8, 11, 12], "built": [0, 8], "get_bundle_set": [0, 8, 11, 12], "test": [0, 8], "behav": [0, 8], "siteset": [0, 8], "get_bundl": [0, 8, 11, 12], "basic": [0, 8], "my_bundl": [0, 8], "createdat": [0, 8], "2023": [0, 8], "06": [0, 8], "12t15": [0, 8], "37": [0, 8], "02": [0, 8], "420260": [0, 8], "installervers": [0, 8], "dependencypackag": [0, 8], "window": [0, 8], "a_windows_package123": [0, 8], "linux": [0, 8], "a_linux_package123": [0, 8], "darwin": [0, 8], "a_mac_package123": [0, 8], "productionbundl": [0, 8], "stagingbundl": [0, 8], "test_bundl": [0, 8], "get_cert": [0, 8, 11, 12], "get_client_vers": [0, 8, 11, 12], "get_default_anatomy_preset_nam": [0, 8, 11, 12], "primari": [0, 8], "But": [0, 7, 8], "instead": [0, 2, 8, 9], "_": [0, 8, 9], "get_project_anatomy_preset": [0, 8, 11, 12], "get_default_fields_for_typ": [0, 8, 11, 12], "most": [0, 8], "commonli": [0, 8], "queri": [0, 2, 4, 5, 8, 9], "classmethod": [0, 2, 8, 9], "get_default_max_retri": [0, 8, 11, 12], "max": [0, 8], "server_retries_env_kei": [0, 8], "affect": [0, 8, 9], "_default_max_retri": [0, 8], "get_default_service_usernam": [0, 8, 11, 12], "callback": [0, 8], "get_default_settings_vari": [0, 8, 9, 11, 12], "get_default_timeout": [0, 8, 9, 11, 12], "util": [0, 8, 11, 12], "second": [0, 8, 9], "get_dependency_packag": [0, 8, 11, 12], "checksumalgorithm": [0, 8], "supportedaddon": [0, 8], "pythonmodul": [0, 8], "known": [0, 2, 4, 8], "get_entities_link": [0, 8, 11, 12], "link_typ": [0, 8], "link_direct": [0, 8], "link_name_regex": [0, 8], "helper": [0, 2, 8], "59a212c0d2e211eda0e20242ac120001": [0, 8], "linktyp": [0, 8], "projectnam": [0, 8], "my_project": [0, 8], "frantadmin": [0, 8], "entityid": [0, 8], "b1df109676db11ed8e8c6c9466b19aa8": [0, 8], "entitytyp": [0, 2, 8], "direct": [0, 8], "param": [0, 8], "info": [0, 7, 8], "get_ev": [0, 8, 11, 12], "explicitli": [0, 7, 8], "status": [0, 2, 8], "include_log": [0, 8], "has_children": [0, 8], "newer_than": [0, 8], "older_than": [0, 8], "Not": [0, 8], "who": [0, 8], "newer": [0, 8], "get_folder_by_id": [0, 2, 8, 11, 12], "own_attribut": [0, 8], "get_folder_by_nam": [0, 8, 11, 12], "folder_nam": [0, 8], "kept": [0, 8], "openpyp": [0, 8], "get_folder_by_path": [0, 8, 11, 12], "folder_path": [0, 8], "join": [0, 8, 9], "slash": [0, 2, 8], "get_folder_ids_with_product": [0, 8, 11, 12], "immut": [0, 2, 8], "thei": [0, 2, 7, 8], "get_folder_link": [0, 8, 11, 12], "get_folder_thumbnail": [0, 8, 11, 12], "locat": [0, 8], "cach": [0, 2, 8], "doe": [0, 2, 4, 7, 8], "permiss": [0, 8], "get_fold": [0, 8, 11, 12], "folder_path_regex": [0, 8], "has_product": [0, 8], "has_task": [0, 8], "assignees_al": [0, 8], "has_link": [0, 8], "we": [0, 8], "don": [0, 7, 8], "child": [0, 2, 8], "assig": [0, 8], "inact": [0, 8], "both": [0, 5, 8], "IN": [0, 8], "get_folders_hierarchi": [0, 8, 11, 12], "search_str": [0, 8], "hierarchi": [0, 2, 8], "foldertyp": [0, 8], "hastask": [0, 8], "tasknam": [0, 8], "parentid": [0, 8], "search": [0, 8, 11], "get_folders_link": [0, 8, 11, 12], "get_folders_rest": [0, 8, 11, 12], "include_attrib": [0, 8], "simplifi": [0, 2, 8], "flat": [0, 8], "faster": [0, 8], "allow": [0, 2, 8, 9], "backend": [0, 8], "112233445566": [0, 8], "112233445567": [0, 8], "root": [0, 2, 8], "haschildren": [0, 8], "composit": [0, 8], "ownattrib": [0, 8], "updatedat": [0, 8], "get_rest_fold": [0, 8, 11, 12], "renam": [0, 8], "get_rest_project": [0, 8, 11, 12], "etc": [0, 5, 7, 8], "Will": [0, 8], "7": [0, 8], "includ": [0, 8], "slower": [0, 8], "get_full_link_type_nam": [0, 8, 11, 12], "get_graphql_schema": [0, 8, 11, 12], "get_head": [0, 8, 11, 12], "content_typ": [0, 8, 9], "get_hero_version_by_id": [0, 8, 11, 12], "hero": [0, 7, 8], "get_hero_version_by_product_id": [0, 8, 11, 12], "get_hero_vers": [0, 8, 11, 12], "multipl": [0, 2, 5, 7, 8], "get_info": [0, 8, 11, 12], "uptim": [0, 8], "With": [0, 8], "get_us": [0, 8, 11, 12], "get_instal": [0, 8, 11, 12], "artist": [0, 7, 8], "get_last_version_by_product_id": [0, 8, 11, 12], "last": [0, 8], "get_last_version_by_product_nam": [0, 8, 11, 12], "product_nam": [0, 8], "get_last_vers": [0, 8, 11, 12], "get_link_typ": [0, 8, 11, 12], "There": [0, 8], "dedic": [0, 8], "get_max_retri": [0, 8, 11, 12], "get_product_by_id": [0, 2, 8, 11, 12], "get_product_by_nam": [0, 8, 11, 12], "get_product_link": [0, 8, 11, 12], "get_product_type_nam": [0, 8, 11, 12], "probabl": [0, 8], "products_id": [0, 8], "real": [0, 8], "get_product_typ": [0, 2, 8, 11, 12], "wide": [0, 8], "icon": [0, 2, 8], "color": [0, 2, 8], "get_product": [0, 8, 11, 12], "product_name_regex": [0, 8], "product_path_regex": [0, 8], "names_by_folder_id": [0, 8], "separ": [0, 2, 8, 9], "name_by_folder_id": [0, 8], "combin": [0, 8], "end": [0, 8], "get_products_link": [0, 8, 11, 12], "get_project": [0, 7, 8, 11, 12], "content": [0, 2, 8, 9], "netflix_vfx": [0, 8], "get_project_nam": [0, 8, 11, 12], "standard": [0, 7, 8], "get_project_product_typ": [0, 8, 11, 12], "get_project_root_overrid": [0, 8, 11, 12], "get_project_root_overrides_by_site_id": [0, 8, 11, 12], "get_project_roots_by_platform": [0, 8, 11, 12], "get_project_roots_by_sit": [0, 8, 11, 12], "get_project_roots_by_site_id": [0, 8, 11, 12], "get_project_roots_for_sit": [0, 8, 11, 12], "get_repre_ids_by_context_filt": [0, 8, 11, 12], "context_filt": [0, 8], "representation_nam": [0, 8], "context": [0, 8], "integr": [0, 8], "databas": [0, 7, 8], "implement": [0, 8], "quickli": [0, 8], "nest": [0, 2, 5, 8], "subfield": [0, 8], "dot": [0, 8], "delimit": [0, 8], "pattern": [0, 8], "just": [0, 5, 8], "funtion": [0, 8], "testproject": [0, 8], "aa": [0, 8], "nimat": [0, 8], "mm": [0, 8], "ain": [0, 8], "repre_id": [0, 8], "repr": [0, 8], "get_represent": [0, 8, 11, 12], "get_representation_by_id": [0, 8, 11, 12], "get_representation_by_nam": [0, 8, 11, 12], "get_representation_hierarchi": [0, 8, 11, 12], "project_field": [0, 8], "folder_field": [0, 6, 8], "task_field": [0, 6, 8], "product_field": [0, 6, 8], "version_field": [0, 6, 8], "representation_field": [0, 6, 8], "up": [0, 8], "representationhierarchi": [0, 8, 9, 12], "get_representation_link": [0, 8, 11, 12], "get_representation_par": [0, 8, 11, 12], "representationpar": [0, 8, 9, 12], "names_by_version_id": [0, 8], "discard": [0, 2, 8], "get_representations_hierarchi": [0, 8, 11, 12], "tupl": [0, 8, 9], "get_representations_link": [0, 8, 11, 12], "get_representations_par": [0, 8, 11, 12], "get_rest_entity_by_id": [0, 8, 11, 12], "its": [0, 2, 8], "get_rest_product": [0, 8, 11, 12], "get_rest_represent": [0, 8, 11, 12], "get_rest_task": [0, 8, 11, 12], "get_rest_url": [0, 8, 11, 12], "get_rest_vers": [0, 8, 11, 12], "get_schema": [0, 8, 11, 12], "compon": [0, 8], "projectmodel": [0, 8], "map": [0, 2, 8], "definit": [0, 2, 5, 8], "get_secret": [0, 8, 11, 12], "secret_valu": [0, 8], "secret_1": [0, 8], "secret_value_1": [0, 8], "secret_2": [0, 8], "secret_value_2": [0, 8], "get_send": [0, 8, 11, 12], "get_sender_typ": [0, 8, 11, 12], "5": [0, 8, 9], "get_server_schema": [0, 8, 11, 12], "outdat": [0, 8], "get_server_vers": [0, 8, 11, 12], "semant": [0, 8], "semver": [0, 8], "org": [0, 8], "get_server_version_tupl": [0, 8, 11, 12], "three": [0, 8], "get_site_id": [0, 8, 11, 12], "tell": [0, 8], "get_ssl_verifi": [0, 8, 11, 12], "verif": [0, 8], "get_task_by_folder_path": [0, 8, 11, 12], "task_nam": [0, 8], "get_task_by_id": [0, 2, 8, 11, 12], "get_task_by_nam": [0, 8, 11, 12], "get_task_link": [0, 8, 11, 12], "get_task": [0, 8, 11, 12], "get_tasks_by_folder_path": [0, 8, 11, 12], "get_tasks_link": [0, 8, 11, 12], "get_thumbnail": [0, 8, 11, 12], "get_version_thumbnail": [0, 8, 11, 12], "get_workfile_thumbnail": [0, 8, 11, 12], "thumbnailid": [0, 8], "repres": [0, 2, 5, 7, 8], "get_thumbnail_by_id": [0, 8, 11, 12], "wrapper": [0, 2, 8, 9], "thumbnailcont": [0, 8, 9, 12], "get_timeout": [0, 8, 11, 12], "endpoit": [0, 8], "get_user_by_nam": [0, 8, 11, 12], "administr": [0, 8], "manag": [0, 8], "scope": [0, 2, 8], "get_version_by_id": [0, 2, 8, 11, 12], "get_version_by_nam": [0, 8, 11, 12], "get_version_link": [0, 8, 11, 12], "get_vers": [0, 2, 8, 11, 12], "latest": [0, 8, 11], "interest": [0, 8], "non": [0, 8], "get_versions_link": [0, 8, 11, 12], "get_workfile_info": [0, 8, 11, 12], "workfil": [0, 2, 7, 8], "rootless": [0, 7, 8], "get_workfile_info_by_id": [0, 8, 11, 12], "workfile_id": [0, 8], "worfil": [0, 8], "get_workfiles_info": [0, 8, 11, 12], "path_regex": [0, 8], "entit": [0, 8], "graphql_allows_data_in_queri": [0, 8, 11, 12], "still": [0, 5, 8], "has_valid_token": [0, 8, 11, 12], "is_server_avail": [0, 8, 11, 12], "is_service_us": [0, 8, 11, 12], "belong": [0, 8], "authenticationerror": [0, 4, 8, 12], "fail": [0, 8, 9, 11, 12], "logout": [0, 8, 9, 11, 12], "soft": [0, 8], "make_sure_link_type_exist": [0, 8, 11, 12], "query_graphql": [0, 8, 11, 12], "execut": [0, 8], "graphqlrespons": [0, 8, 12], "raw_delet": [0, 8, 11, 12], "raw_get": [0, 8, 11, 12], "raw_patch": [0, 8, 11, 12], "raw_post": [0, 8, 11, 12], "raw_put": [0, 8, 11, 12], "remove_attribute_config": [0, 8, 11, 12], "attribute_nam": [0, 8], "un": [0, 8], "pleas": [0, 8], "carefulli": [0, 8], "reset_attributes_schema": [0, 8, 11, 12], "reset_token": [0, 8, 11, 12], "rest_url": [0, 8, 11, 12], "save_secret": [0, 8, 11, 12], "save": [0, 2, 8], "send_activities_batch_oper": [0, 8, 11, 12], "oper": [0, 2, 4, 8, 11, 12], "can_fail": [0, 8], "raise_on_fail": [0, 8], "crud": [0, 8], "made": [0, 2, 8], "side": [0, 8], "best": [0, 8], "wai": [0, 7, 8], "go": [0, 8], "transact": [0, 8], "On": [0, 7, 8], "try": [0, 2, 8, 9], "even": [0, 8], "them": [0, 2, 8], "except": [0, 8, 9, 11, 12], "own": [0, 8], "convert": [0, 2, 7, 8, 9], "failedoper": [0, 4, 8, 12], "result": [0, 5, 8, 9], "send_batch_oper": [0, 8, 11, 12], "server_vers": [0, 8, 11, 12], "server_version_tupl": [0, 8, 11, 12], "set_attribute_config": [0, 8, 11, 12], "posit": [0, 8], "builtin": [0, 8], "set_cert": [0, 8, 11, 12], "set_client_vers": [0, 8, 11, 12], "set_default_service_usernam": [0, 8, 11, 12], "temporari": [0, 8], "as_us": [0, 8], "set_default_settings_vari": [0, 8, 11, 12], "set_max_retri": [0, 8, 11, 12], "set_send": [0, 8, 11, 12], "set_sender_typ": [0, 8, 11, 12], "set_site_id": [0, 8, 11, 12], "getter": [0, 8], "unset": [0, 2, 7, 8], "set_ssl_verifi": [0, 8, 11, 12], "set_timeout": [0, 8, 11, 12], "set_token": [0, 8, 11, 12], "trigger_server_restart": [0, 8, 11, 12], "restart": [0, 8], "update_act": [0, 8, 11, 12], "append_file_id": [0, 8], "append": [0, 7, 8], "update_bundl": [0, 8, 11, 12], "left": [0, 8], "untouch": [0, 8], "update_dependency_packag": [0, 8, 11, 12], "fulli": [0, 8], "replac": [0, 8, 9], "update_ev": [0, 8, 11, 12], "pend": [0, 8], "in_progress": [0, 2, 8], "abort": [0, 8, 9], "rang": [0, 8], "100": [0, 8], "update_fold": [0, 7, 8, 11, 12], "amd": [0, 7, 8], "would": [0, 2, 7, 8], "update_instal": [0, 8, 11, 12], "update_product": [0, 7, 8, 11, 12], "update_project": [0, 8, 11, 12], "templat": [0, 8], "dure": [0, 5, 8], "document": [0, 8], "update_represent": [0, 7, 8, 11, 12], "update_task": [0, 7, 8, 11, 12], "update_thumbnail": [0, 8, 11, 12], "update_vers": [0, 7, 8, 11, 12], "upload_addon_zip": [0, 8, 11, 12], "part": [0, 8], "eventid": [0, 8], "a1bfbdee27c611eea7580242ac120003": [0, 8], "upload_fil": [0, 8, 11, 12], "request_typ": [0, 8], "upload_file_from_stream": [0, 8, 11, 12], "byte": [0, 8, 9], "upload_review": [0, 8, 11, 12], "header": [0, 5, 8], "review": [0, 8], "automat": [0, 8], "mime": [0, 8, 9], "validate_server_avail": [0, 8, 11, 12], "validate_token": [0, 8, 11, 12], "version_is_latest": [0, 8, 11, 12], "servicecontext": [0, 11, 12], "initi": 0, "know": [0, 2], "what": 0, "init_servic": [0, 11, 12], "load": 0, "purpos": [0, 5, 7], "server_url": [0, 9, 11, 12], "service_nam": [0, 11, 12], "intenum": [0, 9], "sort": [0, 9], "parse_valu": [0, 9, 11, 12], "add_transferred_chunk": [0, 9, 11, 12], "transfer": [0, 9], "content_s": [0, 9, 11, 12], "unknown": [0, 9], "destination_url": [0, 9, 11, 12], "consid": [0, 2, 7, 9], "set_source_url": [0, 9, 11, 12], "fail_reason": [0, 9, 11, 12], "reason": [0, 9], "get_content_s": [0, 9, 11, 12], "get_destination_url": [0, 9, 11, 12], "get_fail_reason": [0, 9, 11, 12], "get_fail": [0, 9, 11, 12], "get_source_url": [0, 9, 11, 12], "get_start": [0, 9, 11, 12], "get_transfer_don": [0, 9, 11, 12], "get_transferred_s": [0, 9, 11, 12], "is_run": [0, 9, 11, 12], "set_content_s": [0, 9, 11, 12], "set_destination_url": [0, 9, 11, 12], "set_fail": [0, 9, 11, 12], "set_start": [0, 9, 11, 12], "set_transfer_don": [0, 9, 11, 12], "wasn": [0, 9], "set_transferred_s": [0, 9, 11, 12], "source_url": [0, 9, 11, 12], "transfer_don": [0, 9, 11, 12], "transfer_progress": [0, 9, 11, 12], "percent": [0, 9], "transferred_s": [0, 9, 11, 12], "abort_web_action_ev": [0, 9, 11, 12], "action_token": [0, 9], "web": [0, 9], "action": [0, 9], "could": [0, 2, 9], "webact": [0, 9], "change_token": [0, 11, 12], "close_connect": [0, 11, 12], "global": [0, 7], "create_connect": [0, 11, 12], "app": 0, "create_dependency_package_basenam": [0, 9, 11, 12], "basenam": [0, 9], "get_server_api_connect": [0, 11, 12], "get_service_addon_nam": [0, 11, 12], "abl": [0, 2], "get_service_addon_set": [0, 11, 12], "get_service_addon_vers": [0, 11, 12], "get_service_nam": [0, 11, 12], "regist": [0, 7], "get_user_by_token": [0, 9, 11, 12], "specifi": [0, 9], "arg": [0, 2, 5, 7], "is_connection_cr": [0, 11, 12], "is_token_valid": [0, 9, 11, 12], "login_to_serv": [0, 9, 11, 12], "successful": [0, 9], "slugify_str": [0, 9, 11, 12], "input_str": [0, 9], "slug_whitelist": [0, 9], "abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz0123456789": [0, 9], "split_char": [0, 9], "min_length": [0, 9], "make_set": [0, 9], "slugifi": [0, 2, 9], "transliter": [0, 9], "ascii": [0, 9], "special": [0, 7, 9], "charact": [0, 9], "element": [0, 9], "letter": [0, 2, 9], "digit": [0, 9], "word": [0, 9], "split": [0, 9], "sane": [0, 9], "minim": [0, 9], "length": [0, 9], "take_web_action_ev": [0, 9, 11, 12], "launcher": [0, 9], "validate_url": [0, 9, 11, 12], "pars": [0, 2, 4, 5, 9], "scheme": [0, 9], "autofix": [0, 9], "thu": [0, 9], "modifi": [0, 9], "my_url": [0, 9], "validated_url": [0, 9], "urlerror": [0, 4, 9, 12], "invalid": [0, 2, 9], "short": [0, 2, 9], "hint": [0, 4, 9], "constant": [0, 11, 12], "entity_hub": [0, 11, 12], "attributevalu": [0, 2, 12], "get_valu": [0, 2], "lock": [0, 2], "set_valu": [0, 2, 7], "get_attribut": [0, 2], "item": [0, 2, 5], "to_dict": [0, 2], "baseent": [0, 2, 12], "add_child": [0, 2], "children_id": [0, 2], "fill_children_id": [0, 2], "from_entity_data": [0, 2], "get_children": [0, 2], "get_children_id": [0, 2], "get_label": [0, 2], "get_nam": [0, 2, 5], "get_par": [0, 2], "get_parent_id": [0, 2], "get_statu": [0, 2], "get_tag": [0, 2], "get_thumbnail_id": [0, 2], "has_cached_immutable_hierarchi": [0, 2], "immutable_for_hierarchi": [0, 2], "orig_parent_id": [0, 2], "parent_entity_typ": [0, 2], "remove_child": [0, 2], "reset_immutable_for_hierarchy_cach": [0, 2], "set_label": [0, 2], "set_nam": [0, 2], "set_par": [0, 2, 5], "set_parent_id": [0, 2], "set_statu": [0, 2], "set_tag": [0, 2], "set_thumbnail_id": [0, 2], "to_create_body_data": [0, 2], "entitydata": [0, 2, 12], "get_chang": [0, 2], "get_new_entity_valu": [0, 2], "entityhub": [0, 2, 12], "add_ent": [0, 2], "add_fold": [0, 2], "add_new_fold": [0, 2], "add_new_product": [0, 2], "add_new_task": [0, 2], "add_new_vers": [0, 2], "add_product": [0, 2], "add_task": [0, 2], "add_vers": [0, 2], "allow_data_chang": [0, 2], "commit_chang": [0, 2], "delete_ent": [0, 2, 7], "fetch_hierarchy_ent": [0, 2], "fill_project_from_serv": [0, 2], "folder_path_reset": [0, 2], "get_entity_by_id": [0, 2], "get_entity_children": [0, 2], "get_or_fetch_entity_by_id": [0, 2], "get_or_query_entity_by_id": [0, 2], "path_start_with_slash": [0, 2], "project_ent": [0, 2], "query_entities_from_serv": [0, 2], "set_entity_par": [0, 2], "unset_entity_par": [0, 2], "folderent": [0, 2, 12], "get_folder_typ": [0, 2], "get_has_published_cont": [0, 2], "get_path": [0, 2], "has_published_cont": [0, 2], "reset_path": [0, 2], "set_folder_typ": [0, 2], "set_has_published_cont": [0, 2], "productent": [0, 2, 12], "get_folder_id": [0, 2], "set_folder_id": [0, 2], "set_product_typ": [0, 2], "projectent": [0, 2, 12], "default_folder_type_icon": [0, 2], "default_task_type_icon": [0, 2], "get_orig_folder_typ": [0, 2], "get_orig_status": [0, 2], "get_orig_task_typ": [0, 2], "get_status_by_slugified_nam": [0, 2], "get_status": [0, 2], "get_task_typ": [0, 2], "set_status_scope_support": [0, 2], "set_status": [0, 2], "set_task_typ": [0, 2], "projectstatu": [0, 2, 12], "color_regex": [0, 2], "default_color": [0, 2], "default_st": [0, 2], "from_data": [0, 2], "get_color": [0, 2], "get_icon": [0, 2], "get_index": [0, 2], "get_project_status": [0, 2], "get_scop": [0, 2], "get_short_nam": [0, 2], "get_stat": [0, 2], "index": [0, 2, 11], "is_available_for_entity_typ": [0, 2], "move_aft": [0, 2], "move_befor": [0, 2], "project_status": [0, 2], "set_color": [0, 2], "set_icon": [0, 2], "set_index": [0, 2], "set_project_status": [0, 2], "set_scop": [0, 2], "set_short_nam": [0, 2], "set_stat": [0, 2], "short_nam": [0, 2], "slugified_nam": [0, 2], "slugify_nam": [0, 2], "to_data": [0, 2, 3, 7], "unset_project_status": [0, 2], "valid_scop": [0, 2], "valid_st": [0, 2], "taskent": [0, 2, 12], "get_assigne": [0, 2], "set_assigne": [0, 2], "versionent": [0, 2, 12], "get_product_id": [0, 2], "get_task_id": [0, 2], "set_product_id": [0, 2], "set_task_id": [0, 2], "set_vers": [0, 2], "serverev": [0, 3, 12], "failedserviceinit": [0, 4, 12], "foldernotfound": [0, 4, 12], "graphqlqueryfail": [0, 4, 12], "missingentityerror": [0, 4, 12], "projectnotfound": [0, 4, 12], "requesterror": [0, 4, 12], "servererror": [0, 4, 12], "servernotreach": [0, 4, 12], "unauthorizederror": [0, 4, 12], "unsupportedserververs": [0, 4, 12], "basegraphqlqueryfield": [0, 5, 12], "add_field": [0, 5], "add_field_with_edg": [0, 5], "add_obj_field": [0, 5], "add_vari": [0, 5], "calculate_queri": [0, 5], "child_has_edg": [0, 5], "child_ind": [0, 5], "get_field_by_kei": [0, 5], "get_filt": [0, 5], "get_vari": [0, 5], "get_variable_valu": [0, 5], "has_edg": [0, 5], "has_filt": [0, 5], "indent": [0, 5], "need_queri": [0, 5], "offset": [0, 5], "parse_result": [0, 5], "query_item": [0, 5], "remove_filt": [0, 5], "reset_cursor": [0, 5], "set_ascending_ord": [0, 5], "set_descending_ord": [0, 5], "set_filt": [0, 5], "set_limit": [0, 5], "set_ord": [0, 5], "set_variable_valu": [0, 5], "sum_edge_field": [0, 5], "graphqlqueri": [0, 5, 12], "continuous_queri": [0, 5], "get_field_by_path": [0, 5], "get_variable_kei": [0, 5], "get_variables_valu": [0, 5], "has_multiple_edge_field": [0, 5], "graphqlqueryedgefield": [0, 5, 12], "add_edge_field": [0, 5], "add_obj_edge_field": [0, 5], "graphqlqueryfield": [0, 5, 12], "queryvari": [0, 5, 12], "variable_nam": [0, 5], "fields_to_dict": [0, 5, 12], "graphql_queri": [0, 11, 12], "activities_graphql_queri": [0, 6, 12], "add_links_field": [0, 6, 12], "events_graphql_queri": [0, 6, 12], "folders_graphql_queri": [0, 6, 12], "product_types_queri": [0, 6, 12], "products_graphql_queri": [0, 6, 12], "project_graphql_queri": [0, 6, 12], "project_product_types_queri": [0, 6, 12], "projects_graphql_queri": [0, 6, 12], "representations_graphql_queri": [0, 6, 12], "representations_hierarchy_qraphql_queri": [0, 6, 12], "representations_parents_qraphql_queri": [0, 6, 12], "tasks_by_folder_paths_graphql_queri": [0, 6, 12], "tasks_graphql_queri": [0, 6, 12], "users_graphql_queri": [0, 6, 12], "versions_graphql_queri": [0, 6, 12], "workfiles_info_graphql_queri": [0, 6, 12], "abstractoper": [0, 7, 12], "operation_nam": [0, 7], "createoper": [0, 7, 12], "con": [0, 5, 7], "to_server_oper": [0, 7], "deleteoper": [0, 7, 12], "operationssess": [0, 7, 12], "clear": [0, 2, 7], "commit": [0, 2, 7], "create_ent": [0, 7], "update_ent": [0, 7], "updateoper": [0, 7, 12], "update_data": [0, 7], "new_folder_ent": [0, 7, 12], "new_hero_version_ent": [0, 7, 12], "new_product_ent": [0, 7, 12], "new_representation_ent": [0, 7, 12], "new_version_ent": [0, 7, 12], "new_workfile_info": [0, 7, 12], "prepare_chang": [0, 7, 12], "orig_respons": [0, 8], "raise_for_statu": [0, 8], "status_cod": [0, 8], "fill_own_attrib": [0, 8, 12], "is_valid": [0, 9], "convert_entity_id": [0, 9, 12], "convert_or_create_entity_id": [0, 9, 12], "create_entity_id": [0, 9, 12], "entity_data_json_default": [0, 9, 12], "failed_json_default": [0, 9, 12], "get_default_site_id": [0, 9, 12], "get_media_mime_typ": [0, 9, 12], "get_media_mime_type_for_cont": [0, 9, 12], "get_media_mime_type_for_stream": [0, 9, 12], "logout_from_serv": [0, 9, 12], "prepare_attribute_chang": [0, 9, 12], "prepare_entity_chang": [0, 9, 12], "prepare_query_str": [0, 9, 12], "attrib_kei": 2, "unknown_valu": 2, "enhanc": 2, "keyerror": [2, 5], "ignore_non": 2, "_customnon": 2, "abc": [2, 5, 7], "captur": 2, "unless": 2, "hub": 2, "typeerror": 2, "abstract": [2, 5, 7], "mayb": 2, "themselv": 2, "newli": 2, "determinist": 2, "model": 2, "Be": 2, "awar": 2, "partial": 2, "correspond": 2, "entity_data": 2, "allow_fetch": 2, "quick": 2, "live": 2, "bottom_to_top": 2, "reset": 2, "top": 2, "bottom": 2, "status_nam": 2, "arbitrari": 2, "sceneinfo": 2, "camera": 2, "camera1": 2, "guid": 2, "were": 2, "frequent": 2, "secur": 2, "slow": 2, "consist": 2, "mean": 2, "whole": 2, "previou": [2, 5], "alwai": 2, "x": 2, "maintain": 2, "_not_set": 2, "dynamic_valu": 2, "task_alt": 2, "is_new": 2, "ip": 2, "statusst": 2, "play_arrow": 2, "eeeeee": 2, "_projectstatus": 2, "compil": 2, "f0": 2, "9": 2, "projectstatus": 2, "wrap": 2, "intern": 2, "now": 2, "move": 2, "hex": 2, "format": 2, "exampl": 2, "ff0000": 2, "comparison": 2, "v": 2, "not_start": 2, "messag": [4, 8], "incompat": 4, "anymor": 4, "fix": 4, "ui": 4, "value_typ": 5, "boolean": 5, "later": 5, "copi": 5, "edg": 5, "pagin": 5, "progress_data": 5, "debug": 5, "max_limit": 5, "yield": 5, "mid": 5, "cursor": 5, "count": 5, "counter": 5, "bigger": 5, "page": [5, 11], "sent": 5, "fiel": 5, "ident": 5, "space": 5, "varibl": 5, "entity_field": 6, "nested_field": 6, "use_st": 6, "oprat": 7, "stringifi": 7, "opeart": 7, "At": 7, "moment": 7, "sonsid": 7, "stupid": 7, "baseoper": 7, "nested_id": 7, "fast": 7, "dicst": 7, "suboper": 7, "removed_valu": 7, "level": 7, "skeleton": 7, "predefin": 7, "primarili": 7, "dic": 7, "old_ent": [7, 9], "new_ent": [7, 9], "might": 7, "provid": 8, "packag": [8, 9, 10, 11, 12], "alia": 9, "4": 9, "imag": 9, "png": 9, "server_timeout_env_kei": 9, "10": 9, "determin": 9, "throw": 9, "awai": 9, "key_valu": 9, "declar": 10, "mainli": 11, "auto": 11, "doc": 11, "pypi": 11, "pip": 11, "github": 11, "altern": 11, "git": 11, "clone": 11, "ynput": 11, "cd": 11, "ensur": 11, "properli": 11, "print": 11, "ayon_api": 11, "c": 11, "__version__": 11, "repositori": 11, "submodul": [11, 12]}, "objects": {"": [[0, 0, 0, "-", "ayon_api"]], "ayon_api": [[0, 1, 1, "", "GlobalServerAPI"], [0, 1, 1, "", "RequestTypes"], [0, 1, 1, "", "ServerAPI"], [0, 1, 1, "", "ServiceContext"], [0, 1, 1, "", "SortOrder"], [0, 1, 1, "", "TransferProgress"], [0, 5, 1, "", "abort_web_action_event"], [0, 5, 1, "", "change_token"], [0, 5, 1, "", "check_bundle_compatibility"], [0, 5, 1, "", "close_connection"], [1, 0, 0, "-", "constants"], [0, 5, 1, "", "create_activity"], [0, 5, 1, "", "create_bundle"], [0, 5, 1, "", "create_connection"], [0, 5, 1, "", "create_dependency_package"], [0, 5, 1, "", "create_dependency_package_basename"], [0, 5, 1, "", "create_folder"], [0, 5, 1, "", "create_installer"], [0, 5, 1, "", "create_link"], [0, 5, 1, "", "create_link_type"], [0, 5, 1, "", "create_product"], [0, 5, 1, "", "create_project"], [0, 5, 1, "", "create_representation"], [0, 5, 1, "", "create_task"], [0, 5, 1, "", "create_thumbnail"], [0, 5, 1, "", "create_version"], [0, 5, 1, "", "delete"], [0, 5, 1, "", "delete_activity"], [0, 5, 1, "", "delete_addon"], [0, 5, 1, "", "delete_addon_version"], [0, 5, 1, "", "delete_bundle"], [0, 5, 1, "", "delete_dependency_package"], [0, 5, 1, "", "delete_event"], [0, 5, 1, "", "delete_folder"], [0, 5, 1, "", "delete_installer"], [0, 5, 1, "", "delete_link"], [0, 5, 1, "", "delete_link_type"], [0, 5, 1, "", "delete_product"], [0, 5, 1, "", "delete_project"], [0, 5, 1, "", "delete_representation"], [0, 5, 1, "", "delete_secret"], [0, 5, 1, "", "delete_task"], [0, 5, 1, "", "delete_version"], [0, 5, 1, "", "dispatch_event"], [0, 5, 1, "", "download_addon_private_file"], [0, 5, 1, "", "download_dependency_package"], [0, 5, 1, "", "download_file"], [0, 5, 1, "", "download_file_to_stream"], [0, 5, 1, "", "download_installer"], [0, 5, 1, "", "enroll_event_job"], [2, 0, 0, "-", "entity_hub"], [3, 0, 0, "-", "events"], [4, 0, 0, "-", "exceptions"], [0, 5, 1, "", "get"], [0, 5, 1, "", "get_activities"], [0, 5, 1, "", "get_activity_by_id"], [0, 5, 1, "", "get_addon_endpoint"], [0, 5, 1, "", "get_addon_project_settings"], [0, 5, 1, "", "get_addon_settings"], [0, 5, 1, "", "get_addon_settings_schema"], [0, 5, 1, "", "get_addon_site_settings"], [0, 5, 1, "", "get_addon_site_settings_schema"], [0, 5, 1, "", "get_addon_studio_settings"], [0, 5, 1, "", "get_addon_url"], [0, 5, 1, "", "get_addons_info"], [0, 5, 1, "", "get_addons_project_settings"], [0, 5, 1, "", "get_addons_settings"], [0, 5, 1, "", "get_addons_studio_settings"], [0, 5, 1, "", "get_attributes_fields_for_type"], [0, 5, 1, "", "get_attributes_for_type"], [0, 5, 1, "", "get_attributes_schema"], [0, 5, 1, "", "get_base_url"], [0, 5, 1, "", "get_build_in_anatomy_preset"], [0, 5, 1, "", "get_bundle_settings"], [0, 5, 1, "", "get_bundles"], [0, 5, 1, "", "get_cert"], [0, 5, 1, "", "get_client_version"], [0, 5, 1, "", "get_default_anatomy_preset_name"], [0, 5, 1, "", "get_default_fields_for_type"], [0, 5, 1, "", "get_default_settings_variant"], [0, 5, 1, "", "get_dependency_packages"], [0, 5, 1, "", "get_entities_links"], [0, 5, 1, "", "get_event"], [0, 5, 1, "", "get_events"], [0, 5, 1, "", "get_folder_by_id"], [0, 5, 1, "", "get_folder_by_name"], [0, 5, 1, "", "get_folder_by_path"], [0, 5, 1, "", "get_folder_ids_with_products"], [0, 5, 1, "", "get_folder_links"], [0, 5, 1, "", "get_folder_thumbnail"], [0, 5, 1, "", "get_folders"], [0, 5, 1, "", "get_folders_hierarchy"], [0, 5, 1, "", "get_folders_links"], [0, 5, 1, "", "get_folders_rest"], [0, 5, 1, "", "get_full_link_type_name"], [0, 5, 1, "", "get_graphql_schema"], [0, 5, 1, "", "get_hero_version_by_id"], [0, 5, 1, "", "get_hero_version_by_product_id"], [0, 5, 1, "", "get_hero_versions"], [0, 5, 1, "", "get_info"], [0, 5, 1, "", "get_installers"], [0, 5, 1, "", "get_last_version_by_product_id"], [0, 5, 1, "", "get_last_version_by_product_name"], [0, 5, 1, "", "get_last_versions"], [0, 5, 1, "", "get_link_type"], [0, 5, 1, "", "get_link_types"], [0, 5, 1, "", "get_max_retries"], [0, 5, 1, "", "get_product_by_id"], [0, 5, 1, "", "get_product_by_name"], [0, 5, 1, "", "get_product_links"], [0, 5, 1, "", "get_product_type_names"], [0, 5, 1, "", "get_product_types"], [0, 5, 1, "", "get_products"], [0, 5, 1, "", "get_products_links"], [0, 5, 1, "", "get_project"], [0, 5, 1, "", "get_project_anatomy_preset"], [0, 5, 1, "", "get_project_anatomy_presets"], [0, 5, 1, "", "get_project_names"], [0, 5, 1, "", "get_project_product_types"], [0, 5, 1, "", "get_project_root_overrides"], [0, 5, 1, "", "get_project_root_overrides_by_site_id"], [0, 5, 1, "", "get_project_roots_by_platform"], [0, 5, 1, "", "get_project_roots_by_site"], [0, 5, 1, "", "get_project_roots_by_site_id"], [0, 5, 1, "", "get_project_roots_for_site"], [0, 5, 1, "", "get_projects"], [0, 5, 1, "", "get_repre_ids_by_context_filters"], [0, 5, 1, "", "get_representation_by_id"], [0, 5, 1, "", "get_representation_by_name"], [0, 5, 1, "", "get_representation_hierarchy"], [0, 5, 1, "", "get_representation_links"], [0, 5, 1, "", "get_representation_parents"], [0, 5, 1, "", "get_representations"], [0, 5, 1, "", "get_representations_hierarchy"], [0, 5, 1, "", "get_representations_links"], [0, 5, 1, "", "get_representations_parents"], [0, 5, 1, "", "get_rest_entity_by_id"], [0, 5, 1, "", "get_rest_folder"], [0, 5, 1, "", "get_rest_folders"], [0, 5, 1, "", "get_rest_product"], [0, 5, 1, "", "get_rest_project"], [0, 5, 1, "", "get_rest_projects"], [0, 5, 1, "", "get_rest_representation"], [0, 5, 1, "", "get_rest_task"], [0, 5, 1, "", "get_rest_url"], [0, 5, 1, "", "get_rest_version"], [0, 5, 1, "", "get_schemas"], [0, 5, 1, "", "get_secret"], [0, 5, 1, "", "get_secrets"], [0, 5, 1, "", "get_sender"], [0, 5, 1, "", "get_sender_type"], [0, 5, 1, "", "get_server_api_connection"], [0, 5, 1, "", "get_server_schema"], [0, 5, 1, "", "get_server_version"], [0, 5, 1, "", "get_server_version_tuple"], [0, 5, 1, "", "get_service_addon_name"], [0, 5, 1, "", "get_service_addon_settings"], [0, 5, 1, "", "get_service_addon_version"], [0, 5, 1, "", "get_service_name"], [0, 5, 1, "", "get_site_id"], [0, 5, 1, "", "get_ssl_verify"], [0, 5, 1, "", "get_task_by_folder_path"], [0, 5, 1, "", "get_task_by_id"], [0, 5, 1, "", "get_task_by_name"], [0, 5, 1, "", "get_task_links"], [0, 5, 1, "", "get_tasks"], [0, 5, 1, "", "get_tasks_by_folder_path"], [0, 5, 1, "", "get_tasks_by_folder_paths"], [0, 5, 1, "", "get_tasks_links"], [0, 5, 1, "", "get_thumbnail"], [0, 5, 1, "", "get_thumbnail_by_id"], [0, 5, 1, "", "get_timeout"], [0, 5, 1, "", "get_user"], [0, 5, 1, "", "get_user_by_name"], [0, 5, 1, "", "get_user_by_token"], [0, 5, 1, "", "get_users"], [0, 5, 1, "", "get_version_by_id"], [0, 5, 1, "", "get_version_by_name"], [0, 5, 1, "", "get_version_links"], [0, 5, 1, "", "get_version_thumbnail"], [0, 5, 1, "", "get_versions"], [0, 5, 1, "", "get_versions_links"], [0, 5, 1, "", "get_workfile_info"], [0, 5, 1, "", "get_workfile_info_by_id"], [0, 5, 1, "", "get_workfile_thumbnail"], [0, 5, 1, "", "get_workfiles_info"], [5, 0, 0, "-", "graphql"], [6, 0, 0, "-", "graphql_queries"], [0, 5, 1, "", "init_service"], [0, 5, 1, "", "is_connection_created"], [0, 5, 1, "", "is_service_user"], [0, 5, 1, "", "is_token_valid"], [0, 5, 1, "", "login_to_server"], [0, 5, 1, "", "make_sure_link_type_exists"], [7, 0, 0, "-", "operations"], [0, 5, 1, "", "patch"], [0, 5, 1, "", "post"], [0, 5, 1, "", "put"], [0, 5, 1, "", "query_graphql"], [0, 5, 1, "", "raw_delete"], [0, 5, 1, "", "raw_get"], [0, 5, 1, "", "raw_patch"], [0, 5, 1, "", "raw_post"], [0, 5, 1, "", "raw_put"], [0, 5, 1, "", "remove_attribute_config"], [0, 5, 1, "", "reset_attributes_schema"], [0, 5, 1, "", "save_secret"], [0, 5, 1, "", "send_activities_batch_operations"], [0, 5, 1, "", "send_batch_operations"], [8, 0, 0, "-", "server_api"], [0, 5, 1, "", "set_attribute_config"], [0, 5, 1, "", "set_cert"], [0, 5, 1, "", "set_client_version"], [0, 5, 1, "", "set_default_settings_variant"], [0, 5, 1, "", "set_environments"], [0, 5, 1, "", "set_max_retries"], [0, 5, 1, "", "set_sender"], [0, 5, 1, "", "set_sender_type"], [0, 5, 1, "", "set_site_id"], [0, 5, 1, "", "set_ssl_verify"], [0, 5, 1, "", "set_timeout"], [0, 5, 1, "", "slugify_string"], [0, 5, 1, "", "take_web_action_event"], [0, 5, 1, "", "trigger_server_restart"], [0, 5, 1, "", "update_activity"], [0, 5, 1, "", "update_bundle"], [0, 5, 1, "", "update_dependency_package"], [0, 5, 1, "", "update_event"], [0, 5, 1, "", "update_folder"], [0, 5, 1, "", "update_installer"], [0, 5, 1, "", "update_product"], [0, 5, 1, "", "update_project"], [0, 5, 1, "", "update_representation"], [0, 5, 1, "", "update_task"], [0, 5, 1, "", "update_thumbnail"], [0, 5, 1, "", "update_version"], [0, 5, 1, "", "upload_addon_zip"], [0, 5, 1, "", "upload_dependency_package"], [0, 5, 1, "", "upload_file"], [0, 5, 1, "", "upload_file_from_stream"], [0, 5, 1, "", "upload_installer"], [0, 5, 1, "", "upload_reviewable"], [9, 0, 0, "-", "utils"], [0, 5, 1, "", "validate_url"], [10, 0, 0, "-", "version"], [0, 5, 1, "", "version_is_latest"]], "ayon_api.GlobalServerAPI": [[0, 2, 1, "", "get_token"], [0, 2, 1, "", "get_url"], [0, 2, 1, "", "login"], [0, 2, 1, "", "set_environments"]], "ayon_api.RequestTypes": [[0, 3, 1, "", "delete"], [0, 3, 1, "", "get"], [0, 3, 1, "", "patch"], [0, 3, 1, "", "post"], [0, 3, 1, "", "put"]], "ayon_api.ServerAPI": [[0, 4, 1, "", "access_token"], [0, 2, 1, "", "as_username"], [0, 4, 1, "", "base_url"], [0, 4, 1, "", "cert"], [0, 2, 1, "", "check_bundle_compatibility"], [0, 4, 1, "", "client_version"], [0, 2, 1, "", "close_session"], [0, 2, 1, "", "create_activity"], [0, 2, 1, "", "create_bundle"], [0, 2, 1, "", "create_dependency_package"], [0, 2, 1, "", "create_folder"], [0, 2, 1, "", "create_installer"], [0, 2, 1, "", "create_link"], [0, 2, 1, "", "create_link_type"], [0, 2, 1, "", "create_product"], [0, 2, 1, "", "create_project"], [0, 2, 1, "", "create_representation"], [0, 2, 1, "", "create_session"], [0, 2, 1, "", "create_task"], [0, 2, 1, "", "create_thumbnail"], [0, 2, 1, "", "create_version"], [0, 3, 1, "", "default_download_chunk_size"], [0, 4, 1, "", "default_settings_variant"], [0, 3, 1, "", "default_upload_chunk_size"], [0, 2, 1, "", "delete"], [0, 2, 1, "", "delete_activity"], [0, 2, 1, "", "delete_addon"], [0, 2, 1, "", "delete_addon_version"], [0, 2, 1, "", "delete_bundle"], [0, 2, 1, "", "delete_dependency_package"], [0, 2, 1, "", "delete_event"], [0, 2, 1, "", "delete_folder"], [0, 2, 1, "", "delete_installer"], [0, 2, 1, "", "delete_link"], [0, 2, 1, "", "delete_link_type"], [0, 2, 1, "", "delete_product"], [0, 2, 1, "", "delete_project"], [0, 2, 1, "", "delete_representation"], [0, 2, 1, "", "delete_secret"], [0, 2, 1, "", "delete_task"], [0, 2, 1, "", "delete_version"], [0, 2, 1, "", "dispatch_event"], [0, 2, 1, "", "download_addon_private_file"], [0, 2, 1, "", "download_dependency_package"], [0, 2, 1, "", "download_file"], [0, 2, 1, "", "download_file_to_stream"], [0, 2, 1, "", "download_installer"], [0, 2, 1, "", "enroll_event_job"], [0, 2, 1, "", "get"], [0, 2, 1, "", "get_activities"], [0, 2, 1, "", "get_activity_by_id"], [0, 2, 1, "", "get_addon_endpoint"], [0, 2, 1, "", "get_addon_project_settings"], [0, 2, 1, "", "get_addon_settings"], [0, 2, 1, "", "get_addon_settings_schema"], [0, 2, 1, "", "get_addon_site_settings"], [0, 2, 1, "", "get_addon_site_settings_schema"], [0, 2, 1, "", "get_addon_studio_settings"], [0, 2, 1, "", "get_addon_url"], [0, 2, 1, "", "get_addons_info"], [0, 2, 1, "", "get_addons_project_settings"], [0, 2, 1, "", "get_addons_settings"], [0, 2, 1, "", "get_addons_studio_settings"], [0, 2, 1, "", "get_attributes_fields_for_type"], [0, 2, 1, "", "get_attributes_for_type"], [0, 2, 1, "", "get_attributes_schema"], [0, 2, 1, "", "get_base_url"], [0, 2, 1, "", "get_build_in_anatomy_preset"], [0, 2, 1, "", "get_bundle_settings"], [0, 2, 1, "", "get_bundles"], [0, 2, 1, "", "get_cert"], [0, 2, 1, "", "get_client_version"], [0, 2, 1, "", "get_default_anatomy_preset_name"], [0, 2, 1, "", "get_default_fields_for_type"], [0, 2, 1, "", "get_default_max_retries"], [0, 2, 1, "", "get_default_service_username"], [0, 2, 1, "", "get_default_settings_variant"], [0, 2, 1, "", "get_default_timeout"], [0, 2, 1, "", "get_dependency_packages"], [0, 2, 1, "", "get_entities_links"], [0, 2, 1, "", "get_event"], [0, 2, 1, "", "get_events"], [0, 2, 1, "", "get_folder_by_id"], [0, 2, 1, "", "get_folder_by_name"], [0, 2, 1, "", "get_folder_by_path"], [0, 2, 1, "", "get_folder_ids_with_products"], [0, 2, 1, "", "get_folder_links"], [0, 2, 1, "", "get_folder_thumbnail"], [0, 2, 1, "", "get_folders"], [0, 2, 1, "", "get_folders_hierarchy"], [0, 2, 1, "", "get_folders_links"], [0, 2, 1, "", "get_folders_rest"], [0, 2, 1, "", "get_full_link_type_name"], [0, 2, 1, "", "get_graphql_schema"], [0, 2, 1, "", "get_headers"], [0, 2, 1, "", "get_hero_version_by_id"], [0, 2, 1, "", "get_hero_version_by_product_id"], [0, 2, 1, "", "get_hero_versions"], [0, 2, 1, "", "get_info"], [0, 2, 1, "", "get_installers"], [0, 2, 1, "", "get_last_version_by_product_id"], [0, 2, 1, "", "get_last_version_by_product_name"], [0, 2, 1, "", "get_last_versions"], [0, 2, 1, "", "get_link_type"], [0, 2, 1, "", "get_link_types"], [0, 2, 1, "", "get_max_retries"], [0, 2, 1, "", "get_product_by_id"], [0, 2, 1, "", "get_product_by_name"], [0, 2, 1, "", "get_product_links"], [0, 2, 1, "", "get_product_type_names"], [0, 2, 1, "", "get_product_types"], [0, 2, 1, "", "get_products"], [0, 2, 1, "", "get_products_links"], [0, 2, 1, "", "get_project"], [0, 2, 1, "", "get_project_anatomy_preset"], [0, 2, 1, "", "get_project_anatomy_presets"], [0, 2, 1, "", "get_project_names"], [0, 2, 1, "", "get_project_product_types"], [0, 2, 1, "", "get_project_root_overrides"], [0, 2, 1, "", "get_project_root_overrides_by_site_id"], [0, 2, 1, "", "get_project_roots_by_platform"], [0, 2, 1, "", "get_project_roots_by_site"], [0, 2, 1, "", "get_project_roots_by_site_id"], [0, 2, 1, "", "get_project_roots_for_site"], [0, 2, 1, "", "get_projects"], [0, 2, 1, "", "get_repre_ids_by_context_filters"], [0, 2, 1, "", "get_representation_by_id"], [0, 2, 1, "", "get_representation_by_name"], [0, 2, 1, "", "get_representation_hierarchy"], [0, 2, 1, "", "get_representation_links"], [0, 2, 1, "", "get_representation_parents"], [0, 2, 1, "", "get_representations"], [0, 2, 1, "", "get_representations_hierarchy"], [0, 2, 1, "", "get_representations_links"], [0, 2, 1, "", "get_representations_parents"], [0, 2, 1, "", "get_rest_entity_by_id"], [0, 2, 1, "", "get_rest_folder"], [0, 2, 1, "", "get_rest_folders"], [0, 2, 1, "", "get_rest_product"], [0, 2, 1, "", "get_rest_project"], [0, 2, 1, "", "get_rest_projects"], [0, 2, 1, "", "get_rest_representation"], [0, 2, 1, "", "get_rest_task"], [0, 2, 1, "", "get_rest_url"], [0, 2, 1, "", "get_rest_version"], [0, 2, 1, "", "get_schemas"], [0, 2, 1, "", "get_secret"], [0, 2, 1, "", "get_secrets"], [0, 2, 1, "", "get_sender"], [0, 2, 1, "", "get_sender_type"], [0, 2, 1, "", "get_server_schema"], [0, 2, 1, "", "get_server_version"], [0, 2, 1, "", "get_server_version_tuple"], [0, 2, 1, "", "get_site_id"], [0, 2, 1, "", "get_ssl_verify"], [0, 2, 1, "", "get_task_by_folder_path"], [0, 2, 1, "", "get_task_by_id"], [0, 2, 1, "", "get_task_by_name"], [0, 2, 1, "", "get_task_links"], [0, 2, 1, "", "get_tasks"], [0, 2, 1, "", "get_tasks_by_folder_path"], [0, 2, 1, "", "get_tasks_by_folder_paths"], [0, 2, 1, "", "get_tasks_links"], [0, 2, 1, "", "get_thumbnail"], [0, 2, 1, "", "get_thumbnail_by_id"], [0, 2, 1, "", "get_timeout"], [0, 2, 1, "", "get_user"], [0, 2, 1, "", "get_user_by_name"], [0, 2, 1, "", "get_users"], [0, 2, 1, "", "get_version_by_id"], [0, 2, 1, "", "get_version_by_name"], [0, 2, 1, "", "get_version_links"], [0, 2, 1, "", "get_version_thumbnail"], [0, 2, 1, "", "get_versions"], [0, 2, 1, "", "get_versions_links"], [0, 2, 1, "", "get_workfile_info"], [0, 2, 1, "", "get_workfile_info_by_id"], [0, 2, 1, "", "get_workfile_thumbnail"], [0, 2, 1, "", "get_workfiles_info"], [0, 4, 1, "", "graphql_allows_data_in_query"], [0, 4, 1, "", "has_valid_token"], [0, 4, 1, "", "is_server_available"], [0, 2, 1, "", "is_service_user"], [0, 4, 1, "", "log"], [0, 2, 1, "", "login"], [0, 2, 1, "", "logout"], [0, 2, 1, "", "make_sure_link_type_exists"], [0, 4, 1, "", "max_retries"], [0, 2, 1, "", "patch"], [0, 2, 1, "", "post"], [0, 2, 1, "", "put"], [0, 2, 1, "", "query_graphql"], [0, 2, 1, "", "raw_delete"], [0, 2, 1, "", "raw_get"], [0, 2, 1, "", "raw_patch"], [0, 2, 1, "", "raw_post"], [0, 2, 1, "", "raw_put"], [0, 2, 1, "", "remove_attribute_config"], [0, 2, 1, "", "reset_attributes_schema"], [0, 2, 1, "", "reset_token"], [0, 4, 1, "", "rest_url"], [0, 2, 1, "", "save_secret"], [0, 2, 1, "", "send_activities_batch_operations"], [0, 2, 1, "", "send_batch_operations"], [0, 4, 1, "", "sender"], [0, 4, 1, "", "sender_type"], [0, 4, 1, "", "server_version"], [0, 4, 1, "", "server_version_tuple"], [0, 2, 1, "", "set_attribute_config"], [0, 2, 1, "", "set_cert"], [0, 2, 1, "", "set_client_version"], [0, 2, 1, "", "set_default_service_username"], [0, 2, 1, "", "set_default_settings_variant"], [0, 2, 1, "", "set_max_retries"], [0, 2, 1, "", "set_sender"], [0, 2, 1, "", "set_sender_type"], [0, 2, 1, "", "set_site_id"], [0, 2, 1, "", "set_ssl_verify"], [0, 2, 1, "", "set_timeout"], [0, 2, 1, "", "set_token"], [0, 4, 1, "", "site_id"], [0, 4, 1, "", "ssl_verify"], [0, 4, 1, "", "timeout"], [0, 2, 1, "", "trigger_server_restart"], [0, 2, 1, "", "update_activity"], [0, 2, 1, "", "update_bundle"], [0, 2, 1, "", "update_dependency_package"], [0, 2, 1, "", "update_event"], [0, 2, 1, "", "update_folder"], [0, 2, 1, "", "update_installer"], [0, 2, 1, "", "update_product"], [0, 2, 1, "", "update_project"], [0, 2, 1, "", "update_representation"], [0, 2, 1, "", "update_task"], [0, 2, 1, "", "update_thumbnail"], [0, 2, 1, "", "update_version"], [0, 2, 1, "", "upload_addon_zip"], [0, 2, 1, "", "upload_dependency_package"], [0, 2, 1, "", "upload_file"], [0, 2, 1, "", "upload_file_from_stream"], [0, 2, 1, "", "upload_installer"], [0, 2, 1, "", "upload_reviewable"], [0, 2, 1, "", "validate_server_availability"], [0, 2, 1, "", "validate_token"], [0, 2, 1, "", "version_is_latest"]], "ayon_api.ServiceContext": [[0, 3, 1, "", "addon_name"], [0, 3, 1, "", "addon_version"], [0, 2, 1, "", "init_service"], [0, 3, 1, "", "server_url"], [0, 3, 1, "", "service_name"], [0, 3, 1, "", "token"]], "ayon_api.SortOrder": [[0, 3, 1, "", "ascending"], [0, 3, 1, "", "descending"], [0, 2, 1, "", "parse_value"]], "ayon_api.TransferProgress": [[0, 2, 1, "", "add_transferred_chunk"], [0, 4, 1, "", "content_size"], [0, 4, 1, "", "destination_url"], [0, 4, 1, "", "fail_reason"], [0, 4, 1, "", "failed"], [0, 2, 1, "", "get_content_size"], [0, 2, 1, "", "get_destination_url"], [0, 2, 1, "", "get_fail_reason"], [0, 2, 1, "", "get_failed"], [0, 2, 1, "", "get_source_url"], [0, 2, 1, "", "get_started"], [0, 2, 1, "", "get_transfer_done"], [0, 2, 1, "", "get_transferred_size"], [0, 4, 1, "", "is_running"], [0, 2, 1, "", "set_content_size"], [0, 2, 1, "", "set_destination_url"], [0, 2, 1, "", "set_failed"], [0, 2, 1, "", "set_source_url"], [0, 2, 1, "", "set_started"], [0, 2, 1, "", "set_transfer_done"], [0, 2, 1, "", "set_transferred_size"], [0, 4, 1, "", "source_url"], [0, 4, 1, "", "started"], [0, 4, 1, "", "transfer_done"], [0, 4, 1, "", "transfer_progress"], [0, 4, 1, "", "transferred_size"]], "ayon_api.entity_hub": [[2, 1, 1, "", "AttributeValue"], [2, 1, 1, "", "Attributes"], [2, 1, 1, "", "BaseEntity"], [2, 1, 1, "", "EntityData"], [2, 1, 1, "", "EntityHub"], [2, 1, 1, "", "FolderEntity"], [2, 1, 1, "", "ProductEntity"], [2, 1, 1, "", "ProjectEntity"], [2, 1, 1, "", "ProjectStatus"], [2, 1, 1, "", "TaskEntity"], [2, 1, 1, "", "VersionEntity"]], "ayon_api.entity_hub.AttributeValue": [[2, 4, 1, "", "changed"], [2, 2, 1, "", "get_value"], [2, 2, 1, "", "lock"], [2, 2, 1, "", "set_value"], [2, 4, 1, "", "value"]], "ayon_api.entity_hub.Attributes": [[2, 4, 1, "", "changes"], [2, 2, 1, "", "get"], [2, 2, 1, "", "get_attribute"], [2, 2, 1, "", "items"], [2, 2, 1, "", "keys"], [2, 2, 1, "", "lock"], [2, 2, 1, "", "set"], [2, 2, 1, "", "to_dict"], [2, 2, 1, "", "values"]], "ayon_api.entity_hub.BaseEntity": [[2, 2, 1, "", "add_child"], [2, 4, 1, "", "attribs"], [2, 4, 1, "", "changes"], [2, 4, 1, "", "children"], [2, 4, 1, "", "children_ids"], [2, 4, 1, "", "created"], [2, 4, 1, "", "data"], [2, 4, 1, "", "entity_type"], [2, 2, 1, "", "fill_children_ids"], [2, 2, 1, "", "from_entity_data"], [2, 2, 1, "", "get_children"], [2, 2, 1, "", "get_children_ids"], [2, 2, 1, "", "get_label"], [2, 2, 1, "", "get_name"], [2, 2, 1, "", "get_parent"], [2, 2, 1, "", "get_parent_id"], [2, 2, 1, "", "get_status"], [2, 2, 1, "", "get_tags"], [2, 2, 1, "", "get_thumbnail_id"], [2, 4, 1, "", "has_cached_immutable_hierarchy"], [2, 4, 1, "", "id"], [2, 4, 1, "", "immutable_for_hierarchy"], [2, 4, 1, "", "label"], [2, 2, 1, "", "lock"], [2, 4, 1, "", "name"], [2, 4, 1, "", "orig_parent_id"], [2, 4, 1, "", "parent"], [2, 4, 1, "", "parent_entity_types"], [2, 4, 1, "", "parent_id"], [2, 4, 1, "", "project_name"], [2, 2, 1, "", "remove_child"], [2, 4, 1, "", "removed"], [2, 2, 1, "", "reset_immutable_for_hierarchy_cache"], [2, 2, 1, "", "set_label"], [2, 2, 1, "", "set_name"], [2, 2, 1, "", "set_parent"], [2, 2, 1, "", "set_parent_id"], [2, 2, 1, "", "set_status"], [2, 2, 1, "", "set_tags"], [2, 2, 1, "", "set_thumbnail_id"], [2, 4, 1, "", "status"], [2, 4, 1, "", "tags"], [2, 4, 1, "", "thumbnail_id"], [2, 2, 1, "", "to_create_body_data"]], "ayon_api.entity_hub.EntityData": [[2, 2, 1, "", "get_changes"], [2, 2, 1, "", "get_new_entity_value"], [2, 2, 1, "", "lock"]], "ayon_api.entity_hub.EntityHub": [[2, 2, 1, "", "add_entity"], [2, 2, 1, "", "add_folder"], [2, 2, 1, "", "add_new_folder"], [2, 2, 1, "", "add_new_product"], [2, 2, 1, "", "add_new_task"], [2, 2, 1, "", "add_new_version"], [2, 2, 1, "", "add_product"], [2, 2, 1, "", "add_task"], [2, 2, 1, "", "add_version"], [2, 4, 1, "", "allow_data_changes"], [2, 2, 1, "", "commit_changes"], [2, 2, 1, "", "delete_entity"], [2, 4, 1, "", "entities"], [2, 2, 1, "", "fetch_hierarchy_entities"], [2, 2, 1, "", "fill_project_from_server"], [2, 2, 1, "", "folder_path_reseted"], [2, 2, 1, "", "get_attributes_for_type"], [2, 2, 1, "", "get_entity_by_id"], [2, 2, 1, "", "get_entity_children"], [2, 2, 1, "", "get_folder_by_id"], [2, 2, 1, "", "get_or_fetch_entity_by_id"], [2, 2, 1, "", "get_or_query_entity_by_id"], [2, 2, 1, "", "get_product_by_id"], [2, 2, 1, "", "get_task_by_id"], [2, 2, 1, "", "get_version_by_id"], [2, 2, 1, "", "lock"], [2, 4, 1, "", "path_start_with_slash"], [2, 4, 1, "", "project_entity"], [2, 4, 1, "", "project_name"], [2, 2, 1, "", "query_entities_from_server"], [2, 2, 1, "", "reset_immutable_for_hierarchy_cache"], [2, 2, 1, "", "set_entity_parent"], [2, 2, 1, "", "unset_entity_parent"]], "ayon_api.entity_hub.FolderEntity": [[2, 4, 1, "", "changes"], [2, 3, 1, "", "entity_type"], [2, 4, 1, "", "folder_type"], [2, 2, 1, "", "from_entity_data"], [2, 2, 1, "", "get_folder_type"], [2, 2, 1, "", "get_has_published_content"], [2, 2, 1, "", "get_path"], [2, 4, 1, "", "has_published_content"], [2, 2, 1, "", "lock"], [2, 3, 1, "", "parent_entity_types"], [2, 4, 1, "", "path"], [2, 2, 1, "", "reset_path"], [2, 2, 1, "", "set_folder_type"], [2, 2, 1, "", "set_has_published_content"], [2, 2, 1, "", "to_create_body_data"]], "ayon_api.entity_hub.ProductEntity": [[2, 4, 1, "", "changes"], [2, 3, 1, "", "entity_type"], [2, 4, 1, "", "folder_id"], [2, 2, 1, "", "from_entity_data"], [2, 2, 1, "", "get_folder_id"], [2, 2, 1, "", "get_product_type"], [2, 2, 1, "", "lock"], [2, 3, 1, "", "parent_entity_types"], [2, 4, 1, "", "product_type"], [2, 2, 1, "", "set_folder_id"], [2, 2, 1, "", "set_product_type"], [2, 2, 1, "", "to_create_body_data"]], "ayon_api.entity_hub.ProjectEntity": [[2, 4, 1, "", "changes"], [2, 3, 1, "", "default_folder_type_icon"], [2, 3, 1, "", "default_task_type_icon"], [2, 3, 1, "", "entity_type"], [2, 4, 1, "", "folder_types"], [2, 2, 1, "", "from_entity_data"], [2, 2, 1, "", "get_folder_types"], [2, 2, 1, "", "get_orig_folder_types"], [2, 2, 1, "", "get_orig_statuses"], [2, 2, 1, "", "get_orig_task_types"], [2, 2, 1, "", "get_parent"], [2, 2, 1, "", "get_status_by_slugified_name"], [2, 2, 1, "", "get_statuses"], [2, 2, 1, "", "get_task_types"], [2, 2, 1, "", "lock"], [2, 4, 1, "", "parent"], [2, 3, 1, "", "parent_entity_types"], [2, 2, 1, "", "set_folder_types"], [2, 2, 1, "", "set_name"], [2, 2, 1, "", "set_parent"], [2, 2, 1, "", "set_status_scope_supported"], [2, 2, 1, "", "set_statuses"], [2, 2, 1, "", "set_task_types"], [2, 4, 1, "", "statuses"], [2, 4, 1, "", "task_types"], [2, 2, 1, "", "to_create_body_data"]], "ayon_api.entity_hub.ProjectStatus": [[2, 4, 1, "", "changed"], [2, 4, 1, "", "color"], [2, 3, 1, "", "color_regex"], [2, 3, 1, "", "default_color"], [2, 3, 1, "", "default_state"], [2, 2, 1, "", "delete"], [2, 2, 1, "", "from_data"], [2, 2, 1, "", "get_color"], [2, 2, 1, "", "get_icon"], [2, 2, 1, "", "get_index"], [2, 2, 1, "", "get_name"], [2, 2, 1, "", "get_project_statuses"], [2, 2, 1, "", "get_scope"], [2, 2, 1, "", "get_short_name"], [2, 2, 1, "", "get_state"], [2, 4, 1, "", "icon"], [2, 4, 1, "", "index"], [2, 2, 1, "", "is_available_for_entity_type"], [2, 2, 1, "", "lock"], [2, 2, 1, "", "move_after"], [2, 2, 1, "", "move_before"], [2, 4, 1, "", "name"], [2, 4, 1, "", "project_statuses"], [2, 4, 1, "", "scope"], [2, 2, 1, "", "set_color"], [2, 2, 1, "", "set_icon"], [2, 2, 1, "", "set_index"], [2, 2, 1, "", "set_name"], [2, 2, 1, "", "set_project_statuses"], [2, 2, 1, "", "set_scope"], [2, 2, 1, "", "set_short_name"], [2, 2, 1, "", "set_state"], [2, 4, 1, "", "short_name"], [2, 4, 1, "", "slugified_name"], [2, 2, 1, "", "slugify_name"], [2, 4, 1, "", "state"], [2, 2, 1, "", "to_data"], [2, 2, 1, "", "unset_project_statuses"], [2, 3, 1, "", "valid_scope"], [2, 3, 1, "", "valid_states"]], "ayon_api.entity_hub.TaskEntity": [[2, 2, 1, "", "add_child"], [2, 4, 1, "", "assignees"], [2, 4, 1, "", "changes"], [2, 3, 1, "", "entity_type"], [2, 4, 1, "", "folder_id"], [2, 2, 1, "", "from_entity_data"], [2, 2, 1, "", "get_assignees"], [2, 2, 1, "", "get_folder_id"], [2, 2, 1, "", "get_task_type"], [2, 2, 1, "", "lock"], [2, 3, 1, "", "parent_entity_types"], [2, 2, 1, "", "set_assignees"], [2, 2, 1, "", "set_folder_id"], [2, 2, 1, "", "set_task_type"], [2, 4, 1, "", "task_type"], [2, 2, 1, "", "to_create_body_data"]], "ayon_api.entity_hub.VersionEntity": [[2, 4, 1, "", "changes"], [2, 3, 1, "", "entity_type"], [2, 2, 1, "", "from_entity_data"], [2, 2, 1, "", "get_product_id"], [2, 2, 1, "", "get_task_id"], [2, 2, 1, "", "get_version"], [2, 2, 1, "", "lock"], [2, 3, 1, "", "parent_entity_types"], [2, 4, 1, "", "product_id"], [2, 2, 1, "", "set_product_id"], [2, 2, 1, "", "set_task_id"], [2, 2, 1, "", "set_version"], [2, 4, 1, "", "task_id"], [2, 2, 1, "", "to_create_body_data"], [2, 4, 1, "", "version"]], "ayon_api.events": [[3, 1, 1, "", "ServerEvent"]], "ayon_api.events.ServerEvent": [[3, 2, 1, "", "to_data"]], "ayon_api.exceptions": [[4, 6, 1, "", "AuthenticationError"], [4, 6, 1, "", "FailedOperations"], [4, 6, 1, "", "FailedServiceInit"], [4, 6, 1, "", "FolderNotFound"], [4, 6, 1, "", "GraphQlQueryFailed"], [4, 6, 1, "", "HTTPRequestError"], [4, 6, 1, "", "MissingEntityError"], [4, 6, 1, "", "ProjectNotFound"], [4, 6, 1, "", "RequestError"], [4, 6, 1, "", "ServerError"], [4, 6, 1, "", "ServerNotReached"], [4, 6, 1, "", "UnauthorizedError"], [4, 6, 1, "", "UnsupportedServerVersion"], [4, 6, 1, "", "UrlError"]], "ayon_api.graphql": [[5, 1, 1, "", "BaseGraphQlQueryField"], [5, 1, 1, "", "GraphQlQuery"], [5, 1, 1, "", "GraphQlQueryEdgeField"], [5, 1, 1, "", "GraphQlQueryField"], [5, 1, 1, "", "QueryVariable"], [5, 5, 1, "", "fields_to_dict"]], "ayon_api.graphql.BaseGraphQlQueryField": [[5, 2, 1, "", "add_field"], [5, 2, 1, "", "add_field_with_edges"], [5, 2, 1, "", "add_obj_field"], [5, 2, 1, "", "add_variable"], [5, 2, 1, "", "calculate_query"], [5, 4, 1, "", "child_has_edges"], [5, 4, 1, "", "child_indent"], [5, 2, 1, "", "get_field_by_keys"], [5, 2, 1, "", "get_filters"], [5, 2, 1, "", "get_name"], [5, 2, 1, "", "get_variable"], [5, 2, 1, "", "get_variable_value"], [5, 4, 1, "", "has_edges"], [5, 2, 1, "", "has_filter"], [5, 4, 1, "", "indent"], [5, 4, 1, "", "name"], [5, 4, 1, "", "need_query"], [5, 4, 1, "", "offset"], [5, 2, 1, "", "parse_result"], [5, 4, 1, "", "path"], [5, 4, 1, "", "query_item"], [5, 2, 1, "", "remove_filter"], [5, 2, 1, "", "reset_cursor"], [5, 2, 1, "", "set_ascending_order"], [5, 2, 1, "", "set_descending_order"], [5, 2, 1, "", "set_filter"], [5, 2, 1, "", "set_limit"], [5, 2, 1, "", "set_order"], [5, 2, 1, "", "set_parent"], [5, 2, 1, "", "set_variable_value"], [5, 2, 1, "", "sum_edge_fields"]], "ayon_api.graphql.GraphQlQuery": [[5, 2, 1, "", "add_field"], [5, 2, 1, "", "add_field_with_edges"], [5, 2, 1, "", "add_obj_field"], [5, 2, 1, "", "add_variable"], [5, 2, 1, "", "calculate_query"], [5, 4, 1, "", "child_indent"], [5, 2, 1, "", "continuous_query"], [5, 2, 1, "", "get_field_by_keys"], [5, 2, 1, "", "get_field_by_path"], [5, 2, 1, "", "get_variable"], [5, 2, 1, "", "get_variable_keys"], [5, 2, 1, "", "get_variable_value"], [5, 2, 1, "", "get_variables_values"], [5, 4, 1, "", "has_multiple_edge_fields"], [5, 4, 1, "", "indent"], [5, 4, 1, "", "need_query"], [5, 3, 1, "", "offset"], [5, 2, 1, "", "parse_result"], [5, 2, 1, "", "query"], [5, 2, 1, "", "set_variable_value"]], "ayon_api.graphql.GraphQlQueryEdgeField": [[5, 2, 1, "", "add_edge_field"], [5, 2, 1, "", "add_obj_edge_field"], [5, 2, 1, "", "add_obj_field"], [5, 2, 1, "", "calculate_query"], [5, 4, 1, "", "child_indent"], [5, 2, 1, "", "get_filters"], [5, 3, 1, "", "has_edges"], [5, 2, 1, "", "parse_result"], [5, 2, 1, "", "reset_cursor"]], "ayon_api.graphql.GraphQlQueryField": [[5, 2, 1, "", "calculate_query"], [5, 4, 1, "", "child_indent"], [5, 3, 1, "", "has_edges"], [5, 2, 1, "", "parse_result"]], "ayon_api.graphql.QueryVariable": [[5, 4, 1, "", "name"], [5, 4, 1, "", "variable_name"]], "ayon_api.graphql_queries": [[6, 5, 1, "", "activities_graphql_query"], [6, 5, 1, "", "add_links_fields"], [6, 5, 1, "", "events_graphql_query"], [6, 5, 1, "", "folders_graphql_query"], [6, 5, 1, "", "product_types_query"], [6, 5, 1, "", "products_graphql_query"], [6, 5, 1, "", "project_graphql_query"], [6, 5, 1, "", "project_product_types_query"], [6, 5, 1, "", "projects_graphql_query"], [6, 5, 1, "", "representations_graphql_query"], [6, 5, 1, "", "representations_hierarchy_qraphql_query"], [6, 5, 1, "", "representations_parents_qraphql_query"], [6, 5, 1, "", "tasks_by_folder_paths_graphql_query"], [6, 5, 1, "", "tasks_graphql_query"], [6, 5, 1, "", "users_graphql_query"], [6, 5, 1, "", "versions_graphql_query"], [6, 5, 1, "", "workfiles_info_graphql_query"]], "ayon_api.operations": [[7, 1, 1, "", "AbstractOperation"], [7, 1, 1, "", "CreateOperation"], [7, 1, 1, "", "DeleteOperation"], [7, 1, 1, "", "OperationsSession"], [7, 1, 1, "", "UpdateOperation"], [7, 5, 1, "", "new_folder_entity"], [7, 5, 1, "", "new_hero_version_entity"], [7, 5, 1, "", "new_product_entity"], [7, 5, 1, "", "new_representation_entity"], [7, 5, 1, "", "new_version_entity"], [7, 5, 1, "", "new_workfile_info"], [7, 5, 1, "", "prepare_changes"]], "ayon_api.operations.AbstractOperation": [[7, 4, 1, "", "entity_type"], [7, 4, 1, "", "id"], [7, 4, 1, "", "operation_name"], [7, 4, 1, "", "project_name"], [7, 2, 1, "", "to_data"]], "ayon_api.operations.CreateOperation": [[7, 4, 1, "", "con"], [7, 4, 1, "", "data"], [7, 4, 1, "", "entity_id"], [7, 2, 1, "", "get"], [7, 3, 1, "", "operation_name"], [7, 4, 1, "", "session"], [7, 2, 1, "", "set_value"], [7, 2, 1, "", "to_data"], [7, 2, 1, "", "to_server_operation"]], "ayon_api.operations.DeleteOperation": [[7, 4, 1, "", "con"], [7, 4, 1, "", "entity_id"], [7, 3, 1, "", "operation_name"], [7, 4, 1, "", "session"], [7, 2, 1, "", "to_data"], [7, 2, 1, "", "to_server_operation"]], "ayon_api.operations.OperationsSession": [[7, 2, 1, "", "add"], [7, 2, 1, "", "append"], [7, 2, 1, "", "clear"], [7, 2, 1, "", "commit"], [7, 4, 1, "", "con"], [7, 2, 1, "", "create_entity"], [7, 2, 1, "", "create_folder"], [7, 2, 1, "", "create_product"], [7, 2, 1, "", "create_representation"], [7, 2, 1, "", "create_task"], [7, 2, 1, "", "create_version"], [7, 2, 1, "", "delete_entity"], [7, 2, 1, "", "delete_folder"], [7, 2, 1, "", "delete_product"], [7, 2, 1, "", "delete_representation"], [7, 2, 1, "", "delete_task"], [7, 2, 1, "", "delete_version"], [7, 2, 1, "", "extend"], [7, 2, 1, "", "get_project"], [7, 2, 1, "", "remove"], [7, 2, 1, "", "to_data"], [7, 2, 1, "", "update_entity"], [7, 2, 1, "", "update_folder"], [7, 2, 1, "", "update_product"], [7, 2, 1, "", "update_representation"], [7, 2, 1, "", "update_task"], [7, 2, 1, "", "update_version"]], "ayon_api.operations.UpdateOperation": [[7, 4, 1, "", "con"], [7, 4, 1, "", "entity_id"], [7, 3, 1, "", "operation_name"], [7, 4, 1, "", "session"], [7, 2, 1, "", "to_data"], [7, 2, 1, "", "to_server_operation"], [7, 4, 1, "", "update_data"]], "ayon_api.server_api": [[8, 1, 1, "", "GraphQlResponse"], [8, 1, 1, "", "RequestType"], [8, 1, 1, "", "RequestTypes"], [8, 1, 1, "", "RestApiResponse"], [8, 1, 1, "", "ServerAPI"], [8, 5, 1, "", "fill_own_attribs"]], "ayon_api.server_api.RequestTypes": [[8, 3, 1, "", "delete"], [8, 3, 1, "", "get"], [8, 3, 1, "", "patch"], [8, 3, 1, "", "post"], [8, 3, 1, "", "put"]], "ayon_api.server_api.RestApiResponse": [[8, 4, 1, "", "content"], [8, 4, 1, "", "content_type"], [8, 4, 1, "", "data"], [8, 4, 1, "", "detail"], [8, 2, 1, "", "get"], [8, 4, 1, "", "headers"], [8, 4, 1, "", "orig_response"], [8, 2, 1, "", "raise_for_status"], [8, 4, 1, "", "status_code"], [8, 4, 1, "", "text"]], "ayon_api.server_api.ServerAPI": [[8, 4, 1, "", "access_token"], [8, 2, 1, "", "as_username"], [8, 4, 1, "", "base_url"], [8, 4, 1, "", "cert"], [8, 2, 1, "", "check_bundle_compatibility"], [8, 4, 1, "", "client_version"], [8, 2, 1, "", "close_session"], [8, 2, 1, "", "create_activity"], [8, 2, 1, "", "create_bundle"], [8, 2, 1, "", "create_dependency_package"], [8, 2, 1, "", "create_folder"], [8, 2, 1, "", "create_installer"], [8, 2, 1, "", "create_link"], [8, 2, 1, "", "create_link_type"], [8, 2, 1, "", "create_product"], [8, 2, 1, "", "create_project"], [8, 2, 1, "", "create_representation"], [8, 2, 1, "", "create_session"], [8, 2, 1, "", "create_task"], [8, 2, 1, "", "create_thumbnail"], [8, 2, 1, "", "create_version"], [8, 3, 1, "", "default_download_chunk_size"], [8, 4, 1, "", "default_settings_variant"], [8, 3, 1, "", "default_upload_chunk_size"], [8, 2, 1, "", "delete"], [8, 2, 1, "", "delete_activity"], [8, 2, 1, "", "delete_addon"], [8, 2, 1, "", "delete_addon_version"], [8, 2, 1, "", "delete_bundle"], [8, 2, 1, "", "delete_dependency_package"], [8, 2, 1, "", "delete_event"], [8, 2, 1, "", "delete_folder"], [8, 2, 1, "", "delete_installer"], [8, 2, 1, "", "delete_link"], [8, 2, 1, "", "delete_link_type"], [8, 2, 1, "", "delete_product"], [8, 2, 1, "", "delete_project"], [8, 2, 1, "", "delete_representation"], [8, 2, 1, "", "delete_secret"], [8, 2, 1, "", "delete_task"], [8, 2, 1, "", "delete_version"], [8, 2, 1, "", "dispatch_event"], [8, 2, 1, "", "download_addon_private_file"], [8, 2, 1, "", "download_dependency_package"], [8, 2, 1, "", "download_file"], [8, 2, 1, "", "download_file_to_stream"], [8, 2, 1, "", "download_installer"], [8, 2, 1, "", "enroll_event_job"], [8, 2, 1, "", "get"], [8, 2, 1, "", "get_activities"], [8, 2, 1, "", "get_activity_by_id"], [8, 2, 1, "", "get_addon_endpoint"], [8, 2, 1, "", "get_addon_project_settings"], [8, 2, 1, "", "get_addon_settings"], [8, 2, 1, "", "get_addon_settings_schema"], [8, 2, 1, "", "get_addon_site_settings"], [8, 2, 1, "", "get_addon_site_settings_schema"], [8, 2, 1, "", "get_addon_studio_settings"], [8, 2, 1, "", "get_addon_url"], [8, 2, 1, "", "get_addons_info"], [8, 2, 1, "", "get_addons_project_settings"], [8, 2, 1, "", "get_addons_settings"], [8, 2, 1, "", "get_addons_studio_settings"], [8, 2, 1, "", "get_attributes_fields_for_type"], [8, 2, 1, "", "get_attributes_for_type"], [8, 2, 1, "", "get_attributes_schema"], [8, 2, 1, "", "get_base_url"], [8, 2, 1, "", "get_build_in_anatomy_preset"], [8, 2, 1, "", "get_bundle_settings"], [8, 2, 1, "", "get_bundles"], [8, 2, 1, "", "get_cert"], [8, 2, 1, "", "get_client_version"], [8, 2, 1, "", "get_default_anatomy_preset_name"], [8, 2, 1, "", "get_default_fields_for_type"], [8, 2, 1, "", "get_default_max_retries"], [8, 2, 1, "", "get_default_service_username"], [8, 2, 1, "", "get_default_settings_variant"], [8, 2, 1, "", "get_default_timeout"], [8, 2, 1, "", "get_dependency_packages"], [8, 2, 1, "", "get_entities_links"], [8, 2, 1, "", "get_event"], [8, 2, 1, "", "get_events"], [8, 2, 1, "", "get_folder_by_id"], [8, 2, 1, "", "get_folder_by_name"], [8, 2, 1, "", "get_folder_by_path"], [8, 2, 1, "", "get_folder_ids_with_products"], [8, 2, 1, "", "get_folder_links"], [8, 2, 1, "", "get_folder_thumbnail"], [8, 2, 1, "", "get_folders"], [8, 2, 1, "", "get_folders_hierarchy"], [8, 2, 1, "", "get_folders_links"], [8, 2, 1, "", "get_folders_rest"], [8, 2, 1, "", "get_full_link_type_name"], [8, 2, 1, "", "get_graphql_schema"], [8, 2, 1, "", "get_headers"], [8, 2, 1, "", "get_hero_version_by_id"], [8, 2, 1, "", "get_hero_version_by_product_id"], [8, 2, 1, "", "get_hero_versions"], [8, 2, 1, "", "get_info"], [8, 2, 1, "", "get_installers"], [8, 2, 1, "", "get_last_version_by_product_id"], [8, 2, 1, "", "get_last_version_by_product_name"], [8, 2, 1, "", "get_last_versions"], [8, 2, 1, "", "get_link_type"], [8, 2, 1, "", "get_link_types"], [8, 2, 1, "", "get_max_retries"], [8, 2, 1, "", "get_product_by_id"], [8, 2, 1, "", "get_product_by_name"], [8, 2, 1, "", "get_product_links"], [8, 2, 1, "", "get_product_type_names"], [8, 2, 1, "", "get_product_types"], [8, 2, 1, "", "get_products"], [8, 2, 1, "", "get_products_links"], [8, 2, 1, "", "get_project"], [8, 2, 1, "", "get_project_anatomy_preset"], [8, 2, 1, "", "get_project_anatomy_presets"], [8, 2, 1, "", "get_project_names"], [8, 2, 1, "", "get_project_product_types"], [8, 2, 1, "", "get_project_root_overrides"], [8, 2, 1, "", "get_project_root_overrides_by_site_id"], [8, 2, 1, "", "get_project_roots_by_platform"], [8, 2, 1, "", "get_project_roots_by_site"], [8, 2, 1, "", "get_project_roots_by_site_id"], [8, 2, 1, "", "get_project_roots_for_site"], [8, 2, 1, "", "get_projects"], [8, 2, 1, "", "get_repre_ids_by_context_filters"], [8, 2, 1, "", "get_representation_by_id"], [8, 2, 1, "", "get_representation_by_name"], [8, 2, 1, "", "get_representation_hierarchy"], [8, 2, 1, "", "get_representation_links"], [8, 2, 1, "", "get_representation_parents"], [8, 2, 1, "", "get_representations"], [8, 2, 1, "", "get_representations_hierarchy"], [8, 2, 1, "", "get_representations_links"], [8, 2, 1, "", "get_representations_parents"], [8, 2, 1, "", "get_rest_entity_by_id"], [8, 2, 1, "", "get_rest_folder"], [8, 2, 1, "", "get_rest_folders"], [8, 2, 1, "", "get_rest_product"], [8, 2, 1, "", "get_rest_project"], [8, 2, 1, "", "get_rest_projects"], [8, 2, 1, "", "get_rest_representation"], [8, 2, 1, "", "get_rest_task"], [8, 2, 1, "", "get_rest_url"], [8, 2, 1, "", "get_rest_version"], [8, 2, 1, "", "get_schemas"], [8, 2, 1, "", "get_secret"], [8, 2, 1, "", "get_secrets"], [8, 2, 1, "", "get_sender"], [8, 2, 1, "", "get_sender_type"], [8, 2, 1, "", "get_server_schema"], [8, 2, 1, "", "get_server_version"], [8, 2, 1, "", "get_server_version_tuple"], [8, 2, 1, "", "get_site_id"], [8, 2, 1, "", "get_ssl_verify"], [8, 2, 1, "", "get_task_by_folder_path"], [8, 2, 1, "", "get_task_by_id"], [8, 2, 1, "", "get_task_by_name"], [8, 2, 1, "", "get_task_links"], [8, 2, 1, "", "get_tasks"], [8, 2, 1, "", "get_tasks_by_folder_path"], [8, 2, 1, "", "get_tasks_by_folder_paths"], [8, 2, 1, "", "get_tasks_links"], [8, 2, 1, "", "get_thumbnail"], [8, 2, 1, "", "get_thumbnail_by_id"], [8, 2, 1, "", "get_timeout"], [8, 2, 1, "", "get_user"], [8, 2, 1, "", "get_user_by_name"], [8, 2, 1, "", "get_users"], [8, 2, 1, "", "get_version_by_id"], [8, 2, 1, "", "get_version_by_name"], [8, 2, 1, "", "get_version_links"], [8, 2, 1, "", "get_version_thumbnail"], [8, 2, 1, "", "get_versions"], [8, 2, 1, "", "get_versions_links"], [8, 2, 1, "", "get_workfile_info"], [8, 2, 1, "", "get_workfile_info_by_id"], [8, 2, 1, "", "get_workfile_thumbnail"], [8, 2, 1, "", "get_workfiles_info"], [8, 4, 1, "", "graphql_allows_data_in_query"], [8, 4, 1, "", "has_valid_token"], [8, 4, 1, "", "is_server_available"], [8, 2, 1, "", "is_service_user"], [8, 4, 1, "", "log"], [8, 2, 1, "", "login"], [8, 2, 1, "", "logout"], [8, 2, 1, "", "make_sure_link_type_exists"], [8, 4, 1, "", "max_retries"], [8, 2, 1, "", "patch"], [8, 2, 1, "", "post"], [8, 2, 1, "", "put"], [8, 2, 1, "", "query_graphql"], [8, 2, 1, "", "raw_delete"], [8, 2, 1, "", "raw_get"], [8, 2, 1, "", "raw_patch"], [8, 2, 1, "", "raw_post"], [8, 2, 1, "", "raw_put"], [8, 2, 1, "", "remove_attribute_config"], [8, 2, 1, "", "reset_attributes_schema"], [8, 2, 1, "", "reset_token"], [8, 4, 1, "", "rest_url"], [8, 2, 1, "", "save_secret"], [8, 2, 1, "", "send_activities_batch_operations"], [8, 2, 1, "", "send_batch_operations"], [8, 4, 1, "", "sender"], [8, 4, 1, "", "sender_type"], [8, 4, 1, "", "server_version"], [8, 4, 1, "", "server_version_tuple"], [8, 2, 1, "", "set_attribute_config"], [8, 2, 1, "", "set_cert"], [8, 2, 1, "", "set_client_version"], [8, 2, 1, "", "set_default_service_username"], [8, 2, 1, "", "set_default_settings_variant"], [8, 2, 1, "", "set_max_retries"], [8, 2, 1, "", "set_sender"], [8, 2, 1, "", "set_sender_type"], [8, 2, 1, "", "set_site_id"], [8, 2, 1, "", "set_ssl_verify"], [8, 2, 1, "", "set_timeout"], [8, 2, 1, "", "set_token"], [8, 4, 1, "", "site_id"], [8, 4, 1, "", "ssl_verify"], [8, 4, 1, "", "timeout"], [8, 2, 1, "", "trigger_server_restart"], [8, 2, 1, "", "update_activity"], [8, 2, 1, "", "update_bundle"], [8, 2, 1, "", "update_dependency_package"], [8, 2, 1, "", "update_event"], [8, 2, 1, "", "update_folder"], [8, 2, 1, "", "update_installer"], [8, 2, 1, "", "update_product"], [8, 2, 1, "", "update_project"], [8, 2, 1, "", "update_representation"], [8, 2, 1, "", "update_task"], [8, 2, 1, "", "update_thumbnail"], [8, 2, 1, "", "update_version"], [8, 2, 1, "", "upload_addon_zip"], [8, 2, 1, "", "upload_dependency_package"], [8, 2, 1, "", "upload_file"], [8, 2, 1, "", "upload_file_from_stream"], [8, 2, 1, "", "upload_installer"], [8, 2, 1, "", "upload_reviewable"], [8, 2, 1, "", "validate_server_availability"], [8, 2, 1, "", "validate_token"], [8, 2, 1, "", "version_is_latest"]], "ayon_api.utils": [[9, 1, 1, "", "RepresentationHierarchy"], [9, 1, 1, "", "RepresentationParents"], [9, 1, 1, "", "SortOrder"], [9, 1, 1, "", "ThumbnailContent"], [9, 1, 1, "", "TransferProgress"], [9, 5, 1, "", "abort_web_action_event"], [9, 5, 1, "", "convert_entity_id"], [9, 5, 1, "", "convert_or_create_entity_id"], [9, 5, 1, "", "create_dependency_package_basename"], [9, 5, 1, "", "create_entity_id"], [9, 5, 1, "", "entity_data_json_default"], [9, 5, 1, "", "failed_json_default"], [9, 5, 1, "", "get_default_settings_variant"], [9, 5, 1, "", "get_default_site_id"], [9, 5, 1, "", "get_default_timeout"], [9, 5, 1, "", "get_media_mime_type"], [9, 5, 1, "", "get_media_mime_type_for_content"], [9, 5, 1, "", "get_media_mime_type_for_stream"], [9, 5, 1, "", "get_user_by_token"], [9, 5, 1, "", "is_token_valid"], [9, 5, 1, "", "login_to_server"], [9, 5, 1, "", "logout_from_server"], [9, 5, 1, "", "prepare_attribute_changes"], [9, 5, 1, "", "prepare_entity_changes"], [9, 5, 1, "", "prepare_query_string"], [9, 5, 1, "", "slugify_string"], [9, 5, 1, "", "take_web_action_event"], [9, 5, 1, "", "validate_url"]], "ayon_api.utils.RepresentationHierarchy": [[9, 3, 1, "", "folder"], [9, 3, 1, "", "product"], [9, 3, 1, "", "project"], [9, 3, 1, "", "representation"], [9, 3, 1, "", "task"], [9, 3, 1, "", "version"]], "ayon_api.utils.RepresentationParents": [[9, 3, 1, "", "folder"], [9, 3, 1, "", "product"], [9, 3, 1, "", "project"], [9, 3, 1, "", "version"]], "ayon_api.utils.SortOrder": [[9, 3, 1, "", "ascending"], [9, 3, 1, "", "descending"], [9, 2, 1, "", "parse_value"]], "ayon_api.utils.ThumbnailContent": [[9, 4, 1, "", "id"], [9, 4, 1, "", "is_valid"]], "ayon_api.utils.TransferProgress": [[9, 2, 1, "", "add_transferred_chunk"], [9, 4, 1, "", "content_size"], [9, 4, 1, "", "destination_url"], [9, 4, 1, "", "fail_reason"], [9, 4, 1, "", "failed"], [9, 2, 1, "", "get_content_size"], [9, 2, 1, "", "get_destination_url"], [9, 2, 1, "", "get_fail_reason"], [9, 2, 1, "", "get_failed"], [9, 2, 1, "", "get_source_url"], [9, 2, 1, "", "get_started"], [9, 2, 1, "", "get_transfer_done"], [9, 2, 1, "", "get_transferred_size"], [9, 4, 1, "", "is_running"], [9, 2, 1, "", "set_content_size"], [9, 2, 1, "", "set_destination_url"], [9, 2, 1, "", "set_failed"], [9, 2, 1, "", "set_source_url"], [9, 2, 1, "", "set_started"], [9, 2, 1, "", "set_transfer_done"], [9, 2, 1, "", "set_transferred_size"], [9, 4, 1, "", "source_url"], [9, 4, 1, "", "started"], [9, 4, 1, "", "transfer_done"], [9, 4, 1, "", "transfer_progress"], [9, 4, 1, "", "transferred_size"]]}, "objtypes": {"0": "py:module", "1": "py:class", "2": "py:method", "3": "py:attribute", "4": "py:property", "5": "py:function", "6": "py:exception"}, "objnames": {"0": ["py", "module", "Python module"], "1": ["py", "class", "Python class"], "2": ["py", "method", "Python method"], "3": ["py", "attribute", "Python attribute"], "4": ["py", "property", "Python property"], "5": ["py", "function", "Python function"], "6": ["py", "exception", "Python exception"]}, "titleterms": {"ayon_api": [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 12], "packag": 0, "exampl": [0, 8], "note": [0, 7, 8], "todo": [0, 2, 8], "submodul": 0, "constant": 1, "modul": [1, 2, 3, 4, 5, 6, 7, 8, 9, 10], "entity_hub": 2, "event": 3, "except": 4, "graphql": 5, "graphql_queri": 6, "oper": 7, "server_api": 8, "util": 9, "version": 10, "welcom": 11, "ayon": 11, "python": 11, "api": 11, "document": 11, "get": 11, "start": 11, "miscellan": 11, "summari": 11}, "envversion": {"sphinx.domains.c": 2, "sphinx.domains.changeset": 1, "sphinx.domains.citation": 1, "sphinx.domains.cpp": 8, "sphinx.domains.index": 1, "sphinx.domains.javascript": 2, "sphinx.domains.math": 2, "sphinx.domains.python": 3, "sphinx.domains.rst": 2, "sphinx.domains.std": 2, "sphinx.ext.todo": 2, "sphinx.ext.viewcode": 1, "sphinx": 57}, "alltitles": {"ayon_api package": [[0, "module-ayon_api"]], "Examples": [[0, null], [0, null], [0, null], [0, null], [8, null], [8, null]], "Notes": [[0, null], [0, null], [0, null], [0, null], [0, null], [0, null], [0, null], [0, null], [7, null], [8, null], [8, null], [8, null], [8, null]], "Example": [[0, null], [0, null], [8, null]], "Todo": [[0, "id9"], [0, "id18"], [2, "id1"], [8, "id9"]], "Submodules": [[0, "submodules"]], "ayon_api.constants module": [[1, "module-ayon_api.constants"]], "ayon_api.entity_hub module": [[2, "module-ayon_api.entity_hub"]], "ayon_api.events module": [[3, "module-ayon_api.events"]], "ayon_api.exceptions module": [[4, "module-ayon_api.exceptions"]], "ayon_api.graphql module": [[5, "module-ayon_api.graphql"]], "ayon_api.graphql_queries module": [[6, "module-ayon_api.graphql_queries"]], "ayon_api.operations module": [[7, "module-ayon_api.operations"]], "ayon_api.server_api module": [[8, "module-ayon_api.server_api"]], "ayon_api.utils module": [[9, "module-ayon_api.utils"]], "ayon_api.version module": [[10, "module-ayon_api.version"]], "Welcome to AYON Python API documentation!": [[11, "welcome-to-ayon-python-api-documentation"]], "Getting Started": [[11, "getting-started"]], "Python API": [[11, "python-api"]], "Miscellaneous": [[11, "miscellaneous"]], "Summary": [[11, "summary"]], "ayon_api": [[12, "ayon-api"]]}, "indexentries": {"globalserverapi (class in ayon_api)": [[0, "ayon_api.GlobalServerAPI"]], "requesttypes (class in ayon_api)": [[0, "ayon_api.RequestTypes"]], "serverapi (class in ayon_api)": [[0, "ayon_api.ServerAPI"]], "servicecontext (class in ayon_api)": [[0, "ayon_api.ServiceContext"]], "sortorder (class in ayon_api)": [[0, "ayon_api.SortOrder"]], "transferprogress (class in ayon_api)": [[0, "ayon_api.TransferProgress"]], "abort_web_action_event() (in module ayon_api)": [[0, "ayon_api.abort_web_action_event"]], "access_token (serverapi property)": [[0, "ayon_api.ServerAPI.access_token"], [8, "ayon_api.server_api.ServerAPI.access_token"]], "add_transferred_chunk() (transferprogress method)": [[0, "ayon_api.TransferProgress.add_transferred_chunk"], [9, "ayon_api.utils.TransferProgress.add_transferred_chunk"]], "addon_name (servicecontext attribute)": [[0, "ayon_api.ServiceContext.addon_name"]], "addon_version (servicecontext attribute)": [[0, "ayon_api.ServiceContext.addon_version"]], "as_username() (serverapi method)": [[0, "ayon_api.ServerAPI.as_username"], [8, "ayon_api.server_api.ServerAPI.as_username"]], "ascending (sortorder attribute)": [[0, "ayon_api.SortOrder.ascending"], [9, "ayon_api.utils.SortOrder.ascending"]], "ayon_api": [[0, "module-ayon_api"]], "base_url (serverapi property)": [[0, "ayon_api.ServerAPI.base_url"], [8, "ayon_api.server_api.ServerAPI.base_url"]], "cert (serverapi property)": [[0, "ayon_api.ServerAPI.cert"], [8, "ayon_api.server_api.ServerAPI.cert"]], "change_token() (in module ayon_api)": [[0, "ayon_api.change_token"]], "check_bundle_compatibility() (serverapi method)": [[0, "ayon_api.ServerAPI.check_bundle_compatibility"], [8, "ayon_api.server_api.ServerAPI.check_bundle_compatibility"]], "check_bundle_compatibility() (in module ayon_api)": [[0, "ayon_api.check_bundle_compatibility"]], "client_version (serverapi property)": [[0, "ayon_api.ServerAPI.client_version"], [8, "ayon_api.server_api.ServerAPI.client_version"]], "close_connection() (in module ayon_api)": [[0, "ayon_api.close_connection"]], "close_session() (serverapi method)": [[0, "ayon_api.ServerAPI.close_session"], [8, "ayon_api.server_api.ServerAPI.close_session"]], "content_size (transferprogress property)": [[0, "ayon_api.TransferProgress.content_size"], [9, "ayon_api.utils.TransferProgress.content_size"]], "create_activity() (serverapi method)": [[0, "ayon_api.ServerAPI.create_activity"], [8, "ayon_api.server_api.ServerAPI.create_activity"]], "create_activity() (in module ayon_api)": [[0, "ayon_api.create_activity"]], "create_bundle() (serverapi method)": [[0, "ayon_api.ServerAPI.create_bundle"], [8, "ayon_api.server_api.ServerAPI.create_bundle"]], "create_bundle() (in module ayon_api)": [[0, "ayon_api.create_bundle"]], "create_connection() (in module ayon_api)": [[0, "ayon_api.create_connection"]], "create_dependency_package() (serverapi method)": [[0, "ayon_api.ServerAPI.create_dependency_package"], [8, "ayon_api.server_api.ServerAPI.create_dependency_package"]], "create_dependency_package() (in module ayon_api)": [[0, "ayon_api.create_dependency_package"]], "create_dependency_package_basename() (in module ayon_api)": [[0, "ayon_api.create_dependency_package_basename"]], "create_folder() (serverapi method)": [[0, "ayon_api.ServerAPI.create_folder"], [8, "ayon_api.server_api.ServerAPI.create_folder"]], "create_folder() (in module ayon_api)": [[0, "ayon_api.create_folder"]], "create_installer() (serverapi method)": [[0, "ayon_api.ServerAPI.create_installer"], [8, "ayon_api.server_api.ServerAPI.create_installer"]], "create_installer() (in module ayon_api)": [[0, "ayon_api.create_installer"]], "create_link() (serverapi method)": [[0, "ayon_api.ServerAPI.create_link"], [8, "ayon_api.server_api.ServerAPI.create_link"]], "create_link() (in module ayon_api)": [[0, "ayon_api.create_link"]], "create_link_type() (serverapi method)": [[0, "ayon_api.ServerAPI.create_link_type"], [8, "ayon_api.server_api.ServerAPI.create_link_type"]], "create_link_type() (in module ayon_api)": [[0, "ayon_api.create_link_type"]], "create_product() (serverapi method)": [[0, "ayon_api.ServerAPI.create_product"], [8, "ayon_api.server_api.ServerAPI.create_product"]], "create_product() (in module ayon_api)": [[0, "ayon_api.create_product"]], "create_project() (serverapi method)": [[0, "ayon_api.ServerAPI.create_project"], [8, "ayon_api.server_api.ServerAPI.create_project"]], "create_project() (in module ayon_api)": [[0, "ayon_api.create_project"]], "create_representation() (serverapi method)": [[0, "ayon_api.ServerAPI.create_representation"], [8, "ayon_api.server_api.ServerAPI.create_representation"]], "create_representation() (in module ayon_api)": [[0, "ayon_api.create_representation"]], "create_session() (serverapi method)": [[0, "ayon_api.ServerAPI.create_session"], [8, "ayon_api.server_api.ServerAPI.create_session"]], "create_task() (serverapi method)": [[0, "ayon_api.ServerAPI.create_task"], [8, "ayon_api.server_api.ServerAPI.create_task"]], "create_task() (in module ayon_api)": [[0, "ayon_api.create_task"]], "create_thumbnail() (serverapi method)": [[0, "ayon_api.ServerAPI.create_thumbnail"], [8, "ayon_api.server_api.ServerAPI.create_thumbnail"]], "create_thumbnail() (in module ayon_api)": [[0, "ayon_api.create_thumbnail"]], "create_version() (serverapi method)": [[0, "ayon_api.ServerAPI.create_version"], [8, "ayon_api.server_api.ServerAPI.create_version"]], "create_version() (in module ayon_api)": [[0, "ayon_api.create_version"]], "default_download_chunk_size (serverapi attribute)": [[0, "ayon_api.ServerAPI.default_download_chunk_size"], [8, "ayon_api.server_api.ServerAPI.default_download_chunk_size"]], "default_settings_variant (serverapi property)": [[0, "ayon_api.ServerAPI.default_settings_variant"], [8, "ayon_api.server_api.ServerAPI.default_settings_variant"]], "default_upload_chunk_size (serverapi attribute)": [[0, "ayon_api.ServerAPI.default_upload_chunk_size"], [8, "ayon_api.server_api.ServerAPI.default_upload_chunk_size"]], "delete (requesttypes attribute)": [[0, "ayon_api.RequestTypes.delete"], [8, "ayon_api.server_api.RequestTypes.delete"]], "delete() (serverapi method)": [[0, "ayon_api.ServerAPI.delete"], [8, "ayon_api.server_api.ServerAPI.delete"]], "delete() (in module ayon_api)": [[0, "ayon_api.delete"]], "delete_activity() (serverapi method)": [[0, "ayon_api.ServerAPI.delete_activity"], [8, "ayon_api.server_api.ServerAPI.delete_activity"]], "delete_activity() (in module ayon_api)": [[0, "ayon_api.delete_activity"]], "delete_addon() (serverapi method)": [[0, "ayon_api.ServerAPI.delete_addon"], [8, "ayon_api.server_api.ServerAPI.delete_addon"]], "delete_addon() (in module ayon_api)": [[0, "ayon_api.delete_addon"]], "delete_addon_version() (serverapi method)": [[0, "ayon_api.ServerAPI.delete_addon_version"], [8, "ayon_api.server_api.ServerAPI.delete_addon_version"]], "delete_addon_version() (in module ayon_api)": [[0, "ayon_api.delete_addon_version"]], "delete_bundle() (serverapi method)": [[0, "ayon_api.ServerAPI.delete_bundle"], [8, "ayon_api.server_api.ServerAPI.delete_bundle"]], "delete_bundle() (in module ayon_api)": [[0, "ayon_api.delete_bundle"]], "delete_dependency_package() (serverapi method)": [[0, "ayon_api.ServerAPI.delete_dependency_package"], [8, "ayon_api.server_api.ServerAPI.delete_dependency_package"]], "delete_dependency_package() (in module ayon_api)": [[0, "ayon_api.delete_dependency_package"]], "delete_event() (serverapi method)": [[0, "ayon_api.ServerAPI.delete_event"], [8, "ayon_api.server_api.ServerAPI.delete_event"]], "delete_event() (in module ayon_api)": [[0, "ayon_api.delete_event"]], "delete_folder() (serverapi method)": [[0, "ayon_api.ServerAPI.delete_folder"], [8, "ayon_api.server_api.ServerAPI.delete_folder"]], "delete_folder() (in module ayon_api)": [[0, "ayon_api.delete_folder"]], "delete_installer() (serverapi method)": [[0, "ayon_api.ServerAPI.delete_installer"], [8, "ayon_api.server_api.ServerAPI.delete_installer"]], "delete_installer() (in module ayon_api)": [[0, "ayon_api.delete_installer"]], "delete_link() (serverapi method)": [[0, "ayon_api.ServerAPI.delete_link"], [8, "ayon_api.server_api.ServerAPI.delete_link"]], "delete_link() (in module ayon_api)": [[0, "ayon_api.delete_link"]], "delete_link_type() (serverapi method)": [[0, "ayon_api.ServerAPI.delete_link_type"], [8, "ayon_api.server_api.ServerAPI.delete_link_type"]], "delete_link_type() (in module ayon_api)": [[0, "ayon_api.delete_link_type"]], "delete_product() (serverapi method)": [[0, "ayon_api.ServerAPI.delete_product"], [8, "ayon_api.server_api.ServerAPI.delete_product"]], "delete_product() (in module ayon_api)": [[0, "ayon_api.delete_product"]], "delete_project() (serverapi method)": [[0, "ayon_api.ServerAPI.delete_project"], [8, "ayon_api.server_api.ServerAPI.delete_project"]], "delete_project() (in module ayon_api)": [[0, "ayon_api.delete_project"]], "delete_representation() (serverapi method)": [[0, "ayon_api.ServerAPI.delete_representation"], [8, "ayon_api.server_api.ServerAPI.delete_representation"]], "delete_representation() (in module ayon_api)": [[0, "ayon_api.delete_representation"]], "delete_secret() (serverapi method)": [[0, "ayon_api.ServerAPI.delete_secret"], [8, "ayon_api.server_api.ServerAPI.delete_secret"]], "delete_secret() (in module ayon_api)": [[0, "ayon_api.delete_secret"]], "delete_task() (serverapi method)": [[0, "ayon_api.ServerAPI.delete_task"], [8, "ayon_api.server_api.ServerAPI.delete_task"]], "delete_task() (in module ayon_api)": [[0, "ayon_api.delete_task"]], "delete_version() (serverapi method)": [[0, "ayon_api.ServerAPI.delete_version"], [8, "ayon_api.server_api.ServerAPI.delete_version"]], "delete_version() (in module ayon_api)": [[0, "ayon_api.delete_version"]], "descending (sortorder attribute)": [[0, "ayon_api.SortOrder.descending"], [9, "ayon_api.utils.SortOrder.descending"]], "destination_url (transferprogress property)": [[0, "ayon_api.TransferProgress.destination_url"], [9, "ayon_api.utils.TransferProgress.destination_url"]], "dispatch_event() (serverapi method)": [[0, "ayon_api.ServerAPI.dispatch_event"], [8, "ayon_api.server_api.ServerAPI.dispatch_event"]], "dispatch_event() (in module ayon_api)": [[0, "ayon_api.dispatch_event"]], "download_addon_private_file() (serverapi method)": [[0, "ayon_api.ServerAPI.download_addon_private_file"], [8, "ayon_api.server_api.ServerAPI.download_addon_private_file"]], "download_addon_private_file() (in module ayon_api)": [[0, "ayon_api.download_addon_private_file"]], "download_dependency_package() (serverapi method)": [[0, "ayon_api.ServerAPI.download_dependency_package"], [8, "ayon_api.server_api.ServerAPI.download_dependency_package"]], "download_dependency_package() (in module ayon_api)": [[0, "ayon_api.download_dependency_package"]], "download_file() (serverapi method)": [[0, "ayon_api.ServerAPI.download_file"], [8, "ayon_api.server_api.ServerAPI.download_file"]], "download_file() (in module ayon_api)": [[0, "ayon_api.download_file"]], "download_file_to_stream() (serverapi method)": [[0, "ayon_api.ServerAPI.download_file_to_stream"], [8, "ayon_api.server_api.ServerAPI.download_file_to_stream"]], "download_file_to_stream() (in module ayon_api)": [[0, "ayon_api.download_file_to_stream"]], "download_installer() (serverapi method)": [[0, "ayon_api.ServerAPI.download_installer"], [8, "ayon_api.server_api.ServerAPI.download_installer"]], "download_installer() (in module ayon_api)": [[0, "ayon_api.download_installer"]], "enroll_event_job() (serverapi method)": [[0, "ayon_api.ServerAPI.enroll_event_job"], [8, "ayon_api.server_api.ServerAPI.enroll_event_job"]], "enroll_event_job() (in module ayon_api)": [[0, "ayon_api.enroll_event_job"]], "fail_reason (transferprogress property)": [[0, "ayon_api.TransferProgress.fail_reason"], [9, "ayon_api.utils.TransferProgress.fail_reason"]], "failed (transferprogress property)": [[0, "ayon_api.TransferProgress.failed"], [9, "ayon_api.utils.TransferProgress.failed"]], "get (requesttypes attribute)": [[0, "ayon_api.RequestTypes.get"], [8, "ayon_api.server_api.RequestTypes.get"]], "get() (serverapi method)": [[0, "ayon_api.ServerAPI.get"], [8, "ayon_api.server_api.ServerAPI.get"]], "get() (in module ayon_api)": [[0, "ayon_api.get"]], "get_activities() (serverapi method)": [[0, "ayon_api.ServerAPI.get_activities"], [8, "ayon_api.server_api.ServerAPI.get_activities"]], "get_activities() (in module ayon_api)": [[0, "ayon_api.get_activities"]], "get_activity_by_id() (serverapi method)": [[0, "ayon_api.ServerAPI.get_activity_by_id"], [8, "ayon_api.server_api.ServerAPI.get_activity_by_id"]], "get_activity_by_id() (in module ayon_api)": [[0, "ayon_api.get_activity_by_id"]], "get_addon_endpoint() (serverapi method)": [[0, "ayon_api.ServerAPI.get_addon_endpoint"], [8, "ayon_api.server_api.ServerAPI.get_addon_endpoint"]], "get_addon_endpoint() (in module ayon_api)": [[0, "ayon_api.get_addon_endpoint"]], "get_addon_project_settings() (serverapi method)": [[0, "ayon_api.ServerAPI.get_addon_project_settings"], [8, "ayon_api.server_api.ServerAPI.get_addon_project_settings"]], "get_addon_project_settings() (in module ayon_api)": [[0, "ayon_api.get_addon_project_settings"]], "get_addon_settings() (serverapi method)": [[0, "ayon_api.ServerAPI.get_addon_settings"], [8, "ayon_api.server_api.ServerAPI.get_addon_settings"]], "get_addon_settings() (in module ayon_api)": [[0, "ayon_api.get_addon_settings"]], "get_addon_settings_schema() (serverapi method)": [[0, "ayon_api.ServerAPI.get_addon_settings_schema"], [8, "ayon_api.server_api.ServerAPI.get_addon_settings_schema"]], "get_addon_settings_schema() (in module ayon_api)": [[0, "ayon_api.get_addon_settings_schema"]], "get_addon_site_settings() (serverapi method)": [[0, "ayon_api.ServerAPI.get_addon_site_settings"], [8, "ayon_api.server_api.ServerAPI.get_addon_site_settings"]], "get_addon_site_settings() (in module ayon_api)": [[0, "ayon_api.get_addon_site_settings"]], "get_addon_site_settings_schema() (serverapi method)": [[0, "ayon_api.ServerAPI.get_addon_site_settings_schema"], [8, "ayon_api.server_api.ServerAPI.get_addon_site_settings_schema"]], "get_addon_site_settings_schema() (in module ayon_api)": [[0, "ayon_api.get_addon_site_settings_schema"]], "get_addon_studio_settings() (serverapi method)": [[0, "ayon_api.ServerAPI.get_addon_studio_settings"], [8, "ayon_api.server_api.ServerAPI.get_addon_studio_settings"]], "get_addon_studio_settings() (in module ayon_api)": [[0, "ayon_api.get_addon_studio_settings"]], "get_addon_url() (serverapi method)": [[0, "ayon_api.ServerAPI.get_addon_url"], [8, "ayon_api.server_api.ServerAPI.get_addon_url"]], "get_addon_url() (in module ayon_api)": [[0, "ayon_api.get_addon_url"]], "get_addons_info() (serverapi method)": [[0, "ayon_api.ServerAPI.get_addons_info"], [8, "ayon_api.server_api.ServerAPI.get_addons_info"]], "get_addons_info() (in module ayon_api)": [[0, "ayon_api.get_addons_info"]], "get_addons_project_settings() (serverapi method)": [[0, "ayon_api.ServerAPI.get_addons_project_settings"], [8, "ayon_api.server_api.ServerAPI.get_addons_project_settings"]], "get_addons_project_settings() (in module ayon_api)": [[0, "ayon_api.get_addons_project_settings"]], "get_addons_settings() (serverapi method)": [[0, "ayon_api.ServerAPI.get_addons_settings"], [8, "ayon_api.server_api.ServerAPI.get_addons_settings"]], "get_addons_settings() (in module ayon_api)": [[0, "ayon_api.get_addons_settings"]], "get_addons_studio_settings() (serverapi method)": [[0, "ayon_api.ServerAPI.get_addons_studio_settings"], [8, "ayon_api.server_api.ServerAPI.get_addons_studio_settings"]], "get_addons_studio_settings() (in module ayon_api)": [[0, "ayon_api.get_addons_studio_settings"]], "get_attributes_fields_for_type() (serverapi method)": [[0, "ayon_api.ServerAPI.get_attributes_fields_for_type"], [8, "ayon_api.server_api.ServerAPI.get_attributes_fields_for_type"]], "get_attributes_fields_for_type() (in module ayon_api)": [[0, "ayon_api.get_attributes_fields_for_type"]], "get_attributes_for_type() (serverapi method)": [[0, "ayon_api.ServerAPI.get_attributes_for_type"], [8, "ayon_api.server_api.ServerAPI.get_attributes_for_type"]], "get_attributes_for_type() (in module ayon_api)": [[0, "ayon_api.get_attributes_for_type"]], "get_attributes_schema() (serverapi method)": [[0, "ayon_api.ServerAPI.get_attributes_schema"], [8, "ayon_api.server_api.ServerAPI.get_attributes_schema"]], "get_attributes_schema() (in module ayon_api)": [[0, "ayon_api.get_attributes_schema"]], "get_base_url() (serverapi method)": [[0, "ayon_api.ServerAPI.get_base_url"], [8, "ayon_api.server_api.ServerAPI.get_base_url"]], "get_base_url() (in module ayon_api)": [[0, "ayon_api.get_base_url"]], "get_build_in_anatomy_preset() (serverapi method)": [[0, "ayon_api.ServerAPI.get_build_in_anatomy_preset"], [8, "ayon_api.server_api.ServerAPI.get_build_in_anatomy_preset"]], "get_build_in_anatomy_preset() (in module ayon_api)": [[0, "ayon_api.get_build_in_anatomy_preset"]], "get_bundle_settings() (serverapi method)": [[0, "ayon_api.ServerAPI.get_bundle_settings"], [8, "ayon_api.server_api.ServerAPI.get_bundle_settings"]], "get_bundle_settings() (in module ayon_api)": [[0, "ayon_api.get_bundle_settings"]], "get_bundles() (serverapi method)": [[0, "ayon_api.ServerAPI.get_bundles"], [8, "ayon_api.server_api.ServerAPI.get_bundles"]], "get_bundles() (in module ayon_api)": [[0, "ayon_api.get_bundles"]], "get_cert() (serverapi method)": [[0, "ayon_api.ServerAPI.get_cert"], [8, "ayon_api.server_api.ServerAPI.get_cert"]], "get_cert() (in module ayon_api)": [[0, "ayon_api.get_cert"]], "get_client_version() (serverapi method)": [[0, "ayon_api.ServerAPI.get_client_version"], [8, "ayon_api.server_api.ServerAPI.get_client_version"]], "get_client_version() (in module ayon_api)": [[0, "ayon_api.get_client_version"]], "get_content_size() (transferprogress method)": [[0, "ayon_api.TransferProgress.get_content_size"], [9, "ayon_api.utils.TransferProgress.get_content_size"]], "get_default_anatomy_preset_name() (serverapi method)": [[0, "ayon_api.ServerAPI.get_default_anatomy_preset_name"], [8, "ayon_api.server_api.ServerAPI.get_default_anatomy_preset_name"]], "get_default_anatomy_preset_name() (in module ayon_api)": [[0, "ayon_api.get_default_anatomy_preset_name"]], "get_default_fields_for_type() (serverapi method)": [[0, "ayon_api.ServerAPI.get_default_fields_for_type"], [8, "ayon_api.server_api.ServerAPI.get_default_fields_for_type"]], "get_default_fields_for_type() (in module ayon_api)": [[0, "ayon_api.get_default_fields_for_type"]], "get_default_max_retries() (serverapi class method)": [[0, "ayon_api.ServerAPI.get_default_max_retries"], [8, "ayon_api.server_api.ServerAPI.get_default_max_retries"]], "get_default_service_username() (serverapi method)": [[0, "ayon_api.ServerAPI.get_default_service_username"], [8, "ayon_api.server_api.ServerAPI.get_default_service_username"]], "get_default_settings_variant() (serverapi method)": [[0, "ayon_api.ServerAPI.get_default_settings_variant"], [8, "ayon_api.server_api.ServerAPI.get_default_settings_variant"]], "get_default_settings_variant() (in module ayon_api)": [[0, "ayon_api.get_default_settings_variant"]], "get_default_timeout() (serverapi class method)": [[0, "ayon_api.ServerAPI.get_default_timeout"], [8, "ayon_api.server_api.ServerAPI.get_default_timeout"]], "get_dependency_packages() (serverapi method)": [[0, "ayon_api.ServerAPI.get_dependency_packages"], [8, "ayon_api.server_api.ServerAPI.get_dependency_packages"]], "get_dependency_packages() (in module ayon_api)": [[0, "ayon_api.get_dependency_packages"]], "get_destination_url() (transferprogress method)": [[0, "ayon_api.TransferProgress.get_destination_url"], [9, "ayon_api.utils.TransferProgress.get_destination_url"]], "get_entities_links() (serverapi method)": [[0, "ayon_api.ServerAPI.get_entities_links"], [8, "ayon_api.server_api.ServerAPI.get_entities_links"]], "get_entities_links() (in module ayon_api)": [[0, "ayon_api.get_entities_links"]], "get_event() (serverapi method)": [[0, "ayon_api.ServerAPI.get_event"], [8, "ayon_api.server_api.ServerAPI.get_event"]], "get_event() (in module ayon_api)": [[0, "ayon_api.get_event"]], "get_events() (serverapi method)": [[0, "ayon_api.ServerAPI.get_events"], [8, "ayon_api.server_api.ServerAPI.get_events"]], "get_events() (in module ayon_api)": [[0, "ayon_api.get_events"]], "get_fail_reason() (transferprogress method)": [[0, "ayon_api.TransferProgress.get_fail_reason"], [9, "ayon_api.utils.TransferProgress.get_fail_reason"]], "get_failed() (transferprogress method)": [[0, "ayon_api.TransferProgress.get_failed"], [9, "ayon_api.utils.TransferProgress.get_failed"]], "get_folder_by_id() (serverapi method)": [[0, "ayon_api.ServerAPI.get_folder_by_id"], [8, "ayon_api.server_api.ServerAPI.get_folder_by_id"]], "get_folder_by_id() (in module ayon_api)": [[0, "ayon_api.get_folder_by_id"]], "get_folder_by_name() (serverapi method)": [[0, "ayon_api.ServerAPI.get_folder_by_name"], [8, "ayon_api.server_api.ServerAPI.get_folder_by_name"]], "get_folder_by_name() (in module ayon_api)": [[0, "ayon_api.get_folder_by_name"]], "get_folder_by_path() (serverapi method)": [[0, "ayon_api.ServerAPI.get_folder_by_path"], [8, "ayon_api.server_api.ServerAPI.get_folder_by_path"]], "get_folder_by_path() (in module ayon_api)": [[0, "ayon_api.get_folder_by_path"]], "get_folder_ids_with_products() (serverapi method)": [[0, "ayon_api.ServerAPI.get_folder_ids_with_products"], [8, "ayon_api.server_api.ServerAPI.get_folder_ids_with_products"]], "get_folder_ids_with_products() (in module ayon_api)": [[0, "ayon_api.get_folder_ids_with_products"]], "get_folder_links() (serverapi method)": [[0, "ayon_api.ServerAPI.get_folder_links"], [8, "ayon_api.server_api.ServerAPI.get_folder_links"]], "get_folder_links() (in module ayon_api)": [[0, "ayon_api.get_folder_links"]], "get_folder_thumbnail() (serverapi method)": [[0, "ayon_api.ServerAPI.get_folder_thumbnail"], [8, "ayon_api.server_api.ServerAPI.get_folder_thumbnail"]], "get_folder_thumbnail() (in module ayon_api)": [[0, "ayon_api.get_folder_thumbnail"]], "get_folders() (serverapi method)": [[0, "ayon_api.ServerAPI.get_folders"], [8, "ayon_api.server_api.ServerAPI.get_folders"]], "get_folders() (in module ayon_api)": [[0, "ayon_api.get_folders"]], "get_folders_hierarchy() (serverapi method)": [[0, "ayon_api.ServerAPI.get_folders_hierarchy"], [8, "ayon_api.server_api.ServerAPI.get_folders_hierarchy"]], "get_folders_hierarchy() (in module ayon_api)": [[0, "ayon_api.get_folders_hierarchy"]], "get_folders_links() (serverapi method)": [[0, "ayon_api.ServerAPI.get_folders_links"], [8, "ayon_api.server_api.ServerAPI.get_folders_links"]], "get_folders_links() (in module ayon_api)": [[0, "ayon_api.get_folders_links"]], "get_folders_rest() (serverapi method)": [[0, "ayon_api.ServerAPI.get_folders_rest"], [8, "ayon_api.server_api.ServerAPI.get_folders_rest"]], "get_folders_rest() (in module ayon_api)": [[0, "ayon_api.get_folders_rest"]], "get_full_link_type_name() (serverapi method)": [[0, "ayon_api.ServerAPI.get_full_link_type_name"], [8, "ayon_api.server_api.ServerAPI.get_full_link_type_name"]], "get_full_link_type_name() (in module ayon_api)": [[0, "ayon_api.get_full_link_type_name"]], "get_graphql_schema() (serverapi method)": [[0, "ayon_api.ServerAPI.get_graphql_schema"], [8, "ayon_api.server_api.ServerAPI.get_graphql_schema"]], "get_graphql_schema() (in module ayon_api)": [[0, "ayon_api.get_graphql_schema"]], "get_headers() (serverapi method)": [[0, "ayon_api.ServerAPI.get_headers"], [8, "ayon_api.server_api.ServerAPI.get_headers"]], "get_hero_version_by_id() (serverapi method)": [[0, "ayon_api.ServerAPI.get_hero_version_by_id"], [8, "ayon_api.server_api.ServerAPI.get_hero_version_by_id"]], "get_hero_version_by_id() (in module ayon_api)": [[0, "ayon_api.get_hero_version_by_id"]], "get_hero_version_by_product_id() (serverapi method)": [[0, "ayon_api.ServerAPI.get_hero_version_by_product_id"], [8, "ayon_api.server_api.ServerAPI.get_hero_version_by_product_id"]], "get_hero_version_by_product_id() (in module ayon_api)": [[0, "ayon_api.get_hero_version_by_product_id"]], "get_hero_versions() (serverapi method)": [[0, "ayon_api.ServerAPI.get_hero_versions"], [8, "ayon_api.server_api.ServerAPI.get_hero_versions"]], "get_hero_versions() (in module ayon_api)": [[0, "ayon_api.get_hero_versions"]], "get_info() (serverapi method)": [[0, "ayon_api.ServerAPI.get_info"], [8, "ayon_api.server_api.ServerAPI.get_info"]], "get_info() (in module ayon_api)": [[0, "ayon_api.get_info"]], "get_installers() (serverapi method)": [[0, "ayon_api.ServerAPI.get_installers"], [8, "ayon_api.server_api.ServerAPI.get_installers"]], "get_installers() (in module ayon_api)": [[0, "ayon_api.get_installers"]], "get_last_version_by_product_id() (serverapi method)": [[0, "ayon_api.ServerAPI.get_last_version_by_product_id"], [8, "ayon_api.server_api.ServerAPI.get_last_version_by_product_id"]], "get_last_version_by_product_id() (in module ayon_api)": [[0, "ayon_api.get_last_version_by_product_id"]], "get_last_version_by_product_name() (serverapi method)": [[0, "ayon_api.ServerAPI.get_last_version_by_product_name"], [8, "ayon_api.server_api.ServerAPI.get_last_version_by_product_name"]], "get_last_version_by_product_name() (in module ayon_api)": [[0, "ayon_api.get_last_version_by_product_name"]], "get_last_versions() (serverapi method)": [[0, "ayon_api.ServerAPI.get_last_versions"], [8, "ayon_api.server_api.ServerAPI.get_last_versions"]], "get_last_versions() (in module ayon_api)": [[0, "ayon_api.get_last_versions"]], "get_link_type() (serverapi method)": [[0, "ayon_api.ServerAPI.get_link_type"], [8, "ayon_api.server_api.ServerAPI.get_link_type"]], "get_link_type() (in module ayon_api)": [[0, "ayon_api.get_link_type"]], "get_link_types() (serverapi method)": [[0, "ayon_api.ServerAPI.get_link_types"], [8, "ayon_api.server_api.ServerAPI.get_link_types"]], "get_link_types() (in module ayon_api)": [[0, "ayon_api.get_link_types"]], "get_max_retries() (serverapi method)": [[0, "ayon_api.ServerAPI.get_max_retries"], [8, "ayon_api.server_api.ServerAPI.get_max_retries"]], "get_max_retries() (in module ayon_api)": [[0, "ayon_api.get_max_retries"]], "get_product_by_id() (serverapi method)": [[0, "ayon_api.ServerAPI.get_product_by_id"], [8, "ayon_api.server_api.ServerAPI.get_product_by_id"]], "get_product_by_id() (in module ayon_api)": [[0, "ayon_api.get_product_by_id"]], "get_product_by_name() (serverapi method)": [[0, "ayon_api.ServerAPI.get_product_by_name"], [8, "ayon_api.server_api.ServerAPI.get_product_by_name"]], "get_product_by_name() (in module ayon_api)": [[0, "ayon_api.get_product_by_name"]], "get_product_links() (serverapi method)": [[0, "ayon_api.ServerAPI.get_product_links"], [8, "ayon_api.server_api.ServerAPI.get_product_links"]], "get_product_links() (in module ayon_api)": [[0, "ayon_api.get_product_links"]], "get_product_type_names() (serverapi method)": [[0, "ayon_api.ServerAPI.get_product_type_names"], [8, "ayon_api.server_api.ServerAPI.get_product_type_names"]], "get_product_type_names() (in module ayon_api)": [[0, "ayon_api.get_product_type_names"]], "get_product_types() (serverapi method)": [[0, "ayon_api.ServerAPI.get_product_types"], [8, "ayon_api.server_api.ServerAPI.get_product_types"]], "get_product_types() (in module ayon_api)": [[0, "ayon_api.get_product_types"]], "get_products() (serverapi method)": [[0, "ayon_api.ServerAPI.get_products"], [8, "ayon_api.server_api.ServerAPI.get_products"]], "get_products() (in module ayon_api)": [[0, "ayon_api.get_products"]], "get_products_links() (serverapi method)": [[0, "ayon_api.ServerAPI.get_products_links"], [8, "ayon_api.server_api.ServerAPI.get_products_links"]], "get_products_links() (in module ayon_api)": [[0, "ayon_api.get_products_links"]], "get_project() (serverapi method)": [[0, "ayon_api.ServerAPI.get_project"], [8, "ayon_api.server_api.ServerAPI.get_project"]], "get_project() (in module ayon_api)": [[0, "ayon_api.get_project"]], "get_project_anatomy_preset() (serverapi method)": [[0, "ayon_api.ServerAPI.get_project_anatomy_preset"], [8, "ayon_api.server_api.ServerAPI.get_project_anatomy_preset"]], "get_project_anatomy_preset() (in module ayon_api)": [[0, "ayon_api.get_project_anatomy_preset"]], "get_project_anatomy_presets() (serverapi method)": [[0, "ayon_api.ServerAPI.get_project_anatomy_presets"], [8, "ayon_api.server_api.ServerAPI.get_project_anatomy_presets"]], "get_project_anatomy_presets() (in module ayon_api)": [[0, "ayon_api.get_project_anatomy_presets"]], "get_project_names() (serverapi method)": [[0, "ayon_api.ServerAPI.get_project_names"], [8, "ayon_api.server_api.ServerAPI.get_project_names"]], "get_project_names() (in module ayon_api)": [[0, "ayon_api.get_project_names"]], "get_project_product_types() (serverapi method)": [[0, "ayon_api.ServerAPI.get_project_product_types"], [8, "ayon_api.server_api.ServerAPI.get_project_product_types"]], "get_project_product_types() (in module ayon_api)": [[0, "ayon_api.get_project_product_types"]], "get_project_root_overrides() (serverapi method)": [[0, "ayon_api.ServerAPI.get_project_root_overrides"], [8, "ayon_api.server_api.ServerAPI.get_project_root_overrides"]], "get_project_root_overrides() (in module ayon_api)": [[0, "ayon_api.get_project_root_overrides"]], "get_project_root_overrides_by_site_id() (serverapi method)": [[0, "ayon_api.ServerAPI.get_project_root_overrides_by_site_id"], [8, "ayon_api.server_api.ServerAPI.get_project_root_overrides_by_site_id"]], "get_project_root_overrides_by_site_id() (in module ayon_api)": [[0, "ayon_api.get_project_root_overrides_by_site_id"]], "get_project_roots_by_platform() (serverapi method)": [[0, "ayon_api.ServerAPI.get_project_roots_by_platform"], [8, "ayon_api.server_api.ServerAPI.get_project_roots_by_platform"]], "get_project_roots_by_platform() (in module ayon_api)": [[0, "ayon_api.get_project_roots_by_platform"]], "get_project_roots_by_site() (serverapi method)": [[0, "ayon_api.ServerAPI.get_project_roots_by_site"], [8, "ayon_api.server_api.ServerAPI.get_project_roots_by_site"]], "get_project_roots_by_site() (in module ayon_api)": [[0, "ayon_api.get_project_roots_by_site"]], "get_project_roots_by_site_id() (serverapi method)": [[0, "ayon_api.ServerAPI.get_project_roots_by_site_id"], [8, "ayon_api.server_api.ServerAPI.get_project_roots_by_site_id"]], "get_project_roots_by_site_id() (in module ayon_api)": [[0, "ayon_api.get_project_roots_by_site_id"]], "get_project_roots_for_site() (serverapi method)": [[0, "ayon_api.ServerAPI.get_project_roots_for_site"], [8, "ayon_api.server_api.ServerAPI.get_project_roots_for_site"]], "get_project_roots_for_site() (in module ayon_api)": [[0, "ayon_api.get_project_roots_for_site"]], "get_projects() (serverapi method)": [[0, "ayon_api.ServerAPI.get_projects"], [8, "ayon_api.server_api.ServerAPI.get_projects"]], "get_projects() (in module ayon_api)": [[0, "ayon_api.get_projects"]], "get_repre_ids_by_context_filters() (serverapi method)": [[0, "ayon_api.ServerAPI.get_repre_ids_by_context_filters"], [8, "ayon_api.server_api.ServerAPI.get_repre_ids_by_context_filters"]], "get_repre_ids_by_context_filters() (in module ayon_api)": [[0, "ayon_api.get_repre_ids_by_context_filters"]], "get_representation_by_id() (serverapi method)": [[0, "ayon_api.ServerAPI.get_representation_by_id"], [8, "ayon_api.server_api.ServerAPI.get_representation_by_id"]], "get_representation_by_id() (in module ayon_api)": [[0, "ayon_api.get_representation_by_id"]], "get_representation_by_name() (serverapi method)": [[0, "ayon_api.ServerAPI.get_representation_by_name"], [8, "ayon_api.server_api.ServerAPI.get_representation_by_name"]], "get_representation_by_name() (in module ayon_api)": [[0, "ayon_api.get_representation_by_name"]], "get_representation_hierarchy() (serverapi method)": [[0, "ayon_api.ServerAPI.get_representation_hierarchy"], [8, "ayon_api.server_api.ServerAPI.get_representation_hierarchy"]], "get_representation_hierarchy() (in module ayon_api)": [[0, "ayon_api.get_representation_hierarchy"]], "get_representation_links() (serverapi method)": [[0, "ayon_api.ServerAPI.get_representation_links"], [8, "ayon_api.server_api.ServerAPI.get_representation_links"]], "get_representation_links() (in module ayon_api)": [[0, "ayon_api.get_representation_links"]], "get_representation_parents() (serverapi method)": [[0, "ayon_api.ServerAPI.get_representation_parents"], [8, "ayon_api.server_api.ServerAPI.get_representation_parents"]], "get_representation_parents() (in module ayon_api)": [[0, "ayon_api.get_representation_parents"]], "get_representations() (serverapi method)": [[0, "ayon_api.ServerAPI.get_representations"], [8, "ayon_api.server_api.ServerAPI.get_representations"]], "get_representations() (in module ayon_api)": [[0, "ayon_api.get_representations"]], "get_representations_hierarchy() (serverapi method)": [[0, "ayon_api.ServerAPI.get_representations_hierarchy"], [8, "ayon_api.server_api.ServerAPI.get_representations_hierarchy"]], "get_representations_hierarchy() (in module ayon_api)": [[0, "ayon_api.get_representations_hierarchy"]], "get_representations_links() (serverapi method)": [[0, "ayon_api.ServerAPI.get_representations_links"], [8, "ayon_api.server_api.ServerAPI.get_representations_links"]], "get_representations_links() (in module ayon_api)": [[0, "ayon_api.get_representations_links"]], "get_representations_parents() (serverapi method)": [[0, "ayon_api.ServerAPI.get_representations_parents"], [8, "ayon_api.server_api.ServerAPI.get_representations_parents"]], "get_representations_parents() (in module ayon_api)": [[0, "ayon_api.get_representations_parents"]], "get_rest_entity_by_id() (serverapi method)": [[0, "ayon_api.ServerAPI.get_rest_entity_by_id"], [8, "ayon_api.server_api.ServerAPI.get_rest_entity_by_id"]], "get_rest_entity_by_id() (in module ayon_api)": [[0, "ayon_api.get_rest_entity_by_id"]], "get_rest_folder() (serverapi method)": [[0, "ayon_api.ServerAPI.get_rest_folder"], [8, "ayon_api.server_api.ServerAPI.get_rest_folder"]], "get_rest_folder() (in module ayon_api)": [[0, "ayon_api.get_rest_folder"]], "get_rest_folders() (serverapi method)": [[0, "ayon_api.ServerAPI.get_rest_folders"], [8, "ayon_api.server_api.ServerAPI.get_rest_folders"]], "get_rest_folders() (in module ayon_api)": [[0, "ayon_api.get_rest_folders"]], "get_rest_product() (serverapi method)": [[0, "ayon_api.ServerAPI.get_rest_product"], [8, "ayon_api.server_api.ServerAPI.get_rest_product"]], "get_rest_product() (in module ayon_api)": [[0, "ayon_api.get_rest_product"]], "get_rest_project() (serverapi method)": [[0, "ayon_api.ServerAPI.get_rest_project"], [8, "ayon_api.server_api.ServerAPI.get_rest_project"]], "get_rest_project() (in module ayon_api)": [[0, "ayon_api.get_rest_project"]], "get_rest_projects() (serverapi method)": [[0, "ayon_api.ServerAPI.get_rest_projects"], [8, "ayon_api.server_api.ServerAPI.get_rest_projects"]], "get_rest_projects() (in module ayon_api)": [[0, "ayon_api.get_rest_projects"]], "get_rest_representation() (serverapi method)": [[0, "ayon_api.ServerAPI.get_rest_representation"], [8, "ayon_api.server_api.ServerAPI.get_rest_representation"]], "get_rest_representation() (in module ayon_api)": [[0, "ayon_api.get_rest_representation"]], "get_rest_task() (serverapi method)": [[0, "ayon_api.ServerAPI.get_rest_task"], [8, "ayon_api.server_api.ServerAPI.get_rest_task"]], "get_rest_task() (in module ayon_api)": [[0, "ayon_api.get_rest_task"]], "get_rest_url() (serverapi method)": [[0, "ayon_api.ServerAPI.get_rest_url"], [8, "ayon_api.server_api.ServerAPI.get_rest_url"]], "get_rest_url() (in module ayon_api)": [[0, "ayon_api.get_rest_url"]], "get_rest_version() (serverapi method)": [[0, "ayon_api.ServerAPI.get_rest_version"], [8, "ayon_api.server_api.ServerAPI.get_rest_version"]], "get_rest_version() (in module ayon_api)": [[0, "ayon_api.get_rest_version"]], "get_schemas() (serverapi method)": [[0, "ayon_api.ServerAPI.get_schemas"], [8, "ayon_api.server_api.ServerAPI.get_schemas"]], "get_schemas() (in module ayon_api)": [[0, "ayon_api.get_schemas"]], "get_secret() (serverapi method)": [[0, "ayon_api.ServerAPI.get_secret"], [8, "ayon_api.server_api.ServerAPI.get_secret"]], "get_secret() (in module ayon_api)": [[0, "ayon_api.get_secret"]], "get_secrets() (serverapi method)": [[0, "ayon_api.ServerAPI.get_secrets"], [8, "ayon_api.server_api.ServerAPI.get_secrets"]], "get_secrets() (in module ayon_api)": [[0, "ayon_api.get_secrets"]], "get_sender() (serverapi method)": [[0, "ayon_api.ServerAPI.get_sender"], [8, "ayon_api.server_api.ServerAPI.get_sender"]], "get_sender() (in module ayon_api)": [[0, "ayon_api.get_sender"]], "get_sender_type() (serverapi method)": [[0, "ayon_api.ServerAPI.get_sender_type"], [8, "ayon_api.server_api.ServerAPI.get_sender_type"]], "get_sender_type() (in module ayon_api)": [[0, "ayon_api.get_sender_type"]], "get_server_api_connection() (in module ayon_api)": [[0, "ayon_api.get_server_api_connection"]], "get_server_schema() (serverapi method)": [[0, "ayon_api.ServerAPI.get_server_schema"], [8, "ayon_api.server_api.ServerAPI.get_server_schema"]], "get_server_schema() (in module ayon_api)": [[0, "ayon_api.get_server_schema"]], "get_server_version() (serverapi method)": [[0, "ayon_api.ServerAPI.get_server_version"], [8, "ayon_api.server_api.ServerAPI.get_server_version"]], "get_server_version() (in module ayon_api)": [[0, "ayon_api.get_server_version"]], "get_server_version_tuple() (serverapi method)": [[0, "ayon_api.ServerAPI.get_server_version_tuple"], [8, "ayon_api.server_api.ServerAPI.get_server_version_tuple"]], "get_server_version_tuple() (in module ayon_api)": [[0, "ayon_api.get_server_version_tuple"]], "get_service_addon_name() (in module ayon_api)": [[0, "ayon_api.get_service_addon_name"]], "get_service_addon_settings() (in module ayon_api)": [[0, "ayon_api.get_service_addon_settings"]], "get_service_addon_version() (in module ayon_api)": [[0, "ayon_api.get_service_addon_version"]], "get_service_name() (in module ayon_api)": [[0, "ayon_api.get_service_name"]], "get_site_id() (serverapi method)": [[0, "ayon_api.ServerAPI.get_site_id"], [8, "ayon_api.server_api.ServerAPI.get_site_id"]], "get_site_id() (in module ayon_api)": [[0, "ayon_api.get_site_id"]], "get_source_url() (transferprogress method)": [[0, "ayon_api.TransferProgress.get_source_url"], [9, "ayon_api.utils.TransferProgress.get_source_url"]], "get_ssl_verify() (serverapi method)": [[0, "ayon_api.ServerAPI.get_ssl_verify"], [8, "ayon_api.server_api.ServerAPI.get_ssl_verify"]], "get_ssl_verify() (in module ayon_api)": [[0, "ayon_api.get_ssl_verify"]], "get_started() (transferprogress method)": [[0, "ayon_api.TransferProgress.get_started"], [9, "ayon_api.utils.TransferProgress.get_started"]], "get_task_by_folder_path() (serverapi method)": [[0, "ayon_api.ServerAPI.get_task_by_folder_path"], [8, "ayon_api.server_api.ServerAPI.get_task_by_folder_path"]], "get_task_by_folder_path() (in module ayon_api)": [[0, "ayon_api.get_task_by_folder_path"]], "get_task_by_id() (serverapi method)": [[0, "ayon_api.ServerAPI.get_task_by_id"], [8, "ayon_api.server_api.ServerAPI.get_task_by_id"]], "get_task_by_id() (in module ayon_api)": [[0, "ayon_api.get_task_by_id"]], "get_task_by_name() (serverapi method)": [[0, "ayon_api.ServerAPI.get_task_by_name"], [8, "ayon_api.server_api.ServerAPI.get_task_by_name"]], "get_task_by_name() (in module ayon_api)": [[0, "ayon_api.get_task_by_name"]], "get_task_links() (serverapi method)": [[0, "ayon_api.ServerAPI.get_task_links"], [8, "ayon_api.server_api.ServerAPI.get_task_links"]], "get_task_links() (in module ayon_api)": [[0, "ayon_api.get_task_links"]], "get_tasks() (serverapi method)": [[0, "ayon_api.ServerAPI.get_tasks"], [8, "ayon_api.server_api.ServerAPI.get_tasks"]], "get_tasks() (in module ayon_api)": [[0, "ayon_api.get_tasks"]], "get_tasks_by_folder_path() (serverapi method)": [[0, "ayon_api.ServerAPI.get_tasks_by_folder_path"], [8, "ayon_api.server_api.ServerAPI.get_tasks_by_folder_path"]], "get_tasks_by_folder_path() (in module ayon_api)": [[0, "ayon_api.get_tasks_by_folder_path"]], "get_tasks_by_folder_paths() (serverapi method)": [[0, "ayon_api.ServerAPI.get_tasks_by_folder_paths"], [8, "ayon_api.server_api.ServerAPI.get_tasks_by_folder_paths"]], "get_tasks_by_folder_paths() (in module ayon_api)": [[0, "ayon_api.get_tasks_by_folder_paths"]], "get_tasks_links() (serverapi method)": [[0, "ayon_api.ServerAPI.get_tasks_links"], [8, "ayon_api.server_api.ServerAPI.get_tasks_links"]], "get_tasks_links() (in module ayon_api)": [[0, "ayon_api.get_tasks_links"]], "get_thumbnail() (serverapi method)": [[0, "ayon_api.ServerAPI.get_thumbnail"], [8, "ayon_api.server_api.ServerAPI.get_thumbnail"]], "get_thumbnail() (in module ayon_api)": [[0, "ayon_api.get_thumbnail"]], "get_thumbnail_by_id() (serverapi method)": [[0, "ayon_api.ServerAPI.get_thumbnail_by_id"], [8, "ayon_api.server_api.ServerAPI.get_thumbnail_by_id"]], "get_thumbnail_by_id() (in module ayon_api)": [[0, "ayon_api.get_thumbnail_by_id"]], "get_timeout() (serverapi method)": [[0, "ayon_api.ServerAPI.get_timeout"], [8, "ayon_api.server_api.ServerAPI.get_timeout"]], "get_timeout() (in module ayon_api)": [[0, "ayon_api.get_timeout"]], "get_token() (globalserverapi static method)": [[0, "ayon_api.GlobalServerAPI.get_token"]], "get_transfer_done() (transferprogress method)": [[0, "ayon_api.TransferProgress.get_transfer_done"], [9, "ayon_api.utils.TransferProgress.get_transfer_done"]], "get_transferred_size() (transferprogress method)": [[0, "ayon_api.TransferProgress.get_transferred_size"], [9, "ayon_api.utils.TransferProgress.get_transferred_size"]], "get_url() (globalserverapi static method)": [[0, "ayon_api.GlobalServerAPI.get_url"]], "get_user() (serverapi method)": [[0, "ayon_api.ServerAPI.get_user"], [8, "ayon_api.server_api.ServerAPI.get_user"]], "get_user() (in module ayon_api)": [[0, "ayon_api.get_user"]], "get_user_by_name() (serverapi method)": [[0, "ayon_api.ServerAPI.get_user_by_name"], [8, "ayon_api.server_api.ServerAPI.get_user_by_name"]], "get_user_by_name() (in module ayon_api)": [[0, "ayon_api.get_user_by_name"]], "get_user_by_token() (in module ayon_api)": [[0, "ayon_api.get_user_by_token"]], "get_users() (serverapi method)": [[0, "ayon_api.ServerAPI.get_users"], [8, "ayon_api.server_api.ServerAPI.get_users"]], "get_users() (in module ayon_api)": [[0, "ayon_api.get_users"]], "get_version_by_id() (serverapi method)": [[0, "ayon_api.ServerAPI.get_version_by_id"], [8, "ayon_api.server_api.ServerAPI.get_version_by_id"]], "get_version_by_id() (in module ayon_api)": [[0, "ayon_api.get_version_by_id"]], "get_version_by_name() (serverapi method)": [[0, "ayon_api.ServerAPI.get_version_by_name"], [8, "ayon_api.server_api.ServerAPI.get_version_by_name"]], "get_version_by_name() (in module ayon_api)": [[0, "ayon_api.get_version_by_name"]], "get_version_links() (serverapi method)": [[0, "ayon_api.ServerAPI.get_version_links"], [8, "ayon_api.server_api.ServerAPI.get_version_links"]], "get_version_links() (in module ayon_api)": [[0, "ayon_api.get_version_links"]], "get_version_thumbnail() (serverapi method)": [[0, "ayon_api.ServerAPI.get_version_thumbnail"], [8, "ayon_api.server_api.ServerAPI.get_version_thumbnail"]], "get_version_thumbnail() (in module ayon_api)": [[0, "ayon_api.get_version_thumbnail"]], "get_versions() (serverapi method)": [[0, "ayon_api.ServerAPI.get_versions"], [8, "ayon_api.server_api.ServerAPI.get_versions"]], "get_versions() (in module ayon_api)": [[0, "ayon_api.get_versions"]], "get_versions_links() (serverapi method)": [[0, "ayon_api.ServerAPI.get_versions_links"], [8, "ayon_api.server_api.ServerAPI.get_versions_links"]], "get_versions_links() (in module ayon_api)": [[0, "ayon_api.get_versions_links"]], "get_workfile_info() (serverapi method)": [[0, "ayon_api.ServerAPI.get_workfile_info"], [8, "ayon_api.server_api.ServerAPI.get_workfile_info"]], "get_workfile_info() (in module ayon_api)": [[0, "ayon_api.get_workfile_info"]], "get_workfile_info_by_id() (serverapi method)": [[0, "ayon_api.ServerAPI.get_workfile_info_by_id"], [8, "ayon_api.server_api.ServerAPI.get_workfile_info_by_id"]], "get_workfile_info_by_id() (in module ayon_api)": [[0, "ayon_api.get_workfile_info_by_id"]], "get_workfile_thumbnail() (serverapi method)": [[0, "ayon_api.ServerAPI.get_workfile_thumbnail"], [8, "ayon_api.server_api.ServerAPI.get_workfile_thumbnail"]], "get_workfile_thumbnail() (in module ayon_api)": [[0, "ayon_api.get_workfile_thumbnail"]], "get_workfiles_info() (serverapi method)": [[0, "ayon_api.ServerAPI.get_workfiles_info"], [8, "ayon_api.server_api.ServerAPI.get_workfiles_info"]], "get_workfiles_info() (in module ayon_api)": [[0, "ayon_api.get_workfiles_info"]], "graphql_allows_data_in_query (serverapi property)": [[0, "ayon_api.ServerAPI.graphql_allows_data_in_query"], [8, "ayon_api.server_api.ServerAPI.graphql_allows_data_in_query"]], "has_valid_token (serverapi property)": [[0, "ayon_api.ServerAPI.has_valid_token"], [8, "ayon_api.server_api.ServerAPI.has_valid_token"]], "init_service() (servicecontext class method)": [[0, "ayon_api.ServiceContext.init_service"]], "init_service() (in module ayon_api)": [[0, "ayon_api.init_service"]], "is_connection_created() (in module ayon_api)": [[0, "ayon_api.is_connection_created"]], "is_running (transferprogress property)": [[0, "ayon_api.TransferProgress.is_running"], [9, "ayon_api.utils.TransferProgress.is_running"]], "is_server_available (serverapi property)": [[0, "ayon_api.ServerAPI.is_server_available"], [8, "ayon_api.server_api.ServerAPI.is_server_available"]], "is_service_user() (serverapi method)": [[0, "ayon_api.ServerAPI.is_service_user"], [8, "ayon_api.server_api.ServerAPI.is_service_user"]], "is_service_user() (in module ayon_api)": [[0, "ayon_api.is_service_user"]], "is_token_valid() (in module ayon_api)": [[0, "ayon_api.is_token_valid"]], "log (serverapi property)": [[0, "ayon_api.ServerAPI.log"], [8, "ayon_api.server_api.ServerAPI.log"]], "login() (globalserverapi method)": [[0, "ayon_api.GlobalServerAPI.login"]], "login() (serverapi method)": [[0, "ayon_api.ServerAPI.login"], [8, "ayon_api.server_api.ServerAPI.login"]], "login_to_server() (in module ayon_api)": [[0, "ayon_api.login_to_server"]], "logout() (serverapi method)": [[0, "ayon_api.ServerAPI.logout"], [8, "ayon_api.server_api.ServerAPI.logout"]], "make_sure_link_type_exists() (serverapi method)": [[0, "ayon_api.ServerAPI.make_sure_link_type_exists"], [8, "ayon_api.server_api.ServerAPI.make_sure_link_type_exists"]], "make_sure_link_type_exists() (in module ayon_api)": [[0, "ayon_api.make_sure_link_type_exists"]], "max_retries (serverapi property)": [[0, "ayon_api.ServerAPI.max_retries"], [8, "ayon_api.server_api.ServerAPI.max_retries"]], "module": [[0, "module-ayon_api"], [1, "module-ayon_api.constants"], [2, "module-ayon_api.entity_hub"], [3, "module-ayon_api.events"], [4, "module-ayon_api.exceptions"], [5, "module-ayon_api.graphql"], [6, "module-ayon_api.graphql_queries"], [7, "module-ayon_api.operations"], [8, "module-ayon_api.server_api"], [9, "module-ayon_api.utils"], [10, "module-ayon_api.version"]], "parse_value() (sortorder class method)": [[0, "ayon_api.SortOrder.parse_value"], [9, "ayon_api.utils.SortOrder.parse_value"]], "patch (requesttypes attribute)": [[0, "ayon_api.RequestTypes.patch"], [8, "ayon_api.server_api.RequestTypes.patch"]], "patch() (serverapi method)": [[0, "ayon_api.ServerAPI.patch"], [8, "ayon_api.server_api.ServerAPI.patch"]], "patch() (in module ayon_api)": [[0, "ayon_api.patch"]], "post (requesttypes attribute)": [[0, "ayon_api.RequestTypes.post"], [8, "ayon_api.server_api.RequestTypes.post"]], "post() (serverapi method)": [[0, "ayon_api.ServerAPI.post"], [8, "ayon_api.server_api.ServerAPI.post"]], "post() (in module ayon_api)": [[0, "ayon_api.post"]], "put (requesttypes attribute)": [[0, "ayon_api.RequestTypes.put"], [8, "ayon_api.server_api.RequestTypes.put"]], "put() (serverapi method)": [[0, "ayon_api.ServerAPI.put"], [8, "ayon_api.server_api.ServerAPI.put"]], "put() (in module ayon_api)": [[0, "ayon_api.put"]], "query_graphql() (serverapi method)": [[0, "ayon_api.ServerAPI.query_graphql"], [8, "ayon_api.server_api.ServerAPI.query_graphql"]], "query_graphql() (in module ayon_api)": [[0, "ayon_api.query_graphql"]], "raw_delete() (serverapi method)": [[0, "ayon_api.ServerAPI.raw_delete"], [8, "ayon_api.server_api.ServerAPI.raw_delete"]], "raw_delete() (in module ayon_api)": [[0, "ayon_api.raw_delete"]], "raw_get() (serverapi method)": [[0, "ayon_api.ServerAPI.raw_get"], [8, "ayon_api.server_api.ServerAPI.raw_get"]], "raw_get() (in module ayon_api)": [[0, "ayon_api.raw_get"]], "raw_patch() (serverapi method)": [[0, "ayon_api.ServerAPI.raw_patch"], [8, "ayon_api.server_api.ServerAPI.raw_patch"]], "raw_patch() (in module ayon_api)": [[0, "ayon_api.raw_patch"]], "raw_post() (serverapi method)": [[0, "ayon_api.ServerAPI.raw_post"], [8, "ayon_api.server_api.ServerAPI.raw_post"]], "raw_post() (in module ayon_api)": [[0, "ayon_api.raw_post"]], "raw_put() (serverapi method)": [[0, "ayon_api.ServerAPI.raw_put"], [8, "ayon_api.server_api.ServerAPI.raw_put"]], "raw_put() (in module ayon_api)": [[0, "ayon_api.raw_put"]], "remove_attribute_config() (serverapi method)": [[0, "ayon_api.ServerAPI.remove_attribute_config"], [8, "ayon_api.server_api.ServerAPI.remove_attribute_config"]], "remove_attribute_config() (in module ayon_api)": [[0, "ayon_api.remove_attribute_config"]], "reset_attributes_schema() (serverapi method)": [[0, "ayon_api.ServerAPI.reset_attributes_schema"], [8, "ayon_api.server_api.ServerAPI.reset_attributes_schema"]], "reset_attributes_schema() (in module ayon_api)": [[0, "ayon_api.reset_attributes_schema"]], "reset_token() (serverapi method)": [[0, "ayon_api.ServerAPI.reset_token"], [8, "ayon_api.server_api.ServerAPI.reset_token"]], "rest_url (serverapi property)": [[0, "ayon_api.ServerAPI.rest_url"], [8, "ayon_api.server_api.ServerAPI.rest_url"]], "save_secret() (serverapi method)": [[0, "ayon_api.ServerAPI.save_secret"], [8, "ayon_api.server_api.ServerAPI.save_secret"]], "save_secret() (in module ayon_api)": [[0, "ayon_api.save_secret"]], "send_activities_batch_operations() (serverapi method)": [[0, "ayon_api.ServerAPI.send_activities_batch_operations"], [8, "ayon_api.server_api.ServerAPI.send_activities_batch_operations"]], "send_activities_batch_operations() (in module ayon_api)": [[0, "ayon_api.send_activities_batch_operations"]], "send_batch_operations() (serverapi method)": [[0, "ayon_api.ServerAPI.send_batch_operations"], [8, "ayon_api.server_api.ServerAPI.send_batch_operations"]], "send_batch_operations() (in module ayon_api)": [[0, "ayon_api.send_batch_operations"]], "sender (serverapi property)": [[0, "ayon_api.ServerAPI.sender"], [8, "ayon_api.server_api.ServerAPI.sender"]], "sender_type (serverapi property)": [[0, "ayon_api.ServerAPI.sender_type"], [8, "ayon_api.server_api.ServerAPI.sender_type"]], "server_url (servicecontext attribute)": [[0, "ayon_api.ServiceContext.server_url"]], "server_version (serverapi property)": [[0, "ayon_api.ServerAPI.server_version"], [8, "ayon_api.server_api.ServerAPI.server_version"]], "server_version_tuple (serverapi property)": [[0, "ayon_api.ServerAPI.server_version_tuple"], [8, "ayon_api.server_api.ServerAPI.server_version_tuple"]], "service_name (servicecontext attribute)": [[0, "ayon_api.ServiceContext.service_name"]], "set_attribute_config() (serverapi method)": [[0, "ayon_api.ServerAPI.set_attribute_config"], [8, "ayon_api.server_api.ServerAPI.set_attribute_config"]], "set_attribute_config() (in module ayon_api)": [[0, "ayon_api.set_attribute_config"]], "set_cert() (serverapi method)": [[0, "ayon_api.ServerAPI.set_cert"], [8, "ayon_api.server_api.ServerAPI.set_cert"]], "set_cert() (in module ayon_api)": [[0, "ayon_api.set_cert"]], "set_client_version() (serverapi method)": [[0, "ayon_api.ServerAPI.set_client_version"], [8, "ayon_api.server_api.ServerAPI.set_client_version"]], "set_client_version() (in module ayon_api)": [[0, "ayon_api.set_client_version"]], "set_content_size() (transferprogress method)": [[0, "ayon_api.TransferProgress.set_content_size"], [9, "ayon_api.utils.TransferProgress.set_content_size"]], "set_default_service_username() (serverapi method)": [[0, "ayon_api.ServerAPI.set_default_service_username"], [8, "ayon_api.server_api.ServerAPI.set_default_service_username"]], "set_default_settings_variant() (serverapi method)": [[0, "ayon_api.ServerAPI.set_default_settings_variant"], [8, "ayon_api.server_api.ServerAPI.set_default_settings_variant"]], "set_default_settings_variant() (in module ayon_api)": [[0, "ayon_api.set_default_settings_variant"]], "set_destination_url() (transferprogress method)": [[0, "ayon_api.TransferProgress.set_destination_url"], [9, "ayon_api.utils.TransferProgress.set_destination_url"]], "set_environments() (globalserverapi static method)": [[0, "ayon_api.GlobalServerAPI.set_environments"]], "set_environments() (in module ayon_api)": [[0, "ayon_api.set_environments"]], "set_failed() (transferprogress method)": [[0, "ayon_api.TransferProgress.set_failed"], [9, "ayon_api.utils.TransferProgress.set_failed"]], "set_max_retries() (serverapi method)": [[0, "ayon_api.ServerAPI.set_max_retries"], [8, "ayon_api.server_api.ServerAPI.set_max_retries"]], "set_max_retries() (in module ayon_api)": [[0, "ayon_api.set_max_retries"]], "set_sender() (serverapi method)": [[0, "ayon_api.ServerAPI.set_sender"], [8, "ayon_api.server_api.ServerAPI.set_sender"]], "set_sender() (in module ayon_api)": [[0, "ayon_api.set_sender"]], "set_sender_type() (serverapi method)": [[0, "ayon_api.ServerAPI.set_sender_type"], [8, "ayon_api.server_api.ServerAPI.set_sender_type"]], "set_sender_type() (in module ayon_api)": [[0, "ayon_api.set_sender_type"]], "set_site_id() (serverapi method)": [[0, "ayon_api.ServerAPI.set_site_id"], [8, "ayon_api.server_api.ServerAPI.set_site_id"]], "set_site_id() (in module ayon_api)": [[0, "ayon_api.set_site_id"]], "set_source_url() (transferprogress method)": [[0, "ayon_api.TransferProgress.set_source_url"], [9, "ayon_api.utils.TransferProgress.set_source_url"]], "set_ssl_verify() (serverapi method)": [[0, "ayon_api.ServerAPI.set_ssl_verify"], [8, "ayon_api.server_api.ServerAPI.set_ssl_verify"]], "set_ssl_verify() (in module ayon_api)": [[0, "ayon_api.set_ssl_verify"]], "set_started() (transferprogress method)": [[0, "ayon_api.TransferProgress.set_started"], [9, "ayon_api.utils.TransferProgress.set_started"]], "set_timeout() (serverapi method)": [[0, "ayon_api.ServerAPI.set_timeout"], [8, "ayon_api.server_api.ServerAPI.set_timeout"]], "set_timeout() (in module ayon_api)": [[0, "ayon_api.set_timeout"]], "set_token() (serverapi method)": [[0, "ayon_api.ServerAPI.set_token"], [8, "ayon_api.server_api.ServerAPI.set_token"]], "set_transfer_done() (transferprogress method)": [[0, "ayon_api.TransferProgress.set_transfer_done"], [9, "ayon_api.utils.TransferProgress.set_transfer_done"]], "set_transferred_size() (transferprogress method)": [[0, "ayon_api.TransferProgress.set_transferred_size"], [9, "ayon_api.utils.TransferProgress.set_transferred_size"]], "site_id (serverapi property)": [[0, "ayon_api.ServerAPI.site_id"], [8, "ayon_api.server_api.ServerAPI.site_id"]], "slugify_string() (in module ayon_api)": [[0, "ayon_api.slugify_string"]], "source_url (transferprogress property)": [[0, "ayon_api.TransferProgress.source_url"], [9, "ayon_api.utils.TransferProgress.source_url"]], "ssl_verify (serverapi property)": [[0, "ayon_api.ServerAPI.ssl_verify"], [8, "ayon_api.server_api.ServerAPI.ssl_verify"]], "started (transferprogress property)": [[0, "ayon_api.TransferProgress.started"], [9, "ayon_api.utils.TransferProgress.started"]], "take_web_action_event() (in module ayon_api)": [[0, "ayon_api.take_web_action_event"]], "timeout (serverapi property)": [[0, "ayon_api.ServerAPI.timeout"], [8, "ayon_api.server_api.ServerAPI.timeout"]], "token (servicecontext attribute)": [[0, "ayon_api.ServiceContext.token"]], "transfer_done (transferprogress property)": [[0, "ayon_api.TransferProgress.transfer_done"], [9, "ayon_api.utils.TransferProgress.transfer_done"]], "transfer_progress (transferprogress property)": [[0, "ayon_api.TransferProgress.transfer_progress"], [9, "ayon_api.utils.TransferProgress.transfer_progress"]], "transferred_size (transferprogress property)": [[0, "ayon_api.TransferProgress.transferred_size"], [9, "ayon_api.utils.TransferProgress.transferred_size"]], "trigger_server_restart() (serverapi method)": [[0, "ayon_api.ServerAPI.trigger_server_restart"], [8, "ayon_api.server_api.ServerAPI.trigger_server_restart"]], "trigger_server_restart() (in module ayon_api)": [[0, "ayon_api.trigger_server_restart"]], "update_activity() (serverapi method)": [[0, "ayon_api.ServerAPI.update_activity"], [8, "ayon_api.server_api.ServerAPI.update_activity"]], "update_activity() (in module ayon_api)": [[0, "ayon_api.update_activity"]], "update_bundle() (serverapi method)": [[0, "ayon_api.ServerAPI.update_bundle"], [8, "ayon_api.server_api.ServerAPI.update_bundle"]], "update_bundle() (in module ayon_api)": [[0, "ayon_api.update_bundle"]], "update_dependency_package() (serverapi method)": [[0, "ayon_api.ServerAPI.update_dependency_package"], [8, "ayon_api.server_api.ServerAPI.update_dependency_package"]], "update_dependency_package() (in module ayon_api)": [[0, "ayon_api.update_dependency_package"]], "update_event() (serverapi method)": [[0, "ayon_api.ServerAPI.update_event"], [8, "ayon_api.server_api.ServerAPI.update_event"]], "update_event() (in module ayon_api)": [[0, "ayon_api.update_event"]], "update_folder() (serverapi method)": [[0, "ayon_api.ServerAPI.update_folder"], [8, "ayon_api.server_api.ServerAPI.update_folder"]], "update_folder() (in module ayon_api)": [[0, "ayon_api.update_folder"]], "update_installer() (serverapi method)": [[0, "ayon_api.ServerAPI.update_installer"], [8, "ayon_api.server_api.ServerAPI.update_installer"]], "update_installer() (in module ayon_api)": [[0, "ayon_api.update_installer"]], "update_product() (serverapi method)": [[0, "ayon_api.ServerAPI.update_product"], [8, "ayon_api.server_api.ServerAPI.update_product"]], "update_product() (in module ayon_api)": [[0, "ayon_api.update_product"]], "update_project() (serverapi method)": [[0, "ayon_api.ServerAPI.update_project"], [8, "ayon_api.server_api.ServerAPI.update_project"]], "update_project() (in module ayon_api)": [[0, "ayon_api.update_project"]], "update_representation() (serverapi method)": [[0, "ayon_api.ServerAPI.update_representation"], [8, "ayon_api.server_api.ServerAPI.update_representation"]], "update_representation() (in module ayon_api)": [[0, "ayon_api.update_representation"]], "update_task() (serverapi method)": [[0, "ayon_api.ServerAPI.update_task"], [8, "ayon_api.server_api.ServerAPI.update_task"]], "update_task() (in module ayon_api)": [[0, "ayon_api.update_task"]], "update_thumbnail() (serverapi method)": [[0, "ayon_api.ServerAPI.update_thumbnail"], [8, "ayon_api.server_api.ServerAPI.update_thumbnail"]], "update_thumbnail() (in module ayon_api)": [[0, "ayon_api.update_thumbnail"]], "update_version() (serverapi method)": [[0, "ayon_api.ServerAPI.update_version"], [8, "ayon_api.server_api.ServerAPI.update_version"]], "update_version() (in module ayon_api)": [[0, "ayon_api.update_version"]], "upload_addon_zip() (serverapi method)": [[0, "ayon_api.ServerAPI.upload_addon_zip"], [8, "ayon_api.server_api.ServerAPI.upload_addon_zip"]], "upload_addon_zip() (in module ayon_api)": [[0, "ayon_api.upload_addon_zip"]], "upload_dependency_package() (serverapi method)": [[0, "ayon_api.ServerAPI.upload_dependency_package"], [8, "ayon_api.server_api.ServerAPI.upload_dependency_package"]], "upload_dependency_package() (in module ayon_api)": [[0, "ayon_api.upload_dependency_package"]], "upload_file() (serverapi method)": [[0, "ayon_api.ServerAPI.upload_file"], [8, "ayon_api.server_api.ServerAPI.upload_file"]], "upload_file() (in module ayon_api)": [[0, "ayon_api.upload_file"]], "upload_file_from_stream() (serverapi method)": [[0, "ayon_api.ServerAPI.upload_file_from_stream"], [8, "ayon_api.server_api.ServerAPI.upload_file_from_stream"]], "upload_file_from_stream() (in module ayon_api)": [[0, "ayon_api.upload_file_from_stream"]], "upload_installer() (serverapi method)": [[0, "ayon_api.ServerAPI.upload_installer"], [8, "ayon_api.server_api.ServerAPI.upload_installer"]], "upload_installer() (in module ayon_api)": [[0, "ayon_api.upload_installer"]], "upload_reviewable() (serverapi method)": [[0, "ayon_api.ServerAPI.upload_reviewable"], [8, "ayon_api.server_api.ServerAPI.upload_reviewable"]], "upload_reviewable() (in module ayon_api)": [[0, "ayon_api.upload_reviewable"]], "validate_server_availability() (serverapi method)": [[0, "ayon_api.ServerAPI.validate_server_availability"], [8, "ayon_api.server_api.ServerAPI.validate_server_availability"]], "validate_token() (serverapi method)": [[0, "ayon_api.ServerAPI.validate_token"], [8, "ayon_api.server_api.ServerAPI.validate_token"]], "validate_url() (in module ayon_api)": [[0, "ayon_api.validate_url"]], "version_is_latest() (serverapi method)": [[0, "ayon_api.ServerAPI.version_is_latest"], [8, "ayon_api.server_api.ServerAPI.version_is_latest"]], "version_is_latest() (in module ayon_api)": [[0, "ayon_api.version_is_latest"]], "ayon_api.constants": [[1, "module-ayon_api.constants"]], "attributevalue (class in ayon_api.entity_hub)": [[2, "ayon_api.entity_hub.AttributeValue"]], "attributes (class in ayon_api.entity_hub)": [[2, "ayon_api.entity_hub.Attributes"]], "baseentity (class in ayon_api.entity_hub)": [[2, "ayon_api.entity_hub.BaseEntity"]], "entitydata (class in ayon_api.entity_hub)": [[2, "ayon_api.entity_hub.EntityData"]], "entityhub (class in ayon_api.entity_hub)": [[2, "ayon_api.entity_hub.EntityHub"]], "folderentity (class in ayon_api.entity_hub)": [[2, "ayon_api.entity_hub.FolderEntity"]], "productentity (class in ayon_api.entity_hub)": [[2, "ayon_api.entity_hub.ProductEntity"]], "projectentity (class in ayon_api.entity_hub)": [[2, "ayon_api.entity_hub.ProjectEntity"]], "projectstatus (class in ayon_api.entity_hub)": [[2, "ayon_api.entity_hub.ProjectStatus"]], "taskentity (class in ayon_api.entity_hub)": [[2, "ayon_api.entity_hub.TaskEntity"]], "versionentity (class in ayon_api.entity_hub)": [[2, "ayon_api.entity_hub.VersionEntity"]], "add_child() (baseentity method)": [[2, "ayon_api.entity_hub.BaseEntity.add_child"]], "add_child() (taskentity method)": [[2, "ayon_api.entity_hub.TaskEntity.add_child"]], "add_entity() (entityhub method)": [[2, "ayon_api.entity_hub.EntityHub.add_entity"]], "add_folder() (entityhub method)": [[2, "ayon_api.entity_hub.EntityHub.add_folder"]], "add_new_folder() (entityhub method)": [[2, "ayon_api.entity_hub.EntityHub.add_new_folder"]], "add_new_product() (entityhub method)": [[2, "ayon_api.entity_hub.EntityHub.add_new_product"]], "add_new_task() (entityhub method)": [[2, "ayon_api.entity_hub.EntityHub.add_new_task"]], "add_new_version() (entityhub method)": [[2, "ayon_api.entity_hub.EntityHub.add_new_version"]], "add_product() (entityhub method)": [[2, "ayon_api.entity_hub.EntityHub.add_product"]], "add_task() (entityhub method)": [[2, "ayon_api.entity_hub.EntityHub.add_task"]], "add_version() (entityhub method)": [[2, "ayon_api.entity_hub.EntityHub.add_version"]], "allow_data_changes (entityhub property)": [[2, "ayon_api.entity_hub.EntityHub.allow_data_changes"]], "assignees (taskentity property)": [[2, "ayon_api.entity_hub.TaskEntity.assignees"]], "attribs (baseentity property)": [[2, "ayon_api.entity_hub.BaseEntity.attribs"]], "ayon_api.entity_hub": [[2, "module-ayon_api.entity_hub"]], "changed (attributevalue property)": [[2, "ayon_api.entity_hub.AttributeValue.changed"]], "changed (projectstatus property)": [[2, "ayon_api.entity_hub.ProjectStatus.changed"]], "changes (attributes property)": [[2, "ayon_api.entity_hub.Attributes.changes"]], "changes (baseentity property)": [[2, "ayon_api.entity_hub.BaseEntity.changes"]], "changes (folderentity property)": [[2, "ayon_api.entity_hub.FolderEntity.changes"]], "changes (productentity property)": [[2, "ayon_api.entity_hub.ProductEntity.changes"]], "changes (projectentity property)": [[2, "ayon_api.entity_hub.ProjectEntity.changes"]], "changes (taskentity property)": [[2, "ayon_api.entity_hub.TaskEntity.changes"]], "changes (versionentity property)": [[2, "ayon_api.entity_hub.VersionEntity.changes"]], "children (baseentity property)": [[2, "ayon_api.entity_hub.BaseEntity.children"]], "children_ids (baseentity property)": [[2, "ayon_api.entity_hub.BaseEntity.children_ids"]], "color (projectstatus property)": [[2, "ayon_api.entity_hub.ProjectStatus.color"]], "color_regex (projectstatus attribute)": [[2, "ayon_api.entity_hub.ProjectStatus.color_regex"]], "commit_changes() (entityhub method)": [[2, "ayon_api.entity_hub.EntityHub.commit_changes"]], "created (baseentity property)": [[2, "ayon_api.entity_hub.BaseEntity.created"]], "data (baseentity property)": [[2, "ayon_api.entity_hub.BaseEntity.data"]], "default_color (projectstatus attribute)": [[2, "ayon_api.entity_hub.ProjectStatus.default_color"]], "default_folder_type_icon (projectentity attribute)": [[2, "ayon_api.entity_hub.ProjectEntity.default_folder_type_icon"]], "default_state (projectstatus attribute)": [[2, "ayon_api.entity_hub.ProjectStatus.default_state"]], "default_task_type_icon (projectentity attribute)": [[2, "ayon_api.entity_hub.ProjectEntity.default_task_type_icon"]], "delete() (projectstatus method)": [[2, "ayon_api.entity_hub.ProjectStatus.delete"]], "delete_entity() (entityhub method)": [[2, "ayon_api.entity_hub.EntityHub.delete_entity"]], "entities (entityhub property)": [[2, "ayon_api.entity_hub.EntityHub.entities"]], "entity_type (baseentity property)": [[2, "ayon_api.entity_hub.BaseEntity.entity_type"]], "entity_type (folderentity attribute)": [[2, "ayon_api.entity_hub.FolderEntity.entity_type"]], "entity_type (productentity attribute)": [[2, "ayon_api.entity_hub.ProductEntity.entity_type"]], "entity_type (projectentity attribute)": [[2, "ayon_api.entity_hub.ProjectEntity.entity_type"]], "entity_type (taskentity attribute)": [[2, "ayon_api.entity_hub.TaskEntity.entity_type"]], "entity_type (versionentity attribute)": [[2, "ayon_api.entity_hub.VersionEntity.entity_type"]], "fetch_hierarchy_entities() (entityhub method)": [[2, "ayon_api.entity_hub.EntityHub.fetch_hierarchy_entities"]], "fill_children_ids() (baseentity method)": [[2, "ayon_api.entity_hub.BaseEntity.fill_children_ids"]], "fill_project_from_server() (entityhub method)": [[2, "ayon_api.entity_hub.EntityHub.fill_project_from_server"]], "folder_id (productentity property)": [[2, "ayon_api.entity_hub.ProductEntity.folder_id"]], "folder_id (taskentity property)": [[2, "ayon_api.entity_hub.TaskEntity.folder_id"]], "folder_path_reseted() (entityhub method)": [[2, "ayon_api.entity_hub.EntityHub.folder_path_reseted"]], "folder_type (folderentity property)": [[2, "ayon_api.entity_hub.FolderEntity.folder_type"]], "folder_types (projectentity property)": [[2, "ayon_api.entity_hub.ProjectEntity.folder_types"]], "from_data() (projectstatus class method)": [[2, "ayon_api.entity_hub.ProjectStatus.from_data"]], "from_entity_data() (baseentity class method)": [[2, "ayon_api.entity_hub.BaseEntity.from_entity_data"]], "from_entity_data() (folderentity class method)": [[2, "ayon_api.entity_hub.FolderEntity.from_entity_data"]], "from_entity_data() (productentity class method)": [[2, "ayon_api.entity_hub.ProductEntity.from_entity_data"]], "from_entity_data() (projectentity class method)": [[2, "ayon_api.entity_hub.ProjectEntity.from_entity_data"]], "from_entity_data() (taskentity class method)": [[2, "ayon_api.entity_hub.TaskEntity.from_entity_data"]], "from_entity_data() (versionentity class method)": [[2, "ayon_api.entity_hub.VersionEntity.from_entity_data"]], "get() (attributes method)": [[2, "ayon_api.entity_hub.Attributes.get"]], "get_assignees() (taskentity method)": [[2, "ayon_api.entity_hub.TaskEntity.get_assignees"]], "get_attribute() (attributes method)": [[2, "ayon_api.entity_hub.Attributes.get_attribute"]], "get_attributes_for_type() (entityhub method)": [[2, "ayon_api.entity_hub.EntityHub.get_attributes_for_type"]], "get_changes() (entitydata method)": [[2, "ayon_api.entity_hub.EntityData.get_changes"]], "get_children() (baseentity method)": [[2, "ayon_api.entity_hub.BaseEntity.get_children"]], "get_children_ids() (baseentity method)": [[2, "ayon_api.entity_hub.BaseEntity.get_children_ids"]], "get_color() (projectstatus method)": [[2, "ayon_api.entity_hub.ProjectStatus.get_color"]], "get_entity_by_id() (entityhub method)": [[2, "ayon_api.entity_hub.EntityHub.get_entity_by_id"]], "get_entity_children() (entityhub method)": [[2, "ayon_api.entity_hub.EntityHub.get_entity_children"]], "get_folder_by_id() (entityhub method)": [[2, "ayon_api.entity_hub.EntityHub.get_folder_by_id"]], "get_folder_id() (productentity method)": [[2, "ayon_api.entity_hub.ProductEntity.get_folder_id"]], "get_folder_id() (taskentity method)": [[2, "ayon_api.entity_hub.TaskEntity.get_folder_id"]], "get_folder_type() (folderentity method)": [[2, "ayon_api.entity_hub.FolderEntity.get_folder_type"]], "get_folder_types() (projectentity method)": [[2, "ayon_api.entity_hub.ProjectEntity.get_folder_types"]], "get_has_published_content() (folderentity method)": [[2, "ayon_api.entity_hub.FolderEntity.get_has_published_content"]], "get_icon() (projectstatus method)": [[2, "ayon_api.entity_hub.ProjectStatus.get_icon"]], "get_index() (projectstatus method)": [[2, "ayon_api.entity_hub.ProjectStatus.get_index"]], "get_label() (baseentity method)": [[2, "ayon_api.entity_hub.BaseEntity.get_label"]], "get_name() (baseentity method)": [[2, "ayon_api.entity_hub.BaseEntity.get_name"]], "get_name() (projectstatus method)": [[2, "ayon_api.entity_hub.ProjectStatus.get_name"]], "get_new_entity_value() (entitydata method)": [[2, "ayon_api.entity_hub.EntityData.get_new_entity_value"]], "get_or_fetch_entity_by_id() (entityhub method)": [[2, "ayon_api.entity_hub.EntityHub.get_or_fetch_entity_by_id"]], "get_or_query_entity_by_id() (entityhub method)": [[2, "ayon_api.entity_hub.EntityHub.get_or_query_entity_by_id"]], "get_orig_folder_types() (projectentity method)": [[2, "ayon_api.entity_hub.ProjectEntity.get_orig_folder_types"]], "get_orig_statuses() (projectentity method)": [[2, "ayon_api.entity_hub.ProjectEntity.get_orig_statuses"]], "get_orig_task_types() (projectentity method)": [[2, "ayon_api.entity_hub.ProjectEntity.get_orig_task_types"]], "get_parent() (baseentity method)": [[2, "ayon_api.entity_hub.BaseEntity.get_parent"]], "get_parent() (projectentity method)": [[2, "ayon_api.entity_hub.ProjectEntity.get_parent"]], "get_parent_id() (baseentity method)": [[2, "ayon_api.entity_hub.BaseEntity.get_parent_id"]], "get_path() (folderentity method)": [[2, "ayon_api.entity_hub.FolderEntity.get_path"]], "get_product_by_id() (entityhub method)": [[2, "ayon_api.entity_hub.EntityHub.get_product_by_id"]], "get_product_id() (versionentity method)": [[2, "ayon_api.entity_hub.VersionEntity.get_product_id"]], "get_product_type() (productentity method)": [[2, "ayon_api.entity_hub.ProductEntity.get_product_type"]], "get_project_statuses() (projectstatus method)": [[2, "ayon_api.entity_hub.ProjectStatus.get_project_statuses"]], "get_scope() (projectstatus method)": [[2, "ayon_api.entity_hub.ProjectStatus.get_scope"]], "get_short_name() (projectstatus method)": [[2, "ayon_api.entity_hub.ProjectStatus.get_short_name"]], "get_state() (projectstatus method)": [[2, "ayon_api.entity_hub.ProjectStatus.get_state"]], "get_status() (baseentity method)": [[2, "ayon_api.entity_hub.BaseEntity.get_status"]], "get_status_by_slugified_name() (projectentity method)": [[2, "ayon_api.entity_hub.ProjectEntity.get_status_by_slugified_name"]], "get_statuses() (projectentity method)": [[2, "ayon_api.entity_hub.ProjectEntity.get_statuses"]], "get_tags() (baseentity method)": [[2, "ayon_api.entity_hub.BaseEntity.get_tags"]], "get_task_by_id() (entityhub method)": [[2, "ayon_api.entity_hub.EntityHub.get_task_by_id"]], "get_task_id() (versionentity method)": [[2, "ayon_api.entity_hub.VersionEntity.get_task_id"]], "get_task_type() (taskentity method)": [[2, "ayon_api.entity_hub.TaskEntity.get_task_type"]], "get_task_types() (projectentity method)": [[2, "ayon_api.entity_hub.ProjectEntity.get_task_types"]], "get_thumbnail_id() (baseentity method)": [[2, "ayon_api.entity_hub.BaseEntity.get_thumbnail_id"]], "get_value() (attributevalue method)": [[2, "ayon_api.entity_hub.AttributeValue.get_value"]], "get_version() (versionentity method)": [[2, "ayon_api.entity_hub.VersionEntity.get_version"]], "get_version_by_id() (entityhub method)": [[2, "ayon_api.entity_hub.EntityHub.get_version_by_id"]], "has_cached_immutable_hierarchy (baseentity property)": [[2, "ayon_api.entity_hub.BaseEntity.has_cached_immutable_hierarchy"]], "has_published_content (folderentity property)": [[2, "ayon_api.entity_hub.FolderEntity.has_published_content"]], "icon (projectstatus property)": [[2, "ayon_api.entity_hub.ProjectStatus.icon"]], "id (baseentity property)": [[2, "ayon_api.entity_hub.BaseEntity.id"]], "immutable_for_hierarchy (baseentity property)": [[2, "ayon_api.entity_hub.BaseEntity.immutable_for_hierarchy"]], "index (projectstatus property)": [[2, "ayon_api.entity_hub.ProjectStatus.index"]], "is_available_for_entity_type() (projectstatus method)": [[2, "ayon_api.entity_hub.ProjectStatus.is_available_for_entity_type"]], "items() (attributes method)": [[2, "ayon_api.entity_hub.Attributes.items"]], "keys() (attributes method)": [[2, "ayon_api.entity_hub.Attributes.keys"]], "label (baseentity property)": [[2, "ayon_api.entity_hub.BaseEntity.label"]], "lock() (attributevalue method)": [[2, "ayon_api.entity_hub.AttributeValue.lock"]], "lock() (attributes method)": [[2, "ayon_api.entity_hub.Attributes.lock"]], "lock() (baseentity method)": [[2, "ayon_api.entity_hub.BaseEntity.lock"]], "lock() (entitydata method)": [[2, "ayon_api.entity_hub.EntityData.lock"]], "lock() (entityhub method)": [[2, "ayon_api.entity_hub.EntityHub.lock"]], "lock() (folderentity method)": [[2, "ayon_api.entity_hub.FolderEntity.lock"]], "lock() (productentity method)": [[2, "ayon_api.entity_hub.ProductEntity.lock"]], "lock() (projectentity method)": [[2, "ayon_api.entity_hub.ProjectEntity.lock"]], "lock() (projectstatus method)": [[2, "ayon_api.entity_hub.ProjectStatus.lock"]], "lock() (taskentity method)": [[2, "ayon_api.entity_hub.TaskEntity.lock"]], "lock() (versionentity method)": [[2, "ayon_api.entity_hub.VersionEntity.lock"]], "move_after() (projectstatus method)": [[2, "ayon_api.entity_hub.ProjectStatus.move_after"]], "move_before() (projectstatus method)": [[2, "ayon_api.entity_hub.ProjectStatus.move_before"]], "name (baseentity property)": [[2, "ayon_api.entity_hub.BaseEntity.name"]], "name (projectstatus property)": [[2, "ayon_api.entity_hub.ProjectStatus.name"]], "orig_parent_id (baseentity property)": [[2, "ayon_api.entity_hub.BaseEntity.orig_parent_id"]], "parent (baseentity property)": [[2, "ayon_api.entity_hub.BaseEntity.parent"]], "parent (projectentity property)": [[2, "ayon_api.entity_hub.ProjectEntity.parent"]], "parent_entity_types (baseentity property)": [[2, "ayon_api.entity_hub.BaseEntity.parent_entity_types"]], "parent_entity_types (folderentity attribute)": [[2, "ayon_api.entity_hub.FolderEntity.parent_entity_types"]], "parent_entity_types (productentity attribute)": [[2, "ayon_api.entity_hub.ProductEntity.parent_entity_types"]], "parent_entity_types (projectentity attribute)": [[2, "ayon_api.entity_hub.ProjectEntity.parent_entity_types"]], "parent_entity_types (taskentity attribute)": [[2, "ayon_api.entity_hub.TaskEntity.parent_entity_types"]], "parent_entity_types (versionentity attribute)": [[2, "ayon_api.entity_hub.VersionEntity.parent_entity_types"]], "parent_id (baseentity property)": [[2, "ayon_api.entity_hub.BaseEntity.parent_id"]], "path (folderentity property)": [[2, "ayon_api.entity_hub.FolderEntity.path"]], "path_start_with_slash (entityhub property)": [[2, "ayon_api.entity_hub.EntityHub.path_start_with_slash"]], "product_id (versionentity property)": [[2, "ayon_api.entity_hub.VersionEntity.product_id"]], "product_type (productentity property)": [[2, "ayon_api.entity_hub.ProductEntity.product_type"]], "project_entity (entityhub property)": [[2, "ayon_api.entity_hub.EntityHub.project_entity"]], "project_name (baseentity property)": [[2, "ayon_api.entity_hub.BaseEntity.project_name"]], "project_name (entityhub property)": [[2, "ayon_api.entity_hub.EntityHub.project_name"]], "project_statuses (projectstatus property)": [[2, "ayon_api.entity_hub.ProjectStatus.project_statuses"]], "query_entities_from_server() (entityhub method)": [[2, "ayon_api.entity_hub.EntityHub.query_entities_from_server"]], "remove_child() (baseentity method)": [[2, "ayon_api.entity_hub.BaseEntity.remove_child"]], "removed (baseentity property)": [[2, "ayon_api.entity_hub.BaseEntity.removed"]], "reset_immutable_for_hierarchy_cache() (baseentity method)": [[2, "ayon_api.entity_hub.BaseEntity.reset_immutable_for_hierarchy_cache"]], "reset_immutable_for_hierarchy_cache() (entityhub method)": [[2, "ayon_api.entity_hub.EntityHub.reset_immutable_for_hierarchy_cache"]], "reset_path() (folderentity method)": [[2, "ayon_api.entity_hub.FolderEntity.reset_path"]], "scope (projectstatus property)": [[2, "ayon_api.entity_hub.ProjectStatus.scope"]], "set() (attributes method)": [[2, "ayon_api.entity_hub.Attributes.set"]], "set_assignees() (taskentity method)": [[2, "ayon_api.entity_hub.TaskEntity.set_assignees"]], "set_color() (projectstatus method)": [[2, "ayon_api.entity_hub.ProjectStatus.set_color"]], "set_entity_parent() (entityhub method)": [[2, "ayon_api.entity_hub.EntityHub.set_entity_parent"]], "set_folder_id() (productentity method)": [[2, "ayon_api.entity_hub.ProductEntity.set_folder_id"]], "set_folder_id() (taskentity method)": [[2, "ayon_api.entity_hub.TaskEntity.set_folder_id"]], "set_folder_type() (folderentity method)": [[2, "ayon_api.entity_hub.FolderEntity.set_folder_type"]], "set_folder_types() (projectentity method)": [[2, "ayon_api.entity_hub.ProjectEntity.set_folder_types"]], "set_has_published_content() (folderentity method)": [[2, "ayon_api.entity_hub.FolderEntity.set_has_published_content"]], "set_icon() (projectstatus method)": [[2, "ayon_api.entity_hub.ProjectStatus.set_icon"]], "set_index() (projectstatus method)": [[2, "ayon_api.entity_hub.ProjectStatus.set_index"]], "set_label() (baseentity method)": [[2, "ayon_api.entity_hub.BaseEntity.set_label"]], "set_name() (baseentity method)": [[2, "ayon_api.entity_hub.BaseEntity.set_name"]], "set_name() (projectentity method)": [[2, "ayon_api.entity_hub.ProjectEntity.set_name"]], "set_name() (projectstatus method)": [[2, "ayon_api.entity_hub.ProjectStatus.set_name"]], "set_parent() (baseentity method)": [[2, "ayon_api.entity_hub.BaseEntity.set_parent"]], "set_parent() (projectentity method)": [[2, "ayon_api.entity_hub.ProjectEntity.set_parent"]], "set_parent_id() (baseentity method)": [[2, "ayon_api.entity_hub.BaseEntity.set_parent_id"]], "set_product_id() (versionentity method)": [[2, "ayon_api.entity_hub.VersionEntity.set_product_id"]], "set_product_type() (productentity method)": [[2, "ayon_api.entity_hub.ProductEntity.set_product_type"]], "set_project_statuses() (projectstatus method)": [[2, "ayon_api.entity_hub.ProjectStatus.set_project_statuses"]], "set_scope() (projectstatus method)": [[2, "ayon_api.entity_hub.ProjectStatus.set_scope"]], "set_short_name() (projectstatus method)": [[2, "ayon_api.entity_hub.ProjectStatus.set_short_name"]], "set_state() (projectstatus method)": [[2, "ayon_api.entity_hub.ProjectStatus.set_state"]], "set_status() (baseentity method)": [[2, "ayon_api.entity_hub.BaseEntity.set_status"]], "set_status_scope_supported() (projectentity method)": [[2, "ayon_api.entity_hub.ProjectEntity.set_status_scope_supported"]], "set_statuses() (projectentity method)": [[2, "ayon_api.entity_hub.ProjectEntity.set_statuses"]], "set_tags() (baseentity method)": [[2, "ayon_api.entity_hub.BaseEntity.set_tags"]], "set_task_id() (versionentity method)": [[2, "ayon_api.entity_hub.VersionEntity.set_task_id"]], "set_task_type() (taskentity method)": [[2, "ayon_api.entity_hub.TaskEntity.set_task_type"]], "set_task_types() (projectentity method)": [[2, "ayon_api.entity_hub.ProjectEntity.set_task_types"]], "set_thumbnail_id() (baseentity method)": [[2, "ayon_api.entity_hub.BaseEntity.set_thumbnail_id"]], "set_value() (attributevalue method)": [[2, "ayon_api.entity_hub.AttributeValue.set_value"]], "set_version() (versionentity method)": [[2, "ayon_api.entity_hub.VersionEntity.set_version"]], "short_name (projectstatus property)": [[2, "ayon_api.entity_hub.ProjectStatus.short_name"]], "slugified_name (projectstatus property)": [[2, "ayon_api.entity_hub.ProjectStatus.slugified_name"]], "slugify_name() (projectstatus static method)": [[2, "ayon_api.entity_hub.ProjectStatus.slugify_name"]], "state (projectstatus property)": [[2, "ayon_api.entity_hub.ProjectStatus.state"]], "status (baseentity property)": [[2, "ayon_api.entity_hub.BaseEntity.status"]], "statuses (projectentity property)": [[2, "ayon_api.entity_hub.ProjectEntity.statuses"]], "tags (baseentity property)": [[2, "ayon_api.entity_hub.BaseEntity.tags"]], "task_id (versionentity property)": [[2, "ayon_api.entity_hub.VersionEntity.task_id"]], "task_type (taskentity property)": [[2, "ayon_api.entity_hub.TaskEntity.task_type"]], "task_types (projectentity property)": [[2, "ayon_api.entity_hub.ProjectEntity.task_types"]], "thumbnail_id (baseentity property)": [[2, "ayon_api.entity_hub.BaseEntity.thumbnail_id"]], "to_create_body_data() (baseentity method)": [[2, "ayon_api.entity_hub.BaseEntity.to_create_body_data"]], "to_create_body_data() (folderentity method)": [[2, "ayon_api.entity_hub.FolderEntity.to_create_body_data"]], "to_create_body_data() (productentity method)": [[2, "ayon_api.entity_hub.ProductEntity.to_create_body_data"]], "to_create_body_data() (projectentity method)": [[2, "ayon_api.entity_hub.ProjectEntity.to_create_body_data"]], "to_create_body_data() (taskentity method)": [[2, "ayon_api.entity_hub.TaskEntity.to_create_body_data"]], "to_create_body_data() (versionentity method)": [[2, "ayon_api.entity_hub.VersionEntity.to_create_body_data"]], "to_data() (projectstatus method)": [[2, "ayon_api.entity_hub.ProjectStatus.to_data"]], "to_dict() (attributes method)": [[2, "ayon_api.entity_hub.Attributes.to_dict"]], "unset_entity_parent() (entityhub method)": [[2, "ayon_api.entity_hub.EntityHub.unset_entity_parent"]], "unset_project_statuses() (projectstatus method)": [[2, "ayon_api.entity_hub.ProjectStatus.unset_project_statuses"]], "valid_scope (projectstatus attribute)": [[2, "ayon_api.entity_hub.ProjectStatus.valid_scope"]], "valid_states (projectstatus attribute)": [[2, "ayon_api.entity_hub.ProjectStatus.valid_states"]], "value (attributevalue property)": [[2, "ayon_api.entity_hub.AttributeValue.value"]], "values() (attributes method)": [[2, "ayon_api.entity_hub.Attributes.values"]], "version (versionentity property)": [[2, "ayon_api.entity_hub.VersionEntity.version"]], "serverevent (class in ayon_api.events)": [[3, "ayon_api.events.ServerEvent"]], "ayon_api.events": [[3, "module-ayon_api.events"]], "to_data() (serverevent method)": [[3, "ayon_api.events.ServerEvent.to_data"]], "authenticationerror": [[4, "ayon_api.exceptions.AuthenticationError"]], "failedoperations": [[4, "ayon_api.exceptions.FailedOperations"]], "failedserviceinit": [[4, "ayon_api.exceptions.FailedServiceInit"]], "foldernotfound": [[4, "ayon_api.exceptions.FolderNotFound"]], "graphqlqueryfailed": [[4, "ayon_api.exceptions.GraphQlQueryFailed"]], "httprequesterror": [[4, "ayon_api.exceptions.HTTPRequestError"]], "missingentityerror": [[4, "ayon_api.exceptions.MissingEntityError"]], "projectnotfound": [[4, "ayon_api.exceptions.ProjectNotFound"]], "requesterror": [[4, "ayon_api.exceptions.RequestError"]], "servererror": [[4, "ayon_api.exceptions.ServerError"]], "servernotreached": [[4, "ayon_api.exceptions.ServerNotReached"]], "unauthorizederror": [[4, "ayon_api.exceptions.UnauthorizedError"]], "unsupportedserverversion": [[4, "ayon_api.exceptions.UnsupportedServerVersion"]], "urlerror": [[4, "ayon_api.exceptions.UrlError"]], "ayon_api.exceptions": [[4, "module-ayon_api.exceptions"]], "basegraphqlqueryfield (class in ayon_api.graphql)": [[5, "ayon_api.graphql.BaseGraphQlQueryField"]], "graphqlquery (class in ayon_api.graphql)": [[5, "ayon_api.graphql.GraphQlQuery"]], "graphqlqueryedgefield (class in ayon_api.graphql)": [[5, "ayon_api.graphql.GraphQlQueryEdgeField"]], "graphqlqueryfield (class in ayon_api.graphql)": [[5, "ayon_api.graphql.GraphQlQueryField"]], "queryvariable (class in ayon_api.graphql)": [[5, "ayon_api.graphql.QueryVariable"]], "add_edge_field() (graphqlqueryedgefield method)": [[5, "ayon_api.graphql.GraphQlQueryEdgeField.add_edge_field"]], "add_field() (basegraphqlqueryfield method)": [[5, "ayon_api.graphql.BaseGraphQlQueryField.add_field"]], "add_field() (graphqlquery method)": [[5, "ayon_api.graphql.GraphQlQuery.add_field"]], "add_field_with_edges() (basegraphqlqueryfield method)": [[5, "ayon_api.graphql.BaseGraphQlQueryField.add_field_with_edges"]], "add_field_with_edges() (graphqlquery method)": [[5, "ayon_api.graphql.GraphQlQuery.add_field_with_edges"]], "add_obj_edge_field() (graphqlqueryedgefield method)": [[5, "ayon_api.graphql.GraphQlQueryEdgeField.add_obj_edge_field"]], "add_obj_field() (basegraphqlqueryfield method)": [[5, "ayon_api.graphql.BaseGraphQlQueryField.add_obj_field"]], "add_obj_field() (graphqlquery method)": [[5, "ayon_api.graphql.GraphQlQuery.add_obj_field"]], "add_obj_field() (graphqlqueryedgefield method)": [[5, "ayon_api.graphql.GraphQlQueryEdgeField.add_obj_field"]], "add_variable() (basegraphqlqueryfield method)": [[5, "ayon_api.graphql.BaseGraphQlQueryField.add_variable"]], "add_variable() (graphqlquery method)": [[5, "ayon_api.graphql.GraphQlQuery.add_variable"]], "ayon_api.graphql": [[5, "module-ayon_api.graphql"]], "calculate_query() (basegraphqlqueryfield method)": [[5, "ayon_api.graphql.BaseGraphQlQueryField.calculate_query"]], "calculate_query() (graphqlquery method)": [[5, "ayon_api.graphql.GraphQlQuery.calculate_query"]], "calculate_query() (graphqlqueryedgefield method)": [[5, "ayon_api.graphql.GraphQlQueryEdgeField.calculate_query"]], "calculate_query() (graphqlqueryfield method)": [[5, "ayon_api.graphql.GraphQlQueryField.calculate_query"]], "child_has_edges (basegraphqlqueryfield property)": [[5, "ayon_api.graphql.BaseGraphQlQueryField.child_has_edges"]], "child_indent (basegraphqlqueryfield property)": [[5, "ayon_api.graphql.BaseGraphQlQueryField.child_indent"]], "child_indent (graphqlquery property)": [[5, "ayon_api.graphql.GraphQlQuery.child_indent"]], "child_indent (graphqlqueryedgefield property)": [[5, "ayon_api.graphql.GraphQlQueryEdgeField.child_indent"]], "child_indent (graphqlqueryfield property)": [[5, "ayon_api.graphql.GraphQlQueryField.child_indent"]], "continuous_query() (graphqlquery method)": [[5, "ayon_api.graphql.GraphQlQuery.continuous_query"]], "fields_to_dict() (in module ayon_api.graphql)": [[5, "ayon_api.graphql.fields_to_dict"]], "get_field_by_keys() (basegraphqlqueryfield method)": [[5, "ayon_api.graphql.BaseGraphQlQueryField.get_field_by_keys"]], "get_field_by_keys() (graphqlquery method)": [[5, "ayon_api.graphql.GraphQlQuery.get_field_by_keys"]], "get_field_by_path() (graphqlquery method)": [[5, "ayon_api.graphql.GraphQlQuery.get_field_by_path"]], "get_filters() (basegraphqlqueryfield method)": [[5, "ayon_api.graphql.BaseGraphQlQueryField.get_filters"]], "get_filters() (graphqlqueryedgefield method)": [[5, "ayon_api.graphql.GraphQlQueryEdgeField.get_filters"]], "get_name() (basegraphqlqueryfield method)": [[5, "ayon_api.graphql.BaseGraphQlQueryField.get_name"]], "get_variable() (basegraphqlqueryfield method)": [[5, "ayon_api.graphql.BaseGraphQlQueryField.get_variable"]], "get_variable() (graphqlquery method)": [[5, "ayon_api.graphql.GraphQlQuery.get_variable"]], "get_variable_keys() (graphqlquery method)": [[5, "ayon_api.graphql.GraphQlQuery.get_variable_keys"]], "get_variable_value() (basegraphqlqueryfield method)": [[5, "ayon_api.graphql.BaseGraphQlQueryField.get_variable_value"]], "get_variable_value() (graphqlquery method)": [[5, "ayon_api.graphql.GraphQlQuery.get_variable_value"]], "get_variables_values() (graphqlquery method)": [[5, "ayon_api.graphql.GraphQlQuery.get_variables_values"]], "has_edges (basegraphqlqueryfield property)": [[5, "ayon_api.graphql.BaseGraphQlQueryField.has_edges"]], "has_edges (graphqlqueryedgefield attribute)": [[5, "ayon_api.graphql.GraphQlQueryEdgeField.has_edges"]], "has_edges (graphqlqueryfield attribute)": [[5, "ayon_api.graphql.GraphQlQueryField.has_edges"]], "has_filter() (basegraphqlqueryfield method)": [[5, "ayon_api.graphql.BaseGraphQlQueryField.has_filter"]], "has_multiple_edge_fields (graphqlquery property)": [[5, "ayon_api.graphql.GraphQlQuery.has_multiple_edge_fields"]], "indent (basegraphqlqueryfield property)": [[5, "ayon_api.graphql.BaseGraphQlQueryField.indent"]], "indent (graphqlquery property)": [[5, "ayon_api.graphql.GraphQlQuery.indent"]], "name (basegraphqlqueryfield property)": [[5, "ayon_api.graphql.BaseGraphQlQueryField.name"]], "name (queryvariable property)": [[5, "ayon_api.graphql.QueryVariable.name"]], "need_query (basegraphqlqueryfield property)": [[5, "ayon_api.graphql.BaseGraphQlQueryField.need_query"]], "need_query (graphqlquery property)": [[5, "ayon_api.graphql.GraphQlQuery.need_query"]], "offset (basegraphqlqueryfield property)": [[5, "ayon_api.graphql.BaseGraphQlQueryField.offset"]], "offset (graphqlquery attribute)": [[5, "ayon_api.graphql.GraphQlQuery.offset"]], "parse_result() (basegraphqlqueryfield method)": [[5, "ayon_api.graphql.BaseGraphQlQueryField.parse_result"]], "parse_result() (graphqlquery method)": [[5, "ayon_api.graphql.GraphQlQuery.parse_result"]], "parse_result() (graphqlqueryedgefield method)": [[5, "ayon_api.graphql.GraphQlQueryEdgeField.parse_result"]], "parse_result() (graphqlqueryfield method)": [[5, "ayon_api.graphql.GraphQlQueryField.parse_result"]], "path (basegraphqlqueryfield property)": [[5, "ayon_api.graphql.BaseGraphQlQueryField.path"]], "query() (graphqlquery method)": [[5, "ayon_api.graphql.GraphQlQuery.query"]], "query_item (basegraphqlqueryfield property)": [[5, "ayon_api.graphql.BaseGraphQlQueryField.query_item"]], "remove_filter() (basegraphqlqueryfield method)": [[5, "ayon_api.graphql.BaseGraphQlQueryField.remove_filter"]], "reset_cursor() (basegraphqlqueryfield method)": [[5, "ayon_api.graphql.BaseGraphQlQueryField.reset_cursor"]], "reset_cursor() (graphqlqueryedgefield method)": [[5, "ayon_api.graphql.GraphQlQueryEdgeField.reset_cursor"]], "set_ascending_order() (basegraphqlqueryfield method)": [[5, "ayon_api.graphql.BaseGraphQlQueryField.set_ascending_order"]], "set_descending_order() (basegraphqlqueryfield method)": [[5, "ayon_api.graphql.BaseGraphQlQueryField.set_descending_order"]], "set_filter() (basegraphqlqueryfield method)": [[5, "ayon_api.graphql.BaseGraphQlQueryField.set_filter"]], "set_limit() (basegraphqlqueryfield method)": [[5, "ayon_api.graphql.BaseGraphQlQueryField.set_limit"]], "set_order() (basegraphqlqueryfield method)": [[5, "ayon_api.graphql.BaseGraphQlQueryField.set_order"]], "set_parent() (basegraphqlqueryfield method)": [[5, "ayon_api.graphql.BaseGraphQlQueryField.set_parent"]], "set_variable_value() (basegraphqlqueryfield method)": [[5, "ayon_api.graphql.BaseGraphQlQueryField.set_variable_value"]], "set_variable_value() (graphqlquery method)": [[5, "ayon_api.graphql.GraphQlQuery.set_variable_value"]], "sum_edge_fields() (basegraphqlqueryfield method)": [[5, "ayon_api.graphql.BaseGraphQlQueryField.sum_edge_fields"]], "variable_name (queryvariable property)": [[5, "ayon_api.graphql.QueryVariable.variable_name"]], "activities_graphql_query() (in module ayon_api.graphql_queries)": [[6, "ayon_api.graphql_queries.activities_graphql_query"]], "add_links_fields() (in module ayon_api.graphql_queries)": [[6, "ayon_api.graphql_queries.add_links_fields"]], "ayon_api.graphql_queries": [[6, "module-ayon_api.graphql_queries"]], "events_graphql_query() (in module ayon_api.graphql_queries)": [[6, "ayon_api.graphql_queries.events_graphql_query"]], "folders_graphql_query() (in module ayon_api.graphql_queries)": [[6, "ayon_api.graphql_queries.folders_graphql_query"]], "product_types_query() (in module ayon_api.graphql_queries)": [[6, "ayon_api.graphql_queries.product_types_query"]], "products_graphql_query() (in module ayon_api.graphql_queries)": [[6, "ayon_api.graphql_queries.products_graphql_query"]], "project_graphql_query() (in module ayon_api.graphql_queries)": [[6, "ayon_api.graphql_queries.project_graphql_query"]], "project_product_types_query() (in module ayon_api.graphql_queries)": [[6, "ayon_api.graphql_queries.project_product_types_query"]], "projects_graphql_query() (in module ayon_api.graphql_queries)": [[6, "ayon_api.graphql_queries.projects_graphql_query"]], "representations_graphql_query() (in module ayon_api.graphql_queries)": [[6, "ayon_api.graphql_queries.representations_graphql_query"]], "representations_hierarchy_qraphql_query() (in module ayon_api.graphql_queries)": [[6, "ayon_api.graphql_queries.representations_hierarchy_qraphql_query"]], "representations_parents_qraphql_query() (in module ayon_api.graphql_queries)": [[6, "ayon_api.graphql_queries.representations_parents_qraphql_query"]], "tasks_by_folder_paths_graphql_query() (in module ayon_api.graphql_queries)": [[6, "ayon_api.graphql_queries.tasks_by_folder_paths_graphql_query"]], "tasks_graphql_query() (in module ayon_api.graphql_queries)": [[6, "ayon_api.graphql_queries.tasks_graphql_query"]], "users_graphql_query() (in module ayon_api.graphql_queries)": [[6, "ayon_api.graphql_queries.users_graphql_query"]], "versions_graphql_query() (in module ayon_api.graphql_queries)": [[6, "ayon_api.graphql_queries.versions_graphql_query"]], "workfiles_info_graphql_query() (in module ayon_api.graphql_queries)": [[6, "ayon_api.graphql_queries.workfiles_info_graphql_query"]], "abstractoperation (class in ayon_api.operations)": [[7, "ayon_api.operations.AbstractOperation"]], "createoperation (class in ayon_api.operations)": [[7, "ayon_api.operations.CreateOperation"]], "deleteoperation (class in ayon_api.operations)": [[7, "ayon_api.operations.DeleteOperation"]], "operationssession (class in ayon_api.operations)": [[7, "ayon_api.operations.OperationsSession"]], "updateoperation (class in ayon_api.operations)": [[7, "ayon_api.operations.UpdateOperation"]], "add() (operationssession method)": [[7, "ayon_api.operations.OperationsSession.add"]], "append() (operationssession method)": [[7, "ayon_api.operations.OperationsSession.append"]], "ayon_api.operations": [[7, "module-ayon_api.operations"]], "clear() (operationssession method)": [[7, "ayon_api.operations.OperationsSession.clear"]], "commit() (operationssession method)": [[7, "ayon_api.operations.OperationsSession.commit"]], "con (createoperation property)": [[7, "ayon_api.operations.CreateOperation.con"]], "con (deleteoperation property)": [[7, "ayon_api.operations.DeleteOperation.con"]], "con (operationssession property)": [[7, "ayon_api.operations.OperationsSession.con"]], "con (updateoperation property)": [[7, "ayon_api.operations.UpdateOperation.con"]], "create_entity() (operationssession method)": [[7, "ayon_api.operations.OperationsSession.create_entity"]], "create_folder() (operationssession method)": [[7, "ayon_api.operations.OperationsSession.create_folder"]], "create_product() (operationssession method)": [[7, "ayon_api.operations.OperationsSession.create_product"]], "create_representation() (operationssession method)": [[7, "ayon_api.operations.OperationsSession.create_representation"]], "create_task() (operationssession method)": [[7, "ayon_api.operations.OperationsSession.create_task"]], "create_version() (operationssession method)": [[7, "ayon_api.operations.OperationsSession.create_version"]], "data (createoperation property)": [[7, "ayon_api.operations.CreateOperation.data"]], "delete_entity() (operationssession method)": [[7, "ayon_api.operations.OperationsSession.delete_entity"]], "delete_folder() (operationssession method)": [[7, "ayon_api.operations.OperationsSession.delete_folder"]], "delete_product() (operationssession method)": [[7, "ayon_api.operations.OperationsSession.delete_product"]], "delete_representation() (operationssession method)": [[7, "ayon_api.operations.OperationsSession.delete_representation"]], "delete_task() (operationssession method)": [[7, "ayon_api.operations.OperationsSession.delete_task"]], "delete_version() (operationssession method)": [[7, "ayon_api.operations.OperationsSession.delete_version"]], "entity_id (createoperation property)": [[7, "ayon_api.operations.CreateOperation.entity_id"]], "entity_id (deleteoperation property)": [[7, "ayon_api.operations.DeleteOperation.entity_id"]], "entity_id (updateoperation property)": [[7, "ayon_api.operations.UpdateOperation.entity_id"]], "entity_type (abstractoperation property)": [[7, "ayon_api.operations.AbstractOperation.entity_type"]], "extend() (operationssession method)": [[7, "ayon_api.operations.OperationsSession.extend"]], "get() (createoperation method)": [[7, "ayon_api.operations.CreateOperation.get"]], "get_project() (operationssession method)": [[7, "ayon_api.operations.OperationsSession.get_project"]], "id (abstractoperation property)": [[7, "ayon_api.operations.AbstractOperation.id"]], "new_folder_entity() (in module ayon_api.operations)": [[7, "ayon_api.operations.new_folder_entity"]], "new_hero_version_entity() (in module ayon_api.operations)": [[7, "ayon_api.operations.new_hero_version_entity"]], "new_product_entity() (in module ayon_api.operations)": [[7, "ayon_api.operations.new_product_entity"]], "new_representation_entity() (in module ayon_api.operations)": [[7, "ayon_api.operations.new_representation_entity"]], "new_version_entity() (in module ayon_api.operations)": [[7, "ayon_api.operations.new_version_entity"]], "new_workfile_info() (in module ayon_api.operations)": [[7, "ayon_api.operations.new_workfile_info"]], "operation_name (abstractoperation property)": [[7, "ayon_api.operations.AbstractOperation.operation_name"]], "operation_name (createoperation attribute)": [[7, "ayon_api.operations.CreateOperation.operation_name"]], "operation_name (deleteoperation attribute)": [[7, "ayon_api.operations.DeleteOperation.operation_name"]], "operation_name (updateoperation attribute)": [[7, "ayon_api.operations.UpdateOperation.operation_name"]], "prepare_changes() (in module ayon_api.operations)": [[7, "ayon_api.operations.prepare_changes"]], "project_name (abstractoperation property)": [[7, "ayon_api.operations.AbstractOperation.project_name"]], "remove() (operationssession method)": [[7, "ayon_api.operations.OperationsSession.remove"]], "session (createoperation property)": [[7, "ayon_api.operations.CreateOperation.session"]], "session (deleteoperation property)": [[7, "ayon_api.operations.DeleteOperation.session"]], "session (updateoperation property)": [[7, "ayon_api.operations.UpdateOperation.session"]], "set_value() (createoperation method)": [[7, "ayon_api.operations.CreateOperation.set_value"]], "to_data() (abstractoperation method)": [[7, "ayon_api.operations.AbstractOperation.to_data"]], "to_data() (createoperation method)": [[7, "ayon_api.operations.CreateOperation.to_data"]], "to_data() (deleteoperation method)": [[7, "ayon_api.operations.DeleteOperation.to_data"]], "to_data() (operationssession method)": [[7, "ayon_api.operations.OperationsSession.to_data"]], "to_data() (updateoperation method)": [[7, "ayon_api.operations.UpdateOperation.to_data"]], "to_server_operation() (createoperation method)": [[7, "ayon_api.operations.CreateOperation.to_server_operation"]], "to_server_operation() (deleteoperation method)": [[7, "ayon_api.operations.DeleteOperation.to_server_operation"]], "to_server_operation() (updateoperation method)": [[7, "ayon_api.operations.UpdateOperation.to_server_operation"]], "update_data (updateoperation property)": [[7, "ayon_api.operations.UpdateOperation.update_data"]], "update_entity() (operationssession method)": [[7, "ayon_api.operations.OperationsSession.update_entity"]], "update_folder() (operationssession method)": [[7, "ayon_api.operations.OperationsSession.update_folder"]], "update_product() (operationssession method)": [[7, "ayon_api.operations.OperationsSession.update_product"]], "update_representation() (operationssession method)": [[7, "ayon_api.operations.OperationsSession.update_representation"]], "update_task() (operationssession method)": [[7, "ayon_api.operations.OperationsSession.update_task"]], "update_version() (operationssession method)": [[7, "ayon_api.operations.OperationsSession.update_version"]], "graphqlresponse (class in ayon_api.server_api)": [[8, "ayon_api.server_api.GraphQlResponse"]], "requesttype (class in ayon_api.server_api)": [[8, "ayon_api.server_api.RequestType"]], "requesttypes (class in ayon_api.server_api)": [[8, "ayon_api.server_api.RequestTypes"]], "restapiresponse (class in ayon_api.server_api)": [[8, "ayon_api.server_api.RestApiResponse"]], "serverapi (class in ayon_api.server_api)": [[8, "ayon_api.server_api.ServerAPI"]], "ayon_api.server_api": [[8, "module-ayon_api.server_api"]], "content (restapiresponse property)": [[8, "ayon_api.server_api.RestApiResponse.content"]], "content_type (restapiresponse property)": [[8, "ayon_api.server_api.RestApiResponse.content_type"]], "data (restapiresponse property)": [[8, "ayon_api.server_api.RestApiResponse.data"]], "detail (restapiresponse property)": [[8, "ayon_api.server_api.RestApiResponse.detail"]], "fill_own_attribs() (in module ayon_api.server_api)": [[8, "ayon_api.server_api.fill_own_attribs"]], "get() (restapiresponse method)": [[8, "ayon_api.server_api.RestApiResponse.get"]], "headers (restapiresponse property)": [[8, "ayon_api.server_api.RestApiResponse.headers"]], "orig_response (restapiresponse property)": [[8, "ayon_api.server_api.RestApiResponse.orig_response"]], "raise_for_status() (restapiresponse method)": [[8, "ayon_api.server_api.RestApiResponse.raise_for_status"]], "status_code (restapiresponse property)": [[8, "ayon_api.server_api.RestApiResponse.status_code"]], "text (restapiresponse property)": [[8, "ayon_api.server_api.RestApiResponse.text"]], "representationhierarchy (class in ayon_api.utils)": [[9, "ayon_api.utils.RepresentationHierarchy"]], "representationparents (class in ayon_api.utils)": [[9, "ayon_api.utils.RepresentationParents"]], "sortorder (class in ayon_api.utils)": [[9, "ayon_api.utils.SortOrder"]], "thumbnailcontent (class in ayon_api.utils)": [[9, "ayon_api.utils.ThumbnailContent"]], "transferprogress (class in ayon_api.utils)": [[9, "ayon_api.utils.TransferProgress"]], "abort_web_action_event() (in module ayon_api.utils)": [[9, "ayon_api.utils.abort_web_action_event"]], "ayon_api.utils": [[9, "module-ayon_api.utils"]], "convert_entity_id() (in module ayon_api.utils)": [[9, "ayon_api.utils.convert_entity_id"]], "convert_or_create_entity_id() (in module ayon_api.utils)": [[9, "ayon_api.utils.convert_or_create_entity_id"]], "create_dependency_package_basename() (in module ayon_api.utils)": [[9, "ayon_api.utils.create_dependency_package_basename"]], "create_entity_id() (in module ayon_api.utils)": [[9, "ayon_api.utils.create_entity_id"]], "entity_data_json_default() (in module ayon_api.utils)": [[9, "ayon_api.utils.entity_data_json_default"]], "failed_json_default() (in module ayon_api.utils)": [[9, "ayon_api.utils.failed_json_default"]], "folder (representationhierarchy attribute)": [[9, "ayon_api.utils.RepresentationHierarchy.folder"]], "folder (representationparents attribute)": [[9, "ayon_api.utils.RepresentationParents.folder"]], "get_default_settings_variant() (in module ayon_api.utils)": [[9, "ayon_api.utils.get_default_settings_variant"]], "get_default_site_id() (in module ayon_api.utils)": [[9, "ayon_api.utils.get_default_site_id"]], "get_default_timeout() (in module ayon_api.utils)": [[9, "ayon_api.utils.get_default_timeout"]], "get_media_mime_type() (in module ayon_api.utils)": [[9, "ayon_api.utils.get_media_mime_type"]], "get_media_mime_type_for_content() (in module ayon_api.utils)": [[9, "ayon_api.utils.get_media_mime_type_for_content"]], "get_media_mime_type_for_stream() (in module ayon_api.utils)": [[9, "ayon_api.utils.get_media_mime_type_for_stream"]], "get_user_by_token() (in module ayon_api.utils)": [[9, "ayon_api.utils.get_user_by_token"]], "id (thumbnailcontent property)": [[9, "ayon_api.utils.ThumbnailContent.id"]], "is_token_valid() (in module ayon_api.utils)": [[9, "ayon_api.utils.is_token_valid"]], "is_valid (thumbnailcontent property)": [[9, "ayon_api.utils.ThumbnailContent.is_valid"]], "login_to_server() (in module ayon_api.utils)": [[9, "ayon_api.utils.login_to_server"]], "logout_from_server() (in module ayon_api.utils)": [[9, "ayon_api.utils.logout_from_server"]], "prepare_attribute_changes() (in module ayon_api.utils)": [[9, "ayon_api.utils.prepare_attribute_changes"]], "prepare_entity_changes() (in module ayon_api.utils)": [[9, "ayon_api.utils.prepare_entity_changes"]], "prepare_query_string() (in module ayon_api.utils)": [[9, "ayon_api.utils.prepare_query_string"]], "product (representationhierarchy attribute)": [[9, "ayon_api.utils.RepresentationHierarchy.product"]], "product (representationparents attribute)": [[9, "ayon_api.utils.RepresentationParents.product"]], "project (representationhierarchy attribute)": [[9, "ayon_api.utils.RepresentationHierarchy.project"]], "project (representationparents attribute)": [[9, "ayon_api.utils.RepresentationParents.project"]], "representation (representationhierarchy attribute)": [[9, "ayon_api.utils.RepresentationHierarchy.representation"]], "slugify_string() (in module ayon_api.utils)": [[9, "ayon_api.utils.slugify_string"]], "take_web_action_event() (in module ayon_api.utils)": [[9, "ayon_api.utils.take_web_action_event"]], "task (representationhierarchy attribute)": [[9, "ayon_api.utils.RepresentationHierarchy.task"]], "validate_url() (in module ayon_api.utils)": [[9, "ayon_api.utils.validate_url"]], "version (representationhierarchy attribute)": [[9, "ayon_api.utils.RepresentationHierarchy.version"]], "version (representationparents attribute)": [[9, "ayon_api.utils.RepresentationParents.version"]], "ayon_api.version": [[10, "module-ayon_api.version"]]}}) \ No newline at end of file