Mari Default hook

# Copyright (c) 2021 Autodesk, 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 Autodesk, Inc.

import os

import sgtk
from sgtk import TankError

import mari

HookBaseClass = sgtk.get_hook_baseclass()


class BreakdownSceneOperations(HookBaseClass):
    """
    Breakdown operations for Mari.

    This implementation handles detection of mari geometry versions
    """

    def scan_scene(self):
        """
        The scan scene method is executed once at startup and its purpose is
        to analyze the current scene and return a list of references that are
        to be potentially operated on.

        The return data structure is a list of dictionaries. Each scene reference
        that is returned should be represented by a dictionary with three keys:

        - "node_name": The name of the 'node' that is to be operated on. Most DCCs have
          a concept of a node, path or some other way to address a particular
          object in the scene.
        - "node_type": The object type that this is. This is later passed to the
          update method so that it knows how to handle the object.
        - "path": Path on disk to the referenced object.
        - "extra_data": Optional key to pass some extra data to the update method
          in case we'd like to access them when updating the nodes.

        Toolkit will scan the list of items, see if any of the objects matches
        a published file and try to determine if there is a more recent version
        available. Any such versions are then displayed in the UI as out of date.
        """

        if not mari.projects.current():
            # can't do anything if we don't have an open project!
            return []

        # find all geo in the current project using the engine utility method:
        mari_engine = self.parent.engine
        all_geo = mari_engine.list_geometry()

        # now, for all geo, find all versions:
        found_versions = []
        for geo in [g.get("geo") for g in all_geo]:

            # get all versions for this geo:
            all_geo_versions = mari_engine.list_geometry_versions(geo)

            # now find the publish path for the current version:
            current_version = geo.currentVersion()
            for geo_version, path in [
                (v["geo_version"], v.get("path")) for v in all_geo_versions
            ]:
                if geo_version == current_version:
                    # found the current version :)
                    found_versions.append(
                        {"node_name": geo.name(), "node_type": "geo", "path": path}
                    )
                    break

        return found_versions

    def update(self, item):
        """
        Perform replacements given a number of scene items passed from the app.

        Once a selection has been performed in the main UI and the user clicks
        the update button, this method is called.

        :param item: Dictionary on the same form as was generated by the scan_scene hook above.
                     The path key now holds the path that the node should be updated *to* rather than the current path.
        """

        mari_engine = self.parent.engine

        node_name = item["node_name"]
        node_type = item["node_type"]
        path = item["path"]

        if node_type == "geo":

            geo = mari.geo.find(node_name)
            if not geo:
                raise TankError(
                    "Failed to find geometry '%s' in the current project" % node_name
                )

            already_loaded = False
            all_geo_versions = mari_engine.list_geometry_versions(geo)
            for geo_version, geo_path in [
                (v["geo_version"], v.get("path")) for v in all_geo_versions
            ]:
                if geo_path == path:
                    # we already have this version loaded so just set it as current
                    geo.setCurrentVersion(geo_version.name())
                    already_loaded = True
                    break

            if not already_loaded:
                # add the new version
                fields = ["id", "path", "version_number"]
                found_publishes = sgtk.util.find_publish(
                    self.parent.sgtk, [path], fields=fields
                )
                sg_publish_data = found_publishes.get(path)
                if not sg_publish_data:
                    raise TankError(
                        "Failed to find geometry '%s' in the current project"
                        % node_name
                    )
                new_version = mari_engine.add_geometry_version(geo, sg_publish_data)
                if new_version:
                    geo.setCurrentVersion(new_version.name())