diff --git a/openpype/hosts/harmony/plugins/publish/collect_farm_render.py b/openpype/hosts/harmony/plugins/publish/collect_farm_render.py index fc80e7c029e..31a249591e3 100644 --- a/openpype/hosts/harmony/plugins/publish/collect_farm_render.py +++ b/openpype/hosts/harmony/plugins/publish/collect_farm_render.py @@ -126,7 +126,8 @@ def get_instances(self, context): # because of using 'renderFarm' as a family, replace 'Farm' with # capitalized task name - issue of avalon-core Creator app subset_name = node.split("/")[1] - task_name = context.data["anatomyData"]["task"].capitalize() + task_name = context.data["anatomyData"]["task"][ + "name"].capitalize() replace_str = "" if task_name.lower() not in subset_name.lower(): replace_str = task_name diff --git a/openpype/hosts/harmony/plugins/publish/collect_palettes.py b/openpype/hosts/harmony/plugins/publish/collect_palettes.py index b8671badb38..e47cbaf17e6 100644 --- a/openpype/hosts/harmony/plugins/publish/collect_palettes.py +++ b/openpype/hosts/harmony/plugins/publish/collect_palettes.py @@ -28,7 +28,7 @@ def process(self, context): # skip collecting if not in allowed task if self.allowed_tasks: - task_name = context.data["anatomyData"]["task"].lower() + task_name = context.data["anatomyData"]["task"]["name"].lower() if (not any([re.search(pattern, task_name) for pattern in self.allowed_tasks])): return diff --git a/openpype/hosts/standalonepublisher/plugins/publish/collect_harmony_scenes.py b/openpype/hosts/standalonepublisher/plugins/publish/collect_harmony_scenes.py index a4fed3bc3f8..48c36aa067e 100644 --- a/openpype/hosts/standalonepublisher/plugins/publish/collect_harmony_scenes.py +++ b/openpype/hosts/standalonepublisher/plugins/publish/collect_harmony_scenes.py @@ -49,10 +49,22 @@ def process(self, instance): # fix anatomy data anatomy_data_new = copy.deepcopy(anatomy_data) + + project_entity = context.data["projectEntity"] + asset_entity = context.data["assetEntity"] + + task_type = asset_entity["data"]["tasks"].get(task, {}).get("type") + project_task_types = project_entity["config"]["tasks"] + task_code = project_task_types.get(task_type, {}).get("short_name") + # updating hierarchy data anatomy_data_new.update({ "asset": asset_data["name"], - "task": task, + "task": { + "name": task, + "type": task_type, + "short": task_code, + }, "subset": subset_name }) diff --git a/openpype/hosts/standalonepublisher/plugins/publish/collect_harmony_zips.py b/openpype/hosts/standalonepublisher/plugins/publish/collect_harmony_zips.py index 93eff854863..40a969f8df3 100644 --- a/openpype/hosts/standalonepublisher/plugins/publish/collect_harmony_zips.py +++ b/openpype/hosts/standalonepublisher/plugins/publish/collect_harmony_zips.py @@ -27,6 +27,7 @@ def process(self, instance): anatomy_data = instance.context.data["anatomyData"] repres = instance.data["representations"] files = repres[0]["files"] + project_entity = context.data["projectEntity"] if files.endswith(".zip"): # A zip file was dropped @@ -45,14 +46,24 @@ def process(self, instance): self.log.info("Copied data: {}".format(new_instance.data)) + task_type = asset_data["data"]["tasks"].get(task, {}).get("type") + project_task_types = project_entity["config"]["tasks"] + task_code = project_task_types.get(task_type, {}).get("short_name") + # fix anatomy data anatomy_data_new = copy.deepcopy(anatomy_data) # updating hierarchy data - anatomy_data_new.update({ - "asset": asset_data["name"], - "task": task, - "subset": subset_name - }) + anatomy_data_new.update( + { + "asset": asset_data["name"], + "task": { + "name": task, + "type": task_type, + "short": task_code, + }, + "subset": subset_name + } + ) new_instance.data["label"] = f"{instance_name}" new_instance.data["subset"] = subset_name diff --git a/openpype/hosts/standalonepublisher/plugins/publish/extract_harmony_zip.py b/openpype/hosts/standalonepublisher/plugins/publish/extract_harmony_zip.py index adbac6ef09a..ceac710bb5e 100644 --- a/openpype/hosts/standalonepublisher/plugins/publish/extract_harmony_zip.py +++ b/openpype/hosts/standalonepublisher/plugins/publish/extract_harmony_zip.py @@ -11,7 +11,7 @@ import pyblish.api from avalon import api, io import openpype.api -from openpype.lib import get_workfile_template_key_from_context +from openpype.lib import get_workfile_template_key class ExtractHarmonyZip(openpype.api.Extractor): @@ -31,8 +31,10 @@ class ExtractHarmonyZip(openpype.api.Extractor): # Presets create_workfile = True - default_task = "harmonyIngest" - default_task_type = "Ingest" + default_task = { + "name": "harmonyIngest", + "type": "Ingest", + } default_task_status = "Ingested" assetversion_status = "Ingested" @@ -219,6 +221,19 @@ def extract_workfile(self, instance, staging_scene): # Setup the data needed to form a valid work path filename anatomy = openpype.api.Anatomy() project_entity = instance.context.data["projectEntity"] + asset_entity = io.find_one({ + "type": "asset", + "name": instance.data["asset"] + }) + + task_name = instance.data.get("task") + task_type = asset_entity["data"]["tasks"][task_name].get("type") + + if task_type: + task_short = project_entity["config"]["tasks"].get( + task_type, {}).get("short_name") + else: + task_short = None data = { "root": api.registered_root(), @@ -229,18 +244,20 @@ def extract_workfile(self, instance, staging_scene): "asset": instance.data["asset"], "hierarchy": openpype.api.get_hierarchy(instance.data["asset"]), "family": instance.data["family"], - "task": instance.data.get("task"), + "task": { + "name": task_name, + "type": task_type, + "short": task_short, + }, "subset": instance.data["subset"], "version": 1, "ext": "zip", } host_name = "harmony" - template_name = get_workfile_template_key_from_context( - instance.data["asset"], - instance.data.get("task"), + template_name = get_workfile_template_key( + instance.data.get("task").get("type"), host_name, project_name=project_entity["name"], - dbcon=io ) # Get a valid work filename first with version 1 diff --git a/openpype/lib/anatomy.py b/openpype/lib/anatomy.py index 7a4a55363cf..aaf10479fd2 100644 --- a/openpype/lib/anatomy.py +++ b/openpype/lib/anatomy.py @@ -989,6 +989,14 @@ def _format(self, orig_template, data): invalid_required = [] missing_required = [] replace_keys = [] + + task_data = data.get("task") + if ( + isinstance(task_data, StringType) + and "{task[name]}" in orig_template + ): + data["task"] = {"name": task_data} + for group in self.key_pattern.findall(template): orig_key = group[1:-1] key = str(orig_key) @@ -1074,6 +1082,10 @@ def solve_dict(self, templates, data): output = collections.defaultdict(dict) for key, orig_value in templates.items(): if isinstance(orig_value, StringType): + # Replace {task} by '{task[name]}' for backward compatibility + if '{task}' in orig_value: + orig_value = orig_value.replace('{task}', '{task[name]}') + output[key] = self._format(orig_value, data) continue diff --git a/openpype/lib/applications.py b/openpype/lib/applications.py index cc8cb8e7be8..e190c38fd94 100644 --- a/openpype/lib/applications.py +++ b/openpype/lib/applications.py @@ -1246,23 +1246,12 @@ def prepare_context_environments(data): anatomy = data["anatomy"] - asset_tasks = asset_doc.get("data", {}).get("tasks") or {} - task_info = asset_tasks.get(task_name) or {} - task_type = task_info.get("type") + task_type = workdir_data["task"]["type"] # Temp solution how to pass task type to `_prepare_last_workfile` data["task_type"] = task_type - workfile_template_key = get_workfile_template_key( - task_type, - app.host_name, - project_name=project_name, - project_settings=project_settings - ) - try: - workdir = get_workdir_with_workdir_data( - workdir_data, anatomy, template_key=workfile_template_key - ) + workdir = get_workdir_with_workdir_data(workdir_data, anatomy) except Exception as exc: raise ApplicationLaunchFailed( @@ -1295,10 +1284,10 @@ def prepare_context_environments(data): ) data["env"].update(context_env) - _prepare_last_workfile(data, workdir, workfile_template_key) + _prepare_last_workfile(data, workdir) -def _prepare_last_workfile(data, workdir, workfile_template_key): +def _prepare_last_workfile(data, workdir): """last workfile workflow preparation. Function check if should care about last workfile workflow and tries @@ -1361,6 +1350,10 @@ def _prepare_last_workfile(data, workdir, workfile_template_key): anatomy = data["anatomy"] # Find last workfile file_template = anatomy.templates["work"]["file"] + # Replace {task} by '{task[name]}' for backward compatibility + if '{task}' in file_template: + file_template = file_template.replace('{task}', '{task[name]}') + workdir_data.update({ "version": 1, "user": get_openpype_username(), diff --git a/openpype/lib/avalon_context.py b/openpype/lib/avalon_context.py index b043cbfdb43..dd9e8b2ab86 100644 --- a/openpype/lib/avalon_context.py +++ b/openpype/lib/avalon_context.py @@ -7,6 +7,7 @@ import logging import collections import functools +import getpass from openpype.settings import get_project_settings from .anatomy import Anatomy @@ -479,22 +480,31 @@ def get_workdir_data(project_doc, asset_doc, task_name, host_name): """ hierarchy = "/".join(asset_doc["data"]["parents"]) + task_type = asset_doc['data']['tasks'].get(task_name, {}).get('type') + + project_task_types = project_doc["config"]["tasks"] + task_code = project_task_types.get(task_type, {}).get("short_name") + data = { "project": { "name": project_doc["name"], "code": project_doc["data"].get("code") }, - "task": task_name, + "task": { + "name": task_name, + "type": task_type, + "short": task_code, + }, "asset": asset_doc["name"], "app": host_name, - "hierarchy": hierarchy + "user": getpass.getuser(), + "hierarchy": hierarchy, } return data def get_workdir_with_workdir_data( - workdir_data, anatomy=None, project_name=None, - template_key=None, dbcon=None + workdir_data, anatomy=None, project_name=None, template_key=None ): """Fill workdir path from entered data and project's anatomy. @@ -529,12 +539,10 @@ def get_workdir_with_workdir_data( anatomy = Anatomy(project_name) if not template_key: - template_key = get_workfile_template_key_from_context( - workdir_data["asset"], - workdir_data["task"], + template_key = get_workfile_template_key( + workdir_data["task"]["type"], workdir_data["app"], - project_name=workdir_data["project"]["name"], - dbcon=dbcon + project_name=workdir_data["project"]["name"] ) anatomy_filled = anatomy.format(workdir_data) @@ -648,7 +656,7 @@ def create_workfile_doc(asset_doc, task_name, filename, workdir, dbcon=None): anatomy = Anatomy(project_doc["name"]) # Get workdir path (result is anatomy.TemplateResult) template_workdir = get_workdir_with_workdir_data( - workdir_data, anatomy, dbcon=dbcon + workdir_data, anatomy ) template_workdir_path = str(template_workdir).replace("\\", "/") diff --git a/openpype/plugins/publish/collect_anatomy_context_data.py b/openpype/plugins/publish/collect_anatomy_context_data.py index ec88d5669d4..6b95979b76f 100644 --- a/openpype/plugins/publish/collect_anatomy_context_data.py +++ b/openpype/plugins/publish/collect_anatomy_context_data.py @@ -54,6 +54,12 @@ def process(self, context): if hierarchy_items: hierarchy = os.path.join(*hierarchy_items) + asset_tasks = asset_entity["data"]["tasks"] + task_type = asset_tasks.get(task_name, {}).get("type") + + project_task_types = project_entity["config"]["tasks"] + task_code = project_task_types.get(task_type, {}).get("short_name") + context_data = { "project": { "name": project_entity["name"], @@ -61,7 +67,11 @@ def process(self, context): }, "asset": asset_entity["name"], "hierarchy": hierarchy.replace("\\", "/"), - "task": task_name, + "task": { + "name": task_name, + "type": task_type, + "short": task_code, + }, "username": context.data["user"], "app": context.data["hostName"] } diff --git a/openpype/plugins/publish/collect_anatomy_instance_data.py b/openpype/plugins/publish/collect_anatomy_instance_data.py index 4fd657167cf..286a14485f8 100644 --- a/openpype/plugins/publish/collect_anatomy_instance_data.py +++ b/openpype/plugins/publish/collect_anatomy_instance_data.py @@ -212,6 +212,8 @@ def fill_anatomy_data(self, context): project_doc = context.data["projectEntity"] context_asset_doc = context.data["assetEntity"] + project_task_types = project_doc["config"]["tasks"] + for instance in context: version_number = instance.data.get("version") # If version is not specified for instance or context @@ -240,7 +242,18 @@ def fill_anatomy_data(self, context): # Task task_name = instance.data.get("task") if task_name: - anatomy_updates["task"] = task_name + asset_tasks = asset_doc["data"]["tasks"] + task_type = asset_tasks.get(task_name, {}).get("type") + task_code = ( + project_task_types + .get(task_type, {}) + .get("short_name") + ) + anatomy_updates["task"] = { + "name": task_name, + "type": task_type, + "short": task_code + } # Additional data resolution_width = instance.data.get("resolutionWidth") diff --git a/openpype/plugins/publish/extract_burnin.py b/openpype/plugins/publish/extract_burnin.py index 06eb85c593c..cbebed927a1 100644 --- a/openpype/plugins/publish/extract_burnin.py +++ b/openpype/plugins/publish/extract_burnin.py @@ -184,7 +184,9 @@ def main_process(self, instance): for key in self.positions: value = burnin_def.get(key) if value: - burnin_values[key] = value + burnin_values[key] = value.replace( + "{task}", "{task[name]}" + ) # Remove "delete" tag from new representation if "delete" in new_repre["tags"]: diff --git a/openpype/plugins/publish/integrate_new.py b/openpype/plugins/publish/integrate_new.py index 451ea1d80d8..0898275cd80 100644 --- a/openpype/plugins/publish/integrate_new.py +++ b/openpype/plugins/publish/integrate_new.py @@ -171,21 +171,26 @@ def register(self, instance): anatomy_data["hierarchy"] = hierarchy # Make sure task name in anatomy data is same as on instance.data + asset_tasks = ( + asset_entity.get("data", {}).get("tasks") + ) or {} task_name = instance.data.get("task") if task_name: - anatomy_data["task"] = task_name + task_info = asset_tasks.get(task_name) or {} + task_type = task_info.get("type") + + project_task_types = project_entity["config"]["tasks"] + task_code = project_task_types.get(task_type, {}).get("short_name") + anatomy_data["task"] = { + "name": task_name, + "type": task_type, + "short": task_code + } + else: # Just set 'task_name' variable to context task - task_name = anatomy_data["task"] - - # Find task type for current task name - # - this should be already prepared on instance - asset_tasks = ( - asset_entity.get("data", {}).get("tasks") - ) or {} - task_info = asset_tasks.get(task_name) or {} - task_type = task_info.get("type") - instance.data["task_type"] = task_type + task_name = anatomy_data["task"]["name"] + task_type = anatomy_data["task"]["type"] # Fill family in anatomy data anatomy_data["family"] = instance.data.get("family") @@ -803,11 +808,8 @@ def _get_subset_group(self, instance): # - is there a chance that task name is not filled in anatomy # data? # - should we use context task in that case? - task_name = ( - instance.data["anatomyData"]["task"] - or io.Session["AVALON_TASK"] - ) - task_type = instance.data["task_type"] + task_name = instance.data["anatomyData"]["task"]["name"] + task_type = instance.data["anatomyData"]["task"]["type"] filtering_criteria = { "families": instance.data["family"], "hosts": instance.context.data["hostName"], diff --git a/openpype/settings/defaults/project_anatomy/templates.json b/openpype/settings/defaults/project_anatomy/templates.json index 53abd35ed56..9a03b893bf3 100644 --- a/openpype/settings/defaults/project_anatomy/templates.json +++ b/openpype/settings/defaults/project_anatomy/templates.json @@ -6,8 +6,8 @@ "frame": "{frame:0>{@frame_padding}}" }, "work": { - "folder": "{root[work]}/{project[name]}/{hierarchy}/{asset}/work/{task}", - "file": "{project[code]}_{asset}_{task}_{@version}<_{comment}>.{ext}", + "folder": "{root[work]}/{project[name]}/{hierarchy}/{asset}/work/{task[name]}", + "file": "{project[code]}_{asset}_{task[name]}_{@version}<_{comment}>.{ext}", "path": "{@folder}/{@file}" }, "render": { diff --git a/openpype/settings/entities/schemas/projects_schema/schemas/schema_anatomy_templates.json b/openpype/settings/entities/schemas/projects_schema/schemas/schema_anatomy_templates.json index a8534e7e294..e208069e6f1 100644 --- a/openpype/settings/entities/schemas/projects_schema/schemas/schema_anatomy_templates.json +++ b/openpype/settings/entities/schemas/projects_schema/schemas/schema_anatomy_templates.json @@ -11,6 +11,10 @@ "type": "dict", "key": "defaults", "children": [ + { + "type": "label", + "label": "The list of existing placeholders is available here:
https://openpype.io/docs/admin_settings_project_anatomy/#available-template-keys " + }, { "type": "number", "key": "version_padding", diff --git a/openpype/settings/lib.py b/openpype/settings/lib.py index 60ed54bd4a2..ff755624132 100644 --- a/openpype/settings/lib.py +++ b/openpype/settings/lib.py @@ -856,6 +856,7 @@ def get_anatomy_settings( apply_local_settings_on_anatomy_settings( result, local_settings, project_name, site_name ) + return result diff --git a/openpype/tools/workfiles/app.py b/openpype/tools/workfiles/app.py index 6fff0d02780..314ec0a0e16 100644 --- a/openpype/tools/workfiles/app.py +++ b/openpype/tools/workfiles/app.py @@ -61,20 +61,39 @@ def __init__(self, parent, root, anatomy, template_key, session=None): # Set work file data for template formatting asset_name = session["AVALON_ASSET"] + task_name = session["AVALON_TASK"] project_doc = io.find_one( {"type": "project"}, { "name": True, - "data.code": True + "data.code": True, + "config.tasks": True, } ) + asset_doc = io.find_one( + { + "type": "asset", + "name": asset_name + }, + {"data.tasks": True} + ) + + task_type = asset_doc["data"]["tasks"].get(task_name, {}).get("type") + + project_task_types = project_doc["config"]["tasks"] + task_short = project_task_types.get(task_type, {}).get("short_name") + self.data = { "project": { "name": project_doc["name"], "code": project_doc["data"].get("code") }, "asset": asset_name, - "task": session["AVALON_TASK"], + "task": { + "name": task_name, + "type": task_type, + "short": task_short, + }, "version": 1, "user": getpass.getuser(), "comment": "", diff --git a/website/docs/admin_settings_project_anatomy.md b/website/docs/admin_settings_project_anatomy.md index 54023d468f7..30784686e26 100644 --- a/website/docs/admin_settings_project_anatomy.md +++ b/website/docs/admin_settings_project_anatomy.md @@ -57,7 +57,9 @@ We have a few required anatomy templates for OpenPype to work properly, however | `project[code]` | Project's code | | `hierarchy` | All hierarchical parents as subfolders | | `asset` | Name of asset or shot | -| `task` | Name of task | +| `task[name]` | Name of task | +| `task[type]` | Type of task | +| `task[short]` | Shortname of task | | `version` | Version number | | `subset` | Subset name | | `family` | Main family name |