Merge pull request #2192 from davidlatwe/feature/typed_asset_version_dependencies

This commit is contained in:
Milan Kolar 2021-11-24 20:35:03 +01:00 committed by GitHub
commit a8f2e0f7ec
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
6 changed files with 283 additions and 6 deletions

View file

@ -0,0 +1,55 @@
import pyblish.api
from avalon import api, io
class CollectSceneLoadedVersions(pyblish.api.ContextPlugin):
order = pyblish.api.CollectorOrder + 0.0001
label = "Collect Versions Loaded in Scene"
hosts = [
"aftereffects",
"blender",
"celaction",
"fusion",
"harmony",
"hiero",
"houdini",
"maya",
"nuke",
"photoshop",
"resolve",
"tvpaint"
]
def process(self, context):
host = api.registered_host()
if host is None:
self.log.warn("No registered host.")
return
if not hasattr(host, "ls"):
host_name = host.__name__
self.log.warn("Host %r doesn't have ls() implemented." % host_name)
return
loaded_versions = []
_containers = list(host.ls())
_repr_ids = [io.ObjectId(c["representation"]) for c in _containers]
version_by_repr = {
str(doc["_id"]): doc["parent"] for doc in
io.find({"_id": {"$in": _repr_ids}}, projection={"parent": 1})
}
for con in _containers:
# NOTE:
# may have more then one representation that are same version
version = {
"objectName": con["objectName"], # container node name
"subsetName": con["name"],
"representation": io.ObjectId(con["representation"]),
"version": version_by_repr[con["representation"]], # _id
}
loaded_versions.append(version)
context.data["loadedVersions"] = loaded_versions

View file

@ -10,7 +10,7 @@ class CollectSceneVersion(pyblish.api.ContextPlugin):
"""
order = pyblish.api.CollectorOrder
label = 'Collect Version'
label = 'Collect Scene Version'
hosts = [
"aftereffects",
"blender",

View file

@ -0,0 +1,130 @@
from collections import OrderedDict
from avalon import io
import pyblish.api
class IntegrateInputLinks(pyblish.api.ContextPlugin):
"""Connecting version level dependency links"""
order = pyblish.api.IntegratorOrder + 0.2
label = "Connect Dependency InputLinks"
def process(self, context):
"""Connect dependency links for all instances, globally
Code steps:
* filter out instances that has "versionEntity" entry in data
* find workfile instance within context
* if workfile found:
- link all `loadedVersions` as input of the workfile
- link workfile as input of all publishing instances
* else:
- show "no workfile" warning
* link instances' inputs if it's data has "inputVersions" entry
* Write into database
inputVersions:
The "inputVersions" in instance.data should be a list of
version document's Id (str or ObjectId), which are the
dependencies of the publishing instance that should be
extracted from working scene by the DCC specific publish
plugin.
"""
workfile = None
publishing = []
for instance in context:
if not instance.data.get("publish", True):
# Skip inactive instances
continue
version_doc = instance.data.get("versionEntity")
if not version_doc:
self.log.debug("Instance %s doesn't have version." % instance)
continue
version_data = version_doc.get("data", {})
families = version_data.get("families", [])
if "workfile" in families:
workfile = instance
else:
publishing.append(instance)
if workfile is None:
self.log.warn("No workfile in this publish session.")
else:
workfile_version_doc = workfile.data["versionEntity"]
# link all loaded versions in scene into workfile
for version in context.data.get("loadedVersions", []):
self.add_link(
link_type="reference",
input_id=version["version"],
version_doc=workfile_version_doc,
)
# link workfile to all publishing versions
for instance in publishing:
self.add_link(
link_type="generative",
input_id=workfile_version_doc["_id"],
version_doc=instance.data["versionEntity"],
)
# link versions as dependencies to the instance
for instance in publishing:
for input_version in instance.data.get("inputVersions") or []:
self.add_link(
link_type="generative",
input_id=input_version,
version_doc=instance.data["versionEntity"],
)
publishing.append(workfile)
self.write_links_to_database(publishing)
def add_link(self, link_type, input_id, version_doc):
"""Add dependency link data into version document
Args:
link_type (str): Type of link, one of 'reference' or 'generative'
input_id (str or ObjectId): Document Id of input version
version_doc (dict): The version document that takes the input
Returns:
None
"""
# NOTE:
# using OrderedDict() here is just for ensuring field order between
# python versions, if we ever need to use mongodb operation '$addToSet'
# to update and avoid duplicating elements in 'inputLinks' array in the
# future.
link = OrderedDict()
link["type"] = link_type
link["input"] = io.ObjectId(input_id)
link["linkedBy"] = "publish"
if "inputLinks" not in version_doc["data"]:
version_doc["data"]["inputLinks"] = []
version_doc["data"]["inputLinks"].append(link)
def write_links_to_database(self, instances):
"""Iter instances in context to update database
If `versionEntity.data.inputLinks` not None in `instance.data`, doc
in database will be updated.
"""
for instance in instances:
version_doc = instance.data.get("versionEntity")
if version_doc is None:
continue
input_links = version_doc["data"].get("inputLinks")
if input_links is None:
continue
io.update_one({"_id": version_doc["_id"]},
{"$set": {"data.inputLinks": input_links}})

View file

View file

@ -0,0 +1,85 @@
from Qt import QtWidgets
class SimpleLinkView(QtWidgets.QWidget):
def __init__(self, dbcon, parent=None):
super(SimpleLinkView, self).__init__(parent=parent)
self.dbcon = dbcon
# TODO: display selected target
in_text = QtWidgets.QLabel("Inputs")
in_view = QtWidgets.QListWidget(parent=self)
out_text = QtWidgets.QLabel("Outputs")
out_view = QtWidgets.QListWidget(parent=self)
layout = QtWidgets.QGridLayout(self)
layout.setContentsMargins(0, 0, 0, 0)
layout.addWidget(in_text, 0, 0)
layout.addWidget(in_view, 1, 0)
layout.addWidget(out_text, 0, 1)
layout.addWidget(out_view, 1, 1)
self._in_view = in_view
self._out_view = out_view
def clear(self):
self._in_view.clear()
self._out_view.clear()
def set_version(self, version_doc):
self.clear()
if not version_doc or not self.isVisible():
return
# inputs
#
for link in version_doc["data"].get("inputLinks", []):
version = self.dbcon.find_one(
{"_id": link["input"], "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}
)
self._in_view.addItem("{asset} {subset} v{version:0>3}".format(
asset=asset["name"],
subset=subset["name"],
version=version["name"],
))
# 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}
)
self._out_view.addItem("{asset} {subset} v{version:0>3}".format(
asset=asset["name"],
subset=subset["name"],
version=version["name"],
))

View file

@ -21,6 +21,7 @@ from openpype.tools.utils.views import (
TreeViewSpinner,
DeselectableTreeView
)
from openpype.tools.assetlinks.widgets import SimpleLinkView
from .model import (
SubsetsModel,
@ -845,19 +846,25 @@ class VersionWidget(QtWidgets.QWidget):
def __init__(self, dbcon, parent=None):
super(VersionWidget, self).__init__(parent=parent)
layout = QtWidgets.QVBoxLayout(self)
layout.setContentsMargins(0, 0, 0, 0)
label = QtWidgets.QLabel("Version", self)
data = VersionTextEdit(dbcon, self)
data.setReadOnly(True)
layout.addWidget(label)
layout.addWidget(data)
depend_widget = SimpleLinkView(dbcon, self)
tab = QtWidgets.QTabWidget()
tab.addTab(data, "Version Info")
tab.addTab(depend_widget, "Dependency")
layout = QtWidgets.QVBoxLayout(self)
layout.setContentsMargins(0, 0, 0, 0)
layout.addWidget(tab)
self.data = data
self.depend_widget = depend_widget
def set_version(self, version_doc):
self.data.set_version(version_doc)
self.depend_widget.set_version(version_doc)
class FamilyModel(QtGui.QStandardItemModel):