Skip to content

raesl.excel

Module to export as an Excel workbook.

component_overview

component_overview(
    graph: Graph,
    component_path: str,
    flow_labels: Optional[List[str]] = None,
    output: Optional[Union[str, Path]] = None,
    language: str = "en",
) -> Workbook

Create a component overview Excel sheet. Somewhat like a free body diagram for a single component. Meant to be more discrete but also compact than the complete overview Excel.

Parameters:

Name Type Description Default
graph Graph

Compiled ESL graph.

required
component_path str

ESL specification paths.

required
flow_labels Optional[List[str]]

Flow types to include in sheets.

None
output Optional[Union[str, Path]]

Optional Workbook output path (will be overwritten without warning).

None
language str

Output language.

'en'

Returns:

Type Description
Workbook

Excel workbook instance.

Source code in src/raesl/excel/__init__.py
def component_overview(
    graph: Graph,
    component_path: str,
    flow_labels: Optional[List[str]] = None,
    output: Optional[Union[str, Path]] = None,
    language: str = "en",
) -> Workbook:
    """Create a component overview Excel sheet. Somewhat like a free body diagram for a single
    component. Meant to be more discrete but also compact than the complete overview Excel.

    Arguments:
        graph: Compiled ESL graph.
        component_path: ESL specification paths.
        flow_labels: Flow types to include in sheets.
        output: Optional Workbook output path (will be overwritten without warning).
        language: Output language.

    Returns:
        Excel workbook instance.
    """
    register_locale(language)

    # Create workbook, but delete default sheet.
    wb = Workbook()
    wb.remove(wb["Sheet"])

    try:
        if not component_path.startswith("world."):
            component_path = "world." + component_path
        component = graph[component_path]
    except KeyError:
        logger.error("Could not find component!")
        return

    sheets.add_component_active_goals_sheet(wb, graph, component, flow_labels)
    sheets.add_component_passive_goals_sheet(wb, graph, component, flow_labels)

    if output:
        wb.save(str(output))
    return wb

convert

convert(
    *paths: Union[str, Path],
    output: Optional[Union[str, Path]] = OUTPUT,
    scopes: Dict[str, Optional[int]] = SCOPES,
    language: str = "en"
) -> Workbook

Write (part of) an ESL specification to a Excel workbook.

Parameters:

Name Type Description Default
paths Union[str, Path]

ESL specification paths.

()
output Optional[Union[str, Path]]

Optional Workbook output path (will be overwritten without warning).

OUTPUT
scopes Dict[str, Optional[int]]

Dictionary of component paths to relative depths of subcomponents to include as scopes for the generated output. Defaults to the complete tree.

SCOPES
language str

Output language.

'en'

Returns:

Type Description
Workbook

Excel workbook instance.

Source code in src/raesl/excel/__init__.py
def convert(
    *paths: Union[str, Path],
    output: Optional[Union[str, Path]] = defaults.OUTPUT,
    scopes: Dict[str, Optional[int]] = defaults.SCOPES,
    language: str = "en",
) -> Workbook:
    """Write (part of) an ESL specification to a Excel workbook.

    Arguments:
        paths: ESL specification paths.
        output: Optional Workbook output path (will be overwritten without warning).
        scopes: Dictionary of component paths to relative depths of subcomponents to
            include as scopes for the generated output. Defaults to the complete tree.
        language: Output language.

    Returns:
        Excel workbook instance.
    """

    graph = to_graph(*paths)
    return overview(graph, output, scopes, language=language)

overview

overview(
    graph: Graph,
    output: Optional[Union[str, Path]] = OUTPUT,
    scopes: Dict[str, Optional[int]] = SCOPES,
    language: str = "en",
) -> Workbook

Write (part of) an ESL specification to a Excel workbook.

Parameters:

Name Type Description Default
graph Graph

Compiled ESL graph.

required
output Optional[Union[str, Path]]

Optional Workbook output path (will be overwritten without warning).

OUTPUT
scopes Dict[str, Optional[int]]

Dictionary of component paths to relative depths of subcomponents to include as scopes for the generated output. Defaults to the complete tree.

SCOPES
language str

Output language.

'en'

Returns:

Type Description
Workbook

Excel workbook instance.

Source code in src/raesl/excel/__init__.py
def overview(
    graph: Graph,
    output: Optional[Union[str, Path]] = defaults.OUTPUT,
    scopes: Dict[str, Optional[int]] = defaults.SCOPES,
    language: str = "en",
) -> Workbook:
    """Write (part of) an ESL specification to a Excel workbook.

    Arguments:
        graph: Compiled ESL graph.
        output: Optional Workbook output path (will be overwritten without warning).
        scopes: Dictionary of component paths to relative depths of subcomponents to
            include as scopes for the generated output. Defaults to the complete tree.
        language: Output language.

    Returns:
        Excel workbook instance.
    """
    # Create workbook, but delete default sheet.
    register_locale(language)

    wb = Workbook()
    wb.remove(wb["Sheet"])

    # Derive components from scopes.
    components = [node for node in get_scoped_nodes(graph, scopes) if node.kind == "component"]
    if not components:
        raise ValueError(f"No components found in selected scopes ('{scopes}'). Please reconsider.")

    # Add sheets.
    _, components = sheets.add_components_sheet(wb, components)
    _, goals = sheets.add_goals_sheet(wb, graph, components)
    _, transformations = sheets.add_transformations_sheet(wb, graph, components)
    _, designs = sheets.add_designs_sheet(wb, graph, components)
    _, behaviors = sheets.add_behaviors_sheet(wb, graph, components)
    _, needs = sheets.add_needs_sheet(wb, graph, components)
    _, variables = sheets.add_variable_sheet(wb, graph, components)

    sheets.add_overview_sheet(
        wb, graph, components, goals, transformations, designs, behaviors, needs
    )

    # Protect all sheets.
    for sheet in wb.sheetnames:
        wb[sheet].protection = defaults.SHEETPROTECTION

    # Write output if path is given.
    if output:
        wb.save(str(output))
    return wb

cli

ESL to Excel Command Line Interface.

component_excel

component_excel(
    paths: Iterable[str],
    component: str,
    flow: Iterable[str],
    output: str,
)

Convert ESL files and/or directories to an Excel workbook.

Source code in src/raesl/excel/cli.py
@click.command("component-excel")
@click.argument("paths", nargs=-1, type=click.Path(exists=True, file_okay=True, dir_okay=True))
@click.option("--component", "-c", type=str, help="Path to subject component.")
@click.option(
    "--flow",
    "-f",
    type=str,
    help="Flow types to include. Defaults to all.",
    multiple=True,
    default=[],
)
@click.option(
    "--output",
    "-o",
    default=raesl.excel.defaults.OUTPUT,
    type=click.Path(file_okay=True, dir_okay=False, writable=True),
    help="Output file to write to.",
    show_default=True,
)
def component_excel(paths: Iterable[str], component: str, flow: Iterable[str], output: str):
    """Convert ESL files and/or directories to an Excel workbook."""
    logger.info("This is the Ratio ESL Component-Excel command line utility.")
    logger.info(f"Populating '{output}' with component overview...")
    from raesl.compile import to_graph

    try:
        graph = to_graph(*paths)
        flow_labels = flow if flow else None
        raesl.excel.component_overview(
            graph,
            component,
            flow_labels,
            output=output,
        )
        logger.info("Excel generation done!")
    except Exception as e:
        logger.error(str(e))
        sys.exit(1)

excel

excel(
    paths: Iterable[str],
    output: str,
    scope: Iterable[Tuple[str, int]],
)

Convert ESL files and/or directories to an Excel workbook.

Source code in src/raesl/excel/cli.py
@click.command("excel")
@click.argument("paths", nargs=-1, type=click.Path(exists=True, file_okay=True, dir_okay=True))
@click.option(
    "--output",
    "-o",
    default=raesl.excel.defaults.OUTPUT,
    type=click.Path(file_okay=True, dir_okay=False, writable=True),
    help="Output file to write to.",
    show_default=True,
)
@click.option(
    "--scope",
    "-s",
    default=[("world", -1)],
    type=(str, int),
    multiple=True,
    help=(
        "Scopes in the component hierarchy. "
        "Expects component instance paths and the relative depth you want to include. "
        "A depth of -1 includes everything below that component."
    ),
    show_default=True,
)
def excel(paths: Iterable[str], output: str, scope: Iterable[Tuple[str, int]]):
    """Convert ESL files and/or directories to an Excel workbook."""
    logger.info("This is the Ratio ESL Excel command line utility.")
    logger.info(f"Populating '{output}'...")
    try:
        raesl.excel.convert(
            *paths,
            output=output,
            scopes={s[0]: s[1] if s[1] >= 0 else None for s in scope},
        )
        logger.info("Excel generation done!")
    except Exception as e:
        logger.error(str(e))
        sys.exit(1)

defaults

Default options for ESL to Excel workbook conversion.

sheets

Excel sheet generation.

add_behaviors_sheet

add_behaviors_sheet(
    wb: Workbook, graph: Graph, components: List[Node]
) -> Tuple[Worksheet, List[Node]]

Add a behavior requirements sheet to an Excel workbook.

Parameters:

Name Type Description Default
wb Workbook

Excel workbook to add the behavior requirements sheet to.

required
graph Graph

Graph to fetch behavior nodes from.

required
components List[Node]

Component nodes to fetch behavior requirements for.

required

Returns:

Type Description
Tuple[Worksheet, List[Node]]

Behavior requirements worksheet instance.

Source code in src/raesl/excel/sheets.py
def add_behaviors_sheet(
    wb: Workbook, graph: Graph, components: List[Node]
) -> Tuple[Worksheet, List[Node]]:
    """Add a behavior requirements sheet to an Excel workbook.

    Arguments:
        wb: Excel workbook to add the behavior requirements sheet to.
        graph: Graph to fetch behavior nodes from.
        components: Component nodes to fetch behavior requirements for.

    Returns:
        Behavior requirements worksheet instance.
    """
    ws = wb.create_sheet("Behavior requirements")

    # Select requirements.
    requirements = utils.dedupe(
        [
            r
            for c in components
            for r in doc_utils.get_component_behaviors(c, graph, constraint=False)
        ]
        + [
            r
            for c in components
            for r in doc_utils.get_component_behaviors(c, graph, constraint=True)
        ]
    )

    # Handle headers.
    default_headers = [
        "instance path",
        "component definition",
        "form",
        "case",
        "when",
        "then",
        "comments",
    ]
    tags = utils.get_all_tags(requirements)
    headers = default_headers + tags
    ws.append(headers)
    rows = 1

    # Handle content.
    for requirement in requirements:
        info = requirement.annotations.esl_info
        tagged_content = [
            utils.format_multiline(info["tagged_comments"].get(tag, [])) for tag in tags
        ]
        if info["default"]:
            default_content = [
                node_path(requirement.name, italic=False, arrows=False, skip="world"),
                utils.parent_def(graph, requirement),
                info["form"],
                "default",
                "no other case applies",
                "\n".join(
                    text.designclause_text(clause["body"], graph, label=clause["name"])
                    for clause in info["default"]
                ),
                utils.format_multiline(info["comments"]),
            ]
            ws.append(default_content + tagged_content)
            rows += 1
        for case in info["cases"]:
            default_content = [
                node_path(requirement.name, italic=False, arrows=False, skip="world"),
                utils.parent_def(graph, requirement),
                info["form"],
                case["name"],
                "\n".join(
                    text.designclause_text(clause["body"], graph, label=clause["name"])
                    for clause in case["when_clauses"]
                ),
                "\n".join(
                    text.designclause_text(clause["body"], graph, label=clause["name"])
                    for clause in case["then_clauses"]
                ),
                utils.format_multiline(info["comments"]),
            ]
            ws.append(default_content + tagged_content)
            rows += 1

    # Handle styling.
    utils.make_table(
        ws,
        "behaviors",
        min_row=1,
        max_row=rows,
        min_col=1,
        max_col=len(headers),
    )
    utils.apply_styling(ws, headers, defaults=OPTIONS)
    return ws, requirements

add_components_sheet

add_components_sheet(
    wb: Workbook, components: List[Node]
) -> Tuple[Worksheet, List[Node]]

Add a components overview sheet to an Excel workbook.

Parameters:

Name Type Description Default
wb Workbook

Excel workbook to add the components sheet to.

required
components List[Node]

List of component nodes.

required

Returns:

Type Description
Tuple[Worksheet, List[Node]]

Components worksheet instance.

Source code in src/raesl/excel/sheets.py
def add_components_sheet(wb: Workbook, components: List[Node]) -> Tuple[Worksheet, List[Node]]:
    """Add a components overview sheet to an Excel workbook.

    Arguments:
        wb: Excel workbook to add the components sheet to.
        components: List of component nodes.

    Returns:
        Components worksheet instance.
    """
    ws = wb.create_sheet("Components")

    # Handle headers.
    tags = utils.get_all_tags(components)
    headers = [
        "instance path",
        "component definition",
        "parent component",
        "comments",
    ] + tags
    ws.append(headers)

    # Handle content.
    def write_component_row(ws: Worksheet, tags: List[str], component: Node):
        """Write a component sheet row."""
        info = component.annotations.esl_info
        row = [
            node_path(component.name, italic=False, arrows=False, skip="world"),
            info["definition_name"],
            (
                node_path(component.parent.name, italic=False, arrows=False, skip="world")
                if component.parent
                else None
            ),
            utils.format_multiline(info["comments"]),
        ]
        for tag in tags:
            comments = info["tagged_comments"].get(tag, [])
            row.append(utils.format_multiline(comments))
        ws.append(row)

    for component in components:
        write_component_row(ws, tags, component)

    # Handle styling.
    utils.apply_styling(ws, headers, defaults=OPTIONS)
    utils.make_table(
        ws,
        name="components",
        min_row=1,
        max_row=len(components) + 1,
        min_col=1,
        max_col=len(headers),
    )
    return ws, components

add_designs_sheet

add_designs_sheet(
    wb: Workbook, graph: Graph, components: List[Node]
) -> Tuple[Worksheet, List[Node]]

Add a design requirements sheet to an Excel workbook.

Parameters:

Name Type Description Default
wb Workbook

Excel workbook to add the design requirements sheet to.

required
graph Graph

Graph to fetch designrule nodes from.

required
components List[Node]

Component nodes to fetch design requirements for.

required

Returns:

Type Description
Tuple[Worksheet, List[Node]]

Design requirements worksheet instance.

Source code in src/raesl/excel/sheets.py
def add_designs_sheet(
    wb: Workbook, graph: Graph, components: List[Node]
) -> Tuple[Worksheet, List[Node]]:
    """Add a design requirements sheet to an Excel workbook.

    Arguments:
        wb: Excel workbook to add the design requirements sheet to.
        graph: Graph to fetch designrule nodes from.
        components: Component nodes to fetch design requirements for.

    Returns:
        Design requirements worksheet instance.
    """
    ws = wb.create_sheet("Design requirements")

    # Select requirements.
    requirements = utils.dedupe(
        [r for c in components for r in doc_utils.get_component_designs(c, graph, constraint=False)]
        + [
            r
            for c in components
            for r in doc_utils.get_component_designs(c, graph, constraint=True)
        ]
    )

    # Handle headers.
    default_headers = [
        "instance path",
        "component definition",
        "form",
        "subject",
        "auxiliary",
        "comparison",
        "bound",
        "subclauses",
        "comments",
    ]
    tags = utils.get_all_tags(requirements)
    headers = default_headers + tags
    ws.append(headers)
    rows = 1

    # Handle content.
    for requirement in requirements:
        info = requirement.annotations.esl_info
        for body in info["body"]:
            default_content = [
                node_path(requirement.name, italic=False, arrows=False, skip="world"),
                utils.parent_def(graph, requirement),
                info["form"],
                var_path(graph[body["subject"]], italic=False, arrows=False, skip="world"),
                "{}{}".format("EITHER " if len(info["body"]) > 1 else "", body["auxiliary"]),
                body["comparison"],
                "{} {}".format(body["bound"]["value"], body["bound"]["unit"]),
                text.subclauses_text(requirement, graph, skip="world", spaces=0),
                utils.format_multiline(info["comments"]),
            ]
            tagged_content = [
                utils.format_multiline(info["tagged_comments"].get(tag, [])) for tag in tags
            ]
            ws.append(default_content + tagged_content)
            rows += 1

    # Handle styling.
    utils.make_table(
        ws,
        "designrequirements",
        min_row=1,
        max_row=rows,
        min_col=1,
        max_col=len(headers),
    )
    utils.apply_styling(ws, headers, defaults=OPTIONS)
    return ws, requirements

add_goals_sheet

add_goals_sheet(
    wb: Workbook, graph: Graph, components: List[Node]
) -> Tuple[Worksheet, List[Node]]

Add a goal requirements sheet to an Excel workbook.

Parameters:

Name Type Description Default
wb Workbook

Excel workbook to add the goals sheet to.

required
graph Graph

Graph to fetch goals from.

required
components List[Node]

Component nodes to fetch goals for.

required

Returns:

Type Description
Tuple[Worksheet, List[Node]]

Goal requirements worksheet instance.

Source code in src/raesl/excel/sheets.py
def add_goals_sheet(
    wb: Workbook, graph: Graph, components: List[Node]
) -> Tuple[Worksheet, List[Node]]:
    """Add a goal requirements sheet to an Excel workbook.

    Arguments:
        wb: Excel workbook to add the goals sheet to.
        graph: Graph to fetch goals from.
        components: Component nodes to fetch goals for.

    Returns:
        Goal requirements worksheet instance.
    """
    ws = wb.create_sheet("Goals")

    # Get requirements.
    requirements = utils.dedupe(
        [
            r
            for c in components
            for r in doc_utils.get_component_goals(c, graph, constraint=False, inherited=False)
        ]
        + [
            r
            for c in components
            for r in doc_utils.get_component_goals(c, graph, constraint=False, inherited=True)
        ]
        + [
            r
            for c in components
            for r in doc_utils.get_component_goals(c, graph, constraint=True, inherited=False)
        ]
        + [
            r
            for c in components
            for r in doc_utils.get_component_goals(c, graph, constraint=True, inherited=True)
        ]
    )

    # Handle headers.
    default_headers = [
        "instance path",
        "component definition",
        "form",
        "source component",
        "auxiliary",
        "verb",
        "variables",
        "preposition",
        "target component",
        "subclauses",
        "comments",
    ]
    tags = utils.get_all_tags(requirements)
    headers = default_headers + tags
    ws.append(headers)

    # Handle content.
    for requirement in requirements:
        info = requirement.annotations.esl_info
        body = info["body"]

        default_content = [
            node_path(requirement.name, italic=False, arrows=False, skip="world"),
            utils.parent_def(graph, requirement),
            info["form"],
            node_path(body["active"], italic=False, arrows=False, skip="world"),
            body["auxiliary"],
            body["verb"],
            ", ".join(
                var_path(
                    graph[var],
                    italic=False,
                    arrows=False,
                    skip="world",
                )
                for var in body["variables"]
            ),
            body["preposition"],
            node_path(body["passive"], italic=False, arrows=False, skip="world"),
            text.subclauses_text(requirement, graph, skip="world", spaces=0),
            utils.format_multiline(info["comments"]),
        ]
        tagged_content = [
            utils.format_multiline(info["tagged_comments"].get(tag, [])) for tag in tags
        ]
        ws.append(default_content + tagged_content)

    # Handle styling.
    utils.make_table(
        ws,
        "goals",
        min_row=1,
        max_row=1 + len(requirements),
        min_col=1,
        max_col=len(headers),
    )
    utils.apply_styling(ws, headers, defaults=OPTIONS)
    return ws, requirements

add_needs_sheet

add_needs_sheet(
    wb: Workbook, graph: Graph, components: List[Node]
) -> Tuple[Worksheet, List[Node]]

Add a needs sheet to an Excel workbook.

Parameters:

Name Type Description Default
wb Workbook

Excel workbook to add the needs sheet to.

required
graph Graph

Graph to fetch need nodes from.

required
components List[Node]

Component nodes to fetch needs for.

required

Returns:

Type Description
Tuple[Worksheet, List[Node]]

Needs worksheet instance.

Source code in src/raesl/excel/sheets.py
def add_needs_sheet(
    wb: Workbook, graph: Graph, components: List[Node]
) -> Tuple[Worksheet, List[Node]]:
    """Add a needs sheet to an Excel workbook.

    Arguments:
        wb: Excel workbook to add the needs sheet to.
        graph: Graph to fetch need nodes from.
        components: Component nodes to fetch needs for.

    Returns:
        Needs worksheet instance.
    """
    ws = wb.create_sheet("Needs")

    # Select requirements.
    requirements = utils.dedupe(
        r for c in components for r in doc_utils.get_component_needs(c, graph)
    )

    # Handle headers.
    default_headers = [
        "instance path",
        "component definition",
        "subject",
        "text",
    ]
    tags = utils.get_all_tags(requirements)
    headers = default_headers + tags
    ws.append(headers)

    # Handle content.
    for requirement in requirements:
        info = requirement.annotations.esl_info
        default_content = [
            requirement.name.split(".")[-1],
            utils.parent_def(graph, requirement),
            var_path(graph[info["subject"]], italic=False, arrows=False, skip="world"),
            info["text"],
            utils.format_multiline(info["comments"]),
        ]
        tagged_content = [
            utils.format_multiline(info["tagged_comments"].get(tag, [])) for tag in tags
        ]
        ws.append(default_content + tagged_content)

    # Handle styling.
    utils.make_table(
        ws,
        "needs",
        min_row=1,
        max_row=1 + len(requirements),
        min_col=1,
        max_col=len(headers),
    )
    utils.apply_styling(ws, headers, defaults=OPTIONS)
    return ws, requirements

add_overview_sheet

add_overview_sheet(
    wb: Workbook,
    graph: Graph,
    components: List[Node],
    goals: List[Node],
    transformations: List[Node],
    designs: List[Node],
    behaviors: List[Node],
    needs: List[Node],
) -> Worksheet

Add an overview sheet to an Excel workbook.

Parameters:

Name Type Description Default
wb Workbook

Excel workbook to add the needs sheet to.

required
components List[Node]

Component nodes.

required
goals List[Node]

Goal requirement nodes.

required
transformations List[Node]

Transformation requirement nodes.

required
designs List[Node]

Design requirement nodes.

required
behaviors List[Node]

Behavior requirement nodes.

required
needs List[Node]

Need nodes.

required

Returns:

Type Description
Worksheet

Overview worksheet instance.

Source code in src/raesl/excel/sheets.py
def add_overview_sheet(
    wb: Workbook,
    graph: Graph,
    components: List[Node],
    goals: List[Node],
    transformations: List[Node],
    designs: List[Node],
    behaviors: List[Node],
    needs: List[Node],
) -> Worksheet:
    """Add an overview sheet to an Excel workbook.

    Arguments:
        wb: Excel workbook to add the needs sheet to.
        components: Component nodes.
        goals: Goal requirement nodes.
        transformations: Transformation requirement nodes.
        designs: Design requirement nodes.
        behaviors: Behavior requirement nodes.
        needs: Need nodes.

    Returns:
        Overview worksheet instance.
    """
    ws = wb.create_sheet("Overview", index=0)

    # Select requirements.
    requirements = goals + transformations + designs + behaviors + needs

    # Handle headers.
    default_headers = [
        "instance name",
        "specification text",
        "component path",
        "component definition",
        "kind",
        "form",
        "comments",
    ]
    tags = utils.get_all_tags(components + requirements)
    headers = default_headers + tags
    ws.append(headers)

    # Handle content.
    for requirement in requirements:
        info = requirement.annotations.esl_info
        skip = utils.parent_component(requirement, skip=None)
        default_content = [
            requirement.name.split(".")[-1],
            text.requirement_text(requirement, graph, skip=skip),
            utils.parent_component(requirement, skip="world"),
            utils.parent_def(graph, requirement),
            utils.requirement_kind(requirement),
            None if requirement.kind == "need" else info["form"],
            utils.format_multiline(info["comments"]),
        ]
        tagged_content = [
            utils.format_multiline(info["tagged_comments"].get(tag, [])) for tag in tags
        ]
        ws.append(default_content + tagged_content)

    utils.make_table(
        ws,
        "overview",
        min_row=1,
        max_row=1 + len(requirements),
        min_col=1,
        max_col=len(headers),
    )
    utils.apply_styling(ws, headers, defaults=OPTIONS)

    return ws

add_transformations_sheet

add_transformations_sheet(
    wb: Workbook, graph: Graph, components: List[Node]
) -> Tuple[Worksheet, List[Node]]

Add a transformation requirements sheet to an Excel workbook.

Parameters:

Name Type Description Default
wb Workbook

Excel workbook to add the transformations sheet to.

required
graph Graph

Graph to fetch transformation nodes from.

required
components List[Node]

Component nodes to fetch transformations for.

required

Returns:

Type Description
Tuple[Worksheet, List[Node]]

Transformation requirements worksheet instance.

Source code in src/raesl/excel/sheets.py
def add_transformations_sheet(
    wb: Workbook, graph: Graph, components: List[Node]
) -> Tuple[Worksheet, List[Node]]:
    """Add a transformation requirements sheet to an Excel workbook.

    Arguments:
        wb: Excel workbook to add the transformations sheet to.
        graph: Graph to fetch transformation nodes from.
        components: Component nodes to fetch transformations for.

    Returns:
        Transformation requirements worksheet instance.
    """
    ws = wb.create_sheet("Transformations")

    # Select requirements.
    requirements = utils.dedupe(
        [
            r
            for c in components
            for r in doc_utils.get_component_transformations(c, graph, constraint=False)
        ]
        + [
            r
            for c in components
            for r in doc_utils.get_component_transformations(c, graph, constraint=True)
        ]
    )

    # Handle headers.
    default_headers = [
        "instance path",
        "component definition",
        "form",
        "auxiliary",
        "verb",
        "input_variables",
        "preposition",
        "output_variables",
        "subclauses",
        "comments",
    ]
    tags = utils.get_all_tags(requirements)
    headers = default_headers + tags
    ws.append(headers)

    for requirement in requirements:
        info = requirement.annotations.esl_info
        body = info["body"]

        default_content = [
            node_path(requirement.name, italic=False, arrows=False, skip="world"),
            utils.parent_def(graph, requirement),
            info["form"],
            body["auxiliary"],
            body["verb"],
            ", ".join(
                var_path(graph[var], italic=False, arrows=False, skip="world")
                for var in body["input_variables"]
            ),
            body["preposition"],
            ", ".join(
                var_path(graph[var], italic=False, arrows=False, skip="world")
                for var in body["output_variables"]
            ),
            text.subclauses_text(requirement, graph, skip="world", spaces=0),
            utils.format_multiline(info["comments"]),
        ]
        tagged_content = [
            utils.format_multiline(info["tagged_comments"].get(tag, [])) for tag in tags
        ]
        ws.append(default_content + tagged_content)

    # Handle styling.
    utils.make_table(
        ws,
        "tranformations",
        min_row=1,
        max_row=1 + len(requirements),
        min_col=1,
        max_col=len(headers),
    )
    utils.apply_styling(ws, headers, defaults=OPTIONS)

    return ws, requirements

add_variable_sheet

add_variable_sheet(
    wb: Workbook, graph: Graph, components: List[Node]
) -> Tuple[Worksheet, List[Node]]

Add a variable sheet to an Excel workbook.

Parameters:

Name Type Description Default
wb Workbook

Excel workbook to add the needs sheet to.

required
graph Graph

Graph to fetch need nodes from.

required
components List[Node]

Component nodes to fetch needs for.

required

Returns:

Type Description
Tuple[Worksheet, List[Node]]

Needs worksheet instance.

Source code in src/raesl/excel/sheets.py
def add_variable_sheet(
    wb: Workbook, graph: Graph, components: List[Node]
) -> Tuple[Worksheet, List[Node]]:
    """Add a variable sheet to an Excel workbook.

    Arguments:
        wb: Excel workbook to add the needs sheet to.
        graph: Graph to fetch need nodes from.
        components: Component nodes to fetch needs for.

    Returns:
        Needs worksheet instance.
    """
    ws = wb.create_sheet("Variables")

    # Variables
    vrs = [n for n in graph.nodes if n.kind == "variable"]
    vrs = sorted(vrs, key=lambda x: x.name)

    # Handle headers.
    default_headers = ["instance path", "type", "domain", "units", "clarifaction"]
    tags = utils.get_all_tags(vrs)
    headers = default_headers + tags
    ws.append(headers)

    # Handle content.
    for var in vrs:
        info = var.annotations.esl_info
        default_content = list(get_var_table_row_elements(graph, var))
        tagged_content = [
            utils.format_multiline(info["tagged_comments"].get(tag, [])) for tag in tags
        ]
        ws.append(default_content + tagged_content)

    # Handle styling.
    utils.make_table(
        ws,
        "variables",
        min_row=1,
        max_row=1 + len(vrs),
        min_col=1,
        max_col=len(headers),
    )
    utils.apply_styling(ws, headers, defaults=OPTIONS)
    return ws, vrs

text

Raw text export of ESL requirement nodes.

abbreviate_comparison

abbreviate_comparison(comp_str: str) -> str

Use symbols instead of full text for comparison text.

Source code in src/raesl/excel/text.py
def abbreviate_comparison(comp_str: str) -> str:
    """Use symbols instead of full text for comparison text."""
    if comp_str == "equal to":
        return '="=="'
    elif comp_str == "at most":
        return '="<="'
    elif comp_str == "at least":
        return '=">="'
    elif comp_str == "smaller than":
        return '="<"'
    elif comp_str == "greater than":
        return '=">"'
    elif comp_str == "maximized":
        return '="++"'
    elif comp_str == "minimized":
        return '="--"'
    else:
        return comp_str

behavior_text

behavior_text(
    requirement: Node,
    graph: Graph,
    skip: Optional[str] = "world",
) -> str

Re-format behavior requirement as text.

Source code in src/raesl/excel/text.py
def behavior_text(requirement: Node, graph: Graph, skip: Optional[str] = "world") -> str:
    """Re-format behavior requirement as text."""
    info = requirement.annotations.esl_info

    text = "\n\n".join(case_text(case, graph, skip=skip) for case in info["cases"])
    if info["default"]:
        text += "\n\nwhen no other case applies:\n{}".format(
            indent(
                "\n".join(
                    designclause_text(clause["body"], graph, label=clause["name"], skip=skip)
                    for clause in info["default"]
                ),
                prefix=4 * " ",
            ),
        )
    return text

case_text

case_text(
    case: Dict[str, Any], graph: Graph, skip: str = "world"
) -> str

Re-format behavior requirement case as text.

Source code in src/raesl/excel/text.py
def case_text(case: Dict[str, Any], graph: Graph, skip: str = "world") -> str:
    """Re-format behavior requirement case as text."""
    return "{name}:\n  when:\n{whens}\n  then:\n{thens}".format(
        name=case["name"],
        whens=indent(
            "\n".join(
                designclause_text(clause["body"], graph, label=clause["name"], skip=skip)
                for clause in case["when_clauses"]
            ),
            prefix=4 * " ",
        ),
        thens=indent(
            "\n".join(
                designclause_text(clause["body"], graph, label=clause["name"], skip=skip)
                for clause in case["then_clauses"]
            ),
            prefix=4 * " ",
        ),
    )

design_text

design_text(
    requirement: Node,
    graph: Graph,
    skip: Optional[str] = "world",
) -> str

Re-format design requirement as text.

Source code in src/raesl/excel/text.py
def design_text(requirement: Node, graph: Graph, skip: Optional[str] = "world") -> str:
    """Re-format design requirement as text."""
    return designclause_text(requirement.annotations.esl_info["body"], graph, skip=skip)

designclause_text

designclause_text(
    bodies: List[Dict[str, Any]],
    graph: Graph,
    label: Optional[str] = None,
    skip: Optional[str] = "world",
) -> str

Re-format design clause as text.

Source code in src/raesl/excel/text.py
def designclause_text(
    bodies: List[Dict[str, Any]],
    graph: Graph,
    label: Optional[str] = None,
    skip: Optional[str] = "world",
) -> str:
    """Re-format design clause as text."""
    rule = " or ".join(designrule_text(rule, graph, skip=skip) for rule in bodies)
    return "{}: {}".format(label, rule) if label else rule

designrule_text

designrule_text(
    body: Dict[str, Any],
    graph: Graph,
    skip: Optional[str] = "world",
) -> str

Re-format design rule as text.

Source code in src/raesl/excel/text.py
def designrule_text(
    body: Dict[str, Any],
    graph: Graph,
    skip: Optional[str] = "world",
) -> str:
    """Re-format design rule as text."""
    return "{subject} {auxiliary} {comparison} {value} {unit}".format(
        subject=lines.var_path(graph[body["subject"]], italic=False, arrows=False, skip=skip),
        auxiliary=body["auxiliary"],
        comparison=body["comparison"],
        value=body["bound"]["value"],
        unit=body["bound"]["unit"],
    )

get_common_parts

get_common_parts(strings: Iterable[str]) -> List[str]

Find out the largest shared substrings separated on dots '.'.

Source code in src/raesl/excel/text.py
def get_common_parts(strings: Iterable[str]) -> List[str]:
    """Find out the largest shared substrings separated on dots '.'."""
    result = []
    # Get dotted string splits, reversed so later pops start at the first part.
    splits = [s.split(".")[::-1] for s in strings]
    if len(splits) == 1:
        return splits[0][::-1]
    while True:
        try:
            parts = [s.pop() for s in splits]
            head = parts[0]
            for p in parts[1:]:
                if p != head:
                    return result
            result.append(head)
        except IndexError:
            return result

goal_text

goal_text(
    requirement: Node,
    graph: Graph,
    skip: Optional[str] = "world",
) -> str

Re-format goal requirement as text.

Source code in src/raesl/excel/text.py
def goal_text(requirement: Node, graph: Graph, skip: Optional[str] = "world") -> str:
    """Re-format goal requirement as text."""
    body = requirement.annotations.esl_info["body"]
    text = "{active} {auxiliary} {verb} {variables} {preposition} {passive}".format(
        active=lines.node_path(body["active"], italic=False, arrows=False, skip=skip),
        auxiliary=body["auxiliary"],
        verb=body["verb"],
        variables=", ".join(
            lines.var_path(graph[var], italic=False, arrows=False, skip=skip)
            for var in body["variables"]
        ),
        preposition=body["preposition"],
        passive=lines.node_path(body["passive"], italic=False, arrows=False, skip=skip),
    )
    return text

need_text

need_text(
    requirement: Node,
    graph: Graph,
    skip: Optional[str] = "world",
) -> str

Re-format need as text.

Source code in src/raesl/excel/text.py
def need_text(requirement: Node, graph: Graph, skip: Optional[str] = "world") -> str:
    """Re-format need as text."""
    info = requirement.annotations.esl_info
    text = ("{subject} {text}").format(
        subject=lines.var_path(graph[info["subject"]], italic=False, arrows=False, skip=skip),
        text=info["text"],
    )
    return text

requirement_text

requirement_text(
    requirement: Node,
    graph: Graph,
    skip: Optional[str] = "world",
) -> str

Re-format the requirement as text.

Source code in src/raesl/excel/text.py
def requirement_text(requirement: Node, graph: Graph, skip: Optional[str] = "world") -> str:
    """Re-format the requirement as text."""
    info = requirement.annotations.esl_info
    if requirement.kind == "function_spec":
        if info["sub_kind"] == "goal":
            func = goal_text
        elif info["sub_kind"] == "transformation":
            func = transformation_text
        else:
            raise ValueError(f"Unknown sub kind for function spec: {info['sub_kind']}.")
    elif requirement.kind == "design_spec":
        func = design_text
    elif requirement.kind == "behavior_spec":
        func = behavior_text
    elif requirement.kind == "need":
        func = need_text
    else:
        raise ValueError(f"Unknown requirement type of node {requirement.json_dict}.")

    text = func(requirement, graph, skip=skip)

    if (
        requirement.kind == "function_spec"
        and info["body"].get("subclauses")
        or requirement.kind == "design_spec"
        and info["sub_clauses"]
    ):
        return "{}, with subclauses:\n{}".format(
            text, subclauses_text(requirement, graph, skip=skip, spaces=2)
        )
    else:
        return text

strip_prefix

strip_prefix(input: str, prefix: str) -> str

Strip a prefix from a string if it starts with it.

Source code in src/raesl/excel/text.py
def strip_prefix(input: str, prefix: str) -> str:
    """Strip a prefix from a string if it starts with it."""
    if input.startswith(prefix):
        return input[len(prefix) :]
    return input

subclauses_text

subclauses_text(
    requirement: Node,
    graph: Graph,
    skip: Optional[str] = "world",
    spaces: int = 2,
) -> str

Re-format subclauses as text.

Source code in src/raesl/excel/text.py
def subclauses_text(
    requirement: Node, graph: Graph, skip: Optional[str] = "world", spaces: int = 2
) -> str:
    """Re-format subclauses as text."""
    if requirement.kind == "function_spec":
        clauses = requirement.annotations.esl_info["body"]["subclauses"]
    elif requirement.kind == "design_spec":
        clauses = requirement.annotations.esl_info["sub_clauses"]
    else:
        raise ValueError("Unsupported node kind '{}'.".format(requirement.kind))
    return "\n".join(
        indent(
            designclause_text(clause["body"], graph, label=clause["name"], skip=skip),
            prefix="{}{}".format(spaces * " ", "- " if spaces else ""),
        )
        for clause in clauses
    )

transformation_text

transformation_text(
    requirement: Node,
    graph: Graph,
    skip: Optional[str] = "world",
) -> str

Re-format transformation requirement as text.

Source code in src/raesl/excel/text.py
def transformation_text(requirement: Node, graph: Graph, skip: Optional[str] = "world") -> str:
    """Re-format transformation requirement as text."""
    body = requirement.annotations.esl_info["body"]
    text = ("{auxiliary} {verb} {input_variables} {preposition} {output_variables}").format(
        auxiliary=body["auxiliary"],
        verb=body["verb"],
        input_variables=", ".join(
            lines.var_path(graph[var], italic=False, arrows=False, skip=skip)
            for var in body["input_variables"]
        ),
        preposition=body["preposition"],
        output_variables=", ".join(
            lines.var_path(graph[var], italic=False, arrows=False, skip=skip)
            for var in body["output_variables"]
        ),
    )
    return text

utils

Excel export utility methods.

apply_styling

apply_styling(
    ws: Worksheet,
    headers: List[str],
    defaults: Dict[str, Any] = dict(),
    start_row: int = 0,
    end_row: Optional[int] = None,
)

Apply styling to columns given some default option dictionary.

Source code in src/raesl/excel/utils.py
def apply_styling(
    ws: Worksheet,
    headers: List[str],
    defaults: Dict[str, Any] = dict(),
    start_row: int = 0,
    end_row: Optional[int] = None,
):
    """Apply styling to columns given some default option dictionary."""
    for i, header in enumerate(headers):
        styles = defaults.get(header, dict()).get("styles", dict())
        for i, row in enumerate(ws.rows):
            if i < start_row:
                continue
            if end_row is not None and i == end_row:
                break
            for cell in row:
                cell.font = styles.get("font", "monospace")

    dims = {}
    for i, row in enumerate(ws.rows):
        if i < start_row:
            continue
        if end_row is not None and i == end_row:
            break
        if i == start_row:
            continue

        for cell in row:
            if cell.value:
                dims[cell.column_letter] = max(
                    (dims.get(cell.column_letter, 0), len(str(cell.value)))
                )
    for col, value in dims.items():
        ws.column_dimensions[col].width = 1.5 * value

build_active_goal_data

build_active_goal_data(
    graph: Graph,
    edge: Edge,
    goal: Node,
    function_specifications: Set[Node] = set(),
) -> Generator[Dict[str, Any], None, None]

Create data dictionaries for the outgoing goals in the component overview.

Keys
Source code in src/raesl/excel/utils.py
def build_active_goal_data(
    graph: Graph, edge: Edge, goal: Node, function_specifications: Set[Node] = set()
) -> Generator[Dict[str, Any], None, None]:
    """Create data dictionaries for the outgoing goals in the component overview.

    Keys:
        label: Goal label.
        target: Target component (shared path skipped).
        flows: Sent flow variables (shared path skipped).
        types: Flow variable types.
        subclause: Subclause label, optionally prefixed with numbers for OR-concatenations.
        subject: Subclause subject with prefix skipped.
        comparison: Abbreviated comparison (<, <=, ==, >=, >, ++, --).
        bound: Comparison variable or value.
        unit: Comparison value unit.
    """
    label = goal.name
    active = get_goal_active(goal)
    target = edge.target.name

    flows = get_function_flow_variables(goal, edge, function_specifications)
    types = set([get_variable_type(graph[f]) for f in flows if graph.node_dict.get(f, False)])
    types = ", ".join(types)
    subclauses = get_function_subclauses(goal)

    # Skip everything in common, except the last part, the flow's own name.
    active_skip = active[: active.rfind(".") + 1] if active else ""
    stripped_flows = [text.strip_prefix(f, active_skip) for f in flows]
    display_flows = ", ".join(stripped_flows)

    common_parts = text.get_common_parts(flows)
    skip_prefix = ".".join(common_parts[:-1])

    label = text.strip_prefix(label, active_skip)
    target = text.strip_prefix(target, active_skip)

    if not subclauses:
        data = dict(label=label, target=target, flows=display_flows, types=types)
        yield data

    for sc in subclauses:
        for sc_data in build_subclause_data(sc, skip_prefix):
            data = dict(label=label, target=target, flows=display_flows, types=types, **sc_data)
            yield data

build_passive_goal_data

build_passive_goal_data(
    graph: Graph,
    edge: Edge,
    goal: Node,
    function_specifications: Set[Node],
) -> Generator[Dict[str, Any], None, None]

Create data dictionaries for the incoming goals in the component overview.

Keys
Source code in src/raesl/excel/utils.py
def build_passive_goal_data(
    graph: Graph, edge: Edge, goal: Node, function_specifications: Set[Node]
) -> Generator[Dict[str, Any], None, None]:
    """Create data dictionaries for the incoming goals in the component overview.

    Keys:
        label: Goal label.
        target: Target component (shared path skipped).
        flows: Sent flow variables (shared path skipped).
        types: Flow variable types.
        subclause: Subclause label, optionally prefixed with numbers for OR-concatenations.
        subject: Subclause subject with prefix skipped.
        comparison: Abbreviated comparison (<, <=, ==, >=, >, ++, --).
        bound: Comparison variable or value.
        unit: Comparison value unit.
    """
    label = goal.name
    active = edge.target.name
    passive = get_goal_passive(goal)
    flows = get_function_flow_variables(goal, passive, function_specifications)
    types = set([get_variable_type(graph[f]) for f in flows if graph.node_dict.get(f, False)])
    types = ", ".join(types)
    subclauses = get_function_subclauses(goal)

    # Skip everything in common, except the last part, the flow's own name.
    passive_skip = passive[: passive.rfind(".") + 1] if passive else ""
    stripped_flows = [text.strip_prefix(f, passive_skip) for f in flows]
    display_flows = ", ".join(stripped_flows)

    common_parts = text.get_common_parts(flows)
    skip_prefix = ".".join(common_parts[:-1])

    label = text.strip_prefix(label, passive_skip)
    active = text.strip_prefix(active, passive_skip)

    if not subclauses:
        data = dict(label=label, source=active, flows=display_flows, types=types)
        yield data

    for sc in subclauses:
        for sc_data in build_subclause_data(sc, skip_prefix):
            data = dict(label=label, source=active, flows=display_flows, types=types, **sc_data)
            yield data

build_subclause_body_data

build_subclause_body_data(
    subclause_body: Dict[str, Any], skip_prefix: str
) -> Dict[str, Any]

Create a subclause's body data dict for use in the component overview.

Keys
Source code in src/raesl/excel/utils.py
def build_subclause_body_data(subclause_body: Dict[str, Any], skip_prefix: str) -> Dict[str, Any]:
    """Create a subclause's body data dict for use in the component overview.

    Keys:
        subject: Subclause subject with prefix skipped.
        comparison: Abbreviated comparison (<, <=, ==, >=, >, ++, --).
        bound: Comparison variable or value.
        unit: Comparison value unit.
    """
    result = dict()

    subject = subclause_body.get("subject", "")
    result["subject"] = text.strip_prefix(subject, skip_prefix)

    comparison = subclause_body.get("comparison", None)
    if comparison is not None:
        result["comparison"] = text.abbreviate_comparison(comparison)

    bound = subclause_body.get("bound", None)
    if isinstance(bound, str):  # Other variable
        result["bound"] = text.strip_prefix(bound, skip_prefix)
    elif isinstance(bound, dict):  # value / unit
        result["bound"] = bound.get("value", None)
        result["unit"] = bound.get("unit", None).strip("[]")

    return result

build_subclause_data

build_subclause_data(
    subclause_info: Dict[str, Any], skip_prefix: str
) -> Generator[Dict[str, Any], None, None]

Create a subclause data dictionary for each OR clause for use in the component overview.

Keys
Source code in src/raesl/excel/utils.py
def build_subclause_data(
    subclause_info: Dict[str, Any], skip_prefix: str
) -> Generator[Dict[str, Any], None, None]:
    """Create a subclause data dictionary for each OR clause for use in the component overview.

    Keys:
        subclause: Subclause label, optionally prefixed with numbers for OR-concatenations.
        subject: Subclause subject with prefix skipped.
        comparison: Abbreviated comparison (<, <=, ==, >=, >, ++, --).
        bound: Comparison variable or value.
        unit: Comparison value unit.
    """
    name = subclause_info.get("name", None)
    if name is None:
        return  # No label, no joy.
    bodies = subclause_info.get("body", [])  # concatenated OR clauses
    for i, body in enumerate(bodies):
        if len(bodies) == 1:
            subclause = name
        else:
            subclause = f"OR-{i}-{name}"
        data = build_subclause_body_data(body, skip_prefix)
        data["subclause"] = subclause
        yield data

dedupe

dedupe(iterable: Iterable) -> List[Any]

Deduplicate any iterable into a list where the first occurrence is preserved.

Source code in src/raesl/excel/utils.py
def dedupe(iterable: Iterable) -> List[Any]:
    """Deduplicate any iterable into a list where the first occurrence is preserved."""
    seen: Set[Any] = set()
    unique: List[Any] = list()
    for item in iterable:
        if item in seen:
            continue
        seen.add(item)
        unique.append(item)
    return unique

format_multiline

format_multiline(comments: List[str]) -> str

Format multiline (list) text.

Source code in src/raesl/excel/utils.py
def format_multiline(comments: List[str]) -> str:
    """Format multiline (list) text."""
    return "\n".join(c.rstrip("\\") for c in comments)

get_all_tags

get_all_tags(nodes: List[Node]) -> List[str]

Get all tagged comment keys from a list of nodes.

Source code in src/raesl/excel/utils.py
def get_all_tags(nodes: List[Node]) -> List[str]:
    """Get all tagged comment keys from a list of nodes."""
    tags: List[str] = []
    seen: Set[str] = set()
    for node in nodes:
        node_tags = [
            tag for tag in node.annotations.esl_info["tagged_comments"].keys() if tag not in seen
        ]
        tags.extend(node_tags)
        seen.update(node_tags)
    return tags

get_function_flow_variables

get_function_flow_variables(
    function_specification: Node,
    edge: Edge,
    function_specifications: Set[Node] = set(),
) -> Set[str]

Get the variables involved with a functional dependency.

Source code in src/raesl/excel/utils.py
def get_function_flow_variables(
    function_specification: Node, edge: Edge, function_specifications: Set[Node] = set()
) -> Set[str]:
    """Get the variables involved with a functional dependency."""
    esl_info = function_specification.annotations.get("esl_info", dict())

    transformations = set(
        [
            f
            for (e, f) in function_specifications
            if f.annotations["esl_info"]["sub_kind"] == "transformation"
            and f.name in e.annotations["esl_info"]["reason"]["function_specifications"]
            and f.annotations["esl_info"]["body"]["active"] == edge.target.name
        ]
    )

    goal_vars = set(esl_info.get("body", dict()).get("variables", []))

    if transformations:
        trans_vars = set()
        for t in transformations:
            trans_vars = trans_vars.union(
                set(
                    t.annotations.get("esl_info", dict())
                    .get("body", dict())
                    .get("input_variables", [])
                    + t.annotations.get("esl_info", dict())
                    .get("body", dict())
                    .get("output_variables", [])
                )
            )

        flow_vars = goal_vars.intersection(trans_vars)
        return flow_vars if flow_vars else goal_vars
    else:
        return goal_vars

get_function_specification_names

get_function_specification_names(
    functional_dependency: Edge,
) -> List[str]

Get the function specification node names corresponding to a functional dependency edge.

Source code in src/raesl/excel/utils.py
def get_function_specification_names(functional_dependency: Edge) -> List[str]:
    """Get the function specification node names corresponding to a functional dependency edge."""
    return (
        functional_dependency.annotations.get("esl_info", dict())
        .get("reason", dict())
        .get("function_specifications", [])
    )

get_function_subclauses

get_function_subclauses(
    function_specification: Node,
) -> List[str]

Get the subclauses belonging to a function specification.

Source code in src/raesl/excel/utils.py
def get_function_subclauses(function_specification: Node) -> List[str]:
    """Get the subclauses belonging to a function specification."""
    esl_info = function_specification.annotations.get("esl_info", dict())
    return esl_info.get("body", dict()).get("subclauses", [])

get_goal_active

get_goal_active(
    function_specification: Node,
) -> Optional[str]

Get the active component in a function specification.

Source code in src/raesl/excel/utils.py
def get_goal_active(function_specification: Node) -> Optional[str]:
    """Get the active component in a function specification."""
    esl_info = function_specification.annotations.get("esl_info", dict())
    if esl_info.get("sub_kind", None) != "goal":
        return None
    return esl_info.get("body", dict()).get("active", None)

get_goal_passive

get_goal_passive(
    function_specification: Node,
) -> Optional[str]

Get the receiving component of a goal function.

Source code in src/raesl/excel/utils.py
def get_goal_passive(function_specification: Node) -> Optional[str]:
    """Get the receiving component of a goal function."""
    esl_info = function_specification.annotations.get("esl_info", dict())
    if esl_info.get("sub_kind", None) != "goal":
        return None
    return esl_info.get("body", dict()).get("passive", None)

get_variable_type

get_variable_type(variable: Node) -> str

Get the variable type of a node.

Source code in src/raesl/excel/utils.py
def get_variable_type(variable: Node) -> str:
    """Get the variable type of a node."""
    return variable.annotations.get("esl_info", dict()).get("type_ref", "")

make_table

make_table(
    ws: Worksheet,
    name: str = "Table",
    min_row: int = 1,
    max_row: int = 1,
    min_col: int = 1,
    max_col: int = 1,
    style: TableStyleInfo = TableStyleInfo(
        name="TableStyleMedium9", showRowStripes=True
    ),
) -> Table

Make a table of a cell range in a worksheet.

Source code in src/raesl/excel/utils.py
def make_table(
    ws: Worksheet,
    name: str = "Table",
    min_row: int = 1,
    max_row: int = 1,
    min_col: int = 1,
    max_col: int = 1,
    style: TableStyleInfo = TableStyleInfo(name="TableStyleMedium9", showRowStripes=True),
) -> Table:
    """Make a table of a cell range in a worksheet."""
    ref = CellRange(min_row=min_row, max_row=max_row, min_col=min_col, max_col=max_col)
    table = Table(name=name, displayName=name, ref=ref.coord)
    table.tableStyleInfo = style
    ws.add_table(table)
    return table

parent_component

parent_component(
    requirement: Node, skip: Optional[str] = "world"
) -> str

Get parent component name of a requirement.

Source code in src/raesl/excel/utils.py
def parent_component(requirement: Node, skip: Optional[str] = "world") -> str:
    """Get parent component name of a requirement."""
    path = ".".join(requirement.name.split(".")[:-1])
    return lines.node_path(path, arrows=False, skip=skip) if skip else path

parent_def

parent_def(graph: Graph, requirement: Node) -> str

Get the parent (component) definition of a requirement.

Source code in src/raesl/excel/utils.py
def parent_def(graph: Graph, requirement: Node) -> str:
    """Get the parent (component) definition of a requirement."""
    parent_comp = ".".join(requirement.name.split(".")[:-1])
    parent_def = graph[parent_comp].annotations.esl_info["definition_name"]
    return parent_def

requirement_kind

requirement_kind(requirement: Node) -> str

Get requirement kind.

Source code in src/raesl/excel/utils.py
def requirement_kind(requirement: Node) -> str:
    """Get requirement kind."""
    info = requirement.annotations.esl_info
    if requirement.kind == "function_spec":
        return info["sub_kind"]
    elif requirement.kind == "design_spec":
        return "design"
    elif requirement.kind == "behavior_spec":
        return "behavior"
    else:
        return requirement.kind

yield_active_functions

yield_active_functions(
    function_specifications: Iterable[Node],
    active_path: str,
) -> Generator[Node, None, None]

Yield all edge function combinations for which the active component matches the given path.

Source code in src/raesl/excel/utils.py
def yield_active_functions(
    function_specifications: Iterable[Node], active_path: str
) -> Generator[Node, None, None]:
    """Yield all edge function combinations for which the active component matches the given
    path.
    """
    for n in function_specifications:
        if get_goal_active(n[1]) == active_path:
            yield n

yield_all_function_specifications

yield_all_function_specifications(
    g: Graph, functional_dependencies: Iterable[Edge]
) -> Generator[Tuple[Node, Node], None, None]

Yield all function specification nodes and edge combinations for a given iterable of dependency edges.

Source code in src/raesl/excel/utils.py
def yield_all_function_specifications(
    g: Graph, functional_dependencies: Iterable[Edge]
) -> Generator[Tuple[Node, Node], None, None]:
    """Yield all function specification nodes and edge combinations
    for a given iterable of dependency edges.
    """
    for e in functional_dependencies:
        yield from yield_function_specifications(g, e)

yield_function_specifications

yield_function_specifications(
    g: Graph, functional_dependency: Edge
) -> Generator[Tuple[Node, Node], None, None]

Get all function specification nodes corresponding to a functional dependency edge.

Source code in src/raesl/excel/utils.py
def yield_function_specifications(
    g: Graph, functional_dependency: Edge
) -> Generator[Tuple[Node, Node], None, None]:
    """Get all function specification nodes corresponding to a functional dependency edge."""
    for spec in get_function_specification_names(functional_dependency):
        yield (functional_dependency, g[spec])

yield_functional_dependencies

yield_functional_dependencies(
    g: Graph, component: Node, flows: Optional[Set[str]]
) -> Generator[Edge, None, None]

Yield all functional dependencies with which a component is involved.

Source code in src/raesl/excel/utils.py
def yield_functional_dependencies(
    g: Graph, component: Node, flows: Optional[Set[str]]
) -> Generator[Edge, None, None]:
    """Yield all functional dependencies with which a component is involved."""
    if flows is None:
        for e in g.edges_from(component):
            if e.kind == "functional_dependency":
                yield e
    else:
        for e in g.edges_from(component):
            if e.kind == "functional_dependency" and flows.intersection(e.labels):
                yield e

yield_passive_functions

yield_passive_functions(
    function_specifications: Iterable[Node],
    passive_path: str,
) -> Generator[Tuple[Node, Node], None, None]

Yield all edge function combinations for which the passive component matches the given path.

Source code in src/raesl/excel/utils.py
def yield_passive_functions(
    function_specifications: Iterable[Node], passive_path: str
) -> Generator[Tuple[Node, Node], None, None]:
    """Yield all edge function combinations for which the passive component matches the given
    path.
    """
    for n in function_specifications:
        if get_goal_passive(n[1]) == passive_path:
            yield n