Filtering needs

Sphinx-Needs supports the filtering of need and need_parts by using easy to use options or powerful filter string.

Available options are specific to the used directive, whereas the filter string is supported by all directives and roles, which provide filter capabilities.

Filter options

The following filter options are supported by directives:

Related to the used directive and its representation, the filter options create a list of needs, which match the filters for status, tags, types and filter.

For :status:, :tags: and :types: values are separated by “;”. :filter: gets evaluated.

The logic, if a need belongs to the final result list, is as followed:

status = (open OR in_progress) AND tags = (user OR login) AND types = (req OR spec) AND eval(filter) is True

status

Use the status option to filter needs by their status.

You can easily filter for multiple statuses by separating them by “;”. Example: open; in progress; reopen.

Show example

.. needlist::
   :status: open
   :show_status:
R_2A9D0: Requirement A (open)
S_01A67: Specification B (open)

tags

tags allows to filter needs by one or multiple tags.

To search for multiple tags, simply separate them by using “;”.

types

For :types: the type itself or the human-readable type-title can be used as filter value.

Show example

.. needtable::
   :types: test

ID

Title

Status

Outgoing

copy_3

test of specification and requirement

copy_4

test of current_need value

EXTRA_TEST_001

Test of requirements

T_5CCAA

Test 1

T_C3893

Test for XY

implemented

test_001

test a requirement

test_flow_001

A test case

sort_by

Sorts the result list. Allowed values are status or any alphanumerical property.

Show example

.. needtable::
   :sort_by: id
   :status: open

ID

Title

Status

Outgoing

copy_1

copy-example

open

copy_2

copy-example implementation

open

df_1

my test requirement

open

EX_CLEAN

CLEAN layout

open

EX_CLEAN_L

CLEAN_L layout

open

EX_CLEAN_LP

CLEAN_LP layout

open

EX_CLEAN_R

CLEAN_R layout

open

EX_CLEAN_RP

CLEAN_RP layout

open

EX_COMPLETE

COMPLETE layout

open

EX_DEBUG

DEBUG layout

open

EX_FOCUS

FOCUS layout

open

EX_FOCUS_F

FOCUS_F layout

open

EX_FOCUS_L

FOCUS_L layout

open

EX_FOCUS_R

FOCUS_R layout

open

EX_REQ_1

A normal requirement

open

EX_REQ_2

A more complex and highlighted requirement

open

EX_REQ_3

A focused requirement

open

EX_REQ_4

A custom requirement with picture

open

EX_REQ_5

A requirement with a permalink

open

EX_ROW_2

Not implemented spec

open

EXAMPLE_166

Fails to build PDF when 'req' directive contains nested directives

open

GH2_166

Fails to build PDF when 'req' directive contains nested directives

open

GH_166

Fails to build PDF when 'req' directive contains nested directives

open

GH_ISSUE_166

Fails to build PDF when 'req' directive contains nested directives

open

ID123

User needs to login

open

IMAGE_EXAMPLE

My test spec

open

my_car_1

Car must be awesome

open

→ my_car_1.1

→  A top speed of 300 km/h

open

→ my_car_1.2

→  An acceleration of 200 m/s² or much much more

open

→ my_car_1.awesome_3

→  a turbo button

open

R_2A9D0

Requirement A

open

R_F4722

My first requirement

open

roles_req_1

Sliced Bread

open

S_01A67

Specification B

open

SP_INT

Interfaces

open

SPEC_1

Content of each need

open

STYLE_006

My automatically styled requirement

open

TEMPL_SPEC

My specification

open

xyz_123

My requirement with custom options

open

filter

The filter option allows the definition of a complex query string, which gets evaluated via eval() in Python. Please see Filter string for more details.

Filter string

The usage of a filter string is supported/required by:

The filter string must be a valid Python expression:

:need_count:`type=='spec' and status.upper()!='OPEN'`

A filter string gets evaluated on needs and need_parts! A need_part inherits all options from its parent need, if the need_part has no own content for this option. E.g. the need_part title is kept, but the status attribute is taken from its parent need.

Note

Following attributes are kept inside a need_part: id, title, links_back

This allows to perform searches for need_parts, where search options are based on parent attributes.

The following filter will find all need_parts, which are part of a need, which has a tag called important.

:need_count:`is_part and 'important' in tags`

Inside a filter string the following variables/functions can be used:

  • tags as Python list (compare like "B" in tags)

  • type as Python string (compare like "story" == type)

  • status as Python string (compare like "opened" != status)

  • sections as Python list with the hierarchy of sections with lowest-level section first. (compare like "Section Header" in sections)

  • id as Python string (compare like "MY_ID_" in id)

  • title as Python string (compare like len(title.split(" ")) > 5)

  • links as Python list (compare like "ID_123" not in links)

  • links_back as Python list (compare like "ID_123" not in links_back)

  • content as Python string (compare like len(content) == 0)

  • is_need as Python boolean. (compare like is_need)

  • is_part as Python boolean. (compare like is_part)

  • parts as Python list with need_part / np of the current need. (compare like len(parts)>0)

  • search, as Python function for performing searches with a regular expression

  • needs as Python dict. Contains all needs. Helpful to perform complex filters on links (added 0.3.15).

  • sections as list of sections names, th which the need belongs to.

  • section_name as string, which defines the last/lowest section a need belongs to.

  • docname as string, which defines the name of the document in which a need is defined, without the extension (similar to Sphinx’ :doc: role)

  • signature as string, which contains a function-name, possible set by sphinx-autodoc above the need.

  • parent_need as string, which is an id of the need, which has the current need defined in its content (added 0.6.2).

  • parent_needs as string, which is a list of need ids (added 0.6.2).

Additional variables for need_part / np:

  • id_parent as Python string, which contains the id of the parent need. (compare like id_parent == "ABC_01")

  • id_complete as Python string. Contains the concatenated ids of parent need and need_part. (compare like id_complete != 'ABC_01.03')

Note

If extra options were specified using needs_extra_options then those will be available for use in filter expressions as well.

If your expression is valid and it’s True, the related need is added to the filter result list. If it is invalid or returns False, the related need is not taken into account for the current filter.

Show example

.. req:: Requirement A
   :tags: A; filter_example
   :status: open

.. req:: Requirement B
   :tags: B; filter_example
   :status: closed

.. spec:: Specification A
   :tags: A; filter_example
   :status: closed

.. spec:: Specification B
   :tags: B; filter_example
   :status: open

.. test:: Test 1
   :tags: filter_example

.. needtable::
   :filter: "filter_example" in tags and ("B" in tags or ("spec" == type and "closed" == status)) or "test" == type

This will have the following result:

R_22EB2: Requirement B
S_D70B0: Specification A
S_01A67: Specification B
T_5CCAA: Test 1

search(pattern, variable) is based on Pythons re.search() function

The first parameter must be a regular expression pattern. The second parameter should be one of the above variables(status, id, content, ..)

Show example

This example uses a regular expressions to find all needs with an e-mail address in title.

.. req:: Set admin e-mail to admin@mycompany.com

.. needlist::
   :filter: search("([a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+\.[a-zA-Z0-9-.]+$)", title)
Requirement: Set admin e-mail to admin@mycompany.com R_17EB4 _images/arrow-right-circle.svg

export_id

New in version 0.3.11.

If set, the filter results get exported to needs.json, if the builder needs is used:

.. needtable::
   :status: open
   :filter: "test" in tags
   :export_id: filter_01

See Exporting filters for more details.

Filter code

New in version 0.5.3.

The content of a needlist, needtable or needflow can be used to define own filters with the help of Python.

The used code must define a variable results, which must be a list and contains the filtered needs.

.. needtable::
   :columns: id, title, type, links, links_back
   :style: table

   # Collect all requirements and specs,
   # which are linked to each other.

   results = []
   # Lets create a needs_dict to address needs by ids more easily.
   needs_dict = {x['id']: x for x in needs}

   for need in needs:
      if need['type'] == 'req':
         for links_id in need['links']:
            if needs_dict[links_id]['type'] == 'spec':
               results.append(need)
               results.append(needs_dict[links_id])

ID

Title

Type

Links

Links Back

R_3C95C

Result 4

req

R_D0791

Result 3

req

sum_input_1

Do this

spec

sum_input_3

Do too much

spec

The code has access to a variable called needs, which contains a copy of all needs. So manipulations on the values in needs do not have any affects.

This mechanism can also be a good alternative for complex filter strings to save performance. For example if a filter string is using list comprehensions to get access to linked needs.

If filter code is used, all other filter related options (like status or filters) are ignored.

Warning

This feature executes every given Python code. So be sure to trust the input/writers.

Filter function

New in version 0.7.3.

Nearly same behavior as Filter code, but the code gets read from an external python file and a function must be referenced.

option name:

filter-func

default:

None

Usage inside a rst file:

.. needtable:: Filter function example
   :filter-func: filter_file.own_filter_code

The code of the referenced file filter_file.py with function own_filter_code:

def own_filter_code(needs, results, **kwargs):
    for need in needs:
        if need["type"] == "test":
            results.append(need)

The function gets executed by Sphinx-Needs and it must provide two keyword arguments: needs and results.

Also the given package/module must be importable by the used Python environment. So it must be part of the Python Path variable. To update this, add sys.path.insert(0, os.path.abspath("folder/to/filter_files")) to your conf.py file.

Arguments

New in version 0.7.6.

Filter function are supporting arguments: filter_file.own_filter_code(value_1,value_2).

Please note, that the part between (...) is just a comma separated list and each element will be given as string to the function.

The functions get the values as part of **kwargs with the name is arg<pos>, starting from 1.

Example:

.. needtable:: Filter function example
   :filter-func: filter_file.own_filter_code(1,2.5,open)
def own_filter_code(needs, results, **kwargs):
    for need in needs:
        if int(need["price"]) > int(kwargs["arg1"]) or need["status"] == kwargs["arg3"]:
            results.append(need)

The function developer is responsible to perform any needed typecast.

Needpie

needpie also supports filter-code. But instead of needs, a list of resulting numbers must be returned.

Example:

.. needpie:: Filter code func pie
   :labels: new,done
   :filter-func: filter_code_func.my_pie_filter_code_args(new,done)
def my_pie_filter_code_args(needs, results, **kwargs):
    cnt_x = 0
    cnt_y = 0
    for need in needs:
        if need["status"] == kwargs['arg1']:
            cnt_x += 1
        if need["status"] == kwargs['arg2']:
            cnt_y += 1

   results.append(cnt_x)
   results.append(cnt_y)