diff --git a/CHANGELOG.md b/CHANGELOG.md index a0924db29..3eebeb87e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,7 @@ **Merged pull requests:** +- refactor: move RAMSTKFailureMode to its own package [\#939](https://github.com/ReliaQualAssociates/ramstk/pull/939) ([weibullguy](https://github.com/weibullguy)) - refactor: move RAMSTKSubCategory to its own package [\#937](https://github.com/ReliaQualAssociates/ramstk/pull/937) ([weibullguy](https://github.com/weibullguy)) - refactor: move RAMSTKCategory to its own package [\#933](https://github.com/ReliaQualAssociates/ramstk/pull/933) ([weibullguy](https://github.com/weibullguy)) - refactor: retire listviews and listbook [\#932](https://github.com/ReliaQualAssociates/ramstk/pull/932) ([weibullguy](https://github.com/weibullguy)) diff --git a/src/ramstk/views/gtk3/hardware/panel.py b/src/ramstk/views/gtk3/hardware/panel.py index 35d1d936a..509b4c2c8 100644 --- a/src/ramstk/views/gtk3/hardware/panel.py +++ b/src/ramstk/views/gtk3/hardware/panel.py @@ -35,6 +35,7 @@ class HardwareTreePanel(RAMSTKTreePanel): # Define private dictionary class attributes. # Define private list class attributes. + _lst_cost_types: List[str] = ["", "Assessed", "Specified"] # Define private scalar class attributes. _record_field = "hardware_id" @@ -273,19 +274,19 @@ def __init__(self) -> None: ], "manufacturer_id": [ 13, - Gtk.CellRendererText(), - "edited", - None, + Gtk.CellRendererCombo(), + "changed", + super().on_cell_change, f"mvw_editing_{self._tag}", 0, { "bg_color": "#FFFFFF", "editable": True, "fg_color": "#000000", - "visible": False, + "visible": True, }, _("Manufacturer"), - "gint", + "gchararray", ], "mission_time": [ 14, @@ -545,19 +546,19 @@ def __init__(self) -> None: ], "cost_type_id": [ 30, - Gtk.CellRendererText(), - "edited", - None, + Gtk.CellRendererCombo(), + "changed", + super().on_cell_change, f"mvw_editing_{self._tag}", 0, { "bg_color": "#FFFFFF", "editable": True, "fg_color": "#000000", - "visible": False, + "visible": True, }, _("Cost Type"), - "gint", + "gchararray", ], "attachments": [ 31, @@ -577,40 +578,43 @@ def __init__(self) -> None: ], "category_id": [ 32, - Gtk.CellRendererText(), - "edited", - None, + Gtk.CellRendererCombo(), + "changed", + super().on_cell_change, f"mvw_editing_{self._tag}", 0, { "bg_color": "#FFFFFF", "editable": True, "fg_color": "#000000", - "visible": False, + "visible": True, }, _("Category"), - "gint", + "gchararray", ], "subcategory_id": [ 33, - Gtk.CellRendererText(), - "edited", - None, + Gtk.CellRendererCombo(), + "changed", + super().on_cell_change, f"mvw_editing_{self._tag}", 0, { "bg_color": "#FFFFFF", "editable": True, "fg_color": "#000000", - "visible": False, + "visible": True, }, _("Subcategory"), - "gint", + "gchararray", ], } self.dic_icons = {"assembly": None, "part": None} + self.dic_subcategories: Dict[int, List[str]] = {0: [""]} # Initialize public list class attributes. + self.lst_categories: List[str] = [""] + self.lst_manufacturers: List[str] = [""] # Initialize public scalar class attributes. @@ -628,8 +632,51 @@ def __init__(self) -> None: "mvwSwitchedPage", ) + def do_load_comboboxes(self) -> None: + """Load the Gtk.CellRendererCombo()s. + + :return: None + :rtype: None + """ + self.tvwTreeView.do_load_combo_cell( + self.tvwTreeView.position["category_id"], + self.lst_categories, + ) + + self.tvwTreeView.do_load_combo_cell( + self.tvwTreeView.position["manufacturer_id"], + self.lst_manufacturers, + ) + + self.tvwTreeView.do_load_combo_cell( + self.tvwTreeView.position["cost_type_id"], + self._lst_cost_types, + ) + + _cell = self.tvwTreeView.get_column( + self.tvwTreeView.position["category_id"] + ).get_cells() + _cell[0].connect("edited", self._on_category_change) + + def _on_category_change( + self, __combo: Gtk.CellRendererCombo, path: str, new_text: str + ) -> None: + """Load the subcategories whenever the category combo is changed. + + :param __combo: the category list Gtk.CellRendererCombo(). Unused in + this method. + :param path: the path identifying the edited cell. + :param new_text: the new text (category description). + :return: None + :rtype: None + """ + self.__do_load_subcategories(new_text) + + _model = self.tvwTreeView.get_model() + _model[path][self.tvwTreeView.position["subcategory_id"]] = "" + def _on_module_switch(self, module: str = "") -> None: - """Respond to changes in selected Module View module (tab). + """Respond to change in selected Module View module (tab). :param module: the name of the module that was just selected. :return: None @@ -660,6 +707,19 @@ def _on_row_change(self, selection: Gtk.TreeSelection) -> None: self._record_id = _attributes["hardware_id"] self._parent_id = _attributes["parent_id"] + _attributes["category_id"] = self.lst_categories.index( + _attributes["category_id"] + ) + _attributes["cost_type_id"] = ["", "Assessed", "Specified"].index( + _attributes["cost_type_id"] + ) + _attributes["manufacturer_id"] = self.lst_manufacturers.index( + _attributes["manufacturer_id"] + ) + _attributes["subcategory_id"] = self.dic_subcategories[ + _attributes["category_id"] + ].index(_attributes["subcategory_id"]) + _title = _("Analyzing hardware item {0}: {1}").format( str(_attributes["comp_ref_des"]), str(_attributes["name"]) ) @@ -687,7 +747,7 @@ def __do_load_hardware(self, node: treelib.Node, row: Gtk.TreeIter) -> Gtk.TreeI """Load a hardware item into the RAMSTKTreeView(). :param node: the treelib Node() with the mode data to load. - :param row: the parent row of the mode to load into the hardware tree. + :param row: the parent row of the item to load into the hardware tree. :return: _new_row; the row that was just populated with hardware data. :rtype: :class:`Gtk.TreeIter` """ @@ -696,8 +756,6 @@ def __do_load_hardware(self, node: treelib.Node, row: Gtk.TreeIter) -> Gtk.TreeI # pylint: disable=unused-variable _entity = node.data["hardware"] - _model = self.tvwTreeView.get_model() - # noinspection PyArgumentList _icon = GdkPixbuf.Pixbuf.new_from_file_at_size( self.dic_icons["assembly"], 22, 22 @@ -723,7 +781,7 @@ def __do_load_hardware(self, node: treelib.Node, row: Gtk.TreeIter) -> Gtk.TreeI _entity.figure_number, _entity.lcn, _entity.level, - _entity.manufacturer_id, + self.lst_manufacturers[_entity.manufacturer_id], _entity.mission_time, _entity.name, _entity.nsn, @@ -740,24 +798,23 @@ def __do_load_hardware(self, node: treelib.Node, row: Gtk.TreeIter) -> Gtk.TreeI _entity.total_part_count, _entity.total_power_dissipation, _entity.year_of_manufacture, - _entity.cost_type_id, + self._lst_cost_types[_entity.cost_type_id], _entity.attachments, - _entity.category_id, - _entity.subcategory_id, + self.lst_categories[_entity.category_id], + self.dic_subcategories[_entity.category_id][_entity.subcategory_id], _icon, ] try: - _new_row = _model.append(row, _attributes) + _new_row = self.tvwTreeView.unfilt_model.append(row, _attributes) except (AttributeError, TypeError, ValueError): - _new_row = None _message = _( - "An error occurred when loading hardware item {0} into the " - "hardware tree. This might indicate it was missing it's data " - "package, some of the data in the package was missing, or " - "some of the data was the wrong type. Row data was: " - "{1}" - ).format(str(node.identifier), _attributes) + f"An error occurred when loading hardware item {node.identifier} into " + f"the hardware tree. This might indicate it was missing it's data " + f"package, some of the data in the package was missing, or " + f"some of the data was the wrong type. Row data was: " + f"{_attributes}" + ) pub.sendMessage( "do_log_warning_msg", logger_name="WARNING", @@ -766,6 +823,20 @@ def __do_load_hardware(self, node: treelib.Node, row: Gtk.TreeIter) -> Gtk.TreeI return _new_row + def __do_load_subcategories(self, category: str) -> None: + """Load subcategory Gtk.CellRendererCombo() when a new category is selected. + + :param category: the ID of the newly selected category. + :return: None + :rtype: None + """ + _category_id = self.lst_categories.index(category) + + self.tvwTreeView.do_load_combo_cell( + self.tvwTreeView.position["subcategory_id"], + self.dic_subcategories[_category_id], + ) + class HardwareGeneralDataPanel(RAMSTKFixedPanel): """Panel to display general data about the selected Hardware item.""" diff --git a/src/ramstk/views/gtk3/hardware/view.py b/src/ramstk/views/gtk3/hardware/view.py index af21efb51..955ba224b 100644 --- a/src/ramstk/views/gtk3/hardware/view.py +++ b/src/ramstk/views/gtk3/hardware/view.py @@ -306,6 +306,28 @@ def _do_set_record_id(self, attributes: Dict[str, Any]) -> None: self.dic_pkeys["parent_id"] = attributes["parent_id"] self.dic_pkeys["record_id"] = attributes["hardware_id"] + def __do_load_lists(self) -> None: + """Load the pick lists associated with Hardware. + + :return: None + :rtype: None + """ + for _key in self.RAMSTK_USER_CONFIGURATION.RAMSTK_CATEGORIES: + self._pnlPanel.lst_categories.append( + self.RAMSTK_USER_CONFIGURATION.RAMSTK_CATEGORIES[_key] + ) + self._pnlPanel.dic_subcategories[_key] = [""] + + for _subkey in self.RAMSTK_USER_CONFIGURATION.RAMSTK_SUBCATEGORIES[_key]: + self._pnlPanel.dic_subcategories[_key].append( + self.RAMSTK_USER_CONFIGURATION.RAMSTK_SUBCATEGORIES[_key][_subkey] + ) + + for _key in self.RAMSTK_USER_CONFIGURATION.RAMSTK_MANUFACTURERS: + self._pnlPanel.lst_manufacturers.append( + self.RAMSTK_USER_CONFIGURATION.RAMSTK_MANUFACTURERS[_key][0] + ) + def __make_ui(self) -> None: """Build the user interface for the function module view. @@ -313,6 +335,11 @@ def __make_ui(self) -> None: """ super().make_ui() + self._pnlPanel.dic_icons = self._dic_icons + + self.__do_load_lists() + self._pnlPanel.do_load_comboboxes() + self._pnlPanel.do_set_cell_callbacks( "mvw_editing_hardware", [ @@ -344,8 +371,6 @@ def __make_ui(self) -> None: ] = self._pnlPanel.tvwTreeView.connect( "button_press_event", super().on_button_press ) - for _element in ["assembly", "part"]: - self._pnlPanel.dic_icons[_element] = self._dic_icons[_element] class HardwareGeneralDataView(RAMSTKWorkView): diff --git a/src/ramstk/views/gtk3/requirement/panel.py b/src/ramstk/views/gtk3/requirement/panel.py index b3a859706..09647c6e4 100644 --- a/src/ramstk/views/gtk3/requirement/panel.py +++ b/src/ramstk/views/gtk3/requirement/panel.py @@ -849,7 +849,7 @@ def __init__(self) -> None: pub.subscribe(self._on_module_switch, "mvwSwitchedPage") def _on_module_switch(self, module: str = "") -> None: - """Respond to changes in selected Module View module (tab). + """Respond to change in selected Module View module (tab). :param module: the name of the module that was just selected. :return: None @@ -1230,11 +1230,8 @@ def do_load_requirement_types( :return: None :rtype: None """ - _requirement_types: List[Tuple[str]] = [] + _requirement_types: List[Tuple[str]] = list(requirement_types.values()) - # pylint: disable=unused-variable - for __, _key in enumerate(requirement_types): - _requirement_types.append(requirement_types[_key]) self.cmbRequirementType.do_load_combo(entries=_requirement_types, simple=False) def do_load_workgroups(self, workgroups: Dict[int, Tuple[str]]) -> None: @@ -1244,11 +1241,8 @@ def do_load_workgroups(self, workgroups: Dict[int, Tuple[str]]) -> None: :return: None :rtype: None """ - _owners = [] + _owners = list(workgroups.values()) - # pylint: disable=unused-variable - for __, _key in enumerate(workgroups): - _owners.append(workgroups[_key]) self.cmbOwner.do_load_combo(_owners) def _do_load_code(self, requirement_code: int) -> None: diff --git a/src/ramstk/views/gtk3/requirement/view.py b/src/ramstk/views/gtk3/requirement/view.py index 09d5af7a9..37e1f45cf 100644 --- a/src/ramstk/views/gtk3/requirement/view.py +++ b/src/ramstk/views/gtk3/requirement/view.py @@ -158,16 +158,12 @@ def __make_ui(self) -> None: """ super().make_ui() - for (__, _key) in enumerate( # pylint: disable=unused-variable - self.RAMSTK_USER_CONFIGURATION.RAMSTK_WORKGROUPS - ): + for _key in self.RAMSTK_USER_CONFIGURATION.RAMSTK_WORKGROUPS: self._pnlPanel.lst_owner.append( self.RAMSTK_USER_CONFIGURATION.RAMSTK_WORKGROUPS[_key][0] ) - for (__, _key) in enumerate( # pylint: disable=unused-variable - self.RAMSTK_USER_CONFIGURATION.RAMSTK_REQUIREMENT_TYPE - ): + for _key in self.RAMSTK_USER_CONFIGURATION.RAMSTK_REQUIREMENT_TYPE: self._pnlPanel.lst_type.append( self.RAMSTK_USER_CONFIGURATION.RAMSTK_REQUIREMENT_TYPE[_key][1] ) diff --git a/src/ramstk/views/gtk3/similar_item/panel.py b/src/ramstk/views/gtk3/similar_item/panel.py index c6b1ac062..228cebfdb 100644 --- a/src/ramstk/views/gtk3/similar_item/panel.py +++ b/src/ramstk/views/gtk3/similar_item/panel.py @@ -128,24 +128,24 @@ class SimilarItemTreePanel(RAMSTKTreePanel): """Panel to display Similar Item analysis worksheet.""" # Define private dict class attributes. - _dic_quality: Dict[int, str] = { - 0: "", - 1: "Space", - 2: "Full Military", - 3: "Ruggedized", - 4: "Commercial", - } - _dic_environment: Dict[int, str] = { - 0: "", - 1: "Ground, Benign", - 2: "Ground,Mobile", - 3: "Naval, Sheltered", - 4: "Airborne, Inhabited, Cargo", - 5: "Airborne, Rotary Wing", - 6: "Space, Flight", - } # Define private list class attributes. + _lst_environments: List[str] = [ + "", + "Ground, Benign", + "Ground,Mobile", + "Naval, Sheltered", + "Airborne, Inhabited, Cargo", + "Airborne, Rotary Wing", + "Space, Flight", + ] + _lst_qualities: List[str] = [ + "", + "Space", + "Full Military", + "Ruggedized", + "Commercial", + ] # Define private scalar class attributes. _select_msg = "succeed_retrieve_similar_items" @@ -421,8 +421,8 @@ def __init__(self) -> None: "quality_from_id": [ 4, Gtk.CellRendererCombo(), - "edited", - super().on_cell_edit, + "changed", + super().on_cell_change, self._on_edit_message, "", { @@ -437,8 +437,8 @@ def __init__(self) -> None: "quality_to_id": [ 5, Gtk.CellRendererCombo(), - "edited", - super().on_cell_edit, + "changed", + super().on_cell_change, self._on_edit_message, "", { @@ -453,8 +453,8 @@ def __init__(self) -> None: "environment_from_id": [ 6, Gtk.CellRendererCombo(), - "edited", - super().on_cell_edit, + "changed", + super().on_cell_change, self._on_edit_message, "", { @@ -469,8 +469,8 @@ def __init__(self) -> None: "environment_to_id": [ 7, Gtk.CellRendererCombo(), - "edited", - super().on_cell_edit, + "changed", + super().on_cell_change, self._on_edit_message, "", { @@ -1298,17 +1298,22 @@ def do_load_comboboxes(self) -> None: :return: None :rtype: None """ - # Load the quality from and quality to Gtk.CellRendererCombo(). - for _idx in [4, 5]: - _model = self.tvwTreeView.get_cell_model(_idx, True) - for _quality in self._dic_quality.values(): - _model.append([_quality]) - - # Load the environment from and environment to Gtk.CellRendererCombo(). - for _idx in [6, 7]: - _model = self.tvwTreeView.get_cell_model(_idx, True) - for _environment in self._dic_environment.values(): - _model.append([_environment]) + self.tvwTreeView.do_load_combo_cell( + self.tvwTreeView.position["environment_from_id"], + self._lst_environments, + ) + self.tvwTreeView.do_load_combo_cell( + self.tvwTreeView.position["environment_to_id"], + self._lst_environments, + ) + self.tvwTreeView.do_load_combo_cell( + self.tvwTreeView.position["quality_from_id"], + self._lst_qualities, + ) + self.tvwTreeView.do_load_combo_cell( + self.tvwTreeView.position["quality_to_id"], + self._lst_qualities, + ) def do_refresh_functions(self, row: Gtk.TreeIter, function: List[str]) -> None: """Refresh the Similar Item functions in the RAMSTKTreeView(). @@ -1421,16 +1426,16 @@ def __do_load_similar_item(self, node: Any = "", row: Gtk.TreeIter = None) -> No """ _entity = node.data["similar_item"] - if not _entity.parent_id == 0: + if _entity.parent_id != 0: _attributes = [ _entity.revision_id, _entity.hardware_id, "", 0.0, - self._dic_quality[_entity.quality_from_id], - self._dic_quality[_entity.quality_to_id], - self._dic_environment[_entity.environment_from_id], - self._dic_environment[_entity.environment_to_id], + self._lst_qualities[_entity.quality_from_id], + self._lst_qualities[_entity.quality_to_id], + self._lst_environments[_entity.environment_from_id], + self._lst_environments[_entity.environment_to_id], _entity.temperature_from, _entity.temperature_to, _entity.change_description_1, diff --git a/src/ramstk/views/gtk3/similar_item/view.py b/src/ramstk/views/gtk3/similar_item/view.py index e80635e5d..7a77f2377 100644 --- a/src/ramstk/views/gtk3/similar_item/view.py +++ b/src/ramstk/views/gtk3/similar_item/view.py @@ -29,10 +29,6 @@ class SimilarItemWorkView(RAMSTKWorkView): The WorkView displays all the attributes for the Similar Item Analysis. The attributes of a SimilarItem Work View are: - :cvar dict _dic_quality: the quality levels and associated index to use in - a Topic 633 analysis. - :cvar dict _dic_environment: the environments and associated index to use - in a Topic 633 analysis. :cvar str _tag: the name of the module. :ivar dict _dic_hardware: dict to hold information from the Hardware diff --git a/src/ramstk/views/gtk3/validation/panel.py b/src/ramstk/views/gtk3/validation/panel.py index de564c8e9..e7b17e2cb 100644 --- a/src/ramstk/views/gtk3/validation/panel.py +++ b/src/ramstk/views/gtk3/validation/panel.py @@ -11,6 +11,7 @@ from typing import Dict, List, Tuple, Union # Third Party Imports +import treelib from pubsub import pub # RAMSTK Package Imports @@ -51,7 +52,7 @@ def __init__(self) -> None: # Initialize private dictionary class attributes. self.tvwTreeView.dic_row_loader = { - "validation": super().do_load_treerow, + "validation": self.__do_load_validation, } # Initialize private list class attributes. @@ -337,8 +338,8 @@ def __init__(self) -> None: "measurement_unit": [ 17, Gtk.CellRendererCombo(), - "edited", - super().on_cell_edit, + "changed", + super().on_cell_change, "mvw_editing_validation", "", { @@ -348,7 +349,7 @@ def __init__(self) -> None: "visible": True, }, _("Unit of Measure"), - "gint", + "gchararray", ], "name": [ 18, @@ -401,8 +402,8 @@ def __init__(self) -> None: "task_type": [ 21, Gtk.CellRendererCombo(), - "edited", - super().on_cell_edit, + "changed", + super().on_cell_change, "mvw_editing_validation", "", { @@ -412,7 +413,7 @@ def __init__(self) -> None: "visible": True, }, _("Task Type"), - "gint", + "gchararray", ], "time_average": [ 22, @@ -620,6 +621,13 @@ def _on_row_change(self, selection: Gtk.TreeSelection) -> None: if _attributes: self._record_id = _attributes["validation_id"] + _attributes["measurement_unit"] = self._lst_measurement_units.index( + _attributes["measurement_unit"] + ) + _attributes["task_type"] = self._lst_verification_types.index( + _attributes["task_type"] + ) + _title = _(f"Analyzing Verification Task {_attributes['name']}") pub.sendMessage( @@ -631,6 +639,71 @@ def _on_row_change(self, selection: Gtk.TreeSelection) -> None: title=_title, ) + def __do_load_validation( + self, node: treelib.Node, row: Gtk.TreeIter + ) -> Gtk.TreeIter: + """Load a verification task into the RAMSTKTreeView(). + + :param node: the treelib Node() with the mode data to load. + :param row: the parent row of the task to load into the validation tree. + :return: _new_row; the row that was just populated with validation data. + :rtype: :class:`Gtk.TreeIter` + """ + _new_row = None + + # pylint: disable=unused-variable + _entity = node.data["validation"] + + _attributes = [ + _entity.revision_id, + _entity.validation_id, + _entity.acceptable_maximum, + _entity.acceptable_mean, + _entity.acceptable_minimum, + _entity.acceptable_variance, + _entity.confidence, + _entity.cost_average, + _entity.cost_ll, + _entity.cost_maximum, + _entity.cost_mean, + _entity.cost_minimum, + _entity.cost_ul, + _entity.cost_variance, + _entity.date_end, + _entity.date_start, + _entity.description, + self._lst_measurement_units[_entity.measurement_unit], + _entity.name, + _entity.status, + _entity.task_specification, + self._lst_verification_types[_entity.task_type], + _entity.time_average, + _entity.time_ll, + _entity.time_maximum, + _entity.time_mean, + _entity.time_minimum, + _entity.time_ul, + _entity.time_variance, + ] + + try: + _new_row = self.tvwTreeView.unfilt_model.append(row, _attributes) + except (AttributeError, TypeError, ValueError): + _message = _( + f"An error occurred when loading verification task {node.identifier} " + f"into the verification tree. This might indicate it was missing it's " + f"data package, some of the data in the package was missing, or " + f"some of the data was the wrong type. Row data was: " + f"{_attributes}" + ) + pub.sendMessage( + "do_log_warning_msg", + logger_name="WARNING", + message=_message, + ) + + return _new_row + class ValidationTaskDescriptionPanel(RAMSTKFixedPanel): """Panel to display general data about the selected Validation task.""" diff --git a/src/ramstk/views/gtk3/widgets/panel.pyi b/src/ramstk/views/gtk3/widgets/panel.pyi index 67ab53b35..15a33ea89 100644 --- a/src/ramstk/views/gtk3/widgets/panel.pyi +++ b/src/ramstk/views/gtk3/widgets/panel.pyi @@ -87,7 +87,7 @@ class RAMSTKTreePanel(RAMSTKPanel): def do_make_treeview(self, **kwargs: Dict[str, Any]) -> None: ... def do_refresh_tree(self, node_id: List, package: Dict[str, Any]) -> None: ... def do_set_callbacks(self) -> None: ... - def do_set_cell_callbacks(self, message: str, columns: List[int]) -> None: ... + def do_set_cell_callbacks(self, message: str, columns: List[str]) -> None: ... def do_set_headings(self) -> None: ... def do_set_properties(self, **kwargs: Any) -> None: ... def on_cell_edit( diff --git a/src/ramstk/views/gtk3/widgets/treeview.py b/src/ramstk/views/gtk3/widgets/treeview.py index c55071997..52f711d30 100644 --- a/src/ramstk/views/gtk3/widgets/treeview.py +++ b/src/ramstk/views/gtk3/widgets/treeview.py @@ -171,7 +171,7 @@ def do_change_cell( _iter = _cell_model.get_iter_first() while _iter is not None: if _cell_model.get_value(_iter, 0) == _new_text: - _model[path][position] = _idx + _model[path][position] = _new_text break _iter = _cell_model.iter_next(_iter) _idx += 1