mirror of
https://github.com/ynput/ayon-core.git
synced 2025-12-24 12:54:40 +01:00
Merge branch 'develop' into enhancement/1416-loader-actions
# Conflicts: # client/ayon_core/plugins/load/push_to_project.py
This commit is contained in:
commit
4c25826a9c
12 changed files with 277 additions and 151 deletions
1
.github/ISSUE_TEMPLATE/bug_report.yml
vendored
1
.github/ISSUE_TEMPLATE/bug_report.yml
vendored
|
|
@ -35,6 +35,7 @@ body:
|
|||
label: Version
|
||||
description: What version are you running? Look to AYON Tray
|
||||
options:
|
||||
- 1.6.0
|
||||
- 1.5.3
|
||||
- 1.5.2
|
||||
- 1.5.1
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@ class OCIOEnvHook(PreLaunchHook):
|
|||
"fusion",
|
||||
"blender",
|
||||
"aftereffects",
|
||||
"3dsmax",
|
||||
"max",
|
||||
"houdini",
|
||||
"maya",
|
||||
"nuke",
|
||||
|
|
|
|||
|
|
@ -18,12 +18,25 @@ class PushToProject(LoaderActionPlugin):
|
|||
def get_action_items(
|
||||
self, selection: LoaderActionSelection
|
||||
) -> list[LoaderActionItem]:
|
||||
folder_ids = set()
|
||||
version_ids = set()
|
||||
if selection.selected_type == "version":
|
||||
version_ids = set(selection.selected_ids)
|
||||
product_ids = {
|
||||
product["id"]
|
||||
for product in selection.entities.get_versions_products(
|
||||
version_ids
|
||||
)
|
||||
}
|
||||
folder_ids = {
|
||||
folder["id"]
|
||||
for folder in selection.entities.get_products_folders(
|
||||
product_ids
|
||||
)
|
||||
}
|
||||
|
||||
output = []
|
||||
if len(version_ids) == 1:
|
||||
if version_ids and len(folder_ids) == 1:
|
||||
output.append(
|
||||
LoaderActionItem(
|
||||
identifier="core.push-to-project",
|
||||
|
|
@ -61,12 +74,10 @@ class PushToProject(LoaderActionPlugin):
|
|||
"main.py"
|
||||
)
|
||||
|
||||
version_id = next(iter(entity_ids))
|
||||
|
||||
args = get_ayon_launcher_args(
|
||||
push_tool_script_path,
|
||||
"--project", selection.project_name,
|
||||
"--version", version_id
|
||||
"--versions", ",".join(entity_ids)
|
||||
)
|
||||
run_detached_process(args)
|
||||
return LoaderActionResult(
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
import threading
|
||||
from typing import Dict
|
||||
|
||||
import ayon_api
|
||||
|
||||
|
|
@ -13,10 +14,11 @@ from .models import (
|
|||
UserPublishValuesModel,
|
||||
IntegrateModel,
|
||||
)
|
||||
from .models.integrate import ProjectPushItemProcess
|
||||
|
||||
|
||||
class PushToContextController:
|
||||
def __init__(self, project_name=None, version_id=None):
|
||||
def __init__(self, project_name=None, version_ids=None):
|
||||
self._event_system = self._create_event_system()
|
||||
|
||||
self._projects_model = ProjectsModel(self)
|
||||
|
|
@ -27,18 +29,20 @@ class PushToContextController:
|
|||
self._user_values = UserPublishValuesModel(self)
|
||||
|
||||
self._src_project_name = None
|
||||
self._src_version_id = None
|
||||
self._src_version_ids = []
|
||||
self._src_folder_entity = None
|
||||
self._src_folder_task_entities = {}
|
||||
self._src_product_entity = None
|
||||
self._src_version_entity = None
|
||||
self._src_version_entities = []
|
||||
self._src_product_entities = {}
|
||||
self._src_label = None
|
||||
|
||||
self._submission_enabled = False
|
||||
self._process_thread = None
|
||||
self._process_item_id = None
|
||||
|
||||
self.set_source(project_name, version_id)
|
||||
self._use_original_name = False
|
||||
|
||||
self.set_source(project_name, version_ids)
|
||||
|
||||
# Events system
|
||||
def emit_event(self, topic, data=None, source=None):
|
||||
|
|
@ -51,38 +55,47 @@ class PushToContextController:
|
|||
def register_event_callback(self, topic, callback):
|
||||
self._event_system.add_callback(topic, callback)
|
||||
|
||||
def set_source(self, project_name, version_id):
|
||||
def set_source(self, project_name, version_ids):
|
||||
"""Set source project and version.
|
||||
|
||||
There is currently assumption that tool is working on products of same
|
||||
folder.
|
||||
|
||||
Args:
|
||||
project_name (Union[str, None]): Source project name.
|
||||
version_id (Union[str, None]): Source version id.
|
||||
version_ids (Optional[list[str]]): Version ids.
|
||||
"""
|
||||
|
||||
if not project_name or not version_ids:
|
||||
return
|
||||
if (
|
||||
project_name == self._src_project_name
|
||||
and version_id == self._src_version_id
|
||||
and version_ids == self._src_version_ids
|
||||
):
|
||||
return
|
||||
|
||||
self._src_project_name = project_name
|
||||
self._src_version_id = version_id
|
||||
self._src_version_ids = version_ids
|
||||
self._src_label = None
|
||||
folder_entity = None
|
||||
task_entities = {}
|
||||
product_entity = None
|
||||
version_entity = None
|
||||
if project_name and version_id:
|
||||
version_entity = ayon_api.get_version_by_id(
|
||||
project_name, version_id
|
||||
product_entities = []
|
||||
version_entities = []
|
||||
if project_name and self._src_version_ids:
|
||||
version_entities = list(ayon_api.get_versions(
|
||||
project_name, version_ids=self._src_version_ids))
|
||||
|
||||
if version_entities:
|
||||
product_ids = [
|
||||
version_entity["productId"]
|
||||
for version_entity in version_entities
|
||||
]
|
||||
product_entities = list(ayon_api.get_products(
|
||||
project_name, product_ids=product_ids)
|
||||
)
|
||||
|
||||
if version_entity:
|
||||
product_entity = ayon_api.get_product_by_id(
|
||||
project_name, version_entity["productId"]
|
||||
)
|
||||
|
||||
if product_entity:
|
||||
if product_entities:
|
||||
# all products for same folder
|
||||
product_entity = product_entities[0]
|
||||
folder_entity = ayon_api.get_folder_by_id(
|
||||
project_name, product_entity["folderId"]
|
||||
)
|
||||
|
|
@ -97,15 +110,18 @@ class PushToContextController:
|
|||
|
||||
self._src_folder_entity = folder_entity
|
||||
self._src_folder_task_entities = task_entities
|
||||
self._src_product_entity = product_entity
|
||||
self._src_version_entity = version_entity
|
||||
self._src_version_entities = version_entities
|
||||
self._src_product_entities = {
|
||||
product["id"]: product
|
||||
for product in product_entities
|
||||
}
|
||||
if folder_entity:
|
||||
self._user_values.set_new_folder_name(folder_entity["name"])
|
||||
variant = self._get_src_variant()
|
||||
if variant:
|
||||
self._user_values.set_variant(variant)
|
||||
|
||||
comment = version_entity["attrib"].get("comment")
|
||||
comment = version_entities[0]["attrib"].get("comment")
|
||||
if comment:
|
||||
self._user_values.set_comment(comment)
|
||||
|
||||
|
|
@ -113,7 +129,7 @@ class PushToContextController:
|
|||
"source.changed",
|
||||
{
|
||||
"project_name": project_name,
|
||||
"version_id": version_id
|
||||
"version_ids": self._src_version_ids
|
||||
}
|
||||
)
|
||||
|
||||
|
|
@ -142,6 +158,14 @@ class PushToContextController:
|
|||
def get_user_values(self):
|
||||
return self._user_values.get_data()
|
||||
|
||||
def original_names_required(self):
|
||||
"""Checks if original product names must be used.
|
||||
|
||||
Currently simple check if multiple versions, but if multiple products
|
||||
with different product_type were used, it wouldn't be necessary.
|
||||
"""
|
||||
return len(self._src_version_entities) > 1
|
||||
|
||||
def set_user_value_folder_name(self, folder_name):
|
||||
self._user_values.set_new_folder_name(folder_name)
|
||||
self._invalidate()
|
||||
|
|
@ -165,8 +189,9 @@ class PushToContextController:
|
|||
def set_selected_task(self, task_id, task_name):
|
||||
self._selection_model.set_selected_task(task_id, task_name)
|
||||
|
||||
def get_process_item_status(self, item_id):
|
||||
return self._integrate_model.get_item_status(item_id)
|
||||
def get_process_items(self) -> Dict[str, ProjectPushItemProcess]:
|
||||
"""Returns dict of all ProjectPushItemProcess items """
|
||||
return self._integrate_model.get_items()
|
||||
|
||||
# Processing methods
|
||||
def submit(self, wait=True):
|
||||
|
|
@ -176,29 +201,33 @@ class PushToContextController:
|
|||
if self._process_thread is not None:
|
||||
return
|
||||
|
||||
item_id = self._integrate_model.create_process_item(
|
||||
self._src_project_name,
|
||||
self._src_version_id,
|
||||
self._selection_model.get_selected_project_name(),
|
||||
self._selection_model.get_selected_folder_id(),
|
||||
self._selection_model.get_selected_task_name(),
|
||||
self._user_values.variant,
|
||||
comment=self._user_values.comment,
|
||||
new_folder_name=self._user_values.new_folder_name,
|
||||
dst_version=1
|
||||
)
|
||||
item_ids = []
|
||||
for src_version_entity in self._src_version_entities:
|
||||
item_id = self._integrate_model.create_process_item(
|
||||
self._src_project_name,
|
||||
src_version_entity["id"],
|
||||
self._selection_model.get_selected_project_name(),
|
||||
self._selection_model.get_selected_folder_id(),
|
||||
self._selection_model.get_selected_task_name(),
|
||||
self._user_values.variant,
|
||||
comment=self._user_values.comment,
|
||||
new_folder_name=self._user_values.new_folder_name,
|
||||
dst_version=1,
|
||||
use_original_name=self._use_original_name,
|
||||
)
|
||||
item_ids.append(item_id)
|
||||
|
||||
self._process_item_id = item_id
|
||||
self._process_item_ids = item_ids
|
||||
self._emit_event("submit.started")
|
||||
if wait:
|
||||
self._submit_callback()
|
||||
self._process_item_id = None
|
||||
self._process_item_ids = []
|
||||
return item_id
|
||||
|
||||
thread = threading.Thread(target=self._submit_callback)
|
||||
self._process_thread = thread
|
||||
thread.start()
|
||||
return item_id
|
||||
return item_ids
|
||||
|
||||
def wait_for_process_thread(self):
|
||||
if self._process_thread is None:
|
||||
|
|
@ -207,7 +236,7 @@ class PushToContextController:
|
|||
self._process_thread = None
|
||||
|
||||
def _prepare_source_label(self):
|
||||
if not self._src_project_name or not self._src_version_id:
|
||||
if not self._src_project_name or not self._src_version_ids:
|
||||
return "Source is not defined"
|
||||
|
||||
folder_entity = self._src_folder_entity
|
||||
|
|
@ -215,14 +244,21 @@ class PushToContextController:
|
|||
return "Source is invalid"
|
||||
|
||||
folder_path = folder_entity["path"]
|
||||
product_entity = self._src_product_entity
|
||||
version_entity = self._src_version_entity
|
||||
return "Source: {}{}/{}/v{:0>3}".format(
|
||||
self._src_project_name,
|
||||
folder_path,
|
||||
product_entity["name"],
|
||||
version_entity["version"]
|
||||
)
|
||||
src_labels = []
|
||||
for version_entity in self._src_version_entities:
|
||||
product_entity = self._src_product_entities.get(
|
||||
version_entity["productId"]
|
||||
)
|
||||
src_labels.append(
|
||||
"Source: {}{}/{}/v{:0>3}".format(
|
||||
self._src_project_name,
|
||||
folder_path,
|
||||
product_entity["name"],
|
||||
version_entity["version"],
|
||||
)
|
||||
)
|
||||
|
||||
return "\n".join(src_labels)
|
||||
|
||||
def _get_task_info_from_repre_entities(
|
||||
self, task_entities, repre_entities
|
||||
|
|
@ -256,7 +292,8 @@ class PushToContextController:
|
|||
|
||||
def _get_src_variant(self):
|
||||
project_name = self._src_project_name
|
||||
version_entity = self._src_version_entity
|
||||
# parse variant only from first version
|
||||
version_entity = self._src_version_entities[0]
|
||||
task_entities = self._src_folder_task_entities
|
||||
repre_entities = ayon_api.get_representations(
|
||||
project_name, version_ids={version_entity["id"]}
|
||||
|
|
@ -264,9 +301,12 @@ class PushToContextController:
|
|||
task_name, task_type = self._get_task_info_from_repre_entities(
|
||||
task_entities, repre_entities
|
||||
)
|
||||
product_entity = self._src_product_entities.get(
|
||||
version_entity["productId"]
|
||||
)
|
||||
|
||||
project_settings = get_project_settings(project_name)
|
||||
product_type = self._src_product_entity["productType"]
|
||||
product_type = product_entity["productType"]
|
||||
template = get_product_name_template(
|
||||
self._src_project_name,
|
||||
product_type,
|
||||
|
|
@ -300,7 +340,7 @@ class PushToContextController:
|
|||
print("Failed format", exc)
|
||||
return ""
|
||||
|
||||
product_name = self._src_product_entity["name"]
|
||||
product_name = product_entity["name"]
|
||||
if (
|
||||
(product_s and not product_name.startswith(product_s))
|
||||
or (product_e and not product_name.endswith(product_e))
|
||||
|
|
@ -314,9 +354,6 @@ class PushToContextController:
|
|||
return product_name
|
||||
|
||||
def _check_submit_validations(self):
|
||||
if not self._user_values.is_valid:
|
||||
return False
|
||||
|
||||
if not self._selection_model.get_selected_project_name():
|
||||
return False
|
||||
|
||||
|
|
@ -325,6 +362,13 @@ class PushToContextController:
|
|||
and not self._selection_model.get_selected_folder_id()
|
||||
):
|
||||
return False
|
||||
|
||||
if self._use_original_name:
|
||||
return True
|
||||
|
||||
if not self._user_values.is_valid:
|
||||
return False
|
||||
|
||||
return True
|
||||
|
||||
def _invalidate(self):
|
||||
|
|
@ -338,13 +382,14 @@ class PushToContextController:
|
|||
)
|
||||
|
||||
def _submit_callback(self):
|
||||
process_item_id = self._process_item_id
|
||||
if process_item_id is None:
|
||||
return
|
||||
self._integrate_model.integrate_item(process_item_id)
|
||||
process_item_ids = self._process_item_ids
|
||||
for process_item_id in process_item_ids:
|
||||
self._integrate_model.integrate_item(process_item_id)
|
||||
|
||||
self._emit_event("submit.finished", {})
|
||||
if process_item_id == self._process_item_id:
|
||||
self._process_item_id = None
|
||||
|
||||
if process_item_ids is self._process_item_ids:
|
||||
self._process_item_ids = []
|
||||
|
||||
def _emit_event(self, topic, data=None):
|
||||
if data is None:
|
||||
|
|
|
|||
|
|
@ -4,28 +4,28 @@ from ayon_core.tools.utils import get_ayon_qt_app
|
|||
from ayon_core.tools.push_to_project.ui import PushToContextSelectWindow
|
||||
|
||||
|
||||
def main_show(project_name, version_id):
|
||||
def main_show(project_name, version_ids):
|
||||
app = get_ayon_qt_app()
|
||||
|
||||
window = PushToContextSelectWindow()
|
||||
window.show()
|
||||
window.set_source(project_name, version_id)
|
||||
window.set_source(project_name, version_ids)
|
||||
|
||||
app.exec_()
|
||||
|
||||
|
||||
@click.command()
|
||||
@click.option("--project", help="Source project name")
|
||||
@click.option("--version", help="Source version id")
|
||||
def main(project, version):
|
||||
@click.option("--versions", help="Source version ids")
|
||||
def main(project, versions):
|
||||
"""Run PushToProject tool to integrate version in different project.
|
||||
|
||||
Args:
|
||||
project (str): Source project name.
|
||||
version (str): Version id.
|
||||
versions (str): comma separated versions for same context
|
||||
"""
|
||||
|
||||
main_show(project, version)
|
||||
main_show(project, versions.split(","))
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ import itertools
|
|||
import sys
|
||||
import traceback
|
||||
import uuid
|
||||
from typing import Optional
|
||||
from typing import Optional, Dict
|
||||
|
||||
import ayon_api
|
||||
from ayon_api.utils import create_entity_id
|
||||
|
|
@ -90,6 +90,7 @@ class ProjectPushItem:
|
|||
new_folder_name,
|
||||
dst_version,
|
||||
item_id=None,
|
||||
use_original_name=False
|
||||
):
|
||||
if not item_id:
|
||||
item_id = uuid.uuid4().hex
|
||||
|
|
@ -104,6 +105,7 @@ class ProjectPushItem:
|
|||
self.comment = comment or ""
|
||||
self.item_id = item_id
|
||||
self._repr_value = None
|
||||
self.use_original_name = use_original_name
|
||||
|
||||
@property
|
||||
def _repr(self):
|
||||
|
|
@ -115,7 +117,8 @@ class ProjectPushItem:
|
|||
str(self.dst_folder_id),
|
||||
str(self.new_folder_name),
|
||||
str(self.dst_task_name),
|
||||
str(self.dst_version)
|
||||
str(self.dst_version),
|
||||
self.use_original_name
|
||||
])
|
||||
return self._repr_value
|
||||
|
||||
|
|
@ -134,6 +137,7 @@ class ProjectPushItem:
|
|||
"comment": self.comment,
|
||||
"new_folder_name": self.new_folder_name,
|
||||
"item_id": self.item_id,
|
||||
"use_original_name": self.use_original_name
|
||||
}
|
||||
|
||||
@classmethod
|
||||
|
|
@ -313,7 +317,7 @@ class ProjectPushRepreItem:
|
|||
if self._src_files is not None:
|
||||
return self._src_files, self._resource_files
|
||||
|
||||
repre_context = self._repre_entity["context"]
|
||||
repre_context = self.repre_entity["context"]
|
||||
if "frame" in repre_context or "udim" in repre_context:
|
||||
src_files, resource_files = self._get_source_files_with_frames()
|
||||
else:
|
||||
|
|
@ -330,7 +334,7 @@ class ProjectPushRepreItem:
|
|||
udim_placeholder = "__udim__"
|
||||
src_files = []
|
||||
resource_files = []
|
||||
template = self._repre_entity["attrib"]["template"]
|
||||
template = self.repre_entity["attrib"]["template"]
|
||||
# Remove padding from 'udim' and 'frame' formatting keys
|
||||
# - "{frame:0>4}" -> "{frame}"
|
||||
for key in ("udim", "frame"):
|
||||
|
|
@ -338,7 +342,7 @@ class ProjectPushRepreItem:
|
|||
replacement = "{{{}}}".format(key)
|
||||
template = re.sub(sub_part, replacement, template)
|
||||
|
||||
repre_context = self._repre_entity["context"]
|
||||
repre_context = self.repre_entity["context"]
|
||||
fill_repre_context = copy.deepcopy(repre_context)
|
||||
if "frame" in fill_repre_context:
|
||||
fill_repre_context["frame"] = frame_placeholder
|
||||
|
|
@ -359,7 +363,7 @@ class ProjectPushRepreItem:
|
|||
.replace(udim_placeholder, "(?P<udim>[0-9]+)")
|
||||
)
|
||||
src_basename_regex = re.compile("^{}$".format(src_basename))
|
||||
for file_info in self._repre_entity["files"]:
|
||||
for file_info in self.repre_entity["files"]:
|
||||
filepath_template = self._clean_path(file_info["path"])
|
||||
filepath = self._clean_path(
|
||||
filepath_template.format(root=self._roots)
|
||||
|
|
@ -390,8 +394,8 @@ class ProjectPushRepreItem:
|
|||
def _get_source_files(self):
|
||||
src_files = []
|
||||
resource_files = []
|
||||
template = self._repre_entity["attrib"]["template"]
|
||||
repre_context = self._repre_entity["context"]
|
||||
template = self.repre_entity["attrib"]["template"]
|
||||
repre_context = self.repre_entity["context"]
|
||||
fill_repre_context = copy.deepcopy(repre_context)
|
||||
fill_roots = fill_repre_context["root"]
|
||||
for root_name in tuple(fill_roots.keys()):
|
||||
|
|
@ -400,7 +404,7 @@ class ProjectPushRepreItem:
|
|||
fill_repre_context)
|
||||
repre_path = self._clean_path(repre_path)
|
||||
src_dirpath = os.path.dirname(repre_path)
|
||||
for file_info in self._repre_entity["files"]:
|
||||
for file_info in self.repre_entity["files"]:
|
||||
filepath_template = self._clean_path(file_info["path"])
|
||||
filepath = self._clean_path(
|
||||
filepath_template.format(root=self._roots))
|
||||
|
|
@ -493,8 +497,11 @@ class ProjectPushItemProcess:
|
|||
|
||||
except Exception as exc:
|
||||
_exc, _value, _tb = sys.exc_info()
|
||||
product_name = self._src_product_entity["name"]
|
||||
self._status.set_failed(
|
||||
"Unhandled error happened: {}".format(str(exc)),
|
||||
"Unhandled error happened for `{}`: {}".format(
|
||||
product_name, str(exc)
|
||||
),
|
||||
(_exc, _value, _tb)
|
||||
)
|
||||
|
||||
|
|
@ -817,31 +824,34 @@ class ProjectPushItemProcess:
|
|||
self._template_name = template_name
|
||||
|
||||
def _determine_product_name(self):
|
||||
product_type = self._product_type
|
||||
task_info = self._task_info
|
||||
task_name = task_type = None
|
||||
if task_info:
|
||||
task_name = task_info["name"]
|
||||
task_type = task_info["taskType"]
|
||||
if self._item.use_original_name:
|
||||
product_name = self._src_product_entity["name"]
|
||||
else:
|
||||
product_type = self._product_type
|
||||
task_info = self._task_info
|
||||
task_name = task_type = None
|
||||
if task_info:
|
||||
task_name = task_info["name"]
|
||||
task_type = task_info["taskType"]
|
||||
|
||||
try:
|
||||
product_name = get_product_name(
|
||||
self._item.dst_project_name,
|
||||
task_name,
|
||||
task_type,
|
||||
self.host_name,
|
||||
product_type,
|
||||
self._item.variant,
|
||||
project_settings=self._project_settings
|
||||
)
|
||||
except TaskNotSetError:
|
||||
self._status.set_failed(
|
||||
"Target product name template requires task name. To continue"
|
||||
" you have to select target task or change settings"
|
||||
" <b>ayon+settings://core/tools/creator/product_name_profiles"
|
||||
f"?project={self._item.dst_project_name}</b>."
|
||||
)
|
||||
raise PushToProjectError(self._status.fail_reason)
|
||||
try:
|
||||
product_name = get_product_name(
|
||||
self._item.dst_project_name,
|
||||
task_name,
|
||||
task_type,
|
||||
self.host_name,
|
||||
product_type,
|
||||
self._item.variant,
|
||||
project_settings=self._project_settings
|
||||
)
|
||||
except TaskNotSetError:
|
||||
self._status.set_failed(
|
||||
"Target product name template requires task name. To "
|
||||
"continue you have to select target task or change settings " # noqa: E501
|
||||
" <b>ayon+settings://core/tools/creator/product_name_profiles" # noqa: E501
|
||||
f"?project={self._item.dst_project_name}</b>."
|
||||
)
|
||||
raise PushToProjectError(self._status.fail_reason)
|
||||
|
||||
self._log_info(
|
||||
f"Push will be integrating to product with name '{product_name}'"
|
||||
|
|
@ -1012,10 +1022,18 @@ class ProjectPushItemProcess:
|
|||
self, anatomy, template_name, formatting_data, file_template
|
||||
):
|
||||
processed_repre_items = []
|
||||
repre_context = None
|
||||
for repre_item in self._src_repre_items:
|
||||
repre_entity = repre_item.repre_entity
|
||||
repre_name = repre_entity["name"]
|
||||
repre_format_data = copy.deepcopy(formatting_data)
|
||||
|
||||
if not repre_context:
|
||||
repre_context = self._update_repre_context(
|
||||
copy.deepcopy(repre_entity),
|
||||
formatting_data
|
||||
)
|
||||
|
||||
repre_format_data["representation"] = repre_name
|
||||
for src_file in repre_item.src_files:
|
||||
ext = os.path.splitext(src_file.path)[-1]
|
||||
|
|
@ -1031,7 +1049,6 @@ class ProjectPushItemProcess:
|
|||
"publish", template_name, "directory"
|
||||
)
|
||||
folder_path = template_obj.format_strict(formatting_data)
|
||||
repre_context = folder_path.used_values
|
||||
folder_path_rootless = folder_path.rootless
|
||||
repre_filepaths = []
|
||||
published_path = None
|
||||
|
|
@ -1054,7 +1071,6 @@ class ProjectPushItemProcess:
|
|||
)
|
||||
if published_path is None or frame == repre_item.frame:
|
||||
published_path = dst_filepath
|
||||
repre_context.update(filename.used_values)
|
||||
|
||||
repre_filepaths.append((dst_filepath, dst_rootless_path))
|
||||
self._file_transaction.add(src_file.path, dst_filepath)
|
||||
|
|
@ -1141,7 +1157,7 @@ class ProjectPushItemProcess:
|
|||
self._item.dst_project_name,
|
||||
"representation",
|
||||
entity_id,
|
||||
changes
|
||||
changes,
|
||||
)
|
||||
|
||||
existing_repre_names = set(existing_repres_by_low_name.keys())
|
||||
|
|
@ -1171,6 +1187,28 @@ class ProjectPushItemProcess:
|
|||
path
|
||||
)
|
||||
|
||||
def _update_repre_context(self, repre_entity, formatting_data):
|
||||
"""Replace old context value with new ones.
|
||||
|
||||
Folder might change, project definitely changes etc.
|
||||
"""
|
||||
repre_context = repre_entity["context"]
|
||||
for context_key, context_value in repre_context.items():
|
||||
if context_value and isinstance(context_value, dict):
|
||||
for context_sub_key in context_value.keys():
|
||||
value_to_update = formatting_data.get(context_key, {}).get(
|
||||
context_sub_key)
|
||||
if value_to_update:
|
||||
repre_context[context_key][
|
||||
context_sub_key] = value_to_update
|
||||
else:
|
||||
value_to_update = formatting_data.get(context_key)
|
||||
if value_to_update:
|
||||
repre_context[context_key] = value_to_update
|
||||
if "task" not in formatting_data:
|
||||
repre_context.pop("task")
|
||||
return repre_context
|
||||
|
||||
|
||||
class IntegrateModel:
|
||||
def __init__(self, controller):
|
||||
|
|
@ -1194,6 +1232,7 @@ class IntegrateModel:
|
|||
comment,
|
||||
new_folder_name,
|
||||
dst_version,
|
||||
use_original_name
|
||||
):
|
||||
"""Create new item for integration.
|
||||
|
||||
|
|
@ -1207,6 +1246,7 @@ class IntegrateModel:
|
|||
comment (Union[str, None]): Comment.
|
||||
new_folder_name (Union[str, None]): New folder name.
|
||||
dst_version (int): Destination version number.
|
||||
use_original_name (bool): If original product names should be used
|
||||
|
||||
Returns:
|
||||
str: Item id. The id can be used to trigger integration or get
|
||||
|
|
@ -1222,7 +1262,8 @@ class IntegrateModel:
|
|||
variant,
|
||||
comment=comment,
|
||||
new_folder_name=new_folder_name,
|
||||
dst_version=dst_version
|
||||
dst_version=dst_version,
|
||||
use_original_name=use_original_name
|
||||
)
|
||||
process_item = ProjectPushItemProcess(self, item)
|
||||
self._process_items[item.item_id] = process_item
|
||||
|
|
@ -1240,17 +1281,6 @@ class IntegrateModel:
|
|||
return
|
||||
item.integrate()
|
||||
|
||||
def get_item_status(self, item_id):
|
||||
"""Status of an item.
|
||||
|
||||
Args:
|
||||
item_id (str): Item id for which status should be returned.
|
||||
|
||||
Returns:
|
||||
dict[str, Any]: Status data.
|
||||
"""
|
||||
|
||||
item = self._process_items.get(item_id)
|
||||
if item is not None:
|
||||
return item.get_status_data()
|
||||
return None
|
||||
def get_items(self) -> Dict[str, ProjectPushItemProcess]:
|
||||
"""Returns dict of all ProjectPushItemProcess items """
|
||||
return self._process_items
|
||||
|
|
|
|||
|
|
@ -133,6 +133,7 @@ class PushToContextSelectWindow(QtWidgets.QWidget):
|
|||
inputs_widget = QtWidgets.QWidget(main_splitter)
|
||||
|
||||
new_folder_checkbox = NiceCheckbox(True, parent=inputs_widget)
|
||||
original_names_checkbox = NiceCheckbox(False, parent=inputs_widget)
|
||||
|
||||
folder_name_input = PlaceholderLineEdit(inputs_widget)
|
||||
folder_name_input.setPlaceholderText("< Name of new folder >")
|
||||
|
|
@ -151,6 +152,8 @@ class PushToContextSelectWindow(QtWidgets.QWidget):
|
|||
inputs_layout.addRow("Create new folder", new_folder_checkbox)
|
||||
inputs_layout.addRow("New folder name", folder_name_input)
|
||||
inputs_layout.addRow("Variant", variant_input)
|
||||
inputs_layout.addRow(
|
||||
"Use original product names", original_names_checkbox)
|
||||
inputs_layout.addRow("Comment", comment_input)
|
||||
|
||||
main_splitter.addWidget(context_widget)
|
||||
|
|
@ -205,6 +208,10 @@ class PushToContextSelectWindow(QtWidgets.QWidget):
|
|||
show_detail_btn.setToolTip(
|
||||
"Show error detail dialog to copy full error."
|
||||
)
|
||||
original_names_checkbox.setToolTip(
|
||||
"Required for multi copy, doesn't allow changes "
|
||||
"variant values."
|
||||
)
|
||||
|
||||
overlay_close_btn = QtWidgets.QPushButton(
|
||||
"Close", overlay_btns_widget
|
||||
|
|
@ -250,6 +257,8 @@ class PushToContextSelectWindow(QtWidgets.QWidget):
|
|||
variant_input.textChanged.connect(self._on_variant_change)
|
||||
comment_input.textChanged.connect(self._on_comment_change)
|
||||
library_only_checkbox.stateChanged.connect(self._on_library_only_change)
|
||||
original_names_checkbox.stateChanged.connect(
|
||||
self._on_original_names_change)
|
||||
|
||||
publish_btn.clicked.connect(self._on_select_click)
|
||||
cancel_btn.clicked.connect(self._on_close_click)
|
||||
|
|
@ -298,6 +307,7 @@ class PushToContextSelectWindow(QtWidgets.QWidget):
|
|||
self._new_folder_checkbox = new_folder_checkbox
|
||||
self._folder_name_input = folder_name_input
|
||||
self._comment_input = comment_input
|
||||
self._use_original_names_checkbox = original_names_checkbox
|
||||
|
||||
self._publish_btn = publish_btn
|
||||
|
||||
|
|
@ -326,7 +336,6 @@ class PushToContextSelectWindow(QtWidgets.QWidget):
|
|||
self._main_thread_timer = main_thread_timer
|
||||
self._main_thread_timer_can_stop = True
|
||||
self._last_submit_message = None
|
||||
self._process_item_id = None
|
||||
|
||||
self._variant_is_valid = None
|
||||
self._folder_is_valid = None
|
||||
|
|
@ -337,17 +346,17 @@ class PushToContextSelectWindow(QtWidgets.QWidget):
|
|||
overlay_try_btn.setVisible(False)
|
||||
|
||||
# Support of public api function of controller
|
||||
def set_source(self, project_name, version_id):
|
||||
def set_source(self, project_name, version_ids):
|
||||
"""Set source project and version.
|
||||
|
||||
Call the method on controller.
|
||||
|
||||
Args:
|
||||
project_name (Union[str, None]): Name of project.
|
||||
version_id (Union[str, None]): Version id.
|
||||
version_ids (Union[str, None]): comma separated Version ids.
|
||||
"""
|
||||
|
||||
self._controller.set_source(project_name, version_id)
|
||||
self._controller.set_source(project_name, version_ids)
|
||||
|
||||
def showEvent(self, event):
|
||||
super(PushToContextSelectWindow, self).showEvent(event)
|
||||
|
|
@ -362,10 +371,12 @@ class PushToContextSelectWindow(QtWidgets.QWidget):
|
|||
self._folder_name_input.setText(new_folder_name or "")
|
||||
self._variant_input.setText(variant or "")
|
||||
self._invalidate_variant(user_values["is_variant_valid"])
|
||||
self._invalidate_use_original_names(
|
||||
self._use_original_names_checkbox.isChecked())
|
||||
self._invalidate_new_folder_name(
|
||||
new_folder_name, user_values["is_new_folder_name_valid"]
|
||||
)
|
||||
|
||||
self._controller._invalidate()
|
||||
self._projects_combobox.refresh()
|
||||
|
||||
def _on_first_show(self):
|
||||
|
|
@ -409,6 +420,10 @@ class PushToContextSelectWindow(QtWidgets.QWidget):
|
|||
state = bool(state)
|
||||
self._projects_combobox.set_standard_filter_enabled(state)
|
||||
|
||||
def _on_original_names_change(self, state: int) -> None:
|
||||
use_original_name = bool(state)
|
||||
self._invalidate_use_original_names(use_original_name)
|
||||
|
||||
def _on_user_input_timer(self):
|
||||
folder_name_enabled = self._new_folder_name_enabled
|
||||
folder_name = self._new_folder_name_input_text
|
||||
|
|
@ -471,17 +486,27 @@ class PushToContextSelectWindow(QtWidgets.QWidget):
|
|||
state = ""
|
||||
if folder_name is not None:
|
||||
state = "valid" if is_valid else "invalid"
|
||||
set_style_property(
|
||||
self._folder_name_input, "state", state
|
||||
)
|
||||
set_style_property(self._folder_name_input, "state", state)
|
||||
|
||||
def _invalidate_variant(self, is_valid):
|
||||
if self._variant_is_valid is is_valid:
|
||||
return
|
||||
self._variant_is_valid = is_valid
|
||||
state = "valid" if is_valid else "invalid"
|
||||
set_style_property(self._variant_input, "state", state)
|
||||
|
||||
def _invalidate_use_original_names(self, use_original_names):
|
||||
"""Checks if original names must be used.
|
||||
|
||||
Invalidates Variant if necessary
|
||||
"""
|
||||
if self._controller.original_names_required():
|
||||
use_original_names = True
|
||||
|
||||
self._variant_input.setEnabled(not use_original_names)
|
||||
self._invalidate_variant(not use_original_names)
|
||||
|
||||
self._controller._use_original_name = use_original_names
|
||||
self._use_original_names_checkbox.setChecked(use_original_names)
|
||||
|
||||
def _on_submission_change(self, event):
|
||||
self._publish_btn.setEnabled(event["enabled"])
|
||||
|
||||
|
|
@ -510,31 +535,43 @@ class PushToContextSelectWindow(QtWidgets.QWidget):
|
|||
self._overlay_label.setText(self._last_submit_message)
|
||||
self._last_submit_message = None
|
||||
|
||||
process_status = self._controller.get_process_item_status(
|
||||
self._process_item_id
|
||||
)
|
||||
push_failed = process_status["failed"]
|
||||
fail_traceback = process_status["full_traceback"]
|
||||
failed_pushes = []
|
||||
fail_tracebacks = []
|
||||
for process_item in self._controller.get_process_items().values():
|
||||
process_status = process_item.get_status_data()
|
||||
if process_status["failed"]:
|
||||
failed_pushes.append(process_status)
|
||||
# push_failed = process_status["failed"]
|
||||
# fail_traceback = process_status["full_traceback"]
|
||||
if self._main_thread_timer_can_stop:
|
||||
self._main_thread_timer.stop()
|
||||
self._overlay_close_btn.setVisible(True)
|
||||
if push_failed:
|
||||
if failed_pushes:
|
||||
self._overlay_try_btn.setVisible(True)
|
||||
if fail_traceback:
|
||||
fail_tracebacks = [
|
||||
process_status["full_traceback"]
|
||||
for process_status in failed_pushes
|
||||
if process_status["full_traceback"]
|
||||
]
|
||||
if fail_tracebacks:
|
||||
self._show_detail_btn.setVisible(True)
|
||||
|
||||
if push_failed:
|
||||
reason = process_status["fail_reason"]
|
||||
if fail_traceback:
|
||||
if failed_pushes:
|
||||
reasons = [
|
||||
process_status["fail_reason"]
|
||||
for process_status in failed_pushes
|
||||
]
|
||||
if fail_tracebacks:
|
||||
reason = "\n".join(reasons)
|
||||
message = (
|
||||
"Unhandled error happened."
|
||||
" Check error detail for more information."
|
||||
)
|
||||
self._error_detail_dialog.set_detail(
|
||||
reason, fail_traceback
|
||||
reason, "\n".join(fail_tracebacks)
|
||||
)
|
||||
else:
|
||||
message = f"Push Failed:\n{reason}"
|
||||
message = f"Push Failed:\n{reasons}"
|
||||
|
||||
self._overlay_label.setText(message)
|
||||
set_style_property(self._overlay_close_btn, "state", "error")
|
||||
|
|
|
|||
|
|
@ -127,6 +127,7 @@ class SelectVersionComboBox(QtWidgets.QComboBox):
|
|||
status_text_rect.setLeft(icon_rect.right() + 2)
|
||||
|
||||
if status_text_rect.width() <= 0:
|
||||
painter.restore()
|
||||
return
|
||||
|
||||
if status_text_rect.width() < metrics.width(status_name):
|
||||
|
|
@ -144,6 +145,7 @@ class SelectVersionComboBox(QtWidgets.QComboBox):
|
|||
QtCore.Qt.AlignLeft | QtCore.Qt.AlignVCenter,
|
||||
status_name
|
||||
)
|
||||
painter.restore()
|
||||
|
||||
def set_current_index(self, index):
|
||||
model = self._combo_view.model()
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
"""Package declaring AYON addon 'core' version."""
|
||||
__version__ = "1.5.3+dev"
|
||||
__version__ = "1.6.0+dev"
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@ qtawesome = "0.7.3"
|
|||
[ayon.runtimeDependencies]
|
||||
aiohttp-middlewares = "^2.0.0"
|
||||
Click = "^8"
|
||||
OpenTimelineIO = "0.16.0"
|
||||
OpenTimelineIO = "0.17.0"
|
||||
opencolorio = "^2.3.2,<2.4.0"
|
||||
Pillow = "9.5.0"
|
||||
websocket-client = ">=0.40.0,<2"
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
name = "core"
|
||||
title = "Core"
|
||||
version = "1.5.3+dev"
|
||||
version = "1.6.0+dev"
|
||||
|
||||
client_dir = "ayon_core"
|
||||
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@
|
|||
|
||||
[tool.poetry]
|
||||
name = "ayon-core"
|
||||
version = "1.5.3+dev"
|
||||
version = "1.6.0+dev"
|
||||
description = ""
|
||||
authors = ["Ynput Team <team@ynput.io>"]
|
||||
readme = "README.md"
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue