interop/tools/jsonToRst_schema_1.py
Roman Popelka 147b9d4d1a jsonToRst add support for 1.x schema
Added support to 1.x schema to make sure that
    older guidelines can still be converted.

    * created new script for 1.x schema
    * argparse used for argument handling
    * script can be run from top level dir
    * added --outdir option

Change-Id: I051008040b4dc05e6a7a2d2ff045ddbe6629deb1
2022-01-20 11:24:06 +01:00

248 lines
7.5 KiB
Python
Executable File

#!/usr/bin/env python3
#
# Copyright 2015 Alexander Hirschfeld
# Copyright 2021 Red Hat, Inc.
#
# 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.
#
"""
This script will convert .json guideline to .rst guideline.
Only schema 1.x is supported (guidelines prior to 2018.02 version)
This script takes filename (not file path) as argument.
(File has to be located in interop/guidelines directory)
It is possible to specify output directory with --outdir option.
This option takes path to output directory as argument.
If this option isn't used file will be stored in interop/doc/source/guidelines
Examples:
[Generating out.2017.09.rst file to interop/doc/source/guidelines directory]
python3 jsonToRst.py 2017.09
[Generating out.2017.09.rst file to current directory]
python3 jsonToRst.py 2017.09.json --outdir .
"""
import argparse
import json
from json.decoder import JSONDecodeError
import os
import sys
import textwrap
def print_help_arrays(input):
if not input:
return None
output = ""
for i in input:
output = output + i.capitalize() + ', '
return output[0:-2]
def print_error(msg):
print(msg)
sys.exit(1)
def add_extension(filename, extension):
if extension not in filename:
filename += extension
return filename
def parse_arguments():
default_outdir = os.path.join(os.path.dirname(os.path.realpath(__file__)),
"../doc/source/guidelines/")
parser = argparse.ArgumentParser(__doc__)
parser.add_argument('--outdir',
help='Path to output file',
metavar='FILENAME',
default=default_outdir)
parser.add_argument('file')
return parser.parse_args()
def get_file_path(in_file_name):
# filename
fname = __file__.split('/')[-1]
interop_path = (os.path.realpath(__file__)).replace('/tools/' + fname, '')
# check if file exists
if os.path.isfile(interop_path + '/guidelines/' + in_file_name):
return interop_path + '/guidelines/' + in_file_name
else:
return None
def write_intro(data, out_file):
if data.get('id') is None:
print_error('Make sure there is a valid id')
line01 = "OpenStack Interoperability Guideline %s" % data["id"]
out_file.write('=' * len(line01) + '\n')
out_file.write(line01 + '\n')
out_file.write('=' * len(line01) + '\n')
# Nonlooping
if data.get('platform') is None:
print_error("The platform section is not found")
# Correct Source
if data.get('source') not in (
'http://opendev.org/openstack/defcore/',
'http://opendev.org/openstack/interop/',
'https://opendev.org/openinfra/interop/'):
print_error("The expected interoperability guideline source not found")
out_file.write("""
:Status: {status}
:Replaces: {replaces}
:JSON Master: http://opendev.org/openstack/interop/raw/branch/master/{id}.json
This document outlines the mandatory capabilities and designated
sections required to exist in a software installation in order to
be eligible to use marks controlled by the OpenStack Foundation.
This document was generated from the `master JSON version <{id}.json>`_.
Releases Covered
==============================
Applies to {releases}
Platform Components
==============================
:Required: {platformRequired}
:Advisory: {platformAdvisory}
:Deprecated: {platformDepric}
:Removed: {platformRemoved}
""".format(status=data.get("status"),
replaces=data.get("replaces"),
id=data.get("id"),
releases=print_help_arrays(data.get("releases")),
platformRequired=print_help_arrays(data["platform"]["required"]),
platformAdvisory=print_help_arrays(data["platform"]["advisory"]),
platformDepric=print_help_arrays(data["platform"]["deprecated"]),
platformRemoved=print_help_arrays(data["platform"]["removed"])))
def write_components(data, out_file):
# looping
if data.get('components') is None:
print_error("No components found")
components = sorted(data["components"].keys())
order = ["required", "advisory", "deprecated", "removed"]
for component in components:
out_file.write("""
{component} Component Capabilities
""".format(component=component.capitalize()))
out_file.write('=' * (len(component) + 23)) # footer
for event in order:
out_file.write("\n{event} Capabilities\n".format(
event=event.capitalize()))
out_file.write("-" * (len(event) + 15) + "\n")
if(len(data['components'][component][event]) == 0):
out_file.write("None\n")
for req in data['components'][component][event]:
out_file.write("* {name} ({project})\n".format(
name=req,
project=data["capabilities"][req].get(
"project").capitalize()))
def write_designated_sections(data, out_file):
wrapper = textwrap.TextWrapper(width=79, subsequent_indent=' ')
if 'designated-sections' not in data:
print_error("designated-sections not in json file")
out_file.write("""
Designated Sections
=====================================
The following designated sections apply to the same releases as
this specification.""")
order = ['required', 'advisory', 'deprecated', 'removed']
desig = data.get("designated-sections")
for event in order:
out_file.write('\n\n{event} Designated Sections\n'.format(
event=event.capitalize()))
# +20 is for length of header
out_file.write('-' * (len(event) + 20) + '\n\n')
names = sorted(desig[event].keys())
if len(names) == 0:
out_file.write('None')
outlines = []
for name in names:
outlines.append(
wrapper.fill(
"* {name} : {guide}".format(
name=name.capitalize(),
guide=desig[event][name].get('guidance'))))
out_file.write("\n".join(outlines))
out_file.write('\n')
def run(in_file_name, out_file_path):
in_file_path = get_file_path(in_file_name)
if in_file_path is None:
print_error("[ERROR] " + in_file_name + " doesn't exist")
print("[INFO] Reading from: " + in_file_path)
with open(in_file_path) as f:
try:
data = json.load(f)
except JSONDecodeError:
print_error('[ERROR] Make sure this is a valid file')
print("[INFO] Writing to: " + out_file_path)
# intro
with open(out_file_path, "w") as out_file:
write_intro(data, out_file)
write_components(data, out_file)
write_designated_sections(data, out_file)
if __name__ == '__main__':
args = parse_arguments()
# add extension, if only version of guideline was specified as file
args.file = add_extension(args.file, ".json")
out_file_path = os.path.join(args.outdir,
"out." + args.file.replace("json", "rst"))
run(args.file, out_file_path)