Skip to content
Snippets Groups Projects
Commit ca642c60 authored by Gigg, Martyn Anthony's avatar Gigg, Martyn Anthony
Browse files

Require versioned algorithm files

Implements a test for a simple redirect system to the highest version
Refs #9521
parent dfae6838
No related merge requests found
from base import BaseDirective
import os
REDIRECT_TEMPLATE = "redirect.html"
class AlgorithmDirective(BaseDirective):
"""
......@@ -8,19 +10,19 @@ class AlgorithmDirective(BaseDirective):
and a screenshot of the algorithm to an rst file.
"""
required_arguments, optional_arguments = 1, 0
required_arguments, optional_arguments = 0, 0
def run(self):
"""
Called by Sphinx when the ..algorithm:: directive is encountered
"""
algorithm_name = str(self.arguments[0])
algorithm_name, version = self._algorithm_name_and_version()
# Seperate methods for each unique piece of functionality.
reference = self._make_reference_link(algorithm_name)
title = self._make_header(algorithm_name, True)
toc = self._make_local_toc()
imgpath = self._create_screenshot(algorithm_name)
imgpath = self._create_screenshot(algorithm_name, version)
screenshot = self._make_screenshot_link(algorithm_name, imgpath)
return self._insert_rest(reference + title + screenshot + toc)
......@@ -36,20 +38,21 @@ class AlgorithmDirective(BaseDirective):
Returns:
str: A ReST formatted reference.
"""
return ".. _%s:\n" % algorithm_name
return ".. _algorithm|%s:\n" % algorithm_name
def _make_local_toc(self):
return ".. contents:: Table of Contents\n :local:\n"
def _create_screenshot(self, algorithm_name):
def _create_screenshot(self, algorithm_name, version):
"""
Creates a screenshot for the named algorithm in an "images/screenshots"
subdirectory of the currently processed document
The file will be named "algorithmname_dlg.png", e.g. Rebin_dlg.png
The file will be named "algorithmname-vX_dlg.png", e.g. Rebin-v1_dlg.png
Args:
algorithm_name (str): The name of the algorithm.
version (int): The version of the algorithm
Returns:
str: The full path to the created image
......@@ -62,7 +65,7 @@ class AlgorithmDirective(BaseDirective):
os.makedirs(screenshots_dir)
try:
imgpath = algorithm_screenshot(algorithm_name, screenshots_dir)
imgpath = algorithm_screenshot(algorithm_name, screenshots_dir, version=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")
......@@ -85,7 +88,7 @@ class AlgorithmDirective(BaseDirective):
"""
format_str = ".. figure:: %s\n"\
" :class: screenshot\n\n"\
" %s\n"
" %s\n\n"
filename = os.path.split(img_path)[1]
path = "/images/screenshots/" + filename
......@@ -111,6 +114,18 @@ class AlgorithmDirective(BaseDirective):
############################################################################################################
def html_collect_pages(app):
"""
Write out unversioned algorithm pages that redirect to the highest version of the algorithm
"""
name = "algorithms/Rebin"
context = {"name" : "Rebin", "target" : "Rebin-v1.html"}
template = REDIRECT_TEMPLATE
return [(name, context, template)]
############################################################################################################
def setup(app):
"""
Setup the directives when the extension is activated
......@@ -119,3 +134,7 @@ def setup(app):
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)
......@@ -7,24 +7,25 @@ class AliasesDirective(BaseDirective):
Obtains the aliases for a given algorithm based on it's name.
"""
required_arguments, optional_arguments = 1, 0
required_arguments, optional_arguments = 0, 0
def run(self):
"""
Called by Sphinx when the ..aliases:: directive is encountered.
"""
title = self._make_header("Aliases")
alias = self._get_alias(str(self.arguments[0]))
alias = self._get_alias()
return self._insert_rest(title + alias)
def _get_alias(self, algorithm_name):
def _get_alias(self):
"""
Return the alias for the named algorithm.
Args:
algorithm_name (str): The name of the algorithm to get the alias for.
"""
alg = self._create_mantid_algorithm(algorithm_name)
name, version = self._algorithm_name_and_version()
alg = self._create_mantid_algorithm(name, version)
return "This algorithm is also known as: " + "**" + alg.alias() + "**"
......
from docutils import statemachine
from docutils.parsers.rst import Directive
import re
class BaseDirective(Directive):
......@@ -11,21 +11,40 @@ class BaseDirective(Directive):
has_content = True
final_argument_whitespace = True
def _make_header(self, name, title=False):
alg_docname_re = re.compile(r'^([A-Z][a-zA-Z0-9]+)-v([0-9][0-9]*)$')
def _algorithm_name_and_version(self):
"""
Returns the name and version of an algorithm based on the name of the
document. The expected name of the document is "AlgorithmName-v?", which
is the name of the file with the extension removed
"""
env = self.state.document.settings.env
# env.docname includes path, using forward slashes, from root of documentation directory
docname = env.docname.split("/")[-1]
match = self.alg_docname_re.match(docname)
if not match or len(match.groups()) != 2:
raise RuntimeError("Document filename '%s.rst' does not match the expected format: AlgorithmName-vX.rst" % docname)
grps = match.groups()
return (str(grps[0]), int(grps[1]))
def _make_header(self, name, pagetitle=False):
"""
Makes a ReStructuredText title from the algorithm's name.
Args:
algorithm_name (str): The name of the algorithm to use for the title.
title (bool): If True, line is inserted above & below algorithm name.
pagetitle (bool): If True, line is inserted above & below algorithm name.
Returns:
str: ReST formatted header with algorithm_name as content.
"""
line = "\n" + "-" * len(name) + "\n"
if title:
if pagetitle:
line = "\n" + "=" * len(name) + "\n"
return line + name + line
else:
line = "\n" + "-" * len(name) + "\n"
return name + line
def _insert_rest(self, text):
......@@ -41,17 +60,18 @@ class BaseDirective(Directive):
self.state_machine.insert_input(statemachine.string2lines(text), "")
return []
def _create_mantid_algorithm(self, algorithm_name):
def _create_mantid_algorithm(self, algorithm_name, version):
"""
Create and initializes a Mantid algorithm.
Args:
algorithm_name (str): The name of the algorithm to use for the title.
version (int): Version of the algorithm to create
Returns:
algorithm: An instance of a Mantid algorithm.
"""
from mantid.api import AlgorithmManager
alg = AlgorithmManager.createUnmanaged(algorithm_name)
alg = AlgorithmManager.createUnmanaged(algorithm_name, version)
alg.initialize()
return alg
"""
Provides directives for dealing with category pages.
While parsing the directives a list of the categories and associated pages/subcategories
is tracked. When the final set of html pages is collected, a processing function
creates "index" pages that lists the contents of each category. The display of each
"index" page is controlled by a jinja2 template.
"""
from base import BaseDirective
CATEGORY_INDEX_TEMPLATE = "category.html"
# relative to the "root" directory
CATEGORIES_HTML_DIR = "categories"
class CategoriesDirective(BaseDirective):
class LinkItem(object):
"""
Defines a linkable item with a name and html reference
"""
# Name displayed on listing page
name = None
# html link
link = None
def __init__(self, name, env):
"""
Arguments:
env (Sphinx.BuildEnvironment): The current environment processing object
"""
self.name = str(name)
rel_path = env.docname # no suffix
# Assumes the link is for the current document and that the
# page that will use this reference is in a single
# subdirectory from root
self.link = "../%s.html" % rel_path
def __eq__(self, other):
"""
Define comparison for two objects as the comparison of their names
Arguments:
other (PageRef): Another PageRef object to compare
"""
return self.name == other.name
def __hash__(self):
return hash(self.name)
def __repr__(self):
return self.name
def html_link(self):
"""
Returns a link for use as a href to refer to this document from a
categories page. It assumes that the category pages are in a subdirectory
of the root and that the item to be referenced is in the algorithms directory
under the root.
Returns:
str: A string containing the link
"""
return self.link
# endclass
class PageRef(LinkItem):
"""
Obtains the categories for a given algorithm based on it's name.
Store details of a single page reference
"""
required_arguments, optional_arguments = 1, 0
def __init__(self, name, env):
super(PageRef, self).__init__(name, env)
#endclass
class Category(LinkItem):
"""
Store information about a single category
"""
# Collection of PageRef objects that link to members of the category
pages = None
# Collection of PageRef objects that form subcategories of this category
subcategories = None
def __init__(self, name, env):
super(Category, self).__init__(name, env)
# override default link
self.link = "../categories/%s.html" % name
self.pages = set([])
self.subcategories = set([])
#endclass
class CategoriesDirective(BaseDirective):
"""
Records the page as part of the given categories. Index pages for each
category are then automatically created after all pages are collected
together.
Subcategories can be given using the "\\" separator, e.g. Algorithms\\Transforms
"""
# requires at least 1 category
required_arguments = 1
# it can be in many categories and we put an arbitrary upper limit here
optional_arguments = 25
def run(self):
"""
Called by Sphinx when the ..categories:: directive is encountered.
Called by Sphinx when the defined directive is encountered.
"""
categories = self._get_categories(str(self.arguments[0]))
return self._insert_rest("\n" + categories)
categories = self._get_categories_list()
display_name = self._get_display_name()
links = self._create_links_and_track(display_name, categories)
return self._insert_rest("\n" + links)
def _get_categories(self, algorithm_name):
def _get_categories_list(self):
"""
Return the categories for the named algorithm.
Returns a list of the category strings
Returns:
list: A list of strings containing the required categories
"""
# Simply return all of the arguments as strings
return self.arguments
def _get_display_name(self):
"""
Returns the name of the item as it should appear in the category
"""
env = self.state.document.settings.env
# env.docname returns relative path from doc root. Use name after last "/" separator
return env.docname.split("/")[-1]
def _create_links_and_track(self, page_name, category_list):
"""
Return the reST text required to link to the given
categories. As the categories are parsed they are
stored within the current environment for use in the
"html_collect_pages" function.
Args:
algorithm_name (str): The name of the algorithm.
page_name (str): Name to use to refer to this page on the category index page
category_list (list): List of category strings
Returns:
str: A string of reST that will define the links
"""
alg = self._create_mantid_algorithm(algorithm_name)
env = self.state.document.settings.env
if not hasattr(env, "categories"):
env.categories = {}
link_rst = ""
ncategs = 0
for item in category_list:
if r"\\" in item:
categs = item.split(r"\\")
else:
categs = [item]
# endif
# Create a list containing each category.
categories = alg.category().split("\\")
parent = None
for index, categ_name in enumerate(categs):
if categ_name not in env.categories:
category = Category(categ_name, env)
env.categories[categ_name] = category
else:
category = env.categories[categ_name]
#endif
if len(categories) >= 2:
# Add a cross reference for each catagory.
links = (":ref:`%s` | " * len(categories)) % tuple(categories)
# Remove last three characters to remove last |
return ("`Categories: <categories.html>`_ " + links)[:-3]
category.pages.add(PageRef(page_name, env))
if index > 0: # first is never a child
parent.subcategories.add(Category(categ_name, env))
#endif
link_rst += "`%s <../%s/%s.html>`_ | " % (categ_name, CATEGORIES_HTML_DIR, categ_name)
ncategs += 1
parent = category
# endfor
# endfor
link_rst = "**%s**: " + link_rst.rstrip(" | ") # remove final separator
if ncategs == 1:
link_rst = link_rst % "Category"
else:
return "`Category: <categoies.html>`_ :ref:`%s`" % (categories)
link_rst = link_rst % "Categories"
#endif
return link_rst
#end def
def setup(app):
#---------------------------------------------------------------------------------
class AlgorithmCategoryDirective(CategoriesDirective):
"""
Setup the directives when the extension is activated
Supports the "algm_categories" directive that takes a single
argument and pulls the categories from an algorithm object.
Args:
app: The main Sphinx application object
In addition to adding the named page to the requested category, it
also appends it to the "Algorithms" category
"""
# requires at least 1 argument
required_arguments = 0
# no other arguments
optional_arguments = 0
def _get_categories_list(self):
"""
Returns a list of the category strings
Returns:
list: A list of strings containing the required categories
"""
category_list = ["Algorithms"]
algname, version = self._algorithm_name_and_version()
alg_cats = self._create_mantid_algorithm(algname, version).categories()
for cat in alg_cats:
# double up the category separators so they are not treated as escape characters
category_list.append(cat.replace("\\", "\\\\"))
return category_list
def _get_display_name(self):
"""
Returns the name of the item as it should appear in the category
"""
return self._algorithm_name_and_version()[0]
#---------------------------------------------------------------------------------
def html_collect_pages(app):
"""
Callback for the 'html-collect-pages' Sphinx event. Adds category
pages + a global Categories.html page that lists the pages included.
Function returns an iterable (pagename, context, html_template),
where context is a dictionary defining the content that will fill the template
Arguments:
app: A Sphinx application object
"""
if not hasattr(app.builder.env, "categories"):
return # nothing to do
for name, context, template in create_category_pages(app):
yield (name, context, template)
# enddef
def create_category_pages(app):
"""
Returns an iterable of (category_name, context, "category.html")
Arguments:
app: A Sphinx application object
"""
env = app.builder.env
# jinja2 html template
template = CATEGORY_INDEX_TEMPLATE
categories = env.categories
for name, category in categories.iteritems():
context = {}
context["title"] = category.name
# sort subcategories & pages by first letter
context["subcategories"] = sorted(category.subcategories, key = lambda x: x.name[0])
context["pages"] = sorted(category.pages, key = lambda x: x.name[0])
yield (CATEGORIES_HTML_DIR + "/" + name, context, template)
# enddef
#------------------------------------------------------------------------------
def setup(app):
# Add categories directive
app.add_directive('categories', CategoriesDirective)
# Add algm_categories directive
app.add_directive('algm_categories', AlgorithmCategoryDirective)
# connect event html collection to handler
app.connect("html-collect-pages", html_collect_pages)
......@@ -7,25 +7,23 @@ class PropertiesDirective(BaseDirective):
Outputs the given algorithm's properties into a ReST formatted table.
"""
# Accept one required argument and no optional arguments.
required_arguments, optional_arguments = 1, 0
required_arguments, optional_arguments = 0, 0
def run(self):
"""
Called by Sphinx when the ..properties:: directive is encountered.
"""
alg_name = str(self.arguments[0])
title = self._make_header("Properties")
properties_table = self._populate_properties_table(alg_name)
properties_table = self._populate_properties_table()
return self._insert_rest(title + properties_table)
def _populate_properties_table(self, algorithm_name):
def _populate_properties_table(self):
"""
Populates the ReST table with algorithm properties.
Args:
algorithm_name (str): The name of the algorithm.
"""
alg = self._create_mantid_algorithm(algorithm_name)
name, version = self._algorithm_name_and_version()
alg = self._create_mantid_algorithm(name, version)
alg_properties = alg.getProperties()
# Stores each property of the algorithm in a tuple.
......
......@@ -7,24 +7,25 @@ class SummaryDirective(BaseDirective):
Obtains the summary for a given algorithm based on it's name.
"""
required_arguments, optional_arguments = 1, 0
required_arguments, optional_arguments = 0, 0
def run(self):
"""
Called by Sphinx when the ..summary:: directive is encountered.
"""
title = self._make_header("Summary")
summary = self._get_summary(str(self.arguments[0]))
summary = self._get_summary()
return self._insert_rest(title + summary)
def _get_summary(self, algorithm_name):
def _get_summary(self):
"""
Return the summary for the named algorithm.
Args:
algorithm_name (str): The name of the algorithm.
"""
alg = self._create_mantid_algorithm(algorithm_name)
name, version = self._algorithm_name_and_version()
alg = self._create_mantid_algorithm(name, version)
return alg.getWikiSummary()
......
......@@ -11,7 +11,7 @@
ISIS Rutherford Appleton Laboratory & NScD Oak Ridge National Laboratory
"""
def algorithm_screenshot(name, directory, ext=".png"):
def algorithm_screenshot(name, directory, version = -1, ext = ".png"):
"""
Takes a snapshot of an algorithm dialog and saves it as an image
named "name_dlg.png"
......@@ -19,6 +19,7 @@ def algorithm_screenshot(name, directory, ext=".png"):
Args:
name (str): The name of the algorithm
directory (str): An directory path where the image should be saved
version (str): A version of the algorithm to use (default=latest)
ext (str): An optional extension (including the period). Default=.png
Returns:
......@@ -29,9 +30,11 @@ def algorithm_screenshot(name, directory, ext=".png"):
iface_mgr = mantidqt.MantidQt.API.InterfaceManager()
# threadsafe_call required for MantidPlot
dlg = threadsafe_call(iface_mgr.createDialogFromName, name, True)
dlg = threadsafe_call(iface_mgr.createDialogFromName, name, True, None)
suffix = ("-v%d" % version) if version != -1 else ""
filename = "%s%s_dlg%s" % (name, suffix, ext)
filename = name + "_dlg" + ext
img_path = screenshot_to_dir(widget=dlg, filename=filename, screenshot_dir=directory)
threadsafe_call(dlg.close)
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment