Skip to content
Snippets Groups Projects

Compare revisions

Changes are shown as if the source revision was being merged into the target revision. Learn more about comparing revisions.

Source

Select target project
No results found

Target

Select target project
  • software_public/passoft/sohstationviewer
1 result
Show changes
Commits on Source (10)
Showing
with 240 additions and 207 deletions
...@@ -20,7 +20,7 @@ stages: ...@@ -20,7 +20,7 @@ stages:
- Build Env and Test - Build Env and Test
before_script: before_script:
- pip install -e .[dev] - 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: flake8:
image: python:3.8 image: python:3.8
......
...@@ -32,7 +32,7 @@ setup( ...@@ -32,7 +32,7 @@ setup(
], ],
}, },
install_requires=['obspy', install_requires=['obspy',
'PySide2', 'PySide6',
'matplotlib', 'matplotlib',
'numpy'], 'numpy'],
setup_requires=[], setup_requires=[],
......
...@@ -2,27 +2,16 @@ ...@@ -2,27 +2,16 @@
import platform import platform
import os import os
import sys import sys
from PySide2 import QtWidgets from PySide6 import QtWidgets
from sohstationviewer.view.main_window import MainWindow 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(): def main():
app = QtWidgets.QApplication(sys.argv) app = QtWidgets.QApplication(sys.argv)
wnd = MainWindow() wnd = MainWindow()
wnd.show() wnd.show()
sys.exit(app.exec_()) sys.exit(app.exec())
if __name__ == '__main__': if __name__ == '__main__':
......
...@@ -9,9 +9,9 @@ import re ...@@ -9,9 +9,9 @@ import re
from pathlib import Path from pathlib import Path
from typing import List, Optional, Dict, Tuple from typing import List, Optional, Dict, Tuple
from PySide2.QtCore import QEventLoop, Qt from PySide6.QtCore import QEventLoop, Qt
from PySide2.QtGui import QCursor from PySide6.QtGui import QCursor
from PySide2.QtWidgets import QTextBrowser, QApplication from PySide6.QtWidgets import QTextBrowser, QApplication
from obspy.io import reftek from obspy.io import reftek
from obspy import UTCDateTime from obspy import UTCDateTime
...@@ -183,9 +183,10 @@ def detect_data_type(list_of_dir: List[str]) -> Optional[str]: ...@@ -183,9 +183,10 @@ def detect_data_type(list_of_dir: List[str]) -> Optional[str]:
sign_chan_data_type_dict = get_signature_channels() sign_chan_data_type_dict = get_signature_channels()
dir_data_type_dict = {} dir_data_type_dict = {}
is_multiplex = None is_multiplex_dict = {}
for d in list_of_dir: for d in list_of_dir:
data_type = "Unknown" data_type = "Unknown"
is_multiplex = None
for path, subdirs, files in os.walk(d): for path, subdirs, files in os.walk(d):
for file_name in files: for file_name in files:
path2file = Path(path).joinpath(file_name) path2file = Path(path).joinpath(file_name)
...@@ -205,10 +206,12 @@ def detect_data_type(list_of_dir: List[str]) -> Optional[str]: ...@@ -205,10 +206,12 @@ def detect_data_type(list_of_dir: List[str]) -> Optional[str]:
if is_multiplex is None: if is_multiplex is None:
raise Exception("No channel found for the data set") raise Exception("No channel found for the data set")
is_multiplex_dict[d] = is_multiplex
if data_type == "Unknown": if data_type == "Unknown":
dir_data_type_dict[d] = "Unknown" dir_data_type_dict[d] = "Unknown"
else: else:
dir_data_type_dict[d] = data_type 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())) data_type_list = list(set(dir_data_type_dict.values()))
if len(data_type_list) > 1: if len(data_type_list) > 1:
dir_data_type_str = json.dumps(dir_data_type_dict) 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]: ...@@ -218,13 +221,17 @@ def detect_data_type(list_of_dir: List[str]) -> Optional[str]:
f"{dir_data_type_str}\n\n" f"{dir_data_type_str}\n\n"
f"Please have only data that related to each other.") f"Please have only data that related to each other.")
raise Exception(msg) 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']: elif data_type_list == ['Unknown']:
msg = ("There are no known data detected.\n\n" msg = ("There are no known data detected.\n\n"
"Do you want to cancel to select different folder(s)\n" "Do you want to cancel to select different folder(s)\n"
"Or continue to read any available mseed file?") "Or continue to read any available mseed file?")
raise Exception(msg) 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( def get_data_type_from_file(
......
...@@ -9,10 +9,10 @@ from datetime import datetime ...@@ -9,10 +9,10 @@ from datetime import datetime
from pathlib import Path from pathlib import Path
from typing import List, Dict from typing import List, Dict
from PySide2 import QtCore from PySide6 import QtCore
from typing import Tuple, Union from typing import Tuple, Union
from PySide2.QtWidgets import QTextBrowser from PySide6.QtWidgets import QTextBrowser
from obspy import UTCDateTime from obspy import UTCDateTime
from sohstationviewer.view.util.enums import LogType from sohstationviewer.view.util.enums import LogType
...@@ -24,7 +24,6 @@ def validate_file(path2file: Union[str, Path], file_name: str): ...@@ -24,7 +24,6 @@ def validate_file(path2file: Union[str, Path], file_name: str):
:param file_name: name of the file :param file_name: name of the file
:return: True if pass checking, False if not. :return: True if pass checking, False if not.
""" """
if file_name.startswith('.'): if file_name.startswith('.'):
# skip info file # skip info file
return False return False
...@@ -312,3 +311,21 @@ def check_data_sdata(root_dir: str) -> bool: ...@@ -312,3 +311,21 @@ def check_data_sdata(root_dir: str) -> bool:
dir_list = [d for d in os.listdir(root_dir) dir_list = [d for d in os.listdir(root_dir)
if os.path.isdir(os.path.join(root_dir, d))] if os.path.isdir(os.path.join(root_dir, d))]
return 'data' in dir_list and 'sdata' in dir_list 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
No preview for this file type
...@@ -5,7 +5,7 @@ import traceback ...@@ -5,7 +5,7 @@ import traceback
from pathlib import Path from pathlib import Path
from typing import Union, List, Optional from typing import Union, List, Optional
from PySide2 import QtCore, QtWidgets from PySide6 import QtCore, QtWidgets
from sohstationviewer.conf import constants from sohstationviewer.conf import constants
from sohstationviewer.controller.util import display_tracking_info from sohstationviewer.controller.util import display_tracking_info
...@@ -27,7 +27,7 @@ class DataLoaderWorker(QtCore.QObject): ...@@ -27,7 +27,7 @@ class DataLoaderWorker(QtCore.QObject):
def __init__(self, data_type: str, tracking_box: QtWidgets.QTextBrowser, def __init__(self, data_type: str, tracking_box: QtWidgets.QTextBrowser,
is_multiplex: Optional[bool], 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_wf_chans: Union[List[str], List[int]] = [],
req_soh_chans: List[str] = [], read_start: float = 0, req_soh_chans: List[str] = [], read_start: float = 0,
gap_minimum: Optional[float] = None, gap_minimum: Optional[float] = None,
...@@ -38,7 +38,7 @@ class DataLoaderWorker(QtCore.QObject): ...@@ -38,7 +38,7 @@ class DataLoaderWorker(QtCore.QObject):
self.data_type = data_type self.data_type = data_type
self.tracking_box = tracking_box self.tracking_box = tracking_box
self.is_multiplex = is_multiplex 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.list_of_rt130_paths = list_of_rt130_paths
self.req_wf_chans = req_wf_chans self.req_wf_chans = req_wf_chans
self.req_soh_chans = req_soh_chans self.req_soh_chans = req_soh_chans
...@@ -57,6 +57,9 @@ class DataLoaderWorker(QtCore.QObject): ...@@ -57,6 +57,9 @@ class DataLoaderWorker(QtCore.QObject):
self.end_msg = None self.end_msg = None
def run(self): 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: try:
if self.data_type == 'RT130': if self.data_type == 'RT130':
from sohstationviewer.model.reftek_data.reftek import RT130 from sohstationviewer.model.reftek_data.reftek import RT130
...@@ -71,7 +74,7 @@ class DataLoaderWorker(QtCore.QObject): ...@@ -71,7 +74,7 @@ class DataLoaderWorker(QtCore.QObject):
type=QtCore.Qt.DirectConnection) type=QtCore.Qt.DirectConnection)
data_object.__init__( data_object.__init__(
self.data_type, self.tracking_box, 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, self.list_of_rt130_paths, req_wf_chans=self.req_wf_chans,
req_soh_chans=self.req_soh_chans, gap_minimum=self.gap_minimum, req_soh_chans=self.req_soh_chans, gap_minimum=self.gap_minimum,
read_start=self.read_start, read_end=self.read_end, read_start=self.read_start, read_end=self.read_end,
...@@ -91,11 +94,11 @@ class DataLoaderWorker(QtCore.QObject): ...@@ -91,11 +94,11 @@ class DataLoaderWorker(QtCore.QObject):
self.failed.emit() self.failed.emit()
except Exception: except Exception:
fmt = traceback.format_exc() 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)}") f"due to error: {str(fmt)}")
self.failed.emit() self.failed.emit()
else: 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) self.finished.emit(data_object)
...@@ -155,7 +158,7 @@ class DataLoader(QtCore.QObject): ...@@ -155,7 +158,7 @@ class DataLoader(QtCore.QObject):
data_type, data_type,
tracking_box, tracking_box,
is_multiplex, is_multiplex,
list_of_dir[0], # Only work on one directory for now. list_of_dir,
list_of_rt130_paths, list_of_rt130_paths,
req_wf_chans=req_wf_chans, req_wf_chans=req_wf_chans,
req_soh_chans=req_soh_chans, req_soh_chans=req_soh_chans,
...@@ -237,7 +240,7 @@ class DataLoader(QtCore.QObject): ...@@ -237,7 +240,7 @@ class DataLoader(QtCore.QObject):
) )
abort_button = msg_box.addButton(QtWidgets.QMessageBox.Abort) abort_button = msg_box.addButton(QtWidgets.QMessageBox.Abort)
msg_box.exec_() msg_box.exec()
if msg_box.clickedButton() == abort_button: if msg_box.clickedButton() == abort_button:
# The default choice is the first item, so we default to it if the # The default choice is the first item, so we default to it if the
......
...@@ -7,8 +7,8 @@ from typing import Optional, Union, List, Tuple, Dict ...@@ -7,8 +7,8 @@ from typing import Optional, Union, List, Tuple, Dict
from obspy import UTCDateTime from obspy import UTCDateTime
from obspy.core import Stream from obspy.core import Stream
from PySide2 import QtCore from PySide6 import QtCore
from PySide2 import QtWidgets from PySide6 import QtWidgets
from sohstationviewer.controller.util import display_tracking_info from sohstationviewer.controller.util import display_tracking_info
from sohstationviewer.conf import constants from sohstationviewer.conf import constants
......
from PySide2 import QtCore from PySide6 import QtCore
from obspy import Trace from obspy import Trace
# Global flag that determines whether the user requested to stop processing and # Global flag that determines whether the user requested to stop processing and
......
...@@ -4,7 +4,7 @@ This module provides access to a class that loads data in a separate thread. ...@@ -4,7 +4,7 @@ This module provides access to a class that loads data in a separate thread.
import math import math
import numpy as np import numpy as np
from PySide2 import QtCore from PySide6 import QtCore
from sohstationviewer.conf import constants as const from sohstationviewer.conf import constants as const
......
from __future__ import annotations from __future__ import annotations
import os
from pathlib import Path from pathlib import Path
from tempfile import TemporaryDirectory from tempfile import TemporaryDirectory
from typing import Optional, Union, List, Tuple, Dict from typing import Optional, Union, List, Tuple, Dict
import traceback
from obspy import UTCDateTime from obspy import UTCDateTime
from PySide2 import QtCore from PySide6 import QtCore
from PySide2 import QtWidgets 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.plotting.gps_plot.gps_point import GPSPoint
from sohstationviewer.view.util.enums import LogType from sohstationviewer.view.util.enums import LogType
from sohstationviewer.database.process_db import execute_db from sohstationviewer.database.process_db import execute_db
...@@ -36,7 +38,7 @@ class ThreadStopped(Exception): ...@@ -36,7 +38,7 @@ class ThreadStopped(Exception):
class GeneralData(): class GeneralData():
def __init__(self, data_type, def __init__(self, data_type,
tracking_box: Optional[QtWidgets.QTextBrowser] = None, 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] = [], list_of_rt130_paths: List[Path] = [],
req_wf_chans: Union[List[str], List[int]] = [], req_wf_chans: Union[List[str], List[int]] = [],
req_soh_chans: List[str] = [], req_soh_chans: List[str] = [],
...@@ -64,7 +66,8 @@ class GeneralData(): ...@@ -64,7 +66,8 @@ class GeneralData():
:param data_type: type of the object :param data_type: type of the object
:param tracking_box: widget to display tracking info :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 list_of_rt130_paths: path to the folders of RT130 data
:param req_wf_chans: requested waveform channel list :param req_wf_chans: requested waveform channel list
:param req_soh_chans: requested SOH channel list :param req_soh_chans: requested SOH channel list
...@@ -82,9 +85,9 @@ class GeneralData(): ...@@ -82,9 +85,9 @@ class GeneralData():
data loader is paused. data loader is paused.
""" """
self.data_type = data_type self.data_type = data_type
self.is_multiplex = is_multiplex
self.tracking_box = tracking_box 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.list_of_rt130_paths = list_of_rt130_paths
self.req_soh_chans = req_soh_chans self.req_soh_chans = req_soh_chans
self.req_wf_chans = req_wf_chans self.req_wf_chans = req_wf_chans
...@@ -95,7 +98,6 @@ class GeneralData(): ...@@ -95,7 +98,6 @@ class GeneralData():
self.include_mp456uvw = include_mp456uvw self.include_mp456uvw = include_mp456uvw
self.rt130_waveform_data_req = rt130_waveform_data_req self.rt130_waveform_data_req = rt130_waveform_data_req
self.on_unittest = on_unittest self.on_unittest = on_unittest
if creator_thread is None: if creator_thread is None:
err_msg = ( err_msg = (
'A signal is not None while running in main thread' 'A signal is not None while running in main thread'
...@@ -158,15 +160,6 @@ class GeneralData(): ...@@ -158,15 +160,6 @@ class GeneralData():
self.gps_points: List[GPSPoint] = [] 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]]: def select_key(self) -> Union[str, Tuple[str, str]]:
""" """
FROM data_type_model.DataTypeModel.select_key FROM data_type_model.DataTypeModel.select_key
...@@ -182,8 +175,7 @@ class GeneralData(): ...@@ -182,8 +175,7 @@ class GeneralData():
if self.creator_thread.isInterruptionRequested(): if self.creator_thread.isInterruptionRequested():
raise ThreadStopped() raise ThreadStopped()
self.read_folder(self.dir) self.read_folders()
self.selected_key = self.select_key() self.selected_key = self.select_key()
self.fill_empty_data() self.fill_empty_data()
...@@ -191,6 +183,65 @@ class GeneralData(): ...@@ -191,6 +183,65 @@ class GeneralData():
raise ThreadStopped() raise ThreadStopped()
self.finalize_data() 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): def finalize_data(self):
""" """
CHANGED FROM data_type_model.Data_Type_Model.finalize_data CHANGED FROM data_type_model.Data_Type_Model.finalize_data
......
...@@ -3,15 +3,12 @@ MSeed object to hold and process MSeed data ...@@ -3,15 +3,12 @@ MSeed object to hold and process MSeed data
""" """
import os import os
import re import re
import traceback
from pathlib import Path 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.view.util.enums import LogType
from sohstationviewer.model.general_data.general_data import \ from sohstationviewer.model.general_data.general_data import GeneralData
GeneralData, ThreadStopped, ProcessingDataError
from sohstationviewer.model.general_data.general_data_helper import read_text from sohstationviewer.model.general_data.general_data_helper import read_text
from sohstationviewer.model.mseed_data.mseed_helper import \ from sohstationviewer.model.mseed_data.mseed_helper import \
...@@ -31,6 +28,8 @@ class MSeed(GeneralData): ...@@ -31,6 +28,8 @@ class MSeed(GeneralData):
# FROM mseed.mseed.MSEED.__init__ # FROM mseed.mseed.MSEED.__init__
super().__init__(*args, **kwargs) super().__init__(*args, **kwargs)
self.nets_by_sta: Dict[str, List[str]] = {} self.nets_by_sta: Dict[str, List[str]] = {}
self.invalid_blockettes = False
self.not_mseed_files = []
self.processing_data() self.processing_data()
def finalize_data(self): def finalize_data(self):
...@@ -46,83 +45,55 @@ class MSeed(GeneralData): ...@@ -46,83 +45,55 @@ class MSeed(GeneralData):
self.retrieve_nets_from_data_dicts() self.retrieve_nets_from_data_dicts()
super().finalize_data() 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 from list_of_dir for soh, mass position and waveform.
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
""" """
if not os.path.isdir(folder): super().read_folders(self.list_of_dir)
raise ProcessingDataError(f"Path '{folder}' not exist") if self.not_mseed_files:
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:
self.track_info( self.track_info(
f"Not MSeed files: {not_mseed_files}", LogType.WARNING) f"Not MSeed files: {self.not_mseed_files}", LogType.WARNING)
if invalid_blockettes: if self.invalid_blockettes:
# This check to only print out message once # This check to only print out message once
print("We currently only handle blockettes 500, 1000," print("We currently only handle blockettes 500, 1000,"
" and 1001.") " 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): def retrieve_nets_from_data_dicts(self):
""" """
......
from __future__ import annotations 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.conf import constants
from sohstationviewer.controller.util import ( from sohstationviewer.controller.util import (
get_time_6, get_time_4, get_val, rtn_pattern) get_time_6, get_time_4, get_val, rtn_pattern)
from sohstationviewer.view.util.enums import LogType from sohstationviewer.view.util.enums import LogType
from sohstationviewer.model.reftek_data.log_info_helper import (
remove_question_marks)
if TYPE_CHECKING: if TYPE_CHECKING:
from sohstationviewer.model.reftek_data.reftek import RT130 from sohstationviewer.model.reftek_data.reftek import RT130
...@@ -33,7 +35,7 @@ class LogInfo(): ...@@ -33,7 +35,7 @@ class LogInfo():
self.key = key self.key = key
self.unit_id, self.exp_no = key self.unit_id, self.exp_no = key
self.req_data_streams = req_data_streams 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 self.is_log_file = is_log_file
""" """
track_year to add year to time since year time not include year after track_year to add year to time since year time not include year after
...@@ -49,7 +51,7 @@ class LogInfo(): ...@@ -49,7 +51,7 @@ class LogInfo():
self.model = "72A" self.model = "72A"
self.max_epoch = 0 self.max_epoch = 0
self.min_epoch = constants.HIGHEST_INT 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.cpu_vers: Set[str] = set()
self.gps_vers: Set[str] = set() self.gps_vers: Set[str] = set()
self.extract_info() self.extract_info()
...@@ -314,9 +316,10 @@ class LogInfo(): ...@@ -314,9 +316,10 @@ class LogInfo():
:param d: float - value of data :param d: float - value of data
:param idx: int - index of SOH message line :param idx: int - index of SOH message line
""" """
if self.no_question_req_soh_chans and not chan_id.startswith(
if self.req_soh_chans and chan_id not in self.req_soh_chans: tuple(self.no_question_req_soh_chans)):
return return
if chan_id not in self.chans: if chan_id not in self.chans:
self.chans[chan_id] = { self.chans[chan_id] = {
'reftek': True, 'reftek': True,
......
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
""" """
RT130 object to hold and process RefTek data RT130 object to hold and process RefTek data
""" """
import os
from pathlib import Path from pathlib import Path
from typing import Union, List, Tuple, Dict from typing import Union, List, Tuple, Dict
import traceback import traceback
...@@ -9,7 +8,6 @@ import numpy as np ...@@ -9,7 +8,6 @@ import numpy as np
from obspy.core import Stream from obspy.core import Stream
from sohstationviewer.conf import constants from sohstationviewer.conf import constants
from sohstationviewer.controller.util import validate_file
from sohstationviewer.view.util.enums import LogType from sohstationviewer.view.util.enums import LogType
from sohstationviewer.model.general_data.general_data import \ from sohstationviewer.model.general_data.general_data import \
...@@ -64,13 +62,8 @@ class RT130(GeneralData): ...@@ -64,13 +62,8 @@ class RT130(GeneralData):
def processing_data(self): def processing_data(self):
if self.creator_thread.isInterruptionRequested(): if self.creator_thread.isInterruptionRequested():
raise ThreadStopped() raise ThreadStopped()
self.read_folder(self.dir) self.read_folders()
if self.creator_thread.isInterruptionRequested():
raise ThreadStopped()
self.selected_key = self.select_key() self.selected_key = self.select_key()
if self.selected_key is None:
raise ThreadStopped()
if self.creator_thread.isInterruptionRequested(): if self.creator_thread.isInterruptionRequested():
raise ThreadStopped() raise ThreadStopped()
...@@ -109,51 +102,34 @@ class RT130(GeneralData): ...@@ -109,51 +102,34 @@ class RT130(GeneralData):
# this happens when there is text or ascii only in the data # this happens when there is text or ascii only in the data
self.data_time[key] = [self.read_start, self.read_end] 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, Read data from list_of_dir or list_of_rt130_paths for soh,
mass position data and mass position and waveform.
index waveform data with filename and corresponding time range
:param folder: absolute path to data set folder
""" """
count = 0
total = 0
if self.list_of_rt130_paths != []: if self.list_of_rt130_paths != []:
folders = 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: else:
folders = [self.dir] folders = self.list_of_dir
total = sum([len(files) for _, _, files in os.walk(self.dir)]) super().read_folders(folders)
for folder in folders: def read_file(self, path2file: Path, file_name: str,
if not os.path.isdir(folder): count: int, total: int) -> None:
raise ProcessingDataError(f"Path '{folder}' not exist") """
for path, subdirs, files in os.walk(folder): Read data or text from file
for file_name in files: :param path2file: absolute path to file
if self.creator_thread.isInterruptionRequested(): :param file_name: name of file
raise ThreadStopped() :param count: total number of file read
path2file = Path(path).joinpath(file_name) :param total: total number of all valid files
if not validate_file(path2file, file_name): """
continue if count % 50 == 0:
try: self.track_info(
if not self.read_reftek_130(path2file): f"Read {count}/{total} files", LogType.INFO)
read_text(path2file, self.log_data['TEXT']) log_text = read_text(path2file)
except Exception: if log_text is not None:
fmt = traceback.format_exc() self.log_texts['TEXT'].append(log_text)
self.track_info(f"Skip file {path2file} can't be read " return
f"due to error: {str(fmt)}", self.read_reftek_130(path2file)
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)
def select_key(self) -> Tuple[str, str]: def select_key(self) -> Tuple[str, str]:
""" """
...@@ -174,7 +150,7 @@ class RT130(GeneralData): ...@@ -174,7 +150,7 @@ class RT130(GeneralData):
raise ProcessingDataError(msg) raise ProcessingDataError(msg)
selected_key = keys[0] 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" msg = ("There are more than one keys in the given data.\n"
"Please select one to display") "Please select one to display")
self.pause_signal.emit(msg, keys) self.pause_signal.emit(msg, keys)
......
from PySide2 import QtWidgets from PySide6 import QtWidgets
from sohstationviewer.view.ui.calendar_ui_qtdesigner import Ui_CalendarDialog from sohstationviewer.view.ui.calendar_ui_qtdesigner import Ui_CalendarDialog
......
from PySide2 import QtCore, QtGui, QtWidgets from PySide6 import QtCore, QtGui, QtWidgets
class CalendarWidget(QtWidgets.QCalendarWidget): class CalendarWidget(QtWidgets.QCalendarWidget):
......
from typing import Dict, List, Union from typing import Dict, List, Union
from pathlib import Path from pathlib import Path
from PySide2 import QtWidgets, QtCore from PySide6 import QtWidgets, QtCore
from PySide2.QtWidgets import QDialogButtonBox, QDialog, QPlainTextEdit, \ from PySide6.QtWidgets import QDialogButtonBox, QDialog, QPlainTextEdit, \
QMainWindow QMainWindow
from sohstationviewer.database.process_db import ( from sohstationviewer.database.process_db import (
...@@ -48,7 +48,7 @@ class InputDialog(QDialog): ...@@ -48,7 +48,7 @@ class InputDialog(QDialog):
class ChannelPreferDialog(OneWindowAtATimeDialog): 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 Dialog to create lists of preferred SOH channels that users want to
select for plotting. select for plotting.
...@@ -57,11 +57,11 @@ class ChannelPreferDialog(OneWindowAtATimeDialog): ...@@ -57,11 +57,11 @@ class ChannelPreferDialog(OneWindowAtATimeDialog):
:param parent: widget that calls this plotting :param parent: widget that calls this plotting
widget 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__() super(ChannelPreferDialog, self).__init__()
self.parent = parent self.parent = parent
self.dir_names = dir_names self.list_of_dir = list_of_dir
self.setWindowTitle("SOH Channel Preferences") self.setWindowTitle("SOH Channel Preferences")
self.setGeometry(100, 100, 1100, 800) self.setGeometry(100, 100, 1100, 800)
main_layout = QtWidgets.QVBoxLayout() main_layout = QtWidgets.QVBoxLayout()
...@@ -91,7 +91,7 @@ class ChannelPreferDialog(OneWindowAtATimeDialog): ...@@ -91,7 +91,7 @@ class ChannelPreferDialog(OneWindowAtATimeDialog):
""" """
self.add_db_chan_btn: Union[QtWidgets.QPushButton, None] = None 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 folder for the list of channels and data type to add
to the current row to the current row
""" """
...@@ -198,7 +198,7 @@ class ChannelPreferDialog(OneWindowAtATimeDialog): ...@@ -198,7 +198,7 @@ class ChannelPreferDialog(OneWindowAtATimeDialog):
self.scan_chan_btn = QtWidgets.QPushButton( self.scan_chan_btn = QtWidgets.QPushButton(
self, text='Scan Channels from MSeed Data') self, text='Scan Channels from MSeed Data')
self.scan_chan_btn.clicked.connect(self.scan_channels) 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) self.scan_chan_btn.setEnabled(False)
h_layout.addWidget(self.scan_chan_btn) h_layout.addWidget(self.scan_chan_btn)
...@@ -361,7 +361,7 @@ class ChannelPreferDialog(OneWindowAtATimeDialog): ...@@ -361,7 +361,7 @@ class ChannelPreferDialog(OneWindowAtATimeDialog):
def edit_soh_list(self, row_idx): def edit_soh_list(self, row_idx):
soh_list_item = self.soh_list_table_widget.item(row_idx, COL['IDs']) soh_list_item = self.soh_list_table_widget.item(row_idx, COL['IDs'])
edit_dialog = InputDialog(text=soh_list_item.text()) edit_dialog = InputDialog(text=soh_list_item.text())
if edit_dialog.exec_(): if edit_dialog.exec():
soh_list_item.setText(edit_dialog.get_input()) soh_list_item.setText(edit_dialog.get_input())
@QtCore.Slot() @QtCore.Slot()
...@@ -431,7 +431,7 @@ class ChannelPreferDialog(OneWindowAtATimeDialog): ...@@ -431,7 +431,7 @@ class ChannelPreferDialog(OneWindowAtATimeDialog):
@QtCore.Slot() @QtCore.Slot()
def scan_channels(self): 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. preferred channel list.
For RT130, all SOH channels are kept in a log file. It will be more 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 reasonable to get channels from DB because the task of getting
...@@ -457,15 +457,17 @@ class ChannelPreferDialog(OneWindowAtATimeDialog): ...@@ -457,15 +457,17 @@ class ChannelPreferDialog(OneWindowAtATimeDialog):
if result == QtWidgets.QMessageBox.Ok: if result == QtWidgets.QMessageBox.Ok:
self.add_db_channels() self.add_db_channels()
else: else:
self.scan_chan_btn.setEnabled(True)
# clear all key radio buttons # clear all key radio buttons
for i in reversed(range(self.data_set_key_layout.count())): for i in reversed(range(self.data_set_key_layout.count())):
widget = self.data_set_key_layout.takeAt(i).widget() widget = self.data_set_key_layout.takeAt(i).widget()
if widget is not None: if widget is not None:
widget.setParent(None) widget.setParent(None)
self.channel_info = read_mseed_channels( 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: 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) return QtWidgets.QMessageBox.warning(self, "No data", msg)
self.scan_chan_btn.setEnabled(True) self.scan_chan_btn.setEnabled(True)
self.data_set_key_layout.addWidget(QtWidgets.QLabel("Data Key:")) self.data_set_key_layout.addWidget(QtWidgets.QLabel("Data Key:"))
......
from PySide2.QtCore import Qt, QDate from PySide6.QtCore import Qt, QDate
from PySide2.QtGui import ( from PySide6.QtGui import (
QKeyEvent, QWheelEvent, QContextMenuEvent, QKeyEvent, QWheelEvent, QContextMenuEvent, QAction
) )
from PySide2.QtWidgets import ( from PySide6.QtWidgets import (
QDateEdit, QLineEdit, QMenu, QAction, QDateEdit, QLineEdit, QMenu,
) )
...@@ -86,7 +86,7 @@ class DayOfYearSupportedDateTextBox(QDateEdit): ...@@ -86,7 +86,7 @@ class DayOfYearSupportedDateTextBox(QDateEdit):
menu.addAction(copy_action) menu.addAction(copy_action)
menu.addAction(select_all_action) menu.addAction(select_all_action)
menu.exec_(event.globalPos()) menu.exec(event.globalPos())
def setDisplayFormat(self, format): def setDisplayFormat(self, format):
""" """
......
from __future__ import annotations from __future__ import annotations
from typing import Set, Dict 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.database.process_db import execute_db
from sohstationviewer.view.util.one_instance_at_a_time import \ from sohstationviewer.view.util.one_instance_at_a_time import \
...@@ -268,7 +268,7 @@ class UiDBInfoDialog(OneWindowAtATimeDialog): ...@@ -268,7 +268,7 @@ class UiDBInfoDialog(OneWindowAtATimeDialog):
msgbox.setText(msg) msgbox.setText(msg)
msgbox.addButton(QtWidgets.QMessageBox.Cancel) msgbox.addButton(QtWidgets.QMessageBox.Cancel)
msgbox.addButton('Continue', QtWidgets.QMessageBox.YesRole) msgbox.addButton('Continue', QtWidgets.QMessageBox.YesRole)
result = msgbox.exec_() result = msgbox.exec()
if result == QtWidgets.QMessageBox.Cancel: if result == QtWidgets.QMessageBox.Cancel:
return return
self.close() self.close()
......