diff --git a/openpype/hosts/aftereffects/plugins/publish/collect_workfile.py b/openpype/hosts/aftereffects/plugins/publish/collect_workfile.py index c1c2be4855..cb5a2bad4f 100644 --- a/openpype/hosts/aftereffects/plugins/publish/collect_workfile.py +++ b/openpype/hosts/aftereffects/plugins/publish/collect_workfile.py @@ -1,6 +1,7 @@ import os from avalon import api import pyblish.api +from openpype.lib import get_subset_name_with_asset_doc class CollectWorkfile(pyblish.api.ContextPlugin): @@ -38,7 +39,14 @@ class CollectWorkfile(pyblish.api.ContextPlugin): # workfile instance family = "workfile" - subset = family + task.capitalize() + subset = get_subset_name_with_asset_doc( + family, + "", + context.data["anatomyData"]["task"]["name"], + context.data["assetEntity"], + context.data["anatomyData"]["project"]["name"], + host_name=context.data["hostName"] + ) # Create instance instance = context.create_instance(subset) diff --git a/openpype/hosts/harmony/plugins/publish/collect_workfile.py b/openpype/hosts/harmony/plugins/publish/collect_workfile.py index 63bfd5929b..c0493315a4 100644 --- a/openpype/hosts/harmony/plugins/publish/collect_workfile.py +++ b/openpype/hosts/harmony/plugins/publish/collect_workfile.py @@ -3,6 +3,8 @@ import pyblish.api import os +from openpype.lib import get_subset_name_with_asset_doc + class CollectWorkfile(pyblish.api.ContextPlugin): """Collect current script for publish.""" @@ -14,10 +16,15 @@ class CollectWorkfile(pyblish.api.ContextPlugin): def process(self, context): """Plugin entry point.""" family = "workfile" - task = os.getenv("AVALON_TASK", None) - sanitized_task_name = task[0].upper() + task[1:] basename = os.path.basename(context.data["currentFile"]) - subset = "{}{}".format(family, sanitized_task_name) + subset = get_subset_name_with_asset_doc( + family, + "", + context.data["anatomyData"]["task"]["name"], + context.data["assetEntity"], + context.data["anatomyData"]["project"]["name"], + host_name=context.data["hostName"] + ) # Create instance instance = context.create_instance(subset) diff --git a/openpype/hosts/maya/plugins/create/create_render.py b/openpype/hosts/maya/plugins/create/create_render.py index 9002ae3876..4f0a394f85 100644 --- a/openpype/hosts/maya/plugins/create/create_render.py +++ b/openpype/hosts/maya/plugins/create/create_render.py @@ -252,6 +252,7 @@ class CreateRender(plugin.Creator): """Create instance settings.""" # get pools pool_names = [] + default_priority = 50 self.server_aliases = list(self.deadline_servers.keys()) self.data["deadlineServers"] = self.server_aliases @@ -260,7 +261,8 @@ class CreateRender(plugin.Creator): self.data["extendFrames"] = False self.data["overrideExistingFrame"] = True # self.data["useLegacyRenderLayers"] = True - self.data["priority"] = 50 + self.data["priority"] = default_priority + self.data["tile_priority"] = default_priority self.data["framesPerTask"] = 1 self.data["whitelist"] = False self.data["machineList"] = "" @@ -294,6 +296,16 @@ class CreateRender(plugin.Creator): deadline_url = next(iter(self.deadline_servers.values())) pool_names = self._get_deadline_pools(deadline_url) + maya_submit_dl = self._project_settings.get( + "deadline", {}).get( + "publish", {}).get( + "MayaSubmitDeadline", {}) + priority = maya_submit_dl.get("priority", default_priority) + self.data["priority"] = priority + + tile_priority = maya_submit_dl.get("tile_priority", + default_priority) + self.data["tile_priority"] = tile_priority if muster_enabled: self.log.info(">>> Loading Muster credentials ...") diff --git a/openpype/hosts/maya/plugins/publish/validate_camera_contents.py b/openpype/hosts/maya/plugins/publish/validate_camera_contents.py index d9e88edaac..20af8d2315 100644 --- a/openpype/hosts/maya/plugins/publish/validate_camera_contents.py +++ b/openpype/hosts/maya/plugins/publish/validate_camera_contents.py @@ -40,7 +40,14 @@ class ValidateCameraContents(pyblish.api.InstancePlugin): # list when there are no actual cameras results in # still an empty 'invalid' list if len(cameras) < 1: - raise RuntimeError("No cameras in instance.") + if members: + # If there are members in the instance return all of + # them as 'invalid' so the user can still select invalid + cls.log.error("No cameras found in instance " + "members: {}".format(members)) + return members + + raise RuntimeError("No cameras found in empty instance.") # non-camera shapes valid_shapes = cmds.ls(shapes, type=('camera', 'locator'), long=True) diff --git a/openpype/hosts/nuke/plugins/publish/extract_review_data_mov.py b/openpype/hosts/nuke/plugins/publish/extract_review_data_mov.py index 22b371d8e9..2e8843d2e0 100644 --- a/openpype/hosts/nuke/plugins/publish/extract_review_data_mov.py +++ b/openpype/hosts/nuke/plugins/publish/extract_review_data_mov.py @@ -123,7 +123,7 @@ class ExtractReviewDataMov(openpype.api.Extractor): if generated_repres: # assign to representations instance.data["representations"] += generated_repres - instance.data["hasReviewableRepresentations"] = True + instance.data["useSequenceForReview"] = False else: instance.data["families"].remove("review") self.log.info(( diff --git a/openpype/modules/base.py b/openpype/modules/base.py index 5cdeb86087..23c908299f 100644 --- a/openpype/modules/base.py +++ b/openpype/modules/base.py @@ -37,6 +37,8 @@ IGNORED_DEFAULT_FILENAMES = ( "__init__.py", "base.py", "interfaces.py", + "example_addons", + "default_modules", ) @@ -303,7 +305,16 @@ def _load_modules(): fullpath = os.path.join(current_dir, filename) basename, ext = os.path.splitext(filename) - if not os.path.isdir(fullpath) and ext not in (".py", ): + if os.path.isdir(fullpath): + # Check existence of init fil + init_path = os.path.join(fullpath, "__init__.py") + if not os.path.exists(init_path): + log.debug(( + "Module directory does not contan __init__.py file {}" + ).format(fullpath)) + continue + + elif ext not in (".py", ): continue try: @@ -341,7 +352,16 @@ def _load_modules(): fullpath = os.path.join(dirpath, filename) basename, ext = os.path.splitext(filename) - if not os.path.isdir(fullpath) and ext not in (".py", ): + if os.path.isdir(fullpath): + # Check existence of init fil + init_path = os.path.join(fullpath, "__init__.py") + if not os.path.exists(init_path): + log.debug(( + "Module directory does not contan __init__.py file {}" + ).format(fullpath)) + continue + + elif ext not in (".py", ): continue # TODO add more logic how to define if folder is module or not diff --git a/openpype/modules/deadline/plugins/publish/submit_maya_deadline.py b/openpype/modules/deadline/plugins/publish/submit_maya_deadline.py index 15a6f8d828..34147712bc 100644 --- a/openpype/modules/deadline/plugins/publish/submit_maya_deadline.py +++ b/openpype/modules/deadline/plugins/publish/submit_maya_deadline.py @@ -254,7 +254,11 @@ class MayaSubmitDeadline(pyblish.api.InstancePlugin): use_published = True tile_assembler_plugin = "OpenPypeTileAssembler" asset_dependencies = False + priority = 50 + tile_priority = 50 limit_groups = [] + jobInfo = {} + pluginInfo = {} group = "none" def process(self, instance): @@ -272,37 +276,12 @@ class MayaSubmitDeadline(pyblish.api.InstancePlugin): self.deadline_url = instance.data.get("deadlineUrl") assert self.deadline_url, "Requires Deadline Webservice URL" - self._job_info = ( - context.data["project_settings"].get( - "deadline", {}).get( - "publish", {}).get( - "MayaSubmitDeadline", {}).get( - "jobInfo", {}) - ) + # just using existing names from Setting + self._job_info = self.jobInfo - self._plugin_info = ( - context.data["project_settings"].get( - "deadline", {}).get( - "publish", {}).get( - "MayaSubmitDeadline", {}).get( - "pluginInfo", {}) - ) + self._plugin_info = self.pluginInfo - self.limit_groups = ( - context.data["project_settings"].get( - "deadline", {}).get( - "publish", {}).get( - "MayaSubmitDeadline", {}).get( - "limit", []) - ) - - self.group = ( - context.data["project_settings"].get( - "deadline", {}).get( - "publish", {}).get( - "MayaSubmitDeadline", {}).get( - "group", "none") - ) + self.limit_groups = self.limit context = instance.context workspace = context.data["workspaceDir"] @@ -465,7 +444,7 @@ class MayaSubmitDeadline(pyblish.api.InstancePlugin): self.payload_skeleton["JobInfo"]["UserName"] = deadline_user # Set job priority self.payload_skeleton["JobInfo"]["Priority"] = \ - self._instance.data.get("priority", 50) + self._instance.data.get("priority", self.priority) if self.group != "none" and self.group: self.payload_skeleton["JobInfo"]["Group"] = self.group @@ -635,7 +614,7 @@ class MayaSubmitDeadline(pyblish.api.InstancePlugin): } assembly_payload["JobInfo"].update(output_filenames) assembly_payload["JobInfo"]["Priority"] = self._instance.data.get( - "priority", 50) + "tile_priority", self.tile_priority) assembly_payload["JobInfo"]["UserName"] = deadline_user frame_payloads = [] diff --git a/openpype/modules/deadline/plugins/publish/submit_publish_job.py b/openpype/modules/deadline/plugins/publish/submit_publish_job.py index a8f4fec563..921b172f2b 100644 --- a/openpype/modules/deadline/plugins/publish/submit_publish_job.py +++ b/openpype/modules/deadline/plugins/publish/submit_publish_job.py @@ -235,6 +235,8 @@ class ProcessSubmittedJobOnFarm(pyblish.api.InstancePlugin): if mongo_url: environment["OPENPYPE_MONGO"] = mongo_url + priority = self.deadline_priority or instance.data.get("priority", 50) + args = [ "--headless", 'publish', @@ -254,7 +256,7 @@ class ProcessSubmittedJobOnFarm(pyblish.api.InstancePlugin): "Department": self.deadline_department, "ChunkSize": self.deadline_chunk_size, - "Priority": job["Props"]["Pri"], + "Priority": priority, "Group": self.deadline_group, "Pool": self.deadline_pool, @@ -524,26 +526,31 @@ class ProcessSubmittedJobOnFarm(pyblish.api.InstancePlugin): for collection in collections: ext = collection.tail.lstrip(".") preview = False - # if filtered aov name is found in filename, toggle it for - # preview video rendering - for app in self.aov_filter.keys(): - if os.environ.get("AVALON_APP", "") == app: - # no need to add review if `hasReviewableRepresentations` - if instance.get("hasReviewableRepresentations"): - break + # TODO 'useSequenceForReview' is temporary solution which does + # not work for 100% of cases. We must be able to tell what + # expected files contains more explicitly and from what + # should be review made. + # - "review" tag is never added when is set to 'False' + use_sequence_for_review = instance.get( + "useSequenceForReview", True + ) + if use_sequence_for_review: + # if filtered aov name is found in filename, toggle it for + # preview video rendering + for app in self.aov_filter.keys(): + if os.environ.get("AVALON_APP", "") == app: + # iteratre all aov filters + for aov in self.aov_filter[app]: + if re.match( + aov, + list(collection)[0] + ): + preview = True + break - # iteratre all aov filters - for aov in self.aov_filter[app]: - if re.match( - aov, - list(collection)[0] - ): - preview = True - break - - # toggle preview on if multipart is on - if instance.get("multipartExr", False): - preview = True + # toggle preview on if multipart is on + if instance.get("multipartExr", False): + preview = True staging = os.path.dirname(list(collection)[0]) success, rootless_staging_dir = ( @@ -730,8 +737,7 @@ class ProcessSubmittedJobOnFarm(pyblish.api.InstancePlugin): "resolutionHeight": data.get("resolutionHeight", 1080), "multipartExr": data.get("multipartExr", False), "jobBatchName": data.get("jobBatchName", ""), - "hasReviewableRepresentations": data.get( - "hasReviewableRepresentations") + "useSequenceForReview": data.get("useSequenceForReview") } if "prerender" in instance.data["families"]: @@ -923,12 +929,6 @@ class ProcessSubmittedJobOnFarm(pyblish.api.InstancePlugin): # User is deadline user render_job["Props"]["User"] = context.data.get( "deadlineUser", getpass.getuser()) - # Priority is now not handled at all - - if self.deadline_priority: - render_job["Props"]["Pri"] = self.deadline_priority - else: - render_job["Props"]["Pri"] = instance.data.get("priority") render_job["Props"]["Env"] = { "FTRACK_API_USER": os.environ.get("FTRACK_API_USER"), diff --git a/openpype/modules/ftrack/event_handlers_user/action_create_folders.py b/openpype/modules/ftrack/event_handlers_user/action_create_folders.py index d15a865124..0ed12bd03e 100644 --- a/openpype/modules/ftrack/event_handlers_user/action_create_folders.py +++ b/openpype/modules/ftrack/event_handlers_user/action_create_folders.py @@ -1,11 +1,6 @@ import os from openpype_modules.ftrack.lib import BaseAction, statics_icon -from avalon import lib as avalonlib -from openpype.api import ( - Anatomy, - get_project_settings -) -from openpype.lib import ApplicationManager +from openpype.api import Anatomy class CreateFolders(BaseAction): diff --git a/openpype/modules/python_console_interpreter/window/widgets.py b/openpype/modules/python_console_interpreter/window/widgets.py index ecf41eaf3e..36ce1b61a2 100644 --- a/openpype/modules/python_console_interpreter/window/widgets.py +++ b/openpype/modules/python_console_interpreter/window/widgets.py @@ -389,7 +389,8 @@ class PythonInterpreterWidget(QtWidgets.QWidget): self._append_lines([openpype_art]) - self.setStyleSheet(load_stylesheet()) + self._first_show = True + self._splitter_size_ratio = None self._init_from_registry() @@ -416,9 +417,9 @@ class PythonInterpreterWidget(QtWidgets.QWidget): self.resize(width, height) try: - sizes = setting_registry.get_item("splitter_sizes") - if len(sizes) == len(self._widgets_splitter.sizes()): - self._widgets_splitter.setSizes(sizes) + self._splitter_size_ratio = ( + setting_registry.get_item("splitter_sizes") + ) except ValueError: pass @@ -627,8 +628,29 @@ class PythonInterpreterWidget(QtWidgets.QWidget): def showEvent(self, event): self._line_check_timer.start() super(PythonInterpreterWidget, self).showEvent(event) + # First show setup + if self._first_show: + self._first_show = False + self._on_first_show() + self._output_widget.scroll_to_bottom() + def _on_first_show(self): + # Change stylesheet + self.setStyleSheet(load_stylesheet()) + # Check if splitter size ratio is set + # - first store value to local variable and then unset it + splitter_size_ratio = self._splitter_size_ratio + self._splitter_size_ratio = None + # Skip if is not set + if not splitter_size_ratio: + return + + # Skip if number of size items does not match to splitter + splitters_count = len(self._widgets_splitter.sizes()) + if len(splitter_size_ratio) == splitters_count: + self._widgets_splitter.setSizes(splitter_size_ratio) + def closeEvent(self, event): self.save_registry() super(PythonInterpreterWidget, self).closeEvent(event) diff --git a/openpype/plugins/publish/validate_aseset_docs.py b/openpype/plugins/publish/validate_asset_docs.py similarity index 69% rename from openpype/plugins/publish/validate_aseset_docs.py rename to openpype/plugins/publish/validate_asset_docs.py index eed75cdf8a..bc1f9b9e6c 100644 --- a/openpype/plugins/publish/validate_aseset_docs.py +++ b/openpype/plugins/publish/validate_asset_docs.py @@ -2,8 +2,8 @@ import pyblish.api from openpype.pipeline import PublishValidationError -class ValidateContainers(pyblish.api.InstancePlugin): - """Validate existence of asset asset documents on instances. +class ValidateAssetDocs(pyblish.api.InstancePlugin): + """Validate existence of asset documents on instances. Without asset document it is not possible to publish the instance. @@ -22,10 +22,10 @@ class ValidateContainers(pyblish.api.InstancePlugin): return if instance.data.get("assetEntity"): - self.log.info("Instance have set asset document in it's data.") + self.log.info("Instance has set asset document in its data.") else: raise PublishValidationError(( - "Instance \"{}\" don't have set asset" - " document which is needed for publishing." + "Instance \"{}\" doesn't have asset document " + "set which is needed for publishing." ).format(instance.data["name"])) diff --git a/openpype/settings/defaults/project_settings/deadline.json b/openpype/settings/defaults/project_settings/deadline.json index efaaa07be6..1ef169e387 100644 --- a/openpype/settings/defaults/project_settings/deadline.json +++ b/openpype/settings/defaults/project_settings/deadline.json @@ -15,33 +15,6 @@ "deadline" ] }, - "ProcessSubmittedJobOnFarm": { - "enabled": true, - "deadline_department": "", - "deadline_pool": "", - "deadline_group": "", - "deadline_chunk_size": 1, - "deadline_priority": 50, - "publishing_script": "", - "skip_integration_repre_list": [], - "aov_filter": { - "maya": [ - ".+(?:\\.|_)([Bb]eauty)(?:\\.|_).*" - ], - "nuke": [ - ".*" - ], - "aftereffects": [ - ".*" - ], - "celaction": [ - ".*" - ], - "harmony": [ - ".*" - ] - } - }, "MayaSubmitDeadline": { "enabled": true, "optional": false, @@ -49,6 +22,8 @@ "tile_assembler_plugin": "OpenPypeTileAssembler", "use_published": true, "asset_dependencies": true, + "priority": 50, + "tile_priority": 50, "group": "none", "limit": [], "jobInfo": {}, @@ -96,6 +71,33 @@ "group": "", "department": "", "multiprocess": true + }, + "ProcessSubmittedJobOnFarm": { + "enabled": true, + "deadline_department": "", + "deadline_pool": "", + "deadline_group": "", + "deadline_chunk_size": 1, + "deadline_priority": 50, + "publishing_script": "", + "skip_integration_repre_list": [], + "aov_filter": { + "maya": [ + ".+(?:\\.|_)([Bb]eauty)(?:\\.|_).*" + ], + "nuke": [ + ".*" + ], + "aftereffects": [ + ".*" + ], + "celaction": [ + ".*" + ], + "harmony": [ + ".*" + ] + } } } } \ No newline at end of file diff --git a/openpype/settings/defaults/project_settings/global.json b/openpype/settings/defaults/project_settings/global.json index 4c94eee254..58659d5d41 100644 --- a/openpype/settings/defaults/project_settings/global.json +++ b/openpype/settings/defaults/project_settings/global.json @@ -279,6 +279,15 @@ "tasks": [], "template": "{family}{variant}" }, + { + "families": [ + "workfile" + ], + "hosts": [], + "task_types": [], + "tasks": [], + "template": "{family}{Task}" + }, { "families": [ "render" diff --git a/openpype/settings/entities/schemas/projects_schema/schema_project_deadline.json b/openpype/settings/entities/schemas/projects_schema/schema_project_deadline.json index ea1173313b..5bf0a81a4d 100644 --- a/openpype/settings/entities/schemas/projects_schema/schema_project_deadline.json +++ b/openpype/settings/entities/schemas/projects_schema/schema_project_deadline.json @@ -117,6 +117,16 @@ "key": "asset_dependencies", "label": "Use Asset dependencies" }, + { + "type": "number", + "key": "priority", + "label": "Priority" + }, + { + "type": "number", + "key": "tile_priority", + "label": "Tile Assembler Priority" + }, { "type": "text", "key": "group", diff --git a/tests/lib/assert_classes.py b/tests/lib/assert_classes.py index 98f758767d..7f4d8efc10 100644 --- a/tests/lib/assert_classes.py +++ b/tests/lib/assert_classes.py @@ -24,16 +24,18 @@ class DBAssert: else: args[key] = val - msg = None - no_of_docs = dbcon.count_documents(args) - if expected != no_of_docs: - msg = "Not expected no of versions. "\ - "Expected {}, found {}".format(expected, no_of_docs) - args.pop("type") detail_str = " " if args: - detail_str = " with {}".format(args) + detail_str = " with '{}'".format(args) + + msg = None + no_of_docs = dbcon.count_documents(args) + if expected != no_of_docs: + msg = "Not expected no of '{}'{}."\ + "Expected {}, found {}".format(queried_type, + detail_str, + expected, no_of_docs) status = "successful" if msg: @@ -42,7 +44,5 @@ class DBAssert: print("Comparing count of {}{} {}".format(queried_type, detail_str, status)) - if msg: - print(msg) return msg