Skip to content

Experimental requirements management using asciidoc files

License

Notifications You must be signed in to change notification settings

hpenne/asciireqs

Repository files navigation

AsciiReqs - Requirements Management using AsciiDoc markdown and git

What it is

Requirement Management software can be complicated and inconvenient to use. Support for collaboration and change management can be cumbersome and poor. What if we could instead store our requirements in git and use that for versioning and use pull requests for review and management of changes?

This project is a (simple) parser for AsciiDoc files written in Python that will identify requirements in AsciiDoc files, and build a requirements project model from this. It uses this to do reporting and data export, and will also post process the AsciiDoc files to insert cross-links and report data. This can then be turned into HTML or PDF by tools like AsciiDoctor.

The parser supports requirement document hierarchies, and can find and report inconsistencies in requirement cross-links between documents.

This allows complicated sets of product requirements to be managed in AsciiDoc.

AsciiReqs is to some degree inspired by Sphinx-Needs and Doorstop.

Status

This is a prototype that I wrote out of frustration with an ancient (but common) Requirement Management System. I also wanted to freshen up my Python skills. Python is fun, so it ended up with more features than I had originally planned. It must still be considered a prototype, but it is in a workable state and the current functionality is at a level where it can be quite useful.

Features

AsciiReqs can:

  • Find requirements defined using AsciiDoc "terms".

  • Find requirements defined using blocks of YAML (and convert them to the term form in post-processing).

  • Find and parse sub specifications using AsciiDoc attributes, to parse a hierarchy of specifications.

  • Identify requirement links to parent and child requirements.

  • Post process requirement documents and insert AsciiDoc anchors and cross-links for the requirements, which turn into hyperlinks in generated HTML.

  • Detect and execute inline "macros" that generate tables of requirements in the post processed document, based on arbitrary user-specified filters.

  • Export to CVS and Excel.

Possible future directions

Incomplete list:

  • Export to ReqIF

  • Set, add to or remove from attributes using external (imported) data. E.g. add a new attribute (or add a new value to an attribute) to all or many requirements using external data (e.g. CVS).

  • Import (generate a document from CVS and/or ReqIF)

How does it work?

Write your requirement specification in AsciiDoc. Define the requirements as terms or YAML. Your document is where the requirements are actually stored, and everything else is post-processing. Use your favourite version control system for storage, versioning and collaboration.

Asciireqs will parse these documents to build up an internal project model of your requirements. A second pass will then be made through the documents to post process them and generate modified Asciidoc fils to an output folder. The post-processing inserts navigable cross-links and also generates report tables based on macros that you can insert into the original documents. These tables use filters that you specify using Python expression syntax. You can also write custom report templates outside your specification documents, that can also be processed in the post-processing stage.

Defining requirements

Requirements as AsciiDoc "terms"

A requirement can be defined a an AsciiDoc "term":

UR-RMS-011::
This is the (optional) requirement title:
+
This is the text defining the first requirement.
The text can span multiple lines.
+
Tags: V.1;
Child: SW-RMS-REQ-011
Note
The attribute "req-regex" (see below) must be set to "UR-RMS-\d+" so that AsciiReqs knows that the term is a requirement and not something else.

This renders as:

UR-RMS-011

This is the (optional) requirement title:

This is the text defining the first requirement. The text can span multiple lines.

Tags: V.1; Child: SW-RMS-REQ-011

The term itself becomes the requirement ID. The title is optional. It is restricted to a single line and ends with a colon and a paragraph break (the line with the "+").

The text that follows the title becomes the "Text" attribute.

If the text is followed by a line with a "+" (AsciiDoc hard line break), then what follows is interpreted as a list of semicolon-separated list of "name: value" attribute pairs. You can put them all on a single line, or split them after the semicolon as shown above. Using multiple lines plays better with diff-views.

YAML block with a single requirement

It is also possible to define a requirement as a block of YAML. YAML is a simple property-value format, which is easy to read and write. The following lines of AsciiDoc will define a requirement using YAML:

[.reqy]
----
ID: UR-RMS-021
Child: SW-RMS-021
Tags: V.1
Title: This is the requirement title
Text: This is the text that defines the requirement
----

Some may find this format easier to read and write while working with the requirements, but it will look odd in the final print. AsciiReqs will therefore transform this into an AsciiDoc "Term" in the following style when the adoc files are processed:

UR-RMS-021

This is the requirement title:

This is the text that defines the requirement

Child: SW-RMS-021; Tags: V.1

The title is (again) optional.

It is also possible to define several requirements in one YAML block using a slightly different format:

[.reqy]
----
UR-RMS-021:
  Child: SW-RMS-021
  Tags: V.1
  Text: This is the text that defines the requirement
UR-RMS-022:
  Child: SW-RMS-022
  Tags: V.1
  Text: |
    This is the text for the second requirement
    The vertical bar lets you write multi-line YAML texts
----

The "Text" field for the second requirement will be split across two lines in the AsciiDoc output. Using an empty line between them will make them different paragraphs.

Requirement properties

The requirements consist of named properties with values. The following property names are treated specially by AsciiReqs:

  • ID: The requirement identifier

  • Title: This is the title identified just after the ID when parsing term-requirements.

  • Text: This is the requirement text identified just after the ID/Title when parsing term-requirements.

  • Child: The value is interpreted as a comma separated list of requirement identifiers for child requirements in a sub specification (links from a high level requirement to requirement further down in the specification hierarchy)

  • Parent: The value is interpreted as a comma separated list of requirement identifiers linking up to parent requirements higher in the hierarchy.

  • Line: This attribute is set by AsciiReqs to the line number where the requirement was found.

Child specifications

You can use a document attribute named req-children to define child specifications to parse. The value is a comma separated list of child requirement documents.

Adding the following line to the attribute section at the start of your document will define child-reqs.adoc and child-reqs-2.adoc as a child requirement documents:

Note that the child documents must reside in the same folder as the top level project document (use of paths is not supported).

:req-children: child-reqs.adoc, child-reqs-2.adoc

Asciireqs will parse these documents as well, as child documents in the specification hierarchy.

Other document attributes

You also need to define an attribute to tell AsciiReqs the pattern for your requirement IDs. This uses normal "regex" syntax. The following defines "SW-REQ-" as the prefix for the current document’s requirements, followed by one or more digits (all requirement IDs are "SW-REQ-" followed by one or more digits):

:req_regex: SW-REQ-\d+

This attribute is necessary in order to identify the requirement IDs and insert cross-links when post-processing the documents.

Running AsciiReqs

Invoke AsciiReqs with the name of the top level requirement document and an option to specify the output directory:

asciireq -o outputdir my-spec.adoc

This will make Asciireqs parse my-spec.adoc and all child documents (recursively). Parsing is followed by post-processing. This writes each document to the output directory ("outputdir" in the example). These documents have all requirements turned into hyperlinks which also work across documents, to make the specification hierarchy easy to navigate.

Report generation macros are also processed, to put extra report data in the output documents.

Report generation macros

There are currently two "macros" that will be expanded by the post processing done by AsciiReqs:

The document hierarchy macro

Putting the following in a document will make ascireqs replace it with a hierarchical list that defines the document hierarchy (a figure generated from inline PlantUML would have been even nicer, of course):

`asciireq-hierarchy`

The requirement table macro

Putting the following in a document will make ascireqs replace it with a requirement table:

`asciireq-table: ID, Title, Line; "Rel-1" in elements(Tags)`

The list after the colon is a list of the column headings to put in the table (each heading is a requirement property name). The list of property names can be followed by a semicolon and a filter expression.

The filter expression is a Python expression operating on a limited set of variables and functions. If a requirement attribute name starts with a letter and contains only letters and numbers then the attribute value will available by its attribute name in expressions. Spaces are also permitted, but will be turned into underscores. The requirement object itself is also a variable named req and is a dictionary of property names and values (of type str). If the expression evaluates to true then the requirement will be in the table.

In the example, the filter references the Tag attribute. It uses the helper function elements which splits a comma separated string into a list of its comma separated elements. The filter therefore picks out any requirement that has "Rel-1" as one element of a comma-separated list in the Tag attribute.

The following helper functions are supported (Python comparisons and keywords can also be used):

  • elements: Takes a string which should be a comma separated list, and returns the elements as a list of strings. Spaces before and after each element are removed and empty elements are discarded.

  • has_invalid_link: The is true if the Parent or Child attribute contains an unknown requirement ID.

  • link_error: This true if the requirement has a link to one or more parents, where the parent has no link back to the requirement. This is useful to generate tables of requirements with broken/inconsistent links.

  • startswith: The standard startswith function from the str class.

  • re.fullmatch, re.search, re.match: String matching functions from the "re" namespace.

If the filter expression is omitted then all the requirements in the current document are put in the table.

Test drive (for Linux)

The testdata folder contains two AsciiDoc spec files, one parent and one child spec. There is also one report template. To process the specs, run the following command inside the testdata folder:

asciireq -o ../output -t report-template req-tool-user-reqs.adoc

This processes both specs (since they form a hierarchy) and puts the processed AsciiDoc files in the output folder. It will also process a separate report template and expand the table macros found there. You can then generate HTML from these files:

asciidoc ../output/req-tool-user-reqs.adoc
asciidoc ../output/req-tool-sw-reqs.adoc
asciidoc ../output/report-template.adoc

The HTML files will have cross-links for all requirement relations and mentions, and contain some examples of how macros can be used for reporting.

If you won’t or can’t run the examples, you will find the AsciiDoc output files in the output folder of the repo.

About

Experimental requirements management using asciidoc files

Resources

License

Stars

Watchers

Forks

Packages

No packages published

Languages