From b8c839fa3dc364bc6602e7ebc8032e5dea234704 Mon Sep 17 00:00:00 2001 From: Grant Murphy Date: Wed, 10 Dec 2014 14:27:33 -0800 Subject: [PATCH] Use templates to generate rst documentation Using Jinja2 to generate ReSTructured text documents. Additional templates could be added to generate emails using this approach. Change-Id: I384971732166fbeb123d572d3ccbcde6bad39dfc --- doc/source/index.rst | 2 +- render.py | 79 +++++++++++++++++++++++++++++++++++++++++++ rst.jinja | 50 +++++++++++++++++++++++++++ test-requirements.txt | 2 ++ tox.ini | 5 ++- yaml2rst.py | 18 ++++++++++ 6 files changed, 154 insertions(+), 2 deletions(-) create mode 100644 render.py create mode 100644 rst.jinja create mode 100644 yaml2rst.py diff --git a/doc/source/index.rst b/doc/source/index.rst index 0bb2b7a..0346ffd 100644 --- a/doc/source/index.rst +++ b/doc/source/index.rst @@ -8,7 +8,7 @@ These pages contain published OpenStack Security Advisories. :maxdepth: 2 :glob: - ossa/index + ./ossa/* .. seealso:: diff --git a/render.py b/render.py new file mode 100644 index 0000000..85acb54 --- /dev/null +++ b/render.py @@ -0,0 +1,79 @@ +import argparse +from jinja2 import FileSystemLoader +from jinja2.environment import Environment +import os +import sys +import yaml + + +def to_snake_case(d): + for k in d: + v = d[k] + del d[k] + d[k.replace('-', '_')] = v + if type(k) == dict: + to_snake_case(d) + + +def wrap_text(text, width=80): + out = "" + i = 0 + for word in text.split(): + i += len(word) + if len(word) > width: + out += word + out += "\n" + i = 0 + if i > width: + out += "\n" + out += word + i = len(word) + else: + out += word + out += " " + i += 1 + + return out + + +def to_paragraphs(d, *args): + for k in args: + if type(d[k]) == str: + d[k] = wrap_text(d[k]) + + +def ossa_date_formatter(value): + return "{:%B %d, %Y}".format(value) + + +def csv_list_formatter(value): + return ", ".join(value) + + +def render_template(template, data): + loader = FileSystemLoader(os.getcwd()) + env = Environment(trim_blocks=True, loader=loader) + env.filters["ossa_date"] = ossa_date_formatter + env.filters["csv_list"] = csv_list_formatter + template = env.get_template(template) + return template.render(**data) + + +def render(source, template): + vals = yaml.safe_load(open(source).read()) + to_snake_case(vals) + to_paragraphs(vals, 'description') + return render_template(template, vals) + + +def main(): + parser = argparse.ArgumentParser(description="Convert YAML to RST") + parser.add_argument("--source", required=True, help="YAML source file") + parser.add_argument("--template", required=True, + help="Jinja2 template file") + args = parser.parse_args() + print(render(args.source, args.template)) + + +if __name__ == "__main__": + main() diff --git a/rst.jinja b/rst.jinja new file mode 100644 index 0000000..3a5fefc --- /dev/null +++ b/rst.jinja @@ -0,0 +1,50 @@ +============= +{{ id }} +============= + +{{ title }} +-------------------------------------------------------------------------------- +:Date: {{ date | ossa_date }} + +:Description: + +{{ description | indent(4, true) }} + +{% if reference is defined %} +:Announcement: + + - `{{ reference}} <{{ reference }}>`_ +{% endif %} + +:Affects: + +{% for affected in affected_products %} + - {{ affected['product'] }}: {{ affected['version'] }} +{% endfor %} + +:Credits: + +{% for reporter in reporters %} +{% if 'affiliation' in reporter and reporter['affiliation'] and reporter['affiliation'] != 'UNKNOWN' %} + - {{ reporter['name'] | trim | indent }} from {{ reporter ['affiliation'] }} ({{ reporter['reported'] | csv_list }}) +{% else %} + - {{ reporter['name'] | trim | indent }} ({{ reporter['reported'] | csv_list }}) +{% endif %} +{% endfor %} + +:Bug reports: + +{% for link in issues['links'] %} + - `{{ link }} <{{ link }}>`_ +{% endfor %} + + +:Reviews: + +{% for release in reviews %} +{% if release != 'type' %} +{% for link in reviews[release] %} + - `{{ link | trim }} ({{ release | trim }}) <{{link | trim }}>`_ +{% endfor %} +{% endif %} +{% endfor %} diff --git a/test-requirements.txt b/test-requirements.txt index 8e7314b..9d515c0 100644 --- a/test-requirements.txt +++ b/test-requirements.txt @@ -1,3 +1,5 @@ # needed for doc build +Jinja2>=2.7.3 +PyYAML==3.11 sphinx>=1.1.2,!=1.2.0,<1.3 oslosphinx>=2.2.0 # Apache-2.0 diff --git a/tox.ini b/tox.ini index d16e0f4..b32096c 100644 --- a/tox.ini +++ b/tox.ini @@ -13,4 +13,7 @@ deps = -r{toxinidir}/test-requirements.txt commands = {posargs} [testenv:docs] -commands = python setup.py build_sphinx +deps = -r{toxinidir}/test-requirements.txt +commands = + python yaml2rst.py + python setup.py build_sphinx diff --git a/yaml2rst.py b/yaml2rst.py new file mode 100644 index 0000000..e9191a3 --- /dev/null +++ b/yaml2rst.py @@ -0,0 +1,18 @@ +#!/usr/bin/env python +import render +import glob +import os +import sys +reload(sys) +sys.setdefaultencoding("utf-8") + + +def main(): + input_files = glob.glob(os.path.join(".", "ossa", "*.yaml")) + output_files = [x.replace(".yaml", ".rst") for x in input_files] + for old, new in zip(input_files, output_files): + with open(new, "w") as out: + out.write(render.render(old, "rst.jinja")) + +if __name__ == '__main__': + main()