Skip to content
Snippets Groups Projects
about.py 4.05 KiB
"""
This file contains the routines used to compile the 'about' page's text.
This text comes to us in three parts, which are stored in three markdown
files in the data directory, along with an about_meta.yaml file describing
how they are to be described at the top of the page.

The markdown files are then compiled to two pieces of HTML, one for the
sidebar and one for the main part of the text, as well as a small
dictionary that describes the content of the cards at the top of the page.

Created 16-08-2017 by Josh Borrow (joshua.borrow@durham.ac.uk)
"""

#Disable errors associated with un-overwritten methods in HTMLParser.
#pylint: disable=W0223

from html.parser import HTMLParser

from mdcomp import convert_text
import yaml


def open_meta(filename="about_meta.yaml", data_dir="../data"):
    """ Read the meta file and return the associated data """
    with open(f"{data_dir}/{filename}", "r") as handle:
        return yaml.load(handle)


def compile_markdown(data, data_dir="../data"):
    """ 'data' is the dictionary provided by reading about_meta.yaml """

    output_text = ""

    for item in data["files"]:
        with open(f"{data_dir}/{item['name']}", "r") as handle:
            input_text = handle.read()
        
        compiled_text = convert_text(input_text)

        output_text += f"<div id=\"{item['slug']}\">{compiled_text}</div>"
    return output_text


class Parser(HTMLParser):
    """ Custom HTML Parser that builds the headings tree """
    def __init__(self):
        super(Parser, self).__init__()

        self.headings = []
        self.waiting_for_data = False

    def handle_starttag(self, tag, attrs):
        for attr in attrs:
            if attr[0] == "id":
                this_id = attr[1]
                break
            else:
                this_id = ""
                continue

        if (tag == "h1") or (tag == "h2"):
            self.headings.append([tag, this_id])
            self.waiting_for_data = True


    def handle_endtag(self, tag):
        pass


    def handle_data(self, data):
        if self.waiting_for_data:
            self.headings[-1].append(data)
            self.waiting_for_data = False


def parse_html(text):
    """ To generate the sidebar automatically we must parse the HTML and
        extract the appropriate headings. """

    parser = Parser()
    parser.feed(text)

    return parser.headings


def parse_headings(headings):
    """ Creates the HTML for the sidebar based on the headings """

    sidebar_content = ""

    # Heading has the structure [tag, id, text]
    for heading in headings:
        if heading[0] == "h1":
            if sidebar_content != "":
                sidebar_content += "</ul>"

            sidebar_content += f"<h2>{heading[2]}</h2><ul>"

        elif heading[0] == "h2":
            sidebar_content += f"<li><a href=\"#{heading[1]}\">{heading[2]}</a></li>"

        else:
            raise Exception(f"There is an issue with Parser (about.py). It picked up {heading}")

    return f"{sidebar_content}</ul>"


def compile_to_yaml(in_filename="about_meta.yaml", out_filename="about.yaml", data_dir="../data"):
    """ Compiles the information in the markdown files and sends it out to an
        external yaml file. """

    meta_data = open_meta(in_filename, data_dir)
    # At this stage it is smart to remove the 'hidden' files.
    meta_data_clean = {
    "files" : [
            item for item in meta_data["files"] if not item["hidden"]
        ]
    }

    output_text = compile_markdown(meta_data_clean, data_dir)

    sidebar = parse_headings(parse_html(output_text))

    output_data = {
        "meta_data": meta_data_clean,
        "output_text": output_text,
        "sidebar": sidebar
    }

    with open(f"{data_dir}/{out_filename}", "w") as handle:
        yaml.dump(output_data, handle)

    return output_data

if __name__ == "__main__":
    print("Running this script directly will only compile the markdown in data.")
    print("If you wish to continue, please enter Y, if not, please enter N.")

    CHOICE = input()

    if CHOICE in ['y', 'Y']:
        compile_to_yaml()
    else:
        exit(0)