Skip to content
Snippets Groups Projects
algorithm.py 8.01 KiB
Newer Older
from base import BaseDirective
from docutils import nodes
from sphinx.locale import _
from sphinx.util.compat import make_admonition
REDIRECT_TEMPLATE = "redirect.html"

DEPRECATE_USE_ALG_RE = re.compile(r'Use\s([A-Z][a-zA-Z0-9]+)\sinstead')

#--------------------------------------------------------------------------
class AlgorithmDirective(BaseDirective):

    """
    Inserts details of an algorithm by querying Mantid

    Adds:
     - A referenceable link for use with Sphinx ":ref:`` tags". If this is
       the highest version of the algorithm being processed then a both
       a versioned link is created and a non-versioned link
     - A title
     - A screenshot of the algorithm
     - Table of contents

    If the algorithms is deprecated then a warning is inserted.

    It requires a SCREENSHOTS_DIR environment variable to be set to the
    directory where a screenshot should be generated. If it is not set then
    a RuntimeError occurs
    required_arguments, optional_arguments = 0, 0

    def run(self):
        """
        Called by Sphinx when the ..algorithm:: directive is encountered
        """
        self._track_algorithm()
        self._insert_reference_link()
        self._insert_pagetitle()
        imgpath = self._create_screenshot()
        self._insert_screenshot_link(imgpath)
        self._insert_toc()
        self._insert_deprecation_warning()
        self.commit_rst()
        return []
    def _track_algorithm(self):
        Keep a track of the highest versions of algorithms encountered.
        The algorithm name and version are retrieved from the document name.
        See BaseDirective::set_algorithm_and_version()
        """
        env = self.state.document.settings.env
        if not hasattr(env, "algorithm"):
            env.algorithms = {}
        #endif

        name, version = self.algorithm_name(), self.algorithm_version()
        algorithms = env.algorithms
        if name in algorithms:
            prev_version = algorithms[name][1]
            if version > prev_version:
                algorithms[name][1] = version
        else:
            algorithms[name] = (name, version)

    def _insert_reference_link(self):
        Outputs a reference to the top of the algorithm's rst
        of the form ".. _algm-AlgorithmName-vVersion:", so that
        the page can be referenced using 
        :ref:`algm-AlgorithmName-version`. If this is the highest 
        version then it also outputs a reference ".. _algm-AlgorithmName:
        from mantid.api import AlgorithmFactory

        alg_name = self.algorithm_name()
        version = self.algorithm_version()
        self.add_rst(".. _algm-%s-v%d:\n" % (alg_name, version))

        if AlgorithmFactory.highestVersion(alg_name) == version:
            self.add_rst(".. _algm-%s:\n" % alg_name)
    def _insert_pagetitle(self):
        Outputs a title for the page
        """
        title = "%s v%d" % (self.algorithm_name(), self.algorithm_version())
        self.add_rst(self.make_header(title, True))
    def _insert_toc(self):
        """
        Outputs a title for the page
        """
        self.add_rst(".. contents:: Table of Contents\n    :local:\n")
    def _create_screenshot(self):
        Creates a screenshot for the named algorithm in the "images/screenshots"
        subdirectory.
        The file will be named "algorithmname-vX_dlg.png", e.g. Rebin-v1_dlg.png
          str: The full path to the created image
        """
        from mantiddoc.tools.screenshot import algorithm_screenshot

        env = self.state.document.settings.env
        screenshots_dir = self._screenshot_directory()
        if not os.path.exists(screenshots_dir):
            os.makedirs(screenshots_dir)

        try:
            imgpath = algorithm_screenshot(self.algorithm_name(), screenshots_dir, version=self.algorithm_version())
        except Exception, exc:
            env.warn(env.docname, "Unable to generate screenshot for '%s' - %s" % (algorithm_name, str(exc)))
            imgpath = os.path.join(screenshots_dir, "failed_dialog.png")

        return imgpath

    def _insert_screenshot_link(self, img_path):
        Outputs an image link with a custom :class: style. The filename is
        extracted from the path given and then a relative link to the
        directory specified by the SCREENSHOTS_DIR environment variable from
        the root source directory is formed.

        Args:
          img_path (str): The full path as on the filesystem to the image
        """
        env = self.state.document.settings.env
        format_str = ".. figure:: %s\n"\
                     "    :class: screenshot\n\n"\
        # Sphinx assumes that an absolute path is actually relative to the directory containing the
        # conf.py file and a relative path is relative to the directory where the current rst file
        # is located.

        filename = os.path.split(img_path)[1]
        cfgdir = env.srcdir
        screenshots_dir = self._screenshot_directory()
        rel_path = os.path.relpath(screenshots_dir, cfgdir)
        # This is a href link so is expected to be in unix style
        rel_path = rel_path.replace("\\","/")
        # stick a "/" as the first character so Sphinx computes relative location from source directory
        path = "/" + rel_path + "/" + filename
        caption = "A screenshot of the **" + self.algorithm_name() + "** dialog."
        self.add_rst(format_str % (path, caption))
        """
        Returns a full path where the screenshots should be generated. They are
        put in a screenshots subdirectory of the main images directory in the source
        tree. Sphinx then handles copying them to the final location

        Arguments:
          env (BuildEnvironment): Allows access to find the source directory

        Returns:
          str: A string containing a path to where the screenshots should be created. This will
          be a filesystem path
        """
        try:
            return os.environ["SCREENSHOTS_DIR"]
        except:
            raise RuntimeError("The '.. algorithm::' directive requires a SCREENSHOTS_DIR environment variable to be set.")
    def _insert_deprecation_warning(self):
        """
        If the algorithm version is deprecated then construct a warning message
        """
        from mantid.api import DeprecatedAlgorithmChecker

        checker = DeprecatedAlgorithmChecker(self.algorithm_name(), self.algorithm_version())
        msg = checker.isDeprecated()
        if len(msg) == 0:
            return

        # Check for message to use another algorithm an insert a link
        match = DEPRECATE_USE_ALG_RE.search(msg)
        if match is not None and len(match.groups()) == 1:
            name = match.group(0)
            msg = DEPRECATE_USE_ALG_RE.sub(r"Use :ref:`algm-\1` instead.", msg)
        self.add_rst(".. warning:: %s" % msg)


#------------------------------------------------------------------------------------------------------------

def html_collect_pages(app):
    """
    Write out unversioned algorithm pages that redirect to the highest version of the algorithm
    """
    env = app.builder.env
    if not hasattr(env, "algorithms"):
        return # nothing to do

    template = REDIRECT_TEMPLATE

    algorithms = env.algorithms
    for name, highest_version in algorithms.itervalues():
        redirect_pagename = "algorithms/%s" % name
        target = "%s-v%d.html" % (name, highest_version)
        context = {"name" : name, "target" : target}
        yield (redirect_pagename, context, template)
#------------------------------------------------------------------------------------------------------------

def setup(app):
    """
    Setup the directives when the extension is activated

    Args:
      app: The main Sphinx application object
    """
    app.add_directive('algorithm', AlgorithmDirective)

    # connect event html collection to handler
    app.connect("html-collect-pages", html_collect_pages)