From c32f2bd75344c4d14000a203991399f87d01cd23 Mon Sep 17 00:00:00 2001 From: Walter Baccinelli Date: Fri, 12 Apr 2024 09:51:05 +0200 Subject: [PATCH 01/16] filtering structure --- eit_dash/callbacks/preprocessing_callbacks.py | 167 +++++++++++++++++- eit_dash/definitions/element_ids.py | 9 + eit_dash/definitions/option_lists.py | 8 + eit_dash/pages/preprocessing.py | 105 ++++++++++- 4 files changed, 276 insertions(+), 13 deletions(-) diff --git a/eit_dash/callbacks/preprocessing_callbacks.py b/eit_dash/callbacks/preprocessing_callbacks.py index 1fe1522..83b3e7f 100644 --- a/eit_dash/callbacks/preprocessing_callbacks.py +++ b/eit_dash/callbacks/preprocessing_callbacks.py @@ -2,11 +2,12 @@ from dash import ALL, Input, Output, State, callback, ctx, dcc, html from dash.exceptions import PreventUpdate from eitprocessing.datahandling.sequence import Sequence +from eitprocessing.filters.butterworth_filters import ButterworthFilter, FILTER_TYPES import eit_dash.definitions.element_ids as ids import eit_dash.definitions.layout_styles as styles from eit_dash.app import data_object -from eit_dash.definitions.option_lists import PeriodsSelectMethods +from eit_dash.definitions.option_lists import FilterTypes, PeriodsSelectMethods from eit_dash.utils.common import ( create_slider_figure, get_selections_slidebar, @@ -14,6 +15,8 @@ mark_selected_periods, ) +import plotly.graph_objects as go + # ruff: noqa: D103 #TODO remove this line when finalizing this module @@ -47,7 +50,10 @@ def create_resampling_card(loaded_data): for data in loaded_data ] - options = [{"label": f'{data["Name"]}', "value": str(i)} for i, data in enumerate(loaded_data)] + options = [ + {"label": f'{data["Name"]}', "value": str(i)} + for i, data in enumerate(loaded_data) + ] return row, options @@ -55,7 +61,10 @@ def create_resampling_card(loaded_data): def create_loaded_data_summary(): loaded_data = data_object.get_all_sequences() - return [dbc.Row([html.Div(f"Loaded {dataset.label}", style={"textAlign": "left"})]) for dataset in loaded_data] + return [ + dbc.Row([html.Div(f"Loaded {dataset.label}", style={"textAlign": "left"})]) + for dataset in loaded_data + ] def create_selected_period_card(period: Sequence, dataset: str, index: int) -> dbc.Card: @@ -77,7 +86,10 @@ def create_selected_period_card(period: Sequence, dataset: str, index: int) -> d card_list = [ html.H4(period.label, className="card-title"), ] - card_list += [dbc.Row(f"{data}: {value}", style=styles.INFO_CARD) for data, value in info_data.items()] + card_list += [ + dbc.Row(f"{data}: {value}", style=styles.INFO_CARD) + for data, value in info_data.items() + ] card_list += [ dbc.Button( "Remove", @@ -97,7 +109,10 @@ def get_loaded_data(): for dataset in loaded_data: name = dataset.label if dataset.continuous_data: - data += [{"Name": name, "Data type": channel} for channel in dataset.continuous_data] + data += [ + {"Name": name, "Data type": channel} + for channel in dataset.continuous_data + ] if dataset.eit_data: data.append( { @@ -157,7 +172,6 @@ def load_datasets(title): [ Output(ids.OPEN_SYNCH_BUTTON, "disabled"), Output(ids.OPEN_SELECT_PERIODS_BUTTON, "disabled"), - Output(ids.OPEN_FILTER_DATA_BUTTON, "disabled"), Output(ids.SUMMARY_COLUMN, "children"), Output(ids.PREPROCESING_RESULTS_CONTAINER, "children", allow_duplicate=True), ], @@ -190,7 +204,7 @@ def update_summary(start, summary): create_selected_period_card(data, data.label, p.get_period_index()), ) - return False, False, False, summary, results + return False, False, summary, results @callback( @@ -229,6 +243,24 @@ def open_periods_modal(open_click, confirm_click) -> bool: return False +@callback( + Output(ids.FILTERING_SELECTION_POPUP, "is_open"), + [ + Input(ids.OPEN_FILTER_DATA_BUTTON, "n_clicks"), + Input(ids.FILTERING_CONFIRM_BUTTON, "n_clicks"), + ], + prevent_initial_call=True, +) +def open_filtering_modal(open_click, confirm_click) -> bool: + """open/close modal dialog for filtering data.""" + trigger = ctx.triggered_id + + if trigger == ids.OPEN_FILTER_DATA_BUTTON: + return True + + return False + + @callback( Output(ids.PERIODS_SELECTION_SELECT_DATASET, "children"), Input(ids.PERIODS_METHOD_SELECTOR, "value"), @@ -240,7 +272,10 @@ def populate_periods_selection_modal(method): if int_value == PeriodsSelectMethods.Manual.value: signals = data_object.get_all_sequences() - options = [{"label": sequence.label, "value": index} for index, sequence in enumerate(signals)] + options = [ + {"label": sequence.label, "value": index} + for index, sequence in enumerate(signals) + ] body = [ html.H6("Select one dataset"), @@ -465,8 +500,122 @@ def remove_period(n_clicks, container, figure): data_object.remove_stable_period(int(input_id)) # remove from the figure - figure["data"] = [trace for trace in figure["data"] if "meta" not in trace or trace["meta"]["uid"] != int(input_id)] + figure["data"] = [ + trace + for trace in figure["data"] + if "meta" not in trace or trace["meta"]["uid"] != int(input_id) + ] results = [card for card in container if f"'index': '{input_id}'" not in str(card)] return results, figure + + +# filters +@callback( + Output(ids.OPEN_FILTER_DATA_BUTTON, "disabled"), + Input(ids.PREPROCESING_RESULTS_CONTAINER, "children"), + prevent_initial_call=True, +) +def enable_filter_button(results): + """Enable the button for opening the filter modal.""" + if results: + return False + return True + + +@callback( + [ + Output(ids.FILTER_PARAMS, "hidden"), + Output(ids.FILTER_CUTOFF_LOW, "disabled"), + Output(ids.FILTER_CUTOFF_HIGH, "disabled"), + ], + Input(ids.FILTER_SELECTOR, "value"), + prevent_initial_call=True, +) +def show_filters_params(selected): + """Make visible the div containing the filters params.""" + cutoff_low = cutoff_high = filter_params = False + + # if no filter has been selected, hide the params + if not selected: + filter_params = True + + if int(selected) == FilterTypes.lowpass.value: + cutoff_low = True + elif int(selected) == FilterTypes.highpass.value: + cutoff_high = True + + return filter_params, cutoff_low, cutoff_high + + +@callback( + Output(ids.FILTER_APPLY, "disabled"), + [ + Input(ids.FILTER_CUTOFF_LOW, "value"), + Input(ids.FILTER_CUTOFF_HIGH, "value"), + Input(ids.FILTER_ORDER, "value"), + ], + [ + State(ids.FILTER_CUTOFF_LOW, "value"), + State(ids.FILTER_CUTOFF_HIGH, "value"), + State(ids.FILTER_ORDER, "value"), + State(ids.FILTER_SELECTOR, "value"), + ], + prevent_initial_call=True, +) +def enable_apply_button( + co_low_in, co_high_in, order_in, co_low, co_high, order, filter_selected +): + """Enable the apply button.""" + if ( + (int(filter_selected) == FilterTypes.lowpass.value and co_high and co_high > 0) + or ( + int(filter_selected) == FilterTypes.highpass.value and co_low and co_low > 0 + ) + ) and order: + return False + + return True + + +@callback( + [ + Output(ids.PREPROCESING_RESULTS_CONTAINER, "children", allow_duplicate=True), + Output(ids.FILTERING_RESULTS_GRAPH, "figure"), + ], + [ + Input(ids.FILTER_APPLY, "n_clicks"), + ], + [ + State(ids.FILTER_CUTOFF_LOW, "value"), + State(ids.FILTER_CUTOFF_HIGH, "value"), + State(ids.FILTER_ORDER, "value"), + State(ids.FILTER_SELECTOR, "value"), + State(ids.PREPROCESING_RESULTS_CONTAINER, "children"), + ], + prevent_initial_call=True, +) +def enable_apply_button(_, co_low, co_high, order, filter_selected, results): + """Apply the filter.""" + + if co_high is None: + cutoff_frequency = co_low + elif co_low is None: + cutoff_frequency = co_high + else: + cutoff_frequency = [co_low, co_high] + + for period in data_object.get_all_stable_periods(): + filt = ButterworthFilter( + filter_type=FilterTypes(int(filter_selected)).name, + cutoff_frequency=cutoff_frequency, + order=order, + sample_frequency=period.get_data().eit_data.data["raw"].framerate, + ) + + res = filt.apply_filter(period.get_data().eit_data.data["raw"].global_impedance) + + # TODO: update the results view + + return results, go.Figure(go.Scatter(y=res)) diff --git a/eit_dash/definitions/element_ids.py b/eit_dash/definitions/element_ids.py index 110a3f0..74b0a20 100644 --- a/eit_dash/definitions/element_ids.py +++ b/eit_dash/definitions/element_ids.py @@ -22,6 +22,15 @@ CONFIRM_RESAMPLING_BUTTON = "confirm-resampling-button" CONFIRM_SYNCH_BUTTON = "confirm-synch-button" DATASET_SELECTION_CHECKBOX = "dataset-selection-checkbox" +FILTER_APPLY = "filter-apply" +FILTER_ORDER = "filter-order" +FILTER_CUTOFF_LOW = "filter-cutoff-low" +FILTER_CUTOFF_HIGH = "filter-cutoff-high" +FILTERING_CONFIRM_BUTTON = "filtering-confirm-button" +FILTER_PARAMS = "filter-params" +FILTERING_SELECTION_POPUP = "filtering-selection-popup" +FILTER_SELECTOR = "filter-selector" +FILTERING_RESULTS_GRAPH = "filtering-results-graph" SYNCHRONIZATION_POPUP = "synchronization-popup" SYNCHRONIZATION_CONFIRM_BUTTON = "synchronization-confirm-button" SYNC_DATA_PREVIEW_CONTAINER = "sync-data-preview-container" diff --git a/eit_dash/definitions/option_lists.py b/eit_dash/definitions/option_lists.py index 5ffe9e5..5a15d4b 100644 --- a/eit_dash/definitions/option_lists.py +++ b/eit_dash/definitions/option_lists.py @@ -1,5 +1,7 @@ from enum import Enum +from eitprocessing.filters.butterworth_filters import FILTER_TYPES + class InputFiletypes(Enum): """One hot encoding of input file types (EIT vendors).""" @@ -11,6 +13,12 @@ class InputFiletypes(Enum): Poly5 = 4 +# create the filters enum, according to what has been defined in the filters module +filters = {value: idx for idx, value in enumerate(FILTER_TYPES) if FILTER_TYPES[value].available_in_gui} + +FilterTypes = Enum("FilterTypes", filters) + + class SignalSelections(Enum): """One hot encoding of selectable signals.""" diff --git a/eit_dash/pages/preprocessing.py b/eit_dash/pages/preprocessing.py index 652038c..5524af5 100644 --- a/eit_dash/pages/preprocessing.py +++ b/eit_dash/pages/preprocessing.py @@ -3,7 +3,11 @@ import eit_dash.definitions.element_ids as ids import eit_dash.definitions.layout_styles as styles -from eit_dash.definitions.option_lists import PeriodsSelectMethods, SynchMethods +from eit_dash.definitions.option_lists import ( + FilterTypes, + PeriodsSelectMethods, + SynchMethods, +) register_page(__name__, path="/preprocessing") @@ -71,7 +75,7 @@ ), html.P(), dbc.Row( - dbc.Button("Filter data", id=ids.OPEN_FILTER_DATA_BUTTON, disabled=False), + dbc.Button("Filter data", id=ids.OPEN_FILTER_DATA_BUTTON, disabled=True), ), ], ) @@ -96,7 +100,10 @@ [ dbc.Select( id=ids.SYNC_METHOD_SELECTOR, - options=[{"label": method.name, "value": method.value} for method in SynchMethods], + options=[ + {"label": method.name, "value": method.value} + for method in SynchMethods + ], value=str(SynchMethods.manual.value), ), html.P(), @@ -171,7 +178,10 @@ html.H6("Periods selection method"), dbc.Select( id=ids.PERIODS_METHOD_SELECTOR, - options=[{"label": method.name, "value": method.value} for method in PeriodsSelectMethods], + options=[ + {"label": method.name, "value": method.value} + for method in PeriodsSelectMethods + ], ), modal_selection_body, ], @@ -195,6 +205,92 @@ ], ) + +filter_params = html.Div( + [ + dbc.Row( + [ + dbc.Col( + [ + html.P("Filter Order"), + dbc.Input(id=ids.FILTER_ORDER, type="number", min=0), + ], + ), + dbc.Col( + [ + html.P("Cut off frequency low"), + dbc.Input(id=ids.FILTER_CUTOFF_LOW, type="number", min=0), + ], + ), + dbc.Col( + [ + html.P("Cut off frequency high"), + dbc.Input(id=ids.FILTER_CUTOFF_HIGH, type="number", min=0), + ], + ), + ], + ), + dbc.Row( + [ + dbc.Col( + [ + dbc.Button( + "Apply", + id=ids.FILTER_APPLY, + disabled=True, + ), + ], + ), + ], + style=styles.BUTTONS_ROW, + ), + dbc.Row( + [dcc.Graph(id=ids.FILTERING_RESULTS_GRAPH)], + style=styles.BUTTONS_ROW, + ), + ], + id=ids.FILTER_PARAMS, + hidden=True, +) + +modal_filtering = html.Div( + [ + dbc.Modal( + [ + dbc.ModalHeader(dbc.ModalTitle("Filter"), close_button=True), + dbc.ModalBody( + [ + html.H6("Select a filter"), + dbc.Select( + id=ids.FILTER_SELECTOR, + options=[ + {"label": filt.name, "value": filt.value} + for filt in FilterTypes + ], + ), + html.P(), + filter_params, + ], + ), + dbc.ModalFooter( + dbc.Button( + "Close", + id=ids.FILTERING_CONFIRM_BUTTON, + className="ms-auto", + n_clicks=0, + ), + ), + ], + id=ids.FILTERING_SELECTION_POPUP, + centered=True, + is_open=False, + backdrop=False, + scrollable=True, + size="xl", + ), + ], +) + layout = dbc.Row( [ html.H1("PRE-PROCESSING", style=styles.COLUMN_TITLE), @@ -203,5 +299,6 @@ results, modal_synchronization, modal_selection, + modal_filtering, ], ) From 33f067061f6588b9f5ae8c32c37376bdb9259514 Mon Sep 17 00:00:00 2001 From: Walter Baccinelli Date: Fri, 12 Apr 2024 15:52:20 +0200 Subject: [PATCH 02/16] apply filter and store data --- eit_dash/callbacks/preprocessing_callbacks.py | 91 ++++++++++++++++--- eit_dash/pages/preprocessing.py | 4 +- eit_dash/utils/common.py | 23 ++--- eit_dash/utils/data_singleton.py | 30 +++++- 4 files changed, 121 insertions(+), 27 deletions(-) diff --git a/eit_dash/callbacks/preprocessing_callbacks.py b/eit_dash/callbacks/preprocessing_callbacks.py index 83b3e7f..2b19be3 100644 --- a/eit_dash/callbacks/preprocessing_callbacks.py +++ b/eit_dash/callbacks/preprocessing_callbacks.py @@ -1,5 +1,8 @@ +import time + import dash_bootstrap_components as dbc from dash import ALL, Input, Output, State, callback, ctx, dcc, html +from eitprocessing.datahandling.continuousdata import ContinuousData from dash.exceptions import PreventUpdate from eitprocessing.datahandling.sequence import Sequence from eitprocessing.filters.butterworth_filters import ButterworthFilter, FILTER_TYPES @@ -17,6 +20,9 @@ import plotly.graph_objects as go +from eit_dash.utils.data_singleton import Period + + # ruff: noqa: D103 #TODO remove this line when finalizing this module @@ -360,6 +366,9 @@ def initialize_figure( if saved_periods := data_object.get_dataset_stable_periods(int(dataset)): current_figure = mark_selected_periods(current_figure, saved_periods) + # THIS IS A TEMPORARY PATCH + time.sleep(2) + return current_figure, style @@ -415,7 +424,9 @@ def select_period( data_object.add_stable_period(cut_data, int(dataset)) # TODO: explore Patch https://dash.plotly.com/partial-properties - current_figure = mark_selected_periods(current_figure, [cut_data], period_index) + current_figure = mark_selected_periods( + current_figure, [data_object.get_stable_period(period_index)] + ) # TODO: refactor to avoid duplications ok = [options[s]["label"] for s in signals] @@ -573,6 +584,12 @@ def enable_apply_button( or ( int(filter_selected) == FilterTypes.highpass.value and co_low and co_low > 0 ) + or ( + int(filter_selected) + in [FilterTypes.bandpass.value, FilterTypes.bandstop.value] + and co_low > 0 + and co_low > 0 + ) ) and order: return False @@ -583,6 +600,7 @@ def enable_apply_button( [ Output(ids.PREPROCESING_RESULTS_CONTAINER, "children", allow_duplicate=True), Output(ids.FILTERING_RESULTS_GRAPH, "figure"), + Output(ids.FILTERING_RESULTS_GRAPH, "style"), ], [ Input(ids.FILTER_APPLY, "n_clicks"), @@ -596,9 +614,33 @@ def enable_apply_button( ], prevent_initial_call=True, ) -def enable_apply_button(_, co_low, co_high, order, filter_selected, results): +def apply_filter(_, co_low, co_high, order, filter_selected, results): """Apply the filter.""" + # build filter params + filter_params = get_selected_parameters(co_high, co_low, order, filter_selected) + + # filter all the periods + for period in data_object.get_all_stable_periods(): + filtered_data = filter_data(period.get_data(), filter_params) + period.update_data(filtered_data) + + # TODO: update the results view + + return results, go.Figure(), styles.GRAPH + + +def get_selected_parameters(co_high, co_low, order, filter_selected) -> dict: + """Build the parameters dictionary for the filter. + + Args: + co_high: cut off upper limit + co_low: cut off lower limit + order: filter order + filter_selected: vlue coming from the filter selection dropbox + + Returns: dictionary containing parameters for the filter + """ if co_high is None: cutoff_frequency = co_low elif co_low is None: @@ -606,16 +648,41 @@ def enable_apply_button(_, co_low, co_high, order, filter_selected, results): else: cutoff_frequency = [co_low, co_high] - for period in data_object.get_all_stable_periods(): - filt = ButterworthFilter( - filter_type=FilterTypes(int(filter_selected)).name, - cutoff_frequency=cutoff_frequency, - order=order, - sample_frequency=period.get_data().eit_data.data["raw"].framerate, - ) + return dict( + filter_type=FilterTypes(int(filter_selected)).name, + cutoff_frequency=cutoff_frequency, + order=order, + ) - res = filt.apply_filter(period.get_data().eit_data.data["raw"].global_impedance) - # TODO: update the results view +def filter_data(data: Sequence, filter_params: dict) -> Sequence | None: + """Filter the impedance data in a period. + + Args: + data: sequence containing the data + filter_params: parameters for the filter - return results, go.Figure(go.Scatter(y=res)) + Returns: the data with the filtered version added + """ + + filter_params["sample_frequency"] = data.eit_data.data["raw"].framerate + + filt = ButterworthFilter(**filter_params) + + try: + data.continuous_data.add( + ContinuousData( + "global_impedance_filtered", + "global_impedance filtered with " f"{filter_params['filter_type']}", + "a.u.", + "impedance", + parameters=filter_params, + time=data.eit_data.data["raw"].time, + values=filt.apply_filter(data.eit_data.data["raw"].global_impedance), + ), + ) + except Exception as e: + print(f"Error{e}") + return None + + return data diff --git a/eit_dash/pages/preprocessing.py b/eit_dash/pages/preprocessing.py index 5524af5..c7cc0ac 100644 --- a/eit_dash/pages/preprocessing.py +++ b/eit_dash/pages/preprocessing.py @@ -188,7 +188,7 @@ ), dbc.ModalFooter( dbc.Button( - "Close", + "Confirm", id=ids.PERIODS_CONFIRM_BUTTON, className="ms-auto", n_clicks=0, @@ -245,7 +245,7 @@ style=styles.BUTTONS_ROW, ), dbc.Row( - [dcc.Graph(id=ids.FILTERING_RESULTS_GRAPH)], + [dcc.Graph(id=ids.FILTERING_RESULTS_GRAPH, style=styles.EMPTY_ELEMENT)], style=styles.BUTTONS_ROW, ), ], diff --git a/eit_dash/utils/common.py b/eit_dash/utils/common.py index 4d13c41..bf9db8e 100644 --- a/eit_dash/utils/common.py +++ b/eit_dash/utils/common.py @@ -2,6 +2,8 @@ from typing import TYPE_CHECKING +from eit_dash.utils.data_singleton import Period + import plotly.graph_objects as go if TYPE_CHECKING: @@ -101,9 +103,7 @@ def create_slider_figure( def mark_selected_periods( - original_figure: go.Figure | dict, - periods: list[Sequence], - period_index: int, + original_figure: go.Figure | dict, periods: list[Period] ) -> go.Figure: """ Create the figure for the selection of range. @@ -115,12 +115,13 @@ def mark_selected_periods( period_index: index of the selected period """ for period in periods: - for eit_variant in period.eit_data: + seq = period.get_data() + for eit_variant in seq.eit_data: selected_impedance = go.Scatter( - x=period.eit_data[eit_variant].time, - y=period.eit_data[eit_variant].global_impedance, + x=seq.eit_data[eit_variant].time, + y=seq.eit_data[eit_variant].global_impedance, name=eit_variant, - meta={"uid": period_index}, + meta={"uid": period.get_period_index()}, line={"color": "black"}, showlegend=False, ).to_plotly_json() @@ -130,12 +131,12 @@ def mark_selected_periods( else: original_figure["data"].append(selected_impedance) - for n, cont_signal in enumerate(period.continuous_data): + for n, cont_signal in enumerate(seq.continuous_data): selected_signal = go.Scatter( - x=period.continuous_data[cont_signal].time, - y=period.continuous_data[cont_signal].values, + x=seq.continuous_data[cont_signal].time, + y=seq.continuous_data[cont_signal].values, name=cont_signal, - meta={"uid": period_index}, + meta={"uid": period.get_period_index()}, opacity=0.5, yaxis=f"y{n + 2}", line={"color": "black"}, diff --git a/eit_dash/utils/data_singleton.py b/eit_dash/utils/data_singleton.py index 3bd426a..56a98fd 100644 --- a/eit_dash/utils/data_singleton.py +++ b/eit_dash/utils/data_singleton.py @@ -111,7 +111,7 @@ def remove_stable_period(self, index: int): self._stable_periods.remove(period) return - msg = f"Period with index {index}not found" + msg = f"Period with index {index} not found" raise ValueError(msg) def get_stable_periods_list_length(self): @@ -129,7 +129,11 @@ def get_dataset_stable_periods(self, dataset_index: int) -> list[Sequence]: msg = f"Index higher than list length {self.get_sequence_list_length()}" raise ValueError(msg) - return [period.get_data() for period in self._stable_periods if period.get_dataset_index() == dataset_index] + return [ + period + for period in self._stable_periods + if period.get_dataset_index() == dataset_index + ] def get_all_stable_periods(self) -> list[Period]: """Retrieve all the saved stable periods. @@ -138,6 +142,19 @@ def get_all_stable_periods(self) -> list[Period]: """ return self._stable_periods + def get_stable_period(self, index: int): + """Get a stable period from the singleton, using its index. + + Args: + index: index of the sable period to be retrieved. + """ + for period in self._stable_periods: + if period.get_period_index() == index: + return period + + msg = f"Period with index {index} not found" + raise ValueError(msg) + @dataclass class Period: @@ -179,3 +196,12 @@ def set_data(self, data: Sequence, dataset_index: int, period_index: int): self._data = data self._dataset_index = dataset_index self._period_index = period_index + + def update_data(self, data: Sequence) -> Sequence: + """Update the data of a period. + + Args: + data: The sequence with the updated period data + """ + + self._data = data From c2c1a0243a1313dcae5233a77d01e2642609c517 Mon Sep 17 00:00:00 2001 From: Walter Baccinelli Date: Fri, 12 Apr 2024 17:07:22 +0200 Subject: [PATCH 03/16] view filtered data --- eit_dash/callbacks/preprocessing_callbacks.py | 85 ++++++++++++++----- eit_dash/definitions/element_ids.py | 2 + eit_dash/pages/preprocessing.py | 14 ++- 3 files changed, 79 insertions(+), 22 deletions(-) diff --git a/eit_dash/callbacks/preprocessing_callbacks.py b/eit_dash/callbacks/preprocessing_callbacks.py index 2b19be3..eeb3984 100644 --- a/eit_dash/callbacks/preprocessing_callbacks.py +++ b/eit_dash/callbacks/preprocessing_callbacks.py @@ -599,8 +599,8 @@ def enable_apply_button( @callback( [ Output(ids.PREPROCESING_RESULTS_CONTAINER, "children", allow_duplicate=True), - Output(ids.FILTERING_RESULTS_GRAPH, "figure"), - Output(ids.FILTERING_RESULTS_GRAPH, "style"), + Output(ids.FILTERING_RESULTS_DIV, "hidden"), + Output(ids.FILTERING_SELCET_PERIOD_VIEW, "options"), ], [ Input(ids.FILTER_APPLY, "n_clicks"), @@ -620,14 +620,61 @@ def apply_filter(_, co_low, co_high, order, filter_selected, results): # build filter params filter_params = get_selected_parameters(co_high, co_low, order, filter_selected) + options = [] + # filter all the periods for period in data_object.get_all_stable_periods(): - filtered_data = filter_data(period.get_data(), filter_params) - period.update_data(filtered_data) + try: + filtered_data = filter_data(period.get_data(), filter_params) + period.update_data(filtered_data) + + options.append( + { + "label": f"Period {period.get_period_index()}", + "value": period.get_period_index(), + } + ) + except Exception as e: + print(f"Error{e}") - # TODO: update the results view + return results, False, options - return results, go.Figure(), styles.GRAPH + +@callback( + [ + Output(ids.FILTERING_RESULTS_GRAPH, "figure"), + Output(ids.FILTERING_RESULTS_GRAPH, "style"), + ], + Input(ids.FILTERING_SELCET_PERIOD_VIEW, "value"), + Input(ids.FILTER_APPLY, "n_clicks"), + prevent_initial_call=True, +) +def show_filtered_results(selected, _): + """When selecting a period, shows the original and the filtered signal.""" + if not selected: + raise PreventUpdate + + fig = go.Figure() + + data = data_object.get_stable_period(int(selected)).get_data() + + fig.add_trace( + go.Scatter( + x=data.eit_data.data["raw"].time, + y=data.eit_data.data["raw"].global_impedance, + name="Original signal", + ) + ) + + fig.add_trace( + go.Scatter( + x=data.continuous_data.data["global_impedance_filtered"].time, + y=data.continuous_data.data["global_impedance_filtered"].values, + name="Filtered signal", + ) + ) + + return fig, styles.GRAPH def get_selected_parameters(co_high, co_low, order, filter_selected) -> dict: @@ -669,20 +716,16 @@ def filter_data(data: Sequence, filter_params: dict) -> Sequence | None: filt = ButterworthFilter(**filter_params) - try: - data.continuous_data.add( - ContinuousData( - "global_impedance_filtered", - "global_impedance filtered with " f"{filter_params['filter_type']}", - "a.u.", - "impedance", - parameters=filter_params, - time=data.eit_data.data["raw"].time, - values=filt.apply_filter(data.eit_data.data["raw"].global_impedance), - ), - ) - except Exception as e: - print(f"Error{e}") - return None + data.continuous_data.add( + ContinuousData( + "global_impedance_filtered", + "global_impedance filtered with " f"{filter_params['filter_type']}", + "a.u.", + "impedance", + parameters=filter_params, + time=data.eit_data.data["raw"].time, + values=filt.apply_filter(data.eit_data.data["raw"].global_impedance), + ), + ) return data diff --git a/eit_dash/definitions/element_ids.py b/eit_dash/definitions/element_ids.py index 74b0a20..d786fc3 100644 --- a/eit_dash/definitions/element_ids.py +++ b/eit_dash/definitions/element_ids.py @@ -30,6 +30,8 @@ FILTER_PARAMS = "filter-params" FILTERING_SELECTION_POPUP = "filtering-selection-popup" FILTER_SELECTOR = "filter-selector" +FILTERING_SELCET_PERIOD_VIEW = "filtering-select-period-view" +FILTERING_RESULTS_DIV = "filtering-results-div" FILTERING_RESULTS_GRAPH = "filtering-results-graph" SYNCHRONIZATION_POPUP = "synchronization-popup" SYNCHRONIZATION_CONFIRM_BUTTON = "synchronization-confirm-button" diff --git a/eit_dash/pages/preprocessing.py b/eit_dash/pages/preprocessing.py index c7cc0ac..f1bd99a 100644 --- a/eit_dash/pages/preprocessing.py +++ b/eit_dash/pages/preprocessing.py @@ -245,7 +245,19 @@ style=styles.BUTTONS_ROW, ), dbc.Row( - [dcc.Graph(id=ids.FILTERING_RESULTS_GRAPH, style=styles.EMPTY_ELEMENT)], + [ + html.Div( + [ + html.H6("Select a period to view"), + dbc.Select(id=ids.FILTERING_SELCET_PERIOD_VIEW), + dcc.Graph( + id=ids.FILTERING_RESULTS_GRAPH, style=styles.EMPTY_ELEMENT + ), + ], + id=ids.FILTERING_RESULTS_DIV, + hidden=True, + ) + ], style=styles.BUTTONS_ROW, ), ], From 0dd3e3442b12b7439380d8a03b861e6bb8a2ed26 Mon Sep 17 00:00:00 2001 From: Walter Baccinelli Date: Fri, 12 Apr 2024 17:23:00 +0200 Subject: [PATCH 04/16] alert message --- eit_dash/callbacks/preprocessing_callbacks.py | 16 ++++++++++++++-- eit_dash/definitions/element_ids.py | 1 + eit_dash/pages/preprocessing.py | 9 +++++++++ 3 files changed, 24 insertions(+), 2 deletions(-) diff --git a/eit_dash/callbacks/preprocessing_callbacks.py b/eit_dash/callbacks/preprocessing_callbacks.py index eeb3984..67d7717 100644 --- a/eit_dash/callbacks/preprocessing_callbacks.py +++ b/eit_dash/callbacks/preprocessing_callbacks.py @@ -601,6 +601,8 @@ def enable_apply_button( Output(ids.PREPROCESING_RESULTS_CONTAINER, "children", allow_duplicate=True), Output(ids.FILTERING_RESULTS_DIV, "hidden"), Output(ids.FILTERING_SELCET_PERIOD_VIEW, "options"), + Output(ids.ALERT_FILTER, "is_open"), + Output(ids.ALERT_FILTER, "children"), ], [ Input(ids.FILTER_APPLY, "n_clicks"), @@ -616,6 +618,13 @@ def enable_apply_button( ) def apply_filter(_, co_low, co_high, order, filter_selected, results): """Apply the filter.""" + # flag for the alert message + show_alert = False + # alert message + alert_msg = "" + + # flag for showing graphs + hidden_div = False # build filter params filter_params = get_selected_parameters(co_high, co_low, order, filter_selected) @@ -635,9 +644,12 @@ def apply_filter(_, co_low, co_high, order, filter_selected, results): } ) except Exception as e: - print(f"Error{e}") + show_alert = True + alert_msg = f"{e}" + hidden_div = True + break - return results, False, options + return results, hidden_div, options, show_alert, alert_msg @callback( diff --git a/eit_dash/definitions/element_ids.py b/eit_dash/definitions/element_ids.py index d786fc3..633508c 100644 --- a/eit_dash/definitions/element_ids.py +++ b/eit_dash/definitions/element_ids.py @@ -19,6 +19,7 @@ SELECT_CONFIRM_BUTTON = "select-confirm-button" # preprocessing page +ALERT_FILTER = "alert-filter" CONFIRM_RESAMPLING_BUTTON = "confirm-resampling-button" CONFIRM_SYNCH_BUTTON = "confirm-synch-button" DATASET_SELECTION_CHECKBOX = "dataset-selection-checkbox" diff --git a/eit_dash/pages/preprocessing.py b/eit_dash/pages/preprocessing.py index f1bd99a..dbe0e3c 100644 --- a/eit_dash/pages/preprocessing.py +++ b/eit_dash/pages/preprocessing.py @@ -205,9 +205,18 @@ ], ) +alert_filter = dbc.Alert( + [], + id=ids.ALERT_FILTER, + color="danger", + dismissable=True, + is_open=False, + duration=3000, +) filter_params = html.Div( [ + dbc.Row(alert_filter), dbc.Row( [ dbc.Col( From da8200b4b78e6abede72ae78a37a5d25cb6bace6 Mon Sep 17 00:00:00 2001 From: Walter Baccinelli Date: Fri, 12 Apr 2024 17:24:47 +0200 Subject: [PATCH 05/16] updated lock file --- poetry.lock | 811 ++++++++++++++++++++++++++++------------------------ 1 file changed, 444 insertions(+), 367 deletions(-) diff --git a/poetry.lock b/poetry.lock index bf983fe..413f94e 100644 --- a/poetry.lock +++ b/poetry.lock @@ -252,64 +252,64 @@ files = [ [[package]] name = "contourpy" -version = "1.2.0" +version = "1.2.1" description = "Python library for calculating contours of 2D quadrilateral grids" optional = false python-versions = ">=3.9" files = [ - {file = "contourpy-1.2.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:0274c1cb63625972c0c007ab14dd9ba9e199c36ae1a231ce45d725cbcbfd10a8"}, - {file = "contourpy-1.2.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:ab459a1cbbf18e8698399c595a01f6dcc5c138220ca3ea9e7e6126232d102bb4"}, - {file = "contourpy-1.2.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6fdd887f17c2f4572ce548461e4f96396681212d858cae7bd52ba3310bc6f00f"}, - {file = "contourpy-1.2.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5d16edfc3fc09968e09ddffada434b3bf989bf4911535e04eada58469873e28e"}, - {file = "contourpy-1.2.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1c203f617abc0dde5792beb586f827021069fb6d403d7f4d5c2b543d87edceb9"}, - {file = "contourpy-1.2.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b69303ceb2e4d4f146bf82fda78891ef7bcd80c41bf16bfca3d0d7eb545448aa"}, - {file = "contourpy-1.2.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:884c3f9d42d7218304bc74a8a7693d172685c84bd7ab2bab1ee567b769696df9"}, - {file = "contourpy-1.2.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:4a1b1208102be6e851f20066bf0e7a96b7d48a07c9b0cfe6d0d4545c2f6cadab"}, - {file = "contourpy-1.2.0-cp310-cp310-win32.whl", hash = "sha256:34b9071c040d6fe45d9826cbbe3727d20d83f1b6110d219b83eb0e2a01d79488"}, - {file = "contourpy-1.2.0-cp310-cp310-win_amd64.whl", hash = "sha256:bd2f1ae63998da104f16a8b788f685e55d65760cd1929518fd94cd682bf03e41"}, - {file = "contourpy-1.2.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:dd10c26b4eadae44783c45ad6655220426f971c61d9b239e6f7b16d5cdaaa727"}, - {file = "contourpy-1.2.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:5c6b28956b7b232ae801406e529ad7b350d3f09a4fde958dfdf3c0520cdde0dd"}, - {file = "contourpy-1.2.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ebeac59e9e1eb4b84940d076d9f9a6cec0064e241818bcb6e32124cc5c3e377a"}, - {file = "contourpy-1.2.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:139d8d2e1c1dd52d78682f505e980f592ba53c9f73bd6be102233e358b401063"}, - {file = "contourpy-1.2.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1e9dc350fb4c58adc64df3e0703ab076f60aac06e67d48b3848c23647ae4310e"}, - {file = "contourpy-1.2.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:18fc2b4ed8e4a8fe849d18dce4bd3c7ea637758c6343a1f2bae1e9bd4c9f4686"}, - {file = "contourpy-1.2.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:16a7380e943a6d52472096cb7ad5264ecee36ed60888e2a3d3814991a0107286"}, - {file = "contourpy-1.2.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:8d8faf05be5ec8e02a4d86f616fc2a0322ff4a4ce26c0f09d9f7fb5330a35c95"}, - {file = "contourpy-1.2.0-cp311-cp311-win32.whl", hash = "sha256:67b7f17679fa62ec82b7e3e611c43a016b887bd64fb933b3ae8638583006c6d6"}, - {file = "contourpy-1.2.0-cp311-cp311-win_amd64.whl", hash = "sha256:99ad97258985328b4f207a5e777c1b44a83bfe7cf1f87b99f9c11d4ee477c4de"}, - {file = "contourpy-1.2.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:575bcaf957a25d1194903a10bc9f316c136c19f24e0985a2b9b5608bdf5dbfe0"}, - {file = "contourpy-1.2.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:9e6c93b5b2dbcedad20a2f18ec22cae47da0d705d454308063421a3b290d9ea4"}, - {file = "contourpy-1.2.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:464b423bc2a009088f19bdf1f232299e8b6917963e2b7e1d277da5041f33a779"}, - {file = "contourpy-1.2.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:68ce4788b7d93e47f84edd3f1f95acdcd142ae60bc0e5493bfd120683d2d4316"}, - {file = "contourpy-1.2.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3d7d1f8871998cdff5d2ff6a087e5e1780139abe2838e85b0b46b7ae6cc25399"}, - {file = "contourpy-1.2.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6e739530c662a8d6d42c37c2ed52a6f0932c2d4a3e8c1f90692ad0ce1274abe0"}, - {file = "contourpy-1.2.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:247b9d16535acaa766d03037d8e8fb20866d054d3c7fbf6fd1f993f11fc60ca0"}, - {file = "contourpy-1.2.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:461e3ae84cd90b30f8d533f07d87c00379644205b1d33a5ea03381edc4b69431"}, - {file = "contourpy-1.2.0-cp312-cp312-win32.whl", hash = "sha256:1c2559d6cffc94890b0529ea7eeecc20d6fadc1539273aa27faf503eb4656d8f"}, - {file = "contourpy-1.2.0-cp312-cp312-win_amd64.whl", hash = "sha256:491b1917afdd8638a05b611a56d46587d5a632cabead889a5440f7c638bc6ed9"}, - {file = "contourpy-1.2.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:5fd1810973a375ca0e097dee059c407913ba35723b111df75671a1976efa04bc"}, - {file = "contourpy-1.2.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:999c71939aad2780f003979b25ac5b8f2df651dac7b38fb8ce6c46ba5abe6ae9"}, - {file = "contourpy-1.2.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b7caf9b241464c404613512d5594a6e2ff0cc9cb5615c9475cc1d9b514218ae8"}, - {file = "contourpy-1.2.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:266270c6f6608340f6c9836a0fb9b367be61dde0c9a9a18d5ece97774105ff3e"}, - {file = "contourpy-1.2.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:dbd50d0a0539ae2e96e537553aff6d02c10ed165ef40c65b0e27e744a0f10af8"}, - {file = "contourpy-1.2.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:11f8d2554e52f459918f7b8e6aa20ec2a3bce35ce95c1f0ef4ba36fbda306df5"}, - {file = "contourpy-1.2.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:ce96dd400486e80ac7d195b2d800b03e3e6a787e2a522bfb83755938465a819e"}, - {file = "contourpy-1.2.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:6d3364b999c62f539cd403f8123ae426da946e142312a514162adb2addd8d808"}, - {file = "contourpy-1.2.0-cp39-cp39-win32.whl", hash = "sha256:1c88dfb9e0c77612febebb6ac69d44a8d81e3dc60f993215425b62c1161353f4"}, - {file = "contourpy-1.2.0-cp39-cp39-win_amd64.whl", hash = "sha256:78e6ad33cf2e2e80c5dfaaa0beec3d61face0fb650557100ee36db808bfa6843"}, - {file = "contourpy-1.2.0-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:be16975d94c320432657ad2402f6760990cb640c161ae6da1363051805fa8108"}, - {file = "contourpy-1.2.0-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b95a225d4948b26a28c08307a60ac00fb8671b14f2047fc5476613252a129776"}, - {file = "contourpy-1.2.0-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:0d7e03c0f9a4f90dc18d4e77e9ef4ec7b7bbb437f7f675be8e530d65ae6ef956"}, - {file = "contourpy-1.2.0.tar.gz", hash = "sha256:171f311cb758de7da13fc53af221ae47a5877be5a0843a9fe150818c51ed276a"}, + {file = "contourpy-1.2.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:bd7c23df857d488f418439686d3b10ae2fbf9bc256cd045b37a8c16575ea1040"}, + {file = "contourpy-1.2.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:5b9eb0ca724a241683c9685a484da9d35c872fd42756574a7cfbf58af26677fd"}, + {file = "contourpy-1.2.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4c75507d0a55378240f781599c30e7776674dbaf883a46d1c90f37e563453480"}, + {file = "contourpy-1.2.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:11959f0ce4a6f7b76ec578576a0b61a28bdc0696194b6347ba3f1c53827178b9"}, + {file = "contourpy-1.2.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:eb3315a8a236ee19b6df481fc5f997436e8ade24a9f03dfdc6bd490fea20c6da"}, + {file = "contourpy-1.2.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:39f3ecaf76cd98e802f094e0d4fbc6dc9c45a8d0c4d185f0f6c2234e14e5f75b"}, + {file = "contourpy-1.2.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:94b34f32646ca0414237168d68a9157cb3889f06b096612afdd296003fdd32fd"}, + {file = "contourpy-1.2.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:457499c79fa84593f22454bbd27670227874cd2ff5d6c84e60575c8b50a69619"}, + {file = "contourpy-1.2.1-cp310-cp310-win32.whl", hash = "sha256:ac58bdee53cbeba2ecad824fa8159493f0bf3b8ea4e93feb06c9a465d6c87da8"}, + {file = "contourpy-1.2.1-cp310-cp310-win_amd64.whl", hash = "sha256:9cffe0f850e89d7c0012a1fb8730f75edd4320a0a731ed0c183904fe6ecfc3a9"}, + {file = "contourpy-1.2.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:6022cecf8f44e36af10bd9118ca71f371078b4c168b6e0fab43d4a889985dbb5"}, + {file = "contourpy-1.2.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:ef5adb9a3b1d0c645ff694f9bca7702ec2c70f4d734f9922ea34de02294fdf72"}, + {file = "contourpy-1.2.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6150ffa5c767bc6332df27157d95442c379b7dce3a38dff89c0f39b63275696f"}, + {file = "contourpy-1.2.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4c863140fafc615c14a4bf4efd0f4425c02230eb8ef02784c9a156461e62c965"}, + {file = "contourpy-1.2.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:00e5388f71c1a0610e6fe56b5c44ab7ba14165cdd6d695429c5cd94021e390b2"}, + {file = "contourpy-1.2.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d4492d82b3bc7fbb7e3610747b159869468079fe149ec5c4d771fa1f614a14df"}, + {file = "contourpy-1.2.1-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:49e70d111fee47284d9dd867c9bb9a7058a3c617274900780c43e38d90fe1205"}, + {file = "contourpy-1.2.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:b59c0ffceff8d4d3996a45f2bb6f4c207f94684a96bf3d9728dbb77428dd8cb8"}, + {file = "contourpy-1.2.1-cp311-cp311-win32.whl", hash = "sha256:7b4182299f251060996af5249c286bae9361fa8c6a9cda5efc29fe8bfd6062ec"}, + {file = "contourpy-1.2.1-cp311-cp311-win_amd64.whl", hash = "sha256:2855c8b0b55958265e8b5888d6a615ba02883b225f2227461aa9127c578a4922"}, + {file = "contourpy-1.2.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:62828cada4a2b850dbef89c81f5a33741898b305db244904de418cc957ff05dc"}, + {file = "contourpy-1.2.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:309be79c0a354afff9ff7da4aaed7c3257e77edf6c1b448a779329431ee79d7e"}, + {file = "contourpy-1.2.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2e785e0f2ef0d567099b9ff92cbfb958d71c2d5b9259981cd9bee81bd194c9a4"}, + {file = "contourpy-1.2.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1cac0a8f71a041aa587410424ad46dfa6a11f6149ceb219ce7dd48f6b02b87a7"}, + {file = "contourpy-1.2.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:af3f4485884750dddd9c25cb7e3915d83c2db92488b38ccb77dd594eac84c4a0"}, + {file = "contourpy-1.2.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9ce6889abac9a42afd07a562c2d6d4b2b7134f83f18571d859b25624a331c90b"}, + {file = "contourpy-1.2.1-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:a1eea9aecf761c661d096d39ed9026574de8adb2ae1c5bd7b33558af884fb2ce"}, + {file = "contourpy-1.2.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:187fa1d4c6acc06adb0fae5544c59898ad781409e61a926ac7e84b8f276dcef4"}, + {file = "contourpy-1.2.1-cp312-cp312-win32.whl", hash = "sha256:c2528d60e398c7c4c799d56f907664673a807635b857df18f7ae64d3e6ce2d9f"}, + {file = "contourpy-1.2.1-cp312-cp312-win_amd64.whl", hash = "sha256:1a07fc092a4088ee952ddae19a2b2a85757b923217b7eed584fdf25f53a6e7ce"}, + {file = "contourpy-1.2.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:bb6834cbd983b19f06908b45bfc2dad6ac9479ae04abe923a275b5f48f1a186b"}, + {file = "contourpy-1.2.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:1d59e739ab0e3520e62a26c60707cc3ab0365d2f8fecea74bfe4de72dc56388f"}, + {file = "contourpy-1.2.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bd3db01f59fdcbce5b22afad19e390260d6d0222f35a1023d9adc5690a889364"}, + {file = "contourpy-1.2.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a12a813949e5066148712a0626895c26b2578874e4cc63160bb007e6df3436fe"}, + {file = "contourpy-1.2.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:fe0ccca550bb8e5abc22f530ec0466136379c01321fd94f30a22231e8a48d985"}, + {file = "contourpy-1.2.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e1d59258c3c67c865435d8fbeb35f8c59b8bef3d6f46c1f29f6123556af28445"}, + {file = "contourpy-1.2.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:f32c38afb74bd98ce26de7cc74a67b40afb7b05aae7b42924ea990d51e4dac02"}, + {file = "contourpy-1.2.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:d31a63bc6e6d87f77d71e1abbd7387ab817a66733734883d1fc0021ed9bfa083"}, + {file = "contourpy-1.2.1-cp39-cp39-win32.whl", hash = "sha256:ddcb8581510311e13421b1f544403c16e901c4e8f09083c881fab2be80ee31ba"}, + {file = "contourpy-1.2.1-cp39-cp39-win_amd64.whl", hash = "sha256:10a37ae557aabf2509c79715cd20b62e4c7c28b8cd62dd7d99e5ed3ce28c3fd9"}, + {file = "contourpy-1.2.1-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:a31f94983fecbac95e58388210427d68cd30fe8a36927980fab9c20062645609"}, + {file = "contourpy-1.2.1-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ef2b055471c0eb466033760a521efb9d8a32b99ab907fc8358481a1dd29e3bd3"}, + {file = "contourpy-1.2.1-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:b33d2bc4f69caedcd0a275329eb2198f560b325605810895627be5d4b876bf7f"}, + {file = "contourpy-1.2.1.tar.gz", hash = "sha256:4d8908b3bee1c889e547867ca4cdc54e5ab6be6d3e078556814a22457f49423c"}, ] [package.dependencies] -numpy = ">=1.20,<2.0" +numpy = ">=1.20" [package.extras] bokeh = ["bokeh", "selenium"] docs = ["furo", "sphinx (>=7.2)", "sphinx-copybutton"] -mypy = ["contourpy[bokeh,docs]", "docutils-stubs", "mypy (==1.6.1)", "types-Pillow"] +mypy = ["contourpy[bokeh,docs]", "docutils-stubs", "mypy (==1.8.0)", "types-Pillow"] test = ["Pillow", "contourpy[test-no-images]", "matplotlib"] test-no-images = ["pytest", "pytest-cov", "pytest-xdist", "wurlitzer"] @@ -529,7 +529,7 @@ publishing = ["build", "twine", "wheel"] type = "git" url = "https://github.com/EIT-ALIVE/eitprocessing" reference = "HEAD" -resolved_reference = "75e69f1ea19b38a1db5cc6c3f96ba7392ebe3bc3" +resolved_reference = "4955d8a6db2ed55120f9afd670016c7e4b246fe4" [[package]] name = "exceptiongroup" @@ -547,13 +547,13 @@ test = ["pytest (>=6)"] [[package]] name = "flask" -version = "3.0.2" +version = "3.0.3" description = "A simple framework for building complex web applications." optional = false python-versions = ">=3.8" files = [ - {file = "flask-3.0.2-py3-none-any.whl", hash = "sha256:3232e0e9c850d781933cf0207523d1ece087eb8d87b23777ae38456e2fbe7c6e"}, - {file = "flask-3.0.2.tar.gz", hash = "sha256:822c03f4b799204250a7ee84b1eddc40665395333973dfb9deebfe425fefcb7d"}, + {file = "flask-3.0.3-py3-none-any.whl", hash = "sha256:34e815dfaa43340d1d15a5c3a02b8476004037eb4840b34910c6e21679d288f3"}, + {file = "flask-3.0.3.tar.gz", hash = "sha256:ceb27b0af3823ea2737928a4d99d125a06175b8512c445cbd9a9ce200ef76842"}, ] [package.dependencies] @@ -569,53 +569,53 @@ dotenv = ["python-dotenv"] [[package]] name = "fonttools" -version = "4.50.0" +version = "4.51.0" description = "Tools to manipulate font files" optional = false python-versions = ">=3.8" files = [ - {file = "fonttools-4.50.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:effd303fb422f8ce06543a36ca69148471144c534cc25f30e5be752bc4f46736"}, - {file = "fonttools-4.50.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:7913992ab836f621d06aabac118fc258b9947a775a607e1a737eb3a91c360335"}, - {file = "fonttools-4.50.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8e0a1c5bd2f63da4043b63888534b52c5a1fd7ae187c8ffc64cbb7ae475b9dab"}, - {file = "fonttools-4.50.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d40fc98540fa5360e7ecf2c56ddf3c6e7dd04929543618fd7b5cc76e66390562"}, - {file = "fonttools-4.50.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:9fff65fbb7afe137bac3113827855e0204482727bddd00a806034ab0d3951d0d"}, - {file = "fonttools-4.50.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:b1aeae3dd2ee719074a9372c89ad94f7c581903306d76befdaca2a559f802472"}, - {file = "fonttools-4.50.0-cp310-cp310-win32.whl", hash = "sha256:e9623afa319405da33b43c85cceb0585a6f5d3a1d7c604daf4f7e1dd55c03d1f"}, - {file = "fonttools-4.50.0-cp310-cp310-win_amd64.whl", hash = "sha256:778c5f43e7e654ef7fe0605e80894930bc3a7772e2f496238e57218610140f54"}, - {file = "fonttools-4.50.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:3dfb102e7f63b78c832e4539969167ffcc0375b013080e6472350965a5fe8048"}, - {file = "fonttools-4.50.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:9e58fe34cb379ba3d01d5d319d67dd3ce7ca9a47ad044ea2b22635cd2d1247fc"}, - {file = "fonttools-4.50.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2c673ab40d15a442a4e6eb09bf007c1dda47c84ac1e2eecbdf359adacb799c24"}, - {file = "fonttools-4.50.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9b3ac35cdcd1a4c90c23a5200212c1bb74fa05833cc7c14291d7043a52ca2aaa"}, - {file = "fonttools-4.50.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:8844e7a2c5f7ecf977e82eb6b3014f025c8b454e046d941ece05b768be5847ae"}, - {file = "fonttools-4.50.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:f849bd3c5c2249b49c98eca5aaebb920d2bfd92b3c69e84ca9bddf133e9f83f0"}, - {file = "fonttools-4.50.0-cp311-cp311-win32.whl", hash = "sha256:39293ff231b36b035575e81c14626dfc14407a20de5262f9596c2cbb199c3625"}, - {file = "fonttools-4.50.0-cp311-cp311-win_amd64.whl", hash = "sha256:c33d5023523b44d3481624f840c8646656a1def7630ca562f222eb3ead16c438"}, - {file = "fonttools-4.50.0-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:b4a886a6dbe60100ba1cd24de962f8cd18139bd32808da80de1fa9f9f27bf1dc"}, - {file = "fonttools-4.50.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:b2ca1837bfbe5eafa11313dbc7edada79052709a1fffa10cea691210af4aa1fa"}, - {file = "fonttools-4.50.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a0493dd97ac8977e48ffc1476b932b37c847cbb87fd68673dee5182004906828"}, - {file = "fonttools-4.50.0-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:77844e2f1b0889120b6c222fc49b2b75c3d88b930615e98893b899b9352a27ea"}, - {file = "fonttools-4.50.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:3566bfb8c55ed9100afe1ba6f0f12265cd63a1387b9661eb6031a1578a28bad1"}, - {file = "fonttools-4.50.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:35e10ddbc129cf61775d58a14f2d44121178d89874d32cae1eac722e687d9019"}, - {file = "fonttools-4.50.0-cp312-cp312-win32.whl", hash = "sha256:cc8140baf9fa8f9b903f2b393a6c413a220fa990264b215bf48484f3d0bf8710"}, - {file = "fonttools-4.50.0-cp312-cp312-win_amd64.whl", hash = "sha256:0ccc85fd96373ab73c59833b824d7a73846670a0cb1f3afbaee2b2c426a8f931"}, - {file = "fonttools-4.50.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:e270a406219af37581d96c810172001ec536e29e5593aa40d4c01cca3e145aa6"}, - {file = "fonttools-4.50.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:ac2463de667233372e9e1c7e9de3d914b708437ef52a3199fdbf5a60184f190c"}, - {file = "fonttools-4.50.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:47abd6669195abe87c22750dbcd366dc3a0648f1b7c93c2baa97429c4dc1506e"}, - {file = "fonttools-4.50.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:074841375e2e3d559aecc86e1224caf78e8b8417bb391e7d2506412538f21adc"}, - {file = "fonttools-4.50.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:0743fd2191ad7ab43d78cd747215b12033ddee24fa1e088605a3efe80d6984de"}, - {file = "fonttools-4.50.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:3d7080cce7be5ed65bee3496f09f79a82865a514863197ff4d4d177389e981b0"}, - {file = "fonttools-4.50.0-cp38-cp38-win32.whl", hash = "sha256:a467ba4e2eadc1d5cc1a11d355abb945f680473fbe30d15617e104c81f483045"}, - {file = "fonttools-4.50.0-cp38-cp38-win_amd64.whl", hash = "sha256:f77e048f805e00870659d6318fd89ef28ca4ee16a22b4c5e1905b735495fc422"}, - {file = "fonttools-4.50.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:b6245eafd553c4e9a0708e93be51392bd2288c773523892fbd616d33fd2fda59"}, - {file = "fonttools-4.50.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:a4062cc7e8de26f1603323ef3ae2171c9d29c8a9f5e067d555a2813cd5c7a7e0"}, - {file = "fonttools-4.50.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:34692850dfd64ba06af61e5791a441f664cb7d21e7b544e8f385718430e8f8e4"}, - {file = "fonttools-4.50.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:678dd95f26a67e02c50dcb5bf250f95231d455642afbc65a3b0bcdacd4e4dd38"}, - {file = "fonttools-4.50.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:4f2ce7b0b295fe64ac0a85aef46a0f2614995774bd7bc643b85679c0283287f9"}, - {file = "fonttools-4.50.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:d346f4dc2221bfb7ab652d1e37d327578434ce559baf7113b0f55768437fe6a0"}, - {file = "fonttools-4.50.0-cp39-cp39-win32.whl", hash = "sha256:a51eeaf52ba3afd70bf489be20e52fdfafe6c03d652b02477c6ce23c995222f4"}, - {file = "fonttools-4.50.0-cp39-cp39-win_amd64.whl", hash = "sha256:8639be40d583e5d9da67795aa3eeeda0488fb577a1d42ae11a5036f18fb16d93"}, - {file = "fonttools-4.50.0-py3-none-any.whl", hash = "sha256:48fa36da06247aa8282766cfd63efff1bb24e55f020f29a335939ed3844d20d3"}, - {file = "fonttools-4.50.0.tar.gz", hash = "sha256:fa5cf61058c7dbb104c2ac4e782bf1b2016a8cf2f69de6e4dd6a865d2c969bb5"}, + {file = "fonttools-4.51.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:84d7751f4468dd8cdd03ddada18b8b0857a5beec80bce9f435742abc9a851a74"}, + {file = "fonttools-4.51.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:8b4850fa2ef2cfbc1d1f689bc159ef0f45d8d83298c1425838095bf53ef46308"}, + {file = "fonttools-4.51.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b5b48a1121117047d82695d276c2af2ee3a24ffe0f502ed581acc2673ecf1037"}, + {file = "fonttools-4.51.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:180194c7fe60c989bb627d7ed5011f2bef1c4d36ecf3ec64daec8302f1ae0716"}, + {file = "fonttools-4.51.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:96a48e137c36be55e68845fc4284533bda2980f8d6f835e26bca79d7e2006438"}, + {file = "fonttools-4.51.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:806e7912c32a657fa39d2d6eb1d3012d35f841387c8fc6cf349ed70b7c340039"}, + {file = "fonttools-4.51.0-cp310-cp310-win32.whl", hash = "sha256:32b17504696f605e9e960647c5f64b35704782a502cc26a37b800b4d69ff3c77"}, + {file = "fonttools-4.51.0-cp310-cp310-win_amd64.whl", hash = "sha256:c7e91abdfae1b5c9e3a543f48ce96013f9a08c6c9668f1e6be0beabf0a569c1b"}, + {file = "fonttools-4.51.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:a8feca65bab31479d795b0d16c9a9852902e3a3c0630678efb0b2b7941ea9c74"}, + {file = "fonttools-4.51.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:8ac27f436e8af7779f0bb4d5425aa3535270494d3bc5459ed27de3f03151e4c2"}, + {file = "fonttools-4.51.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0e19bd9e9964a09cd2433a4b100ca7f34e34731e0758e13ba9a1ed6e5468cc0f"}, + {file = "fonttools-4.51.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b2b92381f37b39ba2fc98c3a45a9d6383bfc9916a87d66ccb6553f7bdd129097"}, + {file = "fonttools-4.51.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:5f6bc991d1610f5c3bbe997b0233cbc234b8e82fa99fc0b2932dc1ca5e5afec0"}, + {file = "fonttools-4.51.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:9696fe9f3f0c32e9a321d5268208a7cc9205a52f99b89479d1b035ed54c923f1"}, + {file = "fonttools-4.51.0-cp311-cp311-win32.whl", hash = "sha256:3bee3f3bd9fa1d5ee616ccfd13b27ca605c2b4270e45715bd2883e9504735034"}, + {file = "fonttools-4.51.0-cp311-cp311-win_amd64.whl", hash = "sha256:0f08c901d3866a8905363619e3741c33f0a83a680d92a9f0e575985c2634fcc1"}, + {file = "fonttools-4.51.0-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:4060acc2bfa2d8e98117828a238889f13b6f69d59f4f2d5857eece5277b829ba"}, + {file = "fonttools-4.51.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:1250e818b5f8a679ad79660855528120a8f0288f8f30ec88b83db51515411fcc"}, + {file = "fonttools-4.51.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:76f1777d8b3386479ffb4a282e74318e730014d86ce60f016908d9801af9ca2a"}, + {file = "fonttools-4.51.0-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8b5ad456813d93b9c4b7ee55302208db2b45324315129d85275c01f5cb7e61a2"}, + {file = "fonttools-4.51.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:68b3fb7775a923be73e739f92f7e8a72725fd333eab24834041365d2278c3671"}, + {file = "fonttools-4.51.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:8e2f1a4499e3b5ee82c19b5ee57f0294673125c65b0a1ff3764ea1f9db2f9ef5"}, + {file = "fonttools-4.51.0-cp312-cp312-win32.whl", hash = "sha256:278e50f6b003c6aed19bae2242b364e575bcb16304b53f2b64f6551b9c000e15"}, + {file = "fonttools-4.51.0-cp312-cp312-win_amd64.whl", hash = "sha256:b3c61423f22165541b9403ee39874dcae84cd57a9078b82e1dce8cb06b07fa2e"}, + {file = "fonttools-4.51.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:1621ee57da887c17312acc4b0e7ac30d3a4fb0fec6174b2e3754a74c26bbed1e"}, + {file = "fonttools-4.51.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:e9d9298be7a05bb4801f558522adbe2feea1b0b103d5294ebf24a92dd49b78e5"}, + {file = "fonttools-4.51.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ee1af4be1c5afe4c96ca23badd368d8dc75f611887fb0c0dac9f71ee5d6f110e"}, + {file = "fonttools-4.51.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c18b49adc721a7d0b8dfe7c3130c89b8704baf599fb396396d07d4aa69b824a1"}, + {file = "fonttools-4.51.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:de7c29bdbdd35811f14493ffd2534b88f0ce1b9065316433b22d63ca1cd21f14"}, + {file = "fonttools-4.51.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:cadf4e12a608ef1d13e039864f484c8a968840afa0258b0b843a0556497ea9ed"}, + {file = "fonttools-4.51.0-cp38-cp38-win32.whl", hash = "sha256:aefa011207ed36cd280babfaa8510b8176f1a77261833e895a9d96e57e44802f"}, + {file = "fonttools-4.51.0-cp38-cp38-win_amd64.whl", hash = "sha256:865a58b6e60b0938874af0968cd0553bcd88e0b2cb6e588727117bd099eef836"}, + {file = "fonttools-4.51.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:60a3409c9112aec02d5fb546f557bca6efa773dcb32ac147c6baf5f742e6258b"}, + {file = "fonttools-4.51.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:f7e89853d8bea103c8e3514b9f9dc86b5b4120afb4583b57eb10dfa5afbe0936"}, + {file = "fonttools-4.51.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:56fc244f2585d6c00b9bcc59e6593e646cf095a96fe68d62cd4da53dd1287b55"}, + {file = "fonttools-4.51.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0d145976194a5242fdd22df18a1b451481a88071feadf251221af110ca8f00ce"}, + {file = "fonttools-4.51.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:c5b8cab0c137ca229433570151b5c1fc6af212680b58b15abd797dcdd9dd5051"}, + {file = "fonttools-4.51.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:54dcf21a2f2d06ded676e3c3f9f74b2bafded3a8ff12f0983160b13e9f2fb4a7"}, + {file = "fonttools-4.51.0-cp39-cp39-win32.whl", hash = "sha256:0118ef998a0699a96c7b28457f15546815015a2710a1b23a7bf6c1be60c01636"}, + {file = "fonttools-4.51.0-cp39-cp39-win_amd64.whl", hash = "sha256:599bdb75e220241cedc6faebfafedd7670335d2e29620d207dd0378a4e9ccc5a"}, + {file = "fonttools-4.51.0-py3-none-any.whl", hash = "sha256:15c94eeef6b095831067f72c825eb0e2d48bb4cea0647c1b05c981ecba2bf39f"}, + {file = "fonttools-4.51.0.tar.gz", hash = "sha256:dc0673361331566d7a663d7ce0f6fdcbfbdc1f59c6e3ed1165ad7202ca183c68"}, ] [package.extras] @@ -645,13 +645,13 @@ files = [ [[package]] name = "idna" -version = "3.6" +version = "3.7" description = "Internationalized Domain Names in Applications (IDNA)" optional = false python-versions = ">=3.5" files = [ - {file = "idna-3.6-py3-none-any.whl", hash = "sha256:c05567e9c24a6b9faaa835c4821bad0590fbb9d5779e7caa6e1cc4978e7eb24f"}, - {file = "idna-3.6.tar.gz", hash = "sha256:9ecdbbd083b06798ae1e86adcbfe8ab1479cf864e4ee30fe4e46a003d12491ca"}, + {file = "idna-3.7-py3-none-any.whl", hash = "sha256:82fee1fc78add43492d3a1898bfa6d8a904cc97d8427f683ed8e798d07761aa0"}, + {file = "idna-3.7.tar.gz", hash = "sha256:028ff3aadf0609c1fd278d8ea3089299412a7a8b9bd005dd08b9f8285bcb5cfc"}, ] [[package]] @@ -827,96 +827,174 @@ files = [ [[package]] name = "lxml" -version = "5.1.0" +version = "5.2.1" description = "Powerful and Pythonic XML processing library combining libxml2/libxslt with the ElementTree API." optional = false python-versions = ">=3.6" files = [ - {file = "lxml-5.1.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:704f5572ff473a5f897745abebc6df40f22d4133c1e0a1f124e4f2bd3330ff7e"}, - {file = "lxml-5.1.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:9d3c0f8567ffe7502d969c2c1b809892dc793b5d0665f602aad19895f8d508da"}, - {file = "lxml-5.1.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:5fcfbebdb0c5d8d18b84118842f31965d59ee3e66996ac842e21f957eb76138c"}, - {file = "lxml-5.1.0-cp310-cp310-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2f37c6d7106a9d6f0708d4e164b707037b7380fcd0b04c5bd9cae1fb46a856fb"}, - {file = "lxml-5.1.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2befa20a13f1a75c751f47e00929fb3433d67eb9923c2c0b364de449121f447c"}, - {file = "lxml-5.1.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:22b7ee4c35f374e2c20337a95502057964d7e35b996b1c667b5c65c567d2252a"}, - {file = "lxml-5.1.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:bf8443781533b8d37b295016a4b53c1494fa9a03573c09ca5104550c138d5c05"}, - {file = "lxml-5.1.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:82bddf0e72cb2af3cbba7cec1d2fd11fda0de6be8f4492223d4a268713ef2147"}, - {file = "lxml-5.1.0-cp310-cp310-win32.whl", hash = "sha256:b66aa6357b265670bb574f050ffceefb98549c721cf28351b748be1ef9577d93"}, - {file = "lxml-5.1.0-cp310-cp310-win_amd64.whl", hash = "sha256:4946e7f59b7b6a9e27bef34422f645e9a368cb2be11bf1ef3cafc39a1f6ba68d"}, - {file = "lxml-5.1.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:14deca1460b4b0f6b01f1ddc9557704e8b365f55c63070463f6c18619ebf964f"}, - {file = "lxml-5.1.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:ed8c3d2cd329bf779b7ed38db176738f3f8be637bb395ce9629fc76f78afe3d4"}, - {file = "lxml-5.1.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:436a943c2900bb98123b06437cdd30580a61340fbdb7b28aaf345a459c19046a"}, - {file = "lxml-5.1.0-cp311-cp311-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:acb6b2f96f60f70e7f34efe0c3ea34ca63f19ca63ce90019c6cbca6b676e81fa"}, - {file = "lxml-5.1.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:af8920ce4a55ff41167ddbc20077f5698c2e710ad3353d32a07d3264f3a2021e"}, - {file = "lxml-5.1.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7cfced4a069003d8913408e10ca8ed092c49a7f6cefee9bb74b6b3e860683b45"}, - {file = "lxml-5.1.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:9e5ac3437746189a9b4121db2a7b86056ac8786b12e88838696899328fc44bb2"}, - {file = "lxml-5.1.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:f4c9bda132ad108b387c33fabfea47866af87f4ea6ffb79418004f0521e63204"}, - {file = "lxml-5.1.0-cp311-cp311-win32.whl", hash = "sha256:bc64d1b1dab08f679fb89c368f4c05693f58a9faf744c4d390d7ed1d8223869b"}, - {file = "lxml-5.1.0-cp311-cp311-win_amd64.whl", hash = "sha256:a5ab722ae5a873d8dcee1f5f45ddd93c34210aed44ff2dc643b5025981908cda"}, - {file = "lxml-5.1.0-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:9aa543980ab1fbf1720969af1d99095a548ea42e00361e727c58a40832439114"}, - {file = "lxml-5.1.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:6f11b77ec0979f7e4dc5ae081325a2946f1fe424148d3945f943ceaede98adb8"}, - {file = "lxml-5.1.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:a36c506e5f8aeb40680491d39ed94670487ce6614b9d27cabe45d94cd5d63e1e"}, - {file = "lxml-5.1.0-cp312-cp312-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f643ffd2669ffd4b5a3e9b41c909b72b2a1d5e4915da90a77e119b8d48ce867a"}, - {file = "lxml-5.1.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:16dd953fb719f0ffc5bc067428fc9e88f599e15723a85618c45847c96f11f431"}, - {file = "lxml-5.1.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:16018f7099245157564d7148165132c70adb272fb5a17c048ba70d9cc542a1a1"}, - {file = "lxml-5.1.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:82cd34f1081ae4ea2ede3d52f71b7be313756e99b4b5f829f89b12da552d3aa3"}, - {file = "lxml-5.1.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:19a1bc898ae9f06bccb7c3e1dfd73897ecbbd2c96afe9095a6026016e5ca97b8"}, - {file = "lxml-5.1.0-cp312-cp312-win32.whl", hash = "sha256:13521a321a25c641b9ea127ef478b580b5ec82aa2e9fc076c86169d161798b01"}, - {file = "lxml-5.1.0-cp312-cp312-win_amd64.whl", hash = "sha256:1ad17c20e3666c035db502c78b86e58ff6b5991906e55bdbef94977700c72623"}, - {file = "lxml-5.1.0-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:24ef5a4631c0b6cceaf2dbca21687e29725b7c4e171f33a8f8ce23c12558ded1"}, - {file = "lxml-5.1.0-cp36-cp36m-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8d2900b7f5318bc7ad8631d3d40190b95ef2aa8cc59473b73b294e4a55e9f30f"}, - {file = "lxml-5.1.0-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:601f4a75797d7a770daed8b42b97cd1bb1ba18bd51a9382077a6a247a12aa38d"}, - {file = "lxml-5.1.0-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b4b68c961b5cc402cbd99cca5eb2547e46ce77260eb705f4d117fd9c3f932b95"}, - {file = "lxml-5.1.0-cp36-cp36m-musllinux_1_1_aarch64.whl", hash = "sha256:afd825e30f8d1f521713a5669b63657bcfe5980a916c95855060048b88e1adb7"}, - {file = "lxml-5.1.0-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:262bc5f512a66b527d026518507e78c2f9c2bd9eb5c8aeeb9f0eb43fcb69dc67"}, - {file = "lxml-5.1.0-cp36-cp36m-win32.whl", hash = "sha256:e856c1c7255c739434489ec9c8aa9cdf5179785d10ff20add308b5d673bed5cd"}, - {file = "lxml-5.1.0-cp36-cp36m-win_amd64.whl", hash = "sha256:c7257171bb8d4432fe9d6fdde4d55fdbe663a63636a17f7f9aaba9bcb3153ad7"}, - {file = "lxml-5.1.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:b9e240ae0ba96477682aa87899d94ddec1cc7926f9df29b1dd57b39e797d5ab5"}, - {file = "lxml-5.1.0-cp37-cp37m-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a96f02ba1bcd330807fc060ed91d1f7a20853da6dd449e5da4b09bfcc08fdcf5"}, - {file = "lxml-5.1.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3e3898ae2b58eeafedfe99e542a17859017d72d7f6a63de0f04f99c2cb125936"}, - {file = "lxml-5.1.0-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:61c5a7edbd7c695e54fca029ceb351fc45cd8860119a0f83e48be44e1c464862"}, - {file = "lxml-5.1.0-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:3aeca824b38ca78d9ee2ab82bd9883083d0492d9d17df065ba3b94e88e4d7ee6"}, - {file = "lxml-5.1.0-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:8f52fe6859b9db71ee609b0c0a70fea5f1e71c3462ecf144ca800d3f434f0764"}, - {file = "lxml-5.1.0-cp37-cp37m-win32.whl", hash = "sha256:d42e3a3fc18acc88b838efded0e6ec3edf3e328a58c68fbd36a7263a874906c8"}, - {file = "lxml-5.1.0-cp37-cp37m-win_amd64.whl", hash = "sha256:eac68f96539b32fce2c9b47eb7c25bb2582bdaf1bbb360d25f564ee9e04c542b"}, - {file = "lxml-5.1.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:ae15347a88cf8af0949a9872b57a320d2605ae069bcdf047677318bc0bba45b1"}, - {file = "lxml-5.1.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:c26aab6ea9c54d3bed716b8851c8bfc40cb249b8e9880e250d1eddde9f709bf5"}, - {file = "lxml-5.1.0-cp38-cp38-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:342e95bddec3a698ac24378d61996b3ee5ba9acfeb253986002ac53c9a5f6f84"}, - {file = "lxml-5.1.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:725e171e0b99a66ec8605ac77fa12239dbe061482ac854d25720e2294652eeaa"}, - {file = "lxml-5.1.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3d184e0d5c918cff04cdde9dbdf9600e960161d773666958c9d7b565ccc60c45"}, - {file = "lxml-5.1.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:98f3f020a2b736566c707c8e034945c02aa94e124c24f77ca097c446f81b01f1"}, - {file = "lxml-5.1.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:6d48fc57e7c1e3df57be5ae8614bab6d4e7b60f65c5457915c26892c41afc59e"}, - {file = "lxml-5.1.0-cp38-cp38-win32.whl", hash = "sha256:7ec465e6549ed97e9f1e5ed51c657c9ede767bc1c11552f7f4d022c4df4a977a"}, - {file = "lxml-5.1.0-cp38-cp38-win_amd64.whl", hash = "sha256:b21b4031b53d25b0858d4e124f2f9131ffc1530431c6d1321805c90da78388d1"}, - {file = "lxml-5.1.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:52427a7eadc98f9e62cb1368a5079ae826f94f05755d2d567d93ee1bc3ceb354"}, - {file = "lxml-5.1.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:6a2a2c724d97c1eb8cf966b16ca2915566a4904b9aad2ed9a09c748ffe14f969"}, - {file = "lxml-5.1.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:843b9c835580d52828d8f69ea4302537337a21e6b4f1ec711a52241ba4a824f3"}, - {file = "lxml-5.1.0-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9b99f564659cfa704a2dd82d0684207b1aadf7d02d33e54845f9fc78e06b7581"}, - {file = "lxml-5.1.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4f8b0c78e7aac24979ef09b7f50da871c2de2def043d468c4b41f512d831e912"}, - {file = "lxml-5.1.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9bcf86dfc8ff3e992fed847c077bd875d9e0ba2fa25d859c3a0f0f76f07f0c8d"}, - {file = "lxml-5.1.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:49a9b4af45e8b925e1cd6f3b15bbba2c81e7dba6dce170c677c9cda547411e14"}, - {file = "lxml-5.1.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:280f3edf15c2a967d923bcfb1f8f15337ad36f93525828b40a0f9d6c2ad24890"}, - {file = "lxml-5.1.0-cp39-cp39-win32.whl", hash = "sha256:ed7326563024b6e91fef6b6c7a1a2ff0a71b97793ac33dbbcf38f6005e51ff6e"}, - {file = "lxml-5.1.0-cp39-cp39-win_amd64.whl", hash = "sha256:8d7b4beebb178e9183138f552238f7e6613162a42164233e2bda00cb3afac58f"}, - {file = "lxml-5.1.0-pp310-pypy310_pp73-macosx_10_9_x86_64.whl", hash = "sha256:9bd0ae7cc2b85320abd5e0abad5ccee5564ed5f0cc90245d2f9a8ef330a8deae"}, - {file = "lxml-5.1.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d8c1d679df4361408b628f42b26a5d62bd3e9ba7f0c0e7969f925021554755aa"}, - {file = "lxml-5.1.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:2ad3a8ce9e8a767131061a22cd28fdffa3cd2dc193f399ff7b81777f3520e372"}, - {file = "lxml-5.1.0-pp37-pypy37_pp73-macosx_10_9_x86_64.whl", hash = "sha256:304128394c9c22b6569eba2a6d98392b56fbdfbad58f83ea702530be80d0f9df"}, - {file = "lxml-5.1.0-pp37-pypy37_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d74fcaf87132ffc0447b3c685a9f862ffb5b43e70ea6beec2fb8057d5d2a1fea"}, - {file = "lxml-5.1.0-pp37-pypy37_pp73-win_amd64.whl", hash = "sha256:8cf5877f7ed384dabfdcc37922c3191bf27e55b498fecece9fd5c2c7aaa34c33"}, - {file = "lxml-5.1.0-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:877efb968c3d7eb2dad540b6cabf2f1d3c0fbf4b2d309a3c141f79c7e0061324"}, - {file = "lxml-5.1.0-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3f14a4fb1c1c402a22e6a341a24c1341b4a3def81b41cd354386dcb795f83897"}, - {file = "lxml-5.1.0-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:25663d6e99659544ee8fe1b89b1a8c0aaa5e34b103fab124b17fa958c4a324a6"}, - {file = "lxml-5.1.0-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:8b9f19df998761babaa7f09e6bc169294eefafd6149aaa272081cbddc7ba4ca3"}, - {file = "lxml-5.1.0-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5e53d7e6a98b64fe54775d23a7c669763451340c3d44ad5e3a3b48a1efbdc96f"}, - {file = "lxml-5.1.0-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:c3cd1fc1dc7c376c54440aeaaa0dcc803d2126732ff5c6b68ccd619f2e64be4f"}, - {file = "lxml-5.1.0.tar.gz", hash = "sha256:3eea6ed6e6c918e468e693c41ef07f3c3acc310b70ddd9cc72d9ef84bc9564ca"}, + {file = "lxml-5.2.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:1f7785f4f789fdb522729ae465adcaa099e2a3441519df750ebdccc481d961a1"}, + {file = "lxml-5.2.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:6cc6ee342fb7fa2471bd9b6d6fdfc78925a697bf5c2bcd0a302e98b0d35bfad3"}, + {file = "lxml-5.2.1-cp310-cp310-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:794f04eec78f1d0e35d9e0c36cbbb22e42d370dda1609fb03bcd7aeb458c6377"}, + {file = "lxml-5.2.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c817d420c60a5183953c783b0547d9eb43b7b344a2c46f69513d5952a78cddf3"}, + {file = "lxml-5.2.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:2213afee476546a7f37c7a9b4ad4d74b1e112a6fafffc9185d6d21f043128c81"}, + {file = "lxml-5.2.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b070bbe8d3f0f6147689bed981d19bbb33070225373338df755a46893528104a"}, + {file = "lxml-5.2.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e02c5175f63effbd7c5e590399c118d5db6183bbfe8e0d118bdb5c2d1b48d937"}, + {file = "lxml-5.2.1-cp310-cp310-manylinux_2_28_aarch64.whl", hash = "sha256:3dc773b2861b37b41a6136e0b72a1a44689a9c4c101e0cddb6b854016acc0aa8"}, + {file = "lxml-5.2.1-cp310-cp310-manylinux_2_28_ppc64le.whl", hash = "sha256:d7520db34088c96cc0e0a3ad51a4fd5b401f279ee112aa2b7f8f976d8582606d"}, + {file = "lxml-5.2.1-cp310-cp310-manylinux_2_28_s390x.whl", hash = "sha256:bcbf4af004f98793a95355980764b3d80d47117678118a44a80b721c9913436a"}, + {file = "lxml-5.2.1-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:a2b44bec7adf3e9305ce6cbfa47a4395667e744097faed97abb4728748ba7d47"}, + {file = "lxml-5.2.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:1c5bb205e9212d0ebddf946bc07e73fa245c864a5f90f341d11ce7b0b854475d"}, + {file = "lxml-5.2.1-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:2c9d147f754b1b0e723e6afb7ba1566ecb162fe4ea657f53d2139bbf894d050a"}, + {file = "lxml-5.2.1-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:3545039fa4779be2df51d6395e91a810f57122290864918b172d5dc7ca5bb433"}, + {file = "lxml-5.2.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:a91481dbcddf1736c98a80b122afa0f7296eeb80b72344d7f45dc9f781551f56"}, + {file = "lxml-5.2.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:2ddfe41ddc81f29a4c44c8ce239eda5ade4e7fc305fb7311759dd6229a080052"}, + {file = "lxml-5.2.1-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:a7baf9ffc238e4bf401299f50e971a45bfcc10a785522541a6e3179c83eabf0a"}, + {file = "lxml-5.2.1-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:31e9a882013c2f6bd2f2c974241bf4ba68c85eba943648ce88936d23209a2e01"}, + {file = "lxml-5.2.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:0a15438253b34e6362b2dc41475e7f80de76320f335e70c5528b7148cac253a1"}, + {file = "lxml-5.2.1-cp310-cp310-win32.whl", hash = "sha256:6992030d43b916407c9aa52e9673612ff39a575523c5f4cf72cdef75365709a5"}, + {file = "lxml-5.2.1-cp310-cp310-win_amd64.whl", hash = "sha256:da052e7962ea2d5e5ef5bc0355d55007407087392cf465b7ad84ce5f3e25fe0f"}, + {file = "lxml-5.2.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:70ac664a48aa64e5e635ae5566f5227f2ab7f66a3990d67566d9907edcbbf867"}, + {file = "lxml-5.2.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:1ae67b4e737cddc96c99461d2f75d218bdf7a0c3d3ad5604d1f5e7464a2f9ffe"}, + {file = "lxml-5.2.1-cp311-cp311-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f18a5a84e16886898e51ab4b1d43acb3083c39b14c8caeb3589aabff0ee0b270"}, + {file = "lxml-5.2.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c6f2c8372b98208ce609c9e1d707f6918cc118fea4e2c754c9f0812c04ca116d"}, + {file = "lxml-5.2.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:394ed3924d7a01b5bd9a0d9d946136e1c2f7b3dc337196d99e61740ed4bc6fe1"}, + {file = "lxml-5.2.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5d077bc40a1fe984e1a9931e801e42959a1e6598edc8a3223b061d30fbd26bbc"}, + {file = "lxml-5.2.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:764b521b75701f60683500d8621841bec41a65eb739b8466000c6fdbc256c240"}, + {file = "lxml-5.2.1-cp311-cp311-manylinux_2_28_aarch64.whl", hash = "sha256:3a6b45da02336895da82b9d472cd274b22dc27a5cea1d4b793874eead23dd14f"}, + {file = "lxml-5.2.1-cp311-cp311-manylinux_2_28_ppc64le.whl", hash = "sha256:5ea7b6766ac2dfe4bcac8b8595107665a18ef01f8c8343f00710b85096d1b53a"}, + {file = "lxml-5.2.1-cp311-cp311-manylinux_2_28_s390x.whl", hash = "sha256:e196a4ff48310ba62e53a8e0f97ca2bca83cdd2fe2934d8b5cb0df0a841b193a"}, + {file = "lxml-5.2.1-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:200e63525948e325d6a13a76ba2911f927ad399ef64f57898cf7c74e69b71095"}, + {file = "lxml-5.2.1-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:dae0ed02f6b075426accbf6b2863c3d0a7eacc1b41fb40f2251d931e50188dad"}, + {file = "lxml-5.2.1-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:ab31a88a651039a07a3ae327d68ebdd8bc589b16938c09ef3f32a4b809dc96ef"}, + {file = "lxml-5.2.1-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:df2e6f546c4df14bc81f9498bbc007fbb87669f1bb707c6138878c46b06f6510"}, + {file = "lxml-5.2.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:5dd1537e7cc06efd81371f5d1a992bd5ab156b2b4f88834ca852de4a8ea523fa"}, + {file = "lxml-5.2.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:9b9ec9c9978b708d488bec36b9e4c94d88fd12ccac3e62134a9d17ddba910ea9"}, + {file = "lxml-5.2.1-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:8e77c69d5892cb5ba71703c4057091e31ccf534bd7f129307a4d084d90d014b8"}, + {file = "lxml-5.2.1-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:a8d5c70e04aac1eda5c829a26d1f75c6e5286c74743133d9f742cda8e53b9c2f"}, + {file = "lxml-5.2.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:c94e75445b00319c1fad60f3c98b09cd63fe1134a8a953dcd48989ef42318534"}, + {file = "lxml-5.2.1-cp311-cp311-win32.whl", hash = "sha256:4951e4f7a5680a2db62f7f4ab2f84617674d36d2d76a729b9a8be4b59b3659be"}, + {file = "lxml-5.2.1-cp311-cp311-win_amd64.whl", hash = "sha256:5c670c0406bdc845b474b680b9a5456c561c65cf366f8db5a60154088c92d102"}, + {file = "lxml-5.2.1-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:abc25c3cab9ec7fcd299b9bcb3b8d4a1231877e425c650fa1c7576c5107ab851"}, + {file = "lxml-5.2.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:6935bbf153f9a965f1e07c2649c0849d29832487c52bb4a5c5066031d8b44fd5"}, + {file = "lxml-5.2.1-cp312-cp312-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d793bebb202a6000390a5390078e945bbb49855c29c7e4d56a85901326c3b5d9"}, + {file = "lxml-5.2.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:afd5562927cdef7c4f5550374acbc117fd4ecc05b5007bdfa57cc5355864e0a4"}, + {file = "lxml-5.2.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:0e7259016bc4345a31af861fdce942b77c99049d6c2107ca07dc2bba2435c1d9"}, + {file = "lxml-5.2.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:530e7c04f72002d2f334d5257c8a51bf409db0316feee7c87e4385043be136af"}, + {file = "lxml-5.2.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:59689a75ba8d7ffca577aefd017d08d659d86ad4585ccc73e43edbfc7476781a"}, + {file = "lxml-5.2.1-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:f9737bf36262046213a28e789cc82d82c6ef19c85a0cf05e75c670a33342ac2c"}, + {file = "lxml-5.2.1-cp312-cp312-manylinux_2_28_ppc64le.whl", hash = "sha256:3a74c4f27167cb95c1d4af1c0b59e88b7f3e0182138db2501c353555f7ec57f4"}, + {file = "lxml-5.2.1-cp312-cp312-manylinux_2_28_s390x.whl", hash = "sha256:68a2610dbe138fa8c5826b3f6d98a7cfc29707b850ddcc3e21910a6fe51f6ca0"}, + {file = "lxml-5.2.1-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:f0a1bc63a465b6d72569a9bba9f2ef0334c4e03958e043da1920299100bc7c08"}, + {file = "lxml-5.2.1-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:c2d35a1d047efd68027817b32ab1586c1169e60ca02c65d428ae815b593e65d4"}, + {file = "lxml-5.2.1-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:79bd05260359170f78b181b59ce871673ed01ba048deef4bf49a36ab3e72e80b"}, + {file = "lxml-5.2.1-cp312-cp312-musllinux_1_1_s390x.whl", hash = "sha256:865bad62df277c04beed9478fe665b9ef63eb28fe026d5dedcb89b537d2e2ea6"}, + {file = "lxml-5.2.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:44f6c7caff88d988db017b9b0e4ab04934f11e3e72d478031efc7edcac6c622f"}, + {file = "lxml-5.2.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:71e97313406ccf55d32cc98a533ee05c61e15d11b99215b237346171c179c0b0"}, + {file = "lxml-5.2.1-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:057cdc6b86ab732cf361f8b4d8af87cf195a1f6dc5b0ff3de2dced242c2015e0"}, + {file = "lxml-5.2.1-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:f3bbbc998d42f8e561f347e798b85513ba4da324c2b3f9b7969e9c45b10f6169"}, + {file = "lxml-5.2.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:491755202eb21a5e350dae00c6d9a17247769c64dcf62d8c788b5c135e179dc4"}, + {file = "lxml-5.2.1-cp312-cp312-win32.whl", hash = "sha256:8de8f9d6caa7f25b204fc861718815d41cbcf27ee8f028c89c882a0cf4ae4134"}, + {file = "lxml-5.2.1-cp312-cp312-win_amd64.whl", hash = "sha256:f2a9efc53d5b714b8df2b4b3e992accf8ce5bbdfe544d74d5c6766c9e1146a3a"}, + {file = "lxml-5.2.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:70a9768e1b9d79edca17890175ba915654ee1725975d69ab64813dd785a2bd5c"}, + {file = "lxml-5.2.1-cp36-cp36m-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c38d7b9a690b090de999835f0443d8aa93ce5f2064035dfc48f27f02b4afc3d0"}, + {file = "lxml-5.2.1-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5670fb70a828663cc37552a2a85bf2ac38475572b0e9b91283dc09efb52c41d1"}, + {file = "lxml-5.2.1-cp36-cp36m-manylinux_2_28_x86_64.whl", hash = "sha256:958244ad566c3ffc385f47dddde4145088a0ab893504b54b52c041987a8c1863"}, + {file = "lxml-5.2.1-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:b6241d4eee5f89453307c2f2bfa03b50362052ca0af1efecf9fef9a41a22bb4f"}, + {file = "lxml-5.2.1-cp36-cp36m-musllinux_1_1_aarch64.whl", hash = "sha256:2a66bf12fbd4666dd023b6f51223aed3d9f3b40fef06ce404cb75bafd3d89536"}, + {file = "lxml-5.2.1-cp36-cp36m-musllinux_1_1_ppc64le.whl", hash = "sha256:9123716666e25b7b71c4e1789ec829ed18663152008b58544d95b008ed9e21e9"}, + {file = "lxml-5.2.1-cp36-cp36m-musllinux_1_1_s390x.whl", hash = "sha256:0c3f67e2aeda739d1cc0b1102c9a9129f7dc83901226cc24dd72ba275ced4218"}, + {file = "lxml-5.2.1-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:5d5792e9b3fb8d16a19f46aa8208987cfeafe082363ee2745ea8b643d9cc5b45"}, + {file = "lxml-5.2.1-cp36-cp36m-musllinux_1_2_aarch64.whl", hash = "sha256:88e22fc0a6684337d25c994381ed8a1580a6f5ebebd5ad41f89f663ff4ec2885"}, + {file = "lxml-5.2.1-cp36-cp36m-musllinux_1_2_ppc64le.whl", hash = "sha256:21c2e6b09565ba5b45ae161b438e033a86ad1736b8c838c766146eff8ceffff9"}, + {file = "lxml-5.2.1-cp36-cp36m-musllinux_1_2_s390x.whl", hash = "sha256:afbbdb120d1e78d2ba8064a68058001b871154cc57787031b645c9142b937a62"}, + {file = "lxml-5.2.1-cp36-cp36m-musllinux_1_2_x86_64.whl", hash = "sha256:627402ad8dea044dde2eccde4370560a2b750ef894c9578e1d4f8ffd54000461"}, + {file = "lxml-5.2.1-cp36-cp36m-win32.whl", hash = "sha256:e89580a581bf478d8dcb97d9cd011d567768e8bc4095f8557b21c4d4c5fea7d0"}, + {file = "lxml-5.2.1-cp36-cp36m-win_amd64.whl", hash = "sha256:59565f10607c244bc4c05c0c5fa0c190c990996e0c719d05deec7030c2aa8289"}, + {file = "lxml-5.2.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:857500f88b17a6479202ff5fe5f580fc3404922cd02ab3716197adf1ef628029"}, + {file = "lxml-5.2.1-cp37-cp37m-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:56c22432809085b3f3ae04e6e7bdd36883d7258fcd90e53ba7b2e463efc7a6af"}, + {file = "lxml-5.2.1-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a55ee573116ba208932e2d1a037cc4b10d2c1cb264ced2184d00b18ce585b2c0"}, + {file = "lxml-5.2.1-cp37-cp37m-manylinux_2_28_x86_64.whl", hash = "sha256:6cf58416653c5901e12624e4013708b6e11142956e7f35e7a83f1ab02f3fe456"}, + {file = "lxml-5.2.1-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:64c2baa7774bc22dd4474248ba16fe1a7f611c13ac6123408694d4cc93d66dbd"}, + {file = "lxml-5.2.1-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:74b28c6334cca4dd704e8004cba1955af0b778cf449142e581e404bd211fb619"}, + {file = "lxml-5.2.1-cp37-cp37m-musllinux_1_1_s390x.whl", hash = "sha256:7221d49259aa1e5a8f00d3d28b1e0b76031655ca74bb287123ef56c3db92f213"}, + {file = "lxml-5.2.1-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:3dbe858ee582cbb2c6294dc85f55b5f19c918c2597855e950f34b660f1a5ede6"}, + {file = "lxml-5.2.1-cp37-cp37m-musllinux_1_2_aarch64.whl", hash = "sha256:04ab5415bf6c86e0518d57240a96c4d1fcfc3cb370bb2ac2a732b67f579e5a04"}, + {file = "lxml-5.2.1-cp37-cp37m-musllinux_1_2_ppc64le.whl", hash = "sha256:6ab833e4735a7e5533711a6ea2df26459b96f9eec36d23f74cafe03631647c41"}, + {file = "lxml-5.2.1-cp37-cp37m-musllinux_1_2_s390x.whl", hash = "sha256:f443cdef978430887ed55112b491f670bba6462cea7a7742ff8f14b7abb98d75"}, + {file = "lxml-5.2.1-cp37-cp37m-musllinux_1_2_x86_64.whl", hash = "sha256:9e2addd2d1866fe112bc6f80117bcc6bc25191c5ed1bfbcf9f1386a884252ae8"}, + {file = "lxml-5.2.1-cp37-cp37m-win32.whl", hash = "sha256:f51969bac61441fd31f028d7b3b45962f3ecebf691a510495e5d2cd8c8092dbd"}, + {file = "lxml-5.2.1-cp37-cp37m-win_amd64.whl", hash = "sha256:b0b58fbfa1bf7367dde8a557994e3b1637294be6cf2169810375caf8571a085c"}, + {file = "lxml-5.2.1-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:3e183c6e3298a2ed5af9d7a356ea823bccaab4ec2349dc9ed83999fd289d14d5"}, + {file = "lxml-5.2.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:804f74efe22b6a227306dd890eecc4f8c59ff25ca35f1f14e7482bbce96ef10b"}, + {file = "lxml-5.2.1-cp38-cp38-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:08802f0c56ed150cc6885ae0788a321b73505d2263ee56dad84d200cab11c07a"}, + {file = "lxml-5.2.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0f8c09ed18ecb4ebf23e02b8e7a22a05d6411911e6fabef3a36e4f371f4f2585"}, + {file = "lxml-5.2.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e3d30321949861404323c50aebeb1943461a67cd51d4200ab02babc58bd06a86"}, + {file = "lxml-5.2.1-cp38-cp38-manylinux_2_28_aarch64.whl", hash = "sha256:b560e3aa4b1d49e0e6c847d72665384db35b2f5d45f8e6a5c0072e0283430533"}, + {file = "lxml-5.2.1-cp38-cp38-manylinux_2_28_x86_64.whl", hash = "sha256:058a1308914f20784c9f4674036527e7c04f7be6fb60f5d61353545aa7fcb739"}, + {file = "lxml-5.2.1-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:adfb84ca6b87e06bc6b146dc7da7623395db1e31621c4785ad0658c5028b37d7"}, + {file = "lxml-5.2.1-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:417d14450f06d51f363e41cace6488519038f940676ce9664b34ebf5653433a5"}, + {file = "lxml-5.2.1-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:a2dfe7e2473f9b59496247aad6e23b405ddf2e12ef0765677b0081c02d6c2c0b"}, + {file = "lxml-5.2.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:bf2e2458345d9bffb0d9ec16557d8858c9c88d2d11fed53998512504cd9df49b"}, + {file = "lxml-5.2.1-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:58278b29cb89f3e43ff3e0c756abbd1518f3ee6adad9e35b51fb101c1c1daaec"}, + {file = "lxml-5.2.1-cp38-cp38-musllinux_1_2_ppc64le.whl", hash = "sha256:64641a6068a16201366476731301441ce93457eb8452056f570133a6ceb15fca"}, + {file = "lxml-5.2.1-cp38-cp38-musllinux_1_2_s390x.whl", hash = "sha256:78bfa756eab503673991bdcf464917ef7845a964903d3302c5f68417ecdc948c"}, + {file = "lxml-5.2.1-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:11a04306fcba10cd9637e669fd73aa274c1c09ca64af79c041aa820ea992b637"}, + {file = "lxml-5.2.1-cp38-cp38-win32.whl", hash = "sha256:66bc5eb8a323ed9894f8fa0ee6cb3e3fb2403d99aee635078fd19a8bc7a5a5da"}, + {file = "lxml-5.2.1-cp38-cp38-win_amd64.whl", hash = "sha256:9676bfc686fa6a3fa10cd4ae6b76cae8be26eb5ec6811d2a325636c460da1806"}, + {file = "lxml-5.2.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:cf22b41fdae514ee2f1691b6c3cdeae666d8b7fa9434de445f12bbeee0cf48dd"}, + {file = "lxml-5.2.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:ec42088248c596dbd61d4ae8a5b004f97a4d91a9fd286f632e42e60b706718d7"}, + {file = "lxml-5.2.1-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:cd53553ddad4a9c2f1f022756ae64abe16da1feb497edf4d9f87f99ec7cf86bd"}, + {file = "lxml-5.2.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:feaa45c0eae424d3e90d78823f3828e7dc42a42f21ed420db98da2c4ecf0a2cb"}, + {file = "lxml-5.2.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ddc678fb4c7e30cf830a2b5a8d869538bc55b28d6c68544d09c7d0d8f17694dc"}, + {file = "lxml-5.2.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:853e074d4931dbcba7480d4dcab23d5c56bd9607f92825ab80ee2bd916edea53"}, + {file = "lxml-5.2.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cc4691d60512798304acb9207987e7b2b7c44627ea88b9d77489bbe3e6cc3bd4"}, + {file = "lxml-5.2.1-cp39-cp39-manylinux_2_28_aarch64.whl", hash = "sha256:beb72935a941965c52990f3a32d7f07ce869fe21c6af8b34bf6a277b33a345d3"}, + {file = "lxml-5.2.1-cp39-cp39-manylinux_2_28_ppc64le.whl", hash = "sha256:6588c459c5627fefa30139be4d2e28a2c2a1d0d1c265aad2ba1935a7863a4913"}, + {file = "lxml-5.2.1-cp39-cp39-manylinux_2_28_s390x.whl", hash = "sha256:588008b8497667f1ddca7c99f2f85ce8511f8f7871b4a06ceede68ab62dff64b"}, + {file = "lxml-5.2.1-cp39-cp39-manylinux_2_28_x86_64.whl", hash = "sha256:b6787b643356111dfd4032b5bffe26d2f8331556ecb79e15dacb9275da02866e"}, + {file = "lxml-5.2.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:7c17b64b0a6ef4e5affae6a3724010a7a66bda48a62cfe0674dabd46642e8b54"}, + {file = "lxml-5.2.1-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:27aa20d45c2e0b8cd05da6d4759649170e8dfc4f4e5ef33a34d06f2d79075d57"}, + {file = "lxml-5.2.1-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:d4f2cc7060dc3646632d7f15fe68e2fa98f58e35dd5666cd525f3b35d3fed7f8"}, + {file = "lxml-5.2.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:ff46d772d5f6f73564979cd77a4fffe55c916a05f3cb70e7c9c0590059fb29ef"}, + {file = "lxml-5.2.1-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:96323338e6c14e958d775700ec8a88346014a85e5de73ac7967db0367582049b"}, + {file = "lxml-5.2.1-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:52421b41ac99e9d91934e4d0d0fe7da9f02bfa7536bb4431b4c05c906c8c6919"}, + {file = "lxml-5.2.1-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:7a7efd5b6d3e30d81ec68ab8a88252d7c7c6f13aaa875009fe3097eb4e30b84c"}, + {file = "lxml-5.2.1-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:0ed777c1e8c99b63037b91f9d73a6aad20fd035d77ac84afcc205225f8f41188"}, + {file = "lxml-5.2.1-cp39-cp39-win32.whl", hash = "sha256:644df54d729ef810dcd0f7732e50e5ad1bd0a135278ed8d6bcb06f33b6b6f708"}, + {file = "lxml-5.2.1-cp39-cp39-win_amd64.whl", hash = "sha256:9ca66b8e90daca431b7ca1408cae085d025326570e57749695d6a01454790e95"}, + {file = "lxml-5.2.1-pp310-pypy310_pp73-macosx_10_9_x86_64.whl", hash = "sha256:9b0ff53900566bc6325ecde9181d89afadc59c5ffa39bddf084aaedfe3b06a11"}, + {file = "lxml-5.2.1-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fd6037392f2d57793ab98d9e26798f44b8b4da2f2464388588f48ac52c489ea1"}, + {file = "lxml-5.2.1-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8b9c07e7a45bb64e21df4b6aa623cb8ba214dfb47d2027d90eac197329bb5e94"}, + {file = "lxml-5.2.1-pp310-pypy310_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:3249cc2989d9090eeac5467e50e9ec2d40704fea9ab72f36b034ea34ee65ca98"}, + {file = "lxml-5.2.1-pp310-pypy310_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:f42038016852ae51b4088b2862126535cc4fc85802bfe30dea3500fdfaf1864e"}, + {file = "lxml-5.2.1-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:533658f8fbf056b70e434dff7e7aa611bcacb33e01f75de7f821810e48d1bb66"}, + {file = "lxml-5.2.1-pp37-pypy37_pp73-macosx_10_9_x86_64.whl", hash = "sha256:622020d4521e22fb371e15f580d153134bfb68d6a429d1342a25f051ec72df1c"}, + {file = "lxml-5.2.1-pp37-pypy37_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:efa7b51824aa0ee957ccd5a741c73e6851de55f40d807f08069eb4c5a26b2baa"}, + {file = "lxml-5.2.1-pp37-pypy37_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9c6ad0fbf105f6bcc9300c00010a2ffa44ea6f555df1a2ad95c88f5656104817"}, + {file = "lxml-5.2.1-pp37-pypy37_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:e233db59c8f76630c512ab4a4daf5a5986da5c3d5b44b8e9fc742f2a24dbd460"}, + {file = "lxml-5.2.1-pp37-pypy37_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:6a014510830df1475176466b6087fc0c08b47a36714823e58d8b8d7709132a96"}, + {file = "lxml-5.2.1-pp37-pypy37_pp73-win_amd64.whl", hash = "sha256:d38c8f50ecf57f0463399569aa388b232cf1a2ffb8f0a9a5412d0db57e054860"}, + {file = "lxml-5.2.1-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:5aea8212fb823e006b995c4dda533edcf98a893d941f173f6c9506126188860d"}, + {file = "lxml-5.2.1-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ff097ae562e637409b429a7ac958a20aab237a0378c42dabaa1e3abf2f896e5f"}, + {file = "lxml-5.2.1-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0f5d65c39f16717a47c36c756af0fb36144069c4718824b7533f803ecdf91138"}, + {file = "lxml-5.2.1-pp38-pypy38_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:3d0c3dd24bb4605439bf91068598d00c6370684f8de4a67c2992683f6c309d6b"}, + {file = "lxml-5.2.1-pp38-pypy38_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:e32be23d538753a8adb6c85bd539f5fd3b15cb987404327c569dfc5fd8366e85"}, + {file = "lxml-5.2.1-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:cc518cea79fd1e2f6c90baafa28906d4309d24f3a63e801d855e7424c5b34144"}, + {file = "lxml-5.2.1-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:a0af35bd8ebf84888373630f73f24e86bf016642fb8576fba49d3d6b560b7cbc"}, + {file = "lxml-5.2.1-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f8aca2e3a72f37bfc7b14ba96d4056244001ddcc18382bd0daa087fd2e68a354"}, + {file = "lxml-5.2.1-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5ca1e8188b26a819387b29c3895c47a5e618708fe6f787f3b1a471de2c4a94d9"}, + {file = "lxml-5.2.1-pp39-pypy39_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:c8ba129e6d3b0136a0f50345b2cb3db53f6bda5dd8c7f5d83fbccba97fb5dcb5"}, + {file = "lxml-5.2.1-pp39-pypy39_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:e998e304036198b4f6914e6a1e2b6f925208a20e2042563d9734881150c6c246"}, + {file = "lxml-5.2.1-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:d3be9b2076112e51b323bdf6d5a7f8a798de55fb8d95fcb64bd179460cdc0704"}, + {file = "lxml-5.2.1.tar.gz", hash = "sha256:3f7765e69bbce0906a7c74d5fe46d2c7a7596147318dbc08e4a2431f3060e306"}, ] [package.extras] cssselect = ["cssselect (>=0.7)"] +html-clean = ["lxml-html-clean"] html5 = ["html5lib"] htmlsoup = ["BeautifulSoup4"] -source = ["Cython (>=3.0.7)"] +source = ["Cython (>=3.0.10)"] [[package]] name = "markupsafe" @@ -989,39 +1067,39 @@ files = [ [[package]] name = "matplotlib" -version = "3.8.3" +version = "3.8.4" description = "Python plotting package" optional = false python-versions = ">=3.9" files = [ - {file = "matplotlib-3.8.3-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:cf60138ccc8004f117ab2a2bad513cc4d122e55864b4fe7adf4db20ca68a078f"}, - {file = "matplotlib-3.8.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:5f557156f7116be3340cdeef7f128fa99b0d5d287d5f41a16e169819dcf22357"}, - {file = "matplotlib-3.8.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f386cf162b059809ecfac3bcc491a9ea17da69fa35c8ded8ad154cd4b933d5ec"}, - {file = "matplotlib-3.8.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b3c5f96f57b0369c288bf6f9b5274ba45787f7e0589a34d24bdbaf6d3344632f"}, - {file = "matplotlib-3.8.3-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:83e0f72e2c116ca7e571c57aa29b0fe697d4c6425c4e87c6e994159e0c008635"}, - {file = "matplotlib-3.8.3-cp310-cp310-win_amd64.whl", hash = "sha256:1c5c8290074ba31a41db1dc332dc2b62def469ff33766cbe325d32a3ee291aea"}, - {file = "matplotlib-3.8.3-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:5184e07c7e1d6d1481862ee361905b7059f7fe065fc837f7c3dc11eeb3f2f900"}, - {file = "matplotlib-3.8.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:d7e7e0993d0758933b1a241a432b42c2db22dfa37d4108342ab4afb9557cbe3e"}, - {file = "matplotlib-3.8.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:04b36ad07eac9740fc76c2aa16edf94e50b297d6eb4c081e3add863de4bb19a7"}, - {file = "matplotlib-3.8.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7c42dae72a62f14982f1474f7e5c9959fc4bc70c9de11cc5244c6e766200ba65"}, - {file = "matplotlib-3.8.3-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:bf5932eee0d428192c40b7eac1399d608f5d995f975cdb9d1e6b48539a5ad8d0"}, - {file = "matplotlib-3.8.3-cp311-cp311-win_amd64.whl", hash = "sha256:40321634e3a05ed02abf7c7b47a50be50b53ef3eaa3a573847431a545585b407"}, - {file = "matplotlib-3.8.3-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:09074f8057917d17ab52c242fdf4916f30e99959c1908958b1fc6032e2d0f6d4"}, - {file = "matplotlib-3.8.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:5745f6d0fb5acfabbb2790318db03809a253096e98c91b9a31969df28ee604aa"}, - {file = "matplotlib-3.8.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b97653d869a71721b639714b42d87cda4cfee0ee74b47c569e4874c7590c55c5"}, - {file = "matplotlib-3.8.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:242489efdb75b690c9c2e70bb5c6550727058c8a614e4c7716f363c27e10bba1"}, - {file = "matplotlib-3.8.3-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:83c0653c64b73926730bd9ea14aa0f50f202ba187c307a881673bad4985967b7"}, - {file = "matplotlib-3.8.3-cp312-cp312-win_amd64.whl", hash = "sha256:ef6c1025a570354297d6c15f7d0f296d95f88bd3850066b7f1e7b4f2f4c13a39"}, - {file = "matplotlib-3.8.3-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:c4af3f7317f8a1009bbb2d0bf23dfaba859eb7dd4ccbd604eba146dccaaaf0a4"}, - {file = "matplotlib-3.8.3-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:4c6e00a65d017d26009bac6808f637b75ceade3e1ff91a138576f6b3065eeeba"}, - {file = "matplotlib-3.8.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e7b49ab49a3bea17802df6872f8d44f664ba8f9be0632a60c99b20b6db2165b7"}, - {file = "matplotlib-3.8.3-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6728dde0a3997396b053602dbd907a9bd64ec7d5cf99e728b404083698d3ca01"}, - {file = "matplotlib-3.8.3-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:813925d08fb86aba139f2d31864928d67511f64e5945ca909ad5bc09a96189bb"}, - {file = "matplotlib-3.8.3-cp39-cp39-win_amd64.whl", hash = "sha256:cd3a0c2be76f4e7be03d34a14d49ded6acf22ef61f88da600a18a5cd8b3c5f3c"}, - {file = "matplotlib-3.8.3-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:fa93695d5c08544f4a0dfd0965f378e7afc410d8672816aff1e81be1f45dbf2e"}, - {file = "matplotlib-3.8.3-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e9764df0e8778f06414b9d281a75235c1e85071f64bb5d71564b97c1306a2afc"}, - {file = "matplotlib-3.8.3-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:5e431a09e6fab4012b01fc155db0ce6dccacdbabe8198197f523a4ef4805eb26"}, - {file = "matplotlib-3.8.3.tar.gz", hash = "sha256:7b416239e9ae38be54b028abbf9048aff5054a9aba5416bef0bd17f9162ce161"}, + {file = "matplotlib-3.8.4-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:abc9d838f93583650c35eca41cfcec65b2e7cb50fd486da6f0c49b5e1ed23014"}, + {file = "matplotlib-3.8.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:8f65c9f002d281a6e904976007b2d46a1ee2bcea3a68a8c12dda24709ddc9106"}, + {file = "matplotlib-3.8.4-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ce1edd9f5383b504dbc26eeea404ed0a00656c526638129028b758fd43fc5f10"}, + {file = "matplotlib-3.8.4-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ecd79298550cba13a43c340581a3ec9c707bd895a6a061a78fa2524660482fc0"}, + {file = "matplotlib-3.8.4-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:90df07db7b599fe7035d2f74ab7e438b656528c68ba6bb59b7dc46af39ee48ef"}, + {file = "matplotlib-3.8.4-cp310-cp310-win_amd64.whl", hash = "sha256:ac24233e8f2939ac4fd2919eed1e9c0871eac8057666070e94cbf0b33dd9c338"}, + {file = "matplotlib-3.8.4-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:72f9322712e4562e792b2961971891b9fbbb0e525011e09ea0d1f416c4645661"}, + {file = "matplotlib-3.8.4-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:232ce322bfd020a434caaffbd9a95333f7c2491e59cfc014041d95e38ab90d1c"}, + {file = "matplotlib-3.8.4-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6addbd5b488aedb7f9bc19f91cd87ea476206f45d7116fcfe3d31416702a82fa"}, + {file = "matplotlib-3.8.4-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cc4ccdc64e3039fc303defd119658148f2349239871db72cd74e2eeaa9b80b71"}, + {file = "matplotlib-3.8.4-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:b7a2a253d3b36d90c8993b4620183b55665a429da8357a4f621e78cd48b2b30b"}, + {file = "matplotlib-3.8.4-cp311-cp311-win_amd64.whl", hash = "sha256:8080d5081a86e690d7688ffa542532e87f224c38a6ed71f8fbed34dd1d9fedae"}, + {file = "matplotlib-3.8.4-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:6485ac1f2e84676cff22e693eaa4fbed50ef5dc37173ce1f023daef4687df616"}, + {file = "matplotlib-3.8.4-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:c89ee9314ef48c72fe92ce55c4e95f2f39d70208f9f1d9db4e64079420d8d732"}, + {file = "matplotlib-3.8.4-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:50bac6e4d77e4262c4340d7a985c30912054745ec99756ce213bfbc3cb3808eb"}, + {file = "matplotlib-3.8.4-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f51c4c869d4b60d769f7b4406eec39596648d9d70246428745a681c327a8ad30"}, + {file = "matplotlib-3.8.4-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:b12ba985837e4899b762b81f5b2845bd1a28f4fdd1a126d9ace64e9c4eb2fb25"}, + {file = "matplotlib-3.8.4-cp312-cp312-win_amd64.whl", hash = "sha256:7a6769f58ce51791b4cb8b4d7642489df347697cd3e23d88266aaaee93b41d9a"}, + {file = "matplotlib-3.8.4-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:843cbde2f0946dadd8c5c11c6d91847abd18ec76859dc319362a0964493f0ba6"}, + {file = "matplotlib-3.8.4-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:1c13f041a7178f9780fb61cc3a2b10423d5e125480e4be51beaf62b172413b67"}, + {file = "matplotlib-3.8.4-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fb44f53af0a62dc80bba4443d9b27f2fde6acfdac281d95bc872dc148a6509cc"}, + {file = "matplotlib-3.8.4-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:606e3b90897554c989b1e38a258c626d46c873523de432b1462f295db13de6f9"}, + {file = "matplotlib-3.8.4-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:9bb0189011785ea794ee827b68777db3ca3f93f3e339ea4d920315a0e5a78d54"}, + {file = "matplotlib-3.8.4-cp39-cp39-win_amd64.whl", hash = "sha256:6209e5c9aaccc056e63b547a8152661324404dd92340a6e479b3a7f24b42a5d0"}, + {file = "matplotlib-3.8.4-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:c7064120a59ce6f64103c9cefba8ffe6fba87f2c61d67c401186423c9a20fd35"}, + {file = "matplotlib-3.8.4-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a0e47eda4eb2614300fc7bb4657fced3e83d6334d03da2173b09e447418d499f"}, + {file = "matplotlib-3.8.4-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:493e9f6aa5819156b58fce42b296ea31969f2aab71c5b680b4ea7a3cb5c07d94"}, + {file = "matplotlib-3.8.4.tar.gz", hash = "sha256:8aac397d5e9ec158960e31c381c5ffc52ddd52bd9a47717e2a694038167dffea"}, ] [package.dependencies] @@ -1029,7 +1107,7 @@ contourpy = ">=1.0.1" cycler = ">=0.10" fonttools = ">=4.22.0" kiwisolver = ">=1.3.1" -numpy = ">=1.21,<2" +numpy = ">=1.21" packaging = ">=20.0" pillow = ">=8" pyparsing = ">=2.3.1" @@ -1142,47 +1220,45 @@ files = [ [[package]] name = "pandas" -version = "2.2.1" +version = "2.2.2" description = "Powerful data structures for data analysis, time series, and statistics" optional = false python-versions = ">=3.9" files = [ - {file = "pandas-2.2.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:8df8612be9cd1c7797c93e1c5df861b2ddda0b48b08f2c3eaa0702cf88fb5f88"}, - {file = "pandas-2.2.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:0f573ab277252ed9aaf38240f3b54cfc90fff8e5cab70411ee1d03f5d51f3944"}, - {file = "pandas-2.2.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f02a3a6c83df4026e55b63c1f06476c9aa3ed6af3d89b4f04ea656ccdaaaa359"}, - {file = "pandas-2.2.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c38ce92cb22a4bea4e3929429aa1067a454dcc9c335799af93ba9be21b6beb51"}, - {file = "pandas-2.2.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:c2ce852e1cf2509a69e98358e8458775f89599566ac3775e70419b98615f4b06"}, - {file = "pandas-2.2.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:53680dc9b2519cbf609c62db3ed7c0b499077c7fefda564e330286e619ff0dd9"}, - {file = "pandas-2.2.1-cp310-cp310-win_amd64.whl", hash = "sha256:94e714a1cca63e4f5939cdce5f29ba8d415d85166be3441165edd427dc9f6bc0"}, - {file = "pandas-2.2.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:f821213d48f4ab353d20ebc24e4faf94ba40d76680642fb7ce2ea31a3ad94f9b"}, - {file = "pandas-2.2.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:c70e00c2d894cb230e5c15e4b1e1e6b2b478e09cf27cc593a11ef955b9ecc81a"}, - {file = "pandas-2.2.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e97fbb5387c69209f134893abc788a6486dbf2f9e511070ca05eed4b930b1b02"}, - {file = "pandas-2.2.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:101d0eb9c5361aa0146f500773395a03839a5e6ecde4d4b6ced88b7e5a1a6403"}, - {file = "pandas-2.2.1-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:7d2ed41c319c9fb4fd454fe25372028dfa417aacb9790f68171b2e3f06eae8cd"}, - {file = "pandas-2.2.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:af5d3c00557d657c8773ef9ee702c61dd13b9d7426794c9dfeb1dc4a0bf0ebc7"}, - {file = "pandas-2.2.1-cp311-cp311-win_amd64.whl", hash = "sha256:06cf591dbaefb6da9de8472535b185cba556d0ce2e6ed28e21d919704fef1a9e"}, - {file = "pandas-2.2.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:88ecb5c01bb9ca927ebc4098136038519aa5d66b44671861ffab754cae75102c"}, - {file = "pandas-2.2.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:04f6ec3baec203c13e3f8b139fb0f9f86cd8c0b94603ae3ae8ce9a422e9f5bee"}, - {file = "pandas-2.2.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a935a90a76c44fe170d01e90a3594beef9e9a6220021acfb26053d01426f7dc2"}, - {file = "pandas-2.2.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c391f594aae2fd9f679d419e9a4d5ba4bce5bb13f6a989195656e7dc4b95c8f0"}, - {file = "pandas-2.2.1-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:9d1265545f579edf3f8f0cb6f89f234f5e44ba725a34d86535b1a1d38decbccc"}, - {file = "pandas-2.2.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:11940e9e3056576ac3244baef2fedade891977bcc1cb7e5cc8f8cc7d603edc89"}, - {file = "pandas-2.2.1-cp312-cp312-win_amd64.whl", hash = "sha256:4acf681325ee1c7f950d058b05a820441075b0dd9a2adf5c4835b9bc056bf4fb"}, - {file = "pandas-2.2.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:9bd8a40f47080825af4317d0340c656744f2bfdb6819f818e6ba3cd24c0e1397"}, - {file = "pandas-2.2.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:df0c37ebd19e11d089ceba66eba59a168242fc6b7155cba4ffffa6eccdfb8f16"}, - {file = "pandas-2.2.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:739cc70eaf17d57608639e74d63387b0d8594ce02f69e7a0b046f117974b3019"}, - {file = "pandas-2.2.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f9d3558d263073ed95e46f4650becff0c5e1ffe0fc3a015de3c79283dfbdb3df"}, - {file = "pandas-2.2.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:4aa1d8707812a658debf03824016bf5ea0d516afdea29b7dc14cf687bc4d4ec6"}, - {file = "pandas-2.2.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:76f27a809cda87e07f192f001d11adc2b930e93a2b0c4a236fde5429527423be"}, - {file = "pandas-2.2.1-cp39-cp39-win_amd64.whl", hash = "sha256:1ba21b1d5c0e43416218db63037dbe1a01fc101dc6e6024bcad08123e48004ab"}, - {file = "pandas-2.2.1.tar.gz", hash = "sha256:0ab90f87093c13f3e8fa45b48ba9f39181046e8f3317d3aadb2fffbb1b978572"}, + {file = "pandas-2.2.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:90c6fca2acf139569e74e8781709dccb6fe25940488755716d1d354d6bc58bce"}, + {file = "pandas-2.2.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4abfe0be0d7221be4f12552995e58723c7422c80a659da13ca382697de830c08"}, + {file = "pandas-2.2.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8635c16bf3d99040fdf3ca3db669a7250ddf49c55dc4aa8fe0ae0fa8d6dcc1f0"}, + {file = "pandas-2.2.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:40ae1dffb3967a52203105a077415a86044a2bea011b5f321c6aa64b379a3f51"}, + {file = "pandas-2.2.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:8e5a0b00e1e56a842f922e7fae8ae4077aee4af0acb5ae3622bd4b4c30aedf99"}, + {file = "pandas-2.2.2-cp310-cp310-win_amd64.whl", hash = "sha256:ddf818e4e6c7c6f4f7c8a12709696d193976b591cc7dc50588d3d1a6b5dc8772"}, + {file = "pandas-2.2.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:696039430f7a562b74fa45f540aca068ea85fa34c244d0deee539cb6d70aa288"}, + {file = "pandas-2.2.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:8e90497254aacacbc4ea6ae5e7a8cd75629d6ad2b30025a4a8b09aa4faf55151"}, + {file = "pandas-2.2.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:58b84b91b0b9f4bafac2a0ac55002280c094dfc6402402332c0913a59654ab2b"}, + {file = "pandas-2.2.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6d2123dc9ad6a814bcdea0f099885276b31b24f7edf40f6cdbc0912672e22eee"}, + {file = "pandas-2.2.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:2925720037f06e89af896c70bca73459d7e6a4be96f9de79e2d440bd499fe0db"}, + {file = "pandas-2.2.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:0cace394b6ea70c01ca1595f839cf193df35d1575986e484ad35c4aeae7266c1"}, + {file = "pandas-2.2.2-cp311-cp311-win_amd64.whl", hash = "sha256:873d13d177501a28b2756375d59816c365e42ed8417b41665f346289adc68d24"}, + {file = "pandas-2.2.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:9dfde2a0ddef507a631dc9dc4af6a9489d5e2e740e226ad426a05cabfbd7c8ef"}, + {file = "pandas-2.2.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:e9b79011ff7a0f4b1d6da6a61aa1aa604fb312d6647de5bad20013682d1429ce"}, + {file = "pandas-2.2.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1cb51fe389360f3b5a4d57dbd2848a5f033350336ca3b340d1c53a1fad33bcad"}, + {file = "pandas-2.2.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:eee3a87076c0756de40b05c5e9a6069c035ba43e8dd71c379e68cab2c20f16ad"}, + {file = "pandas-2.2.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:3e374f59e440d4ab45ca2fffde54b81ac3834cf5ae2cdfa69c90bc03bde04d76"}, + {file = "pandas-2.2.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:43498c0bdb43d55cb162cdc8c06fac328ccb5d2eabe3cadeb3529ae6f0517c32"}, + {file = "pandas-2.2.2-cp312-cp312-win_amd64.whl", hash = "sha256:d187d355ecec3629624fccb01d104da7d7f391db0311145817525281e2804d23"}, + {file = "pandas-2.2.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:0ca6377b8fca51815f382bd0b697a0814c8bda55115678cbc94c30aacbb6eff2"}, + {file = "pandas-2.2.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:001910ad31abc7bf06f49dcc903755d2f7f3a9186c0c040b827e522e9cef0863"}, + {file = "pandas-2.2.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:66b479b0bd07204e37583c191535505410daa8df638fd8e75ae1b383851fe921"}, + {file = "pandas-2.2.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:a77e9d1c386196879aa5eb712e77461aaee433e54c68cf253053a73b7e49c33a"}, + {file = "pandas-2.2.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:92fd6b027924a7e178ac202cfbe25e53368db90d56872d20ffae94b96c7acc57"}, + {file = "pandas-2.2.2-cp39-cp39-win_amd64.whl", hash = "sha256:640cef9aa381b60e296db324337a554aeeb883ead99dc8f6c18e81a93942f5f4"}, + {file = "pandas-2.2.2.tar.gz", hash = "sha256:9e79019aba43cb4fda9e4d983f8e88ca0373adbb697ae9c6c43093218de28b54"}, ] [package.dependencies] numpy = [ - {version = ">=1.22.4,<2", markers = "python_version < \"3.11\""}, - {version = ">=1.23.2,<2", markers = "python_version == \"3.11\""}, - {version = ">=1.26.0,<2", markers = "python_version >= \"3.12\""}, + {version = ">=1.22.4", markers = "python_version < \"3.11\""}, + {version = ">=1.23.2", markers = "python_version == \"3.11\""}, + {version = ">=1.26.0", markers = "python_version >= \"3.12\""}, ] python-dateutil = ">=2.8.2" pytz = ">=2020.1" @@ -1229,79 +1305,80 @@ requests = ">=2.14.0" [[package]] name = "pillow" -version = "10.2.0" +version = "10.3.0" description = "Python Imaging Library (Fork)" optional = false python-versions = ">=3.8" files = [ - {file = "pillow-10.2.0-cp310-cp310-macosx_10_10_x86_64.whl", hash = "sha256:7823bdd049099efa16e4246bdf15e5a13dbb18a51b68fa06d6c1d4d8b99a796e"}, - {file = "pillow-10.2.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:83b2021f2ade7d1ed556bc50a399127d7fb245e725aa0113ebd05cfe88aaf588"}, - {file = "pillow-10.2.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6fad5ff2f13d69b7e74ce5b4ecd12cc0ec530fcee76356cac6742785ff71c452"}, - {file = "pillow-10.2.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:da2b52b37dad6d9ec64e653637a096905b258d2fc2b984c41ae7d08b938a67e4"}, - {file = "pillow-10.2.0-cp310-cp310-manylinux_2_28_aarch64.whl", hash = "sha256:47c0995fc4e7f79b5cfcab1fc437ff2890b770440f7696a3ba065ee0fd496563"}, - {file = "pillow-10.2.0-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:322bdf3c9b556e9ffb18f93462e5f749d3444ce081290352c6070d014c93feb2"}, - {file = "pillow-10.2.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:51f1a1bffc50e2e9492e87d8e09a17c5eea8409cda8d3f277eb6edc82813c17c"}, - {file = "pillow-10.2.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:69ffdd6120a4737710a9eee73e1d2e37db89b620f702754b8f6e62594471dee0"}, - {file = "pillow-10.2.0-cp310-cp310-win32.whl", hash = "sha256:c6dafac9e0f2b3c78df97e79af707cdc5ef8e88208d686a4847bab8266870023"}, - {file = "pillow-10.2.0-cp310-cp310-win_amd64.whl", hash = "sha256:aebb6044806f2e16ecc07b2a2637ee1ef67a11840a66752751714a0d924adf72"}, - {file = "pillow-10.2.0-cp310-cp310-win_arm64.whl", hash = "sha256:7049e301399273a0136ff39b84c3678e314f2158f50f517bc50285fb5ec847ad"}, - {file = "pillow-10.2.0-cp311-cp311-macosx_10_10_x86_64.whl", hash = "sha256:35bb52c37f256f662abdfa49d2dfa6ce5d93281d323a9af377a120e89a9eafb5"}, - {file = "pillow-10.2.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:9c23f307202661071d94b5e384e1e1dc7dfb972a28a2310e4ee16103e66ddb67"}, - {file = "pillow-10.2.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:773efe0603db30c281521a7c0214cad7836c03b8ccff897beae9b47c0b657d61"}, - {file = "pillow-10.2.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:11fa2e5984b949b0dd6d7a94d967743d87c577ff0b83392f17cb3990d0d2fd6e"}, - {file = "pillow-10.2.0-cp311-cp311-manylinux_2_28_aarch64.whl", hash = "sha256:716d30ed977be8b37d3ef185fecb9e5a1d62d110dfbdcd1e2a122ab46fddb03f"}, - {file = "pillow-10.2.0-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:a086c2af425c5f62a65e12fbf385f7c9fcb8f107d0849dba5839461a129cf311"}, - {file = "pillow-10.2.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:c8de2789052ed501dd829e9cae8d3dcce7acb4777ea4a479c14521c942d395b1"}, - {file = "pillow-10.2.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:609448742444d9290fd687940ac0b57fb35e6fd92bdb65386e08e99af60bf757"}, - {file = "pillow-10.2.0-cp311-cp311-win32.whl", hash = "sha256:823ef7a27cf86df6597fa0671066c1b596f69eba53efa3d1e1cb8b30f3533068"}, - {file = "pillow-10.2.0-cp311-cp311-win_amd64.whl", hash = "sha256:1da3b2703afd040cf65ec97efea81cfba59cdbed9c11d8efc5ab09df9509fc56"}, - {file = "pillow-10.2.0-cp311-cp311-win_arm64.whl", hash = "sha256:edca80cbfb2b68d7b56930b84a0e45ae1694aeba0541f798e908a49d66b837f1"}, - {file = "pillow-10.2.0-cp312-cp312-macosx_10_10_x86_64.whl", hash = "sha256:1b5e1b74d1bd1b78bc3477528919414874748dd363e6272efd5abf7654e68bef"}, - {file = "pillow-10.2.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:0eae2073305f451d8ecacb5474997c08569fb4eb4ac231ffa4ad7d342fdc25ac"}, - {file = "pillow-10.2.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b7c2286c23cd350b80d2fc9d424fc797575fb16f854b831d16fd47ceec078f2c"}, - {file = "pillow-10.2.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1e23412b5c41e58cec602f1135c57dfcf15482013ce6e5f093a86db69646a5aa"}, - {file = "pillow-10.2.0-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:52a50aa3fb3acb9cf7213573ef55d31d6eca37f5709c69e6858fe3bc04a5c2a2"}, - {file = "pillow-10.2.0-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:127cee571038f252a552760076407f9cff79761c3d436a12af6000cd182a9d04"}, - {file = "pillow-10.2.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:8d12251f02d69d8310b046e82572ed486685c38f02176bd08baf216746eb947f"}, - {file = "pillow-10.2.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:54f1852cd531aa981bc0965b7d609f5f6cc8ce8c41b1139f6ed6b3c54ab82bfb"}, - {file = "pillow-10.2.0-cp312-cp312-win32.whl", hash = "sha256:257d8788df5ca62c980314053197f4d46eefedf4e6175bc9412f14412ec4ea2f"}, - {file = "pillow-10.2.0-cp312-cp312-win_amd64.whl", hash = "sha256:154e939c5f0053a383de4fd3d3da48d9427a7e985f58af8e94d0b3c9fcfcf4f9"}, - {file = "pillow-10.2.0-cp312-cp312-win_arm64.whl", hash = "sha256:f379abd2f1e3dddb2b61bc67977a6b5a0a3f7485538bcc6f39ec76163891ee48"}, - {file = "pillow-10.2.0-cp38-cp38-macosx_10_10_x86_64.whl", hash = "sha256:8373c6c251f7ef8bda6675dd6d2b3a0fcc31edf1201266b5cf608b62a37407f9"}, - {file = "pillow-10.2.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:870ea1ada0899fd0b79643990809323b389d4d1d46c192f97342eeb6ee0b8483"}, - {file = "pillow-10.2.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b4b6b1e20608493548b1f32bce8cca185bf0480983890403d3b8753e44077129"}, - {file = "pillow-10.2.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3031709084b6e7852d00479fd1d310b07d0ba82765f973b543c8af5061cf990e"}, - {file = "pillow-10.2.0-cp38-cp38-manylinux_2_28_aarch64.whl", hash = "sha256:3ff074fc97dd4e80543a3e91f69d58889baf2002b6be64347ea8cf5533188213"}, - {file = "pillow-10.2.0-cp38-cp38-manylinux_2_28_x86_64.whl", hash = "sha256:cb4c38abeef13c61d6916f264d4845fab99d7b711be96c326b84df9e3e0ff62d"}, - {file = "pillow-10.2.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:b1b3020d90c2d8e1dae29cf3ce54f8094f7938460fb5ce8bc5c01450b01fbaf6"}, - {file = "pillow-10.2.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:170aeb00224ab3dc54230c797f8404507240dd868cf52066f66a41b33169bdbe"}, - {file = "pillow-10.2.0-cp38-cp38-win32.whl", hash = "sha256:c4225f5220f46b2fde568c74fca27ae9771536c2e29d7c04f4fb62c83275ac4e"}, - {file = "pillow-10.2.0-cp38-cp38-win_amd64.whl", hash = "sha256:0689b5a8c5288bc0504d9fcee48f61a6a586b9b98514d7d29b840143d6734f39"}, - {file = "pillow-10.2.0-cp39-cp39-macosx_10_10_x86_64.whl", hash = "sha256:b792a349405fbc0163190fde0dc7b3fef3c9268292586cf5645598b48e63dc67"}, - {file = "pillow-10.2.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:c570f24be1e468e3f0ce7ef56a89a60f0e05b30a3669a459e419c6eac2c35364"}, - {file = "pillow-10.2.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d8ecd059fdaf60c1963c58ceb8997b32e9dc1b911f5da5307aab614f1ce5c2fb"}, - {file = "pillow-10.2.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c365fd1703040de1ec284b176d6af5abe21b427cb3a5ff68e0759e1e313a5e7e"}, - {file = "pillow-10.2.0-cp39-cp39-manylinux_2_28_aarch64.whl", hash = "sha256:70c61d4c475835a19b3a5aa42492409878bbca7438554a1f89d20d58a7c75c01"}, - {file = "pillow-10.2.0-cp39-cp39-manylinux_2_28_x86_64.whl", hash = "sha256:b6f491cdf80ae540738859d9766783e3b3c8e5bd37f5dfa0b76abdecc5081f13"}, - {file = "pillow-10.2.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:9d189550615b4948f45252d7f005e53c2040cea1af5b60d6f79491a6e147eef7"}, - {file = "pillow-10.2.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:49d9ba1ed0ef3e061088cd1e7538a0759aab559e2e0a80a36f9fd9d8c0c21591"}, - {file = "pillow-10.2.0-cp39-cp39-win32.whl", hash = "sha256:babf5acfede515f176833ed6028754cbcd0d206f7f614ea3447d67c33be12516"}, - {file = "pillow-10.2.0-cp39-cp39-win_amd64.whl", hash = "sha256:0304004f8067386b477d20a518b50f3fa658a28d44e4116970abfcd94fac34a8"}, - {file = "pillow-10.2.0-cp39-cp39-win_arm64.whl", hash = "sha256:0fb3e7fc88a14eacd303e90481ad983fd5b69c761e9e6ef94c983f91025da869"}, - {file = "pillow-10.2.0-pp310-pypy310_pp73-macosx_10_10_x86_64.whl", hash = "sha256:322209c642aabdd6207517e9739c704dc9f9db943015535783239022002f054a"}, - {file = "pillow-10.2.0-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3eedd52442c0a5ff4f887fab0c1c0bb164d8635b32c894bc1faf4c618dd89df2"}, - {file = "pillow-10.2.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cb28c753fd5eb3dd859b4ee95de66cc62af91bcff5db5f2571d32a520baf1f04"}, - {file = "pillow-10.2.0-pp310-pypy310_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:33870dc4653c5017bf4c8873e5488d8f8d5f8935e2f1fb9a2208c47cdd66efd2"}, - {file = "pillow-10.2.0-pp310-pypy310_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:3c31822339516fb3c82d03f30e22b1d038da87ef27b6a78c9549888f8ceda39a"}, - {file = "pillow-10.2.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:a2b56ba36e05f973d450582fb015594aaa78834fefe8dfb8fcd79b93e64ba4c6"}, - {file = "pillow-10.2.0-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:d8e6aeb9201e655354b3ad049cb77d19813ad4ece0df1249d3c793de3774f8c7"}, - {file = "pillow-10.2.0-pp39-pypy39_pp73-macosx_10_10_x86_64.whl", hash = "sha256:2247178effb34a77c11c0e8ac355c7a741ceca0a732b27bf11e747bbc950722f"}, - {file = "pillow-10.2.0-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:15587643b9e5eb26c48e49a7b33659790d28f190fc514a322d55da2fb5c2950e"}, - {file = "pillow-10.2.0-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:753cd8f2086b2b80180d9b3010dd4ed147efc167c90d3bf593fe2af21265e5a5"}, - {file = "pillow-10.2.0-pp39-pypy39_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:7c8f97e8e7a9009bcacbe3766a36175056c12f9a44e6e6f2d5caad06dcfbf03b"}, - {file = "pillow-10.2.0-pp39-pypy39_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:d1b35bcd6c5543b9cb547dee3150c93008f8dd0f1fef78fc0cd2b141c5baf58a"}, - {file = "pillow-10.2.0-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:fe4c15f6c9285dc54ce6553a3ce908ed37c8f3825b5a51a15c91442bb955b868"}, - {file = "pillow-10.2.0.tar.gz", hash = "sha256:e87f0b2c78157e12d7686b27d63c070fd65d994e8ddae6f328e0dcf4a0cd007e"}, + {file = "pillow-10.3.0-cp310-cp310-macosx_10_10_x86_64.whl", hash = "sha256:90b9e29824800e90c84e4022dd5cc16eb2d9605ee13f05d47641eb183cd73d45"}, + {file = "pillow-10.3.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:a2c405445c79c3f5a124573a051062300936b0281fee57637e706453e452746c"}, + {file = "pillow-10.3.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:78618cdbccaa74d3f88d0ad6cb8ac3007f1a6fa5c6f19af64b55ca170bfa1edf"}, + {file = "pillow-10.3.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:261ddb7ca91fcf71757979534fb4c128448b5b4c55cb6152d280312062f69599"}, + {file = "pillow-10.3.0-cp310-cp310-manylinux_2_28_aarch64.whl", hash = "sha256:ce49c67f4ea0609933d01c0731b34b8695a7a748d6c8d186f95e7d085d2fe475"}, + {file = "pillow-10.3.0-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:b14f16f94cbc61215115b9b1236f9c18403c15dd3c52cf629072afa9d54c1cbf"}, + {file = "pillow-10.3.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:d33891be6df59d93df4d846640f0e46f1a807339f09e79a8040bc887bdcd7ed3"}, + {file = "pillow-10.3.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:b50811d664d392f02f7761621303eba9d1b056fb1868c8cdf4231279645c25f5"}, + {file = "pillow-10.3.0-cp310-cp310-win32.whl", hash = "sha256:ca2870d5d10d8726a27396d3ca4cf7976cec0f3cb706debe88e3a5bd4610f7d2"}, + {file = "pillow-10.3.0-cp310-cp310-win_amd64.whl", hash = "sha256:f0d0591a0aeaefdaf9a5e545e7485f89910c977087e7de2b6c388aec32011e9f"}, + {file = "pillow-10.3.0-cp310-cp310-win_arm64.whl", hash = "sha256:ccce24b7ad89adb5a1e34a6ba96ac2530046763912806ad4c247356a8f33a67b"}, + {file = "pillow-10.3.0-cp311-cp311-macosx_10_10_x86_64.whl", hash = "sha256:5f77cf66e96ae734717d341c145c5949c63180842a545c47a0ce7ae52ca83795"}, + {file = "pillow-10.3.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:e4b878386c4bf293578b48fc570b84ecfe477d3b77ba39a6e87150af77f40c57"}, + {file = "pillow-10.3.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fdcbb4068117dfd9ce0138d068ac512843c52295ed996ae6dd1faf537b6dbc27"}, + {file = "pillow-10.3.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9797a6c8fe16f25749b371c02e2ade0efb51155e767a971c61734b1bf6293994"}, + {file = "pillow-10.3.0-cp311-cp311-manylinux_2_28_aarch64.whl", hash = "sha256:9e91179a242bbc99be65e139e30690e081fe6cb91a8e77faf4c409653de39451"}, + {file = "pillow-10.3.0-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:1b87bd9d81d179bd8ab871603bd80d8645729939f90b71e62914e816a76fc6bd"}, + {file = "pillow-10.3.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:81d09caa7b27ef4e61cb7d8fbf1714f5aec1c6b6c5270ee53504981e6e9121ad"}, + {file = "pillow-10.3.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:048ad577748b9fa4a99a0548c64f2cb8d672d5bf2e643a739ac8faff1164238c"}, + {file = "pillow-10.3.0-cp311-cp311-win32.whl", hash = "sha256:7161ec49ef0800947dc5570f86568a7bb36fa97dd09e9827dc02b718c5643f09"}, + {file = "pillow-10.3.0-cp311-cp311-win_amd64.whl", hash = "sha256:8eb0908e954d093b02a543dc963984d6e99ad2b5e36503d8a0aaf040505f747d"}, + {file = "pillow-10.3.0-cp311-cp311-win_arm64.whl", hash = "sha256:4e6f7d1c414191c1199f8996d3f2282b9ebea0945693fb67392c75a3a320941f"}, + {file = "pillow-10.3.0-cp312-cp312-macosx_10_10_x86_64.whl", hash = "sha256:e46f38133e5a060d46bd630faa4d9fa0202377495df1f068a8299fd78c84de84"}, + {file = "pillow-10.3.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:50b8eae8f7334ec826d6eeffaeeb00e36b5e24aa0b9df322c247539714c6df19"}, + {file = "pillow-10.3.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9d3bea1c75f8c53ee4d505c3e67d8c158ad4df0d83170605b50b64025917f338"}, + {file = "pillow-10.3.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:19aeb96d43902f0a783946a0a87dbdad5c84c936025b8419da0a0cd7724356b1"}, + {file = "pillow-10.3.0-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:74d28c17412d9caa1066f7a31df8403ec23d5268ba46cd0ad2c50fb82ae40462"}, + {file = "pillow-10.3.0-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:ff61bfd9253c3915e6d41c651d5f962da23eda633cf02262990094a18a55371a"}, + {file = "pillow-10.3.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:d886f5d353333b4771d21267c7ecc75b710f1a73d72d03ca06df49b09015a9ef"}, + {file = "pillow-10.3.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:4b5ec25d8b17217d635f8935dbc1b9aa5907962fae29dff220f2659487891cd3"}, + {file = "pillow-10.3.0-cp312-cp312-win32.whl", hash = "sha256:51243f1ed5161b9945011a7360e997729776f6e5d7005ba0c6879267d4c5139d"}, + {file = "pillow-10.3.0-cp312-cp312-win_amd64.whl", hash = "sha256:412444afb8c4c7a6cc11a47dade32982439925537e483be7c0ae0cf96c4f6a0b"}, + {file = "pillow-10.3.0-cp312-cp312-win_arm64.whl", hash = "sha256:798232c92e7665fe82ac085f9d8e8ca98826f8e27859d9a96b41d519ecd2e49a"}, + {file = "pillow-10.3.0-cp38-cp38-macosx_10_10_x86_64.whl", hash = "sha256:4eaa22f0d22b1a7e93ff0a596d57fdede2e550aecffb5a1ef1106aaece48e96b"}, + {file = "pillow-10.3.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:cd5e14fbf22a87321b24c88669aad3a51ec052eb145315b3da3b7e3cc105b9a2"}, + {file = "pillow-10.3.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1530e8f3a4b965eb6a7785cf17a426c779333eb62c9a7d1bbcf3ffd5bf77a4aa"}, + {file = "pillow-10.3.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5d512aafa1d32efa014fa041d38868fda85028e3f930a96f85d49c7d8ddc0383"}, + {file = "pillow-10.3.0-cp38-cp38-manylinux_2_28_aarch64.whl", hash = "sha256:339894035d0ede518b16073bdc2feef4c991ee991a29774b33e515f1d308e08d"}, + {file = "pillow-10.3.0-cp38-cp38-manylinux_2_28_x86_64.whl", hash = "sha256:aa7e402ce11f0885305bfb6afb3434b3cd8f53b563ac065452d9d5654c7b86fd"}, + {file = "pillow-10.3.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:0ea2a783a2bdf2a561808fe4a7a12e9aa3799b701ba305de596bc48b8bdfce9d"}, + {file = "pillow-10.3.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:c78e1b00a87ce43bb37642c0812315b411e856a905d58d597750eb79802aaaa3"}, + {file = "pillow-10.3.0-cp38-cp38-win32.whl", hash = "sha256:72d622d262e463dfb7595202d229f5f3ab4b852289a1cd09650362db23b9eb0b"}, + {file = "pillow-10.3.0-cp38-cp38-win_amd64.whl", hash = "sha256:2034f6759a722da3a3dbd91a81148cf884e91d1b747992ca288ab88c1de15999"}, + {file = "pillow-10.3.0-cp39-cp39-macosx_10_10_x86_64.whl", hash = "sha256:2ed854e716a89b1afcedea551cd85f2eb2a807613752ab997b9974aaa0d56936"}, + {file = "pillow-10.3.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:dc1a390a82755a8c26c9964d457d4c9cbec5405896cba94cf51f36ea0d855002"}, + {file = "pillow-10.3.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4203efca580f0dd6f882ca211f923168548f7ba334c189e9eab1178ab840bf60"}, + {file = "pillow-10.3.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3102045a10945173d38336f6e71a8dc71bcaeed55c3123ad4af82c52807b9375"}, + {file = "pillow-10.3.0-cp39-cp39-manylinux_2_28_aarch64.whl", hash = "sha256:6fb1b30043271ec92dc65f6d9f0b7a830c210b8a96423074b15c7bc999975f57"}, + {file = "pillow-10.3.0-cp39-cp39-manylinux_2_28_x86_64.whl", hash = "sha256:1dfc94946bc60ea375cc39cff0b8da6c7e5f8fcdc1d946beb8da5c216156ddd8"}, + {file = "pillow-10.3.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:b09b86b27a064c9624d0a6c54da01c1beaf5b6cadfa609cf63789b1d08a797b9"}, + {file = "pillow-10.3.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:d3b2348a78bc939b4fed6552abfd2e7988e0f81443ef3911a4b8498ca084f6eb"}, + {file = "pillow-10.3.0-cp39-cp39-win32.whl", hash = "sha256:45ebc7b45406febf07fef35d856f0293a92e7417ae7933207e90bf9090b70572"}, + {file = "pillow-10.3.0-cp39-cp39-win_amd64.whl", hash = "sha256:0ba26351b137ca4e0db0342d5d00d2e355eb29372c05afd544ebf47c0956ffeb"}, + {file = "pillow-10.3.0-cp39-cp39-win_arm64.whl", hash = "sha256:50fd3f6b26e3441ae07b7c979309638b72abc1a25da31a81a7fbd9495713ef4f"}, + {file = "pillow-10.3.0-pp310-pypy310_pp73-macosx_10_10_x86_64.whl", hash = "sha256:6b02471b72526ab8a18c39cb7967b72d194ec53c1fd0a70b050565a0f366d355"}, + {file = "pillow-10.3.0-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:8ab74c06ffdab957d7670c2a5a6e1a70181cd10b727cd788c4dd9005b6a8acd9"}, + {file = "pillow-10.3.0-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:048eeade4c33fdf7e08da40ef402e748df113fd0b4584e32c4af74fe78baaeb2"}, + {file = "pillow-10.3.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9e2ec1e921fd07c7cda7962bad283acc2f2a9ccc1b971ee4b216b75fad6f0463"}, + {file = "pillow-10.3.0-pp310-pypy310_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:4c8e73e99da7db1b4cad7f8d682cf6abad7844da39834c288fbfa394a47bbced"}, + {file = "pillow-10.3.0-pp310-pypy310_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:16563993329b79513f59142a6b02055e10514c1a8e86dca8b48a893e33cf91e3"}, + {file = "pillow-10.3.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:dd78700f5788ae180b5ee8902c6aea5a5726bac7c364b202b4b3e3ba2d293170"}, + {file = "pillow-10.3.0-pp39-pypy39_pp73-macosx_10_10_x86_64.whl", hash = "sha256:aff76a55a8aa8364d25400a210a65ff59d0168e0b4285ba6bf2bd83cf675ba32"}, + {file = "pillow-10.3.0-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:b7bc2176354defba3edc2b9a777744462da2f8e921fbaf61e52acb95bafa9828"}, + {file = "pillow-10.3.0-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:793b4e24db2e8742ca6423d3fde8396db336698c55cd34b660663ee9e45ed37f"}, + {file = "pillow-10.3.0-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d93480005693d247f8346bc8ee28c72a2191bdf1f6b5db469c096c0c867ac015"}, + {file = "pillow-10.3.0-pp39-pypy39_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:c83341b89884e2b2e55886e8fbbf37c3fa5efd6c8907124aeb72f285ae5696e5"}, + {file = "pillow-10.3.0-pp39-pypy39_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:1a1d1915db1a4fdb2754b9de292642a39a7fb28f1736699527bb649484fb966a"}, + {file = "pillow-10.3.0-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:a0eaa93d054751ee9964afa21c06247779b90440ca41d184aeb5d410f20ff591"}, + {file = "pillow-10.3.0.tar.gz", hash = "sha256:9d2455fbf44c914840c793e89aa82d0e1763a14253a000743719ae5946814b2d"}, ] [package.extras] @@ -1372,13 +1449,13 @@ test = ["enum34", "ipaddress", "mock", "pywin32", "wmi"] [[package]] name = "pycparser" -version = "2.21" +version = "2.22" description = "C parser in Python" optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" +python-versions = ">=3.8" files = [ - {file = "pycparser-2.21-py2.py3-none-any.whl", hash = "sha256:8ee45429555515e1f6b185e78100aea234072576aa43ab53aefcae078162fca9"}, - {file = "pycparser-2.21.tar.gz", hash = "sha256:e644fdec12f7872f86c58ff790da456218b10f863970249516d60a5eaca77206"}, + {file = "pycparser-2.22-py3-none-any.whl", hash = "sha256:c3702b6d3dd8c7abc1afa565d7e63d53a1d0bd86cdc24edd75470f4de499cfcc"}, + {file = "pycparser-2.22.tar.gz", hash = "sha256:491c8be9c040f5390f5bf44a5b07752bd07f56edf992381b05c701439eec10f6"}, ] [[package]] @@ -1509,71 +1586,71 @@ six = ">=1.7.0" [[package]] name = "ruff" -version = "0.3.4" +version = "0.3.7" description = "An extremely fast Python linter and code formatter, written in Rust." optional = false python-versions = ">=3.7" files = [ - {file = "ruff-0.3.4-py3-none-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:60c870a7d46efcbc8385d27ec07fe534ac32f3b251e4fc44b3cbfd9e09609ef4"}, - {file = "ruff-0.3.4-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:6fc14fa742e1d8f24910e1fff0bd5e26d395b0e0e04cc1b15c7c5e5fe5b4af91"}, - {file = "ruff-0.3.4-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d3ee7880f653cc03749a3bfea720cf2a192e4f884925b0cf7eecce82f0ce5854"}, - {file = "ruff-0.3.4-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:cf133dd744f2470b347f602452a88e70dadfbe0fcfb5fd46e093d55da65f82f7"}, - {file = "ruff-0.3.4-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3f3860057590e810c7ffea75669bdc6927bfd91e29b4baa9258fd48b540a4365"}, - {file = "ruff-0.3.4-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:986f2377f7cf12efac1f515fc1a5b753c000ed1e0a6de96747cdf2da20a1b369"}, - {file = "ruff-0.3.4-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c4fd98e85869603e65f554fdc5cddf0712e352fe6e61d29d5a6fe087ec82b76c"}, - {file = "ruff-0.3.4-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:64abeed785dad51801b423fa51840b1764b35d6c461ea8caef9cf9e5e5ab34d9"}, - {file = "ruff-0.3.4-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:df52972138318bc7546d92348a1ee58449bc3f9eaf0db278906eb511889c4b50"}, - {file = "ruff-0.3.4-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:98e98300056445ba2cc27d0b325fd044dc17fcc38e4e4d2c7711585bd0a958ed"}, - {file = "ruff-0.3.4-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:519cf6a0ebed244dce1dc8aecd3dc99add7a2ee15bb68cf19588bb5bf58e0488"}, - {file = "ruff-0.3.4-py3-none-musllinux_1_2_i686.whl", hash = "sha256:bb0acfb921030d00070539c038cd24bb1df73a2981e9f55942514af8b17be94e"}, - {file = "ruff-0.3.4-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:cf187a7e7098233d0d0c71175375c5162f880126c4c716fa28a8ac418dcf3378"}, - {file = "ruff-0.3.4-py3-none-win32.whl", hash = "sha256:af27ac187c0a331e8ef91d84bf1c3c6a5dea97e912a7560ac0cef25c526a4102"}, - {file = "ruff-0.3.4-py3-none-win_amd64.whl", hash = "sha256:de0d5069b165e5a32b3c6ffbb81c350b1e3d3483347196ffdf86dc0ef9e37dd6"}, - {file = "ruff-0.3.4-py3-none-win_arm64.whl", hash = "sha256:6810563cc08ad0096b57c717bd78aeac888a1bfd38654d9113cb3dc4d3f74232"}, - {file = "ruff-0.3.4.tar.gz", hash = "sha256:f0f4484c6541a99862b693e13a151435a279b271cff20e37101116a21e2a1ad1"}, + {file = "ruff-0.3.7-py3-none-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:0e8377cccb2f07abd25e84fc5b2cbe48eeb0fea9f1719cad7caedb061d70e5ce"}, + {file = "ruff-0.3.7-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:15a4d1cc1e64e556fa0d67bfd388fed416b7f3b26d5d1c3e7d192c897e39ba4b"}, + {file = "ruff-0.3.7-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d28bdf3d7dc71dd46929fafeec98ba89b7c3550c3f0978e36389b5631b793663"}, + {file = "ruff-0.3.7-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:379b67d4f49774ba679593b232dcd90d9e10f04d96e3c8ce4a28037ae473f7bb"}, + {file = "ruff-0.3.7-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c060aea8ad5ef21cdfbbe05475ab5104ce7827b639a78dd55383a6e9895b7c51"}, + {file = "ruff-0.3.7-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:ebf8f615dde968272d70502c083ebf963b6781aacd3079081e03b32adfe4d58a"}, + {file = "ruff-0.3.7-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d48098bd8f5c38897b03604f5428901b65e3c97d40b3952e38637b5404b739a2"}, + {file = "ruff-0.3.7-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:da8a4fda219bf9024692b1bc68c9cff4b80507879ada8769dc7e985755d662ea"}, + {file = "ruff-0.3.7-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6c44e0149f1d8b48c4d5c33d88c677a4aa22fd09b1683d6a7ff55b816b5d074f"}, + {file = "ruff-0.3.7-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:3050ec0af72b709a62ecc2aca941b9cd479a7bf2b36cc4562f0033d688e44fa1"}, + {file = "ruff-0.3.7-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:a29cc38e4c1ab00da18a3f6777f8b50099d73326981bb7d182e54a9a21bb4ff7"}, + {file = "ruff-0.3.7-py3-none-musllinux_1_2_i686.whl", hash = "sha256:5b15cc59c19edca917f51b1956637db47e200b0fc5e6e1878233d3a938384b0b"}, + {file = "ruff-0.3.7-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:e491045781b1e38b72c91247cf4634f040f8d0cb3e6d3d64d38dcf43616650b4"}, + {file = "ruff-0.3.7-py3-none-win32.whl", hash = "sha256:bc931de87593d64fad3a22e201e55ad76271f1d5bfc44e1a1887edd0903c7d9f"}, + {file = "ruff-0.3.7-py3-none-win_amd64.whl", hash = "sha256:5ef0e501e1e39f35e03c2acb1d1238c595b8bb36cf7a170e7c1df1b73da00e74"}, + {file = "ruff-0.3.7-py3-none-win_arm64.whl", hash = "sha256:789e144f6dc7019d1f92a812891c645274ed08af6037d11fc65fcbc183b7d59f"}, + {file = "ruff-0.3.7.tar.gz", hash = "sha256:d5c1aebee5162c2226784800ae031f660c350e7a3402c4d1f8ea4e97e232e3ba"}, ] [[package]] name = "scipy" -version = "1.12.0" +version = "1.13.0" description = "Fundamental algorithms for scientific computing in Python" optional = false python-versions = ">=3.9" files = [ - {file = "scipy-1.12.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:78e4402e140879387187f7f25d91cc592b3501a2e51dfb320f48dfb73565f10b"}, - {file = "scipy-1.12.0-cp310-cp310-macosx_12_0_arm64.whl", hash = "sha256:f5f00ebaf8de24d14b8449981a2842d404152774c1a1d880c901bf454cb8e2a1"}, - {file = "scipy-1.12.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e53958531a7c695ff66c2e7bb7b79560ffdc562e2051644c5576c39ff8efb563"}, - {file = "scipy-1.12.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5e32847e08da8d895ce09d108a494d9eb78974cf6de23063f93306a3e419960c"}, - {file = "scipy-1.12.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:4c1020cad92772bf44b8e4cdabc1df5d87376cb219742549ef69fc9fd86282dd"}, - {file = "scipy-1.12.0-cp310-cp310-win_amd64.whl", hash = "sha256:75ea2a144096b5e39402e2ff53a36fecfd3b960d786b7efd3c180e29c39e53f2"}, - {file = "scipy-1.12.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:408c68423f9de16cb9e602528be4ce0d6312b05001f3de61fe9ec8b1263cad08"}, - {file = "scipy-1.12.0-cp311-cp311-macosx_12_0_arm64.whl", hash = "sha256:5adfad5dbf0163397beb4aca679187d24aec085343755fcdbdeb32b3679f254c"}, - {file = "scipy-1.12.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c3003652496f6e7c387b1cf63f4bb720951cfa18907e998ea551e6de51a04467"}, - {file = "scipy-1.12.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8b8066bce124ee5531d12a74b617d9ac0ea59245246410e19bca549656d9a40a"}, - {file = "scipy-1.12.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:8bee4993817e204d761dba10dbab0774ba5a8612e57e81319ea04d84945375ba"}, - {file = "scipy-1.12.0-cp311-cp311-win_amd64.whl", hash = "sha256:a24024d45ce9a675c1fb8494e8e5244efea1c7a09c60beb1eeb80373d0fecc70"}, - {file = "scipy-1.12.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:e7e76cc48638228212c747ada851ef355c2bb5e7f939e10952bc504c11f4e372"}, - {file = "scipy-1.12.0-cp312-cp312-macosx_12_0_arm64.whl", hash = "sha256:f7ce148dffcd64ade37b2df9315541f9adad6efcaa86866ee7dd5db0c8f041c3"}, - {file = "scipy-1.12.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9c39f92041f490422924dfdb782527a4abddf4707616e07b021de33467f917bc"}, - {file = "scipy-1.12.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a7ebda398f86e56178c2fa94cad15bf457a218a54a35c2a7b4490b9f9cb2676c"}, - {file = "scipy-1.12.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:95e5c750d55cf518c398a8240571b0e0782c2d5a703250872f36eaf737751338"}, - {file = "scipy-1.12.0-cp312-cp312-win_amd64.whl", hash = "sha256:e646d8571804a304e1da01040d21577685ce8e2db08ac58e543eaca063453e1c"}, - {file = "scipy-1.12.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:913d6e7956c3a671de3b05ccb66b11bc293f56bfdef040583a7221d9e22a2e35"}, - {file = "scipy-1.12.0-cp39-cp39-macosx_12_0_arm64.whl", hash = "sha256:bba1b0c7256ad75401c73e4b3cf09d1f176e9bd4248f0d3112170fb2ec4db067"}, - {file = "scipy-1.12.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:730badef9b827b368f351eacae2e82da414e13cf8bd5051b4bdfd720271a5371"}, - {file = "scipy-1.12.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6546dc2c11a9df6926afcbdd8a3edec28566e4e785b915e849348c6dd9f3f490"}, - {file = "scipy-1.12.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:196ebad3a4882081f62a5bf4aeb7326aa34b110e533aab23e4374fcccb0890dc"}, - {file = "scipy-1.12.0-cp39-cp39-win_amd64.whl", hash = "sha256:b360f1b6b2f742781299514e99ff560d1fe9bd1bff2712894b52abe528d1fd1e"}, - {file = "scipy-1.12.0.tar.gz", hash = "sha256:4bf5abab8a36d20193c698b0f1fc282c1d083c94723902c447e5d2f1780936a3"}, + {file = "scipy-1.13.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:ba419578ab343a4e0a77c0ef82f088238a93eef141b2b8017e46149776dfad4d"}, + {file = "scipy-1.13.0-cp310-cp310-macosx_12_0_arm64.whl", hash = "sha256:22789b56a999265431c417d462e5b7f2b487e831ca7bef5edeb56efe4c93f86e"}, + {file = "scipy-1.13.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:05f1432ba070e90d42d7fd836462c50bf98bd08bed0aa616c359eed8a04e3922"}, + {file = "scipy-1.13.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b8434f6f3fa49f631fae84afee424e2483289dfc30a47755b4b4e6b07b2633a4"}, + {file = "scipy-1.13.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:dcbb9ea49b0167de4167c40eeee6e167caeef11effb0670b554d10b1e693a8b9"}, + {file = "scipy-1.13.0-cp310-cp310-win_amd64.whl", hash = "sha256:1d2f7bb14c178f8b13ebae93f67e42b0a6b0fc50eba1cd8021c9b6e08e8fb1cd"}, + {file = "scipy-1.13.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:0fbcf8abaf5aa2dc8d6400566c1a727aed338b5fe880cde64907596a89d576fa"}, + {file = "scipy-1.13.0-cp311-cp311-macosx_12_0_arm64.whl", hash = "sha256:5e4a756355522eb60fcd61f8372ac2549073c8788f6114449b37e9e8104f15a5"}, + {file = "scipy-1.13.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b5acd8e1dbd8dbe38d0004b1497019b2dbbc3d70691e65d69615f8a7292865d7"}, + {file = "scipy-1.13.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9ff7dad5d24a8045d836671e082a490848e8639cabb3dbdacb29f943a678683d"}, + {file = "scipy-1.13.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:4dca18c3ffee287ddd3bc8f1dabaf45f5305c5afc9f8ab9cbfab855e70b2df5c"}, + {file = "scipy-1.13.0-cp311-cp311-win_amd64.whl", hash = "sha256:a2f471de4d01200718b2b8927f7d76b5d9bde18047ea0fa8bd15c5ba3f26a1d6"}, + {file = "scipy-1.13.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:d0de696f589681c2802f9090fff730c218f7c51ff49bf252b6a97ec4a5d19e8b"}, + {file = "scipy-1.13.0-cp312-cp312-macosx_12_0_arm64.whl", hash = "sha256:b2a3ff461ec4756b7e8e42e1c681077349a038f0686132d623fa404c0bee2551"}, + {file = "scipy-1.13.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6bf9fe63e7a4bf01d3645b13ff2aa6dea023d38993f42aaac81a18b1bda7a82a"}, + {file = "scipy-1.13.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1e7626dfd91cdea5714f343ce1176b6c4745155d234f1033584154f60ef1ff42"}, + {file = "scipy-1.13.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:109d391d720fcebf2fbe008621952b08e52907cf4c8c7efc7376822151820820"}, + {file = "scipy-1.13.0-cp312-cp312-win_amd64.whl", hash = "sha256:8930ae3ea371d6b91c203b1032b9600d69c568e537b7988a3073dfe4d4774f21"}, + {file = "scipy-1.13.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:5407708195cb38d70fd2d6bb04b1b9dd5c92297d86e9f9daae1576bd9e06f602"}, + {file = "scipy-1.13.0-cp39-cp39-macosx_12_0_arm64.whl", hash = "sha256:ac38c4c92951ac0f729c4c48c9e13eb3675d9986cc0c83943784d7390d540c78"}, + {file = "scipy-1.13.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:09c74543c4fbeb67af6ce457f6a6a28e5d3739a87f62412e4a16e46f164f0ae5"}, + {file = "scipy-1.13.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:28e286bf9ac422d6beb559bc61312c348ca9b0f0dae0d7c5afde7f722d6ea13d"}, + {file = "scipy-1.13.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:33fde20efc380bd23a78a4d26d59fc8704e9b5fd9b08841693eb46716ba13d86"}, + {file = "scipy-1.13.0-cp39-cp39-win_amd64.whl", hash = "sha256:45c08bec71d3546d606989ba6e7daa6f0992918171e2a6f7fbedfa7361c2de1e"}, + {file = "scipy-1.13.0.tar.gz", hash = "sha256:58569af537ea29d3f78e5abd18398459f195546bb3be23d16677fb26616cc11e"}, ] [package.dependencies] -numpy = ">=1.22.4,<1.29.0" +numpy = ">=1.22.4,<2.3" [package.extras] -dev = ["click", "cython-lint (>=0.12.2)", "doit (>=0.36.0)", "mypy", "pycodestyle", "pydevtool", "rich-click", "ruff", "types-psutil", "typing_extensions"] -doc = ["jupytext", "matplotlib (>2)", "myst-nb", "numpydoc", "pooch", "pydata-sphinx-theme (==0.9.0)", "sphinx (!=4.1.0)", "sphinx-design (>=0.2.0)"] -test = ["asv", "gmpy2", "hypothesis", "mpmath", "pooch", "pytest", "pytest-cov", "pytest-timeout", "pytest-xdist", "scikit-umfpack", "threadpoolctl"] +dev = ["cython-lint (>=0.12.2)", "doit (>=0.36.0)", "mypy", "pycodestyle", "pydevtool", "rich-click", "ruff", "types-psutil", "typing_extensions"] +doc = ["jupyterlite-pyodide-kernel", "jupyterlite-sphinx (>=0.12.0)", "jupytext", "matplotlib (>=3.5)", "myst-nb", "numpydoc", "pooch", "pydata-sphinx-theme (>=0.15.2)", "sphinx (>=5.0.0)", "sphinx-design (>=0.4.0)"] +test = ["array-api-strict", "asv", "gmpy2", "hypothesis (>=6.30)", "mpmath", "pooch", "pytest", "pytest-cov", "pytest-timeout", "pytest-xdist", "scikit-umfpack", "threadpoolctl"] [[package]] name = "selenium" @@ -1749,13 +1826,13 @@ wsproto = ">=0.14" [[package]] name = "typing-extensions" -version = "4.10.0" +version = "4.11.0" description = "Backported and Experimental Type Hints for Python 3.8+" optional = false python-versions = ">=3.8" files = [ - {file = "typing_extensions-4.10.0-py3-none-any.whl", hash = "sha256:69b1a937c3a517342112fb4c6df7e72fc39a38e7891a5730ed4985b5214b5475"}, - {file = "typing_extensions-4.10.0.tar.gz", hash = "sha256:b0abd7c89e8fb96f98db18d86106ff1d90ab692004eb746cf6eda2682f91b3cb"}, + {file = "typing_extensions-4.11.0-py3-none-any.whl", hash = "sha256:c1f94d72897edaf4ce775bb7558d5b79d8126906a14ea5ed1635921406c0387a"}, + {file = "typing_extensions-4.11.0.tar.gz", hash = "sha256:83f085bd5ca59c80295fc2a82ab5dac679cbe02b9f33f7d83af68e241bea51b0"}, ] [[package]] @@ -1821,13 +1898,13 @@ testing = ["coverage (>=5.0)", "pytest", "pytest-cov"] [[package]] name = "werkzeug" -version = "3.0.1" +version = "3.0.2" description = "The comprehensive WSGI web application library." optional = false python-versions = ">=3.8" files = [ - {file = "werkzeug-3.0.1-py3-none-any.whl", hash = "sha256:90a285dc0e42ad56b34e696398b8122ee4c681833fb35b8334a095d82c56da10"}, - {file = "werkzeug-3.0.1.tar.gz", hash = "sha256:507e811ecea72b18a404947aded4b3390e1db8f826b494d76550ef45bb3b1dcc"}, + {file = "werkzeug-3.0.2-py3-none-any.whl", hash = "sha256:3aac3f5da756f93030740bc235d3e09449efcf65f2f55e3602e1d851b8f48795"}, + {file = "werkzeug-3.0.2.tar.gz", hash = "sha256:e39b645a6ac92822588e7b39a692e7828724ceae0b0d702ef96701f90e70128d"}, ] [package.dependencies] From 96cac8740f5d2e2840d33c25415f831bde7d008c Mon Sep 17 00:00:00 2001 From: Walter Baccinelli Date: Mon, 15 Apr 2024 13:53:16 +0200 Subject: [PATCH 06/16] save results with button --- eit_dash/callbacks/preprocessing_callbacks.py | 198 +++++++++++------- eit_dash/definitions/element_ids.py | 3 + eit_dash/pages/preprocessing.py | 46 ++-- eit_dash/utils/common.py | 7 +- eit_dash/utils/data_singleton.py | 7 +- 5 files changed, 161 insertions(+), 100 deletions(-) diff --git a/eit_dash/callbacks/preprocessing_callbacks.py b/eit_dash/callbacks/preprocessing_callbacks.py index 67d7717..051cfc5 100644 --- a/eit_dash/callbacks/preprocessing_callbacks.py +++ b/eit_dash/callbacks/preprocessing_callbacks.py @@ -1,11 +1,15 @@ +from __future__ import annotations + +import contextlib import time +from typing import TYPE_CHECKING import dash_bootstrap_components as dbc +import plotly.graph_objects as go from dash import ALL, Input, Output, State, callback, ctx, dcc, html -from eitprocessing.datahandling.continuousdata import ContinuousData from dash.exceptions import PreventUpdate -from eitprocessing.datahandling.sequence import Sequence -from eitprocessing.filters.butterworth_filters import ButterworthFilter, FILTER_TYPES +from eitprocessing.datahandling.continuousdata import ContinuousData +from eitprocessing.filters.butterworth_filters import ButterworthFilter import eit_dash.definitions.element_ids as ids import eit_dash.definitions.layout_styles as styles @@ -17,11 +21,12 @@ get_signal_options, mark_selected_periods, ) +from eit_dash.utils.data_singleton import LoadedData -import plotly.graph_objects as go - -from eit_dash.utils.data_singleton import Period +if TYPE_CHECKING: + from eitprocessing.datahandling.sequence import Sequence +tmp_results: LoadedData = LoadedData() # ruff: noqa: D103 #TODO remove this line when finalizing this module @@ -56,10 +61,7 @@ def create_resampling_card(loaded_data): for data in loaded_data ] - options = [ - {"label": f'{data["Name"]}', "value": str(i)} - for i, data in enumerate(loaded_data) - ] + options = [{"label": f'{data["Name"]}', "value": str(i)} for i, data in enumerate(loaded_data)] return row, options @@ -67,10 +69,7 @@ def create_resampling_card(loaded_data): def create_loaded_data_summary(): loaded_data = data_object.get_all_sequences() - return [ - dbc.Row([html.Div(f"Loaded {dataset.label}", style={"textAlign": "left"})]) - for dataset in loaded_data - ] + return [dbc.Row([html.Div(f"Loaded {dataset.label}", style={"textAlign": "left"})]) for dataset in loaded_data] def create_selected_period_card(period: Sequence, dataset: str, index: int) -> dbc.Card: @@ -92,10 +91,7 @@ def create_selected_period_card(period: Sequence, dataset: str, index: int) -> d card_list = [ html.H4(period.label, className="card-title"), ] - card_list += [ - dbc.Row(f"{data}: {value}", style=styles.INFO_CARD) - for data, value in info_data.items() - ] + card_list += [dbc.Row(f"{data}: {value}", style=styles.INFO_CARD) for data, value in info_data.items()] card_list += [ dbc.Button( "Remove", @@ -109,16 +105,30 @@ def create_selected_period_card(period: Sequence, dataset: str, index: int) -> d ) +def create_filter_results_card(parameters: dict) -> dbc.Card: + """ + Create the card with the information on the parameters used for filtering the data. + + Args: + parameters: dictionary containing the filter information + """ + card_list = [ + html.H4("Data filtered", className="card-title"), + ] + card_list += [dbc.Row(f"{data}: {value}", style=styles.INFO_CARD) for data, value in parameters.items()] + + return dbc.Card( + dbc.CardBody(card_list), + ) + + def get_loaded_data(): loaded_data = data_object.get_all_sequences() data = [] for dataset in loaded_data: name = dataset.label if dataset.continuous_data: - data += [ - {"Name": name, "Data type": channel} - for channel in dataset.continuous_data - ] + data += [{"Name": name, "Data type": channel} for channel in dataset.continuous_data] if dataset.eit_data: data.append( { @@ -253,7 +263,7 @@ def open_periods_modal(open_click, confirm_click) -> bool: Output(ids.FILTERING_SELECTION_POPUP, "is_open"), [ Input(ids.OPEN_FILTER_DATA_BUTTON, "n_clicks"), - Input(ids.FILTERING_CONFIRM_BUTTON, "n_clicks"), + Input(ids.FILTERING_CLOSE_BUTTON, "n_clicks"), ], prevent_initial_call=True, ) @@ -278,10 +288,7 @@ def populate_periods_selection_modal(method): if int_value == PeriodsSelectMethods.Manual.value: signals = data_object.get_all_sequences() - options = [ - {"label": sequence.label, "value": index} - for index, sequence in enumerate(signals) - ] + options = [{"label": sequence.label, "value": index} for index, sequence in enumerate(signals)] body = [ html.H6("Select one dataset"), @@ -425,7 +432,8 @@ def select_period( # TODO: explore Patch https://dash.plotly.com/partial-properties current_figure = mark_selected_periods( - current_figure, [data_object.get_stable_period(period_index)] + current_figure, + [data_object.get_stable_period(period_index)], ) # TODO: refactor to avoid duplications @@ -510,12 +518,14 @@ def remove_period(n_clicks, container, figure): # remove from the singleton data_object.remove_stable_period(int(input_id)) - # remove from the figure - figure["data"] = [ - trace - for trace in figure["data"] - if "meta" not in trace or trace["meta"]["uid"] != int(input_id) - ] + # remove from the figure (if the figure exists) + + try: + figure["data"] = [ + trace for trace in figure["data"] if "meta" not in trace or trace["meta"]["uid"] != int(input_id) + ] + except TypeError: + contextlib.suppress(Exception) results = [card for card in container if f"'index': '{input_id}'" not in str(card)] @@ -576,17 +586,20 @@ def show_filters_params(selected): prevent_initial_call=True, ) def enable_apply_button( - co_low_in, co_high_in, order_in, co_low, co_high, order, filter_selected + co_low_in, + co_high_in, + order_in, + co_low, + co_high, + order, + filter_selected, ): """Enable the apply button.""" if ( (int(filter_selected) == FilterTypes.lowpass.value and co_high and co_high > 0) + or (int(filter_selected) == FilterTypes.highpass.value and co_low and co_low > 0) or ( - int(filter_selected) == FilterTypes.highpass.value and co_low and co_low > 0 - ) - or ( - int(filter_selected) - in [FilterTypes.bandpass.value, FilterTypes.bandstop.value] + int(filter_selected) in [FilterTypes.bandpass.value, FilterTypes.bandstop.value] and co_low > 0 and co_low > 0 ) @@ -600,6 +613,7 @@ def enable_apply_button( [ Output(ids.PREPROCESING_RESULTS_CONTAINER, "children", allow_duplicate=True), Output(ids.FILTERING_RESULTS_DIV, "hidden"), + Output(ids.FILTERING_CONFIRM_DIV, "hidden"), Output(ids.FILTERING_SELCET_PERIOD_VIEW, "options"), Output(ids.ALERT_FILTER, "is_open"), Output(ids.ALERT_FILTER, "children"), @@ -618,12 +632,13 @@ def enable_apply_button( ) def apply_filter(_, co_low, co_high, order, filter_selected, results): """Apply the filter.""" + global tmp_results # noqa: PLW0602 # flag for the alert message show_alert = False # alert message alert_msg = "" - # flag for showing graphs + # flag for showing graphs and confirm button hidden_div = False # build filter params @@ -631,25 +646,32 @@ def apply_filter(_, co_low, co_high, order, filter_selected, results): options = [] + tmp_results.clear_data() + # filter all the periods - for period in data_object.get_all_stable_periods(): - try: + try: + for period in data_object.get_all_stable_periods(): filtered_data = filter_data(period.get_data(), filter_params) - period.update_data(filtered_data) + data = period.get_data() + data.continuous_data.add(filtered_data) + tmp_results.add_stable_period( + data, + 0, + period.get_period_index(), + ) options.append( { "label": f"Period {period.get_period_index()}", "value": period.get_period_index(), - } + }, ) - except Exception as e: - show_alert = True - alert_msg = f"{e}" - hidden_div = True - break + except ValueError as e: + show_alert = True + alert_msg = f"{e}" + hidden_div = True - return results, hidden_div, options, show_alert, alert_msg + return results, hidden_div, hidden_div, options, show_alert, alert_msg @callback( @@ -675,20 +697,51 @@ def show_filtered_results(selected, _): x=data.eit_data.data["raw"].time, y=data.eit_data.data["raw"].global_impedance, name="Original signal", - ) + ), ) + filtered_data = tmp_results.get_stable_period(int(selected)).get_data() + fig.add_trace( go.Scatter( - x=data.continuous_data.data["global_impedance_filtered"].time, - y=data.continuous_data.data["global_impedance_filtered"].values, + x=filtered_data.continuous_data.data["global_impedance_filtered"].time, + y=filtered_data.continuous_data.data["global_impedance_filtered"].values, name="Filtered signal", - ) + ), ) return fig, styles.GRAPH +@callback( + [ + Output(ids.PREPROCESING_RESULTS_CONTAINER, "children", allow_duplicate=True), + Output(ids.ALERT_SAVED_RESULTS, "is_open"), + Output(ids.ALERT_SAVED_RESULTS, "children"), + ], + Input(ids.FILTERING_CONFIRM_BUTTON, "n_clicks"), + State(ids.PREPROCESING_RESULTS_CONTAINER, "children"), + prevent_initial_call=True, +) +def save_filtered_signal(confirm, results): + """When clocking the confirm button, store the results in the singleton.""" + params = {} + + # save the filtered data + for res in tmp_results.get_all_stable_periods(): + data = data_object.get_stable_period(res.get_period_index()) + tmp_data = res.get_data() + data.update_data(tmp_data) + + if not params: + params = tmp_data.continuous_data.data["global_impedance_filtered"].parameters + + # show info card + results += [create_filter_results_card(params)] + + return results, True, "Results have been saved" + + def get_selected_parameters(co_high, co_low, order, filter_selected) -> dict: """Build the parameters dictionary for the filter. @@ -696,7 +749,7 @@ def get_selected_parameters(co_high, co_low, order, filter_selected) -> dict: co_high: cut off upper limit co_low: cut off lower limit order: filter order - filter_selected: vlue coming from the filter selection dropbox + filter_selected: value coming from the filter selection dropbox Returns: dictionary containing parameters for the filter """ @@ -707,14 +760,14 @@ def get_selected_parameters(co_high, co_low, order, filter_selected) -> dict: else: cutoff_frequency = [co_low, co_high] - return dict( - filter_type=FilterTypes(int(filter_selected)).name, - cutoff_frequency=cutoff_frequency, - order=order, - ) + return { + "filter_type": FilterTypes(int(filter_selected)).name, + "cutoff_frequency": cutoff_frequency, + "order": order, + } -def filter_data(data: Sequence, filter_params: dict) -> Sequence | None: +def filter_data(data: Sequence, filter_params: dict) -> ContinuousData | None: """Filter the impedance data in a period. Args: @@ -723,21 +776,16 @@ def filter_data(data: Sequence, filter_params: dict) -> Sequence | None: Returns: the data with the filtered version added """ - filter_params["sample_frequency"] = data.eit_data.data["raw"].framerate filt = ButterworthFilter(**filter_params) - data.continuous_data.add( - ContinuousData( - "global_impedance_filtered", - "global_impedance filtered with " f"{filter_params['filter_type']}", - "a.u.", - "impedance", - parameters=filter_params, - time=data.eit_data.data["raw"].time, - values=filt.apply_filter(data.eit_data.data["raw"].global_impedance), - ), + return ContinuousData( + "global_impedance_filtered", + f"global_impedance filtered with {filter_params['filter_type']}", + "a.u.", + "impedance", + parameters=filter_params, + time=data.eit_data.data["raw"].time, + values=filt.apply_filter(data.eit_data.data["raw"].global_impedance), ) - - return data diff --git a/eit_dash/definitions/element_ids.py b/eit_dash/definitions/element_ids.py index 633508c..7545a3e 100644 --- a/eit_dash/definitions/element_ids.py +++ b/eit_dash/definitions/element_ids.py @@ -20,6 +20,7 @@ # preprocessing page ALERT_FILTER = "alert-filter" +ALERT_SAVED_RESULTS = "alert-saved-results" CONFIRM_RESAMPLING_BUTTON = "confirm-resampling-button" CONFIRM_SYNCH_BUTTON = "confirm-synch-button" DATASET_SELECTION_CHECKBOX = "dataset-selection-checkbox" @@ -27,7 +28,9 @@ FILTER_ORDER = "filter-order" FILTER_CUTOFF_LOW = "filter-cutoff-low" FILTER_CUTOFF_HIGH = "filter-cutoff-high" +FILTERING_CLOSE_BUTTON = "filtering-close-button" FILTERING_CONFIRM_BUTTON = "filtering-confirm-button" +FILTERING_CONFIRM_DIV = "filtering-confirm-div" FILTER_PARAMS = "filter-params" FILTERING_SELECTION_POPUP = "filtering-selection-popup" FILTER_SELECTOR = "filter-selector" diff --git a/eit_dash/pages/preprocessing.py b/eit_dash/pages/preprocessing.py index dbe0e3c..1949f9a 100644 --- a/eit_dash/pages/preprocessing.py +++ b/eit_dash/pages/preprocessing.py @@ -100,10 +100,7 @@ [ dbc.Select( id=ids.SYNC_METHOD_SELECTOR, - options=[ - {"label": method.name, "value": method.value} - for method in SynchMethods - ], + options=[{"label": method.name, "value": method.value} for method in SynchMethods], value=str(SynchMethods.manual.value), ), html.P(), @@ -178,10 +175,7 @@ html.H6("Periods selection method"), dbc.Select( id=ids.PERIODS_METHOD_SELECTOR, - options=[ - {"label": method.name, "value": method.value} - for method in PeriodsSelectMethods - ], + options=[{"label": method.name, "value": method.value} for method in PeriodsSelectMethods], ), modal_selection_body, ], @@ -214,6 +208,15 @@ duration=3000, ) +alert_saved_results = dbc.Alert( + [], + id=ids.ALERT_SAVED_RESULTS, + color="success", + dismissable=True, + is_open=False, + duration=3000, +) + filter_params = html.Div( [ dbc.Row(alert_filter), @@ -257,15 +260,28 @@ [ html.Div( [ - html.H6("Select a period to view"), + html.H6("Select a period to view the results"), dbc.Select(id=ids.FILTERING_SELCET_PERIOD_VIEW), dcc.Graph( - id=ids.FILTERING_RESULTS_GRAPH, style=styles.EMPTY_ELEMENT + id=ids.FILTERING_RESULTS_GRAPH, + style=styles.EMPTY_ELEMENT, ), ], id=ids.FILTERING_RESULTS_DIV, hidden=True, - ) + ), + ], + style=styles.BUTTONS_ROW, + ), + dbc.Row( + [ + html.Div( + [ + dbc.Button("Confirm", id=ids.FILTERING_CONFIRM_BUTTON), + ], + id=ids.FILTERING_CONFIRM_DIV, + hidden=True, + ), ], style=styles.BUTTONS_ROW, ), @@ -281,13 +297,11 @@ dbc.ModalHeader(dbc.ModalTitle("Filter"), close_button=True), dbc.ModalBody( [ + alert_saved_results, html.H6("Select a filter"), dbc.Select( id=ids.FILTER_SELECTOR, - options=[ - {"label": filt.name, "value": filt.value} - for filt in FilterTypes - ], + options=[{"label": filt.name, "value": filt.value} for filt in FilterTypes], ), html.P(), filter_params, @@ -296,7 +310,7 @@ dbc.ModalFooter( dbc.Button( "Close", - id=ids.FILTERING_CONFIRM_BUTTON, + id=ids.FILTERING_CLOSE_BUTTON, className="ms-auto", n_clicks=0, ), diff --git a/eit_dash/utils/common.py b/eit_dash/utils/common.py index bf9db8e..874f8d0 100644 --- a/eit_dash/utils/common.py +++ b/eit_dash/utils/common.py @@ -2,13 +2,13 @@ from typing import TYPE_CHECKING -from eit_dash.utils.data_singleton import Period - import plotly.graph_objects as go if TYPE_CHECKING: from eitprocessing.sequence import Sequence + from eit_dash.utils.data_singleton import Period + def blank_fig(): """Create an empty figure.""" @@ -103,7 +103,8 @@ def create_slider_figure( def mark_selected_periods( - original_figure: go.Figure | dict, periods: list[Period] + original_figure: go.Figure | dict, + periods: list[Period], ) -> go.Figure: """ Create the figure for the selection of range. diff --git a/eit_dash/utils/data_singleton.py b/eit_dash/utils/data_singleton.py index 56a98fd..50a10dc 100644 --- a/eit_dash/utils/data_singleton.py +++ b/eit_dash/utils/data_singleton.py @@ -129,11 +129,7 @@ def get_dataset_stable_periods(self, dataset_index: int) -> list[Sequence]: msg = f"Index higher than list length {self.get_sequence_list_length()}" raise ValueError(msg) - return [ - period - for period in self._stable_periods - if period.get_dataset_index() == dataset_index - ] + return [period for period in self._stable_periods if period.get_dataset_index() == dataset_index] def get_all_stable_periods(self) -> list[Period]: """Retrieve all the saved stable periods. @@ -203,5 +199,4 @@ def update_data(self, data: Sequence) -> Sequence: Args: data: The sequence with the updated period data """ - self._data = data From 30d6aaab71d080990fb7a702149e6a735ca84f0c Mon Sep 17 00:00:00 2001 From: Walter Baccinelli Date: Tue, 16 Apr 2024 14:17:22 +0200 Subject: [PATCH 07/16] bug fixed --- eit_dash/callbacks/preprocessing_callbacks.py | 82 ++++++++++++++----- eit_dash/definitions/element_ids.py | 1 + 2 files changed, 61 insertions(+), 22 deletions(-) diff --git a/eit_dash/callbacks/preprocessing_callbacks.py b/eit_dash/callbacks/preprocessing_callbacks.py index 051cfc5..c4c8a8c 100644 --- a/eit_dash/callbacks/preprocessing_callbacks.py +++ b/eit_dash/callbacks/preprocessing_callbacks.py @@ -61,7 +61,10 @@ def create_resampling_card(loaded_data): for data in loaded_data ] - options = [{"label": f'{data["Name"]}', "value": str(i)} for i, data in enumerate(loaded_data)] + options = [ + {"label": f'{data["Name"]}', "value": str(i)} + for i, data in enumerate(loaded_data) + ] return row, options @@ -69,7 +72,10 @@ def create_resampling_card(loaded_data): def create_loaded_data_summary(): loaded_data = data_object.get_all_sequences() - return [dbc.Row([html.Div(f"Loaded {dataset.label}", style={"textAlign": "left"})]) for dataset in loaded_data] + return [ + dbc.Row([html.Div(f"Loaded {dataset.label}", style={"textAlign": "left"})]) + for dataset in loaded_data + ] def create_selected_period_card(period: Sequence, dataset: str, index: int) -> dbc.Card: @@ -91,7 +97,10 @@ def create_selected_period_card(period: Sequence, dataset: str, index: int) -> d card_list = [ html.H4(period.label, className="card-title"), ] - card_list += [dbc.Row(f"{data}: {value}", style=styles.INFO_CARD) for data, value in info_data.items()] + card_list += [ + dbc.Row(f"{data}: {value}", style=styles.INFO_CARD) + for data, value in info_data.items() + ] card_list += [ dbc.Button( "Remove", @@ -115,11 +124,12 @@ def create_filter_results_card(parameters: dict) -> dbc.Card: card_list = [ html.H4("Data filtered", className="card-title"), ] - card_list += [dbc.Row(f"{data}: {value}", style=styles.INFO_CARD) for data, value in parameters.items()] + card_list += [ + dbc.Row(f"{data}: {value}", style=styles.INFO_CARD) + for data, value in parameters.items() + ] - return dbc.Card( - dbc.CardBody(card_list), - ) + return dbc.Card(dbc.CardBody(card_list), id=ids.FILTERING_SAVED_CARD) def get_loaded_data(): @@ -128,7 +138,10 @@ def get_loaded_data(): for dataset in loaded_data: name = dataset.label if dataset.continuous_data: - data += [{"Name": name, "Data type": channel} for channel in dataset.continuous_data] + data += [ + {"Name": name, "Data type": channel} + for channel in dataset.continuous_data + ] if dataset.eit_data: data.append( { @@ -288,7 +301,10 @@ def populate_periods_selection_modal(method): if int_value == PeriodsSelectMethods.Manual.value: signals = data_object.get_all_sequences() - options = [{"label": sequence.label, "value": index} for index, sequence in enumerate(signals)] + options = [ + {"label": sequence.label, "value": index} + for index, sequence in enumerate(signals) + ] body = [ html.H6("Select one dataset"), @@ -513,16 +529,21 @@ def remove_period(n_clicks, container, figure): if all(element is None for element in n_clicks): raise PreventUpdate - input_id = ctx.triggered_id["index"] + input_id = int(ctx.triggered_id["index"]) # remove from the singleton - data_object.remove_stable_period(int(input_id)) + data_object.remove_stable_period(input_id) - # remove from the figure (if the figure exists) + # remove from the temp data, if present + if tmp_results and tmp_results.get_stable_period(input_id): + tmp_results.remove_stable_period(input_id) + # remove from the figure (if the figure exists) try: figure["data"] = [ - trace for trace in figure["data"] if "meta" not in trace or trace["meta"]["uid"] != int(input_id) + trace + for trace in figure["data"] + if "meta" not in trace or trace["meta"]["uid"] != input_id ] except TypeError: contextlib.suppress(Exception) @@ -550,24 +571,33 @@ def enable_filter_button(results): Output(ids.FILTER_PARAMS, "hidden"), Output(ids.FILTER_CUTOFF_LOW, "disabled"), Output(ids.FILTER_CUTOFF_HIGH, "disabled"), + Output(ids.FILTER_CUTOFF_LOW, "value"), + Output(ids.FILTER_CUTOFF_HIGH, "value"), ], Input(ids.FILTER_SELECTOR, "value"), prevent_initial_call=True, ) def show_filters_params(selected): """Make visible the div containing the filters params.""" - cutoff_low = cutoff_high = filter_params = False + cutoff_low_enabled = cutoff_high_enabled = filter_params = False + cutoff_low_val = cutoff_high_val = None # if no filter has been selected, hide the params if not selected: filter_params = True if int(selected) == FilterTypes.lowpass.value: - cutoff_low = True + cutoff_low_enabled = True elif int(selected) == FilterTypes.highpass.value: - cutoff_high = True - - return filter_params, cutoff_low, cutoff_high + cutoff_high_enabled = True + + return ( + filter_params, + cutoff_low_enabled, + cutoff_high_enabled, + cutoff_low_val, + cutoff_high_val, + ) @callback( @@ -597,9 +627,12 @@ def enable_apply_button( """Enable the apply button.""" if ( (int(filter_selected) == FilterTypes.lowpass.value and co_high and co_high > 0) - or (int(filter_selected) == FilterTypes.highpass.value and co_low and co_low > 0) or ( - int(filter_selected) in [FilterTypes.bandpass.value, FilterTypes.bandstop.value] + int(filter_selected) == FilterTypes.highpass.value and co_low and co_low > 0 + ) + or ( + int(filter_selected) + in [FilterTypes.bandpass.value, FilterTypes.bandstop.value] and co_low > 0 and co_low > 0 ) @@ -723,7 +756,7 @@ def show_filtered_results(selected, _): State(ids.PREPROCESING_RESULTS_CONTAINER, "children"), prevent_initial_call=True, ) -def save_filtered_signal(confirm, results): +def save_filtered_signal(confirm, results: dict): """When clocking the confirm button, store the results in the singleton.""" params = {} @@ -734,9 +767,14 @@ def save_filtered_signal(confirm, results): data.update_data(tmp_data) if not params: - params = tmp_data.continuous_data.data["global_impedance_filtered"].parameters + params = tmp_data.continuous_data.data[ + "global_impedance_filtered" + ].parameters # show info card + for element in results: + if element["props"]["id"] == ids.FILTERING_SAVED_CARD: + results.remove(element) results += [create_filter_results_card(params)] return results, True, "Results have been saved" diff --git a/eit_dash/definitions/element_ids.py b/eit_dash/definitions/element_ids.py index 7545a3e..ae9f7f1 100644 --- a/eit_dash/definitions/element_ids.py +++ b/eit_dash/definitions/element_ids.py @@ -37,6 +37,7 @@ FILTERING_SELCET_PERIOD_VIEW = "filtering-select-period-view" FILTERING_RESULTS_DIV = "filtering-results-div" FILTERING_RESULTS_GRAPH = "filtering-results-graph" +FILTERING_SAVED_CARD = "filtering-saved-card" SYNCHRONIZATION_POPUP = "synchronization-popup" SYNCHRONIZATION_CONFIRM_BUTTON = "synchronization-confirm-button" SYNC_DATA_PREVIEW_CONTAINER = "sync-data-preview-container" From cdd7c0a845e1fdb61f4faae48c786b962a46f6da Mon Sep 17 00:00:00 2001 From: Walter Baccinelli Date: Tue, 16 Apr 2024 14:19:38 +0200 Subject: [PATCH 08/16] linting --- eit_dash/callbacks/preprocessing_callbacks.py | 47 ++++--------------- 1 file changed, 10 insertions(+), 37 deletions(-) diff --git a/eit_dash/callbacks/preprocessing_callbacks.py b/eit_dash/callbacks/preprocessing_callbacks.py index c4c8a8c..9be4295 100644 --- a/eit_dash/callbacks/preprocessing_callbacks.py +++ b/eit_dash/callbacks/preprocessing_callbacks.py @@ -61,10 +61,7 @@ def create_resampling_card(loaded_data): for data in loaded_data ] - options = [ - {"label": f'{data["Name"]}', "value": str(i)} - for i, data in enumerate(loaded_data) - ] + options = [{"label": f'{data["Name"]}', "value": str(i)} for i, data in enumerate(loaded_data)] return row, options @@ -72,10 +69,7 @@ def create_resampling_card(loaded_data): def create_loaded_data_summary(): loaded_data = data_object.get_all_sequences() - return [ - dbc.Row([html.Div(f"Loaded {dataset.label}", style={"textAlign": "left"})]) - for dataset in loaded_data - ] + return [dbc.Row([html.Div(f"Loaded {dataset.label}", style={"textAlign": "left"})]) for dataset in loaded_data] def create_selected_period_card(period: Sequence, dataset: str, index: int) -> dbc.Card: @@ -97,10 +91,7 @@ def create_selected_period_card(period: Sequence, dataset: str, index: int) -> d card_list = [ html.H4(period.label, className="card-title"), ] - card_list += [ - dbc.Row(f"{data}: {value}", style=styles.INFO_CARD) - for data, value in info_data.items() - ] + card_list += [dbc.Row(f"{data}: {value}", style=styles.INFO_CARD) for data, value in info_data.items()] card_list += [ dbc.Button( "Remove", @@ -124,10 +115,7 @@ def create_filter_results_card(parameters: dict) -> dbc.Card: card_list = [ html.H4("Data filtered", className="card-title"), ] - card_list += [ - dbc.Row(f"{data}: {value}", style=styles.INFO_CARD) - for data, value in parameters.items() - ] + card_list += [dbc.Row(f"{data}: {value}", style=styles.INFO_CARD) for data, value in parameters.items()] return dbc.Card(dbc.CardBody(card_list), id=ids.FILTERING_SAVED_CARD) @@ -138,10 +126,7 @@ def get_loaded_data(): for dataset in loaded_data: name = dataset.label if dataset.continuous_data: - data += [ - {"Name": name, "Data type": channel} - for channel in dataset.continuous_data - ] + data += [{"Name": name, "Data type": channel} for channel in dataset.continuous_data] if dataset.eit_data: data.append( { @@ -301,10 +286,7 @@ def populate_periods_selection_modal(method): if int_value == PeriodsSelectMethods.Manual.value: signals = data_object.get_all_sequences() - options = [ - {"label": sequence.label, "value": index} - for index, sequence in enumerate(signals) - ] + options = [{"label": sequence.label, "value": index} for index, sequence in enumerate(signals)] body = [ html.H6("Select one dataset"), @@ -540,11 +522,7 @@ def remove_period(n_clicks, container, figure): # remove from the figure (if the figure exists) try: - figure["data"] = [ - trace - for trace in figure["data"] - if "meta" not in trace or trace["meta"]["uid"] != input_id - ] + figure["data"] = [trace for trace in figure["data"] if "meta" not in trace or trace["meta"]["uid"] != input_id] except TypeError: contextlib.suppress(Exception) @@ -627,12 +605,9 @@ def enable_apply_button( """Enable the apply button.""" if ( (int(filter_selected) == FilterTypes.lowpass.value and co_high and co_high > 0) + or (int(filter_selected) == FilterTypes.highpass.value and co_low and co_low > 0) or ( - int(filter_selected) == FilterTypes.highpass.value and co_low and co_low > 0 - ) - or ( - int(filter_selected) - in [FilterTypes.bandpass.value, FilterTypes.bandstop.value] + int(filter_selected) in [FilterTypes.bandpass.value, FilterTypes.bandstop.value] and co_low > 0 and co_low > 0 ) @@ -767,9 +742,7 @@ def save_filtered_signal(confirm, results: dict): data.update_data(tmp_data) if not params: - params = tmp_data.continuous_data.data[ - "global_impedance_filtered" - ].parameters + params = tmp_data.continuous_data.data["global_impedance_filtered"].parameters # show info card for element in results: From 56f86c72a192e39417cb296c3c077be534b409bc Mon Sep 17 00:00:00 2001 From: Walter Baccinelli Date: Tue, 16 Apr 2024 14:20:49 +0200 Subject: [PATCH 09/16] type hint change --- eit_dash/callbacks/preprocessing_callbacks.py | 49 ++++++++++++++----- 1 file changed, 38 insertions(+), 11 deletions(-) diff --git a/eit_dash/callbacks/preprocessing_callbacks.py b/eit_dash/callbacks/preprocessing_callbacks.py index 9be4295..b8142d8 100644 --- a/eit_dash/callbacks/preprocessing_callbacks.py +++ b/eit_dash/callbacks/preprocessing_callbacks.py @@ -61,7 +61,10 @@ def create_resampling_card(loaded_data): for data in loaded_data ] - options = [{"label": f'{data["Name"]}', "value": str(i)} for i, data in enumerate(loaded_data)] + options = [ + {"label": f'{data["Name"]}', "value": str(i)} + for i, data in enumerate(loaded_data) + ] return row, options @@ -69,7 +72,10 @@ def create_resampling_card(loaded_data): def create_loaded_data_summary(): loaded_data = data_object.get_all_sequences() - return [dbc.Row([html.Div(f"Loaded {dataset.label}", style={"textAlign": "left"})]) for dataset in loaded_data] + return [ + dbc.Row([html.Div(f"Loaded {dataset.label}", style={"textAlign": "left"})]) + for dataset in loaded_data + ] def create_selected_period_card(period: Sequence, dataset: str, index: int) -> dbc.Card: @@ -91,7 +97,10 @@ def create_selected_period_card(period: Sequence, dataset: str, index: int) -> d card_list = [ html.H4(period.label, className="card-title"), ] - card_list += [dbc.Row(f"{data}: {value}", style=styles.INFO_CARD) for data, value in info_data.items()] + card_list += [ + dbc.Row(f"{data}: {value}", style=styles.INFO_CARD) + for data, value in info_data.items() + ] card_list += [ dbc.Button( "Remove", @@ -115,7 +124,10 @@ def create_filter_results_card(parameters: dict) -> dbc.Card: card_list = [ html.H4("Data filtered", className="card-title"), ] - card_list += [dbc.Row(f"{data}: {value}", style=styles.INFO_CARD) for data, value in parameters.items()] + card_list += [ + dbc.Row(f"{data}: {value}", style=styles.INFO_CARD) + for data, value in parameters.items() + ] return dbc.Card(dbc.CardBody(card_list), id=ids.FILTERING_SAVED_CARD) @@ -126,7 +138,10 @@ def get_loaded_data(): for dataset in loaded_data: name = dataset.label if dataset.continuous_data: - data += [{"Name": name, "Data type": channel} for channel in dataset.continuous_data] + data += [ + {"Name": name, "Data type": channel} + for channel in dataset.continuous_data + ] if dataset.eit_data: data.append( { @@ -286,7 +301,10 @@ def populate_periods_selection_modal(method): if int_value == PeriodsSelectMethods.Manual.value: signals = data_object.get_all_sequences() - options = [{"label": sequence.label, "value": index} for index, sequence in enumerate(signals)] + options = [ + {"label": sequence.label, "value": index} + for index, sequence in enumerate(signals) + ] body = [ html.H6("Select one dataset"), @@ -522,7 +540,11 @@ def remove_period(n_clicks, container, figure): # remove from the figure (if the figure exists) try: - figure["data"] = [trace for trace in figure["data"] if "meta" not in trace or trace["meta"]["uid"] != input_id] + figure["data"] = [ + trace + for trace in figure["data"] + if "meta" not in trace or trace["meta"]["uid"] != input_id + ] except TypeError: contextlib.suppress(Exception) @@ -605,9 +627,12 @@ def enable_apply_button( """Enable the apply button.""" if ( (int(filter_selected) == FilterTypes.lowpass.value and co_high and co_high > 0) - or (int(filter_selected) == FilterTypes.highpass.value and co_low and co_low > 0) or ( - int(filter_selected) in [FilterTypes.bandpass.value, FilterTypes.bandstop.value] + int(filter_selected) == FilterTypes.highpass.value and co_low and co_low > 0 + ) + or ( + int(filter_selected) + in [FilterTypes.bandpass.value, FilterTypes.bandstop.value] and co_low > 0 and co_low > 0 ) @@ -731,7 +756,7 @@ def show_filtered_results(selected, _): State(ids.PREPROCESING_RESULTS_CONTAINER, "children"), prevent_initial_call=True, ) -def save_filtered_signal(confirm, results: dict): +def save_filtered_signal(confirm, results: list): """When clocking the confirm button, store the results in the singleton.""" params = {} @@ -742,7 +767,9 @@ def save_filtered_signal(confirm, results: dict): data.update_data(tmp_data) if not params: - params = tmp_data.continuous_data.data["global_impedance_filtered"].parameters + params = tmp_data.continuous_data.data[ + "global_impedance_filtered" + ].parameters # show info card for element in results: From 991f136940d87fa0783a620dc02a81520af785f2 Mon Sep 17 00:00:00 2001 From: Walter Baccinelli Date: Tue, 16 Apr 2024 14:23:08 +0200 Subject: [PATCH 10/16] linting --- eit_dash/callbacks/preprocessing_callbacks.py | 47 ++++--------------- 1 file changed, 10 insertions(+), 37 deletions(-) diff --git a/eit_dash/callbacks/preprocessing_callbacks.py b/eit_dash/callbacks/preprocessing_callbacks.py index b8142d8..9ecc8c3 100644 --- a/eit_dash/callbacks/preprocessing_callbacks.py +++ b/eit_dash/callbacks/preprocessing_callbacks.py @@ -61,10 +61,7 @@ def create_resampling_card(loaded_data): for data in loaded_data ] - options = [ - {"label": f'{data["Name"]}', "value": str(i)} - for i, data in enumerate(loaded_data) - ] + options = [{"label": f'{data["Name"]}', "value": str(i)} for i, data in enumerate(loaded_data)] return row, options @@ -72,10 +69,7 @@ def create_resampling_card(loaded_data): def create_loaded_data_summary(): loaded_data = data_object.get_all_sequences() - return [ - dbc.Row([html.Div(f"Loaded {dataset.label}", style={"textAlign": "left"})]) - for dataset in loaded_data - ] + return [dbc.Row([html.Div(f"Loaded {dataset.label}", style={"textAlign": "left"})]) for dataset in loaded_data] def create_selected_period_card(period: Sequence, dataset: str, index: int) -> dbc.Card: @@ -97,10 +91,7 @@ def create_selected_period_card(period: Sequence, dataset: str, index: int) -> d card_list = [ html.H4(period.label, className="card-title"), ] - card_list += [ - dbc.Row(f"{data}: {value}", style=styles.INFO_CARD) - for data, value in info_data.items() - ] + card_list += [dbc.Row(f"{data}: {value}", style=styles.INFO_CARD) for data, value in info_data.items()] card_list += [ dbc.Button( "Remove", @@ -124,10 +115,7 @@ def create_filter_results_card(parameters: dict) -> dbc.Card: card_list = [ html.H4("Data filtered", className="card-title"), ] - card_list += [ - dbc.Row(f"{data}: {value}", style=styles.INFO_CARD) - for data, value in parameters.items() - ] + card_list += [dbc.Row(f"{data}: {value}", style=styles.INFO_CARD) for data, value in parameters.items()] return dbc.Card(dbc.CardBody(card_list), id=ids.FILTERING_SAVED_CARD) @@ -138,10 +126,7 @@ def get_loaded_data(): for dataset in loaded_data: name = dataset.label if dataset.continuous_data: - data += [ - {"Name": name, "Data type": channel} - for channel in dataset.continuous_data - ] + data += [{"Name": name, "Data type": channel} for channel in dataset.continuous_data] if dataset.eit_data: data.append( { @@ -301,10 +286,7 @@ def populate_periods_selection_modal(method): if int_value == PeriodsSelectMethods.Manual.value: signals = data_object.get_all_sequences() - options = [ - {"label": sequence.label, "value": index} - for index, sequence in enumerate(signals) - ] + options = [{"label": sequence.label, "value": index} for index, sequence in enumerate(signals)] body = [ html.H6("Select one dataset"), @@ -540,11 +522,7 @@ def remove_period(n_clicks, container, figure): # remove from the figure (if the figure exists) try: - figure["data"] = [ - trace - for trace in figure["data"] - if "meta" not in trace or trace["meta"]["uid"] != input_id - ] + figure["data"] = [trace for trace in figure["data"] if "meta" not in trace or trace["meta"]["uid"] != input_id] except TypeError: contextlib.suppress(Exception) @@ -627,12 +605,9 @@ def enable_apply_button( """Enable the apply button.""" if ( (int(filter_selected) == FilterTypes.lowpass.value and co_high and co_high > 0) + or (int(filter_selected) == FilterTypes.highpass.value and co_low and co_low > 0) or ( - int(filter_selected) == FilterTypes.highpass.value and co_low and co_low > 0 - ) - or ( - int(filter_selected) - in [FilterTypes.bandpass.value, FilterTypes.bandstop.value] + int(filter_selected) in [FilterTypes.bandpass.value, FilterTypes.bandstop.value] and co_low > 0 and co_low > 0 ) @@ -767,9 +742,7 @@ def save_filtered_signal(confirm, results: list): data.update_data(tmp_data) if not params: - params = tmp_data.continuous_data.data[ - "global_impedance_filtered" - ].parameters + params = tmp_data.continuous_data.data["global_impedance_filtered"].parameters # show info card for element in results: From 0f03f121eaad4c271c9805e1a7786ee1ce490dbe Mon Sep 17 00:00:00 2001 From: Walter Baccinelli Date: Tue, 16 Apr 2024 15:34:42 +0200 Subject: [PATCH 11/16] fixed remove bug --- eit_dash/callbacks/preprocessing_callbacks.py | 54 ++++++++++++++----- 1 file changed, 42 insertions(+), 12 deletions(-) diff --git a/eit_dash/callbacks/preprocessing_callbacks.py b/eit_dash/callbacks/preprocessing_callbacks.py index 9ecc8c3..0d09a68 100644 --- a/eit_dash/callbacks/preprocessing_callbacks.py +++ b/eit_dash/callbacks/preprocessing_callbacks.py @@ -61,7 +61,10 @@ def create_resampling_card(loaded_data): for data in loaded_data ] - options = [{"label": f'{data["Name"]}', "value": str(i)} for i, data in enumerate(loaded_data)] + options = [ + {"label": f'{data["Name"]}', "value": str(i)} + for i, data in enumerate(loaded_data) + ] return row, options @@ -69,7 +72,10 @@ def create_resampling_card(loaded_data): def create_loaded_data_summary(): loaded_data = data_object.get_all_sequences() - return [dbc.Row([html.Div(f"Loaded {dataset.label}", style={"textAlign": "left"})]) for dataset in loaded_data] + return [ + dbc.Row([html.Div(f"Loaded {dataset.label}", style={"textAlign": "left"})]) + for dataset in loaded_data + ] def create_selected_period_card(period: Sequence, dataset: str, index: int) -> dbc.Card: @@ -91,7 +97,10 @@ def create_selected_period_card(period: Sequence, dataset: str, index: int) -> d card_list = [ html.H4(period.label, className="card-title"), ] - card_list += [dbc.Row(f"{data}: {value}", style=styles.INFO_CARD) for data, value in info_data.items()] + card_list += [ + dbc.Row(f"{data}: {value}", style=styles.INFO_CARD) + for data, value in info_data.items() + ] card_list += [ dbc.Button( "Remove", @@ -115,7 +124,10 @@ def create_filter_results_card(parameters: dict) -> dbc.Card: card_list = [ html.H4("Data filtered", className="card-title"), ] - card_list += [dbc.Row(f"{data}: {value}", style=styles.INFO_CARD) for data, value in parameters.items()] + card_list += [ + dbc.Row(f"{data}: {value}", style=styles.INFO_CARD) + for data, value in parameters.items() + ] return dbc.Card(dbc.CardBody(card_list), id=ids.FILTERING_SAVED_CARD) @@ -126,7 +138,10 @@ def get_loaded_data(): for dataset in loaded_data: name = dataset.label if dataset.continuous_data: - data += [{"Name": name, "Data type": channel} for channel in dataset.continuous_data] + data += [ + {"Name": name, "Data type": channel} + for channel in dataset.continuous_data + ] if dataset.eit_data: data.append( { @@ -286,7 +301,10 @@ def populate_periods_selection_modal(method): if int_value == PeriodsSelectMethods.Manual.value: signals = data_object.get_all_sequences() - options = [{"label": sequence.label, "value": index} for index, sequence in enumerate(signals)] + options = [ + {"label": sequence.label, "value": index} + for index, sequence in enumerate(signals) + ] body = [ html.H6("Select one dataset"), @@ -517,12 +535,19 @@ def remove_period(n_clicks, container, figure): data_object.remove_stable_period(input_id) # remove from the temp data, if present - if tmp_results and tmp_results.get_stable_period(input_id): - tmp_results.remove_stable_period(input_id) + if tmp_results: + try: + tmp_results.remove_stable_period(input_id) + except ValueError: + contextlib.suppress(Exception) # remove from the figure (if the figure exists) try: - figure["data"] = [trace for trace in figure["data"] if "meta" not in trace or trace["meta"]["uid"] != input_id] + figure["data"] = [ + trace + for trace in figure["data"] + if "meta" not in trace or trace["meta"]["uid"] != input_id + ] except TypeError: contextlib.suppress(Exception) @@ -605,9 +630,12 @@ def enable_apply_button( """Enable the apply button.""" if ( (int(filter_selected) == FilterTypes.lowpass.value and co_high and co_high > 0) - or (int(filter_selected) == FilterTypes.highpass.value and co_low and co_low > 0) or ( - int(filter_selected) in [FilterTypes.bandpass.value, FilterTypes.bandstop.value] + int(filter_selected) == FilterTypes.highpass.value and co_low and co_low > 0 + ) + or ( + int(filter_selected) + in [FilterTypes.bandpass.value, FilterTypes.bandstop.value] and co_low > 0 and co_low > 0 ) @@ -742,7 +770,9 @@ def save_filtered_signal(confirm, results: list): data.update_data(tmp_data) if not params: - params = tmp_data.continuous_data.data["global_impedance_filtered"].parameters + params = tmp_data.continuous_data.data[ + "global_impedance_filtered" + ].parameters # show info card for element in results: From 4992e7a416bebc1de87f583c9c9a1386b4ed94f5 Mon Sep 17 00:00:00 2001 From: Walter Baccinelli Date: Tue, 16 Apr 2024 15:40:50 +0200 Subject: [PATCH 12/16] linting --- eit_dash/callbacks/preprocessing_callbacks.py | 47 ++++--------------- 1 file changed, 10 insertions(+), 37 deletions(-) diff --git a/eit_dash/callbacks/preprocessing_callbacks.py b/eit_dash/callbacks/preprocessing_callbacks.py index 0d09a68..c314d15 100644 --- a/eit_dash/callbacks/preprocessing_callbacks.py +++ b/eit_dash/callbacks/preprocessing_callbacks.py @@ -61,10 +61,7 @@ def create_resampling_card(loaded_data): for data in loaded_data ] - options = [ - {"label": f'{data["Name"]}', "value": str(i)} - for i, data in enumerate(loaded_data) - ] + options = [{"label": f'{data["Name"]}', "value": str(i)} for i, data in enumerate(loaded_data)] return row, options @@ -72,10 +69,7 @@ def create_resampling_card(loaded_data): def create_loaded_data_summary(): loaded_data = data_object.get_all_sequences() - return [ - dbc.Row([html.Div(f"Loaded {dataset.label}", style={"textAlign": "left"})]) - for dataset in loaded_data - ] + return [dbc.Row([html.Div(f"Loaded {dataset.label}", style={"textAlign": "left"})]) for dataset in loaded_data] def create_selected_period_card(period: Sequence, dataset: str, index: int) -> dbc.Card: @@ -97,10 +91,7 @@ def create_selected_period_card(period: Sequence, dataset: str, index: int) -> d card_list = [ html.H4(period.label, className="card-title"), ] - card_list += [ - dbc.Row(f"{data}: {value}", style=styles.INFO_CARD) - for data, value in info_data.items() - ] + card_list += [dbc.Row(f"{data}: {value}", style=styles.INFO_CARD) for data, value in info_data.items()] card_list += [ dbc.Button( "Remove", @@ -124,10 +115,7 @@ def create_filter_results_card(parameters: dict) -> dbc.Card: card_list = [ html.H4("Data filtered", className="card-title"), ] - card_list += [ - dbc.Row(f"{data}: {value}", style=styles.INFO_CARD) - for data, value in parameters.items() - ] + card_list += [dbc.Row(f"{data}: {value}", style=styles.INFO_CARD) for data, value in parameters.items()] return dbc.Card(dbc.CardBody(card_list), id=ids.FILTERING_SAVED_CARD) @@ -138,10 +126,7 @@ def get_loaded_data(): for dataset in loaded_data: name = dataset.label if dataset.continuous_data: - data += [ - {"Name": name, "Data type": channel} - for channel in dataset.continuous_data - ] + data += [{"Name": name, "Data type": channel} for channel in dataset.continuous_data] if dataset.eit_data: data.append( { @@ -301,10 +286,7 @@ def populate_periods_selection_modal(method): if int_value == PeriodsSelectMethods.Manual.value: signals = data_object.get_all_sequences() - options = [ - {"label": sequence.label, "value": index} - for index, sequence in enumerate(signals) - ] + options = [{"label": sequence.label, "value": index} for index, sequence in enumerate(signals)] body = [ html.H6("Select one dataset"), @@ -543,11 +525,7 @@ def remove_period(n_clicks, container, figure): # remove from the figure (if the figure exists) try: - figure["data"] = [ - trace - for trace in figure["data"] - if "meta" not in trace or trace["meta"]["uid"] != input_id - ] + figure["data"] = [trace for trace in figure["data"] if "meta" not in trace or trace["meta"]["uid"] != input_id] except TypeError: contextlib.suppress(Exception) @@ -630,12 +608,9 @@ def enable_apply_button( """Enable the apply button.""" if ( (int(filter_selected) == FilterTypes.lowpass.value and co_high and co_high > 0) + or (int(filter_selected) == FilterTypes.highpass.value and co_low and co_low > 0) or ( - int(filter_selected) == FilterTypes.highpass.value and co_low and co_low > 0 - ) - or ( - int(filter_selected) - in [FilterTypes.bandpass.value, FilterTypes.bandstop.value] + int(filter_selected) in [FilterTypes.bandpass.value, FilterTypes.bandstop.value] and co_low > 0 and co_low > 0 ) @@ -770,9 +745,7 @@ def save_filtered_signal(confirm, results: list): data.update_data(tmp_data) if not params: - params = tmp_data.continuous_data.data[ - "global_impedance_filtered" - ].parameters + params = tmp_data.continuous_data.data["global_impedance_filtered"].parameters # show info card for element in results: From 84d1b018b6e2e971ab9f5f40299f12ddb106bfc3 Mon Sep 17 00:00:00 2001 From: Walter Baccinelli Date: Tue, 16 Apr 2024 15:58:03 +0200 Subject: [PATCH 13/16] smarter periods indexing --- eit_dash/callbacks/preprocessing_callbacks.py | 2 +- eit_dash/utils/data_singleton.py | 12 +++++++++++- 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/eit_dash/callbacks/preprocessing_callbacks.py b/eit_dash/callbacks/preprocessing_callbacks.py index c314d15..d44fc75 100644 --- a/eit_dash/callbacks/preprocessing_callbacks.py +++ b/eit_dash/callbacks/preprocessing_callbacks.py @@ -418,7 +418,7 @@ def select_period( start_sample = data.time[0] stop_sample = data.time[-1] - period_index = data_object.get_stable_periods_list_length() + period_index = data_object.get_next_period_index() cut_data = data.select_by_time( start_time=start_sample, diff --git a/eit_dash/utils/data_singleton.py b/eit_dash/utils/data_singleton.py index 50a10dc..0536117 100644 --- a/eit_dash/utils/data_singleton.py +++ b/eit_dash/utils/data_singleton.py @@ -90,7 +90,7 @@ def add_stable_period( raise ValueError(msg) if not period_index: - period_index = self.get_stable_periods_list_length() + period_index = self.get_next_period_index() # check that the index doesn't exist for period in self._stable_periods: @@ -151,6 +151,16 @@ def get_stable_period(self, index: int): msg = f"Period with index {index} not found" raise ValueError(msg) + def get_stable_periods_indexes(self) -> list[int]: + """Get a list of the indexes of the stable periods currently available.""" + return [period.get_period_index() for period in self._stable_periods] + + def get_next_period_index(self): + """Determines the index to be assigned to the next stable period.""" + available_indexes = self.get_stable_periods_indexes() + + return max(available_indexes) + 1 if available_indexes else 0 + @dataclass class Period: From 60c70ed6514b52ae74da8beb14368b3781c35fb5 Mon Sep 17 00:00:00 2001 From: Walter Baccinelli Date: Wed, 17 Apr 2024 11:24:45 +0200 Subject: [PATCH 14/16] bug fixed --- eit_dash/callbacks/preprocessing_callbacks.py | 133 +++++++++++++----- eit_dash/definitions/element_ids.py | 2 +- eit_dash/pages/preprocessing.py | 17 ++- 3 files changed, 109 insertions(+), 43 deletions(-) diff --git a/eit_dash/callbacks/preprocessing_callbacks.py b/eit_dash/callbacks/preprocessing_callbacks.py index d44fc75..8890123 100644 --- a/eit_dash/callbacks/preprocessing_callbacks.py +++ b/eit_dash/callbacks/preprocessing_callbacks.py @@ -61,7 +61,10 @@ def create_resampling_card(loaded_data): for data in loaded_data ] - options = [{"label": f'{data["Name"]}', "value": str(i)} for i, data in enumerate(loaded_data)] + options = [ + {"label": f'{data["Name"]}', "value": str(i)} + for i, data in enumerate(loaded_data) + ] return row, options @@ -69,7 +72,10 @@ def create_resampling_card(loaded_data): def create_loaded_data_summary(): loaded_data = data_object.get_all_sequences() - return [dbc.Row([html.Div(f"Loaded {dataset.label}", style={"textAlign": "left"})]) for dataset in loaded_data] + return [ + dbc.Row([html.Div(f"Loaded {dataset.label}", style={"textAlign": "left"})]) + for dataset in loaded_data + ] def create_selected_period_card(period: Sequence, dataset: str, index: int) -> dbc.Card: @@ -91,7 +97,10 @@ def create_selected_period_card(period: Sequence, dataset: str, index: int) -> d card_list = [ html.H4(period.label, className="card-title"), ] - card_list += [dbc.Row(f"{data}: {value}", style=styles.INFO_CARD) for data, value in info_data.items()] + card_list += [ + dbc.Row(f"{data}: {value}", style=styles.INFO_CARD) + for data, value in info_data.items() + ] card_list += [ dbc.Button( "Remove", @@ -115,7 +124,10 @@ def create_filter_results_card(parameters: dict) -> dbc.Card: card_list = [ html.H4("Data filtered", className="card-title"), ] - card_list += [dbc.Row(f"{data}: {value}", style=styles.INFO_CARD) for data, value in parameters.items()] + card_list += [ + dbc.Row(f"{data}: {value}", style=styles.INFO_CARD) + for data, value in parameters.items() + ] return dbc.Card(dbc.CardBody(card_list), id=ids.FILTERING_SAVED_CARD) @@ -126,7 +138,10 @@ def get_loaded_data(): for dataset in loaded_data: name = dataset.label if dataset.continuous_data: - data += [{"Name": name, "Data type": channel} for channel in dataset.continuous_data] + data += [ + {"Name": name, "Data type": channel} + for channel in dataset.continuous_data + ] if dataset.eit_data: data.append( { @@ -257,24 +272,6 @@ def open_periods_modal(open_click, confirm_click) -> bool: return False -@callback( - Output(ids.FILTERING_SELECTION_POPUP, "is_open"), - [ - Input(ids.OPEN_FILTER_DATA_BUTTON, "n_clicks"), - Input(ids.FILTERING_CLOSE_BUTTON, "n_clicks"), - ], - prevent_initial_call=True, -) -def open_filtering_modal(open_click, confirm_click) -> bool: - """open/close modal dialog for filtering data.""" - trigger = ctx.triggered_id - - if trigger == ids.OPEN_FILTER_DATA_BUTTON: - return True - - return False - - @callback( Output(ids.PERIODS_SELECTION_SELECT_DATASET, "children"), Input(ids.PERIODS_METHOD_SELECTOR, "value"), @@ -286,7 +283,10 @@ def populate_periods_selection_modal(method): if int_value == PeriodsSelectMethods.Manual.value: signals = data_object.get_all_sequences() - options = [{"label": sequence.label, "value": index} for index, sequence in enumerate(signals)] + options = [ + {"label": sequence.label, "value": index} + for index, sequence in enumerate(signals) + ] body = [ html.H6("Select one dataset"), @@ -525,7 +525,11 @@ def remove_period(n_clicks, container, figure): # remove from the figure (if the figure exists) try: - figure["data"] = [trace for trace in figure["data"] if "meta" not in trace or trace["meta"]["uid"] != input_id] + figure["data"] = [ + trace + for trace in figure["data"] + if "meta" not in trace or trace["meta"]["uid"] != input_id + ] except TypeError: contextlib.suppress(Exception) @@ -547,6 +551,27 @@ def enable_filter_button(results): return True +@callback( + [ + Output(ids.FILTERING_SELECTION_POPUP, "is_open"), + Output(ids.FILTER_SELECTOR, "value"), + ], + [ + Input(ids.OPEN_FILTER_DATA_BUTTON, "n_clicks"), + Input(ids.FILTERING_CLOSE_BUTTON, "n_clicks"), + ], + prevent_initial_call=True, +) +def open_filtering_modal(open_click, confirm_click) -> bool: + """open/close modal dialog for filtering data.""" + trigger = ctx.triggered_id + + if trigger == ids.OPEN_FILTER_DATA_BUTTON: + return True, None + + return False, None + + @callback( [ Output(ids.FILTER_PARAMS, "hidden"), @@ -566,11 +591,11 @@ def show_filters_params(selected): # if no filter has been selected, hide the params if not selected: filter_params = True - - if int(selected) == FilterTypes.lowpass.value: - cutoff_low_enabled = True - elif int(selected) == FilterTypes.highpass.value: - cutoff_high_enabled = True + else: + if int(selected) == FilterTypes.lowpass.value: + cutoff_low_enabled = True + elif int(selected) == FilterTypes.highpass.value: + cutoff_high_enabled = True return ( filter_params, @@ -606,11 +631,17 @@ def enable_apply_button( filter_selected, ): """Enable the apply button.""" + if not filter_selected: + return True + if ( (int(filter_selected) == FilterTypes.lowpass.value and co_high and co_high > 0) - or (int(filter_selected) == FilterTypes.highpass.value and co_low and co_low > 0) or ( - int(filter_selected) in [FilterTypes.bandpass.value, FilterTypes.bandstop.value] + int(filter_selected) == FilterTypes.highpass.value and co_low and co_low > 0 + ) + or ( + int(filter_selected) + in [FilterTypes.bandpass.value, FilterTypes.bandstop.value] and co_low > 0 and co_low > 0 ) @@ -620,12 +651,36 @@ def enable_apply_button( return True +@callback( + [ + Output(ids.FILTERING_RESULTS_DIV, "hidden", allow_duplicate=True), + Output(ids.FILTERING_CONFIRM_DIV, "hidden", allow_duplicate=True), + Output(ids.FILTERING_SELECT_PERIOD_VIEW, "options", allow_duplicate=True), + ], + [ + Input(ids.FILTER_APPLY, "disabled"), + ], + prevent_initial_call=True, +) +def apply_filter(disabled): + """Apply the filter.""" + # flag for showing graphs and confirm button + if not disabled: + raise PreventUpdate + + hidden_div = True + + options = [] + + return hidden_div, hidden_div, options + + @callback( [ Output(ids.PREPROCESING_RESULTS_CONTAINER, "children", allow_duplicate=True), - Output(ids.FILTERING_RESULTS_DIV, "hidden"), - Output(ids.FILTERING_CONFIRM_DIV, "hidden"), - Output(ids.FILTERING_SELCET_PERIOD_VIEW, "options"), + Output(ids.FILTERING_RESULTS_DIV, "hidden", allow_duplicate=True), + Output(ids.FILTERING_CONFIRM_DIV, "hidden", allow_duplicate=True), + Output(ids.FILTERING_SELECT_PERIOD_VIEW, "options", allow_duplicate=True), Output(ids.ALERT_FILTER, "is_open"), Output(ids.ALERT_FILTER, "children"), ], @@ -690,13 +745,13 @@ def apply_filter(_, co_low, co_high, order, filter_selected, results): Output(ids.FILTERING_RESULTS_GRAPH, "figure"), Output(ids.FILTERING_RESULTS_GRAPH, "style"), ], - Input(ids.FILTERING_SELCET_PERIOD_VIEW, "value"), + Input(ids.FILTERING_SELECT_PERIOD_VIEW, "value"), Input(ids.FILTER_APPLY, "n_clicks"), prevent_initial_call=True, ) def show_filtered_results(selected, _): """When selecting a period, shows the original and the filtered signal.""" - if not selected: + if not selected or tmp_results.get_stable_periods_list_length() == 0: raise PreventUpdate fig = go.Figure() @@ -745,7 +800,9 @@ def save_filtered_signal(confirm, results: list): data.update_data(tmp_data) if not params: - params = tmp_data.continuous_data.data["global_impedance_filtered"].parameters + params = tmp_data.continuous_data.data[ + "global_impedance_filtered" + ].parameters # show info card for element in results: diff --git a/eit_dash/definitions/element_ids.py b/eit_dash/definitions/element_ids.py index ae9f7f1..6e2b168 100644 --- a/eit_dash/definitions/element_ids.py +++ b/eit_dash/definitions/element_ids.py @@ -34,7 +34,7 @@ FILTER_PARAMS = "filter-params" FILTERING_SELECTION_POPUP = "filtering-selection-popup" FILTER_SELECTOR = "filter-selector" -FILTERING_SELCET_PERIOD_VIEW = "filtering-select-period-view" +FILTERING_SELECT_PERIOD_VIEW = "filtering-select-period-view" FILTERING_RESULTS_DIV = "filtering-results-div" FILTERING_RESULTS_GRAPH = "filtering-results-graph" FILTERING_SAVED_CARD = "filtering-saved-card" diff --git a/eit_dash/pages/preprocessing.py b/eit_dash/pages/preprocessing.py index 1949f9a..45cd131 100644 --- a/eit_dash/pages/preprocessing.py +++ b/eit_dash/pages/preprocessing.py @@ -100,7 +100,10 @@ [ dbc.Select( id=ids.SYNC_METHOD_SELECTOR, - options=[{"label": method.name, "value": method.value} for method in SynchMethods], + options=[ + {"label": method.name, "value": method.value} + for method in SynchMethods + ], value=str(SynchMethods.manual.value), ), html.P(), @@ -175,7 +178,10 @@ html.H6("Periods selection method"), dbc.Select( id=ids.PERIODS_METHOD_SELECTOR, - options=[{"label": method.name, "value": method.value} for method in PeriodsSelectMethods], + options=[ + {"label": method.name, "value": method.value} + for method in PeriodsSelectMethods + ], ), modal_selection_body, ], @@ -261,7 +267,7 @@ html.Div( [ html.H6("Select a period to view the results"), - dbc.Select(id=ids.FILTERING_SELCET_PERIOD_VIEW), + dbc.Select(id=ids.FILTERING_SELECT_PERIOD_VIEW), dcc.Graph( id=ids.FILTERING_RESULTS_GRAPH, style=styles.EMPTY_ELEMENT, @@ -301,7 +307,10 @@ html.H6("Select a filter"), dbc.Select( id=ids.FILTER_SELECTOR, - options=[{"label": filt.name, "value": filt.value} for filt in FilterTypes], + options=[ + {"label": filt.name, "value": filt.value} + for filt in FilterTypes + ], ), html.P(), filter_params, From 7ca80fa6666fe61b48ada2fd6448606fd190cef1 Mon Sep 17 00:00:00 2001 From: Walter Baccinelli Date: Wed, 17 Apr 2024 11:27:18 +0200 Subject: [PATCH 15/16] linting --- eit_dash/callbacks/preprocessing_callbacks.py | 60 +++++-------------- eit_dash/pages/preprocessing.py | 15 +---- 2 files changed, 19 insertions(+), 56 deletions(-) diff --git a/eit_dash/callbacks/preprocessing_callbacks.py b/eit_dash/callbacks/preprocessing_callbacks.py index 8890123..cb734be 100644 --- a/eit_dash/callbacks/preprocessing_callbacks.py +++ b/eit_dash/callbacks/preprocessing_callbacks.py @@ -61,10 +61,7 @@ def create_resampling_card(loaded_data): for data in loaded_data ] - options = [ - {"label": f'{data["Name"]}', "value": str(i)} - for i, data in enumerate(loaded_data) - ] + options = [{"label": f'{data["Name"]}', "value": str(i)} for i, data in enumerate(loaded_data)] return row, options @@ -72,10 +69,7 @@ def create_resampling_card(loaded_data): def create_loaded_data_summary(): loaded_data = data_object.get_all_sequences() - return [ - dbc.Row([html.Div(f"Loaded {dataset.label}", style={"textAlign": "left"})]) - for dataset in loaded_data - ] + return [dbc.Row([html.Div(f"Loaded {dataset.label}", style={"textAlign": "left"})]) for dataset in loaded_data] def create_selected_period_card(period: Sequence, dataset: str, index: int) -> dbc.Card: @@ -97,10 +91,7 @@ def create_selected_period_card(period: Sequence, dataset: str, index: int) -> d card_list = [ html.H4(period.label, className="card-title"), ] - card_list += [ - dbc.Row(f"{data}: {value}", style=styles.INFO_CARD) - for data, value in info_data.items() - ] + card_list += [dbc.Row(f"{data}: {value}", style=styles.INFO_CARD) for data, value in info_data.items()] card_list += [ dbc.Button( "Remove", @@ -124,10 +115,7 @@ def create_filter_results_card(parameters: dict) -> dbc.Card: card_list = [ html.H4("Data filtered", className="card-title"), ] - card_list += [ - dbc.Row(f"{data}: {value}", style=styles.INFO_CARD) - for data, value in parameters.items() - ] + card_list += [dbc.Row(f"{data}: {value}", style=styles.INFO_CARD) for data, value in parameters.items()] return dbc.Card(dbc.CardBody(card_list), id=ids.FILTERING_SAVED_CARD) @@ -138,10 +126,7 @@ def get_loaded_data(): for dataset in loaded_data: name = dataset.label if dataset.continuous_data: - data += [ - {"Name": name, "Data type": channel} - for channel in dataset.continuous_data - ] + data += [{"Name": name, "Data type": channel} for channel in dataset.continuous_data] if dataset.eit_data: data.append( { @@ -283,10 +268,7 @@ def populate_periods_selection_modal(method): if int_value == PeriodsSelectMethods.Manual.value: signals = data_object.get_all_sequences() - options = [ - {"label": sequence.label, "value": index} - for index, sequence in enumerate(signals) - ] + options = [{"label": sequence.label, "value": index} for index, sequence in enumerate(signals)] body = [ html.H6("Select one dataset"), @@ -525,11 +507,7 @@ def remove_period(n_clicks, container, figure): # remove from the figure (if the figure exists) try: - figure["data"] = [ - trace - for trace in figure["data"] - if "meta" not in trace or trace["meta"]["uid"] != input_id - ] + figure["data"] = [trace for trace in figure["data"] if "meta" not in trace or trace["meta"]["uid"] != input_id] except TypeError: contextlib.suppress(Exception) @@ -591,11 +569,10 @@ def show_filters_params(selected): # if no filter has been selected, hide the params if not selected: filter_params = True - else: - if int(selected) == FilterTypes.lowpass.value: - cutoff_low_enabled = True - elif int(selected) == FilterTypes.highpass.value: - cutoff_high_enabled = True + elif int(selected) == FilterTypes.lowpass.value: + cutoff_low_enabled = True + elif int(selected) == FilterTypes.highpass.value: + cutoff_high_enabled = True return ( filter_params, @@ -636,12 +613,9 @@ def enable_apply_button( if ( (int(filter_selected) == FilterTypes.lowpass.value and co_high and co_high > 0) + or (int(filter_selected) == FilterTypes.highpass.value and co_low and co_low > 0) or ( - int(filter_selected) == FilterTypes.highpass.value and co_low and co_low > 0 - ) - or ( - int(filter_selected) - in [FilterTypes.bandpass.value, FilterTypes.bandstop.value] + int(filter_selected) in [FilterTypes.bandpass.value, FilterTypes.bandstop.value] and co_low > 0 and co_low > 0 ) @@ -662,8 +636,8 @@ def enable_apply_button( ], prevent_initial_call=True, ) -def apply_filter(disabled): - """Apply the filter.""" +def disable_results(disabled): + """Hide and disable results if the apply button is disabled.""" # flag for showing graphs and confirm button if not disabled: raise PreventUpdate @@ -800,9 +774,7 @@ def save_filtered_signal(confirm, results: list): data.update_data(tmp_data) if not params: - params = tmp_data.continuous_data.data[ - "global_impedance_filtered" - ].parameters + params = tmp_data.continuous_data.data["global_impedance_filtered"].parameters # show info card for element in results: diff --git a/eit_dash/pages/preprocessing.py b/eit_dash/pages/preprocessing.py index 45cd131..9f173e1 100644 --- a/eit_dash/pages/preprocessing.py +++ b/eit_dash/pages/preprocessing.py @@ -100,10 +100,7 @@ [ dbc.Select( id=ids.SYNC_METHOD_SELECTOR, - options=[ - {"label": method.name, "value": method.value} - for method in SynchMethods - ], + options=[{"label": method.name, "value": method.value} for method in SynchMethods], value=str(SynchMethods.manual.value), ), html.P(), @@ -178,10 +175,7 @@ html.H6("Periods selection method"), dbc.Select( id=ids.PERIODS_METHOD_SELECTOR, - options=[ - {"label": method.name, "value": method.value} - for method in PeriodsSelectMethods - ], + options=[{"label": method.name, "value": method.value} for method in PeriodsSelectMethods], ), modal_selection_body, ], @@ -307,10 +301,7 @@ html.H6("Select a filter"), dbc.Select( id=ids.FILTER_SELECTOR, - options=[ - {"label": filt.name, "value": filt.value} - for filt in FilterTypes - ], + options=[{"label": filt.name, "value": filt.value} for filt in FilterTypes], ), html.P(), filter_params, From 9bd1a142ab2decaeb331e8699e1a5561d36e70f1 Mon Sep 17 00:00:00 2001 From: Walter Baccinelli Date: Wed, 17 Apr 2024 12:23:43 +0200 Subject: [PATCH 16/16] fixing the bug for real --- eit_dash/callbacks/preprocessing_callbacks.py | 29 +++++++++++++++---- eit_dash/pages/preprocessing.py | 1 + 2 files changed, 24 insertions(+), 6 deletions(-) diff --git a/eit_dash/callbacks/preprocessing_callbacks.py b/eit_dash/callbacks/preprocessing_callbacks.py index cb734be..8bad26b 100644 --- a/eit_dash/callbacks/preprocessing_callbacks.py +++ b/eit_dash/callbacks/preprocessing_callbacks.py @@ -657,6 +657,7 @@ def disable_results(disabled): Output(ids.FILTERING_SELECT_PERIOD_VIEW, "options", allow_duplicate=True), Output(ids.ALERT_FILTER, "is_open"), Output(ids.ALERT_FILTER, "children"), + Output("update-filter-results", "children"), ], [ Input(ids.FILTER_APPLY, "n_clicks"), @@ -678,6 +679,9 @@ def apply_filter(_, co_low, co_high, order, filter_selected, results): # alert message alert_msg = "" + # placeholder to allow results update + placeholder_div = "updated" + # flag for showing graphs and confirm button hidden_div = False @@ -710,8 +714,17 @@ def apply_filter(_, co_low, co_high, order, filter_selected, results): show_alert = True alert_msg = f"{e}" hidden_div = True + placeholder_div = None - return results, hidden_div, hidden_div, options, show_alert, alert_msg + return ( + results, + hidden_div, + hidden_div, + options, + show_alert, + alert_msg, + placeholder_div, + ) @callback( @@ -720,16 +733,22 @@ def apply_filter(_, co_low, co_high, order, filter_selected, results): Output(ids.FILTERING_RESULTS_GRAPH, "style"), ], Input(ids.FILTERING_SELECT_PERIOD_VIEW, "value"), - Input(ids.FILTER_APPLY, "n_clicks"), + Input("update-filter-results", "children"), + State(ids.FILTERING_SELECT_PERIOD_VIEW, "value"), prevent_initial_call=True, ) -def show_filtered_results(selected, _): +def show_filtered_results(_, update, selected): """When selecting a period, shows the original and the filtered signal.""" - if not selected or tmp_results.get_stable_periods_list_length() == 0: + if not selected or not update: raise PreventUpdate fig = go.Figure() + try: + filtered_data = tmp_results.get_stable_period(int(selected)).get_data() + except Exception: + return fig, styles.EMPTY_ELEMENT + data = data_object.get_stable_period(int(selected)).get_data() fig.add_trace( @@ -740,8 +759,6 @@ def show_filtered_results(selected, _): ), ) - filtered_data = tmp_results.get_stable_period(int(selected)).get_data() - fig.add_trace( go.Scatter( x=filtered_data.continuous_data.data["global_impedance_filtered"].time, diff --git a/eit_dash/pages/preprocessing.py b/eit_dash/pages/preprocessing.py index 9f173e1..b98beef 100644 --- a/eit_dash/pages/preprocessing.py +++ b/eit_dash/pages/preprocessing.py @@ -315,6 +315,7 @@ n_clicks=0, ), ), + html.Div(id="update-filter-results", hidden=True), ], id=ids.FILTERING_SELECTION_POPUP, centered=True,