diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 8e60d920f74e966bd986811c33ebc97bb8a1fd6d..e327ad33bd245b6561f3aec6dca6d6d64af4603d 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -20,7 +20,7 @@ stages: - Build Env and Test before_script: - pip install -e .[dev] - - apt-get update && apt install -y libgl1-mesa-glx + - apt-get update && apt install -y libgl1-mesa-glx libxkbcommon-x11-0 libegl1 libdbus-1-3 flake8: image: python:3.8 diff --git a/setup.py b/setup.py index 339530a3b58b92191dc5402a8bd69ec0bc37adcd..d3ebbe37e3cfb977c71ff8756d45c84ef83534b4 100644 --- a/setup.py +++ b/setup.py @@ -32,7 +32,7 @@ setup( ], }, install_requires=['obspy', - 'PySide2', + 'PySide6', 'matplotlib', 'numpy'], setup_requires=[], diff --git a/sohstationviewer.py b/sohstationviewer.py index 296d459799da264f4256030e321e90a299ab8a16..20ecf79e04229f98c5880ddb30c23e8d81a59096 100755 --- a/sohstationviewer.py +++ b/sohstationviewer.py @@ -2,27 +2,16 @@ import platform import os import sys -from PySide2 import QtWidgets +from PySide6 import QtWidgets from sohstationviewer.view.main_window import MainWindow -# Enable Layer-backing for MacOs version >= 11 -# Only needed if using the pyside2 library with version>=5.15. -# Layer-backing is always enabled in pyside6. -os_name, version, *_ = platform.platform().split('-') -# if os_name == 'macOS' and version >= '11': -# mac OSX 11.6 appear to be 10.16 when read with python and still required this -# environment variable -if os_name == 'macOS': - os.environ['QT_MAC_WANTS_LAYER'] = '1' - - def main(): app = QtWidgets.QApplication(sys.argv) wnd = MainWindow() wnd.show() - sys.exit(app.exec_()) + sys.exit(app.exec()) if __name__ == '__main__': diff --git a/sohstationviewer/controller/processing.py b/sohstationviewer/controller/processing.py index d900d1c8a59f69595a08b0db476fcc8f60f11561..64c83a98680d6fd4b0585250062b4dccbf1924e0 100644 --- a/sohstationviewer/controller/processing.py +++ b/sohstationviewer/controller/processing.py @@ -9,9 +9,9 @@ import re from pathlib import Path from typing import List, Optional, Dict, Tuple -from PySide2.QtCore import QEventLoop, Qt -from PySide2.QtGui import QCursor -from PySide2.QtWidgets import QTextBrowser, QApplication +from PySide6.QtCore import QEventLoop, Qt +from PySide6.QtGui import QCursor +from PySide6.QtWidgets import QTextBrowser, QApplication from obspy.io import reftek from obspy import UTCDateTime @@ -183,9 +183,10 @@ def detect_data_type(list_of_dir: List[str]) -> Optional[str]: sign_chan_data_type_dict = get_signature_channels() dir_data_type_dict = {} - is_multiplex = None + is_multiplex_dict = {} for d in list_of_dir: data_type = "Unknown" + is_multiplex = None for path, subdirs, files in os.walk(d): for file_name in files: path2file = Path(path).joinpath(file_name) @@ -205,10 +206,12 @@ def detect_data_type(list_of_dir: List[str]) -> Optional[str]: if is_multiplex is None: raise Exception("No channel found for the data set") + is_multiplex_dict[d] = is_multiplex if data_type == "Unknown": dir_data_type_dict[d] = "Unknown" else: dir_data_type_dict[d] = data_type + is_multiplex_list = list(set(is_multiplex_dict.values())) data_type_list = list(set(dir_data_type_dict.values())) if len(data_type_list) > 1: dir_data_type_str = json.dumps(dir_data_type_dict) @@ -218,13 +221,17 @@ def detect_data_type(list_of_dir: List[str]) -> Optional[str]: f"{dir_data_type_str}\n\n" f"Please have only data that related to each other.") raise Exception(msg) - + elif len(is_multiplex_list) > 1: + msg = ("There are both multiplexed and non-multiplexed data " + "detected.\n\nPlease have only data that related to" + " each other.") + raise Exception(msg) elif data_type_list == ['Unknown']: msg = ("There are no known data detected.\n\n" "Do you want to cancel to select different folder(s)\n" "Or continue to read any available mseed file?") raise Exception(msg) - return data_type_list[0], is_multiplex + return data_type_list[0], is_multiplex_list[0] def get_data_type_from_file( diff --git a/sohstationviewer/controller/util.py b/sohstationviewer/controller/util.py index 0e46a24ab1b673918022c15dacbacce654c11bcf..d846fd0085071d27abcc1df70cf6a1db7f712fe3 100644 --- a/sohstationviewer/controller/util.py +++ b/sohstationviewer/controller/util.py @@ -9,10 +9,10 @@ from datetime import datetime from pathlib import Path from typing import List, Dict -from PySide2 import QtCore +from PySide6 import QtCore from typing import Tuple, Union -from PySide2.QtWidgets import QTextBrowser +from PySide6.QtWidgets import QTextBrowser from obspy import UTCDateTime from sohstationviewer.view.util.enums import LogType @@ -24,7 +24,6 @@ def validate_file(path2file: Union[str, Path], file_name: str): :param file_name: name of the file :return: True if pass checking, False if not. """ - if file_name.startswith('.'): # skip info file return False @@ -312,3 +311,21 @@ def check_data_sdata(root_dir: str) -> bool: dir_list = [d for d in os.listdir(root_dir) if os.path.isdir(os.path.join(root_dir, d))] return 'data' in dir_list and 'sdata' in dir_list + + +def get_valid_file_count(list_of_dir: Path) -> int: + """ + Get total number of valid files in valid directories from the list_of_dir + :param list_of_dir: list of paths to the folders of data + :return total: total number of valid files + """ + total = 0 + for folder in list_of_dir: + for path, _, files in os.walk(folder): + try: + validate_dir(path) + except Exception: + continue + total += len([f for f in files + if validate_file(Path(path).joinpath(f), f)]) + return total diff --git a/sohstationviewer/database/soh.db b/sohstationviewer/database/soh.db index 90430cd3be7ab2b25781a5f0d15c3f640e62b3a2..ad3f16f89d639912d745447ffd9203a951ee9e04 100755 Binary files a/sohstationviewer/database/soh.db and b/sohstationviewer/database/soh.db differ diff --git a/sohstationviewer/model/data_loader.py b/sohstationviewer/model/data_loader.py index a1bfb1a29f90cea2e3fd64e31d6cd0941a86e6df..4878728f08d64d117099b6ff8ebeba2f317ae05f 100644 --- a/sohstationviewer/model/data_loader.py +++ b/sohstationviewer/model/data_loader.py @@ -5,7 +5,7 @@ import traceback from pathlib import Path from typing import Union, List, Optional -from PySide2 import QtCore, QtWidgets +from PySide6 import QtCore, QtWidgets from sohstationviewer.conf import constants from sohstationviewer.controller.util import display_tracking_info @@ -27,7 +27,7 @@ class DataLoaderWorker(QtCore.QObject): def __init__(self, data_type: str, tracking_box: QtWidgets.QTextBrowser, is_multiplex: Optional[bool], - folder: str, list_of_rt130_paths: List[Path], + list_of_dir: List[Path], list_of_rt130_paths: List[Path], req_wf_chans: Union[List[str], List[int]] = [], req_soh_chans: List[str] = [], read_start: float = 0, gap_minimum: Optional[float] = None, @@ -38,7 +38,7 @@ class DataLoaderWorker(QtCore.QObject): self.data_type = data_type self.tracking_box = tracking_box self.is_multiplex = is_multiplex - self.folder = folder + self.list_of_dir = list_of_dir self.list_of_rt130_paths = list_of_rt130_paths self.req_wf_chans = req_wf_chans self.req_soh_chans = req_soh_chans @@ -57,6 +57,9 @@ class DataLoaderWorker(QtCore.QObject): self.end_msg = None def run(self): + folders = (self.list_of_rt130_paths + if self.list_of_dir == [''] else self.list_of_dir) + folders_str = ', '.join([dir.name for dir in folders]) try: if self.data_type == 'RT130': from sohstationviewer.model.reftek_data.reftek import RT130 @@ -71,7 +74,7 @@ class DataLoaderWorker(QtCore.QObject): type=QtCore.Qt.DirectConnection) data_object.__init__( self.data_type, self.tracking_box, - self.is_multiplex, self.folder, + self.is_multiplex, self.list_of_dir, self.list_of_rt130_paths, req_wf_chans=self.req_wf_chans, req_soh_chans=self.req_soh_chans, gap_minimum=self.gap_minimum, read_start=self.read_start, read_end=self.read_end, @@ -91,11 +94,11 @@ class DataLoaderWorker(QtCore.QObject): self.failed.emit() except Exception: fmt = traceback.format_exc() - self.end_msg = (f"Dir {self.folder} can't be read " + self.end_msg = (f"Some in {folders_str} can't be read " f"due to error: {str(fmt)}") self.failed.emit() else: - self.end_msg = f'Finished loading data stored in {self.folder}' + self.end_msg = f'Finished loading data stored in {folders_str}' self.finished.emit(data_object) @@ -155,7 +158,7 @@ class DataLoader(QtCore.QObject): data_type, tracking_box, is_multiplex, - list_of_dir[0], # Only work on one directory for now. + list_of_dir, list_of_rt130_paths, req_wf_chans=req_wf_chans, req_soh_chans=req_soh_chans, @@ -237,7 +240,7 @@ class DataLoader(QtCore.QObject): ) abort_button = msg_box.addButton(QtWidgets.QMessageBox.Abort) - msg_box.exec_() + msg_box.exec() if msg_box.clickedButton() == abort_button: # The default choice is the first item, so we default to it if the diff --git a/sohstationviewer/model/data_type_model.py b/sohstationviewer/model/data_type_model.py index 2f8c6a5dd21e56315dc7a9c6a6527002d3f5fd75..f10173697d2744ab31d25462da8102aab37c7aa9 100644 --- a/sohstationviewer/model/data_type_model.py +++ b/sohstationviewer/model/data_type_model.py @@ -7,8 +7,8 @@ from typing import Optional, Union, List, Tuple, Dict from obspy import UTCDateTime from obspy.core import Stream -from PySide2 import QtCore -from PySide2 import QtWidgets +from PySide6 import QtCore +from PySide6 import QtWidgets from sohstationviewer.controller.util import display_tracking_info from sohstationviewer.conf import constants diff --git a/sohstationviewer/model/decimator.py b/sohstationviewer/model/decimator.py index 7054bd8b0d1b5ccda4a48c2f575fb4c750a2025f..b9577163a3819def58da953c817eb06448c9bd59 100644 --- a/sohstationviewer/model/decimator.py +++ b/sohstationviewer/model/decimator.py @@ -1,4 +1,4 @@ -from PySide2 import QtCore +from PySide6 import QtCore from obspy import Trace # Global flag that determines whether the user requested to stop processing and diff --git a/sohstationviewer/model/downsampler.py b/sohstationviewer/model/downsampler.py index 3f1e5f139f52d87385dd42bb7b176191854912c8..03bf322454cd569c85bce2f1db97c27ab48e1dff 100644 --- a/sohstationviewer/model/downsampler.py +++ b/sohstationviewer/model/downsampler.py @@ -4,7 +4,7 @@ This module provides access to a class that loads data in a separate thread. import math import numpy as np -from PySide2 import QtCore +from PySide6 import QtCore from sohstationviewer.conf import constants as const diff --git a/sohstationviewer/model/general_data/general_data.py b/sohstationviewer/model/general_data/general_data.py index 4e8924e0e9a4ec239e5fd10ed8e61f7c9ceb2b5a..74b25dc74790f93d710d35cdd9e13d22726222a2 100644 --- a/sohstationviewer/model/general_data/general_data.py +++ b/sohstationviewer/model/general_data/general_data.py @@ -1,15 +1,17 @@ from __future__ import annotations - +import os from pathlib import Path from tempfile import TemporaryDirectory from typing import Optional, Union, List, Tuple, Dict +import traceback from obspy import UTCDateTime -from PySide2 import QtCore -from PySide2 import QtWidgets +from PySide6 import QtCore +from PySide6 import QtWidgets -from sohstationviewer.controller.util import display_tracking_info +from sohstationviewer.controller.util import \ + display_tracking_info, get_valid_file_count, validate_file, validate_dir from sohstationviewer.view.plotting.gps_plot.gps_point import GPSPoint from sohstationviewer.view.util.enums import LogType from sohstationviewer.database.process_db import execute_db @@ -36,7 +38,7 @@ class ThreadStopped(Exception): class GeneralData(): def __init__(self, data_type, tracking_box: Optional[QtWidgets.QTextBrowser] = None, - is_multiplex: bool = False, folder: str = '.', + is_multiplex: bool = False, list_of_dir: List[str] = [], list_of_rt130_paths: List[Path] = [], req_wf_chans: Union[List[str], List[int]] = [], req_soh_chans: List[str] = [], @@ -64,7 +66,8 @@ class GeneralData(): :param data_type: type of the object :param tracking_box: widget to display tracking info - :param folder: path to the folder of data + :param is_multiplex: flag showing if a file have more than one channel + :param list_of_dir: list of paths to the folders of data :param list_of_rt130_paths: path to the folders of RT130 data :param req_wf_chans: requested waveform channel list :param req_soh_chans: requested SOH channel list @@ -82,9 +85,9 @@ class GeneralData(): data loader is paused. """ self.data_type = data_type - self.is_multiplex = is_multiplex self.tracking_box = tracking_box - self.dir = folder + self.is_multiplex = is_multiplex + self.list_of_dir = list_of_dir self.list_of_rt130_paths = list_of_rt130_paths self.req_soh_chans = req_soh_chans self.req_wf_chans = req_wf_chans @@ -95,7 +98,6 @@ class GeneralData(): self.include_mp456uvw = include_mp456uvw self.rt130_waveform_data_req = rt130_waveform_data_req self.on_unittest = on_unittest - if creator_thread is None: err_msg = ( 'A signal is not None while running in main thread' @@ -158,15 +160,6 @@ class GeneralData(): self.gps_points: List[GPSPoint] = [] - def read_folder(self, folder: str) -> Tuple[Dict]: - """ - FROM data_type_model.DataTypeModel.read_folder - Read data from given folder - :param folder: path to folder to read data - :return: Tuple of different data dicts - """ - pass - def select_key(self) -> Union[str, Tuple[str, str]]: """ FROM data_type_model.DataTypeModel.select_key @@ -182,8 +175,7 @@ class GeneralData(): if self.creator_thread.isInterruptionRequested(): raise ThreadStopped() - self.read_folder(self.dir) - + self.read_folders() self.selected_key = self.select_key() self.fill_empty_data() @@ -191,6 +183,65 @@ class GeneralData(): raise ThreadStopped() self.finalize_data() + def read_folders(self, folders) -> None: + """ + Read data from given folders to create data dicts which are + attributes of current class + """ + count = 0 + total = get_valid_file_count(folders) + for folder in folders: + count = self.read_folder(folder, total, count) + + def read_folder(self, folder: str, total: int, count: int) -> int: + """ + Read data from current folder. + + :param folder: folder to read data from + :param total: total of all valid files + :param count: total of files that have been processed before this + folder to keep track of progress + :return count: total of files that have been processed after this + folder to keep track of progress + """ + if not os.path.isdir(folder): + raise ProcessingDataError(f"Path '{folder}' not exist") + + for path, sub_dirs, files in os.walk(folder): + try: + validate_dir(path) + except Exception as e: + # skip Information folder + self.track_info(str(e), LogType.WARNING) + continue + for file_name in files: + if self.creator_thread.isInterruptionRequested(): + raise ThreadStopped() + + path2file = Path(path).joinpath(file_name) + if not validate_file(path2file, file_name): + continue + count += 1 + try: + self.read_file(path2file, file_name, count, total) + except Exception: + fmt = traceback.format_exc() + self.track_info(f"Skip file {path2file} can't be read " + f"due to error: {str(fmt)}", + LogType.WARNING) + return count + + def read_file(self, path2file: Path, file_name: str, + count: int, total: int) -> None: + """ + Read data or text from file + :param path2file: absolute path to file + :param file_name: name of file + :param count: total number of file read + :param total: total number of all valid files + """ + pass + def finalize_data(self): """ CHANGED FROM data_type_model.Data_Type_Model.finalize_data diff --git a/sohstationviewer/model/mseed_data/mseed.py b/sohstationviewer/model/mseed_data/mseed.py index bcc61e33a4a32a965069dc700e2008b967d5e080..58acbf31c86335f095ec46dd3017a8c0e9ea4456 100644 --- a/sohstationviewer/model/mseed_data/mseed.py +++ b/sohstationviewer/model/mseed_data/mseed.py @@ -3,15 +3,12 @@ MSeed object to hold and process MSeed data """ import os import re -import traceback from pathlib import Path -from typing import Dict, Tuple, List +from typing import Dict, List -from sohstationviewer.controller.util import validate_file, validate_dir from sohstationviewer.view.util.enums import LogType -from sohstationviewer.model.general_data.general_data import \ - GeneralData, ThreadStopped, ProcessingDataError +from sohstationviewer.model.general_data.general_data import GeneralData from sohstationviewer.model.general_data.general_data_helper import read_text from sohstationviewer.model.mseed_data.mseed_helper import \ @@ -31,6 +28,8 @@ class MSeed(GeneralData): # FROM mseed.mseed.MSEED.__init__ super().__init__(*args, **kwargs) self.nets_by_sta: Dict[str, List[str]] = {} + self.invalid_blockettes = False + self.not_mseed_files = [] self.processing_data() def finalize_data(self): @@ -46,83 +45,55 @@ class MSeed(GeneralData): self.retrieve_nets_from_data_dicts() super().finalize_data() - def read_folder(self, folder: str) -> Tuple[Dict]: + def read_folders(self) -> None: """ - CHANGED FROM mseed.mseed.MSEED.read_folder - - Read data streams for soh, mass position and waveform. - - :param folder: absolute path to data set folder - :return waveform_data: waveform data by station - :return soh_data: soh data by station - :return mass_pos_data: mass position data by station - :return gaps: gap list by station - :return nets_by_sta: netcodes list by station + Read data from list_of_dir for soh, mass position and waveform. """ - if not os.path.isdir(folder): - raise ProcessingDataError(f"Path '{folder}' not exist") - count = 0 - - total = sum([len(files) for _, _, files in os.walk(folder)]) - invalid_blockettes = False - not_mseed_files = [] - for path, sub_dirs, files in os.walk(folder): - try: - validate_dir(path) - except Exception as e: - # skip Information folder - self.track_info(str(e), LogType.WARNING) - continue - for file_name in files: - if self.creator_thread.isInterruptionRequested(): - raise ThreadStopped() - - path2file = Path(path).joinpath(file_name) - - if not validate_file(path2file, file_name): - continue - count += 1 - if count % 10 == 0: - self.track_info( - f'Read {count} files/{total}', LogType.INFO) - log_text = read_text(path2file) - if log_text is not None: - self.log_texts[path2file] = log_text - continue - reader = MSeedReader( - path2file, - read_start=self.read_start, - read_end=self.read_end, - is_multiplex=self.is_multiplex, - req_soh_chans=self.req_soh_chans, - req_wf_chans=self.req_wf_chans, - include_mp123zne=self.include_mp123zne, - include_mp456uvw=self.include_mp456uvw, - soh_data=self.soh_data, - mass_pos_data=self.mass_pos_data, - waveform_data=self.waveform_data, - log_data=self.log_data, - gap_minimum=self.gap_minimum) - try: - reader.read() - invalid_blockettes = (invalid_blockettes - or reader.invalid_blockettes) - except MSeedReadError: - not_mseed_files.append(file_name) - except Exception: - fmt = traceback.format_exc() - self.track_info(f"Skip file {path2file} can't be read " - f"due to error: {str(fmt)}", - LogType.WARNING) - if not_mseed_files: + super().read_folders(self.list_of_dir) + if self.not_mseed_files: self.track_info( - f"Not MSeed files: {not_mseed_files}", LogType.WARNING) - if invalid_blockettes: + f"Not MSeed files: {self.not_mseed_files}", LogType.WARNING) + if self.invalid_blockettes: # This check to only print out message once print("We currently only handle blockettes 500, 1000," " and 1001.") - self.track_info( - f'Skipped {total - count} invalid files.', LogType.INFO) + + def read_file(self, path2file: Path, file_name: str, + count: int, total: int) -> None: + """ + Read data or text from file + :param path2file: absolute path to file + :param file_name: name of file + :param count: total number of file read + :param total: total number of all valid files + """ + if count % 10 == 0: + self.track_info( + f'Read {count}/{total} files', LogType.INFO) + log_text = read_text(path2file) + if log_text is not None: + self.log_texts[path2file] = log_text + return + reader = MSeedReader( + path2file, + read_start=self.read_start, + read_end=self.read_end, + is_multiplex=self.is_multiplex, + req_soh_chans=self.req_soh_chans, + req_wf_chans=self.req_wf_chans, + include_mp123zne=self.include_mp123zne, + include_mp456uvw=self.include_mp456uvw, + soh_data=self.soh_data, + mass_pos_data=self.mass_pos_data, + waveform_data=self.waveform_data, + log_data=self.log_data, + gap_minimum=self.gap_minimum) + try: + reader.read() + self.invalid_blockettes = (self.invalid_blockettes + or reader.invalid_blockettes) + except MSeedReadError: + self.not_mseed_files.append(file_name) def retrieve_nets_from_data_dicts(self): """ diff --git a/sohstationviewer/model/reftek_data/log_info.py b/sohstationviewer/model/reftek_data/log_info.py index 70e5a111fd5a5a8d2ec43c25d69c4b56f0a4a0ff..486efcb069cedebd70ce136d9f5bb2bdcb9d1b98 100644 --- a/sohstationviewer/model/reftek_data/log_info.py +++ b/sohstationviewer/model/reftek_data/log_info.py @@ -1,10 +1,12 @@ from __future__ import annotations -from typing import Callable, Tuple, List, Union, Set, TYPE_CHECKING +from typing import Callable, Tuple, List, Union, Set, Dict, TYPE_CHECKING from sohstationviewer.conf import constants from sohstationviewer.controller.util import ( get_time_6, get_time_4, get_val, rtn_pattern) from sohstationviewer.view.util.enums import LogType +from sohstationviewer.model.reftek_data.log_info_helper import ( + remove_question_marks) if TYPE_CHECKING: from sohstationviewer.model.reftek_data.reftek import RT130 @@ -33,7 +35,7 @@ class LogInfo(): self.key = key self.unit_id, self.exp_no = key self.req_data_streams = req_data_streams - self.req_soh_chans = req_soh_chans + self.no_question_req_soh_chans = remove_question_marks(req_soh_chans) self.is_log_file = is_log_file """ track_year to add year to time since year time not include year after @@ -49,7 +51,7 @@ class LogInfo(): self.model = "72A" self.max_epoch = 0 self.min_epoch = constants.HIGHEST_INT - self.chans: dict = self.parent.soh_data[self.key] + self.chans: Dict = self.parent.soh_data[self.key] self.cpu_vers: Set[str] = set() self.gps_vers: Set[str] = set() self.extract_info() @@ -314,9 +316,10 @@ class LogInfo(): :param d: float - value of data :param idx: int - index of SOH message line """ - - if self.req_soh_chans and chan_id not in self.req_soh_chans: + if self.no_question_req_soh_chans and not chan_id.startswith( + tuple(self.no_question_req_soh_chans)): return + if chan_id not in self.chans: self.chans[chan_id] = { 'reftek': True, diff --git a/sohstationviewer/model/reftek_data/log_info_helper.py b/sohstationviewer/model/reftek_data/log_info_helper.py new file mode 100644 index 0000000000000000000000000000000000000000..3c3180ed08ab37f48b8d614f909141a5c75c956b --- /dev/null +++ b/sohstationviewer/model/reftek_data/log_info_helper.py @@ -0,0 +1,14 @@ +def remove_question_marks(req_soh_chan): + """ + Remove the question marks in the channel request list, req_soh_chan + :param req_soh_chan: soh channel request list + :return no_question_req_soh_chan: the channel request list with the + question marks removed + """ + no_question_req_soh_chan = [] + for idx, req in enumerate(req_soh_chan): + if req.endswith('?'): + no_question_req_soh_chan.append(req_soh_chan[idx][:-1]) + else: + no_question_req_soh_chan.append(req_soh_chan[idx]) + return no_question_req_soh_chan diff --git a/sohstationviewer/model/reftek_data/reftek.py b/sohstationviewer/model/reftek_data/reftek.py index aac4339467c115eeaf2751fe130a9bacef6a2f78..c60514f9dd52c7bee284c17dac7b73ade78e6568 100755 --- a/sohstationviewer/model/reftek_data/reftek.py +++ b/sohstationviewer/model/reftek_data/reftek.py @@ -1,7 +1,6 @@ """ RT130 object to hold and process RefTek data """ -import os from pathlib import Path from typing import Union, List, Tuple, Dict import traceback @@ -9,7 +8,6 @@ import numpy as np from obspy.core import Stream from sohstationviewer.conf import constants -from sohstationviewer.controller.util import validate_file from sohstationviewer.view.util.enums import LogType from sohstationviewer.model.general_data.general_data import \ @@ -64,13 +62,8 @@ class RT130(GeneralData): def processing_data(self): if self.creator_thread.isInterruptionRequested(): raise ThreadStopped() - self.read_folder(self.dir) - - if self.creator_thread.isInterruptionRequested(): - raise ThreadStopped() + self.read_folders() self.selected_key = self.select_key() - if self.selected_key is None: - raise ThreadStopped() if self.creator_thread.isInterruptionRequested(): raise ThreadStopped() @@ -109,51 +102,34 @@ class RT130(GeneralData): # this happens when there is text or ascii only in the data self.data_time[key] = [self.read_start, self.read_end] - def read_folder(self, folder: str) -> None: + def read_folders(self) -> None: """ - Loop all files in dir/list_of_rt130_paths to read for soh data, - mass position data and - index waveform data with filename and corresponding time range - - :param folder: absolute path to data set folder + Read data from list_of_dir or list_of_rt130_paths for soh, + mass position and waveform. """ - count = 0 - - total = 0 if self.list_of_rt130_paths != []: folders = self.list_of_rt130_paths - for folder in folders: - total += sum([len(files) for _, _, files in os.walk(folder)]) else: - folders = [self.dir] - total = sum([len(files) for _, _, files in os.walk(self.dir)]) - - for folder in folders: - if not os.path.isdir(folder): - raise ProcessingDataError(f"Path '{folder}' not exist") - for path, subdirs, files in os.walk(folder): - for file_name in files: - if self.creator_thread.isInterruptionRequested(): - raise ThreadStopped() - path2file = Path(path).joinpath(file_name) - if not validate_file(path2file, file_name): - continue - try: - if not self.read_reftek_130(path2file): - read_text(path2file, self.log_data['TEXT']) - except Exception: - fmt = traceback.format_exc() - self.track_info(f"Skip file {path2file} can't be read " - f"due to error: {str(fmt)}", - LogType.WARNING) - - count += 1 - if count % 50 == 0: - self.track_info( - f"Read {count} files/ {total}", LogType.INFO) - self.total_datafile = count - self.track_info( - f'Skipped {total - count} invalid files.', LogType.INFO) + folders = self.list_of_dir + super().read_folders(folders) + + def read_file(self, path2file: Path, file_name: str, + count: int, total: int) -> None: + """ + Read data or text from file + :param path2file: absolute path to file + :param file_name: name of file + :param count: total number of file read + :param total: total number of all valid files + """ + if count % 50 == 0: + self.track_info( + f"Read {count}/{total} files", LogType.INFO) + log_text = read_text(path2file) + if log_text is not None: + self.log_texts['TEXT'].append(log_text) + return + self.read_reftek_130(path2file) def select_key(self) -> Tuple[str, str]: """ @@ -174,7 +150,7 @@ class RT130(GeneralData): raise ProcessingDataError(msg) selected_key = keys[0] - if len(keys) > 1: + if not self.on_unittest and len(keys) > 1: msg = ("There are more than one keys in the given data.\n" "Please select one to display") self.pause_signal.emit(msg, keys) diff --git a/sohstationviewer/view/calendar/calendar_dialog.py b/sohstationviewer/view/calendar/calendar_dialog.py index ceb2e9ea16242dcc118b2255bb1c9203b442075a..48a491bbf2af868bd9d7425ab3e9ec2b9e3ab718 100644 --- a/sohstationviewer/view/calendar/calendar_dialog.py +++ b/sohstationviewer/view/calendar/calendar_dialog.py @@ -1,4 +1,4 @@ -from PySide2 import QtWidgets +from PySide6 import QtWidgets from sohstationviewer.view.ui.calendar_ui_qtdesigner import Ui_CalendarDialog diff --git a/sohstationviewer/view/calendar/calendar_widget.py b/sohstationviewer/view/calendar/calendar_widget.py index 07996636a2233d8ca31fe11f1e9408a3b9a20e2f..87e1343688cc661d82e14ebd40d577748a261a57 100644 --- a/sohstationviewer/view/calendar/calendar_widget.py +++ b/sohstationviewer/view/calendar/calendar_widget.py @@ -1,4 +1,4 @@ -from PySide2 import QtCore, QtGui, QtWidgets +from PySide6 import QtCore, QtGui, QtWidgets class CalendarWidget(QtWidgets.QCalendarWidget): diff --git a/sohstationviewer/view/channel_prefer_dialog.py b/sohstationviewer/view/channel_prefer_dialog.py index 88a05c192b2e94f1cfa8daa1fc481b4116e81d93..dab6f8b103a93c80c39d7a8947b4fd52f5e452cb 100755 --- a/sohstationviewer/view/channel_prefer_dialog.py +++ b/sohstationviewer/view/channel_prefer_dialog.py @@ -1,8 +1,8 @@ from typing import Dict, List, Union from pathlib import Path -from PySide2 import QtWidgets, QtCore -from PySide2.QtWidgets import QDialogButtonBox, QDialog, QPlainTextEdit, \ +from PySide6 import QtWidgets, QtCore +from PySide6.QtWidgets import QDialogButtonBox, QDialog, QPlainTextEdit, \ QMainWindow from sohstationviewer.database.process_db import ( @@ -48,7 +48,7 @@ class InputDialog(QDialog): class ChannelPreferDialog(OneWindowAtATimeDialog): - def __init__(self, parent: QMainWindow, dir_names: List[Path]): + def __init__(self, parent: QMainWindow, list_of_dir: List[Path]): """ Dialog to create lists of preferred SOH channels that users want to select for plotting. @@ -57,11 +57,11 @@ class ChannelPreferDialog(OneWindowAtATimeDialog): :param parent: widget that calls this plotting widget - :param dir_names: list of absolute paths of data sets + :param list_of_dir: list of absolute paths of data sets """ super(ChannelPreferDialog, self).__init__() self.parent = parent - self.dir_names = dir_names + self.list_of_dir = list_of_dir self.setWindowTitle("SOH Channel Preferences") self.setGeometry(100, 100, 1100, 800) main_layout = QtWidgets.QVBoxLayout() @@ -91,7 +91,7 @@ class ChannelPreferDialog(OneWindowAtATimeDialog): """ self.add_db_chan_btn: Union[QtWidgets.QPushButton, None] = None """ - scan_chan_btn: Button to scan through all channels from dir_names + scan_chan_btn: Button to scan through all channels from list_of_dir folder for the list of channels and data type to add to the current row """ @@ -198,7 +198,7 @@ class ChannelPreferDialog(OneWindowAtATimeDialog): self.scan_chan_btn = QtWidgets.QPushButton( self, text='Scan Channels from MSeed Data') self.scan_chan_btn.clicked.connect(self.scan_channels) - if self.dir_names == [] or self.parent.data_type == "RT130": + if self.list_of_dir == [] or self.parent.data_type == "RT130": self.scan_chan_btn.setEnabled(False) h_layout.addWidget(self.scan_chan_btn) @@ -361,7 +361,7 @@ class ChannelPreferDialog(OneWindowAtATimeDialog): def edit_soh_list(self, row_idx): soh_list_item = self.soh_list_table_widget.item(row_idx, COL['IDs']) edit_dialog = InputDialog(text=soh_list_item.text()) - if edit_dialog.exec_(): + if edit_dialog.exec(): soh_list_item.setText(edit_dialog.get_input()) @QtCore.Slot() @@ -431,7 +431,7 @@ class ChannelPreferDialog(OneWindowAtATimeDialog): @QtCore.Slot() def scan_channels(self): """ - Scan for all available channels in folder self.dir_names to add to + Scan for all available channels in self.list_of_dir to add to preferred channel list. For RT130, all SOH channels are kept in a log file. It will be more reasonable to get channels from DB because the task of getting @@ -457,15 +457,17 @@ class ChannelPreferDialog(OneWindowAtATimeDialog): if result == QtWidgets.QMessageBox.Ok: self.add_db_channels() else: + self.scan_chan_btn.setEnabled(True) # clear all key radio buttons for i in reversed(range(self.data_set_key_layout.count())): widget = self.data_set_key_layout.takeAt(i).widget() if widget is not None: widget.setParent(None) self.channel_info = read_mseed_channels( - self.tracking_info_text_browser, self.dir_names, is_multiplex) + self.tracking_info_text_browser, + self.list_of_dir, is_multiplex) if len(self.channel_info) == 0: - msg = "No data can be read from " + ', '.join(self.dir_names) + msg = "No data can be read from " + ', '.join(self.list_of_dir) return QtWidgets.QMessageBox.warning(self, "No data", msg) self.scan_chan_btn.setEnabled(True) self.data_set_key_layout.addWidget(QtWidgets.QLabel("Data Key:")) diff --git a/sohstationviewer/view/date_edit_with_doy.py b/sohstationviewer/view/date_edit_with_doy.py index 5fc3ea5bd23128577da8b96d217474e783bb9b4e..a93fa143ade745f2250bc0a71a2f9567695b27e1 100644 --- a/sohstationviewer/view/date_edit_with_doy.py +++ b/sohstationviewer/view/date_edit_with_doy.py @@ -1,9 +1,9 @@ -from PySide2.QtCore import Qt, QDate -from PySide2.QtGui import ( - QKeyEvent, QWheelEvent, QContextMenuEvent, +from PySide6.QtCore import Qt, QDate +from PySide6.QtGui import ( + QKeyEvent, QWheelEvent, QContextMenuEvent, QAction ) -from PySide2.QtWidgets import ( - QDateEdit, QLineEdit, QMenu, QAction, +from PySide6.QtWidgets import ( + QDateEdit, QLineEdit, QMenu, ) @@ -86,7 +86,7 @@ class DayOfYearSupportedDateTextBox(QDateEdit): menu.addAction(copy_action) menu.addAction(select_all_action) - menu.exec_(event.globalPos()) + menu.exec(event.globalPos()) def setDisplayFormat(self, format): """ diff --git a/sohstationviewer/view/db_config/db_config_dialog.py b/sohstationviewer/view/db_config/db_config_dialog.py index 151fe6bb19897967c8e5bfd4382f975261cf6d0f..7c6b1fd3cc8035a9ad64b35da90e3d6f9a460009 100755 --- a/sohstationviewer/view/db_config/db_config_dialog.py +++ b/sohstationviewer/view/db_config/db_config_dialog.py @@ -1,7 +1,7 @@ from __future__ import annotations from typing import Set, Dict -from PySide2 import QtWidgets, QtGui, QtCore +from PySide6 import QtWidgets, QtGui, QtCore from sohstationviewer.database.process_db import execute_db from sohstationviewer.view.util.one_instance_at_a_time import \ @@ -268,7 +268,7 @@ class UiDBInfoDialog(OneWindowAtATimeDialog): msgbox.setText(msg) msgbox.addButton(QtWidgets.QMessageBox.Cancel) msgbox.addButton('Continue', QtWidgets.QMessageBox.YesRole) - result = msgbox.exec_() + result = msgbox.exec() if result == QtWidgets.QMessageBox.Cancel: return self.close() diff --git a/sohstationviewer/view/db_config/param_dialog.py b/sohstationviewer/view/db_config/param_dialog.py index 21ecf7bcca7316e30a6b5e7253d7f1ce19ef400b..2a5b6f1312c7aec2efa3f04b3d7d5514bccc2638 100755 --- a/sohstationviewer/view/db_config/param_dialog.py +++ b/sohstationviewer/view/db_config/param_dialog.py @@ -5,9 +5,9 @@ NOTE: Cannot remove or change params that are already used for channels. """ from typing import List -from PySide2 import QtWidgets, QtCore -from PySide2.QtCore import Qt -from PySide2.QtWidgets import QComboBox, QWidget +from PySide6 import QtWidgets, QtCore +from PySide6.QtCore import Qt +from PySide6.QtWidgets import QComboBox, QWidget from sohstationviewer.conf.constants import ColorMode, ALL_COLOR_MODES from sohstationviewer.view.util.plot_func_names import plot_functions diff --git a/sohstationviewer/view/file_information/file_info_widget.py b/sohstationviewer/view/file_information/file_info_widget.py index 8c523ef13b937d0d755884652822cfd42228fa20..e6d155e0ec9f046b4e65eae551105edb1b07e33f 100644 --- a/sohstationviewer/view/file_information/file_info_widget.py +++ b/sohstationviewer/view/file_information/file_info_widget.py @@ -1,9 +1,9 @@ -from PySide2 import QtCore -from PySide2.QtGui import ( +from PySide6 import QtCore +from PySide6.QtGui import ( QContextMenuEvent, QGuiApplication, QKeySequence, - QKeyEvent, + QKeyEvent, QAction ) -from PySide2.QtWidgets import QListWidget, QMenu, QAction +from PySide6.QtWidgets import QListWidget, QMenu class FileInfoWidget(QListWidget): @@ -27,7 +27,7 @@ class FileInfoWidget(QListWidget): menu.addAction(copy_action) menu.addAction(select_all_action) - menu.exec_(event.globalPos()) + menu.exec(event.globalPos()) def keyPressEvent(self, event: QKeyEvent) -> None: if event.matches(QKeySequence.Copy): diff --git a/sohstationviewer/view/file_information/get_file_information.py b/sohstationviewer/view/file_information/get_file_information.py index aa3b3dd8142ee77dcf7c066a169d80234cbde8e8..a1dab80d1225d0566b0782b993075c271def926d 100644 --- a/sohstationviewer/view/file_information/get_file_information.py +++ b/sohstationviewer/view/file_information/get_file_information.py @@ -28,8 +28,11 @@ def extract_data_set_info(data_obj: Union[GeneralData, RT130, MSeed], """ data_set_info = {} - sources_read = data_obj.dir - data_set_info['Sources Read'] = sources_read.name + folders = (data_obj.list_of_rt130_paths + if data_obj.list_of_dir == [''] else data_obj.list_of_dir) + + sources_read = ', '.join([dir.name for dir in folders]) + data_set_info['Sources Read'] = sources_read data_type = data_obj.data_type data_set_info['Data Type'] = data_type diff --git a/sohstationviewer/view/file_list_widget.py b/sohstationviewer/view/file_list_widget.py index 8af3da9fe8c01d1d385ccd8ab12e26a9a52b60f8..37389846888f619fabc8d5822f705d8134e79a6f 100644 --- a/sohstationviewer/view/file_list_widget.py +++ b/sohstationviewer/view/file_list_widget.py @@ -1,4 +1,4 @@ -from PySide2 import QtWidgets +from PySide6 import QtWidgets class FileListItem(QtWidgets.QListWidgetItem): diff --git a/sohstationviewer/view/help_view.py b/sohstationviewer/view/help_view.py index d66da2f13c1b7c5189140afbe6a0e37d5783ff93..a8618b0504b339e68c4151295843376ec4b61bd5 100644 --- a/sohstationviewer/view/help_view.py +++ b/sohstationviewer/view/help_view.py @@ -2,8 +2,8 @@ import sys from pathlib import Path from typing import Tuple, Callable, Union, List, Dict -from PySide2 import QtCore, QtGui, QtWidgets -from PySide2.QtWidgets import QStyle +from PySide6 import QtCore, QtGui, QtWidgets +from PySide6.QtWidgets import QStyle from sohstationviewer.view.util.functions import ( create_search_results_file, create_table_of_content_file) @@ -538,7 +538,7 @@ def main(): wnd = HelpBrowser(home_path='../../') wnd.show() - sys.exit(app.exec_()) + sys.exit(app.exec()) if __name__ == '__main__': diff --git a/sohstationviewer/view/main_window.py b/sohstationviewer/view/main_window.py index 57ac482cb87e2690a07e25e998a5f859b19437c8..e95ed3ff8ad3101df0c1acaaf66f06d425acfa2e 100755 --- a/sohstationviewer/view/main_window.py +++ b/sohstationviewer/view/main_window.py @@ -5,10 +5,10 @@ from datetime import datetime from typing import List, Tuple, Union, Dict from pathlib import Path -from PySide2 import QtCore, QtWidgets, QtGui -from PySide2.QtCore import QSize -from PySide2.QtGui import QFont, QPalette, QColor -from PySide2.QtWidgets import QFrame, QListWidgetItem, QMessageBox +from PySide6 import QtCore, QtWidgets, QtGui +from PySide6.QtCore import QSize +from PySide6.QtGui import QFont, QPalette, QColor +from PySide6.QtWidgets import QFrame, QListWidgetItem, QMessageBox from sohstationviewer.model.data_loader import DataLoader from sohstationviewer.model.general_data.general_data import \ @@ -60,7 +60,7 @@ class MainWindow(QtWidgets.QMainWindow, UIMainWindow): """ dir_names: list of absolute paths of data sets """ - self.dir_names: List[Path] = [] + self.list_of_dir: List[Path] = [] """ current_dir: the current main data directory """ @@ -277,7 +277,7 @@ class MainWindow(QtWidgets.QMainWindow, UIMainWindow): front. """ try: - self.read_from_file_list() + self.get_file_list() except Exception as e: msg = f"{str(e)}!!!\n\nDo you want to continue?" result = QtWidgets.QMessageBox.question( @@ -286,7 +286,7 @@ class MainWindow(QtWidgets.QMainWindow, UIMainWindow): if result == QtWidgets.QMessageBox.No: return try: - win = ChannelPreferDialog(self, self.dir_names) + win = ChannelPreferDialog(self, self.list_of_dir) win.show() except DialogAlreadyOpenedError: ChannelPreferDialog.current_instance.activateWindow() @@ -393,7 +393,7 @@ class MainWindow(QtWidgets.QMainWindow, UIMainWindow): fd = QtWidgets.QFileDialog(self) fd.setFileMode(QtWidgets.QFileDialog.Directory) fd.setDirectory(self.curr_dir_line_edit.text()) - fd.exec_() + fd.exec() try: new_path = fd.selectedFiles()[0] self.set_current_directory(new_path) @@ -470,13 +470,13 @@ class MainWindow(QtWidgets.QMainWindow, UIMainWindow): return (self.pref_soh_list if not self.all_soh_chans_check_box.isChecked() else []) - def read_from_file_list(self): + def get_file_list(self): """ Read from self.open_files_list to identify self.dir_names/self.selected_rt130_paths. self.data_type is identified here too. """ - self.dir_names = [''] + self.list_of_dir = [''] self.selected_rt130_paths = [] root_dir = Path(self.curr_dir_line_edit.text()) if self.rt130_das_dict != {}: @@ -494,28 +494,29 @@ class MainWindow(QtWidgets.QMainWindow, UIMainWindow): # displayed in File List box. The current dir (root_dir) will be # the only one of the selected dir for user to start processing # from. - self.dir_names = [root_dir] + self.list_of_dir = [root_dir] if self.data_radio_button.isEnabled(): # In case of Baler's data, there will be 2 data folders for # user to select from, data/ and sdata/. The radio buttons # data and sdata allow user to select from Main Window. if self.data_radio_button.isChecked(): - self.dir_names = [root_dir.joinpath('data')] + self.list_of_dir = [root_dir.joinpath('data')] else: - self.dir_names = [root_dir.joinpath('sdata')] + self.list_of_dir = [root_dir.joinpath('sdata')] else: # When "From Data Card" checkbox is checked, sub directories of the # current dir (root_dir) will be displayed in File List box. # User can select one or more sub-directories to process from. - self.dir_names = [root_dir.joinpath(item.text()) - for item in self.open_files_list.selectedItems()] - if self.dir_names == []: + self.list_of_dir = [ + root_dir.joinpath(item.text()) + for item in self.open_files_list.selectedItems()] + if self.list_of_dir == []: msg = "No directories have been selected." raise Exception(msg) if self.rt130_das_dict == {}: self.data_type, self.is_multiplex = detect_data_type( - self.dir_names) + self.list_of_dir) def clear_plots(self): self.plotting_widget.clear() @@ -601,7 +602,7 @@ class MainWindow(QtWidgets.QMainWindow, UIMainWindow): pass try: - self.read_from_file_list() + self.get_file_list() except Exception as e: if 'no known data detected' in str(e): msgbox = QtWidgets.QMessageBox() @@ -609,7 +610,7 @@ class MainWindow(QtWidgets.QMainWindow, UIMainWindow): msgbox.setText(str(e)) msgbox.addButton(QtWidgets.QMessageBox.Cancel) msgbox.addButton('Continue', QtWidgets.QMessageBox.YesRole) - result = msgbox.exec_() + result = msgbox.exec() if result == QtWidgets.QMessageBox.Cancel: self.cancel_loading() return @@ -634,7 +635,7 @@ class MainWindow(QtWidgets.QMainWindow, UIMainWindow): QMessageBox.Abort) data_too_big_dialog.setDefaultButton(QMessageBox.Abort) data_too_big_dialog.setIcon(QMessageBox.Question) - ret = data_too_big_dialog.exec_() + ret = data_too_big_dialog.exec() if ret == QMessageBox.Abort: self.cancel_loading() return @@ -654,15 +655,15 @@ class MainWindow(QtWidgets.QMainWindow, UIMainWindow): self.end_tm = datetime.strptime(end_tm_str, TM_FORMAT).timestamp() self.gps_dialog.clear_plot() - self.gps_dialog.data_path = self.dir_names[0] - self.gps_dialog.set_export_directory(str(self.dir_names[0])) + self.gps_dialog.data_path = self.curr_dir_line_edit.text() + self.gps_dialog.set_export_directory(self.curr_dir_line_edit.text()) self.gps_dialog.gps_points = [] self.data_loader.init_loader( self.data_type, self.tracking_info_text_browser, self.is_multiplex, - self.dir_names, + self.list_of_dir, self.selected_rt130_paths, req_wf_chans=self.req_wf_chans, req_soh_chans=self.req_soh_chans, @@ -828,8 +829,7 @@ class MainWindow(QtWidgets.QMainWindow, UIMainWindow): self.is_plotting_tps = True peer_plotting_widgets.append(self.tps_dlg.plotting_widget) self.tps_dlg.set_data( - self.data_type, - ','.join([str(d) for d in self.dir_names])) + self.data_type, ','.join([str(d) for d in self.list_of_dir])) self.tps_dlg.show() # The waveform and TPS plots is being stopped at the same time, so # we can't simply reset all flags. Instead, we use an intermediate @@ -850,8 +850,7 @@ class MainWindow(QtWidgets.QMainWindow, UIMainWindow): # waveformPlot peer_plotting_widgets.append(self.waveform_dlg.plotting_widget) self.waveform_dlg.set_data( - self.data_type, - ','.join([str(d) for d in self.dir_names])) + self.data_type, ','.join([str(d) for d in self.list_of_dir])) self.waveform_dlg.show() waveform_widget = self.waveform_dlg.plotting_widget waveform_widget.stopped.connect(self.reset_is_plotting_waveform) @@ -1099,7 +1098,7 @@ class MainWindow(QtWidgets.QMainWindow, UIMainWindow): print(f"DEVELOPING ERROR: Type of form must be 'QWidget' instead " f"of '{form.__class__.__name__}'") if form not in self.forms_in_forms_menu: - action = QtWidgets.QAction(form_name, self) + action = QtGui.QAction(form_name, self) self.forms_menu.addAction(action) action.triggered.connect(lambda: self.raise_form(form)) self.forms_in_forms_menu.append(form) diff --git a/sohstationviewer/view/plotting/gps_plot/gps_dialog.py b/sohstationviewer/view/plotting/gps_plot/gps_dialog.py index e2221bcf9f8892ec766d28eca8a04244c25a44e7..96e5d1fd50172af028c73e2000d3801c9ae234c0 100644 --- a/sohstationviewer/view/plotting/gps_plot/gps_dialog.py +++ b/sohstationviewer/view/plotting/gps_plot/gps_dialog.py @@ -5,7 +5,7 @@ import traceback from pathlib import Path from typing import List, Optional, Literal, Iterable -from PySide2 import QtWidgets, QtCore +from PySide6 import QtWidgets, QtCore from matplotlib.axes import Axes from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg as Canvas from matplotlib.figure import Figure @@ -326,7 +326,7 @@ class GPSDialog(QtWidgets.QWidget): fd = QtWidgets.QFileDialog(self) fd.setFileMode(QtWidgets.QFileDialog.Directory) fd.setDirectory(self.export_dir_textbox.text()) - fd.exec_() + fd.exec() new_path = fd.selectedFiles()[0] self.set_export_directory(new_path) @@ -432,4 +432,4 @@ if __name__ == '__main__': wnd.data_path = Path(data_path) wnd.show() - sys.exit(app.exec_()) + sys.exit(app.exec()) diff --git a/sohstationviewer/view/plotting/plotting_widget/multi_threaded_plotting_widget.py b/sohstationviewer/view/plotting/plotting_widget/multi_threaded_plotting_widget.py index 511aba4e21b09e40c966436155a7c59cb0691e7f..58ed3507dded12719e6565ecb6a35565afbf43f7 100644 --- a/sohstationviewer/view/plotting/plotting_widget/multi_threaded_plotting_widget.py +++ b/sohstationviewer/view/plotting/plotting_widget/multi_threaded_plotting_widget.py @@ -2,7 +2,7 @@ from typing import Tuple, Union, Dict, List -from PySide2 import QtCore +from PySide6 import QtCore from sohstationviewer.model.data_type_model import DataTypeModel @@ -74,7 +74,7 @@ class MultiThreadedPlottingWidget(PlottingWidget): :param time_ticks_total: max number of tick to show on time bar :param is_waveform: True if this is a WaveformWidget, otherwise False """ - + self.zoom_marker1_shown = False self.key = key self.processing_log = [] # [(message, type)] self.gap_bar = None diff --git a/sohstationviewer/view/plotting/plotting_widget/plotting_processor.py b/sohstationviewer/view/plotting/plotting_widget/plotting_processor.py index 764369320011cf6b6df691b599b165a937a54d4f..5ca9a6f4be004b137640d0f78f6e574abbab37e2 100644 --- a/sohstationviewer/view/plotting/plotting_widget/plotting_processor.py +++ b/sohstationviewer/view/plotting/plotting_widget/plotting_processor.py @@ -1,4 +1,4 @@ -from PySide2 import QtCore +from PySide6 import QtCore from sohstationviewer.conf import constants as const from sohstationviewer.view.plotting.plotting_widget.plotting_processor_helper\ diff --git a/sohstationviewer/view/plotting/plotting_widget/plotting_widget.py b/sohstationviewer/view/plotting/plotting_widget/plotting_widget.py index 970ba1903f2ba734848ea36cd29d45c6b523006c..1dbe7740382add4e52a96fa3e3d0a7a2a68f6871 100755 --- a/sohstationviewer/view/plotting/plotting_widget/plotting_widget.py +++ b/sohstationviewer/view/plotting/plotting_widget/plotting_widget.py @@ -6,9 +6,9 @@ import numpy as np import matplotlib.text from matplotlib import pyplot as pl from matplotlib.transforms import Bbox -from PySide2.QtCore import QTimer, Qt -from PySide2 import QtCore, QtWidgets -from PySide2.QtWidgets import QWidget, QApplication, QTextBrowser +from PySide6.QtCore import QTimer, Qt +from PySide6 import QtCore, QtWidgets +from PySide6.QtWidgets import QWidget, QApplication, QTextBrowser from sohstationviewer.conf import constants from sohstationviewer.view.util.color import set_color_mode @@ -296,9 +296,9 @@ class PlottingWidget(QtWidgets.QScrollArea): focus SOH tab, roll to the corresponding line. """ artist = event.artist - ax = artist.axes - chan_id = ax.chan if isinstance(artist, pl.Line2D): + ax = artist.axes + chan_id = ax.chan chan_data = self.plotting_data1[chan_id] # list of x values of the plot x_list = artist.get_xdata() @@ -650,7 +650,7 @@ class PlottingWidget(QtWidgets.QScrollArea): msgbox.setText(msg) msgbox.addButton(QtWidgets.QMessageBox.Cancel) msgbox.addButton('Continue', QtWidgets.QMessageBox.YesRole) - result = msgbox.exec_() + result = msgbox.exec() if result == QtWidgets.QMessageBox.Cancel: return self.main_window.color_mode = self.c_mode @@ -667,12 +667,12 @@ class PlottingWidget(QtWidgets.QScrollArea): msgbox.setText(msg) msgbox.addButton(QtWidgets.QMessageBox.Cancel) msgbox.addButton('Continue', QtWidgets.QMessageBox.YesRole) - result = msgbox.exec_() + result = msgbox.exec() if result == QtWidgets.QMessageBox.Cancel: return save_plot_dlg = SavePlotDialog( self.parent, self.main_window, default_name) - save_plot_dlg.exec_() + save_plot_dlg.exec() save_file_path = save_plot_dlg.save_file_path if save_file_path is None: return diff --git a/sohstationviewer/view/plotting/time_power_squared_dialog.py b/sohstationviewer/view/plotting/time_power_squared_dialog.py index 5e533b53b591894908a59bca12eb8899c5dde0d3..6778c093c2c8447ad974703da1784239a6c2e75d 100755 --- a/sohstationviewer/view/plotting/time_power_squared_dialog.py +++ b/sohstationviewer/view/plotting/time_power_squared_dialog.py @@ -3,7 +3,7 @@ from math import sqrt from typing import List, Tuple, Union import numpy as np -from PySide2 import QtWidgets, QtCore +from PySide6 import QtWidgets, QtCore from sohstationviewer.conf import constants as const from sohstationviewer.controller.plotting_data import ( @@ -86,6 +86,7 @@ class TimePowerSquaredWidget(plotting_widget.PlottingWidget): :param start_tm: requested start time to read :param end_tm: requested end time to read """ + self.zoom_marker1_shown = False self.is_working = True self.set_key = key self.plotting_data1 = d_obj.waveform_data[key] diff --git a/sohstationviewer/view/plotting/time_power_squared_processor.py b/sohstationviewer/view/plotting/time_power_squared_processor.py index c554c6867417f25344fcbb5387a1f9c74faccdb9..200ea544cef82ab3c8a608654e6431f38894201a 100644 --- a/sohstationviewer/view/plotting/time_power_squared_processor.py +++ b/sohstationviewer/view/plotting/time_power_squared_processor.py @@ -1,7 +1,7 @@ from typing import Dict, Optional, List import numpy as np -from PySide2 import QtCore +from PySide6 import QtCore from sohstationviewer.view.plotting.time_power_squared_helper import \ get_tps_for_discontinuous_data diff --git a/sohstationviewer/view/plotting/waveform_dialog.py b/sohstationviewer/view/plotting/waveform_dialog.py index 24c9a9a8cfb2256c3f40f1a7ef7bbe7776f4305d..2e9cf40b8325fce4da409f71289663cb07eb378a 100755 --- a/sohstationviewer/view/plotting/waveform_dialog.py +++ b/sohstationviewer/view/plotting/waveform_dialog.py @@ -1,7 +1,7 @@ # Drawing waveform and mass position from typing import Tuple, Union, Dict -from PySide2 import QtCore, QtWidgets +from PySide6 import QtCore, QtWidgets from sohstationviewer.model.data_type_model import DataTypeModel diff --git a/sohstationviewer/view/plotting/waveform_processor.py b/sohstationviewer/view/plotting/waveform_processor.py index 8d6ef39b2ee84c0d9c681be97b936818785b604e..f98727f0eeff08d0a366a7c2532f730713cff4db 100644 --- a/sohstationviewer/view/plotting/waveform_processor.py +++ b/sohstationviewer/view/plotting/waveform_processor.py @@ -1,6 +1,6 @@ from typing import List, Dict -from PySide2 import QtCore +from PySide6 import QtCore from obspy import UTCDateTime from obspy.core import Trace diff --git a/sohstationviewer/view/save_plot_dialog.py b/sohstationviewer/view/save_plot_dialog.py index 77a988f25a6679ac7ecd3bd4f916ca625d6a97d1..d5e7a5fc1732d2d00de0bdf445fe41ef2bffaeca 100644 --- a/sohstationviewer/view/save_plot_dialog.py +++ b/sohstationviewer/view/save_plot_dialog.py @@ -4,8 +4,8 @@ import os from pathlib import Path from typing import Union, Optional -from PySide2 import QtWidgets, QtCore, QtGui -from PySide2.QtWidgets import QApplication, QWidget, QDialog +from PySide6 import QtWidgets, QtCore, QtGui +from PySide6.QtWidgets import QApplication, QWidget, QDialog from sohstationviewer.conf import constants @@ -93,7 +93,7 @@ class SavePlotDialog(QDialog): fd = QtWidgets.QFileDialog(self) fd.setFileMode(QtWidgets.QFileDialog.Directory) fd.setDirectory(self.save_dir_textbox.text()) - fd.exec_() + fd.exec() new_path = fd.selectedFiles()[0] self.save_dir_textbox.setText(new_path) self.save_dir_path = new_path @@ -133,7 +133,7 @@ if __name__ == '__main__': save_path = '/Users/ldam/Documents/GIT/sohstationviewer/tests/test_data/Q330-sample' # noqa: E501 test = SavePlotDialog(None, 'test_plot') test.set_save_directory(save_path) - test.exec_() + test.exec() print("dpi:", test.dpi) print("save file path:", test.save_file_path) - sys.exit(app.exec_()) + sys.exit(app.exec()) diff --git a/sohstationviewer/view/search_message/highlight_delegate.py b/sohstationviewer/view/search_message/highlight_delegate.py index cda425ca1c27191f8c0376f048fc36c06ce8d2ef..50f3f86e94cdbe5b762807414ceef50567f9fd32 100644 --- a/sohstationviewer/view/search_message/highlight_delegate.py +++ b/sohstationviewer/view/search_message/highlight_delegate.py @@ -2,8 +2,8 @@ Credit: https://stackoverflow.com/questions/53353450/how-to-highlight-a-words-in-qtablewidget-from-a-searchlist # noqa """ from typing import List, Dict -from PySide2 import QtCore, QtGui, QtWidgets -from PySide2.QtGui import QPen, QTextCursor, QTextCharFormat, QColor +from PySide6 import QtCore, QtGui, QtWidgets +from PySide6.QtGui import QPen, QTextCursor, QTextCharFormat, QColor class HighlightDelegate(QtWidgets.QStyledItemDelegate): diff --git a/sohstationviewer/view/search_message/search_message_dialog.py b/sohstationviewer/view/search_message/search_message_dialog.py index 56b4b2355c120f28e5b2ea81f53b80ba4f10429c..8aeb3f2b75607af0cdeb3d84d3d6064e8daead31 100644 --- a/sohstationviewer/view/search_message/search_message_dialog.py +++ b/sohstationviewer/view/search_message/search_message_dialog.py @@ -3,8 +3,8 @@ import os from pathlib import PosixPath, Path from typing import Dict, List, Tuple, Callable, Union, Optional -from PySide2 import QtGui, QtCore, QtWidgets -from PySide2.QtWidgets import QStyle +from PySide6 import QtGui, QtCore, QtWidgets +from PySide6.QtWidgets import QStyle from sohstationviewer.view.search_message.highlight_delegate import ( HighlightDelegate) @@ -705,7 +705,7 @@ def main(): wnd.show() # wnd.show_log_entry_from_data_index(10) - sys.exit(app.exec_()) + sys.exit(app.exec()) if __name__ == '__main__': diff --git a/sohstationviewer/view/select_buttons_dialog.py b/sohstationviewer/view/select_buttons_dialog.py index e8841fe6d5f98a8168a145604927b8f929de9c92..f4efebe38b0c8d9dd8916f313c2fa02503f1bc1c 100644 --- a/sohstationviewer/view/select_buttons_dialog.py +++ b/sohstationviewer/view/select_buttons_dialog.py @@ -2,7 +2,7 @@ import sys import platform import os -from PySide2 import QtWidgets, QtCore +from PySide6 import QtWidgets, QtCore from functools import partial @@ -62,7 +62,7 @@ if __name__ == '__main__': test = SelectButtonDialog(None, "Testing result from buttons", ['test01', 'test02', 'test03', 'test11', 'test12', 'test13']) - test.exec_() + test.exec() print("return Code:", test.ret) - sys.exit(app.exec_()) + sys.exit(app.exec()) diff --git a/sohstationviewer/view/ui/about_ui_qtdesigner.py b/sohstationviewer/view/ui/about_ui_qtdesigner.py index e1ecbbe2258d2ee9909d583d3ec53d7d548f4af6..8c402dd5ad050f0084e538e6ce1b1d36d2782d72 100644 --- a/sohstationviewer/view/ui/about_ui_qtdesigner.py +++ b/sohstationviewer/view/ui/about_ui_qtdesigner.py @@ -8,7 +8,7 @@ # # WARNING! All changes made in this file will be lost! -from PySide2 import QtCore, QtGui, QtWidgets +from PySide6 import QtCore, QtGui, QtWidgets class Ui_About(object): def setupUi(self, About): diff --git a/sohstationviewer/view/ui/calendar_ui_qtdesigner.py b/sohstationviewer/view/ui/calendar_ui_qtdesigner.py index 81edc2dd9a6fa013340ce159ef58cd58e61b5b52..4e225c0eafb962bb5748971aa948936558c68d5e 100644 --- a/sohstationviewer/view/ui/calendar_ui_qtdesigner.py +++ b/sohstationviewer/view/ui/calendar_ui_qtdesigner.py @@ -8,7 +8,7 @@ # # WARNING! All changes made in this file will be lost! -from PySide2 import QtCore, QtWidgets +from PySide6 import QtCore, QtWidgets from sohstationviewer.view.calendar.calendar_widget import CalendarWidget diff --git a/sohstationviewer/view/ui/main_ui.py b/sohstationviewer/view/ui/main_ui.py index 4c98291a6cc6a45533b847dc17f65cb2f97544ac..d83a14909625275940a25b03145cdb10e7c52cba 100755 --- a/sohstationviewer/view/ui/main_ui.py +++ b/sohstationviewer/view/ui/main_ui.py @@ -3,12 +3,14 @@ import configparser from pathlib import Path from typing import Union, List, Optional -from PySide2 import QtCore, QtGui, QtWidgets -from PySide2.QtWidgets import ( +from PySide6 import QtCore, QtGui, QtWidgets +from PySide6.QtWidgets import ( QMainWindow, QWidget, QTextBrowser, QPushButton, QLineEdit, QDateEdit, - QListWidget, QCheckBox, QRadioButton, QMenu, QAction, QLabel, QFrame, - QVBoxLayout, QHBoxLayout, QGridLayout, QAbstractItemView, QShortcut, - QActionGroup, QButtonGroup, + QListWidget, QCheckBox, QRadioButton, QMenu, QLabel, QFrame, + QVBoxLayout, QHBoxLayout, QGridLayout, QAbstractItemView, QButtonGroup, +) +from PySide6.QtGui import ( + QAction, QActionGroup, QShortcut ) from sohstationviewer.view.calendar.calendar_widget import CalendarWidget diff --git a/sohstationviewer/view/util/one_instance_at_a_time.py b/sohstationviewer/view/util/one_instance_at_a_time.py index f0bbf7a34e8103287bd7f4bbe3dd0fa988491304..dd6cd416df8a2d4136b166a6c55e301e128c9000 100644 --- a/sohstationviewer/view/util/one_instance_at_a_time.py +++ b/sohstationviewer/view/util/one_instance_at_a_time.py @@ -1,7 +1,7 @@ from __future__ import annotations -from PySide2.QtGui import QCloseEvent -from PySide2.QtWidgets import QWidget +from PySide6.QtGui import QCloseEvent +from PySide6.QtWidgets import QWidget class DialogAlreadyOpenedError(Exception): diff --git a/tests/controller/test_processing.py b/tests/controller/test_processing.py index 899e58e95ba3d3e33ce899685b55fdfff1fd08fa..a607aab9e702df84d73c27df6273eba4dec6129f 100644 --- a/tests/controller/test_processing.py +++ b/tests/controller/test_processing.py @@ -12,7 +12,7 @@ from sohstationviewer.controller.processing import ( get_data_type_from_file ) from sohstationviewer.database.extract_data import get_signature_channels -from PySide2 import QtWidgets +from PySide6 import QtWidgets TEST_DATA_DIR = Path(__file__).resolve().parent.parent.joinpath('test_data') @@ -288,6 +288,22 @@ class TestDetectDataType(TestCase): f"{self.dir2.name}: Q330\n\n" f"Please have only data that related to each other.") + def test_same_data_types_different_multiplex(self): + """ + Test basic functionality of detect_data_type - the given directories + contain same data types but different multiplex. + """ + returned_data_types = [('Q330', True), ('Q330', False)] + self.mock_get_data_type_from_file.side_effect = returned_data_types + + with self.assertRaises(Exception) as context: + detect_data_type([self.dir1.name, self.dir2.name]) + self.assertEqual( + str(context.exception), + "There are both multiplexed and non-multiplexed data " + "detected.\n\nPlease have only data that related to" + " each other.") + def test_unknown_data_type(self): """ Test basic functionality of detect_data_type - can't detect any data diff --git a/tests/controller/test_util.py b/tests/controller/test_util.py index e58e8c89b84a1a580991722f73490b2787c75ad8..a4f46f8a8f56f865db3c3d3efc08c499b7068259 100644 --- a/tests/controller/test_util.py +++ b/tests/controller/test_util.py @@ -18,7 +18,8 @@ from sohstationviewer.controller.util import ( get_time_4, get_val, rtn_pattern, - add_thousand_separator + add_thousand_separator, + get_valid_file_count ) TEST_DATA_DIR = os.path.realpath(os.path.join( @@ -367,3 +368,11 @@ class TestFmti(TestCase): val = -62362.32523 expected = '-62,362' self.assertEqual(add_thousand_separator(val), expected) + + +class TestGetTotalFiles(TestCase): + def test_get_valid_file_count(self): + list_of_dir = [os.path.join(TEST_DATA_DIR, 'Centaur-sample'), + os.path.join(TEST_DATA_DIR, 'Q330-sample')] + total_files = get_valid_file_count(list_of_dir) + self.assertEqual(total_files, 6) diff --git a/tests/model/mseed_data/test_mseed.py b/tests/model/mseed_data/test_mseed.py index 8d6835e786e6fa2fcf93a94a5585c1e8dae18216..e12494dccd68a99c6d69e8a6705c9d561faa3739 100644 --- a/tests/model/mseed_data/test_mseed.py +++ b/tests/model/mseed_data/test_mseed.py @@ -21,7 +21,7 @@ class TestMSeed(TestCase): args = { 'data_type': 'Q330', 'is_multiplex': False, - 'folder': '_', + 'list_of_dir': ['_'], 'on_unittest': True } with self.assertRaises(ProcessingDataError) as context: @@ -36,7 +36,7 @@ class TestMSeed(TestCase): args = { 'data_type': 'Pegasus', 'is_multiplex': False, - 'folder': pegasus_data, + 'list_of_dir': [pegasus_data], 'req_soh_chans': ['_'], 'on_unittest': True } @@ -59,7 +59,7 @@ class TestMSeed(TestCase): args = { 'data_type': 'Pegasus', 'is_multiplex': False, - 'folder': pegasus_data, + 'list_of_dir': [pegasus_data], 'req_soh_chans': ['VE1'], 'on_unittest': True } @@ -85,7 +85,7 @@ class TestMSeed(TestCase): args = { 'data_type': 'Pegasus', 'is_multiplex': False, - 'folder': pegasus_data, + 'list_of_dir': [pegasus_data], 'req_wf_chans': ['HH1'], 'req_soh_chans': ['_'], 'on_unittest': True @@ -111,7 +111,7 @@ class TestMSeed(TestCase): args = { 'data_type': 'Q330', 'is_multiplex': False, - 'folder': q330_data, + 'list_of_dir': [q330_data], 'req_soh_chans': ['LOG'], } obj = MSeed(**args) @@ -135,7 +135,7 @@ class TestMSeed(TestCase): args = { 'data_type': 'Q330', 'is_multiplex': True, - 'folder': blockettes_data, + 'list_of_dir': [blockettes_data], 'req_soh_chans': ['ACE'], } obj = MSeed(**args) @@ -155,7 +155,7 @@ class TestMSeed(TestCase): args = { 'data_type': 'Q330', 'is_multiplex': False, - 'folder': multiplex_data, + 'list_of_dir': [multiplex_data], 'req_soh_chans': [], 'req_wf_chans': ['EL1'] } @@ -177,7 +177,7 @@ class TestMSeed(TestCase): args = { 'data_type': 'Q330', 'is_multiplex': True, - 'folder': multiplex_data, + 'list_of_dir': [multiplex_data], 'req_soh_chans': [], 'req_wf_chans': ['EL1'] } @@ -200,7 +200,7 @@ class TestMSeed(TestCase): args = { 'data_type': 'Q330', 'is_multiplex': False, - 'folder': multiplex_data, + 'list_of_dir': [multiplex_data], 'req_soh_chans': [], 'req_wf_chans': ['EL2'] } @@ -212,7 +212,7 @@ class TestMSeed(TestCase): args = { 'data_type': 'Q330', 'is_multiplex': True, - 'folder': multiplex_data, + 'list_of_dir': [multiplex_data], 'req_soh_chans': [], 'req_wf_chans': ['EL2'] } @@ -229,13 +229,39 @@ class TestMSeed(TestCase): self.assertEqual(len(obj.waveform_data['3203']['EL2']['tracesInfo']), 1) + def test_select_2_folders(self): + # is_multiplex = True => the selected channel will be read + args = { + 'data_type': 'Q330', + 'is_multiplex': True, + 'list_of_dir': [multiplex_data, blockettes_data], + 'req_soh_chans': [], + 'req_wf_chans': ['EL2'], + 'gap_minimum': 60 + } + obj = MSeed(**args) + self.assertEqual(list(obj.waveform_data.keys()), ['3203']) + self.assertEqual(list(obj.waveform_data['3203'].keys()), ['EL2']) + self.assertEqual(obj.waveform_data['3203']['EL2']['samplerate'], 200) + self.assertEqual(obj.waveform_data['3203']['EL2']['startTmEpoch'], + 1671730004.3100293) + self.assertEqual(obj.waveform_data['3203']['EL2']['endTmEpoch'], + 1671735657.9148998) + self.assertEqual(obj.waveform_data['3203']['EL2']['size'], 268576) + self.assertEqual(obj.waveform_data['3203']['EL2']['gaps'], + [[1671730720.5549, 1671735031.2799978]]) + self.assertEqual(len(obj.waveform_data['3203']['EL2']['tracesInfo']), + 1) + self.assertEqual(obj.gaps['3203'], + [[1671730720.5549, 1671735031.2799978]]) + def test_existing_time_range(self): # check if data_time is from the given range, end time may get # a little greater than read_end according to record's end time args = { 'data_type': 'Q330', 'is_multiplex': False, - 'folder': q330_data, + 'list_of_dir': [q330_data], 'req_soh_chans': [], 'read_start': 1625456018.0, 'read_end': 1625505627.9998999 @@ -252,7 +278,7 @@ class TestMSeed(TestCase): args = { 'data_type': 'Q330', 'is_multiplex': False, - 'folder': q330_data, + 'list_of_dir': [q330_data], 'req_soh_chans': [], 'read_start': 1625356018.0, 'read_end': 1625405627.9998999 @@ -269,7 +295,7 @@ class TestMSeed(TestCase): args = { 'data_type': 'Q330', 'is_multiplex': False, - 'folder': q330_data, + 'list_of_dir': [q330_data], 'req_soh_chans': [], 'req_wf_chans': ['LHE'] } @@ -292,7 +318,7 @@ class TestMSeed(TestCase): args = { 'data_type': 'Q330', 'is_multiplex': True, - 'folder': q330_data, + 'list_of_dir': [q330_data], 'req_soh_chans': [], 'req_wf_chans': [], 'include_mp123zne': True @@ -315,7 +341,7 @@ class TestMSeed(TestCase): args = { 'data_type': 'Centaur', 'is_multiplex': True, - 'folder': centaur_data, + 'list_of_dir': [centaur_data], 'req_soh_chans': [], 'gap_minimum': 60 } @@ -340,7 +366,7 @@ class TestMSeed(TestCase): args = { 'data_type': 'Centaur', 'is_multiplex': True, - 'folder': centaur_data, + 'list_of_dir': [centaur_data], 'req_soh_chans': [], 'gap_minimum': None } diff --git a/tests/model/reftek_data/test_reftek.py b/tests/model/reftek_data/test_reftek.py index 8bb2a71749d59d30833d4c98014308fff58060df..af03b2aa03606d807d0e888b71a0091a0dcc9901 100644 --- a/tests/model/reftek_data/test_reftek.py +++ b/tests/model/reftek_data/test_reftek.py @@ -17,7 +17,7 @@ class TestReftek(TestCase): # raise exception when path not exist args = { 'data_type': 'RT130', - 'folder': '_', + 'list_of_dir': ['_'], 'rt130_waveform_data_req': False, 'on_unittest': True } @@ -31,7 +31,7 @@ class TestReftek(TestCase): def test_read_soh(self): args = { 'data_type': 'RT130', - 'folder': reftek_data, + 'list_of_dir': [reftek_data], 'req_soh_chans': [], 'rt130_waveform_data_req': False, 'on_unittest': True @@ -56,13 +56,13 @@ class TestReftek(TestCase): '\n150:00:00:00 REF TEK 130' '\r\n150:00:00:00 CPU SOFTWARE') self.assertEqual(list(obj.soh_data.keys()), [('92EB', '25')]) - self.assertEqual(list(obj.soh_data[('92EB', '25')].keys()), - expected_soh) + self.assertEqual(sorted(list(obj.soh_data[('92EB', '25')].keys())), + sorted(expected_soh)) def test_read_waveform(self): args = { 'data_type': 'RT130', - 'folder': reftek_data, + 'list_of_dir': [reftek_data], 'req_soh_chans': [], 'req_wf_chans': [1], 'rt130_waveform_data_req': True, @@ -85,7 +85,7 @@ class TestReftek(TestCase): def test_read_mass_pos(self): args = { 'data_type': 'RT130', - 'folder': reftek_data, + 'list_of_dir': [reftek_data], 'req_soh_chans': ['_'], 'include_mp123zne': True, 'rt130_waveform_data_req': False, @@ -107,7 +107,7 @@ class TestReftek(TestCase): with self.subTest("no gap_minimum set"): args = { 'data_type': 'RT130', - 'folder': reftek_gap_data, + 'list_of_dir': [reftek_gap_data], 'req_soh_chans': [], 'req_wf_chans': ['*'], 'rt130_waveform_data_req': True, @@ -129,7 +129,7 @@ class TestReftek(TestCase): with self.subTest("has gap_minimum set"): args = { 'data_type': 'RT130', - 'folder': reftek_gap_data, + 'list_of_dir': [reftek_gap_data], 'req_soh_chans': [], 'req_wf_chans': ['*'], 'rt130_waveform_data_req': True, @@ -148,3 +148,45 @@ class TestReftek(TestCase): ['TEXT', ('98AD', '0')]) self.assertEqual(obj.gaps[('98AD', '0')], [[1648493999.64, 1648508400.64]]) + + def test_select_2_folders(self): + args = { + 'data_type': 'RT130', + 'list_of_dir': [reftek_data, reftek_gap_data], + 'req_soh_chans': [], + 'req_wf_chans': ['*'], + 'rt130_waveform_data_req': True, + 'gap_minimum': 60, + 'on_unittest': True + } + expected_soh = [ + 'SOH/Data Def', 'Battery Volt', 'DAS Temp', 'Backup Volt', + 'Disk Usage1', 'Disk Usage2', 'Dump Called/Comp', 'GPS On/Off/Err', + 'GPS Lk/Unlk', 'Clk Phase Err', 'Event DS1', 'Event DS9'] + expected_waveform = ['DS1-1', 'DS1-2', 'DS1-3'] + obj = RT130(**args) + self.assertEqual(obj.found_data_streams, [9, 1, 1, 2, 2]) + self.assertEqual(obj.keys, [('92EB', '25'), ('98AD', '0')]) + self.assertEqual(obj.selected_key, ('92EB', '25')) + self.assertEqual( + list(obj.stream_header_by_key_chan[('92EB', '25')].keys()), + expected_waveform) + self.assertEqual(list(obj.log_data.keys()), + ['TEXT', ('92EB', '25'), ('98AD', '0')]) + self.assertEqual(len(obj.log_data['TEXT']), 0) + self.assertEqual(list(obj.log_data[('92EB', '25')].keys()), ['SOH']) + self.assertEqual(len(obj.log_data[('92EB', '25')]['SOH']), 1) + self.assertEqual( + obj.log_data[('92EB', '25')]['SOH'][0][:100], + '\nState of Health 17:150:00:00:00:000 ST: 92EB' + '\n150:00:00:00 REF TEK 130' + '\r\n150:00:00:00 CPU SOFTWARE') + self.assertEqual(list(obj.soh_data.keys()), + [('92EB', '25'), ('98AD', '0')]) + self.assertEqual(sorted(list(obj.soh_data[('92EB', '25')].keys())), + sorted(expected_soh)) + self.assertEqual(list(obj.gaps.keys()), + [('92EB', '25'), ('98AD', '0')]) + self.assertEqual(obj.gaps[('92EB', '25')], []) + self.assertEqual(obj.gaps[('98AD', '0')], + [[1648493999.64, 1648508400.64]])