netcdf_checker.py

changeset 0
fa814cbe01f9
child 2
dcc02b7b6c52
equal deleted inserted replaced
-1:000000000000 0:fa814cbe01f9
1 import os
2 import fnmatch
3 import numpy as np
4 import netCDF4 as netcdf
5
6
7 ERROR_ORDER = {'notification': 1,
8 'warning': 2,
9 'error': 3,
10 }
11
12 # Used when printing the report
13 header_template = """\n----- Report for file {0.filename} -----
14 Checked against specification: {0.specs[name]}.
15
16 Output summary:
17 Errors: {1[error]}, Warnings: {1[warning]}, Notifications: {1[notification]}"""
18
19
20
21 # We firstly define the possible specification elements
22 # In the end of this file we use them to define complete specifications
23
24
25 class SpecGenericError:
26 def __repr__(self):
27 return "{0}: {1}".format(self.level.title(), self.message)
28
29
30 class SpecError(SpecGenericError):
31 def __init__(self, message):
32 self.message = message
33 self.level = 'error'
34
35
36 class SpecWarning(SpecGenericError):
37 def __init__(self, message):
38 self.message = message
39 self.level = 'warning'
40
41
42 class SpecNotification(SpecGenericError):
43 def __init__(self, message):
44 self.message = message
45 self.level = 'notification'
46
47
48 class GenericSpecification:
49 @property
50 def continue_check(self):
51 return True
52
53 class DimensionMandatory(GenericSpecification):
54 def __init__(self, is_mandatory = True):
55 self.block_next = True # if true the next checks for this dimension will not be performed.
56 self.is_mandatory = is_mandatory
57
58 def check(self, netcdf_file, dimension_name):
59 the_dimension = netcdf_file.dimensions.get(dimension_name, None)
60
61 error = []
62 if the_dimension:
63 # If the dimension is found in the file
64 self.dimension_exists = True
65 check_passed = True
66
67 else:
68 self.dimension_exists = False
69 if self.is_mandatory:
70 # If a mandatory dimension is not found in the file
71 check_passed = False
72 error.append(SpecError('The dimension {0} is obligatory but was not found in the file.'.format(dimension_name)))
73 else:
74 check_passed = True
75 error.append(SpecNotification('The optional dimension {0} was not found in the file.'.format(dimension_name)))
76
77 self.check_passed = check_passed
78 self.error = error
79
80 return check_passed, error
81
82 @property
83 def continue_check(self):
84 if (not self.dimension_exists) and (self.block_next):
85 return False
86 else:
87 return True
88
89
90 class DimensionUnlimited(GenericSpecification):
91 def __init__(self, is_unlimited):
92 self.block_next = False
93 self.is_unlimited = is_unlimited
94
95 def check(self, netcdf_file, dimension_name):
96 the_dimension = netcdf_file.dimensions.get(dimension_name, None)
97 error = []
98
99 if the_dimension:
100 if the_dimension.isunlimited() == True and self.is_unlimited == False:
101 check_passed = False
102 error.append(SpecWarning('Dimension {0} should not be unlimited but is.'.format(dimension_name)))
103 elif the_dimension.isunlimited() == False and self.is_unlimited == True:
104 check_passed = False
105 error.append(SpecWarning('Dimension {0} should be unlimited but it is not.'.format(dimension_name)))
106 else:
107 check_passed = True
108 else:
109 check_passed = True
110 error.append(SpecError('Dimension {0} should be unlimited, but was not found in the file.'.format(dimension_name)))
111
112 self.check_passed = check_passed
113 self.error = error
114
115 return check_passed, error
116
117
118 class VariableMandatory(GenericSpecification):
119 def __init__(self, is_mandatory = True):
120 self.block_next = True # if true the next checks for this variable will not be performed.
121 self.is_mandatory = is_mandatory
122
123 def check(self, netcdf_file, variable_name):
124 the_variable = netcdf_file.variables.get(variable_name, None)
125 error = []
126
127 if the_variable != None:
128 # If the variable is found in the file
129 self.variable_exists = True
130 check_passed = True
131
132 else:
133 self.variable_exists = False
134 if self.is_mandatory:
135 # If a mandatory variable is not found in the file
136 check_passed = False
137 error.append(SpecError('The variable {0} is obligatory but was not found in the file.'.format(variable_name)))
138 else:
139 check_passed = True
140 error.append(SpecNotification('The optional variable {0} was not found in the file.'.format(variable_name)))
141
142 self.check_passed = check_passed
143 self.error = error
144
145 return check_passed, error
146
147 @property
148 def continue_check(self):
149 if (not self.variable_exists) and (self.block_next):
150 return False
151 else:
152 return True
153
154 class VariableDimensions(GenericSpecification):
155 def __init__(self, dimensions):
156 self.dimensions = dimensions
157
158 def check(self, netcdf_file, variable_name):
159 the_variable = netcdf_file.variables.get(variable_name, None)
160
161 if the_variable != None:
162 variable_dimensions = list(the_variable.dimensions)
163 error = []
164 check_passed = True
165 for dimension in self.dimensions:
166 if not (dimension in variable_dimensions):
167 check_passed = False
168 error.append(SpecError("Variable {0} does not have dimension {1}.".format(variable_name, dimension)))
169
170 # If all dimensions are present, check if the variables are in the
171 # correct order.
172 if check_passed:
173 if list(self.dimensions) != variable_dimensions:
174 check_passed = False
175 error.append(SpecError("Variable {0} has wrong dimension order: {1} instead of {2}.".format(variable_name,
176 variable_dimensions,
177 list(self.dimensions))))
178 for dimension in variable_dimensions:
179 if dimension not in self.dimensions:
180 error.append(SpecWarning('Dimension {0} found in variable {1} but is not defined in the specifications'.format(dimension, variable_name)))
181
182 else:
183 check_passed = False
184 error = [SpecError('Variable {0} should be checked for dimensions, but was not found in the file.'.format(variable_name)),]
185
186 self.check_passed = check_passed
187 self.error = error
188
189 return check_passed, error
190
191
192 class VariableType(GenericSpecification):
193 def __init__(self, dtype):
194 self.dtype = dtype
195
196 def check(self, netcdf_file, variable_name):
197 the_variable = netcdf_file.variables.get(variable_name, None)
198 error = []
199
200 if the_variable != None:
201 # Get the internal python type and not the numpy.dtype.
202 # The conversions guarantee (?) that a single element is always returned
203 variable_type_python = type(np.asscalar(np.asarray(np.asarray(the_variable[:]).item(0))))
204
205 if not (variable_type_python == self.dtype):
206 check_passed = False
207 error.append(SpecError('Variable {0} is of type {1} while it should be {2}'.format(variable_name, the_variable.dtype, self.dtype)))
208 else:
209 check_passed = True
210 else:
211 check_passed = False
212 error.append(SpecError('Variable {0} should be checked for type, but was not found in the file.'.format(variable_name)))
213
214 self.check_passed = check_passed
215 self.error = error
216
217 return check_passed, error
218
219
220 class AttributeMandatory(GenericSpecification):
221 def __init__(self, is_mandatory = True):
222 self.block_next = True # if true the next checks for this variable will not be performed.
223 self.is_mandatory = is_mandatory
224
225 def check(self, netcdf_file, attribute_name):
226 the_attribute = getattr(netcdf_file, attribute_name, None)
227 error = []
228
229 if the_attribute:
230 # If the variable is found in the file
231 self.attribute_exists = True
232 check_passed = True
233
234 else:
235 self.attribute_exists = False
236 if self.is_mandatory:
237 # If a mandatory variable is not found in the file
238 check_passed = False
239 error.append(SpecError('The attribute {0} is obligatory but was not found in the file.'.format(attribute_name)))
240 else:
241 check_passed = True
242 error.append(SpecNotification('The optional attribute {0} was not found in the file.'.format(attribute_name)))
243
244 self.check_passed = check_passed
245 self.error = error
246
247 return check_passed, error
248
249 @property
250 def continue_check(self):
251 if (not self.attribute_exists) and (self.block_next):
252 return False
253 else:
254 return True
255
256
257 class AttributeType(GenericSpecification):
258 def __init__(self, dtype, block_next = True):
259 self.block_next = block_next
260 self.dtype = dtype
261
262 def check(self, netcdf_file, attribute_name):
263 the_attribute = getattr(netcdf_file, attribute_name, None)
264 error = []
265
266 if the_attribute:
267 # Get the internal python type and not the numpy.dtype.
268 # The conversions guarantee (?) that a single element is always returned
269 try:
270 attribute_type_python = type(np.asscalar(np.asarray(np.asarray(the_attribute[:]).item(0))))
271 except:
272 attribute_type_python = type(np.asscalar(the_attribute))
273
274 if not (attribute_type_python == self.dtype):
275 check_passed = False
276 error.append(SpecError('Attribute {0} is of type {1} while it should be {2}'.format(attribute_name, type(the_attribute).__name__, self.dtype.__name__)))
277 else:
278 error = None
279 check_passed = True
280 else:
281 check_passed = False
282 error.append(SpecError('Attribute {0} should be checked for type, but was not found in the file.'.format(attribute_name)))
283
284 self.check_passed = check_passed
285 self.error = error
286
287 return check_passed, error
288
289 @property
290 def continue_check(self):
291 if (not self.check_passed) and (self.block_next):
292 return False
293 else:
294 return True
295
296
297 class AttributeStrLength(GenericSpecification):
298
299 def __init__(self, length):
300 self.length = length
301
302
303 def check(self, netcdf_file, attribute_name):
304 the_attribute = getattr(netcdf_file, attribute_name, None)
305 error = []
306
307 if the_attribute:
308 if len(the_attribute) != self.length:
309 check_passed = False
310 error.append(SpecError('Attribute {0} should be of length {1} while it has length {2}'.format(attribute_name, self.length, len(the_attribute))))
311 else:
312 check_passed = True
313 else:
314 check_passed = False
315 error.append(SpecError('Attribute {0} should be checked for length, but was not found in the file.'.format(attribute_name)))
316
317 self.check_passed = check_passed
318 self.error = error
319
320 return check_passed, error
321
322
323 class FilenameShellPattern(GenericSpecification):
324 def __init__(self, shell_pattern):
325 self.pattern = shell_pattern
326
327 def check(self, netcdf_file, filename):
328 error = []
329
330 if fnmatch.fnmatch(filename, self.pattern):
331 check_passed = True
332 else:
333 check_passed = False
334 error.append(SpecError('Filename {0} does not match patter {1}'.format(filename, self.pattern)))
335
336 self.check_passed = check_passed
337 self.error = error
338
339 return check_passed, error
340
341
342 # This is the main class of the script.
343 class FileChecker:
344 """ It uses the provided specifications to check the a file.
345 It can be used with the 'with' statement. For example:
346
347 with FileChecker(filename, specs) as file_checker:
348 file_checker.run_checks()
349 file_checker.print_report('error')
350
351 """
352
353
354 def __init__(self, filepath, specs):
355 self.file = None
356 self.checks_run = False
357 self.filepath = filepath
358 self.filename = os.path.basename(filepath)
359 self.specs = specs
360 self.check_results = {}
361 self.check_results['general'] = []
362
363 def __enter__(self):
364 self.open_file()
365 return self
366
367 def __exit__(self, type, value, traceback):
368 if self.file:
369 self.file.close()
370
371 def open_file(self):
372 try:
373 self.file = netcdf.Dataset(self.filepath)
374 except:
375 self.check_results['general'].append(SpecError('Could not open file {0}.'.format(self.filename)))
376
377 def close_file(self):
378 self.file.close()
379
380 def run_checks(self):
381 if self.file:
382 self.check_file()
383 self.check_attributes()
384 self.check_dimensions()
385 self.check_variables()
386 self.checks_run = True
387
388 def check_file(self):
389 self.check_results['file'] = []
390
391 try:
392 specs_file = self.specs['file']
393 except:
394 specs_file = []
395
396 for file_spec in specs_file:
397 check_passed, error = file_spec.check(self.file, self.filename)
398
399 if error:
400 self.check_results['file'].extend(list(error))
401
402 if not file_spec.continue_check:
403 break
404
405 def check_attributes(self):
406 """ Check if attributes are according to specs """
407
408 self.check_results['attributes'] = []
409
410 try:
411 spec_attributes = self.specs['attributes'].keys()
412 except:
413 spec_attributes = []
414
415 for attribute_name in spec_attributes:
416 attribute_specs = self.specs['attributes'][attribute_name]
417 for attribute_spec in attribute_specs:
418 check_passed, error = attribute_spec.check(self.file, attribute_name)
419
420 if error:
421 self.check_results['attributes'].extend(list(error))
422
423 if not attribute_spec.continue_check:
424 break # Don't continue checking specifications if a blocking check failed.
425
426 for attribute_name in self.file.ncattrs():
427 if attribute_name not in spec_attributes:
428 self.check_results['attributes'].append(SpecWarning('Attribute {0} found in the file but is not defined in the specifications'.format(attribute_name)))
429
430 def check_dimensions(self):
431 """ Check if dimension are according to specs """
432 self.check_results['dimensions'] = []
433
434 try:
435 spec_dimensions = self.specs['dimensions'].keys()
436 except:
437 spec_dimensions = []
438
439
440 for dimension_name in spec_dimensions:
441 dimension_specs = self.specs['dimensions'][dimension_name]
442 for dimension_spec in dimension_specs:
443 check_passed, error = dimension_spec.check(self.file, dimension_name)
444
445 if error:
446 self.check_results['dimensions'].extend(list(error))
447
448 if not dimension_spec.continue_check:
449 break # Don't continue checking specifications if a blocking check failed.
450
451 for dimension in self.file.dimensions:
452 if dimension not in spec_dimensions:
453 self.check_results['dimensions'].append(SpecWarning('Dimension {0} found in the file but is not defined in the specifications'.format(dimension)))
454
455 def check_variables(self):
456 """ Check if variables are according to specs """
457
458 self.check_results['variables'] = []
459
460 try:
461 spec_variables = self.specs['variables'].keys()
462 except:
463 spec_variables = []
464
465 for variable_name in spec_variables:
466 variable_specs = self.specs['variables'][variable_name]
467 for variable_spec in variable_specs:
468 check_passed, error = variable_spec.check(self.file, variable_name)
469
470 if error:
471 self.check_results['variables'].extend(list(error))
472
473 if not variable_spec.continue_check:
474 break # Don't continue checking specifications if a blocking check failed.
475
476 for variable_name in self.file.variables:
477 if variable_name not in spec_variables:
478 self.check_results['variables'].append(SpecWarning('Variable {0} found in the file but is not defined in the specifications'.format(variable_name)))
479
480 def file_ok(self, level = 'error'):
481 """ Check if the file checked is ok. What ok means is defined by the level variable """
482
483 status = None
484 if self.checks_run:
485 status = True
486 for category, result_list in self.check_results.items():
487 for result in result_list:
488 if ERROR_ORDER[result.level] >= ERROR_ORDER[level]:
489 status = False
490
491 return status
492
493 def results_for_level(self, level):
494 """ Returns all the results of a specific level """
495 results = None
496 if self.checks_run:
497 results = []
498 for category, result_list in self.check_results.items():
499 for result in result_list:
500 if ERROR_ORDER[result.level] == ERROR_ORDER[level]:
501 results.append(result)
502
503 return results
504
505 def results_by_level(self):
506 """ Returns a dictionary with the results by level. """
507
508 results = {}
509 for level, order in ERROR_ORDER.items():
510 results[level] = self.results_for_level(level)
511
512 return results
513
514 def result_count(self):
515 """ Returns a dictionary with the number of results per category. """
516
517 result_number = {}
518 results = self.results_by_level()
519
520 for category, error_list in results.items():
521 if error_list is None:
522 result_number[category] = 0
523 else:
524 result_number[category] = len(error_list)
525 return result_number
526
527 def print_report(self, level):
528 """ Print a report for the given level. """
529
530 print header_template.format(self, self.result_count())
531
532 results = self.results_by_level()
533
534 for result_level in ['error', 'warning', 'notification']:
535 if ERROR_ORDER[result_level] >= ERROR_ORDER[level]:
536 print "\n{0} details".format(result_level.capitalize())
537 print "----------------"
538 for result in results[result_level]:
539 print result
540
541
542 # Sounding file specifications
543 sounding_specs = {'file': [FilenameShellPattern('rs_*.nc'),],
544 'dimensions': {'points': [DimensionMandatory(True),
545 DimensionUnlimited(False),],
546 },
547 'variables': {'Altitude': [VariableMandatory(True),
548 VariableDimensions(['points',]),
549 VariableType(float)],
550 'Temperature': [VariableMandatory(True),
551 VariableDimensions(['points',]),
552 VariableType(float)],
553 'Pressure': [VariableMandatory(True),
554 VariableDimensions(['points',]),
555 VariableType(float)],
556 'RelativeHumidity': [VariableMandatory(False),
557 VariableDimensions(['points',]),
558 VariableType(float)],
559 },
560 'attributes': {'Latitude_degrees_north': [AttributeMandatory(True),
561 AttributeType(float),],
562 'Longitude_degrees_east': [AttributeMandatory(True),
563 AttributeType(float),],
564 'Altitude_meter_asl': [AttributeMandatory(True),
565 AttributeType(float),],
566 'Location': [AttributeMandatory(False),
567 AttributeType(unicode),],
568 'Sounding_Station_Name': [AttributeMandatory(False),
569 AttributeType(unicode),],
570 'WMO_Station_Number': [AttributeMandatory(False),
571 AttributeType(unicode),],
572 'WBAN_Station_Number':[AttributeMandatory(False),
573 AttributeType(unicode),],
574 'Sounding_Start_Date':[AttributeMandatory(True),
575 AttributeType(unicode, block_next = True),
576 AttributeStrLength(8)],
577 'Sounding_Start_Time_UT':[AttributeMandatory(True),
578 AttributeType(unicode, block_next = True),
579 AttributeStrLength(6)],
580 'Sounding_Stop_Time_UT':[AttributeMandatory(False),
581 AttributeType(unicode, block_next = True),
582 AttributeStrLength(6)],
583 },
584 'name': "SCC Sounding file"
585 }
586
587 # Lidar ratio file specifications
588 lidar_ratio_specs = {'file': [FilenameShellPattern('*.nc'),],
589 'dimensions': {'points': [DimensionMandatory(True),
590 DimensionUnlimited(False),],
591 'products': [DimensionMandatory(True),
592 DimensionUnlimited(False),],
593 },
594 'variables': {'Altitude': [VariableMandatory(True),
595 VariableDimensions(['points',]),
596 VariableType(float)],
597 'Lidar_Ratio': [VariableMandatory(True),
598 VariableDimensions(['points', 'products']),
599 VariableType(float)],
600 'product_ID': [VariableMandatory(True),
601 VariableDimensions(['products',]),
602 VariableType(int)],
603 },
604 'attributes': {'Lidar_Station_Name': [AttributeMandatory(True),
605 AttributeType(unicode),],
606 },
607 'name': "SCC Lidar ratio file"
608 }
609
610 # Overlap file specifications
611 overlap_specs = {'file': [FilenameShellPattern('ov_*.nc'),],
612 'dimensions': {'points': [DimensionMandatory(True),
613 DimensionUnlimited(False),],
614 'channels': [DimensionMandatory(True),
615 DimensionUnlimited(False),],
616 },
617 'variables': {'Altitude': [VariableMandatory(True),
618 VariableDimensions(['points',]),
619 VariableType(float)],
620 'Overlap_Function': [VariableMandatory(True),
621 VariableDimensions(['points', 'channels']),
622 VariableType(float)],
623 'channel_ID': [VariableMandatory(True),
624 VariableDimensions(['channels',]),
625 VariableType(int)],
626 },
627 'attributes': {'Lidar_Station_Name': [AttributeMandatory(True),
628 AttributeType(unicode, block_next = True),
629 AttributeStrLength(2)],
630 'Overlap_Measurement_Date': [AttributeMandatory(True),
631 AttributeType(unicode, block_next = True),
632 AttributeStrLength(8)],
633 },
634 'name': "SCC Overlap file"
635 }
636
637 # Raw data file specifications
638 data_specs = {'file': [FilenameShellPattern('*.nc'),],
639 'dimensions': {'points': [DimensionMandatory(True),
640 DimensionUnlimited(False),],
641 'channels': [DimensionMandatory(True),
642 DimensionUnlimited(False),],
643 'nb_of_time_scales': [DimensionMandatory(True),
644 DimensionUnlimited(False),],
645 'time': [DimensionMandatory(True),
646 DimensionUnlimited(True),],
647 'time_bck': [DimensionMandatory(False),
648 DimensionUnlimited(False),],
649 'scan_angles': [DimensionMandatory(True),
650 DimensionUnlimited(False),],
651 },
652 'variables': {'channel_ID': [VariableMandatory(True),
653 VariableDimensions(['channels',]),
654 VariableType(int)],
655 'Laser_Repetition_Rate': [VariableMandatory(False),
656 VariableDimensions(['channels',]),
657 VariableType(int)],
658 'Laser_Pointing_Angle': [VariableMandatory(True),
659 VariableDimensions(['scan_angles',]),
660 VariableType(float)],
661 'ID_Range': [VariableMandatory(False),
662 VariableDimensions(['channels',]),
663 VariableType(int)],
664 'Scattering_Mechanism': [VariableMandatory(False),
665 VariableDimensions(['channels',]),
666 VariableType(int)],
667 'Emitted_Wavelength': [VariableMandatory(False),
668 VariableDimensions(['channels',]),
669 VariableType(float)],
670 'Detected_Wavelength': [VariableMandatory(False),
671 VariableDimensions(['channels',]),
672 VariableType(float)],
673 'Raw_Data_Range_Resolution': [VariableMandatory(False),
674 VariableDimensions(['channels',]),
675 VariableType(float)],
676 'Background_Mode': [VariableMandatory(False),
677 VariableDimensions(['channels',]),
678 VariableType(int)],
679 'Background_Low': [VariableMandatory(True),
680 VariableDimensions(['channels',]),
681 VariableType(float)],
682 'Background_High': [VariableMandatory(True),
683 VariableDimensions(['channels',]),
684 VariableType(float)],
685 'Molecular_Calc': [VariableMandatory(True),
686 VariableDimensions([]),
687 VariableType(int)],
688 'id_timescale': [VariableMandatory(True),
689 VariableDimensions(['channels',]),
690 VariableType(int)],
691 'Dead_Time_Corr_Type': [VariableMandatory(False),
692 VariableDimensions(['channels',]),
693 VariableType(int)],
694 'Dead_Time': [VariableMandatory(False),
695 VariableDimensions(['channels',]),
696 VariableType(float)],
697 'Acquisition_Mode': [VariableMandatory(False),
698 VariableDimensions(['channels',]),
699 VariableType(int)],
700 'Trigger_Delay': [VariableMandatory(False),
701 VariableDimensions(['channels',]),
702 VariableType(float)],
703 'Laser_Pointing_Angle_of_Profiles': [VariableMandatory(True),
704 VariableDimensions(['time','nb_of_time_scales',]),
705 VariableType(int)],
706 'Raw_Data_Start_Time': [VariableMandatory(True),
707 VariableDimensions(['time','nb_of_time_scales',]),
708 VariableType(int)],
709 'Raw_Data_Stop_Time': [VariableMandatory(True),
710 VariableDimensions(['time','nb_of_time_scales',]),
711 VariableType(int)],
712 'Laser_Shots': [VariableMandatory(True),
713 VariableDimensions(['time','channels',]),
714 VariableType(int)],
715 'Raw_Lidar_Data': [VariableMandatory(False),
716 VariableDimensions(['time', 'channels', 'points']),
717 VariableType(float)],
718 'Depolarization_Factor': [VariableMandatory(False),
719 VariableDimensions(['channels',]),
720 VariableType(float)],
721 'LR_Input': [VariableMandatory(False),
722 VariableDimensions(['channels',]),
723 VariableType(int)],
724 'DAQ_Range': [VariableMandatory(False),
725 VariableDimensions(['channels',]),
726 VariableType(float)],
727 'Pressure_at_Lidar_Station': [VariableMandatory(False),
728 VariableDimensions([]),
729 VariableType(float)],
730 'Temperature_at_Lidar_Station': [VariableMandatory(False),
731 VariableDimensions([]),
732 VariableType(float)],
733 'Background_Profile': [VariableMandatory(False),
734 VariableDimensions(['time_bck', 'channels', 'points']),
735 VariableType(float)],
736 'Raw_Bck_Start_Time': [VariableMandatory(False),
737 VariableDimensions(['time_bck','nb_of_time_scales',]),
738 VariableType(int)],
739 'Raw_Bck_Stop_Time': [VariableMandatory(False),
740 VariableDimensions(['time_bck','nb_of_time_scales',]),
741 VariableType(int)],
742 'Error_On_Raw_Lidar_Data': [VariableMandatory(False),
743 VariableDimensions(['time','channels', 'points']),
744 VariableType(float)],
745 'First_Signal_Rangebin': [VariableMandatory(False),
746 VariableDimensions(['channels',]),
747 VariableType(int)],
748 },
749 'attributes': {'Measurement_ID': [AttributeMandatory(True),
750 AttributeType(unicode, block_next = True),
751 AttributeStrLength(12)],
752 'RawData_Start_Date': [AttributeMandatory(True),
753 AttributeType(unicode, block_next = True),
754 AttributeStrLength(8)],
755 'RawData_Start_Time_UT': [AttributeMandatory(True),
756 AttributeType(unicode, block_next = True),
757 AttributeStrLength(6)],
758 'RawData_Stop_Time_UT': [AttributeMandatory(True),
759 AttributeType(unicode, block_next = True),
760 AttributeStrLength(6)],
761 'RawBck_Start_Date': [AttributeMandatory(False),
762 AttributeType(unicode, block_next = True),
763 AttributeStrLength(8)],
764 'RawBck_Start_Time_UT': [AttributeMandatory(False),
765 AttributeType(unicode, block_next = True),
766 AttributeStrLength(6)],
767 'RawBck_Stop_Time_UT': [AttributeMandatory(False),
768 AttributeType(unicode, block_next = True),
769 AttributeStrLength(6)],
770 'Sounding_File_Name': [AttributeMandatory(False),
771 AttributeType(unicode),],
772 'LR_File_Name': [AttributeMandatory(False),
773 AttributeType(unicode),],
774 'Overlap_File_Name': [AttributeMandatory(False),
775 AttributeType(unicode),],
776 'Location': [AttributeMandatory(False),
777 AttributeType(unicode),],
778 'System': [AttributeMandatory(False),
779 AttributeType(unicode),],
780 'Latitude_degrees_north': [AttributeMandatory(False),
781 AttributeType(float),],
782 'Longitude_degrees_east': [AttributeMandatory(False),
783 AttributeType(float),],
784 'Altitude_meter_asl': [AttributeMandatory(False),
785 AttributeType(float),],
786 },
787 'name': "SCC Raw input file"
788 }
789
790 # Used for the command line arguments
791 spec_shorthands = {'sounding': sounding_specs,
792 'lidar_ratio': lidar_ratio_specs,
793 'overlap': overlap_specs,
794 'data': data_specs,}
795
796
797 if __name__ == "__main__":
798
799 # For use from a terminal
800 import argparse
801
802 parser = argparse.ArgumentParser()
803 parser.add_argument("file", help = "The path of the file to be checked")
804 parser.add_argument("-s", "--specs", default = 'data',
805 help = "The specificiations to use",
806 choices = ['data', 'overlap', 'lidar_ratio', 'sounding'])
807 parser.add_argument("-l", "--level", default = 'warning',
808 help = "The output level",
809 choices = ['error', 'warning', 'notification'])
810
811 # Check the arguments
812 args = parser.parse_args()
813
814 specs = spec_shorthands[args.specs]
815
816 with FileChecker(args.file, specs) as file_checker:
817 file_checker.run_checks()
818 file_checker.print_report(args.level)
819

mercurial