Filtering needs

The filtering of needs and need parts is supported consistently across numerous directives and roles, either by using filter options or by using a filter string.

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

Example 1

.. 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 “;”.

Show example

Example 2

.. needlist::
   :tags: main_example
   :show_tags:

No needs passed the filters

types

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

Show example

Example 3

.. 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

NEED-D

Recalculate hash and compare

T_0F2B7

Level 4

T_5CCAA

Test 1

T_C3893

Test for XY

implemented

T_EA5AF

Sub-Need on level 3

test_001

test a requirement

test_arch_001

Test Arch

test_flow_001

A test case

sort_by

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

Show example

Example 4

.. 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

extend_test_003

needextend Example 3

open

extend_test_004

needextend Example 4

open

extend_test_005

needextend Example 5

open

extend_test_006

needextend Example 6

open

ID123

User needs to login

open

IMAGE_EXAMPLE

My test spec

open

JINJAID125

Nested Spec Need

open

JINJAID126

First Spec Need

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

S_B4B62

Sub-Need on level 2 with status option set

open

SP_INT

Interfaces

open

STYLE_006

My automatically styled requirement

open

T_003

Unit test 3

open

T_004

Unit test 4

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:

Example 5

:need_count:`type=='spec' and status != 'open'`

31

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 content is kept, but the status attribute is taken from its parent need.

Note

The 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.

Example 6

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

3

Inside a filter string all the fields of NeedsInfoType can be used, including.

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.

Finally, the following are available:

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

  • needs as NeedsAndPartsListView object, which contains all needs and need_parts.

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

Example 7: filter option

needs:

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

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

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

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

.. test:: Test 1
   :tags: filter_example
   :hide:

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

needs:

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

Filtering for needs on the current page

Added in version 5.0.0.

Additionally, to common Filter string variables, the c.this_doc() function is for most directives, to filter for needs only in the same document as the directive.

Example 8

.. needtable::
   :filter: c.this_doc()
   :style: datatables

ID

Title

Status

Outgoing

IMPL_01

Implementation for specification

OWN_ID_123

Specification of a requirement

R_17EB4

Set admin e-mail to admin@mycompany.com

R_22EB2

Requirement B

closed

R_2A9D0

Requirement A

open

R_F4722

My first requirement

open

S_01A67

Specification B

open

S_D70B0

Specification A

closed

T_5CCAA

Test 1

T_C3893

Test for XY

implemented

Filter string performance

Added in version 4.0.0.

The filter string is evaluated by default for each need and need part and, therefore, can be become a performance bottleneck for projects with large numbers of needs.

To improve performance, certain common patterns are identified and optimized by the filter engine, and so using such patterns is recommended:

  • is_external / is_external == True / is_external == False

  • id == 'value' / id == "value" / 'value' == id / "value" == id

  • id in ['value1', 'value2', ...] / id in ("value1", "value2", ...)

  • type == 'value' / type == "value" / 'value' == type / "value" == type

  • type in ['value1', 'value2', ...] / type in ("value1", "value2", ...)

  • status == 'value' / status == "value" / 'value' == status / "value" == status

  • status in ['value1', 'value2', ...] / status in ("value1", "value2", ...)

  • 'value' in tags / "value" in tags

Also filters containing and will be split into multiple filters and evaluated separately for the above patterns. For example, type == 'spec' and other == 'value' will first be filtered performantly by type == 'spec' and then the remaining needs will be filtered by other == 'value'.

To guard against long running filters, the needs_filter_max_time configuration option can be used to set a maximum time limit for filter evaluation. Also see needs_uml_process_max_time, to guard against long running needuml / needarch processes containing filters.

To debug which filters are being used across your project and their run times, you can enable the needs_debug_filters configuration option.

Deprecated since version 4.0.0: The directive option export_id was previously used to export filter information in the needs.json file. This is deprecated and will be removed in a future version. Instead use the above needs_debug_filters configuration option.

Filter code

Added 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.

The code also has access to a variable called needs, which is a NeedsAndPartsListView instance.

Example 10

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

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

   results = []

   for need in needs.filter_types(["req"]):
      for links_id in need['links']:
         linked_need = needs.get_need(links_id)
         if linked_need and linked_need['type'] == 'spec':
            results.append(need)
            results.append(linked_need)

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

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

Added 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

Added 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)

Filter matches nothing

Depending on the directive used a filter that matches no needs may add text to inform that no needs are found.

The default text “No needs passed the filter”.

If this is not intended, add the option

filter_warning

Add specific text with this option or add no text to display nothing. The default text will not be shown.

The specified output could be styled with the css class needs_filter_warning

More Examples

Setup

Example 11

.. req:: My first requirement
   :status: open
   :tags: requirement; test; awesome

   This is my **first** requirement!!

   .. note:: You can use any rst code inside it :)

.. spec:: Specification of a requirement
   :id: OWN_ID_123
   :links: R_F4722

   Outgoing links of this spec: :need_outgoing:`OWN_ID_123`.

.. impl:: Implementation for specification
   :id: IMPL_01
   :links: OWN_ID_123

   Incoming links of this spec: :need_incoming:`IMPL_01`.

.. test:: Test for XY
   :status: implemented
   :tags: test; user_interface; python27
   :links: OWN_ID_123; IMPL_01

   This test checks :need:`IMPL_01` for :need:`OWN_ID_123` inside a
   Python 2.7 environment.
Requirement: My first requirement R_F4722
status: open
tags: requirement, test, awesome
links incoming: OWN_ID_123

This is my first requirement!!

Note

You can use any rst code inside it :)

Specification: Specification of a requirement OWN_ID_123
links outgoing: R_F4722
links incoming: IMPL_01, T_C3893

Outgoing links of this spec: R_F4722.

Implementation: Implementation for specification IMPL_01
links outgoing: OWN_ID_123
links incoming: T_C3893

Incoming links of this spec: T_C3893.

Test Case: Test for XY T_C3893
status: implemented
tags: test, user_interface, python27
links outgoing: OWN_ID_123, IMPL_01

This test checks Implementation for specific... (IMPL_01) for Specification of a requirement (OWN_ID_123) inside a Python 2.7 environment.

Example 12: Filter result as table

.. needtable::
   :tags: test
   :status: implemented; open

ID

Title

Status

Outgoing

df_1

my test requirement

open

R_F4722

My first requirement

open

T_C3893

Test for XY

implemented

Example 13: Filter result as diagram

.. needflow::
   :filter: "More Examples" == section_name

@startuml

' Nodes definition 

node "<size:12>Requirement</size>\n**My first**\n**requirement**\n<size:10>R_F4722</size>" as R_F4722 [[../filter.html#R_F4722]] #BFD8D2
node "<size:12>Specification</size>\n**Specification**\n**of a**\n**requirement**\n<size:10>OWN_ID_123</size>" as OWN_ID_123 [[../filter.html#OWN_ID_123]] #FEDCD2
node "<size:12>Implementation</size>\n**Implementation**\n**for**\n**specification**\n<size:10>IMPL_01</size>" as IMPL_01 [[../filter.html#IMPL_01]] #DF744A
node "<size:12>Test Case</size>\n**Test for XY**\n<size:10>T_C3893</size>" as T_C3893 [[../filter.html#T_C3893]] #DCB239
node "<size:12>Requirement</size>\n**A normal**\n**requirement**\n<size:10>EX_REQ_1</size>" as EX_REQ_1 [[../layout_styles.html#EX_REQ_1]] #BFD8D2
node "<size:12>Requirement</size>\n**A more complex**\n**and highlighted**\n**requirement**\n<size:10>EX_REQ_2</size>" as EX_REQ_2 [[../layout_styles.html#EX_REQ_2]] #BFD8D2
node "<size:12>Requirement</size>\n**A focused**\n**requirement**\n<size:10>EX_REQ_3</size>" as EX_REQ_3 [[../layout_styles.html#EX_REQ_3]] #BFD8D2
node "<size:12>Requirement</size>\n**A custom**\n**requirement**\n**with picture**\n<size:10>EX_REQ_4</size>" as EX_REQ_4 [[../layout_styles.html#EX_REQ_4]] #BFD8D2
node "<size:12>Requirement</size>\n**A requirement**\n**with a**\n**permalink**\n<size:10>EX_REQ_5</size>" as EX_REQ_5 [[../layout_styles.html#EX_REQ_5]] #BFD8D2

' Connection definition 

OWN_ID_123 --> R_F4722
IMPL_01 --> OWN_ID_123
T_C3893 --> OWN_ID_123
T_C3893 --> IMPL_01
EX_REQ_2 --> EX_REQ_1

@enduml