pyadps 0.2.1b0__py3-none-any.whl → 0.3.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- pyadps/Home_Page.py +11 -5
- pyadps/pages/01_Read_File.py +623 -215
- pyadps/pages/02_View_Raw_Data.py +97 -41
- pyadps/pages/03_Download_Raw_File.py +200 -67
- pyadps/pages/04_Sensor_Health.py +905 -0
- pyadps/pages/05_QC_Test.py +493 -0
- pyadps/pages/06_Profile_Test.py +971 -0
- pyadps/pages/07_Velocity_Test.py +600 -0
- pyadps/pages/08_Write_File.py +623 -0
- pyadps/pages/09_Add-Ons.py +168 -0
- pyadps/utils/__init__.py +5 -3
- pyadps/utils/autoprocess.py +371 -80
- pyadps/utils/logging_utils.py +269 -0
- pyadps/utils/metadata/config.ini +22 -4
- pyadps/utils/metadata/demo.000 +0 -0
- pyadps/utils/metadata/flmeta.json +420 -420
- pyadps/utils/metadata/vlmeta.json +611 -565
- pyadps/utils/multifile.py +292 -0
- pyadps/utils/plotgen.py +505 -3
- pyadps/utils/profile_test.py +720 -125
- pyadps/utils/pyreadrdi.py +164 -92
- pyadps/utils/readrdi.py +436 -186
- pyadps/utils/script.py +197 -147
- pyadps/utils/sensor_health.py +120 -0
- pyadps/utils/signal_quality.py +472 -68
- pyadps/utils/velocity_test.py +79 -31
- pyadps/utils/writenc.py +222 -39
- {pyadps-0.2.1b0.dist-info → pyadps-0.3.0.dist-info}/METADATA +13 -14
- pyadps-0.3.0.dist-info/RECORD +35 -0
- {pyadps-0.2.1b0.dist-info → pyadps-0.3.0.dist-info}/WHEEL +1 -1
- {pyadps-0.2.1b0.dist-info → pyadps-0.3.0.dist-info}/entry_points.txt +1 -0
- pyadps/pages/04_QC_Test.py +0 -334
- pyadps/pages/05_Profile_Test.py +0 -575
- pyadps/pages/06_Velocity_Test.py +0 -341
- pyadps/pages/07_Write_File.py +0 -452
- pyadps/utils/cutbin.py +0 -413
- pyadps/utils/regrid.py +0 -279
- pyadps-0.2.1b0.dist-info/RECORD +0 -31
- {pyadps-0.2.1b0.dist-info → pyadps-0.3.0.dist-info}/LICENSE +0 -0
@@ -0,0 +1,269 @@
|
|
1
|
+
"""
|
2
|
+
Reusable Logging Utilities
|
3
|
+
A clean, configurable logging module that can be used across multiple projects.
|
4
|
+
"""
|
5
|
+
|
6
|
+
import logging
|
7
|
+
import sys
|
8
|
+
from enum import Enum
|
9
|
+
from typing import Optional, Union
|
10
|
+
from pathlib import Path
|
11
|
+
|
12
|
+
|
13
|
+
class LogLevel(Enum):
|
14
|
+
"""Log level enumeration"""
|
15
|
+
|
16
|
+
DEBUG = logging.DEBUG
|
17
|
+
INFO = logging.INFO
|
18
|
+
WARNING = logging.WARNING
|
19
|
+
ERROR = logging.ERROR
|
20
|
+
CRITICAL = logging.CRITICAL
|
21
|
+
|
22
|
+
|
23
|
+
class CustomFormatter(logging.Formatter):
|
24
|
+
"""Custom colored formatter for console logging"""
|
25
|
+
|
26
|
+
COLORS = {
|
27
|
+
logging.DEBUG: "\x1b[36m", # Cyan
|
28
|
+
logging.INFO: "\x1b[32m", # Green
|
29
|
+
logging.WARNING: "\x1b[33m", # Yellow
|
30
|
+
logging.ERROR: "\x1b[31m", # Red
|
31
|
+
logging.CRITICAL: "\x1b[31;1m", # Bold Red
|
32
|
+
}
|
33
|
+
RESET = "\x1b[0m"
|
34
|
+
|
35
|
+
def __init__(self, include_timestamp: bool = True, include_module: bool = False):
|
36
|
+
"""
|
37
|
+
Initialize formatter with optional components
|
38
|
+
|
39
|
+
Args:
|
40
|
+
include_timestamp: Whether to include timestamp in log format
|
41
|
+
include_module: Whether to include module name in log format
|
42
|
+
"""
|
43
|
+
self.include_timestamp = include_timestamp
|
44
|
+
self.include_module = include_module
|
45
|
+
super().__init__()
|
46
|
+
|
47
|
+
def format(self, record):
|
48
|
+
"""Format log record with colors and optional components"""
|
49
|
+
# Build format string based on options
|
50
|
+
format_parts = []
|
51
|
+
|
52
|
+
if self.include_timestamp:
|
53
|
+
format_parts.append("%(asctime)s")
|
54
|
+
|
55
|
+
format_parts.append("%(levelname)s")
|
56
|
+
|
57
|
+
if self.include_module:
|
58
|
+
format_parts.append("%(name)s")
|
59
|
+
|
60
|
+
format_parts.append("%(message)s")
|
61
|
+
|
62
|
+
log_format = " - ".join(format_parts)
|
63
|
+
|
64
|
+
# Apply color
|
65
|
+
color = self.COLORS.get(record.levelno, "")
|
66
|
+
colored_format = color + log_format + self.RESET
|
67
|
+
|
68
|
+
formatter = logging.Formatter(
|
69
|
+
colored_format,
|
70
|
+
datefmt="%Y-%m-%d %H:%M:%S" if self.include_timestamp else None,
|
71
|
+
)
|
72
|
+
return formatter.format(record)
|
73
|
+
|
74
|
+
|
75
|
+
class LoggerConfig:
|
76
|
+
"""Configuration class for logger setup"""
|
77
|
+
|
78
|
+
def __init__(
|
79
|
+
self,
|
80
|
+
level: LogLevel = LogLevel.INFO,
|
81
|
+
include_timestamp: bool = True,
|
82
|
+
include_module: bool = False,
|
83
|
+
log_to_file: bool = False,
|
84
|
+
log_file_path: Optional[Union[str, Path]] = None,
|
85
|
+
file_log_level: Optional[LogLevel] = None,
|
86
|
+
max_file_size: int = 10 * 1024 * 1024, # 10MB
|
87
|
+
backup_count: int = 5,
|
88
|
+
):
|
89
|
+
"""
|
90
|
+
Initialize logger configuration
|
91
|
+
|
92
|
+
Args:
|
93
|
+
level: Console logging level
|
94
|
+
include_timestamp: Include timestamp in console output
|
95
|
+
include_module: Include module name in output
|
96
|
+
log_to_file: Whether to also log to file
|
97
|
+
log_file_path: Path for log file (if log_to_file is True)
|
98
|
+
file_log_level: File logging level (defaults to console level)
|
99
|
+
max_file_size: Maximum size of log file before rotation
|
100
|
+
backup_count: Number of backup files to keep
|
101
|
+
"""
|
102
|
+
self.level = level
|
103
|
+
self.include_timestamp = include_timestamp
|
104
|
+
self.include_module = include_module
|
105
|
+
self.log_to_file = log_to_file
|
106
|
+
self.log_file_path = Path(log_file_path) if log_file_path else None
|
107
|
+
self.file_log_level = file_log_level or level
|
108
|
+
self.max_file_size = max_file_size
|
109
|
+
self.backup_count = backup_count
|
110
|
+
|
111
|
+
|
112
|
+
class LoggerManager:
|
113
|
+
"""Manages logger configuration and setup"""
|
114
|
+
|
115
|
+
_loggers = {} # Cache for created loggers
|
116
|
+
|
117
|
+
@classmethod
|
118
|
+
def setup_logger(
|
119
|
+
self, name: str = "app", config: Optional[LoggerConfig] = None
|
120
|
+
) -> logging.Logger:
|
121
|
+
"""
|
122
|
+
Set up and configure logger with given configuration
|
123
|
+
|
124
|
+
Args:
|
125
|
+
name: Logger name
|
126
|
+
config: Logger configuration (uses defaults if None)
|
127
|
+
|
128
|
+
Returns:
|
129
|
+
Configured logger instance
|
130
|
+
"""
|
131
|
+
# Use default config if none provided
|
132
|
+
if config is None:
|
133
|
+
config = LoggerConfig()
|
134
|
+
|
135
|
+
# Return cached logger if it exists
|
136
|
+
cache_key = f"{name}_{id(config)}"
|
137
|
+
if cache_key in self._loggers:
|
138
|
+
return self._loggers[cache_key]
|
139
|
+
|
140
|
+
logger = logging.getLogger(name)
|
141
|
+
logger.setLevel(config.level.value)
|
142
|
+
|
143
|
+
# Remove existing handlers to avoid duplicates
|
144
|
+
logger.handlers.clear()
|
145
|
+
|
146
|
+
# Create console handler
|
147
|
+
console_handler = logging.StreamHandler(sys.stdout)
|
148
|
+
console_handler.setLevel(config.level.value)
|
149
|
+
console_formatter = CustomFormatter(
|
150
|
+
include_timestamp=config.include_timestamp,
|
151
|
+
include_module=config.include_module,
|
152
|
+
)
|
153
|
+
console_handler.setFormatter(console_formatter)
|
154
|
+
logger.addHandler(console_handler)
|
155
|
+
|
156
|
+
# Add file handler if requested
|
157
|
+
if config.log_to_file and config.log_file_path:
|
158
|
+
self._add_file_handler(logger, config)
|
159
|
+
|
160
|
+
# Prevent propagation to root logger
|
161
|
+
logger.propagate = False
|
162
|
+
|
163
|
+
# Cache the logger
|
164
|
+
self._loggers[cache_key] = logger
|
165
|
+
|
166
|
+
return logger
|
167
|
+
|
168
|
+
@classmethod
|
169
|
+
def _add_file_handler(self, logger: logging.Logger, config: LoggerConfig):
|
170
|
+
"""Add rotating file handler to logger"""
|
171
|
+
from logging.handlers import RotatingFileHandler
|
172
|
+
|
173
|
+
# Ensure log directory exists
|
174
|
+
config.log_file_path.parent.mkdir(parents=True, exist_ok=True)
|
175
|
+
|
176
|
+
file_handler = RotatingFileHandler(
|
177
|
+
config.log_file_path,
|
178
|
+
maxBytes=config.max_file_size,
|
179
|
+
backupCount=config.backup_count,
|
180
|
+
)
|
181
|
+
file_handler.setLevel(config.file_log_level.value)
|
182
|
+
|
183
|
+
# File logs typically include more detail
|
184
|
+
file_formatter = logging.Formatter(
|
185
|
+
"%(asctime)s - %(name)s - %(levelname)s - %(funcName)s:%(lineno)d - %(message)s",
|
186
|
+
datefmt="%Y-%m-%d %H:%M:%S",
|
187
|
+
)
|
188
|
+
file_handler.setFormatter(file_formatter)
|
189
|
+
logger.addHandler(file_handler)
|
190
|
+
|
191
|
+
@classmethod
|
192
|
+
def get_logger(self, name: str = "app") -> logging.Logger:
|
193
|
+
"""Get existing logger or create with default config"""
|
194
|
+
return logging.getLogger(name) or self.setup_logger(name)
|
195
|
+
|
196
|
+
@classmethod
|
197
|
+
def clear_cache(self):
|
198
|
+
"""Clear logger cache (useful for testing)"""
|
199
|
+
self._loggers.clear()
|
200
|
+
|
201
|
+
|
202
|
+
# Convenience functions for quick setup
|
203
|
+
def get_console_logger(
|
204
|
+
name: str = "app", level: LogLevel = LogLevel.INFO, include_timestamp: bool = True
|
205
|
+
) -> logging.Logger:
|
206
|
+
"""Quick setup for console-only logger"""
|
207
|
+
config = LoggerConfig(
|
208
|
+
level=level, include_timestamp=include_timestamp, include_module=False
|
209
|
+
)
|
210
|
+
return LoggerManager.setup_logger(name, config)
|
211
|
+
|
212
|
+
|
213
|
+
def get_file_logger(
|
214
|
+
name: str = "app",
|
215
|
+
log_file: Union[str, Path] = "app.log",
|
216
|
+
level: LogLevel = LogLevel.INFO,
|
217
|
+
file_level: Optional[LogLevel] = None,
|
218
|
+
) -> logging.Logger:
|
219
|
+
"""Quick setup for file + console logger"""
|
220
|
+
config = LoggerConfig(
|
221
|
+
level=level,
|
222
|
+
log_to_file=True,
|
223
|
+
log_file_path=log_file,
|
224
|
+
file_log_level=file_level or LogLevel.DEBUG,
|
225
|
+
)
|
226
|
+
return LoggerManager.setup_logger(name, config)
|
227
|
+
|
228
|
+
|
229
|
+
def get_detailed_logger(
|
230
|
+
name: str = "app",
|
231
|
+
log_file: Union[str, Path] = "app.log",
|
232
|
+
console_level: LogLevel = LogLevel.INFO,
|
233
|
+
file_level: LogLevel = LogLevel.DEBUG,
|
234
|
+
) -> logging.Logger:
|
235
|
+
"""Setup logger with detailed configuration"""
|
236
|
+
config = LoggerConfig(
|
237
|
+
level=console_level,
|
238
|
+
include_timestamp=True,
|
239
|
+
include_module=True,
|
240
|
+
log_to_file=True,
|
241
|
+
log_file_path=log_file,
|
242
|
+
file_log_level=file_level,
|
243
|
+
)
|
244
|
+
return LoggerManager.setup_logger(name, config)
|
245
|
+
|
246
|
+
|
247
|
+
# Example usage
|
248
|
+
if __name__ == "__main__":
|
249
|
+
# Test different logger configurations
|
250
|
+
|
251
|
+
# Simple console logger
|
252
|
+
simple_logger = get_console_logger("simple", LogLevel.DEBUG)
|
253
|
+
simple_logger.debug("Debug message")
|
254
|
+
simple_logger.info("Info message")
|
255
|
+
simple_logger.warning("Warning message")
|
256
|
+
simple_logger.error("Error message")
|
257
|
+
|
258
|
+
print("\n" + "=" * 50 + "\n")
|
259
|
+
|
260
|
+
# File + console logger
|
261
|
+
file_logger = get_file_logger("file_test", "test.log", LogLevel.INFO)
|
262
|
+
file_logger.info("This goes to both console and file")
|
263
|
+
file_logger.debug("This only goes to file")
|
264
|
+
|
265
|
+
print("\n" + "=" * 50 + "\n")
|
266
|
+
|
267
|
+
# Detailed logger
|
268
|
+
detailed_logger = get_detailed_logger("detailed", "detailed.log")
|
269
|
+
detailed_logger.info("Detailed logging with module names")
|
pyadps/utils/metadata/config.ini
CHANGED
@@ -4,8 +4,9 @@ input_file_path = /home/user/data/
|
|
4
4
|
input_file_name = adcp_raw.000
|
5
5
|
|
6
6
|
# Output file settings. Do not enter file extension.
|
7
|
-
output_file_path = /home/
|
7
|
+
output_file_path = /home/nio/Videos/output/
|
8
8
|
output_file_name_raw = adcp_raw.nc
|
9
|
+
output_file_name_vlead = adcp_vlead.nc
|
9
10
|
output_file_name_processed = adcp_proc.nc
|
10
11
|
|
11
12
|
# Choose between 'netcdf' or 'csv' for the raw output format
|
@@ -16,11 +17,18 @@ output_format_processed = csv
|
|
16
17
|
|
17
18
|
[DownloadOptions]
|
18
19
|
# Options to download raw and/or processed output files
|
20
|
+
axis_option = ensemble
|
19
21
|
download_raw = True
|
22
|
+
download_vlead = True
|
20
23
|
download_processed = True
|
21
24
|
apply_mask = True
|
22
25
|
download_mask = True
|
23
26
|
|
27
|
+
[SensorTest]
|
28
|
+
sensor_test = True
|
29
|
+
transducer_depth = 200
|
30
|
+
|
31
|
+
|
24
32
|
[QCTest]
|
25
33
|
# Enable or Disable QC Test (True/False)
|
26
34
|
qc_test = True
|
@@ -30,6 +38,8 @@ echo_intensity = 40
|
|
30
38
|
false_target = 50
|
31
39
|
three_beam = True
|
32
40
|
percentage_good = 50
|
41
|
+
orientation = up
|
42
|
+
|
33
43
|
|
34
44
|
[ProfileTest]
|
35
45
|
# Enable or Disable Profile Test (True/False)
|
@@ -39,17 +49,22 @@ trim_ends_start_index = 2
|
|
39
49
|
trim_ends_end_index = 17086
|
40
50
|
cut_bins = True
|
41
51
|
cut_bins_add_cells = 2
|
52
|
+
manual_cutbins = True
|
53
|
+
manual_cut_bins = [2,10,0,8000] [40,42,0,8000] [12,14,0,8000]
|
42
54
|
regrid = True
|
43
|
-
regrid_option =
|
55
|
+
regrid_option = Cell
|
56
|
+
regrid_interpolation = nearest
|
57
|
+
transducer_depth = 200
|
58
|
+
water_column_depth = 200
|
44
59
|
|
45
60
|
[VelocityTest]
|
46
61
|
# Enable or Disable Velocity Test (True/False)
|
47
62
|
velocity_test = True
|
48
63
|
magnetic_declination = True
|
49
64
|
latitude = 0.0
|
50
|
-
longitude = 0
|
65
|
+
longitude = 83.0
|
51
66
|
depth = 0
|
52
|
-
year =
|
67
|
+
year = 2025
|
53
68
|
cutoff = True
|
54
69
|
max_zonal_velocity = 250
|
55
70
|
max_meridional_velocity = 250
|
@@ -79,3 +94,6 @@ file_created_by = xxxx
|
|
79
94
|
contact = abcd
|
80
95
|
comments = No comments
|
81
96
|
|
97
|
+
|
98
|
+
|
99
|
+
|
Binary file
|