[Automated] Merged develop into main

This commit is contained in:
ynbot 2023-04-08 05:24:19 +02:00 committed by GitHub
commit f2a68abf6e
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
33 changed files with 417 additions and 183 deletions

View file

@ -1,33 +0,0 @@
---
name: Bug report
about: Create a report to help us improve
title: ''
labels: bug
assignees: ''
---
**Running version**
[ex. 3.14.1-nightly.2]
**Describe the bug**
A clear and concise description of what the bug is.
**To Reproduce**
Steps to reproduce the behavior:
1. Go to '...'
2. Click on '....'
3. Scroll down to '....'
4. See error
**Expected behavior**
A clear and concise description of what you expected to happen.
**Screenshots**
If applicable, add screenshots to help explain your problem.
**Desktop (please complete the following information):**
- OS: [e.g. windows]
- Host: [e.g. Maya, Nuke, Houdini]
**Additional context**
Add any other context about the problem here.

108
.github/ISSUE_TEMPLATE/bug_report.yml vendored Normal file
View file

@ -0,0 +1,108 @@
name: Bug Report
description: File a bug report
title: 'bug: '
labels:
- 'type: bug'
body:
- type: markdown
attributes:
value: |
Thanks for taking the time to fill out this bug report!
- type: checkboxes
attributes:
label: Is there an existing issue for this?
description: >-
Please search to see if an issue already exists for the bug you
encountered.
options:
- label: I have searched the existing issues
required: true
- type: textarea
attributes:
label: 'Current Behavior:'
description: A concise description of what you're experiencing.
validations:
required: true
- type: textarea
attributes:
label: 'Expected Behavior:'
description: A concise description of what you expected to happen.
validations:
required: false
- type: dropdown
id: _version
attributes:
label: Version
description: What version are you running? Look to OpenPype Tray
options:
- 3.15.3
- 3.15.2
- 3.15.1
- 3.15.0
- 3.14.10
- 3.14.9
- 3.14.8
- 3.14.7
- 3.14.6
- 3.14.5
- 3.14.4
- 3.14.3
- 3.14.2
- 3.14.1
- 3.14.0
- 3.13.0
- 3.12.2
- 3.12.1
- 3.12.0
- 3.11.1
- 3.11.0
- 3.10.0
- 3.9.8
- 3.9.7
- 3.9.6
validations:
required: true
- type: dropdown
validations:
required: true
attributes:
label: What platform you are running OpenPype on?
description: |
Please specify the operating systems you are running OpenPype with.
multiple: true
options:
- Windows
- Linux / Centos
- Linux / Ubuntu
- Linux / RedHat
- MacOS
- type: textarea
id: to-reproduce
attributes:
label: 'Steps To Reproduce:'
description: Steps to reproduce the behavior.
placeholder: |
1. How did the configuration look like
2. What type of action was made
validations:
required: true
- type: checkboxes
attributes:
label: Is there any more labels you wish to add?
description: Please search labels and identify those related to your bug.
options:
- label: I have searched labels and added any
required: true
- type: textarea
id: logs
attributes:
label: 'Relevant log output:'
description: >-
Please copy and paste any relevant log output. This will be
automatically formatted into code, so no need for backticks.
render: shell
- type: textarea
id: additional-context
attributes:
label: 'Additional context:'
description: Add any other context about the problem here.

5
.github/ISSUE_TEMPLATE/config.yml vendored Normal file
View file

@ -0,0 +1,5 @@
blank_issues_enabled: false
contact_links:
- name: Ynput Discord Server
url: https://discord.gg/ynput
about: For community quick chats.

View file

@ -0,0 +1,52 @@
name: Enhancement Request
description: Create a report to help us enhance a particular feature
title: "enhancement: "
labels:
- "type: enhancement"
body:
- type: markdown
attributes:
value: |
Thanks for taking the time to fill out this enhancement request report!
- type: checkboxes
attributes:
label: Is there an existing issue for this?
description: Please search to see if an issue already exists for the bug you encountered.
options:
- label: I have searched the existing issues
required: true
- type: textarea
id: related-feature
attributes:
label: Please state which of feature you have in mind and describe what are its shortcomings?
description: A clear and concise description of what the problem is.
validations:
required: true
- type: textarea
id: enhancement-proposal
attributes:
label: How would you imagine the enhancement of the feature?
description: A clear and concise description of what you want to happen.
validations:
required: true
- type: checkboxes
attributes:
label: Is there any more labels you wish to add?
description: Please search labels and identify those related to your enhancement.
options:
- label: I have searched labels and added any
required: true
- type: textarea
id: alternatives
attributes:
label: "Describe alternatives you've considered:"
description: A clear and concise description of any alternative solutions or features you've considered.
validations:
required: false
- type: textarea
id: additional-context
attributes:
label: "Additional context:"
description: Add any other context or screenshots about the enhancement request here.
validations:
required: false

View file

@ -1,20 +0,0 @@
---
name: Feature request
about: Suggest an idea for this project
title: ''
labels: enhancement
assignees: ''
---
**Is your feature request related to a problem? Please describe.**
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
**Describe the solution you'd like**
A clear and concise description of what you want to happen.
**Describe alternatives you've considered**
A clear and concise description of any alternative solutions or features you've considered.
**Additional context**
Add any other context or screenshots about the feature request here.

View file

@ -1,4 +1,4 @@
name: documentation
name: 📜 Documentation
on:
pull_request:

View file

@ -1,4 +1,4 @@
name: Milestone - assign to PRs
name: 👉🏻 Milestone - assign to PRs
on:
pull_request_target:

View file

@ -1,4 +1,4 @@
name: Milestone - create default
name: Milestone - create default
on:
milestone:

View file

@ -1,4 +1,4 @@
name: Milestone Release [trigger]
name: 🚩 Milestone Release [trigger]
on:
workflow_dispatch:

View file

@ -1,4 +1,4 @@
name: Dev -> Main
name: 🔀 Dev -> Main
on:
schedule:

49
.github/workflows/pr_labels.yml vendored Normal file
View file

@ -0,0 +1,49 @@
name: 🔖 PR labels
on:
pull_request_target:
types: [opened, assigned]
jobs:
size-label:
name: pr_size_label
runs-on: ubuntu-latest
if: github.event.action == 'assigned' || github.event.action == 'opened'
steps:
- name: Add size label
uses: "pascalgn/size-label-action@v0.4.3"
env:
GITHUB_TOKEN: "${{ secrets.YNPUT_BOT_TOKEN }}"
IGNORED: ".gitignore\n*.md\n*.json"
with:
sizes: >
{
"0": "XS",
"100": "S",
"500": "M",
"1000": "L",
"1500": "XL",
"2500": "XXL"
}
label_prs_branch:
name: pr_branch_label
runs-on: ubuntu-latest
if: github.event.action == 'assigned' || github.event.action == 'opened'
steps:
- name: Label PRs - Branch name detection
uses: ffittschen/pr-branch-labeler@v1
with:
repo-token: ${{ secrets.YNPUT_BOT_TOKEN }}
label_prs_globe:
name: pr_globe_label
runs-on: ubuntu-latest
if: github.event.action == 'assigned' || github.event.action == 'opened'
steps:
- name: Label PRs - Globe detection
uses: actions/labeler@v4.0.3
with:
repo-token: ${{ secrets.YNPUT_BOT_TOKEN }}
configuration-path: ".github/pr-glob-labeler.yml"
sync-labels: false

View file

@ -1,4 +1,4 @@
name: Nightly Prerelease
name: Nightly Prerelease
on:
workflow_dispatch:

View file

@ -1,8 +1,6 @@
name: project-actions
name: 📊 Project task statuses
on:
pull_request_target:
types: [opened, assigned]
pull_request_review:
types: [submitted]
issue_comment:
@ -25,7 +23,11 @@ jobs:
if: |
(github.event_name == 'issue_comment' && github.event.pull_request.head.repo.owner.login == 'ynput' && github.event.comment.user.id != 82967070) ||
(github.event_name == 'pull_request_review_comment' && github.event.pull_request.head.repo.owner.login == 'ynput' && github.event.comment.user.type != 'Bot') ||
(github.event_name == 'pull_request_review' && github.event.pull_request.head.repo.owner.login == 'ynput' && github.event.review.state != 'changes_requested' && github.event.review.user.type != 'Bot')
(github.event_name == 'pull_request_review' &&
github.event.pull_request.head.repo.owner.login == 'ynput' &&
github.event.review.state != 'changes_requested' &&
github.event.review.state != 'approved' &&
github.event.review.user.type != 'Bot')
steps:
- name: Move PR to 'Review In Progress'
uses: leonsteinhaeuser/project-beta-automations@v2.1.0
@ -66,53 +68,3 @@ jobs:
-d '{
"status": "in progress"
}'
size-label:
name: pr_size_label
runs-on: ubuntu-latest
if: |
(github.event_name == 'pull_request' && github.event.action == 'assigned') ||
(github.event_name == 'pull_request' && github.event.action == 'opened')
steps:
- name: Add size label
uses: "pascalgn/size-label-action@v0.4.3"
env:
GITHUB_TOKEN: "${{ secrets.YNPUT_BOT_TOKEN }}"
IGNORED: ".gitignore\n*.md\n*.json"
with:
sizes: >
{
"0": "XS",
"100": "S",
"500": "M",
"1000": "L",
"1500": "XL",
"2500": "XXL"
}
label_prs_branch:
name: pr_branch_label
runs-on: ubuntu-latest
if: |
(github.event_name == 'pull_request' && github.event.action == 'assigned') ||
(github.event_name == 'pull_request' && github.event.action == 'opened')
steps:
- name: Label PRs - Branch name detection
uses: ffittschen/pr-branch-labeler@v1
with:
repo-token: ${{ secrets.YNPUT_BOT_TOKEN }}
label_prs_globe:
name: pr_globe_label
runs-on: ubuntu-latest
if: |
(github.event_name == 'pull_request' && github.event.action == 'assigned') ||
(github.event_name == 'pull_request' && github.event.action == 'opened')
steps:
- name: Label PRs - Globe detection
uses: actions/labeler@v4.0.3
with:
repo-token: ${{ secrets.YNPUT_BOT_TOKEN }}
configuration-path: ".github/pr-glob-labeler.yml"
sync-labels: false

View file

@ -1,7 +1,7 @@
# This workflow will upload a Python Package using Twine when a release is created
# For more information see: https://help.github.com/en/actions/language-and-framework-guides/using-python-with-github-actions#publishing-to-package-registries
name: Test Build
name: 🏗️ Test Build
on:
pull_request:

25
.github/workflows/update_bug_report.yml vendored Normal file
View file

@ -0,0 +1,25 @@
name: 🐞 Update Bug Report
on:
workflow_dispatch:
release:
# https://docs.github.com/en/actions/using-workflows/events-that-trigger-workflows#release
types: [published]
jobs:
update-bug-report:
runs-on: ubuntu-latest
name: Update bug report
steps:
- uses: actions/checkout@v3
with:
ref: ${{ github.event.release.target_commitish }}
- name: Update version
uses: ShaMan123/gha-populate-form-version@v2.0.2
with:
github_token: ${{ secrets.YNPUT_BOT_TOKEN }}
registry: github
dropdown: _version
limit_to: 25
form: .github/ISSUE_TEMPLATE/bug_report.yml
commit_message: 'chore(): update bug report / version'

View file

@ -242,9 +242,15 @@ def launch_zip_file(filepath):
print(f"Localizing {filepath}")
temp_path = get_local_harmony_path(filepath)
scene_name = os.path.basename(temp_path)
if os.path.exists(os.path.join(temp_path, scene_name)):
# unzipped with duplicated scene_name
temp_path = os.path.join(temp_path, scene_name)
scene_path = os.path.join(
temp_path, os.path.basename(temp_path) + ".xstage"
temp_path, scene_name + ".xstage"
)
unzip = False
if os.path.exists(scene_path):
# Check remote scene is newer than local.
@ -262,6 +268,10 @@ def launch_zip_file(filepath):
with _ZipFile(filepath, "r") as zip_ref:
zip_ref.extractall(temp_path)
if os.path.exists(os.path.join(temp_path, scene_name)):
# unzipped with duplicated scene_name
temp_path = os.path.join(temp_path, scene_name)
# Close existing scene.
if ProcessContext.pid:
os.kill(ProcessContext.pid, signal.SIGTERM)
@ -309,7 +319,7 @@ def launch_zip_file(filepath):
)
if not os.path.exists(scene_path):
print("error: cannot determine scene file")
print("error: cannot determine scene file {}".format(scene_path))
ProcessContext.server.stop()
return

View file

@ -26,7 +26,7 @@ HARDLINK = 2
@attr.s
class TextureResult:
class TextureResult(object):
"""The resulting texture of a processed file for a resource"""
# Path to the file
path = attr.ib()

View file

@ -23,6 +23,9 @@ from openpype.client import (
from openpype.host import HostDirmap
from openpype.tools.utils import host_tools
from openpype.pipeline.workfile.workfile_template_builder import (
TemplateProfileNotFound
)
from openpype.lib import (
env_value_to_bool,
Logger,
@ -2684,7 +2687,10 @@ def start_workfile_template_builder():
# to avoid looping of the callback, remove it!
log.info("Starting workfile template builder...")
build_workfile_template(workfile_creation_enabled=True)
try:
build_workfile_template(workfile_creation_enabled=True)
except TemplateProfileNotFound:
log.warning("Template profile not found. Skipping...")
# remove callback since it would be duplicating the workfile
nuke.removeOnCreate(start_workfile_template_builder, nodeClass="Root")

View file

@ -208,6 +208,12 @@ class NukeCreator(NewCreator):
def collect_instances(self):
cached_instances = _collect_and_cache_nodes(self)
attr_def_keys = {
attr_def.key
for attr_def in self.get_instance_attr_defs()
}
attr_def_keys.discard(None)
for (node, data) in cached_instances[self.identifier]:
created_instance = CreatedInstance.from_existing(
data, self
@ -215,6 +221,12 @@ class NukeCreator(NewCreator):
created_instance.transient_data["node"] = node
self._add_instance_to_context(created_instance)
for key in (
set(created_instance["creator_attributes"].keys())
- attr_def_keys
):
created_instance["creator_attributes"].pop(key)
def update_instances(self, update_list):
for created_inst, _changes in update_list:
instance_node = created_inst.transient_data["node"]
@ -301,8 +313,11 @@ class NukeWriteCreator(NukeCreator):
def get_instance_attr_defs(self):
attr_defs = [
self._get_render_target_enum(),
self._get_reviewable_bool()
]
# add reviewable attribute
if "reviewable" in self.instance_attributes:
attr_defs.append(self._get_reviewable_bool())
return attr_defs
def _get_render_target_enum(self):
@ -322,7 +337,7 @@ class NukeWriteCreator(NukeCreator):
def _get_reviewable_bool(self):
return BoolDef(
"review",
default=("reviewable" in self.instance_attributes),
default=True,
label="Review"
)

View file

@ -219,14 +219,17 @@ class NukePlaceholderLoadPlugin(NukePlaceholderPlugin, PlaceholderLoadMixin):
# fix the problem of z_order for backdrops
self._fix_z_order(placeholder)
self._imprint_siblings(placeholder)
if placeholder.data.get("keep_placeholder"):
self._imprint_siblings(placeholder)
if placeholder.data["nb_children"] == 0:
# save initial nodes positions and dimensions, update them
# and set inputs and outputs of loaded nodes
if placeholder.data.get("keep_placeholder"):
self._imprint_inits()
self._update_nodes(placeholder, nuke.allNodes(), nodes_loaded)
self._imprint_inits()
self._update_nodes(placeholder, nuke.allNodes(), nodes_loaded)
self._set_loaded_connections(placeholder)
elif placeholder.data["siblings"]:
@ -629,14 +632,18 @@ class NukePlaceholderCreatePlugin(
# fix the problem of z_order for backdrops
self._fix_z_order(placeholder)
self._imprint_siblings(placeholder)
if placeholder.data.get("keep_placeholder"):
self._imprint_siblings(placeholder)
if placeholder.data["nb_children"] == 0:
# save initial nodes positions and dimensions, update them
# and set inputs and outputs of created nodes
self._imprint_inits()
self._update_nodes(placeholder, nuke.allNodes(), nodes_created)
if placeholder.data.get("keep_placeholder"):
self._imprint_inits()
self._update_nodes(placeholder, nuke.allNodes(), nodes_created)
self._set_created_connections(placeholder)
elif placeholder.data["siblings"]:

View file

@ -63,13 +63,6 @@ class CreateWriteImage(napi.NukeWriteCreator):
default=nuke.frame()
)
def get_instance_attr_defs(self):
attr_defs = [
self._get_render_target_enum(),
self._get_reviewable_bool()
]
return attr_defs
def create_instance_node(self, subset_name, instance_data):
linked_knobs_ = []
if "use_range_limit" in self.instance_attributes:

View file

@ -41,13 +41,6 @@ class CreateWritePrerender(napi.NukeWriteCreator):
]
return attr_defs
def get_instance_attr_defs(self):
attr_defs = [
self._get_render_target_enum(),
self._get_reviewable_bool()
]
return attr_defs
def create_instance_node(self, subset_name, instance_data):
linked_knobs_ = []
if "use_range_limit" in self.instance_attributes:

View file

@ -38,13 +38,6 @@ class CreateWriteRender(napi.NukeWriteCreator):
]
return attr_defs
def get_instance_attr_defs(self):
attr_defs = [
self._get_render_target_enum(),
self._get_reviewable_bool()
]
return attr_defs
def create_instance_node(self, subset_name, instance_data):
# add fpath_template
write_data = {

View file

@ -9,9 +9,9 @@ import openpype.hosts.nuke.api.lib as nlib
from openpype.pipeline.publish import (
ValidateContentsOrder,
PublishXmlValidationError,
OptionalPyblishPluginMixin
)
class SelectInvalidInstances(pyblish.api.Action):
"""Select invalid instances in Outliner."""
@ -92,7 +92,10 @@ class RepairSelectInvalidInstances(pyblish.api.Action):
nlib.set_node_data(node, nlib.INSTANCE_DATA_KNOB, node_data)
class ValidateCorrectAssetName(pyblish.api.InstancePlugin):
class ValidateCorrectAssetName(
pyblish.api.InstancePlugin,
OptionalPyblishPluginMixin
):
"""Validator to check if instance asset match context asset.
When working in per-shot style you always publish data in context of
@ -111,6 +114,9 @@ class ValidateCorrectAssetName(pyblish.api.InstancePlugin):
optional = True
def process(self, instance):
if not self.is_active(instance.data):
return
asset = instance.data.get("asset")
context_asset = instance.context.data["assetEntity"]["name"]
node = instance.data["transientData"]["node"]

View file

@ -1,8 +1,12 @@
import nuke
import pyblish
from openpype.hosts.nuke import api as napi
from openpype.pipeline import PublishXmlValidationError
from openpype.pipeline.publish import (
ValidateContentsOrder,
PublishXmlValidationError,
OptionalPyblishPluginMixin
)
class SelectCenterInNodeGraph(pyblish.api.Action):
"""
@ -46,12 +50,15 @@ class SelectCenterInNodeGraph(pyblish.api.Action):
nuke.zoom(2, [min(all_xC), min(all_yC)])
class ValidateBackdrop(pyblish.api.InstancePlugin):
class ValidateBackdrop(
pyblish.api.InstancePlugin,
OptionalPyblishPluginMixin
):
""" Validate amount of nodes on backdrop node in case user
forgotten to add nodes above the publishing backdrop node.
"""
order = pyblish.api.ValidatorOrder
order = ValidateContentsOrder
optional = True
families = ["nukenodes"]
label = "Validate Backdrop"
@ -59,6 +66,9 @@ class ValidateBackdrop(pyblish.api.InstancePlugin):
actions = [SelectCenterInNodeGraph]
def process(self, instance):
if not self.is_active(instance.data):
return
child_nodes = instance.data["transientData"]["childNodes"]
connections_out = instance.data["transientData"]["nodeConnectionsOut"]

View file

@ -18,7 +18,7 @@ class ValidateScriptAttributes(
order = pyblish.api.ValidatorOrder + 0.1
families = ["workfile"]
label = "Validatte script attributes"
label = "Validate script attributes"
hosts = ["nuke"]
optional = True
actions = [RepairAction]

View file

@ -256,17 +256,18 @@ class TemplatesDict(object):
elif isinstance(templates, dict):
self._raw_templates = copy.deepcopy(templates)
self._templates = templates
self._objected_templates = self.create_ojected_templates(templates)
self._objected_templates = self.create_objected_templates(
templates)
else:
raise TypeError("<{}> argument must be a dict, not {}.".format(
self.__class__.__name__, str(type(templates))
))
def __getitem__(self, key):
return self.templates[key]
return self.objected_templates[key]
def get(self, key, *args, **kwargs):
return self.templates.get(key, *args, **kwargs)
return self.objected_templates.get(key, *args, **kwargs)
@property
def raw_templates(self):
@ -280,8 +281,21 @@ class TemplatesDict(object):
def objected_templates(self):
return self._objected_templates
@classmethod
def create_ojected_templates(cls, templates):
def _create_template_object(self, template):
"""Create template object from a template string.
Separated into method to give option change class of templates.
Args:
template (str): Template string.
Returns:
StringTemplate: Object of template.
"""
return StringTemplate(template)
def create_objected_templates(self, templates):
if not isinstance(templates, dict):
raise TypeError("Expected dict object, got {}".format(
str(type(templates))
@ -297,7 +311,7 @@ class TemplatesDict(object):
for key in tuple(item.keys()):
value = item[key]
if isinstance(value, six.string_types):
item[key] = StringTemplate(value)
item[key] = self._create_template_object(value)
elif isinstance(value, dict):
inner_queue.append(value)
return objected_templates

View file

@ -19,6 +19,7 @@ from openpype.client import get_project
from openpype.lib.path_templates import (
TemplateUnsolved,
TemplateResult,
StringTemplate,
TemplatesDict,
FormatObject,
)
@ -606,6 +607,32 @@ class AnatomyTemplateResult(TemplateResult):
return self.__class__(tmp, self.rootless)
class AnatomyStringTemplate(StringTemplate):
"""String template which has access to anatomy."""
def __init__(self, anatomy_templates, template):
self.anatomy_templates = anatomy_templates
super(AnatomyStringTemplate, self).__init__(template)
def format(self, data):
"""Format template and add 'root' key to data if not available.
Args:
data (dict[str, Any]): Formatting data for template.
Returns:
AnatomyTemplateResult: Formatting result.
"""
anatomy_templates = self.anatomy_templates
if not data.get("root"):
data = copy.deepcopy(data)
data["root"] = anatomy_templates.anatomy.roots
result = StringTemplate.format(self, data)
rootless_path = anatomy_templates.rootless_path_from_result(result)
return AnatomyTemplateResult(result, rootless_path)
class AnatomyTemplates(TemplatesDict):
inner_key_pattern = re.compile(r"(\{@.*?[^{}0]*\})")
inner_key_name_pattern = re.compile(r"\{@(.*?[^{}0]*)\}")
@ -615,12 +642,6 @@ class AnatomyTemplates(TemplatesDict):
self.anatomy = anatomy
self.loaded_project = None
def __getitem__(self, key):
return self.templates[key]
def get(self, key, default=None):
return self.templates.get(key, default)
def reset(self):
self._raw_templates = None
self._templates = None
@ -655,12 +676,7 @@ class AnatomyTemplates(TemplatesDict):
def _format_value(self, value, data):
if isinstance(value, RootItem):
return self._solve_dict(value, data)
result = super(AnatomyTemplates, self)._format_value(value, data)
if isinstance(result, TemplateResult):
rootless_path = self._rootless_path(result, data)
result = AnatomyTemplateResult(result, rootless_path)
return result
return super(AnatomyTemplates, self)._format_value(value, data)
def set_templates(self, templates):
if not templates:
@ -689,10 +705,13 @@ class AnatomyTemplates(TemplatesDict):
solved_templates = self.solve_template_inner_links(templates)
self._templates = solved_templates
self._objected_templates = self.create_ojected_templates(
self._objected_templates = self.create_objected_templates(
solved_templates
)
def _create_template_object(self, template):
return AnatomyStringTemplate(self, template)
def default_templates(self):
"""Return default templates data with solved inner keys."""
return self.solve_template_inner_links(
@ -886,7 +905,8 @@ class AnatomyTemplates(TemplatesDict):
return keys_by_subkey
def _dict_to_subkeys_list(self, subdict, pre_keys=None):
@classmethod
def _dict_to_subkeys_list(cls, subdict, pre_keys=None):
if pre_keys is None:
pre_keys = []
output = []
@ -895,7 +915,7 @@ class AnatomyTemplates(TemplatesDict):
result = list(pre_keys)
result.append(key)
if isinstance(value, dict):
for item in self._dict_to_subkeys_list(value, result):
for item in cls._dict_to_subkeys_list(value, result):
output.append(item)
else:
output.append(result)
@ -908,7 +928,17 @@ class AnatomyTemplates(TemplatesDict):
return {key_list[0]: value}
return {key_list[0]: self._keys_to_dicts(key_list[1:], value)}
def _rootless_path(self, result, final_data):
@classmethod
def rootless_path_from_result(cls, result):
"""Calculate rootless path from formatting result.
Args:
result (TemplateResult): Result of StringTemplate formatting.
Returns:
str: Rootless path if result contains one of anatomy roots.
"""
used_values = result.used_values
missing_keys = result.missing_keys
template = result.template
@ -924,7 +954,7 @@ class AnatomyTemplates(TemplatesDict):
if "root" in invalid_type:
return
root_keys = self._dict_to_subkeys_list({"root": used_values["root"]})
root_keys = cls._dict_to_subkeys_list({"root": used_values["root"]})
if not root_keys:
return

View file

@ -596,7 +596,14 @@ class AttributeValues(object):
self[_key] = _value
def pop(self, key, default=None):
return self._data.pop(key, default)
value = self._data.pop(key, default)
# Remove attribute definition if is 'UnknownDef'
# - gives option to get rid of unknown values
attr_def = self._attr_defs_by_key.get(key)
if isinstance(attr_def, UnknownDef):
self._attr_defs_by_key.pop(key)
self._attr_defs.remove(attr_def)
return value
def reset_values(self):
self._data = {}

View file

@ -87,7 +87,9 @@ class CollectOtioReview(pyblish.api.InstancePlugin):
otio_review_clips.append(otio_gap)
if otio_review_clips:
instance.data["label"] += " (review)"
# add review track to instance and change label to reflect it
label = instance.data.get("label", instance.data["subset"])
instance.data["label"] = label + " (review)"
instance.data["families"] += ["review", "ftrack"]
instance.data["otioReviewClips"] = otio_review_clips
self.log.info(

View file

@ -363,6 +363,11 @@
"optional": true,
"active": true
},
"ValidateBackdrop": {
"enabled": true,
"optional": true,
"active": true
},
"ValidateScript": {
"enabled": true,
"optional": true,

View file

@ -62,7 +62,7 @@
"template_data": [
{
"key": "ValidateCorrectAssetName",
"label": "Validate Correct Asset name"
"label": "Validate Correct Asset Name"
}
]
},
@ -72,7 +72,7 @@
"template_data": [
{
"key": "ValidateContainers",
"label": "ValidateContainers"
"label": "Validate Containers"
}
]
},
@ -81,7 +81,7 @@
"collapsible": true,
"checkbox_key": "enabled",
"key": "ValidateKnobs",
"label": "ValidateKnobs",
"label": "Validate Knobs",
"is_group": true,
"children": [
{
@ -104,6 +104,10 @@
"key": "ValidateOutputResolution",
"label": "Validate Output Resolution"
},
{
"key": "ValidateBackdrop",
"label": "Validate Backdrop"
},
{
"key": "ValidateGizmo",
"label": "Validate Gizmo (Group)"

View file

@ -361,10 +361,11 @@ class SubsetsModel(TreeModel, BaseRepresentationModel):
version_data.get("endFrame", None)
)
handles_label = None
handle_start = version_data.get("handleStart", None)
handle_end = version_data.get("handleEnd", None)
if handle_start is not None and handle_end is not None:
handles = "{}-{}".format(str(handle_start), str(handle_end))
handles_label = "{}-{}".format(str(handle_start), str(handle_end))
if frame_start is not None and frame_end is not None:
# Remove superfluous zeros from numbers (3.0 -> 3) to improve
@ -401,7 +402,7 @@ class SubsetsModel(TreeModel, BaseRepresentationModel):
"frameStart": frame_start,
"frameEnd": frame_end,
"duration": duration,
"handles": handles,
"handles": handles_label,
"frames": frames,
"step": version_data.get("step", None),
})