From 8c8590f3c7a878825bfadfbb3266f1704d40df29 Mon Sep 17 00:00:00 2001 From: jmurugan-fzj Date: Tue, 12 Sep 2023 14:10:33 +0200 Subject: [PATCH 01/22] - Corrected the link line-edits to accept empty strings also, if the user needs to clear the link/url field he could enter empty string and can save in DB - Modified the database.py to accept ontology version 3.0 also - Corrected required column data from string to bool --- .../ontology_configuration_extended.py | 4 ++-- .../ontology_configuration/ontology_tableview_data_model.py | 3 +-- .../GUI/ontology_configuration/required_column_delegate.py | 6 +++--- pasta_eln/database.py | 5 +++-- .../component_tests/test_ontology_configuration_extended.py | 4 ++-- .../test_ontology_config_required_column_delegate.py | 4 ++-- 6 files changed, 13 insertions(+), 13 deletions(-) diff --git a/pasta_eln/GUI/ontology_configuration/ontology_configuration_extended.py b/pasta_eln/GUI/ontology_configuration/ontology_configuration_extended.py index 3b9c7682..940a8b63 100644 --- a/pasta_eln/GUI/ontology_configuration/ontology_configuration_extended.py +++ b/pasta_eln/GUI/ontology_configuration/ontology_configuration_extended.py @@ -206,7 +206,7 @@ def update_structure_label(self, Returns: Nothing """ current_type = self.typeComboBox.currentText() - if modified_type_label and current_type in self.ontology_types: + if modified_type_label is not None and current_type in self.ontology_types: self.ontology_types.get(current_type)["label"] = modified_type_label def update_type_link(self, modified_link: str) -> None: @@ -219,7 +219,7 @@ def update_type_link(self, modified_link: str) -> None: Returns: Nothing """ current_type = self.typeComboBox.currentText() - if modified_link and current_type in self.ontology_types: + if modified_link is not None and current_type in self.ontology_types: self.ontology_types.get(current_type)["link"] = modified_link def delete_selected_type(self) -> None: diff --git a/pasta_eln/GUI/ontology_configuration/ontology_tableview_data_model.py b/pasta_eln/GUI/ontology_configuration/ontology_tableview_data_model.py index b872db51..c86026fb 100644 --- a/pasta_eln/GUI/ontology_configuration/ontology_tableview_data_model.py +++ b/pasta_eln/GUI/ontology_configuration/ontology_tableview_data_model.py @@ -146,8 +146,7 @@ def data(self, role in (Qt.DisplayRole, Qt.EditRole, Qt.UserRole)): row = index.row() column = index.column() - value = self.data_set[row].get(self.data_name_map.get(column)) - return str(value if value else '') + return self.data_set[row].get(self.data_name_map.get(column)) else: return None diff --git a/pasta_eln/GUI/ontology_configuration/required_column_delegate.py b/pasta_eln/GUI/ontology_configuration/required_column_delegate.py index 411b2a26..d457bacb 100644 --- a/pasta_eln/GUI/ontology_configuration/required_column_delegate.py +++ b/pasta_eln/GUI/ontology_configuration/required_column_delegate.py @@ -49,9 +49,9 @@ def paint(self, option.rect.top(), option.rect.width(), option.rect.height()) - is_user_role = index.data(Qt.UserRole) == 'True' # type: ignore[arg-type] + is_required = bool(index.data(Qt.UserRole)) # type: ignore[arg-type] opt.state = QStyle.State_On \ - if is_user_role \ + if is_required \ else QStyle.State_Off style.drawControl(QStyle.CE_RadioButton, opt, painter, widget) @@ -72,7 +72,7 @@ def editorEvent(self, """ if event.type() == QEvent.MouseButtonRelease: - model.setData(index, str(not index.data(Qt.UserRole) == 'True'), Qt.UserRole) # type: ignore[arg-type] + model.setData(index, not bool(index.data(Qt.UserRole)), Qt.UserRole) # type: ignore[arg-type] return super().editorEvent(event, model, option, index) def createEditor(self, diff --git a/pasta_eln/database.py b/pasta_eln/database.py index f8ee700f..66406499 100644 --- a/pasta_eln/database.py +++ b/pasta_eln/database.py @@ -44,9 +44,10 @@ def __init__(self, user:str, password:str, databaseName:str, configuration:dict[ self.initDocTypeViews( configuration['tableColumnsMax'] ) self.initGeneralViews() self.ontology = self.db['-ontology-'] - if '-version' not in self.ontology or self.ontology['-version']!=2: + if ('-version' not in self.ontology + or self.ontology['-version'] not in [2, 3]): print(F"**ERROR wrong ontology version: {self.ontology['-version']}") - raise ValueError("Wrong ontology version") + raise ValueError(F"**ERROR wrong ontology version: {self.ontology['-version']}") self.dataLabels = {i:self.ontology[i]['label'] for i in self.ontology if i[0] not in ['_','-']} self.basePath = basePath return diff --git a/tests/app_tests/component_tests/test_ontology_configuration_extended.py b/tests/app_tests/component_tests/test_ontology_configuration_extended.py index f4da4089..f999c44e 100644 --- a/tests/app_tests/component_tests/test_ontology_configuration_extended.py +++ b/tests/app_tests/component_tests/test_ontology_configuration_extended.py @@ -90,9 +90,9 @@ def check_table_view_model(model, column_names, data_selected): index = model.index(row, column) if column_names[column] in data: assert (model.data(index, Qt.DisplayRole) - == str(data[column_names[column]])), f"{column_names[column]} not loaded!" + == data[column_names[column]]), f"{column_names[column]} not loaded!" else: - assert model.data(index, Qt.DisplayRole) == "", f"{column_names[column]} should be null string!" + assert model.data(index, Qt.DisplayRole) is None, f"{column_names[column]} should be None!" def check_table_contents(self, attachments_column_names, props_column_names, selected_type, ui_form): categories = list(selected_type["prop"].keys()) diff --git a/tests/app_tests/unit_tests/test_ontology_config_required_column_delegate.py b/tests/app_tests/unit_tests/test_ontology_config_required_column_delegate.py index c0ca9d44..97bc7342 100644 --- a/tests/app_tests/unit_tests/test_ontology_config_required_column_delegate.py +++ b/tests/app_tests/unit_tests/test_ontology_config_required_column_delegate.py @@ -29,8 +29,8 @@ def test_delegate_create_editor_method(self, delegate_editor_method_common(required_delegate, mocker) @pytest.mark.parametrize("test_data_value, expected", [ - ('False', 'True'), - ('True', 'False') + (False, True), + (True, False) ]) def test_delegate_editor_event_method(self, mocker, From 12d2b25da2fffbabdc7a3eab8350b451d6220512 Mon Sep 17 00:00:00 2001 From: jmurugan-fzj Date: Tue, 12 Sep 2023 15:14:45 +0200 Subject: [PATCH 02/22] - Deleted the cancel button from ontology editor - Removed sorting indicators from the --- .../ontology_configuration.py | 11 +++-------- .../ontology_configuration.ui | 16 +++------------- 2 files changed, 6 insertions(+), 21 deletions(-) diff --git a/pasta_eln/GUI/ontology_configuration/ontology_configuration.py b/pasta_eln/GUI/ontology_configuration/ontology_configuration.py index ca90cc04..c0044792 100644 --- a/pasta_eln/GUI/ontology_configuration/ontology_configuration.py +++ b/pasta_eln/GUI/ontology_configuration/ontology_configuration.py @@ -31,13 +31,13 @@ def setupUi(self, OntologyConfigurationBaseForm): self.mainGridLayout.setContentsMargins(30, 0, 30, 0) self.mainGridLayout.setObjectName("mainGridLayout") self.typePropsTableView = QtWidgets.QTableView(parent=self.verticalLayoutWidget_2) - self.typePropsTableView.setSortingEnabled(True) + self.typePropsTableView.setSortingEnabled(False) self.typePropsTableView.setObjectName("typePropsTableView") self.typePropsTableView.horizontalHeader().setCascadingSectionResizes(True) - self.typePropsTableView.horizontalHeader().setSortIndicatorShown(True) + self.typePropsTableView.horizontalHeader().setSortIndicatorShown(False) self.typePropsTableView.horizontalHeader().setStretchLastSection(True) self.typePropsTableView.verticalHeader().setCascadingSectionResizes(True) - self.typePropsTableView.verticalHeader().setSortIndicatorShown(True) + self.typePropsTableView.verticalHeader().setSortIndicatorShown(False) self.typePropsTableView.verticalHeader().setStretchLastSection(False) self.mainGridLayout.addWidget(self.typePropsTableView, 6, 0, 1, 1) self.attachmentsHeaderHorizontalLayout = QtWidgets.QHBoxLayout() @@ -181,9 +181,6 @@ def setupUi(self, OntologyConfigurationBaseForm): self.helpPushButton = QtWidgets.QPushButton(parent=self.verticalLayoutWidget_2) self.helpPushButton.setObjectName("helpPushButton") self.headerHorizontalLayout.addWidget(self.helpPushButton) - self.cancelPushButton = QtWidgets.QPushButton(parent=self.verticalLayoutWidget_2) - self.cancelPushButton.setObjectName("cancelPushButton") - self.headerHorizontalLayout.addWidget(self.cancelPushButton) self.mainGridLayout.addLayout(self.headerHorizontalLayout, 0, 0, 1, 1) self.gridLayout.addWidget(self.mainWidget, 0, 1, 1, 1) @@ -225,8 +222,6 @@ def retranslateUi(self, OntologyConfigurationBaseForm): self.saveOntologyPushButton.setText(_translate("OntologyConfigurationBaseForm", "Save")) self.helpPushButton.setToolTip(_translate("OntologyConfigurationBaseForm", "Navigate to the help page")) self.helpPushButton.setText(_translate("OntologyConfigurationBaseForm", "Help")) - self.cancelPushButton.setToolTip(_translate("OntologyConfigurationBaseForm", "Close the editor")) - self.cancelPushButton.setText(_translate("OntologyConfigurationBaseForm", "Cancel")) if __name__ == "__main__": diff --git a/pasta_eln/GUI/ontology_configuration/ontology_configuration.ui b/pasta_eln/GUI/ontology_configuration/ontology_configuration.ui index 570aa64d..98a853c0 100644 --- a/pasta_eln/GUI/ontology_configuration/ontology_configuration.ui +++ b/pasta_eln/GUI/ontology_configuration/ontology_configuration.ui @@ -59,13 +59,13 @@ Table which lists and allows editing of all the properties associated with the above selected type - true + false true - true + false true @@ -74,7 +74,7 @@ true - true + false false @@ -514,16 +514,6 @@ - - - - Close the editor - - - Cancel - - - From 09d5928d1639d3387324142558999c7ddb168453 Mon Sep 17 00:00:00 2001 From: jmurugan-fzj Date: Tue, 12 Sep 2023 16:39:12 +0200 Subject: [PATCH 03/22] - Corrected the logic for getting/setting the list property from the table - Corrected the respective tests --- .../ontology_configuration_constants.py | 1 + .../ontology_props_tableview_data_model.py | 45 ++++++++++++++++++- .../test_ontology_configuration_extended.py | 5 ++- 3 files changed, 47 insertions(+), 4 deletions(-) diff --git a/pasta_eln/GUI/ontology_configuration/ontology_configuration_constants.py b/pasta_eln/GUI/ontology_configuration/ontology_configuration_constants.py index 37ed3dac..3407e777 100644 --- a/pasta_eln/GUI/ontology_configuration/ontology_configuration_constants.py +++ b/pasta_eln/GUI/ontology_configuration/ontology_configuration_constants.py @@ -9,6 +9,7 @@ # You should have received a copy of the license with this file. Please refer the license file for more information. # Ontology Table view constants +PROPS_TABLE_LIST_COLUMN_INDEX = 2 PROPS_TABLE_REQUIRED_COLUMN_INDEX = 4 PROPS_TABLE_DELETE_COLUMN_INDEX = 6 PROPS_TABLE_REORDER_COLUMN_INDEX = 7 diff --git a/pasta_eln/GUI/ontology_configuration/ontology_props_tableview_data_model.py b/pasta_eln/GUI/ontology_configuration/ontology_props_tableview_data_model.py index f54c3ec4..7e26ab78 100644 --- a/pasta_eln/GUI/ontology_configuration/ontology_props_tableview_data_model.py +++ b/pasta_eln/GUI/ontology_configuration/ontology_props_tableview_data_model.py @@ -1,5 +1,5 @@ """ Table view model used for the properties table in the ontology editor """ -# PASTA-ELN and all its sub-parts are covered by the MIT license. +# Context from Code Snippet pasta_eln/GUI/ontology_configuration/ontology_props_tableview_data_model.py:## PASTA-ELN and all its sub-parts are covered by the MIT license. # # Copyright (c) 2023 # @@ -8,10 +8,12 @@ # # You should have received a copy of the license with this file. Please refer the license file for more information. import logging -from typing import Union +from typing import Union, Any +from PySide6.QtCore import QModelIndex, QPersistentModelIndex, Qt from PySide6.QtWidgets import QWidget +from .ontology_configuration_constants import PROPS_TABLE_LIST_COLUMN_INDEX from .ontology_tableview_data_model import OntologyTableViewModel @@ -42,3 +44,42 @@ def __init__(self, } self.header_values: list[str] = list(self.data_name_map.values()) self.columns_count: int = len(self.header_values) + + def setData(self, + index: Union[QModelIndex, QPersistentModelIndex], + value: Any, role: int = Qt.ItemDataRole) -> bool: # type: ignore[assignment] + """ + Overridden method for setting the data, only handling the PROPS_TABLE_LIST_COLUMN_INDEX, the rest is handled by base class + Args: + index (Union[QModelIndex, QPersistentModelIndex]): Table cell index + value (Any): New value + role (int): Role for the data + + Returns: Nothing + """ + if (index.isValid() and + index.column() == PROPS_TABLE_LIST_COLUMN_INDEX): + value = [i.strip() for i in value.split(',')] \ + if ',' in value \ + else value + return super().setData(index, value, role) + + def data(self, + index: Union[QModelIndex, QPersistentModelIndex], + role: int = Qt.ItemDataRole) -> Any: # type: ignore[assignment] + """ + Overriden method for getting the data, only handles the PROPS_TABLE_LIST_COLUMN_INDEX case + Args: + index (Union[QModelIndex, QPersistentModelIndex]): Table cell index + role (int): Role for the data + + Returns: Data retrieved for the respective index + + """ + if (index.isValid() + and index.column() == PROPS_TABLE_LIST_COLUMN_INDEX): + data = super().data(index, role) + return ",".join(data) \ + if isinstance(data, list) \ + else data + return super().data(index, role) diff --git a/tests/app_tests/component_tests/test_ontology_configuration_extended.py b/tests/app_tests/component_tests/test_ontology_configuration_extended.py index f999c44e..5f8dfc2c 100644 --- a/tests/app_tests/component_tests/test_ontology_configuration_extended.py +++ b/tests/app_tests/component_tests/test_ontology_configuration_extended.py @@ -39,7 +39,6 @@ def test_component_launch_should_display_all_ui_elements(self, assert ui_form.loadOntologyPushButton is not None, "Bush button not loaded!" assert ui_form.saveOntologyPushButton is not None, "Save button not loaded!" assert ui_form.helpPushButton is not None, "Help button not loaded!" - assert ui_form.cancelPushButton is not None, "Cancel button not loaded!" assert ui_form.typePropsTableView is not None, "Properties table view not loaded!" assert ui_form.typeAttachmentsTableView is not None, "Type table view not loaded!" assert ui_form.addAttachmentPushButton is not None, "Add attachment button not loaded!" @@ -89,8 +88,10 @@ def check_table_view_model(model, column_names, data_selected): for column in range(model.columnCount() - 2): index = model.index(row, column) if column_names[column] in data: + cell_data = data[column_names[column]] assert (model.data(index, Qt.DisplayRole) - == data[column_names[column]]), f"{column_names[column]} not loaded!" + == ','.join(cell_data) if isinstance(cell_data, list) else cell_data), \ + f"{column_names[column]} not loaded!" else: assert model.data(index, Qt.DisplayRole) is None, f"{column_names[column]} should be None!" From 1582af42cb75a1955dbad12bc7d6b4abde052cd0 Mon Sep 17 00:00:00 2001 From: jmurugan-fzj Date: Tue, 12 Sep 2023 16:51:23 +0200 Subject: [PATCH 04/22] - Re-ordered the columns for property table to [name, query, list, unit, link, required, delete, re-order] --- .../ontology_configuration_constants.py | 2 +- .../ontology_props_tableview_data_model.py | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/pasta_eln/GUI/ontology_configuration/ontology_configuration_constants.py b/pasta_eln/GUI/ontology_configuration/ontology_configuration_constants.py index 3407e777..6bd44d1a 100644 --- a/pasta_eln/GUI/ontology_configuration/ontology_configuration_constants.py +++ b/pasta_eln/GUI/ontology_configuration/ontology_configuration_constants.py @@ -10,7 +10,7 @@ # Ontology Table view constants PROPS_TABLE_LIST_COLUMN_INDEX = 2 -PROPS_TABLE_REQUIRED_COLUMN_INDEX = 4 +PROPS_TABLE_REQUIRED_COLUMN_INDEX = 5 PROPS_TABLE_DELETE_COLUMN_INDEX = 6 PROPS_TABLE_REORDER_COLUMN_INDEX = 7 diff --git a/pasta_eln/GUI/ontology_configuration/ontology_props_tableview_data_model.py b/pasta_eln/GUI/ontology_configuration/ontology_props_tableview_data_model.py index 7e26ab78..d997054a 100644 --- a/pasta_eln/GUI/ontology_configuration/ontology_props_tableview_data_model.py +++ b/pasta_eln/GUI/ontology_configuration/ontology_props_tableview_data_model.py @@ -36,9 +36,9 @@ def __init__(self, 0: "name", 1: "query", 2: "list", - 3: "link", - 4: "required", - 5: "unit", + 3: "unit", + 4: "link", + 5: "required", 6: "delete", 7: "re-order" } From b7394d54e2568b78be3751f77373b0405f4de19e Mon Sep 17 00:00:00 2001 From: jmurugan-fzj Date: Tue, 12 Sep 2023 16:56:58 +0200 Subject: [PATCH 05/22] - Corrected the test fixture for the changes made in the header of property table --- tests/app_tests/common/fixtures.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/app_tests/common/fixtures.py b/tests/app_tests/common/fixtures.py index 45032f07..c4d4179e 100644 --- a/tests/app_tests/common/fixtures.py +++ b/tests/app_tests/common/fixtures.py @@ -161,9 +161,9 @@ def props_column_names(): 0: "name", 1: "query", 2: "list", - 3: "link", - 4: "required", - 5: "unit" + 3: "unit", + 4: "link", + 5: "required" } From 3e8315198081b821b04837a6631f6ff4cfba9bca Mon Sep 17 00:00:00 2001 From: jmurugan-fzj Date: Tue, 12 Sep 2023 17:24:25 +0200 Subject: [PATCH 06/22] - Added the logic to adjust the column width of properties table --- .../ontology_configuration_extended.py | 3 +++ .../ontology_props_tableview_data_model.py | 10 ++++++++++ 2 files changed, 13 insertions(+) diff --git a/pasta_eln/GUI/ontology_configuration/ontology_configuration_extended.py b/pasta_eln/GUI/ontology_configuration/ontology_configuration_extended.py index 940a8b63..1cb2dd71 100644 --- a/pasta_eln/GUI/ontology_configuration/ontology_configuration_extended.py +++ b/pasta_eln/GUI/ontology_configuration/ontology_configuration_extended.py @@ -88,6 +88,9 @@ def __init__(self, self.reorder_column_delegate_props_table) self.typePropsTableView.setModel(self.props_table_data_model) + for index, width in self.props_table_data_model.column_widths.items(): + self.typePropsTableView.setColumnWidth(index, width) + self.typeAttachmentsTableView.setItemDelegateForColumn( ATTACHMENT_TABLE_DELETE_COLUMN_INDEX, self.delete_column_delegate_attach_table) diff --git a/pasta_eln/GUI/ontology_configuration/ontology_props_tableview_data_model.py b/pasta_eln/GUI/ontology_configuration/ontology_props_tableview_data_model.py index d997054a..87aacca1 100644 --- a/pasta_eln/GUI/ontology_configuration/ontology_props_tableview_data_model.py +++ b/pasta_eln/GUI/ontology_configuration/ontology_props_tableview_data_model.py @@ -43,6 +43,16 @@ def __init__(self, 7: "re-order" } self.header_values: list[str] = list(self.data_name_map.values()) + self.column_widths: dict[int, int] = { + 0: 100, # Name column width + 1: 400, # Query column width + 2: 200, # list column width + 3: 100, # unit column width + 4: 150, # link column width + 5: 120, # required column width + 6: 120, # delete column width + 7: 120 # re-order column width + } self.columns_count: int = len(self.header_values) def setData(self, From 55eaef3fb44caf4f7a2146224cd7bda9135bc1a2 Mon Sep 17 00:00:00 2001 From: jmurugan-fzj Date: Wed, 13 Sep 2023 11:42:30 +0200 Subject: [PATCH 07/22] - Added the logic to adjust the column width of properties table and attachment table - Minor refactorings suggested by sourcery plugin --- ...tology_attachments_tableview_data_model.py | 8 +++- .../ontology_configuration_extended.py | 40 +++++++++---------- .../ontology_props_tableview_data_model.py | 6 +-- .../utility_functions.py | 20 ++++------ 4 files changed, 37 insertions(+), 37 deletions(-) diff --git a/pasta_eln/GUI/ontology_configuration/ontology_attachments_tableview_data_model.py b/pasta_eln/GUI/ontology_configuration/ontology_attachments_tableview_data_model.py index 51e961c3..3307e597 100644 --- a/pasta_eln/GUI/ontology_configuration/ontology_attachments_tableview_data_model.py +++ b/pasta_eln/GUI/ontology_configuration/ontology_attachments_tableview_data_model.py @@ -28,7 +28,7 @@ def __init__(self, parent (QWidget): Parent view or the widget """ super().__init__(parent) - self.logger = logging.getLogger(__name__ + "." + self.__class__.__name__) + self.logger = logging.getLogger(f"{__name__}.{self.__class__.__name__}") self.data_set = [] self.data_name_map = { 0: "location", @@ -36,5 +36,11 @@ def __init__(self, 2: "delete", 3: "re-order" } + self.column_widths: dict[int, int] = { + 0: 300, # Location column width + 1: 600, # Link column width + 2: 120, # Delete column width + 3: 120, # Re-order column width + } self.header_values: list[str] = list(self.data_name_map.values()) self.columns_count: int = len(self.header_values) diff --git a/pasta_eln/GUI/ontology_configuration/ontology_configuration_extended.py b/pasta_eln/GUI/ontology_configuration/ontology_configuration_extended.py index 1cb2dd71..f0542a23 100644 --- a/pasta_eln/GUI/ontology_configuration/ontology_configuration_extended.py +++ b/pasta_eln/GUI/ontology_configuration/ontology_configuration_extended.py @@ -16,16 +16,16 @@ from cloudant.document import Document from .create_type_dialog_extended import CreateTypeDialog +from .delete_column_delegate import DeleteColumnDelegate +from .ontology_attachments_tableview_data_model import OntologyAttachmentsTableViewModel from .ontology_config_generic_exception import OntologyConfigGenericException from .ontology_config_key_not_found_exception import \ OntologyConfigKeyNotFoundException -from .ontology_document_null_exception import OntologyDocumentNullException -from .ontology_attachments_tableview_data_model import OntologyAttachmentsTableViewModel from .ontology_configuration import Ui_OntologyConfigurationBaseForm from .ontology_configuration_constants import PROPS_TABLE_DELETE_COLUMN_INDEX, PROPS_TABLE_REORDER_COLUMN_INDEX, \ PROPS_TABLE_REQUIRED_COLUMN_INDEX, ATTACHMENT_TABLE_DELETE_COLUMN_INDEX, ATTACHMENT_TABLE_REORDER_COLUMN_INDEX +from .ontology_document_null_exception import OntologyDocumentNullException from .ontology_props_tableview_data_model import OntologyPropsTableViewModel -from .delete_column_delegate import DeleteColumnDelegate from .reorder_column_delegate import ReorderColumnDelegate from .required_column_delegate import RequiredColumnDelegate from .utility_functions import adjust_ontology_data_to_v3, show_message, \ @@ -53,7 +53,7 @@ def __init__(self, Raises: OntologyDocumentNullException: Raised when passed in argument @ontology_document is null. """ - self.logger = logging.getLogger(__name__ + "." + self.__class__.__name__) + self.logger = logging.getLogger(f"{__name__}.{self.__class__.__name__}") self.ontology_loaded: bool = False self.ontology_types: Any = {} @@ -88,8 +88,8 @@ def __init__(self, self.reorder_column_delegate_props_table) self.typePropsTableView.setModel(self.props_table_data_model) - for index, width in self.props_table_data_model.column_widths.items(): - self.typePropsTableView.setColumnWidth(index, width) + for column_index, width in self.props_table_data_model.column_widths.items(): + self.typePropsTableView.setColumnWidth(column_index, width) self.typeAttachmentsTableView.setItemDelegateForColumn( ATTACHMENT_TABLE_DELETE_COLUMN_INDEX, @@ -99,6 +99,9 @@ def __init__(self, self.reorder_column_delegate_attach_table) self.typeAttachmentsTableView.setModel(self.attachments_table_data_model) + for column_index, width in self.attachments_table_data_model.column_widths.items(): + self.typeAttachmentsTableView.setColumnWidth(column_index, width) + # Create the dialog for new type creation self.create_type_dialog = CreateTypeDialog(self.create_type_accepted_callback, self.create_type_rejected_callback) @@ -124,19 +127,18 @@ def type_combo_box_changed(self, if new_type_selected not in self.ontology_types: raise OntologyConfigKeyNotFoundException(f"Key {new_type_selected} " f"not found in ontology_types", {}) - if new_type_selected in self.ontology_types: - selected_type = self.ontology_types.get(new_type_selected) - # Get the properties for the selected type and store the list in selected_type_properties - self.selected_type_properties = selected_type.get('prop') + selected_type = self.ontology_types.get(new_type_selected) + # Get the properties for the selected type and store the list in selected_type_properties + self.selected_type_properties = selected_type.get('prop') - # Type label is set in a line edit - self.typeLabelLineEdit.setText(selected_type.get('label')) + # Type label is set in a line edit + self.typeLabelLineEdit.setText(selected_type.get('label')) - # Type link is set in a line edit - self.typeLinkLineEdit.setText(selected_type.get('link')) + # Type link is set in a line edit + self.typeLinkLineEdit.setText(selected_type.get('link')) - # Gets the attachment data from selected type and set it in table view - self.attachments_table_data_model.update(selected_type.get('attachments')) + # Gets the attachment data from selected type and set it in table view + self.attachments_table_data_model.update(selected_type.get('attachments')) # Reset the props category combo-box self.propsCategoryComboBox.addItems(list(self.selected_type_properties.keys()) @@ -402,11 +404,7 @@ def get_gui(ontology_document: Document) -> tuple[ """ instance = QApplication.instance() - if instance is None: - application = QApplication(sys.argv) - else: - application = instance - + application = QApplication(sys.argv) if instance is None else instance ontology_form: OntologyConfigurationForm = OntologyConfigurationForm(ontology_document) return application, ontology_form.instance, ontology_form diff --git a/pasta_eln/GUI/ontology_configuration/ontology_props_tableview_data_model.py b/pasta_eln/GUI/ontology_configuration/ontology_props_tableview_data_model.py index 87aacca1..07ff273f 100644 --- a/pasta_eln/GUI/ontology_configuration/ontology_props_tableview_data_model.py +++ b/pasta_eln/GUI/ontology_configuration/ontology_props_tableview_data_model.py @@ -45,10 +45,10 @@ def __init__(self, self.header_values: list[str] = list(self.data_name_map.values()) self.column_widths: dict[int, int] = { 0: 100, # Name column width - 1: 400, # Query column width - 2: 200, # list column width + 1: 300, # Query column width + 2: 150, # list column width 3: 100, # unit column width - 4: 150, # link column width + 4: 100, # link column width 5: 120, # required column width 6: 120, # delete column width 7: 120 # re-order column width diff --git a/pasta_eln/GUI/ontology_configuration/utility_functions.py b/pasta_eln/GUI/ontology_configuration/utility_functions.py index 50cab182..73c3669f 100644 --- a/pasta_eln/GUI/ontology_configuration/utility_functions.py +++ b/pasta_eln/GUI/ontology_configuration/utility_functions.py @@ -34,9 +34,8 @@ def is_click_within_bounds(event: QEvent, click_x = e.x() click_y = e.y() r = option.rect - if r.left() < click_x < r.left() + r.width(): - if r.top() < click_y < r.top() + r.height(): - return True + return (r.left() < click_x < r.left() + r.width() + and r.top() < click_y < r.top() + r.height()) return False @@ -50,10 +49,10 @@ def adjust_ontology_data_to_v3(ontology_doc: Document) -> None: """ if not ontology_doc: return None - type_structures = {} - for data in ontology_doc: - if isinstance(ontology_doc[data], dict): - type_structures[data] = ontology_doc[data] + type_structures = { + data: ontology_doc[data] + for data in ontology_doc if isinstance(ontology_doc[data], dict) + } ontology_doc["-version"] = 3 if type_structures: for _, type_structure in type_structures.items(): @@ -132,8 +131,5 @@ def get_db(db_name: str, if logger: logger.error(f'Could not connect with username+password to local server, error: {ex}') return None - if db_name in client.all_dbs(): - db_instance = client[db_name] - else: - db_instance = client.create_database(db_name) - return db_instance + return (client[db_name] + if db_name in client.all_dbs() else client.create_database(db_name)) From 9df3487c25043dfbcecf1c926512090851c73ad6 Mon Sep 17 00:00:00 2001 From: jmurugan-fzj Date: Wed, 13 Sep 2023 12:31:29 +0200 Subject: [PATCH 08/22] - Reverted the removal of cancel button --- .../ontology_configuration/ontology_configuration.py | 7 ++++++- .../ontology_configuration/ontology_configuration.ui | 12 +++++++++++- 2 files changed, 17 insertions(+), 2 deletions(-) diff --git a/pasta_eln/GUI/ontology_configuration/ontology_configuration.py b/pasta_eln/GUI/ontology_configuration/ontology_configuration.py index c0044792..09b94777 100644 --- a/pasta_eln/GUI/ontology_configuration/ontology_configuration.py +++ b/pasta_eln/GUI/ontology_configuration/ontology_configuration.py @@ -181,6 +181,9 @@ def setupUi(self, OntologyConfigurationBaseForm): self.helpPushButton = QtWidgets.QPushButton(parent=self.verticalLayoutWidget_2) self.helpPushButton.setObjectName("helpPushButton") self.headerHorizontalLayout.addWidget(self.helpPushButton) + self.cancelPushButton = QtWidgets.QPushButton(parent=self.verticalLayoutWidget_2) + self.cancelPushButton.setObjectName("cancelPushButton") + self.headerHorizontalLayout.addWidget(self.cancelPushButton) self.mainGridLayout.addLayout(self.headerHorizontalLayout, 0, 0, 1, 1) self.gridLayout.addWidget(self.mainWidget, 0, 1, 1, 1) @@ -205,7 +208,7 @@ def retranslateUi(self, OntologyConfigurationBaseForm): self.typeLabelLineEdit.setToolTip(_translate("OntologyConfigurationBaseForm", "Modify the label property of the type")) self.typeLabelLineEdit.setPlaceholderText(_translate("OntologyConfigurationBaseForm", "Modify the type label here")) self.typeLinkLineEdit.setToolTip(_translate("OntologyConfigurationBaseForm", "Enter the link/url to be associated with this data-type")) - self.typeLinkLineEdit.setPlaceholderText(_translate("OntologyConfigurationBaseForm", "Enter the type for the link")) + self.typeLinkLineEdit.setPlaceholderText(_translate("OntologyConfigurationBaseForm", "Enter the uri for the type")) self.addTypePushButton.setToolTip(_translate("OntologyConfigurationBaseForm", "Add a new type (structural or normal type) to the ontology data set.")) self.addTypePushButton.setText(_translate("OntologyConfigurationBaseForm", "+ Add")) self.deleteTypePushButton.setToolTip(_translate("OntologyConfigurationBaseForm", "Delete the type with the full properties completely")) @@ -222,6 +225,8 @@ def retranslateUi(self, OntologyConfigurationBaseForm): self.saveOntologyPushButton.setText(_translate("OntologyConfigurationBaseForm", "Save")) self.helpPushButton.setToolTip(_translate("OntologyConfigurationBaseForm", "Navigate to the help page")) self.helpPushButton.setText(_translate("OntologyConfigurationBaseForm", "Help")) + self.cancelPushButton.setToolTip(_translate("OntologyConfigurationBaseForm", "Close the editor")) + self.cancelPushButton.setText(_translate("OntologyConfigurationBaseForm", "Cancel")) if __name__ == "__main__": diff --git a/pasta_eln/GUI/ontology_configuration/ontology_configuration.ui b/pasta_eln/GUI/ontology_configuration/ontology_configuration.ui index 98a853c0..dc459781 100644 --- a/pasta_eln/GUI/ontology_configuration/ontology_configuration.ui +++ b/pasta_eln/GUI/ontology_configuration/ontology_configuration.ui @@ -295,7 +295,7 @@ Enter the link/url to be associated with this data-type - Enter the type for the link + Enter the uri for the type @@ -514,6 +514,16 @@ + + + + Close the editor + + + Cancel + + + From 3df8cff63065dd83aa61dc77a1eb82121eaca8ab Mon Sep 17 00:00:00 2001 From: jmurugan-fzj Date: Wed, 13 Sep 2023 15:07:33 +0200 Subject: [PATCH 09/22] - Enabled cancel button functionality ad adjusted the needed tests --- .../ontology_configuration_extended.py | 1 + tests/app_tests/common/fixtures.py | 4 ++-- .../test_ontology_configuration_extended.py | 1 + .../test_ontology_config_configuration_extended.py | 7 ++++--- 4 files changed, 8 insertions(+), 5 deletions(-) diff --git a/pasta_eln/GUI/ontology_configuration/ontology_configuration_extended.py b/pasta_eln/GUI/ontology_configuration/ontology_configuration_extended.py index f0542a23..b4b04d07 100644 --- a/pasta_eln/GUI/ontology_configuration/ontology_configuration_extended.py +++ b/pasta_eln/GUI/ontology_configuration/ontology_configuration_extended.py @@ -309,6 +309,7 @@ def setup_slots(self) -> None: self.deletePropsCategoryPushButton.clicked.connect(self.delete_selected_prop_category) self.deleteTypePushButton.clicked.connect(self.delete_selected_type) self.addTypePushButton.clicked.connect(self.show_create_type_dialog) + self.cancelPushButton.clicked.connect(self.instance.close) # Slots for the combo-boxes self.typeComboBox.currentTextChanged.connect(self.type_combo_box_changed) diff --git a/tests/app_tests/common/fixtures.py b/tests/app_tests/common/fixtures.py index c4d4179e..8c567c68 100644 --- a/tests/app_tests/common/fixtures.py +++ b/tests/app_tests/common/fixtures.py @@ -65,6 +65,7 @@ def configuration_extended(mocker) -> OntologyConfigurationForm: mocker.patch.object(OntologyConfigurationForm, 'deletePropsCategoryPushButton', create=True) mocker.patch.object(OntologyConfigurationForm, 'deleteTypePushButton', create=True) mocker.patch.object(OntologyConfigurationForm, 'addTypePushButton', create=True) + mocker.patch.object(OntologyConfigurationForm, 'cancelPushButton', create=True) mocker.patch.object(OntologyConfigurationForm, 'typeComboBox', create=True) mocker.patch.object(OntologyConfigurationForm, 'propsCategoryComboBox', create=True) mocker.patch.object(OntologyConfigurationForm, 'typeLabelLineEdit', create=True) @@ -74,8 +75,7 @@ def configuration_extended(mocker) -> OntologyConfigurationForm: mocker.patch.object(OntologyConfigurationForm, 'delete_column_delegate_attach_table', create=True) mocker.patch.object(OntologyConfigurationForm, 'reorder_column_delegate_attach_table', create=True) mocker.patch.object(CreateTypeDialog, '__new__') - config_instance = OntologyConfigurationForm(mock_document) - return config_instance + return OntologyConfigurationForm(mock_document) @fixture() diff --git a/tests/app_tests/component_tests/test_ontology_configuration_extended.py b/tests/app_tests/component_tests/test_ontology_configuration_extended.py index 5f8dfc2c..8cf4af78 100644 --- a/tests/app_tests/component_tests/test_ontology_configuration_extended.py +++ b/tests/app_tests/component_tests/test_ontology_configuration_extended.py @@ -45,6 +45,7 @@ def test_component_launch_should_display_all_ui_elements(self, assert ui_form.addTypePushButton is not None, "Add type button not loaded!" assert ui_form.addPropsRowPushButton is not None, "Add property row button not loaded!" assert ui_form.addPropsCategoryPushButton is not None, "Add property category button not loaded!" + assert ui_form.cancelPushButton is not None, "Cancel button not loaded!" assert ui_form.typeLabelLineEdit is not None, "Data type line edit not loaded!" assert ui_form.typeLinkLineEdit is not None, "Data type link line edit not loaded!" assert ui_form.addPropsCategoryLineEdit is not None, "Property category line edit not loaded!" diff --git a/tests/app_tests/unit_tests/test_ontology_config_configuration_extended.py b/tests/app_tests/unit_tests/test_ontology_config_configuration_extended.py index bcc80273..3bf01f3d 100644 --- a/tests/app_tests/unit_tests/test_ontology_config_configuration_extended.py +++ b/tests/app_tests/unit_tests/test_ontology_config_configuration_extended.py @@ -12,13 +12,13 @@ from pasta_eln.GUI.ontology_configuration.create_type_dialog_extended import CreateTypeDialog from pasta_eln.GUI.ontology_configuration.delete_column_delegate import DeleteColumnDelegate +from pasta_eln.GUI.ontology_configuration.ontology_attachments_tableview_data_model import \ + OntologyAttachmentsTableViewModel from pasta_eln.GUI.ontology_configuration.ontology_config_generic_exception import OntologyConfigGenericException from pasta_eln.GUI.ontology_configuration.ontology_config_key_not_found_exception import \ OntologyConfigKeyNotFoundException -from pasta_eln.GUI.ontology_configuration.ontology_document_null_exception import OntologyDocumentNullException -from pasta_eln.GUI.ontology_configuration.ontology_attachments_tableview_data_model import \ - OntologyAttachmentsTableViewModel from pasta_eln.GUI.ontology_configuration.ontology_configuration_extended import OntologyConfigurationForm, get_gui +from pasta_eln.GUI.ontology_configuration.ontology_document_null_exception import OntologyDocumentNullException from pasta_eln.GUI.ontology_configuration.ontology_props_tableview_data_model import OntologyPropsTableViewModel from pasta_eln.GUI.ontology_configuration.reorder_column_delegate import ReorderColumnDelegate from pasta_eln.GUI.ontology_configuration.required_column_delegate import RequiredColumnDelegate @@ -49,6 +49,7 @@ def test_instantiation_should_succeed(self, mocker.patch.object(OntologyConfigurationForm, 'deletePropsCategoryPushButton', create=True) mocker.patch.object(OntologyConfigurationForm, 'deleteTypePushButton', create=True) mocker.patch.object(OntologyConfigurationForm, 'addTypePushButton', create=True) + mocker.patch.object(OntologyConfigurationForm, 'cancelPushButton', create=True) mocker.patch.object(OntologyConfigurationForm, 'typeComboBox', create=True) mocker.patch.object(OntologyConfigurationForm, 'propsCategoryComboBox', create=True) mocker.patch.object(OntologyConfigurationForm, 'typeLabelLineEdit', create=True) From 0123224ca48ca80ab6a03a466fbd88d1d014abff Mon Sep 17 00:00:00 2001 From: jmurugan-fzj Date: Wed, 13 Sep 2023 17:57:27 +0200 Subject: [PATCH 10/22] - Corrected the mis-alignment of radio button in required column of properties table - Adjusted tests --- .../ontology_configuration/required_column_delegate.py | 9 +++++---- .../test_ontology_config_required_column_delegate.py | 10 +++++++--- .../test_ontology_config_utility_functions.py | 2 +- 3 files changed, 13 insertions(+), 8 deletions(-) diff --git a/pasta_eln/GUI/ontology_configuration/required_column_delegate.py b/pasta_eln/GUI/ontology_configuration/required_column_delegate.py index d457bacb..29f1016f 100644 --- a/pasta_eln/GUI/ontology_configuration/required_column_delegate.py +++ b/pasta_eln/GUI/ontology_configuration/required_column_delegate.py @@ -13,7 +13,7 @@ from PySide6.QtCore import QModelIndex, QPersistentModelIndex, QEvent, QAbstractItemModel, QRect, Qt from PySide6.QtGui import QPainter from PySide6.QtWidgets import QStyledItemDelegate, QWidget, QStyleOptionViewItem, QStyleOptionButton, \ - QStyle, QApplication + QStyle, QApplication, QRadioButton class RequiredColumnDelegate(QStyledItemDelegate): @@ -26,7 +26,7 @@ def __init__(self) -> None: Constructor """ super().__init__() - self.logger = logging.getLogger(__name__ + "." + self.__class__.__name__) + self.logger = logging.getLogger(f"{__name__}.{self.__class__.__name__}") def paint(self, painter: QPainter, @@ -45,7 +45,8 @@ def paint(self, widget = option.widget style = widget.style() if widget else QApplication.style() opt = QStyleOptionButton() - opt.rect = QRect(option.rect.left() + option.rect.width() / 2 - 5, + radio_button = QRadioButton() + opt.rect = QRect(option.rect.left() + option.rect.width() / 2 - 10, option.rect.top(), option.rect.width(), option.rect.height()) @@ -53,7 +54,7 @@ def paint(self, opt.state = QStyle.State_On \ if is_required \ else QStyle.State_Off - style.drawControl(QStyle.CE_RadioButton, opt, painter, widget) + style.drawControl(QStyle.CE_RadioButton, opt, painter, radio_button) def editorEvent(self, event: QEvent, diff --git a/tests/app_tests/unit_tests/test_ontology_config_required_column_delegate.py b/tests/app_tests/unit_tests/test_ontology_config_required_column_delegate.py index 97bc7342..0ce5e43e 100644 --- a/tests/app_tests/unit_tests/test_ontology_config_required_column_delegate.py +++ b/tests/app_tests/unit_tests/test_ontology_config_required_column_delegate.py @@ -8,7 +8,7 @@ # You should have received a copy of the license with this file. Please refer the license file for more information. import pytest from PySide6.QtCore import QRect, QEvent, Qt -from PySide6.QtWidgets import QStyleOptionButton, QApplication, QStyle, QStyledItemDelegate +from PySide6.QtWidgets import QStyleOptionButton, QApplication, QStyle, QStyledItemDelegate, QRadioButton from tests.app_tests.common.fixtures import required_delegate from tests.app_tests.common.test_delegate_funcs_common import delegate_editor_method_common @@ -63,10 +63,13 @@ def verify_delegate_paint_method(mocker, required_delegate, editor_data_value): mock_option_rect = mocker.patch("PySide6.QtCore.QRect") mock_index = mocker.patch("PySide6.QtCore.QModelIndex") mock_option_widget = mocker.patch("PySide6.QtWidgets.QRadioButton") + mock_option_radio_button = mocker.patch("PySide6.QtWidgets.QRadioButton") mock_button_option = mocker.patch("PySide6.QtWidgets.QStyleOptionButton") mock_style = mocker.patch("PySide6.QtWidgets.QStyle") mocker.patch.object(QStyleOptionButton, "__new__", lambda x: mock_button_option) + mocker.patch.object(QRadioButton, "__new__", + lambda x: mock_option_radio_button) mocker.patch.object(mock_option, "widget", None) mocker.patch.object(mock_option, "rect", mock_option_rect) mocker.patch.object(mock_option_rect, "left", mocker.MagicMock(return_value=5)) @@ -83,11 +86,12 @@ def verify_delegate_paint_method(mocker, required_delegate, editor_data_value): mocker.MagicMock(return_value=mock_style)) draw_control_spy = mocker.spy(mock_style, 'drawControl') required_delegate.paint(mock_painter, mock_option, mock_index) - draw_control_spy.assert_called_once_with(QStyle.CE_RadioButton, mock_button_option, mock_painter, None) + draw_control_spy.assert_called_once_with(QStyle.CE_RadioButton, mock_button_option, mock_painter, + mock_option_radio_button) assert mock_option.rect.left.call_count == 1, "rect.left should be called once" assert mock_option.rect.top.call_count == 1, "rect.top should be called once" assert mock_option.rect.width.call_count == 2, "rect.top should be called twice" assert mock_option.rect.height.call_count == 1, "rect.height should be called once" - assert mock_button_option.rect == QRect(2, 5, 5, 5), "rect should be the expected QRect(2, 5, 5, 5)" + assert mock_button_option.rect == QRect(-2, 5, 5, 5), "rect should be the expected QRect(-2, 5, 5, 5)" assert mock_button_option.state == QStyle.State_On if editor_data_value == 'True' else QStyle.State_Off, \ f"button state should be {'on' if editor_data_value == 'True' else 'off'}" diff --git a/tests/app_tests/unit_tests/test_ontology_config_utility_functions.py b/tests/app_tests/unit_tests/test_ontology_config_utility_functions.py index fe7b3593..38dceeca 100644 --- a/tests/app_tests/unit_tests/test_ontology_config_utility_functions.py +++ b/tests/app_tests/unit_tests/test_ontology_config_utility_functions.py @@ -76,7 +76,7 @@ def test_is_click_within_bounds_when_outside_bounds_returns_false(self, def test_adjust_ontology_data_to_v3_when_empty_document_do_nothing(self, mocker): - contents = {} + contents = {"-version": 2} mock_doc = self.create_mock_doc(contents, mocker) assert adjust_ontology_data_to_v3(mock_doc) is None, "adjust_ontology_data_to_v3 should return None" assert list(contents.keys()) == ["-version"], "Only version should be added" From 38aa5265873091707ae768e71f0596bef2e3937a Mon Sep 17 00:00:00 2001 From: jmurugan-fzj Date: Wed, 13 Sep 2023 18:20:27 +0200 Subject: [PATCH 11/22] - Corrected the alignments of the UI and minor other corrections --- .../ontology_configuration.py | 16 ++++++++-------- .../ontology_configuration.ui | 6 +++--- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/pasta_eln/GUI/ontology_configuration/ontology_configuration.py b/pasta_eln/GUI/ontology_configuration/ontology_configuration.py index 09b94777..6dd7dedb 100644 --- a/pasta_eln/GUI/ontology_configuration/ontology_configuration.py +++ b/pasta_eln/GUI/ontology_configuration/ontology_configuration.py @@ -143,16 +143,16 @@ def setupUi(self, OntologyConfigurationBaseForm): spacerItem = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Policy.Expanding, QtWidgets.QSizePolicy.Policy.Minimum) self.propsTableButtonHorizontalLayout.addItem(spacerItem) self.mainGridLayout.addLayout(self.propsTableButtonHorizontalLayout, 8, 0, 1, 1) - self.structureTableHorizontalLayout = QtWidgets.QHBoxLayout() - self.structureTableHorizontalLayout.setContentsMargins(0, 5, 0, 5) - self.structureTableHorizontalLayout.setObjectName("structureTableHorizontalLayout") + self.attchmentTableButtonsHorizontalLayout = QtWidgets.QHBoxLayout() + self.attchmentTableButtonsHorizontalLayout.setContentsMargins(0, 5, 0, 5) + self.attchmentTableButtonsHorizontalLayout.setObjectName("attchmentTableButtonsHorizontalLayout") self.addAttachmentPushButton = QtWidgets.QPushButton(parent=self.verticalLayoutWidget_2) self.addAttachmentPushButton.setMinimumSize(QtCore.QSize(200, 0)) self.addAttachmentPushButton.setObjectName("addAttachmentPushButton") - self.structureTableHorizontalLayout.addWidget(self.addAttachmentPushButton) + self.attchmentTableButtonsHorizontalLayout.addWidget(self.addAttachmentPushButton) spacerItem1 = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Policy.Expanding, QtWidgets.QSizePolicy.Policy.Minimum) - self.structureTableHorizontalLayout.addItem(spacerItem1) - self.mainGridLayout.addLayout(self.structureTableHorizontalLayout, 13, 0, 1, 1) + self.attchmentTableButtonsHorizontalLayout.addItem(spacerItem1) + self.mainGridLayout.addLayout(self.attchmentTableButtonsHorizontalLayout, 13, 0, 1, 1) self.propertiesTableHeaderHorizontalLayout = QtWidgets.QHBoxLayout() self.propertiesTableHeaderHorizontalLayout.setContentsMargins(-1, 5, -1, 5) self.propertiesTableHeaderHorizontalLayout.setObjectName("propertiesTableHeaderHorizontalLayout") @@ -207,8 +207,8 @@ def retranslateUi(self, OntologyConfigurationBaseForm): self.typeComboBox.setToolTip(_translate("OntologyConfigurationBaseForm", "Select the type from the loaded ontology")) self.typeLabelLineEdit.setToolTip(_translate("OntologyConfigurationBaseForm", "Modify the label property of the type")) self.typeLabelLineEdit.setPlaceholderText(_translate("OntologyConfigurationBaseForm", "Modify the type label here")) - self.typeLinkLineEdit.setToolTip(_translate("OntologyConfigurationBaseForm", "Enter the link/url to be associated with this data-type")) - self.typeLinkLineEdit.setPlaceholderText(_translate("OntologyConfigurationBaseForm", "Enter the uri for the type")) + self.typeLinkLineEdit.setToolTip(_translate("OntologyConfigurationBaseForm", "Enter the link/iri to be associated with this data-type")) + self.typeLinkLineEdit.setPlaceholderText(_translate("OntologyConfigurationBaseForm", "Enter the iri for the type")) self.addTypePushButton.setToolTip(_translate("OntologyConfigurationBaseForm", "Add a new type (structural or normal type) to the ontology data set.")) self.addTypePushButton.setText(_translate("OntologyConfigurationBaseForm", "+ Add")) self.deleteTypePushButton.setToolTip(_translate("OntologyConfigurationBaseForm", "Delete the type with the full properties completely")) diff --git a/pasta_eln/GUI/ontology_configuration/ontology_configuration.ui b/pasta_eln/GUI/ontology_configuration/ontology_configuration.ui index dc459781..1d83c8aa 100644 --- a/pasta_eln/GUI/ontology_configuration/ontology_configuration.ui +++ b/pasta_eln/GUI/ontology_configuration/ontology_configuration.ui @@ -292,10 +292,10 @@ - Enter the link/url to be associated with this data-type + Enter the link/iri to be associated with this data-type - Enter the uri for the type + Enter the iri for the type @@ -376,7 +376,7 @@ - + 0 From 8953bae630bb7d37fe4c903baf58127773f4faa6 Mon Sep 17 00:00:00 2001 From: jmurugan-fzj Date: Thu, 14 Sep 2023 16:18:07 +0200 Subject: [PATCH 12/22] - Corrected the display values for the types combo box to format "Structure level n" for the structure types - Added missing tests for the props table view model - Adjusted existing tests for the changes - Other minor refactorings --- .../create_type_dialog_extended.py | 3 +- .../ontology_configuration_extended.py | 24 ++++--- .../utility_functions.py | 51 ++++++++++++-- .../test_ontology_configuration_extended.py | 16 ++--- ..._ontology_config_configuration_extended.py | 34 +++++---- ...logy_config_create_type_dialog_extended.py | 2 +- ...t_ontology_config_table_view_data_model.py | 69 +++++++++++++++++++ 7 files changed, 162 insertions(+), 37 deletions(-) diff --git a/pasta_eln/GUI/ontology_configuration/create_type_dialog_extended.py b/pasta_eln/GUI/ontology_configuration/create_type_dialog_extended.py index 3ee18004..e07827b3 100644 --- a/pasta_eln/GUI/ontology_configuration/create_type_dialog_extended.py +++ b/pasta_eln/GUI/ontology_configuration/create_type_dialog_extended.py @@ -74,7 +74,8 @@ def structural_level_checkbox_callback(self) -> None: Returns: Nothing """ if self.structuralLevelCheckBox.isChecked(): - self.titleLineEdit.setText(self.next_struct_level if self.next_struct_level else "") + self.titleLineEdit.setText(self.next_struct_level.replace("x", "Structure level ") + if self.next_struct_level else "") self.titleLineEdit.setDisabled(True) else: self.titleLineEdit.clear() diff --git a/pasta_eln/GUI/ontology_configuration/ontology_configuration_extended.py b/pasta_eln/GUI/ontology_configuration/ontology_configuration_extended.py index b4b04d07..24172d3c 100644 --- a/pasta_eln/GUI/ontology_configuration/ontology_configuration_extended.py +++ b/pasta_eln/GUI/ontology_configuration/ontology_configuration_extended.py @@ -16,7 +16,6 @@ from cloudant.document import Document from .create_type_dialog_extended import CreateTypeDialog -from .delete_column_delegate import DeleteColumnDelegate from .ontology_attachments_tableview_data_model import OntologyAttachmentsTableViewModel from .ontology_config_generic_exception import OntologyConfigGenericException from .ontology_config_key_not_found_exception import \ @@ -26,10 +25,11 @@ PROPS_TABLE_REQUIRED_COLUMN_INDEX, ATTACHMENT_TABLE_DELETE_COLUMN_INDEX, ATTACHMENT_TABLE_REORDER_COLUMN_INDEX from .ontology_document_null_exception import OntologyDocumentNullException from .ontology_props_tableview_data_model import OntologyPropsTableViewModel +from .delete_column_delegate import DeleteColumnDelegate from .reorder_column_delegate import ReorderColumnDelegate from .required_column_delegate import RequiredColumnDelegate from .utility_functions import adjust_ontology_data_to_v3, show_message, \ - get_next_possible_structural_level_label + get_next_possible_structural_level_label, get_types_for_display, adapt_type class OntologyConfigurationForm(Ui_OntologyConfigurationBaseForm): @@ -123,6 +123,7 @@ def type_combo_box_changed(self, """ self.logger.info("New type selected in UI: {%s}", new_type_selected) self.clear_ui() + new_type_selected = adapt_type(new_type_selected) if new_type_selected and self.ontology_types: if new_type_selected not in self.ontology_types: raise OntologyConfigKeyNotFoundException(f"Key {new_type_selected} " @@ -200,8 +201,8 @@ def delete_selected_prop_category(self) -> None: self.propsCategoryComboBox.setCurrentIndex(len(self.selected_type_properties.keys()) - 1) return None - def update_structure_label(self, - modified_type_label: str) -> None: + def update_type_label(self, + modified_type_label: str) -> None: """ Value changed callback for the type label line edit @@ -211,6 +212,7 @@ def update_structure_label(self, Returns: Nothing """ current_type = self.typeComboBox.currentText() + current_type = adapt_type(current_type) if modified_type_label is not None and current_type in self.ontology_types: self.ontology_types.get(current_type)["label"] = modified_type_label @@ -224,6 +226,7 @@ def update_type_link(self, modified_link: str) -> None: Returns: Nothing """ current_type = self.typeComboBox.currentText() + current_type = adapt_type(current_type) if modified_link is not None and current_type in self.ontology_types: self.ontology_types.get(current_type)["link"] = modified_link @@ -234,6 +237,7 @@ def delete_selected_type(self) -> None: Returns: Nothing """ selected_type = self.typeComboBox.currentText() + selected_type = adapt_type(selected_type) if not self.ontology_loaded: show_message("Load the ontology data first....") return @@ -246,7 +250,7 @@ def delete_selected_type(self) -> None: self.ontology_types.pop(selected_type) self.ontology_document.pop(selected_type) self.typeComboBox.clear() - self.typeComboBox.addItems(self.ontology_types.keys()) + self.typeComboBox.addItems(get_types_for_display(self.ontology_types.keys())) self.typeComboBox.setCurrentIndex(0) def clear_ui(self) -> None: @@ -269,7 +273,9 @@ def create_type_accepted_callback(self) -> None: Returns: Nothing """ - title = self.create_type_dialog.titleLineEdit.text() + title = self.create_type_dialog.next_struct_level \ + if self.create_type_dialog.structuralLevelCheckBox.isChecked() \ + else self.create_type_dialog.titleLineEdit.text() label = self.create_type_dialog.labelLineEdit.text() self.create_type_dialog.clear_ui() self.create_new_type(title, label) @@ -316,7 +322,7 @@ def setup_slots(self) -> None: self.propsCategoryComboBox.currentTextChanged.connect(self.category_combo_box_changed) # Slots for line edits - self.typeLabelLineEdit.textChanged[str].connect(self.update_structure_label) + self.typeLabelLineEdit.textChanged[str].connect(self.update_type_label) self.typeLinkLineEdit.textChanged[str].connect(self.update_type_link) # Slots for the delegates @@ -344,7 +350,7 @@ def load_ontology_data(self) -> None: # Set the types in the type selector combo-box self.typeComboBox.clear() - self.typeComboBox.addItems(self.ontology_types.keys()) + self.typeComboBox.addItems(get_types_for_display(self.ontology_types.keys())) self.typeComboBox.setCurrentIndex(0) def save_ontology(self) -> None: @@ -390,7 +396,7 @@ def create_new_type(self, self.ontology_document[title] = empty_type self.ontology_types[title] = empty_type self.typeComboBox.clear() - self.typeComboBox.addItems(self.ontology_types.keys()) + self.typeComboBox.addItems(get_types_for_display(self.ontology_types.keys())) self.typeComboBox.setCurrentIndex(len(self.ontology_types) - 1) show_message(f"Type (title: {title} label: {label}) has been added....") diff --git a/pasta_eln/GUI/ontology_configuration/utility_functions.py b/pasta_eln/GUI/ontology_configuration/utility_functions.py index 73c3669f..beff1852 100644 --- a/pasta_eln/GUI/ontology_configuration/utility_functions.py +++ b/pasta_eln/GUI/ontology_configuration/utility_functions.py @@ -8,6 +8,7 @@ # # You should have received a copy of the license with this file. Please refer the license file for more information. import logging +import re from typing import Any from PySide6.QtCore import QEvent @@ -50,8 +51,8 @@ def adjust_ontology_data_to_v3(ontology_doc: Document) -> None: if not ontology_doc: return None type_structures = { - data: ontology_doc[data] - for data in ontology_doc if isinstance(ontology_doc[data], dict) + data: ontology_doc[data] + for data in ontology_doc if isinstance(ontology_doc[data], dict) } ontology_doc["-version"] = 3 if type_structures: @@ -92,10 +93,8 @@ def get_next_possible_structural_level_label(existing_type_labels: Any) -> str | """ if existing_type_labels is not None: if len(existing_type_labels) > 0: - import re - regexp = re.compile(r'^[Xx][0-9]+$') labels = [int(label.replace('x', '').replace('X', '')) - for label in existing_type_labels if regexp.match(label)] + for label in existing_type_labels if is_structural_level(label)] new_level = max(labels, default=-1) return f"x{new_level + 1}" else: @@ -133,3 +132,45 @@ def get_db(db_name: str, return None return (client[db_name] if db_name in client.all_dbs() else client.create_database(db_name)) + + +def get_types_for_display(types: list[str]) -> list[str]: + """ + Get the types for display by converting all structural types from format: xn -> 'Structure Level n' + Args: + types (list[str]): List of types + + Returns: Return the list of types after converting all structural types + + """ + name_prefix = "Structure level " + return [ + name.replace('x', name_prefix) if is_structural_level(name) else name + for name in types + ] + + +def adapt_type(title: str) -> str: + """ + Convert only structural type from format: 'Structure Level n' -> xn + Args: + title (str): Title to be adapted + + Returns: Adapted title in the needed format + + """ + return title.replace("Structure level ", "x") \ + if title and title.startswith("Structure level ") \ + else title + + +def is_structural_level(title: str) -> bool: + """ + Check if the title is a structural type + Args: + title (str): Title to be checked + + Returns: True/False + + """ + return re.compile(r'^[Xx][0-9]+$').match(title) is not None diff --git a/tests/app_tests/component_tests/test_ontology_configuration_extended.py b/tests/app_tests/component_tests/test_ontology_configuration_extended.py index 8cf4af78..3f083df3 100644 --- a/tests/app_tests/component_tests/test_ontology_configuration_extended.py +++ b/tests/app_tests/component_tests/test_ontology_configuration_extended.py @@ -21,6 +21,7 @@ from pytestqt.qtbot import QtBot from pasta_eln.GUI.ontology_configuration.ontology_configuration_extended import OntologyConfigurationForm +from pasta_eln.GUI.ontology_configuration.utility_functions import adapt_type, get_types_for_display from tests.app_tests.common.fixtures import ontology_editor_gui, ontology_doc_mock, props_column_names, \ attachments_column_names @@ -66,10 +67,10 @@ def test_component_load_button_click_should_load_ontology_data(self, == []), "Type combo box should not be loaded!" assert ui_form.loadOntologyPushButton.click() is None, "Load button not clicked!" assert ([ui_form.typeComboBox.itemText(i) for i in range(ui_form.typeComboBox.count())] - == ontology_doc_mock.types_list()), "Type combo box not loaded!" - assert (ui_form.typeComboBox.currentText() + == get_types_for_display(ontology_doc_mock.types_list())), "Type combo box not loaded!" + assert (adapt_type(ui_form.typeComboBox.currentText()) == ontology_doc_mock.types_list()[0]), "Type combo box should be selected to first item" - selected_type = ontology_doc_mock.types()[ui_form.typeComboBox.currentText()] + selected_type = ontology_doc_mock.types()[adapt_type(ui_form.typeComboBox.currentText())] assert (ui_form.typeLabelLineEdit.text() == selected_type["label"]), "Data type label line edit not loaded!" assert (ui_form.typeLinkLineEdit.text() == @@ -169,9 +170,9 @@ def test_component_delete_selected_type_with_loaded_ontology_should_delete_and_u f"Deleted type:{current_selected_type} should not exist in combo list!" assert (previous_types_count - 1 == ui_form.typeComboBox.count()), \ f"Combo list should have {previous_types_count - 1} items!" - assert ui_form.typeComboBox.currentText() == ontology_doc_mock.types_list()[1], \ + assert adapt_type(ui_form.typeComboBox.currentText()) == ontology_doc_mock.types_list()[1], \ "Type combo box should be selected to second item" - selected_type = ontology_doc_mock.types()[ui_form.typeComboBox.currentText()] + selected_type = ontology_doc_mock.types()[adapt_type(ui_form.typeComboBox.currentText())] assert ui_form.typeLabelLineEdit.text() == selected_type["label"], \ "Type label line edit should be selected to second item" assert ui_form.typeLinkLineEdit.text() == selected_type["link"], \ @@ -201,11 +202,10 @@ def test_component_add_selected_type_with_loaded_ontology_should_delete_and_upda f"Deleted type:{current_selected_type} should not exist in combo list!" assert (previous_types_count - 1 == ui_form.typeComboBox.count()), \ f"Combo list should have {previous_types_count - 1} items!" - assert ui_form.typeComboBox.currentText() == ontology_doc_mock.types_list()[1], \ + assert adapt_type(ui_form.typeComboBox.currentText()) == ontology_doc_mock.types_list()[1], \ "Type combo box should be selected to second item" types = ontology_doc_mock.types() - text = ui_form.typeComboBox.currentText() - selected_type = types[text] + selected_type = types[adapt_type(ui_form.typeComboBox.currentText())] assert ui_form.typeLabelLineEdit.text() == selected_type["label"], \ "Type label line edit should be selected to second item" assert ui_form.typeLinkLineEdit.text() == selected_type["link"], \ diff --git a/tests/app_tests/unit_tests/test_ontology_config_configuration_extended.py b/tests/app_tests/unit_tests/test_ontology_config_configuration_extended.py index 3bf01f3d..5604d5a6 100644 --- a/tests/app_tests/unit_tests/test_ontology_config_configuration_extended.py +++ b/tests/app_tests/unit_tests/test_ontology_config_configuration_extended.py @@ -22,6 +22,7 @@ from pasta_eln.GUI.ontology_configuration.ontology_props_tableview_data_model import OntologyPropsTableViewModel from pasta_eln.GUI.ontology_configuration.reorder_column_delegate import ReorderColumnDelegate from pasta_eln.GUI.ontology_configuration.required_column_delegate import RequiredColumnDelegate +from pasta_eln.GUI.ontology_configuration.utility_functions import get_types_for_display from tests.app_tests.common.fixtures import configuration_extended, ontology_doc_mock @@ -379,7 +380,7 @@ def test_update_structure_label_should_do_expected(self, get_ontology_types_spy = mocker.spy(configuration_extended.ontology_types, 'get') if modified_type_label: - assert configuration_extended.update_structure_label(modified_type_label) is None, "Nothing should be returned" + assert configuration_extended.update_type_label(modified_type_label) is None, "Nothing should be returned" if ontology_types is not None and current_type in ontology_types: get_ontology_types_spy.assert_called_once_with(current_type) assert ontology_types[current_type]["label"] == modified_type_label @@ -484,7 +485,7 @@ def test_delete_selected_type_should_do_expected(self, pop_items_selected_ontology_document_spy.assert_called_once_with(selected_type) clear_category_combo_box_spy.assert_called_once_with() add_items_selected_spy.assert_called_once_with( - ontology_types.keys() + get_types_for_display(ontology_types.keys()) ) set_current_index_category_combo_box_spy.assert_called_once_with(0) assert selected_type not in ontology_types and ontology_document, "selected_type should be deleted" @@ -497,20 +498,25 @@ def test_delete_selected_type_should_do_expected(self, add_items_selected_spy.assert_not_called() set_current_index_category_combo_box_spy.assert_not_called() - @pytest.mark.parametrize("new_title, new_label", [ - (None, None), - ("x0", None), - (None, "x2"), - ("x3", "x3"), - ("instrument", "new Instrument") + @pytest.mark.parametrize("new_title, new_label, is_structure_level", [ + (None, None, False), + ("x0", None, True), + (None, "x2", True), + ("x3", "x3", True), + ("instrument", "new Instrument", False) ]) def test_create_type_accepted_callback_should_do_expected(self, mocker, configuration_extended: configuration_extended, new_title, - new_label): + new_label, + is_structure_level): mocker.patch.object(configuration_extended, 'create_type_dialog', create=True) mocker.patch.object(configuration_extended.create_type_dialog, 'titleLineEdit', create=True) + mocker.patch.object(configuration_extended.create_type_dialog, 'next_struct_level', new_title, create=True) + mock_check_box = mocker.patch.object(configuration_extended.create_type_dialog, 'structuralLevelCheckBox', + create=True) + mocker.patch.object(mock_check_box, 'isChecked', return_value=is_structure_level, create=True) mocker.patch.object(configuration_extended.create_type_dialog.titleLineEdit, 'text', return_value=new_title) mocker.patch.object(configuration_extended.create_type_dialog, 'labelLineEdit', create=True) mocker.patch.object(configuration_extended.create_type_dialog.labelLineEdit, 'text', return_value=new_label) @@ -520,7 +526,8 @@ def test_create_type_accepted_callback_should_do_expected(self, text_label_line_edit_text_spy = mocker.spy(configuration_extended.create_type_dialog.labelLineEdit, 'text') assert configuration_extended.create_type_accepted_callback() is None, "Nothing should be returned" - text_title_line_edit_text_spy.assert_called_once_with() + if not is_structure_level: + text_title_line_edit_text_spy.assert_called_once_with() text_label_line_edit_text_spy.assert_called_once_with() clear_ui_spy.assert_called_once_with() create_new_type_spy.assert_called_once_with( @@ -610,7 +617,7 @@ def test_setup_slots_should_do_expected(self, # Slots for line edits configuration_extended.typeLabelLineEdit.textChanged[str].connect.assert_called_once_with( - configuration_extended.update_structure_label) + configuration_extended.update_type_label) configuration_extended.typeLinkLineEdit.textChanged[str].connect.assert_called_once_with( configuration_extended.update_type_link) @@ -647,7 +654,8 @@ def test_load_ontology_data_should_with_variant_types_of_doc_should_do_expected( return assert configuration_extended.load_ontology_data() is None, "Nothing should be returned" configuration_extended.typeComboBox.clear.assert_called_once_with() - configuration_extended.typeComboBox.addItems.assert_called_once_with(configuration_extended.ontology_types.keys()) + configuration_extended.typeComboBox.addItems.assert_called_once_with( + get_types_for_display(configuration_extended.ontology_types.keys())) configuration_extended.typeComboBox.setCurrentIndex.assert_called_once_with(0) for data in ontology_document: if type(data) is dict: @@ -774,7 +782,7 @@ def test_create_new_type_should_do_expected(self, })) configuration_extended.typeComboBox.clear.assert_called_once_with() configuration_extended.typeComboBox.addItems.assert_called_once_with( - configuration_extended.ontology_types.keys()) + get_types_for_display(configuration_extended.ontology_types.keys())) mock_show_message.assert_called_once_with(f"Type (title: {new_title} label: {new_label}) has been added....") @pytest.mark.parametrize("instance_exists", [True, False]) diff --git a/tests/app_tests/unit_tests/test_ontology_config_create_type_dialog_extended.py b/tests/app_tests/unit_tests/test_ontology_config_create_type_dialog_extended.py index 4f68c28c..fdee1b1c 100644 --- a/tests/app_tests/unit_tests/test_ontology_config_create_type_dialog_extended.py +++ b/tests/app_tests/unit_tests/test_ontology_config_create_type_dialog_extended.py @@ -34,7 +34,7 @@ def test_structural_level_checkbox_callback_should_do_expected(self, clear_line_edit_spy = mocker.spy(mock_line_edit, 'clear') assert create_type_dialog_mock.structural_level_checkbox_callback() is None, "create_type_dialog_mock.structural_level_checkbox_callback() should return None" if checked: - set_text_line_edit_spy.assert_called_once_with(next_level) + set_text_line_edit_spy.assert_called_once_with(next_level.replace("x", "Structure level ")) set_disabled_line_edit_spy.assert_called_once_with(True) else: clear_line_edit_spy.assert_called_once_with() diff --git a/tests/app_tests/unit_tests/test_ontology_config_table_view_data_model.py b/tests/app_tests/unit_tests/test_ontology_config_table_view_data_model.py index 5045cd55..78f9658a 100644 --- a/tests/app_tests/unit_tests/test_ontology_config_table_view_data_model.py +++ b/tests/app_tests/unit_tests/test_ontology_config_table_view_data_model.py @@ -9,6 +9,7 @@ import pytest from PySide6.QtCore import Qt +from pasta_eln.GUI.ontology_configuration.ontology_configuration_constants import PROPS_TABLE_LIST_COLUMN_INDEX from tests.app_tests.common.fixtures import table_model, props_table_model, attachments_table_model @@ -316,3 +317,71 @@ def test_data_models_basic_slot_re_order_data_should_do_expected(self, assert layout_changed_emit_spy.call_count == 1, "layout_changed_emit() should be called once" else: logger_warning_spy.assert_called_once_with("Invalid position: {%s}", re_order_position) + + @pytest.mark.parametrize("column_index, set_value, convert_value, is_valid, role, set_success", [ + (None, None, None, False, Qt.DisplayRole, True), + (0, None, None, False, Qt.DisplayRole, True), + (0, None, None, True, Qt.UserRole, True), + (PROPS_TABLE_LIST_COLUMN_INDEX, 'test', 'test', True, Qt.UserRole, True), + (PROPS_TABLE_LIST_COLUMN_INDEX, 'test test1', 'test test1', True, Qt.UserRole, True), + (PROPS_TABLE_LIST_COLUMN_INDEX, '1, 2', ['1', '2'], True, Qt.UserRole, True), + (PROPS_TABLE_LIST_COLUMN_INDEX, 'test, 12312', ['test', '12312'], True, Qt.UserRole, True), + (PROPS_TABLE_LIST_COLUMN_INDEX, ' test, 12312 12, 112', ['test', '12312 12', '112'], True, Qt.UserRole, True), + (None, None, None, False, Qt.EditRole, True), + ]) + def test_props_table_data_model_set_data_should_do_expected(self, + props_table_model: props_table_model, + mocker, + column_index, + set_value, + convert_value, + is_valid, + role, + set_success): + + mock_index = mocker.patch("PySide6.QtCore.QModelIndex") + mock_is_valid_spy = mocker.patch.object(mock_index, "isValid", return_value=is_valid) + mock_column_spy = mocker.patch.object(mock_index, "column", return_value=column_index) + mock_data_set_spy = mocker.patch( + "pasta_eln.GUI.ontology_configuration.ontology_tableview_data_model.OntologyTableViewModel.setData", + return_value=set_success) + assert props_table_model.setData(mock_index, set_value, role) is set_success, \ + "set_data() should return expected value" + mock_is_valid_spy.assert_called_once_with() + if is_valid: + mock_column_spy.assert_called_once_with() + mock_data_set_spy.assert_called_once_with(mock_index, convert_value, role) + + @pytest.mark.parametrize("column_index, is_valid, role, base_return_data, return_value", [ + (None, False, Qt.DisplayRole, '', ''), + (0, False, Qt.DisplayRole, 'True', 'True'), + (0, True, Qt.UserRole, '', ''), + (PROPS_TABLE_LIST_COLUMN_INDEX, True, Qt.UserRole, [], ''), + (PROPS_TABLE_LIST_COLUMN_INDEX, True, Qt.UserRole, ['23', '56'], '23,56'), + (PROPS_TABLE_LIST_COLUMN_INDEX, True, Qt.UserRole, ['test 23', 'test'], 'test 23,test'), + (PROPS_TABLE_LIST_COLUMN_INDEX, True, Qt.UserRole, ['test 2334'] * 20, ('test 2334,test 2334,' * 10)[:-1]), + (PROPS_TABLE_LIST_COLUMN_INDEX, True, Qt.UserRole, '', ''), + (PROPS_TABLE_LIST_COLUMN_INDEX, True, Qt.UserRole, 'testsdfsdf', 'testsdfsdf'), + (None, None, Qt.EditRole, False, False) + ]) + def test_props_table_data_model_get_data_should_do_expected(self, + props_table_model: props_table_model, + mocker, + column_index, + is_valid, + role, + base_return_data, + return_value): + + mock_index = mocker.patch("PySide6.QtCore.QModelIndex") + mock_is_valid_spy = mocker.patch.object(mock_index, "isValid", return_value=is_valid) + mock_column_spy = mocker.patch.object(mock_index, "column", return_value=column_index) + mock_data_get_spy = mocker.patch( + "pasta_eln.GUI.ontology_configuration.ontology_tableview_data_model.OntologyTableViewModel.data", + return_value=base_return_data) + assert props_table_model.data(mock_index, role) == return_value, \ + "data() should return expected value" + mock_is_valid_spy.assert_called_once_with() + if is_valid: + mock_column_spy.assert_called_once_with() + mock_data_get_spy.assert_called_once_with(mock_index, role) From 10c753389aa229aea66d40711d2def912e4858bc Mon Sep 17 00:00:00 2001 From: jmurugan-fzj Date: Thu, 14 Sep 2023 16:47:49 +0200 Subject: [PATCH 13/22] - Added two new must properties (-name & -tags) to the newly created type - Other minor refactorings --- .../ontology_configuration_extended.py | 11 ++----- .../utility_functions.py | 30 +++++++++++++++++++ ..._ontology_config_configuration_extended.py | 22 ++------------ 3 files changed, 35 insertions(+), 28 deletions(-) diff --git a/pasta_eln/GUI/ontology_configuration/ontology_configuration_extended.py b/pasta_eln/GUI/ontology_configuration/ontology_configuration_extended.py index 24172d3c..2e52afa0 100644 --- a/pasta_eln/GUI/ontology_configuration/ontology_configuration_extended.py +++ b/pasta_eln/GUI/ontology_configuration/ontology_configuration_extended.py @@ -29,7 +29,7 @@ from .reorder_column_delegate import ReorderColumnDelegate from .required_column_delegate import RequiredColumnDelegate from .utility_functions import adjust_ontology_data_to_v3, show_message, \ - get_next_possible_structural_level_label, get_types_for_display, adapt_type + get_next_possible_structural_level_label, get_types_for_display, adapt_type, generate_empty_type class OntologyConfigurationForm(Ui_OntologyConfigurationBaseForm): @@ -385,14 +385,7 @@ def create_new_type(self, return self.logger.info("User created a new type and added " "to the ontology document: Title: {%s}, Label: {%s}", title, label) - empty_type = { - "link": "", - "label": label, - "prop": { - "default": [] - }, - "attachments": [] - } + empty_type = generate_empty_type(label) self.ontology_document[title] = empty_type self.ontology_types[title] = empty_type self.typeComboBox.clear() diff --git a/pasta_eln/GUI/ontology_configuration/utility_functions.py b/pasta_eln/GUI/ontology_configuration/utility_functions.py index beff1852..b4fb32a0 100644 --- a/pasta_eln/GUI/ontology_configuration/utility_functions.py +++ b/pasta_eln/GUI/ontology_configuration/utility_functions.py @@ -174,3 +174,33 @@ def is_structural_level(title: str) -> bool: """ return re.compile(r'^[Xx][0-9]+$').match(title) is not None + + +def generate_empty_type(label: str) -> dict[str, Any]: + """ + Generate an empty type for creating a new ontology type + Args: + label (str): Label of the new type + + Returns: Dictionary representing a bare new type + + """ + return { + "link": "", + "label": label, + "prop": { + "default": [ + { + "name": "-name", + "query": "What is the name of the project?", + "required": True + }, + { + "name": "-tags", + "query": "What are the tags associated with the project?", + "required": True + } + ] + }, + "attachments": [] + } diff --git a/tests/app_tests/unit_tests/test_ontology_config_configuration_extended.py b/tests/app_tests/unit_tests/test_ontology_config_configuration_extended.py index 5604d5a6..b54f75a1 100644 --- a/tests/app_tests/unit_tests/test_ontology_config_configuration_extended.py +++ b/tests/app_tests/unit_tests/test_ontology_config_configuration_extended.py @@ -22,7 +22,7 @@ from pasta_eln.GUI.ontology_configuration.ontology_props_tableview_data_model import OntologyPropsTableViewModel from pasta_eln.GUI.ontology_configuration.reorder_column_delegate import ReorderColumnDelegate from pasta_eln.GUI.ontology_configuration.required_column_delegate import RequiredColumnDelegate -from pasta_eln.GUI.ontology_configuration.utility_functions import get_types_for_display +from pasta_eln.GUI.ontology_configuration.utility_functions import get_types_for_display, generate_empty_type from tests.app_tests.common.fixtures import configuration_extended, ontology_doc_mock @@ -761,25 +761,9 @@ def test_create_new_type_should_do_expected(self, new_label) (configuration_extended.ontology_document - .__setitem__.assert_called_once_with(new_title, - { - "link": "", - "label": new_label, - "prop": { - "default": [] - }, - "attachments": [] - })) + .__setitem__.assert_called_once_with(new_title, generate_empty_type(new_label))) (configuration_extended.ontology_types - .__setitem__.assert_called_once_with(new_title, - { - "link": "", - "label": new_label, - "prop": { - "default": [] - }, - "attachments": [] - })) + .__setitem__.assert_called_once_with(new_title, generate_empty_type(new_label))) configuration_extended.typeComboBox.clear.assert_called_once_with() configuration_extended.typeComboBox.addItems.assert_called_once_with( get_types_for_display(configuration_extended.ontology_types.keys())) From bd044498dea3bd0d6080cfebddbe305c7a61437c Mon Sep 17 00:00:00 2001 From: jmurugan-fzj Date: Thu, 14 Sep 2023 17:25:50 +0200 Subject: [PATCH 14/22] - Changed property "link" to "IRI" in whole ontology - Changed location to description and link to type for attachment table - Corrected the tests for the changes - Other minor refactorings --- ...tology_attachments_tableview_data_model.py | 4 +- .../ontology_configuration.py | 14 ++-- .../ontology_configuration.ui | 2 +- .../ontology_configuration_extended.py | 19 ++--- .../ontology_props_tableview_data_model.py | 4 +- tests/app_tests/common/fixtures.py | 8 +- .../test_ontology_configuration_extended.py | 12 +-- .../test_data/ontology_document.json | 26 +++--- ..._ontology_config_configuration_extended.py | 80 +++++++++---------- 9 files changed, 85 insertions(+), 84 deletions(-) diff --git a/pasta_eln/GUI/ontology_configuration/ontology_attachments_tableview_data_model.py b/pasta_eln/GUI/ontology_configuration/ontology_attachments_tableview_data_model.py index 3307e597..649e0d2d 100644 --- a/pasta_eln/GUI/ontology_configuration/ontology_attachments_tableview_data_model.py +++ b/pasta_eln/GUI/ontology_configuration/ontology_attachments_tableview_data_model.py @@ -31,8 +31,8 @@ def __init__(self, self.logger = logging.getLogger(f"{__name__}.{self.__class__.__name__}") self.data_set = [] self.data_name_map = { - 0: "location", - 1: "link", + 0: "description", + 1: "type", 2: "delete", 3: "re-order" } diff --git a/pasta_eln/GUI/ontology_configuration/ontology_configuration.py b/pasta_eln/GUI/ontology_configuration/ontology_configuration.py index 6dd7dedb..1a8b5530 100644 --- a/pasta_eln/GUI/ontology_configuration/ontology_configuration.py +++ b/pasta_eln/GUI/ontology_configuration/ontology_configuration.py @@ -113,14 +113,14 @@ def setupUi(self, OntologyConfigurationBaseForm): self.typeLabelLineEdit.setText("") self.typeLabelLineEdit.setObjectName("typeLabelLineEdit") self.datatypeHorizontalLayout.addWidget(self.typeLabelLineEdit) - self.typeLinkLineEdit = QtWidgets.QLineEdit(parent=self.verticalLayoutWidget_2) + self.typeIriLineEdit = QtWidgets.QLineEdit(parent=self.verticalLayoutWidget_2) sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Policy.Minimum, QtWidgets.QSizePolicy.Policy.Minimum) sizePolicy.setHorizontalStretch(0) sizePolicy.setVerticalStretch(0) - sizePolicy.setHeightForWidth(self.typeLinkLineEdit.sizePolicy().hasHeightForWidth()) - self.typeLinkLineEdit.setSizePolicy(sizePolicy) - self.typeLinkLineEdit.setObjectName("typeLinkLineEdit") - self.datatypeHorizontalLayout.addWidget(self.typeLinkLineEdit) + sizePolicy.setHeightForWidth(self.typeIriLineEdit.sizePolicy().hasHeightForWidth()) + self.typeIriLineEdit.setSizePolicy(sizePolicy) + self.typeIriLineEdit.setObjectName("typeIriLineEdit") + self.datatypeHorizontalLayout.addWidget(self.typeIriLineEdit) self.addTypePushButton = QtWidgets.QPushButton(parent=self.verticalLayoutWidget_2) self.addTypePushButton.setObjectName("addTypePushButton") self.datatypeHorizontalLayout.addWidget(self.addTypePushButton) @@ -207,8 +207,8 @@ def retranslateUi(self, OntologyConfigurationBaseForm): self.typeComboBox.setToolTip(_translate("OntologyConfigurationBaseForm", "Select the type from the loaded ontology")) self.typeLabelLineEdit.setToolTip(_translate("OntologyConfigurationBaseForm", "Modify the label property of the type")) self.typeLabelLineEdit.setPlaceholderText(_translate("OntologyConfigurationBaseForm", "Modify the type label here")) - self.typeLinkLineEdit.setToolTip(_translate("OntologyConfigurationBaseForm", "Enter the link/iri to be associated with this data-type")) - self.typeLinkLineEdit.setPlaceholderText(_translate("OntologyConfigurationBaseForm", "Enter the iri for the type")) + self.typeIriLineEdit.setToolTip(_translate("OntologyConfigurationBaseForm", "Enter the link/iri to be associated with this data-type")) + self.typeIriLineEdit.setPlaceholderText(_translate("OntologyConfigurationBaseForm", "Enter the iri for the type")) self.addTypePushButton.setToolTip(_translate("OntologyConfigurationBaseForm", "Add a new type (structural or normal type) to the ontology data set.")) self.addTypePushButton.setText(_translate("OntologyConfigurationBaseForm", "+ Add")) self.deleteTypePushButton.setToolTip(_translate("OntologyConfigurationBaseForm", "Delete the type with the full properties completely")) diff --git a/pasta_eln/GUI/ontology_configuration/ontology_configuration.ui b/pasta_eln/GUI/ontology_configuration/ontology_configuration.ui index 1d83c8aa..b35a7ed9 100644 --- a/pasta_eln/GUI/ontology_configuration/ontology_configuration.ui +++ b/pasta_eln/GUI/ontology_configuration/ontology_configuration.ui @@ -284,7 +284,7 @@ - + 0 diff --git a/pasta_eln/GUI/ontology_configuration/ontology_configuration_extended.py b/pasta_eln/GUI/ontology_configuration/ontology_configuration_extended.py index 2e52afa0..a8af834a 100644 --- a/pasta_eln/GUI/ontology_configuration/ontology_configuration_extended.py +++ b/pasta_eln/GUI/ontology_configuration/ontology_configuration_extended.py @@ -135,8 +135,8 @@ def type_combo_box_changed(self, # Type label is set in a line edit self.typeLabelLineEdit.setText(selected_type.get('label')) - # Type link is set in a line edit - self.typeLinkLineEdit.setText(selected_type.get('link')) + # Type IRI is set in a line edit + self.typeIriLineEdit.setText(selected_type.get('IRI')) # Gets the attachment data from selected type and set it in table view self.attachments_table_data_model.update(selected_type.get('attachments')) @@ -216,19 +216,20 @@ def update_type_label(self, if modified_type_label is not None and current_type in self.ontology_types: self.ontology_types.get(current_type)["label"] = modified_type_label - def update_type_link(self, modified_link: str) -> None: + def update_type_iri(self, + modified_iri: str) -> None: """ - Value changed callback for the link line edit + Value changed callback for the IRI line edit Args: - modified_link (str): Modified link to be set for the selected type + modified_iri (str): Modified IRI to be set for the selected type Returns: Nothing """ current_type = self.typeComboBox.currentText() current_type = adapt_type(current_type) - if modified_link is not None and current_type in self.ontology_types: - self.ontology_types.get(current_type)["link"] = modified_link + if modified_iri is not None and current_type in self.ontology_types: + self.ontology_types.get(current_type)["IRI"] = modified_iri def delete_selected_type(self) -> None: """ @@ -261,7 +262,7 @@ def clear_ui(self) -> None: """ self.typeLabelLineEdit.clear() - self.typeLinkLineEdit.clear() + self.typeIriLineEdit.clear() self.propsCategoryComboBox.clear() self.addPropsCategoryLineEdit.clear() self.typePropsTableView.model().update([]) @@ -323,7 +324,7 @@ def setup_slots(self) -> None: # Slots for line edits self.typeLabelLineEdit.textChanged[str].connect(self.update_type_label) - self.typeLinkLineEdit.textChanged[str].connect(self.update_type_link) + self.typeIriLineEdit.textChanged[str].connect(self.update_type_iri) # Slots for the delegates self.delete_column_delegate_props_table.delete_clicked_signal.connect(self.props_table_data_model.delete_data) diff --git a/pasta_eln/GUI/ontology_configuration/ontology_props_tableview_data_model.py b/pasta_eln/GUI/ontology_configuration/ontology_props_tableview_data_model.py index 07ff273f..89b80cc5 100644 --- a/pasta_eln/GUI/ontology_configuration/ontology_props_tableview_data_model.py +++ b/pasta_eln/GUI/ontology_configuration/ontology_props_tableview_data_model.py @@ -37,7 +37,7 @@ def __init__(self, 1: "query", 2: "list", 3: "unit", - 4: "link", + 4: "IRI", 5: "required", 6: "delete", 7: "re-order" @@ -48,7 +48,7 @@ def __init__(self, 1: 300, # Query column width 2: 150, # list column width 3: 100, # unit column width - 4: 100, # link column width + 4: 100, # IRI column width 5: 120, # required column width 6: 120, # delete column width 7: 120 # re-order column width diff --git a/tests/app_tests/common/fixtures.py b/tests/app_tests/common/fixtures.py index 8c567c68..d662b4eb 100644 --- a/tests/app_tests/common/fixtures.py +++ b/tests/app_tests/common/fixtures.py @@ -69,7 +69,7 @@ def configuration_extended(mocker) -> OntologyConfigurationForm: mocker.patch.object(OntologyConfigurationForm, 'typeComboBox', create=True) mocker.patch.object(OntologyConfigurationForm, 'propsCategoryComboBox', create=True) mocker.patch.object(OntologyConfigurationForm, 'typeLabelLineEdit', create=True) - mocker.patch.object(OntologyConfigurationForm, 'typeLinkLineEdit', create=True) + mocker.patch.object(OntologyConfigurationForm, 'typeIriLineEdit', create=True) mocker.patch.object(OntologyConfigurationForm, 'delete_column_delegate_props_table', create=True) mocker.patch.object(OntologyConfigurationForm, 'reorder_column_delegate_props_table', create=True) mocker.patch.object(OntologyConfigurationForm, 'delete_column_delegate_attach_table', create=True) @@ -162,7 +162,7 @@ def props_column_names(): 1: "query", 2: "list", 3: "unit", - 4: "link", + 4: "IRI", 5: "required" } @@ -170,8 +170,8 @@ def props_column_names(): @fixture() def attachments_column_names(): return { - 0: "location", - 1: "link" + 0: "description", + 1: "type" } diff --git a/tests/app_tests/component_tests/test_ontology_configuration_extended.py b/tests/app_tests/component_tests/test_ontology_configuration_extended.py index 3f083df3..a7faadac 100644 --- a/tests/app_tests/component_tests/test_ontology_configuration_extended.py +++ b/tests/app_tests/component_tests/test_ontology_configuration_extended.py @@ -48,7 +48,7 @@ def test_component_launch_should_display_all_ui_elements(self, assert ui_form.addPropsCategoryPushButton is not None, "Add property category button not loaded!" assert ui_form.cancelPushButton is not None, "Cancel button not loaded!" assert ui_form.typeLabelLineEdit is not None, "Data type line edit not loaded!" - assert ui_form.typeLinkLineEdit is not None, "Data type link line edit not loaded!" + assert ui_form.typeIriLineEdit is not None, "Data type IRI line edit not loaded!" assert ui_form.addPropsCategoryLineEdit is not None, "Property category line edit not loaded!" assert ui_form.typeComboBox is not None, "Data type combo box not loaded!" assert ui_form.propsCategoryComboBox is not None, "Property category combo box not loaded!" @@ -73,8 +73,8 @@ def test_component_load_button_click_should_load_ontology_data(self, selected_type = ontology_doc_mock.types()[adapt_type(ui_form.typeComboBox.currentText())] assert (ui_form.typeLabelLineEdit.text() == selected_type["label"]), "Data type label line edit not loaded!" - assert (ui_form.typeLinkLineEdit.text() == - selected_type["link"]), "Data type link line edit not loaded!" + assert (ui_form.typeIriLineEdit.text() == + selected_type["IRI"]), "Data type IRI line edit not loaded!" categories = list(selected_type["prop"].keys()) assert ([ui_form.propsCategoryComboBox.itemText(i) for i in range(ui_form.propsCategoryComboBox.count())] @@ -175,7 +175,7 @@ def test_component_delete_selected_type_with_loaded_ontology_should_delete_and_u selected_type = ontology_doc_mock.types()[adapt_type(ui_form.typeComboBox.currentText())] assert ui_form.typeLabelLineEdit.text() == selected_type["label"], \ "Type label line edit should be selected to second item" - assert ui_form.typeLinkLineEdit.text() == selected_type["link"], \ + assert ui_form.typeIriLineEdit.text() == selected_type["IRI"], \ "Type label line edit should be selected to second item" assert ui_form.propsCategoryComboBox.currentText() == list(selected_type["prop"].keys())[0], \ "Type label line edit should be selected to second item" @@ -208,8 +208,8 @@ def test_component_add_selected_type_with_loaded_ontology_should_delete_and_upda selected_type = types[adapt_type(ui_form.typeComboBox.currentText())] assert ui_form.typeLabelLineEdit.text() == selected_type["label"], \ "Type label line edit should be selected to second item" - assert ui_form.typeLinkLineEdit.text() == selected_type["link"], \ - "Type Link line edit should be selected to second item" + assert ui_form.typeIriLineEdit.text() == selected_type["IRI"], \ + "Type IRI line edit should be selected to second item" assert ui_form.propsCategoryComboBox.currentText() == list(selected_type["prop"].keys())[0], \ "Type label line edit should be selected to second item" self.check_table_contents(attachments_column_names, props_column_names, selected_type, ui_form) diff --git a/tests/app_tests/test_data/ontology_document.json b/tests/app_tests/test_data/ontology_document.json index d5a7c801..19e12386 100644 --- a/tests/app_tests/test_data/ontology_document.json +++ b/tests/app_tests/test_data/ontology_document.json @@ -3,14 +3,14 @@ "_rev": "5-a3f3f6b96ba5839081fa8b2f745df64a", "-version": 3, "x0": { - "link": "http://url.com", + "IRI": "http://url.com", "label": "Projects", "prop": { "default": [ { "name": "-name", "query": "What is the name of the project?", - "link": "http://url.com", + "IRI": "http://url.com", "required": "True" }, { @@ -40,14 +40,14 @@ "name": "-name", "query": "What is the name of the property?", "required": "True", - "link": "http://url.com", + "IRI": "http://url.com", "unit": "" }, { "name": "sample1", "query": "", "required": "True", - "link": "http://url.com", + "IRI": "http://url.com", "unit": "m" } ] @@ -55,7 +55,7 @@ "attachments": [] }, "x1": { - "link": "", + "IRI": "", "label": "Tasks", "prop": { "default": [ @@ -72,7 +72,7 @@ "attachments": [] }, "x2": { - "link": "", + "IRI": "", "label": "Subtasks", "prop": { "default": [ @@ -89,7 +89,7 @@ "attachments": [] }, "measurement": { - "link": "", + "IRI": "", "label": "Measurements", "prop": { "default": [ @@ -119,7 +119,7 @@ "list": "sample", "unit": "m", "required": "True", - "link": "http://url.com" + "IRI": "http://url.com" }, { "name": "procedure", @@ -131,7 +131,7 @@ "attachments": [] }, "sample": { - "link": "", + "IRI": "", "label": "Samples", "prop": { "default": [ @@ -158,7 +158,7 @@ "attachments": [] }, "procedure": { - "link": "", + "IRI": "", "label": "Procedures", "prop": { "default": [ @@ -182,7 +182,7 @@ "attachments": [] }, "instrument": { - "link": "", + "IRI": "", "label": "Instruments", "prop": { "default": [ @@ -203,8 +203,8 @@ }, "attachments": [ { - "location": "Right side of the instrument", - "link": "device1" + "description": "Right side of the instrument", + "type": "device1" } ] } diff --git a/tests/app_tests/unit_tests/test_ontology_config_configuration_extended.py b/tests/app_tests/unit_tests/test_ontology_config_configuration_extended.py index b54f75a1..af5aa7f0 100644 --- a/tests/app_tests/unit_tests/test_ontology_config_configuration_extended.py +++ b/tests/app_tests/unit_tests/test_ontology_config_configuration_extended.py @@ -54,7 +54,7 @@ def test_instantiation_should_succeed(self, mocker.patch.object(OntologyConfigurationForm, 'typeComboBox', create=True) mocker.patch.object(OntologyConfigurationForm, 'propsCategoryComboBox', create=True) mocker.patch.object(OntologyConfigurationForm, 'typeLabelLineEdit', create=True) - mocker.patch.object(OntologyConfigurationForm, 'typeLinkLineEdit', create=True) + mocker.patch.object(OntologyConfigurationForm, 'typeIriLineEdit', create=True) mocker.patch.object(OntologyConfigurationForm, 'delete_column_delegate_props_table', create=True) mocker.patch.object(OntologyConfigurationForm, 'reorder_column_delegate_props_table', create=True) mocker.patch.object(OntologyConfigurationForm, 'delete_column_delegate_attach_table', create=True) @@ -76,7 +76,7 @@ def test_instantiation_with_null_document_should_throw_exception(self, ("x0", { "x0": { "label": "x0", - "link": "url", + "IRI": "url", "prop": { "default": [ { @@ -94,7 +94,7 @@ def test_instantiation_with_null_document_should_throw_exception(self, }, "x1": { "label": "x0", - "link": "url", + "IRI": "url", "prop": { "default": [ { @@ -114,7 +114,7 @@ def test_instantiation_with_null_document_should_throw_exception(self, ("x1", { "x0": { "label": "x0", - "link": "url", + "IRI": "url", "prop": { "default": [ { @@ -132,7 +132,7 @@ def test_instantiation_with_null_document_should_throw_exception(self, }, "x1": { "label": "x0", - "link": "url", + "IRI": "url", "prop": { "default": [ { @@ -153,8 +153,8 @@ def test_instantiation_with_null_document_should_throw_exception(self, ("x0", {}), ("x0", {"x1": {}}), ("x0", {"x0": {}}), - ("x0", {"x0": {"label": None, "link": None, "prop": None, "attachments": None}}), - ("x0", {"x0": {"label": None, "link": None, "prop": {"": None}, "attachments": [{"": None}]}}), + ("x0", {"x0": {"label": None, "IRI": None, "prop": None, "attachments": None}}), + ("x0", {"x0": {"label": None, "IRI": None, "prop": {"": None}, "attachments": [{"": None}]}}), ("x0", {"x0": {"": None, "ยง": None, "props": {"": None}, "attachment": [{"": None}]}}) ]) def test_type_combo_box_changed_should_do_expected(self, @@ -166,11 +166,11 @@ def test_type_combo_box_changed_should_do_expected(self, mocker.patch.object(configuration_extended, 'addPropsCategoryLineEdit', create=True) mocker.patch.object(configuration_extended, 'ontology_types', mock_ontology_types, create=True) mocker.patch.object(configuration_extended, 'typeLabelLineEdit', create=True) - mocker.patch.object(configuration_extended, 'typeLinkLineEdit', create=True) + mocker.patch.object(configuration_extended, 'typeIriLineEdit', create=True) mocker.patch.object(configuration_extended, 'attachments_table_data_model', create=True) mocker.patch.object(configuration_extended, 'propsCategoryComboBox', create=True) set_text_label_line_edit_spy = mocker.spy(configuration_extended.typeLabelLineEdit, 'setText') - set_text_link_line_edit_spy = mocker.spy(configuration_extended.typeLinkLineEdit, 'setText') + set_text_iri_line_edit_spy = mocker.spy(configuration_extended.typeIriLineEdit, 'setText') set_current_index_category_combo_box_spy = mocker.spy(configuration_extended.propsCategoryComboBox, 'setCurrentIndex') clear_add_props_category_line_edit_spy = mocker.spy(configuration_extended.addPropsCategoryLineEdit, 'clear') @@ -191,7 +191,7 @@ def test_type_combo_box_changed_should_do_expected(self, logger_info_spy.assert_called_once_with("New type selected in UI: {%s}", new_type_selected) clear_add_props_category_line_edit_spy.assert_called_once_with() set_text_label_line_edit_spy.assert_called_once_with(mock_ontology_types.get(new_type_selected).get('label')) - set_text_link_line_edit_spy.assert_called_once_with(mock_ontology_types.get(new_type_selected).get('link')) + set_text_iri_line_edit_spy.assert_called_once_with(mock_ontology_types.get(new_type_selected).get('IRI')) set_current_index_category_combo_box_spy.assert_called_once_with(0) clear_category_combo_box_spy.assert_called_once_with() add_items_category_combo_box_spy.assert_called_once_with( @@ -385,7 +385,7 @@ def test_update_structure_label_should_do_expected(self, get_ontology_types_spy.assert_called_once_with(current_type) assert ontology_types[current_type]["label"] == modified_type_label - @pytest.mark.parametrize("modified_type_link, current_type, ontology_types", [ + @pytest.mark.parametrize("modified_type_iri, current_type, ontology_types", [ (None, None, None), ("new_url", None, None), (None, "x0", {"x0": {"label": "x0"}, "x1": {"label": "x1"}}), @@ -393,10 +393,10 @@ def test_update_structure_label_should_do_expected(self, ("new_url_2", "instrument", {"x0": {"label": "x0"}, "instrument": {"label": "x1"}}), ("type_new_url", "subtask4", {"x0": {"label": "x0"}, "subtask5": {"label": "x1"}}), ]) - def test_update_type_link_should_do_expected(self, + def test_update_type_iri_should_do_expected(self, mocker, configuration_extended: configuration_extended, - modified_type_link, + modified_type_iri, current_type, ontology_types): mocker.patch.object(configuration_extended, 'typeComboBox', create=True) @@ -413,24 +413,24 @@ def test_update_type_link_should_do_expected(self, get_ontology_types_spy = mocker.spy(configuration_extended.ontology_types, 'get') - if modified_type_link: - assert configuration_extended.update_type_link(modified_type_link) is None, "Nothing should be returned" + if modified_type_iri: + assert configuration_extended.update_type_iri(modified_type_iri) is None, "Nothing should be returned" if ontology_types is not None and current_type in ontology_types: get_ontology_types_spy.assert_called_once_with(current_type) - assert ontology_types[current_type]["link"] == modified_type_link + assert ontology_types[current_type]["IRI"] == modified_type_iri @pytest.mark.parametrize("selected_type, ontology_types, ontology_document", [ (None, None, None), ("x0", None, None), - (None, {"x0": {"link": "x0"}, "x1": {"link": "x1"}}, {"x0": {"link": "x0"}, "x1": {"link": "x1"}}), - ("x3", {"x0": {"link": "x0"}, "x1": {"link": "x1"}}, {"x0": {"link": "x0"}, "x1": {"link": "x1"}}), - ("instrument", {"x0": {"link": "x0"}, "instrument": {"link": "x1"}}, - {"x0": {"link": "x0"}, "instrument": {"link": "x1"}}), + (None, {"x0": {"IRI": "x0"}, "x1": {"IRI": "x1"}}, {"x0": {"IRI": "x0"}, "x1": {"IRI": "x1"}}), + ("x3", {"x0": {"IRI": "x0"}, "x1": {"IRI": "x1"}}, {"x0": {"IRI": "x0"}, "x1": {"IRI": "x1"}}), + ("instrument", {"x0": {"IRI": "x0"}, "instrument": {"IRI": "x1"}}, + {"x0": {"IRI": "x0"}, "instrument": {"IRI": "x1"}}), ( - "subtask5", {"x0": {"link": "x0"}, "subtask5": {"link": "x1"}}, - {"x0": {"link": "x0"}, "subtask5": {"link": "x1"}}), - ("x0", {"x0": {"link": "x0"}, "subtask5": {"link": "x1"}}, {"subtask5": {"link": "x1"}}), - ("x0", {"subtask5": {"link": "x1"}}, {"x0": {"link": "x0"}, "subtask5": {"link": "x1"}}), + "subtask5", {"x0": {"IRI": "x0"}, "subtask5": {"IRI": "x1"}}, + {"x0": {"IRI": "x0"}, "subtask5": {"IRI": "x1"}}), + ("x0", {"x0": {"IRI": "x0"}, "subtask5": {"IRI": "x1"}}, {"subtask5": {"IRI": "x1"}}), + ("x0", {"subtask5": {"IRI": "x1"}}, {"x0": {"IRI": "x0"}, "subtask5": {"IRI": "x1"}}), ]) def test_delete_selected_type_should_do_expected(self, mocker, @@ -545,10 +545,10 @@ def test_create_type_rejected_callback_should_do_expected(self, @pytest.mark.parametrize("new_structural_title, ontology_types", [ (None, None), ("x0", None), - (None, {"x0": {"link": "x0"}, "x1": {"link": "x1"}}), - ("x3", {"x0": {"link": "x0"}, "x1": {"link": "x1"}}), - ("x7", {"x0": {"link": "x0"}, "instrument": {"link": "x1"}}), - ("x6", {"x0": {"link": "x0"}, "subtask5": {"link": "x1"}}) + (None, {"x0": {"IRI": "x0"}, "x1": {"IRI": "x1"}}), + ("x3", {"x0": {"IRI": "x0"}, "x1": {"IRI": "x1"}}), + ("x7", {"x0": {"IRI": "x0"}, "instrument": {"IRI": "x1"}}), + ("x6", {"x0": {"IRI": "x0"}, "subtask5": {"IRI": "x1"}}) ]) def test_show_create_type_dialog_should_do_expected(self, mocker, @@ -618,8 +618,8 @@ def test_setup_slots_should_do_expected(self, # Slots for line edits configuration_extended.typeLabelLineEdit.textChanged[str].connect.assert_called_once_with( configuration_extended.update_type_label) - configuration_extended.typeLinkLineEdit.textChanged[str].connect.assert_called_once_with( - configuration_extended.update_type_link) + configuration_extended.typeIriLineEdit.textChanged[str].connect.assert_called_once_with( + configuration_extended.update_type_iri) # Slots for the delegates configuration_extended.delete_column_delegate_props_table.delete_clicked_signal.connect.assert_called_once_with( @@ -635,8 +635,8 @@ def test_setup_slots_should_do_expected(self, @pytest.mark.parametrize("ontology_document", [ 'ontology_doc_mock', None, - {"x0": {"link": "x0"}, "": {"link": "x1"}}, - {"x0": {"link": "x0"}, "": {"link": "x1"}, 23: "test", "__id": "test"}, + {"x0": {"IRI": "x0"}, "": {"IRI": "x1"}}, + {"x0": {"IRI": "x0"}, "": {"IRI": "x1"}, 23: "test", "__id": "test"}, {"test": ["test1", "test2", "test3"]} ]) def test_load_ontology_data_should_with_variant_types_of_doc_should_do_expected(self, @@ -664,8 +664,8 @@ def test_load_ontology_data_should_with_variant_types_of_doc_should_do_expected( @pytest.mark.parametrize("ontology_document", [ 'ontology_doc_mock', None, - {"x0": {"link": "x0"}, "": {"link": "x1"}}, - {"x0": {"link": "x0"}, "": {"link": "x1"}, 23: "test", "__id": "test"}, + {"x0": {"IRI": "x0"}, "": {"IRI": "x1"}}, + {"x0": {"IRI": "x0"}, "": {"IRI": "x1"}, 23: "test", "__id": "test"}, {"test": ["test1", "test2", "test3"]} ]) def test_save_ontology_should_do_expected(self, @@ -693,13 +693,13 @@ def test_save_ontology_should_do_expected(self, @pytest.mark.parametrize("new_title, new_label, ontology_document, ontology_types", [ (None, None, None, None), - (None, None, {"x0": {"link": "x0"}, "x1": {"link": "x1"}}, {"x0": {"link": "x0"}, "x1": {"link": "x1"}}), - ("x0", None, {"x0": {"link": "x0"}, "x1": {"link": "x1"}}, {"x0": {"link": "x0"}, "x1": {"link": "x1"}}), - (None, "x1", {"x0": {"link": "x0"}, "x1": {"link": "x1"}}, {"x0": {"link": "x0"}, "x1": {"link": "x1"}}), - ("x0", "x1", {"x0": {"link": "x0"}, "x1": {"link": "x1"}}, {"x0": {"link": "x0"}, "x1": {"link": "x1"}}), + (None, None, {"x0": {"IRI": "x0"}, "x1": {"IRI": "x1"}}, {"x0": {"IRI": "x0"}, "x1": {"IRI": "x1"}}), + ("x0", None, {"x0": {"IRI": "x0"}, "x1": {"IRI": "x1"}}, {"x0": {"IRI": "x0"}, "x1": {"IRI": "x1"}}), + (None, "x1", {"x0": {"IRI": "x0"}, "x1": {"IRI": "x1"}}, {"x0": {"IRI": "x0"}, "x1": {"IRI": "x1"}}), + ("x0", "x1", {"x0": {"IRI": "x0"}, "x1": {"IRI": "x1"}}, {"x0": {"IRI": "x0"}, "x1": {"IRI": "x1"}}), ("x0", "x1", None, None), - ("instrument", "new Instrument", {"x0": {"link": "x0"}, "x1": {"link": "x1"}}, - {"x0": {"link": "x0"}, "x1": {"link": "x1"}}) + ("instrument", "new Instrument", {"x0": {"IRI": "x0"}, "x1": {"IRI": "x1"}}, + {"x0": {"IRI": "x0"}, "x1": {"IRI": "x1"}}) ]) def test_create_new_type_should_do_expected(self, mocker, From 836714ce4562fdcfe2a3c2be8392af732a15e75e Mon Sep 17 00:00:00 2001 From: jmurugan-fzj Date: Fri, 15 Sep 2023 11:43:23 +0200 Subject: [PATCH 15/22] - Set minimum width for the line edits in create_type_dialog.py and corrected the alignments --- .../GUI/ontology_configuration/create_type_dialog.py | 2 ++ .../GUI/ontology_configuration/create_type_dialog.ui | 12 ++++++++++++ 2 files changed, 14 insertions(+) diff --git a/pasta_eln/GUI/ontology_configuration/create_type_dialog.py b/pasta_eln/GUI/ontology_configuration/create_type_dialog.py index ef59a335..258019ec 100644 --- a/pasta_eln/GUI/ontology_configuration/create_type_dialog.py +++ b/pasta_eln/GUI/ontology_configuration/create_type_dialog.py @@ -27,6 +27,7 @@ def setupUi(self, CreateTypeDialog): self.tileHorizontalLayout = QtWidgets.QHBoxLayout() self.tileHorizontalLayout.setObjectName("tileHorizontalLayout") self.titleLabel = QtWidgets.QLabel(parent=self.verticalLayoutWidget) + self.titleLabel.setMinimumSize(QtCore.QSize(120, 0)) self.titleLabel.setObjectName("titleLabel") self.tileHorizontalLayout.addWidget(self.titleLabel) spacerItem = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Policy.Minimum, QtWidgets.QSizePolicy.Policy.Minimum) @@ -39,6 +40,7 @@ def setupUi(self, CreateTypeDialog): self.horizontalLayout = QtWidgets.QHBoxLayout() self.horizontalLayout.setObjectName("horizontalLayout") self.typeLabel = QtWidgets.QLabel(parent=self.verticalLayoutWidget) + self.typeLabel.setMinimumSize(QtCore.QSize(120, 0)) self.typeLabel.setObjectName("typeLabel") self.horizontalLayout.addWidget(self.typeLabel) spacerItem1 = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Policy.Minimum, QtWidgets.QSizePolicy.Policy.Minimum) diff --git a/pasta_eln/GUI/ontology_configuration/create_type_dialog.ui b/pasta_eln/GUI/ontology_configuration/create_type_dialog.ui index 51407ade..60b2f9de 100644 --- a/pasta_eln/GUI/ontology_configuration/create_type_dialog.ui +++ b/pasta_eln/GUI/ontology_configuration/create_type_dialog.ui @@ -49,6 +49,12 @@ + + + 120 + 0 + + Enter Type title @@ -86,6 +92,12 @@ + + + 120 + 0 + + Enter Type Label From d4efa047d5e7b1a75103f24d36aba59349f2845e Mon Sep 17 00:00:00 2001 From: jmurugan-fzj Date: Fri, 15 Sep 2023 14:18:54 +0200 Subject: [PATCH 16/22] - Corrected the tool tip for the line edits in create_type_dialog.ui - Added validator for the titleLineEdit in create_type_dialog_extended.py - Corrected "link" to "IRI" in the files missed in prior commit - Adjusted the tests for the changes --- .../GUI/ontology_configuration/create_type_dialog.py | 7 ++++--- .../GUI/ontology_configuration/create_type_dialog.ui | 9 ++++++--- .../create_type_dialog_extended.py | 7 ++++++- .../GUI/ontology_configuration/utility_functions.py | 2 +- tests/app_tests/common/fixtures.py | 1 + .../test_ontology_config_table_view_data_model.py | 4 ++-- 6 files changed, 20 insertions(+), 10 deletions(-) diff --git a/pasta_eln/GUI/ontology_configuration/create_type_dialog.py b/pasta_eln/GUI/ontology_configuration/create_type_dialog.py index 258019ec..daf5264b 100644 --- a/pasta_eln/GUI/ontology_configuration/create_type_dialog.py +++ b/pasta_eln/GUI/ontology_configuration/create_type_dialog.py @@ -33,7 +33,6 @@ def setupUi(self, CreateTypeDialog): spacerItem = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Policy.Minimum, QtWidgets.QSizePolicy.Policy.Minimum) self.tileHorizontalLayout.addItem(spacerItem) self.titleLineEdit = QtWidgets.QLineEdit(parent=self.verticalLayoutWidget) - self.titleLineEdit.setToolTip("") self.titleLineEdit.setObjectName("titleLineEdit") self.tileHorizontalLayout.addWidget(self.titleLineEdit) self.mainVerticalLayout.addLayout(self.tileHorizontalLayout) @@ -65,9 +64,11 @@ def retranslateUi(self, CreateTypeDialog): _translate = QtCore.QCoreApplication.translate CreateTypeDialog.setWindowTitle(_translate("CreateTypeDialog", "Create New Type")) self.titleLabel.setText(_translate("CreateTypeDialog", "Enter Type title")) - self.titleLineEdit.setPlaceholderText(_translate("CreateTypeDialog", "Enter the title for the new type")) + self.titleLineEdit.setToolTip(_translate("CreateTypeDialog", "Exclude titles which start with \'x\' (reserved for structure level titles) or whitespace")) + self.titleLineEdit.setPlaceholderText(_translate("CreateTypeDialog", "Enter title for the new type")) self.typeLabel.setText(_translate("CreateTypeDialog", "Enter Type Label")) - self.labelLineEdit.setPlaceholderText(_translate("CreateTypeDialog", "Enter the label for the new type")) + self.labelLineEdit.setToolTip(_translate("CreateTypeDialog", "Enter label for the new type, which can also be modified later in the main editor window")) + self.labelLineEdit.setPlaceholderText(_translate("CreateTypeDialog", "Enter label for the new type")) self.structuralLevelCheckBox.setToolTip(_translate("CreateTypeDialog", "If this is a structural type, then title will be automatically populated as (x0, x1...xn). Next number will be chosen for xn from the existing list of structural items.")) self.structuralLevelCheckBox.setText(_translate("CreateTypeDialog", "Is this a structural Type?")) diff --git a/pasta_eln/GUI/ontology_configuration/create_type_dialog.ui b/pasta_eln/GUI/ontology_configuration/create_type_dialog.ui index 60b2f9de..89a56441 100644 --- a/pasta_eln/GUI/ontology_configuration/create_type_dialog.ui +++ b/pasta_eln/GUI/ontology_configuration/create_type_dialog.ui @@ -79,10 +79,10 @@ - + Exclude titles which start with 'x' (reserved for structure level titles) or whitespace - Enter the title for the new type + Enter title for the new type @@ -121,8 +121,11 @@ + + Enter label for the new type, which can also be modified later in the main editor window + - Enter the label for the new type + Enter label for the new type diff --git a/pasta_eln/GUI/ontology_configuration/create_type_dialog_extended.py b/pasta_eln/GUI/ontology_configuration/create_type_dialog_extended.py index e07827b3..df218d02 100644 --- a/pasta_eln/GUI/ontology_configuration/create_type_dialog_extended.py +++ b/pasta_eln/GUI/ontology_configuration/create_type_dialog_extended.py @@ -20,6 +20,8 @@ from typing import Any from PySide6 import QtCore +from PySide6.QtCore import QRegularExpression +from PySide6.QtGui import QRegularExpressionValidator from PySide6.QtWidgets import QDialog from pasta_eln.GUI.ontology_configuration.create_type_dialog import Ui_CreateTypeDialog @@ -45,10 +47,13 @@ def __init__(self, accepted_callback (Callable): Accepted button parent callback. rejected_callback (Callable): Rejected button parent callback. """ - self.logger = logging.getLogger(__name__ + "." + self.__class__.__name__) + self.logger = logging.getLogger(f"{__name__}.{self.__class__.__name__}") self.next_struct_level: str | None = "" self.instance = QDialog() super().setupUi(self.instance) + # Restricts the title input to allow anything except x or space + # as the first character which is reserved for structural level + self.titleLineEdit.setValidator(QRegularExpressionValidator(QRegularExpression("^[^ Ax].*"))) self.setup_slots(accepted_callback, rejected_callback) def setup_slots(self, diff --git a/pasta_eln/GUI/ontology_configuration/utility_functions.py b/pasta_eln/GUI/ontology_configuration/utility_functions.py index b4fb32a0..3fafb449 100644 --- a/pasta_eln/GUI/ontology_configuration/utility_functions.py +++ b/pasta_eln/GUI/ontology_configuration/utility_functions.py @@ -186,7 +186,7 @@ def generate_empty_type(label: str) -> dict[str, Any]: """ return { - "link": "", + "IRI": "", "label": label, "prop": { "default": [ diff --git a/tests/app_tests/common/fixtures.py b/tests/app_tests/common/fixtures.py index d662b4eb..1df8f526 100644 --- a/tests/app_tests/common/fixtures.py +++ b/tests/app_tests/common/fixtures.py @@ -40,6 +40,7 @@ def create_type_dialog_mock(mocker) -> CreateTypeDialog: mocker.patch( 'pasta_eln.GUI.ontology_configuration.create_type_dialog_extended.Ui_CreateTypeDialog.setupUi') mocker.patch.object(QDialog, '__new__') + mocker.patch.object(CreateTypeDialog, 'titleLineEdit', create=True) return CreateTypeDialog(mock_callable_1, mock_callable_2) diff --git a/tests/app_tests/unit_tests/test_ontology_config_table_view_data_model.py b/tests/app_tests/unit_tests/test_ontology_config_table_view_data_model.py index 78f9658a..bf4b23d0 100644 --- a/tests/app_tests/unit_tests/test_ontology_config_table_view_data_model.py +++ b/tests/app_tests/unit_tests/test_ontology_config_table_view_data_model.py @@ -27,8 +27,8 @@ def test_data_models_property_table_model(self, qtmodeltester): props_items = [ - {"name": "name", "query": "query", "list": "list", "link": "link", "required": "required", "unit": "unit"}, - {"name": "name", "query": "query", "list": "list", "link": "link", "required": "required", "unit": "unit"} + {"name": "name", "query": "query", "list": "list", "IRI": "link", "required": "required", "unit": "unit"}, + {"name": "name", "query": "query", "list": "list", "IRI": "link", "required": "required", "unit": "unit"} ] props_table_model.update(props_items) with pytest.raises(AssertionError): From 354caeb4eb502bb02c4a354e5663b83529d60d1b Mon Sep 17 00:00:00 2001 From: jmurugan-fzj Date: Fri, 15 Sep 2023 15:18:52 +0200 Subject: [PATCH 17/22] - Disabled the slots before clearing the line edits on type level, this will create a problem of clearing also the respective "label" and "IRI" in ontology document if loaded already --- .../ontology_configuration_extended.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/pasta_eln/GUI/ontology_configuration/ontology_configuration_extended.py b/pasta_eln/GUI/ontology_configuration/ontology_configuration_extended.py index a8af834a..27993a3a 100644 --- a/pasta_eln/GUI/ontology_configuration/ontology_configuration_extended.py +++ b/pasta_eln/GUI/ontology_configuration/ontology_configuration_extended.py @@ -261,8 +261,15 @@ def clear_ui(self) -> None: Returns: None """ + # Disable the signals for the line edits before clearing in order to avoid clearing the respective + # iri and labels for the selected type from ontology document + self.typeLabelLineEdit.textChanged[str].disconnect() + self.typeIriLineEdit.textChanged[str].disconnect() self.typeLabelLineEdit.clear() self.typeIriLineEdit.clear() + self.typeLabelLineEdit.textChanged[str].connect(self.update_type_label) + self.typeIriLineEdit.textChanged[str].connect(self.update_type_iri) + self.propsCategoryComboBox.clear() self.addPropsCategoryLineEdit.clear() self.typePropsTableView.model().update([]) From c7d5c7b98b023a78de0f7723ee1feea2d09d2f7e Mon Sep 17 00:00:00 2001 From: jmurugan-fzj Date: Fri, 15 Sep 2023 16:33:18 +0200 Subject: [PATCH 18/22] - Removed load button and load the data in the __init__ method itself - Adjusted the tests to reflect the changes --- .../ontology_configuration.py | 5 -- .../ontology_configuration.ui | 10 ---- .../ontology_configuration_extended.py | 3 +- tests/app_tests/common/fixtures.py | 1 - .../test_ontology_configuration_extended.py | 55 +++---------------- ..._ontology_config_configuration_extended.py | 35 ++++++------ 6 files changed, 28 insertions(+), 81 deletions(-) diff --git a/pasta_eln/GUI/ontology_configuration/ontology_configuration.py b/pasta_eln/GUI/ontology_configuration/ontology_configuration.py index 1a8b5530..98c401d8 100644 --- a/pasta_eln/GUI/ontology_configuration/ontology_configuration.py +++ b/pasta_eln/GUI/ontology_configuration/ontology_configuration.py @@ -172,9 +172,6 @@ def setupUi(self, OntologyConfigurationBaseForm): self.headerHorizontalLayout.addWidget(self.headerLabel) spacerItem2 = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Policy.Expanding, QtWidgets.QSizePolicy.Policy.Minimum) self.headerHorizontalLayout.addItem(spacerItem2) - self.loadOntologyPushButton = QtWidgets.QPushButton(parent=self.verticalLayoutWidget_2) - self.loadOntologyPushButton.setObjectName("loadOntologyPushButton") - self.headerHorizontalLayout.addWidget(self.loadOntologyPushButton) self.saveOntologyPushButton = QtWidgets.QPushButton(parent=self.verticalLayoutWidget_2) self.saveOntologyPushButton.setObjectName("saveOntologyPushButton") self.headerHorizontalLayout.addWidget(self.saveOntologyPushButton) @@ -219,8 +216,6 @@ def retranslateUi(self, OntologyConfigurationBaseForm): self.propertiesTableHeaderLabel.setText(_translate("OntologyConfigurationBaseForm", "Properties")) self.typeAttachmentsTableView.setToolTip(_translate("OntologyConfigurationBaseForm", "Table which displays the attachments for the above selected data type")) self.headerLabel.setText(_translate("OntologyConfigurationBaseForm", "Edit Questionnaires")) - self.loadOntologyPushButton.setToolTip(_translate("OntologyConfigurationBaseForm", "Load ontology from local database")) - self.loadOntologyPushButton.setText(_translate("OntologyConfigurationBaseForm", "Load")) self.saveOntologyPushButton.setToolTip(_translate("OntologyConfigurationBaseForm", "Save loaded ontology in local database")) self.saveOntologyPushButton.setText(_translate("OntologyConfigurationBaseForm", "Save")) self.helpPushButton.setToolTip(_translate("OntologyConfigurationBaseForm", "Navigate to the help page")) diff --git a/pasta_eln/GUI/ontology_configuration/ontology_configuration.ui b/pasta_eln/GUI/ontology_configuration/ontology_configuration.ui index b35a7ed9..03c98609 100644 --- a/pasta_eln/GUI/ontology_configuration/ontology_configuration.ui +++ b/pasta_eln/GUI/ontology_configuration/ontology_configuration.ui @@ -484,16 +484,6 @@ - - - - Load ontology from local database - - - Load - - - diff --git a/pasta_eln/GUI/ontology_configuration/ontology_configuration_extended.py b/pasta_eln/GUI/ontology_configuration/ontology_configuration_extended.py index 27993a3a..a0b11a0f 100644 --- a/pasta_eln/GUI/ontology_configuration/ontology_configuration_extended.py +++ b/pasta_eln/GUI/ontology_configuration/ontology_configuration_extended.py @@ -108,6 +108,8 @@ def __init__(self, # Set up the slots for the UI items self.setup_slots() + self.load_ontology_data() + def type_combo_box_changed(self, new_type_selected: Any) -> None: """ @@ -315,7 +317,6 @@ def setup_slots(self) -> None: """ self.logger.info("Setting up slots for the editor..") # Slots for the buttons - self.loadOntologyPushButton.clicked.connect(self.load_ontology_data) self.addPropsRowPushButton.clicked.connect(self.props_table_data_model.add_data_row) self.addAttachmentPushButton.clicked.connect(self.attachments_table_data_model.add_data_row) self.saveOntologyPushButton.clicked.connect(self.save_ontology) diff --git a/tests/app_tests/common/fixtures.py b/tests/app_tests/common/fixtures.py index 1df8f526..c5ede090 100644 --- a/tests/app_tests/common/fixtures.py +++ b/tests/app_tests/common/fixtures.py @@ -58,7 +58,6 @@ def configuration_extended(mocker) -> OntologyConfigurationForm: mocker.patch.object(ReorderColumnDelegate, '__new__', lambda _: mocker.MagicMock()) mocker.patch.object(OntologyConfigurationForm, 'typePropsTableView', create=True) mocker.patch.object(OntologyConfigurationForm, 'typeAttachmentsTableView', create=True) - mocker.patch.object(OntologyConfigurationForm, 'loadOntologyPushButton', create=True) mocker.patch.object(OntologyConfigurationForm, 'addPropsRowPushButton', create=True) mocker.patch.object(OntologyConfigurationForm, 'addAttachmentPushButton', create=True) mocker.patch.object(OntologyConfigurationForm, 'saveOntologyPushButton', create=True) diff --git a/tests/app_tests/component_tests/test_ontology_configuration_extended.py b/tests/app_tests/component_tests/test_ontology_configuration_extended.py index a7faadac..274cd7d9 100644 --- a/tests/app_tests/component_tests/test_ontology_configuration_extended.py +++ b/tests/app_tests/component_tests/test_ontology_configuration_extended.py @@ -37,7 +37,6 @@ def test_component_launch_should_display_all_ui_elements(self, app, ui_dialog, ui_form, qtbot = ontology_editor_gui assert ui_form.headerLabel is not None, "Header not loaded!" assert ui_form.typeLabel is not None, "Data type label not loaded!" - assert ui_form.loadOntologyPushButton is not None, "Bush button not loaded!" assert ui_form.saveOntologyPushButton is not None, "Save button not loaded!" assert ui_form.helpPushButton is not None, "Help button not loaded!" assert ui_form.typePropsTableView is not None, "Properties table view not loaded!" @@ -53,19 +52,16 @@ def test_component_launch_should_display_all_ui_elements(self, assert ui_form.typeComboBox is not None, "Data type combo box not loaded!" assert ui_form.propsCategoryComboBox is not None, "Property category combo box not loaded!" - def test_component_load_button_click_should_load_ontology_data(self, - ontology_editor_gui: tuple[ - QApplication, - QtWidgets.QDialog, - OntologyConfigurationForm, - QtBot], - ontology_doc_mock: ontology_doc_mock, - props_column_names: props_column_names, - attachments_column_names: attachments_column_names): + def test_component_launch_should_load_ontology_data(self, + ontology_editor_gui: tuple[ + QApplication, + QtWidgets.QDialog, + OntologyConfigurationForm, + QtBot], + ontology_doc_mock: ontology_doc_mock, + props_column_names: props_column_names, + attachments_column_names: attachments_column_names): app, ui_dialog, ui_form, qtbot = ontology_editor_gui - assert ([ui_form.typeComboBox.itemText(i) for i in range(ui_form.typeComboBox.count())] - == []), "Type combo box should not be loaded!" - assert ui_form.loadOntologyPushButton.click() is None, "Load button not clicked!" assert ([ui_form.typeComboBox.itemText(i) for i in range(ui_form.typeComboBox.count())] == get_types_for_display(ontology_doc_mock.types_list())), "Type combo box not loaded!" assert (adapt_type(ui_form.typeComboBox.currentText()) @@ -106,21 +102,6 @@ def check_table_contents(self, attachments_column_names, props_column_names, sel model = ui_form.typeAttachmentsTableView.model() self.check_table_view_model(model, attachments_column_names, selected_type["attachments"]) - def test_component_add_new_type_with_ontology_loaded_should_display_error_message(self, - ontology_editor_gui: tuple[ - QApplication, - QtWidgets.QDialog, - OntologyConfigurationForm, - QtBot], - ontology_doc_mock: ontology_doc_mock, - mocker): - app, ui_dialog, ui_form, qtbot = ontology_editor_gui - mock_show_message = mocker.patch( - "pasta_eln.GUI.ontology_configuration.ontology_configuration_extended.show_message") - qtbot.mouseClick(ui_form.addTypePushButton, Qt.LeftButton) - mock_show_message.assert_called_once_with("Load the ontology data first...") - assert ui_form.create_type_dialog.buttonBox.isVisible() is False, "Create new type dialog should not be shown!" - def test_component_add_new_type_with_loaded_ontology_should_display_create_new_type_window(self, ontology_editor_gui: tuple[ QApplication, @@ -130,25 +111,9 @@ def test_component_add_new_type_with_loaded_ontology_should_display_create_new_t ontology_doc_mock: ontology_doc_mock): app, ui_dialog, ui_form, qtbot = ontology_editor_gui assert ui_form.create_type_dialog.buttonBox.isVisible() is False, "Create new type dialog should not be shown!" - qtbot.mouseClick(ui_form.loadOntologyPushButton, Qt.LeftButton) qtbot.mouseClick(ui_form.addTypePushButton, Qt.LeftButton) assert ui_form.create_type_dialog.buttonBox.isVisible() is True, "Create new type dialog not shown!" - def test_component_delete_new_type_with_ontology_loaded_should_show_error_message(self, - ontology_editor_gui: tuple[ - QApplication, - QtWidgets.QDialog, - OntologyConfigurationForm, - QtBot], - ontology_doc_mock: ontology_doc_mock, - mocker): - app, ui_dialog, ui_form, qtbot = ontology_editor_gui - mock_show_message = mocker.patch( - "pasta_eln.GUI.ontology_configuration.ontology_configuration_extended.show_message") - qtbot.mouseClick(ui_form.deleteTypePushButton, Qt.LeftButton) - mock_show_message.assert_called_once_with("Load the ontology data first....") - assert ui_form.create_type_dialog.buttonBox.isVisible() is False, "Create new type dialog should not be shown!" - def test_component_delete_selected_type_with_loaded_ontology_should_delete_and_update_ui(self, ontology_editor_gui: tuple[ @@ -161,7 +126,6 @@ def test_component_delete_selected_type_with_loaded_ontology_should_delete_and_u attachments_column_names: attachments_column_names): app, ui_dialog, ui_form, qtbot = ontology_editor_gui assert ui_form.create_type_dialog.buttonBox.isVisible() is False, "Create new type dialog should not be shown!" - qtbot.mouseClick(ui_form.loadOntologyPushButton, Qt.LeftButton) current_selected_type = ui_form.typeComboBox.currentText() previous_types_count = ui_form.typeComboBox.count() qtbot.mouseClick(ui_form.deleteTypePushButton, Qt.LeftButton) @@ -193,7 +157,6 @@ def test_component_add_selected_type_with_loaded_ontology_should_delete_and_upda attachments_column_names: attachments_column_names): app, ui_dialog, ui_form, qtbot = ontology_editor_gui assert ui_form.create_type_dialog.buttonBox.isVisible() is False, "Create new type dialog should not be shown!" - qtbot.mouseClick(ui_form.loadOntologyPushButton, Qt.LeftButton) current_selected_type = ui_form.typeComboBox.currentText() previous_types_count = ui_form.typeComboBox.count() qtbot.mouseClick(ui_form.deleteTypePushButton, Qt.LeftButton) diff --git a/tests/app_tests/unit_tests/test_ontology_config_configuration_extended.py b/tests/app_tests/unit_tests/test_ontology_config_configuration_extended.py index af5aa7f0..d0df4f54 100644 --- a/tests/app_tests/unit_tests/test_ontology_config_configuration_extended.py +++ b/tests/app_tests/unit_tests/test_ontology_config_configuration_extended.py @@ -42,7 +42,6 @@ def test_instantiation_should_succeed(self, mocker.patch.object(ReorderColumnDelegate, '__new__', lambda _: mocker.MagicMock()) mocker.patch.object(OntologyConfigurationForm, 'typePropsTableView', create=True) mocker.patch.object(OntologyConfigurationForm, 'typeAttachmentsTableView', create=True) - mocker.patch.object(OntologyConfigurationForm, 'loadOntologyPushButton', create=True) mocker.patch.object(OntologyConfigurationForm, 'addPropsRowPushButton', create=True) mocker.patch.object(OntologyConfigurationForm, 'addAttachmentPushButton', create=True) mocker.patch.object(OntologyConfigurationForm, 'saveOntologyPushButton', create=True) @@ -394,11 +393,11 @@ def test_update_structure_label_should_do_expected(self, ("type_new_url", "subtask4", {"x0": {"label": "x0"}, "subtask5": {"label": "x1"}}), ]) def test_update_type_iri_should_do_expected(self, - mocker, - configuration_extended: configuration_extended, - modified_type_iri, - current_type, - ontology_types): + mocker, + configuration_extended: configuration_extended, + modified_type_iri, + current_type, + ontology_types): mocker.patch.object(configuration_extended, 'typeComboBox', create=True) mocker.patch.object(configuration_extended.typeComboBox, 'currentText', return_value=current_type) @@ -587,13 +586,10 @@ def test_show_create_type_dialog_should_do_expected(self, set_structural_level_title_spy.assert_not_called() show_create_type_dialog_spy.assert_not_called() - def test_setup_slots_should_do_expected(self, - configuration_extended: configuration_extended): - configuration_extended.logger.info.assert_called_once_with(f"Setting up slots for the editor..") - configuration_extended.loadOntologyPushButton.clicked.connect.assert_called_once_with( - configuration_extended.load_ontology_data) - configuration_extended.loadOntologyPushButton.clicked.connect.assert_called_once_with( - configuration_extended.load_ontology_data) + def test_initialize_should_setup_slots_and_should_do_expected(self, + configuration_extended: configuration_extended): + configuration_extended.logger.info.assert_any_call("Setting up slots for the editor..") + configuration_extended.logger.info.assert_any_call("User loaded the ontology data in UI") configuration_extended.addPropsRowPushButton.clicked.connect.assert_called_once_with( configuration_extended.props_table_data_model.add_data_row) configuration_extended.addAttachmentPushButton.clicked.connect.assert_called_once_with( @@ -653,10 +649,12 @@ def test_load_ontology_data_should_with_variant_types_of_doc_should_do_expected( assert configuration_extended.load_ontology_data() is None, "Nothing should be returned" return assert configuration_extended.load_ontology_data() is None, "Nothing should be returned" - configuration_extended.typeComboBox.clear.assert_called_once_with() - configuration_extended.typeComboBox.addItems.assert_called_once_with( + assert configuration_extended.typeComboBox.clear.call_count == 2, "Clear should be called twice" + assert configuration_extended.typeComboBox.addItems.call_count == 2, "addItems should be called twice" + configuration_extended.typeComboBox.addItems.assert_called_with( get_types_for_display(configuration_extended.ontology_types.keys())) - configuration_extended.typeComboBox.setCurrentIndex.assert_called_once_with(0) + assert configuration_extended.typeComboBox.setCurrentIndex.call_count == 2, "setCurrentIndex should be called twice" + configuration_extended.typeComboBox.setCurrentIndex.assert_called_with(0) for data in ontology_document: if type(data) is dict: assert data in configuration_extended.ontology_types, "Data should be loaded" @@ -764,8 +762,9 @@ def test_create_new_type_should_do_expected(self, .__setitem__.assert_called_once_with(new_title, generate_empty_type(new_label))) (configuration_extended.ontology_types .__setitem__.assert_called_once_with(new_title, generate_empty_type(new_label))) - configuration_extended.typeComboBox.clear.assert_called_once_with() - configuration_extended.typeComboBox.addItems.assert_called_once_with( + assert configuration_extended.typeComboBox.clear.call_count == 2, "ComboBox should be cleared twice" + assert configuration_extended.typeComboBox.addItems.call_count == 2, "ComboBox addItems should be called twice" + configuration_extended.typeComboBox.addItems.assert_called_with( get_types_for_display(configuration_extended.ontology_types.keys())) mock_show_message.assert_called_once_with(f"Type (title: {new_title} label: {new_label}) has been added....") From 983b955e40aa1a96a920927928e6c9f7a61797be Mon Sep 17 00:00:00 2001 From: jmurugan-fzj Date: Fri, 15 Sep 2023 16:57:26 +0200 Subject: [PATCH 19/22] - Corrected the typo in the var attachmentTableButtonsHorizontalLayout --- .../ontology_configuration/ontology_configuration.py | 12 ++++++------ .../ontology_configuration/ontology_configuration.ui | 2 +- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/pasta_eln/GUI/ontology_configuration/ontology_configuration.py b/pasta_eln/GUI/ontology_configuration/ontology_configuration.py index 98c401d8..50939d9e 100644 --- a/pasta_eln/GUI/ontology_configuration/ontology_configuration.py +++ b/pasta_eln/GUI/ontology_configuration/ontology_configuration.py @@ -143,16 +143,16 @@ def setupUi(self, OntologyConfigurationBaseForm): spacerItem = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Policy.Expanding, QtWidgets.QSizePolicy.Policy.Minimum) self.propsTableButtonHorizontalLayout.addItem(spacerItem) self.mainGridLayout.addLayout(self.propsTableButtonHorizontalLayout, 8, 0, 1, 1) - self.attchmentTableButtonsHorizontalLayout = QtWidgets.QHBoxLayout() - self.attchmentTableButtonsHorizontalLayout.setContentsMargins(0, 5, 0, 5) - self.attchmentTableButtonsHorizontalLayout.setObjectName("attchmentTableButtonsHorizontalLayout") + self.attachmentTableButtonsHorizontalLayout = QtWidgets.QHBoxLayout() + self.attachmentTableButtonsHorizontalLayout.setContentsMargins(0, 5, 0, 5) + self.attachmentTableButtonsHorizontalLayout.setObjectName("attachmentTableButtonsHorizontalLayout") self.addAttachmentPushButton = QtWidgets.QPushButton(parent=self.verticalLayoutWidget_2) self.addAttachmentPushButton.setMinimumSize(QtCore.QSize(200, 0)) self.addAttachmentPushButton.setObjectName("addAttachmentPushButton") - self.attchmentTableButtonsHorizontalLayout.addWidget(self.addAttachmentPushButton) + self.attachmentTableButtonsHorizontalLayout.addWidget(self.addAttachmentPushButton) spacerItem1 = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Policy.Expanding, QtWidgets.QSizePolicy.Policy.Minimum) - self.attchmentTableButtonsHorizontalLayout.addItem(spacerItem1) - self.mainGridLayout.addLayout(self.attchmentTableButtonsHorizontalLayout, 13, 0, 1, 1) + self.attachmentTableButtonsHorizontalLayout.addItem(spacerItem1) + self.mainGridLayout.addLayout(self.attachmentTableButtonsHorizontalLayout, 13, 0, 1, 1) self.propertiesTableHeaderHorizontalLayout = QtWidgets.QHBoxLayout() self.propertiesTableHeaderHorizontalLayout.setContentsMargins(-1, 5, -1, 5) self.propertiesTableHeaderHorizontalLayout.setObjectName("propertiesTableHeaderHorizontalLayout") diff --git a/pasta_eln/GUI/ontology_configuration/ontology_configuration.ui b/pasta_eln/GUI/ontology_configuration/ontology_configuration.ui index 03c98609..193f0bfe 100644 --- a/pasta_eln/GUI/ontology_configuration/ontology_configuration.ui +++ b/pasta_eln/GUI/ontology_configuration/ontology_configuration.ui @@ -376,7 +376,7 @@ - + 0 From 9e4db1ac6897cde0b63b59a53b899f044093c8a3 Mon Sep 17 00:00:00 2001 From: jmurugan-fzj Date: Fri, 15 Sep 2023 17:09:41 +0200 Subject: [PATCH 20/22] - Added a new function to return a list of required properties and is used whenever a new category of property list is added to the type - Corrected the tests --- .../ontology_configuration_extended.py | 5 +-- .../utility_functions.py | 33 ++++++++++++------- ..._ontology_config_configuration_extended.py | 7 ++-- 3 files changed, 28 insertions(+), 17 deletions(-) diff --git a/pasta_eln/GUI/ontology_configuration/ontology_configuration_extended.py b/pasta_eln/GUI/ontology_configuration/ontology_configuration_extended.py index a0b11a0f..28ba56ce 100644 --- a/pasta_eln/GUI/ontology_configuration/ontology_configuration_extended.py +++ b/pasta_eln/GUI/ontology_configuration/ontology_configuration_extended.py @@ -29,7 +29,8 @@ from .reorder_column_delegate import ReorderColumnDelegate from .required_column_delegate import RequiredColumnDelegate from .utility_functions import adjust_ontology_data_to_v3, show_message, \ - get_next_possible_structural_level_label, get_types_for_display, adapt_type, generate_empty_type + get_next_possible_structural_level_label, get_types_for_display, adapt_type, generate_empty_type, \ + generate_required_properties class OntologyConfigurationForm(Ui_OntologyConfigurationBaseForm): @@ -179,7 +180,7 @@ def add_new_prop_category(self) -> None: return None # Add the new category to the property list and refresh the category combo box self.logger.info("User added new category: {%s}", new_category) - self.selected_type_properties[new_category] = [] + self.selected_type_properties[new_category] = generate_required_properties() self.propsCategoryComboBox.clear() self.propsCategoryComboBox.addItems(list(self.selected_type_properties.keys())) self.propsCategoryComboBox.setCurrentIndex(len(self.selected_type_properties.keys()) - 1) diff --git a/pasta_eln/GUI/ontology_configuration/utility_functions.py b/pasta_eln/GUI/ontology_configuration/utility_functions.py index 0dc83eb3..220c0480 100644 --- a/pasta_eln/GUI/ontology_configuration/utility_functions.py +++ b/pasta_eln/GUI/ontology_configuration/utility_functions.py @@ -189,18 +189,27 @@ def generate_empty_type(label: str) -> dict[str, Any]: "IRI": "", "label": label, "prop": { - "default": [ - { - "name": "-name", - "query": "What is the name of the project?", - "required": True - }, - { - "name": "-tags", - "query": "What are the tags associated with the project?", - "required": True - } - ] + "default": generate_required_properties() }, "attachments": [] } + + +def generate_required_properties() -> list[dict[str, Any]]: + """ + Generate a list of required properties for creating a new ontology type + Returns (list[dict[str, Any]]): List of required properties + + """ + return [ + { + "name": "-name", + "query": "What is the name of the project?", + "required": True + }, + { + "name": "-tags", + "query": "What are the tags associated with the project?", + "required": True + } + ] diff --git a/tests/app_tests/unit_tests/test_ontology_config_configuration_extended.py b/tests/app_tests/unit_tests/test_ontology_config_configuration_extended.py index d0df4f54..db328213 100644 --- a/tests/app_tests/unit_tests/test_ontology_config_configuration_extended.py +++ b/tests/app_tests/unit_tests/test_ontology_config_configuration_extended.py @@ -22,8 +22,9 @@ from pasta_eln.GUI.ontology_configuration.ontology_props_tableview_data_model import OntologyPropsTableViewModel from pasta_eln.GUI.ontology_configuration.reorder_column_delegate import ReorderColumnDelegate from pasta_eln.GUI.ontology_configuration.required_column_delegate import RequiredColumnDelegate -from pasta_eln.GUI.ontology_configuration.utility_functions import get_types_for_display, generate_empty_type -from tests.app_tests.common.fixtures import configuration_extended, ontology_doc_mock +from pasta_eln.GUI.ontology_configuration.utility_functions import get_types_for_display, generate_empty_type, \ + generate_required_properties +from tests.app_tests.common.fixtures import configuration_extended class TestOntologyConfigConfiguration(object): @@ -284,7 +285,7 @@ def test_add_new_prop_category_should_do_expected(self, if new_category: assert configuration_extended.add_new_prop_category() is None, "Nothing should be returned" logger_info_spy.assert_called_once_with("User added new category: {%s}", new_category) - set_items_selected_spy.assert_called_once_with(new_category, []) + set_items_selected_spy.assert_called_once_with(new_category, generate_required_properties()) set_current_index_category_combo_box_spy.assert_called_once_with(len(selected_type_properties.keys()) - 1) clear_category_combo_box_spy.assert_called_once_with() add_items_selected_spy.assert_called_once_with( From f6e11f8f6c1a7190f6dee43dfb290109dedc6fb7 Mon Sep 17 00:00:00 2001 From: jmurugan-fzj Date: Fri, 15 Sep 2023 17:37:32 +0200 Subject: [PATCH 21/22] - Added the missing test and corrected the failing tests --- .../test_ontology_configuration_extended.py | 16 ++++++++++++++++ ...est_ontology_config_configuration_extended.py | 2 +- 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/tests/app_tests/component_tests/test_ontology_configuration_extended.py b/tests/app_tests/component_tests/test_ontology_configuration_extended.py index 274cd7d9..bb702488 100644 --- a/tests/app_tests/component_tests/test_ontology_configuration_extended.py +++ b/tests/app_tests/component_tests/test_ontology_configuration_extended.py @@ -114,6 +114,22 @@ def test_component_add_new_type_with_loaded_ontology_should_display_create_new_t qtbot.mouseClick(ui_form.addTypePushButton, Qt.LeftButton) assert ui_form.create_type_dialog.buttonBox.isVisible() is True, "Create new type dialog not shown!" + def test_component_delete_new_type_without_ontology_loaded_should_show_error_message(self, + ontology_editor_gui: tuple[ + QApplication, + QtWidgets.QDialog, + OntologyConfigurationForm, + QtBot], + ontology_doc_mock: ontology_doc_mock, + mocker): + app, ui_dialog, ui_form, qtbot = ontology_editor_gui + mock_show_message = mocker.patch( + "pasta_eln.GUI.ontology_configuration.ontology_configuration_extended.show_message") + mocker.patch.object(ui_form, "ontology_loaded", False) + qtbot.mouseClick(ui_form.deleteTypePushButton, Qt.LeftButton) + mock_show_message.assert_called_once_with("Load the ontology data first....") + assert ui_form.create_type_dialog.buttonBox.isVisible() is False, "Create new type dialog should not be shown!" + def test_component_delete_selected_type_with_loaded_ontology_should_delete_and_update_ui(self, ontology_editor_gui: tuple[ diff --git a/tests/app_tests/unit_tests/test_ontology_config_configuration_extended.py b/tests/app_tests/unit_tests/test_ontology_config_configuration_extended.py index db328213..7a830c80 100644 --- a/tests/app_tests/unit_tests/test_ontology_config_configuration_extended.py +++ b/tests/app_tests/unit_tests/test_ontology_config_configuration_extended.py @@ -24,7 +24,7 @@ from pasta_eln.GUI.ontology_configuration.required_column_delegate import RequiredColumnDelegate from pasta_eln.GUI.ontology_configuration.utility_functions import get_types_for_display, generate_empty_type, \ generate_required_properties -from tests.app_tests.common.fixtures import configuration_extended +from tests.app_tests.common.fixtures import configuration_extended, ontology_doc_mock class TestOntologyConfigConfiguration(object): From 26bc0280d7b963116930972ba50b5783631513b3 Mon Sep 17 00:00:00 2001 From: jmurugan-fzj Date: Fri, 15 Sep 2023 17:49:44 +0200 Subject: [PATCH 22/22] - Changed "link" to "IRI" in fixedStringsJson.py --- pasta_eln/fixedStringsJson.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/pasta_eln/fixedStringsJson.py b/pasta_eln/fixedStringsJson.py index 2b402c9d..95f108f5 100644 --- a/pasta_eln/fixedStringsJson.py +++ b/pasta_eln/fixedStringsJson.py @@ -5,23 +5,23 @@ "_id":"-ontology-", "-version":3, - "x0": {"link":"", "label":"Projects", "prop": {"default": [ + "x0": {"IRI":"", "label":"Projects", "prop": {"default": [ {"name":"-name", "query":"What is the name of the project?"}, {"name":"status", "query":"What is the project status", "list":["active","paused","passive","finished"]}, {"name":"objective","query":"What is the objective?"}, {"name":"-tags"}, {"name":"comment", "query":"#tags comments remarks :field:value:"} ]}}, - "x1": {"link":"", "label":"Tasks", "prop": {"default": [ + "x1": {"IRI":"", "label":"Tasks", "prop": {"default": [ {"name":"-name", "query":"What is the name of task?"}, {"name":"comment", "query":"#tags comments remarks :field:value:"} ]}}, - "x2": {"link":"", "label":"Subtasks", "prop": {"default": [ + "x2": {"IRI":"", "label":"Subtasks", "prop": {"default": [ {"name":"-name", "query":"What is the name of subtask?"}, {"name":"comment", "query":"#tags comments remarks :field:value:"} ]}}, - "measurement": {"link":"", "label":"Measurements", "prop": {"default": [ + "measurement": {"IRI":"", "label":"Measurements", "prop": {"default": [ {"name":"-name", "query":"What is the file name?"}, {"name":"-tags"}, {"name":"comment", "query":"#tags comments remarks :field:value:"}, @@ -31,20 +31,20 @@ {"name":"sample", "query":"Which sample was used?", "list":"sample"}, {"name":"procedure", "query":"Which procedure was used?", "list":"procedure"} ]}}, - "sample": {"link":"", "label":"Samples", "prop": {"default": [ + "sample": {"IRI":"", "label":"Samples", "prop": {"default": [ {"name":"-name", "query":"What is the name / identifier of the sample?"}, {"name":"chemistry", "query":"What is its chemical composition?"}, {"name":"-tags"}, {"name":"comment", "query":"#tags comments remarks :field:value:"}, {"name":"qrCode"} ]}}, - "procedure": {"link":"", "label":"Procedures", "prop": {"default": [ + "procedure": {"IRI":"", "label":"Procedures", "prop": {"default": [ {"name":"-name", "query":"What is the name / path?"}, {"name":"-tags"}, {"name":"comment", "query":"#tags comments :field:value: e.g. #SOP_v1"}, {"name":"content", "query":"What is procedure (Markdown possible; autofilled if file given)?"} ]}}, - "instrument": {"link":"", "label":"Instruments", "prop": {"default": [ + "instrument": {"IRI":"", "label":"Instruments", "prop": {"default": [ {"name":"-name", "query":"What is the name / path?"}, {"name":"comment", "query":"#tags comments :field:value: e.g. #SOP_v1"}, {"name":"vendor", "query":"Who is the vendor?"}