From 368b42c1aee16bca9d7e4fe54273e965e25d03ee Mon Sep 17 00:00:00 2001 From: Dirk Alders Date: Sat, 16 Jan 2021 13:47:05 +0100 Subject: [PATCH] REQIF->TEX->PDF implemented --- __init__.py | 23 ++++- reqif_conv.py | 35 ++++++++ templates/Makefile | 17 ++++ templates/tex/foot.tex | 1 + templates/tex/head.tex | 96 +++++++++++++++++++++ templates/tex/macros.tex | 13 +++ templates/tex/macros.tex~ | 13 +++ templates/tex/requirement.tex | 15 ++++ templates/tex/requirement_specification.tex | 32 +++++++ templates/tex/titlepage.tex | 15 ++++ 10 files changed, 258 insertions(+), 2 deletions(-) create mode 100644 reqif_conv.py create mode 100644 templates/Makefile create mode 100644 templates/tex/foot.tex create mode 100644 templates/tex/head.tex create mode 100644 templates/tex/macros.tex create mode 100644 templates/tex/macros.tex~ create mode 100644 templates/tex/requirement.tex create mode 100644 templates/tex/requirement_specification.tex create mode 100644 templates/tex/titlepage.tex diff --git a/__init__.py b/__init__.py index 0165c4e..39bf46a 100644 --- a/__init__.py +++ b/__init__.py @@ -1,5 +1,8 @@ +import optparse import os -from reqif import xml_parser + +import xml_parser +import reqif_conv class reqif_dict(dict): @@ -17,7 +20,7 @@ class reqif_dict(dict): for specification in self.rd.get_specifications(): if specification.get(specification_key) == specification_identifier: sw_spec = specification - break + break # self[self.KEY_ITEM_DICT] = {} for obj_id in sw_spec.children: @@ -65,3 +68,19 @@ class reqif(object): def get_title(self): return self._data._title + + +if __name__ == '__main__': + parser = optparse.OptionParser("usage: %%prog reqif_file [options]") + (options, args) = parser.parse_args() + + # + # Check options and args + if len(args) != 1 or not os.path.isfile(args[0]): + parser.print_help() + else: + reqif_path = args[0] + tex_path = os.path.abspath(os.path.splitext(reqif_path)[0] + '.tex') + reqif_data = reqif_dict(reqif_path, 'Heading', 'Software Specification') + reqif_conv.tex_gen(reqif_data, tex_path) + reqif_conv.pdf_gen(tex_path) diff --git a/reqif_conv.py b/reqif_conv.py new file mode 100644 index 0000000..7e4ab44 --- /dev/null +++ b/reqif_conv.py @@ -0,0 +1,35 @@ +#!/usr/bin/env python + +import jinja2 +import os +import sys + + +"""fn_tex = 'requirements_specification.tex' +fn_pdf = 'requirements_specification.pdf' +tmp_path = 'tmp'""" + + +def tex_gen(reqif_data, tex_path): + print('Creating LaTeX-File %s' % tex_path) + with open(tex_path, 'w') as fh: + # + template_path = os.path.abspath(os.path.join(os.path.dirname(__file__), 'templates', 'tex')) + template_filename = 'requirement_specification.tex' + jenv = jinja2.Environment(loader=jinja2.FileSystemLoader(template_path)) + template = jenv.get_template(template_filename) + fh.write(template.render(data=reqif_data)) + + +def pdf_gen(tex_path): + pdf_path = os.path.splitext(tex_path)[0] + '.pdf' + print('Creating PDF %s' % pdf_path) + for i in range(3): + sys.stdout.write(' Starting run %d/3 of pdflatex... ' % (i + 1)) + sys.stdout.flush() + exit_value = os.system("pdflatex -interaction nonstopmode %s 1> /dev/null" % tex_path) + if exit_value != 0: + print('FAILED') + break + else: + print('SUCCESS') diff --git a/templates/Makefile b/templates/Makefile new file mode 100644 index 0000000..87c5f1f --- /dev/null +++ b/templates/Makefile @@ -0,0 +1,17 @@ +SPEC_FILENAME=requirements + +.PHONY: specification + +specification: + venv/bin/python reqif/__init__.py $(SPEC_FILENAME).reqif + @$(MAKE) --no-print-directory clean + xdg-open $(SPEC_FILENAME).pdf + +clean: + @echo Removing generated files for target + @rm -f $(SPEC_FILENAME).tex $(SPEC_FILENAME).aux $(SPEC_FILENAME).log $(SPEC_FILENAME).out $(SPEC_FILENAME).toc *~ + + +cleanall: clean + @echo Removing target + @rm -f $(SPEC_FILENAME).pdf \ No newline at end of file diff --git a/templates/tex/foot.tex b/templates/tex/foot.tex new file mode 100644 index 0000000..737da30 --- /dev/null +++ b/templates/tex/foot.tex @@ -0,0 +1 @@ +\end{document} diff --git a/templates/tex/head.tex b/templates/tex/head.tex new file mode 100644 index 0000000..2783a26 --- /dev/null +++ b/templates/tex/head.tex @@ -0,0 +1,96 @@ +{%- import 'macros.tex' as macros %} +\documentclass[a4paper]{article} +%\documentclass[a4paper,landscape]{article} + +\renewcommand{\familydefault}{\sfdefault} +\usepackage[table]{xcolor} +\definecolor{orange}{rgb}{1, 0.7, 0} +\definecolor{lightgrey}{rgb}{0.925, 0.925, 0.925} + +\setlength{\topmargin}{-3cm} +\setlength{\oddsidemargin}{-0.5cm} +\setlength{\evensidemargin}{0cm} +\setlength{\textwidth}{17.5cm} +\setlength{\textheight}{24.5cm} +%\setlength{\textwidth}{25cm} +%\setlength{\textheight}{15cm} +\setlength{\headheight}{84pt} + +\usepackage{fancyvrb} +\usepackage{fvextra} +%\usepackage{framed,color} +%\newenvironment{modulelog}{\snugshade\Verbatim}{\endVerbatim\endsnugshade} +\usepackage{adjustbox} +\newenvironment{modulelog}% +{\par\noindent\adjustbox{margin=0ex,bgcolor=shadecolor,margin=0ex}\bgroup\varwidth\linewidth\Verbatim}% +{\endVerbatim\endvarwidth\egroup} +%\usepackage{xcolor} + +\renewcommand{\baselinestretch}{1,2} +\setlength{\parindent}{0pt} +\setlength{\parskip}{9pt plus3pt minus3pt} + +\usepackage{listings} +\usepackage{color} +\definecolor{bg-partially-covered}{rgb}{1,1,0.6} % light-yellow +\definecolor{bg-uncovered}{rgb}{1,0.8,0.8} % light-red +\definecolor{bg-covered}{rgb}{0.95,1,0.95} % very light-green +\definecolor{bg-clean}{rgb}{1,1,1} % white +\definecolor{mygreen}{rgb}{0,0.6,0} +\definecolor{mygray}{rgb}{0.5,0.5,0.5} +\definecolor{mymauve}{rgb}{0.58,0,0.82} +\lstset{ % + backgroundcolor=\color{white}, % choose the background color; you must add \usepackage{color} or \usepackage{xcolor}; should come as last argument + basicstyle=\footnotesize, % the size of the fonts that are used for the code + breakatwhitespace=false, % sets if automatic breaks should only happen at whitespace + breaklines=true, % sets automatic line breaking + captionpos=b, % sets the caption-position to bottom + commentstyle=\color{mygreen}, % comment style + deletekeywords={...}, % if you want to delete keywords from the given language + escapeinside={\%*}{*)}, % if you want to add LaTeX within your code + extendedchars=true, % lets you use non-ASCII characters; for 8-bits encodings only, does not work with UTF-8 + frame=none, % adds a frame around the code + keepspaces=true, % keeps spaces in text, useful for keeping indentation of code (possibly needs columns=flexible) + keywordstyle=\color{blue}, % keyword style + language=Octave, % the language of the code + morekeywords={*,...}, % if you want to add more keywords to the set + numbers=left, % where to put the line-numbers; possible values are (none, left, right) + numbersep=5pt, % how far the line-numbers are from the code + numberstyle=\tiny\color{mygray}, % the style that is used for the line-numbers + rulecolor=\color{black}, % if not set, the frame-color may be changed on line-breaks within not-black text (e.g. comments (green here)) + showlines=true, + showspaces=false, % show spaces everywhere adding particular underscores; it overrides 'showstringspaces' + showstringspaces=false, % underline spaces within strings only + showtabs=false, % show tabs within strings adding particular underscores + stepnumber=1, % the step between two line-numbers. If it's 1, each line will be numbered + stringstyle=\color{mymauve}, % string literal style + tabsize=2, % sets default tabsize to 2 spaces +} +\usepackage{hyperref} +\usepackage{longtable} +\usepackage{tabu} +\usepackage{multicol} +\usepackage{booktabs} +\usepackage{graphicx} +\usepackage{lastpage} % for the number of the last page in the document +\usepackage{fancyhdr} +\usepackage{xspace} + + +% define commands for easy access +\newcommand{\req}[2]{\begin{infoBox} \textbf{ #1:} #2 \end{infoBox}} +\newcommand{\rfori}[1]{\textbf{Reason for Implementation:} #1 \\} +\newcommand{\fitcrit}[1]{\textbf{Fitcriterion:} #1 \\} + +\fancyhf{} +\renewcommand{\headrulewidth}{0pt} +\renewcommand{\footrulewidth}{0pt} +\lhead{\textcolor{gray}{}} +\chead{\textcolor{gray}{ Requirement Specification for {\tt {{ macros.latex_filter(data.titel) }} }}} +\rhead{\textcolor{gray}{}} +\lfoot{\textcolor{gray}{}} +\cfoot{\textcolor{gray}{}} +\rfoot{\textcolor{gray}{\thepage\,/ \pageref{LastPage}}} + +\begin{document} + diff --git a/templates/tex/macros.tex b/templates/tex/macros.tex new file mode 100644 index 0000000..58ef810 --- /dev/null +++ b/templates/tex/macros.tex @@ -0,0 +1,13 @@ +{%- macro requirement_filter(text) -%}{{ text.replace('<', '$<$').replace('>', '$>$').replace('_', '\\_') }} +{%- endmacro -%} +{%- macro latex_filter(text) -%}{{ requirement_filter(text.replace('"', '\'')).replace('/xc2/xb0', '$^\circ$').replace('/', '/\\allowbreak ').replace('&', '\\allowbreak \\&').replace('->', '$\\rightarrow$').replace('<-', '$\\leftarrow$').replace('=>', '$\\Rightarrow$').replace('<=', '$\\leq$').replace('>=', '$\\geq$').replace('{', '\{').replace('}', '\}').replace('#', '\\#')}} +{%- endmacro -%} + +{%- macro color_by_level(level) -%}{% if level <= 10 %}black{% else %}{% if level <= 20 %}green{% else %}{% if level <= 30 %}orange{% else %}red{% endif %}{% endif %}{% endif %} +{%- endmacro -%} + +{%- macro bg_by_levelno(level) -%}{% if level <= 10 %}0.8 0.8 0.8{% else %}{% if level <= 20 %}0.8 0.95 0.8{% else %}{% if level <= 30 %}1 0.75 0.45{% else %}0.95 0.8 0.8{% endif %}{% endif %}{% endif %} +{%- endmacro -%} + +{%- macro result(level) -%}{% if level <= 10 %}Info{% else %}{% if level <= 20 %}\textcolor{green}{Success}{% else %}{% if level <= 30 %}\textcolor{orange}{Warning}{% else %}\textcolor{red}{Failed}{% endif %}{% endif %}{% endif %} +{%- endmacro -%} diff --git a/templates/tex/macros.tex~ b/templates/tex/macros.tex~ new file mode 100644 index 0000000..54ff737 --- /dev/null +++ b/templates/tex/macros.tex~ @@ -0,0 +1,13 @@ +{%- macro requirement_filter(text) -%}{{ text.replace('<', '$<$').replace('>', '$>$') }} +{%- endmacro -%} +{%- macro latex_filter(text) -%}{{ requirement_filter(text).replace('\\', '/').replace('/xc2/xb0', '$^\circ$').replace('"', '\'').replace('/', '/\\allowbreak ').replace('&', '\\allowbreak \\&').replace('_', '\\_').replace('->', '$\\rightarrow$').replace('<-', '$\\leftarrow$').replace('=>', '$\\Rightarrow$').replace('<=', '$\\leq$').replace('>=', '$\\geq$').replace('{', '\{').replace('}', '\}').replace('#', '\\#')}} +{%- endmacro -%} + +{%- macro color_by_level(level) -%}{% if level <= 10 %}black{% else %}{% if level <= 20 %}green{% else %}{% if level <= 30 %}orange{% else %}red{% endif %}{% endif %}{% endif %} +{%- endmacro -%} + +{%- macro bg_by_levelno(level) -%}{% if level <= 10 %}0.8 0.8 0.8{% else %}{% if level <= 20 %}0.8 0.95 0.8{% else %}{% if level <= 30 %}1 0.75 0.45{% else %}0.95 0.8 0.8{% endif %}{% endif %}{% endif %} +{%- endmacro -%} + +{%- macro result(level) -%}{% if level <= 10 %}Info{% else %}{% if level <= 20 %}\textcolor{green}{Success}{% else %}{% if level <= 30 %}\textcolor{orange}{Warning}{% else %}\textcolor{red}{Failed}{% endif %}{% endif %}{% endif %} +{%- endmacro -%} diff --git a/templates/tex/requirement.tex b/templates/tex/requirement.tex new file mode 100644 index 0000000..beab387 --- /dev/null +++ b/templates/tex/requirement.tex @@ -0,0 +1,15 @@ +{%- import 'macros.tex' as macros %} +{%- if 'Description' in item and item.Description != '' %} +\paragraph{\xspace{}{{ macros.latex_filter(item.ID) }} ({{macros.latex_filter(item.Heading)}}):} + +{{ macros.requirement_filter(item.Description) }} + +{%- endif %} +{%- if 'ReasonForImplementation' in item and item.ReasonForImplementation != '' %} + +Reason for Implementation: {{ macros.requirement_filter(item.ReasonForImplementation) }} +{%- endif %} +{%- if 'Fitcriterion' in item and item.Fitcriterion != '' %} + +Fitcriterion: {{ macros.requirement_filter(item.Fitcriterion) }} +{%- endif %} diff --git a/templates/tex/requirement_specification.tex b/templates/tex/requirement_specification.tex new file mode 100644 index 0000000..05b4adf --- /dev/null +++ b/templates/tex/requirement_specification.tex @@ -0,0 +1,32 @@ +{%- import 'macros.tex' as macros %} +{%- include 'head.tex' %} +{%- include 'titlepage.tex' %} +\tableofcontents +\newpage + +{%- for uid in data.uid_list_sorted %} + {% with item=data.item_dict[uid] %} + {%- if item.system_type_uid == '_4-K5EHYYEem_kd-7nxt1sg' %} + \section{ {{macros.latex_filter(item.Heading)}} } + {%- elif item.system_type_uid == '_MR7eNHYYEem_kd-7nxt1sg' %} + \subsection{\xspace{}{{ macros.latex_filter(item.ID) }}: {{macros.latex_filter(item.Heading)}} } + {%- if 'Description' in item and item.Description != '' %} + {{ macros.requirement_filter(item.Description) }} + {%- if 'ReasonForImplementation' in item and item.ReasonForImplementation != '' or 'Fitcriterion' in item and item.Fitcriterion != ''%} + + \begin{tabu}{lX} + \toprule + {%- if 'ReasonForImplementation' in item and item.ReasonForImplementation != '' %} + \emph{Reason} & {{ macros.requirement_filter(item.ReasonForImplementation) }}\\ + {%- endif %} + {%- if 'Fitcriterion' in item and item.Fitcriterion != '' %} + \emph{Fitcriterion} & {{ macros.requirement_filter(item.Fitcriterion) }}\\ + {%- endif %} + \bottomrule + \end{tabu} + {%- endif %} + {%- endif %} + {%- endif %} + {% endwith %} +{%- endfor %} +{% include 'foot.tex' %} diff --git a/templates/tex/titlepage.tex b/templates/tex/titlepage.tex new file mode 100644 index 0000000..47ffdbe --- /dev/null +++ b/templates/tex/titlepage.tex @@ -0,0 +1,15 @@ +{%- import 'macros.tex' as macros %} +\begin{titlepage} +\date{\today} +\title{ + Requirement Specification for\\{\tt {{ macros.latex_filter(data.titel) }} } +} +\date{\today} +\maketitle +\thispagestyle{empty} +\newpage +\end{titlepage} + +\setcounter{page}{1} +\pagestyle{fancy} +