From 85748589ef4cdbda6ad65c59c3130ad2901dc0b4 Mon Sep 17 00:00:00 2001 From: Ondrej Samohel Date: Thu, 2 Jun 2022 11:18:02 +0200 Subject: [PATCH 01/51] :bug: skip empty attributes --- openpype/hosts/maya/plugins/publish/collect_look.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/openpype/hosts/maya/plugins/publish/collect_look.py b/openpype/hosts/maya/plugins/publish/collect_look.py index 323bede761..9b6d1d999c 100644 --- a/openpype/hosts/maya/plugins/publish/collect_look.py +++ b/openpype/hosts/maya/plugins/publish/collect_look.py @@ -601,6 +601,9 @@ class CollectLook(pyblish.api.InstancePlugin): source, computed_source)) + if not source: + self.log.info("source is empty, skipping...") + continue # We replace backslashes with forward slashes because V-Ray # can't handle the UDIM files with the backslashes in the # paths as the computed patterns From 5526435e41554a685864218fd601dd276cfc0cdd Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Sat, 4 Jun 2022 21:07:49 +0200 Subject: [PATCH 02/51] initial commit of client entities functions --- openpype/client/__init__.py | 59 +++ openpype/client/entities.py | 897 ++++++++++++++++++++++++++++++++++++ 2 files changed, 956 insertions(+) create mode 100644 openpype/client/__init__.py create mode 100644 openpype/client/entities.py diff --git a/openpype/client/__init__.py b/openpype/client/__init__.py new file mode 100644 index 0000000000..861f828e67 --- /dev/null +++ b/openpype/client/__init__.py @@ -0,0 +1,59 @@ +from .entities import ( + get_projects, + get_project, + + get_asset, + get_assets, + get_asset_ids_with_subsets, + + get_subset, + get_subsets, + get_subset_families, + + get_version_by_name, + get_version, + get_versions, + get_last_versions, + get_last_version_for_subset, + get_hero_version, + get_hero_versions, + get_version_links, + + get_representation, + get_representation_by_name, + get_representations, + get_representations_parents, + + get_thumbnail, + get_thumbnail_id_from_source, +) + +__all__ = ( + "get_projects", + "get_project", + + "get_asset", + "get_assets", + "get_asset_ids_with_subsets", + + "get_subset", + "get_subsets", + "get_subset_families", + + "get_version_by_name", + "get_version", + "get_versions", + "get_last_versions", + "get_last_version_for_subset", + "get_hero_version", + "get_hero_versions", + "get_version_links", + + "get_representation", + "get_representation_by_name", + "get_representations", + "get_representations_parents", + + "get_thubmnail", + "get_thumbnail_id_from_source", +) diff --git a/openpype/client/entities.py b/openpype/client/entities.py new file mode 100644 index 0000000000..4b52f8cf2d --- /dev/null +++ b/openpype/client/entities.py @@ -0,0 +1,897 @@ +"""Unclear if these will have public functions like these. + +Goal is that most of functions here are called on (or with) an object +that has project name as a context (e.g. on 'ProjectEntity'?). + ++ We will need more specific functions doing wery specific queires really fast. +""" + +import os +import collections + +import six +from bson.objectid import ObjectId + +from openpype.lib.mongo import OpenPypeMongoConnection + + +def _get_project_connection(project_name=None): + db_name = os.environ.get("AVALON_DB") or "avalon" + mongodb = OpenPypeMongoConnection.get_mongo_client()[db_name] + if project_name: + return mongodb[project_name] + return mongodb + + +def _prepare_fields(fields): + if not fields: + return None + + output = { + field: True + for field in fields + } + if "_id" not in output: + output["_id"] = True + return output + + +def _convert_id(in_id): + if isinstance(in_id, six.string_types): + return ObjectId(in_id) + return in_id + + +def _convert_ids(in_ids): + _output = set() + for in_id in in_ids: + if in_id is not None: + _output.add(_convert_id(in_id)) + return list(_output) + + +def get_projects(active=True, inactive=False, fields=None): + mongodb = _get_project_connection() + for project_name in mongodb.collection_names(): + if project_name in ("system.indexes",): + continue + project_doc = get_project( + project_name, active=active, inactive=inactive, fields=fields + ) + if project_doc is not None: + yield project_doc + + +def get_project(project_name, active=True, inactive=False, fields=None): + # Skip if both are disabled + if not active and not inactive: + return None + + query_filter = {"type": "project"} + # Keep query untouched if both should be available + if active and inactive: + pass + + # Add filter to keep only active + elif active: + query_filter["$or"] = [ + {"data.active": {"$exists": False}}, + {"data.active": True}, + ] + + # Add filter to keep only inactive + elif inactive: + query_filter["$or"] = [ + {"data.active": {"$exists": False}}, + {"data.active": False}, + ] + + conn = _get_project_connection(project_name) + return conn.find_one(query_filter, _prepare_fields(fields)) + + +def get_asset(project_name, asset_name=None, asset_id=None, fields=None): + query_filter = {"type": "asset"} + has_filter = False + if asset_name: + has_filter = True + query_filter["name"] = asset_name + + if asset_id: + has_filter = True + query_filter["_id"] = _convert_id(asset_id) + + # Avoid random asset quqery + if not has_filter: + return None + + conn = _get_project_connection(project_name) + + return conn.find_one(query_filter, _prepare_fields(fields)) + + +def get_assets( + project_name, asset_ids=None, asset_names=None, archived=False, fields=None +): + asset_types = ["asset"] + if archived: + asset_types.append("archived_asset") + + if len(asset_types) == 1: + query_filter = {"type": asset_types[0]} + else: + query_filter = {"type": {"$in": asset_types}} + + if asset_ids is not None: + asset_ids = _convert_ids(asset_ids) + if not asset_ids: + return [] + query_filter["_id"] = {"$in": asset_ids} + + if asset_names is not None: + if not asset_names: + return [] + query_filter["name"] = {"$in": list(asset_names)} + + conn = _get_project_connection(project_name) + + return conn.find(query_filter, _prepare_fields(fields)) + + +def get_asset_ids_with_subsets(project_name, asset_ids=None): + subset_query = { + "type": "subset" + } + if asset_ids is not None: + asset_ids = _convert_ids(asset_ids) + if not asset_ids: + return [] + subset_query["parent"] = {"$in": asset_ids} + + conn = _get_project_connection(project_name) + result = conn.aggregate([ + { + "$match": subset_query + }, + { + "$group": { + "_id": "$parent", + "count": {"$sum": 1} + } + } + ]) + asset_ids_with_subsets = [] + for item in result: + asset_id = item["_id"] + count = item["count"] + if count > 0: + asset_ids_with_subsets.append(asset_id) + return asset_ids_with_subsets + + +def get_subset( + project_name, + subset_id=None, + subset_name=None, + asset_id=None, + fields=None +): + """Single subset document by subset id or name and parent id. + + When subset id is defined it is not needed to add any other arguments but + subset name filter must be always combined with asset id (or subset id). + + Question: + This could be split into more functions? + + Args: + project_name (str): Name of project where to look for subset. + subset_id (ObjectId): Id of subset which should be found. + subset_name (str): Name of asset. Must be combined with 'asset_id' or + 'subset_id' arguments otherwise result is 'None'. + asset_id (ObjectId): Id of parent asset. Must be combined with + 'subset_name' or 'subset_id'. + fields (list): Fields that should be returned. All fields are + returned if 'None' is passed. + + Returns: + None: If subset with specified filters was not found. + Dict: Subset document which can be reduced to specified 'fields'. + """ + + query_filters = {"type": "subset"} + has_valid_filters = False + if subset_id is not None: + query_filters["_id"] = _convert_id(asset_id) + has_valid_filters = True + + if subset_name is not None: + if asset_id is not None: + has_valid_filters = True + query_filters["name"] = subset_name + + if asset_id is not None: + query_filters["parent"] = _convert_id(asset_id) + + if not has_valid_filters: + return None + + conn = _get_project_connection(project_name) + return conn.find_one(query_filters, _prepare_fields(fields)) + + +def get_subsets( + project_name, + asset_ids=None, + subset_ids=None, + subset_names=None, + archived=False, + fields=None +): + subset_types = ["subset"] + if archived: + subset_types.append("archived_subset") + + if len(subset_types) == 1: + query_filter = {"type": subset_types[0]} + else: + query_filter = {"type": {"$in": subset_types}} + + if asset_ids is not None: + asset_ids = _convert_ids(asset_ids) + if not asset_ids: + return [] + query_filter["parent"] = {"$in": asset_ids} + + if subset_ids is not None: + subset_ids = _convert_ids(subset_ids) + if not subset_ids: + return [] + query_filter["_id"] = {"$in": subset_ids} + + if subset_names is not None: + if not subset_names: + return [] + query_filter["name"] = {"$in": list(subset_names)} + + conn = _get_project_connection(project_name) + return conn.find(query_filter, _prepare_fields(fields)) + + +def get_subset_families(project_name, subset_ids=None): + subset_filter = { + "type": "subset" + } + if subset_ids is not None: + if not subset_ids: + return set() + subset_filter["_id"] = {"$in": list(subset_ids)} + + conn = _get_project_connection(project_name) + result = list(conn.aggregate([ + {"$match": subset_filter}, + {"$project": { + "family": {"$arrayElemAt": ["$data.families", 0]} + }}, + {"$group": { + "_id": "family_group", + "families": {"$addToSet": "$family"} + }} + ])) + if result: + return set(result[0]["families"]) + return set() + + +def get_version_by_name(project_name, subset_id, version, fields=None): + conn = _get_project_connection(project_name) + query_filter = { + "type": "version", + "parent": _convert_id(subset_id), + "name": version + } + return conn.find_one(query_filter, _prepare_fields(fields)) + + +def get_version(project_name, version_id, fields=None): + if not version_id: + return None + conn = _get_project_connection(project_name) + query_filter = { + "type": {"$in": ["version", "hero_version"]}, + "_id": _convert_id(version_id) + } + return conn.find_one(query_filter, _prepare_fields(fields)) + + +def _get_versions( + project_name, + subset_ids=None, + version_ids=None, + standard=True, + hero=False, + fields=None +): + version_types = [] + if standard: + version_types.append("version") + + if hero: + version_types.append("hero_version") + + if not version_types: + return [] + elif len(version_types) == 1: + query_filter = {"type": version_types[0]} + else: + query_filter = {"type": {"$in": version_types}} + + if subset_ids is not None: + subset_ids = _convert_ids(subset_ids) + if not subset_ids: + return [] + query_filter["parent"] = {"$in": subset_ids} + + if version_ids is not None: + version_ids = _convert_ids(version_ids) + if not version_ids: + return [] + query_filter["_id"] = {"$in": version_ids} + + conn = _get_project_connection(project_name) + + return conn.find(query_filter, _prepare_fields(fields)) + + +def get_versions( + project_name, + subset_ids=None, + version_ids=None, + hero=False, + fields=None +): + return _get_versions( + project_name, + subset_ids, + version_ids, + standard=True, + hero=hero, + fields=fields + ) + + +def get_hero_version( + project_name, + subset_id=None, + version_id=None, + fields=None +): + if not subset_id and not version_id: + return None + + subset_ids = None + if subset_id is not None: + subset_ids = [subset_id] + + version_ids = None + if version_id is not None: + version_ids = [version_id] + + versions = list(_get_versions( + project_name, + subset_ids=subset_ids, + version_ids=version_ids, + standard=False, + hero=True, + fields=fields + )) + if versions: + return versions[0] + return None + + +def get_hero_versions( + project_name, + subset_ids=None, + version_ids=None, + fields=None +): + return _get_versions( + project_name, + subset_ids, + version_ids, + standard=False, + hero=True, + fields=fields + ) + + +def get_version_links(project_name, version_id, fields=None): + conn = _get_project_connection(project_name) + # Does make sense to look for hero versions? + query_filter = { + "type": "version", + "data.inputLinks.input": _convert_id(version_id) + } + return conn.find(query_filter, _prepare_fields(fields)) + + +def get_last_versions(project_name, subset_ids, fields=None): + """Retrieve all latest versions for entered subset_ids. + + Args: + subset_ids (list): List of subset ids. + + Returns: + dict: Key is subset id and value is last version name. + """ + + subset_ids = _convert_ids(subset_ids) + if not subset_ids: + return {} + + _pipeline = [ + # Find all versions of those subsets + {"$match": { + "type": "version", + "parent": {"$in": subset_ids} + }}, + # Sorting versions all together + {"$sort": {"name": 1}}, + # Group them by "parent", but only take the last + {"$group": { + "_id": "$parent", + "_version_id": {"$last": "$_id"} + }} + ] + + conn = _get_project_connection(project_name) + version_ids = [ + doc["_version_id"] + for doc in conn.aggregate(_pipeline) + ] + version_docs = get_versions( + project_name, version_ids=version_ids, fields=fields + ) + + return { + version_doc["parent"]: version_doc + for version_doc in version_docs + } + + +def get_last_version_for_subset( + project_name, subset_id=None, subset_name=None, asset_id=None, fields=None +): + subset_doc = get_subset( + project_name, + subset_id=subset_id, + subset_name=subset_name, + asset_id=asset_id, + fields=["_id"] + ) + if not subset_doc: + return None + subset_id = subset_doc["_id"] + last_versions = get_last_versions( + project_name, subset_ids=[subset_id], fields=fields + ) + return last_versions.get(subset_id) + + +def get_representation( + project_name, + representation_id=None, + representation_name=None, + version_id=None, + fields=None +): + if not representation_id: + if not representation_name or not version_id: + return None + + repre_types = ["representation", "archived_representations"] + query_filter = { + "type": {"$in": repre_types} + } + if representation_id is not None: + query_filter["_id"] = _convert_id(representation_id) + + if representation_name is not None: + query_filter["name"] = representation_name + + if version_id is not None: + query_filter["parent"] = version_id + + conn = _get_project_connection(project_name) + + return conn.find_one(query_filter, _prepare_fields(fields)) + + +def get_representation_by_name( + project_name, representation_name, version_id, fields=None +): + if not version_id or not representation_name: + return None + repre_types = ["representation", "archived_representations"] + query_filter = { + "type": {"$in": repre_types}, + "name": representation_name, + "parent": _convert_id(version_id) + } + + conn = _get_project_connection(project_name) + + return conn.find_one(query_filter, _prepare_fields(fields)) + + +def get_representations( + project_name, + representation_ids=None, + representation_names=None, + version_ids=None, + extensions=None, + names_by_version_ids=None, + check_site_name=False, + archived=False, + fields=None +): + repre_types = ["representation"] + if archived: + repre_types.append("archived_representations") + if len(repre_types) == 1: + query_filter = {"type": repre_types[0]} + else: + query_filter = {"type": {"$in": repre_types}} + + if check_site_name: + query_filter["files.site.name"] = {"$exists": True} + + if representation_ids is not None: + representation_ids = _convert_ids(representation_ids) + if not representation_ids: + return [] + query_filter["_id"] = {"$in": representation_ids} + + if representation_names is not None: + if not representation_names: + return [] + query_filter["name"] = {"$in": list(representation_names)} + + if version_ids is not None: + version_ids = _convert_ids(version_ids) + if not version_ids: + return [] + query_filter["parent"] = {"$in": version_ids} + + if extensions is not None: + if not extensions: + return [] + query_filter["context.ext"] = {"$in": list(extensions)} + + if names_by_version_ids is not None: + or_query = [] + for version_id, names in names_by_version_ids.items(): + if version_id and names: + or_query.append({ + "parent": _convert_id(version_id), + "name": {"$in": list(names)} + }) + if not or_query: + return [] + query_filter["$or"] = or_query + + conn = _get_project_connection(project_name) + + return conn.find(query_filter, _prepare_fields(fields)) + + +def get_representations_parents(project_name, representations): + repres_by_version_id = collections.defaultdict(list) + versions_by_version_id = {} + versions_by_subset_id = collections.defaultdict(list) + subsets_by_subset_id = {} + subsets_by_asset_id = collections.defaultdict(list) + for representation in representations: + repre_id = representation["_id"] + version_id = representation["parent"] + repres_by_version_id[version_id].append(representation) + + versions = get_versions( + project_name, version_ids=repres_by_version_id.keys() + ) + for version in versions: + version_id = version["_id"] + subset_id = version["parent"] + versions_by_version_id[version_id] = version + versions_by_subset_id[subset_id].append(version) + + subsets = get_subsets( + project_name, subset_ids=versions_by_subset_id.keys() + ) + for subset in subsets: + subset_id = subset["_id"] + asset_id = subset["parent"] + subsets_by_subset_id[subset_id] = subset + subsets_by_asset_id[asset_id].append(subset) + + assets = get_assets(project_name, asset_ids=subsets_by_asset_id.keys()) + assets_by_id = { + asset["_id"]: asset + for asset in assets + } + + project = get_project(project_name) + + output = {} + for version_id, representations in repres_by_version_id.items(): + asset = None + subset = None + version = versions_by_version_id.get(version_id) + if version: + subset_id = version["parent"] + subset = subsets_by_subset_id.get(subset_id) + if subset: + asset_id = subset["parent"] + asset = assets_by_id.get(asset_id) + + for representation in representations: + repre_id = representation["_id"] + output[repre_id] = (version, subset, asset, project) + return output + + +def get_thumbnail_id_from_source(project_name, src_type, src_id): + if not src_type or not src_id: + return None + + query_filter = {"_id": _convert_id(src_id)} + + 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") + return None + + +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) + return conn.find(query_filter, _prepare_fields(fields)) + + +""" +Custom data storage: +- Webpublisher - jobs +- Ftrack - events + +openpype/tools/assetlinks/widgets.py +- SimpleLinkView + Query: + - get_versions + - get_subsets + - get_assets + - get_version_links + +openpype/tools/creator/window.py +- CreatorWindow + Query: + - get_asset + - get_subsets + +openpype/tools/launcher/models.py +- LauncherModel + Query: + - get_project + - get_assets + +openpype/tools/libraryloader/app.py +- LibraryLoaderWindow + Query: + - get_project + +openpype/tools/loader/app.py +- LoaderWindow + Query: + - get_project +- show + Query: + - get_projects + +openpype/tools/loader/model.py +- SubsetsModel + Query: + - get_assets + - get_subsets + - get_last_versions + - get_versions + - get_hero_versions + - get_version_by_name +- RepresentationModel + Query: + - get_representations + - sync server specific queries (separated into multiple functions?) + - NOT REPLACED + +openpype/tools/loader/widgets.py +- FamilyModel + Query: + - get_subset_families +- VersionTextEdit + Query: + - get_subset + - get_version +- SubsetWidget + Query: + - get_subsets + - get_representations +- RepresentationWidget + Query: + - get_subsets + - get_versions + - get_representations +- ThumbnailWidget + Query: + - get_thumbnail_id_from_source + - get_thumbnail + +openpype/tools/mayalookassigner/app.py +- MayaLookAssignerWindow + Query: + - get_last_version_for_subset + +openpype/tools/mayalookassigner/commands.py +- create_items_from_nodes + Query: + - get_asset + +openpype/tools/mayalookassigner/vray_proxies.py +- get_look_relationships + Query: + - get_representation_by_name +- load_look + Query: + - get_representation_by_name +- vrayproxy_assign_look + Query: + - get_last_version_for_subset + +openpype/tools/project_manager/project_manager/model.py +- HierarchyModel + Query: + - get_asset_ids_with_subsets + - get_project + - get_assets + +openpype/tools/project_manager/project_manager/view.py +- ProjectDocCache + Query: + - get_project + +openpype/tools/project_manager/project_manager/widgets.py +- CreateProjectDialog + Query: + - get_projects + +openpype/tools/publisher/widgets/create_dialog.py +- CreateDialog + Query: + - get_asset + - get_subsets + +openpype/tools/publisher/control.py +- AssetDocsCache + Query: + - get_assets + +openpype/tools/sceneinventory/model.py +- InventoryModel + Query: + - get_asset + - get_subset + - get_version + - get_last_version_for_subset + - get_representation + +openpype/tools/sceneinventory/switch_dialog.py +- SwitchAssetDialog + Query: + - get_asset + - get_assets + - get_subset + - get_subsets + - get_versions + - get_hero_versions + - get_last_versions + - get_representations + +openpype/tools/sceneinventory/view.py +- SceneInventoryView + Query: + - get_version + - get_versions + - get_hero_versions + - get_representation + - get_representations + +openpype/tools/standalonepublish/widgets/model_asset.py +- AssetModel + Query: + - get_assets + +openpype/tools/standalonepublish/widgets/widget_asset.py +- AssetWidget + Query: + - get_project + - get_asset + +openpype/tools/standalonepublish/widgets/widget_family.py +- FamilyWidget + Query: + - get_asset + - get_subset + - get_subsets + - get_last_version_for_subset + +openpype/tools/standalonepublish/app.py +- Window + Query: + - get_asset + +openpype/tools/texture_copy/app.py +- TextureCopy + Query: + - get_project + - get_asset + +openpype/tools/workfiles/files_widget.py +- FilesWidget + Query: + - get_asset + +openpype/tools/workfiles/model.py +- PublishFilesModel + Query: + - get_subsets + - get_versions + - get_representations + +openpype/tools/workfiles/save_as_dialog.py +- build_workfile_data + Query: + - get_project + - get_asset + +openpype/tools/workfiles/window.py +- Window + Query: + - get_asset + +openpype/tools/utils/assets_widget.py +- AssetModel + Query: + - get_project + - get_assets + +openpype/tools/utils/delegates.py +- VersionDelegate + Query: + - get_versions + - get_hero_versions + +openpype/tools/utils/lib.py +- GroupsConfig + Query: + - get_project +- FamilyConfigCache + Query: + - get_asset + +openpype/tools/utils/tasks_widget.py +- TasksModel + Query: + - get_project + - get_asset +""" From 7109d932ec2ed70a2042d2df7563de368293a88e Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Sat, 4 Jun 2022 21:08:42 +0200 Subject: [PATCH 03/51] replace queries in tools --- openpype/tools/assetlinks/widgets.py | 159 +++-- openpype/tools/creator/window.py | 21 +- openpype/tools/launcher/models.py | 21 +- openpype/tools/libraryloader/app.py | 57 +- openpype/tools/loader/app.py | 17 +- openpype/tools/loader/model.py | 289 +++++---- openpype/tools/loader/widgets.py | 231 ++++--- openpype/tools/mayalookassigner/app.py | 21 +- openpype/tools/mayalookassigner/commands.py | 18 +- .../tools/mayalookassigner/vray_proxies.py | 77 +-- .../project_manager/project_manager/model.py | 42 +- .../project_manager/project_manager/view.py | 9 +- .../project_manager/widgets.py | 13 +- openpype/tools/publisher/control.py | 9 +- .../tools/publisher/widgets/create_dialog.py | 22 +- openpype/tools/sceneinventory/model.py | 38 +- .../tools/sceneinventory/switch_dialog.py | 569 ++++++++---------- openpype/tools/sceneinventory/view.py | 113 ++-- openpype/tools/standalonepublish/app.py | 18 +- .../standalonepublish/widgets/model_asset.py | 12 +- .../standalonepublish/widgets/widget_asset.py | 27 +- .../widgets/widget_family.py | 81 ++- openpype/tools/texture_copy/app.py | 28 +- openpype/tools/utils/assets_widget.py | 19 +- openpype/tools/utils/delegates.py | 40 +- openpype/tools/utils/lib.py | 14 +- openpype/tools/utils/tasks_widget.py | 13 +- openpype/tools/workfiles/files_widget.py | 5 +- openpype/tools/workfiles/model.py | 48 +- openpype/tools/workfiles/save_as_dialog.py | 30 +- openpype/tools/workfiles/window.py | 23 +- 31 files changed, 1010 insertions(+), 1074 deletions(-) diff --git a/openpype/tools/assetlinks/widgets.py b/openpype/tools/assetlinks/widgets.py index 22e8848a60..5ce2a835ef 100644 --- a/openpype/tools/assetlinks/widgets.py +++ b/openpype/tools/assetlinks/widgets.py @@ -1,10 +1,16 @@ +import collections +from openpype.client import ( + get_versions, + get_subsets, + get_assets, + get_version_links, +) from Qt import QtWidgets class SimpleLinkView(QtWidgets.QWidget): - - def __init__(self, dbcon, parent=None): + def __init__(self, dbcon, parent): super(SimpleLinkView, self).__init__(parent=parent) self.dbcon = dbcon @@ -24,6 +30,11 @@ class SimpleLinkView(QtWidgets.QWidget): self._in_view = in_view self._out_view = out_view + self._version_doc_to_process = None + + @property + def project_name(self): + return self.dbcon.current_project() def clear(self): self._in_view.clear() @@ -31,60 +42,114 @@ class SimpleLinkView(QtWidgets.QWidget): def set_version(self, version_doc): self.clear() - if not version_doc or not self.isVisible(): - return + self._version_doc_to_process = version_doc + if version_doc and self.isVisible(): + self._fill_values() - # inputs - # + def showEvent(self, event): + super(SimpleLinkView, self).showEvent(event) + self._fill_values() + + def _fill_values(self): + if self._version_doc_to_process is None: + return + version_doc = self._version_doc_to_process + self._version_doc_to_process = None + self._fill_inputs(version_doc) + self._fill_outputs(version_doc) + + def _fill_inputs(self, version_doc): + version_ids = set() for link in version_doc["data"].get("inputLinks", []): # Backwards compatibility for "input" key used as "id" if "id" not in link: link_id = link["input"] else: link_id = link["id"] - version = self.dbcon.find_one( - {"_id": link_id, "type": "version"}, - projection={"name": 1, "parent": 1} - ) - if not version: - continue - subset = self.dbcon.find_one( - {"_id": version["parent"], "type": "subset"}, - projection={"name": 1, "parent": 1} - ) - if not subset: - continue - asset = self.dbcon.find_one( - {"_id": subset["parent"], "type": "asset"}, - projection={"name": 1} - ) + version_ids.add(link_id) - self._in_view.addItem("{asset} {subset} v{version:0>3}".format( - asset=asset["name"], - subset=subset["name"], - version=version["name"], + version_docs = list(get_versions( + self.project_name, + version_ids=version_ids, + fields=["name", "parent"] + )) + + subset_docs = [] + versions_by_subset_id = collections.defaultdict(list) + if versions_by_subset_id: + for version_doc in version_docs: + subset_id = version_doc["parent"] + versions_by_subset_id[subset_id].append(version_doc) + + subset_docs = list(get_subsets( + self.project_name, + subset_ids=versions_by_subset_id.keys(), + fields=["_id", "name", "parent"] )) - # outputs - # - outputs = self.dbcon.find( - {"type": "version", "data.inputLinks.input": version_doc["_id"]}, - projection={"name": 1, "parent": 1} - ) - for version in outputs or []: - subset = self.dbcon.find_one( - {"_id": version["parent"], "type": "subset"}, - projection={"name": 1, "parent": 1} - ) - if not subset: - continue - asset = self.dbcon.find_one( - {"_id": subset["parent"], "type": "asset"}, - projection={"name": 1} - ) + asset_docs = [] + subsets_by_asset_id = collections.defaultdict(list) + if subset_docs: + for subset_doc in subset_docs: + asset_id = subset_doc["parent"] + subsets_by_asset_id[asset_id].append(subset_doc) - self._out_view.addItem("{asset} {subset} v{version:0>3}".format( - asset=asset["name"], - subset=subset["name"], - version=version["name"], + asset_docs = list(get_assets( + self.project_name, + asset_ids=subsets_by_asset_id.keys(), + fields=["_id", "name"] )) + + for asset_doc in asset_docs: + asset_id = asset_doc["_id"] + for subset_doc in subsets_by_asset_id[asset_id]: + subset_id = subset_doc["_id"] + for version_doc in versions_by_subset_id[subset_id]: + self._in_view.addItem("{} {} v{:0>3}".format( + asset_doc["name"], + subset_doc["name"], + version_doc["name"], + )) + + def _fill_outputs(self, version_doc): + version_docs = list(get_version_links( + self.project_name, + version_doc["_id"], + fields=["name", "parent"] + )) + subset_docs = [] + versions_by_subset_id = collections.defaultdict(list) + if versions_by_subset_id: + for version_doc in version_docs: + subset_id = version_doc["parent"] + versions_by_subset_id[subset_id].append(version_doc) + + subset_docs = list(get_subsets( + self.project_name, + subset_ids=versions_by_subset_id.keys(), + fields=["_id", "name", "parent"] + )) + + asset_docs = [] + subsets_by_asset_id = collections.defaultdict(list) + if subset_docs: + for subset_doc in subset_docs: + asset_id = subset_doc["parent"] + subsets_by_asset_id[asset_id].append(subset_doc) + + asset_docs = list(get_assets( + self.project_name, + asset_ids=subsets_by_asset_id.keys(), + fields=["_id", "name"] + )) + + for asset_doc in asset_docs: + asset_id = asset_doc["_id"] + for subset_doc in subsets_by_asset_id[asset_id]: + subset_id = subset_doc["_id"] + for version_doc in versions_by_subset_id[subset_id]: + self._out_view.addItem("{} {} v{:0>3}".format( + asset_doc["name"], + subset_doc["name"], + version_doc["name"], + )) diff --git a/openpype/tools/creator/window.py b/openpype/tools/creator/window.py index e0c329fb78..a85f47a060 100644 --- a/openpype/tools/creator/window.py +++ b/openpype/tools/creator/window.py @@ -4,6 +4,7 @@ import re from Qt import QtWidgets, QtCore +from openpype.client import get_asset, get_subsets from openpype import style from openpype.api import get_current_project_settings from openpype.tools.utils.lib import qt_app_context @@ -215,12 +216,12 @@ class CreatorWindow(QtWidgets.QDialog): self._set_valid_state(False) return + project_name = legacy_io.active_project() asset_doc = None if creator_plugin: # Get the asset from the database which match with the name - asset_doc = legacy_io.find_one( - {"name": asset_name, "type": "asset"}, - projection={"_id": 1} + asset_doc = get_asset( + project_name, asset_name=asset_name, fields=["_id"] ) # Get plugin @@ -235,7 +236,6 @@ class CreatorWindow(QtWidgets.QDialog): self._set_valid_state(False) return - project_name = legacy_io.Session["AVALON_PROJECT"] asset_id = asset_doc["_id"] task_name = legacy_io.Session["AVALON_TASK"] @@ -269,14 +269,13 @@ class CreatorWindow(QtWidgets.QDialog): self._subset_name_input.setText(subset_name) # Get all subsets of the current asset - subset_docs = legacy_io.find( - { - "type": "subset", - "parent": asset_id - }, - {"name": 1} + subset_docs = get_subsets( + project_name, asset_ids=[asset_id], fields=["name"] ) - existing_subset_names = set(subset_docs.distinct("name")) + existing_subset_names = { + subset_doc["name"] + for subset_doc in subset_docs + } existing_subset_names_low = set( _name.lower() for _name in existing_subset_names diff --git a/openpype/tools/launcher/models.py b/openpype/tools/launcher/models.py index 13567e7916..307f591d96 100644 --- a/openpype/tools/launcher/models.py +++ b/openpype/tools/launcher/models.py @@ -9,6 +9,10 @@ import appdirs from Qt import QtCore, QtGui import qtawesome +from openpype.client import ( + get_project, + get_assets, +) from openpype.lib import JSONSettingRegistry from openpype.lib.applications import ( CUSTOM_LAUNCH_APP_GROUPS, @@ -81,13 +85,11 @@ class ActionModel(QtGui.QStandardItemModel): def get_application_actions(self): actions = [] - if not self.dbcon.Session.get("AVALON_PROJECT"): + if not self.dbcon.current_project(): return actions - project_doc = self.dbcon.find_one( - {"type": "project"}, - {"config.apps": True} - ) + project_name = self.dbcon.active_project() + project_doc = get_project(project_name, fields=["config.apps"]) if not project_doc: return actions @@ -448,7 +450,7 @@ class LauncherModel(QtCore.QObject): @property def project_name(self): """Current project name.""" - return self._dbcon.Session.get("AVALON_PROJECT") + return self._dbcon.current_project() @property def refreshing_assets(self): @@ -649,10 +651,9 @@ class LauncherModel(QtCore.QObject): self._asset_refresh_thread = None def _refresh_assets(self): - asset_docs = list(self._dbcon.find( - {"type": "asset"}, - self._asset_projection - )) + asset_docs = get_assets( + self._last_project_name, fields=list(self._asset_projection.keys()) + ) if not self._refreshing_assets: return self._refreshing_assets = False diff --git a/openpype/tools/libraryloader/app.py b/openpype/tools/libraryloader/app.py index 7fda6bd6f9..5f4d10d796 100644 --- a/openpype/tools/libraryloader/app.py +++ b/openpype/tools/libraryloader/app.py @@ -3,6 +3,7 @@ import sys from Qt import QtWidgets, QtCore, QtGui from openpype import style +from openpype.client import get_project from openpype.pipeline import AvalonMongoDB from openpype.tools.utils import lib as tools_lib from openpype.tools.loader.widgets import ( @@ -303,14 +304,26 @@ class LibraryLoaderWindow(QtWidgets.QDialog): families = self._subsets_widget.get_subsets_families() self._families_filter_view.set_enabled_families(families) - def set_context(self, context, refresh=True): - self.echo("Setting context: {}".format(context)) - lib.schedule( - lambda: self._set_context(context, refresh=refresh), - 50, channel="mongo" - ) - # ------------------------------ + def set_context(self, context, refresh=True): + """Set the selection in the interface using a context. + The context must contain `asset` data by name. + + Args: + context (dict): The context to apply. + Returns: + None + """ + + asset_name = context.get("asset", None) + if asset_name is None: + return + + if refresh: + self._refresh_assets() + + self._assets_widget.select_asset_by_name(asset_name) + def _on_family_filter_change(self, families): self._subsets_widget.set_family_filters(families) @@ -323,10 +336,7 @@ class LibraryLoaderWindow(QtWidgets.QDialog): """Load assets from database""" if self.current_project is not None: # Ensure a project is loaded - project_doc = self.dbcon.find_one( - {"type": "project"}, - {"type": 1} - ) + project_doc = get_project(self.current_project, fields=["_id"]) assert project_doc, "This is a bug" self._families_filter_view.set_enabled_families(set()) @@ -371,7 +381,7 @@ class LibraryLoaderWindow(QtWidgets.QDialog): # Clear the version information on asset change self._version_info_widget.set_version(None) - self._thumbnail_widget.set_thumbnail(asset_ids) + self._thumbnail_widget.set_thumbnail("asset", asset_ids) self.data["state"]["assetIds"] = asset_ids @@ -426,34 +436,17 @@ class LibraryLoaderWindow(QtWidgets.QDialog): version_doc["_id"] for version_doc in version_docs ] + src_type = "version" if not thumbnail_src_ids: + src_type = "asset" thumbnail_src_ids = self._assets_widget.get_selected_asset_ids() - self._thumbnail_widget.set_thumbnail(thumbnail_src_ids) + self._thumbnail_widget.set_thumbnail(src_type, thumbnail_src_ids) version_ids = [doc["_id"] for doc in version_docs or []] if self._repres_widget: self._repres_widget.set_version_ids(version_ids) - def _set_context(self, context, refresh=True): - """Set the selection in the interface using a context. - The context must contain `asset` data by name. - - Args: - context (dict): The context to apply. - Returns: - None - """ - - asset_name = context.get("asset", None) - if asset_name is None: - return - - if refresh: - self._refresh_assets() - - self._assets_widget.select_asset_by_name(asset_name) - def _on_message_timeout(self): self._message_label.setText("") diff --git a/openpype/tools/loader/app.py b/openpype/tools/loader/app.py index bb589c199d..1917f23c60 100644 --- a/openpype/tools/loader/app.py +++ b/openpype/tools/loader/app.py @@ -3,6 +3,7 @@ import traceback from Qt import QtWidgets, QtCore +from openpype.client import get_projects, get_project from openpype import style from openpype.lib import register_event_callback from openpype.pipeline import ( @@ -39,7 +40,7 @@ class LoaderWindow(QtWidgets.QDialog): def __init__(self, parent=None): super(LoaderWindow, self).__init__(parent) title = "Asset Loader 2.1" - project_name = legacy_io.Session.get("AVALON_PROJECT") + project_name = legacy_io.active_project() if project_name: title += " - {}".format(project_name) self.setWindowTitle(title) @@ -274,8 +275,9 @@ class LoaderWindow(QtWidgets.QDialog): """Load assets from database""" # Ensure a project is loaded - project = legacy_io.find_one({"type": "project"}, {"type": 1}) - assert project, "Project was not found! This is a bug" + project_name = legacy_io.active_project() + project_doc = get_project(project_name, fields=["_id"]) + assert project_doc, "Project was not found! This is a bug" self._assets_widget.refresh() self._assets_widget.setFocus() @@ -314,7 +316,7 @@ class LoaderWindow(QtWidgets.QDialog): ) # Clear the version information on asset change - self._thumbnail_widget.set_thumbnail(asset_ids) + self._thumbnail_widget.set_thumbnail("asset", asset_ids) self._version_info_widget.set_version(None) self.data["state"]["assetIds"] = asset_ids @@ -371,10 +373,12 @@ class LoaderWindow(QtWidgets.QDialog): version_doc["_id"] for version_doc in version_docs ] + source_type = "version" if not thumbnail_src_ids: + source_type = "asset" thumbnail_src_ids = self._assets_widget.get_selected_asset_ids() - self._thumbnail_widget.set_thumbnail(thumbnail_src_ids) + self._thumbnail_widget.set_thumbnail(source_type, thumbnail_src_ids) if self._repres_widget is not None: version_ids = [doc["_id"] for doc in version_docs] @@ -576,8 +580,7 @@ def show(debug=False, parent=None, use_context=False): legacy_io.install() any_project = next( - project for project in legacy_io.projects() - if project.get("active", True) is not False + project for project in get_projects(fields=["name"]) ) legacy_io.Session["AVALON_PROJECT"] = any_project["name"] diff --git a/openpype/tools/loader/model.py b/openpype/tools/loader/model.py index 6f39c428be..e8e0480d9c 100644 --- a/openpype/tools/loader/model.py +++ b/openpype/tools/loader/model.py @@ -7,6 +7,15 @@ from uuid import uuid4 from Qt import QtCore, QtGui import qtawesome +from openpype.client import ( + get_assets, + get_subsets, + get_last_versions, + get_versions, + get_hero_versions, + get_version_by_name, + get_representations +) from openpype.pipeline import ( HeroVersionType, schema, @@ -56,7 +65,7 @@ class BaseRepresentationModel(object): remote_site = remote_provider = None if not project_name: - project_name = self.dbcon.Session.get("AVALON_PROJECT") + project_name = self.dbcon.active_project() else: self.dbcon.Session["AVALON_PROJECT"] = project_name @@ -254,57 +263,61 @@ class SubsetsModel(TreeModel, BaseRepresentationModel): # because it also updates the information in other columns if index.column() == self.columns_index["version"]: item = index.internalPointer() - parent = item["_id"] + subset_id = item["_id"] if isinstance(value, HeroVersionType): - versions = list(self.dbcon.find({ - "type": {"$in": ["version", "hero_version"]}, - "parent": parent - }, sort=[("name", -1)])) - - version = None - last_version = None - for __version in versions: - if __version["type"] == "hero_version": - version = __version - elif last_version is None: - last_version = __version - - if version is not None and last_version is not None: - break - - _version = None - for __version in versions: - if __version["_id"] == version["version_id"]: - _version = __version - break - - version["data"] = _version["data"] - version["name"] = _version["name"] - version["is_from_latest"] = ( - last_version["_id"] == _version["_id"] - ) + version_doc = self._get_hero_version(subset_id) else: - version = self.dbcon.find_one({ - "name": value, - "type": "version", - "parent": parent - }) + project_name = self.dbcon.active_project() + version_doc = get_version_by_name( + project_name, subset_id, value + ) # update availability on active site when version changes - if self.sync_server.enabled and version: - query = self._repre_per_version_pipeline([version["_id"]], - self.active_site, - self.remote_site) + if self.sync_server.enabled and version_doc: + query = self._repre_per_version_pipeline( + [version_doc["_id"]], + self.active_site, + self.remote_site + ) docs = list(self.dbcon.aggregate(query)) if docs: repre = docs.pop() - version["data"].update(self._get_repre_dict(repre)) + version_doc["data"].update(self._get_repre_dict(repre)) - self.set_version(index, version) + self.set_version(index, version_doc) return super(SubsetsModel, self).setData(index, value, role) + def _get_hero_version(self, subset_id): + project_name = self.dbcon.active_project() + version_docs = get_versions( + project_name, subset_ids=[subset_id], hero=True + ) + standard_versions = [] + hero_version_doc = None + for version_doc in version_docs: + if version_doc["type"] == "hero_version": + hero_version_doc = version_doc + continue + standard_versions.append(version_doc) + + src_version_id = hero_version_doc["version_id"] + src_version = None + is_from_latest = True + for version_doc in reversed(sorted( + standard_versions, key=lambda item: item["name"] + )): + if version_doc["_id"] == src_version_id: + src_version = version_doc + break + is_from_latest = False + + hero_version_doc["data"] = src_version["data"] + hero_version_doc["name"] = src_version["name"] + hero_version_doc["is_from_latest"] = is_from_latest + return hero_version_doc + def set_version(self, index, version): """Update the version data of the given index. @@ -391,26 +404,25 @@ class SubsetsModel(TreeModel, BaseRepresentationModel): item["repre_info"] = repre_info def _fetch(self): - asset_docs = self.dbcon.find( - { - "type": "asset", - "_id": {"$in": self._asset_ids} - }, - self.asset_doc_projection + project_name = self.dbcon.active_project() + asset_docs = get_assets( + project_name, + asset_ids=self._asset_ids, + fields=self.asset_doc_projection.keys() ) + asset_docs_by_id = { asset_doc["_id"]: asset_doc for asset_doc in asset_docs } subset_docs_by_id = {} - subset_docs = self.dbcon.find( - { - "type": "subset", - "parent": {"$in": self._asset_ids} - }, - self.subset_doc_projection + subset_docs = get_subsets( + project_name, + asset_ids=self._asset_ids, + fields=self.subset_doc_projection.keys() ) + subset_families = set() for subset_doc in subset_docs: if self._doc_fetching_stop: @@ -423,37 +435,13 @@ class SubsetsModel(TreeModel, BaseRepresentationModel): subset_docs_by_id[subset_doc["_id"]] = subset_doc subset_ids = list(subset_docs_by_id.keys()) - _pipeline = [ - # Find all versions of those subsets - {"$match": { - "type": "version", - "parent": {"$in": subset_ids} - }}, - # Sorting versions all together - {"$sort": {"name": 1}}, - # Group them by "parent", but only take the last - {"$group": { - "_id": "$parent", - "_version_id": {"$last": "$_id"}, - "name": {"$last": "$name"}, - "type": {"$last": "$type"}, - "data": {"$last": "$data"}, - "locations": {"$last": "$locations"}, - "schema": {"$last": "$schema"} - }} - ] - last_versions_by_subset_id = dict() - for doc in self.dbcon.aggregate(_pipeline): - if self._doc_fetching_stop: - return - doc["parent"] = doc["_id"] - doc["_id"] = doc.pop("_version_id") - last_versions_by_subset_id[doc["parent"]] = doc + last_versions_by_subset_id = get_last_versions( + project_name, + subset_ids, + fields=["_id", "parent", "name", "type", "data", "schema"] + ) - hero_versions = self.dbcon.find({ - "type": "hero_version", - "parent": {"$in": subset_ids} - }) + hero_versions = get_hero_versions(project_name, subset_ids=subset_ids) missing_versions = [] for hero_version in hero_versions: version_id = hero_version["version_id"] @@ -462,10 +450,9 @@ class SubsetsModel(TreeModel, BaseRepresentationModel): missing_versions_by_id = {} if missing_versions: - missing_version_docs = self.dbcon.find({ - "type": "version", - "_id": {"$in": missing_versions} - }) + missing_version_docs = get_versions( + project_name, version_ids=missing_versions + ) missing_versions_by_id = { missing_version_doc["_id"]: missing_version_doc for missing_version_doc in missing_version_docs @@ -488,23 +475,16 @@ class SubsetsModel(TreeModel, BaseRepresentationModel): last_versions_by_subset_id[subset_id] = hero_version - self._doc_payload = { - "asset_docs_by_id": asset_docs_by_id, - "subset_docs_by_id": subset_docs_by_id, - "subset_families": subset_families, - "last_versions_by_subset_id": last_versions_by_subset_id - } - + repre_info = {} if self.sync_server.enabled: version_ids = set() for _subset_id, doc in last_versions_by_subset_id.items(): version_ids.add(doc["_id"]) - query = self._repre_per_version_pipeline(list(version_ids), - self.active_site, - self.remote_site) + query = self._repre_per_version_pipeline( + list(version_ids), self.active_site, self.remote_site + ) - repre_info = {} for doc in self.dbcon.aggregate(query): if self._doc_fetching_stop: return @@ -512,7 +492,13 @@ class SubsetsModel(TreeModel, BaseRepresentationModel): doc["remote_provider"] = self.remote_provider repre_info[doc["_id"]] = doc - self._doc_payload["repre_info_by_version_id"] = repre_info + self._doc_payload = { + "asset_docs_by_id": asset_docs_by_id, + "subset_docs_by_id": subset_docs_by_id, + "subset_families": subset_families, + "last_versions_by_subset_id": last_versions_by_subset_id, + "repre_info_by_version_id": repre_info + } self.doc_fetched.emit() @@ -1062,6 +1048,16 @@ class RepresentationModel(TreeModel, BaseRepresentationModel): "remote_site": "Remote" } + repre_projection = { + "_id": 1, + "name": 1, + "context.subset": 1, + "context.asset": 1, + "context.version": 1, + "context.representation": 1, + 'files.sites': 1 + } + def __init__(self, dbcon, header, version_ids): super(RepresentationModel, self).__init__() self.dbcon = dbcon @@ -1073,22 +1069,22 @@ class RepresentationModel(TreeModel, BaseRepresentationModel): sync_server = active_site = remote_site = None active_provider = remote_provider = None - project = dbcon.Session["AVALON_PROJECT"] - if project: + project_name = dbcon.current_project() + if project_name: sync_server = manager.modules_by_name["sync_server"] - active_site = sync_server.get_active_site(project) - remote_site = sync_server.get_remote_site(project) + active_site = sync_server.get_active_site(project_name) + remote_site = sync_server.get_remote_site(project_name) # TODO refactor - active_provider = \ - sync_server.get_provider_for_site(project, - active_site) + active_provider = sync_server.get_provider_for_site( + project_name, active_site + ) if active_site == 'studio': active_provider = 'studio' - remote_provider = \ - sync_server.get_provider_for_site(project, - remote_site) + remote_provider = sync_server.get_provider_for_site( + project_name, remote_site + ) if remote_site == 'studio': remote_provider = 'studio' @@ -1127,8 +1123,7 @@ class RepresentationModel(TreeModel, BaseRepresentationModel): if index.column() == self.Columns.index("name"): if item.get("isMerged"): return item["icon"] - else: - return self._icons["repre"] + return self._icons["repre"] active_index = self.Columns.index("active_site") remote_index = self.Columns.index("remote_site") @@ -1144,12 +1139,12 @@ class RepresentationModel(TreeModel, BaseRepresentationModel): # site added, sync in progress progress_str = "not avail." if progress >= 0: - # progress == 0 for isMerged is unavailable if progress == 0 and item.get("isMerged"): progress_str = "not avail." else: - progress_str = "{}% {}".format(int(progress * 100), - label) + progress_str = "{}% {}".format( + int(progress * 100), label + ) return progress_str @@ -1192,7 +1187,6 @@ class RepresentationModel(TreeModel, BaseRepresentationModel): if len(self.version_ids) > 1: group = repre_groups.get(doc["name"]) if not group: - group_item = Item() item_id = str(uuid4()) group_item.update({ @@ -1213,9 +1207,9 @@ class RepresentationModel(TreeModel, BaseRepresentationModel): repre_groups_items[doc["name"]] = 0 group = group_item - progress = lib.get_progress_for_repre(doc, - self.active_site, - self.remote_site) + progress = lib.get_progress_for_repre( + doc, self.active_site, self.remote_site + ) active_site_icon = self._icons.get(self.active_provider) remote_site_icon = self._icons.get(self.remote_provider) @@ -1248,9 +1242,9 @@ class RepresentationModel(TreeModel, BaseRepresentationModel): 'remote_site_progress': progress[self.remote_site] } if group: - group = self._sum_group_progress(doc["name"], group, - current_progress, - repre_groups_items) + group = self._sum_group_progress( + doc["name"], group, current_progress, repre_groups_items + ) self.add_child(item, group) @@ -1269,47 +1263,40 @@ class RepresentationModel(TreeModel, BaseRepresentationModel): return self._items_by_id.get(item_id) def refresh(self): - docs = [] - session_project = self.dbcon.Session['AVALON_PROJECT'] - if not session_project: + project_name = self.dbcon.current_project() + if not project_name: return + repre_docs = [] if self.version_ids: # Simple find here for now, expected to receive lower number of # representations and logic could be in Python - docs = list(self.dbcon.find( - {"type": "representation", "parent": {"$in": self.version_ids}, - "files.sites.name": {"$exists": 1}}, self.projection())) - self._docs = docs + repre_docs = list(get_representations( + project_name, + version_ids=self.version_ids, + check_site_name=True, + fields=self.repre_projection.keys() + )) + + self._docs = repre_docs self.doc_fetched.emit() - @classmethod - def projection(cls): - return { - "_id": 1, - "name": 1, - "context.subset": 1, - "context.asset": 1, - "context.version": 1, - "context.representation": 1, - 'files.sites': 1 - } + def _sum_group_progress( + self, repre_name, group, current_item_progress, repre_groups_items + ): + """Update final group progress - def _sum_group_progress(self, repre_name, group, current_item_progress, - repre_groups_items): - """ - Update final group progress - Called after every item in group is added + Called after every item in group is added - Args: - repre_name(string) - group(dict): info about group of selected items - current_item_progress(dict): {'active_site_progress': XX, - 'remote_site_progress': YY} - repre_groups_items(dict) - Returns: - (dict): updated group info + Args: + repre_name(string) + group(dict): info about group of selected items + current_item_progress(dict): {'active_site_progress': XX, + 'remote_site_progress': YY} + repre_groups_items(dict) + Returns: + (dict): updated group info """ repre_groups_items[repre_name] += 1 diff --git a/openpype/tools/loader/widgets.py b/openpype/tools/loader/widgets.py index 42fb62b632..6c7acc593d 100644 --- a/openpype/tools/loader/widgets.py +++ b/openpype/tools/loader/widgets.py @@ -7,6 +7,16 @@ import collections from Qt import QtWidgets, QtCore, QtGui +from openpype.client import ( + get_subset_families, + get_subset, + get_subsets, + get_version, + get_versions, + get_representations, + get_thumbnail_id_from_source, + get_thumbnail, +) from openpype.api import Anatomy from openpype.pipeline import HeroVersionType from openpype.pipeline.thumbnail import get_thumbnail_binary @@ -237,8 +247,7 @@ class SubsetWidget(QtWidgets.QWidget): self.model = model self.view = view - actual_project = dbcon.Session["AVALON_PROJECT"] - self.on_project_change(actual_project) + self.on_project_change(dbcon.current_project()) view.customContextMenuRequested.connect(self.on_context_menu) @@ -302,33 +311,23 @@ class SubsetWidget(QtWidgets.QWidget): item["version_document"] ) - subset_docs = list(self.dbcon.find( - { - "_id": {"$in": list(version_docs_by_subset_id.keys())}, - "type": "subset" - }, - { - "schema": 1, - "data.families": 1 - } + project_name = self.dbcon.active_project() + subset_docs = list(get_subsets( + project_name, + subset_ids=version_docs_by_subset_id.keys(), + fields=["schema", "data.families"] )) subset_docs_by_id = { subset_doc["_id"]: subset_doc for subset_doc in subset_docs } version_ids = list(version_docs_by_id.keys()) - repre_docs = self.dbcon.find( - # Query all representations for selected versions at once - { - "type": "representation", - "parent": {"$in": version_ids} - }, - # Query only name and parent from representation - { - "name": 1, - "parent": 1 - } + repre_docs = get_representations( + project_name, + version_ids=version_ids, + fields=["name", "parent"] ) + repre_docs_by_version_id = { version_id: [] for version_id in version_ids @@ -566,28 +565,42 @@ class SubsetWidget(QtWidgets.QWidget): # same representation available # Trigger - repre_ids = [] + project_name = self.dbcon.active_project() + subset_names_by_version_id = collections.defaultdict(set) for item in items: - representation = self.dbcon.find_one( - { - "type": "representation", - "name": representation_name, - "parent": item["version_document"]["_id"] - }, - {"_id": 1} - ) - if not representation: - self.echo("Subset '{}' has no representation '{}'".format( - item["subset"], representation_name - )) - continue - repre_ids.append(representation["_id"]) + version_id = item["version_document"]["_id"] + subset_names_by_version_id[version_id].add(item["subset"]) + + version_ids = set(subset_names_by_version_id.keys()) + repre_docs = get_representations( + project_name, + representation_names=[representation_name], + version_ids=version_ids, + fields=["_id", "parent"] + ) + + repre_ids = [] + for repre_doc in repre_docs: + repre_ids.append(repre_doc["_id"]) + + version_id = repre_doc["parent"] + if version_id not in version_ids: + version_ids.remove(version_id) + + for version_id in version_ids: + joined_subset_names = ", ".join([ + '"{}"'.format(subset) + for subset in subset_names_by_version_id[version_id] + ]) + self.echo("Subsets {} don't have representation '{}'".format( + joined_subset_names, representation_name + )) # get contexts only for selected menu option repre_contexts = get_repres_contexts(repre_ids, self.dbcon) - options = lib.get_options(action, loader, self, - list(repre_contexts.values())) - + options = lib.get_options( + action, loader, self, list(repre_contexts.values()) + ) error_info = _load_representations_by_loader( loader, repre_contexts, options=options ) @@ -661,27 +674,21 @@ class VersionTextEdit(QtWidgets.QTextEdit): print("Querying..") + project_name = self.dbcon.active_project() if not version_doc: - version_doc = self.dbcon.find_one({ - "_id": version_id, - "type": {"$in": ["version", "hero_version"]} - }) + version_doc = get_version(project_name, version_id=version_id) assert version_doc, "Not a valid version id" if version_doc["type"] == "hero_version": - _version_doc = self.dbcon.find_one({ - "_id": version_doc["version_id"], - "type": "version" - }) + _version_doc = get_version( + project_name, version_id=version_doc["version_id"] + ) version_doc["data"] = _version_doc["data"] version_doc["name"] = HeroVersionType( _version_doc["name"] ) - subset = self.dbcon.find_one({ - "_id": version_doc["parent"], - "type": "subset" - }) + subset = get_subset(project_name, subset_id=version_doc["parent"]) assert subset, "No valid subset parent for version" # Define readable creation timestamp @@ -752,7 +759,7 @@ class VersionTextEdit(QtWidgets.QTextEdit): if not source: return - project_name = self.dbcon.Session["AVALON_PROJECT"] + project_name = self.dbcon.current_project() if self._anatomy is None or self._anatomy.project_name != project_name: self._anatomy = Anatomy(project_name) @@ -833,24 +840,19 @@ class ThumbnailWidget(QtWidgets.QLabel): QtCore.Qt.SmoothTransformation ) - def set_thumbnail(self, doc_id=None): - if not doc_id: + def set_thumbnail(self, src_type, doc_ids): + if not doc_ids: self.set_pixmap() return - if isinstance(doc_id, (list, tuple)): - if len(doc_id) < 1: - self.set_pixmap() - return - doc_id = doc_id[0] + src_id = doc_ids[0] - doc = self.dbcon.find_one( - {"_id": doc_id}, - {"data.thumbnail_id"} + project_name = self.dbcon.active_project() + thumbnail_id = get_thumbnail_id_from_source( + project_name, + src_type, + src_id, ) - thumbnail_id = None - if doc: - thumbnail_id = doc.get("data", {}).get("thumbnail_id") if thumbnail_id == self.current_thumb_id: if self.current_thumbnail is None: self.set_pixmap() @@ -861,9 +863,7 @@ class ThumbnailWidget(QtWidgets.QLabel): self.set_pixmap() return - thumbnail_ent = self.dbcon.find_one( - {"type": "thumbnail", "_id": thumbnail_id} - ) + thumbnail_ent = get_thumbnail(project_name, thumbnail_id) if not thumbnail_ent: return @@ -917,21 +917,9 @@ class FamilyModel(QtGui.QStandardItemModel): def refresh(self): families = set() - if self.dbcon.Session.get("AVALON_PROJECT"): - result = list(self.dbcon.aggregate([ - {"$match": { - "type": "subset" - }}, - {"$project": { - "family": {"$arrayElemAt": ["$data.families", 0]} - }}, - {"$group": { - "_id": "family_group", - "families": {"$addToSet": "$family"} - }} - ])) - if result: - families = set(result[0]["families"]) + project_name = self.dbcon.current_project() + if project_name: + families = get_subset_families(project_name) root_item = self.invisibleRootItem() @@ -1213,8 +1201,8 @@ class RepresentationWidget(QtWidgets.QWidget): self.proxy_model = proxy_model self.sync_server_enabled = False - actual_project = dbcon.Session["AVALON_PROJECT"] - self.on_project_change(actual_project) + + self.on_project_change(dbcon.current_project()) self.model.refresh() @@ -1243,23 +1231,18 @@ class RepresentationWidget(QtWidgets.QWidget): for item in items: repre_ids.append(item["_id"]) - repre_docs = list(self.dbcon.find( - { - "type": "representation", - "_id": {"$in": repre_ids} - }, - { - "name": 1, - "parent": 1 - } - )) + project_name = self.dbcon.actual_project() + repre_docs = get_representations( + project_name, + representation_ids=repre_ids, + fields=["name", "parent"] + ) + version_ids = [ repre_doc["parent"] for repre_doc in repre_docs ] - version_docs = self.dbcon.find({ - "_id": {"$in": version_ids} - }) + version_docs = get_versions(project_name, version_ids=version_ids) version_docs_by_id = {} version_docs_by_subset_id = collections.defaultdict(list) @@ -1269,15 +1252,10 @@ class RepresentationWidget(QtWidgets.QWidget): version_docs_by_id[version_id] = version_doc version_docs_by_subset_id[subset_id].append(version_doc) - subset_docs = list(self.dbcon.find( - { - "_id": {"$in": list(version_docs_by_subset_id.keys())}, - "type": "subset" - }, - { - "schema": 1, - "data.families": 1 - } + subset_docs = list(get_subsets( + project_name, + subset_ids=version_docs_by_subset_id.keys(), + fields=["schema", "data.families"] )) subset_docs_by_id = { subset_doc["_id"]: subset_doc @@ -1446,13 +1424,12 @@ class RepresentationWidget(QtWidgets.QWidget): self._process_action(items, menu, point) def _process_action(self, items, menu, point): - """ - Show the context action menu and process selected + """Show the context action menu and process selected - Args: - items(dict): menu items - menu(OptionalMenu) - point(PointIndex) + Args: + items(dict): menu items + menu(OptionalMenu) + point(PointIndex) """ global_point = self.tree_view.mapToGlobal(point) action = menu.exec_(global_point) @@ -1468,21 +1445,23 @@ class RepresentationWidget(QtWidgets.QWidget): data_by_repre_id = {} selected_side = action_representation.get("selected_side") + is_sync_loader = tools_lib.is_sync_loader(loader) for item in items: - if tools_lib.is_sync_loader(loader): - site_name = "{}_site_name".format(selected_side) - data = { - "_id": item.get("_id"), - "site_name": item.get(site_name), - "project_name": self.dbcon.Session["AVALON_PROJECT"] - } + item_id = item.get("_id") + repre_ids.append(item_id) + if not is_sync_loader: + continue - if not data["site_name"]: - continue + site_name = "{}_site_name".format(selected_side) + data_site_name = item.get(site_name) + if not data_site_name: + continue - data_by_repre_id[data["_id"]] = data - - repre_ids.append(item.get("_id")) + data_by_repre_id[item_id] = { + "_id": item_id, + "site_name": data_site_name, + "project_name": self.dbcon.active_project() + } repre_contexts = get_repres_contexts(repre_ids, self.dbcon) options = lib.get_options(action, loader, self, diff --git a/openpype/tools/mayalookassigner/app.py b/openpype/tools/mayalookassigner/app.py index 1b6cad77a8..427edf8245 100644 --- a/openpype/tools/mayalookassigner/app.py +++ b/openpype/tools/mayalookassigner/app.py @@ -4,6 +4,7 @@ import logging from Qt import QtWidgets, QtCore +from openpype.client import get_last_version_for_subset from openpype import style from openpype.pipeline import legacy_io from openpype.tools.utils.lib import qt_app_context @@ -211,6 +212,7 @@ class MayaLookAssignerWindow(QtWidgets.QWidget): selection = self.assign_selected.isChecked() asset_nodes = self.asset_outliner.get_nodes(selection=selection) + project_name = legacy_io.active_project() start = time.time() for i, (asset, item) in enumerate(asset_nodes.items()): @@ -222,23 +224,20 @@ class MayaLookAssignerWindow(QtWidgets.QWidget): assign_look = next((subset for subset in item["looks"] if subset["name"] in looks), None) if not assign_look: - self.echo("{} No matching selected " - "look for {}".format(prefix, asset)) + self.echo( + "{} No matching selected look for {}".format(prefix, asset) + ) continue # Get the latest version of this asset's look subset - version = legacy_io.find_one( - { - "type": "version", - "parent": assign_look["_id"] - }, - sort=[("name", -1)] + version = get_last_version_for_subset( + project_name, subset_id=assign_look["_id"], fields=["_id"] ) subset_name = assign_look["name"] - self.echo("{} Assigning {} to {}\t".format(prefix, - subset_name, - asset)) + self.echo("{} Assigning {} to {}\t".format( + prefix, subset_name, asset + )) nodes = item["nodes"] if cmds.pluginInfo('vrayformaya', query=True, loaded=True): diff --git a/openpype/tools/mayalookassigner/commands.py b/openpype/tools/mayalookassigner/commands.py index d41d8ca5a2..a4fc1fab70 100644 --- a/openpype/tools/mayalookassigner/commands.py +++ b/openpype/tools/mayalookassigner/commands.py @@ -2,9 +2,9 @@ from collections import defaultdict import logging import os -from bson.objectid import ObjectId import maya.cmds as cmds +from openpype.client import get_asset from openpype.pipeline import ( legacy_io, remove_container, @@ -159,11 +159,9 @@ def create_items_from_nodes(nodes): log.warning("No id hashes") return asset_view_items + project_name = legacy_io.active_project() for _id, id_nodes in id_hashes.items(): - asset = legacy_io.find_one( - {"_id": ObjectId(_id)}, - projection={"name": True} - ) + asset = get_asset(project_name, asset_id=_id, fields=["name"]) # Skip if asset id is not found if not asset: @@ -180,10 +178,12 @@ def create_items_from_nodes(nodes): namespace = get_namespace_from_node(node) namespaces.add(namespace) - asset_view_items.append({"label": asset["name"], - "asset": asset, - "looks": looks, - "namespaces": namespaces}) + asset_view_items.append({ + "label": asset["name"], + "asset": asset, + "looks": looks, + "namespaces": namespaces + }) return asset_view_items diff --git a/openpype/tools/mayalookassigner/vray_proxies.py b/openpype/tools/mayalookassigner/vray_proxies.py index 3523b24bf3..b2ba21f944 100644 --- a/openpype/tools/mayalookassigner/vray_proxies.py +++ b/openpype/tools/mayalookassigner/vray_proxies.py @@ -6,11 +6,14 @@ import logging import json import six -from bson.objectid import ObjectId import alembic.Abc from maya import cmds +from openpype.client import ( + get_representation_by_name, + get_last_version_for_subset, +) from openpype.pipeline import ( legacy_io, load_container, @@ -155,13 +158,12 @@ def get_look_relationships(version_id): Returns: dict: Dictionary of relations. - """ - json_representation = legacy_io.find_one({ - "type": "representation", - "parent": version_id, - "name": "json" - }) + + project_name = legacy_io.active_project() + json_representation = get_representation_by_name( + project_name, representation_name="json", version_id=version_id + ) # Load relationships shader_relation = get_representation_path(json_representation) @@ -184,12 +186,12 @@ def load_look(version_id): list of shader nodes. """ + + project_name = legacy_io.active_project() # Get representations of shader file and relationships - look_representation = legacy_io.find_one({ - "type": "representation", - "parent": version_id, - "name": "ma" - }) + look_representation = get_representation_by_name( + project_name, representation_name="ma", version_id=version_id + ) # See if representation is already loaded, if so reuse it. host = registered_host() @@ -220,42 +222,6 @@ def load_look(version_id): return shader_nodes -def get_latest_version(asset_id, subset): - # type: (str, str) -> dict - """Get latest version of subset. - - Args: - asset_id (str): Asset ID - subset (str): Subset name. - - Returns: - Latest version - - Throws: - RuntimeError: When subset or version doesn't exist. - - """ - subset = legacy_io.find_one({ - "name": subset, - "parent": ObjectId(asset_id), - "type": "subset" - }) - if not subset: - raise RuntimeError("Subset does not exist: %s" % subset) - - version = legacy_io.find_one( - { - "type": "version", - "parent": subset["_id"] - }, - sort=[("name", -1)] - ) - if not version: - raise RuntimeError("Version does not exist.") - - return version - - def vrayproxy_assign_look(vrayproxy, subset="lookDefault"): # type: (str, str) -> None """Assign look to vray proxy. @@ -281,13 +247,20 @@ def vrayproxy_assign_look(vrayproxy, subset="lookDefault"): asset_id = node_id.split(":", 1)[0] node_ids_by_asset_id[asset_id].add(node_id) + project_name = legacy_io.active_project() for asset_id, node_ids in node_ids_by_asset_id.items(): # Get latest look version - try: - version = get_latest_version(asset_id, subset=subset) - except RuntimeError as exc: - print(exc) + version = get_last_version_for_subset( + project_name, + subset_name=subset, + asset_id=asset_id, + fields=["_id"] + ) + if not version: + print("Didn't find last version for subset name {}".format( + subset + )) continue relationships = get_look_relationships(version["_id"]) diff --git a/openpype/tools/project_manager/project_manager/model.py b/openpype/tools/project_manager/project_manager/model.py index 223cfa629d..c5bde5aaec 100644 --- a/openpype/tools/project_manager/project_manager/model.py +++ b/openpype/tools/project_manager/project_manager/model.py @@ -7,6 +7,11 @@ from pymongo import UpdateOne, DeleteOne from Qt import QtCore, QtGui +from openpype.client import ( + get_project, + get_assets, + get_asset_ids_with_subsets, +) from openpype.lib import ( CURRENT_DOC_SCHEMAS, PypeLogger, @@ -255,10 +260,11 @@ class HierarchyModel(QtCore.QAbstractItemModel): return # Find project'd document - project_doc = self.dbcon.database[project_name].find_one( - {"type": "project"}, - ProjectItem.query_projection + project_doc = get_project( + project_name, + fields=list(ProjectItem.query_projection.keys()) ) + # Skip if project document does not exist # - this shouldn't happen using only UI elements if not project_doc: @@ -269,9 +275,8 @@ class HierarchyModel(QtCore.QAbstractItemModel): self.add_item(project_item) # Query all assets of the project - asset_docs = self.dbcon.database[project_name].find( - {"type": "asset"}, - AssetItem.query_projection + asset_docs = get_assets( + project_name, fields=AssetItem.query_projection.keys() ) asset_docs_by_id = { asset_doc["_id"]: asset_doc @@ -282,31 +287,16 @@ class HierarchyModel(QtCore.QAbstractItemModel): # if asset item can be modified (name and hierarchy change) # - the same must be applied to all it's parents asset_ids = list(asset_docs_by_id.keys()) - result = [] + asset_ids_with_subsets = [] if asset_ids: - result = self.dbcon.database[project_name].aggregate([ - { - "$match": { - "type": "subset", - "parent": {"$in": asset_ids} - } - }, - { - "$group": { - "_id": "$parent", - "count": {"$sum": 1} - } - } - ]) + asset_ids_with_subsets = get_asset_ids_with_subsets( + project_name, asset_ids=asset_ids + ) asset_modifiable = { - asset_id: True + asset_id: asset_id not in asset_ids_with_subsets for asset_id in asset_docs_by_id.keys() } - for item in result: - asset_id = item["_id"] - count = item["count"] - asset_modifiable[asset_id] = count < 1 # Store assets by their visual parent to be able create their hierarchy asset_docs_by_parent_id = collections.defaultdict(list) diff --git a/openpype/tools/project_manager/project_manager/view.py b/openpype/tools/project_manager/project_manager/view.py index 4b5bc36aeb..e9d2874c54 100644 --- a/openpype/tools/project_manager/project_manager/view.py +++ b/openpype/tools/project_manager/project_manager/view.py @@ -3,6 +3,7 @@ from queue import Queue from Qt import QtWidgets, QtCore, QtGui +from openpype.client import get_project from .delegates import ( NumberDelegate, NameDelegate, @@ -47,12 +48,8 @@ class ProjectDocCache: def set_project(self, project_name): self.project_doc = None - if not project_name: - return - - self.project_doc = self.dbcon.database[project_name].find_one( - {"type": "project"} - ) + if project_name: + self.project_doc = get_project(project_name) class ToolsCache: diff --git a/openpype/tools/project_manager/project_manager/widgets.py b/openpype/tools/project_manager/project_manager/widgets.py index dc75b30bd7..371d1ba2ef 100644 --- a/openpype/tools/project_manager/project_manager/widgets.py +++ b/openpype/tools/project_manager/project_manager/widgets.py @@ -1,5 +1,6 @@ import re +from openpype.client import get_projects from .constants import ( NAME_ALLOWED_SYMBOLS, NAME_REGEX @@ -272,15 +273,9 @@ class CreateProjectDialog(QtWidgets.QDialog): def _get_existing_projects(self): project_names = set() project_codes = set() - for project_name in self.dbcon.database.collection_names(): - # Each collection will have exactly one project document - project_doc = self.dbcon.database[project_name].find_one( - {"type": "project"}, - {"name": 1, "data.code": 1} - ) - if not project_doc: - continue - + for project_doc in get_projects( + inactive=True, fields=["name", "data.code"] + ): project_name = project_doc.get("name") if not project_name: continue diff --git a/openpype/tools/publisher/control.py b/openpype/tools/publisher/control.py index 2973d6a5bb..915fb7f32e 100644 --- a/openpype/tools/publisher/control.py +++ b/openpype/tools/publisher/control.py @@ -13,6 +13,7 @@ except Exception: import pyblish.api +from openpype.client import get_assets from openpype.pipeline import ( PublishValidationError, registered_host, @@ -116,10 +117,10 @@ class AssetDocsCache: def _query(self): if self._asset_docs is None: - asset_docs = list(self.dbcon.find( - {"type": "asset"}, - self.projection - )) + project_name = self.dbcon.active_project() + asset_docs = get_assets( + project_name, fields=self.projection.keys() + ) task_names_by_asset_name = {} for asset_doc in asset_docs: asset_name = asset_doc["name"] diff --git a/openpype/tools/publisher/widgets/create_dialog.py b/openpype/tools/publisher/widgets/create_dialog.py index 9e357f3a56..d579831b21 100644 --- a/openpype/tools/publisher/widgets/create_dialog.py +++ b/openpype/tools/publisher/widgets/create_dialog.py @@ -9,6 +9,8 @@ try: except Exception: commonmark = None from Qt import QtWidgets, QtCore, QtGui + +from openpype.client import get_asset, get_subsets from openpype.lib import TaskNotSetError from openpype.pipeline.create import ( CreatorError, @@ -647,21 +649,19 @@ class CreateDialog(QtWidgets.QDialog): if asset_name is None: return - asset_doc = self.dbcon.find_one({ - "type": "asset", - "name": asset_name - }) + project_name = self.dbcon.active_project() + asset_doc = get_asset(project_name, asset_name=asset_name) self._asset_doc = asset_doc if asset_doc: - subset_docs = self.dbcon.find( - { - "type": "subset", - "parent": asset_doc["_id"] - }, - {"name": 1} + asset_id = asset_doc["_id"] + subset_docs = get_subsets( + project_name, asset_ids=[asset_id], fields=["name"] ) - self._subset_names = set(subset_docs.distinct("name")) + self._subset_names = { + subset_doc["name"] + for subset_doc in subset_docs + } if not asset_doc: self.subset_name_input.setText("< Asset is not set >") diff --git a/openpype/tools/sceneinventory/model.py b/openpype/tools/sceneinventory/model.py index 8d72020c98..9cf69ed650 100644 --- a/openpype/tools/sceneinventory/model.py +++ b/openpype/tools/sceneinventory/model.py @@ -5,8 +5,14 @@ from collections import defaultdict from Qt import QtCore, QtGui import qtawesome -from bson.objectid import ObjectId +from openpype.client import ( + get_asset, + get_subset, + get_version, + get_last_version_for_subset, + get_representation, +) from openpype.pipeline import ( legacy_io, schema, @@ -55,7 +61,7 @@ class InventoryModel(TreeModel): if not self.sync_enabled: return - project_name = legacy_io.Session["AVALON_PROJECT"] + project_name = legacy_io.current_project() active_site = sync_server.get_active_site(project_name) remote_site = sync_server.get_remote_site(project_name) @@ -291,6 +297,9 @@ class InventoryModel(TreeModel): node.Item: root node which has children added based on the data """ + # NOTE: @iLLiCiTiT this need refactor + project_name = legacy_io.active_project() + self.beginResetModel() # Group by representation @@ -304,32 +313,36 @@ class InventoryModel(TreeModel): for repre_id, group_dict in sorted(grouped.items()): group_items = group_dict["items"] # Get parenthood per group - representation = legacy_io.find_one({"_id": ObjectId(repre_id)}) + representation = get_representation( + project_name, representation_id=repre_id + ) if not representation: not_found["representation"].append(group_items) not_found_ids.append(repre_id) continue - version = legacy_io.find_one({"_id": representation["parent"]}) + version = get_version( + project_name, version_id=representation["parent"] + ) if not version: not_found["version"].append(group_items) not_found_ids.append(repre_id) continue elif version["type"] == "hero_version": - _version = legacy_io.find_one({ - "_id": version["version_id"] - }) + _version = get_version( + project_name, version_id=version["version_id"] + ) version["name"] = HeroVersionType(_version["name"]) version["data"] = _version["data"] - subset = legacy_io.find_one({"_id": version["parent"]}) + subset = get_subset(project_name, subset_id=version["parent"]) if not subset: not_found["subset"].append(group_items) not_found_ids.append(repre_id) continue - asset = legacy_io.find_one({"_id": subset["parent"]}) + asset = get_asset(project_name, asset_id=subset["parent"]) if not asset: not_found["asset"].append(group_items) not_found_ids.append(repre_id) @@ -390,10 +403,9 @@ class InventoryModel(TreeModel): # Store the highest available version so the model can know # whether current version is currently up-to-date. - highest_version = legacy_io.find_one({ - "type": "version", - "parent": version["parent"] - }, sort=[("name", -1)]) + highest_version = get_last_version_for_subset( + project_name, subset_id=version["parent"] + ) # create the group header group_node = Item() diff --git a/openpype/tools/sceneinventory/switch_dialog.py b/openpype/tools/sceneinventory/switch_dialog.py index b2d770330f..b940c66a56 100644 --- a/openpype/tools/sceneinventory/switch_dialog.py +++ b/openpype/tools/sceneinventory/switch_dialog.py @@ -4,6 +4,16 @@ from Qt import QtWidgets, QtCore import qtawesome from bson.objectid import ObjectId +from openpype.client import ( + get_asset, + get_assets, + get_subset, + get_subsets, + get_versions, + get_hero_versions, + get_last_versions, + get_representations, +) from openpype.pipeline import legacy_io from openpype.pipeline.load import ( discover_loader_plugins, @@ -144,6 +154,9 @@ class SwitchAssetDialog(QtWidgets.QDialog): self._prepare_content_data() self.refresh(True) + def active_project(self): + return legacy_io.active_project() + def _prepare_content_data(self): repre_ids = set() content_loaders = set() @@ -151,10 +164,12 @@ class SwitchAssetDialog(QtWidgets.QDialog): repre_ids.add(ObjectId(item["representation"])) content_loaders.add(item["loader"]) - repres = list(legacy_io.find({ - "type": {"$in": ["representation", "archived_representation"]}, - "_id": {"$in": list(repre_ids)} - })) + project_name = self.active_project() + repres = get_representations( + project_name, + representation_ids=repre_ids, + archived=True + ) repres_by_id = {repre["_id"]: repre for repre in repres} # stash context values, works only for single representation @@ -179,10 +194,11 @@ class SwitchAssetDialog(QtWidgets.QDialog): content_repres[repre_id] = repres_by_id[repre_id] version_ids.append(repre["parent"]) - versions = legacy_io.find({ - "type": {"$in": ["version", "hero_version"]}, - "_id": {"$in": list(set(version_ids))} - }) + versions = get_versions( + project_name, + version_ids=set(version_ids), + hero=True + ) content_versions = {} hero_version_ids = set() for version in versions: @@ -198,10 +214,9 @@ class SwitchAssetDialog(QtWidgets.QDialog): else: subset_ids.append(content_versions[version_id]["parent"]) - subsets = legacy_io.find({ - "type": {"$in": ["subset", "archived_subset"]}, - "_id": {"$in": subset_ids} - }) + subsets = get_subsets( + project_name, subset_ids=subset_ids, archived=True + ) subsets_by_id = {sub["_id"]: sub for sub in subsets} asset_ids = [] @@ -220,10 +235,7 @@ class SwitchAssetDialog(QtWidgets.QDialog): asset_ids.append(subset["parent"]) content_subsets[subset_id] = subset - assets = legacy_io.find({ - "type": {"$in": ["asset", "archived_asset"]}, - "_id": {"$in": list(asset_ids)} - }) + assets = get_assets(project_name, asset_ids=asset_ids, archived=True) assets_by_id = {asset["_id"]: asset for asset in assets} missing_assets = [] @@ -472,9 +484,10 @@ class SwitchAssetDialog(QtWidgets.QDialog): # Prepare asset document if asset is selected asset_doc = None if selected_asset: - asset_doc = legacy_io.find_one( - {"type": "asset", "name": selected_asset}, - {"_id": True} + asset_doc = get_asset( + self.active_project(), + asset_name=selected_asset, + fields=["_id"] ) if not asset_doc: return [] @@ -523,38 +536,35 @@ class SwitchAssetDialog(QtWidgets.QDialog): def _get_current_output_repre_ids_xxx( self, asset_doc, selected_subset, selected_repre ): - subset_doc = legacy_io.find_one( - { - "type": "subset", - "name": selected_subset, - "parent": asset_doc["_id"] - }, - {"_id": True} + project_name = self.active_project() + subset_doc = get_subset( + project_name, + subset_name=selected_subset, + asset_id=asset_doc["_id"], + fields=["_id"] ) + subset_id = subset_doc["_id"] last_versions_by_subset_id = self.find_last_versions([subset_id]) version_doc = last_versions_by_subset_id.get(subset_id) if not version_doc: return [] - repre_docs = legacy_io.find( - { - "type": "representation", - "parent": version_doc["_id"], - "name": selected_repre - }, - {"_id": True} + repre_docs = get_representations( + project_name, + version_ids=[version_doc["_id"]], + representation_names=[selected_repre], + fields=["_id"] ) return [repre_doc["_id"] for repre_doc in repre_docs] def _get_current_output_repre_ids_xxo(self, asset_doc, selected_subset): - subset_doc = legacy_io.find_one( - { - "type": "subset", - "parent": asset_doc["_id"], - "name": selected_subset - }, - {"_id": True} + project_name = self.active_project() + subset_doc = get_subset( + project_name, + asset_id=asset_doc["_id"], + subset_name=selected_subset, + fields=["_id"] ) if not subset_doc: return [] @@ -563,41 +573,51 @@ class SwitchAssetDialog(QtWidgets.QDialog): for repre_doc in self.content_repres.values(): repre_names.add(repre_doc["name"]) - repre_docs = legacy_io.find( - { - "type": "representation", - "parent": subset_doc["_id"], - "name": {"$in": list(repre_names)} - }, - {"_id": True} + # TODO where to take version ids? + version_ids = [] + repre_docs = get_representations( + project_name, + representation_names=repre_names, + version_ids=version_ids, + fields=["_id"] ) return [repre_doc["_id"] for repre_doc in repre_docs] def _get_current_output_repre_ids_xox(self, asset_doc, selected_repre): - susbet_names = set() + subset_names = set() for subset_doc in self.content_subsets.values(): - susbet_names.add(subset_doc["name"]) + subset_names.add(subset_doc["name"]) - subset_docs = legacy_io.find( - { - "type": "subset", - "name": {"$in": list(susbet_names)}, - "parent": asset_doc["_id"] - }, - {"_id": True} + project_name = self.active_project() + subset_docs = get_subsets( + project_name, + asset_ids=[asset_doc["_id"]], + subset_names=subset_names, + fields=["_id", "name"] ) - subset_ids = [subset_doc["_id"] for subset_doc in subset_docs] - repre_docs = legacy_io.find( - { - "type": "representation", - "parent": {"$in": subset_ids}, - "name": selected_repre - }, - {"_id": True} + subset_name_by_id = { + subset_doc["_id"]: subset_doc["name"] + for subset_doc in subset_docs + } + subset_ids = list(subset_name_by_id.keys()) + last_versions_by_subset_id = self.find_last_versions(subset_ids) + last_version_id_by_subset_name = {} + for subset_id, last_version in last_versions_by_subset_id.items(): + subset_name = subset_name_by_id[subset_id] + last_version_id_by_subset_name[subset_name] = ( + last_version["_id"] + ) + + repre_docs = get_representations( + project_name, + version_ids=last_version_id_by_subset_name.values(), + representation_names=[selected_repre], + fields=["_id"] ) return [repre_doc["_id"] for repre_doc in repre_docs] def _get_current_output_repre_ids_xoo(self, asset_doc): + project_name = self.active_project() repres_by_subset_name = collections.defaultdict(set) for repre_doc in self.content_repres.values(): repre_name = repre_doc["name"] @@ -606,13 +626,11 @@ class SwitchAssetDialog(QtWidgets.QDialog): subset_name = subset_doc["name"] repres_by_subset_name[subset_name].add(repre_name) - subset_docs = list(legacy_io.find( - { - "type": "subset", - "parent": asset_doc["_id"], - "name": {"$in": list(repres_by_subset_name.keys())} - }, - {"_id": True, "name": True} + subset_docs = list(get_subsets( + project_name, + asset_ids=[asset_doc["_id"]], + subset_names=repres_by_subset_name.keys(), + fields=["_id", "name"] )) subset_name_by_id = { subset_doc["_id"]: subset_doc["name"] @@ -627,60 +645,59 @@ class SwitchAssetDialog(QtWidgets.QDialog): last_version["_id"] ) - repre_or_query = [] + repre_names_by_version_id = {} for subset_name, repre_names in repres_by_subset_name.items(): version_id = last_version_id_by_subset_name.get(subset_name) # This should not happen but why to crash? - if version_id is None: - continue - repre_or_query.append({ - "parent": version_id, - "name": {"$in": list(repre_names)} - }) - repre_docs = legacy_io.find( - {"$or": repre_or_query}, - {"_id": True} + if version_id is not None: + repre_names_by_version_id[version_id] = list(repre_names) + + repre_docs = get_representations( + project_name, + names_by_version_ids=repre_names_by_version_id, + fields=["_id"] ) return [repre_doc["_id"] for repre_doc in repre_docs] def _get_current_output_repre_ids_oxx( self, selected_subset, selected_repre ): - subset_docs = list(legacy_io.find({ - "type": "subset", - "parent": {"$in": list(self.content_assets.keys())}, - "name": selected_subset - })) + project_name = self.active_project() + subset_docs = get_subsets( + project_name, + asset_ids=self.content_assets.keys(), + subset_names=[selected_subset], + fields=["_id"] + ) subset_ids = [subset_doc["_id"] for subset_doc in subset_docs] last_versions_by_subset_id = self.find_last_versions(subset_ids) last_version_ids = [ last_version["_id"] for last_version in last_versions_by_subset_id.values() ] - repre_docs = legacy_io.find({ - "type": "representation", - "parent": {"$in": last_version_ids}, - "name": selected_repre - }) - + repre_docs = get_representations( + project_name, + version_ids=last_version_ids, + representation_names=[selected_repre], + fields=["_id"] + ) return [repre_doc["_id"] for repre_doc in repre_docs] def _get_current_output_repre_ids_oxo(self, selected_subset): - subset_docs = list(legacy_io.find( - { - "type": "subset", - "parent": {"$in": list(self.content_assets.keys())}, - "name": selected_subset - }, - {"_id": True, "parent": True} - )) - if not subset_docs: - return list() - + project_name = self.active_project() + subset_docs = get_subsets( + project_name, + asset_ids=self.content_assets.keys(), + subset_names=[selected_subset], + fields=["_id", "parent"] + ) subset_docs_by_id = { subset_doc["_id"]: subset_doc for subset_doc in subset_docs } + if not subset_docs: + return list() + last_versions_by_subset_id = self.find_last_versions( subset_docs_by_id.keys() ) @@ -702,56 +719,44 @@ class SwitchAssetDialog(QtWidgets.QDialog): asset_id = asset_doc["_id"] repre_names_by_asset_id[asset_id].add(repre_name) - repre_or_query = [] + repre_names_by_version_id = {} for last_version_id, subset_id in subset_id_by_version_id.items(): subset_doc = subset_docs_by_id[subset_id] asset_id = subset_doc["parent"] repre_names = repre_names_by_asset_id.get(asset_id) if not repre_names: continue - repre_or_query.append({ - "parent": last_version_id, - "name": {"$in": list(repre_names)} - }) - repre_docs = legacy_io.find( - { - "type": "representation", - "$or": repre_or_query - }, - {"_id": True} - ) + repre_names_by_version_id[last_version_id] = repre_names + repre_docs = get_representations( + project_name, + names_by_version_ids=repre_names_by_version_id, + fields=["_id"] + ) return [repre_doc["_id"] for repre_doc in repre_docs] def _get_current_output_repre_ids_oox(self, selected_repre): - repre_docs = legacy_io.find( - { - "name": selected_repre, - "parent": {"$in": list(self.content_versions.keys())} - }, - {"_id": True} + project_name = self.active_project() + repre_docs = get_representations( + project_name, + representation_names=[selected_repre], + version_ids=self.content_versions.keys(), + fields=["_id"] ) return [repre_doc["_id"] for repre_doc in repre_docs] def _get_asset_box_values(self): - asset_docs = legacy_io.find( - {"type": "asset"}, - {"_id": 1, "name": 1} - ) + project_name = self.active_project() + asset_docs = get_assets(project_name, fields=["_id", "name"]) asset_names_by_id = { asset_doc["_id"]: asset_doc["name"] for asset_doc in asset_docs } - subsets = legacy_io.find( - { - "type": "subset", - "parent": {"$in": list(asset_names_by_id.keys())} - }, - { - "parent": 1 - } + subsets = get_subsets( + project_name, + asset_ids=asset_names_by_id.keys(), + fields=["parent"] ) - filtered_assets = [] for subset in subsets: asset_name = asset_names_by_id[subset["parent"]] @@ -760,25 +765,20 @@ class SwitchAssetDialog(QtWidgets.QDialog): return sorted(filtered_assets) def _get_subset_box_values(self): + project_name = self.active_project() selected_asset = self._assets_box.get_valid_value() if selected_asset: - asset_doc = legacy_io.find_one({ - "type": "asset", - "name": selected_asset - }) + asset_doc = get_asset( + project_name, asset_name=selected_asset, fields=["_id"] + ) asset_ids = [asset_doc["_id"]] else: asset_ids = list(self.content_assets.keys()) - subsets = legacy_io.find( - { - "type": "subset", - "parent": {"$in": asset_ids} - }, - { - "parent": 1, - "name": 1 - } + subsets = get_subsets( + project_name, + asset_ids=asset_ids, + fields=["parent", "name"] ) subset_names_by_parent_id = collections.defaultdict(set) @@ -800,6 +800,7 @@ class SwitchAssetDialog(QtWidgets.QDialog): def _representations_box_values(self): # NOTE hero versions are not used because it is expected that # hero version has same representations as latests + project_name = self.active_project() selected_asset = self._assets_box.currentText() selected_subset = self._subsets_box.currentText() @@ -807,16 +808,11 @@ class SwitchAssetDialog(QtWidgets.QDialog): # [ ] [ ] [?] if not selected_asset and not selected_subset: # Find all representations of selection's subsets - possible_repres = list(legacy_io.find( - { - "type": "representation", - "parent": {"$in": list(self.content_versions.keys())} - }, - { - "parent": 1, - "name": 1 - } - )) + possible_repres = get_representations( + project_name, + version_ids=self.content_versions.keys(), + fields=["parent", "name"] + ) possible_repres_by_parent = collections.defaultdict(set) for repre in possible_repres: @@ -836,29 +832,23 @@ class SwitchAssetDialog(QtWidgets.QDialog): # [x] [x] [?] if selected_asset and selected_subset: - asset_doc = legacy_io.find_one( - {"type": "asset", "name": selected_asset}, - {"_id": 1} + asset_doc = get_asset( + project_name, asset_name=selected_asset, fields=["_id"] ) - subset_doc = legacy_io.find_one( - { - "type": "subset", - "name": selected_subset, - "parent": asset_doc["_id"] - }, - {"_id": 1} + subset_doc = get_subset( + project_name, + asset_id=asset_doc["_id"], + subset_name=selected_subset, + fields=["_id"] ) + subset_id = subset_doc["_id"] last_versions_by_subset_id = self.find_last_versions([subset_id]) version_doc = last_versions_by_subset_id.get(subset_id) - repre_docs = legacy_io.find( - { - "type": "representation", - "parent": version_doc["_id"] - }, - { - "name": 1 - } + repre_docs = get_representations( + project_name, + version_ids=[version_doc["_id"]], + fields=["name"] ) return [ repre_doc["name"] @@ -868,9 +858,8 @@ class SwitchAssetDialog(QtWidgets.QDialog): # [x] [ ] [?] # If asset only is selected if selected_asset: - asset_doc = legacy_io.find_one( - {"type": "asset", "name": selected_asset}, - {"_id": 1} + asset_doc = get_asset( + project_name, asset_name=selected_asset, fields=["_id"] ) if not asset_doc: return list() @@ -879,13 +868,12 @@ class SwitchAssetDialog(QtWidgets.QDialog): subset_names = set() for subset_doc in self.content_subsets.values(): subset_names.add(subset_doc["name"]) - subset_docs = legacy_io.find( - { - "type": "subset", - "parent": asset_doc["_id"], - "name": {"$in": list(subset_names)} - }, - {"_id": 1} + + subset_docs = get_subsets( + project_name, + asset_ids=[asset_doc["_id"]], + subset_names=subset_names, + fields=["_id"] ) subset_ids = [ subset_doc["_id"] @@ -903,15 +891,10 @@ class SwitchAssetDialog(QtWidgets.QDialog): if not subset_id_by_version_id: return list() - repre_docs = list(legacy_io.find( - { - "type": "representation", - "parent": {"$in": list(subset_id_by_version_id.keys())} - }, - { - "name": 1, - "parent": 1 - } + repre_docs = list(get_representations( + project_name, + version_ids=subset_id_by_version_id.keys(), + fields=["name", "parent"] )) if not repre_docs: return list() @@ -933,13 +916,11 @@ class SwitchAssetDialog(QtWidgets.QDialog): return list(available_repres) # [ ] [x] [?] - subset_docs = list(legacy_io.find( - { - "type": "subset", - "parent": {"$in": list(self.content_assets.keys())}, - "name": selected_subset - }, - {"_id": 1, "parent": 1} + subset_docs = list(get_subsets( + project_name, + asset_ids=self.content_assets.keys(), + subset_names=[selected_subset], + fields=["_id", "parent"] )) if not subset_docs: return list() @@ -960,16 +941,13 @@ class SwitchAssetDialog(QtWidgets.QDialog): if not subset_id_by_version_id: return list() - repre_docs = list(legacy_io.find( - { - "type": "representation", - "parent": {"$in": list(subset_id_by_version_id.keys())} - }, - { - "name": 1, - "parent": 1 - } - )) + repre_docs = list( + get_representations( + project_name, + subset_ids=subset_id_by_version_id.keys(), + fields=["name", "parent"] + ) + ) if not repre_docs: return list() @@ -1016,14 +994,14 @@ class SwitchAssetDialog(QtWidgets.QDialog): return # [x] [ ] [?] - asset_doc = legacy_io.find_one( - {"type": "asset", "name": selected_asset}, - {"_id": 1} + project_name = self.active_project() + asset_doc = get_asset( + project_name, asset_name=selected_asset, fields=["_id"] ) - subset_docs = legacy_io.find( - {"type": "subset", "parent": asset_doc["_id"]}, - {"name": 1} + subset_docs = get_subsets( + project_name, asset_ids=[asset_doc["_id"]], fields=["name"] ) + subset_names = set( subset_doc["name"] for subset_doc in subset_docs @@ -1035,27 +1013,12 @@ class SwitchAssetDialog(QtWidgets.QDialog): break def find_last_versions(self, subset_ids): - _pipeline = [ - # Find all versions of those subsets - {"$match": { - "type": "version", - "parent": {"$in": list(subset_ids)} - }}, - # Sorting versions all together - {"$sort": {"name": 1}}, - # Group them by "parent", but only take the last - {"$group": { - "_id": "$parent", - "_version_id": {"$last": "$_id"}, - "type": {"$last": "$type"} - }} - ] - last_versions_by_subset_id = dict() - for doc in legacy_io.aggregate(_pipeline): - doc["parent"] = doc["_id"] - doc["_id"] = doc.pop("_version_id") - last_versions_by_subset_id[doc["parent"]] = doc - return last_versions_by_subset_id + project_name = self.active_project() + return get_last_versions( + project_name, + subset_ids=subset_ids, + fields=["_id", "parent", "type"] + ) def _is_repre_ok(self, validation_state): selected_asset = self._assets_box.get_valid_value() @@ -1078,33 +1041,28 @@ class SwitchAssetDialog(QtWidgets.QDialog): return # [x] [x] [ ] + project_name = self.active_project() if selected_asset is not None and selected_subset is not None: - asset_doc = legacy_io.find_one( - {"type": "asset", "name": selected_asset}, - {"_id": 1} + asset_doc = get_asset( + project_name, asset_name=selected_asset, fields=["_id"] ) - subset_doc = legacy_io.find_one( - { - "type": "subset", - "parent": asset_doc["_id"], - "name": selected_subset - }, - {"_id": 1} + subset_doc = get_subset( + project_name, + asset_id=asset_doc["_id"], + subset_name=selected_subset, + fields=["_id"] ) - last_versions_by_subset_id = self.find_last_versions( - [subset_doc["_id"]] - ) - last_version = last_versions_by_subset_id.get(subset_doc["_id"]) + subset_id = subset_doc["_id"] + last_versions_by_subset_id = self.find_last_versions([subset_id]) + last_version = last_versions_by_subset_id.get(subset_id) if not last_version: validation_state.repre_ok = False return - repre_docs = legacy_io.find( - { - "type": "representation", - "parent": last_version["_id"] - }, - {"name": 1} + repre_docs = get_representations( + project_name, + version_ids=[last_version["_id"]], + fields=["name"] ) repre_names = set( @@ -1119,16 +1077,13 @@ class SwitchAssetDialog(QtWidgets.QDialog): # [x] [ ] [ ] if selected_asset is not None: - asset_doc = legacy_io.find_one( - {"type": "asset", "name": selected_asset}, - {"_id": 1} + asset_doc = get_asset( + project_name, asset_name=selected_asset, fields=["_id"] ) - subset_docs = list(legacy_io.find( - { - "type": "subset", - "parent": asset_doc["_id"] - }, - {"_id": 1, "name": 1} + subset_docs = list(get_subsets( + project_name, + asset_ids=[asset_doc["_id"]], + fields=["_id", "name"] )) subset_name_by_id = {} @@ -1145,15 +1100,10 @@ class SwitchAssetDialog(QtWidgets.QDialog): version_id = last_version["_id"] subset_id_by_version_id[version_id] = subset_id - repre_docs = legacy_io.find( - { - "type": "representation", - "parent": {"$in": list(subset_id_by_version_id.keys())} - }, - { - "name": 1, - "parent": 1 - } + repre_docs = get_representations( + project_name, + subset_ids=subset_id_by_version_id.keys(), + fields=["name", "parent"] ) repres_by_subset_name = {} for repre_doc in repre_docs: @@ -1176,15 +1126,12 @@ class SwitchAssetDialog(QtWidgets.QDialog): # [ ] [x] [ ] # Subset documents - subset_docs = legacy_io.find( - { - "type": "subset", - "parent": {"$in": list(self.content_assets.keys())}, - "name": selected_subset - }, - {"_id": 1, "name": 1, "parent": 1} + subset_docs = get_subsets( + project_name, + asset_ids=self.content_assets.keys(), + subset_names=[selected_subset], + fields=["_id", "name", "parent"] ) - subset_docs_by_id = {} for subset_doc in subset_docs: subset_docs_by_id[subset_doc["_id"]] = subset_doc @@ -1197,15 +1144,10 @@ class SwitchAssetDialog(QtWidgets.QDialog): version_id = last_version["_id"] subset_id_by_version_id[version_id] = subset_id - repre_docs = legacy_io.find( - { - "type": "representation", - "parent": {"$in": list(subset_id_by_version_id.keys())} - }, - { - "name": 1, - "parent": 1 - } + repre_docs = get_representations( + project_name, + version_ids=subset_id_by_version_id.keys(), + fields=["name", "parent"] ) repres_by_asset_id = {} for repre_doc in repre_docs: @@ -1245,11 +1187,9 @@ class SwitchAssetDialog(QtWidgets.QDialog): selected_subset = self._subsets_box.get_valid_value() selected_representation = self._representations_box.get_valid_value() + project_name = self.active_project() if selected_asset: - asset_doc = legacy_io.find_one({ - "type": "asset", - "name": selected_asset - }) + asset_doc = get_asset(project_name, asset_name=selected_asset) asset_docs_by_id = {asset_doc["_id"]: asset_doc} else: asset_docs_by_id = self.content_assets @@ -1259,16 +1199,15 @@ class SwitchAssetDialog(QtWidgets.QDialog): for asset_doc in asset_docs_by_id.values() } - asset_ids = list(asset_docs_by_id.keys()) - - subset_query = { - "type": "subset", - "parent": {"$in": asset_ids} - } + subset_names = None if selected_subset: - subset_query["name"] = selected_subset + subset_names = [selected_subset] - subset_docs = list(legacy_io.find(subset_query)) + subset_docs = list(get_subsets( + project_name, + subset_names=subset_names, + asset_ids=asset_docs_by_id.keys() + )) subset_ids = [] subset_docs_by_parent_and_name = collections.defaultdict(dict) for subset in subset_docs: @@ -1278,15 +1217,14 @@ class SwitchAssetDialog(QtWidgets.QDialog): subset_docs_by_parent_and_name[parent_id][name] = subset # versions - version_docs = list(legacy_io.find({ - "type": "version", - "parent": {"$in": subset_ids} - }, sort=[("name", -1)])) + _version_docs = get_versions(project_name, subset_ids=subset_ids) + version_docs = list(reversed( + sorted(_version_docs, key=lambda item: item["name"]) + )) - hero_version_docs = list(legacy_io.find({ - "type": "hero_version", - "parent": {"$in": subset_ids} - })) + hero_version_docs = list(get_hero_versions( + project_name, subset_ids=subset_ids + )) version_ids = list() @@ -1303,10 +1241,7 @@ class SwitchAssetDialog(QtWidgets.QDialog): parent_id = hero_version_doc["parent"] hero_version_docs_by_parent_id[parent_id] = hero_version_doc - repre_docs = legacy_io.find({ - "type": "representation", - "parent": {"$in": version_ids} - }) + repre_docs = get_representations(project_name, version_ids=version_ids) repre_docs_by_parent_id_by_name = collections.defaultdict(dict) for repre_doc in repre_docs: parent_id = repre_doc["parent"] diff --git a/openpype/tools/sceneinventory/view.py b/openpype/tools/sceneinventory/view.py index 448e3f4e6f..6a95ccb57b 100644 --- a/openpype/tools/sceneinventory/view.py +++ b/openpype/tools/sceneinventory/view.py @@ -6,6 +6,13 @@ from Qt import QtWidgets, QtCore import qtawesome from bson.objectid import ObjectId +from openpype.client import ( + get_version, + get_versions, + get_hero_versions, + get_representation, + get_representations, +) from openpype import style from openpype.pipeline import ( legacy_io, @@ -83,12 +90,9 @@ class SceneInventoryView(QtWidgets.QTreeView): if item_id not in repre_ids: repre_ids.append(item_id) - repre_docs = legacy_io.find( - { - "type": "representation", - "_id": {"$in": repre_ids} - }, - {"parent": 1} + project_name = legacy_io.active_project() + repre_docs = get_representations( + project_name, representaion_ids=repre_ids, fields=["parent"] ) version_ids = [] @@ -97,10 +101,9 @@ class SceneInventoryView(QtWidgets.QTreeView): if version_id not in version_ids: version_ids.append(version_id) - loaded_versions = legacy_io.find({ - "_id": {"$in": version_ids}, - "type": {"$in": ["version", "hero_version"]} - }) + loaded_versions = get_versions( + project_name, version_ids=version_ids, hero=True + ) loaded_hero_versions = [] versions_by_parent_id = collections.defaultdict(list) @@ -114,10 +117,9 @@ class SceneInventoryView(QtWidgets.QTreeView): if parent_id not in version_parents: version_parents.append(parent_id) - all_versions = legacy_io.find({ - "type": {"$in": ["hero_version", "version"]}, - "parent": {"$in": version_parents} - }) + all_versions = get_versions( + project_name, subset_ids=version_parents, hero=True + ) hero_versions = [] versions = [] for version in all_versions: @@ -150,12 +152,10 @@ class SceneInventoryView(QtWidgets.QTreeView): if item_id not in repre_ids: repre_ids.append(item_id) - repre_docs = legacy_io.find( - { - "type": "representation", - "_id": {"$in": repre_ids} - }, - {"parent": 1} + repre_docs = get_representations( + project_name, + representation_ids=repre_ids, + fields=["parent"] ) version_ids = [] @@ -165,13 +165,13 @@ class SceneInventoryView(QtWidgets.QTreeView): version_id_by_repre_id[repre_doc["_id"]] = version_id if version_id not in version_ids: version_ids.append(version_id) - hero_versions = legacy_io.find( - { - "_id": {"$in": version_ids}, - "type": "hero_version" - }, - {"version_id": 1} + + hero_versions = get_hero_versions( + project_name, + version_ids=version_ids, + fields=["version_id"] ) + version_ids = set() for hero_version in hero_versions: version_id = hero_version["version_id"] @@ -183,12 +183,10 @@ class SceneInventoryView(QtWidgets.QTreeView): if current_version_id == hero_version_id: version_id_by_repre_id[_repre_id] = version_id - version_docs = legacy_io.find( - { - "_id": {"$in": list(version_ids)}, - "type": "version" - }, - {"name": 1} + version_docs = get_versions( + project_name, + version_ids=version_ids, + fields=["name"] ) version_name_by_id = {} for version_doc in version_docs: @@ -370,10 +368,9 @@ class SceneInventoryView(QtWidgets.QTreeView): active_site = self.sync_server.get_active_site(project_name) remote_site = self.sync_server.get_remote_site(project_name) - repre_docs = legacy_io.find({ - "type": "representation", - "_id": {"$in": repre_ids} - }) + repre_docs = get_representations( + project_name, representation_ids=repre_ids + ) repre_docs_by_id = { repre_doc["_id"]: repre_doc for repre_doc in repre_docs @@ -658,25 +655,37 @@ class SceneInventoryView(QtWidgets.QTreeView): active = items[-1] + project_name = legacy_io.active_project() # Get available versions for active representation representation_id = ObjectId(active["representation"]) - representation = legacy_io.find_one({"_id": representation_id}) - version = legacy_io.find_one({ - "_id": representation["parent"] - }) - versions = list(legacy_io.find( - { - "parent": version["parent"], - "type": "version" - }, - sort=[("name", 1)] + repre_doc = get_representation( + project_name, + representation_id=representation_id, + fields=["parent"] + ) + + repre_version_doc = get_version( + project_name, + version_id=repre_doc["parent"], + fields=["parent"] + ) + + version_docs = get_versions( + project_name, + subset_ids=[repre_version_doc["parent"]], + hero=True + ) + hero_version = None + standard_versions = [] + for version_doc in version_docs: + if version_doc["type"] == "hero_version": + hero_version = version_doc + else: + standard_versions.append(version_doc) + versions = list(reversed( + sorted(standard_versions, key=lambda item: item["name"]) )) - - hero_version = legacy_io.find_one({ - "parent": version["parent"], - "type": "hero_version" - }) if hero_version: _version_id = hero_version["version_id"] for _version in versions: @@ -703,7 +712,7 @@ class SceneInventoryView(QtWidgets.QTreeView): all_versions = [] if hero_version: all_versions.append(hero_version) - all_versions.extend(reversed(versions)) + all_versions.extend(versions) if current_item: index = all_versions.index(current_item) diff --git a/openpype/tools/standalonepublish/app.py b/openpype/tools/standalonepublish/app.py index 1ad5cd119e..4831db038c 100644 --- a/openpype/tools/standalonepublish/app.py +++ b/openpype/tools/standalonepublish/app.py @@ -6,6 +6,8 @@ import signal from bson.objectid import ObjectId from Qt import QtWidgets, QtCore, QtGui +from openpype.client import get_asset + from .widgets import ( AssetWidget, FamilyWidget, ComponentsWidget, ShadowWidget ) @@ -126,17 +128,6 @@ class Window(QtWidgets.QDialog): if event: super().resizeEvent(event) - def get_avalon_parent(self, entity): - ''' Avalon DB entities helper - get all parents (exclude project). - ''' - parent_id = entity['data']['visualParent'] - parents = [] - if parent_id is not None: - parent = self.db.find_one({'_id': parent_id}) - parents.extend(self.get_avalon_parent(parent)) - parents.append(parent['name']) - return parents - def on_project_change(self, project_name): self.widget_family.refresh() @@ -152,7 +143,10 @@ class Window(QtWidgets.QDialog): ] if len(selected) == 1: self.valid_parent = True - asset = self.db.find_one({"_id": selected[0], "type": "asset"}) + project_name = self.db.active_project() + asset = get_asset( + project_name, asset_id=selected[0], fields=["name"] + ) self.widget_family.change_asset(asset['name']) else: self.valid_parent = False diff --git a/openpype/tools/standalonepublish/widgets/model_asset.py b/openpype/tools/standalonepublish/widgets/model_asset.py index 02e9073555..abfc0a2145 100644 --- a/openpype/tools/standalonepublish/widgets/model_asset.py +++ b/openpype/tools/standalonepublish/widgets/model_asset.py @@ -4,6 +4,7 @@ import collections from Qt import QtCore, QtGui import qtawesome +from openpype.client import get_assets from openpype.style import ( get_default_entity_icon_color, get_deprecated_entity_font_color, @@ -104,17 +105,18 @@ class AssetModel(TreeModel): def refresh(self): """Refresh the data for the model.""" + project_name = self.dbcon.active_project() self.clear() - if ( - self.dbcon.active_project() is None or - self.dbcon.active_project() == '' - ): + if not project_name: return self.beginResetModel() # Get all assets in current project sorted by name - db_assets = self.dbcon.find({"type": "asset"}).sort("name", 1) + asset_docs = get_assets(project_name) + db_assets = list( + sorted(asset_docs, key=lambda item: item["name"]) + ) # Group the assets by their visual parent's id assets_by_parent = collections.defaultdict(list) diff --git a/openpype/tools/standalonepublish/widgets/widget_asset.py b/openpype/tools/standalonepublish/widgets/widget_asset.py index 8b43cd7cf8..0b5802ed9e 100644 --- a/openpype/tools/standalonepublish/widgets/widget_asset.py +++ b/openpype/tools/standalonepublish/widgets/widget_asset.py @@ -2,6 +2,10 @@ import contextlib from Qt import QtWidgets, QtCore import qtawesome +from openpype.client import ( + get_project, + get_asset, +) from openpype.tools.utils import PlaceholderLineEdit from openpype.style import get_default_tools_icon_color @@ -218,7 +222,8 @@ class AssetWidget(QtWidgets.QWidget): self.view = view def collect_data(self): - project = self.dbcon.find_one({'type': 'project'}) + project_name = self.dbcon.active_project() + project = get_project(project_name, fields=["name"]) asset = self.get_active_asset() try: @@ -241,9 +246,16 @@ class AssetWidget(QtWidgets.QWidget): return ent_parents output = [] - if entity.get('data', {}).get('visualParent', None) is None: + parent_asset_id = entity.get('data', {}).get('visualParent', None) + if parent_asset_id is None: return output - parent = self.dbcon.find_one({'_id': entity['data']['visualParent']}) + + project_name = self.dbcon.active_project() + parent = get_asset( + project_name, + asset_id=parent_asset_id, + fields=["name", "data.visualParent"] + ) output.append(parent['name']) output.extend(self.get_parents(parent)) return output @@ -349,9 +361,10 @@ class AssetWidget(QtWidgets.QWidget): tasks = [] selected = self.get_selected_assets() if len(selected) == 1: - asset = self.dbcon.find_one({ - "_id": selected[0], "type": "asset" - }) + project_name = self.dbcon.active_project() + asset = get_asset( + project_name, asset_id=selected[0], fields=["data.tasks"] + ) if asset: tasks = asset.get('data', {}).get('tasks', []) self.task_model.set_tasks(tasks) @@ -400,7 +413,7 @@ class AssetWidget(QtWidgets.QWidget): # Select mode = selection_model.Select | selection_model.Rows - for index in lib.iter_model_rows( + for index in _iter_model_rows( self.proxy, column=0, include_root=False ): # stop iteration if there are no assets to process diff --git a/openpype/tools/standalonepublish/widgets/widget_family.py b/openpype/tools/standalonepublish/widgets/widget_family.py index 08cd45bbf2..ed9f405f38 100644 --- a/openpype/tools/standalonepublish/widgets/widget_family.py +++ b/openpype/tools/standalonepublish/widgets/widget_family.py @@ -1,14 +1,21 @@ import re from Qt import QtWidgets, QtCore -from . import HelpRole, FamilyRole, ExistsRole, PluginRole, PluginKeyRole -from . import FamilyDescriptionWidget +from openpype.client import ( + get_asset, + get_subset, + get_subsets, + get_last_version_for_subset, +) from openpype.api import get_project_settings from openpype.pipeline import LegacyCreator from openpype.lib import TaskNotSetError from openpype.pipeline.create import SUBSET_NAME_ALLOWED_SYMBOLS +from . import HelpRole, FamilyRole, ExistsRole, PluginRole, PluginKeyRole +from . import FamilyDescriptionWidget + class FamilyWidget(QtWidgets.QWidget): @@ -180,12 +187,9 @@ class FamilyWidget(QtWidgets.QWidget): asset_doc = None if asset_name != self.NOT_SELECTED: # Get the assets from the database which match with the name - asset_doc = self.dbcon.find_one( - { - "type": "asset", - "name": asset_name - }, - {"_id": 1} + project_name = self.dbcon.active_project() + asset_doc = get_asset( + project_name, asset_name=asset_name, fields=["_id"] ) # Get plugin and family @@ -200,14 +204,13 @@ class FamilyWidget(QtWidgets.QWidget): return # Get the asset from the database which match with the name - asset_doc = self.dbcon.find_one( - {"name": asset_name, "type": "asset"}, - projection={"_id": 1} + project_name = self.dbcon.active_project() + asset_doc = get_asset( + project_name, asset_name=asset_name, fields=["_id"] ) # Get plugin plugin = item.data(PluginRole) if asset_doc and plugin: - project_name = self.dbcon.Session["AVALON_PROJECT"] asset_id = asset_doc["_id"] task_name = self.dbcon.Session["AVALON_TASK"] @@ -231,14 +234,14 @@ class FamilyWidget(QtWidgets.QWidget): self.input_result.setText("Select task please") # Get all subsets of the current asset - subset_docs = self.dbcon.find( - { - "type": "subset", - "parent": asset_id - }, - {"name": 1} + subset_docs = get_subsets( + project_name, asset_ids=[asset_id], fields=["name"] ) - existing_subset_names = set(subset_docs.distinct("name")) + + existing_subset_names = { + subset_doc["name"] + for subset_doc in subset_docs + } # Defaults to dropdown defaults = [] @@ -296,47 +299,37 @@ class FamilyWidget(QtWidgets.QWidget): if not auto_version: return + project_name = self.dbcon.active_project() asset_name = self.asset_name subset_name = self.input_result.text() version = 1 asset_doc = None subset_doc = None - versions = None if ( asset_name != self.NOT_SELECTED and subset_name.strip() != '' ): - asset_doc = self.dbcon.find_one( - { - 'type': 'asset', - 'name': asset_name - }, - {"_id": 1} + asset_doc = get_asset( + project_name, asset_name=asset_name, fields=["_id"] ) if asset_doc: - subset_doc = self.dbcon.find_one( - { - 'type': 'subset', - 'parent': asset_doc['_id'], - 'name': subset_name - }, - {"_id": 1} + subset_doc = get_subset( + project_name, + subset_name=subset_name, + asset_id=asset_doc['_id'], + fields=["_id"] ) if subset_doc: - versions = self.dbcon.find( - { - 'type': 'version', - 'parent': subset_doc['_id'] - }, - {"name": 1} - ).distinct("name") - - if versions: - versions = sorted(versions) - version = int(versions[-1]) + 1 + last_version = get_last_version_for_subset( + project_name, + subset_id=subset_doc["_id"], + fields=["name"] + ) + if last_version: + version = last_version["name"] + 1 self.version_spinbox.setValue(version) diff --git a/openpype/tools/texture_copy/app.py b/openpype/tools/texture_copy/app.py index fd8d6dc02e..8703f075d3 100644 --- a/openpype/tools/texture_copy/app.py +++ b/openpype/tools/texture_copy/app.py @@ -4,6 +4,7 @@ import click import speedcopy +from openpype.client import get_project, get_asset from openpype.lib import Terminal from openpype.api import Anatomy from openpype.pipeline import legacy_io @@ -29,20 +30,6 @@ class TextureCopy: if os.path.splitext(x)[1].lower() in texture_extensions) return textures - def _get_project(self, project_name): - project = legacy_io.find_one({ - 'type': 'project', - 'name': project_name - }) - return project - - def _get_asset(self, asset_name): - asset = legacy_io.find_one({ - 'type': 'asset', - 'name': asset_name - }) - return asset - def _get_destination_path(self, asset, project): project_name = project["name"] hierarchy = "" @@ -88,11 +75,12 @@ class TextureCopy: t.echo("!!! {}".format(e)) exit(1) - def process(self, asset, project, path): + def process(self, asset_name, project_name, path): """ Process all textures found in path and copy them to asset under project. """ + t.echo(">>> Looking for textures ...") textures = self._get_textures(path) if len(textures) < 1: @@ -101,14 +89,14 @@ class TextureCopy: else: t.echo(">>> Found {} textures ...".format(len(textures))) - project = self._get_project(project) + project = get_project(project_name) if not project: - t.echo("!!! Project name [ {} ] not found.".format(project)) + t.echo("!!! Project name [ {} ] not found.".format(project_name)) exit(1) - asset = self._get_asset(asset) - if not project: - t.echo("!!! Asset [ {} ] not found in project".format(asset)) + asset = get_asset(project_name, asset_name=asset_name) + if not asset: + t.echo("!!! Asset [ {} ] not found in project".format(asset_name)) exit(1) t.echo((">>> Project [ {} ] and " "asset [ {} ] seems to be OK ...").format(project['name'], diff --git a/openpype/tools/utils/assets_widget.py b/openpype/tools/utils/assets_widget.py index 82bdcd63a2..772946e9e1 100644 --- a/openpype/tools/utils/assets_widget.py +++ b/openpype/tools/utils/assets_widget.py @@ -5,6 +5,10 @@ import Qt from Qt import QtWidgets, QtCore, QtGui import qtawesome +from openpype.client import ( + get_project, + get_assets, +) from openpype.style import ( get_objected_colors, get_default_tools_icon_color, @@ -525,21 +529,18 @@ class AssetModel(QtGui.QStandardItemModel): self._doc_fetched.emit() def _fetch_asset_docs(self): - if not self.dbcon.Session.get("AVALON_PROJECT"): + project_name = self.dbcon.current_project() + if not project_name: return [] - project_doc = self.dbcon.find_one( - {"type": "project"}, - {"_id": True} - ) + project_doc = get_project(project_name, fields=["_id"]) if not project_doc: return [] # Get all assets sorted by name - return list(self.dbcon.find( - {"type": "asset"}, - self._asset_projection - )) + return list( + get_assets(project_name, fields=self._asset_projection.keys()) + ) def _stop_fetch_thread(self): self._refreshing = False diff --git a/openpype/tools/utils/delegates.py b/openpype/tools/utils/delegates.py index 71f817a1d7..d6c2d69e76 100644 --- a/openpype/tools/utils/delegates.py +++ b/openpype/tools/utils/delegates.py @@ -6,15 +6,19 @@ import numbers import Qt from Qt import QtWidgets, QtGui, QtCore -from openpype.pipeline import HeroVersionType -from .models import TreeModel -from . import lib - if Qt.__binding__ == "PySide": from PySide.QtGui import QStyleOptionViewItemV4 elif Qt.__binding__ == "PyQt4": from PyQt4.QtGui import QStyleOptionViewItemV4 +from openpype.client import ( + get_versions, + get_hero_versions, +) +from openpype.pipeline import HeroVersionType +from .models import TreeModel +from . import lib + log = logging.getLogger(__name__) @@ -114,26 +118,24 @@ class VersionDelegate(QtWidgets.QStyledItemDelegate): "Version is not integer" ) + project_name = self.dbcon.active_project() # Add all available versions to the editor parent_id = item["version_document"]["parent"] - version_docs = list(self.dbcon.find( - { - "type": "version", - "parent": parent_id - }, - sort=[("name", 1)] + version_docs = list(sorted( + get_versions(project_name, subset_ids=[parent_id]), + key=lambda item: item["name"] )) - hero_version_doc = self.dbcon.find_one( - { - "type": "hero_version", - "parent": parent_id - }, { - "name": 1, - "data.tags": 1, - "version_id": 1 - } + hero_versions = list( + get_hero_versions( + project_name, + subset_ids=[parent_id], + fields=["name", "data.tags", "version_id"] + ) ) + hero_version_doc = None + if hero_versions: + hero_version_doc = hero_versions[0] doc_for_hero_version = None diff --git a/openpype/tools/utils/lib.py b/openpype/tools/utils/lib.py index 20fea6600b..72ebfcc063 100644 --- a/openpype/tools/utils/lib.py +++ b/openpype/tools/utils/lib.py @@ -6,6 +6,10 @@ import collections from Qt import QtWidgets, QtCore, QtGui import qtawesome +from openpype.client import ( + get_project, + get_asset, +) from openpype.style import ( get_default_entity_icon_color, get_objected_colors, @@ -430,9 +434,8 @@ class FamilyConfigCache: database = getattr(self.dbcon, "database", None) if database is None: database = self.dbcon._database - asset_doc = database[project_name].find_one( - {"type": "asset", "name": asset_name}, - {"data.tasks": True} + asset_doc = get_asset( + project_name, asset_name=asset_name, fields=["data.tasks"] ) or {} tasks_info = asset_doc.get("data", {}).get("tasks") or {} task_type = tasks_info.get(task_name, {}).get("type") @@ -500,10 +503,7 @@ class GroupsConfig: project_name = self.dbcon.Session.get("AVALON_PROJECT") if project_name: # Get pre-defined group name and appearance from project config - project_doc = self.dbcon.find_one( - {"type": "project"}, - projection={"config.groups": True} - ) + project_doc = get_project(project_name, fields=["config.groups"]) if project_doc: group_configs = project_doc["config"].get("groups") or [] diff --git a/openpype/tools/utils/tasks_widget.py b/openpype/tools/utils/tasks_widget.py index eab183d5f3..fcbec318f5 100644 --- a/openpype/tools/utils/tasks_widget.py +++ b/openpype/tools/utils/tasks_widget.py @@ -1,6 +1,10 @@ from Qt import QtWidgets, QtCore, QtGui import qtawesome +from openpype.client import ( + get_project, + get_asset, +) from openpype.style import get_disabled_entity_icon_color from openpype.tools.utils.lib import get_task_icon @@ -47,7 +51,8 @@ class TasksModel(QtGui.QStandardItemModel): # Get the project configured icons from database project_doc = {} if self._context_is_valid(): - project_doc = self.dbcon.find_one({"type": "project"}) + project_name = self.dbcon.active_project() + project_doc = get_project(project_name) self._loaded_project_name = self._get_current_project() self._project_doc = project_doc @@ -71,9 +76,9 @@ class TasksModel(QtGui.QStandardItemModel): def set_asset_id(self, asset_id): asset_doc = None if self._context_is_valid(): - asset_doc = self.dbcon.find_one( - {"_id": asset_id}, - {"data.tasks": True} + project_name = self._get_current_project() + asset_doc = get_asset( + project_name, asset_id=asset_id, fields=["data.tasks"] ) self._set_asset(asset_doc) diff --git a/openpype/tools/workfiles/files_widget.py b/openpype/tools/workfiles/files_widget.py index 977111b71b..36b9a055d8 100644 --- a/openpype/tools/workfiles/files_widget.py +++ b/openpype/tools/workfiles/files_widget.py @@ -5,6 +5,7 @@ import shutil import Qt from Qt import QtWidgets, QtCore +from openpype.client import get_asset from openpype.tools.utils import PlaceholderLineEdit from openpype.tools.utils.delegates import PrettyTimeDelegate from openpype.lib import ( @@ -384,7 +385,9 @@ class FilesWidget(QtWidgets.QWidget): return None if self._asset_doc is None: - self._asset_doc = legacy_io.find_one({"_id": self._asset_id}) + project_name = legacy_io.active_project() + self._asset_doc = get_asset(project_name, asset_id=self._asset_id) + return self._asset_doc def _get_session(self): diff --git a/openpype/tools/workfiles/model.py b/openpype/tools/workfiles/model.py index 8f9dd8c6ba..d5b7cef339 100644 --- a/openpype/tools/workfiles/model.py +++ b/openpype/tools/workfiles/model.py @@ -4,6 +4,11 @@ import logging from Qt import QtCore, QtGui import qtawesome +from openpype.client import ( + get_subsets, + get_versions, + get_representations, +) from openpype.style import ( get_default_entity_icon_color, get_disabled_entity_icon_color, @@ -215,6 +220,7 @@ class PublishFilesModel(QtGui.QStandardItemModel): self._dbcon = dbcon self._anatomy = anatomy + self._file_extensions = extensions self._invalid_context_item = None @@ -234,6 +240,10 @@ class PublishFilesModel(QtGui.QStandardItemModel): self._asset_id = None self._task_name = None + @property + def project_name(self): + return self._dbcon.Session["AVALON_PROJECT"] + def _set_item_invalid(self, item): item.setFlags(QtCore.Qt.NoItemFlags) item.setData(self._invalid_icon, QtCore.Qt.DecorationRole) @@ -285,15 +295,11 @@ class PublishFilesModel(QtGui.QStandardItemModel): def _get_workfie_representations(self): output = [] # Get subset docs of asset - subset_docs = self._dbcon.find( - { - "type": "subset", - "parent": self._asset_id - }, - { - "_id": True, - "name": True - } + subset_docs = get_subsets( + self.project_name, + asset_ids=[self._asset_id], + fields=["_id", "name"] + ) subset_ids = [subset_doc["_id"] for subset_doc in subset_docs] @@ -301,17 +307,12 @@ class PublishFilesModel(QtGui.QStandardItemModel): return output # Get version docs of subsets with their families - version_docs = self._dbcon.find( - { - "type": "version", - "parent": {"$in": subset_ids} - }, - { - "_id": True, - "data.families": True, - "parent": True - } + version_docs = get_versions( + self.project_name, + subset_ids=subset_ids, + fields=["_id", "parent", "data.families"] ) + # Filter versions if they contain 'workfile' family filtered_versions = [] for version_doc in version_docs: @@ -327,13 +328,10 @@ class PublishFilesModel(QtGui.QStandardItemModel): # Query representations of filtered versions and add filter for # extension extensions = [ext.replace(".", "") for ext in self._file_extensions] - repre_docs = self._dbcon.find( - { - "type": "representation", - "parent": {"$in": version_ids}, - "context.ext": {"$in": extensions} - } + repre_docs = get_representations( + self.project_name, version_ids, extensions ) + # Filter queried representations by task name if task is set filtered_repre_docs = [] for repre_doc in repre_docs: diff --git a/openpype/tools/workfiles/save_as_dialog.py b/openpype/tools/workfiles/save_as_dialog.py index 3e97d6c938..1fbcbfeb22 100644 --- a/openpype/tools/workfiles/save_as_dialog.py +++ b/openpype/tools/workfiles/save_as_dialog.py @@ -5,6 +5,10 @@ import logging from Qt import QtWidgets, QtCore +from openpype.client import ( + get_project, + get_asset, +) from openpype.lib import ( get_last_workfile_with_version, get_workdir_data, @@ -22,29 +26,19 @@ def build_workfile_data(session): """Get the data required for workfile formatting from avalon `session`""" # Set work file data for template formatting + project_name = session["AVALON_PROJECT"] asset_name = session["AVALON_ASSET"] task_name = session["AVALON_TASK"] host_name = session["AVALON_APP"] - project_doc = legacy_io.find_one( - {"type": "project"}, - { - "name": True, - "data.code": True, - "config.tasks": True, - } + project_doc = get_project( + project_name, fields=["name", "data.code", "config.tasks"] + ) + asset_doc = get_asset( + project_name, + asset_name=asset_name, + fields=["name", "data.tasks", "data.parents"] ) - asset_doc = legacy_io.find_one( - { - "type": "asset", - "name": asset_name - }, - { - "name": True, - "data.tasks": True, - "data.parents": True - } - ) data = get_workdir_data(project_doc, asset_doc, task_name, host_name) data.update({ "version": 1, diff --git a/openpype/tools/workfiles/window.py b/openpype/tools/workfiles/window.py index 02a22af26c..45d8d41d16 100644 --- a/openpype/tools/workfiles/window.py +++ b/openpype/tools/workfiles/window.py @@ -2,6 +2,7 @@ import os import datetime from Qt import QtCore, QtWidgets +from openpype.client import get_asset from openpype import style from openpype.lib import ( get_workfile_doc, @@ -223,6 +224,10 @@ class Window(QtWidgets.QMainWindow): self._first_show = True self._context_to_set = None + @property + def project_name(self): + return legacy_io.Session["AVALON_PROJECT"] + def showEvent(self, event): super(Window, self).showEvent(event) if self._first_show: @@ -296,7 +301,8 @@ class Window(QtWidgets.QMainWindow): if not workfile_doc: workdir, filename = os.path.split(filepath) asset_id = self.assets_widget.get_selected_asset_id() - asset_doc = legacy_io.find_one({"_id": asset_id}) + project_name = legacy_io.active_project() + asset_doc = get_asset(project_name, asset_id=asset_id) task_name = self.tasks_widget.get_selected_task_name() create_workfile_doc( asset_doc, task_name, filename, workdir, legacy_io @@ -322,14 +328,13 @@ class Window(QtWidgets.QMainWindow): self._context_to_set, context = None, self._context_to_set if "asset" in context: - asset_doc = legacy_io.find_one( - { - "name": context["asset"], - "type": "asset" - }, - {"_id": 1} - ) or {} - asset_id = asset_doc.get("_id") + asset_doc = get_asset( + self.project_name, context["asset"], fields=["_id"] + ) + + asset_id = None + if asset_doc: + asset_id = asset_doc["_id"] # Select the asset self.assets_widget.select_asset(asset_id) self.tasks_widget.set_asset_id(asset_id) From 9babdb7832c9cc6644e0dc4ff8f3d024b068fd52 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ond=C5=99ej=20Samohel?= Date: Tue, 7 Jun 2022 14:18:48 +0200 Subject: [PATCH 04/51] :recycle: limit the scope of file attribute skipping filter --- openpype/hosts/maya/plugins/publish/collect_look.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/openpype/hosts/maya/plugins/publish/collect_look.py b/openpype/hosts/maya/plugins/publish/collect_look.py index 9b6d1d999c..60af5238d0 100644 --- a/openpype/hosts/maya/plugins/publish/collect_look.py +++ b/openpype/hosts/maya/plugins/publish/collect_look.py @@ -601,8 +601,10 @@ class CollectLook(pyblish.api.InstancePlugin): source, computed_source)) - if not source: - self.log.info("source is empty, skipping...") + # renderman allows nodes to have filename attribute empty while + # you can have another incoming connection from different node. + if not source and cmds.nodeType(node).lower().startswith("pxr"): + self.log.info("Renderman: source is empty, skipping...") continue # We replace backslashes with forward slashes because V-Ray # can't handle the UDIM files with the backslashes in the From 9829410932e18f85f4e899a2af045e65aa1d12ab Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Tue, 7 Jun 2022 15:16:39 +0200 Subject: [PATCH 05/51] implemented get_representation_parents for single representation --- openpype/client/__init__.py | 2 ++ openpype/client/entities.py | 9 +++++++++ 2 files changed, 11 insertions(+) diff --git a/openpype/client/__init__.py b/openpype/client/__init__.py index 861f828e67..e4c01fc0cc 100644 --- a/openpype/client/__init__.py +++ b/openpype/client/__init__.py @@ -22,6 +22,7 @@ from .entities import ( get_representation, get_representation_by_name, get_representations, + get_representation_parents, get_representations_parents, get_thumbnail, @@ -52,6 +53,7 @@ __all__ = ( "get_representation", "get_representation_by_name", "get_representations", + "get_representation_parents", "get_representations_parents", "get_thubmnail", diff --git a/openpype/client/entities.py b/openpype/client/entities.py index 4b52f8cf2d..e9b820dd1a 100644 --- a/openpype/client/entities.py +++ b/openpype/client/entities.py @@ -641,6 +641,15 @@ def get_representations_parents(project_name, representations): return output +def get_representation_parents(project_name, representation): + if not representation: + return None + + repre_id = representation["_id"] + parents_by_repre_id = get_representations(project_name, [representation]) + return parents_by_repre_id.get(repre_id) + + def get_thumbnail_id_from_source(project_name, src_type, src_id): if not src_type or not src_id: return None From f760c9d767c46a0ee433e83890cc15b4565f412f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ond=C5=99ej=20Samohel?= Date: Tue, 7 Jun 2022 15:28:11 +0200 Subject: [PATCH 06/51] :recycle: more explicit renderman check --- openpype/hosts/maya/plugins/publish/collect_look.py | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/openpype/hosts/maya/plugins/publish/collect_look.py b/openpype/hosts/maya/plugins/publish/collect_look.py index 60af5238d0..4a34cfdea2 100644 --- a/openpype/hosts/maya/plugins/publish/collect_look.py +++ b/openpype/hosts/maya/plugins/publish/collect_look.py @@ -603,7 +603,14 @@ class CollectLook(pyblish.api.InstancePlugin): # renderman allows nodes to have filename attribute empty while # you can have another incoming connection from different node. - if not source and cmds.nodeType(node).lower().startswith("pxr"): + pxr_nodes = set() + if cmds.pluginInfo("RenderMan_for_Maya", query=True, loaded=True): + pxr_nodes = set( + cmds.pluginInfo("RenderMan_for_Maya", + query=True, + dependNode=True) + ) + if not source and cmds.nodeType(node) in pxr_nodes: self.log.info("Renderman: source is empty, skipping...") continue # We replace backslashes with forward slashes because V-Ray From 0d38c76f5402cd99e5a70385667ff1a5f0885eb7 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Tue, 7 Jun 2022 16:28:53 +0200 Subject: [PATCH 07/51] separated functions to query asset by name and id --- openpype/client/__init__.py | 8 ++-- openpype/client/entities.py | 43 ++++++++++++++----- openpype/tools/creator/window.py | 6 +-- openpype/tools/mayalookassigner/commands.py | 4 +- .../tools/publisher/widgets/create_dialog.py | 4 +- openpype/tools/sceneinventory/model.py | 4 +- .../tools/sceneinventory/switch_dialog.py | 32 +++++++------- openpype/tools/sceneinventory/view.py | 4 +- openpype/tools/standalonepublish/app.py | 6 +-- .../standalonepublish/widgets/widget_asset.py | 10 ++--- .../widgets/widget_family.py | 14 +++--- openpype/tools/texture_copy/app.py | 4 +- openpype/tools/utils/lib.py | 6 +-- openpype/tools/utils/tasks_widget.py | 6 +-- openpype/tools/workfiles/files_widget.py | 4 +- openpype/tools/workfiles/save_as_dialog.py | 6 +-- openpype/tools/workfiles/window.py | 6 +-- 17 files changed, 94 insertions(+), 73 deletions(-) diff --git a/openpype/client/__init__.py b/openpype/client/__init__.py index e4c01fc0cc..2ba2a3693e 100644 --- a/openpype/client/__init__.py +++ b/openpype/client/__init__.py @@ -2,7 +2,8 @@ from .entities import ( get_projects, get_project, - get_asset, + get_asset_by_id, + get_asset_by_name, get_assets, get_asset_ids_with_subsets, @@ -33,7 +34,8 @@ __all__ = ( "get_projects", "get_project", - "get_asset", + "get_asset_by_id", + "get_asset_by_name", "get_assets", "get_asset_ids_with_subsets", @@ -56,6 +58,6 @@ __all__ = ( "get_representation_parents", "get_representations_parents", - "get_thubmnail", + "get_thumbnail", "get_thumbnail_id_from_source", ) diff --git a/openpype/client/entities.py b/openpype/client/entities.py index e9b820dd1a..97553f30fe 100644 --- a/openpype/client/entities.py +++ b/openpype/client/entities.py @@ -90,23 +90,44 @@ def get_project(project_name, active=True, inactive=False, fields=None): return conn.find_one(query_filter, _prepare_fields(fields)) -def get_asset(project_name, asset_name=None, asset_id=None, fields=None): - query_filter = {"type": "asset"} - has_filter = False - if asset_name: - has_filter = True - query_filter["name"] = asset_name +def get_asset_by_id(project_name, asset_id, fields=None): + """Receive asset data by it's id. - if asset_id: - has_filter = True - query_filter["_id"] = _convert_id(asset_id) + Args: + project_name (str): Name of project where to look for subset. + asset_id (str|ObjectId): Asset's id. - # Avoid random asset quqery - if not has_filter: + Returns: + dict: Asset entity data. + None: Asset was not found by id. + """ + + asset_id = _convert_id(asset_id) + if not asset_id: return None + query_filter = {"type": "asset", "_id": asset_id} conn = _get_project_connection(project_name) + return conn.find_one(query_filter, _prepare_fields(fields)) + +def get_asset_by_name(project_name, asset_name, fields=None): + """Receive asset data by it's name. + + Args: + project_name (str): Name of project where to look for subset. + asset_name (str): Asset's name. + + Returns: + dict: Asset entity data. + None: Asset was not found by name. + """ + + if not asset_name: + return None + + query_filter = {"type": "asset", "name": asset_name} + conn = _get_project_connection(project_name) return conn.find_one(query_filter, _prepare_fields(fields)) diff --git a/openpype/tools/creator/window.py b/openpype/tools/creator/window.py index a85f47a060..a3937d6a40 100644 --- a/openpype/tools/creator/window.py +++ b/openpype/tools/creator/window.py @@ -4,7 +4,7 @@ import re from Qt import QtWidgets, QtCore -from openpype.client import get_asset, get_subsets +from openpype.client import get_asset_by_name, get_subsets from openpype import style from openpype.api import get_current_project_settings from openpype.tools.utils.lib import qt_app_context @@ -220,8 +220,8 @@ class CreatorWindow(QtWidgets.QDialog): asset_doc = None if creator_plugin: # Get the asset from the database which match with the name - asset_doc = get_asset( - project_name, asset_name=asset_name, fields=["_id"] + asset_doc = get_asset_by_name( + project_name, asset_name, fields=["_id"] ) # Get plugin diff --git a/openpype/tools/mayalookassigner/commands.py b/openpype/tools/mayalookassigner/commands.py index a4fc1fab70..2e7a51efde 100644 --- a/openpype/tools/mayalookassigner/commands.py +++ b/openpype/tools/mayalookassigner/commands.py @@ -4,7 +4,7 @@ import os import maya.cmds as cmds -from openpype.client import get_asset +from openpype.client import get_asset_by_id from openpype.pipeline import ( legacy_io, remove_container, @@ -161,7 +161,7 @@ def create_items_from_nodes(nodes): project_name = legacy_io.active_project() for _id, id_nodes in id_hashes.items(): - asset = get_asset(project_name, asset_id=_id, fields=["name"]) + asset = get_asset_by_id(project_name, _id, fields=["name"]) # Skip if asset id is not found if not asset: diff --git a/openpype/tools/publisher/widgets/create_dialog.py b/openpype/tools/publisher/widgets/create_dialog.py index d579831b21..53bbef8b75 100644 --- a/openpype/tools/publisher/widgets/create_dialog.py +++ b/openpype/tools/publisher/widgets/create_dialog.py @@ -10,7 +10,7 @@ except Exception: commonmark = None from Qt import QtWidgets, QtCore, QtGui -from openpype.client import get_asset, get_subsets +from openpype.client import get_asset_by_name, get_subsets from openpype.lib import TaskNotSetError from openpype.pipeline.create import ( CreatorError, @@ -650,7 +650,7 @@ class CreateDialog(QtWidgets.QDialog): return project_name = self.dbcon.active_project() - asset_doc = get_asset(project_name, asset_name=asset_name) + asset_doc = get_asset_by_name(project_name, asset_name) self._asset_doc = asset_doc if asset_doc: diff --git a/openpype/tools/sceneinventory/model.py b/openpype/tools/sceneinventory/model.py index 9cf69ed650..6d813af45b 100644 --- a/openpype/tools/sceneinventory/model.py +++ b/openpype/tools/sceneinventory/model.py @@ -7,7 +7,7 @@ from Qt import QtCore, QtGui import qtawesome from openpype.client import ( - get_asset, + get_asset_by_id, get_subset, get_version, get_last_version_for_subset, @@ -342,7 +342,7 @@ class InventoryModel(TreeModel): not_found_ids.append(repre_id) continue - asset = get_asset(project_name, asset_id=subset["parent"]) + asset = get_asset_by_id(project_name, subset["parent"]) if not asset: not_found["asset"].append(group_items) not_found_ids.append(repre_id) diff --git a/openpype/tools/sceneinventory/switch_dialog.py b/openpype/tools/sceneinventory/switch_dialog.py index b940c66a56..6d6294af6f 100644 --- a/openpype/tools/sceneinventory/switch_dialog.py +++ b/openpype/tools/sceneinventory/switch_dialog.py @@ -5,7 +5,7 @@ import qtawesome from bson.objectid import ObjectId from openpype.client import ( - get_asset, + get_asset_by_name, get_assets, get_subset, get_subsets, @@ -484,9 +484,9 @@ class SwitchAssetDialog(QtWidgets.QDialog): # Prepare asset document if asset is selected asset_doc = None if selected_asset: - asset_doc = get_asset( + asset_doc = get_asset_by_name( self.active_project(), - asset_name=selected_asset, + selected_asset, fields=["_id"] ) if not asset_doc: @@ -768,8 +768,8 @@ class SwitchAssetDialog(QtWidgets.QDialog): project_name = self.active_project() selected_asset = self._assets_box.get_valid_value() if selected_asset: - asset_doc = get_asset( - project_name, asset_name=selected_asset, fields=["_id"] + asset_doc = get_asset_by_name( + project_name, selected_asset, fields=["_id"] ) asset_ids = [asset_doc["_id"]] else: @@ -832,8 +832,8 @@ class SwitchAssetDialog(QtWidgets.QDialog): # [x] [x] [?] if selected_asset and selected_subset: - asset_doc = get_asset( - project_name, asset_name=selected_asset, fields=["_id"] + asset_doc = get_asset_by_name( + project_name, selected_asset, fields=["_id"] ) subset_doc = get_subset( project_name, @@ -858,8 +858,8 @@ class SwitchAssetDialog(QtWidgets.QDialog): # [x] [ ] [?] # If asset only is selected if selected_asset: - asset_doc = get_asset( - project_name, asset_name=selected_asset, fields=["_id"] + asset_doc = get_asset_by_name( + project_name, selected_asset, fields=["_id"] ) if not asset_doc: return list() @@ -995,8 +995,8 @@ class SwitchAssetDialog(QtWidgets.QDialog): # [x] [ ] [?] project_name = self.active_project() - asset_doc = get_asset( - project_name, asset_name=selected_asset, fields=["_id"] + asset_doc = get_asset_by_name( + project_name, selected_asset, fields=["_id"] ) subset_docs = get_subsets( project_name, asset_ids=[asset_doc["_id"]], fields=["name"] @@ -1043,8 +1043,8 @@ class SwitchAssetDialog(QtWidgets.QDialog): # [x] [x] [ ] project_name = self.active_project() if selected_asset is not None and selected_subset is not None: - asset_doc = get_asset( - project_name, asset_name=selected_asset, fields=["_id"] + asset_doc = get_asset_by_name( + project_name, selected_asset, fields=["_id"] ) subset_doc = get_subset( project_name, @@ -1077,8 +1077,8 @@ class SwitchAssetDialog(QtWidgets.QDialog): # [x] [ ] [ ] if selected_asset is not None: - asset_doc = get_asset( - project_name, asset_name=selected_asset, fields=["_id"] + asset_doc = get_asset_by_name( + project_name, selected_asset, fields=["_id"] ) subset_docs = list(get_subsets( project_name, @@ -1189,7 +1189,7 @@ class SwitchAssetDialog(QtWidgets.QDialog): project_name = self.active_project() if selected_asset: - asset_doc = get_asset(project_name, asset_name=selected_asset) + asset_doc = get_asset_by_name(project_name, selected_asset) asset_docs_by_id = {asset_doc["_id"]: asset_doc} else: asset_docs_by_id = self.content_assets diff --git a/openpype/tools/sceneinventory/view.py b/openpype/tools/sceneinventory/view.py index 6a95ccb57b..04e0f67a15 100644 --- a/openpype/tools/sceneinventory/view.py +++ b/openpype/tools/sceneinventory/view.py @@ -657,11 +657,9 @@ class SceneInventoryView(QtWidgets.QTreeView): project_name = legacy_io.active_project() # Get available versions for active representation - representation_id = ObjectId(active["representation"]) - repre_doc = get_representation( project_name, - representation_id=representation_id, + representation_id=active["representation"], fields=["parent"] ) diff --git a/openpype/tools/standalonepublish/app.py b/openpype/tools/standalonepublish/app.py index 4831db038c..3ceeb3ad48 100644 --- a/openpype/tools/standalonepublish/app.py +++ b/openpype/tools/standalonepublish/app.py @@ -6,7 +6,7 @@ import signal from bson.objectid import ObjectId from Qt import QtWidgets, QtCore, QtGui -from openpype.client import get_asset +from openpype.client import get_asset_by_id from .widgets import ( AssetWidget, FamilyWidget, ComponentsWidget, ShadowWidget @@ -144,8 +144,8 @@ class Window(QtWidgets.QDialog): if len(selected) == 1: self.valid_parent = True project_name = self.db.active_project() - asset = get_asset( - project_name, asset_id=selected[0], fields=["name"] + asset = get_asset_by_id( + project_name, selected[0], fields=["name"] ) self.widget_family.change_asset(asset['name']) else: diff --git a/openpype/tools/standalonepublish/widgets/widget_asset.py b/openpype/tools/standalonepublish/widgets/widget_asset.py index 0b5802ed9e..73114f7960 100644 --- a/openpype/tools/standalonepublish/widgets/widget_asset.py +++ b/openpype/tools/standalonepublish/widgets/widget_asset.py @@ -4,7 +4,7 @@ import qtawesome from openpype.client import ( get_project, - get_asset, + get_asset_by_id, ) from openpype.tools.utils import PlaceholderLineEdit @@ -251,9 +251,9 @@ class AssetWidget(QtWidgets.QWidget): return output project_name = self.dbcon.active_project() - parent = get_asset( + parent = get_asset_by_id( project_name, - asset_id=parent_asset_id, + parent_asset_id, fields=["name", "data.visualParent"] ) output.append(parent['name']) @@ -362,8 +362,8 @@ class AssetWidget(QtWidgets.QWidget): selected = self.get_selected_assets() if len(selected) == 1: project_name = self.dbcon.active_project() - asset = get_asset( - project_name, asset_id=selected[0], fields=["data.tasks"] + asset = get_asset_by_id( + project_name, selected[0], fields=["data.tasks"] ) if asset: tasks = asset.get('data', {}).get('tasks', []) diff --git a/openpype/tools/standalonepublish/widgets/widget_family.py b/openpype/tools/standalonepublish/widgets/widget_family.py index ed9f405f38..fa157d37f1 100644 --- a/openpype/tools/standalonepublish/widgets/widget_family.py +++ b/openpype/tools/standalonepublish/widgets/widget_family.py @@ -3,7 +3,7 @@ import re from Qt import QtWidgets, QtCore from openpype.client import ( - get_asset, + get_asset_by_name, get_subset, get_subsets, get_last_version_for_subset, @@ -188,8 +188,8 @@ class FamilyWidget(QtWidgets.QWidget): if asset_name != self.NOT_SELECTED: # Get the assets from the database which match with the name project_name = self.dbcon.active_project() - asset_doc = get_asset( - project_name, asset_name=asset_name, fields=["_id"] + asset_doc = get_asset_by_name( + project_name, asset_name, fields=["_id"] ) # Get plugin and family @@ -205,8 +205,8 @@ class FamilyWidget(QtWidgets.QWidget): # Get the asset from the database which match with the name project_name = self.dbcon.active_project() - asset_doc = get_asset( - project_name, asset_name=asset_name, fields=["_id"] + asset_doc = get_asset_by_name( + project_name, asset_name, fields=["_id"] ) # Get plugin plugin = item.data(PluginRole) @@ -310,8 +310,8 @@ class FamilyWidget(QtWidgets.QWidget): asset_name != self.NOT_SELECTED and subset_name.strip() != '' ): - asset_doc = get_asset( - project_name, asset_name=asset_name, fields=["_id"] + asset_doc = get_asset_by_name( + project_name, asset_name, fields=["_id"] ) if asset_doc: diff --git a/openpype/tools/texture_copy/app.py b/openpype/tools/texture_copy/app.py index 8703f075d3..746a72b3ec 100644 --- a/openpype/tools/texture_copy/app.py +++ b/openpype/tools/texture_copy/app.py @@ -4,7 +4,7 @@ import click import speedcopy -from openpype.client import get_project, get_asset +from openpype.client import get_project, get_asset_by_name from openpype.lib import Terminal from openpype.api import Anatomy from openpype.pipeline import legacy_io @@ -94,7 +94,7 @@ class TextureCopy: t.echo("!!! Project name [ {} ] not found.".format(project_name)) exit(1) - asset = get_asset(project_name, asset_name=asset_name) + asset = get_asset_by_name(project_name, asset_name) if not asset: t.echo("!!! Asset [ {} ] not found in project".format(asset_name)) exit(1) diff --git a/openpype/tools/utils/lib.py b/openpype/tools/utils/lib.py index 72ebfcc063..ea1362945f 100644 --- a/openpype/tools/utils/lib.py +++ b/openpype/tools/utils/lib.py @@ -8,7 +8,7 @@ import qtawesome from openpype.client import ( get_project, - get_asset, + get_asset_by_name, ) from openpype.style import ( get_default_entity_icon_color, @@ -434,8 +434,8 @@ class FamilyConfigCache: database = getattr(self.dbcon, "database", None) if database is None: database = self.dbcon._database - asset_doc = get_asset( - project_name, asset_name=asset_name, fields=["data.tasks"] + asset_doc = get_asset_by_name( + project_name, asset_name, fields=["data.tasks"] ) or {} tasks_info = asset_doc.get("data", {}).get("tasks") or {} task_type = tasks_info.get(task_name, {}).get("type") diff --git a/openpype/tools/utils/tasks_widget.py b/openpype/tools/utils/tasks_widget.py index fcbec318f5..0353f3dd2f 100644 --- a/openpype/tools/utils/tasks_widget.py +++ b/openpype/tools/utils/tasks_widget.py @@ -3,7 +3,7 @@ import qtawesome from openpype.client import ( get_project, - get_asset, + get_asset_by_id, ) from openpype.style import get_disabled_entity_icon_color from openpype.tools.utils.lib import get_task_icon @@ -77,8 +77,8 @@ class TasksModel(QtGui.QStandardItemModel): asset_doc = None if self._context_is_valid(): project_name = self._get_current_project() - asset_doc = get_asset( - project_name, asset_id=asset_id, fields=["data.tasks"] + asset_doc = get_asset_by_id( + project_name, asset_id, fields=["data.tasks"] ) self._set_asset(asset_doc) diff --git a/openpype/tools/workfiles/files_widget.py b/openpype/tools/workfiles/files_widget.py index 36b9a055d8..68fe8301c9 100644 --- a/openpype/tools/workfiles/files_widget.py +++ b/openpype/tools/workfiles/files_widget.py @@ -5,7 +5,7 @@ import shutil import Qt from Qt import QtWidgets, QtCore -from openpype.client import get_asset +from openpype.client import get_asset_by_id from openpype.tools.utils import PlaceholderLineEdit from openpype.tools.utils.delegates import PrettyTimeDelegate from openpype.lib import ( @@ -386,7 +386,7 @@ class FilesWidget(QtWidgets.QWidget): if self._asset_doc is None: project_name = legacy_io.active_project() - self._asset_doc = get_asset(project_name, asset_id=self._asset_id) + self._asset_doc = get_asset_by_id(project_name, self._asset_id) return self._asset_doc diff --git a/openpype/tools/workfiles/save_as_dialog.py b/openpype/tools/workfiles/save_as_dialog.py index 1fbcbfeb22..b62fd2c889 100644 --- a/openpype/tools/workfiles/save_as_dialog.py +++ b/openpype/tools/workfiles/save_as_dialog.py @@ -7,7 +7,7 @@ from Qt import QtWidgets, QtCore from openpype.client import ( get_project, - get_asset, + get_asset_by_name, ) from openpype.lib import ( get_last_workfile_with_version, @@ -33,9 +33,9 @@ def build_workfile_data(session): project_doc = get_project( project_name, fields=["name", "data.code", "config.tasks"] ) - asset_doc = get_asset( + asset_doc = get_asset_by_name( project_name, - asset_name=asset_name, + asset_name, fields=["name", "data.tasks", "data.parents"] ) diff --git a/openpype/tools/workfiles/window.py b/openpype/tools/workfiles/window.py index 45d8d41d16..9f4cea2f8a 100644 --- a/openpype/tools/workfiles/window.py +++ b/openpype/tools/workfiles/window.py @@ -2,7 +2,7 @@ import os import datetime from Qt import QtCore, QtWidgets -from openpype.client import get_asset +from openpype.client import get_asset_by_id, get_asset_by_name from openpype import style from openpype.lib import ( get_workfile_doc, @@ -302,7 +302,7 @@ class Window(QtWidgets.QMainWindow): workdir, filename = os.path.split(filepath) asset_id = self.assets_widget.get_selected_asset_id() project_name = legacy_io.active_project() - asset_doc = get_asset(project_name, asset_id=asset_id) + asset_doc = get_asset_by_id(project_name, asset_id) task_name = self.tasks_widget.get_selected_task_name() create_workfile_doc( asset_doc, task_name, filename, workdir, legacy_io @@ -328,7 +328,7 @@ class Window(QtWidgets.QMainWindow): self._context_to_set, context = None, self._context_to_set if "asset" in context: - asset_doc = get_asset( + asset_doc = get_asset_by_name( self.project_name, context["asset"], fields=["_id"] ) From 20ecefa4aae7918fb6a0dbdd5d3de141249eb69d Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Tue, 7 Jun 2022 16:43:07 +0200 Subject: [PATCH 08/51] added docstrings for asset queries --- openpype/client/entities.py | 37 +++++++++++++++++++++++++++++++++++-- 1 file changed, 35 insertions(+), 2 deletions(-) diff --git a/openpype/client/entities.py b/openpype/client/entities.py index 97553f30fe..16dc3d76a9 100644 --- a/openpype/client/entities.py +++ b/openpype/client/entities.py @@ -94,8 +94,10 @@ def get_asset_by_id(project_name, asset_id, fields=None): """Receive asset data by it's id. Args: - project_name (str): Name of project where to look for subset. + project_name (str): Name of project where to look for queried entities. asset_id (str|ObjectId): Asset's id. + fields (list[str]): Fields that should be returned. All fields are + returned if 'None' is passed. Returns: dict: Asset entity data. @@ -115,8 +117,10 @@ def get_asset_by_name(project_name, asset_name, fields=None): """Receive asset data by it's name. Args: - project_name (str): Name of project where to look for subset. + project_name (str): Name of project where to look for queried entities. asset_name (str): Asset's name. + fields (list[str]): Fields that should be returned. All fields are + returned if 'None' is passed. Returns: dict: Asset entity data. @@ -134,6 +138,25 @@ def get_asset_by_name(project_name, asset_name, fields=None): def get_assets( project_name, asset_ids=None, asset_names=None, archived=False, fields=None ): + """Assets for specified project by passed filters. + + Passed filters (ids and names) are always combined so all conditions must + match. + + To receive all assets from project just keep filters empty. + + Args: + project_name (str): Name of project where to look for queried entities. + asset_ids (list[str, ObjectId]): Asset ids that should be found. + asset_names (list[str]): Name assets that should be found. + fields (list[str]): Fields that should be returned. All fields are + returned if 'None' is passed. + + Returns: + Cursor: Query cursor as iterable which returns asset documents matching + passed filters. + """ + asset_types = ["asset"] if archived: asset_types.append("archived_asset") @@ -160,6 +183,16 @@ def get_assets( def get_asset_ids_with_subsets(project_name, asset_ids=None): + """Find out which assets have existing subsets. + + Args: + project_name (str): Name of project where to look for queried entities. + asset_ids (list[str|ObjectId]): Look only for entered asset ids. + + Returns: + List[ObjectId]: Asset ids that have existing subsets. + """ + subset_query = { "type": "subset" } From 9456dac8b92862120f580bfbd59327b83a78fd4c Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Tue, 7 Jun 2022 16:44:26 +0200 Subject: [PATCH 09/51] separated function get_subset into 2 separated functions --- openpype/client/__init__.py | 6 +- openpype/client/entities.py | 73 ++++++++++--------- openpype/tools/loader/widgets.py | 4 +- openpype/tools/sceneinventory/model.py | 4 +- .../tools/sceneinventory/switch_dialog.py | 26 +++---- .../widgets/widget_family.py | 8 +- 6 files changed, 63 insertions(+), 58 deletions(-) diff --git a/openpype/client/__init__.py b/openpype/client/__init__.py index 2ba2a3693e..34257cf3dc 100644 --- a/openpype/client/__init__.py +++ b/openpype/client/__init__.py @@ -7,7 +7,8 @@ from .entities import ( get_assets, get_asset_ids_with_subsets, - get_subset, + get_subset_by_id, + get_subset_by_name, get_subsets, get_subset_families, @@ -39,7 +40,8 @@ __all__ = ( "get_assets", "get_asset_ids_with_subsets", - "get_subset", + "get_subset_by_id", + "get_subset_by_name", "get_subsets", "get_subset_families", diff --git a/openpype/client/entities.py b/openpype/client/entities.py index 16dc3d76a9..07f6bb2e65 100644 --- a/openpype/client/entities.py +++ b/openpype/client/entities.py @@ -223,29 +223,13 @@ def get_asset_ids_with_subsets(project_name, asset_ids=None): return asset_ids_with_subsets -def get_subset( - project_name, - subset_id=None, - subset_name=None, - asset_id=None, - fields=None -): - """Single subset document by subset id or name and parent id. - - When subset id is defined it is not needed to add any other arguments but - subset name filter must be always combined with asset id (or subset id). - - Question: - This could be split into more functions? +def get_subset_by_id(project_name, subset_id, fields=None): + """Single subset document by it's id. Args: - project_name (str): Name of project where to look for subset. + project_name (str): Name of project where to look for queried entities. subset_id (ObjectId): Id of subset which should be found. - subset_name (str): Name of asset. Must be combined with 'asset_id' or - 'subset_id' arguments otherwise result is 'None'. - asset_id (ObjectId): Id of parent asset. Must be combined with - 'subset_name' or 'subset_id'. - fields (list): Fields that should be returned. All fields are + fields (list[str]): Fields that should be returned. All fields are returned if 'None' is passed. Returns: @@ -253,23 +237,42 @@ def get_subset( Dict: Subset document which can be reduced to specified 'fields'. """ - query_filters = {"type": "subset"} - has_valid_filters = False - if subset_id is not None: - query_filters["_id"] = _convert_id(asset_id) - has_valid_filters = True - - if subset_name is not None: - if asset_id is not None: - has_valid_filters = True - query_filters["name"] = subset_name - - if asset_id is not None: - query_filters["parent"] = _convert_id(asset_id) - - if not has_valid_filters: + subset_id = _convert_id(subset_id) + if not subset_id: return None + query_filters = {"type": "subset", "_id": subset_id} + conn = _get_project_connection(project_name) + return conn.find_one(query_filters, _prepare_fields(fields)) + + +def get_subset_by_name(project_name, subset_name, asset_id, fields=None): + """Single subset document by subset name and it's version id. + + Args: + project_name (str): Name of project where to look for queried entities. + subset_name (str): Name of subset. + asset_id (str|ObjectId): Id of parent asset. + fields (list[str]): Fields that should be returned. All fields are + returned if 'None' is passed. + + Returns: + None: If subset with specified filters was not found. + Dict: Subset document which can be reduced to specified 'fields'. + """ + + if not subset_name: + return None + + asset_id = _convert_id(asset_id) + if not asset_id: + return None + + query_filters = { + "type": "subset", + "name": subset_name, + "parent": asset_id + } conn = _get_project_connection(project_name) return conn.find_one(query_filters, _prepare_fields(fields)) diff --git a/openpype/tools/loader/widgets.py b/openpype/tools/loader/widgets.py index 6c7acc593d..4a9a911f93 100644 --- a/openpype/tools/loader/widgets.py +++ b/openpype/tools/loader/widgets.py @@ -9,7 +9,7 @@ from Qt import QtWidgets, QtCore, QtGui from openpype.client import ( get_subset_families, - get_subset, + get_subset_by_id, get_subsets, get_version, get_versions, @@ -688,7 +688,7 @@ class VersionTextEdit(QtWidgets.QTextEdit): _version_doc["name"] ) - subset = get_subset(project_name, subset_id=version_doc["parent"]) + subset = get_subset_by_id(project_name, version_doc["parent"]) assert subset, "No valid subset parent for version" # Define readable creation timestamp diff --git a/openpype/tools/sceneinventory/model.py b/openpype/tools/sceneinventory/model.py index 6d813af45b..b36f7f4cea 100644 --- a/openpype/tools/sceneinventory/model.py +++ b/openpype/tools/sceneinventory/model.py @@ -8,7 +8,7 @@ import qtawesome from openpype.client import ( get_asset_by_id, - get_subset, + get_subset_by_id, get_version, get_last_version_for_subset, get_representation, @@ -336,7 +336,7 @@ class InventoryModel(TreeModel): version["name"] = HeroVersionType(_version["name"]) version["data"] = _version["data"] - subset = get_subset(project_name, subset_id=version["parent"]) + subset = get_subset_by_id(project_name, version["parent"]) if not subset: not_found["subset"].append(group_items) not_found_ids.append(repre_id) diff --git a/openpype/tools/sceneinventory/switch_dialog.py b/openpype/tools/sceneinventory/switch_dialog.py index 6d6294af6f..92ef2b3553 100644 --- a/openpype/tools/sceneinventory/switch_dialog.py +++ b/openpype/tools/sceneinventory/switch_dialog.py @@ -7,7 +7,7 @@ from bson.objectid import ObjectId from openpype.client import ( get_asset_by_name, get_assets, - get_subset, + get_subset_by_name, get_subsets, get_versions, get_hero_versions, @@ -537,10 +537,10 @@ class SwitchAssetDialog(QtWidgets.QDialog): self, asset_doc, selected_subset, selected_repre ): project_name = self.active_project() - subset_doc = get_subset( + subset_doc = get_subset_by_name( project_name, - subset_name=selected_subset, - asset_id=asset_doc["_id"], + selected_subset, + asset_doc["_id"], fields=["_id"] ) @@ -560,10 +560,10 @@ class SwitchAssetDialog(QtWidgets.QDialog): def _get_current_output_repre_ids_xxo(self, asset_doc, selected_subset): project_name = self.active_project() - subset_doc = get_subset( + subset_doc = get_subset_by_name( project_name, - asset_id=asset_doc["_id"], - subset_name=selected_subset, + selected_subset, + asset_doc["_id"], fields=["_id"] ) if not subset_doc: @@ -835,10 +835,10 @@ class SwitchAssetDialog(QtWidgets.QDialog): asset_doc = get_asset_by_name( project_name, selected_asset, fields=["_id"] ) - subset_doc = get_subset( + subset_doc = get_subset_by_name( project_name, - asset_id=asset_doc["_id"], - subset_name=selected_subset, + selected_subset, + asset_doc["_id"], fields=["_id"] ) @@ -1046,10 +1046,10 @@ class SwitchAssetDialog(QtWidgets.QDialog): asset_doc = get_asset_by_name( project_name, selected_asset, fields=["_id"] ) - subset_doc = get_subset( + subset_doc = get_subset_by_name( project_name, - asset_id=asset_doc["_id"], - subset_name=selected_subset, + selected_subset, + asset_doc["_id"], fields=["_id"] ) subset_id = subset_doc["_id"] diff --git a/openpype/tools/standalonepublish/widgets/widget_family.py b/openpype/tools/standalonepublish/widgets/widget_family.py index fa157d37f1..2f00cfe7bb 100644 --- a/openpype/tools/standalonepublish/widgets/widget_family.py +++ b/openpype/tools/standalonepublish/widgets/widget_family.py @@ -4,7 +4,7 @@ from Qt import QtWidgets, QtCore from openpype.client import ( get_asset_by_name, - get_subset, + get_subset_by_name, get_subsets, get_last_version_for_subset, ) @@ -315,10 +315,10 @@ class FamilyWidget(QtWidgets.QWidget): ) if asset_doc: - subset_doc = get_subset( + subset_doc = get_subset_by_name( project_name, - subset_name=subset_name, - asset_id=asset_doc['_id'], + subset_name, + asset_doc['_id'], fields=["_id"] ) From 0d5cc529d502cdf2a421be4f7db27dcbd54eb9ff Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Tue, 7 Jun 2022 17:18:21 +0200 Subject: [PATCH 10/51] renamed 'get_version' to 'get_version_by_id' --- openpype/client/__init__.py | 4 +- openpype/client/entities.py | 107 ++++++++++++++++++++++--- openpype/tools/loader/widgets.py | 8 +- openpype/tools/sceneinventory/model.py | 10 +-- openpype/tools/sceneinventory/view.py | 6 +- 5 files changed, 108 insertions(+), 27 deletions(-) diff --git a/openpype/client/__init__.py b/openpype/client/__init__.py index 34257cf3dc..e8e3a81d5d 100644 --- a/openpype/client/__init__.py +++ b/openpype/client/__init__.py @@ -12,8 +12,8 @@ from .entities import ( get_subsets, get_subset_families, + get_version_by_id, get_version_by_name, - get_version, get_versions, get_last_versions, get_last_version_for_subset, @@ -45,8 +45,8 @@ __all__ = ( "get_subsets", "get_subset_families", + "get_version_by_id", "get_version_by_name", - "get_version", "get_versions", "get_last_versions", "get_last_version_for_subset", diff --git a/openpype/client/entities.py b/openpype/client/entities.py index 07f6bb2e65..1a45be6e93 100644 --- a/openpype/client/entities.py +++ b/openpype/client/entities.py @@ -224,7 +224,7 @@ def get_asset_ids_with_subsets(project_name, asset_ids=None): def get_subset_by_id(project_name, subset_id, fields=None): - """Single subset document by it's id. + """Single subset entity data by it's id. Args: project_name (str): Name of project where to look for queried entities. @@ -247,7 +247,7 @@ def get_subset_by_id(project_name, subset_id, fields=None): def get_subset_by_name(project_name, subset_name, asset_id, fields=None): - """Single subset document by subset name and it's version id. + """Single subset entity data by it's name and it's version id. Args: project_name (str): Name of project where to look for queried entities. @@ -279,12 +279,31 @@ def get_subset_by_name(project_name, subset_name, asset_id, fields=None): def get_subsets( project_name, - asset_ids=None, subset_ids=None, subset_names=None, + asset_ids=None, archived=False, fields=None ): + """Subset entities data from one project filtered by entered filters. + + Filters are additive (all conditions must pass to return subset). + + Args: + project_name (str): Name of project where to look for queried entities. + subset_ids (list[str, ObjectId]): Subset ids that should be queried. + Filter ignored if 'None' is passed. + subset_names (list[str]): Subset names that should be queried. + Filter ignored if 'None' is passed. + asset_ids (list[str, ObjectId]): Asset ids under which should look for + the subsets. Filter ignored if 'None' is passed. + fields (list[str]): Fields that should be returned. All fields are + returned if 'None' is passed. + + Returns: + Cursor: Iterable cursor yielding all matching subsets. + """ + subset_types = ["subset"] if archived: subset_types.append("archived_subset") @@ -316,6 +335,17 @@ def get_subsets( def get_subset_families(project_name, subset_ids=None): + """Set of main families of subsets. + + Args: + project_name (str): Name of project where to look for queried entities. + subset_ids (list[str, ObjectId]): Subset ids that should be queried. + All subsets from project are used if 'None' is passed. + + Returns: + set[str]: Main families of matching subsets. + """ + subset_filter = { "type": "subset" } @@ -340,23 +370,56 @@ def get_subset_families(project_name, subset_ids=None): return set() -def get_version_by_name(project_name, subset_id, version, fields=None): - conn = _get_project_connection(project_name) +def get_version_by_id(project_name, version_id, fields=None): + """Single version entity data by it's id. + + Args: + project_name (str): Name of project where to look for queried entities. + version_id (ObjectId): Id of version which should be found. + fields (list[str]): Fields that should be returned. All fields are + returned if 'None' is passed. + + Returns: + None: If version with specified filters was not found. + Dict: Version document which can be reduced to specified 'fields'. + """ + + version_id = _convert_id(version_id) + if not version_id: + return None + query_filter = { - "type": "version", - "parent": _convert_id(subset_id), - "name": version + "type": {"$in": ["version", "hero_version"]}, + "_id": version_id } + conn = _get_project_connection(project_name) return conn.find_one(query_filter, _prepare_fields(fields)) -def get_version(project_name, version_id, fields=None): - if not version_id: +def get_version_by_name(project_name, version, subset_id, fields=None): + """Single version entity data by it's name and subset id. + + Args: + project_name (str): Name of project where to look for queried entities. + version (int): name of version entity (it's version). + subset_id (ObjectId): Id of version which should be found. + fields (list[str]): Fields that should be returned. All fields are + returned if 'None' is passed. + + Returns: + None: If version with specified filters was not found. + Dict: Version document which can be reduced to specified 'fields'. + """ + + subset_id = _convert_id(subset_id) + if not subset_id: return None + conn = _get_project_connection(project_name) query_filter = { - "type": {"$in": ["version", "hero_version"]}, - "_id": _convert_id(version_id) + "type": "version", + "parent": subset_id, + "name": version } return conn.find_one(query_filter, _prepare_fields(fields)) @@ -402,11 +465,29 @@ def _get_versions( def get_versions( project_name, - subset_ids=None, version_ids=None, + subset_ids=None, hero=False, fields=None ): + """Version entities data from one project filtered by entered filters. + + Filters are additive (all conditions must pass to return subset). + + Args: + project_name (str): Name of project where to look for queried entities. + version_ids (list[str, ObjectId]): Version ids that will be queried. + Filter ignored if 'None' is passed. + subset_ids (list[str]): Subset ids that will be queried. + Filter ignored if 'None' is passed. + hero (bool): Look also for hero versions. + fields (list[str]): Fields that should be returned. All fields are + returned if 'None' is passed. + + Returns: + Cursor: Iterable cursor yielding all matching versions. + """ + return _get_versions( project_name, subset_ids, diff --git a/openpype/tools/loader/widgets.py b/openpype/tools/loader/widgets.py index 4a9a911f93..921708922e 100644 --- a/openpype/tools/loader/widgets.py +++ b/openpype/tools/loader/widgets.py @@ -11,7 +11,7 @@ from openpype.client import ( get_subset_families, get_subset_by_id, get_subsets, - get_version, + get_version_by_id, get_versions, get_representations, get_thumbnail_id_from_source, @@ -676,12 +676,12 @@ class VersionTextEdit(QtWidgets.QTextEdit): project_name = self.dbcon.active_project() if not version_doc: - version_doc = get_version(project_name, version_id=version_id) + version_doc = get_version_by_id(project_name, version_id) assert version_doc, "Not a valid version id" if version_doc["type"] == "hero_version": - _version_doc = get_version( - project_name, version_id=version_doc["version_id"] + _version_doc = get_version_by_id( + project_name, version_doc["version_id"] ) version_doc["data"] = _version_doc["data"] version_doc["name"] = HeroVersionType( diff --git a/openpype/tools/sceneinventory/model.py b/openpype/tools/sceneinventory/model.py index b36f7f4cea..a5d856fe72 100644 --- a/openpype/tools/sceneinventory/model.py +++ b/openpype/tools/sceneinventory/model.py @@ -9,7 +9,7 @@ import qtawesome from openpype.client import ( get_asset_by_id, get_subset_by_id, - get_version, + get_version_by_id, get_last_version_for_subset, get_representation, ) @@ -321,8 +321,8 @@ class InventoryModel(TreeModel): not_found_ids.append(repre_id) continue - version = get_version( - project_name, version_id=representation["parent"] + version = get_version_by_id( + project_name, representation["parent"] ) if not version: not_found["version"].append(group_items) @@ -330,8 +330,8 @@ class InventoryModel(TreeModel): continue elif version["type"] == "hero_version": - _version = get_version( - project_name, version_id=version["version_id"] + _version = get_version_by_id( + project_name, version["version_id"] ) version["name"] = HeroVersionType(_version["name"]) version["data"] = _version["data"] diff --git a/openpype/tools/sceneinventory/view.py b/openpype/tools/sceneinventory/view.py index 04e0f67a15..d1ff91535f 100644 --- a/openpype/tools/sceneinventory/view.py +++ b/openpype/tools/sceneinventory/view.py @@ -7,7 +7,7 @@ import qtawesome from bson.objectid import ObjectId from openpype.client import ( - get_version, + get_version_by_id, get_versions, get_hero_versions, get_representation, @@ -663,9 +663,9 @@ class SceneInventoryView(QtWidgets.QTreeView): fields=["parent"] ) - repre_version_doc = get_version( + repre_version_doc = get_version_by_id( project_name, - version_id=repre_doc["parent"], + repre_doc["parent"], fields=["parent"] ) From 0f95a66359c693382af236e4d695764ecbfb7f14 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Tue, 7 Jun 2022 17:43:44 +0200 Subject: [PATCH 11/51] renamed get_version_links to get_output_link_versions --- openpype/client/__init__.py | 4 ++-- openpype/client/entities.py | 26 ++++++++++++++++++++++++-- openpype/tools/assetlinks/widgets.py | 4 ++-- 3 files changed, 28 insertions(+), 6 deletions(-) diff --git a/openpype/client/__init__.py b/openpype/client/__init__.py index e8e3a81d5d..7230a7c153 100644 --- a/openpype/client/__init__.py +++ b/openpype/client/__init__.py @@ -19,7 +19,7 @@ from .entities import ( get_last_version_for_subset, get_hero_version, get_hero_versions, - get_version_links, + get_output_link_versions, get_representation, get_representation_by_name, @@ -52,7 +52,7 @@ __all__ = ( "get_last_version_for_subset", "get_hero_version", "get_hero_versions", - "get_version_links", + "get_output_link_versions", "get_representation", "get_representation_by_name", diff --git a/openpype/client/entities.py b/openpype/client/entities.py index 1a45be6e93..0eb0367452 100644 --- a/openpype/client/entities.py +++ b/openpype/client/entities.py @@ -544,12 +544,34 @@ def get_hero_versions( ) -def get_version_links(project_name, version_id, fields=None): +def get_output_link_versions(project_name, version_id, fields=None): + """Versions where passed version was used as input. + + Question: + Not 100% sure about the usage of the function so the name and docstring + maybe does not match what it does? + + Args: + project_name (str): Name of project where to look for queried entities. + version_id (str|ObjectId): Version id which can be used as input link + for other versions. + fields (list[str]): Fields that should be returned. All fields are + returned if 'None' is passed. + + Returns: + Cursor|list: Iterable cursor yielding versions that are used as input + links for passed version. + """ + + version_id = _convert_id(version_id) + if not version_id: + return [] + conn = _get_project_connection(project_name) # Does make sense to look for hero versions? query_filter = { "type": "version", - "data.inputLinks.input": _convert_id(version_id) + "data.inputLinks.input": version_id } return conn.find(query_filter, _prepare_fields(fields)) diff --git a/openpype/tools/assetlinks/widgets.py b/openpype/tools/assetlinks/widgets.py index 5ce2a835ef..3078585ed1 100644 --- a/openpype/tools/assetlinks/widgets.py +++ b/openpype/tools/assetlinks/widgets.py @@ -3,7 +3,7 @@ from openpype.client import ( get_versions, get_subsets, get_assets, - get_version_links, + get_output_link_versions, ) from Qt import QtWidgets @@ -112,7 +112,7 @@ class SimpleLinkView(QtWidgets.QWidget): )) def _fill_outputs(self, version_doc): - version_docs = list(get_version_links( + version_docs = list(get_output_link_versions( self.project_name, version_doc["_id"], fields=["name", "parent"] From 675da63f8ccb862797c298ba9acd6a57b7f7e1e6 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Tue, 7 Jun 2022 18:04:04 +0200 Subject: [PATCH 12/51] split 'get_hero_version' to 'get_hero_version_by_id' and 'get_hero_version_by_subset_id' --- openpype/client/__init__.py | 10 +++-- openpype/client/entities.py | 79 +++++++++++++++++++++++++++++-------- 2 files changed, 68 insertions(+), 21 deletions(-) diff --git a/openpype/client/__init__.py b/openpype/client/__init__.py index 7230a7c153..4f8b948c93 100644 --- a/openpype/client/__init__.py +++ b/openpype/client/__init__.py @@ -15,10 +15,11 @@ from .entities import ( get_version_by_id, get_version_by_name, get_versions, + get_hero_version_by_id, + get_hero_version_by_subset_id, + get_hero_versions, get_last_versions, get_last_version_for_subset, - get_hero_version, - get_hero_versions, get_output_link_versions, get_representation, @@ -48,10 +49,11 @@ __all__ = ( "get_version_by_id", "get_version_by_name", "get_versions", + "get_hero_version_by_id", + "get_hero_version_by_subset_id", + "get_hero_versions", "get_last_versions", "get_last_version_for_subset", - "get_hero_version", - "get_hero_versions", "get_output_link_versions", "get_representation", diff --git a/openpype/client/entities.py b/openpype/client/entities.py index 0eb0367452..bf033e7c81 100644 --- a/openpype/client/entities.py +++ b/openpype/client/entities.py @@ -498,27 +498,57 @@ def get_versions( ) -def get_hero_version( - project_name, - subset_id=None, - version_id=None, - fields=None -): - if not subset_id and not version_id: +def get_hero_version_by_subset_id(project_name, subset_id, fields=None): + """Hero version by subset id. + + Args: + project_name (str): Name of project where to look for queried entities. + subset_id (str|ObjectId): Subset id under which is hero version. + fields (list[str]): Fields that should be returned. All fields are + returned if 'None' is passed. + + Returns: + None: If hero version for passed subset id does not exists. + Dict: Hero version entity data. + """ + + subset_id = _convert_id(subset_id) + if not subset_id: return None - subset_ids = None - if subset_id is not None: - subset_ids = [subset_id] - - version_ids = None - if version_id is not None: - version_ids = [version_id] - versions = list(_get_versions( project_name, - subset_ids=subset_ids, - version_ids=version_ids, + subset_ids=[subset_id], + standard=False, + hero=True, + fields=fields + )) + if versions: + return versions[0] + return None + + +def get_hero_version_by_id(project_name, version_id, fields=None): + """Hero version by it's id. + + Args: + project_name (str): Name of project where to look for queried entities. + version_id (str|ObjectId): Hero version id. + fields (list[str]): Fields that should be returned. All fields are + returned if 'None' is passed. + + Returns: + None: If hero version with passed id was not found. + Dict: Hero version entity data. + """ + + version_id = _convert_id(version_id) + if not version_id: + return None + + versions = list(_get_versions( + project_name, + version_ids=[version_id], standard=False, hero=True, fields=fields @@ -534,6 +564,21 @@ def get_hero_versions( version_ids=None, fields=None ): + """Hero version entities data from one project filtered by entered filters. + + Args: + project_name (str): Name of project where to look for queried entities. + subset_ids (list[str|ObjectId]): Subset ids for which should look for + hero versions. Filter ignored if 'None' is passed. + version_ids (list[str|ObjectId]): Hero version ids. Filter ignored if + 'None' is passed. + fields (list[str]): Fields that should be returned. All fields are + returned if 'None' is passed. + + Returns: + Cursor|list: Iterable yielding hero versions matching passed filters. + """ + return _get_versions( project_name, subset_ids, From ba6ef6d2ae035361d0a7282983555990c322cc7c Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Tue, 7 Jun 2022 18:23:44 +0200 Subject: [PATCH 13/51] split 'get_last_version_for_subset' into 'get_last_version_by_subset_id' and 'get_last_version_by_subset_name' --- openpype/client/__init__.py | 6 +- openpype/client/entities.py | 81 ++++++++++++------- openpype/tools/mayalookassigner/app.py | 6 +- .../tools/mayalookassigner/vray_proxies.py | 4 +- openpype/tools/sceneinventory/model.py | 6 +- .../widgets/widget_family.py | 6 +- 6 files changed, 65 insertions(+), 44 deletions(-) diff --git a/openpype/client/__init__.py b/openpype/client/__init__.py index 4f8b948c93..2ef32d6a83 100644 --- a/openpype/client/__init__.py +++ b/openpype/client/__init__.py @@ -19,7 +19,8 @@ from .entities import ( get_hero_version_by_subset_id, get_hero_versions, get_last_versions, - get_last_version_for_subset, + get_last_version_by_subset_id, + get_last_version_by_subset_name, get_output_link_versions, get_representation, @@ -53,7 +54,8 @@ __all__ = ( "get_hero_version_by_subset_id", "get_hero_versions", "get_last_versions", - "get_last_version_for_subset", + "get_last_version_by_subset_id", + "get_last_version_by_subset_name", "get_output_link_versions", "get_representation", diff --git a/openpype/client/entities.py b/openpype/client/entities.py index bf033e7c81..232d9aebcc 100644 --- a/openpype/client/entities.py +++ b/openpype/client/entities.py @@ -228,7 +228,7 @@ def get_subset_by_id(project_name, subset_id, fields=None): Args: project_name (str): Name of project where to look for queried entities. - subset_id (ObjectId): Id of subset which should be found. + subset_id (str|ObjectId): Id of subset which should be found. fields (list[str]): Fields that should be returned. All fields are returned if 'None' is passed. @@ -375,7 +375,7 @@ def get_version_by_id(project_name, version_id, fields=None): Args: project_name (str): Name of project where to look for queried entities. - version_id (ObjectId): Id of version which should be found. + version_id (str|ObjectId): Id of version which should be found. fields (list[str]): Fields that should be returned. All fields are returned if 'None' is passed. @@ -402,7 +402,7 @@ def get_version_by_name(project_name, version, subset_id, fields=None): Args: project_name (str): Name of project where to look for queried entities. version (int): name of version entity (it's version). - subset_id (ObjectId): Id of version which should be found. + subset_id (str|ObjectId): Id of version which should be found. fields (list[str]): Fields that should be returned. All fields are returned if 'None' is passed. @@ -622,13 +622,13 @@ def get_output_link_versions(project_name, version_id, fields=None): def get_last_versions(project_name, subset_ids, fields=None): - """Retrieve all latest versions for entered subset_ids. + """Latest versions for entered subset_ids. Args: subset_ids (list): List of subset ids. Returns: - dict: Key is subset id and value is last version name. + dict[ObjectId, int]: Key is subset id and value is last version name. """ subset_ids = _convert_ids(subset_ids) @@ -665,35 +665,60 @@ def get_last_versions(project_name, subset_ids, fields=None): } -def get_last_version_for_subset( - project_name, subset_id=None, subset_name=None, asset_id=None, fields=None -): - subset_doc = get_subset( - project_name, - subset_id=subset_id, - subset_name=subset_name, - asset_id=asset_id, - fields=["_id"] - ) - if not subset_doc: +def get_last_version_by_subset_id(project_name, subset_id, fields=None): + """Last version for passed subset id. + + Args: + project_name (str): Name of project where to look for queried entities. + subset_id (str|ObjectId): Id of version which should be found. + fields (list[str]): Fields that should be returned. All fields are + returned if 'None' is passed. + + Returns: + None: If version with specified filters was not found. + Dict: Version document which can be reduced to specified 'fields'. + """ + + subset_id = _convert_id(subset_id) + if not subset_id: return None - subset_id = subset_doc["_id"] + last_versions = get_last_versions( project_name, subset_ids=[subset_id], fields=fields ) return last_versions.get(subset_id) -def get_representation( - project_name, - representation_id=None, - representation_name=None, - version_id=None, - fields=None +def get_last_version_by_subset_name( + project_name, subset_name, asset_id, fields=None ): + """Last version for passed subset name under asset id. + + Args: + project_name (str): Name of project where to look for queried entities. + subset_name (str): Name of subset. + asset_id (str|ObjectId): Asset id which is parnt of passed subset name. + fields (list[str]): Fields that should be returned. All fields are + returned if 'None' is passed. + + Returns: + None: If version with specified filters was not found. + Dict: Version document which can be reduced to specified 'fields'. + """ + + subset_doc = get_subset_by_name( + project_name, subset_name, asset_id, fields=["_id"] + ) + if not subset_doc: + return None + return get_last_version_by_subset_id( + project_name, subset_doc["_id"], fields=fields + ) + + +def get_representation(project_name, representation_id, fields=None): if not representation_id: - if not representation_name or not version_id: - return None + return None repre_types = ["representation", "archived_representations"] query_filter = { @@ -702,12 +727,6 @@ def get_representation( if representation_id is not None: query_filter["_id"] = _convert_id(representation_id) - if representation_name is not None: - query_filter["name"] = representation_name - - if version_id is not None: - query_filter["parent"] = version_id - conn = _get_project_connection(project_name) return conn.find_one(query_filter, _prepare_fields(fields)) diff --git a/openpype/tools/mayalookassigner/app.py b/openpype/tools/mayalookassigner/app.py index 427edf8245..5665acea42 100644 --- a/openpype/tools/mayalookassigner/app.py +++ b/openpype/tools/mayalookassigner/app.py @@ -4,7 +4,7 @@ import logging from Qt import QtWidgets, QtCore -from openpype.client import get_last_version_for_subset +from openpype.client import get_last_version_by_subset_id from openpype import style from openpype.pipeline import legacy_io from openpype.tools.utils.lib import qt_app_context @@ -230,8 +230,8 @@ class MayaLookAssignerWindow(QtWidgets.QWidget): continue # Get the latest version of this asset's look subset - version = get_last_version_for_subset( - project_name, subset_id=assign_look["_id"], fields=["_id"] + version = get_last_version_by_subset_id( + project_name, assign_look["_id"], fields=["_id"] ) subset_name = assign_look["name"] diff --git a/openpype/tools/mayalookassigner/vray_proxies.py b/openpype/tools/mayalookassigner/vray_proxies.py index b2ba21f944..889396e555 100644 --- a/openpype/tools/mayalookassigner/vray_proxies.py +++ b/openpype/tools/mayalookassigner/vray_proxies.py @@ -12,7 +12,7 @@ from maya import cmds from openpype.client import ( get_representation_by_name, - get_last_version_for_subset, + get_last_version_by_subset_name, ) from openpype.pipeline import ( legacy_io, @@ -251,7 +251,7 @@ def vrayproxy_assign_look(vrayproxy, subset="lookDefault"): for asset_id, node_ids in node_ids_by_asset_id.items(): # Get latest look version - version = get_last_version_for_subset( + version = get_last_version_by_subset_name( project_name, subset_name=subset, asset_id=asset_id, diff --git a/openpype/tools/sceneinventory/model.py b/openpype/tools/sceneinventory/model.py index a5d856fe72..8c49933e80 100644 --- a/openpype/tools/sceneinventory/model.py +++ b/openpype/tools/sceneinventory/model.py @@ -10,7 +10,7 @@ from openpype.client import ( get_asset_by_id, get_subset_by_id, get_version_by_id, - get_last_version_for_subset, + get_last_version_by_subset_id, get_representation, ) from openpype.pipeline import ( @@ -403,8 +403,8 @@ class InventoryModel(TreeModel): # Store the highest available version so the model can know # whether current version is currently up-to-date. - highest_version = get_last_version_for_subset( - project_name, subset_id=version["parent"] + highest_version = get_last_version_by_subset_id( + project_name, version["parent"] ) # create the group header diff --git a/openpype/tools/standalonepublish/widgets/widget_family.py b/openpype/tools/standalonepublish/widgets/widget_family.py index 2f00cfe7bb..1736be84ab 100644 --- a/openpype/tools/standalonepublish/widgets/widget_family.py +++ b/openpype/tools/standalonepublish/widgets/widget_family.py @@ -6,7 +6,7 @@ from openpype.client import ( get_asset_by_name, get_subset_by_name, get_subsets, - get_last_version_for_subset, + get_last_version_by_subset_id, ) from openpype.api import get_project_settings from openpype.pipeline import LegacyCreator @@ -323,9 +323,9 @@ class FamilyWidget(QtWidgets.QWidget): ) if subset_doc: - last_version = get_last_version_for_subset( + last_version = get_last_version_by_subset_id( project_name, - subset_id=subset_doc["_id"], + subset_doc["_id"], fields=["name"] ) if last_version: From cde47eefa8201fb6fc7b922427436ec2f3bd0202 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Tue, 7 Jun 2022 18:27:54 +0200 Subject: [PATCH 14/51] renamed 'get_representation' to 'get_representation_by_id' --- openpype/client/__init__.py | 4 +-- openpype/client/entities.py | 35 +++++++++++++++++++++++--- openpype/tools/sceneinventory/model.py | 6 ++--- openpype/tools/sceneinventory/view.py | 6 ++--- 4 files changed, 40 insertions(+), 11 deletions(-) diff --git a/openpype/client/__init__.py b/openpype/client/__init__.py index 2ef32d6a83..2fa7730839 100644 --- a/openpype/client/__init__.py +++ b/openpype/client/__init__.py @@ -23,7 +23,7 @@ from .entities import ( get_last_version_by_subset_name, get_output_link_versions, - get_representation, + get_representation_by_id, get_representation_by_name, get_representations, get_representation_parents, @@ -58,7 +58,7 @@ __all__ = ( "get_last_version_by_subset_name", "get_output_link_versions", - "get_representation", + "get_representation_by_id", "get_representation_by_name", "get_representations", "get_representation_parents", diff --git a/openpype/client/entities.py b/openpype/client/entities.py index 232d9aebcc..7abb25e380 100644 --- a/openpype/client/entities.py +++ b/openpype/client/entities.py @@ -716,7 +716,21 @@ def get_last_version_by_subset_name( ) -def get_representation(project_name, representation_id, fields=None): +def get_representation_by_id(project_name, representation_id, fields=None): + """Representation entity data by it's id. + + Args: + project_name (str): Name of project where to look for queried entities. + representation_id (str|ObjectId): Representation id. + fields (list[str]): Fields that should be returned. All fields are + returned if 'None' is passed. + + Returns: + None: If representation with specified filters was not found. + Dict: Representation entity data which can be reduced + to specified 'fields'. + """ + if not representation_id: return None @@ -735,17 +749,32 @@ def get_representation(project_name, representation_id, fields=None): def get_representation_by_name( project_name, representation_name, version_id, fields=None ): + """Representation entity data by it's name and it's version id. + + Args: + project_name (str): Name of project where to look for queried entities. + representation_name (str): Representation name. + version_id (str|ObjectId): Id of parent version entity. + fields (list[str]): Fields that should be returned. All fields are + returned if 'None' is passed. + + Returns: + None: If representation with specified filters was not found. + Dict: Representation entity data which can be reduced + to specified 'fields'. + """ + + version_id = _convert_id(version_id) if not version_id or not representation_name: return None repre_types = ["representation", "archived_representations"] query_filter = { "type": {"$in": repre_types}, "name": representation_name, - "parent": _convert_id(version_id) + "parent": version_id } conn = _get_project_connection(project_name) - return conn.find_one(query_filter, _prepare_fields(fields)) diff --git a/openpype/tools/sceneinventory/model.py b/openpype/tools/sceneinventory/model.py index 8c49933e80..117bdfcba1 100644 --- a/openpype/tools/sceneinventory/model.py +++ b/openpype/tools/sceneinventory/model.py @@ -11,7 +11,7 @@ from openpype.client import ( get_subset_by_id, get_version_by_id, get_last_version_by_subset_id, - get_representation, + get_representation_by_id, ) from openpype.pipeline import ( legacy_io, @@ -313,8 +313,8 @@ class InventoryModel(TreeModel): for repre_id, group_dict in sorted(grouped.items()): group_items = group_dict["items"] # Get parenthood per group - representation = get_representation( - project_name, representation_id=repre_id + representation = get_representation_by_id( + project_name, repre_id ) if not representation: not_found["representation"].append(group_items) diff --git a/openpype/tools/sceneinventory/view.py b/openpype/tools/sceneinventory/view.py index d1ff91535f..8164c48a5d 100644 --- a/openpype/tools/sceneinventory/view.py +++ b/openpype/tools/sceneinventory/view.py @@ -10,7 +10,7 @@ from openpype.client import ( get_version_by_id, get_versions, get_hero_versions, - get_representation, + get_representation_by_id, get_representations, ) from openpype import style @@ -657,9 +657,9 @@ class SceneInventoryView(QtWidgets.QTreeView): project_name = legacy_io.active_project() # Get available versions for active representation - repre_doc = get_representation( + repre_doc = get_representation_by_id( project_name, - representation_id=active["representation"], + active["representation"], fields=["parent"] ) From aa91db6883485f33ba7bd2c74e2250ec5ebd906d Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Tue, 7 Jun 2022 18:36:54 +0200 Subject: [PATCH 15/51] added docstring for get_representations --- openpype/client/entities.py | 36 +++++++++++++++++++++++++++++++----- 1 file changed, 31 insertions(+), 5 deletions(-) diff --git a/openpype/client/entities.py b/openpype/client/entities.py index 7abb25e380..6c0170ea40 100644 --- a/openpype/client/entities.py +++ b/openpype/client/entities.py @@ -147,7 +147,7 @@ def get_assets( Args: project_name (str): Name of project where to look for queried entities. - asset_ids (list[str, ObjectId]): Asset ids that should be found. + asset_ids (list[str|ObjectId]): Asset ids that should be found. asset_names (list[str]): Name assets that should be found. fields (list[str]): Fields that should be returned. All fields are returned if 'None' is passed. @@ -291,11 +291,11 @@ def get_subsets( Args: project_name (str): Name of project where to look for queried entities. - subset_ids (list[str, ObjectId]): Subset ids that should be queried. + subset_ids (list[str|ObjectId]): Subset ids that should be queried. Filter ignored if 'None' is passed. subset_names (list[str]): Subset names that should be queried. Filter ignored if 'None' is passed. - asset_ids (list[str, ObjectId]): Asset ids under which should look for + asset_ids (list[str|ObjectId]): Asset ids under which should look for the subsets. Filter ignored if 'None' is passed. fields (list[str]): Fields that should be returned. All fields are returned if 'None' is passed. @@ -339,7 +339,7 @@ def get_subset_families(project_name, subset_ids=None): Args: project_name (str): Name of project where to look for queried entities. - subset_ids (list[str, ObjectId]): Subset ids that should be queried. + subset_ids (list[str|ObjectId]): Subset ids that should be queried. All subsets from project are used if 'None' is passed. Returns: @@ -476,7 +476,7 @@ def get_versions( Args: project_name (str): Name of project where to look for queried entities. - version_ids (list[str, ObjectId]): Version ids that will be queried. + version_ids (list[str|ObjectId]): Version ids that will be queried. Filter ignored if 'None' is passed. subset_ids (list[str]): Subset ids that will be queried. Filter ignored if 'None' is passed. @@ -789,6 +789,32 @@ def get_representations( archived=False, fields=None ): + """Representaion entities data from one project filtered by filters. + + Filters are additive (all conditions must pass to return subset). + + Args: + project_name (str): Name of project where to look for queried entities. + representation_ids (list[str|ObjectId]): Representation ids used as + filter. Filter ignored if 'None' is passed. + representation_names (list[str]): Representations names used as filter. + Filter ignored if 'None' is passed. + version_ids (list[str]): Subset ids used as parent filter. Filter + ignored if 'None' is passed. + extensions (list[str]): Filter by extension of main representation + file (without dot). + names_by_version_ids (dict[ObjectId, list[str]]): Complex filtering + using version ids and list of names under the version. + check_site_name (bool): Filter only representation that have existing + site name. + archived (bool): Output will also contain archived representations. + fields (list[str]): Fields that should be returned. All fields are + returned if 'None' is passed. + + Returns: + Cursor: Iterable cursor yielding all matching representations. + """ + repre_types = ["representation"] if archived: repre_types.append("archived_representations") From 7d13ba2706d0cdc11f6b6358bafc432c87522d04 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Tue, 7 Jun 2022 18:37:09 +0200 Subject: [PATCH 16/51] added missing functions to legacy_io and mongodb --- openpype/pipeline/legacy_io.py | 9 +++++++++ openpype/pipeline/mongodb.py | 4 ++++ 2 files changed, 13 insertions(+) diff --git a/openpype/pipeline/legacy_io.py b/openpype/pipeline/legacy_io.py index c8e7e79600..9359e3057b 100644 --- a/openpype/pipeline/legacy_io.py +++ b/openpype/pipeline/legacy_io.py @@ -144,3 +144,12 @@ def parenthood(*args, **kwargs): @requires_install def bulk_write(*args, **kwargs): return _connection_object.bulk_write(*args, **kwargs) + + +@requires_install +def active_project(*args, **kwargs): + return _connection_object.active_project(*args, **kwargs) + + +def current_project(*args, **kwargs): + return Session.get("AVALON_PROJECT") diff --git a/openpype/pipeline/mongodb.py b/openpype/pipeline/mongodb.py index 565e26b966..dab5bb9e13 100644 --- a/openpype/pipeline/mongodb.py +++ b/openpype/pipeline/mongodb.py @@ -199,6 +199,10 @@ class AvalonMongoDB: """Return the name of the active project""" return self.Session["AVALON_PROJECT"] + def current_project(self): + """Currently set project in Session without triggering installation.""" + return self.Session.get("AVALON_PROJECT") + @requires_install @auto_reconnect def projects(self, projection=None, only_active=True): From 0ce3045ad0bf399389cdbc7605762ecfe7b638f2 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Tue, 7 Jun 2022 18:47:42 +0200 Subject: [PATCH 17/51] added missing docstrings --- openpype/client/__init__.py | 2 + openpype/client/entities.py | 88 ++++++++++++++++++++++++++++++++++++- 2 files changed, 88 insertions(+), 2 deletions(-) diff --git a/openpype/client/__init__.py b/openpype/client/__init__.py index 2fa7730839..16b1dcf321 100644 --- a/openpype/client/__init__.py +++ b/openpype/client/__init__.py @@ -30,6 +30,7 @@ from .entities import ( get_representations_parents, get_thumbnail, + get_thumbnails, get_thumbnail_id_from_source, ) @@ -65,5 +66,6 @@ __all__ = ( "get_representations_parents", "get_thumbnail", + "get_thumbnails", "get_thumbnail_id_from_source", ) diff --git a/openpype/client/entities.py b/openpype/client/entities.py index 6c0170ea40..c50ae32d7f 100644 --- a/openpype/client/entities.py +++ b/openpype/client/entities.py @@ -866,6 +866,20 @@ def get_representations( def get_representations_parents(project_name, representations): + """Prepare parents of representation entities. + + Each item of returned dictionary contains version, subset, asset + and project in that order. + + Args: + project_name (str): Name of project where to look for queried entities. + representations (list[dict]): Representation entities with at least + '_id' and 'parent' keys. + + Returns: + dict[ObjectId, tuple]: Parents by representation id. + """ + repres_by_version_id = collections.defaultdict(list) versions_by_version_id = {} versions_by_subset_id = collections.defaultdict(list) @@ -921,15 +935,43 @@ def get_representations_parents(project_name, representations): def get_representation_parents(project_name, representation): + """Prepare parents of representation entity. + + Each item of returned dictionary contains version, subset, asset + and project in that order. + + Args: + project_name (str): Name of project where to look for queried entities. + representation (dict): Representation entities with at least + '_id' and 'parent' keys. + + Returns: + dict[ObjectId, tuple]: Parents by representation id. + """ + if not representation: return None repre_id = representation["_id"] - parents_by_repre_id = get_representations(project_name, [representation]) + parents_by_repre_id = get_representations_parents( + project_name, [representation] + ) return parents_by_repre_id.get(repre_id) def get_thumbnail_id_from_source(project_name, src_type, src_id): + """Receive thumbnail id from source entity. + + Args: + project_name (str): Name of project where to look for queried entities. + src_type (str): Type of source entity ('asset', 'version'). + src_id (str|objectId): Id of source entity. + + Returns: + ObjectId: Thumbnail id assigned to entity. + None: If Source entity does not have any thumbnail id assigned. + """ + if not src_type or not src_id: return None @@ -942,12 +984,54 @@ def get_thumbnail_id_from_source(project_name, src_type, src_id): return None +def get_thumbnails(project_name, thumbnail_ids, fields=None): + """Receive thumbnails entity data. + + Thumbnail entity can be used to receive binary content of thumbnail based + on it's content and ThumbnailResolvers. + + Args: + project_name (str): Name of project where to look for queried entities. + thumbnail_ids (list[str|ObjectId]): Ids of thumbnail entities. + fields (list[str]): Fields that should be returned. All fields are + returned if 'None' is passed. + + Returns: + cursor: Cursor of queried documents. + """ + + if thumbnail_ids: + thumbnail_ids = _convert_ids(thumbnail_ids) + + if not thumbnail_ids: + return [] + query_filter = { + "type": "thumbnail", + "_id": {"$in": thumbnail_ids} + } + conn = _get_project_connection(project_name) + return conn.find(query_filter, _prepare_fields(fields)) + + def get_thumbnail(project_name, thumbnail_id, fields=None): + """Receive thumbnail entity data. + + Args: + project_name (str): Name of project where to look for queried entities. + thumbnail_id (str|ObjectId): Id of thumbnail entity. + fields (list[str]): Fields that should be returned. All fields are + returned if 'None' is passed. + + Returns: + None: If thumbnail with specified id was not found. + Dict: Thumbnail entity data which can be reduced to specified 'fields'. + """ + if not thumbnail_id: return None query_filter = {"type": "thumbnail", "_id": _convert_id(thumbnail_id)} conn = _get_project_connection(project_name) - return conn.find(query_filter, _prepare_fields(fields)) + return conn.find_one(query_filter, _prepare_fields(fields)) """ From 125af17c2244f902447048c803d649f657f16f38 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Tue, 7 Jun 2022 18:47:47 +0200 Subject: [PATCH 18/51] updated usages --- openpype/client/entities.py | 57 +++++++++++++++++++------------------ 1 file changed, 30 insertions(+), 27 deletions(-) diff --git a/openpype/client/entities.py b/openpype/client/entities.py index c50ae32d7f..2459ea3e92 100644 --- a/openpype/client/entities.py +++ b/openpype/client/entities.py @@ -1045,12 +1045,12 @@ openpype/tools/assetlinks/widgets.py - get_versions - get_subsets - get_assets - - get_version_links + - get_output_link_versions openpype/tools/creator/window.py - CreatorWindow Query: - - get_asset + - get_asset_by_name - get_subsets openpype/tools/launcher/models.py @@ -1093,12 +1093,14 @@ openpype/tools/loader/widgets.py - get_subset_families - VersionTextEdit Query: - - get_subset - - get_version + - get_subset_by_id + - get_version_by_id - SubsetWidget Query: - get_subsets - get_representations + Update: + - Subset groups (combination of asset id and subset names) - RepresentationWidget Query: - get_subsets @@ -1112,12 +1114,12 @@ openpype/tools/loader/widgets.py openpype/tools/mayalookassigner/app.py - MayaLookAssignerWindow Query: - - get_last_version_for_subset + - get_last_version_by_subset_id openpype/tools/mayalookassigner/commands.py - create_items_from_nodes Query: - - get_asset + - get_asset_by_id openpype/tools/mayalookassigner/vray_proxies.py - get_look_relationships @@ -1128,7 +1130,7 @@ openpype/tools/mayalookassigner/vray_proxies.py - get_representation_by_name - vrayproxy_assign_look Query: - - get_last_version_for_subset + - get_last_version_by_subset_name openpype/tools/project_manager/project_manager/model.py - HierarchyModel @@ -1150,7 +1152,7 @@ openpype/tools/project_manager/project_manager/widgets.py openpype/tools/publisher/widgets/create_dialog.py - CreateDialog Query: - - get_asset + - get_asset_by_name - get_subsets openpype/tools/publisher/control.py @@ -1161,18 +1163,18 @@ openpype/tools/publisher/control.py openpype/tools/sceneinventory/model.py - InventoryModel Query: - - get_asset - - get_subset - - get_version - - get_last_version_for_subset + - get_asset_by_id + - get_subset_by_id + - get_version_by_id + - get_last_version_by_subset_id - get_representation openpype/tools/sceneinventory/switch_dialog.py - SwitchAssetDialog Query: - - get_asset + - get_asset_by_name - get_assets - - get_subset + - get_subset_by_name - get_subsets - get_versions - get_hero_versions @@ -1182,10 +1184,10 @@ openpype/tools/sceneinventory/switch_dialog.py openpype/tools/sceneinventory/view.py - SceneInventoryView Query: - - get_version + - get_version_by_id - get_versions - get_hero_versions - - get_representation + - get_representation_by_id - get_representations openpype/tools/standalonepublish/widgets/model_asset.py @@ -1197,31 +1199,31 @@ openpype/tools/standalonepublish/widgets/widget_asset.py - AssetWidget Query: - get_project - - get_asset + - get_asset_by_id openpype/tools/standalonepublish/widgets/widget_family.py - FamilyWidget Query: - - get_asset - - get_subset + - get_asset_by_name + - get_subset_by_name - get_subsets - - get_last_version_for_subset + - get_last_version_by_subset_id openpype/tools/standalonepublish/app.py - Window Query: - - get_asset + - get_asset_by_id openpype/tools/texture_copy/app.py - TextureCopy Query: - get_project - - get_asset + - get_asset_by_name openpype/tools/workfiles/files_widget.py - FilesWidget Query: - - get_asset + - get_asset_by_id openpype/tools/workfiles/model.py - PublishFilesModel @@ -1234,12 +1236,13 @@ openpype/tools/workfiles/save_as_dialog.py - build_workfile_data Query: - get_project - - get_asset + - get_asset_by_name openpype/tools/workfiles/window.py - Window Query: - - get_asset + - get_asset_by_id + - get_asset_by_name openpype/tools/utils/assets_widget.py - AssetModel @@ -1259,11 +1262,11 @@ openpype/tools/utils/lib.py - get_project - FamilyConfigCache Query: - - get_asset + - get_asset_by_name openpype/tools/utils/tasks_widget.py - TasksModel Query: - get_project - - get_asset + - get_asset_by_id """ From e4cee4a98ddd4150ee371d338350aefc4964df49 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Wed, 8 Jun 2022 19:18:20 +0200 Subject: [PATCH 19/51] fix loader --- openpype/tools/loader/model.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/openpype/tools/loader/model.py b/openpype/tools/loader/model.py index e8e0480d9c..f2b7e9a6a4 100644 --- a/openpype/tools/loader/model.py +++ b/openpype/tools/loader/model.py @@ -206,9 +206,6 @@ class SubsetsModel(TreeModel, BaseRepresentationModel): if subset_doc_projection: self.subset_doc_projection = subset_doc_projection - self.asset_doc_projection = asset_doc_projection - self.subset_doc_projection = subset_doc_projection - self.repre_icons = {} self.sync_server = None self.active_site = self.active_provider = None From 1cea16b97b416cda2cd12fca9365b046bc05a2eb Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Wed, 8 Jun 2022 19:18:27 +0200 Subject: [PATCH 20/51] fix launcher --- openpype/tools/launcher/models.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/openpype/tools/launcher/models.py b/openpype/tools/launcher/models.py index 307f591d96..3f899cc05e 100644 --- a/openpype/tools/launcher/models.py +++ b/openpype/tools/launcher/models.py @@ -651,9 +651,9 @@ class LauncherModel(QtCore.QObject): self._asset_refresh_thread = None def _refresh_assets(self): - asset_docs = get_assets( - self._last_project_name, fields=list(self._asset_projection.keys()) - ) + asset_docs = list(get_assets( + self._last_project_name, fields=self._asset_projection.keys() + )) if not self._refreshing_assets: return self._refreshing_assets = False From 6fcf8722370fcbdf7fabba7dcc1012f8484f836a Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Wed, 8 Jun 2022 19:20:53 +0200 Subject: [PATCH 21/51] fix get_last_version --- openpype/client/entities.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/openpype/client/entities.py b/openpype/client/entities.py index 2459ea3e92..66204a4b19 100644 --- a/openpype/client/entities.py +++ b/openpype/client/entities.py @@ -655,6 +655,10 @@ def get_last_versions(project_name, subset_ids, fields=None): doc["_version_id"] for doc in conn.aggregate(_pipeline) ] + fields = _prepare_fields(fields) + if fields and "parent" not in fields: + fields.append("parent") + version_docs = get_versions( project_name, version_ids=version_ids, fields=fields ) From 6b9a1b834d3de4f1d2b399c916a50ac1282a4b6f Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Thu, 9 Jun 2022 10:48:29 +0200 Subject: [PATCH 22/51] convert queried cursor to list in scene inventory --- openpype/tools/sceneinventory/view.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/openpype/tools/sceneinventory/view.py b/openpype/tools/sceneinventory/view.py index 8164c48a5d..0b54d40ed7 100644 --- a/openpype/tools/sceneinventory/view.py +++ b/openpype/tools/sceneinventory/view.py @@ -669,11 +669,11 @@ class SceneInventoryView(QtWidgets.QTreeView): fields=["parent"] ) - version_docs = get_versions( + version_docs = list(get_versions( project_name, subset_ids=[repre_version_doc["parent"]], hero=True - ) + )) hero_version = None standard_versions = [] for version_doc in version_docs: From 39ea438d7d8c2010b914c300ae84f6536e71e0a4 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Thu, 9 Jun 2022 10:51:33 +0200 Subject: [PATCH 23/51] fix typo in passed kwarg --- openpype/tools/sceneinventory/view.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openpype/tools/sceneinventory/view.py b/openpype/tools/sceneinventory/view.py index 0b54d40ed7..63d181b2d6 100644 --- a/openpype/tools/sceneinventory/view.py +++ b/openpype/tools/sceneinventory/view.py @@ -92,7 +92,7 @@ class SceneInventoryView(QtWidgets.QTreeView): project_name = legacy_io.active_project() repre_docs = get_representations( - project_name, representaion_ids=repre_ids, fields=["parent"] + project_name, representation_ids=repre_ids, fields=["parent"] ) version_ids = [] From a48d77e609f84a7a04bf5de95fce12e05461ca6c Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Thu, 9 Jun 2022 10:53:15 +0200 Subject: [PATCH 24/51] conver repres cursor to list --- openpype/tools/sceneinventory/switch_dialog.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/openpype/tools/sceneinventory/switch_dialog.py b/openpype/tools/sceneinventory/switch_dialog.py index 92ef2b3553..aa4dcd73cd 100644 --- a/openpype/tools/sceneinventory/switch_dialog.py +++ b/openpype/tools/sceneinventory/switch_dialog.py @@ -165,11 +165,11 @@ class SwitchAssetDialog(QtWidgets.QDialog): content_loaders.add(item["loader"]) project_name = self.active_project() - repres = get_representations( + repres = list(get_representations( project_name, representation_ids=repre_ids, archived=True - ) + )) repres_by_id = {repre["_id"]: repre for repre in repres} # stash context values, works only for single representation From bae1e38112cc8a756388b92f1447c5e69112e6ab Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Thu, 9 Jun 2022 10:59:04 +0200 Subject: [PATCH 25/51] fix kwargs name in switch dialog --- openpype/tools/sceneinventory/switch_dialog.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/openpype/tools/sceneinventory/switch_dialog.py b/openpype/tools/sceneinventory/switch_dialog.py index aa4dcd73cd..1d1d5cbb91 100644 --- a/openpype/tools/sceneinventory/switch_dialog.py +++ b/openpype/tools/sceneinventory/switch_dialog.py @@ -944,7 +944,7 @@ class SwitchAssetDialog(QtWidgets.QDialog): repre_docs = list( get_representations( project_name, - subset_ids=subset_id_by_version_id.keys(), + version_ids=subset_id_by_version_id.keys(), fields=["name", "parent"] ) ) @@ -1102,7 +1102,7 @@ class SwitchAssetDialog(QtWidgets.QDialog): repre_docs = get_representations( project_name, - subset_ids=subset_id_by_version_id.keys(), + version_ids=subset_id_by_version_id.keys(), fields=["name", "parent"] ) repres_by_subset_name = {} From 57e72c2c3722f232b58a81454b3f46dbd7cba647 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Thu, 9 Jun 2022 11:32:49 +0200 Subject: [PATCH 26/51] minor changesminor changes in representation widget --- openpype/tools/loader/model.py | 23 +++++++++++------------ openpype/tools/loader/widgets.py | 4 ++-- 2 files changed, 13 insertions(+), 14 deletions(-) diff --git a/openpype/tools/loader/model.py b/openpype/tools/loader/model.py index f2b7e9a6a4..f030e94256 100644 --- a/openpype/tools/loader/model.py +++ b/openpype/tools/loader/model.py @@ -231,7 +231,7 @@ class SubsetsModel(TreeModel, BaseRepresentationModel): self._doc_fetching_stop = False self._doc_payload = {} - self.doc_fetched.connect(self.on_doc_fetched) + self.doc_fetched.connect(self._on_doc_fetched) self.refresh() @@ -250,7 +250,7 @@ class SubsetsModel(TreeModel, BaseRepresentationModel): def set_grouping(self, state): self._grouping = state - self.on_doc_fetched() + self._on_doc_fetched() def get_subsets_families(self): return self._doc_payload.get("subset_families") or set() @@ -528,7 +528,7 @@ class SubsetsModel(TreeModel, BaseRepresentationModel): self.fetch_subset_and_version() - def on_doc_fetched(self): + def _on_doc_fetched(self): self.clear() self._items_by_id = {} self.beginResetModel() @@ -1019,7 +1019,6 @@ class RepresentationSortProxyModel(GroupMemberFilterProxyModel): class RepresentationModel(TreeModel, BaseRepresentationModel): - doc_fetched = QtCore.Signal() refreshed = QtCore.Signal(bool) @@ -1055,12 +1054,12 @@ class RepresentationModel(TreeModel, BaseRepresentationModel): 'files.sites': 1 } - def __init__(self, dbcon, header, version_ids): + def __init__(self, dbcon, header): super(RepresentationModel, self).__init__() self.dbcon = dbcon self._data = [] self._header = header - self.version_ids = version_ids + self._version_ids = [] manager = ModulesManager() sync_server = active_site = remote_site = None @@ -1092,7 +1091,7 @@ class RepresentationModel(TreeModel, BaseRepresentationModel): self.remote_site = remote_site self.remote_provider = remote_provider - self.doc_fetched.connect(self.on_doc_fetched) + self.doc_fetched.connect(self._on_doc_fetched) self._docs = {} self._icons = lib.get_repre_icons() @@ -1103,7 +1102,7 @@ class RepresentationModel(TreeModel, BaseRepresentationModel): self._items_by_id = {} def set_version_ids(self, version_ids): - self.version_ids = version_ids + self._version_ids = version_ids self.refresh() def data(self, index, role): @@ -1171,7 +1170,7 @@ class RepresentationModel(TreeModel, BaseRepresentationModel): return super(RepresentationModel, self).data(index, role) - def on_doc_fetched(self): + def _on_doc_fetched(self): self.clear() self.beginResetModel() subsets = set() @@ -1181,7 +1180,7 @@ class RepresentationModel(TreeModel, BaseRepresentationModel): group = None self._items_by_id = {} for doc in self._docs: - if len(self.version_ids) > 1: + if len(self._version_ids) > 1: group = repre_groups.get(doc["name"]) if not group: group_item = Item() @@ -1265,12 +1264,12 @@ class RepresentationModel(TreeModel, BaseRepresentationModel): return repre_docs = [] - if self.version_ids: + if self._version_ids: # Simple find here for now, expected to receive lower number of # representations and logic could be in Python repre_docs = list(get_representations( project_name, - version_ids=self.version_ids, + version_ids=self._version_ids, check_site_name=True, fields=self.repre_projection.keys() )) diff --git a/openpype/tools/loader/widgets.py b/openpype/tools/loader/widgets.py index 921708922e..fd43435b15 100644 --- a/openpype/tools/loader/widgets.py +++ b/openpype/tools/loader/widgets.py @@ -1164,7 +1164,7 @@ class RepresentationWidget(QtWidgets.QWidget): headers = [item[0] for item in self.default_widths] - model = RepresentationModel(self.dbcon, headers, []) + model = RepresentationModel(self.dbcon, headers) proxy_model = RepresentationSortProxyModel(self) proxy_model.setSourceModel(model) @@ -1231,7 +1231,7 @@ class RepresentationWidget(QtWidgets.QWidget): for item in items: repre_ids.append(item["_id"]) - project_name = self.dbcon.actual_project() + project_name = self.dbcon.active_project() repre_docs = get_representations( project_name, representation_ids=repre_ids, From de2fb5d1ff808d81a56a84c8240cac2465feae42 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Thu, 9 Jun 2022 11:38:18 +0200 Subject: [PATCH 27/51] convert cursor of representations to list --- openpype/tools/loader/widgets.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/openpype/tools/loader/widgets.py b/openpype/tools/loader/widgets.py index fd43435b15..0482bad642 100644 --- a/openpype/tools/loader/widgets.py +++ b/openpype/tools/loader/widgets.py @@ -1232,11 +1232,11 @@ class RepresentationWidget(QtWidgets.QWidget): repre_ids.append(item["_id"]) project_name = self.dbcon.active_project() - repre_docs = get_representations( + repre_docs = list(get_representations( project_name, representation_ids=repre_ids, fields=["name", "parent"] - ) + )) version_ids = [ repre_doc["parent"] From 2df00d5caefd92aad61112b110d8b7a9303f5230 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Thu, 9 Jun 2022 11:38:43 +0200 Subject: [PATCH 28/51] remove site name check in representations widget --- openpype/client/entities.py | 6 ------ openpype/tools/loader/model.py | 1 - 2 files changed, 7 deletions(-) diff --git a/openpype/client/entities.py b/openpype/client/entities.py index 66204a4b19..91646e7a1d 100644 --- a/openpype/client/entities.py +++ b/openpype/client/entities.py @@ -789,7 +789,6 @@ def get_representations( version_ids=None, extensions=None, names_by_version_ids=None, - check_site_name=False, archived=False, fields=None ): @@ -809,8 +808,6 @@ def get_representations( file (without dot). names_by_version_ids (dict[ObjectId, list[str]]): Complex filtering using version ids and list of names under the version. - check_site_name (bool): Filter only representation that have existing - site name. archived (bool): Output will also contain archived representations. fields (list[str]): Fields that should be returned. All fields are returned if 'None' is passed. @@ -827,9 +824,6 @@ def get_representations( else: query_filter = {"type": {"$in": repre_types}} - if check_site_name: - query_filter["files.site.name"] = {"$exists": True} - if representation_ids is not None: representation_ids = _convert_ids(representation_ids) if not representation_ids: diff --git a/openpype/tools/loader/model.py b/openpype/tools/loader/model.py index f030e94256..46acc68f67 100644 --- a/openpype/tools/loader/model.py +++ b/openpype/tools/loader/model.py @@ -1270,7 +1270,6 @@ class RepresentationModel(TreeModel, BaseRepresentationModel): repre_docs = list(get_representations( project_name, version_ids=self._version_ids, - check_site_name=True, fields=self.repre_projection.keys() )) From 483ca9b301680cdfee8eee7f4270eb5a1654f312 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Thu, 9 Jun 2022 11:49:03 +0200 Subject: [PATCH 29/51] fix args order --- openpype/tools/loader/model.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openpype/tools/loader/model.py b/openpype/tools/loader/model.py index 46acc68f67..b5dc16a680 100644 --- a/openpype/tools/loader/model.py +++ b/openpype/tools/loader/model.py @@ -267,7 +267,7 @@ class SubsetsModel(TreeModel, BaseRepresentationModel): else: project_name = self.dbcon.active_project() version_doc = get_version_by_name( - project_name, subset_id, value + project_name, value, subset_id ) # update availability on active site when version changes From a63e5592ab02c61c3712d6cc0a10716ffb2efb33 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Thu, 9 Jun 2022 11:52:59 +0200 Subject: [PATCH 30/51] fixed asset links widget --- openpype/tools/assetlinks/widgets.py | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/openpype/tools/assetlinks/widgets.py b/openpype/tools/assetlinks/widgets.py index 3078585ed1..1b168e542c 100644 --- a/openpype/tools/assetlinks/widgets.py +++ b/openpype/tools/assetlinks/widgets.py @@ -74,13 +74,13 @@ class SimpleLinkView(QtWidgets.QWidget): fields=["name", "parent"] )) - subset_docs = [] versions_by_subset_id = collections.defaultdict(list) - if versions_by_subset_id: - for version_doc in version_docs: - subset_id = version_doc["parent"] - versions_by_subset_id[subset_id].append(version_doc) + for version_doc in version_docs: + subset_id = version_doc["parent"] + versions_by_subset_id[subset_id].append(version_doc) + subset_docs = [] + if versions_by_subset_id: subset_docs = list(get_subsets( self.project_name, subset_ids=versions_by_subset_id.keys(), @@ -117,13 +117,13 @@ class SimpleLinkView(QtWidgets.QWidget): version_doc["_id"], fields=["name", "parent"] )) - subset_docs = [] versions_by_subset_id = collections.defaultdict(list) - if versions_by_subset_id: - for version_doc in version_docs: - subset_id = version_doc["parent"] - versions_by_subset_id[subset_id].append(version_doc) + for version_doc in version_docs: + subset_id = version_doc["parent"] + versions_by_subset_id[subset_id].append(version_doc) + subset_docs = [] + if versions_by_subset_id: subset_docs = list(get_subsets( self.project_name, subset_ids=versions_by_subset_id.keys(), From 34156e280bf7ebbd565e9754ae9f71fdf463d79f Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Thu, 9 Jun 2022 18:17:38 +0200 Subject: [PATCH 31/51] create new action to create daily review sessions --- .../action_create_review_session.py | 286 ++++++++++++++++++ 1 file changed, 286 insertions(+) create mode 100644 openpype/modules/ftrack/event_handlers_server/action_create_review_session.py diff --git a/openpype/modules/ftrack/event_handlers_server/action_create_review_session.py b/openpype/modules/ftrack/event_handlers_server/action_create_review_session.py new file mode 100644 index 0000000000..3b7cb224f0 --- /dev/null +++ b/openpype/modules/ftrack/event_handlers_server/action_create_review_session.py @@ -0,0 +1,286 @@ +import threading +import datetime +import copy +import collections + +import ftrack_api + +from openpype.lib import get_datetime_data +from openpype.api import get_project_settings +from openpype_modules.ftrack.lib import ServerAction + + +class CreateDailyReviewSessionServerAction(ServerAction): + """Create daily review session object per project. + + Action creates review sessions based on settings. Settings define if is + action enabled and what is a template for review session name. Logic works + in a way that if review session with the name already exists then skip + process. If review session for current day does not exist but yesterdays + review exists and is empty then yesterdays is renamed otherwise creates + new review session. + + Also contains cycle creation of dailies which is triggered each morning. + This option must be enabled in project settings. Cycle creation is also + checked on registration of action. + """ + + identifier = "create.daily.review.session" + #: Action label. + label = "OpenPype Admin" + variant = "- Create Daily Review Session (Server)" + #: Action description. + description = "Manually create daily review session" + role_list = {"Pypeclub", "Administrator", "Project Manager"} + + settings_key = "create_daily_review_session" + default_template = "{yy}{mm}{dd}" + + def __init__(self, *args, **kwargs): + super(CreateDailyReviewSessionServerAction, self).__init__(*args, **kwargs) + + self._cycle_timer = None + self._last_cyle_time = None + self._day_delta = datetime.timedelta(days=1) + + def discover(self, session, entities, event): + """Show action only on AssetVersions.""" + + valid_selection = False + for ent in event["data"]["selection"]: + # Ignore entities that are not tasks or projects + if ent["entityType"].lower() in ( + "show", "task", "reviewsession", "assetversion" + ): + valid_selection = True + break + else: + self.log.info(ent["entityType"]) + + if not valid_selection: + return False + return self.valid_roles(session, entities, event) + + def launch(self, session, entities, event): + project_entity = self.get_project_from_entity(entities[0], session) + project_name = project_entity["full_name"] + project_settings = self.get_project_settings_from_event( + event, project_name + ) + action_settings = self._extract_action_settings(project_settings) + project_name_by_id = { + project_entity["id"]: project_name + } + settings_by_project_id = { + project_entity["id"]: action_settings + } + self._process_review_session( + session, settings_by_project_id, project_name_by_id + ) + return True + + def register(self, *args, **kwargs): + """Override register to be able trigger """ + # Register server action as would be normally + super(CreateDailyReviewSessionServerAction, self).register(*args, **kwargs) + + # Create threading timer which will trigger creation of report + # at the 00:00:01 of next day + # - callback will trigger another timer which will have 1 day offset + now = datetime.datetime.now() + # Create object of today morning + today_morning = datetime.datetime( + now.year, now.month, now.day, 0, 0, 1 + ) + # Add a day delta (to calculate next day date) + next_day_morning = today_morning + self._day_delta + # Calculate first delta in seconds for first threading timer + first_delta = (next_day_morning - now).total_seconds() + # Store cycle time which will be used to create next timer + self._last_cyle_time = next_day_morning + # Create timer thread + self._cycle_timer = threading.Timer(first_delta, self._timer_callback) + self._cycle_timer.start() + + self._check_review_session() + + def _timer_callback(self): + if ( + self._cycle_timer is not None + and self._last_cyle_time is not None + ): + now = datetime.datetime.now() + while self._last_cyle_time < now: + self._last_cyle_time = self._last_cyle_time + self._day_delta + + delay = (self._last_cyle_time - now).total_seconds() + + self._cycle_timer = threading.Timer(delay, self._timer_callback) + self._cycle_timer.start() + self._check_review_session() + + def _check_review_session(self): + session = ftrack_api.Session( + server_url=self.session.server_url, + api_key=self.session.api_key, + api_user=self.session.api_user, + auto_connect_event_hub=False + ) + project_entities = session.query( + "select id, full_name from Project" + ).all() + project_names_by_id = { + project_entity["id"]: project_entity["full_name"] + for project_entity in project_entities + } + + action_settings_by_project_id = self._get_action_settings( + project_names_by_id + ) + enabled_action_settings_by_project_id = {} + for item in action_settings_by_project_id.items(): + project_id, action_settings = item + if action_settings.get("cycle_enabled"): + enabled_action_settings_by_project_id[project_id] = ( + action_settings + ) + + if not enabled_action_settings_by_project_id: + self.log.info(( + "There are no projects that have enabled" + " cycle review sesison creation" + )) + + else: + self._process_review_session( + session, + enabled_action_settings_by_project_id, + project_names_by_id + ) + + session.close() + + def _process_review_session( + self, session, settings_by_project_id, project_names_by_id + ): + review_sessions = session.query(( + "select id, name, project_id" + " from ReviewSession where project_id in ({})" + ).format(self.join_query_keys(settings_by_project_id))).all() + + review_sessions_by_project_id = collections.defaultdict(list) + for review_session in review_sessions: + project_id = review_session["project_id"] + review_sessions_by_project_id[project_id].append(review_session) + + # Prepare fill data for today's review sesison and yesterdays + now = datetime.datetime.now() + today_obj = datetime.datetime( + now.year, now.month, now.day, 0, 0, 0 + ) + yesterday_obj = today_obj - self._day_delta + + today_fill_data = get_datetime_data(today_obj) + yesterday_fill_data = get_datetime_data(yesterday_obj) + + # Loop through projects and try to create daily reviews + for project_id, action_settings in settings_by_project_id.items(): + review_session_template = ( + action_settings["review_session_template"] + ).strip() or self.default_template + + today_project_fill_data = copy.deepcopy(today_fill_data) + yesterday_project_fill_data = copy.deepcopy(yesterday_fill_data) + project_name = project_names_by_id[project_id] + today_project_fill_data["project_name"] = project_name + yesterday_project_fill_data["project_name"] = project_name + + today_session_name = self._fill_review_template( + review_session_template, today_project_fill_data + ) + yesterday_session_name = self._fill_review_template( + review_session_template, yesterday_project_fill_data + ) + # Skip if today's session name could not be filled + if not today_session_name: + continue + + # Find matchin review session + project_review_sessions = review_sessions_by_project_id[project_id] + todays_session = None + yesterdays_session = None + for review_session in project_review_sessions: + session_name = review_session["name"] + if session_name == today_session_name: + todays_session = review_session + break + elif session_name == yesterday_session_name: + yesterdays_session = review_session + + # Skip if today's session already exist + if todays_session is not None: + self.log.debug(( + "Todays ReviewSession \"{}\"" + " in project \"{}\" already exists" + ).format(today_session_name, project_name)) + continue + + # Check if there is yesterday's session and is empty + # - in that case just rename it + if ( + yesterdays_session is not None + and len(yesterdays_session["review_session_objects"]) == 0 + ): + self.log.debug(( + "Renaming yesterdays empty review session \"{}\" to \"{}\"" + " in project \"{}\"" + ).format( + yesterday_session_name, today_session_name, project_name + )) + yesterdays_session["name"] = today_session_name + session.commit() + continue + + # Create new review session with new name + self.log.debug(( + "Creating new review session \"{}\" in project \"{}\"" + ).format(today_session_name, project_name)) + session.create("ReviewSession", { + "project_id": project_id, + "name": today_session_name + }) + session.commit() + + def _get_action_settings(self, project_names_by_id): + settings_by_project_id = {} + for project_id, project_name in project_names_by_id.items(): + project_settings = get_project_settings(project_name) + action_settings = self._extract_action_settings(project_settings) + settings_by_project_id[project_id] = action_settings + return settings_by_project_id + + def _extract_action_settings(self, project_settings): + return ( + project_settings + .get("ftrack", {}) + .get(self.settings_frack_subkey, {}) + .get(self.settings_key) + ) or {} + + def _fill_review_template(self, template, data): + output = None + try: + output = template.format(**data) + except Exception: + self.log.warning( + ( + "Failed to fill review session template {} with data {}" + ).format(template, data), + exc_info=True + ) + return output + + +def register(session): + '''Register plugin. Called when used as an plugin.''' + CreateDailyReviewSessionServerAction(session).register() From 08d1f97f1112e7efc0190f146139c366d8f84de8 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Thu, 9 Jun 2022 18:17:54 +0200 Subject: [PATCH 32/51] added settings for new action --- .../defaults/project_settings/ftrack.json | 9 +++++ .../schema_project_ftrack.json | 38 +++++++++++++++++++ 2 files changed, 47 insertions(+) diff --git a/openpype/settings/defaults/project_settings/ftrack.json b/openpype/settings/defaults/project_settings/ftrack.json index 9d59deea3d..831c34835e 100644 --- a/openpype/settings/defaults/project_settings/ftrack.json +++ b/openpype/settings/defaults/project_settings/ftrack.json @@ -116,6 +116,15 @@ "Administrator", "Project manager" ] + }, + "create_daily_review_session": { + "enabled": true, + "role_list": [ + "Administrator", + "Project Manager" + ], + "cycle_enabled": false, + "review_session_template": "{yy}{mm}{dd}" } }, "user_handlers": { diff --git a/openpype/settings/entities/schemas/projects_schema/schema_project_ftrack.json b/openpype/settings/entities/schemas/projects_schema/schema_project_ftrack.json index 16cab49d5d..f8f9d5093d 100644 --- a/openpype/settings/entities/schemas/projects_schema/schema_project_ftrack.json +++ b/openpype/settings/entities/schemas/projects_schema/schema_project_ftrack.json @@ -388,6 +388,44 @@ "object_type": "text" } ] + }, + { + "key": "create_daily_review_session", + "label": "Create daily review session", + "type": "dict", + "is_group": true, + "checkbox_key": "enabled", + "children": [ + { + "type": "boolean", + "key": "enabled" + }, + { + "type": "list", + "key": "role_list", + "label": "Roles", + "object_type": "text", + "use_label_wrap": true + }, + { + "type": "boolean", + "key": "cycle_enabled", + "label": "Create daily review session" + }, + { + "type": "separator" + }, + { + "type": "text", + "key": "review_session_template", + "label": "ReviewSession template", + "placeholder": "Default: {yy}{mm}{dd}" + }, + { + "type": "label", + "label": "Possible formatting keys in template:
- \"project_name\" - <Name of project>
- \"d\" - <Day of month number> in shortest possible way.
- \"dd\" - <Day of month number> with 2 digits.
- \"ddd\" - <Week day name> shortened week day. e.g.: `Mon`, ...
- \"dddd\" - <Week day name> full name of week day. e.g.: `Monday`, ...
- \"m\" - <Month number> in shortest possible way. e.g.: `1` if January
- \"mm\" - <Month number> with 2 digits.
- \"mmm\" - <Month name> shortened month name. e.g.: `Jan`, ...
- \"mmmm\" -<Month name> full month name. e.g.: `January`, ...
- \"yy\" - <Year number> shortened year. e.g.: `19`, `20`, ...
- \"yyyy\" - <Year number> full year. e.g.: `2019`, `2020`, ..." + } + ] } ] }, From 5999693fdf748025aa79cf9cf232d77406c4c9a6 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Thu, 9 Jun 2022 18:27:00 +0200 Subject: [PATCH 33/51] fix too long lines --- .../event_handlers_server/action_create_review_session.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/openpype/modules/ftrack/event_handlers_server/action_create_review_session.py b/openpype/modules/ftrack/event_handlers_server/action_create_review_session.py index 3b7cb224f0..8a1d898193 100644 --- a/openpype/modules/ftrack/event_handlers_server/action_create_review_session.py +++ b/openpype/modules/ftrack/event_handlers_server/action_create_review_session.py @@ -37,7 +37,9 @@ class CreateDailyReviewSessionServerAction(ServerAction): default_template = "{yy}{mm}{dd}" def __init__(self, *args, **kwargs): - super(CreateDailyReviewSessionServerAction, self).__init__(*args, **kwargs) + super(CreateDailyReviewSessionServerAction, self).__init__( + *args, **kwargs + ) self._cycle_timer = None self._last_cyle_time = None @@ -82,7 +84,9 @@ class CreateDailyReviewSessionServerAction(ServerAction): def register(self, *args, **kwargs): """Override register to be able trigger """ # Register server action as would be normally - super(CreateDailyReviewSessionServerAction, self).register(*args, **kwargs) + super(CreateDailyReviewSessionServerAction, self).register( + *args, **kwargs + ) # Create threading timer which will trigger creation of report # at the 00:00:01 of next day From 51fb0385f81aefe0ebc71a4f8256b19976e8fce0 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Fri, 10 Jun 2022 12:16:31 +0200 Subject: [PATCH 34/51] mapped hosts queries --- openpype/client/entities.py | 294 +++++++++++++++++++++++++++++++++++- 1 file changed, 293 insertions(+), 1 deletion(-) diff --git a/openpype/client/entities.py b/openpype/client/entities.py index 91646e7a1d..861ac300e5 100644 --- a/openpype/client/entities.py +++ b/openpype/client/entities.py @@ -1033,10 +1033,302 @@ def get_thumbnail(project_name, thumbnail_id, fields=None): """ -Custom data storage: +## Custom data storage: - Webpublisher - jobs - Ftrack - events +- Maya - Shaders + - openpype/hosts/maya/api/shader_definition_editor.py + - openpype/hosts/maya/plugins/publish/validate_model_name.py +## Global launch hooks +- openpype/hooks/pre_global_host_data.py + Query: + - project + - asset + +## Hosts +### Aftereffects +- openpype/hosts/aftereffects/plugins/create/workfile_creator.py + Query: + - asset + +### Blender +- openpype/hosts/blender/api/pipeline.py + Query: + - asset +- openpype/hosts/blender/plugins/publish/extract_layout.py + Query: + - representation + +### Celaction +- openpype/hosts/celaction/plugins/publish/collect_audio.py + Query: + - subsets + - last versions + - representations + +### Fusion +- openpype/hosts/fusion/api/lib.py + Query: + - asset + - subset + - version + - representation +- openpype/hosts/fusion/plugins/load/load_sequence.py + Query: + - version +- openpype/hosts/fusion/scripts/fusion_switch_shot.py + Query: + - project + - asset + - versions +- openpype/hosts/fusion/utility_scripts/switch_ui.py + Query: + - assets + +### Harmony +- openpype/hosts/harmony/api/pipeline.py + Query: + - representation + +### Hiero +- openpype/hosts/hiero/api/lib.py + Query: + - project + - version + - versions + - representation +- openpype/hosts/hiero/api/tags.py + Query: + - task types + - assets +- openpype/hosts/hiero/plugins/load/load_clip.py + Query: + - version + - versions +- openpype/hosts/hiero/plugins/publish_old_workflow/collect_assetbuilds.py + Query: + - assets + +### Houdini +- openpype/hosts/houdini/api/lib.py + Query: + - asset +- openpype/hosts/houdini/api/usd.py + Query: + - asset +- openpype/hosts/houdini/plugins/create/create_hda.py + Query: + - asset + - subsets +- openpype/hosts/houdini/plugins/publish/collect_usd_bootstrap.py + Query: + - asset + - subset +- openpype/hosts/houdini/plugins/publish/extract_usd_layered.py + Query: + - asset + - subset + - version + - representation +- openpype/hosts/houdini/plugins/publish/validate_usd_shade_model_exists.py + Query: + - asset + - subset +- openpype/hosts/houdini/vendor/husdoutputprocessors/avalon_uri_processor.py + Query: + - project + - asset + +### Maya +- openpype/hosts/maya/api/action.py + Query: + - asset +- openpype/hosts/maya/api/commands.py + Query: + - asset + - project +- openpype/hosts/maya/api/lib.py + Query: + - project + - asset + - subset + - subsets + - version + - representation +- openpype/hosts/maya/api/setdress.py + Query: + - version + - representation +- openpype/hosts/maya/plugins/inventory/import_modelrender.py + Query: + - representation +- openpype/hosts/maya/plugins/load/load_audio.py + Query: + - asset + - subset + - version +- openpype/hosts/maya/plugins/load/load_image_plane.py + Query: + - asset + - subset + - version +- openpype/hosts/maya/plugins/load/load_look.py + Query: + - representation +- openpype/hosts/maya/plugins/load/load_vrayproxy.py + Query: + - representation +- openpype/hosts/maya/plugins/load/load_yeti_cache.py + Query: + - representation +- openpype/hosts/maya/plugins/publish/collect_review.py + Query: + - subsets +- openpype/hosts/maya/plugins/publish/validate_node_ids_in_database.py + Query: + - assets +- openpype/hosts/maya/plugins/publish/validate_node_ids_related.py + Query: + - asset +- openpype/hosts/maya/plugins/publish/validate_renderlayer_aovs.py + Query: + - asset + - subset + +### Nuke +- openpype/hosts/nuke/api/command.py + Query: + - project + - asset +- openpype/hosts/nuke/api/lib.py + Query: + - project + - asset + - version + - versions + - representation +- openpype/hosts/nuke/plugins/load/load_backdrop.py + Query: + - version + - versions +- openpype/hosts/nuke/plugins/load/load_camera_abc.py + Query: + - version + - versions +- openpype/hosts/nuke/plugins/load/load_clip.py + Query: + - version + - versions +- openpype/hosts/nuke/plugins/load/load_effects_ip.py + Query: + - version + - versions +- openpype/hosts/nuke/plugins/load/load_effects.py + Query: + - version + - versions +- openpype/hosts/nuke/plugins/load/load_gizmo_ip.py + Query: + - version + - versions +- openpype/hosts/nuke/plugins/load/load_gizmo.py + Query: + - version + - versions +- openpype/hosts/nuke/plugins/load/load_image.py + Query: + - version + - versions +- openpype/hosts/nuke/plugins/load/load_model.py + Query: + - version + - versions +- openpype/hosts/nuke/plugins/load/load_script_precomp.py + Query: + - version + - versions +- openpype/hosts/nuke/plugins/publish/collect_reads.py + Query: + - asset +- openpype/hosts/nuke/plugins/publish/precollect_instances.py + Query: + - asset +- openpype/hosts/nuke/plugins/publish/precollect_writes.py + Query: + - representation +- openpype/hosts/nuke/plugins/publish/validate_script.py + Query: + - asset + - project + +### Photoshop +- openpype/hosts/photoshop/plugins/create/workfile_creator.py + Query: + - asset + +### Resolve +- openpype/hosts/resolve/plugins/load/load_clip.py + Query: + - version + - versions + +### Standalone publisher +- openpype/hosts/standalonepublisher/plugins/publish/collect_bulk_mov_instances.py + Query: + - asset +- openpype/hosts/standalonepublisher/plugins/publish/collect_matching_asset.py + Query: + - assets +- openpype/hosts/standalonepublisher/plugins/publish/collect_hierarchy.py + Query: + - project + - asset +- openpype/hosts/standalonepublisher/plugins/publish/validate_task_existence.py + Query: + - assets + +### TVPaint +- openpype/hosts/tvpaint/api/pipeline.py + Query: + - project + - asset +- openpype/hosts/tvpaint/plugins/load/load_workfile.py + Query: + - project + - asset +- openpype/hosts/tvpaint/plugins/publish/collect_instances.py + Query: + - asset +- openpype/hosts/tvpaint/plugins/publish/collect_scene_render.py + Query: + - asset +- openpype/hosts/tvpaint/plugins/publish/collect_workfile.py + Query: + - asset + +### Unreal +- openpype/hosts/unreal/plugins/load/load_camera.py + Query: + - asset + - assets +- openpype/hosts/unreal/plugins/load/load_layout.py + Query: + - asset + - assets +- openpype/hosts/unreal/plugins/publish/extract_layout.py + Query: + - representation + +### Webpublisher +- openpype/hosts/webpublisher/webserver_service/webpublish_routes.py + Query: + - assets +- openpype/hosts/webpublisher/plugins/publish/collect_published_files.py + Query: + - last versions + +## Tools openpype/tools/assetlinks/widgets.py - SimpleLinkView Query: From f782cf4f6df2bd1762c662a77d81dd33e54239dc Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Fri, 10 Jun 2022 14:53:40 +0200 Subject: [PATCH 35/51] hiero: otio p3 compatibility issue - metadata on effect use update rather then __setter__ --- openpype/hosts/hiero/api/otio/hiero_export.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openpype/hosts/hiero/api/otio/hiero_export.py b/openpype/hosts/hiero/api/otio/hiero_export.py index 1e4088d9c0..81cb43fa12 100644 --- a/openpype/hosts/hiero/api/otio/hiero_export.py +++ b/openpype/hosts/hiero/api/otio/hiero_export.py @@ -132,7 +132,7 @@ def create_time_effects(otio_clip, track_item): otio_effect = otio.schema.TimeEffect() otio_effect.name = name otio_effect.effect_name = effect_name - otio_effect.metadata = metadata + otio_effect.metadata.update(metadata) # add otio effect to clip effects otio_clip.effects.append(otio_effect) From b227ba58568a3521ead74da5f2135b6e7645b132 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Fri, 10 Jun 2022 14:54:48 +0200 Subject: [PATCH 36/51] removed debug log --- .../event_handlers_server/action_create_review_session.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/openpype/modules/ftrack/event_handlers_server/action_create_review_session.py b/openpype/modules/ftrack/event_handlers_server/action_create_review_session.py index 8a1d898193..8a8e86e7b9 100644 --- a/openpype/modules/ftrack/event_handlers_server/action_create_review_session.py +++ b/openpype/modules/ftrack/event_handlers_server/action_create_review_session.py @@ -56,8 +56,6 @@ class CreateDailyReviewSessionServerAction(ServerAction): ): valid_selection = True break - else: - self.log.info(ent["entityType"]) if not valid_selection: return False From 0ea1032a6114f80cee0a87c1d5736ebe293cefd9 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Fri, 10 Jun 2022 15:06:30 +0200 Subject: [PATCH 37/51] added global plugins --- openpype/client/entities.py | 48 +++++++++++++++++++++++++++++++++++++ 1 file changed, 48 insertions(+) diff --git a/openpype/client/entities.py b/openpype/client/entities.py index 861ac300e5..b61577ac70 100644 --- a/openpype/client/entities.py +++ b/openpype/client/entities.py @@ -1046,6 +1046,54 @@ def get_thumbnail(project_name, thumbnail_id, fields=None): - project - asset +## Global load plugins +- openpype/plugins/load/delete_old_versions.py + Query: + - versions + - representations +- openpype/plugins/load/delivery.py + Query: + - representations + +## Global publish plugins +- openpype/plugins/publish/collect_avalon_entities.py + Query: + - asset + - project +- openpype/plugins/publish/collect_anatomy_instance_data.py + Query: + - assets + - subsets +- openpype/plugins/publish/collect_scene_loaded_versions.py + Query: + - representations +- openpype/plugins/publish/extract_hierarchy_avalon.py + Query: + - asset + - assets + - project + Create: + - asset + Update: + - asset +- openpype/plugins/publish/integrate_hero_version.py + Query: + - version + - hero version + - representations +- openpype/plugins/publish/integrate_new.py + Query: + - asset + - subset + - version + - representations +- openpype/plugins/publish/integrate_thumbnail.py + Query: + - version +- openpype/plugins/publish/validate_editorial_asset_name.py + Query: + - assets + ## Hosts ### Aftereffects - openpype/hosts/aftereffects/plugins/create/workfile_creator.py From 22372361eb29d243ba16df4e83e827d1c8d54f62 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Fri, 10 Jun 2022 15:12:50 +0200 Subject: [PATCH 38/51] added pipeline queries --- openpype/client/entities.py | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/openpype/client/entities.py b/openpype/client/entities.py index b61577ac70..82f16ff032 100644 --- a/openpype/client/entities.py +++ b/openpype/client/entities.py @@ -1064,6 +1064,7 @@ def get_thumbnail(project_name, thumbnail_id, fields=None): Query: - assets - subsets + - last version - openpype/plugins/publish/collect_scene_loaded_versions.py Query: - representations @@ -1094,6 +1095,23 @@ def get_thumbnail(project_name, thumbnail_id, fields=None): Query: - assets +## Pipeline +- openpype/pipeline/load/utils.py + Query: + - project + - assets + - subsets + - version + - versions + - representation + - representations +- openpype/pipeline/mongodb.py + Query: + - project +- openpype/pipeline/thumbnail.py + Query: + - project + ## Hosts ### Aftereffects - openpype/hosts/aftereffects/plugins/create/workfile_creator.py From 956ad8299b652b38e77215b545454ddba3715d87 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Fri, 10 Jun 2022 15:19:18 +0200 Subject: [PATCH 39/51] mapped lib --- openpype/client/entities.py | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/openpype/client/entities.py b/openpype/client/entities.py index 82f16ff032..82caaee7c5 100644 --- a/openpype/client/entities.py +++ b/openpype/client/entities.py @@ -1095,6 +1095,39 @@ def get_thumbnail(project_name, thumbnail_id, fields=None): Query: - assets +## Lib +- openpype/lib/applications.py + Query: + - project + - asset +- openpype/lib/avalon_context.py + Query: + - project + - asset + - linked assets (new function get_linked_assets?) + - subset + - subsets + - version + - versions + - last version + - representations + - linked representations (new function get_linked_ids_for_representations) + Update: + - workfile data +- openpype/lib/plugin_tools.py + Query: + - asset +- openpype/lib/project_backpack.py + Query: + - project + - everything from mongo + Update: + - project +- openpype/lib/usdlib.py + Query: + - project + - asset + ## Pipeline - openpype/pipeline/load/utils.py Query: From 8a21439b99e6d43426cad8c47d29631b73e5706c Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Fri, 10 Jun 2022 16:37:52 +0200 Subject: [PATCH 40/51] aded ability to parse value from clipboard that does not come from settings ui --- openpype/tools/settings/settings/base.py | 96 ++++++++++++++++++------ 1 file changed, 71 insertions(+), 25 deletions(-) diff --git a/openpype/tools/settings/settings/base.py b/openpype/tools/settings/settings/base.py index 44ec09b2ca..64e0a95fee 100644 --- a/openpype/tools/settings/settings/base.py +++ b/openpype/tools/settings/settings/base.py @@ -1,3 +1,4 @@ +import os import sys import json import traceback @@ -190,24 +191,29 @@ class BaseWidget(QtWidgets.QWidget): actions_mapping[action] = remove_from_project_override menu.addAction(action) + def _get_mime_data_from_entity(self): + if self.entity.is_dynamic_item or self.entity.is_in_dynamic_item: + entity_path = None + else: + entity_path = "/".join( + [self.entity.root_key, self.entity.path] + ) + + value = self.entity.value + + # Copy for settings tool + return { + "value": value, + "__root_key__": self.entity.root_key, + "__settings_path__": entity_path + } + def _copy_value_actions(self, menu): def copy_value(): mime_data = QtCore.QMimeData() - if self.entity.is_dynamic_item or self.entity.is_in_dynamic_item: - entity_path = None - else: - entity_path = "/".join( - [self.entity.root_key, self.entity.path] - ) - - value = self.entity.value # Copy for settings tool - settings_data = { - "root_key": self.entity.root_key, - "value": value, - "path": entity_path - } + settings_data = self._get_mime_data_from_entity() settings_encoded_data = QtCore.QByteArray() settings_stream = QtCore.QDataStream( settings_encoded_data, QtCore.QIODevice.WriteOnly @@ -218,6 +224,7 @@ class BaseWidget(QtWidgets.QWidget): ) # Copy as json + value = settings_data["value"] json_encoded_data = None if isinstance(value, (dict, list)): json_encoded_data = QtCore.QByteArray() @@ -241,25 +248,64 @@ class BaseWidget(QtWidgets.QWidget): action = QtWidgets.QAction("Copy", menu) return [(action, copy_value)] + def _parse_source_data_for_paste(self, data): + settings_path = None + root_key = None + if isinstance(data, dict): + settings_path = data.pop("__settings_path__", settings_path) + root_key = data.pop("__root_key__", root_key) + data = data.pop("__value__", data) + + return { + "value": data, + "__settings_path__": settings_path, + "__root_key__": root_key + } + + def _get_value_from_clipboard(self): + clipboard = QtWidgets.QApplication.clipboard() + mime_data = clipboard.mimeData() + app_value = mime_data.data("application/copy_settings_value") + if app_value: + settings_stream = QtCore.QDataStream( + app_value, QtCore.QIODevice.ReadOnly + ) + mime_data_value_str = settings_stream.readQString() + return json.loads(mime_data_value_str) + + if mime_data.hasUrls(): + for url in mime_data.urls(): + local_file = url.toLocalFile() + try: + with open(local_file, "r") as stream: + value = json.load(stream) + except Exception: + continue + if value: + return self._parse_source_data_for_paste(value) + + if mime_data.hasText(): + text = mime_data.text() + try: + value = json.loads(text) + except Exception: + try: + value = self.entity.convert_to_valid_type(text) + except Exception: + return None + return self._parse_source_data_for_paste(value) + def _paste_value_actions(self, menu): output = [] # Allow paste of value only if were copied from this UI - clipboard = QtWidgets.QApplication.clipboard() - mime_data = clipboard.mimeData() - mime_value = mime_data.data("application/copy_settings_value") + mime_data_value = self._get_value_from_clipboard() # Skip if there is nothing to do - if not mime_value: + if not mime_data_value: return output - settings_stream = QtCore.QDataStream( - mime_value, QtCore.QIODevice.ReadOnly - ) - mime_data_value_str = settings_stream.readQString() - mime_data_value = json.loads(mime_data_value_str) - value = mime_data_value["value"] - path = mime_data_value["path"] - root_key = mime_data_value["root_key"] + path = mime_data_value["__settings_path__"] + root_key = mime_data_value["__root_key__"] # Try to find matching entity to be able paste values to same spot # - entity can't by dynamic or in dynamic item From 99bb5250711259a1103bd21bf750a4e332aba8bc Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Fri, 10 Jun 2022 17:38:14 +0200 Subject: [PATCH 41/51] nuke: anatomy compatibility hack --- openpype/hosts/nuke/api/lib.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/openpype/hosts/nuke/api/lib.py b/openpype/hosts/nuke/api/lib.py index 2c5989309b..505eb19419 100644 --- a/openpype/hosts/nuke/api/lib.py +++ b/openpype/hosts/nuke/api/lib.py @@ -539,7 +539,9 @@ def get_created_node_imageio_setting_legacy(nodeclass, creator, subset): imageio_nodes = get_nuke_imageio_settings()["nodes"] required_nodes = imageio_nodes["requiredNodes"] - override_nodes = imageio_nodes["overrideNodes"] + + # HACK: for backward compatibility this needs to be optional + override_nodes = imageio_nodes.get("overrideNodes", []) imageio_node = None for node in required_nodes: From 3b97531c15e17d5f4a95842dd07879772ecd2489 Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Fri, 10 Jun 2022 17:38:51 +0200 Subject: [PATCH 42/51] Nuke: do not offer still creator plugin if anatomy is missing its preset --- .../nuke/plugins/create/create_write_still.py | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/openpype/hosts/nuke/plugins/create/create_write_still.py b/openpype/hosts/nuke/plugins/create/create_write_still.py index 4007ccf51e..3fbe288c78 100644 --- a/openpype/hosts/nuke/plugins/create/create_write_still.py +++ b/openpype/hosts/nuke/plugins/create/create_write_still.py @@ -2,7 +2,19 @@ import nuke from openpype.hosts.nuke.api import plugin from openpype.hosts.nuke.api.lib import ( - create_write_node, create_write_node_legacy) + create_write_node, + create_write_node_legacy, + get_created_node_imageio_setting_legacy +) + +# HACK: just to disable still image on projects which +# are not having anatomy imageio preset for CreateWriteStill +imageio_writes = get_created_node_imageio_setting_legacy( + "Write", + "CreateWriteStill", + "stillMain" +) +print(imageio_writes["knobs"]) class CreateWriteStill(plugin.AbstractWriteRender): From c77f9ce2e52c6f97cb9b4e8cb86ebcfeb5a11153 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Fri, 10 Jun 2022 17:51:55 +0200 Subject: [PATCH 43/51] added option to extract settings to file --- openpype/tools/settings/settings/base.py | 102 +++++++++++++++++++---- 1 file changed, 86 insertions(+), 16 deletions(-) diff --git a/openpype/tools/settings/settings/base.py b/openpype/tools/settings/settings/base.py index 64e0a95fee..3ade5e5ef8 100644 --- a/openpype/tools/settings/settings/base.py +++ b/openpype/tools/settings/settings/base.py @@ -3,6 +3,7 @@ import sys import json import traceback import functools +import datetime from Qt import QtWidgets, QtGui, QtCore @@ -13,10 +14,26 @@ from .widgets import ExpandingWidget from .lib import create_deffered_value_change_timer from .constants import DEFAULT_PROJECT_LABEL +_MENU_SEPARATOR_REQ = object() +SETTINGS_PATH_KEY = "__settings_path__" +ROOT_KEY = "__root_key__" +VALUE_KEY = "__value__" +SAVE_TIME_KEY = "__extracted__" +PROJECT_NAME_KEY = "__project_name__" + class BaseWidget(QtWidgets.QWidget): + _last_save_dir = os.path.expanduser("~") allow_actions = True + @staticmethod + def get_last_save_dir(): + return BaseWidget._last_save_dir + + @staticmethod + def set_last_save_dir(save_dir): + BaseWidget._last_save_dir = save_dir + def __init__(self, category_widget, entity, entity_widget): self.category_widget = category_widget self.entity = entity @@ -203,9 +220,9 @@ class BaseWidget(QtWidgets.QWidget): # Copy for settings tool return { - "value": value, - "__root_key__": self.entity.root_key, - "__settings_path__": entity_path + VALUE_KEY: value, + ROOT_KEY: self.entity.root_key, + SETTINGS_PATH_KEY: entity_path } def _copy_value_actions(self, menu): @@ -248,18 +265,62 @@ class BaseWidget(QtWidgets.QWidget): action = QtWidgets.QAction("Copy", menu) return [(action, copy_value)] + def _extract_to_file(self): + dialog = QtWidgets.QFileDialog( + self, + "Save settings values", + self.get_last_save_dir(), + "Values (*.json)" + ) + # dialog.setOption(dialog.DontUseNativeDialog) + dialog.setAcceptMode(dialog.AcceptSave) + if dialog.exec() != dialog.Accepted: + return + + selected_urls = dialog.selectedUrls() + if not selected_urls: + return + + filepath = selected_urls[0].toLocalFile() + if not filepath: + return + + if not filepath.lower().endswith(".json"): + filepath += ".json" + + settings_data = self._get_mime_data_from_entity() + now = datetime.datetime.now() + settings_data[SAVE_TIME_KEY] = now.strftime("%Y-%m-%d %H:%M:%S") + if hasattr(self.category_widget, "project_name"): + settings_data[PROJECT_NAME_KEY] = self.category_widget.project_name + + with open(filepath, "w") as stream: + json.dump(settings_data, stream, indent=4) + + new_dir = os.path.dirname(filepath) + self.set_last_save_dir(new_dir) + + def _extract_value_to_file_actions(self, menu): + save_action = QtWidgets.QAction("Extract to file", menu) + return [ + _MENU_SEPARATOR_REQ, + (save_action, self._extract_to_file) + ] + def _parse_source_data_for_paste(self, data): settings_path = None root_key = None if isinstance(data, dict): - settings_path = data.pop("__settings_path__", settings_path) - root_key = data.pop("__root_key__", root_key) - data = data.pop("__value__", data) + data.pop(SAVE_TIME_KEY, None) + data.pop(PROJECT_NAME_KEY, None) + settings_path = data.pop(SETTINGS_PATH_KEY, settings_path) + root_key = data.pop(ROOT_KEY, root_key) + data = data.pop(VALUE_KEY, data) return { - "value": data, - "__settings_path__": settings_path, - "__root_key__": root_key + VALUE_KEY: data, + SETTINGS_PATH_KEY: settings_path, + ROOT_KEY: root_key } def _get_value_from_clipboard(self): @@ -303,9 +364,9 @@ class BaseWidget(QtWidgets.QWidget): if not mime_data_value: return output - value = mime_data_value["value"] - path = mime_data_value["__settings_path__"] - root_key = mime_data_value["__root_key__"] + value = mime_data_value[VALUE_KEY] + path = mime_data_value[SETTINGS_PATH_KEY] + root_key = mime_data_value[ROOT_KEY] # Try to find matching entity to be able paste values to same spot # - entity can't by dynamic or in dynamic item @@ -437,10 +498,19 @@ class BaseWidget(QtWidgets.QWidget): ui_actions.extend(self._copy_value_actions(menu)) ui_actions.extend(self._paste_value_actions(menu)) if ui_actions: - menu.addSeparator() - for action, callback in ui_actions: - menu.addAction(action) - actions_mapping[action] = callback + ui_actions.insert(0, _MENU_SEPARATOR_REQ) + + ui_actions.extend(self._extract_value_to_file_actions(menu)) + + for item in ui_actions: + if item is _MENU_SEPARATOR_REQ: + if len(menu.actions()) > 0: + menu.addSeparator() + continue + + action, callback = item + menu.addAction(action) + actions_mapping[action] = callback if not actions_mapping: action = QtWidgets.QAction("< No action >") From b2ed0292adac2c914f50e95575b3494f43b33086 Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Fri, 10 Jun 2022 17:53:34 +0200 Subject: [PATCH 44/51] Nuke: adding todo comment --- openpype/hosts/nuke/plugins/create/create_write_still.py | 1 + 1 file changed, 1 insertion(+) diff --git a/openpype/hosts/nuke/plugins/create/create_write_still.py b/openpype/hosts/nuke/plugins/create/create_write_still.py index 3fbe288c78..bb08e8c2c6 100644 --- a/openpype/hosts/nuke/plugins/create/create_write_still.py +++ b/openpype/hosts/nuke/plugins/create/create_write_still.py @@ -9,6 +9,7 @@ from openpype.hosts.nuke.api.lib import ( # HACK: just to disable still image on projects which # are not having anatomy imageio preset for CreateWriteStill +# TODO: remove this code as soon as it will be obsolete imageio_writes = get_created_node_imageio_setting_legacy( "Write", "CreateWriteStill", From e21caf109fc3405afb36e86ab7f9ff9dbb8b0726 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Fri, 10 Jun 2022 18:26:05 +0200 Subject: [PATCH 45/51] extraction can be called on whole settings --- openpype/tools/settings/settings/base.py | 110 +++++++++++------- .../tools/settings/settings/categories.py | 35 +++++- openpype/tools/settings/settings/constants.py | 13 +++ 3 files changed, 112 insertions(+), 46 deletions(-) diff --git a/openpype/tools/settings/settings/base.py b/openpype/tools/settings/settings/base.py index 3ade5e5ef8..aa2b448ccf 100644 --- a/openpype/tools/settings/settings/base.py +++ b/openpype/tools/settings/settings/base.py @@ -12,28 +12,71 @@ from openpype.tools.settings import CHILD_OFFSET from .widgets import ExpandingWidget from .lib import create_deffered_value_change_timer -from .constants import DEFAULT_PROJECT_LABEL +from .constants import ( + DEFAULT_PROJECT_LABEL, + SETTINGS_PATH_KEY, + ROOT_KEY, + VALUE_KEY, + SAVE_TIME_KEY, + PROJECT_NAME_KEY, +) _MENU_SEPARATOR_REQ = object() -SETTINGS_PATH_KEY = "__settings_path__" -ROOT_KEY = "__root_key__" -VALUE_KEY = "__value__" -SAVE_TIME_KEY = "__extracted__" -PROJECT_NAME_KEY = "__project_name__" + + +class ExtractHelper: + _last_save_dir = os.path.expanduser("~") + + @classmethod + def get_last_save_dir(cls): + return cls._last_save_dir + + @classmethod + def set_last_save_dir(cls, save_dir): + cls._last_save_dir = save_dir + + @classmethod + def ask_for_save_filepath(cls, parent): + dialog = QtWidgets.QFileDialog( + parent, + "Save settings values", + cls.get_last_save_dir(), + "Values (*.json)" + ) + # dialog.setOption(dialog.DontUseNativeDialog) + dialog.setAcceptMode(dialog.AcceptSave) + if dialog.exec() != dialog.Accepted: + return + + selected_urls = dialog.selectedUrls() + if not selected_urls: + return + + filepath = selected_urls[0].toLocalFile() + if not filepath: + return + + if not filepath.lower().endswith(".json"): + filepath += ".json" + return filepath + + @classmethod + def extract_settings_to_json(cls, filepath, settings_data, project_name): + now = datetime.datetime.now() + settings_data[SAVE_TIME_KEY] = now.strftime("%Y-%m-%d %H:%M:%S") + if project_name != 0: + settings_data[PROJECT_NAME_KEY] = project_name + + with open(filepath, "w") as stream: + json.dump(settings_data, stream, indent=4) + + new_dir = os.path.dirname(filepath) + cls.set_last_save_dir(new_dir) class BaseWidget(QtWidgets.QWidget): - _last_save_dir = os.path.expanduser("~") allow_actions = True - @staticmethod - def get_last_save_dir(): - return BaseWidget._last_save_dir - - @staticmethod - def set_last_save_dir(save_dir): - BaseWidget._last_save_dir = save_dir - def __init__(self, category_widget, entity, entity_widget): self.category_widget = category_widget self.entity = entity @@ -266,45 +309,24 @@ class BaseWidget(QtWidgets.QWidget): return [(action, copy_value)] def _extract_to_file(self): - dialog = QtWidgets.QFileDialog( - self, - "Save settings values", - self.get_last_save_dir(), - "Values (*.json)" - ) - # dialog.setOption(dialog.DontUseNativeDialog) - dialog.setAcceptMode(dialog.AcceptSave) - if dialog.exec() != dialog.Accepted: - return - - selected_urls = dialog.selectedUrls() - if not selected_urls: - return - - filepath = selected_urls[0].toLocalFile() + filepath = ExtractHelper.ask_for_save_filepath(self) if not filepath: return - if not filepath.lower().endswith(".json"): - filepath += ".json" - settings_data = self._get_mime_data_from_entity() - now = datetime.datetime.now() - settings_data[SAVE_TIME_KEY] = now.strftime("%Y-%m-%d %H:%M:%S") + project_name = 0 if hasattr(self.category_widget, "project_name"): - settings_data[PROJECT_NAME_KEY] = self.category_widget.project_name + project_name = self.category_widget.project_name - with open(filepath, "w") as stream: - json.dump(settings_data, stream, indent=4) - - new_dir = os.path.dirname(filepath) - self.set_last_save_dir(new_dir) + ExtractHelper.extract_settings_to_json( + filepath, settings_data, project_name + ) def _extract_value_to_file_actions(self, menu): - save_action = QtWidgets.QAction("Extract to file", menu) + extract_action = QtWidgets.QAction("Extract to file", menu) return [ _MENU_SEPARATOR_REQ, - (save_action, self._extract_to_file) + (extract_action, self._extract_to_file) ] def _parse_source_data_for_paste(self, data): diff --git a/openpype/tools/settings/settings/categories.py b/openpype/tools/settings/settings/categories.py index c8ade5fcdb..764f42f1a3 100644 --- a/openpype/tools/settings/settings/categories.py +++ b/openpype/tools/settings/settings/categories.py @@ -45,8 +45,15 @@ from .breadcrumbs_widget import ( SystemSettingsBreadcrumbs, ProjectSettingsBreadcrumbs ) - -from .base import GUIWidget +from .constants import ( + SETTINGS_PATH_KEY, + ROOT_KEY, + VALUE_KEY, +) +from .base import ( + ExtractHelper, + GUIWidget, +) from .list_item_widget import ListWidget from .list_strict_widget import ListStrictWidget from .dict_mutable_widget import DictMutableKeysWidget @@ -627,11 +634,35 @@ class SettingsCategoryWidget(QtWidgets.QWidget): self._on_context_version_trigger ) submenu.addAction(action) + menu.addMenu(submenu) + extract_action = QtWidgets.QAction("Extract to file", menu) + extract_action.triggered.connect(self._on_extract_to_file) + + menu.addAction(extract_action) + def _on_context_version_trigger(self, version): self._on_source_version_change(version) + def _on_extract_to_file(self): + filepath = ExtractHelper.ask_for_save_filepath(self) + if not filepath: + return + + settings_data = { + SETTINGS_PATH_KEY: self.entity.root_key, + ROOT_KEY: self.entity.root_key, + VALUE_KEY: self.entity.value + } + project_name = 0 + if hasattr(self, "project_name"): + project_name = self.project_name + + ExtractHelper.extract_settings_to_json( + filepath, settings_data, project_name + ) + def _on_reset_crash(self): self.save_btn.setEnabled(False) diff --git a/openpype/tools/settings/settings/constants.py b/openpype/tools/settings/settings/constants.py index 9d6d7904d7..d98d18c8bf 100644 --- a/openpype/tools/settings/settings/constants.py +++ b/openpype/tools/settings/settings/constants.py @@ -7,6 +7,12 @@ PROJECT_IS_ACTIVE_ROLE = QtCore.Qt.UserRole + 2 PROJECT_IS_SELECTED_ROLE = QtCore.Qt.UserRole + 3 PROJECT_VERSION_ROLE = QtCore.Qt.UserRole + 4 +# Save/Extract keys +SETTINGS_PATH_KEY = "__settings_path__" +ROOT_KEY = "__root_key__" +VALUE_KEY = "__value__" +SAVE_TIME_KEY = "__extracted__" +PROJECT_NAME_KEY = "__project_name__" __all__ = ( "DEFAULT_PROJECT_LABEL", @@ -15,4 +21,11 @@ __all__ = ( "PROJECT_IS_ACTIVE_ROLE", "PROJECT_IS_SELECTED_ROLE", "PROJECT_VERSION_ROLE", + + "SETTINGS_PATH_KEY", + "ROOT_KEY", + "SETTINGS_PATH_KEY", + "VALUE_KEY", + "SAVE_TIME_KEY", + "PROJECT_NAME_KEY", ) From d2de1539136ad7d4da5902d78aed56dd204f4a50 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Fri, 10 Jun 2022 18:47:42 +0200 Subject: [PATCH 46/51] fix copy value to string --- openpype/tools/settings/settings/base.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openpype/tools/settings/settings/base.py b/openpype/tools/settings/settings/base.py index aa2b448ccf..6def284a83 100644 --- a/openpype/tools/settings/settings/base.py +++ b/openpype/tools/settings/settings/base.py @@ -284,7 +284,7 @@ class BaseWidget(QtWidgets.QWidget): ) # Copy as json - value = settings_data["value"] + value = settings_data[VALUE_KEY] json_encoded_data = None if isinstance(value, (dict, list)): json_encoded_data = QtCore.QByteArray() From 27b8f77855d45446af3b7b742a58d30c2143d32f Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Mon, 13 Jun 2022 16:06:21 +0200 Subject: [PATCH 47/51] fix scene inventory --- openpype/tools/sceneinventory/model.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openpype/tools/sceneinventory/model.py b/openpype/tools/sceneinventory/model.py index 117bdfcba1..0bb9c4a658 100644 --- a/openpype/tools/sceneinventory/model.py +++ b/openpype/tools/sceneinventory/model.py @@ -198,12 +198,12 @@ class InventoryModel(TreeModel): self.clear() if self._hierarchy_view and selected: - if not hasattr(host.pipeline, "update_hierarchy"): # If host doesn't support hierarchical containers, then # cherry-pick only. self.add_items((item for item in items if item["objectName"] in selected)) + return # Update hierarchy info for all containers items_by_name = {item["objectName"]: item From cd6c37ece91caf7af0d40882b1a41f23190ebb38 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Mon, 13 Jun 2022 16:06:57 +0200 Subject: [PATCH 48/51] added remaining custom mongo calls --- openpype/client/entities.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/openpype/client/entities.py b/openpype/client/entities.py index 82caaee7c5..a56288c1e8 100644 --- a/openpype/client/entities.py +++ b/openpype/client/entities.py @@ -1034,6 +1034,8 @@ def get_thumbnail(project_name, thumbnail_id, fields=None): """ ## Custom data storage: +- Settings - OP settings overrides and local settings +- Logging - logs from PypeLogger - Webpublisher - jobs - Ftrack - events - Maya - Shaders From 010ee05eff630531a77964c7bfe39460cee9421c Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Mon, 13 Jun 2022 17:26:46 +0200 Subject: [PATCH 49/51] use query functions in blender --- openpype/hosts/blender/api/pipeline.py | 7 ++-- .../blender/plugins/publish/extract_layout.py | 39 +++++++------------ 2 files changed, 16 insertions(+), 30 deletions(-) diff --git a/openpype/hosts/blender/api/pipeline.py b/openpype/hosts/blender/api/pipeline.py index 5b81764644..93d81145bc 100644 --- a/openpype/hosts/blender/api/pipeline.py +++ b/openpype/hosts/blender/api/pipeline.py @@ -10,6 +10,7 @@ from . import ops import pyblish.api +from openpype.client import get_asset_by_name from openpype.pipeline import ( schema, legacy_io, @@ -83,11 +84,9 @@ def uninstall(): def set_start_end_frames(): + project_name = legacy_io.active_project() asset_name = legacy_io.Session["AVALON_ASSET"] - asset_doc = legacy_io.find_one({ - "type": "asset", - "name": asset_name - }) + asset_doc = get_asset_by_name(project_name, asset_name) scene = bpy.context.scene diff --git a/openpype/hosts/blender/plugins/publish/extract_layout.py b/openpype/hosts/blender/plugins/publish/extract_layout.py index 8ecc78a2c6..2b3fa6a608 100644 --- a/openpype/hosts/blender/plugins/publish/extract_layout.py +++ b/openpype/hosts/blender/plugins/publish/extract_layout.py @@ -1,13 +1,11 @@ import os import json -from bson.objectid import ObjectId - import bpy import bpy_extras import bpy_extras.anim_utils -from openpype.pipeline import legacy_io +from openpype.client import get_representation_by_name from openpype.hosts.blender.api import plugin from openpype.hosts.blender.api.pipeline import AVALON_PROPERTY import openpype.api @@ -131,43 +129,32 @@ class ExtractLayout(openpype.api.Extractor): fbx_count = 0 + project_name = instance.context["projectEntity"]["name"] for asset in asset_group.children: metadata = asset.get(AVALON_PROPERTY) - parent = metadata["parent"] + version_id = metadata["parent"] family = metadata["family"] - self.log.debug("Parent: {}".format(parent)) + self.log.debug("Parent: {}".format(version_id)) # Get blend reference - blend = legacy_io.find_one( - { - "type": "representation", - "parent": ObjectId(parent), - "name": "blend" - }, - projection={"_id": True}) + blend = get_representation_by_name( + project_name, "blend", version_id, fields=["_id"] + ) blend_id = None if blend: blend_id = blend["_id"] # Get fbx reference - fbx = legacy_io.find_one( - { - "type": "representation", - "parent": ObjectId(parent), - "name": "fbx" - }, - projection={"_id": True}) + fbx = get_representation_by_name( + project_name, "fbx", version_id, fields=["_id"] + ) fbx_id = None if fbx: fbx_id = fbx["_id"] # Get abc reference - abc = legacy_io.find_one( - { - "type": "representation", - "parent": ObjectId(parent), - "name": "abc" - }, - projection={"_id": True}) + abc = get_representation_by_name( + project_name, "abc", version_id, fields=["_id"] + ) abc_id = None if abc: abc_id = abc["_id"] From 6e2802cf9280fb00562b8adf25c7114a937783e4 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Tue, 14 Jun 2022 12:28:56 +0200 Subject: [PATCH 50/51] fix project entity access --- openpype/hosts/blender/plugins/publish/extract_layout.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openpype/hosts/blender/plugins/publish/extract_layout.py b/openpype/hosts/blender/plugins/publish/extract_layout.py index 2b3fa6a608..75d9cf440d 100644 --- a/openpype/hosts/blender/plugins/publish/extract_layout.py +++ b/openpype/hosts/blender/plugins/publish/extract_layout.py @@ -129,7 +129,7 @@ class ExtractLayout(openpype.api.Extractor): fbx_count = 0 - project_name = instance.context["projectEntity"]["name"] + project_name = instance.context.data["projectEntity"]["name"] for asset in asset_group.children: metadata = asset.get(AVALON_PROPERTY) From 780ffefea972a3fb1b0c225136c1327878b95a91 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Tue, 14 Jun 2022 18:08:28 +0200 Subject: [PATCH 51/51] fix unhandled empty source on instance --- openpype/plugins/publish/integrate_new.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/openpype/plugins/publish/integrate_new.py b/openpype/plugins/publish/integrate_new.py index 91f6102501..2471105250 100644 --- a/openpype/plugins/publish/integrate_new.py +++ b/openpype/plugins/publish/integrate_new.py @@ -940,9 +940,8 @@ class IntegrateAssetNew(pyblish.api.InstancePlugin): families += current_families # create relative source path for DB - if "source" in instance.data: - source = instance.data["source"] - else: + source = instance.data.get("source") + if not source: source = context.data["currentFile"] anatomy = instance.context.data["anatomy"] source = self.get_rootless_path(anatomy, source)