<reponame>jacob720/Savu<gh_stars>10-100
# Copyright 2014 Diamond Light Source Ltd.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

"""
.. module:: create_plugin_template_doc
   :platform: Unix
   :synopsis: A module to create plugin template rst files for documentation

.. moduleauthor:: <NAME> <<EMAIL>>

"""

import os
import re

from collections import OrderedDict

import savu.plugins.utils as pu
import doc.create_plugin_doc as pdoc


def create_plugin_template_downloads(savu_base_path):
    """Inside plugin_examples/plugin_templates/general
    If the file begins with 'plugin_template' then select it
    Read the lines of the files docstring and set as a descriptor
    """
    dev_file_path = f"{savu_base_path}doc/source/dev_guides/"
    doc_template_file = f"{dev_file_path}dev_plugin_templates.rst"

    # Populate dictionary with template class and template class docstring
    docstring_text = create_template_class_dict(savu_base_path)
    if docstring_text:
        with open(doc_template_file, "w") as doc_template:
            doc_template.write(".. _plugin_templates:\n")
            doc_template.write("\n")
            doc_template.write(f"Plugin templates {pdoc.set_underline(6 ,23)}")
            doc_template.write("\n")

            doc_name = "plugin_template1_with_detailed_notes"
            detailed_template = docstring_text.get(doc_name)

            if detailed_template:
                docstring_text.pop(doc_name)
                title = pdoc.convert_title(doc_name)
                title, number = filter_template_numbers(title)
                # Create the restructured text page for the plugin template
                # python code
                generate_template_files(doc_name, title)
                inner_file_str = \
                    "../../../plugin_examples/plugin_templates/general"
                doc_template.write(f"{title}{pdoc.set_underline(3 ,66)}")
                doc_template.write(
                    "\nA template to create a simple plugin "
                    "that takes one dataset as input and returns "
                    "a similar dataset as output"
                )
                doc_template.write("\n")
                doc_template.write(
                    """
.. list-table::  
   :widths: 10

   * - :ref:`"""
                    + doc_name
                    + """`

"""
                )
            doc_template.write(f"Further Examples{pdoc.set_underline(3 ,66)}")
            # Begin the table layout
            doc_template.write(
                """
.. list-table::  
   :widths: 10 90
   :header-rows: 1

   * - Link
     - Description"""
            )

            for doc_name, doc_str in docstring_text.items():
                title = pdoc.convert_title(doc_name)
                title, number = filter_template_numbers(title)
                desc_str = doc_str["desc"]
                # Create a link to the restructured text page view of
                # the python code for the template
                doc_template.write("\n   * - :ref:`" + doc_name + "`")
                # The template description from the docstring
                doc_template.write("\n     - " + desc_str)
                doc_template.write("\n")
                # Create the restructured text page for the plugin template
                # python code
                generate_template_files(doc_name, title)

            doc_template.write("\n")


def generate_template_files(doc_name, title):
    """Create a restructured text file which will include the python
     code for the plugin template 'doc_name'

    :param doc_name: The name of the template file
    :param title:
    :return:
    """
    inner_file_str = "../../../../plugin_examples/plugin_templates/general"
    template_file_path = \
        f"{savu_base_path}doc/source/dev_guides/templates/{doc_name}.rst"

    with open(template_file_path, "w") as tfile:
        # Add the orphan instruction as this file is not inside a toctree
        tfile.write(":orphan:\n")
        tfile.write("\n.. _" + doc_name + ":\n")
        tfile.write("\n")
        tfile.write(f"{title}{pdoc.set_underline(4, 39)}")
        tfile.write("\n")
        tfile.write(_get_download_string(title, inner_file_str, doc_name))
        tfile.write("\n")
        tfile.write(_get_download_string(f"{title} Tools",
                                         inner_file_str,
                                         f"{doc_name}_tools"))
        tfile.write("\n")
        tfile.write(_get_include_string(doc_name))
        tfile.write("\n    :language: python\n")
        tfile.write("\n")
        tfile.write(_get_include_string(f"{doc_name}_tools"))
        tfile.write("\n    :language: python\n")


def _get_download_string(label, inner_file, doc_name):
    return f":download:`Download {label}<{inner_file}/{doc_name}.py>`\n\n"


def _get_include_string(doc_name):
    include_str = f".. literalinclude:: " \
                  f"/../../plugin_examples/plugin_templates/general/{doc_name}.py"
    return include_str


def filter_template_numbers(name_string):
    """
    :param name_string: The name of the template
    :return: A string with the template number seperated by a space
    """
    number = "".join(l for l in name_string if l.isdigit())
    letters = "".join(l for l in name_string if l.isalpha())
    split_uppercase = [l for l in re.split("([A-Z][^A-Z]*)", letters) if l]
    title = " ".join(split_uppercase)
    name = title + " " + number
    return name, number


def create_template_class_dict(savu_base_path):
    """Iterate through the plugin example folder and store the class
    and it's class docstring into a dictionary docstring_text

    :param savu_base_path:
    :return: docstring_text dictionary of class and docstring
    """
    docstring_text = {}
    plugin_ex_path = \
        f"{savu_base_path}plugin_examples/plugin_templates/general"

    for t_root, t_dirs, template_files \
            in os.walk(plugin_ex_path, topdown=True):
        template_files[:] = [fi for fi in template_files
                             if fi.split(".")[-1] == "py"
                             and "tools" not in fi]
        if "__" not in t_root:
            pkg_path = t_root.split("Savu/")[1]
            module_name = pkg_path.replace("/", ".")

        for fi in template_files:
            file_name = fi.split(".py")[0]
            cls_module = module_name + "." + file_name
            try:
                cls_loaded = pu.load_class(cls_module)()
            except AttributeError as e:
                cls_loaded = None

            if cls_loaded:
                tools = cls_loaded.get_plugin_tools()
                doc = tools.get_doc() if tools else ""
                desc = doc.get("verbose") if isinstance(doc ,dict) else ""
                title = pdoc.convert_title(file_name)
                name, number = filter_template_numbers(title)
                docstring_text[file_name] = {
                    "desc": desc,
                    "number": int(number),
                }

    # Order templates by number
    docstring_text = OrderedDict(
        sorted(docstring_text.items(), key=lambda i: i[1]["number"])
    )

    return docstring_text


if __name__ == "__main__":
    # determine Savu base path
    main_dir = \
        os.path.dirname(os.path.realpath(__file__)).split("/Savu/")[0]
    savu_base_path = f"{main_dir}/Savu/"

    # Create template download page
    create_plugin_template_downloads(savu_base_path)
