mirror of
https://github.com/ynput/ayon-core.git
synced 2026-01-01 08:24:53 +01:00
change how context validation happens on instances
This commit is contained in:
parent
7653e098ce
commit
66353ec8c4
9 changed files with 213 additions and 122 deletions
|
|
@ -6,7 +6,8 @@ import traceback
|
||||||
import collections
|
import collections
|
||||||
import inspect
|
import inspect
|
||||||
from contextlib import contextmanager
|
from contextlib import contextmanager
|
||||||
from typing import Optional
|
import typing
|
||||||
|
from typing import Optional, Iterable, Dict
|
||||||
|
|
||||||
import pyblish.logic
|
import pyblish.logic
|
||||||
import pyblish.api
|
import pyblish.api
|
||||||
|
|
@ -31,13 +32,15 @@ from .exceptions import (
|
||||||
HostMissRequiredMethod,
|
HostMissRequiredMethod,
|
||||||
)
|
)
|
||||||
from .changes import TrackChangesItem
|
from .changes import TrackChangesItem
|
||||||
from .structures import PublishAttributes, ConvertorItem
|
from .structures import PublishAttributes, ConvertorItem, InstanceContextInfo
|
||||||
from .creator_plugins import (
|
from .creator_plugins import (
|
||||||
Creator,
|
Creator,
|
||||||
AutoCreator,
|
AutoCreator,
|
||||||
discover_creator_plugins,
|
discover_creator_plugins,
|
||||||
discover_convertor_plugins,
|
discover_convertor_plugins,
|
||||||
)
|
)
|
||||||
|
if typing.TYPE_CHECKING:
|
||||||
|
from .structures import CreatedInstance
|
||||||
|
|
||||||
# Import of functions and classes that were moved to different file
|
# Import of functions and classes that were moved to different file
|
||||||
# TODO Should be removed in future release - Added 24/08/28, 0.4.3-dev.1
|
# TODO Should be removed in future release - Added 24/08/28, 0.4.3-dev.1
|
||||||
|
|
@ -183,6 +186,10 @@ class CreateContext:
|
||||||
# Shared data across creators during collection phase
|
# Shared data across creators during collection phase
|
||||||
self._collection_shared_data = None
|
self._collection_shared_data = None
|
||||||
|
|
||||||
|
# Context validation cache
|
||||||
|
self._folder_id_by_folder_path = {}
|
||||||
|
self._task_names_by_folder_path = {}
|
||||||
|
|
||||||
self.thumbnail_paths_by_instance_id = {}
|
self.thumbnail_paths_by_instance_id = {}
|
||||||
|
|
||||||
# Trigger reset if was enabled
|
# Trigger reset if was enabled
|
||||||
|
|
@ -202,17 +209,19 @@ class CreateContext:
|
||||||
"""Access to global publish attributes."""
|
"""Access to global publish attributes."""
|
||||||
return self._publish_attributes
|
return self._publish_attributes
|
||||||
|
|
||||||
def get_instance_by_id(self, instance_id):
|
def get_instance_by_id(
|
||||||
|
self, instance_id: str
|
||||||
|
) -> Optional["CreatedInstance"]:
|
||||||
"""Receive instance by id.
|
"""Receive instance by id.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
instance_id (str): Instance id.
|
instance_id (str): Instance id.
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
Union[CreatedInstance, None]: Instance or None if instance with
|
Optional[CreatedInstance]: Instance or None if instance with
|
||||||
given id is not available.
|
given id is not available.
|
||||||
"""
|
|
||||||
|
|
||||||
|
"""
|
||||||
return self._instances_by_id.get(instance_id)
|
return self._instances_by_id.get(instance_id)
|
||||||
|
|
||||||
def get_sorted_creators(self, identifiers=None):
|
def get_sorted_creators(self, identifiers=None):
|
||||||
|
|
@ -224,8 +233,8 @@ class CreateContext:
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
List[BaseCreator]: Sorted creator plugins by 'order' value.
|
List[BaseCreator]: Sorted creator plugins by 'order' value.
|
||||||
"""
|
|
||||||
|
|
||||||
|
"""
|
||||||
if identifiers is not None:
|
if identifiers is not None:
|
||||||
identifiers = set(identifiers)
|
identifiers = set(identifiers)
|
||||||
creators = [
|
creators = [
|
||||||
|
|
@ -491,6 +500,8 @@ class CreateContext:
|
||||||
|
|
||||||
# Give ability to store shared data for collection phase
|
# Give ability to store shared data for collection phase
|
||||||
self._collection_shared_data = {}
|
self._collection_shared_data = {}
|
||||||
|
self._folder_id_by_folder_path = {}
|
||||||
|
self._task_names_by_folder_path = {}
|
||||||
|
|
||||||
def reset_finalization(self):
|
def reset_finalization(self):
|
||||||
"""Cleanup of attributes after reset."""
|
"""Cleanup of attributes after reset."""
|
||||||
|
|
@ -715,7 +726,7 @@ class CreateContext:
|
||||||
self._original_context_data, self.context_data_to_store()
|
self._original_context_data, self.context_data_to_store()
|
||||||
)
|
)
|
||||||
|
|
||||||
def creator_adds_instance(self, instance):
|
def creator_adds_instance(self, instance: "CreatedInstance"):
|
||||||
"""Creator adds new instance to context.
|
"""Creator adds new instance to context.
|
||||||
|
|
||||||
Instances should be added only from creators.
|
Instances should be added only from creators.
|
||||||
|
|
@ -942,7 +953,7 @@ class CreateContext:
|
||||||
def _remove_instance(self, instance):
|
def _remove_instance(self, instance):
|
||||||
self._instances_by_id.pop(instance.id, None)
|
self._instances_by_id.pop(instance.id, None)
|
||||||
|
|
||||||
def creator_removed_instance(self, instance):
|
def creator_removed_instance(self, instance: "CreatedInstance"):
|
||||||
"""When creator removes instance context should be acknowledged.
|
"""When creator removes instance context should be acknowledged.
|
||||||
|
|
||||||
If creator removes instance conext should know about it to avoid
|
If creator removes instance conext should know about it to avoid
|
||||||
|
|
@ -990,7 +1001,7 @@ class CreateContext:
|
||||||
[],
|
[],
|
||||||
self._bulk_instances_to_process
|
self._bulk_instances_to_process
|
||||||
)
|
)
|
||||||
self.validate_instances_context(instances_to_validate)
|
self.get_instances_context_info(instances_to_validate)
|
||||||
|
|
||||||
def reset_instances(self):
|
def reset_instances(self):
|
||||||
"""Reload instances"""
|
"""Reload instances"""
|
||||||
|
|
@ -1079,26 +1090,70 @@ class CreateContext:
|
||||||
if failed_info:
|
if failed_info:
|
||||||
raise CreatorsCreateFailed(failed_info)
|
raise CreatorsCreateFailed(failed_info)
|
||||||
|
|
||||||
def validate_instances_context(self, instances=None):
|
def get_instances_context_info(
|
||||||
"""Validate 'folder' and 'task' instance context."""
|
self, instances: Optional[Iterable["CreatedInstance"]] = None
|
||||||
|
) -> Dict[str, InstanceContextInfo]:
|
||||||
|
"""Validate 'folder' and 'task' instance context.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
instances (Optional[Iterable[CreatedInstance]]): Instances to
|
||||||
|
validate. If not provided all instances are validated.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
Dict[str, InstanceContextInfo]: Validation results by instance id.
|
||||||
|
|
||||||
|
"""
|
||||||
# Use all instances from context if 'instances' are not passed
|
# Use all instances from context if 'instances' are not passed
|
||||||
if instances is None:
|
if instances is None:
|
||||||
instances = tuple(self._instances_by_id.values())
|
instances = self._instances_by_id.values()
|
||||||
|
instances = tuple(instances)
|
||||||
|
info_by_instance_id = {
|
||||||
|
instance.id: InstanceContextInfo(
|
||||||
|
instance.get("folderPath"),
|
||||||
|
instance.get("task"),
|
||||||
|
False,
|
||||||
|
False,
|
||||||
|
)
|
||||||
|
for instance in instances
|
||||||
|
}
|
||||||
|
|
||||||
# Skip if instances are empty
|
# Skip if instances are empty
|
||||||
if not instances:
|
if not info_by_instance_id:
|
||||||
return
|
return info_by_instance_id
|
||||||
|
|
||||||
project_name = self.project_name
|
project_name = self.project_name
|
||||||
|
|
||||||
task_names_by_folder_path = {}
|
to_validate = []
|
||||||
|
task_names_by_folder_path = collections.defaultdict(set)
|
||||||
for instance in instances:
|
for instance in instances:
|
||||||
folder_path = instance.get("folderPath")
|
context_info = info_by_instance_id[instance.id]
|
||||||
task_name = instance.get("task")
|
if instance.has_promised_context:
|
||||||
if folder_path:
|
context_info.folder_is_valid = True
|
||||||
task_names_by_folder_path[folder_path] = set()
|
context_info.task_is_valid = True
|
||||||
if task_name:
|
continue
|
||||||
task_names_by_folder_path[folder_path].add(task_name)
|
# TODO allow context promise
|
||||||
|
folder_path = context_info.folder_path
|
||||||
|
if not folder_path:
|
||||||
|
continue
|
||||||
|
|
||||||
|
if folder_path in self._folder_id_by_folder_path:
|
||||||
|
folder_id = self._folder_id_by_folder_path[folder_path]
|
||||||
|
if folder_id is None:
|
||||||
|
continue
|
||||||
|
context_info.folder_is_valid = True
|
||||||
|
|
||||||
|
task_name = context_info.task_name
|
||||||
|
if task_name is not None:
|
||||||
|
tasks_cache = self._task_names_by_folder_path.get(folder_path)
|
||||||
|
if tasks_cache is not None:
|
||||||
|
context_info.task_is_valid = task_name in tasks_cache
|
||||||
|
continue
|
||||||
|
|
||||||
|
to_validate.append(instance)
|
||||||
|
task_names_by_folder_path[folder_path].add(task_name)
|
||||||
|
|
||||||
|
if not to_validate:
|
||||||
|
return info_by_instance_id
|
||||||
|
|
||||||
# Backwards compatibility for cases where folder name is set instead
|
# Backwards compatibility for cases where folder name is set instead
|
||||||
# of folder path
|
# of folder path
|
||||||
|
|
@ -1120,7 +1175,9 @@ class CreateContext:
|
||||||
fields={"id", "path"}
|
fields={"id", "path"}
|
||||||
):
|
):
|
||||||
folder_id = folder_entity["id"]
|
folder_id = folder_entity["id"]
|
||||||
folder_paths_by_id[folder_id] = folder_entity["path"]
|
folder_path = folder_entity["path"]
|
||||||
|
folder_paths_by_id[folder_id] = folder_path
|
||||||
|
self._folder_id_by_folder_path[folder_path] = folder_id
|
||||||
|
|
||||||
folder_entities_by_name = collections.defaultdict(list)
|
folder_entities_by_name = collections.defaultdict(list)
|
||||||
if folder_names:
|
if folder_names:
|
||||||
|
|
@ -1131,8 +1188,10 @@ class CreateContext:
|
||||||
):
|
):
|
||||||
folder_id = folder_entity["id"]
|
folder_id = folder_entity["id"]
|
||||||
folder_name = folder_entity["name"]
|
folder_name = folder_entity["name"]
|
||||||
folder_paths_by_id[folder_id] = folder_entity["path"]
|
folder_path = folder_entity["path"]
|
||||||
|
folder_paths_by_id[folder_id] = folder_path
|
||||||
folder_entities_by_name[folder_name].append(folder_entity)
|
folder_entities_by_name[folder_name].append(folder_entity)
|
||||||
|
self._folder_id_by_folder_path[folder_path] = folder_id
|
||||||
|
|
||||||
tasks_entities = ayon_api.get_tasks(
|
tasks_entities = ayon_api.get_tasks(
|
||||||
project_name,
|
project_name,
|
||||||
|
|
@ -1145,12 +1204,11 @@ class CreateContext:
|
||||||
folder_id = task_entity["folderId"]
|
folder_id = task_entity["folderId"]
|
||||||
folder_path = folder_paths_by_id[folder_id]
|
folder_path = folder_paths_by_id[folder_id]
|
||||||
task_names_by_folder_path[folder_path].add(task_entity["name"])
|
task_names_by_folder_path[folder_path].add(task_entity["name"])
|
||||||
|
self._task_names_by_folder_path.update(task_names_by_folder_path)
|
||||||
|
|
||||||
for instance in instances:
|
for instance in to_validate:
|
||||||
if not instance.has_valid_folder or not instance.has_valid_task:
|
|
||||||
continue
|
|
||||||
|
|
||||||
folder_path = instance["folderPath"]
|
folder_path = instance["folderPath"]
|
||||||
|
task_name = instance.get("task")
|
||||||
if folder_path and "/" not in folder_path:
|
if folder_path and "/" not in folder_path:
|
||||||
folder_entities = folder_entities_by_name.get(folder_path)
|
folder_entities = folder_entities_by_name.get(folder_path)
|
||||||
if len(folder_entities) == 1:
|
if len(folder_entities) == 1:
|
||||||
|
|
@ -1158,15 +1216,16 @@ class CreateContext:
|
||||||
instance["folderPath"] = folder_path
|
instance["folderPath"] = folder_path
|
||||||
|
|
||||||
if folder_path not in task_names_by_folder_path:
|
if folder_path not in task_names_by_folder_path:
|
||||||
instance.set_folder_invalid(True)
|
|
||||||
continue
|
continue
|
||||||
|
context_info = info_by_instance_id[instance.id]
|
||||||
|
context_info.folder_is_valid = True
|
||||||
|
|
||||||
task_name = instance["task"]
|
if (
|
||||||
if not task_name:
|
not task_name
|
||||||
continue
|
or task_name in task_names_by_folder_path[folder_path]
|
||||||
|
):
|
||||||
if task_name not in task_names_by_folder_path[folder_path]:
|
context_info.task_is_valid = True
|
||||||
instance.set_task_invalid(True)
|
return info_by_instance_id
|
||||||
|
|
||||||
def save_changes(self):
|
def save_changes(self):
|
||||||
"""Save changes. Update all changed values."""
|
"""Save changes. Update all changed values."""
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
import copy
|
import copy
|
||||||
import collections
|
import collections
|
||||||
from uuid import uuid4
|
from uuid import uuid4
|
||||||
|
from typing import Optional
|
||||||
|
|
||||||
from ayon_core.lib.attribute_definitions import (
|
from ayon_core.lib.attribute_definitions import (
|
||||||
UnknownDef,
|
UnknownDef,
|
||||||
|
|
@ -396,6 +397,24 @@ class PublishAttributes:
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class InstanceContextInfo:
|
||||||
|
def __init__(
|
||||||
|
self,
|
||||||
|
folder_path: Optional[str],
|
||||||
|
task_name: Optional[str],
|
||||||
|
folder_is_valid: bool,
|
||||||
|
task_is_valid: bool,
|
||||||
|
):
|
||||||
|
self.folder_path: Optional[str] = folder_path
|
||||||
|
self.task_name: Optional[str] = task_name
|
||||||
|
self.folder_is_valid: bool = folder_is_valid
|
||||||
|
self.task_is_valid: bool = task_is_valid
|
||||||
|
|
||||||
|
@property
|
||||||
|
def is_valid(self) -> bool:
|
||||||
|
return self.folder_is_valid and self.task_is_valid
|
||||||
|
|
||||||
|
|
||||||
class CreatedInstance:
|
class CreatedInstance:
|
||||||
"""Instance entity with data that will be stored to workfile.
|
"""Instance entity with data that will be stored to workfile.
|
||||||
|
|
||||||
|
|
@ -528,9 +547,6 @@ class CreatedInstance:
|
||||||
if not self._data.get("instance_id"):
|
if not self._data.get("instance_id"):
|
||||||
self._data["instance_id"] = str(uuid4())
|
self._data["instance_id"] = str(uuid4())
|
||||||
|
|
||||||
self._folder_is_valid = self.has_set_folder
|
|
||||||
self._task_is_valid = self.has_set_task
|
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return (
|
return (
|
||||||
"<CreatedInstance {product[name]}"
|
"<CreatedInstance {product[name]}"
|
||||||
|
|
@ -699,6 +715,17 @@ class CreatedInstance:
|
||||||
def publish_attributes(self):
|
def publish_attributes(self):
|
||||||
return self._data["publish_attributes"]
|
return self._data["publish_attributes"]
|
||||||
|
|
||||||
|
@property
|
||||||
|
def has_promised_context(self) -> bool:
|
||||||
|
"""Get context data that are promised to be set by creator.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
bool: Has context that won't bo validated. Artist can't change
|
||||||
|
value when set to True.
|
||||||
|
|
||||||
|
"""
|
||||||
|
return self._data.get("has_promised_context", False)
|
||||||
|
|
||||||
def data_to_store(self):
|
def data_to_store(self):
|
||||||
"""Collect data that contain json parsable types.
|
"""Collect data that contain json parsable types.
|
||||||
|
|
||||||
|
|
@ -826,46 +853,3 @@ class CreatedInstance:
|
||||||
obj.publish_attributes.deserialize_attributes(publish_attributes)
|
obj.publish_attributes.deserialize_attributes(publish_attributes)
|
||||||
|
|
||||||
return obj
|
return obj
|
||||||
|
|
||||||
# Context validation related methods/properties
|
|
||||||
@property
|
|
||||||
def has_set_folder(self):
|
|
||||||
"""Folder path is set in data."""
|
|
||||||
|
|
||||||
return "folderPath" in self._data
|
|
||||||
|
|
||||||
@property
|
|
||||||
def has_set_task(self):
|
|
||||||
"""Task name is set in data."""
|
|
||||||
|
|
||||||
return "task" in self._data
|
|
||||||
|
|
||||||
@property
|
|
||||||
def has_valid_context(self):
|
|
||||||
"""Context data are valid for publishing."""
|
|
||||||
|
|
||||||
return self.has_valid_folder and self.has_valid_task
|
|
||||||
|
|
||||||
@property
|
|
||||||
def has_valid_folder(self):
|
|
||||||
"""Folder set in context exists in project."""
|
|
||||||
|
|
||||||
if not self.has_set_folder:
|
|
||||||
return False
|
|
||||||
return self._folder_is_valid
|
|
||||||
|
|
||||||
@property
|
|
||||||
def has_valid_task(self):
|
|
||||||
"""Task set in context exists in project."""
|
|
||||||
|
|
||||||
if not self.has_set_task:
|
|
||||||
return False
|
|
||||||
return self._task_is_valid
|
|
||||||
|
|
||||||
def set_folder_invalid(self, invalid):
|
|
||||||
# TODO replace with `set_folder_path`
|
|
||||||
self._folder_is_valid = not invalid
|
|
||||||
|
|
||||||
def set_task_invalid(self, invalid):
|
|
||||||
# TODO replace with `set_task_name`
|
|
||||||
self._task_is_valid = not invalid
|
|
||||||
|
|
|
||||||
|
|
@ -322,6 +322,12 @@ class AbstractPublisherFrontend(AbstractPublisherCommon):
|
||||||
) -> Dict[str, Union[CreatedInstance, None]]:
|
) -> Dict[str, Union[CreatedInstance, None]]:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
@abstractmethod
|
||||||
|
def get_instances_context_info(
|
||||||
|
self, instance_ids: Optional[Iterable[str]] = None
|
||||||
|
):
|
||||||
|
pass
|
||||||
|
|
||||||
@abstractmethod
|
@abstractmethod
|
||||||
def get_existing_product_names(self, folder_path: str) -> List[str]:
|
def get_existing_product_names(self, folder_path: str) -> List[str]:
|
||||||
pass
|
pass
|
||||||
|
|
|
||||||
|
|
@ -190,6 +190,9 @@ class PublisherController(
|
||||||
def get_instances_by_id(self, instance_ids=None):
|
def get_instances_by_id(self, instance_ids=None):
|
||||||
return self._create_model.get_instances_by_id(instance_ids)
|
return self._create_model.get_instances_by_id(instance_ids)
|
||||||
|
|
||||||
|
def get_instances_context_info(self, instance_ids=None):
|
||||||
|
return self._create_model.get_instances_context_info(instance_ids)
|
||||||
|
|
||||||
def get_convertor_items(self):
|
def get_convertor_items(self):
|
||||||
return self._create_model.get_convertor_items()
|
return self._create_model.get_convertor_items()
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -306,6 +306,14 @@ class CreateModel:
|
||||||
for instance_id in instance_ids
|
for instance_id in instance_ids
|
||||||
}
|
}
|
||||||
|
|
||||||
|
def get_instances_context_info(
|
||||||
|
self, instance_ids: Optional[Iterable[str]] = None
|
||||||
|
):
|
||||||
|
instances = self.get_instances_by_id(instance_ids).values()
|
||||||
|
return self._create_context.get_instances_context_info(
|
||||||
|
instances
|
||||||
|
)
|
||||||
|
|
||||||
def get_convertor_items(self) -> Dict[str, ConvertorItem]:
|
def get_convertor_items(self) -> Dict[str, ConvertorItem]:
|
||||||
return self._create_context.convertor_items_by_id
|
return self._create_context.convertor_items_by_id
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -217,20 +217,22 @@ class InstanceGroupWidget(BaseGroupWidget):
|
||||||
def update_icons(self, group_icons):
|
def update_icons(self, group_icons):
|
||||||
self._group_icons = group_icons
|
self._group_icons = group_icons
|
||||||
|
|
||||||
def update_instance_values(self):
|
def update_instance_values(self, context_info_by_id):
|
||||||
"""Trigger update on instance widgets."""
|
"""Trigger update on instance widgets."""
|
||||||
|
|
||||||
for widget in self._widgets_by_id.values():
|
for instance_id, widget in self._widgets_by_id.items():
|
||||||
widget.update_instance_values()
|
widget.update_instance_values(context_info_by_id[instance_id])
|
||||||
|
|
||||||
def update_instances(self, instances):
|
def update_instances(self, instances, context_info_by_id):
|
||||||
"""Update instances for the group.
|
"""Update instances for the group.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
instances(list<CreatedInstance>): List of instances in
|
instances (list[CreatedInstance]): List of instances in
|
||||||
CreateContext.
|
CreateContext.
|
||||||
"""
|
context_info_by_id (Dict[str, InstanceContextInfo]): Instance
|
||||||
|
context info by instance id.
|
||||||
|
|
||||||
|
"""
|
||||||
# Store instances by id and by product name
|
# Store instances by id and by product name
|
||||||
instances_by_id = {}
|
instances_by_id = {}
|
||||||
instances_by_product_name = collections.defaultdict(list)
|
instances_by_product_name = collections.defaultdict(list)
|
||||||
|
|
@ -249,13 +251,14 @@ class InstanceGroupWidget(BaseGroupWidget):
|
||||||
widget_idx = 1
|
widget_idx = 1
|
||||||
for product_names in sorted_product_names:
|
for product_names in sorted_product_names:
|
||||||
for instance in instances_by_product_name[product_names]:
|
for instance in instances_by_product_name[product_names]:
|
||||||
|
context_info = context_info_by_id[instance.id]
|
||||||
if instance.id in self._widgets_by_id:
|
if instance.id in self._widgets_by_id:
|
||||||
widget = self._widgets_by_id[instance.id]
|
widget = self._widgets_by_id[instance.id]
|
||||||
widget.update_instance(instance)
|
widget.update_instance(instance, context_info)
|
||||||
else:
|
else:
|
||||||
group_icon = self._group_icons[instance.creator_identifier]
|
group_icon = self._group_icons[instance.creator_identifier]
|
||||||
widget = InstanceCardWidget(
|
widget = InstanceCardWidget(
|
||||||
instance, group_icon, self
|
instance, context_info, group_icon, self
|
||||||
)
|
)
|
||||||
widget.selected.connect(self._on_widget_selection)
|
widget.selected.connect(self._on_widget_selection)
|
||||||
widget.active_changed.connect(self._on_active_changed)
|
widget.active_changed.connect(self._on_active_changed)
|
||||||
|
|
@ -388,7 +391,7 @@ class ConvertorItemCardWidget(CardWidget):
|
||||||
self._icon_widget = icon_widget
|
self._icon_widget = icon_widget
|
||||||
self._label_widget = label_widget
|
self._label_widget = label_widget
|
||||||
|
|
||||||
def update_instance_values(self):
|
def update_instance_values(self, context_info):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -397,7 +400,7 @@ class InstanceCardWidget(CardWidget):
|
||||||
|
|
||||||
active_changed = QtCore.Signal(str, bool)
|
active_changed = QtCore.Signal(str, bool)
|
||||||
|
|
||||||
def __init__(self, instance, group_icon, parent):
|
def __init__(self, instance, context_info, group_icon, parent):
|
||||||
super().__init__(parent)
|
super().__init__(parent)
|
||||||
|
|
||||||
self._id = instance.id
|
self._id = instance.id
|
||||||
|
|
@ -458,7 +461,7 @@ class InstanceCardWidget(CardWidget):
|
||||||
self._active_checkbox = active_checkbox
|
self._active_checkbox = active_checkbox
|
||||||
self._expand_btn = expand_btn
|
self._expand_btn = expand_btn
|
||||||
|
|
||||||
self.update_instance_values()
|
self.update_instance_values(context_info)
|
||||||
|
|
||||||
def set_active_toggle_enabled(self, enabled):
|
def set_active_toggle_enabled(self, enabled):
|
||||||
self._active_checkbox.setEnabled(enabled)
|
self._active_checkbox.setEnabled(enabled)
|
||||||
|
|
@ -480,13 +483,13 @@ class InstanceCardWidget(CardWidget):
|
||||||
if checkbox_value != new_value:
|
if checkbox_value != new_value:
|
||||||
self._active_checkbox.setChecked(new_value)
|
self._active_checkbox.setChecked(new_value)
|
||||||
|
|
||||||
def update_instance(self, instance):
|
def update_instance(self, instance, context_info):
|
||||||
"""Update instance object and update UI."""
|
"""Update instance object and update UI."""
|
||||||
self.instance = instance
|
self.instance = instance
|
||||||
self.update_instance_values()
|
self.update_instance_values(context_info)
|
||||||
|
|
||||||
def _validate_context(self):
|
def _validate_context(self, context_info):
|
||||||
valid = self.instance.has_valid_context
|
valid = context_info.is_valid
|
||||||
self._icon_widget.setVisible(valid)
|
self._icon_widget.setVisible(valid)
|
||||||
self._context_warning.setVisible(not valid)
|
self._context_warning.setVisible(not valid)
|
||||||
|
|
||||||
|
|
@ -519,11 +522,11 @@ class InstanceCardWidget(CardWidget):
|
||||||
QtCore.Qt.NoTextInteraction
|
QtCore.Qt.NoTextInteraction
|
||||||
)
|
)
|
||||||
|
|
||||||
def update_instance_values(self):
|
def update_instance_values(self, context_info):
|
||||||
"""Update instance data"""
|
"""Update instance data"""
|
||||||
self._update_product_name()
|
self._update_product_name()
|
||||||
self.set_active(self.instance["active"])
|
self.set_active(self.instance["active"])
|
||||||
self._validate_context()
|
self._validate_context(context_info)
|
||||||
|
|
||||||
def _set_expanded(self, expanded=None):
|
def _set_expanded(self, expanded=None):
|
||||||
if expanded is None:
|
if expanded is None:
|
||||||
|
|
@ -694,6 +697,8 @@ class InstanceCardView(AbstractInstanceView):
|
||||||
|
|
||||||
self._update_convertor_items_group()
|
self._update_convertor_items_group()
|
||||||
|
|
||||||
|
context_info_by_id = self._controller.get_instances_context_info()
|
||||||
|
|
||||||
# Prepare instances by group and identifiers by group
|
# Prepare instances by group and identifiers by group
|
||||||
instances_by_group = collections.defaultdict(list)
|
instances_by_group = collections.defaultdict(list)
|
||||||
identifiers_by_group = collections.defaultdict(set)
|
identifiers_by_group = collections.defaultdict(set)
|
||||||
|
|
@ -747,7 +752,7 @@ class InstanceCardView(AbstractInstanceView):
|
||||||
|
|
||||||
widget_idx += 1
|
widget_idx += 1
|
||||||
group_widget.update_instances(
|
group_widget.update_instances(
|
||||||
instances_by_group[group_name]
|
instances_by_group[group_name], context_info_by_id
|
||||||
)
|
)
|
||||||
group_widget.set_active_toggle_enabled(
|
group_widget.set_active_toggle_enabled(
|
||||||
self._active_toggle_enabled
|
self._active_toggle_enabled
|
||||||
|
|
@ -814,8 +819,9 @@ class InstanceCardView(AbstractInstanceView):
|
||||||
|
|
||||||
def refresh_instance_states(self):
|
def refresh_instance_states(self):
|
||||||
"""Trigger update of instances on group widgets."""
|
"""Trigger update of instances on group widgets."""
|
||||||
|
context_info_by_id = self._controller.get_instances_context_info()
|
||||||
for widget in self._widgets_by_group.values():
|
for widget in self._widgets_by_group.values():
|
||||||
widget.update_instance_values()
|
widget.update_instance_values(context_info_by_id)
|
||||||
|
|
||||||
def _on_active_changed(self, group_name, instance_id, value):
|
def _on_active_changed(self, group_name, instance_id, value):
|
||||||
group_widget = self._widgets_by_group[group_name]
|
group_widget = self._widgets_by_group[group_name]
|
||||||
|
|
|
||||||
|
|
@ -115,7 +115,7 @@ class InstanceListItemWidget(QtWidgets.QWidget):
|
||||||
active_changed = QtCore.Signal(str, bool)
|
active_changed = QtCore.Signal(str, bool)
|
||||||
double_clicked = QtCore.Signal()
|
double_clicked = QtCore.Signal()
|
||||||
|
|
||||||
def __init__(self, instance, parent):
|
def __init__(self, instance, context_info, parent):
|
||||||
super().__init__(parent)
|
super().__init__(parent)
|
||||||
|
|
||||||
self.instance = instance
|
self.instance = instance
|
||||||
|
|
@ -151,7 +151,7 @@ class InstanceListItemWidget(QtWidgets.QWidget):
|
||||||
|
|
||||||
self._has_valid_context = None
|
self._has_valid_context = None
|
||||||
|
|
||||||
self._set_valid_property(instance.has_valid_context)
|
self._set_valid_property(context_info.is_valid)
|
||||||
|
|
||||||
def mouseDoubleClickEvent(self, event):
|
def mouseDoubleClickEvent(self, event):
|
||||||
widget = self.childAt(event.pos())
|
widget = self.childAt(event.pos())
|
||||||
|
|
@ -188,12 +188,12 @@ class InstanceListItemWidget(QtWidgets.QWidget):
|
||||||
if checkbox_value != new_value:
|
if checkbox_value != new_value:
|
||||||
self._active_checkbox.setChecked(new_value)
|
self._active_checkbox.setChecked(new_value)
|
||||||
|
|
||||||
def update_instance(self, instance):
|
def update_instance(self, instance, context_info):
|
||||||
"""Update instance object."""
|
"""Update instance object."""
|
||||||
self.instance = instance
|
self.instance = instance
|
||||||
self.update_instance_values()
|
self.update_instance_values(context_info)
|
||||||
|
|
||||||
def update_instance_values(self):
|
def update_instance_values(self, context_info):
|
||||||
"""Update instance data propagated to widgets."""
|
"""Update instance data propagated to widgets."""
|
||||||
# Check product name
|
# Check product name
|
||||||
label = self.instance.label
|
label = self.instance.label
|
||||||
|
|
@ -202,7 +202,7 @@ class InstanceListItemWidget(QtWidgets.QWidget):
|
||||||
# Check active state
|
# Check active state
|
||||||
self.set_active(self.instance["active"])
|
self.set_active(self.instance["active"])
|
||||||
# Check valid states
|
# Check valid states
|
||||||
self._set_valid_property(self.instance.has_valid_context)
|
self._set_valid_property(context_info.is_valid)
|
||||||
|
|
||||||
def _on_active_change(self):
|
def _on_active_change(self):
|
||||||
new_value = self._active_checkbox.isChecked()
|
new_value = self._active_checkbox.isChecked()
|
||||||
|
|
@ -583,6 +583,8 @@ class InstanceListView(AbstractInstanceView):
|
||||||
|
|
||||||
self._update_convertor_items_group()
|
self._update_convertor_items_group()
|
||||||
|
|
||||||
|
context_info_by_id = self._controller.get_instances_context_info()
|
||||||
|
|
||||||
# Prepare instances by their groups
|
# Prepare instances by their groups
|
||||||
instances_by_group_name = collections.defaultdict(list)
|
instances_by_group_name = collections.defaultdict(list)
|
||||||
group_names = set()
|
group_names = set()
|
||||||
|
|
@ -643,13 +645,15 @@ class InstanceListView(AbstractInstanceView):
|
||||||
elif activity != instance["active"]:
|
elif activity != instance["active"]:
|
||||||
activity = -1
|
activity = -1
|
||||||
|
|
||||||
|
context_info = context_info_by_id[instance_id]
|
||||||
|
|
||||||
self._group_by_instance_id[instance_id] = group_name
|
self._group_by_instance_id[instance_id] = group_name
|
||||||
# Remove instance id from `to_remove` if already exists and
|
# Remove instance id from `to_remove` if already exists and
|
||||||
# trigger update of widget
|
# trigger update of widget
|
||||||
if instance_id in to_remove:
|
if instance_id in to_remove:
|
||||||
to_remove.remove(instance_id)
|
to_remove.remove(instance_id)
|
||||||
widget = self._widgets_by_id[instance_id]
|
widget = self._widgets_by_id[instance_id]
|
||||||
widget.update_instance(instance)
|
widget.update_instance(instance, context_info)
|
||||||
continue
|
continue
|
||||||
|
|
||||||
# Create new item and store it as new
|
# Create new item and store it as new
|
||||||
|
|
@ -695,7 +699,8 @@ class InstanceListView(AbstractInstanceView):
|
||||||
group_item.appendRows(new_items)
|
group_item.appendRows(new_items)
|
||||||
|
|
||||||
for item, instance in new_items_with_instance:
|
for item, instance in new_items_with_instance:
|
||||||
if not instance.has_valid_context:
|
context_info = context_info_by_id[instance.id]
|
||||||
|
if not context_info.is_valid:
|
||||||
expand_groups.add(group_name)
|
expand_groups.add(group_name)
|
||||||
item_index = self._instance_model.index(
|
item_index = self._instance_model.index(
|
||||||
item.row(),
|
item.row(),
|
||||||
|
|
@ -704,7 +709,7 @@ class InstanceListView(AbstractInstanceView):
|
||||||
)
|
)
|
||||||
proxy_index = self._proxy_model.mapFromSource(item_index)
|
proxy_index = self._proxy_model.mapFromSource(item_index)
|
||||||
widget = InstanceListItemWidget(
|
widget = InstanceListItemWidget(
|
||||||
instance, self._instance_view
|
instance, context_info, self._instance_view
|
||||||
)
|
)
|
||||||
widget.set_active_toggle_enabled(
|
widget.set_active_toggle_enabled(
|
||||||
self._active_toggle_enabled
|
self._active_toggle_enabled
|
||||||
|
|
@ -870,8 +875,10 @@ class InstanceListView(AbstractInstanceView):
|
||||||
|
|
||||||
def refresh_instance_states(self):
|
def refresh_instance_states(self):
|
||||||
"""Trigger update of all instances."""
|
"""Trigger update of all instances."""
|
||||||
for widget in self._widgets_by_id.values():
|
context_info_by_id = self._controller.get_instances_context_info()
|
||||||
widget.update_instance_values()
|
for instance_id, widget in self._widgets_by_id.items():
|
||||||
|
context_info = context_info_by_id[instance_id]
|
||||||
|
widget.update_instance_values(context_info)
|
||||||
|
|
||||||
def _on_active_changed(self, changed_instance_id, new_value):
|
def _on_active_changed(self, changed_instance_id, new_value):
|
||||||
selected_instance_ids, _, _ = self.get_selected_items()
|
selected_instance_ids, _, _ = self.get_selected_items()
|
||||||
|
|
|
||||||
|
|
@ -1206,7 +1206,6 @@ class GlobalAttrsWidget(QtWidgets.QWidget):
|
||||||
|
|
||||||
except TaskNotSetError:
|
except TaskNotSetError:
|
||||||
invalid_tasks = True
|
invalid_tasks = True
|
||||||
instance.set_task_invalid(True)
|
|
||||||
product_names.add(instance["productName"])
|
product_names.add(instance["productName"])
|
||||||
continue
|
continue
|
||||||
|
|
||||||
|
|
@ -1216,11 +1215,9 @@ class GlobalAttrsWidget(QtWidgets.QWidget):
|
||||||
|
|
||||||
if folder_path is not None:
|
if folder_path is not None:
|
||||||
instance["folderPath"] = folder_path
|
instance["folderPath"] = folder_path
|
||||||
instance.set_folder_invalid(False)
|
|
||||||
|
|
||||||
if task_name is not None:
|
if task_name is not None:
|
||||||
instance["task"] = task_name or None
|
instance["task"] = task_name or None
|
||||||
instance.set_task_invalid(False)
|
|
||||||
|
|
||||||
instance["productName"] = new_product_name
|
instance["productName"] = new_product_name
|
||||||
|
|
||||||
|
|
@ -1768,9 +1765,16 @@ class ProductAttributesWidget(QtWidgets.QWidget):
|
||||||
self.bottom_separator = bottom_separator
|
self.bottom_separator = bottom_separator
|
||||||
|
|
||||||
def _on_instance_context_changed(self):
|
def _on_instance_context_changed(self):
|
||||||
|
instance_ids = {
|
||||||
|
instance.id
|
||||||
|
for instance in self._current_instances
|
||||||
|
}
|
||||||
|
context_info_by_id = self._controller.get_instances_context_info(
|
||||||
|
instance_ids
|
||||||
|
)
|
||||||
all_valid = True
|
all_valid = True
|
||||||
for instance in self._current_instances:
|
for instance_id, context_info in context_info_by_id.items():
|
||||||
if not instance.has_valid_context:
|
if not context_info.is_valid:
|
||||||
all_valid = False
|
all_valid = False
|
||||||
break
|
break
|
||||||
|
|
||||||
|
|
@ -1795,9 +1799,17 @@ class ProductAttributesWidget(QtWidgets.QWidget):
|
||||||
convertor_identifiers(List[str]): Identifiers of convert items.
|
convertor_identifiers(List[str]): Identifiers of convert items.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
instance_ids = {
|
||||||
|
instance.id
|
||||||
|
for instance in instances
|
||||||
|
}
|
||||||
|
context_info_by_id = self._controller.get_instances_context_info(
|
||||||
|
instance_ids
|
||||||
|
)
|
||||||
|
|
||||||
all_valid = True
|
all_valid = True
|
||||||
for instance in instances:
|
for context_info in context_info_by_id.values():
|
||||||
if not instance.has_valid_context:
|
if not context_info.is_valid:
|
||||||
all_valid = False
|
all_valid = False
|
||||||
break
|
break
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -913,12 +913,18 @@ class PublisherWindow(QtWidgets.QDialog):
|
||||||
self._set_footer_enabled(True)
|
self._set_footer_enabled(True)
|
||||||
return
|
return
|
||||||
|
|
||||||
|
active_instances_by_id = {
|
||||||
|
instance.id: instance
|
||||||
|
for instance in self._controller.get_instances()
|
||||||
|
if instance["active"]
|
||||||
|
}
|
||||||
|
context_info_by_id = self._controller.get_instances_context_info(
|
||||||
|
active_instances_by_id.keys()
|
||||||
|
)
|
||||||
all_valid = None
|
all_valid = None
|
||||||
for instance in self._controller.get_instances():
|
for instance_id, instance in active_instances_by_id.items():
|
||||||
if not instance["active"]:
|
context_info = context_info_by_id[instance_id]
|
||||||
continue
|
if not context_info.is_valid:
|
||||||
|
|
||||||
if not instance.has_valid_context:
|
|
||||||
all_valid = False
|
all_valid = False
|
||||||
break
|
break
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue