From 4e7358f73b007d8a53ae55c5a3b255a45b299876 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Thu, 30 Jun 2022 14:47:08 +0200 Subject: [PATCH 01/16] removed unused function 'get_hierarchy' --- openpype/api.py | 2 - openpype/lib/__init__.py | 2 - openpype/lib/avalon_context.py | 51 ------------------- .../tests/test_lib_restructuralization.py | 1 - 4 files changed, 56 deletions(-) diff --git a/openpype/api.py b/openpype/api.py index 9ce745b653..fac2ae572b 100644 --- a/openpype/api.py +++ b/openpype/api.py @@ -15,7 +15,6 @@ from .lib import ( run_subprocess, version_up, get_asset, - get_hierarchy, get_workdir_data, get_version_from_path, get_last_version_from_path, @@ -101,7 +100,6 @@ __all__ = [ # get contextual data "version_up", "get_asset", - "get_hierarchy", "get_workdir_data", "get_version_from_path", "get_last_version_from_path", diff --git a/openpype/lib/__init__.py b/openpype/lib/__init__.py index 8d4e733b7d..fb52a9aca7 100644 --- a/openpype/lib/__init__.py +++ b/openpype/lib/__init__.py @@ -120,7 +120,6 @@ from .avalon_context import ( is_latest, any_outdated, get_asset, - get_hierarchy, get_linked_assets, get_latest_version, get_system_general_anatomy_data, @@ -292,7 +291,6 @@ __all__ = [ "is_latest", "any_outdated", "get_asset", - "get_hierarchy", "get_linked_assets", "get_latest_version", "get_system_general_anatomy_data", diff --git a/openpype/lib/avalon_context.py b/openpype/lib/avalon_context.py index a03f066300..ad34e24644 100644 --- a/openpype/lib/avalon_context.py +++ b/openpype/lib/avalon_context.py @@ -213,57 +213,6 @@ def get_asset(asset_name=None): return asset_document -@with_pipeline_io -def get_hierarchy(asset_name=None): - """ - Obtain asset hierarchy path string from mongo db - - Args: - asset_name (str) - - Returns: - (string): asset hierarchy path - - """ - if not asset_name: - asset_name = legacy_io.Session.get( - "AVALON_ASSET", - os.environ["AVALON_ASSET"] - ) - - asset_entity = legacy_io.find_one({ - "type": 'asset', - "name": asset_name - }) - - not_set = "PARENTS_NOT_SET" - entity_parents = asset_entity.get("data", {}).get("parents", not_set) - - # If entity already have parents then just return joined - if entity_parents != not_set: - return "/".join(entity_parents) - - # Else query parents through visualParents and store result to entity - hierarchy_items = [] - entity = asset_entity - while True: - parent_id = entity.get("data", {}).get("visualParent") - if not parent_id: - break - entity = legacy_io.find_one({"_id": parent_id}) - hierarchy_items.append(entity["name"]) - - # Add parents to entity data for next query - entity_data = asset_entity.get("data", {}) - entity_data["parents"] = hierarchy_items - legacy_io.update_many( - {"_id": asset_entity["_id"]}, - {"$set": {"data": entity_data}} - ) - - return "/".join(hierarchy_items) - - def get_system_general_anatomy_data(): system_settings = get_system_settings() studio_name = system_settings["general"]["studio_name"] diff --git a/openpype/tests/test_lib_restructuralization.py b/openpype/tests/test_lib_restructuralization.py index 94080e550d..ccccc76a08 100644 --- a/openpype/tests/test_lib_restructuralization.py +++ b/openpype/tests/test_lib_restructuralization.py @@ -21,7 +21,6 @@ def test_backward_compatibility(printer): from openpype.lib import is_latest from openpype.lib import any_outdated from openpype.lib import get_asset - from openpype.lib import get_hierarchy from openpype.lib import get_linked_assets from openpype.lib import get_latest_version from openpype.lib import get_ffprobe_streams From e147cd4132e983e9d70c2be02356a75fd5f70f11 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Thu, 30 Jun 2022 16:04:25 +0200 Subject: [PATCH 02/16] added 'get_workfile_info' to openpype.client which replace 'get_workfile_doc' in openpype.lib --- openpype/client/__init__.py | 4 ++ openpype/client/entities.py | 32 ++++++++++++++++ openpype/lib/avalon_context.py | 59 +++++++++++++++++++++++++++--- openpype/tools/workfiles/window.py | 17 ++++++--- 4 files changed, 100 insertions(+), 12 deletions(-) diff --git a/openpype/client/__init__.py b/openpype/client/__init__.py index e3b4ef5132..e1c8ea9f03 100644 --- a/openpype/client/__init__.py +++ b/openpype/client/__init__.py @@ -33,6 +33,8 @@ from .entities import ( get_thumbnail, get_thumbnails, get_thumbnail_id_from_source, + + get_workfile_info, ) __all__ = ( @@ -70,4 +72,6 @@ __all__ = ( "get_thumbnail", "get_thumbnails", "get_thumbnail_id_from_source", + + "get_workfile_info", ) diff --git a/openpype/client/entities.py b/openpype/client/entities.py index 28cd994254..9771461d2e 100644 --- a/openpype/client/entities.py +++ b/openpype/client/entities.py @@ -1158,6 +1158,38 @@ def get_thumbnail(project_name, thumbnail_id, fields=None): return conn.find_one(query_filter, _prepare_fields(fields)) +def get_workfile_info( + project_name, asset_id, task_name, filename, fields=None +): + """Document with workfile information. + + Warning: + Query is based on filename and context which does not meant it will + find always right expected result. There is a limited amound of usage + and is recommended to not expect that all workfiles are stored in + database. + + Args: + project_name (str): Name of project where to look for queried entities. + asset_id (str|ObjectId): Id of asset entity. + task_name (str): Task name on asset. + fields (list[str]): Fields that should be returned. All fields are + returned if 'None' is passed. + """ + + if not asset_id or not task_name or not filename: + return None + + query_filter = { + "type": "workfile", + "parent": _convert_id(asset_id), + "task_name": task_name, + "filename": filename + } + conn = _get_project_connection(project_name) + return conn.find_one(query_filter, _prepare_fields(fields)) + + """ ## Custom data storage: - Settings - OP settings overrides and local settings diff --git a/openpype/lib/avalon_context.py b/openpype/lib/avalon_context.py index ad34e24644..412d7fa1d3 100644 --- a/openpype/lib/avalon_context.py +++ b/openpype/lib/avalon_context.py @@ -9,7 +9,11 @@ import collections import functools from bson.objectid import ObjectId +import warnings +from openpype.client import ( + get_workfile_info, +) from openpype.settings import ( get_project_settings, get_system_settings @@ -36,6 +40,51 @@ PROJECT_NAME_REGEX = re.compile( ) +class AvalonContextDeprecatedWarning(DeprecationWarning): + pass + + +def deprecated(new_destination): + """Mark functions as deprecated. + + It will result in a warning being emitted when the function is used. + """ + + func = None + if callable(new_destination): + func = new_destination + new_destination = None + + def _decorator(decorated_func): + if new_destination is None: + warning_message = ( + " Please check content of deprecated function to figure out" + " possible replacement." + ) + else: + warning_message = " Please replace your usage with '{}'.".format( + new_destination + ) + + @functools.wraps(decorated_func) + def wrapper(*args, **kwargs): + warnings.simplefilter("always", AvalonContextDeprecatedWarning) + warnings.warn( + ( + "Call to deprecated function '{}'" + "\nFunction was moved or removed.{}" + ).format(decorated_func.__name__, warning_message), + category=AvalonContextDeprecatedWarning, + stacklevel=4 + ) + return decorated_func(*args, **kwargs) + return wrapper + + if func is None: + return _decorator + return _decorator(func) + + def create_project( project_name, project_code, library_project=False, dbcon=None ): @@ -759,6 +808,7 @@ def update_current_task(task=None, asset=None, app=None, template_key=None): @with_pipeline_io +@deprecated("openpype.client.get_workfile_info") def get_workfile_doc(asset_id, task_name, filename, dbcon=None): """Return workfile document for entered context. @@ -775,16 +825,13 @@ def get_workfile_doc(asset_id, task_name, filename, dbcon=None): Returns: dict: Workfile document or None. """ + # Use legacy_io if dbcon is not entered if not dbcon: dbcon = legacy_io - return dbcon.find_one({ - "type": "workfile", - "parent": asset_id, - "task_name": task_name, - "filename": filename - }) + project_name = dbcon.active_project() + return get_workfile_info(project_name, asset_id, task_name, filename) @with_pipeline_io diff --git a/openpype/tools/workfiles/window.py b/openpype/tools/workfiles/window.py index 9f4cea2f8a..c1efe026f2 100644 --- a/openpype/tools/workfiles/window.py +++ b/openpype/tools/workfiles/window.py @@ -2,10 +2,13 @@ import os import datetime from Qt import QtCore, QtWidgets -from openpype.client import get_asset_by_id, get_asset_by_name +from openpype.client import ( + get_asset_by_id, + get_asset_by_name, + get_workfile_info, +) from openpype import style from openpype.lib import ( - get_workfile_doc, create_workfile_doc, save_workfile_data_to_doc, ) @@ -255,8 +258,9 @@ class Window(QtWidgets.QMainWindow): workfile_doc = None if asset_id and task_name and filepath: filename = os.path.split(filepath)[1] - workfile_doc = get_workfile_doc( - asset_id, task_name, filename, legacy_io + project_name = legacy_io.active_project() + workfile_doc = get_workfile_info( + project_name, asset_id, task_name, filename ) self.side_panel.set_context( asset_id, task_name, filepath, workfile_doc @@ -289,8 +293,9 @@ class Window(QtWidgets.QMainWindow): return filename = os.path.split(filepath)[1] - return get_workfile_doc( - asset_id, task_name, filename, legacy_io + project_name = legacy_io.active_project() + return get_workfile_info( + project_name, asset_id, task_name, filename ) def _create_workfile_doc(self, filepath, force=False): From 9f29726d98b7e87f608dd70aa512b8e0c56df949 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Thu, 30 Jun 2022 16:05:22 +0200 Subject: [PATCH 03/16] modified and moved project doc validation --- openpype/lib/avalon_context.py | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-) diff --git a/openpype/lib/avalon_context.py b/openpype/lib/avalon_context.py index 412d7fa1d3..fec87e53d1 100644 --- a/openpype/lib/avalon_context.py +++ b/openpype/lib/avalon_context.py @@ -12,6 +12,7 @@ from bson.objectid import ObjectId import warnings from openpype.client import ( + get_project, get_workfile_info, ) from openpype.settings import ( @@ -114,6 +115,11 @@ def create_project( from openpype.pipeline import AvalonMongoDB from openpype.pipeline.schema import validate + if get_project(project_name, fields=["name"]): + raise ValueError("Project with name \"{}\" already exists".format( + project_name + )) + if dbcon is None: dbcon = AvalonMongoDB() @@ -123,15 +129,6 @@ def create_project( ).format(project_name)) database = dbcon.database - project_doc = database[project_name].find_one( - {"type": "project"}, - {"name": 1} - ) - if project_doc: - raise ValueError("Project with name \"{}\" already exists".format( - project_name - )) - project_doc = { "type": "project", "name": project_name, @@ -154,7 +151,7 @@ def create_project( database[project_name].delete_one({"type": "project"}) raise - project_doc = database[project_name].find_one({"type": "project"}) + project_doc = get_project(project_name) try: # Validate created project document From 5b600b850614b5d67fe6f7eb59c42754b83460b3 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Thu, 30 Jun 2022 16:11:54 +0200 Subject: [PATCH 04/16] is_latest use new query functions --- openpype/lib/avalon_context.py | 23 +++++++++++++---------- 1 file changed, 13 insertions(+), 10 deletions(-) diff --git a/openpype/lib/avalon_context.py b/openpype/lib/avalon_context.py index fec87e53d1..02b4cad6f2 100644 --- a/openpype/lib/avalon_context.py +++ b/openpype/lib/avalon_context.py @@ -13,6 +13,8 @@ import warnings from openpype.client import ( get_project, + get_version_by_id, + get_last_version_by_subset_id, get_workfile_info, ) from openpype.settings import ( @@ -183,23 +185,24 @@ def is_latest(representation): Returns: bool: Whether the representation is of latest version. - """ - version = legacy_io.find_one({"_id": representation['parent']}) + project_name = legacy_io.active_project() + version = get_version_by_id( + project_name, + representation["parent"], + hero=True, + fields=["_id", "type", "parent"] + ) if version["type"] == "hero_version": return True # Get highest version under the parent - highest_version = legacy_io.find_one({ - "type": "version", - "parent": version["parent"] - }, sort=[("name", -1)], projection={"name": True}) + last_version = get_last_version_by_subset_id( + project_name, version["parent"], fields=["_id"] + ) - if version['name'] == highest_version['name']: - return True - else: - return False + return version["_id"] == last_version["_id"] @with_pipeline_io From a0bc06c62084f4116c3e0519522b15f7d4bcde0e Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Thu, 30 Jun 2022 16:18:12 +0200 Subject: [PATCH 05/16] use query functions for rest of avalon context functions --- openpype/lib/avalon_context.py | 232 +++++++++++++-------------------- 1 file changed, 90 insertions(+), 142 deletions(-) diff --git a/openpype/lib/avalon_context.py b/openpype/lib/avalon_context.py index 02b4cad6f2..7b00032027 100644 --- a/openpype/lib/avalon_context.py +++ b/openpype/lib/avalon_context.py @@ -13,8 +13,15 @@ import warnings from openpype.client import ( get_project, + get_assets, + get_asset_by_name, + get_subset_by_name, + get_subsets, get_version_by_id, + get_last_versions, get_last_version_by_subset_id, + get_representations, + get_representation_by_id, get_workfile_info, ) from openpype.settings import ( @@ -210,6 +217,7 @@ def any_outdated(): """Return whether the current scene has any outdated content""" from openpype.pipeline import registered_host + project_name = legacy_io.active_project() checked = set() host = registered_host() for container in host.ls(): @@ -217,12 +225,8 @@ def any_outdated(): if representation in checked: continue - representation_doc = legacy_io.find_one( - { - "_id": ObjectId(representation), - "type": "representation" - }, - projection={"parent": True} + representation_doc = get_representation_by_id( + project_name, representation, fields=["parent"] ) if representation_doc and not is_latest(representation_doc): return True @@ -240,22 +244,20 @@ def any_outdated(): def get_asset(asset_name=None): """ Returning asset document from database by its name. - Doesn't count with duplicities on asset names! + Doesn't count with duplicities on asset names! - Args: - asset_name (str) + Args: + asset_name (str) - Returns: - (MongoDB document) + Returns: + (MongoDB document) """ + + project_name = legacy_io.active_project() if not asset_name: asset_name = legacy_io.Session["AVALON_ASSET"] - asset_document = legacy_io.find_one({ - "name": asset_name, - "type": "asset" - }) - + asset_document = get_asset_by_name(project_name, asset_name) if not asset_document: raise TypeError("Entity \"{}\" was not found in DB".format(asset_name)) @@ -311,11 +313,13 @@ def get_linked_assets(asset_doc): Returns: (list) Asset documents of input links for passed asset doc. """ + link_ids = get_linked_asset_ids(asset_doc) if not link_ids: return [] - return list(legacy_io.find({"_id": {"$in": link_ids}})) + project_name = legacy_io.active_project() + return list(get_assets(project_name, link_ids)) @with_pipeline_io @@ -337,20 +341,14 @@ def get_latest_version(asset_name, subset_name, dbcon=None, project_name=None): dict: Last version document for entered . """ - if not dbcon: - log.debug("Using `legacy_io` for query.") - dbcon = legacy_io - # Make sure is installed - dbcon.install() + if not project_name: + if not dbcon: + log.debug("Using `legacy_io` for query.") + dbcon = legacy_io + # Make sure is installed + dbcon.install() - if project_name and project_name != dbcon.Session.get("AVALON_PROJECT"): - # `legacy_io` has only `_database` attribute - # but `AvalonMongoDB` has `database` - database = getattr(dbcon, "database", dbcon._database) - collection = database[project_name] - else: - project_name = dbcon.Session.get("AVALON_PROJECT") - collection = dbcon + project_name = dbcon.active_project() log.debug(( "Getting latest version for Project: \"{}\" Asset: \"{}\"" @@ -358,19 +356,15 @@ def get_latest_version(asset_name, subset_name, dbcon=None, project_name=None): ).format(project_name, asset_name, subset_name)) # Query asset document id by asset name - asset_doc = collection.find_one( - {"type": "asset", "name": asset_name}, - {"_id": True} - ) + asset_doc = get_asset_by_name(project_name, asset_name, fields=["_id"]) if not asset_doc: log.info( "Asset \"{}\" was not found in Database.".format(asset_name) ) return None - subset_doc = collection.find_one( - {"type": "subset", "name": subset_name, "parent": asset_doc["_id"]}, - {"_id": True} + subset_doc = get_subset_by_name( + project_name, subset_name, asset_doc["_id"] ) if not subset_doc: log.info( @@ -378,10 +372,7 @@ def get_latest_version(asset_name, subset_name, dbcon=None, project_name=None): ) return None - version_doc = collection.find_one( - {"type": "version", "parent": subset_doc["_id"]}, - sort=[("name", -1)], - ) + version_doc = get_last_version_by_subset_id(project_name, subset_doc["_id"]) if not version_doc: log.info( "Subset \"{}\" does not have any version yet.".format(subset_name) @@ -418,28 +409,17 @@ def get_workfile_template_key_from_context( ValueError: When both 'dbcon' and 'project_name' were not passed. """ - if not dbcon: - if not project_name: + if not project_name: + if not dbcon: raise ValueError(( "`get_workfile_template_key_from_context` requires to pass" " one of 'dbcon' or 'project_name' arguments." )) - from openpype.pipeline import AvalonMongoDB - dbcon = AvalonMongoDB() - dbcon.Session["AVALON_PROJECT"] = project_name + project_name = dbcon.active_project() - elif not project_name: - project_name = dbcon.Session["AVALON_PROJECT"] - - asset_doc = dbcon.find_one( - { - "type": "asset", - "name": asset_name - }, - { - "data.tasks": 1 - } + asset_doc = get_asset_by_name( + project_name, asset_name, fields=["data.tasks"] ) asset_tasks = asset_doc.get("data", {}).get("tasks") or {} task_info = asset_tasks.get(task_name) or {} @@ -632,6 +612,7 @@ def get_workdir( Returns: TemplateResult: Workdir path. """ + if not anatomy: anatomy = Anatomy(project_doc["name"]) @@ -659,15 +640,11 @@ def template_data_from_session(session=None): session = legacy_io.Session project_name = session["AVALON_PROJECT"] - project_doc = legacy_io.database[project_name].find_one({ - "type": "project" - }) - asset_doc = legacy_io.database[project_name].find_one({ - "type": "asset", - "name": session["AVALON_ASSET"] - }) + asset_name = session["AVALON_ASSET"] task_name = session["AVALON_TASK"] host_name = session["AVALON_APP"] + project_doc = get_project(project_name) + asset_doc = get_asset_by_name(project_name, asset_name) return get_workdir_data(project_doc, asset_doc, task_name, host_name) @@ -692,8 +669,8 @@ def compute_session_changes( Returns: dict: The required changes in the Session dictionary. - """ + changes = dict() # If no changes, return directly @@ -711,12 +688,9 @@ def compute_session_changes( if not asset_document or not asset_tasks: # Assume asset name - asset_document = legacy_io.find_one( - { - "name": asset, - "type": "asset" - }, - {"data.tasks": True} + project_name = session["AVALON_PROJECT"] + asset_document = get_asset_by_name( + project_name, asset, fields=["data.tasks"] ) assert asset_document, "Asset must exist" @@ -864,12 +838,13 @@ def create_workfile_doc(asset_doc, task_name, filename, workdir, dbcon=None): doc_data = copy.deepcopy(doc_filter) # Prepare project for workdir data - project_doc = dbcon.find_one({"type": "project"}) + project_name = dbcon.active_project() + project_doc = get_project(project_name) workdir_data = get_workdir_data( project_doc, asset_doc, task_name, dbcon.Session["AVALON_APP"] ) # Prepare anatomy - anatomy = Anatomy(project_doc["name"]) + anatomy = Anatomy(project_name) # Get workdir path (result is anatomy.TemplateResult) template_workdir = get_workdir_with_workdir_data( workdir_data, anatomy @@ -984,12 +959,9 @@ class BuildWorkfile: from openpype.pipeline import discover_loader_plugins # Get current asset name and entity + project_name = legacy_io.active_project() current_asset_name = legacy_io.Session["AVALON_ASSET"] - current_asset_entity = legacy_io.find_one({ - "type": "asset", - "name": current_asset_name - }) - + current_asset_entity = get_asset_by_name(project_name, current_asset_name) # Skip if asset was not found if not current_asset_entity: print("Asset entity with name `{}` was not found".format( @@ -1494,7 +1466,7 @@ class BuildWorkfile: return loaded_containers @with_pipeline_io - def _collect_last_version_repres(self, asset_entities): + def _collect_last_version_repres(self, asset_docs): """Collect subsets, versions and representations for asset_entities. Args: @@ -1527,64 +1499,56 @@ class BuildWorkfile: ``` """ - if not asset_entities: - return {} + output = {} + if not asset_docs: + return output - asset_entity_by_ids = {asset["_id"]: asset for asset in asset_entities} + asset_docs_by_ids = {asset["_id"]: asset for asset in asset_docs} - subsets = list(legacy_io.find({ - "type": "subset", - "parent": {"$in": list(asset_entity_by_ids.keys())} - })) + project_name = legacy_io.active_project() + subsets = list(get_subsets( + project_name, asset_ids=asset_docs_by_ids.keys() + )) subset_entity_by_ids = {subset["_id"]: subset for subset in subsets} - sorted_versions = list(legacy_io.find({ - "type": "version", - "parent": {"$in": list(subset_entity_by_ids.keys())} - }).sort("name", -1)) + last_version_by_subset_id = get_last_versions( + project_name, subset_entity_by_ids.keys() + ) + last_version_docs_by_id = { + version["_id"]: version + for version in last_version_by_subset_id.values() + } + repre_docs = get_representations( + project_name, version_ids=last_version_docs_by_id.keys() + ) - subset_id_with_latest_version = [] - last_versions_by_id = {} - for version in sorted_versions: - subset_id = version["parent"] - if subset_id in subset_id_with_latest_version: - continue - subset_id_with_latest_version.append(subset_id) - last_versions_by_id[version["_id"]] = version + for repre_doc in repre_docs: + version_id = repre_doc["parent"] + version_doc = last_version_docs_by_id[version_id] - repres = legacy_io.find({ - "type": "representation", - "parent": {"$in": list(last_versions_by_id.keys())} - }) + subset_id = version_doc["parent"] + subset_doc = subset_entity_by_ids[subset_id] - output = {} - for repre in repres: - version_id = repre["parent"] - version = last_versions_by_id[version_id] - - subset_id = version["parent"] - subset = subset_entity_by_ids[subset_id] - - asset_id = subset["parent"] - asset = asset_entity_by_ids[asset_id] + asset_id = subset_doc["parent"] + asset_doc = asset_docs_by_ids[asset_id] if asset_id not in output: output[asset_id] = { - "asset_entity": asset, + "asset_entity": asset_doc, "subsets": {} } if subset_id not in output[asset_id]["subsets"]: output[asset_id]["subsets"][subset_id] = { - "subset_entity": subset, + "subset_entity": subset_doc, "version": { - "version_entity": version, + "version_entity": version_doc, "repres": [] } } output[asset_id]["subsets"][subset_id]["version"]["repres"].append( - repre + repre_doc ) return output @@ -1790,35 +1754,19 @@ def get_custom_workfile_template_by_string_context( context. (Existence of formatted path is not validated.) """ - if dbcon is None: - from openpype.pipeline import AvalonMongoDB + project_name = None + if anatomy is not None: + project_name = anatomy.project_name - dbcon = AvalonMongoDB() + if not project_name and dbcon is not None: + project_name = dbcon.active_project() - dbcon.install() + if not project_name: + raise ValueError("Can't determina project") - if dbcon.Session["AVALON_PROJECT"] != project_name: - dbcon.Session["AVALON_PROJECT"] = project_name - - project_doc = dbcon.find_one( - {"type": "project"}, - # All we need is "name" and "data.code" keys - { - "name": 1, - "data.code": 1 - } - ) - asset_doc = dbcon.find_one( - { - "type": "asset", - "name": asset_name - }, - # All we need is "name" and "data.tasks" keys - { - "name": 1, - "data.tasks": 1 - } - ) + project_doc = get_project(project_name, fields=["name", "data.code"]) + asset_doc = get_asset_by_name( + project_name, asset_name, fields=["name", "data.tasks"]) return get_custom_workfile_template_by_context( template_profiles, project_doc, asset_doc, task_name, anatomy From 57e0f576c82df6f1510e8954e99db98fb580d1d7 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Thu, 30 Jun 2022 16:18:19 +0200 Subject: [PATCH 06/16] removed unused import --- openpype/lib/avalon_context.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/openpype/lib/avalon_context.py b/openpype/lib/avalon_context.py index 7b00032027..9fc632aa27 100644 --- a/openpype/lib/avalon_context.py +++ b/openpype/lib/avalon_context.py @@ -7,8 +7,6 @@ import platform import logging import collections import functools - -from bson.objectid import ObjectId import warnings from openpype.client import ( From 13123a41277e08fec2345a888834c254e10c8034 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Thu, 30 Jun 2022 16:18:34 +0200 Subject: [PATCH 07/16] get_system_general_anatomy_data can accept already prepare system settings --- openpype/lib/avalon_context.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/openpype/lib/avalon_context.py b/openpype/lib/avalon_context.py index 9fc632aa27..70a4e59279 100644 --- a/openpype/lib/avalon_context.py +++ b/openpype/lib/avalon_context.py @@ -262,8 +262,9 @@ def get_asset(asset_name=None): return asset_document -def get_system_general_anatomy_data(): - system_settings = get_system_settings() +def get_system_general_anatomy_data(system_settings=None): + if not system_settings: + system_settings = get_system_settings() studio_name = system_settings["general"]["studio_name"] studio_code = system_settings["general"]["studio_code"] return { From cb62b175719c9df96ca4641c7a93ad3b1f12777c Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Thu, 30 Jun 2022 16:29:54 +0200 Subject: [PATCH 08/16] added ability to receive all project documents --- openpype/client/__init__.py | 2 ++ openpype/client/entities.py | 15 +++++++++++++++ 2 files changed, 17 insertions(+) diff --git a/openpype/client/__init__.py b/openpype/client/__init__.py index e1c8ea9f03..4fee889f18 100644 --- a/openpype/client/__init__.py +++ b/openpype/client/__init__.py @@ -1,6 +1,7 @@ from .entities import ( get_projects, get_project, + get_whole_project, get_asset_by_id, get_asset_by_name, @@ -40,6 +41,7 @@ from .entities import ( __all__ = ( "get_projects", "get_project", + "get_whole_project", "get_asset_by_id", "get_asset_by_name", diff --git a/openpype/client/entities.py b/openpype/client/entities.py index 9771461d2e..698fba796d 100644 --- a/openpype/client/entities.py +++ b/openpype/client/entities.py @@ -94,6 +94,21 @@ def get_project(project_name, active=True, inactive=False, fields=None): return conn.find_one(query_filter, _prepare_fields(fields)) +def get_whole_project(project_name): + """Receive all documents from project. + + Helper that can be used to get all document from whole project. For example + for backups etc. + + Returns: + Cursor: Query cursor as iterable which returns all documents from + project collection. + """ + + conn = _get_project_connection(project_name) + return conn.find({}) + + def get_asset_by_id(project_name, asset_id, fields=None): """Receive asset data by it's id. From 7416508e8056bcab5de784b4dda4fea0c9859f92 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Thu, 30 Jun 2022 16:33:21 +0200 Subject: [PATCH 09/16] use query functions in project backpack --- openpype/lib/project_backpack.py | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/openpype/lib/project_backpack.py b/openpype/lib/project_backpack.py index 396479c725..f0188e6765 100644 --- a/openpype/lib/project_backpack.py +++ b/openpype/lib/project_backpack.py @@ -24,7 +24,10 @@ from bson.json_util import ( dumps, CANONICAL_JSON_OPTIONS ) - +from openpype.client import ( + get_project, + get_whole_project, +) from openpype.pipeline import AvalonMongoDB DOCUMENTS_FILE_NAME = "database" @@ -55,9 +58,7 @@ def pack_project(project_name, destination_dir=None): """ print("Creating package of project \"{}\"".format(project_name)) # Validate existence of project - dbcon = AvalonMongoDB() - dbcon.Session["AVALON_PROJECT"] = project_name - project_doc = dbcon.find_one({"type": "project"}) + project_doc = get_project(project_name) if not project_doc: raise ValueError("Project \"{}\" was not found in database".format( project_name @@ -118,7 +119,7 @@ def pack_project(project_name, destination_dir=None): temp_docs_json = s.name # Query all project documents and store them to temp json - docs = list(dbcon.find({})) + docs = list(get_whole_project(project_name)) data = dumps( docs, json_options=CANONICAL_JSON_OPTIONS ) @@ -147,7 +148,7 @@ def pack_project(project_name, destination_dir=None): # Cleanup os.remove(temp_docs_json) os.remove(temp_metadata_json) - dbcon.uninstall() + print("*** Packing finished ***") @@ -207,7 +208,7 @@ def unpack_project(path_to_zip, new_root=None): print("Using different root path {}".format(new_root)) root_path = new_root - project_doc = collection.find_one({"type": "project"}) + project_doc = get_project(project_name) roots = project_doc["config"]["roots"] key = tuple(roots.keys())[0] update_key = "config.roots.{}.{}".format(key, low_platform) From 8f46a73ab710ed98a55b475dba44a558213d3a17 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Thu, 30 Jun 2022 16:33:25 +0200 Subject: [PATCH 10/16] use query functions in applications --- openpype/lib/applications.py | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/openpype/lib/applications.py b/openpype/lib/applications.py index a81bdeca0f..6bdf0a237f 100644 --- a/openpype/lib/applications.py +++ b/openpype/lib/applications.py @@ -11,6 +11,10 @@ from abc import ABCMeta, abstractmethod import six +from openpype.client import ( + get_project, + get_asset_by_name, +) from openpype.settings import ( get_system_settings, get_project_settings, @@ -1313,11 +1317,8 @@ def get_app_environments_for_context( dbcon.install() # Project document - project_doc = dbcon.find_one({"type": "project"}) - asset_doc = dbcon.find_one({ - "type": "asset", - "name": asset_name - }) + project_doc = get_project(project_name) + asset_doc = get_asset_by_name(project_name, asset_name) if modules_manager is None: from openpype.modules import ModulesManager From eed6acd53c36e7c831d0201cb9d45a9dd31768fd Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Thu, 30 Jun 2022 16:50:36 +0200 Subject: [PATCH 11/16] use query function in plugin tools --- openpype/lib/plugin_tools.py | 19 ++++++------------- 1 file changed, 6 insertions(+), 13 deletions(-) diff --git a/openpype/lib/plugin_tools.py b/openpype/lib/plugin_tools.py index bcbf06a0e8..1d3c1eec6b 100644 --- a/openpype/lib/plugin_tools.py +++ b/openpype/lib/plugin_tools.py @@ -6,10 +6,10 @@ import logging import re import json -from .profiles_filtering import filter_profiles - +from openpype.client import get_asset_by_id from openpype.settings import get_project_settings +from .profiles_filtering import filter_profiles log = logging.getLogger(__name__) @@ -135,24 +135,17 @@ def get_subset_name( This is legacy function should be replaced with `get_subset_name_with_asset_doc` where asset document is expected. """ - if dbcon is None: - from openpype.pipeline import AvalonMongoDB - dbcon = AvalonMongoDB() - dbcon.Session["AVALON_PROJECT"] = project_name + if project_name is None: + project_name = dbcon.project_name - dbcon.install() - - asset_doc = dbcon.find_one( - {"_id": asset_id}, - {"data.tasks": True} - ) or {} + asset_doc = get_asset_by_id(project_name, asset_id, fields=["data.tasks"]) return get_subset_name_with_asset_doc( family, variant, task_name, - asset_doc, + asset_doc or {}, project_name, host_name, default_template, From 720aba7c44e24cd80cbcb3d498e73ec3cf503659 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Thu, 30 Jun 2022 16:53:24 +0200 Subject: [PATCH 12/16] modified usdlib to use query functions and use anatomy for formatting --- openpype/lib/usdlib.py | 37 ++++++++++++++++++++++--------------- 1 file changed, 22 insertions(+), 15 deletions(-) diff --git a/openpype/lib/usdlib.py b/openpype/lib/usdlib.py index 86de19b4be..2943f6660a 100644 --- a/openpype/lib/usdlib.py +++ b/openpype/lib/usdlib.py @@ -8,10 +8,9 @@ except ImportError: # Allow to fall back on Multiverse 6.3.0+ pxr usd library from mvpxr import Usd, UsdGeom, Sdf, Kind -from openpype.pipeline import ( - registered_root, - legacy_io, -) +from openpype.client import get_project, get_asset_by_name +from openpype.pipeline import legacy_io +from openpypa.lib import Anatomy log = logging.getLogger(__name__) @@ -128,7 +127,8 @@ def create_model(filename, asset, variant_subsets): """ - asset_doc = legacy_io.find_one({"name": asset, "type": "asset"}) + project_name = legacy_io.active_project() + asset_doc = get_asset_by_name(project_name, asset) assert asset_doc, "Asset not found: %s" % asset variants = [] @@ -178,7 +178,8 @@ def create_shade(filename, asset, variant_subsets): """ - asset_doc = legacy_io.find_one({"name": asset, "type": "asset"}) + project_name = legacy_io.active_project() + asset_doc = get_asset_by_name(project_name, asset) assert asset_doc, "Asset not found: %s" % asset variants = [] @@ -213,7 +214,8 @@ def create_shade_variation(filename, asset, model_variant, shade_variants): """ - asset_doc = legacy_io.find_one({"name": asset, "type": "asset"}) + project_name = legacy_io.active_project() + asset_doc = get_asset_by_name(project_name, asset) assert asset_doc, "Asset not found: %s" % asset variants = [] @@ -313,21 +315,25 @@ def get_usd_master_path(asset, subset, representation): """ - project = legacy_io.find_one( - {"type": "project"}, projection={"config.template.publish": True} + project_name = legacy_io.active_project() + anatomy = Anatomy(project_name) + project_doc = get_project( + project_name, + fields=["name", "data.code"] ) - template = project["config"]["template"]["publish"] if isinstance(asset, dict) and "name" in asset: # Allow explicitly passing asset document asset_doc = asset else: - asset_doc = legacy_io.find_one({"name": asset, "type": "asset"}) + asset_doc = get_asset_by_name(project_name, asset, fields=["name"]) - path = template.format( - **{ - "root": registered_root(), - "project": legacy_io.Session["AVALON_PROJECT"], + formatted_result = anatomy.format( + { + "project": { + "name": project_name, + "code": project_doc.get("data", {}).get("code") + }, "asset": asset_doc["name"], "subset": subset, "representation": representation, @@ -335,6 +341,7 @@ def get_usd_master_path(asset, subset, representation): } ) + path = formatted_result["publish"]["path"] # Remove the version folder subset_folder = os.path.dirname(os.path.dirname(path)) master_folder = os.path.join(subset_folder, "master") From 2296a56118265962c87b9c44964affff9fabb129 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Thu, 30 Jun 2022 16:56:15 +0200 Subject: [PATCH 13/16] fix anatomy import --- openpype/lib/usdlib.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/openpype/lib/usdlib.py b/openpype/lib/usdlib.py index 2943f6660a..20703ee308 100644 --- a/openpype/lib/usdlib.py +++ b/openpype/lib/usdlib.py @@ -9,8 +9,7 @@ except ImportError: from mvpxr import Usd, UsdGeom, Sdf, Kind from openpype.client import get_project, get_asset_by_name -from openpype.pipeline import legacy_io -from openpypa.lib import Anatomy +from openpype.pipeline import legacy_io, Anatomy log = logging.getLogger(__name__) From bdff4d5eb0c03ec2a6687e3baa963f6dc5f50cf4 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Thu, 30 Jun 2022 17:05:13 +0200 Subject: [PATCH 14/16] modified docstring --- openpype/client/entities.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/openpype/client/entities.py b/openpype/client/entities.py index 698fba796d..cb20916153 100644 --- a/openpype/client/entities.py +++ b/openpype/client/entities.py @@ -1180,9 +1180,8 @@ def get_workfile_info( Warning: Query is based on filename and context which does not meant it will - find always right expected result. There is a limited amound of usage - and is recommended to not expect that all workfiles are stored in - database. + find always right and expected result. Information have limited usage + and is not recommended to use it as source information about workfile. Args: project_name (str): Name of project where to look for queried entities. From 49cb5a3e162a40e31c02d66bbbe2b2ad90a2b246 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Thu, 30 Jun 2022 17:28:22 +0200 Subject: [PATCH 15/16] hound fixes --- openpype/lib/avalon_context.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/openpype/lib/avalon_context.py b/openpype/lib/avalon_context.py index 70a4e59279..61976f5a5b 100644 --- a/openpype/lib/avalon_context.py +++ b/openpype/lib/avalon_context.py @@ -371,7 +371,9 @@ def get_latest_version(asset_name, subset_name, dbcon=None, project_name=None): ) return None - version_doc = get_last_version_by_subset_id(project_name, subset_doc["_id"]) + version_doc = get_last_version_by_subset_id( + project_name, subset_doc["_id"] + ) if not version_doc: log.info( "Subset \"{}\" does not have any version yet.".format(subset_name) @@ -960,7 +962,9 @@ class BuildWorkfile: # Get current asset name and entity project_name = legacy_io.active_project() current_asset_name = legacy_io.Session["AVALON_ASSET"] - current_asset_entity = get_asset_by_name(project_name, current_asset_name) + current_asset_entity = get_asset_by_name( + project_name, current_asset_name + ) # Skip if asset was not found if not current_asset_entity: print("Asset entity with name `{}` was not found".format( From eb352d0958fda52b2a77ca10bb56a9fb80894e16 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Mon, 4 Jul 2022 16:51:54 +0200 Subject: [PATCH 16/16] fix 'hero' kwargs --- openpype/lib/avalon_context.py | 1 - 1 file changed, 1 deletion(-) diff --git a/openpype/lib/avalon_context.py b/openpype/lib/avalon_context.py index 9ffa5f2732..76ed6cbbd3 100644 --- a/openpype/lib/avalon_context.py +++ b/openpype/lib/avalon_context.py @@ -195,7 +195,6 @@ def is_latest(representation): version = get_version_by_id( project_name, representation["parent"], - hero=True, fields=["_id", "type", "parent"] ) if version["type"] == "hero_version":