ulalume3@0: import os ulalume3@0: import fnmatch ulalume3@0: import numpy as np ulalume3@0: import netCDF4 as netcdf ulalume3@0: ulalume3@0: ulalume3@0: ERROR_ORDER = {'notification': 1, ulalume3@0: 'warning': 2, ulalume3@0: 'error': 3, ulalume3@0: } ulalume3@0: ulalume3@0: # Used when printing the report ulalume3@0: header_template = """\n----- Report for file {0.filename} ----- ulalume3@0: Checked against specification: {0.specs[name]}. ulalume3@0: ulalume3@0: Output summary: ulalume3@0: Errors: {1[error]}, Warnings: {1[warning]}, Notifications: {1[notification]}""" ulalume3@0: ulalume3@0: ulalume3@0: ulalume3@0: # We firstly define the possible specification elements ulalume3@0: # In the end of this file we use them to define complete specifications ulalume3@0: ulalume3@0: ulalume3@0: class SpecGenericError: ulalume3@0: def __repr__(self): ulalume3@0: return "{0}: {1}".format(self.level.title(), self.message) ulalume3@0: ulalume3@0: ulalume3@0: class SpecError(SpecGenericError): ulalume3@0: def __init__(self, message): ulalume3@0: self.message = message ulalume3@0: self.level = 'error' ulalume3@0: ulalume3@0: ulalume3@0: class SpecWarning(SpecGenericError): ulalume3@0: def __init__(self, message): ulalume3@0: self.message = message ulalume3@0: self.level = 'warning' ulalume3@0: ulalume3@0: ulalume3@0: class SpecNotification(SpecGenericError): ulalume3@0: def __init__(self, message): ulalume3@0: self.message = message ulalume3@0: self.level = 'notification' ulalume3@0: ulalume3@0: ulalume3@0: class GenericSpecification: ulalume3@0: @property ulalume3@0: def continue_check(self): ulalume3@0: return True ulalume3@0: ulalume3@0: class DimensionMandatory(GenericSpecification): ulalume3@0: def __init__(self, is_mandatory = True): ulalume3@0: self.block_next = True # if true the next checks for this dimension will not be performed. ulalume3@0: self.is_mandatory = is_mandatory ulalume3@0: ulalume3@0: def check(self, netcdf_file, dimension_name): ulalume3@0: the_dimension = netcdf_file.dimensions.get(dimension_name, None) ulalume3@0: ulalume3@0: error = [] ulalume3@0: if the_dimension: ulalume3@0: # If the dimension is found in the file ulalume3@0: self.dimension_exists = True ulalume3@0: check_passed = True ulalume3@0: ulalume3@0: else: ulalume3@0: self.dimension_exists = False ulalume3@0: if self.is_mandatory: ulalume3@0: # If a mandatory dimension is not found in the file ulalume3@0: check_passed = False ulalume3@0: error.append(SpecError('The dimension {0} is obligatory but was not found in the file.'.format(dimension_name))) ulalume3@0: else: ulalume3@0: check_passed = True ulalume3@0: error.append(SpecNotification('The optional dimension {0} was not found in the file.'.format(dimension_name))) ulalume3@0: ulalume3@0: self.check_passed = check_passed ulalume3@0: self.error = error ulalume3@0: ulalume3@0: return check_passed, error ulalume3@0: ulalume3@0: @property ulalume3@0: def continue_check(self): ulalume3@0: if (not self.dimension_exists) and (self.block_next): ulalume3@0: return False ulalume3@0: else: ulalume3@0: return True ulalume3@0: ulalume3@0: ulalume3@0: class DimensionUnlimited(GenericSpecification): ulalume3@0: def __init__(self, is_unlimited): ulalume3@0: self.block_next = False ulalume3@0: self.is_unlimited = is_unlimited ulalume3@0: ulalume3@0: def check(self, netcdf_file, dimension_name): ulalume3@0: the_dimension = netcdf_file.dimensions.get(dimension_name, None) ulalume3@0: error = [] ulalume3@0: ulalume3@0: if the_dimension: ulalume3@0: if the_dimension.isunlimited() == True and self.is_unlimited == False: ulalume3@0: check_passed = False ulalume3@0: error.append(SpecWarning('Dimension {0} should not be unlimited but is.'.format(dimension_name))) ulalume3@0: elif the_dimension.isunlimited() == False and self.is_unlimited == True: ulalume3@0: check_passed = False ulalume3@0: error.append(SpecWarning('Dimension {0} should be unlimited but it is not.'.format(dimension_name))) ulalume3@0: else: ulalume3@0: check_passed = True ulalume3@0: else: ulalume3@0: check_passed = True ulalume3@0: error.append(SpecError('Dimension {0} should be unlimited, but was not found in the file.'.format(dimension_name))) ulalume3@0: ulalume3@0: self.check_passed = check_passed ulalume3@0: self.error = error ulalume3@0: ulalume3@0: return check_passed, error ulalume3@0: ulalume3@0: ulalume3@0: class VariableMandatory(GenericSpecification): ulalume3@0: def __init__(self, is_mandatory = True): ulalume3@0: self.block_next = True # if true the next checks for this variable will not be performed. ulalume3@0: self.is_mandatory = is_mandatory ulalume3@0: ulalume3@0: def check(self, netcdf_file, variable_name): ulalume3@0: the_variable = netcdf_file.variables.get(variable_name, None) ulalume3@0: error = [] ulalume3@0: ulalume3@0: if the_variable != None: ulalume3@0: # If the variable is found in the file ulalume3@0: self.variable_exists = True ulalume3@0: check_passed = True ulalume3@0: ulalume3@0: else: ulalume3@0: self.variable_exists = False ulalume3@0: if self.is_mandatory: ulalume3@0: # If a mandatory variable is not found in the file ulalume3@0: check_passed = False ulalume3@0: error.append(SpecError('The variable {0} is obligatory but was not found in the file.'.format(variable_name))) ulalume3@0: else: ulalume3@0: check_passed = True ulalume3@0: error.append(SpecNotification('The optional variable {0} was not found in the file.'.format(variable_name))) ulalume3@0: ulalume3@0: self.check_passed = check_passed ulalume3@0: self.error = error ulalume3@0: ulalume3@0: return check_passed, error ulalume3@0: ulalume3@0: @property ulalume3@0: def continue_check(self): ulalume3@0: if (not self.variable_exists) and (self.block_next): ulalume3@0: return False ulalume3@0: else: ulalume3@0: return True ulalume3@0: ulalume3@0: class VariableDimensions(GenericSpecification): ulalume3@0: def __init__(self, dimensions): ulalume3@0: self.dimensions = dimensions ulalume3@0: ulalume3@0: def check(self, netcdf_file, variable_name): ulalume3@0: the_variable = netcdf_file.variables.get(variable_name, None) ulalume3@0: ulalume3@0: if the_variable != None: ulalume3@0: variable_dimensions = list(the_variable.dimensions) ulalume3@0: error = [] ulalume3@0: check_passed = True ulalume3@0: for dimension in self.dimensions: ulalume3@0: if not (dimension in variable_dimensions): ulalume3@0: check_passed = False ulalume3@0: error.append(SpecError("Variable {0} does not have dimension {1}.".format(variable_name, dimension))) ulalume3@0: ulalume3@0: # If all dimensions are present, check if the variables are in the ulalume3@0: # correct order. ulalume3@0: if check_passed: ulalume3@0: if list(self.dimensions) != variable_dimensions: ulalume3@0: check_passed = False ulalume3@0: error.append(SpecError("Variable {0} has wrong dimension order: {1} instead of {2}.".format(variable_name, ulalume3@0: variable_dimensions, ulalume3@0: list(self.dimensions)))) ulalume3@0: for dimension in variable_dimensions: ulalume3@0: if dimension not in self.dimensions: ulalume3@0: error.append(SpecWarning('Dimension {0} found in variable {1} but is not defined in the specifications'.format(dimension, variable_name))) ulalume3@0: ulalume3@0: else: ulalume3@0: check_passed = False ulalume3@0: error = [SpecError('Variable {0} should be checked for dimensions, but was not found in the file.'.format(variable_name)),] ulalume3@0: ulalume3@0: self.check_passed = check_passed ulalume3@0: self.error = error ulalume3@0: ulalume3@0: return check_passed, error ulalume3@0: ulalume3@0: ulalume3@0: class VariableType(GenericSpecification): ulalume3@0: def __init__(self, dtype): ulalume3@0: self.dtype = dtype ulalume3@0: ulalume3@0: def check(self, netcdf_file, variable_name): ulalume3@0: the_variable = netcdf_file.variables.get(variable_name, None) ulalume3@0: error = [] ulalume3@0: ulalume3@0: if the_variable != None: ulalume3@0: # Get the internal python type and not the numpy.dtype. ulalume3@0: # The conversions guarantee (?) that a single element is always returned ulalume3@0: variable_type_python = type(np.asscalar(np.asarray(np.asarray(the_variable[:]).item(0)))) ulalume3@0: ulalume3@0: if not (variable_type_python == self.dtype): ulalume3@0: check_passed = False ulalume3@0: error.append(SpecError('Variable {0} is of type {1} while it should be {2}'.format(variable_name, the_variable.dtype, self.dtype))) ulalume3@0: else: ulalume3@0: check_passed = True ulalume3@0: else: ulalume3@0: check_passed = False ulalume3@0: error.append(SpecError('Variable {0} should be checked for type, but was not found in the file.'.format(variable_name))) ulalume3@0: ulalume3@0: self.check_passed = check_passed ulalume3@0: self.error = error ulalume3@0: ulalume3@0: return check_passed, error ulalume3@0: ulalume3@0: ulalume3@0: class AttributeMandatory(GenericSpecification): ulalume3@0: def __init__(self, is_mandatory = True): ulalume3@0: self.block_next = True # if true the next checks for this variable will not be performed. ulalume3@0: self.is_mandatory = is_mandatory ulalume3@0: ulalume3@0: def check(self, netcdf_file, attribute_name): ulalume3@0: the_attribute = getattr(netcdf_file, attribute_name, None) ulalume3@0: error = [] ulalume3@0: ulalume3@0: if the_attribute: ulalume3@0: # If the variable is found in the file ulalume3@0: self.attribute_exists = True ulalume3@0: check_passed = True ulalume3@0: ulalume3@0: else: ulalume3@0: self.attribute_exists = False ulalume3@0: if self.is_mandatory: ulalume3@0: # If a mandatory variable is not found in the file ulalume3@0: check_passed = False ulalume3@0: error.append(SpecError('The attribute {0} is obligatory but was not found in the file.'.format(attribute_name))) ulalume3@0: else: ulalume3@0: check_passed = True ulalume3@0: error.append(SpecNotification('The optional attribute {0} was not found in the file.'.format(attribute_name))) ulalume3@0: ulalume3@0: self.check_passed = check_passed ulalume3@0: self.error = error ulalume3@0: ulalume3@0: return check_passed, error ulalume3@0: ulalume3@0: @property ulalume3@0: def continue_check(self): ulalume3@0: if (not self.attribute_exists) and (self.block_next): ulalume3@0: return False ulalume3@0: else: ulalume3@0: return True ulalume3@0: ulalume3@0: ulalume3@0: class AttributeType(GenericSpecification): ulalume3@0: def __init__(self, dtype, block_next = True): ulalume3@0: self.block_next = block_next ulalume3@0: self.dtype = dtype ulalume3@0: ulalume3@0: def check(self, netcdf_file, attribute_name): ulalume3@0: the_attribute = getattr(netcdf_file, attribute_name, None) ulalume3@0: error = [] ulalume3@0: ulalume3@0: if the_attribute: ulalume3@0: # Get the internal python type and not the numpy.dtype. ulalume3@0: # The conversions guarantee (?) that a single element is always returned ulalume3@0: try: ulalume3@0: attribute_type_python = type(np.asscalar(np.asarray(np.asarray(the_attribute[:]).item(0)))) ulalume3@0: except: ulalume3@0: attribute_type_python = type(np.asscalar(the_attribute)) ulalume3@0: ulalume3@0: if not (attribute_type_python == self.dtype): ulalume3@0: check_passed = False ulalume3@0: error.append(SpecError('Attribute {0} is of type {1} while it should be {2}'.format(attribute_name, type(the_attribute).__name__, self.dtype.__name__))) ulalume3@0: else: ulalume3@0: error = None ulalume3@0: check_passed = True ulalume3@0: else: ulalume3@0: check_passed = False ulalume3@0: error.append(SpecError('Attribute {0} should be checked for type, but was not found in the file.'.format(attribute_name))) ulalume3@0: ulalume3@0: self.check_passed = check_passed ulalume3@0: self.error = error ulalume3@0: ulalume3@0: return check_passed, error ulalume3@0: ulalume3@0: @property ulalume3@0: def continue_check(self): ulalume3@0: if (not self.check_passed) and (self.block_next): ulalume3@0: return False ulalume3@0: else: ulalume3@0: return True ulalume3@0: ulalume3@0: ulalume3@0: class AttributeStrLength(GenericSpecification): ulalume3@0: ulalume3@0: def __init__(self, length): ulalume3@0: self.length = length ulalume3@0: ulalume3@0: ulalume3@0: def check(self, netcdf_file, attribute_name): ulalume3@0: the_attribute = getattr(netcdf_file, attribute_name, None) ulalume3@0: error = [] ulalume3@0: ulalume3@0: if the_attribute: ulalume3@0: if len(the_attribute) != self.length: ulalume3@0: check_passed = False ulalume3@0: error.append(SpecError('Attribute {0} should be of length {1} while it has length {2}'.format(attribute_name, self.length, len(the_attribute)))) ulalume3@0: else: ulalume3@0: check_passed = True ulalume3@0: else: ulalume3@0: check_passed = False ulalume3@0: error.append(SpecError('Attribute {0} should be checked for length, but was not found in the file.'.format(attribute_name))) ulalume3@0: ulalume3@0: self.check_passed = check_passed ulalume3@0: self.error = error ulalume3@0: ulalume3@0: return check_passed, error ulalume3@0: ulalume3@0: ulalume3@0: class FilenameShellPattern(GenericSpecification): ulalume3@0: def __init__(self, shell_pattern): ulalume3@0: self.pattern = shell_pattern ulalume3@0: ulalume3@0: def check(self, netcdf_file, filename): ulalume3@0: error = [] ulalume3@0: ulalume3@0: if fnmatch.fnmatch(filename, self.pattern): ulalume3@0: check_passed = True ulalume3@0: else: ulalume3@0: check_passed = False ulalume3@0: error.append(SpecError('Filename {0} does not match patter {1}'.format(filename, self.pattern))) ulalume3@0: ulalume3@0: self.check_passed = check_passed ulalume3@0: self.error = error ulalume3@0: ulalume3@0: return check_passed, error ulalume3@0: ulalume3@0: ulalume3@0: # This is the main class of the script. ulalume3@0: class FileChecker: ulalume3@0: """ It uses the provided specifications to check the a file. ulalume3@0: It can be used with the 'with' statement. For example: ulalume3@0: ulalume3@0: with FileChecker(filename, specs) as file_checker: ulalume3@0: file_checker.run_checks() ulalume3@0: file_checker.print_report('error') ulalume3@0: ulalume3@0: """ ulalume3@0: ulalume3@0: ulalume3@0: def __init__(self, filepath, specs): ulalume3@0: self.file = None ulalume3@0: self.checks_run = False ulalume3@0: self.filepath = filepath ulalume3@0: self.filename = os.path.basename(filepath) ulalume3@0: self.specs = specs ulalume3@0: self.check_results = {} ulalume3@0: self.check_results['general'] = [] ulalume3@0: ulalume3@0: def __enter__(self): ulalume3@0: self.open_file() ulalume3@0: return self ulalume3@0: ulalume3@0: def __exit__(self, type, value, traceback): ulalume3@0: if self.file: ulalume3@0: self.file.close() ulalume3@0: ulalume3@0: def open_file(self): ulalume3@0: try: ulalume3@0: self.file = netcdf.Dataset(self.filepath) ulalume3@0: except: ulalume3@0: self.check_results['general'].append(SpecError('Could not open file {0}.'.format(self.filename))) ulalume3@0: ulalume3@0: def close_file(self): ulalume3@0: self.file.close() ulalume3@0: ulalume3@0: def run_checks(self): ulalume3@0: if self.file: ulalume3@0: self.check_file() ulalume3@0: self.check_attributes() ulalume3@0: self.check_dimensions() ulalume3@0: self.check_variables() ulalume3@0: self.checks_run = True ulalume3@0: ulalume3@0: def check_file(self): ulalume3@0: self.check_results['file'] = [] ulalume3@0: ulalume3@0: try: ulalume3@0: specs_file = self.specs['file'] ulalume3@0: except: ulalume3@0: specs_file = [] ulalume3@0: ulalume3@0: for file_spec in specs_file: ulalume3@0: check_passed, error = file_spec.check(self.file, self.filename) ulalume3@0: ulalume3@0: if error: ulalume3@0: self.check_results['file'].extend(list(error)) ulalume3@0: ulalume3@0: if not file_spec.continue_check: ulalume3@0: break ulalume3@0: ulalume3@0: def check_attributes(self): ulalume3@0: """ Check if attributes are according to specs """ ulalume3@0: ulalume3@0: self.check_results['attributes'] = [] ulalume3@0: ulalume3@0: try: ulalume3@0: spec_attributes = self.specs['attributes'].keys() ulalume3@0: except: ulalume3@0: spec_attributes = [] ulalume3@0: ulalume3@0: for attribute_name in spec_attributes: ulalume3@0: attribute_specs = self.specs['attributes'][attribute_name] ulalume3@0: for attribute_spec in attribute_specs: ulalume3@0: check_passed, error = attribute_spec.check(self.file, attribute_name) ulalume3@0: ulalume3@0: if error: ulalume3@0: self.check_results['attributes'].extend(list(error)) ulalume3@0: ulalume3@0: if not attribute_spec.continue_check: ulalume3@0: break # Don't continue checking specifications if a blocking check failed. ulalume3@0: ulalume3@0: for attribute_name in self.file.ncattrs(): ulalume3@0: if attribute_name not in spec_attributes: ulalume3@0: self.check_results['attributes'].append(SpecWarning('Attribute {0} found in the file but is not defined in the specifications'.format(attribute_name))) ulalume3@0: ulalume3@0: def check_dimensions(self): ulalume3@0: """ Check if dimension are according to specs """ ulalume3@0: self.check_results['dimensions'] = [] ulalume3@0: ulalume3@0: try: ulalume3@0: spec_dimensions = self.specs['dimensions'].keys() ulalume3@0: except: ulalume3@0: spec_dimensions = [] ulalume3@0: ulalume3@0: ulalume3@0: for dimension_name in spec_dimensions: ulalume3@0: dimension_specs = self.specs['dimensions'][dimension_name] ulalume3@0: for dimension_spec in dimension_specs: ulalume3@0: check_passed, error = dimension_spec.check(self.file, dimension_name) ulalume3@0: ulalume3@0: if error: ulalume3@0: self.check_results['dimensions'].extend(list(error)) ulalume3@0: ulalume3@0: if not dimension_spec.continue_check: ulalume3@0: break # Don't continue checking specifications if a blocking check failed. ulalume3@0: ulalume3@0: for dimension in self.file.dimensions: ulalume3@0: if dimension not in spec_dimensions: ulalume3@0: self.check_results['dimensions'].append(SpecWarning('Dimension {0} found in the file but is not defined in the specifications'.format(dimension))) ulalume3@0: ulalume3@0: def check_variables(self): ulalume3@0: """ Check if variables are according to specs """ ulalume3@0: ulalume3@0: self.check_results['variables'] = [] ulalume3@0: ulalume3@0: try: ulalume3@0: spec_variables = self.specs['variables'].keys() ulalume3@0: except: ulalume3@0: spec_variables = [] ulalume3@0: ulalume3@0: for variable_name in spec_variables: ulalume3@0: variable_specs = self.specs['variables'][variable_name] ulalume3@0: for variable_spec in variable_specs: ulalume3@0: check_passed, error = variable_spec.check(self.file, variable_name) ulalume3@0: ulalume3@0: if error: ulalume3@0: self.check_results['variables'].extend(list(error)) ulalume3@0: ulalume3@0: if not variable_spec.continue_check: ulalume3@0: break # Don't continue checking specifications if a blocking check failed. ulalume3@0: ulalume3@0: for variable_name in self.file.variables: ulalume3@0: if variable_name not in spec_variables: ulalume3@0: self.check_results['variables'].append(SpecWarning('Variable {0} found in the file but is not defined in the specifications'.format(variable_name))) ulalume3@0: ulalume3@0: def file_ok(self, level = 'error'): ulalume3@0: """ Check if the file checked is ok. What ok means is defined by the level variable """ ulalume3@0: ulalume3@0: status = None ulalume3@0: if self.checks_run: ulalume3@0: status = True ulalume3@0: for category, result_list in self.check_results.items(): ulalume3@0: for result in result_list: ulalume3@0: if ERROR_ORDER[result.level] >= ERROR_ORDER[level]: ulalume3@0: status = False ulalume3@0: ulalume3@0: return status ulalume3@0: ulalume3@0: def results_for_level(self, level): ulalume3@0: """ Returns all the results of a specific level """ ulalume3@0: results = None ulalume3@0: if self.checks_run: ulalume3@0: results = [] ulalume3@0: for category, result_list in self.check_results.items(): ulalume3@0: for result in result_list: ulalume3@0: if ERROR_ORDER[result.level] == ERROR_ORDER[level]: ulalume3@0: results.append(result) ulalume3@0: ulalume3@0: return results ulalume3@0: ulalume3@0: def results_by_level(self): ulalume3@0: """ Returns a dictionary with the results by level. """ ulalume3@0: ulalume3@0: results = {} ulalume3@0: for level, order in ERROR_ORDER.items(): ulalume3@0: results[level] = self.results_for_level(level) ulalume3@0: ulalume3@0: return results ulalume3@0: ulalume3@0: def result_count(self): ulalume3@0: """ Returns a dictionary with the number of results per category. """ ulalume3@0: ulalume3@0: result_number = {} ulalume3@0: results = self.results_by_level() ulalume3@0: ulalume3@0: for category, error_list in results.items(): ulalume3@0: if error_list is None: ulalume3@0: result_number[category] = 0 ulalume3@0: else: ulalume3@0: result_number[category] = len(error_list) ulalume3@0: return result_number ulalume3@0: ulalume3@0: def print_report(self, level): ulalume3@0: """ Print a report for the given level. """ ulalume3@0: ulalume3@0: print header_template.format(self, self.result_count()) ulalume3@0: ulalume3@0: results = self.results_by_level() ulalume3@0: ulalume3@0: for result_level in ['error', 'warning', 'notification']: ulalume3@0: if ERROR_ORDER[result_level] >= ERROR_ORDER[level]: ulalume3@0: print "\n{0} details".format(result_level.capitalize()) ulalume3@0: print "----------------" ulalume3@0: for result in results[result_level]: ulalume3@0: print result ulalume3@0: ulalume3@0: ulalume3@0: # Sounding file specifications ulalume3@0: sounding_specs = {'file': [FilenameShellPattern('rs_*.nc'),], ulalume3@0: 'dimensions': {'points': [DimensionMandatory(True), ulalume3@0: DimensionUnlimited(False),], ulalume3@0: }, ulalume3@0: 'variables': {'Altitude': [VariableMandatory(True), ulalume3@0: VariableDimensions(['points',]), ulalume3@0: VariableType(float)], ulalume3@0: 'Temperature': [VariableMandatory(True), ulalume3@0: VariableDimensions(['points',]), ulalume3@0: VariableType(float)], ulalume3@0: 'Pressure': [VariableMandatory(True), ulalume3@0: VariableDimensions(['points',]), ulalume3@0: VariableType(float)], ulalume3@0: 'RelativeHumidity': [VariableMandatory(False), ulalume3@0: VariableDimensions(['points',]), ulalume3@0: VariableType(float)], ulalume3@0: }, ulalume3@0: 'attributes': {'Latitude_degrees_north': [AttributeMandatory(True), ulalume3@0: AttributeType(float),], ulalume3@0: 'Longitude_degrees_east': [AttributeMandatory(True), ulalume3@0: AttributeType(float),], ulalume3@0: 'Altitude_meter_asl': [AttributeMandatory(True), ulalume3@0: AttributeType(float),], ulalume3@0: 'Location': [AttributeMandatory(False), ulalume3@0: AttributeType(unicode),], ulalume3@0: 'Sounding_Station_Name': [AttributeMandatory(False), ulalume3@0: AttributeType(unicode),], ulalume3@0: 'WMO_Station_Number': [AttributeMandatory(False), ulalume3@0: AttributeType(unicode),], ulalume3@0: 'WBAN_Station_Number':[AttributeMandatory(False), ulalume3@0: AttributeType(unicode),], ulalume3@0: 'Sounding_Start_Date':[AttributeMandatory(True), ulalume3@0: AttributeType(unicode, block_next = True), ulalume3@0: AttributeStrLength(8)], ulalume3@0: 'Sounding_Start_Time_UT':[AttributeMandatory(True), ulalume3@0: AttributeType(unicode, block_next = True), ulalume3@0: AttributeStrLength(6)], ulalume3@0: 'Sounding_Stop_Time_UT':[AttributeMandatory(False), ulalume3@0: AttributeType(unicode, block_next = True), ulalume3@0: AttributeStrLength(6)], ulalume3@0: }, ulalume3@0: 'name': "SCC Sounding file" ulalume3@0: } ulalume3@0: ulalume3@0: # Lidar ratio file specifications ulalume3@0: lidar_ratio_specs = {'file': [FilenameShellPattern('*.nc'),], ulalume3@0: 'dimensions': {'points': [DimensionMandatory(True), ulalume3@0: DimensionUnlimited(False),], ulalume3@0: 'products': [DimensionMandatory(True), ulalume3@0: DimensionUnlimited(False),], ulalume3@0: }, ulalume3@0: 'variables': {'Altitude': [VariableMandatory(True), ulalume3@0: VariableDimensions(['points',]), ulalume3@0: VariableType(float)], ulalume3@0: 'Lidar_Ratio': [VariableMandatory(True), ulalume3@0: VariableDimensions(['points', 'products']), ulalume3@0: VariableType(float)], ulalume3@0: 'product_ID': [VariableMandatory(True), ulalume3@0: VariableDimensions(['products',]), ulalume3@0: VariableType(int)], ulalume3@0: }, ulalume3@0: 'attributes': {'Lidar_Station_Name': [AttributeMandatory(True), ulalume3@0: AttributeType(unicode),], ulalume3@0: }, ulalume3@0: 'name': "SCC Lidar ratio file" ulalume3@0: } ulalume3@0: ulalume3@0: # Overlap file specifications ulalume3@0: overlap_specs = {'file': [FilenameShellPattern('ov_*.nc'),], ulalume3@0: 'dimensions': {'points': [DimensionMandatory(True), ulalume3@0: DimensionUnlimited(False),], ulalume3@0: 'channels': [DimensionMandatory(True), ulalume3@0: DimensionUnlimited(False),], ulalume3@0: }, ulalume3@0: 'variables': {'Altitude': [VariableMandatory(True), ulalume3@0: VariableDimensions(['points',]), ulalume3@0: VariableType(float)], ulalume3@0: 'Overlap_Function': [VariableMandatory(True), ulalume3@0: VariableDimensions(['points', 'channels']), ulalume3@0: VariableType(float)], ulalume3@0: 'channel_ID': [VariableMandatory(True), ulalume3@0: VariableDimensions(['channels',]), ulalume3@0: VariableType(int)], ulalume3@0: }, ulalume3@0: 'attributes': {'Lidar_Station_Name': [AttributeMandatory(True), ulalume3@0: AttributeType(unicode, block_next = True), ulalume3@0: AttributeStrLength(2)], ulalume3@0: 'Overlap_Measurement_Date': [AttributeMandatory(True), ulalume3@0: AttributeType(unicode, block_next = True), ulalume3@0: AttributeStrLength(8)], ulalume3@0: }, ulalume3@0: 'name': "SCC Overlap file" ulalume3@0: } ulalume3@0: ulalume3@0: # Raw data file specifications ulalume3@0: data_specs = {'file': [FilenameShellPattern('*.nc'),], ulalume3@0: 'dimensions': {'points': [DimensionMandatory(True), ulalume3@0: DimensionUnlimited(False),], ulalume3@0: 'channels': [DimensionMandatory(True), ulalume3@0: DimensionUnlimited(False),], ulalume3@0: 'nb_of_time_scales': [DimensionMandatory(True), ulalume3@0: DimensionUnlimited(False),], ulalume3@0: 'time': [DimensionMandatory(True), ulalume3@0: DimensionUnlimited(True),], ulalume3@0: 'time_bck': [DimensionMandatory(False), ulalume3@0: DimensionUnlimited(False),], ulalume3@0: 'scan_angles': [DimensionMandatory(True), ulalume3@0: DimensionUnlimited(False),], ulalume3@0: }, ulalume3@0: 'variables': {'channel_ID': [VariableMandatory(True), ulalume3@0: VariableDimensions(['channels',]), ulalume3@0: VariableType(int)], ulalume3@0: 'Laser_Repetition_Rate': [VariableMandatory(False), ulalume3@0: VariableDimensions(['channels',]), ulalume3@0: VariableType(int)], ulalume3@0: 'Laser_Pointing_Angle': [VariableMandatory(True), ulalume3@0: VariableDimensions(['scan_angles',]), ulalume3@0: VariableType(float)], ulalume3@0: 'ID_Range': [VariableMandatory(False), ulalume3@0: VariableDimensions(['channels',]), ulalume3@0: VariableType(int)], ulalume3@0: 'Scattering_Mechanism': [VariableMandatory(False), ulalume3@0: VariableDimensions(['channels',]), ulalume3@0: VariableType(int)], ulalume3@0: 'Emitted_Wavelength': [VariableMandatory(False), ulalume3@0: VariableDimensions(['channels',]), ulalume3@0: VariableType(float)], ulalume3@0: 'Detected_Wavelength': [VariableMandatory(False), ulalume3@0: VariableDimensions(['channels',]), ulalume3@0: VariableType(float)], ulalume3@0: 'Raw_Data_Range_Resolution': [VariableMandatory(False), ulalume3@0: VariableDimensions(['channels',]), ulalume3@0: VariableType(float)], ulalume3@0: 'Background_Mode': [VariableMandatory(False), ulalume3@0: VariableDimensions(['channels',]), ulalume3@0: VariableType(int)], ulalume3@0: 'Background_Low': [VariableMandatory(True), ulalume3@0: VariableDimensions(['channels',]), ulalume3@0: VariableType(float)], ulalume3@0: 'Background_High': [VariableMandatory(True), ulalume3@0: VariableDimensions(['channels',]), ulalume3@0: VariableType(float)], ulalume3@0: 'Molecular_Calc': [VariableMandatory(True), ulalume3@0: VariableDimensions([]), ulalume3@0: VariableType(int)], ulalume3@0: 'id_timescale': [VariableMandatory(True), ulalume3@0: VariableDimensions(['channels',]), ulalume3@0: VariableType(int)], ulalume3@0: 'Dead_Time_Corr_Type': [VariableMandatory(False), ulalume3@0: VariableDimensions(['channels',]), ulalume3@0: VariableType(int)], ulalume3@0: 'Dead_Time': [VariableMandatory(False), ulalume3@0: VariableDimensions(['channels',]), ulalume3@0: VariableType(float)], ulalume3@0: 'Acquisition_Mode': [VariableMandatory(False), ulalume3@0: VariableDimensions(['channels',]), ulalume3@0: VariableType(int)], ulalume3@0: 'Trigger_Delay': [VariableMandatory(False), ulalume3@0: VariableDimensions(['channels',]), ulalume3@0: VariableType(float)], ulalume3@0: 'Laser_Pointing_Angle_of_Profiles': [VariableMandatory(True), ulalume3@0: VariableDimensions(['time','nb_of_time_scales',]), ulalume3@0: VariableType(int)], ulalume3@0: 'Raw_Data_Start_Time': [VariableMandatory(True), ulalume3@0: VariableDimensions(['time','nb_of_time_scales',]), ulalume3@0: VariableType(int)], ulalume3@0: 'Raw_Data_Stop_Time': [VariableMandatory(True), ulalume3@0: VariableDimensions(['time','nb_of_time_scales',]), ulalume3@0: VariableType(int)], ulalume3@0: 'Laser_Shots': [VariableMandatory(True), ulalume3@0: VariableDimensions(['time','channels',]), ulalume3@0: VariableType(int)], ulalume3@0: 'Raw_Lidar_Data': [VariableMandatory(False), ulalume3@0: VariableDimensions(['time', 'channels', 'points']), ulalume3@0: VariableType(float)], ulalume3@0: 'Depolarization_Factor': [VariableMandatory(False), ulalume3@0: VariableDimensions(['channels',]), ulalume3@0: VariableType(float)], ulalume3@0: 'LR_Input': [VariableMandatory(False), ulalume3@0: VariableDimensions(['channels',]), ulalume3@0: VariableType(int)], ulalume3@0: 'DAQ_Range': [VariableMandatory(False), ulalume3@0: VariableDimensions(['channels',]), ulalume3@0: VariableType(float)], ulalume3@0: 'Pressure_at_Lidar_Station': [VariableMandatory(False), ulalume3@0: VariableDimensions([]), ulalume3@0: VariableType(float)], ulalume3@0: 'Temperature_at_Lidar_Station': [VariableMandatory(False), ulalume3@0: VariableDimensions([]), ulalume3@0: VariableType(float)], ulalume3@0: 'Background_Profile': [VariableMandatory(False), ulalume3@0: VariableDimensions(['time_bck', 'channels', 'points']), ulalume3@0: VariableType(float)], ulalume3@0: 'Raw_Bck_Start_Time': [VariableMandatory(False), ulalume3@0: VariableDimensions(['time_bck','nb_of_time_scales',]), ulalume3@0: VariableType(int)], ulalume3@0: 'Raw_Bck_Stop_Time': [VariableMandatory(False), ulalume3@0: VariableDimensions(['time_bck','nb_of_time_scales',]), ulalume3@0: VariableType(int)], ulalume3@0: 'Error_On_Raw_Lidar_Data': [VariableMandatory(False), ulalume3@0: VariableDimensions(['time','channels', 'points']), ulalume3@0: VariableType(float)], ulalume3@0: 'First_Signal_Rangebin': [VariableMandatory(False), ulalume3@0: VariableDimensions(['channels',]), ulalume3@0: VariableType(int)], ulalume3@0: }, ulalume3@0: 'attributes': {'Measurement_ID': [AttributeMandatory(True), ulalume3@0: AttributeType(unicode, block_next = True), ulalume3@0: AttributeStrLength(12)], ulalume3@0: 'RawData_Start_Date': [AttributeMandatory(True), ulalume3@0: AttributeType(unicode, block_next = True), ulalume3@0: AttributeStrLength(8)], ulalume3@0: 'RawData_Start_Time_UT': [AttributeMandatory(True), ulalume3@0: AttributeType(unicode, block_next = True), ulalume3@0: AttributeStrLength(6)], ulalume3@0: 'RawData_Stop_Time_UT': [AttributeMandatory(True), ulalume3@0: AttributeType(unicode, block_next = True), ulalume3@0: AttributeStrLength(6)], ulalume3@0: 'RawBck_Start_Date': [AttributeMandatory(False), ulalume3@0: AttributeType(unicode, block_next = True), ulalume3@0: AttributeStrLength(8)], ulalume3@0: 'RawBck_Start_Time_UT': [AttributeMandatory(False), ulalume3@0: AttributeType(unicode, block_next = True), ulalume3@0: AttributeStrLength(6)], ulalume3@0: 'RawBck_Stop_Time_UT': [AttributeMandatory(False), ulalume3@0: AttributeType(unicode, block_next = True), ulalume3@0: AttributeStrLength(6)], ulalume3@0: 'Sounding_File_Name': [AttributeMandatory(False), ulalume3@0: AttributeType(unicode),], ulalume3@0: 'LR_File_Name': [AttributeMandatory(False), ulalume3@0: AttributeType(unicode),], ulalume3@0: 'Overlap_File_Name': [AttributeMandatory(False), ulalume3@0: AttributeType(unicode),], ulalume3@0: 'Location': [AttributeMandatory(False), ulalume3@0: AttributeType(unicode),], ulalume3@0: 'System': [AttributeMandatory(False), ulalume3@0: AttributeType(unicode),], ulalume3@0: 'Latitude_degrees_north': [AttributeMandatory(False), ulalume3@0: AttributeType(float),], ulalume3@0: 'Longitude_degrees_east': [AttributeMandatory(False), ulalume3@0: AttributeType(float),], ulalume3@0: 'Altitude_meter_asl': [AttributeMandatory(False), ulalume3@0: AttributeType(float),], ulalume3@0: }, ulalume3@0: 'name': "SCC Raw input file" ulalume3@0: } ulalume3@0: ulalume3@0: # Used for the command line arguments ulalume3@0: spec_shorthands = {'sounding': sounding_specs, ulalume3@0: 'lidar_ratio': lidar_ratio_specs, ulalume3@0: 'overlap': overlap_specs, ulalume3@0: 'data': data_specs,} ulalume3@0: ulalume3@0: ulalume3@0: if __name__ == "__main__": ulalume3@0: ulalume3@0: # For use from a terminal ulalume3@0: import argparse ulalume3@0: ulalume3@0: parser = argparse.ArgumentParser() ulalume3@0: parser.add_argument("file", help = "The path of the file to be checked") ulalume3@0: parser.add_argument("-s", "--specs", default = 'data', ulalume3@0: help = "The specificiations to use", ulalume3@0: choices = ['data', 'overlap', 'lidar_ratio', 'sounding']) ulalume3@0: parser.add_argument("-l", "--level", default = 'warning', ulalume3@0: help = "The output level", ulalume3@0: choices = ['error', 'warning', 'notification']) ulalume3@0: ulalume3@0: # Check the arguments ulalume3@0: args = parser.parse_args() ulalume3@0: ulalume3@0: specs = spec_shorthands[args.specs] ulalume3@0: ulalume3@0: with FileChecker(args.file, specs) as file_checker: ulalume3@0: file_checker.run_checks() ulalume3@0: file_checker.print_report(args.level) ulalume3@0: