-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathcheck_cnv_header.py
177 lines (132 loc) · 4.29 KB
/
check_cnv_header.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
from datetime import datetime
import argparse
import warnings
# hard-coded header entries that must match exactly
target_header = {
'Ship': 'ARANDA',
}
def short_warning_format(msg, *args, **kwargs):
return str(msg) + '\n'
warnings.formatwarning = short_warning_format
def warn(condition, message):
if not condition:
warnings.warn(message)
def no_check(s):
return True
def is_float(s):
try:
float(s)
return True
except ValueError:
return False
def is_int(s):
try:
int(s)
return True
except ValueError:
return False
def has_no_whitespace(s):
return len(s.split()) == 1
def is_fmi_date(d):
try:
datetime.strptime(d, '%d.%m.%Y %H.%M')
return True
except ValueError:
return False
class MessageManager:
"""
Decorator class to read header and generate warning message.
"""
def __init__(self, func):
self.func = func
def __call__(self, key, header, *args, **kwargs):
v = header[key]
fn = header['filename']
msg = f'{fn}: Could not parse "{key}": {v}'
# pass value and message to decorated function
self.func(v, msg, *args, **kwargs)
@MessageManager
def check_latlon(v, msg):
warn(len(v.split()) == 2, msg)
a, b = v.split()
warn(a.isnumeric(), msg)
warn(is_float(b), msg)
def check_kind(v, msg, kind_check, count=1, length=None):
def _check(x):
warn(kind_check(x), msg)
if count > 1:
warn(len(v.split(',')) == count, msg)
for x in v.split(','):
_check(x.strip())
else:
if length is not None:
warn(len(v.strip()) == length, msg)
_check(v)
@MessageManager
def check_float(v, msg, count=1, length=None):
check_kind(v, msg, is_float, count=count, length=length)
@MessageManager
def check_integer(v, msg, count=1, length=None):
check_kind(v, msg, is_int, count=count, length=length)
@MessageManager
def check_string(v, msg, count=1, length=None, alpha_only=False,
no_whitespace=False):
kind_check = no_check
if alpha_only:
kind_check = str.isalpha
if no_whitespace:
kind_check = has_no_whitespace
check_kind(v, msg, kind_check, count=count, length=length)
@MessageManager
def check_datetime(v, msg):
warn(is_fmi_date(v), msg)
@MessageManager
def check_cruise(v, msg):
words = v.split(',')
warn(len(words) == 2, msg)
cruise_number = words[0]
warn(len(cruise_number.split('/')) == 2, msg)
for x in cruise_number.split('/'):
warn(x.isnumeric(), msg)
def check_cnv_header(cnvfile):
"""
Check SeaBird cnv header for validity.
Checks only manually entered header entries. Prints warnings of offending
entries to stdout.
:arg cnvfile: cnv filename to process
"""
header = {}
with open(cnvfile, 'r', encoding="latin-1") as cf:
for line in cf.readlines():
# parse lines that begin with '**'
if line[:2] != '**':
continue
key, value = line.strip().split(':', 1)
key = key.replace('**', '').strip()
value = value.strip()
header[key] = value
# store filename in header dict
header['filename'] = cnvfile
for key in target_header:
msg = f'Bad entry: "{key}"\n "{header[key]}" != "{target_header[key]}"'
warn(header[key] == target_header[key], msg)
# parse entries
check_integer('Index', header, length=4)
check_cruise('Cruise (# , name)', header)
check_latlon('Latitude', header)
check_latlon('Longitude', header)
check_datetime('Date and time (UTC)', header)
check_integer('Depth', header)
check_string('CTD operator , Winch operator', header, count=2,
alpha_only=True, no_whitespace=True)
check_float('Wind speed, Direction, Pressure', header, count=3)
check_float('Sea temperature, Air temperature', header, count=2)
check_integer('Secchi depth', header)
if __name__ == '__main__':
parser = argparse.ArgumentParser(
description='Check validity of SBE cnv file headers')
parser.add_argument('files', metavar='cnv_file', nargs='+',
help='*.cnv files to process')
args = parser.parse_args()
for f in args.files:
check_cnv_header(f)