Skip to content

bycomp_interface

trestle.core.crm.bycomp_interface ¤

Provide interface to by-component allowing queries and operations for exports/inheritance statements.

Attributes¤

logger = logging.getLogger(__name__) module-attribute ¤

Classes¤

ByComponentInterface ¤

Interface to query and modify by-component assembly inheritance contents.

The by-component is contained in two separate forms: As an actual OSCAL by-component assembly, and multiple dicts providing direct lookup of inheritance statement by uuid.

The dicts are created by the ByComponentInterface constructor, parsed and the responsibility and provided statements are separated into three catagories:

isolated responsibilities - A responsibility with no provided statement isolated provided - A provided statement with no referring responsibility statements export set - A set with a single responsibility and referred provided statement

For updating ByComponent inheritance and satisfied statements, the interface provides a method to reconcile the the by-component assembly and merge input inherited and satisfied statements.

Source code in trestle/core/crm/bycomp_interface.py
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
class ByComponentInterface:
    """
    Interface to query and modify by-component assembly inheritance contents.

    The by-component is contained in two separate forms:  As an actual OSCAL by-component assembly,
    and multiple dicts providing direct lookup of inheritance statement by uuid.

    The dicts are created by the ByComponentInterface constructor, parsed and the responsibility and provided statements
    are separated into three catagories:

    isolated responsibilities - A responsibility with no provided statement
    isolated provided - A provided statement with no referring responsibility statements
    export set - A set with a single responsibility and referred provided statement

    For updating ByComponent inheritance and satisfied statements, the interface provides a method to reconcile the
    the by-component assembly and merge input inherited and satisfied statements.
    """

    def __init__(self, by_comp: ossp.ByComponent):
        """Initialize export writer for a single by-component assembly."""
        self._by_comp: ossp.ByComponent = by_comp

        self._provided_dict: Dict[str, ossp.Provided] = {}
        self._responsibility_dict: Dict[str, ossp.Responsibility] = {}
        self._responsibility_by_provided: Dict[str, List[ossp.Responsibility]] = {}

        self._inherited_dict: Dict[str, ossp.Inherited] = self._create_inherited_dict()
        self._satisfied_dict: Dict[str, ossp.Satisfied] = self._create_satisfied_dict()

        if by_comp.export:
            self._provided_dict = self._create_provided_dict()
            self._responsibility_dict = self._create_responsibility_dict()
            self._responsibility_by_provided = self._create_responsibility_by_provided_dict()

    def _create_provided_dict(self) -> Dict[str, ossp.Provided]:
        provided_dict: Dict[str, ossp.Provided] = {}
        for provided in as_list(self._by_comp.export.provided):
            provided_dict[provided.uuid] = provided
        return provided_dict

    def _create_responsibility_dict(self) -> Dict[str, ossp.Responsibility]:
        responsibility_dict: Dict[str, ossp.Responsibility] = {}
        for responsibility in as_list(self._by_comp.export.responsibilities):
            responsibility_dict[responsibility.uuid] = responsibility
        return responsibility_dict

    def _create_responsibility_by_provided_dict(self) -> Dict[str, List[ossp.Responsibility]]:
        responsibility_by_provided: Dict[str, List[ossp.Responsibility]] = {}
        for responsibility in as_list(self._by_comp.export.responsibilities):
            if responsibility.provided_uuid is None:
                continue
            if responsibility.provided_uuid not in responsibility_by_provided:
                responsibility_by_provided[responsibility.provided_uuid] = [responsibility]
            else:
                existing_list: List[ossp.Responsibility] = responsibility_by_provided[responsibility.provided_uuid]
                existing_list.append(responsibility)
        return responsibility_by_provided

    def _create_inherited_dict(self) -> Dict[str, ossp.Inherited]:
        inherited_dict: Dict[str, ossp.Inherited] = {}
        for inherited in as_list(self._by_comp.inherited):
            inherited_dict[str(inherited.provided_uuid)] = inherited
        return inherited_dict

    def _create_satisfied_dict(self) -> Dict[str, ossp.Satisfied]:
        satisfied_dict: Dict[str, ossp.Satisfied] = {}
        for satisfied in as_list(self._by_comp.satisfied):
            satisfied_dict[str(satisfied.responsibility_uuid)] = satisfied
        return satisfied_dict

    def get_isolated_responsibilities(self) -> List[ossp.Responsibility]:
        """Return all isolated exported responsibilities."""
        all_responsibilities: List[ossp.Responsibility] = []
        for resp in as_dict(self._responsibility_dict).values():
            if resp.provided_uuid is None:
                all_responsibilities.append(resp)
        return all_responsibilities

    def get_isolated_provided(self) -> List[ossp.Provided]:
        """Return all isolated exported provided capabilities."""
        all_provided: List[ossp.Provided] = []
        for provided in as_dict(self._provided_dict).values():
            if not self._provided_has_responsibilities(provided.uuid):
                all_provided.append(provided)
        return all_provided

    def get_export_sets(self) -> List[Tuple[ossp.Responsibility, ossp.Provided]]:
        """Return a dictionary of every responsibility relationship with provided."""
        all_export_sets: List[Tuple[ossp.Responsibility, ossp.Provided]] = []
        for provided_uuid, responsibilities in as_dict(self._responsibility_by_provided).items():

            # Ensure the provided object exists in the dictionary.
            # If it doesn't this is a bug.
            if provided_uuid not in self._provided_dict:
                raise TrestleError(f'Provided capability {provided_uuid} not found')
            provided: ossp.Provided = self._provided_dict[provided_uuid]

            for responsibility in responsibilities:
                shared_responsibility: Tuple[ossp.Responsibility, ossp.Provided] = (responsibility, provided)
                all_export_sets.append(shared_responsibility)
        return all_export_sets

    def reconcile_inheritance_by_component(
        self, incoming_inherited: List[ossp.Inherited], incoming_satisfied: List[ossp.Satisfied]
    ) -> ossp.ByComponent:
        """
        Reconcile the inherited and satisfied statements in the by-component assembly with changes from the export.

        Notes:
            A statement is determined as existing if the provided uuid or responsibility uuid is in the existing
            by-component assembly. If existing, the description will be updated if it has changed.

            Any existing inherited or satisfied statements that are not in the incoming export will be removed.
            If a statement is in the incoming export, but not in the existing by-component assembly, it will be added.
        """
        new_inherited: List[ossp.Inherited] = []
        new_satisfied: List[ossp.Satisfied] = []

        # Create a copy of the input by-component assembly to reconcile and return
        new_by_comp: ossp.ByComponent = copy.deepcopy(self._by_comp)

        for statement in incoming_inherited:
            if statement.provided_uuid in self._inherited_dict:
                existing_statement = self._inherited_dict[str(statement.provided_uuid)]
                # Update the description if it has changed
                existing_statement.description = statement.description
                statement = existing_statement
            new_inherited.append(statement)

        new_by_comp.inherited = none_if_empty(new_inherited)

        for statement in incoming_satisfied:
            if statement.responsibility_uuid in self._satisfied_dict:
                existing_statement = self._satisfied_dict[str(statement.responsibility_uuid)]
                # Update the description if it has changed
                existing_statement.description = statement.description
                statement = existing_statement
            new_satisfied.append(statement)

        new_by_comp.satisfied = none_if_empty(new_satisfied)

        return new_by_comp

    def _provided_has_responsibilities(self, provided_uuid: str) -> bool:
        """Return whether a provided UUID has responsibilities."""
        return provided_uuid in self._responsibility_by_provided
Functions¤
__init__(by_comp) ¤

Initialize export writer for a single by-component assembly.

Source code in trestle/core/crm/bycomp_interface.py
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
def __init__(self, by_comp: ossp.ByComponent):
    """Initialize export writer for a single by-component assembly."""
    self._by_comp: ossp.ByComponent = by_comp

    self._provided_dict: Dict[str, ossp.Provided] = {}
    self._responsibility_dict: Dict[str, ossp.Responsibility] = {}
    self._responsibility_by_provided: Dict[str, List[ossp.Responsibility]] = {}

    self._inherited_dict: Dict[str, ossp.Inherited] = self._create_inherited_dict()
    self._satisfied_dict: Dict[str, ossp.Satisfied] = self._create_satisfied_dict()

    if by_comp.export:
        self._provided_dict = self._create_provided_dict()
        self._responsibility_dict = self._create_responsibility_dict()
        self._responsibility_by_provided = self._create_responsibility_by_provided_dict()
get_export_sets() ¤

Return a dictionary of every responsibility relationship with provided.

Source code in trestle/core/crm/bycomp_interface.py
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
def get_export_sets(self) -> List[Tuple[ossp.Responsibility, ossp.Provided]]:
    """Return a dictionary of every responsibility relationship with provided."""
    all_export_sets: List[Tuple[ossp.Responsibility, ossp.Provided]] = []
    for provided_uuid, responsibilities in as_dict(self._responsibility_by_provided).items():

        # Ensure the provided object exists in the dictionary.
        # If it doesn't this is a bug.
        if provided_uuid not in self._provided_dict:
            raise TrestleError(f'Provided capability {provided_uuid} not found')
        provided: ossp.Provided = self._provided_dict[provided_uuid]

        for responsibility in responsibilities:
            shared_responsibility: Tuple[ossp.Responsibility, ossp.Provided] = (responsibility, provided)
            all_export_sets.append(shared_responsibility)
    return all_export_sets
get_isolated_provided() ¤

Return all isolated exported provided capabilities.

Source code in trestle/core/crm/bycomp_interface.py
105
106
107
108
109
110
111
def get_isolated_provided(self) -> List[ossp.Provided]:
    """Return all isolated exported provided capabilities."""
    all_provided: List[ossp.Provided] = []
    for provided in as_dict(self._provided_dict).values():
        if not self._provided_has_responsibilities(provided.uuid):
            all_provided.append(provided)
    return all_provided
get_isolated_responsibilities() ¤

Return all isolated exported responsibilities.

Source code in trestle/core/crm/bycomp_interface.py
 97
 98
 99
100
101
102
103
def get_isolated_responsibilities(self) -> List[ossp.Responsibility]:
    """Return all isolated exported responsibilities."""
    all_responsibilities: List[ossp.Responsibility] = []
    for resp in as_dict(self._responsibility_dict).values():
        if resp.provided_uuid is None:
            all_responsibilities.append(resp)
    return all_responsibilities
reconcile_inheritance_by_component(incoming_inherited, incoming_satisfied) ¤

Reconcile the inherited and satisfied statements in the by-component assembly with changes from the export.

Notes

A statement is determined as existing if the provided uuid or responsibility uuid is in the existing by-component assembly. If existing, the description will be updated if it has changed.

Any existing inherited or satisfied statements that are not in the incoming export will be removed. If a statement is in the incoming export, but not in the existing by-component assembly, it will be added.

Source code in trestle/core/crm/bycomp_interface.py
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
def reconcile_inheritance_by_component(
    self, incoming_inherited: List[ossp.Inherited], incoming_satisfied: List[ossp.Satisfied]
) -> ossp.ByComponent:
    """
    Reconcile the inherited and satisfied statements in the by-component assembly with changes from the export.

    Notes:
        A statement is determined as existing if the provided uuid or responsibility uuid is in the existing
        by-component assembly. If existing, the description will be updated if it has changed.

        Any existing inherited or satisfied statements that are not in the incoming export will be removed.
        If a statement is in the incoming export, but not in the existing by-component assembly, it will be added.
    """
    new_inherited: List[ossp.Inherited] = []
    new_satisfied: List[ossp.Satisfied] = []

    # Create a copy of the input by-component assembly to reconcile and return
    new_by_comp: ossp.ByComponent = copy.deepcopy(self._by_comp)

    for statement in incoming_inherited:
        if statement.provided_uuid in self._inherited_dict:
            existing_statement = self._inherited_dict[str(statement.provided_uuid)]
            # Update the description if it has changed
            existing_statement.description = statement.description
            statement = existing_statement
        new_inherited.append(statement)

    new_by_comp.inherited = none_if_empty(new_inherited)

    for statement in incoming_satisfied:
        if statement.responsibility_uuid in self._satisfied_dict:
            existing_statement = self._satisfied_dict[str(statement.responsibility_uuid)]
            # Update the description if it has changed
            existing_statement.description = statement.description
            statement = existing_statement
        new_satisfied.append(statement)

    new_by_comp.satisfied = none_if_empty(new_satisfied)

    return new_by_comp

Functions¤

handler: python