from mantiddoc.directives.base import AlgorithmBaseDirective import os import re REDIRECT_TEMPLATE = "redirect.html" DEPRECATE_USE_ALG_RE = re.compile(r"Use\s(([A-Z][a-zA-Z0-9]+)\s(version ([0-9])+)?)\s*instead.") # Maximum height in pixels a screenshot image # Any higher than this an an obvious gap starts to appear between the "Properties" header # and the rest SCREENSHOT_MAX_HEIGHT = 250 #-------------------------------------------------------------------------- class AlgorithmDirective(AlgorithmBaseDirective): """ 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 execute(self): """ Called by Sphinx when the ..algorithm:: directive is encountered """ self._insert_pagetitle() picture = self._create_screenshot() self._insert_screenshot_link(picture) self._insert_toc() self._insert_deprecation_warning() return [] def _insert_pagetitle(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 outputs a reference ".. _algm-AlgorithmName: instead It then outputs a title for the page """ from mantid.api import AlgorithmFactory alg_name = self.algorithm_name() version = self.algorithm_version() # page reference must come directly before the title if it wants # to be referenced without defining the link text. Here we put the # specific version one first so that it always must be referenced # using the full link text ":ref`LinkText <algm-AlgorithmName-vX>`:" self.add_rst(".. _algm-%s-v%d:\n\n" % (alg_name, version)) if AlgorithmFactory.highestVersion(alg_name) == version: self.add_rst(".. _algm-%s:\n\n" % alg_name) # title title = "%s v%d" % (alg_name, version) self.add_rst(self.make_header(title, True)) self.add_rst(u"\n.. index:: %s-v%d\n\n" % (alg_name, version)) 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 Returns: screenshot: A mantiddoc.tools.Screenshot object """ try: screenshots_dir = self._screenshot_directory() except RuntimeError: return None # Generate image from mantiddoc.tools.screenshot import algorithm_screenshot if not os.path.exists(screenshots_dir): os.makedirs(screenshots_dir) try: picture = algorithm_screenshot(self.algorithm_name(), screenshots_dir, version=self.algorithm_version()) except RuntimeError, exc: env = self.state.document.settings.env env.warn(env.docname, "Unable to generate screenshot for '%s' - %s" % (self.algorithm_name(), str(exc))) picture = None return picture def _insert_screenshot_link(self, picture): """ 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: picture (Screenshot): A Screenshot object """ env = self.state.document.settings.env format_str = ".. figure:: %s\n"\ " :class: screenshot\n"\ " :width: %dpx\n"\ " :align: right\n\n"\ " %s\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. if picture: screenshots_dir, filename = os.path.split(picture.imgpath) # Find the width of the image width, height = picture.width, picture.height if height > SCREENSHOT_MAX_HEIGHT: aspect_ratio = float(width)/height width = int(SCREENSHOT_MAX_HEIGHT*aspect_ratio) #endif # relative path to image rel_path = os.path.relpath(screenshots_dir, env.srcdir) # 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 else: # use stock not found image path = "/images/ImageNotFound.png" width = 200 caption = "**" + self.algorithm_name() + "** dialog." self.add_rst(format_str % (path, width, caption)) def _screenshot_directory(self): """ 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()) == 4: link_text = match.group(1) alg_name = match.group(2) version = match.group(4) link = "algm-%s%s" if version is not None: link = link % (alg_name, "-v" + str(version)) else: link = link % (alg_name, "") replacement = "Use :ref:`%s <%s>` instead." % (link_text, link) msg = DEPRECATE_USE_ALG_RE.sub(replacement, msg) # endif 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 """ from mantid.api import AlgorithmFactory template = REDIRECT_TEMPLATE all_algs = AlgorithmFactory.getRegisteredAlgorithms(True) for name, versions in all_algs.iteritems(): redirect_pagename = "algorithms/%s" % name versions.sort() highest_version = versions[-1] 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)