From c31e8121ba3d8477e5293f856253c7bec4d11e43 Mon Sep 17 00:00:00 2001 From: Annemijn Jonkman Date: Fri, 21 Jun 2024 15:02:35 +0200 Subject: [PATCH 1/6] adding remove button and delete datasets --- eit_dash/callbacks/load_callbacks.py | 30 +++++++++++++++++++++++++++- eit_dash/definitions/element_ids.py | 1 + eit_dash/utils/common.py | 13 +++++++++--- eit_dash/utils/data_singleton.py | 14 +++++++++++++ 4 files changed, 54 insertions(+), 4 deletions(-) diff --git a/eit_dash/callbacks/load_callbacks.py b/eit_dash/callbacks/load_callbacks.py index afd8da0..f79d30d 100644 --- a/eit_dash/callbacks/load_callbacks.py +++ b/eit_dash/callbacks/load_callbacks.py @@ -210,7 +210,7 @@ def show_info( data_object.add_sequence(cut_data) # create the info summary card - card = create_info_card(cut_data) + card = create_info_card(cut_data, remove_button=True) # add the card to the current results if container_state: @@ -286,3 +286,31 @@ def store_clicked_file(n_clicks, title): return state["value"] return None + + +@callback( + Output(ids.DATASET_CONTAINER, "children", allow_duplicate=True), + [ + Input({"type": ids.REMOVE_DATA_BUTTON, "index": ALL}, "n_clicks"), + ], + [ + State(ids.DATASET_CONTAINER, "children"), + ], + prevent_initial_call=True, +) +def remove_dataset(n_clicks, container): + """React to clicking the remove button of a dataset. + + Removes the card from the results and the dataset from the loaded selections. + """ + # at the element creation time, the update should be avoided + if all(element is None for element in n_clicks): + raise PreventUpdate + + input_id = ctx.triggered_id["index"] + + # remove from the singleton + data_object.remove_data(input_id) + + results = [card for card in container if f"'index': '{input_id}'" not in str(card)] + return results \ No newline at end of file diff --git a/eit_dash/definitions/element_ids.py b/eit_dash/definitions/element_ids.py index 2dc9ad2..cba4a3b 100644 --- a/eit_dash/definitions/element_ids.py +++ b/eit_dash/definitions/element_ids.py @@ -29,6 +29,7 @@ LOAD_CANCEL_BUTTON = "load-cancel-button" LOAD_RESULTS_TITLE = "load-results-title" PARENT_DIR = "parent-dir" +REMOVE_DATA_BUTTON = "remove-data-button" STORED_CWD = "stored-cwd" SELECT_CONFIRM_BUTTON = "select-confirm-button" diff --git a/eit_dash/utils/common.py b/eit_dash/utils/common.py index 87262a6..274e784 100644 --- a/eit_dash/utils/common.py +++ b/eit_dash/utils/common.py @@ -43,11 +43,12 @@ def create_filter_results_card(parameters: dict) -> dbc.Card: return dbc.Card(dbc.CardBody(card_list), id=ids.FILTERING_SAVED_CARD) -def create_info_card(dataset: Sequence) -> dbc.Card: +def create_info_card(dataset: Sequence, remove_button: bool=False) -> dbc.Card: """Create the card with the information on the loaded dataset to be displayed in the Results section. Args: dataset: Sequence object containing the selected dataset + remove_button: add the remove button if set to True """ info_data = { "Name": dataset.eit_data["raw"].path.name, @@ -64,8 +65,14 @@ def create_info_card(dataset: Sequence) -> dbc.Card: html.H6(dataset.eit_data["raw"].vendor, className="card-subtitle"), ] card_list += [dbc.Row(f"{data}: {value}", style=styles.INFO_CARD) for data, value in info_data.items()] - - return dbc.Card(dbc.CardBody(card_list), id="card-1") + if remove_button: + card_list += [ + dbc.Button( + "Remove", + id={"type": ids.REMOVE_DATA_BUTTON, "index": dataset.label}, + ), + ] + return dbc.Card(dbc.CardBody(card_list), id=dataset.label) def create_selected_period_card( diff --git a/eit_dash/utils/data_singleton.py b/eit_dash/utils/data_singleton.py index 0536117..f896280 100644 --- a/eit_dash/utils/data_singleton.py +++ b/eit_dash/utils/data_singleton.py @@ -100,6 +100,20 @@ def add_stable_period( self._stable_periods.append(Period(data, dataset_index, period_index)) + def remove_data(self, label: str): + """Remove a sequence from data from the singleton. + + Args: + label: label of the sequence to be removed. + """ + for sequence in self._data: + if sequence.label == label: + self._data.remove(sequence) + return + + msg = f"Sequence with label {index} not found" + raise ValueError(msg) + def remove_stable_period(self, index: int): """Remove a stable period from the singleton. From 3a488f2d8a7953e9a9fda44cba13e1e68e294b95 Mon Sep 17 00:00:00 2001 From: JulietteFrancovich Date: Fri, 21 Jun 2024 15:25:55 +0200 Subject: [PATCH 2/6] fixing dataset labeling --- eit_dash/callbacks/load_callbacks.py | 4 ++-- eit_dash/utils/data_singleton.py | 16 ++++++++++++++++ 2 files changed, 18 insertions(+), 2 deletions(-) diff --git a/eit_dash/callbacks/load_callbacks.py b/eit_dash/callbacks/load_callbacks.py index f79d30d..88b8885 100644 --- a/eit_dash/callbacks/load_callbacks.py +++ b/eit_dash/callbacks/load_callbacks.py @@ -189,7 +189,7 @@ def show_info( start_sample = file_data.continuous_data[RAW_EIT_LABEL].time[0] stop_sample = file_data.continuous_data[RAW_EIT_LABEL].time[-1] - dataset_name = f"Dataset {data_object.get_sequence_list_length()}" + dataset_name = data_object.get_next_dataset_label() selected_signals = selected_signals or [] # get the name of the selected continuous signals @@ -313,4 +313,4 @@ def remove_dataset(n_clicks, container): data_object.remove_data(input_id) results = [card for card in container if f"'index': '{input_id}'" not in str(card)] - return results \ No newline at end of file + return results diff --git a/eit_dash/utils/data_singleton.py b/eit_dash/utils/data_singleton.py index f896280..81b8ed3 100644 --- a/eit_dash/utils/data_singleton.py +++ b/eit_dash/utils/data_singleton.py @@ -63,6 +63,22 @@ def get_sequence_at(self, index: int): return self._data[index] + def get_dataset_labels(self) -> list[int]: + """Get a list of the labels of the datasets currently available.""" + return [dataset.label for dataset in self._data] + + def get_next_dataset_label(self): + """Determines the label to be assigned to the next dataset.""" + available_labels = self.get_dataset_labels() + label = f"Dataset {self.get_sequence_list_length()}" + + if label in available_labels: + for i in range(self.get_sequence_list_length()): + label = f"Dataset {i}" + if label not in available_labels: + break + return label + def dataset_exists(self, index: int) -> bool: """Verify that a sequence with the provided index exists. From 5da4ef27edab0153e680e7e4af92809cbec5be75 Mon Sep 17 00:00:00 2001 From: JulietteFrancovich Date: Fri, 21 Jun 2024 15:34:43 +0200 Subject: [PATCH 3/6] added remove button when repopulating --- eit_dash/callbacks/load_callbacks.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/eit_dash/callbacks/load_callbacks.py b/eit_dash/callbacks/load_callbacks.py index 669dbce..ae52dad 100644 --- a/eit_dash/callbacks/load_callbacks.py +++ b/eit_dash/callbacks/load_callbacks.py @@ -326,4 +326,4 @@ def repopulate_data(reload): """Repopulate data after reloading page.""" # create the info summary card reloaded_data = data_object.get_all_sequences() - return [create_info_card(element) for element in reloaded_data] + return [create_info_card(element, remove_button=True) for element in reloaded_data] From 6dca9aa6dbb3c1b936f5a5bf4abe9db2567ee70e Mon Sep 17 00:00:00 2001 From: JulietteFrancovich Date: Fri, 21 Jun 2024 15:38:22 +0200 Subject: [PATCH 4/6] fixed linting --- eit_dash/callbacks/load_callbacks.py | 3 +-- eit_dash/utils/data_singleton.py | 2 +- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/eit_dash/callbacks/load_callbacks.py b/eit_dash/callbacks/load_callbacks.py index ae52dad..0e25925 100644 --- a/eit_dash/callbacks/load_callbacks.py +++ b/eit_dash/callbacks/load_callbacks.py @@ -312,8 +312,7 @@ def remove_dataset(n_clicks, container): # remove from the singleton data_object.remove_data(input_id) - results = [card for card in container if f"'index': '{input_id}'" not in str(card)] - return results + return [card for card in container if f"'index': '{input_id}'" not in str(card)] # Repopulate data after reload diff --git a/eit_dash/utils/data_singleton.py b/eit_dash/utils/data_singleton.py index 81b8ed3..457e065 100644 --- a/eit_dash/utils/data_singleton.py +++ b/eit_dash/utils/data_singleton.py @@ -127,7 +127,7 @@ def remove_data(self, label: str): self._data.remove(sequence) return - msg = f"Sequence with label {index} not found" + msg = f"Sequence with label {label} not found" raise ValueError(msg) def remove_stable_period(self, index: int): From d2231271cc580a67c0b6b1e5935203c8e35dc4e2 Mon Sep 17 00:00:00 2001 From: JulietteFrancovich Date: Fri, 21 Jun 2024 15:39:44 +0200 Subject: [PATCH 5/6] reformatted with ruff --- eit_dash/utils/common.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/eit_dash/utils/common.py b/eit_dash/utils/common.py index 274e784..0f7fe1c 100644 --- a/eit_dash/utils/common.py +++ b/eit_dash/utils/common.py @@ -43,7 +43,7 @@ def create_filter_results_card(parameters: dict) -> dbc.Card: return dbc.Card(dbc.CardBody(card_list), id=ids.FILTERING_SAVED_CARD) -def create_info_card(dataset: Sequence, remove_button: bool=False) -> dbc.Card: +def create_info_card(dataset: Sequence, remove_button: bool = False) -> dbc.Card: """Create the card with the information on the loaded dataset to be displayed in the Results section. Args: From 9498b92e06e90d03b6ffd04e9b512757b2100f3b Mon Sep 17 00:00:00 2001 From: JulietteFrancovich Date: Fri, 21 Jun 2024 15:45:24 +0200 Subject: [PATCH 6/6] updated test --- tests/unit_tests/test_load_callbacks.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/tests/unit_tests/test_load_callbacks.py b/tests/unit_tests/test_load_callbacks.py index b7b6519..80f4817 100644 --- a/tests/unit_tests/test_load_callbacks.py +++ b/tests/unit_tests/test_load_callbacks.py @@ -113,7 +113,13 @@ def test_show_info_callback(file_data: Sequence, expected_cut_info_data: dict): html.H6(Vendor.DRAEGER, className="card-subtitle"), ] card_list += [dbc.Row(f"{data}: {value}", style=styles.INFO_CARD) for data, value in expected_cut_info_data.items()] - mock_data_card = [dbc.Card(dbc.CardBody(card_list), id="card-1")] + card_list += [ + dbc.Button( + "Remove", + id={"type": ids.REMOVE_DATA_BUTTON, "index": "Dataset 0"}, + ), + ] + mock_data_card = [dbc.Card(dbc.CardBody(card_list), id="Dataset 0")] # Assessing the string converted objects, to check that the properties are the same. # Assessing for the equivalence of the objects directly will fail