diff --git a/test/test_helper_functions.py b/test/test_helper_functions.py index 558dd45..a7a48a3 100644 --- a/test/test_helper_functions.py +++ b/test/test_helper_functions.py @@ -7,13 +7,17 @@ def setUp(self): pass def test_cpu_usage_widget(self): - from tsvi.mth5_tsviewer.helpers import cpu_usage_widget + from tsvi.mth5_tsviewer.view_helpers import cpu_usage_widget cpu_usage_widget() def test_memory_usage_widget(self): - from tsvi.mth5_tsviewer.helpers import memory_usage_widget + from tsvi.mth5_tsviewer.view_helpers import memory_usage_widget memory_usage_widget() + def test_channel_summary_columns_to_display(self): + from tsvi.mth5_tsviewer.view_helpers import channel_summary_columns_to_display + channel_summary_columns_to_display() + def test_isupper(self): pass # self.assertTrue('FOO'.isupper()) diff --git a/tsvi/mth5_tsviewer/control_helpers.py b/tsvi/mth5_tsviewer/control_helpers.py new file mode 100644 index 0000000..9fd945e --- /dev/null +++ b/tsvi/mth5_tsviewer/control_helpers.py @@ -0,0 +1,71 @@ +""" +The Controller component is responsible for responding to user input and updating +the Model component accordingly. It typically does not have direct access to the +View components, but instead interacts with them through a well-defined interface. +""" + +def list_h5s_to_plot(channels_list): + """ + + Parameters + ---------- + channels_list: string representation of the data paths associated with channels + + Returns + ------- + used_files: list + Each element of the list is the name of an mth5 file that is associated with + at least one channel in the list. + + """ + used_files = [] + for selected_channel in channels_list: + file_name = selected_channel.split("/")[0] + if file_name not in used_files: + used_files.append(file_name) + return used_files + + +def set_channel_paths(df, file_name, file_version): + """ + ToDo: Consider making a class ChannelPathHandler + That has set_channel_paths method, + and also does the string unpacking in parse_channel_path below + Parameters + ---------- + df: pandas.core.frame.DataFrame from mth5 channel_summary + + Returns + ------- + + """ + df["file"] = file_name + if file_version == "0.1.0": + df["channel_path"] = (df["file"] + "/" + + df["station"] + "/" + + df["run"] + "/" + + df["component"]) + elif file_version == "0.2.0": + df["channel_path"] = (df["file"] + "/" + + df["survey"] + "/" + + df["station"] + "/" + + df["run"] + "/" + + df["component"]) + df.set_index("channel_path", inplace = True) + return + +def parse_channel_path(selected_channel): + try: # m.file_version == "0.1.0" + selected_file, station, run, channel = selected_channel.split("/") + survey = None + except ValueError: # m.file_version == "0.2.0" + selected_file, survey, station, run, channel = selected_channel.split("/") + return selected_file, survey, station, run, channel + + + + + +def invert(event, data): + data = -1 * data + return data diff --git a/tsvi/mth5_tsviewer/model_helpers.py b/tsvi/mth5_tsviewer/model_helpers.py new file mode 100644 index 0000000..1cc95f4 --- /dev/null +++ b/tsvi/mth5_tsviewer/model_helpers.py @@ -0,0 +1,93 @@ +""" +The Model component represents the underlying data and business logic of the +application. It is responsible for storing and manipulating data, and for +providing an interface for the View and Controller components to interact with that data. +""" +import panel as pn + +from tsvi.mth5_tsviewer.control_helpers import list_h5s_to_plot +from tsvi.mth5_tsviewer.control_helpers import parse_channel_path + +from mth5.mth5 import MTH5 + +def get_templates_dict(): + """ + Make template choice dictionary + More information about template choices and functionality is here: + https://panel.holoviz.org/user_guide/Templates.html + Returns + templates: dict + + ------- + + """ + templates = {} + templates["bootstrap"] = pn.template.BootstrapTemplate + templates["fast"] = pn.template.FastListTemplate + templates["golden"] = pn.template.GoldenTemplate + templates["grid"] = pn.template.FastGridTemplate + return templates + + +def channel_summary_columns_to_display(): + # Configure the displayed columns in the Channels Tab + displayed_columns = ["survey", "station", "run", + #"latitude", "longitude", "elevation", + "component", + "start", "end", "n_samples", "sample_rate", + "measurement_type", + #"azimuth", "tilt", + #"units" + ] + return displayed_columns + + + + +def get_mth5_data_as_xarrays(selected_channels, file_paths): + """ + ToDo: + - This can be modified in future to support chunking read in + - interaction with the intake package belongs here. + - This function works on multiple mth5 files in sequence. Another way to do this + would to be to invert the two for loops so that the outer loop iterates over + selected_channels first and then a one-line function accesses the data for that + channel. + + Parameters + ---------- + selected_channels: list + file_paths + kwargs + + Returns + ------- + out_dict: dictionary + Keyed by channel name, value is xarray associated with that channel + """ + out_dict = {} + used_files = list_h5s_to_plot(selected_channels) + for file in used_files: + m = MTH5() + m.open_mth5(file_paths[file], mode = "r") + for selected_channel in selected_channels: + selected_file, survey, station, run, channel = parse_channel_path(selected_channel) + if selected_file == file: + data = m.get_channel(station, run, channel, survey=survey).to_channel_ts().to_xarray() + # data = data.rename(data.attrs["mth5_type"]): "ex"--> "Electric" + #self.xarrays.append(data) + out_dict[selected_channel] = data + m.close_mth5() + return out_dict + +# def get_card_controls(): +# THe idea here is to track the buttons /widgets that we want beside the plot +# annotate_button = pn.widgets.Button(name = "Annotate", button_type = "primary", width = 100) +# invert_button = pn.widgets.Button(name = "Invert", button_type = "primary", width = 100) +# # def invert(self, *args, **params): +# # data = -1 * data +# # invert_button.on_click(invert(event, data)) +# controls = pn.Column(annotate_button, +# invert_button, +# sizing_mode = "fixed", width = 200,) +# return controls \ No newline at end of file diff --git a/tsvi/mth5_tsviewer/mth5_viewer_v1.py b/tsvi/mth5_tsviewer/mth5_viewer_v1.py index ba47d6a..61abd83 100644 --- a/tsvi/mth5_tsviewer/mth5_viewer_v1.py +++ b/tsvi/mth5_tsviewer/mth5_viewer_v1.py @@ -14,27 +14,20 @@ import bokeh import holoviews as hv -import hvplot -import hvplot.xarray -import matplotlib as plt -import numpy as np import pandas as pd import panel as pn import pathlib import psutil -import time import xarray -import mt_metadata -import mth5 from mth5.mth5 import MTH5 -from tsvi.mth5_tsviewer.helpers import channel_summary_columns_to_display -from tsvi.mth5_tsviewer.helpers import cpu_usage_widget -from tsvi.mth5_tsviewer.helpers import get_templates_dict -from tsvi.mth5_tsviewer.helpers import make_plots -from tsvi.mth5_tsviewer.helpers import memory_usage_widget -from tsvi.mth5_tsviewer.helpers import set_channel_paths +from tsvi.mth5_tsviewer.control_helpers import set_channel_paths +from tsvi.mth5_tsviewer.model_helpers import get_templates_dict +from tsvi.mth5_tsviewer.view_helpers import channel_summary_columns_to_display +from tsvi.mth5_tsviewer.view_helpers import cpu_usage_widget +from tsvi.mth5_tsviewer.view_helpers import make_plots +from tsvi.mth5_tsviewer.view_helpers import memory_usage_widget hv.extension("bokeh") diff --git a/tsvi/mth5_tsviewer/helpers.py b/tsvi/mth5_tsviewer/view_helpers.py similarity index 52% rename from tsvi/mth5_tsviewer/helpers.py rename to tsvi/mth5_tsviewer/view_helpers.py index 98dc870..b134ff0 100644 --- a/tsvi/mth5_tsviewer/helpers.py +++ b/tsvi/mth5_tsviewer/view_helpers.py @@ -1,8 +1,15 @@ +""" +The View component is responsible for presenting data to the user. The View component is + also responsible for capturing user input and passing it to the Controller component + for further processing. +""" import holoviews as hv import hvplot import panel as pn -from mth5.mth5 import MTH5 +from tsvi.mth5_tsviewer.control_helpers import list_h5s_to_plot +from tsvi.mth5_tsviewer.control_helpers import parse_channel_path +from tsvi.mth5_tsviewer.model_helpers import get_mth5_data_as_xarrays def cpu_usage_widget(): cpu_usage = pn.indicators.Number( @@ -28,27 +35,6 @@ def memory_usage_widget(): ) return memory_usage -def list_h5s_to_plot(channels_list): - """ - - Parameters - ---------- - channels_list: string representation of the data paths associated with channels - - Returns - ------- - used_files: list - Each element of the list is the name of an mth5 file that is associated with - at least one channel in the list. - - """ - used_files = [] - for selected_channel in channels_list: - file_name = selected_channel.split("/")[0] - if file_name not in used_files: - used_files.append(file_name) - return used_files - def channel_summary_columns_to_display(): # Configure the displayed columns in the Channels Tab @@ -63,43 +49,6 @@ def channel_summary_columns_to_display(): return displayed_columns -def set_channel_paths(df, file_name, file_version): - """ - ToDo: Consider making a class ChannelPathHandler - That has set_channel_paths method, - and also does the string unpacking - Parameters - ---------- - df: pandas.core.frame.DataFrame from mth5 channel_summary - - Returns - ------- - - """ - df["file"] = file_name - if file_version == "0.1.0": - df["channel_path"] = (df["file"] + "/" + - df["station"] + "/" + - df["run"] + "/" + - df["component"]) - elif file_version == "0.2.0": - df["channel_path"] = (df["file"] + "/" + - df["survey"] + "/" + - df["station"] + "/" + - df["run"] + "/" + - df["component"]) - df.set_index("channel_path", inplace = True) - return - -def parse_channel_path(selected_channel): - try: # m.file_version == "0.1.0" - selected_file, station, run, channel = selected_channel.split("/") - survey = None - except ValueError: # m.file_version == "0.2.0" - selected_file, survey, station, run, channel = selected_channel.split("/") - return selected_file, survey, station, run, channel - - # def plot_bokeh(xarray, shaded = False, shared = False): # plot = xarray.hvplot( # width = 900, @@ -108,40 +57,8 @@ def parse_channel_path(selected_channel): # shared_axes = shared # ) # return plot -def get_templates_dict(): - """ - Make template choice dictionary - More information about template choices and functionality is here: - https://panel.holoviz.org/user_guide/Templates.html - Returns - templates: dict - - ------- - - """ - templates = {} - templates["bootstrap"] = pn.template.BootstrapTemplate - templates["fast"] = pn.template.FastListTemplate - templates["golden"] = pn.template.GoldenTemplate - templates["grid"] = pn.template.FastGridTemplate - return templates - -def invert(event, data): - data = -1 * data - return data - # def get_card_controls(): - # THe idea here is to track the buttons /widgets that we want beside the plot - # annotate_button = pn.widgets.Button(name = "Annotate", button_type = "primary", width = 100) - # invert_button = pn.widgets.Button(name = "Invert", button_type = "primary", width = 100) - # # def invert(self, *args, **params): - # # data = -1 * data - # # invert_button.on_click(invert(event, data)) - # controls = pn.Column(annotate_button, - # invert_button, - # sizing_mode = "fixed", width = 200,) - # return controls def make_plots(obj): """ Gets the data and plots it. @@ -226,37 +143,3 @@ def make_plots(obj): return -def get_mth5_data_as_xarrays(selected_channels, file_paths): - """ - ToDo: - - This can be modified in future to support chunking read in - - interaction with the intake package belongs here. - - This function works on multiple mth5 files in sequence. Another way to do this - would to be to invert the two for loops so that the outer loop iterates over - selected_channels first and then a one-line function accesses the data for that - channel. - - Parameters - ---------- - selected_channels: list - file_paths - kwargs - - Returns - ------- - - """ - out_dict = {} - used_files = list_h5s_to_plot(selected_channels) - for file in used_files: - m = MTH5() - m.open_mth5(file_paths[file], mode = "r") - for selected_channel in selected_channels: - selected_file, survey, station, run, channel = parse_channel_path(selected_channel) - if selected_file == file: - data = m.get_channel(station, run, channel, survey=survey).to_channel_ts().to_xarray() - # data = data.rename(data.attrs["mth5_type"]): "ex"--> "Electric" - #self.xarrays.append(data) - out_dict[selected_channel] = data - m.close_mth5() - return out_dict