Source code for python.external_config.configuration_state

# Copyright (c) 2018 Shotgun Software Inc.
#
# CONFIDENTIAL AND PROPRIETARY
#
# This work is provided "AS IS" and subject to the Shotgun Pipeline Toolkit
# Source Code License included in this distribution package. See LICENSE.
# By accessing, using, copying or modifying this work you indicate your
# agreement to the Shotgun Pipeline Toolkit Source Code License. All rights
# not expressly granted therein are reserved by Shotgun Software Inc.

import os
import sgtk
from sgtk.platform.qt import QtCore, QtGui

logger = sgtk.platform.get_logger(__name__)

shotgun_model = sgtk.platform.current_bundle().import_module("shotgun_model")
ShotgunModel = shotgun_model.ShotgunModel


[docs]class ConfigurationState(QtCore.QObject): """ Represents the state in Shotgun which may affect configurations and ultimately registered commands. Looks at the following items: - The list of software entities - The list of Pipeline Configurations - The state of TK_BOOTSTRAP_CONFIG_OVERRIDE As an example, changing a software entity may affect the list of registered commands. **Signals** :signal state_changed(): Indicates that the state has changed since it was last checked. """ state_changed = QtCore.Signal() def __init__(self, bg_task_manager, parent): """ Initialize the class with the following parameters: :param bg_task_manager: Background task manager to use for any asynchronous work. :type bg_task_manager: :class:`~task_manager.BackgroundTaskManager` :param parent: QT parent object. :type parent: :class:`~PySide.QtGui.QObject` """ super(ConfigurationState, self).__init__(parent) self._software_model = ConfigStateModel( "Software", [], bg_task_manager, parent ) # Determine the overall state of pipeline configurations # based on configs linked to active projects plus any # site configurations. self._pipeline_config_model = ConfigStateModel( "PipelineConfiguration", [ { "filter_operator": "any", "filters": [ ["project.Project.archived", "is", False], ["project", "is", None], ] } ], bg_task_manager, parent ) self._software_model.data_refreshed.connect(self._on_software_refreshed) self._pipeline_config_model.data_refreshed.connect(self._on_pipeline_configs_refreshed)
[docs] def refresh(self): """ Trigger an asynchronous background check of the Shotgun site configuration state. If a change is detected, indicating that configurations should be recomputed, a ``state_changed`` signal is emitted. """ self._pipeline_config_model.load_and_refresh() self._software_model.load_and_refresh()
[docs] def shut_down(self): """ Shut down and deallocate. """ self._software_model.destroy() self._pipeline_config_model.destroy()
[docs] def get_software_hash(self): """ Returns a hash representing the state of the software entity in Shotgun. :returns: Hash string or ``None`` if not yet defined. """ return self._software_model.get_hash()
[docs] def get_configuration_hash(self): """ Returns a hash representing the global state of Shotgun. :returns: Hash string or ``None`` if not yet defined. """ pc_hash = self._pipeline_config_model.get_hash() if pc_hash is None: return None # note: include the value of TK_BOOTSTRAP_CONFIG_OVERRIDE # as this is a global 'switch' which overrides # the pipeline configuration settings return "%s%s%s" % ( self.get_software_hash(), pc_hash, os.environ.get("TK_BOOTSTRAP_CONFIG_OVERRIDE") )
def _on_software_refreshed(self, has_changed): """ Software entity data has been retrieved :param bool has_changed: The cached data changed """ if has_changed: logger.debug("Shotgun software entity change detected.") self.state_changed.emit() def _on_pipeline_configs_refreshed(self, has_changed): """ Pipeline Config entity data has been retrieved :param bool has_changed: The cached data changed """ if has_changed: logger.debug("Shotgun pipeline config change detected.") self.state_changed.emit()
class ConfigStateModel(ShotgunModel): """ A ShotgunModel use to retrieve the state of a given entity. Holds *all* records for the given entity type (unless external filters have been provided) and exposes a hash representing the full state of the entity via the `get_hash()` method. Any change to the given entity type within the filter subset will be detected and will affect the hash. Internally, the hash is build based on the aggregate of updated_at values found for all records that the model tracks. """ def __init__(self, entity_type, filters, bg_task_manager, parent): """ :param bg_task_manager: Background task manager to use for any asynchronous work. :type bg_task_manager: :class:`~task_manager.BackgroundTaskManager` :param parent: QT parent object. :type parent: :class:`~PySide.QtGui.QObject` """ super(ConfigStateModel, self).__init__( parent, download_thumbs=False, bg_task_manager=bg_task_manager ) self._entity_type = entity_type self._filters = filters def load_and_refresh(self): """ Load cached data into the model and request a refresh. """ # Clear the cache first. It's important that we not let the disk cache # get in the way of having an accurate state to test against. self.hard_refresh() hierarchy = ["id"] fields = ["updated_at", "id"] self._load_data( self._entity_type, self._filters, hierarchy, fields, ) self._refresh_data() def get_hash(self): """ Computes a hash representing the state of all entities. :returns: Hash int or None if nothing is loaded. """ sg_data = self._get_sg_data() if sg_data is None: return None else: # note: there *may* be a bug when deleting items out of a shotgun # model in certain cases, so in order to ensure we # get a correct representation, include entity_ids in the hash. return hash(str(sg_data+self.entity_ids)) def _get_sg_data(self): """ Currently loaded Shotgun data. :returns: List of sg data dictionaries or ``None`` if not data is loaded. """ if self.rowCount() == 0: data = None else: data = [] for idx in range(self.rowCount()): item = self.item(idx) data.append(item.get_sg_data()) return data