Skip to content

visualdynamics.report.requirement

requirement

The test requirement and the result against it, in a report (report feature test_requirement).

A requirement block carries the requirement the plan states (core.requirement.TestRequirement), the specification it is held to and the run it reads, and is saved with the report and its template. With the feature on it draws two things:

  • the requirement in words: the governing document and its revision, the tolerance clause, the control strategy, each band and tolerance, the decision rule, and where each number came from, with "supplied by the customer; the laboratory has not verified its derivation" for a number the customer supplied. With skeleton sections on, it falls under the Requirement section, where the author still owes the plan, the failure definition and the completion criteria.
  • beneath it, with the feature control_uncertainty on as well, the control channels' measurement uncertainty against one third of the tolerance (uncertainty): a row per control channel with its contributions, the combined uncertainty in percent on the acceleration, on the ASD and in dB, the third it is held against, and met, not met or not judged; one line saying it was not judged when nothing is entered.
  • the result table: a row per criterion with what was required, what was measured (each miss with its band, its worst size in dB and its line count) and the result, worded met, not met or not judged, and a note beneath with what the rows cannot say. Its cross-axis rows read each cross-axis channel against its in-axis channel, and its limit-channel rows give each notch a limit channel cut, the as-run notch, against the notches the plan names. A row with a notch the plan does not name, and a cross-axis row over its limit, offers its fact to the findings register (core.requirement.Criterion.finding): draft_finding builds the fact draft, naming the record, and hands it to the register's one entry point for drafts (report.findings.offer), as a comparison's draft is handed. With the feature findings on, a Finding column gives each such row the number of the finding its fact went to, or says none is entered. With skeleton sections on, the table falls under Results.

The pass/fail box reads from a requirement bound to the same specification and run: MET, NOT MET or NOT JUDGED, how many criteria were met against which document, the criteria not met and not judged, and the decision rule. A box with no requirement bound to its pair keeps its reading and is labeled a screening by the Visual Dynamics default rule, so it is never read as a statement of conformity. Both only while the feature is on; with it off a requirement block is not drawn and the box is the box it was.

Nothing here is the author's text: the requirement is the author's own numbers in words, and the table states what the program measured. The statement of conformity in the conclusion stays the author's.

Where the requirement's run takes its duration at level is one function, run_duration, so a later source of run identity feeds it there and nowhere else.

Classes:

Name Description
Made

The page blocks one requirement block draws: its words, for the

Functions:

Name Description
add

Give report a requirement block and return its index.

run_duration

How long the block's run was at level, seconds, or None when

inputs

(requirement, specification, run, the specification's name, the

channel_table

The project's channel table, whose Control flags name the control

roles_on

Whether report reads the cross-axis and limit channels (the

read

The block's core.requirement.Results, or None while a binding

pending

The requirement blocks of report with no kept reading, or whose

prepare

Read every requirement block of report with no kept reading, so

build

The page blocks of one requirement block, or None while a binding

place

The skeleton's page blocks with each requirement's words at the

requirement_lines

The block's requirement in words, a line each

uncertainty

The control channels' measurement uncertainty against one third

table

The result table: a row per criterion, its result cell marked by

fact_draft

The report.findings.FactDraft a row of the result table offers:

draft_finding

Offer finding number the fact a row of a result table offers,

matching

The requirement block the pass/fail block verdict reads from:

verdict

The pass/fail box built from block, as the feature draws it.

Classes

Made dataclass

Made(words: list[dict[str, Any]], results: list[dict[str, Any]])

The page blocks one requirement block draws: its words, for the Requirement section, and its result table and note, for Results.

Functions:

add

add(report: Any, requirement: Any, *, source: str, measured: str, duration_run: float | None = None, caption: str = '', at: int | None = None) -> int

Give report a requirement block and return its index.

The block is a text block carrying the requirement's values (TestRequirement.as_record), the specification (source) and the run (measured) it reads, the run's duration at level when stated, and the table's caption. It is drawn only while the feature is on, and is saved with the report and its template. It is put at at, or after the report's first pass/fail box, or at the end; without the skeleton it draws where it stands.

Raises TypeError when requirement is not a TestRequirement, and ValueError for a duration that is not a positive number or a binding left empty. Nothing is changed when it raises.

Source code in src/visualdynamics/report/requirement.py
def add(report: Any, requirement: Any, *, source: str, measured: str,
        duration_run: float | None = None, caption: str = '',
        at: int | None = None) -> int:
    """Give `report` a requirement block and return its index.

    The block is a text block carrying the requirement's values
    (`TestRequirement.as_record`), the specification (`source`) and the
    run (`measured`) it reads, the run's duration at level when stated,
    and the table's caption. It is drawn only while the feature is on,
    and is saved with the report and its template. It is put at `at`,
    or after the report's first pass/fail box, or at the end; without
    the skeleton it draws where it stands.

    Raises `TypeError` when `requirement` is not a `TestRequirement`,
    and `ValueError` for a duration that is not a positive number or a
    binding left empty. Nothing is changed when it raises.
    """
    from ..core.requirement import TestRequirement, _number

    if not isinstance(requirement, TestRequirement):
        raise TypeError('the requirement is a TestRequirement')
    if not source or not measured:
        raise ValueError('a requirement block names its specification and '
                         'its run')
    if duration_run is not None:
        duration_run = _number(duration_run, 'the duration at level')
        if not duration_run > 0.0:
            raise ValueError('the duration at level is a number of seconds '
                             'above 0')
    block: dict[str, Any] = {
        'kind': 'text', 'text': '', 'requirement': requirement.as_record(),
        'source': str(source), 'measured': str(measured),
        'caption': str(caption)}
    if duration_run is not None:
        block['duration_run'] = duration_run
    if at is None:
        boxes = [k for k, held in enumerate(report.blocks)
                 if held.get('kind') == 'verdict']
        at = boxes[0] + 1 if boxes else len(report.blocks)
    report.blocks.insert(int(at), block)
    return int(at)

run_duration

run_duration(block: Mapping[str, Any], objects: Mapping[str, Any], object_groups: Sequence[Mapping[str, Any]] | None = None) -> float | None

How long the block's run was at level, seconds, or None when nothing says.

Today it is the controller's counter, typed on the block (duration_run). This is the one place the result table takes it from, so a later source (a run's own identity) is read here.

Source code in src/visualdynamics/report/requirement.py
def run_duration(block: Mapping[str, Any], objects: Mapping[str, Any],
                 object_groups: Sequence[Mapping[str, Any]] | None = None
                 ) -> float | None:
    """How long the block's run was at level, seconds, or None when
    nothing says.

    Today it is the controller's counter, typed on the block
    (`duration_run`). This is the one place the result table takes it
    from, so a later source (a run's own identity) is read here.
    """
    value = block.get('duration_run')
    return None if value is None else float(value)

inputs

inputs(block: Mapping[str, Any], objects: Mapping[str, Any], object_groups: Sequence[Mapping[str, Any]] | None = None) -> tuple[Any, Any, Any, str, str] | None

(requirement, specification, run, the specification's name, the run's name), or None while either binding does not resolve.

Raises ValueError for stored values the requirement refuses.

Source code in src/visualdynamics/report/requirement.py
def inputs(block: Mapping[str, Any], objects: Mapping[str, Any],
           object_groups: Sequence[Mapping[str, Any]] | None = None
           ) -> tuple[Any, Any, Any, str, str] | None:
    """(requirement, specification, run, the specification's name, the
    run's name), or None while either binding does not resolve.

    Raises `ValueError` for stored values the requirement refuses.
    """
    from ..core.report import resolve_binding
    from ..core.requirement import TestRequirement

    source = resolve_binding(block.get('source'), objects, object_groups)
    name = resolve_binding(block.get('measured'), objects, object_groups)
    if source not in objects or name not in objects:
        return None
    requirement = TestRequirement.from_record(block['requirement'])
    return requirement, objects[source], objects[name], str(source), str(name)

channel_table

channel_table(objects: Mapping[str, Any]) -> Any

The project's channel table, whose Control flags name the control channels, or None.

Source code in src/visualdynamics/report/requirement.py
def channel_table(objects: Mapping[str, Any]) -> Any:
    """The project's channel table, whose Control flags name the control
    channels, or None."""
    from ..core.channel_table import ChannelTable

    tables = [obj for obj in objects.values() if isinstance(obj, ChannelTable)]
    return tables[0] if len(tables) == 1 else None

roles_on

roles_on(report: Any) -> bool

Whether report reads the cross-axis and limit channels (the feature cross_axis).

Source code in src/visualdynamics/report/requirement.py
def roles_on(report: Any) -> bool:
    """Whether `report` reads the cross-axis and limit channels (the
    feature `cross_axis`)."""
    from .features import enabled

    return ROLES in enabled(report)

read

read(block: Mapping[str, Any], objects: Mapping[str, Any], object_groups: Sequence[Mapping[str, Any]] | None = None, *, roles: bool = True) -> Any

The block's core.requirement.Results, or None while a binding does not resolve. Refusals raise ValueError as core.requirement.evaluate raises them. With roles False (the report's feature cross_axis off) the cross-axis and limit channels are not read, and their rows say so.

Made once and kept while the requirement, the duration and the objects it read stay the same (in check_blocks's store of readings), since a reading of several control channels takes longer than the GUI thread may wait; the app makes it on its worker first (prepare).

Source code in src/visualdynamics/report/requirement.py
def read(block: Mapping[str, Any], objects: Mapping[str, Any],
         object_groups: Sequence[Mapping[str, Any]] | None = None, *,
         roles: bool = True) -> Any:
    """The block's `core.requirement.Results`, or None while a binding
    does not resolve. Refusals raise `ValueError` as
    `core.requirement.evaluate` raises them. With `roles` False (the
    report's feature `cross_axis` off) the cross-axis and limit channels
    are not read, and their rows say so.

    Made once and kept while the requirement, the duration and the
    objects it read stay the same (in `check_blocks`'s store of
    readings), since a reading of several control channels takes longer
    than the GUI thread may wait; the app makes it on its worker first
    (`prepare`)."""
    from dataclasses import replace

    from ..core.requirement import evaluate
    from .check_blocks import _keep, _kept

    read_from = _objects(block, objects, object_groups)
    if read_from is None:
        return None
    duration = run_duration(block, objects, object_groups)
    key = _key(block, read_from, duration, roles)
    kept = _kept(key, read_from)
    if kept is None:
        try:
            requirement, specification, measured, _s, _n = inputs(
                block, objects, object_groups)
            held = requirement if roles else replace(
                requirement, cross_axis_channels={}, limit_channels=())
            kept = evaluate(held, specification, measured,
                            table=read_from.get('table'),
                            duration_run=duration,
                            profiles={name.removeprefix('profile:'): obj
                                      for name, obj in read_from.items()
                                      if name.startswith('profile:')})
            if not roles:
                kept = _unread_roles(requirement, kept)
        except (TypeError, ValueError) as refusal:
            kept = type(refusal)(str(refusal))
        _keep(key, read_from, kept)
    if isinstance(kept, Exception):
        raise type(kept)(str(kept))
    return kept

pending

pending(report: Any, objects: Mapping[str, Any], object_groups: Sequence[Mapping[str, Any]] | None = None) -> list[Mapping[str, Any]]

The requirement blocks of report with no kept reading, or whose reading was canceled: what prepare would read. Empty when the feature is off.

Source code in src/visualdynamics/report/requirement.py
def pending(report: Any, objects: Mapping[str, Any],
            object_groups: Sequence[Mapping[str, Any]] | None = None
            ) -> list[Mapping[str, Any]]:
    """The requirement blocks of `report` with no kept reading, or whose
    reading was canceled: what `prepare` would read. Empty when the
    feature is off."""
    from .check_blocks import _kept
    from .features import enabled

    if FEATURE not in enabled(report):
        return []
    out = []
    for block in report.blocks:
        if 'requirement' not in block:
            continue
        read_from = _objects(block, objects, object_groups)
        if read_from is None:
            continue
        kept = _kept(_key(block, read_from, run_duration(
            block, objects, object_groups), roles_on(report)), read_from)
        if kept is None or str(kept) == CANCELED:
            out.append(block)
    return out

prepare

prepare(report: Any, objects: Mapping[str, Any], object_groups: Sequence[Mapping[str, Any]] | None = None, *, progress: Any = None) -> int

Read every requirement block of report with no kept reading, so the page is drawn without reading any. progress(done, total) is told before each and at the end; what it raises (a cancel) stops there, and each block not yet read is kept as canceled, drawn as nothing judged and offered again by pending. Returns how many were read.

Source code in src/visualdynamics/report/requirement.py
def prepare(report: Any, objects: Mapping[str, Any],
            object_groups: Sequence[Mapping[str, Any]] | None = None, *,
            progress: Any = None) -> int:
    """Read every requirement block of `report` with no kept reading, so
    the page is drawn without reading any. `progress(done, total)` is
    told before each and at the end; what it raises (a cancel) stops
    there, and each block not yet read is kept as canceled, drawn as
    nothing judged and offered again by `pending`. Returns how many were
    read."""
    from .check_blocks import _KEPT, _keep

    waiting = pending(report, objects, object_groups)

    def key_of(block):
        read_from = _objects(block, objects, object_groups)
        return read_from, _key(block, read_from, run_duration(
            block, objects, object_groups), roles_on(report))

    for done, block in enumerate(waiting):
        try:
            if progress is not None:
                progress(done, len(waiting))
        except BaseException:
            for rest in waiting[done:]:
                read_from, key = key_of(rest)
                _keep(key, read_from, ValueError(CANCELED))
            raise
        _KEPT.pop(key_of(block)[1], None)
        try:
            read(block, objects, object_groups, roles=roles_on(report))
        except (TypeError, ValueError):
            pass        # kept, and said on the page
    if progress is not None:
        progress(len(waiting), len(waiting))
    return len(waiting)

build

build(block: Mapping[str, Any], objects: Mapping[str, Any], object_groups: Sequence[Mapping[str, Any]] | None = None, *, uncertainty_on: bool = False, roles_on: bool = False, register: Sequence[Mapping[str, Any]] | None = None) -> Made | None

The page blocks of one requirement block, or None while a binding does not resolve. A requirement that cannot be read against its run at all draws its words and, for the results, one paragraph saying why, since a reading that judged nothing says so. uncertainty_on (the feature control_uncertainty) adds the control channels' uncertainty under the words, roles_on (the feature cross_axis) reads the cross-axis and limit channels, and register (the findings entered, with the feature findings on) gives a row that offers a finding its Finding cell.

Source code in src/visualdynamics/report/requirement.py
def build(block: Mapping[str, Any], objects: Mapping[str, Any],
          object_groups: Sequence[Mapping[str, Any]] | None = None, *,
          uncertainty_on: bool = False, roles_on: bool = False,
          register: Sequence[Mapping[str, Any]] | None = None
          ) -> Made | None:
    """The page blocks of one requirement block, or None while a binding
    does not resolve. A requirement that cannot be read against its run
    at all draws its words and, for the results, one paragraph saying
    why, since a reading that judged nothing says so. `uncertainty_on`
    (the feature `control_uncertainty`) adds the control channels'
    uncertainty under the words, `roles_on` (the feature
    `cross_axis`) reads the cross-axis and limit channels, and
    `register` (the findings entered, with the feature `findings` on)
    gives a row that offers a finding its Finding cell."""
    from ..core.requirement import TestRequirement

    try:
        requirement = TestRequirement.from_record(block['requirement'])
    except (TypeError, ValueError) as refusal:
        return Made([], [{'kind': 'text', 'text':
                          f'**{CAPTION}:** the requirement cannot be read; '
                          f'{refusal}.'}])
    words = [{'kind': 'text', 'text': _words(requirement)}]
    try:
        found = read(block, objects, object_groups, roles=roles_on)
    except (TypeError, ValueError) as refusal:
        if uncertainty_on:
            words += uncertainty(requirement)
        return Made(words, [{'kind': 'text', 'text':
                             f'**{CAPTION}:** nothing judged; {refusal}.'}])
    if found is None:
        return None
    if uncertainty_on:
        words += uncertainty(requirement, found.channels)
    name = str(block.get('measured') or '')
    return Made(words, [table(found, name, str(block.get('caption') or ''),
                              register),
                        {'kind': 'text', 'text': _note(found)}])

place

place(blocks: Sequence[dict[str, Any]], made: Sequence[Made]) -> list[dict[str, Any]]

The skeleton's page blocks with each requirement's words at the end of the Requirement section and its result table and note at the head of Results, before the figures, since the control result against the tolerance is what that section opens with.

Source code in src/visualdynamics/report/requirement.py
def place(blocks: Sequence[dict[str, Any]], made: Sequence[Made]
          ) -> list[dict[str, Any]]:
    """The skeleton's page blocks with each requirement's words at the
    end of the Requirement section and its result table and note at the
    head of Results, before the figures, since the control result
    against the tolerance is what that section opens with."""
    from .skeleton import under

    out = under(blocks, 'requirement',
                [built for each in made for built in each.words])
    head = next(at for at, block in enumerate(out)
                if block.get('skeleton', {}).get('key') == 'results')
    return (out[:head + 1] + [built for each in made for built in each.results]
            + out[head + 1:])

requirement_lines

requirement_lines(block: Mapping[str, Any]) -> list[str]

The block's requirement in words, a line each (TestRequirement.describe). Raises ValueError for stored values the requirement refuses.

Source code in src/visualdynamics/report/requirement.py
def requirement_lines(block: Mapping[str, Any]) -> list[str]:
    """The block's requirement in words, a line each
    (`TestRequirement.describe`). Raises `ValueError` for stored values
    the requirement refuses."""
    from ..core.requirement import TestRequirement

    return TestRequirement.from_record(block['requirement']).describe()

uncertainty

uncertainty(requirement: Any, channels: Sequence[str] | None = None) -> list[dict[str, Any]]

The control channels' measurement uncertainty against one third of the tolerance (core.requirement.uncertainty_budget): a table with a row per control channel and a note, or, with nothing entered for any, one line saying it was not judged.

Source code in src/visualdynamics/report/requirement.py
def uncertainty(requirement: Any, channels: Sequence[str] | None = None
                ) -> list[dict[str, Any]]:
    """The control channels' measurement uncertainty against one third
    of the tolerance (`core.requirement.uncertainty_budget`): a table
    with a row per control channel and a note, or, with nothing entered
    for any, one line saying it was not judged."""
    from ..core.requirement import COVERAGE, uncertainty_budget

    if not requirement.uncertainty:
        return [{'kind': 'text', 'text':
                 f'**{UNCERTAINTY}:** not judged; no contributions are '
                 'entered for the control channels.'}]
    budgets = uncertainty_budget(requirement, channels)
    rows, marks = [], []
    for budget in budgets:
        if budget.contributions:
            numbers = [f'{budget.acceleration_percent:.2g} %',
                       f'{budget.asd_percent:.2g} %', f'{budget.db:.2g} dB']
        else:
            numbers = ['—'] * 3
        third = f'{budget.band_limit_db:.2g} dB (±{budget.band_db:g} dB band)'
        if budget.grms_limit_percent is not None:
            third += (f'; {budget.grms_limit_percent:.2g} % '
                      f'(±{requirement.grms_percent:g} % rms)')
        # a reading not judged says so in its own words
        result = (budget.words if budget.state == 'not judged' else
                  budget.state + (f': {budget.words}' if budget.words else ''))
        rows.append([budget.channel,
                     '; '.join(c.text for c in budget.contributions) or '—',
                     *numbers, third, result])
        marks.append([''] * 6 + [MARKS.get(budget.state, '')])
    built: dict[str, Any] = {
        'kind': 'table', 'caption': UNCERTAINTY,
        'headers': ['Channel', 'Contributions', 'Combined, acceleration',
                    'On the ASD', 'In dB', 'One third of the tolerance',
                    'Result'],
        'rows': rows}
    if any(any(mark) for mark in marks):
        built['marks'] = marks
    lines = [(f'- Each contribution is divided by its coverage factor, the '
             f'results combined as the root of the sum of their squares, '
             f'and the total expanded at k = {COVERAGE:g}; in dB it is '
             'taken on the side it is larger.')]
    found = {budget.channel for budget in budgets}
    others = [channel for channel in requirement.uncertainty
              if channel not in found]
    if others:
        lines.append(f'- Contributions are entered for {", ".join(others)}, '
                     f'which {"is" if len(others) == 1 else "are"} not a '
                     'control channel of the run.')
    return [built, {'kind': 'text', 'text': '\n'.join(lines)}]

table

table(results: Any, name: str = '', caption: str = '', register: Sequence[Mapping[str, Any]] | None = None) -> dict[str, Any]

The result table: a row per criterion, its result cell marked by state. With a findings register (the feature findings on), a Finding column is added when any row offers a finding (Criterion.finding): the numbers of the findings its fact went to, or UNENTERED; blank on a row that offers none.

Source code in src/visualdynamics/report/requirement.py
def table(results: Any, name: str = '', caption: str = '',
          register: Sequence[Mapping[str, Any]] | None = None
          ) -> dict[str, Any]:
    """The result table: a row per criterion, its result cell marked by
    state. With a findings `register` (the feature `findings` on), a
    Finding column is added when any row offers a finding
    (`Criterion.finding`): the numbers of the findings its fact went
    to, or `UNENTERED`; blank on a row that offers none."""
    findings = register is not None and any(
        row.finding is not None for row in results.rows)
    headers = ['Criterion', 'Requirement', 'Measured', 'Result']
    if findings:
        headers.append('Finding')
    rows, marks = [], []
    for row in results.rows:
        cells = [row.name, row.required, row.measured, row.state]
        mark = ['', '', '', MARKS.get(row.state, '')]
        if findings:
            cells.append(_finding_cell(row.finding, register))
            mark.append('')
        rows.append(cells)
        marks.append(mark)
    said = caption or CAPTION
    where = ', '.join(part for part in (
        name, results.requirement.document_text) if part)
    built: dict[str, Any] = {'kind': 'table',
                             'caption': f'{said}: {where}' if where else said,
                             'headers': headers, 'rows': rows}
    if any(any(mark) for mark in marks):
        built['marks'] = marks
    return built

fact_draft

fact_draft(row: Any, results: Any, measured: str, specification: str, provenance: Mapping[str, Mapping[str, Any]] | None = None) -> Any

The report.findings.FactDraft a row of the result table offers: its fact (Criterion.finding), the record it was read off and the time history that record came from where provenance (the project's) says, and the scale the run was compared at. The time history, or else the record, is the run; the channel and band are the offer's.

Raises ValueError for a row that offers no finding.

Source code in src/visualdynamics/report/requirement.py
def fact_draft(row: Any, results: Any, measured: str, specification: str,
               provenance: Mapping[str, Mapping[str, Any]] | None = None
               ) -> Any:
    """The `report.findings.FactDraft` a row of the result table offers:
    its fact (`Criterion.finding`), the record it was read off and the
    time history that record came from where `provenance` (the
    project's) says, and the scale the run was compared at. The time
    history, or else the record, is the run; the channel and band are
    the offer's.

    Raises `ValueError` for a row that offers no finding."""
    from .findings import FactDraft

    offer = row.finding
    if offer is None:
        raise ValueError(f'{row.name} offers no finding: only a notch the '
                         'plan does not name and a cross-axis channel over '
                         'its limit do')
    source = ((provenance or {}).get(measured) or {}).get('source')
    record = (f'PSD {measured!r}, from {source!r}' if source
              else f'PSD {measured!r}')
    text = f'{offer.text}; {record}'
    if results.scale_db:
        text += f'; compared at {results.scale_db:+g} dB'
    return FactDraft(
        text=text + '.',
        source=(f'{measured} against {specification}, '
                f'{results.requirement.document_text}, {row.name}'),
        run=str(source or measured), channel=offer.channel,
        band=tuple(offer.band))

draft_finding

draft_finding(report: Any, number: str, objects: Mapping[str, Any], measured: str, specification: str, criterion: int | str, object_groups: Sequence[Mapping[str, Any]] | None = None, *, provenance: Mapping[str, Mapping[str, Any]] | None = None) -> Any

Offer finding number the fact a row of a result table offers, and return the draft (fact_draft).

The row is of the requirement bound to specification and measured, read as the page reads it, and is named by criterion: its number in the table from 1, or its name. The draft goes to the finding through the register's one entry point for drafts (report.findings.offer), so it fills nothing until the author accepts it.

Raises:

Type Description
ValueError

When the report does not have the features test_requirement and findings enabled; for an unknown finding, no requirement bound to the pair, one that cannot be read, a row number out of range, a name no row has, a name two offering rows share, and a row that offers no finding. Nothing is changed.

Source code in src/visualdynamics/report/requirement.py
def draft_finding(report: Any, number: str, objects: Mapping[str, Any],
                  measured: str, specification: str, criterion: int | str,
                  object_groups: Sequence[Mapping[str, Any]] | None = None,
                  *, provenance: Mapping[str, Mapping[str, Any]]
                  | None = None) -> Any:
    """Offer finding `number` the fact a row of a result table offers,
    and return the draft (`fact_draft`).

    The row is of the requirement bound to `specification` and
    `measured`, read as the page reads it, and is named by `criterion`:
    its number in the table from 1, or its name. The draft goes to the
    finding through the register's one entry point for drafts
    (`report.findings.offer`), so it fills nothing until the author
    accepts it.

    Raises
    ------
    ValueError
        When the report does not have the features `test_requirement`
        and `findings` enabled; for an unknown finding, no requirement
        bound to the pair, one that cannot be read, a row number out of
        range, a name no row has, a name two offering rows share, and a
        row that offers no finding. Nothing is changed.
    """
    from ..core.report import resolve_binding
    from . import findings
    from .features import enabled

    if FEATURE not in enabled(report):
        raise ValueError(f'report {getattr(report, "title", "")!r} does not '
                         'have Requirement and results enabled')
    findings.find(report, number)
    pair = (resolve_binding(specification, objects, object_groups),
            resolve_binding(measured, objects, object_groups))
    bound = [block for block in report.blocks if 'requirement' in block
             and tuple(resolve_binding(block.get(key), objects,
                                       object_groups)
                       for key in ('source', 'measured')) == pair]
    if not bound:
        raise ValueError(f'no requirement is bound to {specification!r} '
                         f'and {measured!r}')
    results = read(bound[0], objects, object_groups, roles=roles_on(report))
    if results is None:
        raise ValueError(f'the requirement bound to {specification!r} and '
                         f'{measured!r} cannot be read: a binding does not '
                         'resolve')
    if isinstance(criterion, int) and not isinstance(criterion, bool):
        if not 1 <= criterion <= len(results.rows):
            raise ValueError(f'the result table has rows 1 to '
                             f'{len(results.rows)}, not {criterion}')
        row = results.rows[criterion - 1]
    else:
        named = [row for row in results.rows if row.name == criterion]
        if not named:
            raise ValueError(f'the result table has no row {criterion!r}')
        offering = [row for row in named if row.finding is not None]
        if len(offering) > 1:
            raise ValueError(f'{len(offering)} rows {criterion!r} offer a '
                             'finding; name the row by its number')
        row = offering[0] if offering else named[0]
    draft = fact_draft(row, results, pair[1], pair[0], provenance)
    findings.offer(report, number, draft)
    return draft

matching

matching(report: Any, verdict: Mapping[str, Any], objects: Mapping[str, Any], object_groups: Sequence[Mapping[str, Any]] | None = None) -> Mapping[str, Any] | None

The requirement block the pass/fail block verdict reads from: the first bound to the same specification and run, else the one requirement of a report that holds one (the box of a generated report judges the octave-band pair, the requirement the narrowband run of the same test), else None.

Source code in src/visualdynamics/report/requirement.py
def matching(report: Any, verdict: Mapping[str, Any],
             objects: Mapping[str, Any],
             object_groups: Sequence[Mapping[str, Any]] | None = None
             ) -> Mapping[str, Any] | None:
    """The requirement block the pass/fail block `verdict` reads from:
    the first bound to the same specification and run, else the one
    requirement of a report that holds one (the box of a generated
    report judges the octave-band pair, the requirement the narrowband
    run of the same test), else None."""
    from ..core.report import resolve_binding

    pair = tuple(resolve_binding(verdict.get(key), objects, object_groups)
                 for key in ('source', 'measured'))
    held = [block for block in report.blocks if 'requirement' in block]
    for block in held:
        if tuple(resolve_binding(block.get(key), objects, object_groups)
                 for key in ('source', 'measured')) == pair:
            return block
    return held[0] if len(held) == 1 else None

verdict

verdict(built: dict[str, Any] | None, block: Mapping[str, Any], report: Any, objects: Mapping[str, Any], object_groups: Sequence[Mapping[str, Any]] | None = None) -> dict[str, Any] | None

The pass/fail box built from block, as the feature draws it.

Read from a requirement bound to the same pair when the report has one: 'requirement' carries the result (its state, how many criteria were met of how many, the document, the criteria not met and not judged, and the rule). Otherwise the box keeps its reading and is marked 'screening'. None stays None.

Source code in src/visualdynamics/report/requirement.py
def verdict(built: dict[str, Any] | None, block: Mapping[str, Any],
            report: Any, objects: Mapping[str, Any],
            object_groups: Sequence[Mapping[str, Any]] | None = None
            ) -> dict[str, Any] | None:
    """The pass/fail box `built` from `block`, as the feature draws it.

    Read from a requirement bound to the same pair when the report has
    one: 'requirement' carries the result (its state, how many criteria
    were met of how many, the document, the criteria not met and not
    judged, and the rule). Otherwise the box keeps its reading and is
    marked 'screening'. None stays None.
    """
    if built is None:
        return None
    bound = matching(report, block, objects, object_groups)
    if bound is not None:
        try:
            found = read(bound, objects, object_groups,
                         roles=roles_on(report))
        except (TypeError, ValueError) as refusal:
            return {**built, 'requirement': {
                'state': 'not judged', 'met': 0, 'rows': 0,
                'document': '', 'missed': [], 'unjudged': [str(refusal)],
                'rule': ''}}
        if found is not None:
            requirement = found.requirement
            return {**built, 'requirement': {
                'state': found.state, 'met': found.met,
                'rows': len(found.rows),
                'document': requirement.document_text,
                'missed': [f'{row.name}, {row.required}' for row in found.rows
                           if row.state == 'not met'],
                'unjudged': [f'{row.name}, {row.required}'
                             for row in found.rows
                             if row.state == 'not judged'],
                'rule': requirement.rule_text}}
    return {**built, 'screening': SCREENING}