mirror of
https://github.com/ynput/ayon-core.git
synced 2025-12-24 21:04:40 +01:00
* Added new SiteSync model Used to get information from SiteSync module to enhance Loader UI. * Added new SiteSync method to controller Other models will be using these to get information pertaining SiteSync * Added missed commit * Implemented collection of SiteSync info * Added AvailabilityDelegate Shows how many representations are present locally and remotely in Loader summary page. * Added fields to store progress info * Fix HiddenAttr to carry value * Refactored to internal variable Changes made after discussion * Implemented ActionItems for upload/download/remove Replaced old Launcher approach, now it is not necessary after refactor of Ayon launcher. * Update openpype/tools/ayon_loader/abstract.py Co-authored-by: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> * Hound * Refactor better retrieval of icon * Refactor better readability * Refactor renamed delegate * Refactor better retrieval of icons * Refactor better readability * Refactor removed unneeded explicit refresh * Hound * Hound * Hound * Fix used wrong type * Update openpype/tools/ayon_loader/ui/products_delegates.py Co-authored-by: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> * Refactor renamed variable name * Refactor formatting * Added progress for representations * cache version availability * cache representations sync status * changed representations count logic and moved it to products model * site sync enabled is cached * active and remote site names are cached * small tweaks in site sync model * change methods called by controller * hide site sync columns if site sync not enabled * use string conversion before iteration * smal formatting changes * updated abstract class with abstract methods * renamed site sync model variable * fixed method name * fix used method name * rename '_sitesync_addon' to '_site_sync_addon' * fix remote site name cache * small formatting changes in delegate * modify site sync delegate to be more dynamic * fix delegate painting * do not handle repre progress in products model * Add comma back * simplify delegate code --------- Co-authored-by: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Co-authored-by: Jakub Trllo <jakub.trllo@gmail.com>
410 lines
13 KiB
Python
410 lines
13 KiB
Python
import logging
|
|
import uuid
|
|
|
|
import ayon_api
|
|
|
|
from openpype.lib.events import QueuedEventSystem
|
|
from openpype.pipeline import Anatomy, get_current_context
|
|
from openpype.host import ILoadHost
|
|
from openpype.tools.ayon_utils.models import (
|
|
ProjectsModel,
|
|
HierarchyModel,
|
|
NestedCacheItem,
|
|
CacheItem,
|
|
ThumbnailsModel,
|
|
)
|
|
|
|
from .abstract import BackendLoaderController, FrontendLoaderController
|
|
from .models import (
|
|
SelectionModel,
|
|
ProductsModel,
|
|
LoaderActionsModel,
|
|
SiteSyncModel
|
|
)
|
|
|
|
|
|
class ExpectedSelection:
|
|
def __init__(self, controller):
|
|
self._project_name = None
|
|
self._folder_id = None
|
|
|
|
self._project_selected = True
|
|
self._folder_selected = True
|
|
|
|
self._controller = controller
|
|
|
|
def _emit_change(self):
|
|
self._controller.emit_event(
|
|
"expected_selection_changed",
|
|
self.get_expected_selection_data(),
|
|
)
|
|
|
|
def set_expected_selection(self, project_name, folder_id):
|
|
self._project_name = project_name
|
|
self._folder_id = folder_id
|
|
|
|
self._project_selected = False
|
|
self._folder_selected = False
|
|
self._emit_change()
|
|
|
|
def get_expected_selection_data(self):
|
|
project_current = False
|
|
folder_current = False
|
|
if not self._project_selected:
|
|
project_current = True
|
|
elif not self._folder_selected:
|
|
folder_current = True
|
|
return {
|
|
"project": {
|
|
"name": self._project_name,
|
|
"current": project_current,
|
|
"selected": self._project_selected,
|
|
},
|
|
"folder": {
|
|
"id": self._folder_id,
|
|
"current": folder_current,
|
|
"selected": self._folder_selected,
|
|
},
|
|
}
|
|
|
|
def is_expected_project_selected(self, project_name):
|
|
return project_name == self._project_name and self._project_selected
|
|
|
|
def is_expected_folder_selected(self, folder_id):
|
|
return folder_id == self._folder_id and self._folder_selected
|
|
|
|
def expected_project_selected(self, project_name):
|
|
if project_name != self._project_name:
|
|
return False
|
|
self._project_selected = True
|
|
self._emit_change()
|
|
return True
|
|
|
|
def expected_folder_selected(self, folder_id):
|
|
if folder_id != self._folder_id:
|
|
return False
|
|
self._folder_selected = True
|
|
self._emit_change()
|
|
return True
|
|
|
|
|
|
class LoaderController(BackendLoaderController, FrontendLoaderController):
|
|
"""
|
|
|
|
Args:
|
|
host (Optional[AbstractHost]): Host object. Defaults to None.
|
|
"""
|
|
|
|
def __init__(self, host=None):
|
|
self._log = None
|
|
self._host = host
|
|
|
|
self._event_system = self._create_event_system()
|
|
|
|
self._project_anatomy_cache = NestedCacheItem(
|
|
levels=1, lifetime=60)
|
|
self._loaded_products_cache = CacheItem(
|
|
default_factory=set, lifetime=60)
|
|
|
|
self._selection_model = SelectionModel(self)
|
|
self._expected_selection = ExpectedSelection(self)
|
|
self._projects_model = ProjectsModel(self)
|
|
self._hierarchy_model = HierarchyModel(self)
|
|
self._products_model = ProductsModel(self)
|
|
self._loader_actions_model = LoaderActionsModel(self)
|
|
self._thumbnails_model = ThumbnailsModel()
|
|
self._site_sync_model = SiteSyncModel(self)
|
|
|
|
@property
|
|
def log(self):
|
|
if self._log is None:
|
|
self._log = logging.getLogger(self.__class__.__name__)
|
|
return self._log
|
|
|
|
# ---------------------------------
|
|
# Implementation of abstract methods
|
|
# ---------------------------------
|
|
# Events system
|
|
def emit_event(self, topic, data=None, source=None):
|
|
"""Use implemented event system to trigger event."""
|
|
|
|
if data is None:
|
|
data = {}
|
|
self._event_system.emit(topic, data, source)
|
|
|
|
def register_event_callback(self, topic, callback):
|
|
self._event_system.add_callback(topic, callback)
|
|
|
|
def reset(self):
|
|
self._emit_event("controller.reset.started")
|
|
|
|
project_name = self.get_selected_project_name()
|
|
folder_ids = self.get_selected_folder_ids()
|
|
|
|
self._project_anatomy_cache.reset()
|
|
self._loaded_products_cache.reset()
|
|
|
|
self._products_model.reset()
|
|
self._hierarchy_model.reset()
|
|
self._loader_actions_model.reset()
|
|
self._projects_model.reset()
|
|
self._thumbnails_model.reset()
|
|
self._site_sync_model.reset()
|
|
|
|
self._projects_model.refresh()
|
|
|
|
if not project_name and not folder_ids:
|
|
context = self.get_current_context()
|
|
project_name = context["project_name"]
|
|
folder_id = context["folder_id"]
|
|
self.set_expected_selection(project_name, folder_id)
|
|
|
|
self._emit_event("controller.reset.finished")
|
|
|
|
# Expected selection helpers
|
|
def get_expected_selection_data(self):
|
|
return self._expected_selection.get_expected_selection_data()
|
|
|
|
def set_expected_selection(self, project_name, folder_id):
|
|
self._expected_selection.set_expected_selection(
|
|
project_name, folder_id
|
|
)
|
|
|
|
def expected_project_selected(self, project_name):
|
|
self._expected_selection.expected_project_selected(project_name)
|
|
|
|
def expected_folder_selected(self, folder_id):
|
|
self._expected_selection.expected_folder_selected(folder_id)
|
|
|
|
# Entity model wrappers
|
|
def get_project_items(self, sender=None):
|
|
return self._projects_model.get_project_items(sender)
|
|
|
|
def get_folder_items(self, project_name, sender=None):
|
|
return self._hierarchy_model.get_folder_items(project_name, sender)
|
|
|
|
def get_product_items(self, project_name, folder_ids, sender=None):
|
|
return self._products_model.get_product_items(
|
|
project_name, folder_ids, sender)
|
|
|
|
def get_product_item(self, project_name, product_id):
|
|
return self._products_model.get_product_item(
|
|
project_name, product_id
|
|
)
|
|
|
|
def get_product_type_items(self, project_name):
|
|
return self._products_model.get_product_type_items(project_name)
|
|
|
|
def get_representation_items(
|
|
self, project_name, version_ids, sender=None
|
|
):
|
|
return self._products_model.get_repre_items(
|
|
project_name, version_ids, sender
|
|
)
|
|
|
|
def get_versions_representation_count(
|
|
self, project_name, version_ids, sender=None
|
|
):
|
|
return self._products_model.get_versions_repre_count(
|
|
project_name, version_ids, sender
|
|
)
|
|
|
|
def get_folder_thumbnail_ids(self, project_name, folder_ids):
|
|
return self._thumbnails_model.get_folder_thumbnail_ids(
|
|
project_name, folder_ids
|
|
)
|
|
|
|
def get_version_thumbnail_ids(self, project_name, version_ids):
|
|
return self._thumbnails_model.get_version_thumbnail_ids(
|
|
project_name, version_ids
|
|
)
|
|
|
|
def get_thumbnail_path(self, project_name, thumbnail_id):
|
|
return self._thumbnails_model.get_thumbnail_path(
|
|
project_name, thumbnail_id
|
|
)
|
|
|
|
def change_products_group(self, project_name, product_ids, group_name):
|
|
self._products_model.change_products_group(
|
|
project_name, product_ids, group_name
|
|
)
|
|
|
|
def get_versions_action_items(self, project_name, version_ids):
|
|
return self._loader_actions_model.get_versions_action_items(
|
|
project_name, version_ids)
|
|
|
|
def get_representations_action_items(
|
|
self, project_name, representation_ids):
|
|
action_items = (
|
|
self._loader_actions_model.get_representations_action_items(
|
|
project_name, representation_ids)
|
|
)
|
|
|
|
action_items.extend(self._site_sync_model.get_site_sync_action_items(
|
|
project_name, representation_ids)
|
|
)
|
|
|
|
return action_items
|
|
|
|
def trigger_action_item(
|
|
self,
|
|
identifier,
|
|
options,
|
|
project_name,
|
|
version_ids,
|
|
representation_ids
|
|
):
|
|
if self._site_sync_model.is_site_sync_action(identifier):
|
|
self._site_sync_model.trigger_action_item(
|
|
identifier,
|
|
project_name,
|
|
representation_ids
|
|
)
|
|
return
|
|
|
|
self._loader_actions_model.trigger_action_item(
|
|
identifier,
|
|
options,
|
|
project_name,
|
|
version_ids,
|
|
representation_ids
|
|
)
|
|
|
|
# Selection model wrappers
|
|
def get_selected_project_name(self):
|
|
return self._selection_model.get_selected_project_name()
|
|
|
|
def set_selected_project(self, project_name):
|
|
self._selection_model.set_selected_project(project_name)
|
|
|
|
# Selection model wrappers
|
|
def get_selected_folder_ids(self):
|
|
return self._selection_model.get_selected_folder_ids()
|
|
|
|
def set_selected_folders(self, folder_ids):
|
|
self._selection_model.set_selected_folders(folder_ids)
|
|
|
|
def get_selected_version_ids(self):
|
|
return self._selection_model.get_selected_version_ids()
|
|
|
|
def set_selected_versions(self, version_ids):
|
|
self._selection_model.set_selected_versions(version_ids)
|
|
|
|
def get_selected_representation_ids(self):
|
|
return self._selection_model.get_selected_representation_ids()
|
|
|
|
def set_selected_representations(self, repre_ids):
|
|
self._selection_model.set_selected_representations(repre_ids)
|
|
|
|
def fill_root_in_source(self, source):
|
|
project_name = self.get_selected_project_name()
|
|
anatomy = self._get_project_anatomy(project_name)
|
|
if anatomy is None:
|
|
return source
|
|
|
|
try:
|
|
return anatomy.fill_root(source)
|
|
except Exception:
|
|
return source
|
|
|
|
def get_current_context(self):
|
|
if self._host is None:
|
|
return {
|
|
"project_name": None,
|
|
"folder_id": None,
|
|
"task_name": None,
|
|
}
|
|
if hasattr(self._host, "get_current_context"):
|
|
context = self._host.get_current_context()
|
|
else:
|
|
context = get_current_context()
|
|
folder_id = None
|
|
project_name = context.get("project_name")
|
|
asset_name = context.get("asset_name")
|
|
if project_name and asset_name:
|
|
folder = ayon_api.get_folder_by_path(
|
|
project_name, asset_name, fields=["id"]
|
|
)
|
|
if folder:
|
|
folder_id = folder["id"]
|
|
return {
|
|
"project_name": project_name,
|
|
"folder_id": folder_id,
|
|
"task_name": context.get("task_name"),
|
|
}
|
|
|
|
def get_loaded_product_ids(self):
|
|
if self._host is None:
|
|
return set()
|
|
|
|
context = self.get_current_context()
|
|
project_name = context["project_name"]
|
|
if not project_name:
|
|
return set()
|
|
|
|
if not self._loaded_products_cache.is_valid:
|
|
if isinstance(self._host, ILoadHost):
|
|
containers = self._host.get_containers()
|
|
else:
|
|
containers = self._host.ls()
|
|
repre_ids = set()
|
|
for container in containers:
|
|
repre_id = container.get("representation")
|
|
# Ignore invalid representation ids.
|
|
# - invalid representation ids may be available if e.g. is
|
|
# opened scene from OpenPype whe 'ObjectId' was used instead
|
|
# of 'uuid'.
|
|
# NOTE: Server call would crash if there is any invalid id.
|
|
# That would cause crash we won't get any information.
|
|
try:
|
|
uuid.UUID(repre_id)
|
|
repre_ids.add(repre_id)
|
|
except ValueError:
|
|
pass
|
|
|
|
product_ids = self._products_model.get_product_ids_by_repre_ids(
|
|
project_name, repre_ids
|
|
)
|
|
self._loaded_products_cache.update_data(product_ids)
|
|
return self._loaded_products_cache.get_data()
|
|
|
|
def is_site_sync_enabled(self, project_name=None):
|
|
return self._site_sync_model.is_site_sync_enabled(project_name)
|
|
|
|
def get_active_site_icon_def(self, project_name):
|
|
return self._site_sync_model.get_active_site_icon_def(project_name)
|
|
|
|
def get_remote_site_icon_def(self, project_name):
|
|
return self._site_sync_model.get_remote_site_icon_def(project_name)
|
|
|
|
def get_version_sync_availability(self, project_name, version_ids):
|
|
return self._site_sync_model.get_version_sync_availability(
|
|
project_name, version_ids
|
|
)
|
|
|
|
def get_representations_sync_status(
|
|
self, project_name, representation_ids
|
|
):
|
|
return self._site_sync_model.get_representations_sync_status(
|
|
project_name, representation_ids
|
|
)
|
|
|
|
def is_loaded_products_supported(self):
|
|
return self._host is not None
|
|
|
|
def is_standard_projects_filter_enabled(self):
|
|
return self._host is not None
|
|
|
|
def _get_project_anatomy(self, project_name):
|
|
if not project_name:
|
|
return None
|
|
cache = self._project_anatomy_cache[project_name]
|
|
if not cache.is_valid:
|
|
cache.update_data(Anatomy(project_name))
|
|
return cache.get_data()
|
|
|
|
def _create_event_system(self):
|
|
return QueuedEventSystem()
|
|
|
|
def _emit_event(self, topic, data=None):
|
|
self._event_system.emit(topic, data or {}, "controller")
|