NASA Logo
Ocean Color Science Software

ocssw V2022
check_products.py
Go to the documentation of this file.
1 #!/usr/bin/env python3
2 
3 import xml.etree.ElementTree as et
4 import argparse
5 
6 def float_or_double(prod_type: str) -> bool:
7  return prod_type == 'float' or prod_type == 'double'
8 
9 def set_integer_bounds(integer_type: str):
10  if integer_type == 'short':
11  return -32768, 32767
12  elif integer_type == 'int':
13  return -2147483648, 2147483647
14  elif integer_type == 'byte':
15  return -128, 127
16  else:
17  return 0, 0
18 
19 parser = argparse.ArgumentParser()
20 parser.add_argument('filename')
21 parser.add_argument('-v', '--verbose', action='store_true')
22 args = parser.parse_args()
23 
24 product_string = '{http://oceancolor.gsfc.nasa.gov}product'
25 type_string = '{http://oceancolor.gsfc.nasa.gov}type'
26 range_string = '{http://oceancolor.gsfc.nasa.gov}range'
27 validMax_string = '{http://oceancolor.gsfc.nasa.gov}validMax'
28 validMin_string = '{http://oceancolor.gsfc.nasa.gov}validMin'
29 displayMin_string = '{http://oceancolor.gsfc.nasa.gov}displayMin'
30 displayMax_string = '{http://oceancolor.gsfc.nasa.gov}displayMax'
31 addOffset_string = '{http://oceancolor.gsfc.nasa.gov}addOffset'
32 scaleFactor_string = '{http://oceancolor.gsfc.nasa.gov}scaleFactor'
33 algorithm_string = '{http://oceancolor.gsfc.nasa.gov}algorithm'
34 
35 xml_tree = et.parse(args.filename)
36 xml_tree_root = xml_tree.getroot()
37 for product in xml_tree_root.findall(product_string):
38  product_name = product.attrib['name']
39  default_range = None
40  product_has_error = False
41  product_invalid_validMin = False
42  product_invalid_validMax = False
43  product_missing_type = False
44  product_missing_validMax_or_validMin = False
45  product_missing_default_range = False
46  product_validMax, product_validMin = 0xdeadbeef, 0xdeadbeef
47  product_addOffset, product_scaleFactor = None, None
48 
49  if args.verbose:
50  print(product_name)
51  else:
52  (error_string := f'{product_name}')
53 
54  try:
55  product_type = product.find(type_string).text
56  except: #TODO: Default to float
57  product_type = 'float'
58  # product_has_error = True
59  # product_missing_type = True
60  # (product_type := 'Missing')
61  # if args.verbose:
62  # print(" Missing type")
63  # else:
64  # error_string += "\n Missing type"
65 
66  try:
67  (default_range := product.find(range_string))
68  _ = default_range.text # Try to induce an error for missing default range
69  except:
70  product_has_error = True
71  product_missing_default_range = True
72  if args.verbose:
73  print(" Missing default range")
74  else:
75  error_string += "\n Missing default range"
76 
77  try:
78  product_validMax = default_range.find(validMax_string).text
79  except:
80  product_has_error = True
81  product_missing_validMax_or_validMin = True
82  if product_missing_type is False and product_missing_default_range is False:
83  if args.verbose:
84  print(" Missing validMax")
85  else:
86  error_string += "\n Missing validMax"
87 
88  try:
89  product_validMin = default_range.find(validMin_string).text
90  except:
91  product_has_error = True
92  product_missing_validMax_or_validMin = True
93  if product_missing_type is False and product_missing_default_range is False:
94  if args.verbose:
95  print(" Missing validMin")
96  else:
97  error_string += "\n Missing validMin"
98 
99 
100  # Try to assign scaleFactor/addOffset. If any errors, they fall back to 1 and 0, respectively
101  try:
102  if not float_or_double(product_type):
103  product_addOffset = float(default_range.find(addOffset_string).text)
104  product_scaleFactor = float(default_range.find(scaleFactor_string).text)
105  else:
106  product_addOffset = 0
107  product_scaleFactor = 1
108 
109  if ((product_addOffset is not None and product_scaleFactor is not None)
110  and (product_addOffset != 0 and product_scaleFactor != 1)
111  and float_or_double(product_type)):
112  # Getting here means that a float/double has a scaleFactor/addOffset
113  raise TypeError # Technically not the right kind of exception; used just to differentiate
114  except AttributeError as ae:
115  if str(ae) == "'NoneType' object has no attribute 'text'":
116  pass
117  else:
118  product_has_error = True
119  if product_missing_default_range is False and not float_or_double(product_type):
120  if args.verbose:
121  print(" If scaleFactor/addOffset is defined, both must be")
122  else:
123  error_string += "\n If scaleFactor/addOffset is defined, both must be"
124  product_addOffset = 0
125  product_scaleFactor = 1
126  except TypeError as te:
127  product_has_error = True
128  if (product_missing_default_range is False and float_or_double(product_type) and
129  product_addOffset is not None and product_scaleFactor is not None):
130  if args.verbose:
131  print(f" {product_type} with a scaleFactor/addOffset")
132  else:
133  error_string += f"\n {product_type} with scaleFactor/addOffset"
134  product_addOffset = 0
135  product_scaleFactor = 1
136 
137  try:
138  _ = default_range.find(displayMin_string).text # Attempt to induce an exception
139  _ = default_range.find(displayMax_string).text # Attempt to induce an exception
140  except:
141  product_has_error = True
142  if product_missing_type is False and product_missing_default_range is False:
143  if args.verbose:
144  print(" Missing displayMin/displayMax")
145  else:
146  error_string += "\n Missing displayMin/displayMax"
147 
148 
149  int_min, int_max = set_integer_bounds(product_type)
150  min_possible = (int_min * product_scaleFactor) + product_addOffset
151  max_possible = (int_max * product_scaleFactor) + product_addOffset
152  # min_is_valid = min_possible <= float(product_validMin)
153  try:
154  min_is_valid = float(product_validMin) >= min_possible
155  except:
156  product_has_error = True
157  if args.verbose:
158  print(f" Issue parsing validMin, {product_validMin} cannot be parsed as a number")
159  else:
160  error_string += f"\n Issue parsing validMin of {product_validMin}"
161  try:
162  max_is_valid = float(product_validMax) <= max_possible
163  except ValueError:
164  product_has_error = True
165  if args.verbose:
166  print(f" Issue parsing validMax, {product_validMax} cannot be parsed as a number")
167  else:
168  error_string += f"\n Issue parsing validMax of {product_validMax}"
169 
170  if float_or_double(product_type): # Assume scaleFactor = 1, addOffset = 0
171  min_is_valid = True
172  max_is_valid = True
173  else:
174  try:
175  if (float(product_validMin) > float(product_validMax) and
176  product_missing_validMax_or_validMin is False):
177  product_has_error = True
178  if args.verbose:
179  print(f" validMin of {product_validMin} is greater than validMax of {product_validMax}")
180  else:
181  error_string += f"\n validMin of {product_validMin} is greater than validMax of {product_validMax}"
182  except:
183  pass
184 
185  if not min_is_valid and product_missing_validMax_or_validMin is False and product_missing_type is False:
186  product_has_error = True
187  product_invalid_validMin = True
188  if args.verbose:
189  print(f" Invalid validMin:", end= ' ') # to include ↓ would make too long a string in the editor
190  print(f"Minimum possible value is {min_possible} but validMin is {product_validMin}")
191  else:
192  error_string += f"\n Invalid validMin, Min possible value is {min_possible}"
193 
194  if not max_is_valid and product_missing_validMax_or_validMin is False and product_missing_type is False:
195  product_has_error = True
196  product_invalid_validMax = True
197  if args.verbose:
198  print(f" Invalid validMax:", end= ' ') # to include ↓ would make too long a string in the editor
199  print(f"Max possible value is {max_possible} but validMax is {product_validMax}")
200  else:
201  error_string += f"\n Invalid validMax, Max possible value is {max_possible}"
202 
203 
204 
205  for algo in product.findall(algorithm_string):
206  algo_validMin = product_validMin
207  algo_validMax = product_validMax
208  algo_scaleFactor = product_scaleFactor
209  algo_addOffset = product_addOffset
210  algo_name = product_name
211  algo_error_string = ''
212  algo_has_error = False
213 
214  try:
215  algo_name = algo.attrib['name']
216  except: # Default algo
217  algo_name = product_name
218  algo_error_string += f"\n {algo_name}"
219 
220  if args.verbose:
221  print(f" {algo_name}")
222 
223  try:
224  (algo_range := algo.find(range_string))
225  except: # Algo ranges default to product range
226  (algo_range := default_range)
227 
228  try:
229  algo_validMax = algo_range.find(validMax_string).text
230  algo_validMin = algo_range.find(validMin_string).text
231  algo_addOffset = float(algo_range.find(addOffset_string).text)
232  algo_scaleFactor = float(algo_range.find(scaleFactor_string).text)
233  except: # Algos default to product's values.
234  algo_validMax = product_validMax
235  algo_validMin = product_validMin
236  algo_addOffset = product_addOffset
237  algo_scaleFactor = product_scaleFactor
238 
239 
240  algo_min_possible = (int_min * product_scaleFactor) + product_addOffset
241  algo_max_possible = (int_max * product_scaleFactor) + product_addOffset
242  try:
243  algo_invalid_validMin = (min_possible > float(algo_validMin))
244  except:
245  if product_has_error: #Assume it would be the same error
246  pass
247  else:
248  if args.verbose:
249  print(f" Issue parsing validMin, {algo_validMin} cannot be parsed as a number")
250  else:
251  error_string += f"\n Issue parsing validMin {algo_validMin}"
252 
253  try:
254  algo_invalid_validMax = (float(algo_validMax) > max_possible)
255  except:
256  if product_has_error: #Assume it would be the same error
257  pass
258  else:
259  if args.verbose:
260  print(f" Issue parsing validMax, {algo_validMin} cannot be parsed as a number")
261  else:
262  error_string += f"\n Issue parsing validMax: {algo_validMax}"
263 
264  if float_or_double(product_type): # min and max should be all valid b/c sF of 1, aO of 0?
265  algo_invalid_validMin = False
266  algo_invalid_validMax = False
267 
268  if algo_invalid_validMin and not product_invalid_validMin:
269  if product_missing_type is False and product_missing_validMax_or_validMin is False:
270  product_has_error = True
271  algo_has_error = True
272  if args.verbose:
273  print(f" Invalid validMin:", end= ' ') # to include ↓ would make too long a string in the editor
274  print(f"Minimum possible value is {min_possible} but validMin is {algo_validMin}")
275  else:
276  algo_error_string += f"\n Invalid validMin, Min possible value is {min_possible}"
277  else:
278  pass
279 
280  if algo_invalid_validMax and not product_invalid_validMax:
281  if product_missing_type is False and product_missing_validMax_or_validMin is False:
282  product_has_error = True
283  algo_has_error = True
284  if args.verbose:
285  print(f" Invalid validMax:", end= ' ') # to include ↓ would make too long a string in the editor
286  print(f"Maximum possible value is {max_possible} but validMax is {algo_validMax}")
287  else:
288  algo_error_string += f"\n Invalid validMax, Max possible value is {max_possible}"
289  else:
290  pass
291 
292  if algo_has_error:
293  error_string += algo_error_string
294  product_type = 'none'
295 
296  # If args.verbose, we've been printing this whole time, no need to print the error string
297  if not args.verbose and product_has_error:
298  print(error_string)
bool float_or_double(str prod_type)
def set_integer_bounds(str integer_type)
void print(std::ostream &stream, const char *format)
Definition: PrintDebug.hpp:38
Definition: aerosol.c:136