예제 #1
0
    def validate(self, settings, item):
        """
        Validates the given item to check that it is ok to publish. Returns a
        boolean to indicate validity.

        :param settings: Dictionary of Settings. The keys are strings, matching
            the keys returned in the settings property. The values are `Setting`
            instances.
        :param item: Item to process
        :returns: True if item is valid, False otherwise.
        """
        item_settings = self._get_item_settings(settings)
        work_template = item.properties["work_template"]
        publish_template = item.properties["publish_template"]
        path = item_settings["to_publish"]
        # Check the filepath is valid
        if not work_template.validate(path):
            raise sgtk.TankError("The filepath '{}' does not match the template '{}'".format(path, work_template.name))
        # Check it exists on disk
        glob_path = re.sub(r"\..+\.", ".*.", path)
        if not glob.glob(glob_path):
            raise sgtk.TankError("The filepath '{}' does not exist on disk".format(path))
        fields = work_template.validate_and_get_fields(path)
        publish_path = publish_template.apply_fields(fields)
        item.properties["publish_path"] = publish_path
        item.properties["path"] = path
        image_seq = self._get_sequence_paths(item)
        if image_seq:
            item.properties["sequence_paths"] = image_seq
        return super(KatanaRenderPublishPlugin, self).validate(settings, item)
예제 #2
0
    def compute_path(self, node):
        # Get relevant fields from the scene filename and contents
        work_file_fields = self.__get_hipfile_fields()
        if not work_file_fields:
            msg = "This Houdini file is not a Shotgun Toolkit work file!"
            raise sgtk.TankError(msg)

        # Get the templates from the app
        template = self._app.get_template("work_cache_template")

        # create fields dict with all the metadata
        fields = {}
        fields["name"] = work_file_fields.get("name")
        fields["version"] = work_file_fields["version"]
        fields["renderpass"] = node.name()
        fields["SEQ"] = "FORMAT: $F"

        # Get the camera width and height if necessary
        if "width" in template.keys or "height" in template.keys:
            # Get the camera
            cam_path = node.parm("geometry1_camera").eval()
            cam_node = hou.node(cam_path)
            if not cam_node:
                raise sgtk.TankError("Camera %s not found." % cam_path)

            fields["width"] = cam_node.parm("resx").eval()
            fields["height"] = cam_node.parm("resy").eval()

        fields.update(self._app.context.as_template_fields(template))

        path = template.apply_fields(fields)
        path = path.replace(os.path.sep, "/")

        return path
예제 #3
0
    def run(self):
        try:
            if sgtk.util.is_macos():
                # use built-in screenshot command on the mac
                ret_code = os.system("screencapture -m -i -s %s" % self._path)
                if ret_code != 0:
                    raise sgtk.TankError(
                        "Screen capture tool returned error code %s" % ret_code
                    )

            elif sgtk.util.is_linux():
                # use image magick
                ret_code = os.system("import %s" % self._path)
                if ret_code != 0:
                    raise sgtk.TankError(
                        "Screen capture tool returned error code %s. "
                        "For screen capture to work on Linux, you need to have "
                        "the imagemagick 'import' executable installed and "
                        "in your PATH." % ret_code
                    )

            else:
                raise sgtk.TankError("Unsupported platform.")
        except Exception as e:
            self._error = str(e)
예제 #4
0
    def compute_path(self, node):
        # Get relevant fields from the scene filename and contents
        work_file_fields = self.__get_hipfile_fields()
        if not work_file_fields:
            msg = "This Houdini file is not a Shotgun Toolkit work file!"
            raise sgtk.TankError(msg)

        # Get the templates from the app
        template = self._app.get_template("output_cache_template")

        # get the Step name field for the templated Mantra Output
        entity_name = ""
        asset_type = ""

        try:
            ctx = self._app.context
            entity_name = ctx.entity['name']
            entity_type = ctx.entity['type']
        except:
            msg = "Could not set the Shotgun context Entity name."
            self._app.log_debug(msg)
            raise sgtk.TankError(msg)
        # create fields dict with all the metadata
        fields = {}

        if entity_type == "Shot":
            fields = {
                "name": work_file_fields.get("name", None),
                "node": node.name(),
                "renderpass": node.name(),
                "HSEQ": "FORMAT: $F",
                "version": work_file_fields.get("version", None),
                "Shot": entity_name,
                "Step": work_file_fields.get("Step", None)
            }

        # Asset Template fields
        if entity_type == "Asset":
            # Set the Custom Asset Type
            asset_type = work_file_fields.get("sg_asset_type", None)

            fields = {
                "name": work_file_fields.get("name", None),
                "node": node.name(),
                "renderpass": node.name(),
                "HSEQ": "FORMAT: $F",
                "version": work_file_fields.get("version", None),
                "Asset": entity_name,
                "sg_asset_type": asset_type,
                "Step": work_file_fields.get("Step", None)
            }

        fields.update(self._app.context.as_template_fields(template))

        path = template.apply_fields(fields)
        path = path.replace(os.path.sep, "/")

        return path
예제 #5
0
def __install_extension(ext_path, dest_dir, logger):
    """
    Installs the supplied extension path by unzipping it directly into the
    supplied destination directory.

    :param ext_path: The path to the .zxp extension.
    :param dest_dir: The CEP extension's destination
    :return:
    """

    # move the installed extension to the backup directory
    if os.path.exists(dest_dir):
        backup_ext_dir = tempfile.mkdtemp()
        logger.debug("Backing up the installed extension to: %s" %
                     (backup_ext_dir, ))
        try:
            backup_folder(dest_dir, backup_ext_dir)
        except Exception:
            shutil.rmtree(backup_ext_dir)
            raise sgtk.TankError(
                "Unable to create backup during extension update.")

        # now remove the installed extension
        logger.debug("Removing the installed extension directory...")
        try:
            shutil.rmtree(dest_dir)
        except Exception:
            # try to restore the backup
            move_folder(backup_ext_dir, dest_dir)
            raise sgtk.TankError(
                "Unable to remove the old extension during update.")

    logger.debug("Installing bundled extension: '%s' to '%s'" %
                 (ext_path, dest_dir))

    # make sure the bundled extension exists
    if not os.path.exists(ext_path):
        # retrieve backup before aborting install
        move_folder(backup_ext_dir, dest_dir)
        raise sgtk.TankError(
            "Expected CEP extension does not exist. Looking for %s" %
            (ext_path, ))

    # extract the .zxp file into the destination dir
    with contextlib.closing(zipfile.ZipFile(ext_path, "r")) as ext_zxp:
        ext_zxp.extractall(dest_dir)

    # if we're here, the install was successful. remove the backup
    try:
        logger.debug("Install success. Removing the backed up extension.")
        shutil.rmtree(backup_ext_dir)
    except Exception:
        # can't remove temp dir. no biggie.
        pass
예제 #6
0
def _external_screenshot():
    """
    Use an external approach for grabbing a screenshot.
    Linux and macosx support only.
    
    :returns: Captured image
    :rtype: :class:`~PySide.QtGui.QPixmap`
    """
    output_path = tempfile.NamedTemporaryFile(suffix=".png",
                                              prefix="screencapture_",
                                              delete=False).name

    pm = None
    try:
        # do screenshot with thread so we don't block anything
        screenshot_thread = ExternalCaptureThread(output_path)
        screenshot_thread.start()
        while not screenshot_thread.isFinished():
            screenshot_thread.wait(100)
            QtGui.QApplication.processEvents()

        if screenshot_thread.error_message:
            raise sgtk.TankError("Failed to capture "
                                 "screenshot: %s" % screenshot_thread.error_message)

        # load into pixmap:
        pm = QtGui.QPixmap(output_path)
    finally:
        # remove the temporary file
        if output_path and os.path.exists(output_path):
            os.remove(output_path)

    return pm
예제 #7
0
def _get_render_resolution(node):
    """Returns render resolution for supplied node based on its camera parm.

    :param hou.Node node: The node being acted upon.

    """

    # Get the camera
    cam_path = node.parm("camera").eval()
    cam_node = hou.node(cam_path)

    if not cam_node:
        raise sgtk.TankError("Camera %s not found." % (cam_path, ))

    width = cam_node.parm("resx").eval()
    height = cam_node.parm("resy").eval()

    # Calculate Resolution Override
    if node.parm("override_camerares").eval():
        scale = node.parm("res_fraction").eval()
        if scale == "specific":
            width = node.parm("res_overridex").eval()
            height = node.parm("res_overridey").eval()
        else:
            width = int(float(width) * float(scale))
            height = int(float(height) * float(scale))

    return width, height
예제 #8
0
 def __getattr__(self, name):
     raise sgtk.TankError(
         "Looks like you are trying to run an App that uses a QT "
         "based UI, however the Shell engine could not find a PyQt "
         "or PySide installation in your python system path. We "
         "recommend that you install PySide if you want to "
         "run UI applications from the Shell.")
예제 #9
0
    def _compute_backup_output_path(self, node):
        # get relevant fields from the current file path
        work_file_fields = self._get_hipfile_fields()

        if not work_file_fields:
            msg = "This Houdini file is not a Shotgun Toolkit work file!"
            raise sgtk.TankError(msg)

        # Get the type of output
        type_parm = node.parm('types')
        extension = type_parm.menuLabels()[type_parm.evalAsInt()]

        # create fields dict with all the metadata
        fields = {
            "name": work_file_fields.get("name", None),
            "node": self._getNodeName(node),
            "version": node.parm('ver').evalAsInt(),
            "ext": extension,
        }

        output_profile = self._get_output_profile(node)
        output_cache_template = self._app.get_template_by_name(
            output_profile["output_backup_template"])

        fields.update(
            self._app.context.as_template_fields(output_cache_template))

        path = output_cache_template.apply_fields(fields)
        path = path.replace(os.path.sep, "/")

        return path
예제 #10
0
    def _compute_backup_output_path(self, node):
        # get relevant fields from the current file path
        work_file_fields = self._get_hipfile_fields()

        if not work_file_fields:
            msg = "This Houdini file is not a Shotgun Toolkit work file!"
            raise sgtk.TankError(msg)

        output_profile = self._get_output_profile(node)
        output_cache_template = self._app.get_template_by_name(
                        output_profile["output_backup_render_template"])

        # create fields dict with all the metadata
        fields = {
            "name": work_file_fields.get("name", None),
            "RenderLayer": node.name().replace('_', ''),
            "version": node.parm('ver').evalAsInt(),
            "Camera": node.parm("camera").evalAsString().split('/')[-1].replace('_', ''),
        }

        fields.update(self._app.context.as_template_fields(
            output_cache_template))

        path = output_cache_template.apply_fields(fields)
        path = path.replace(os.path.sep, "/")

        return path
예제 #11
0
    def test_download_local(self):
        """
        Test that download_local downloads from the correct URL, and handles an Exception as expected.
        """
        desc = self._create_desc()
        expected_url = "https://github.com/{o}/{r}/archive/{v}.zip"
        expected_url = expected_url.format(
            o=self.default_location_dict["organization"],
            r=self.default_location_dict["repository"],
            v=self.default_location_dict["version"],
        )

        with patch("tank.util.shotgun.download.download_and_unpack_url"
                   ) as download_and_unpack_url_mock:
            # Raise an exception first and ensure it's caught and a TankDescriptorError is raised.
            download_and_unpack_url_mock.side_effect = sgtk.TankError()
            with self.assertRaises(sgtk.descriptor.TankDescriptorError):
                desc.download_local()

        with patch("tank.util.shotgun.download.download_and_unpack_url"
                   ) as download_and_unpack_url_mock:
            # Now reset and let the download "succeed" and ensure the correct calls were made, and
            # the expected arguments were passed.
            desc.download_local()
            calls = download_and_unpack_url_mock.call_args_list
            self.assertEqual(len(calls), 1)
            # calls will be a tuple of call objects, which can be indexed into to
            # get tuples of (args, kwargs).
            # first positional arg of first call
            self.assertEqual(calls[0][0][0], self.mockgun)
            # second positional arg of first call
            self.assertEqual(calls[0][0][1], expected_url)
    def search(self, text):
        """
        Triggers the popup to display results based on the supplied text.

        :param text: current contents of editor
        """
        if len(text) < self.COMPLETE_MINIMUM_CHARACTERS:
            # global search wont work with shorter than 3 len strings
            # for these cases, clear the auto completer model fully
            # there is no more work to do
            self.clear()
            return

        # now we are about to populate the model with data
        # and therefore trigger the completer to pop up.
        #
        # The completer seems to have some internal properties
        # which are transitory and won't last between sessions.
        # for these, we have to set them up every time the
        # completion process is about to start it seems.

        # tell completer to render matches using our delegate
        # configure how the popup should look

        self._set_item_delegate(self.popup(), text)

        # try to disconnect and reconnect the activated signal
        # it seems this signal is lost every time the widget
        # looses focus.
        try:
            self.activated[QtCore.QModelIndex].disconnect(self._on_select)
        except Exception:
            self._bundle.log_debug(
                "Could not disconnect activated signal prior to "
                "reconnect. Looks like this connection must have been "
                "discarded at some point along the way."
            )

        self.activated[QtCore.QModelIndex].connect(self._on_select)

        # now clear the model
        self._clear_model()

        # clear thumbnail map
        self._thumb_map = {}

        # kick off async data request from shotgun
        # we request to run an arbitrary method in the worker thread
        # this  _do_sg_global_search method will be called by the worker
        # thread when the worker queue reaches that point and will
        # call out to execute it. The data dictionary specified will
        # be forwarded to the method.
        if self._sg_data_retriever:
            # clear download queue and do the new search
            self._sg_data_retriever.clear()
            self._processing_id = self._launch_sg_search(text)
        else:
            raise sgtk.TankError(
                "Please associate this class with a background task manager."
            )
예제 #13
0
    def _compute_output_path(self, node):

        # get relevant fields from the current file path
        work_file_fields = self._get_hipfile_fields()

        if not work_file_fields:
            msg = "This Houdini file is not a Shotgun Toolkit work file!"
            raise sgtk.TankError(msg)

        output_profile = self._get_output_profile(node)

        # Get the cache templates from the app
        output_cache_template = self._app.get_template_by_name(
            output_profile["output_cache_template"])

        # create fields dict with all the metadata
        fields = {
            "name": work_file_fields.get("name", None),
            "node": node.name(),
            "renderpass": node.name(),
            "SEQ": "FORMAT: $F",
            "version": work_file_fields.get("version", None),
        }

        fields.update(
            self._app.context.as_template_fields(output_cache_template))

        path = output_cache_template.apply_fields(fields)
        path = path.replace(os.path.sep, "/")

        return path
예제 #14
0
def ensure_extension_up_to_date(logger):
    """
    Carry out the necessary operations needed in order for the
    Adobe extension to be recognized.

    This inlcudes copying the extension from the engine location
    to a OS-specific location.
    """

    # the basic plugin needs to be installed in order to launch the Adobe
    # engine. we need to make sure the plugin is installed and up-to-date.
    # will only run if SHOTGUN_ADOBE_DISABLE_AUTO_INSTALL is not set.
    if "SHOTGUN_ADOBE_DISABLE_AUTO_INSTALL" not in os.environ:
        logger.debug("Ensuring Adobe extension is up-to-date...")
        try:
            __ensure_extension_up_to_date(logger)
        except Exception:
            exc = traceback.format_exc()
            raise sgtk.TankError(
                "There was a problem ensuring the Adobe integration extension "
                "was up-to-date with your toolkit engine. If this is a "
                "recurring issue please contact us via %s. "
                "The specific error message encountered was:\n'%s'." % (
                    sgtk.support_url,
                    exc,
                ))
예제 #15
0
 def __getattr__(self, name):
     raise sgtk.TankError(
         "Looks like you are trying to run an App that uses a QT based UI, however the "
         "python installation that the Desktop engine is currently using does not seem "
         "to contain a valid PySide or PyQt4 install. Either install PySide into your "
         "python environment or alternatively switch back to using the native Shotgun "
         "Desktop python installation, which includes full QT support."
     )
예제 #16
0
    def validate(self, settings, item):
        """
        Validates the given item to check that it is ok to publish. Returns a
        boolean to indicate validity.

        :param settings: Dictionary of Settings. The keys are strings, matching
            the keys returned in the settings property. The values are `Setting`
            instances.
        :param item: Item to process
        :returns: True if item is valid, False otherwise.
        """
        item_settings = self._get_item_settings(settings)
        # Check the templates exist
        work_template = item.properties["work_template"]
        if not work_template:
            raise sgtk.TankError(
                "'{}': Work template '{}' doesn't exist".format(
                    item.name, work_template.name))
        publish_template = item.properties["publish_template"]
        if not publish_template:
            raise sgtk.TankError(
                "'{}': Publish template '{}' doesn't exist".format(
                    item.name, publish_template.name))
        # Check the file exists still. It might have been removed from the filesystem after collection
        path = item_settings["to_publish"]
        if not os.path.exists(path):
            raise sgtk.TankError(
                "The file '{}' no longer exists on disk. Has it been deleted/published?"
                .format(path))
        # Check the filepath is valid
        if not work_template.validate(path):
            raise sgtk.TankError(
                "The filepath '{}' does not match the template '{}'".format(
                    path, work_template.name))
        # Check if file has already been copied to the publish location
        fields = work_template.validate_and_get_fields(path)
        publish_path = publish_template.apply_fields(fields)
        item.properties["publish_path"] = publish_path
        item.properties["path"] = path
        if os.path.exists(publish_path):
            raise IOError(
                errno.EEXIST,
                "The file '{}' has already been copied to the publish location."
                .format(path))

        return True
예제 #17
0
    def value(self, value):
        """
        Set the widget's value.

        :param value: The value to set the widget's value to.
        :type value: any
        """

        raise sgtk.TankError("Abstract class method not overriden")
예제 #18
0
    def restore(self, state):
        """
        Restore the widget to the provided state.

        :param state: The state to restore the widget from.
        :type state: any
        """

        raise sgtk.TankError("Abstract class method not overriden")
예제 #19
0
    def init_engine(self):
        self.logger.debug("%s: Initializing..." % self)

        if self.context.project is None:
            # must have at least a project in the context to even start!
            raise sgtk.TankError("The Motionbuilder engine needs at least a project in the context "
                                 "in order to start! Your context: %s" % self.context)

        # motionbuilder doesn't have good exception handling, so install our own trap
        sys.excepthook = sgtk_mobu_exception_trap
예제 #20
0
    def persistent(self, is_persistent):
        """Set the item to persistent or not.

        Only top-level items can be set to persistent.
        """
        # It's not a crime to turn persistence off, so raise an error when
        # actually trying it on an invalid item.
        if is_persistent and (not self.parent or not self.parent.is_root):
            raise sgtk.TankError(
                "Only top-level tree items can be made persistent.")

        self._persistent = is_persistent
예제 #21
0
    def remove_item(self, child_item):
        """
        Remove the supplied child :ref:`publish-api-item` of this item.

        :param child_item: The child :ref:`publish-api-item` to remove.
        """

        if child_item not in self.children:
            raise sgtk.TankError(
                "Unable to remove child item. Item %s is not a child of %s in "
                "the publish tree." % (child_item, self))

        self._children.remove(child_item)
예제 #22
0
    def _get_template(self, template_name):
        """
        Get a shotgun template from the given name.

        :param str template_name: The name of the template to get.

        :rtype: An :class:`sgtk.Template` instance.

        :raises: :class:`sgtk.TankError` if no template name supplied
            or the template doesn't exist.
        """
        setting_template_name = self.extra_args.get(template_name)
        if not setting_template_name:
            raise sgtk.TankError(
                "No template name '{}' defined for node type '{}'"
                "".format(template_name, self.NODE_TYPE))
        template = self.parent.get_template_by_name(setting_template_name)
        if not template:
            raise sgtk.TankError(
                "Can't find template called '{}' defined for node type '{}'"
                "".format(setting_template_name, self.NODE_TYPE))
        return template
예제 #23
0
    def __compute_path(self, node, settings, template_alias, aov_name=None):
        # Get relevant fields from the scene filename and contents
        work_file_fields = self.__get_hipfile_fields()
        if not work_file_fields:
            msg = "This Houdini file is not a Shotgun Toolkit work file!"
            raise sgtk.TankError(msg)

        # Get the templates from the node settings
        template_name = settings.get(template_alias)
        template = self._app.get_template_by_name(template_name)

        if not template:
            msg = 'No Template provided for "{0}"'
            raise sgtk.TankError(msg.format(template_name))

        # create fields dict with all the metadata
        fields = dict()
        fields["name"] = work_file_fields.get("name")
        fields["version"] = work_file_fields["version"]
        # fields["node"] = self.get_node_name(node)
        fields["renderpass"] = self.get_node_name(node)
        fields["SEQ"] = "FORMAT: $F"

        if aov_name:
            # fields["channel"] = channel_name
            fields["aov_name"] = aov_name

        # Get the camera width and height if necessary
        if "width" in template.keys or "height" in template.keys:
            width, height = self.__gather_render_resolution(node)
            fields["width"] = width
            fields["height"] = height

        fields.update(self._app.context.as_template_fields(template))

        path = template.apply_fields(fields)
        path = path.replace('\\', '/')
        return path
예제 #24
0
    def init_engine(self):
        """
        Main initialization entry point.
        """

        self.logger.debug("%s: Initializing..." % self)

        if hou.applicationVersion()[0] < 14:
            raise sgtk.TankError(
                "Your version of Houdini is not supported. Currently, Toolkit "
                "only supports version 14+.")

        # keep track of if a UI exists
        self._ui_enabled = hasattr(hou, 'ui')
예제 #25
0
    def _compute_output_path(self, node):

        # get relevant fields from the current file path
        work_file_fields = self._get_hipfile_fields(node)

        if not work_file_fields:
            msg = "This Houdini file is not a Shotgun Toolkit work file!"
            raise sgtk.TankError(msg)

        output_profile = self._get_output_profile(node)

        if node.evalParm('shared_out'):
            output_cache_template = self._app.get_template_by_name(
                output_profile["output_cache_shared_template"])
        else:
            # Get the cache templates from the app
            output_cache_template = self._app.get_template_by_name(
                output_profile["output_cache_template"])

        # Get the type of output
        extension = node.evalParm('types')
        types = {
            0: 'bgeo.sc',
            1: 'obj',
            2: 'bgeo',
            3: 'vdb',
        }

        # create fields dict with all the metadata
        fields = {
            "name": work_file_fields.get("name", None),
            "node": node.name(),
            "renderpass": node.name(),
            "version": work_file_fields.get("version", None),
            "geo.ext": types[extension]
        }

        if node.evalParm('seq') == 1:
            fields["SEQ"] = "FORMAT: $F"
        else:
            fields["SEQ"] = None

        fields.update(
            self._app.context.as_template_fields(output_cache_template))

        path = output_cache_template.apply_fields(fields)
        path = path.replace(os.path.sep, "/")

        return path
예제 #26
0
    def _compute_output_path(self, node, template_name, aov_name=None):
        """Compute output path based on current work file and render template.

        :param hou.Node node: The node being acted upon.
        :param str template_name: The name of template to compute a path for.
        :param str aov_name: Optional AOV name used to compute the path.

        """

        # Get relevant fields from the scene filename and contents
        work_file_fields = self._get_hipfile_fields()

        if not work_file_fields:
            msg = "This Houdini file is not a Shotgun Toolkit work file!"
            raise sgtk.TankError(msg)

        output_profile = self._get_output_profile(node)

        # Get the render template from the app
        output_template = self._app.get_template_by_name(
            output_profile[template_name])

        # create fields dict with all the metadata
        fields = {
            "name": work_file_fields.get("name", None),
            "node": node.name(),
            "renderpass": node.name(),
            "SEQ": "FORMAT: $F",
            "version": work_file_fields.get("version", None),
        }

        # use %V - full view printout as default for the eye field
        fields["eye"] = "%V"

        if aov_name:
            fields["aov_name"] = aov_name

        # Get the camera width and height if necessary
        if "width" in output_template.keys or "height" in output_template.keys:
            width, height = _get_render_resolution(node)
            fields["width"] = width
            fields["height"] = height

        fields.update(self._app.context.as_template_fields(output_template))

        path = output_template.apply_fields(fields)
        path = path.replace(os.path.sep, "/")

        return path
예제 #27
0
 def capture_screenshot(self, path):
     if sys.platform == "linux2":
         # use image magick in sgtk clean env
         context = self.parent.context
         print context
         ret_code, error_msg = dd_jstools_utils.run_in_clean_env(
             ["import", path], context)
         if ret_code != 0:
             raise sgtk.TankError(
                 "Screen capture tool returned error code: %s, message: `%s`."
                 "For screen capture to work on Linux, you need to have "
                 "the imagemagick 'import' executable installed and "
                 "in your PATH." % (ret_code, error_msg))
     else:
         super(ExternalScreenshot, self).capture_screenshot(path)
    def create_new_task(self, name, pipeline_step, entity, assigned_to=None):
        """
        Create a new task with the specified information.

        :param name: Name of the task to be created.

        :param pipeline_step: Pipeline step associated with the task.
        :type pipeline_step: dictionary with keys 'Type' and 'id'

        :param entity: Entity associated with this task.
        :type entity: dictionary with keys 'Type' and 'id'

        :param assigned_to: User assigned to the task. Can be None.
        :type assigned_to: dictionary with keys 'Type' and 'id'

        :returns: The created task.
        :rtype: dictionary with keys 'step', 'project', 'entity', 'content' and 'task_assignees' if
            'assigned_to' was set.

        :raises sgtk.TankError: On error, during validation or creation, this method
            raises a TankError.
        """
        app = self.parent

        # construct the data for the new Task entity:
        data = {
            "step": pipeline_step,
            "project": app.context.project,
            "entity": entity,
            "content": name,
        }
        if assigned_to:
            data["task_assignees"] = [assigned_to]

        # create the task:
        sg_result = app.shotgun.create("Task", data)
        if not sg_result:
            raise sgtk.TankError("Failed to create new task - reason unknown!")

        # try to set it to IP - not all studios use IP so be careful!
        try:
            app.shotgun.update("Task", sg_result["id"],
                               {"sg_status_list": "ip"})
        except:
            pass

        return sg_result
def take_over(func):
    """
    Decorator to accumulate the names of members to take over in derived classes.

    :param func: A function or method to takeover in the derived class.
    """

    if hasattr(func, "__name__"):
        # regular method
        name = func.__name__
    elif hasattr(func, "__func__"):
        # static or class method
        name = func.__func__.__name__
    else:
        raise sgtk.TankError("Don't know how to take over member: %s" % (func,))
    TAKE_OVER_NAMES.append(name)
    return func
예제 #30
0
    def _compute_output_path(self, node, template_name, aov_name=None):
        """Compute output path based on current work file and render template.

        :param hou.Node node: The node being acted upon.
        :param str template_name: The name of template to compute a path for.
        :param str aov_name: Optional AOV name used to compute the path.

        """

        # Get relevant fields from the scene filename and contents
        work_file_fields = self._get_hipfile_fields()

        if not work_file_fields:
            msg = "This Houdini file is not a Shotgun Toolkit work file!"
            raise sgtk.TankError(msg)

        output_profile = self._get_output_profile(node)

        # Get the render template from the app
        output_template = self._app.get_template_by_name(
            output_profile[template_name])

        # create fields dict with all the metadata
        fields = {
            "name": work_file_fields.get("name", None),
            "Step": work_file_fields.get("Step", None),
            "RenderLayer": node.name().replace('_', ''),
            "Camera": node.parm("camera").evalAsString().split('/')[-1].replace('_', ''),
            "SEQ": "FORMAT: $F",
            "version": node.parm('ver').evalAsInt(),
            "AOV": self.TK_DEFAULT_AOV
        } 

        # use %V - full view printout as default for the eye field
        fields["eye"] = "%V"

        if aov_name:
            fields["aov_name"] = aov_name

        fields.update(self._app.context.as_template_fields(output_template))

        path = output_template.apply_fields(fields)
        path = path.replace(os.path.sep, "/")

        return path