Skip to content

Commit

Permalink
Merge pull request #42 from EIT-ALIVE/037_implement_eeli_wbaccinelli
Browse files Browse the repository at this point in the history
037 implement eeli wbaccinelli
  • Loading branch information
wbaccinelli authored Apr 18, 2024
2 parents 754719b + d758b17 commit 8b1826e
Show file tree
Hide file tree
Showing 13 changed files with 462 additions and 205 deletions.
Binary file added docs/images/apply_eeli.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
15 changes: 14 additions & 1 deletion docs/user_manual.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ dashboard remains local on your machine.
Note 3: The Dashboard, this manual, and the (back end) software it is built around are works in progress. The images and
interaction procedures may be slightly off. Please bear with us.

Note 4: If you encounter any issue while using the dashboard, please report it by opening an issue [here](https://github.com/EIT-ALIVE/eit_dash/issues).

## LOAD page

The dashboard should open on the **LOAD** page, but in case this does not happen, navigate there by clicking **LOAD** at
Expand Down Expand Up @@ -41,7 +43,7 @@ they _must_ be selected here already.
Note that on the following page more precise time selections can be made. This page is just intended to make a rough
pre-selection. Also, pre-selections cannot be undone at this point, but do not need to be used for further processing.

When all pre-selections are made, Click **`PRE-PROCESSING`** at the top of the page to proceed to the next stage.
When all pre-selections are made, click **`PRE-PROCESSING`** at the top of the page to proceed to the next stage.

## PRE-PROCESSING page

Expand Down Expand Up @@ -72,3 +74,14 @@ the preview will update. When you are happy with a particular setting/selection,
<kbd>
<img src=images/preprocessing_filter.png width="800px" style="border: 1px solid black">
</kbd>

When you have selected your period and filters, ensure they are both listed on the right side under "Results", and then click on **`ANALYZE`** at the top of the page.

## ANALYZE page

Click `Apply EELI` to run EELI analysis. When the analysis is completed, it is possible to see
the results by selecting the period to visualize using the dropdown menu.

<kbd>
<img src=images/apply_eeli.png width="800px" style="border: 1px solid black">
</kbd>
176 changes: 176 additions & 0 deletions eit_dash/callbacks/analyze_callbacks.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,176 @@
import contextlib

import plotly.graph_objects as go
from dash import Input, Output, State, callback, ctx
from eitprocessing.parameters.eeli import EELI

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.constants import FILTERED_EIT_LABEL, RAW_EIT_LABEL
from eit_dash.utils.common import (
create_filter_results_card,
create_info_card,
create_selected_period_card,
)

eeli = []


@callback(
Output(ids.SUMMARY_COLUMN_ANALYZE, "children", allow_duplicate=True),
Output(ids.ANALYZE_SELECT_PERIOD_VIEW, "options"),
[
Input(ids.ANALYZE_RESULTS_TITLE, "children"),
],
[
State(ids.SUMMARY_COLUMN_ANALYZE, "children"),
],
# this allows duplicate outputs with initial call
prevent_initial_call="initial_duplicate",
)
def page_setup(_, summary):
"""Setups the page elements when it starts up.
When the page is loaded, it populates the summary column
with the info about the loaded datasets and the preprocessing steps.
Populates the periods selections element with the loaded periods.
"""
trigger = ctx.triggered_id
options = []

if trigger is None:
for d in data_object.get_all_sequences():
card = create_info_card(d)
summary += [card]

filter_params = {}

for period in data_object.get_all_stable_periods():
summary += [
create_selected_period_card(
period.get_data(),
period.get_dataset_index(),
period.get_period_index(),
False,
),
]

# populate period selection
options.append(
{
"label": f"Period {period.get_period_index()}",
"value": period.get_period_index(),
},
)

if not filter_params:
try:
filter_params = period.get_data().continuous_data.data[FILTERED_EIT_LABEL].parameters
except KeyError:
contextlib.suppress(Exception)
if filter_params:
summary += [create_filter_results_card(filter_params)]

return summary, options


@callback(
Output(ids.EELI_RESULTS_GRAPH_DIV, "hidden"),
Input(ids.EELI_APPLY, "n_clicks"),
prevent_initial_call=True,
)
def apply_eeli(_):
"""Apply EELI and store results."""
global eeli # noqa: PLW0602

eeli.clear()

periods = data_object.get_all_stable_periods()

for period in periods:
sequence = period.get_data()
if sequence.continuous_data.get(FILTERED_EIT_LABEL):
eeli_result_filtered = EELI().compute_parameter(
sequence,
FILTERED_EIT_LABEL,
)
else:
eeli_result_filtered = EELI().compute_parameter(sequence, RAW_EIT_LABEL)

# TODO: the results should be stored in the sequence object
eeli_result_filtered["index"] = period.get_period_index()

eeli.append(eeli_result_filtered)

return False


@callback(
[
Output(ids.EELI_RESULTS_GRAPH, "figure"),
Output(ids.EELI_RESULTS_GRAPH, "style"),
],
Input(ids.ANALYZE_SELECT_PERIOD_VIEW, "value"),
prevent_initial_call=True,
)
def show_eeli(selected):
"""Show the results of the EELI for the selected period."""
figure = go.Figure()

sequence = data_object.get_stable_period(int(selected)).get_data()
for e in eeli:
if e["index"] == int(selected):
result = e

if sequence.continuous_data.get(FILTERED_EIT_LABEL):
data = sequence.continuous_data.get(FILTERED_EIT_LABEL)
else:
data = sequence.continuous_data.get(RAW_EIT_LABEL)

figure.add_trace(
go.Scatter(
x=data.time,
y=data.values,
name=data.label,
),
)

figure.add_hline(y=result["mean"], line_color="red", name="Mean")
figure.add_hline(y=result["median"], line_color="red", name="Median")

figure.add_scatter(
x=data.time[result["indices"]],
y=result["values"],
line_color="black",
name="EELIs",
mode="markers",
)

sd_upper = result["mean"] + result["standard deviation"]
sd_lower = result["mean"] - result["standard deviation"]

figure.add_trace(
go.Scatter(
x=data.time,
y=[sd_upper] * len(data.time),
fill=None,
mode="lines",
line_color="rgba(0,0,255,0)", # Set to transparent blue
name="Standard deviation",
),
)

# Add the lower bound line
figure.add_trace(
go.Scatter(
x=data.time,
y=[sd_lower] * len(data.time),
fill="tonexty", # Fill area below this line
mode="lines",
line_color="rgba(0,0,255,0.3)", # Set to semi-transparent blue
name="Standard deviation",
),
)

return figure, styles.GRAPH
64 changes: 17 additions & 47 deletions eit_dash/callbacks/load_callbacks.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
import os
from pathlib import Path

import dash_bootstrap_components as dbc
import plotly.graph_objects as go
from dash import ALL, Input, Output, State, callback, ctx, html
from dash.exceptions import PreventUpdate
Expand All @@ -15,9 +14,10 @@

import eit_dash.definitions.element_ids as ids
from eit_dash.app import data_object
from eit_dash.definitions import layout_styles as styles
from eit_dash.definitions.constants import RAW_EIT_LABEL
from eit_dash.definitions.option_lists import InputFiletypes
from eit_dash.utils.common import (
create_info_card,
create_slider_figure,
get_selections_slidebar,
get_signal_options,
Expand All @@ -26,35 +26,6 @@
file_data: Sequence | None = None


def create_info_card(dataset: Sequence, file_type: int) -> dbc.Card:
"""Create the card with the information on the loaded dataset to be displayed in the Results section.
Args:
dataset: Sequence object containing the selected dataset
file_type: Index of the selected type of selected
"""
info_data = {
"Name": dataset.eit_data["raw"].path.name,
"n_frames": dataset.eit_data["raw"].nframes,
"start_time": dataset.eit_data["raw"].time[0],
"end_time": dataset.eit_data["raw"].time[-1],
"vendor": dataset.eit_data["raw"].vendor,
"continuous signals": str(list(dataset.continuous_data)),
"path": str(dataset.eit_data["raw"].path),
}

card_list = [
html.H4(dataset.label, className="card-title"),
html.H6(InputFiletypes(file_type).name, className="card-subtitle"),
]
card_list += [
dbc.Row(f"{data}: {value}", style=styles.INFO_CARD)
for data, value in info_data.items()
]

return dbc.Card(dbc.CardBody(card_list), id="card-1")


# managing the file selection. Confirm button clicked
@callback(
Output(ids.CHOOSE_DATA_POPUP, "is_open"),
Expand Down Expand Up @@ -134,13 +105,13 @@ def open_data_selector(data, cancel_load, sig, file_type, fig):

trigger = ctx.triggered_id

# cancelled selection. Reset the data and turn of the data selector
# cancelled selection. Reset the data and turn off the data selector
if trigger == ids.LOAD_CANCEL_BUTTON:
data = None
file_data = None

if not data:
# this is needed, because a figure object must be returned for the graph, evn if empty
# this is needed, because a figure object must be returned for the graph, even if empty
figure = go.Figure()
return True, [], figure

Expand All @@ -160,12 +131,11 @@ def open_data_selector(data, cancel_load, sig, file_type, fig):

figure = create_slider_figure(
file_data,
["raw"],
list(file_data.continuous_data),
True,
continuous_data=list(file_data.continuous_data),
clickable_legend=True,
)

ok = ["raw"]
ok = [RAW_EIT_LABEL]
if sig:
ok += [options[s]["label"] for s in sig]

Expand Down Expand Up @@ -205,12 +175,12 @@ def show_info(
start_sample, stop_sample = get_selections_slidebar(slidebar_stat)

if not start_sample:
start_sample = file_data.eit_data["raw"].time[0]
start_sample = file_data.continuous_data[RAW_EIT_LABEL].time[0]
if not stop_sample:
stop_sample = file_data.eit_data["raw"].time[-1]
stop_sample = file_data.continuous_data[RAW_EIT_LABEL].time[-1]
else:
start_sample = file_data.eit_data["raw"].time[0]
stop_sample = file_data.eit_data["raw"].time[-1]
start_sample = file_data.continuous_data[RAW_EIT_LABEL].time[0]
stop_sample = file_data.continuous_data[RAW_EIT_LABEL].time[-1]

dataset_name = f"Dataset {data_object.get_sequence_list_length()}"

Expand All @@ -227,8 +197,8 @@ def show_info(
eit_data_cut.add(data[data_type].select_by_time(start_sample, stop_sample))

for data_type in (data := file_data.continuous_data):
# add just the selected signals
if data_type in selected:
# add just the selected signals and the raw EIT
if data_type in selected or data_type == RAW_EIT_LABEL:
continuous_data_cut.add(
data[data_type].select_by_time(start_sample, stop_sample),
)
Expand All @@ -244,7 +214,7 @@ def show_info(
data_object.add_sequence(cut_data)

# create the info summary card
card = create_info_card(cut_data, int(filetype))
card = create_info_card(cut_data)

# add the card to the current results
if container_state:
Expand Down Expand Up @@ -284,9 +254,7 @@ def list_cwd_files(cwd):
full_path = Path(cwd) / filepath

is_dir = Path(full_path).is_dir()
extension = (
filepath.suffix if not filepath.name.startswith(".") else filepath.name
)
extension = filepath.suffix if not filepath.name.startswith(".") else filepath.name

if is_dir or extension in [".bin", ".txt", ".zri"]:
link = html.A(
Expand Down Expand Up @@ -320,3 +288,5 @@ def store_clicked_file(n_clicks, title):
for state in ctx.states_list[0]:
if state["id"]["index"] == index:
return state["value"]

return None
Loading

0 comments on commit 8b1826e

Please sign in to comment.