diff --git a/tosca2doc.py b/tosca2doc.py new file mode 100755 index 0000000000000000000000000000000000000000..475b123bb13e0a0358aff358fdccf5a3662fcffa --- /dev/null +++ b/tosca2doc.py @@ -0,0 +1,192 @@ +#!/usr/bin/python2.7 +''' +Parses a TOSCA template and generates a docx file +''' + + +import sys +import toscaparser.utils.yamlparser as yaml + +import docx +from docx.enum.text import WD_PARAGRAPH_ALIGNMENT + +PRINT_TRESHOLD = 1 + +TEMPLATES = {} + +FNS = { + 'VNFD' : 'etsi_nfv_sol001_vnfd_2_5_1_types.yaml', + 'NSD' : 'etsi_nfv_sol001_nsd_2_5_1_types.yaml', + 'PNFD' : 'etsi_nfv_sol001_pnfd_2_5_1_types.yaml' +} + +TOSCA_TYPES = [ + 'data_types', + 'artifact_types', + 'capability_types', + 'requirements_types', + 'relationship_types', + 'interface_types', + 'node_types', + 'group_types', + 'policy_types' +] + +TOSCA_TYPES_PRETTYPRINT = { + 'data_types': 'Data Types', + 'artifact_types': 'Artifact Types', + 'capability_types': 'Capability Types', + 'requirements_types': 'Requirements types', + 'relationship_types': 'Relationship Types', + 'interface_types': 'Interface Types', + 'node_types': 'Node Types', + 'group_types': 'Group Types', + 'policy_types': 'Policy Types' +} + + +IDS = [6, 2, 1, 1] + +def load_template(file_path, templs, key): + ''' + Loads the content of the file at file_patch into the YAML templates array + ''' + yaml_data = open(file_path).read() + #print("+++++++++++++++++++++++++++++++++++++") + #print(yaml_data) + #print("+++++++++++++++++++++++++++++++++++++") + templs[key] = yaml.simple_ordered_parse(yaml_data) + +def print_lvl(num, txt): + if num + 1 > PRINT_TRESHOLD: + return + index = ".".join(map(str, IDS[:(num+1)])) + print index + " " + ('-'*(num+1)) + ' ' + txt + +def add_heading_with_num(num, txt, doc): + ''' + Add a new heading to the document, with the calculated index number + ''' + index = ".".join(map(str, IDS[:(num+1)])) + doc.add_heading(index + " " + txt, num) + +def cell_text_bold(cell): + cell.paragraphs[0].runs[0].font.bold = True + +def cell_text_centered(cell): + cell.paragraphs[0].alignment = WD_PARAGRAPH_ALIGNMENT.CENTER + +def mk_props_table_hdr(table): + hdr_cells = table.rows[0].cells + hdr_cells[0].text = 'Name' + hdr_cells[1].text = 'Required' + hdr_cells[2].text = 'Type' + hdr_cells[3].text = 'Constraints' + hdr_cells[4].text = 'Description' + map(cell_text_bold, hdr_cells) + map(cell_text_centered, hdr_cells) + +def dt_name_from_path(dt_path): + return dt_path.split('.')[-1] + +def prop_field_to_str(prop_field): + if type(prop_field) == bool: + return 'yes' if prop_field else 'no' + return str(prop_field) + +def add_prop_row(prop_name, prop_fields, table): + ''' + Add a row to table and fills it with info from prop + ''' + nrow = table.add_row() + prop_cells = nrow.cells + prop_cells[0].text = prop_name + + all_fields = ['required', 'type', 'constraints', 'description'] + + counter = 1 + for field in all_fields: + if field in prop_fields: + prop_cells[counter].text = prop_field_to_str(prop_fields[field]) + counter = counter + 1 + +def print_all_types(templ, lvl, doc): + for tosca_type in TOSCA_TYPES: + if not tosca_type in templ: + continue + print_lvl(lvl, tosca_type) + add_heading_with_num(lvl, TOSCA_TYPES_PRETTYPRINT[tosca_type], doc) + for data_type in templ[tosca_type]: + print_lvl(lvl + 1, data_type) + add_heading_with_num(lvl + 1, data_type, doc) + + if 'description' in templ[tosca_type][data_type]: + print_lvl(lvl + 2, 'Description') + add_heading_with_num(lvl + 2, 'Description', doc) + doc.add_paragraph(templ[tosca_type][data_type]['description']) + IDS[lvl+2] = IDS[lvl+2] + 1 + + if 'properties' in templ[tosca_type][data_type]: + print_lvl(lvl + 2, 'Properties') + add_heading_with_num(lvl + 2, 'Properties', doc) + # num_row = templ[tosca_type][data_type]['properties'].items() + doc.add_paragraph("The properties of the "+ + dt_name_from_path(data_type) + +" data type shall comply with the provisions set out in table REF_TBD.") + table = doc.add_table(rows=1, cols=5) + mk_props_table_hdr(table) + for prop in templ[tosca_type][data_type]['properties']: + # print "PROP: " + prop + ", VAL: " + str(templ[tosca_type][data_type]['properties'][prop]) + add_prop_row(prop, templ[tosca_type][data_type]['properties'][prop], table) + IDS[lvl+2] = IDS[lvl+2] + 1 + + print_lvl(lvl + 2, 'Definition') + add_heading_with_num(lvl + 2, 'Definition', doc) + doc.add_paragraph("The syntax of the "+ + dt_name_from_path(data_type)+ + " data type shall comply with the following definition:") + def_table = doc.add_table(rows=1, cols=1) + def_table.rows[0].cells[0].text = 'TBD' + IDS[lvl+2] = IDS[lvl+2] + 1 + + print_lvl(lvl + 2, 'Examples') + add_heading_with_num(lvl + 2, 'Examples', doc) + def_table = doc.add_table(rows=1, cols=1) + def_table.rows[0].cells[0].text = 'TBD' + IDS[lvl+2] = IDS[lvl+2] + 1 + + print_lvl(lvl + 2, 'Additional Requirements') + add_heading_with_num(lvl + 2, 'Additional Requirements', doc) + doc.add_paragraph("None.") + IDS[lvl+2] = IDS[lvl+2] + 1 + + IDS[lvl+1] = IDS[lvl+1] + 1 + IDS[lvl+2] = 1 + + IDS[lvl+1] = 1 + IDS[lvl] = IDS[lvl] + 1 + IDS[lvl] = 2 + +def generate_from_file(fn, doc, fpath): + load_template(fpath, TEMPLATES, fn) + print_lvl(0, fn) + add_heading_with_num(0, fn, doc) + print_all_types(TEMPLATES[fn], 1, doc) + #print TEMPLATES[fn] + +if __name__ == "__main__": + doc = docx.Document() + + if len(sys.argv) < 2: + print "Usage: robot2doc <robot_file_or_dir> [<out_file> [spec_section_title]]" + sys.exit() + + FOLDR = sys.argv[1] + + print "Using folder: " + sys.argv[1] + + for fn in ['VNFD', 'NSD', 'PNFD']: + generate_from_file(fn, doc, FOLDR + '/' + FNS[fn]) + IDS[0] = IDS[0] + 1 + + doc.save('sol001.docx') diff --git a/web_app.py b/web_app.py new file mode 100644 index 0000000000000000000000000000000000000000..203790b80bc67c712edfbe1ece607c5d9fcd3c32 --- /dev/null +++ b/web_app.py @@ -0,0 +1,94 @@ +#!/bin/bash + +import os + +from flask import Flask, flash, request, redirect, url_for, send_file +from werkzeug.utils import secure_filename +import docx + +import tosca2doc + +app = Flask(__name__) + +UPLOAD_FOLDER = '/tmp/upload' + +ALLOWED_EXTENSIONS = set(['txt', 'yaml', 'docx']) + +app.config['UPLOAD_FOLDER'] = UPLOAD_FOLDER +app.secret_key = 'super secret key' +app.config['SESSION_TYPE'] = 'filesystem' + +HOME=''' +<!doctype html> +<title>tosca2doc</title> +<h1>tosca2doc & doc2tosca</h1> +<h2>tosca2doc</h2> +<form action="/tosca2doc" method="post" enctype="multipart/form-data"> +Tosca YAML file: +<input type="file" name="file" multiple=""/> +<input type="submit" value="Upload" /> +</form> +<h2>doc2tosca</h2> +''' + +TOSCA_DEFS=['VNFD', 'NSD', 'PNFD'] + +def allowed_file(filename): + ''' + Check filename is in the list of allowed extensions + ''' + return '.' in filename and \ + filename.rsplit('.', 1)[1].lower() in ALLOWED_EXTENSIONS + + +@app.route("/") +def hello(): + return HOME + +@app.route("/tosca2doc", methods=['POST']) +def mk_tosca2doc(): + + if 'file' not in request.files: + flash('No file part') + return redirect(request.url) + + ufiles=request.files.getlist("file") + + for tosca_def in TOSCA_DEFS: + found=False + for uploaded_file in ufiles: + if tosca_def in uploaded_file.filename or \ + tosca_def.lower() in uploaded_file.filename: + found=True + #if uploaded_file: + # print "-------------------" + # print uploaded_file.read() + # print "-------------------" + if not found: + flash('No file part') + print('No file part') + return redirect("/") + + for file in ufiles: + if file.filename == '': + print('No selected file') + flash('No selected file') + return redirect("/") + + doc = docx.Document() + + for tosca_def in TOSCA_DEFS: + for file in ufiles: + if tosca_def in uploaded_file.filename or \ + tosca_def.lower() in uploaded_file.filename: + filename = secure_filename(file.filename) + filepath = os.path.join(app.config['UPLOAD_FOLDER'], filename) + file.save(filepath) + file.close() + + tosca2doc.generate_from_file(tosca_def, doc, filepath) + + doc.save('/tmp/myfile.docx') + return send_file('/tmp/myfile.docx') + + return ("No content found") \ No newline at end of file