From 846e23dbabbbc9fd64f2620cfa139ea87ca1fcd8 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Mon, 18 Jul 2022 11:16:53 +0200 Subject: [PATCH 01/31] copied mongo.py from lib to client --- openpype/client/mongo.py | 210 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 210 insertions(+) create mode 100644 openpype/client/mongo.py diff --git a/openpype/client/mongo.py b/openpype/client/mongo.py new file mode 100644 index 0000000000..a747250107 --- /dev/null +++ b/openpype/client/mongo.py @@ -0,0 +1,210 @@ +import os +import sys +import time +import logging +import pymongo +import certifi + +if sys.version_info[0] == 2: + from urlparse import urlparse, parse_qs +else: + from urllib.parse import urlparse, parse_qs + + +class MongoEnvNotSet(Exception): + pass + + +def _decompose_url(url): + """Decompose mongo url to basic components. + + Used for creation of MongoHandler which expect mongo url components as + separated kwargs. Components are at the end not used as we're setting + connection directly this is just a dumb components for MongoHandler + validation pass. + """ + + # Use first url from passed url + # - this is because it is possible to pass multiple urls for multiple + # replica sets which would crash on urlparse otherwise + # - please don't use comma in username of password + url = url.split(",")[0] + components = { + "scheme": None, + "host": None, + "port": None, + "username": None, + "password": None, + "auth_db": None + } + + result = urlparse(url) + if result.scheme is None: + _url = "mongodb://{}".format(url) + result = urlparse(_url) + + components["scheme"] = result.scheme + components["host"] = result.hostname + try: + components["port"] = result.port + except ValueError: + raise RuntimeError("invalid port specified") + components["username"] = result.username + components["password"] = result.password + + try: + components["auth_db"] = parse_qs(result.query)['authSource'][0] + except KeyError: + # no auth db provided, mongo will use the one we are connecting to + pass + + return components + + +def get_default_components(): + mongo_url = os.environ.get("OPENPYPE_MONGO") + if mongo_url is None: + raise MongoEnvNotSet( + "URL for Mongo logging connection is not set." + ) + return _decompose_url(mongo_url) + + +def should_add_certificate_path_to_mongo_url(mongo_url): + """Check if should add ca certificate to mongo url. + + Since 30.9.2021 cloud mongo requires newer certificates that are not + available on most of workstation. This adds path to certifi certificate + which is valid for it. To add the certificate path url must have scheme + 'mongodb+srv' or has 'ssl=true' or 'tls=true' in url query. + """ + + parsed = urlparse(mongo_url) + query = parse_qs(parsed.query) + lowered_query_keys = set(key.lower() for key in query.keys()) + add_certificate = False + # Check if url 'ssl' or 'tls' are set to 'true' + for key in ("ssl", "tls"): + if key in query and "true" in query["ssl"]: + add_certificate = True + break + + # Check if url contains 'mongodb+srv' + if not add_certificate and parsed.scheme == "mongodb+srv": + add_certificate = True + + # Check if url does already contain certificate path + if add_certificate and "tlscafile" in lowered_query_keys: + add_certificate = False + + return add_certificate + + +def validate_mongo_connection(mongo_uri): + """Check if provided mongodb URL is valid. + + Args: + mongo_uri (str): URL to validate. + + Raises: + ValueError: When port in mongo uri is not valid. + pymongo.errors.InvalidURI: If passed mongo is invalid. + pymongo.errors.ServerSelectionTimeoutError: If connection timeout + passed so probably couldn't connect to mongo server. + + """ + + client = OpenPypeMongoConnection.create_connection( + mongo_uri, retry_attempts=1 + ) + client.close() + + +class OpenPypeMongoConnection: + """Singleton MongoDB connection. + + Keeps MongoDB connections by url. + """ + + mongo_clients = {} + log = logging.getLogger("OpenPypeMongoConnection") + + @staticmethod + def get_default_mongo_url(): + return os.environ["OPENPYPE_MONGO"] + + @classmethod + def get_mongo_client(cls, mongo_url=None): + if mongo_url is None: + mongo_url = cls.get_default_mongo_url() + + connection = cls.mongo_clients.get(mongo_url) + if connection: + # Naive validation of existing connection + try: + connection.server_info() + with connection.start_session(): + pass + except Exception: + connection = None + + if not connection: + cls.log.debug("Creating mongo connection to {}".format(mongo_url)) + connection = cls.create_connection(mongo_url) + cls.mongo_clients[mongo_url] = connection + + return connection + + @classmethod + def create_connection(cls, mongo_url, timeout=None, retry_attempts=None): + parsed = urlparse(mongo_url) + # Force validation of scheme + if parsed.scheme not in ["mongodb", "mongodb+srv"]: + raise pymongo.errors.InvalidURI(( + "Invalid URI scheme:" + " URI must begin with 'mongodb://' or 'mongodb+srv://'" + )) + + if timeout is None: + timeout = int(os.environ.get("AVALON_TIMEOUT") or 1000) + + kwargs = { + "serverSelectionTimeoutMS": timeout + } + if should_add_certificate_path_to_mongo_url(mongo_url): + kwargs["ssl_ca_certs"] = certifi.where() + + mongo_client = pymongo.MongoClient(mongo_url, **kwargs) + + if retry_attempts is None: + retry_attempts = 3 + + elif not retry_attempts: + retry_attempts = 1 + + last_exc = None + valid = False + t1 = time.time() + for attempt in range(1, retry_attempts + 1): + try: + mongo_client.server_info() + with mongo_client.start_session(): + pass + valid = True + break + + except Exception as exc: + last_exc = exc + if attempt < retry_attempts: + cls.log.warning( + "Attempt {} failed. Retrying... ".format(attempt) + ) + time.sleep(1) + + if not valid: + raise last_exc + + cls.log.info("Connected to {}, delay {:.3f}s".format( + mongo_url, time.time() - t1 + )) + return mongo_client From ccbc18fd82d7a00c2ab187489ca50977da5e9b25 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Mon, 18 Jul 2022 11:17:01 +0200 Subject: [PATCH 02/31] 'OpenPypeMongoConnection' is available in 'openpype.client' --- openpype/client/__init__.py | 6 ++++++ openpype/client/entities.py | 2 +- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/openpype/client/__init__.py b/openpype/client/__init__.py index 97e6755d09..0bd79de140 100644 --- a/openpype/client/__init__.py +++ b/openpype/client/__init__.py @@ -1,3 +1,7 @@ +from .mongo import ( + OpenPypeMongoConnection, +) + from .entities import ( get_projects, get_project, @@ -40,6 +44,8 @@ from .entities import ( ) __all__ = ( + "OpenPypeMongoConnection", + "get_projects", "get_project", "get_whole_project", diff --git a/openpype/client/entities.py b/openpype/client/entities.py index 9d65355d1b..1c32632915 100644 --- a/openpype/client/entities.py +++ b/openpype/client/entities.py @@ -12,7 +12,7 @@ import collections import six from bson.objectid import ObjectId -from openpype.lib.mongo import OpenPypeMongoConnection +from .mongo import OpenPypeMongoConnection def _get_project_database(): From 308d9e9c498642ebc0a8dfa3e2e86603a4c01ad5 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Mon, 18 Jul 2022 11:20:14 +0200 Subject: [PATCH 03/31] use 'OpenPypeMongoConnection' from 'openpype.client' --- openpype/hosts/maya/api/shader_definition_editor.py | 2 +- .../hosts/maya/plugins/publish/validate_model_name.py | 2 +- .../webpublisher/webserver_service/webpublish_routes.py | 2 +- .../hosts/webpublisher/webserver_service/webserver_cli.py | 3 +-- openpype/lib/local_settings.py | 2 +- openpype/lib/log.py | 7 ++++--- openpype/lib/remote_publish.py | 2 +- openpype/modules/ftrack/ftrack_server/event_server_cli.py | 8 ++++---- openpype/modules/ftrack/ftrack_server/lib.py | 2 +- openpype/modules/ftrack/scripts/sub_event_storer.py | 3 ++- .../modules/slack/plugins/publish/integrate_slack_api.py | 2 +- openpype/pipeline/mongodb.py | 4 ++-- openpype/settings/handlers.py | 2 +- 13 files changed, 21 insertions(+), 20 deletions(-) diff --git a/openpype/hosts/maya/api/shader_definition_editor.py b/openpype/hosts/maya/api/shader_definition_editor.py index 911db48ac2..6ea5e1a127 100644 --- a/openpype/hosts/maya/api/shader_definition_editor.py +++ b/openpype/hosts/maya/api/shader_definition_editor.py @@ -6,7 +6,7 @@ Shader names are stored as simple text file over GridFS in mongodb. """ import os from Qt import QtWidgets, QtCore, QtGui -from openpype.lib.mongo import OpenPypeMongoConnection +from openpype.client.mongo import OpenPypeMongoConnection from openpype import resources import gridfs diff --git a/openpype/hosts/maya/plugins/publish/validate_model_name.py b/openpype/hosts/maya/plugins/publish/validate_model_name.py index 50acf2b8b7..02107d5732 100644 --- a/openpype/hosts/maya/plugins/publish/validate_model_name.py +++ b/openpype/hosts/maya/plugins/publish/validate_model_name.py @@ -10,7 +10,7 @@ from openpype.pipeline import legacy_io import openpype.hosts.maya.api.action from openpype.hosts.maya.api.shader_definition_editor import ( DEFINITION_FILENAME) -from openpype.lib.mongo import OpenPypeMongoConnection +from openpype.client.mongo import OpenPypeMongoConnection import gridfs diff --git a/openpype/hosts/webpublisher/webserver_service/webpublish_routes.py b/openpype/hosts/webpublisher/webserver_service/webpublish_routes.py index 4cb3cee8e1..6444a5191d 100644 --- a/openpype/hosts/webpublisher/webserver_service/webpublish_routes.py +++ b/openpype/hosts/webpublisher/webserver_service/webpublish_routes.py @@ -10,9 +10,9 @@ from aiohttp.web_response import Response from openpype.client import ( get_projects, get_assets, + OpenPypeMongoConnection, ) from openpype.lib import ( - OpenPypeMongoConnection, PypeLogger, ) from openpype.lib.remote_publish import ( diff --git a/openpype/hosts/webpublisher/webserver_service/webserver_cli.py b/openpype/hosts/webpublisher/webserver_service/webserver_cli.py index 1ed8f22b2c..6620e5d5cf 100644 --- a/openpype/hosts/webpublisher/webserver_service/webserver_cli.py +++ b/openpype/hosts/webpublisher/webserver_service/webserver_cli.py @@ -6,6 +6,7 @@ import requests import json import subprocess +from openpype.client import OpenPypeMongoConnection from openpype.lib import PypeLogger from .webpublish_routes import ( @@ -121,8 +122,6 @@ def run_webserver(*args, **kwargs): def reprocess_failed(upload_dir, webserver_url): # log.info("check_reprocesable_records") - from openpype.lib import OpenPypeMongoConnection - mongo_client = OpenPypeMongoConnection.get_mongo_client() database_name = os.environ["OPENPYPE_DATABASE_NAME"] dbcon = mongo_client[database_name]["webpublishes"] diff --git a/openpype/lib/local_settings.py b/openpype/lib/local_settings.py index 97e99b4b5a..c6c9699240 100644 --- a/openpype/lib/local_settings.py +++ b/openpype/lib/local_settings.py @@ -34,7 +34,7 @@ from openpype.settings import ( get_system_settings ) -from .import validate_mongo_connection +from openpype.client.mongo import validate_mongo_connection _PLACEHOLDER = object() diff --git a/openpype/lib/log.py b/openpype/lib/log.py index 2cdb7ec8e4..e0fc7b33b1 100644 --- a/openpype/lib/log.py +++ b/openpype/lib/log.py @@ -24,12 +24,13 @@ import traceback import threading import copy -from . import Terminal -from .mongo import ( +from openpype.client.mongo import ( MongoEnvNotSet, get_default_components, - OpenPypeMongoConnection + OpenPypeMongoConnection, ) +from . import Terminal + try: import log4mongo from log4mongo.handlers import MongoHandler diff --git a/openpype/lib/remote_publish.py b/openpype/lib/remote_publish.py index d7884d0200..38c6b07c5b 100644 --- a/openpype/lib/remote_publish.py +++ b/openpype/lib/remote_publish.py @@ -7,7 +7,7 @@ from bson.objectid import ObjectId import pyblish.util import pyblish.api -from openpype.lib.mongo import OpenPypeMongoConnection +from openpype.client.mongo import OpenPypeMongoConnection from openpype.lib.plugin_tools import parse_json ERROR_STATUS = "error" diff --git a/openpype/modules/ftrack/ftrack_server/event_server_cli.py b/openpype/modules/ftrack/ftrack_server/event_server_cli.py index 90ce757242..3ef7c8270a 100644 --- a/openpype/modules/ftrack/ftrack_server/event_server_cli.py +++ b/openpype/modules/ftrack/ftrack_server/event_server_cli.py @@ -1,11 +1,9 @@ import os -import sys import signal import datetime import subprocess import socket import json -import platform import getpass import atexit import time @@ -13,12 +11,14 @@ import uuid import ftrack_api import pymongo +from openpype.client.mongo import ( + OpenPypeMongoConnection, + validate_mongo_connection, +) from openpype.lib import ( get_openpype_execute_args, - OpenPypeMongoConnection, get_openpype_version, get_build_version, - validate_mongo_connection ) from openpype_modules.ftrack import FTRACK_MODULE_DIR from openpype_modules.ftrack.lib import credentials diff --git a/openpype/modules/ftrack/ftrack_server/lib.py b/openpype/modules/ftrack/ftrack_server/lib.py index 5c6d6352d2..3da1e7c7f0 100644 --- a/openpype/modules/ftrack/ftrack_server/lib.py +++ b/openpype/modules/ftrack/ftrack_server/lib.py @@ -24,7 +24,7 @@ except ImportError: from ftrack_api._weakref import WeakMethod from openpype_modules.ftrack.lib import get_ftrack_event_mongo_info -from openpype.lib import OpenPypeMongoConnection +from openpype.client import OpenPypeMongoConnection from openpype.api import Logger TOPIC_STATUS_SERVER = "openpype.event.server.status" diff --git a/openpype/modules/ftrack/scripts/sub_event_storer.py b/openpype/modules/ftrack/scripts/sub_event_storer.py index 946ecbff79..204cce89e8 100644 --- a/openpype/modules/ftrack/scripts/sub_event_storer.py +++ b/openpype/modules/ftrack/scripts/sub_event_storer.py @@ -6,6 +6,8 @@ import socket import pymongo import ftrack_api + +from openpype.client import OpenPypeMongoConnection from openpype_modules.ftrack.ftrack_server.ftrack_server import FtrackServer from openpype_modules.ftrack.ftrack_server.lib import ( SocketSession, @@ -15,7 +17,6 @@ from openpype_modules.ftrack.ftrack_server.lib import ( ) from openpype_modules.ftrack.lib import get_ftrack_event_mongo_info from openpype.lib import ( - OpenPypeMongoConnection, get_openpype_version, get_build_version ) diff --git a/openpype/modules/slack/plugins/publish/integrate_slack_api.py b/openpype/modules/slack/plugins/publish/integrate_slack_api.py index 10bde7d4c0..c3b288f0cd 100644 --- a/openpype/modules/slack/plugins/publish/integrate_slack_api.py +++ b/openpype/modules/slack/plugins/publish/integrate_slack_api.py @@ -4,8 +4,8 @@ import pyblish.api import copy from datetime import datetime +from openpype.client import OpenPypeMongoConnection from openpype.lib.plugin_tools import prepare_template_data -from openpype.lib import OpenPypeMongoConnection class IntegrateSlackAPI(pyblish.api.InstancePlugin): diff --git a/openpype/pipeline/mongodb.py b/openpype/pipeline/mongodb.py index dab5bb9e13..be2b67a5e7 100644 --- a/openpype/pipeline/mongodb.py +++ b/openpype/pipeline/mongodb.py @@ -5,6 +5,8 @@ import logging import pymongo from uuid import uuid4 +from openpype.client import OpenPypeMongoConnection + from . import schema @@ -156,8 +158,6 @@ class AvalonMongoDB: @property def mongo_client(self): - from openpype.lib import OpenPypeMongoConnection - return OpenPypeMongoConnection.get_mongo_client() @property diff --git a/openpype/settings/handlers.py b/openpype/settings/handlers.py index c99fc6080b..2bcc2e06dd 100644 --- a/openpype/settings/handlers.py +++ b/openpype/settings/handlers.py @@ -7,6 +7,7 @@ from abc import ABCMeta, abstractmethod import six import openpype.version +from openpype.client.mongo import OpenPypeMongoConnection from .constants import ( GLOBAL_SETTINGS_KEY, @@ -337,7 +338,6 @@ class MongoSettingsHandler(SettingsHandler): def __init__(self): # Get mongo connection - from openpype.lib import OpenPypeMongoConnection from openpype.pipeline import AvalonMongoDB settings_collection = OpenPypeMongoConnection.get_mongo_client() From dc6b02c234c9862dbcf60139e7f5463b919d3e20 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Mon, 18 Jul 2022 11:20:49 +0200 Subject: [PATCH 04/31] mongo.py in openpype.lib is reusing functionality from openpype.client for backwards compatibility --- openpype/lib/mongo.py | 211 ++++-------------------------------------- 1 file changed, 18 insertions(+), 193 deletions(-) diff --git a/openpype/lib/mongo.py b/openpype/lib/mongo.py index c08e76c75c..80487f317d 100644 --- a/openpype/lib/mongo.py +++ b/openpype/lib/mongo.py @@ -1,206 +1,31 @@ -import os -import sys -import time -import logging -import pymongo -import certifi - -if sys.version_info[0] == 2: - from urlparse import urlparse, parse_qs -else: - from urllib.parse import urlparse, parse_qs - - -class MongoEnvNotSet(Exception): - pass - - -def _decompose_url(url): - """Decompose mongo url to basic components. - - Used for creation of MongoHandler which expect mongo url components as - separated kwargs. Components are at the end not used as we're setting - connection directly this is just a dumb components for MongoHandler - validation pass. - """ - # Use first url from passed url - # - this is because it is possible to pass multiple urls for multiple - # replica sets which would crash on urlparse otherwise - # - please don't use comma in username of password - url = url.split(",")[0] - components = { - "scheme": None, - "host": None, - "port": None, - "username": None, - "password": None, - "auth_db": None - } - - result = urlparse(url) - if result.scheme is None: - _url = "mongodb://{}".format(url) - result = urlparse(_url) - - components["scheme"] = result.scheme - components["host"] = result.hostname - try: - components["port"] = result.port - except ValueError: - raise RuntimeError("invalid port specified") - components["username"] = result.username - components["password"] = result.password - - try: - components["auth_db"] = parse_qs(result.query)['authSource'][0] - except KeyError: - # no auth db provided, mongo will use the one we are connecting to - pass - - return components +from openpype.client.mongo import ( + MongoEnvNotSet, + OpenPypeMongoConnection, +) def get_default_components(): - mongo_url = os.environ.get("OPENPYPE_MONGO") - if mongo_url is None: - raise MongoEnvNotSet( - "URL for Mongo logging connection is not set." - ) - return _decompose_url(mongo_url) + from openpype.client.mongo import get_default_components + + return get_default_components() def should_add_certificate_path_to_mongo_url(mongo_url): - """Check if should add ca certificate to mongo url. + from openpype.client.mongo import should_add_certificate_path_to_mongo_url - Since 30.9.2021 cloud mongo requires newer certificates that are not - available on most of workstation. This adds path to certifi certificate - which is valid for it. To add the certificate path url must have scheme - 'mongodb+srv' or has 'ssl=true' or 'tls=true' in url query. - """ - parsed = urlparse(mongo_url) - query = parse_qs(parsed.query) - lowered_query_keys = set(key.lower() for key in query.keys()) - add_certificate = False - # Check if url 'ssl' or 'tls' are set to 'true' - for key in ("ssl", "tls"): - if key in query and "true" in query["ssl"]: - add_certificate = True - break - - # Check if url contains 'mongodb+srv' - if not add_certificate and parsed.scheme == "mongodb+srv": - add_certificate = True - - # Check if url does already contain certificate path - if add_certificate and "tlscafile" in lowered_query_keys: - add_certificate = False - - return add_certificate + return should_add_certificate_path_to_mongo_url(mongo_url) def validate_mongo_connection(mongo_uri): - """Check if provided mongodb URL is valid. + from openpype.client.mongo import validate_mongo_connection - Args: - mongo_uri (str): URL to validate. - - Raises: - ValueError: When port in mongo uri is not valid. - pymongo.errors.InvalidURI: If passed mongo is invalid. - pymongo.errors.ServerSelectionTimeoutError: If connection timeout - passed so probably couldn't connect to mongo server. - - """ - client = OpenPypeMongoConnection.create_connection( - mongo_uri, retry_attempts=1 - ) - client.close() + return validate_mongo_connection(mongo_uri) -class OpenPypeMongoConnection: - """Singleton MongoDB connection. - - Keeps MongoDB connections by url. - """ - mongo_clients = {} - log = logging.getLogger("OpenPypeMongoConnection") - - @staticmethod - def get_default_mongo_url(): - return os.environ["OPENPYPE_MONGO"] - - @classmethod - def get_mongo_client(cls, mongo_url=None): - if mongo_url is None: - mongo_url = cls.get_default_mongo_url() - - connection = cls.mongo_clients.get(mongo_url) - if connection: - # Naive validation of existing connection - try: - connection.server_info() - with connection.start_session(): - pass - except Exception: - connection = None - - if not connection: - cls.log.debug("Creating mongo connection to {}".format(mongo_url)) - connection = cls.create_connection(mongo_url) - cls.mongo_clients[mongo_url] = connection - - return connection - - @classmethod - def create_connection(cls, mongo_url, timeout=None, retry_attempts=None): - parsed = urlparse(mongo_url) - # Force validation of scheme - if parsed.scheme not in ["mongodb", "mongodb+srv"]: - raise pymongo.errors.InvalidURI(( - "Invalid URI scheme:" - " URI must begin with 'mongodb://' or 'mongodb+srv://'" - )) - - if timeout is None: - timeout = int(os.environ.get("AVALON_TIMEOUT") or 1000) - - kwargs = { - "serverSelectionTimeoutMS": timeout - } - if should_add_certificate_path_to_mongo_url(mongo_url): - kwargs["ssl_ca_certs"] = certifi.where() - - mongo_client = pymongo.MongoClient(mongo_url, **kwargs) - - if retry_attempts is None: - retry_attempts = 3 - - elif not retry_attempts: - retry_attempts = 1 - - last_exc = None - valid = False - t1 = time.time() - for attempt in range(1, retry_attempts + 1): - try: - mongo_client.server_info() - with mongo_client.start_session(): - pass - valid = True - break - - except Exception as exc: - last_exc = exc - if attempt < retry_attempts: - cls.log.warning( - "Attempt {} failed. Retrying... ".format(attempt) - ) - time.sleep(1) - - if not valid: - raise last_exc - - cls.log.info("Connected to {}, delay {:.3f}s".format( - mongo_url, time.time() - t1 - )) - return mongo_client +__all__ = ( + "MongoEnvNotSet", + "OpenPypeMongoConnection", + "get_default_components", + "should_add_certificate_path_to_mongo_url", + "validate_mongo_connection", +) From 69ad12d61dc93b66ad9620496a4a1776f9f92306 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Mon, 18 Jul 2022 11:21:30 +0200 Subject: [PATCH 05/31] made '_get_project_connection' function temporarily public for create, update and remove --- openpype/client/entities.py | 60 +++++++++++++++++++++++-------------- 1 file changed, 37 insertions(+), 23 deletions(-) diff --git a/openpype/client/entities.py b/openpype/client/entities.py index 1c32632915..aacc3a2304 100644 --- a/openpype/client/entities.py +++ b/openpype/client/entities.py @@ -20,7 +20,21 @@ def _get_project_database(): return OpenPypeMongoConnection.get_mongo_client()[db_name] -def _get_project_connection(project_name): +def get_project_connection(project_name): + """Direct access to mongo collection. + + We're trying to avoid using direct access to mongo. This should be used + only for Create, Update and Remove operations until there are implemented + api calls for that. + + Args: + project_name(str): Project name for which collection should be + returned. + + Returns: + pymongo.Collection: Collection realated to passed project. + """ + if not project_name: raise ValueError("Invalid project name {}".format(str(project_name))) return _get_project_database()[project_name] @@ -93,7 +107,7 @@ def get_project(project_name, active=True, inactive=False, fields=None): {"data.active": False}, ] - conn = _get_project_connection(project_name) + conn = get_project_connection(project_name) return conn.find_one(query_filter, _prepare_fields(fields)) @@ -108,7 +122,7 @@ def get_whole_project(project_name): project collection. """ - conn = _get_project_connection(project_name) + conn = get_project_connection(project_name) return conn.find({}) @@ -131,7 +145,7 @@ def get_asset_by_id(project_name, asset_id, fields=None): return None query_filter = {"type": "asset", "_id": asset_id} - conn = _get_project_connection(project_name) + conn = get_project_connection(project_name) return conn.find_one(query_filter, _prepare_fields(fields)) @@ -153,7 +167,7 @@ def get_asset_by_name(project_name, asset_name, fields=None): return None query_filter = {"type": "asset", "name": asset_name} - conn = _get_project_connection(project_name) + conn = get_project_connection(project_name) return conn.find_one(query_filter, _prepare_fields(fields)) @@ -222,7 +236,7 @@ def _get_assets( return [] query_filter["data.visualParent"] = {"$in": parent_ids} - conn = _get_project_connection(project_name) + conn = get_project_connection(project_name) return conn.find(query_filter, _prepare_fields(fields)) @@ -319,7 +333,7 @@ def get_asset_ids_with_subsets(project_name, asset_ids=None): return [] subset_query["parent"] = {"$in": asset_ids} - conn = _get_project_connection(project_name) + conn = get_project_connection(project_name) result = conn.aggregate([ { "$match": subset_query @@ -359,7 +373,7 @@ def get_subset_by_id(project_name, subset_id, fields=None): return None query_filters = {"type": "subset", "_id": subset_id} - conn = _get_project_connection(project_name) + conn = get_project_connection(project_name) return conn.find_one(query_filters, _prepare_fields(fields)) @@ -390,7 +404,7 @@ def get_subset_by_name(project_name, subset_name, asset_id, fields=None): "name": subset_name, "parent": asset_id } - conn = _get_project_connection(project_name) + conn = get_project_connection(project_name) return conn.find_one(query_filters, _prepare_fields(fields)) @@ -463,7 +477,7 @@ def get_subsets( return [] query_filter["$or"] = or_query - conn = _get_project_connection(project_name) + conn = get_project_connection(project_name) return conn.find(query_filter, _prepare_fields(fields)) @@ -487,7 +501,7 @@ def get_subset_families(project_name, subset_ids=None): return set() subset_filter["_id"] = {"$in": list(subset_ids)} - conn = _get_project_connection(project_name) + conn = get_project_connection(project_name) result = list(conn.aggregate([ {"$match": subset_filter}, {"$project": { @@ -525,7 +539,7 @@ def get_version_by_id(project_name, version_id, fields=None): "type": {"$in": ["version", "hero_version"]}, "_id": version_id } - conn = _get_project_connection(project_name) + conn = get_project_connection(project_name) return conn.find_one(query_filter, _prepare_fields(fields)) @@ -548,7 +562,7 @@ def get_version_by_name(project_name, version, subset_id, fields=None): if not subset_id: return None - conn = _get_project_connection(project_name) + conn = get_project_connection(project_name) query_filter = { "type": "version", "parent": subset_id, @@ -602,7 +616,7 @@ def _get_versions( else: query_filter["name"] = {"$in": versions} - conn = _get_project_connection(project_name) + conn = get_project_connection(project_name) return conn.find(query_filter, _prepare_fields(fields)) @@ -760,7 +774,7 @@ def get_output_link_versions(project_name, version_id, fields=None): if not version_id: return [] - conn = _get_project_connection(project_name) + conn = get_project_connection(project_name) # Does make sense to look for hero versions? query_filter = { "type": "version", @@ -825,7 +839,7 @@ def get_last_versions(project_name, subset_ids, fields=None): {"$group": group_item} ] - conn = _get_project_connection(project_name) + conn = get_project_connection(project_name) aggregate_result = conn.aggregate(aggregation_pipeline) if limit_query: output = {} @@ -943,7 +957,7 @@ def get_representation_by_id(project_name, representation_id, fields=None): if representation_id is not None: query_filter["_id"] = _convert_id(representation_id) - conn = _get_project_connection(project_name) + conn = get_project_connection(project_name) return conn.find_one(query_filter, _prepare_fields(fields)) @@ -976,7 +990,7 @@ def get_representation_by_name( "parent": version_id } - conn = _get_project_connection(project_name) + conn = get_project_connection(project_name) return conn.find_one(query_filter, _prepare_fields(fields)) @@ -1039,7 +1053,7 @@ def _get_representations( return [] query_filter["$or"] = or_query - conn = _get_project_connection(project_name) + conn = get_project_connection(project_name) return conn.find(query_filter, _prepare_fields(fields)) @@ -1250,7 +1264,7 @@ def get_thumbnail_id_from_source(project_name, src_type, src_id): query_filter = {"_id": _convert_id(src_id)} - conn = _get_project_connection(project_name) + conn = get_project_connection(project_name) src_doc = conn.find_one(query_filter, {"data.thumbnail_id"}) if src_doc: return src_doc.get("data", {}).get("thumbnail_id") @@ -1282,7 +1296,7 @@ def get_thumbnails(project_name, thumbnail_ids, fields=None): "type": "thumbnail", "_id": {"$in": thumbnail_ids} } - conn = _get_project_connection(project_name) + conn = get_project_connection(project_name) return conn.find(query_filter, _prepare_fields(fields)) @@ -1303,7 +1317,7 @@ def get_thumbnail(project_name, thumbnail_id, fields=None): if not thumbnail_id: return None query_filter = {"type": "thumbnail", "_id": _convert_id(thumbnail_id)} - conn = _get_project_connection(project_name) + conn = get_project_connection(project_name) return conn.find_one(query_filter, _prepare_fields(fields)) @@ -1334,7 +1348,7 @@ def get_workfile_info( "task_name": task_name, "filename": filename } - conn = _get_project_connection(project_name) + conn = get_project_connection(project_name) return conn.find_one(query_filter, _prepare_fields(fields)) From b23d89a149efb105db1f7ff8aa1c71726b7782b8 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Mon, 18 Jul 2022 11:21:59 +0200 Subject: [PATCH 06/31] use client function in settings handlers instead of AvalonMongoDB --- openpype/settings/handlers.py | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/openpype/settings/handlers.py b/openpype/settings/handlers.py index 2bcc2e06dd..15ae2351fd 100644 --- a/openpype/settings/handlers.py +++ b/openpype/settings/handlers.py @@ -8,6 +8,7 @@ import six import openpype.version from openpype.client.mongo import OpenPypeMongoConnection +from openpype.client.entities import get_project_connection, get_project from .constants import ( GLOBAL_SETTINGS_KEY, @@ -338,8 +339,6 @@ class MongoSettingsHandler(SettingsHandler): def __init__(self): # Get mongo connection - from openpype.pipeline import AvalonMongoDB - settings_collection = OpenPypeMongoConnection.get_mongo_client() self._anatomy_keys = None @@ -362,7 +361,6 @@ class MongoSettingsHandler(SettingsHandler): self.collection_name = collection_name self.collection = settings_collection[database_name][collection_name] - self.avalon_db = AvalonMongoDB() self.system_settings_cache = CacheValues() self.project_settings_cache = collections.defaultdict(CacheValues) @@ -607,16 +605,14 @@ class MongoSettingsHandler(SettingsHandler): new_data = data_cache.data_copy() # Prepare avalon project document - collection = self.avalon_db.database[project_name] - project_doc = collection.find_one({ - "type": "project" - }) + project_doc = get_project(project_name) if not project_doc: raise ValueError(( "Project document of project \"{}\" does not exists." " Create project first." ).format(project_name)) + collection = get_project_connection(project_name) # Project's data update_dict_data = {} project_doc_data = project_doc.get("data") or {} @@ -1145,8 +1141,7 @@ class MongoSettingsHandler(SettingsHandler): document, version ) else: - collection = self.avalon_db.database[project_name] - project_doc = collection.find_one({"type": "project"}) + project_doc = get_project(project_name) self.project_anatomy_cache[project_name].update_data( self.project_doc_to_anatomy_data(project_doc), self._current_version From d4a29c39aaae2c383ea9b887b294eab91352901d Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Mon, 18 Jul 2022 11:40:48 +0200 Subject: [PATCH 07/31] added deprecation warning to functions --- openpype/lib/mongo.py | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/openpype/lib/mongo.py b/openpype/lib/mongo.py index 80487f317d..bb2ee6016a 100644 --- a/openpype/lib/mongo.py +++ b/openpype/lib/mongo.py @@ -1,21 +1,51 @@ +import warnings +import functools from openpype.client.mongo import ( MongoEnvNotSet, OpenPypeMongoConnection, ) +class MongoDeprecatedWarning(DeprecationWarning): + pass + + +def mongo_deprecated(func): + """Mark functions as deprecated. + + It will result in a warning being emitted when the function is used. + """ + + @functools.wraps(func) + def new_func(*args, **kwargs): + warnings.simplefilter("always", MongoDeprecatedWarning) + warnings.warn( + ( + "Call to deprecated function '{}'." + " Function was moved to 'openpype.client.mongo'." + ).format(func.__name__), + category=MongoDeprecatedWarning, + stacklevel=2 + ) + return func(*args, **kwargs) + return new_func + + +@mongo_deprecated def get_default_components(): from openpype.client.mongo import get_default_components return get_default_components() +@mongo_deprecated def should_add_certificate_path_to_mongo_url(mongo_url): from openpype.client.mongo import should_add_certificate_path_to_mongo_url return should_add_certificate_path_to_mongo_url(mongo_url) +@mongo_deprecated def validate_mongo_connection(mongo_uri): from openpype.client.mongo import validate_mongo_connection From 5e31967310836d0ef1ad3a1590fa1f3c7d9c682d Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Mon, 25 Jul 2022 10:53:07 +0200 Subject: [PATCH 08/31] implemented helper function to escape html symbols --- openpype/tools/utils/lib.py | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/openpype/tools/utils/lib.py b/openpype/tools/utils/lib.py index ea1362945f..2169cf8ef1 100644 --- a/openpype/tools/utils/lib.py +++ b/openpype/tools/utils/lib.py @@ -37,6 +37,19 @@ def center_window(window): window.move(geo.topLeft()) +def html_escape(text): + """Basic escape of html syntax symbols in text.""" + + return ( + text + .replace("&", "&") + .replace("<", "<") + .replace(">", ">") + .replace('"', """) + .replace("'", "'") + ) + + def set_style_property(widget, property_name, property_value): """Set widget's property that may affect style. From 307612d86878f3d098cc3d5c90f9d3464fb87b5f Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Mon, 25 Jul 2022 10:53:24 +0200 Subject: [PATCH 09/31] replace implemented functions instead of 'html' --- openpype/tools/publisher/publish_report_viewer/model.py | 4 ++-- openpype/tools/publisher/widgets/card_view_widgets.py | 4 ++-- openpype/tools/publisher/widgets/list_view_widgets.py | 6 +++--- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/openpype/tools/publisher/publish_report_viewer/model.py b/openpype/tools/publisher/publish_report_viewer/model.py index bd03376c55..704feeb4bd 100644 --- a/openpype/tools/publisher/publish_report_viewer/model.py +++ b/openpype/tools/publisher/publish_report_viewer/model.py @@ -1,9 +1,9 @@ import uuid -import html from Qt import QtCore, QtGui import pyblish.api +from openpype.tools.utils.lib import html_escape from .constants import ( ITEM_ID_ROLE, ITEM_IS_GROUP_ROLE, @@ -46,7 +46,7 @@ class InstancesModel(QtGui.QStandardItemModel): all_removed = True for instance_item in instance_items: item = QtGui.QStandardItem(instance_item.label) - instance_label = html.escape(instance_item.label) + instance_label = html_escape(instance_item.label) item.setData(instance_label, ITEM_LABEL_ROLE) item.setData(instance_item.errored, ITEM_ERRORED_ROLE) item.setData(instance_item.id, ITEM_ID_ROLE) diff --git a/openpype/tools/publisher/widgets/card_view_widgets.py b/openpype/tools/publisher/widgets/card_view_widgets.py index bd591138f4..fa391f4ba0 100644 --- a/openpype/tools/publisher/widgets/card_view_widgets.py +++ b/openpype/tools/publisher/widgets/card_view_widgets.py @@ -22,13 +22,13 @@ Only one item can be selected at a time. import re import collections -import html from Qt import QtWidgets, QtCore from openpype.widgets.nice_checkbox import NiceCheckbox from openpype.tools.utils import BaseClickableFrame +from openpype.tools.utils.lib import html_escape from .widgets import ( AbstractInstanceView, ContextWarningLabel, @@ -308,7 +308,7 @@ class InstanceCardWidget(CardWidget): self._last_variant = variant self._last_subset_name = subset_name # Make `variant` bold - label = html.escape(self.instance.label) + label = html_escape(self.instance.label) found_parts = set(re.findall(variant, label, re.IGNORECASE)) if found_parts: for part in found_parts: diff --git a/openpype/tools/publisher/widgets/list_view_widgets.py b/openpype/tools/publisher/widgets/list_view_widgets.py index 3e4fd5b72d..6e31ba635b 100644 --- a/openpype/tools/publisher/widgets/list_view_widgets.py +++ b/openpype/tools/publisher/widgets/list_view_widgets.py @@ -23,12 +23,12 @@ selection can be enabled disabled using checkbox or keyboard key presses: ``` """ import collections -import html from Qt import QtWidgets, QtCore, QtGui from openpype.style import get_objected_colors from openpype.widgets.nice_checkbox import NiceCheckbox +from openpype.tools.utils.lib import html_escape from .widgets import AbstractInstanceView from ..constants import ( INSTANCE_ID_ROLE, @@ -114,7 +114,7 @@ class InstanceListItemWidget(QtWidgets.QWidget): self.instance = instance - instance_label = html.escape(instance.label) + instance_label = html_escape(instance.label) subset_name_label = QtWidgets.QLabel(instance_label, self) subset_name_label.setObjectName("ListViewSubsetName") @@ -181,7 +181,7 @@ class InstanceListItemWidget(QtWidgets.QWidget): # Check subset name label = self.instance.label if label != self._instance_label_widget.text(): - self._instance_label_widget.setText(html.escape(label)) + self._instance_label_widget.setText(html_escape(label)) # Check active state self.set_active(self.instance["active"]) # Check valid states From 7905d18e6713b1307c2ea33fc3f4cb07577999b0 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Mon, 25 Jul 2022 12:40:03 +0200 Subject: [PATCH 10/31] check if instance have representations as first thing --- openpype/plugins/publish/extract_thumbnail.py | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/openpype/plugins/publish/extract_thumbnail.py b/openpype/plugins/publish/extract_thumbnail.py index 7933595b89..789b6c75bc 100644 --- a/openpype/plugins/publish/extract_thumbnail.py +++ b/openpype/plugins/publish/extract_thumbnail.py @@ -29,7 +29,17 @@ class ExtractThumbnail(pyblish.api.InstancePlugin): ffmpeg_args = None def process(self, instance): - self.log.info("subset {}".format(instance.data['subset'])) + subset_name = instance.data["subset"] + instance_repres = instance.data.get("representations") + if not instance_repres: + self.log.debug(( + "Instance {} does not have representations. Skipping" + ).format(subset_name)) + return + + self.log.info( + "Processing instance with subset name {}".format(subset_name) + ) # skip crypto passes. # TODO: This is just a quick fix and has its own side-effects - it is From 89705a69d1df6ba69792290d56220e2f2bd317f7 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Mon, 25 Jul 2022 12:43:10 +0200 Subject: [PATCH 11/31] move instance review key check earlier and move the logic to method --- openpype/plugins/publish/extract_thumbnail.py | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/openpype/plugins/publish/extract_thumbnail.py b/openpype/plugins/publish/extract_thumbnail.py index 789b6c75bc..839618bcdd 100644 --- a/openpype/plugins/publish/extract_thumbnail.py +++ b/openpype/plugins/publish/extract_thumbnail.py @@ -41,6 +41,11 @@ class ExtractThumbnail(pyblish.api.InstancePlugin): "Processing instance with subset name {}".format(subset_name) ) + # Skip if instance does not have review + if not self._is_review_instance(instance): + self.log.info("Skipping - no review set on instance.") + return + # skip crypto passes. # TODO: This is just a quick fix and has its own side-effects - it is # affecting every subset name with `crypto` in its name. @@ -51,11 +56,6 @@ class ExtractThumbnail(pyblish.api.InstancePlugin): self.log.info("Skipping crypto passes.") return - # Skip if review not set. - if not instance.data.get("review", True): - self.log.info("Skipping - no review set on instance.") - return - if self._already_has_thumbnail(instance): self.log.info("Thumbnail representation already present.") return @@ -116,6 +116,13 @@ class ExtractThumbnail(pyblish.api.InstancePlugin): # There is no need to create more then one thumbnail break + def _is_review_instance(self, instance): + # TODO: We should probably handle "not creating" of thumbnail + # other way then checking for "review" key on instance data? + if instance.data.get("review", True): + return True + return False + def _already_has_thumbnail(self, instance): for repre in instance.data.get("representations", []): self.log.info("repre {}".format(repre)) From f82c97dc6aae62007de239c30fe8304b561a2a3b Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Mon, 25 Jul 2022 12:45:44 +0200 Subject: [PATCH 12/31] check existing thumbnail before crypto check --- openpype/plugins/publish/extract_thumbnail.py | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/openpype/plugins/publish/extract_thumbnail.py b/openpype/plugins/publish/extract_thumbnail.py index 839618bcdd..51624a3cc7 100644 --- a/openpype/plugins/publish/extract_thumbnail.py +++ b/openpype/plugins/publish/extract_thumbnail.py @@ -41,11 +41,16 @@ class ExtractThumbnail(pyblish.api.InstancePlugin): "Processing instance with subset name {}".format(subset_name) ) - # Skip if instance does not have review + # Skip if instance have 'review' key in data set to 'False' if not self._is_review_instance(instance): self.log.info("Skipping - no review set on instance.") return + # Check if already has thumbnail created + if self._already_has_thumbnail(instance_repres): + self.log.info("Thumbnail representation already present.") + return + # skip crypto passes. # TODO: This is just a quick fix and has its own side-effects - it is # affecting every subset name with `crypto` in its name. @@ -56,9 +61,6 @@ class ExtractThumbnail(pyblish.api.InstancePlugin): self.log.info("Skipping crypto passes.") return - if self._already_has_thumbnail(instance): - self.log.info("Thumbnail representation already present.") - return filtered_repres = self._get_filtered_repres(instance) for repre in filtered_repres: @@ -123,12 +125,11 @@ class ExtractThumbnail(pyblish.api.InstancePlugin): return True return False - def _already_has_thumbnail(self, instance): - for repre in instance.data.get("representations", []): + def _already_has_thumbnail(self, repres): + for repre in repres: self.log.info("repre {}".format(repre)) if repre["name"] == "thumbnail": return True - return False def _get_filtered_repres(self, instance): From 239414ffff9d9ec0e03c6b5239e7206317d5b9fa Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Mon, 25 Jul 2022 12:49:52 +0200 Subject: [PATCH 13/31] try to create thumbnail from all filtered representations --- openpype/plugins/publish/extract_thumbnail.py | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/openpype/plugins/publish/extract_thumbnail.py b/openpype/plugins/publish/extract_thumbnail.py index 51624a3cc7..cb1af12586 100644 --- a/openpype/plugins/publish/extract_thumbnail.py +++ b/openpype/plugins/publish/extract_thumbnail.py @@ -63,6 +63,14 @@ class ExtractThumbnail(pyblish.api.InstancePlugin): filtered_repres = self._get_filtered_repres(instance) + if not filtered_repres: + self.log.info(( + "Instance don't have representations" + " that can be used as source for thumbnail. Skipping" + )) + return + + thumbnail_created = False for repre in filtered_repres: repre_files = repre["files"] if not isinstance(repre_files, (list, tuple)): @@ -81,7 +89,6 @@ class ExtractThumbnail(pyblish.api.InstancePlugin): jpeg_file = filename + "jpg" full_output_path = os.path.join(stagingdir, jpeg_file) - thumbnail_created = False # Try to use FFMPEG if OIIO is not supported (for cases when # oiiotool isn't available) if not is_oiio_supported(): @@ -96,10 +103,9 @@ class ExtractThumbnail(pyblish.api.InstancePlugin): self.log.info("Converting with FFMPEG because input can't be read by OIIO.") # noqa thumbnail_created = self.create_thumbnail_ffmpeg(full_input_path, full_output_path) # noqa - # Skip the rest of the process if the thumbnail wasn't created + # Skip representation and try next one if wasn't created if not thumbnail_created: - self.log.warning("Thumbanil has not been created.") - return + continue new_repre = { "name": "thumbnail", @@ -118,6 +124,9 @@ class ExtractThumbnail(pyblish.api.InstancePlugin): # There is no need to create more then one thumbnail break + if not thumbnail_created: + self.log.warning("Thumbanil has not been created.") + def _is_review_instance(self, instance): # TODO: We should probably handle "not creating" of thumbnail # other way then checking for "review" key on instance data? From 6a018364b2961d21dab66657b12613838ba54750 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Mon, 25 Jul 2022 12:53:27 +0200 Subject: [PATCH 14/31] create custom staging dir for thumbnail representation --- openpype/plugins/publish/extract_thumbnail.py | 28 +++++++++++-------- 1 file changed, 16 insertions(+), 12 deletions(-) diff --git a/openpype/plugins/publish/extract_thumbnail.py b/openpype/plugins/publish/extract_thumbnail.py index cb1af12586..2715aa4db4 100644 --- a/openpype/plugins/publish/extract_thumbnail.py +++ b/openpype/plugins/publish/extract_thumbnail.py @@ -1,4 +1,5 @@ import os +import tempfile import pyblish.api from openpype.lib import ( @@ -8,8 +9,6 @@ from openpype.lib import ( run_subprocess, path_to_subprocess_arg, - - execute, ) @@ -57,11 +56,10 @@ class ExtractThumbnail(pyblish.api.InstancePlugin): # This must be solved properly, maybe using tags on # representation that can be determined much earlier and # with better precision. - if 'crypto' in instance.data['subset'].lower(): + if "crypto" in subset_name.lower(): self.log.info("Skipping crypto passes.") return - filtered_repres = self._get_filtered_repres(instance) if not filtered_repres: self.log.info(( @@ -70,6 +68,15 @@ class ExtractThumbnail(pyblish.api.InstancePlugin): )) return + # Create temp directory for thumbnail + # - this is to avoid "override" of source file + dst_staging = tempfile.mkdtemp(prefix="pyblish_tmp_") + self.log.debug( + "Create temp directory {} for thumbnail".formap(dst_staging) + ) + # Store new staging to cleanup paths + instance.context.data["cleanupFullPaths"].append(dst_staging) + thumbnail_created = False for repre in filtered_repres: repre_files = repre["files"] @@ -79,15 +86,12 @@ class ExtractThumbnail(pyblish.api.InstancePlugin): file_index = int(float(len(repre_files)) * 0.5) input_file = repre_files[file_index] - stagingdir = os.path.normpath(repre["stagingDir"]) - - full_input_path = os.path.join(stagingdir, input_file) + src_staging = os.path.normpath(repre["stagingDir"]) + full_input_path = os.path.join(src_staging, input_file) self.log.info("input {}".format(full_input_path)) filename = os.path.splitext(input_file)[0] - if not filename.endswith('.'): - filename += "." - jpeg_file = filename + "jpg" - full_output_path = os.path.join(stagingdir, jpeg_file) + jpeg_file = filename + ".jpg" + full_output_path = os.path.join(dst_staging, jpeg_file) # Try to use FFMPEG if OIIO is not supported (for cases when # oiiotool isn't available) @@ -111,7 +115,7 @@ class ExtractThumbnail(pyblish.api.InstancePlugin): "name": "thumbnail", "ext": "jpg", "files": jpeg_file, - "stagingDir": stagingdir, + "stagingDir": dst_staging, "thumbnail": True, "tags": ["thumbnail"] } From d1987eed02ba4ca842cd820ef0947adbb4240d2b Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Mon, 25 Jul 2022 12:53:58 +0200 Subject: [PATCH 15/31] removed unneeded f string --- openpype/plugins/publish/extract_thumbnail.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/openpype/plugins/publish/extract_thumbnail.py b/openpype/plugins/publish/extract_thumbnail.py index 2715aa4db4..d944c341e5 100644 --- a/openpype/plugins/publish/extract_thumbnail.py +++ b/openpype/plugins/publish/extract_thumbnail.py @@ -167,12 +167,12 @@ class ExtractThumbnail(pyblish.api.InstancePlugin): def create_thumbnail_oiio(self, src_path, dst_path): self.log.info("outputting {}".format(dst_path)) oiio_tool_path = get_oiio_tools_path() - oiio_cmd = [oiio_tool_path, "-a", - src_path, "-o", - dst_path - ] - subprocess_exr = " ".join(oiio_cmd) - self.log.info(f"running: {subprocess_exr}") + oiio_cmd = [ + oiio_tool_path, + "-a", src_path, + "-o", dst_path + ] + self.log.info("running: {}".format(" ".join(oiio_cmd))) try: run_subprocess(oiio_cmd, logger=self.log) return True From adc9b9303ba090dd1125f83b5d619306d230c5b9 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Mon, 25 Jul 2022 12:55:29 +0200 Subject: [PATCH 16/31] reduced order of thumbnail creation to 2 conditions --- openpype/plugins/publish/extract_thumbnail.py | 28 ++++++++++++------- 1 file changed, 18 insertions(+), 10 deletions(-) diff --git a/openpype/plugins/publish/extract_thumbnail.py b/openpype/plugins/publish/extract_thumbnail.py index d944c341e5..c1eee71376 100644 --- a/openpype/plugins/publish/extract_thumbnail.py +++ b/openpype/plugins/publish/extract_thumbnail.py @@ -78,6 +78,7 @@ class ExtractThumbnail(pyblish.api.InstancePlugin): instance.context.data["cleanupFullPaths"].append(dst_staging) thumbnail_created = False + oiio_supported = is_oiio_supported() for repre in filtered_repres: repre_files = repre["files"] if not isinstance(repre_files, (list, tuple)): @@ -93,19 +94,26 @@ class ExtractThumbnail(pyblish.api.InstancePlugin): jpeg_file = filename + ".jpg" full_output_path = os.path.join(dst_staging, jpeg_file) - # Try to use FFMPEG if OIIO is not supported (for cases when - # oiiotool isn't available) - if not is_oiio_supported(): - thumbnail_created = self.create_thumbnail_ffmpeg(full_input_path, full_output_path) # noqa - else: + if oiio_supported: + self.log.info("Trying to convert with OIIO") # If the input can read by OIIO then use OIIO method for # conversion otherwise use ffmpeg - self.log.info("Trying to convert with OIIO") # noqa - thumbnail_created = self.create_thumbnail_oiio(full_input_path, full_output_path) # noqa + thumbnail_created = self.create_thumbnail_oiio( + full_input_path, full_output_path + ) - if not thumbnail_created: - self.log.info("Converting with FFMPEG because input can't be read by OIIO.") # noqa - thumbnail_created = self.create_thumbnail_ffmpeg(full_input_path, full_output_path) # noqa + # Try to use FFMPEG if OIIO is not supported or for cases when + # oiiotool isn't available + if not thumbnail_created: + if oiio_supported: + self.log.info(( + "Converting with FFMPEG because input" + " can't be read by OIIO." + )) + + thumbnail_created = self.create_thumbnail_ffmpeg( + full_input_path, full_output_path + ) # Skip representation and try next one if wasn't created if not thumbnail_created: From d7917b1950c6d5cd79e2b4d297de99d8a9ce2c82 Mon Sep 17 00:00:00 2001 From: Allan Ihsan Date: Mon, 25 Jul 2022 15:30:03 +0300 Subject: [PATCH 17/31] Handle locked attributes for playblast capture. --- openpype/vendor/python/common/capture.py | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/openpype/vendor/python/common/capture.py b/openpype/vendor/python/common/capture.py index 71b86a5f1a..86c1c60e56 100644 --- a/openpype/vendor/python/common/capture.py +++ b/openpype/vendor/python/common/capture.py @@ -665,7 +665,10 @@ def _applied_camera_options(options, panel): _iteritems = getattr(options, "iteritems", options.items) for opt, value in _iteritems(): - _safe_setAttr(camera + "." + opt, value) + if cmds.getAttr(camera + "." + opt, lock=True): + continue + else: + _safe_setAttr(camera + "." + opt, value) try: yield @@ -673,7 +676,11 @@ def _applied_camera_options(options, panel): if old_options: _iteritems = getattr(old_options, "iteritems", old_options.items) for opt, value in _iteritems(): - _safe_setAttr(camera + "." + opt, value) + # + if cmds.getAttr(camera + "." + opt, lock=True): + continue + else: + _safe_setAttr(camera + "." + opt, value) @contextlib.contextmanager From 649ddf19c9ca49b5d0838045626a47c41ac17767 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Mon, 25 Jul 2022 14:40:53 +0200 Subject: [PATCH 18/31] query representation using query function --- openpype/plugins/publish/integrate.py | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/openpype/plugins/publish/integrate.py b/openpype/plugins/publish/integrate.py index 8532691e61..597ed9844e 100644 --- a/openpype/plugins/publish/integrate.py +++ b/openpype/plugins/publish/integrate.py @@ -10,6 +10,9 @@ from pymongo import DeleteMany, ReplaceOne, InsertOne, UpdateOne import pyblish.api import openpype.api +from openpype.client import ( + get_representations, +) from openpype.lib.profiles_filtering import filter_profiles from openpype.lib.file_transaction import FileTransaction from openpype.pipeline import legacy_io @@ -274,6 +277,8 @@ class IntegrateAsset(pyblish.api.InstancePlugin): return filtered_repres def register(self, instance, file_transactions, filtered_repres): + project_name = legacy_io.active_project() + instance_stagingdir = instance.data.get("stagingDir") if not instance_stagingdir: self.log.info(( @@ -295,13 +300,11 @@ class IntegrateAsset(pyblish.api.InstancePlugin): # Get existing representations (if any) existing_repres_by_name = { - repres["name"].lower(): repres for repres in legacy_io.find( - { - "parent": version["_id"], - "type": "representation" - }, - # Only care about id and name of existing representations - projection={"_id": True, "name": True} + repre_doc["name"].lower(): repre_doc + for repre_doc in get_representations( + project_name, + version_ids=version["_id"], + fields=["_id", "name"] ) } From e7c937bdc086e214fc05a5344b516257ee11751a Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Mon, 25 Jul 2022 14:42:02 +0200 Subject: [PATCH 19/31] use query function to query subset document --- openpype/plugins/publish/integrate.py | 31 +++++++++++++-------------- 1 file changed, 15 insertions(+), 16 deletions(-) diff --git a/openpype/plugins/publish/integrate.py b/openpype/plugins/publish/integrate.py index 597ed9844e..5ac5680cfa 100644 --- a/openpype/plugins/publish/integrate.py +++ b/openpype/plugins/publish/integrate.py @@ -12,6 +12,7 @@ import pyblish.api import openpype.api from openpype.client import ( get_representations, + get_subset_by_name, ) from openpype.lib.profiles_filtering import filter_profiles from openpype.lib.file_transaction import FileTransaction @@ -294,7 +295,7 @@ class IntegrateAsset(pyblish.api.InstancePlugin): template_name = self.get_template_name(instance) - subset, subset_writes = self.prepare_subset(instance) + subset, subset_writes = self.prepare_subset(instance, project_name) version, version_writes = self.prepare_version(instance, subset) instance.data["versionEntity"] = version @@ -429,17 +430,15 @@ class IntegrateAsset(pyblish.api.InstancePlugin): self.log.info("Registered {} representations" "".format(len(prepared_representations))) - def prepare_subset(self, instance): - asset = instance.data.get("assetEntity") + def prepare_subset(self, instance, project_name): + asset_doc = instance.data.get("assetEntity") subset_name = instance.data["subset"] self.log.debug("Subset: {}".format(subset_name)) # Get existing subset if it exists - subset = legacy_io.find_one({ - "type": "subset", - "parent": asset["_id"], - "name": subset_name - }) + subset_doc = get_subset_by_name( + project_name, subset_name, asset_doc["_id"] + ) # Define subset data data = { @@ -451,33 +450,33 @@ class IntegrateAsset(pyblish.api.InstancePlugin): data["subsetGroup"] = subset_group bulk_writes = [] - if subset is None: + if subset_doc is None: # Create a new subset self.log.info("Subset '%s' not found, creating ..." % subset_name) - subset = { + subset_doc = { "_id": ObjectId(), "schema": "openpype:subset-3.0", "type": "subset", "name": subset_name, "data": data, - "parent": asset["_id"] + "parent": asset_doc["_id"] } - bulk_writes.append(InsertOne(subset)) + bulk_writes.append(InsertOne(subset_doc)) else: # Update existing subset data with new data and set in database. # We also change the found subset in-place so we don't need to # re-query the subset afterwards - subset["data"].update(data) + subset_doc["data"].update(data) bulk_writes.append(UpdateOne( - {"type": "subset", "_id": subset["_id"]}, + {"type": "subset", "_id": subset_doc["_id"]}, {"$set": { - "data": subset["data"] + "data": subset_doc["data"] }} )) self.log.info("Prepared subset: {}".format(subset_name)) - return subset, bulk_writes + return subset_doc, bulk_writes def prepare_version(self, instance, subset): From 1a5258b2fe7d0dd2ec642b3648ba2e17707105b0 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Mon, 25 Jul 2022 14:43:08 +0200 Subject: [PATCH 20/31] use query function to get version document --- openpype/plugins/publish/integrate.py | 31 ++++++++++++++------------- 1 file changed, 16 insertions(+), 15 deletions(-) diff --git a/openpype/plugins/publish/integrate.py b/openpype/plugins/publish/integrate.py index 5ac5680cfa..6236724228 100644 --- a/openpype/plugins/publish/integrate.py +++ b/openpype/plugins/publish/integrate.py @@ -13,6 +13,7 @@ import openpype.api from openpype.client import ( get_representations, get_subset_by_name, + get_version_by_name, ) from openpype.lib.profiles_filtering import filter_profiles from openpype.lib.file_transaction import FileTransaction @@ -478,40 +479,40 @@ class IntegrateAsset(pyblish.api.InstancePlugin): self.log.info("Prepared subset: {}".format(subset_name)) return subset_doc, bulk_writes - def prepare_version(self, instance, subset): - + def prepare_version(self, instance, subset_doc, project_name): version_number = instance.data["version"] - version = { + version_doc = { "schema": "openpype:version-3.0", "type": "version", - "parent": subset["_id"], + "parent": subset_doc["_id"], "name": version_number, "data": self.create_version_data(instance) } - existing_version = legacy_io.find_one({ - 'type': 'version', - 'parent': subset["_id"], - 'name': version_number - }, projection={"_id": True}) + existing_version = get_version_by_name( + project_name, + version_number, + subset_doc["_id"], + fields=["_id"] + ) if existing_version: self.log.debug("Updating existing version ...") - version["_id"] = existing_version["_id"] + version_doc["_id"] = existing_version["_id"] else: self.log.debug("Creating new version ...") - version["_id"] = ObjectId() + version_doc["_id"] = ObjectId() bulk_writes = [ReplaceOne( - filter={"_id": version["_id"]}, - replacement=version, + filter={"_id": version_doc["_id"]}, + replacement=version_doc, upsert=True )] - self.log.info("Prepared version: v{0:03d}".format(version["name"])) + self.log.info("Prepared version: v{0:03d}".format(version_doc["name"])) - return version, bulk_writes + return version_doc, bulk_writes def prepare_representation(self, repre, template_name, From 441009fe5f8dbd5422b47624a23ef06ccac2af7d Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Mon, 25 Jul 2022 17:18:02 +0200 Subject: [PATCH 21/31] general: removing exclude family filtering from integrator --- openpype/plugins/publish/integrate.py | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/openpype/plugins/publish/integrate.py b/openpype/plugins/publish/integrate.py index 8532691e61..06909f0ec3 100644 --- a/openpype/plugins/publish/integrate.py +++ b/openpype/plugins/publish/integrate.py @@ -156,7 +156,7 @@ class IntegrateAsset(pyblish.api.InstancePlugin): "mvUsdOverride", "simpleUnrealTexture" ] - exclude_families = ["clip", "render.farm"] + default_template_name = "publish" # Representation context keys that should always be written to @@ -190,14 +190,6 @@ class IntegrateAsset(pyblish.api.InstancePlugin): ).format(instance.data["family"])) return - # Exclude instances that also contain families from exclude families - families = set(get_instance_families(instance)) - exclude = families & set(self.exclude_families) - if exclude: - self.log.debug("Instance not integrated due to exclude " - "families found: {}".format(", ".join(exclude))) - return - file_transactions = FileTransaction(log=self.log) try: self.register(instance, file_transactions, filtered_repres) From 66d283ecdf0ac881624929fc76cbe48e1398b2d3 Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Mon, 25 Jul 2022 17:34:05 +0200 Subject: [PATCH 22/31] nuke: add `farm` key to instance data if render.farm --- openpype/hosts/nuke/plugins/publish/precollect_instances.py | 1 + 1 file changed, 1 insertion(+) diff --git a/openpype/hosts/nuke/plugins/publish/precollect_instances.py b/openpype/hosts/nuke/plugins/publish/precollect_instances.py index 4b3b70fa12..b0da94c4ce 100644 --- a/openpype/hosts/nuke/plugins/publish/precollect_instances.py +++ b/openpype/hosts/nuke/plugins/publish/precollect_instances.py @@ -94,6 +94,7 @@ class PreCollectNukeInstances(pyblish.api.ContextPlugin): # Farm rendering self.log.info("flagged for farm render") instance.data["transfer"] = False + instance.data["farm"] = True families.append("{}.farm".format(family)) family = families_ak.lower() From 53e430ee50eaa9d33157573342c2ffb4020121b0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ond=C5=99ej=20Samohel?= Date: Mon, 25 Jul 2022 17:49:18 +0200 Subject: [PATCH 23/31] :bug: fix active pane loss --- openpype/hosts/maya/plugins/publish/extract_playblast.py | 2 ++ openpype/hosts/maya/plugins/publish/extract_thumbnail.py | 2 ++ 2 files changed, 4 insertions(+) diff --git a/openpype/hosts/maya/plugins/publish/extract_playblast.py b/openpype/hosts/maya/plugins/publish/extract_playblast.py index 233a0b60c2..54ef09e060 100644 --- a/openpype/hosts/maya/plugins/publish/extract_playblast.py +++ b/openpype/hosts/maya/plugins/publish/extract_playblast.py @@ -128,8 +128,10 @@ class ExtractPlayblast(openpype.api.Extractor): # Update preset with current panel setting # if override_viewport_options is turned off if not override_viewport_options: + panel = cmds.getPanel(with_focus=True) panel_preset = capture.parse_active_view() preset.update(panel_preset) + cmds.setFocus(panel) path = capture.capture(**preset) diff --git a/openpype/hosts/maya/plugins/publish/extract_thumbnail.py b/openpype/hosts/maya/plugins/publish/extract_thumbnail.py index 4f28aa167c..d1f43b61be 100644 --- a/openpype/hosts/maya/plugins/publish/extract_thumbnail.py +++ b/openpype/hosts/maya/plugins/publish/extract_thumbnail.py @@ -110,8 +110,10 @@ class ExtractThumbnail(openpype.api.Extractor): # Update preset with current panel setting # if override_viewport_options is turned off if not override_viewport_options: + panel = cmds.getPanel(with_focus=True) panel_preset = capture.parse_active_view() preset.update(panel_preset) + cmds.setFocus(panel) path = capture.capture(**preset) playblast = self._fix_playblast_output_path(path) From ede691c3e90244fa46a388ad4280bf22b0c50d31 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Mon, 25 Jul 2022 18:20:57 +0200 Subject: [PATCH 24/31] fix missing arg --- openpype/plugins/publish/integrate.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/openpype/plugins/publish/integrate.py b/openpype/plugins/publish/integrate.py index 6236724228..0193d136c2 100644 --- a/openpype/plugins/publish/integrate.py +++ b/openpype/plugins/publish/integrate.py @@ -297,7 +297,9 @@ class IntegrateAsset(pyblish.api.InstancePlugin): template_name = self.get_template_name(instance) subset, subset_writes = self.prepare_subset(instance, project_name) - version, version_writes = self.prepare_version(instance, subset) + version, version_writes = self.prepare_version( + instance, subset, project_name + ) instance.data["versionEntity"] = version # Get existing representations (if any) From 6ae84ca5e6dec80990d13950d4226a7e66a99e66 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Mon, 25 Jul 2022 18:23:41 +0200 Subject: [PATCH 25/31] fix passed argument to get_representations --- openpype/plugins/publish/integrate.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openpype/plugins/publish/integrate.py b/openpype/plugins/publish/integrate.py index 0193d136c2..8048ce3ab9 100644 --- a/openpype/plugins/publish/integrate.py +++ b/openpype/plugins/publish/integrate.py @@ -307,7 +307,7 @@ class IntegrateAsset(pyblish.api.InstancePlugin): repre_doc["name"].lower(): repre_doc for repre_doc in get_representations( project_name, - version_ids=version["_id"], + version_ids=[version["_id"]], fields=["_id", "name"] ) } From 84eb91acb7d4fe41a122543493372b7dea6012a0 Mon Sep 17 00:00:00 2001 From: Kayla Man Date: Tue, 26 Jul 2022 08:34:41 +0800 Subject: [PATCH 26/31] bugfix/OP-3356_Maya-Review-Image-plane-attribute --- openpype/hosts/maya/plugins/publish/extract_thumbnail.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/openpype/hosts/maya/plugins/publish/extract_thumbnail.py b/openpype/hosts/maya/plugins/publish/extract_thumbnail.py index 4f28aa167c..7885c1ebc9 100644 --- a/openpype/hosts/maya/plugins/publish/extract_thumbnail.py +++ b/openpype/hosts/maya/plugins/publish/extract_thumbnail.py @@ -116,7 +116,11 @@ class ExtractThumbnail(openpype.api.Extractor): path = capture.capture(**preset) playblast = self._fix_playblast_output_path(path) - _, thumbnail = os.path.split(playblast) + image_plane = instance.data.get("imagePlane") + if image_plane: + _, thumbnail = os.path.split(playblast) + else: + return self.log.info("file list {}".format(thumbnail)) From 34601a6243dc39e09ab208a9e7e859a8e84e5d67 Mon Sep 17 00:00:00 2001 From: Kayla Man Date: Tue, 26 Jul 2022 17:05:39 +0800 Subject: [PATCH 27/31] fix the bug of loading image plane --- .../maya/plugins/publish/extract_thumbnail.py | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/openpype/hosts/maya/plugins/publish/extract_thumbnail.py b/openpype/hosts/maya/plugins/publish/extract_thumbnail.py index 7885c1ebc9..47e9a907a0 100644 --- a/openpype/hosts/maya/plugins/publish/extract_thumbnail.py +++ b/openpype/hosts/maya/plugins/publish/extract_thumbnail.py @@ -100,6 +100,13 @@ class ExtractThumbnail(openpype.api.Extractor): # camera. if preset.pop("isolate_view", False) and instance.data.get("isolate"): preset["isolate"] = instance.data["setMembers"] + + #Show or Hide Image Plane + image_plane = instance.data.get("imagePlane", True) + if "viewport_options" in preset: + preset["viewport_options"]["imagePlane"] = image_plane + else: + preset["viewport_options"] = {"imagePlane": image_plane} with lib.maintained_time(): # Force viewer to False in call to capture because we have our own @@ -116,11 +123,8 @@ class ExtractThumbnail(openpype.api.Extractor): path = capture.capture(**preset) playblast = self._fix_playblast_output_path(path) - image_plane = instance.data.get("imagePlane") - if image_plane: - _, thumbnail = os.path.split(playblast) - else: - return + _, thumbnail = os.path.split(playblast) + self.log.info("file list {}".format(thumbnail)) From cd87b8ba2a5ae9a008123f999e958fe7c1562b54 Mon Sep 17 00:00:00 2001 From: Kayla Man Date: Tue, 26 Jul 2022 17:11:07 +0800 Subject: [PATCH 28/31] bugfix-maya-review-image-plane_attribute --- openpype/hosts/maya/plugins/publish/extract_thumbnail.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openpype/hosts/maya/plugins/publish/extract_thumbnail.py b/openpype/hosts/maya/plugins/publish/extract_thumbnail.py index 47e9a907a0..0d537810c0 100644 --- a/openpype/hosts/maya/plugins/publish/extract_thumbnail.py +++ b/openpype/hosts/maya/plugins/publish/extract_thumbnail.py @@ -101,7 +101,7 @@ class ExtractThumbnail(openpype.api.Extractor): if preset.pop("isolate_view", False) and instance.data.get("isolate"): preset["isolate"] = instance.data["setMembers"] - #Show or Hide Image Plane + # Show or Hide Image Plane image_plane = instance.data.get("imagePlane", True) if "viewport_options" in preset: preset["viewport_options"]["imagePlane"] = image_plane From 642d6ef407630ef2a9dad37551b6725569a7b4d7 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Tue, 26 Jul 2022 11:15:48 +0200 Subject: [PATCH 29/31] fix typo --- openpype/plugins/publish/extract_thumbnail.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openpype/plugins/publish/extract_thumbnail.py b/openpype/plugins/publish/extract_thumbnail.py index c1eee71376..c154275322 100644 --- a/openpype/plugins/publish/extract_thumbnail.py +++ b/openpype/plugins/publish/extract_thumbnail.py @@ -72,7 +72,7 @@ class ExtractThumbnail(pyblish.api.InstancePlugin): # - this is to avoid "override" of source file dst_staging = tempfile.mkdtemp(prefix="pyblish_tmp_") self.log.debug( - "Create temp directory {} for thumbnail".formap(dst_staging) + "Create temp directory {} for thumbnail".format(dst_staging) ) # Store new staging to cleanup paths instance.context.data["cleanupFullPaths"].append(dst_staging) From 955423c9eb379cd9f90662672d8b91f3c96bb85a Mon Sep 17 00:00:00 2001 From: OpenPype Date: Wed, 27 Jul 2022 04:03:52 +0000 Subject: [PATCH 30/31] [Automated] Bump version --- CHANGELOG.md | 38 ++++++++++++-------------------------- openpype/version.py | 2 +- pyproject.toml | 2 +- 3 files changed, 14 insertions(+), 28 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ec880b9c61..133be18f68 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,6 @@ # Changelog -## [3.12.2-nightly.3](https://github.com/pypeclub/OpenPype/tree/HEAD) +## [3.12.2-nightly.4](https://github.com/pypeclub/OpenPype/tree/HEAD) [Full Changelog](https://github.com/pypeclub/OpenPype/compare/3.12.1...HEAD) @@ -11,21 +11,22 @@ **🚀 Enhancements** +- General: Global thumbnail extractor is ready for more cases [\#3561](https://github.com/pypeclub/OpenPype/pull/3561) - Maya: add additional validators to Settings [\#3540](https://github.com/pypeclub/OpenPype/pull/3540) - General: Interactive console in cli [\#3526](https://github.com/pypeclub/OpenPype/pull/3526) - Ftrack: Automatic daily review session creation can define trigger hour [\#3516](https://github.com/pypeclub/OpenPype/pull/3516) - Ftrack: add source into Note [\#3509](https://github.com/pypeclub/OpenPype/pull/3509) -- Ftrack: Trigger custom ftrack topic of project structure creation [\#3506](https://github.com/pypeclub/OpenPype/pull/3506) -- Settings UI: Add extract to file action on project view [\#3505](https://github.com/pypeclub/OpenPype/pull/3505) - Add pack and unpack convenience scripts [\#3502](https://github.com/pypeclub/OpenPype/pull/3502) - General: Event system [\#3499](https://github.com/pypeclub/OpenPype/pull/3499) - NewPublisher: Keep plugins with mismatch target in report [\#3498](https://github.com/pypeclub/OpenPype/pull/3498) - Nuke: load clip with options from settings [\#3497](https://github.com/pypeclub/OpenPype/pull/3497) - TrayPublisher: implemented render\_mov\_batch [\#3486](https://github.com/pypeclub/OpenPype/pull/3486) -- Migrate basic families to the new Tray Publisher [\#3469](https://github.com/pypeclub/OpenPype/pull/3469) **🐛 Bug fixes** +- Maya: fix Review image plane attribute [\#3569](https://github.com/pypeclub/OpenPype/pull/3569) +- Maya: Fix animated attributes \(ie. overscan\) on loaded cameras breaking review publishing. [\#3562](https://github.com/pypeclub/OpenPype/pull/3562) +- NewPublisher: Python 2 compatible html escape [\#3559](https://github.com/pypeclub/OpenPype/pull/3559) - Remove invalid submodules from `/vendor` [\#3557](https://github.com/pypeclub/OpenPype/pull/3557) - General: Remove hosts filter on integrator plugins [\#3556](https://github.com/pypeclub/OpenPype/pull/3556) - Settings: Clean default values of environments [\#3550](https://github.com/pypeclub/OpenPype/pull/3550) @@ -44,13 +45,19 @@ **🔀 Refactored code** +- General: Use query functions in integrator [\#3563](https://github.com/pypeclub/OpenPype/pull/3563) +- General: Mongo core connection moved to client [\#3531](https://github.com/pypeclub/OpenPype/pull/3531) - Refactor Integrate Asset [\#3530](https://github.com/pypeclub/OpenPype/pull/3530) - General: Client docstrings cleanup [\#3529](https://github.com/pypeclub/OpenPype/pull/3529) +- General: Move load related functions into pipeline [\#3527](https://github.com/pypeclub/OpenPype/pull/3527) - General: Get current context document functions [\#3522](https://github.com/pypeclub/OpenPype/pull/3522) - Kitsu: Use query function from client [\#3496](https://github.com/pypeclub/OpenPype/pull/3496) -- TimersManager: Use query functions [\#3495](https://github.com/pypeclub/OpenPype/pull/3495) - Deadline: Use query functions [\#3466](https://github.com/pypeclub/OpenPype/pull/3466) +**Merged pull requests:** + +- Maya: fix active pane loss [\#3566](https://github.com/pypeclub/OpenPype/pull/3566) + ## [3.12.1](https://github.com/pypeclub/OpenPype/tree/3.12.1) (2022-07-13) [Full Changelog](https://github.com/pypeclub/OpenPype/compare/CI/3.12.1-nightly.6...3.12.1) @@ -59,10 +66,6 @@ - Docs: Added minimal permissions for MongoDB [\#3441](https://github.com/pypeclub/OpenPype/pull/3441) -**🆕 New features** - -- Maya: Add VDB to Arnold loader [\#3433](https://github.com/pypeclub/OpenPype/pull/3433) - **🚀 Enhancements** - TrayPublisher: Added more options for grouping of instances [\#3494](https://github.com/pypeclub/OpenPype/pull/3494) @@ -72,8 +75,6 @@ - General: Better arguments order in creator init [\#3475](https://github.com/pypeclub/OpenPype/pull/3475) - Ftrack: Trigger custom ftrack events on project creation and preparation [\#3465](https://github.com/pypeclub/OpenPype/pull/3465) - Windows installer: Clean old files and add version subfolder [\#3445](https://github.com/pypeclub/OpenPype/pull/3445) -- Blender: Bugfix - Set fps properly on open [\#3426](https://github.com/pypeclub/OpenPype/pull/3426) -- Hiero: Add custom scripts menu [\#3425](https://github.com/pypeclub/OpenPype/pull/3425) **🐛 Bug fixes** @@ -92,7 +93,6 @@ - Nuke: prerender reviewable fails [\#3450](https://github.com/pypeclub/OpenPype/pull/3450) - Maya: fix hashing in Python 3 for tile rendering [\#3447](https://github.com/pypeclub/OpenPype/pull/3447) - LogViewer: Escape html characters in log message [\#3443](https://github.com/pypeclub/OpenPype/pull/3443) -- Nuke: Slate frame is integrated [\#3427](https://github.com/pypeclub/OpenPype/pull/3427) **🔀 Refactored code** @@ -111,20 +111,6 @@ [Full Changelog](https://github.com/pypeclub/OpenPype/compare/CI/3.12.0-nightly.3...3.12.0) -**🚀 Enhancements** - -- Webserver: Added CORS middleware [\#3422](https://github.com/pypeclub/OpenPype/pull/3422) - -**🐛 Bug fixes** - -- NewPublisher: Fix subset name change on change of creator plugin [\#3420](https://github.com/pypeclub/OpenPype/pull/3420) -- Bug: fix invalid avalon import [\#3418](https://github.com/pypeclub/OpenPype/pull/3418) - -**🔀 Refactored code** - -- Unreal: Use client query functions [\#3421](https://github.com/pypeclub/OpenPype/pull/3421) -- General: Move editorial lib to pipeline [\#3419](https://github.com/pypeclub/OpenPype/pull/3419) - ## [3.11.1](https://github.com/pypeclub/OpenPype/tree/3.11.1) (2022-06-20) [Full Changelog](https://github.com/pypeclub/OpenPype/compare/CI/3.11.1-nightly.1...3.11.1) diff --git a/openpype/version.py b/openpype/version.py index 9dda1eacce..9388d4219e 100644 --- a/openpype/version.py +++ b/openpype/version.py @@ -1,3 +1,3 @@ # -*- coding: utf-8 -*- """Package declaring Pype version.""" -__version__ = "3.12.2-nightly.3" +__version__ = "3.12.2-nightly.4" diff --git a/pyproject.toml b/pyproject.toml index eebc8a5600..0a9c02834a 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "OpenPype" -version = "3.12.2-nightly.3" # OpenPype +version = "3.12.2-nightly.4" # OpenPype description = "Open VFX and Animation pipeline with support." authors = ["OpenPype Team "] license = "MIT License" From 078bcb3b8e027948b8951920821e63392378c787 Mon Sep 17 00:00:00 2001 From: OpenPype Date: Wed, 27 Jul 2022 07:58:18 +0000 Subject: [PATCH 31/31] [Automated] Release --- CHANGELOG.md | 5 ++--- openpype/version.py | 2 +- pyproject.toml | 2 +- 3 files changed, 4 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 133be18f68..e4fc1d59ca 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,8 +1,8 @@ # Changelog -## [3.12.2-nightly.4](https://github.com/pypeclub/OpenPype/tree/HEAD) +## [3.12.2](https://github.com/pypeclub/OpenPype/tree/3.12.2) (2022-07-27) -[Full Changelog](https://github.com/pypeclub/OpenPype/compare/3.12.1...HEAD) +[Full Changelog](https://github.com/pypeclub/OpenPype/compare/3.12.1...3.12.2) ### 📖 Documentation @@ -17,7 +17,6 @@ - Ftrack: Automatic daily review session creation can define trigger hour [\#3516](https://github.com/pypeclub/OpenPype/pull/3516) - Ftrack: add source into Note [\#3509](https://github.com/pypeclub/OpenPype/pull/3509) - Add pack and unpack convenience scripts [\#3502](https://github.com/pypeclub/OpenPype/pull/3502) -- General: Event system [\#3499](https://github.com/pypeclub/OpenPype/pull/3499) - NewPublisher: Keep plugins with mismatch target in report [\#3498](https://github.com/pypeclub/OpenPype/pull/3498) - Nuke: load clip with options from settings [\#3497](https://github.com/pypeclub/OpenPype/pull/3497) - TrayPublisher: implemented render\_mov\_batch [\#3486](https://github.com/pypeclub/OpenPype/pull/3486) diff --git a/openpype/version.py b/openpype/version.py index 9388d4219e..5c39e9e630 100644 --- a/openpype/version.py +++ b/openpype/version.py @@ -1,3 +1,3 @@ # -*- coding: utf-8 -*- """Package declaring Pype version.""" -__version__ = "3.12.2-nightly.4" +__version__ = "3.12.2" diff --git a/pyproject.toml b/pyproject.toml index 0a9c02834a..175e72be24 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "OpenPype" -version = "3.12.2-nightly.4" # OpenPype +version = "3.12.2" # OpenPype description = "Open VFX and Animation pipeline with support." authors = ["OpenPype Team "] license = "MIT License"