import logging import sys import filecmp import os import numpy import numbers import shutil try: # CASA 6 logging.debug("Importing CASAtools") import casatools _tb = casatools.table() _tb2 = casatools.table() # _casa6 = True except ImportError: # CASA 5 logging.debug("Import casa6 errors. Trying CASA5...") from taskinit import tbtool _tb = tbtool() _tb2 = tbtool() # _casa5 = True #ignore_subversion = shutil.ignore_patterns('.svn') ################ ################## ################ ################## ################ ################## #class TableCacheValidator(object): # def __init__(self): # self.original_cache = get_table_cache() # # def validate(self): # cache = get_table_cache() # #print 'original {} current {}'.format(self.original_cache, cache) # return len(cache) == 0 or cache == self.original_cache #class DictDiffer(object): # """ # Calculate the difference between two dictionaries as: # (1) items added # (2) items removed # (3) keys same in both but changed values # (4) keys same in both and unchanged values # Example: # mydiff = DictDiffer(dict1, dict2) # mydiff.changed() # to show what has changed # """ # def __init__(self, current_dict, past_dict): # self.current_dict, self.past_dict = current_dict, past_dict # self.set_current, self.set_past = set(current_dict.keys()), set(past_dict.keys()) # self.intersect = self.set_current.intersection(self.set_past) # def added(self): # return self.set_current - self.intersect # def removed(self): # return self.set_past - self.intersect # def changed(self): # return set(o for o in self.intersect if self.past_dict[o] != self.current_dict[o]) # def unchanged(self): # return set(o for o in self.intersect if self.past_dict[o] == self.current_dict[o]) ################ ################## ################ ################## ################ ################## def compare_CASA_var_col_tables(referencetab, testtab, varcol, tolerance=0.0): ''' originally: compVarColTables(referencetab, testtab, varcol, tolerance=0.) compare_CASA_variable_cols - Compare a variable column of two CASA tables. @param referencetab --> a reference table @param testtab --> a table to verify @param varcol --> the name of a variable column (str) @param tolerance --> Tolerance @return: True if reference tab == test table else False ''' logging.info("Comparing Column: {} within {} and {}".format(varcol,referencetab, testtab)) logging.debug("Executing: compare_CASA_variable_cols(referencetab={},testtab={}, varcol={}, tolerance={})".format(referencetab, testtab, varcol, tolerance)) retval = True _tb.open(referencetab) cnames = _tb.colnames() _tb2.open(testtab) col = varcol if _tb.isvarcol(col) and _tb2.isvarcol(col): try: # First check if _tb.nrows() != _tb2.nrows(): #print('Length of %s differ from %s, %s!=%s'%(referencetab,testtab,len(rk),len(tk))) logging.error('Length of {} differ from {}, {} != {}'.format(referencetab,testtab,_tb.nrows(),_tb2.nrows())) retval = False else: for therow in range(_tb.nrows()): rdata = _tb.getcell(col,therow) tdata = _tb2.getcell(col,therow) if not rdata.all()==tdata.all(): if (tolerance>0.): differs=False for j in range(0,len(rdata)): if ((isinstance(rdata[j],float)) or (isinstance(rdata[j],int))): if (abs(rdata[j]-tdata[j]) > tolerance*abs(rdata[j]+tdata[j])): # print('Column ', col,' differs in tables ', referencetab, ' and ', testtab) # print(therow, j) # print(rdata[j]) # print(tdata[j]) differs = True elif (isinstance(rdata[j],list)) or (isinstance(rdata[j],np.ndarray)): for k in range(0,len(rdata[j])): if (abs(rdata[j][k]-tdata[j][k]) > tolerance*abs(rdata[j][k]+tdata[j][k])): # print('Column ', col,' differs in tables ', referencetab, ' and ', testtab) # print(therow, j, k) # print(rdata[j][k]) # print(tdata[j][k]) differs = True if differs: print('ERROR: Column %s of %s and %s do not agree within tolerance %s'%(col,referencetab, testtab, tolerance)) retval = False break else: print('ERROR: Column %s of %s and %s do not agree.'%(col,referencetab, testtab)) print('ERROR: First row to differ is row=%s'%therow) retval = False break finally: _tb.close() _tb2.close() else: print('Columns are not varcolumns.') retval = False if retval: print('Column %s of %s and %s agree'%(col,referencetab, testtab)) return retval def compare_CASA_tables(referencetab, testtab, excludecols, tolerance=0.001, mode="percentage", startrow = 0, nrow = -1, rowincr = 1): ''' originally: testhelpers.compTables(referencetab, testtab, excludecols, tolerance=0.001, mode="percentage", startrow = 0, nrow = -1, rowincr = 1) compare_CASA_tables - compare two CASA tables @param referencetab - the table which is assumed to be correct @param testtab - the table which is to be compared to referencetab @param excludecols - list of column names which are to be ignored @param tolerance - permitted fractional difference (default 0.001 = 0.1 percent) @param mode - comparison is made as "percentage", "absolute", "phaseabsdeg" (for complex numbers = difference of the phases in degrees) @return: True if reference tab == test table else False ''' logging.info("Comparing {} to {}".format(referencetab, testtab)) logging.debug("Executing: compare_CASA_tables(referencetab = {}, testtab = {}, excludecols = {}, tolerance={}, mode={}, startrow = {}, nrow = {}, rowincr = {})".format(referencetab, testtab, excludecols, tolerance, mode, startrow, nrow , rowincr)) if excludecols is None: excludecols = [] if not isinstance(excludecols, list): logging.error("excludecols not in correct format") raise TypeError("excludecols must be a list") if referencetab.endswith(".cal") or testtab.endswith(".cal"): logging.warning("WARNING: Will compare caltables using compare_caltables") return compare_caltables(referencetab, testtab, cols= excludecols, rtol=8e-7, atol=1e-8) ##### Begin: Tempory Fix if len(excludecols) == 0: excludecols = ["FLAG_CATEGORY"] else: excludecols.append('FLAG_CATEGORY') """ #TODO: Fix Error in checking FLAG_CATEGORY _tb.getcol("FLAG_CATEGORY") SEVERE getcol::FLAG_CATEGORY Exception Reported: Table DataManager error: Invalid operation: TSM: no array in row 0 of column FLAG_CATEGORY in ** RuntimeError: Table DataManager error: Invalid operation: TSM: no array in row 0 of column FLAG_CATEGORY in ** """ ##### End: Tempory Fix rval = True _tb.open(referencetab) cnames = _tb.colnames() _tb2.open(testtab) cnames2 = _tb2.colnames() if sorted(cnames) != sorted(cnames2): logging.debug("Available columns in Reference Table {}: {}".format(referencetab,cnames)) logging.debug("Available columns in Test Table{}: {}".format(testtab,cnames2)) return False for excludecol in excludecols: if (excludecol not in cnames) and (excludecol not in cnames2): logging.warning("Column {} Not in {} or {}. Will Continue without Checking against this column".format(excludecol,referencetab,testtab)) logging.debug("Available columns in Reference Table {}: {}".format(referencetab,cnames)) logging.debug("Available columns in Test Table{}: {}".format(testtab,cnames2)) try: for c in cnames: if c in excludecols: continue print("\nTesting column {}".format(c)) a = 0 try: a = _tb.getcol(c,startrow=startrow,nrow=nrow,rowincr=rowincr) except: rval = False print('Error accessing column ', c, ' in table ', referencetab) print(sys.exc_info()[0]) break b = 0 try: b = _tb2.getcol(c,startrow=startrow,nrow=nrow,rowincr=rowincr) except: rval = False print('Error accessing column ', c, ' in table ', testtab) print(sys.exc_info()[0]) break if not (len(a)==len(b)): print('Column ',c,' has different length in tables ', referencetab, ' and ', testtab) print(a) print(b) rval = False break else: differs = False if not (a==b).all(): for i in range(0,len(a)): if (isinstance(a[i],float)): if ((mode=="percentage") and (abs(a[i]-b[i]) > tolerance*abs(a[i]))) or ((mode=="absolute") and (abs(a[i]-b[i]) > tolerance)): print("Column " + c + " differs") print("Row=" + str(i)) print("Reference file value: " + str(a[i])) print("Input file value: " + str(b[i])) if (mode=="percentage"): print("Tolerance is {0}%; observed difference was {1} %".format (tolerance * 100, 100*abs(a[i]-b[i])/abs(a[i]))) else: print("Absolute tolerance is {0}; observed difference: {1}".format (tolerance, (abs(a[i]-b[i])))) differs = True rval = False break elif (isinstance(a[i],int) or isinstance(a[i],numpy.int32)): if (abs(a[i]-b[i]) > 0): print("Column " + c + " differs") print("Row=" + str(i)) print("Reference file value: " + str(a[i])) print("Input file value: " + str(b[i])) if (mode=="percentage"): print("tolerance in % should be " + str(100*abs(a[i]-b[i])/abs(a[i]))) else: print("absolute tolerance should be " + str(abs(a[i]-b[i]))) differs = True rval = False break elif (isinstance(a[i],str) or isinstance(a[i],numpy.bool_)): if not (a[i]==b[i]): print("Column " + c + " differs") print("Row=" + str(i)) print("Reference file value: " + str(a[i])) print("Input file value: " + str(b[i])) if (mode=="percentage"): print("tolerance in % should be " + str(100*abs(a[i]-b[i])/abs(a[i]))) else: print("absolute tolerance should be " + str(abs(a[i]-b[i]))) differs = True rval = False break elif (isinstance(a[i],list)) or (isinstance(a[i],numpy.ndarray)): for j in range(0,len(a[i])): if differs: break if ((isinstance(a[i][j],float)) or (isinstance(a[i][j],int))): if ((mode=="percentage") and (abs(a[i][j]-b[i][j]) > tolerance*abs(a[i][j]))) or ((mode=="absolute") and (abs(a[i][j]-b[i][j]) > tolerance)): print("Column " + c + " differs") print("(Row,Element)=(" + str(j) + "," + str(i) + ")") print("Reference file value: " + str(a[i][j])) print("Input file value: " + str(b[i][j])) if (mode=="percentage"): print("Tolerance in % should be " + str(100*abs(a[i][j]-b[i][j])/abs(a[i][j]))) else: print("Absolute tolerance should be " + str(abs(a[i][j]-b[i][j]))) differs = True rval = False break elif (isinstance(a[i][j],list)) or (isinstance(a[i][j],numpy.ndarray)): it = range(0,len(a[i][j])) if mode=="percentage": diff = numpy.abs(numpy.subtract(a[i][j], b[i][j])) > tolerance * numpy.abs(a[i][j]) it = numpy.where(diff)[0] elif (mode=="absolute"): diff = numpy.abs(numpy.subtract(a[i][j], b[i][j])) > tolerance it = numpy.where(diff)[0] for k in it: if differs: break if ( ((mode=="percentage") and (abs(a[i][j][k]-b[i][j][k]) > tolerance*abs(a[i][j][k]))) \ or ((mode=="absolute") and (abs(a[i][j][k]-b[i][j][k]) > tolerance)) \ or ((mode=="phaseabsdeg") and (phasediffabsdeg(a[i][j][k],b[i][j][k])>tolerance)) \ ): print("Column " + c + " differs") print("(Row,Channel,Corr)=(" + str(k) + "," + str(j) + "," + str(i) + ")") print("Reference file value: " + str(a[i][j][k])) print("Input file value: " + str(b[i][j][k])) if (mode=="percentage"): print("Tolerance in % should be " + str(100*abs(a[i][j][k]-b[i][j][k])/abs(a[i][j][k]))) elif (mode=="absolute"): print("Absolute tolerance should be " + str(abs(a[i][j][k]-b[i][j][k]))) elif (mode=="phaseabsdeg"): print("Phase tolerance in degrees should be " + str(phasediffabsdeg(a[i][j][k],b[i][j][k]))) else: print("Unknown comparison mode: ",mode) differs = True rval = False break else: print("Unknown data type: ",type(a[i])) differs = True rval = False break if not differs: print("Column " + c + " PASSED") finally: _tb.close() _tb2.close() logging.debug("compare_CASA_tables(referencetab = {}, testtab = {}): {}".format(referencetab,testtab, rval)) return rval def compare_files( file1, file2, shallow=False): ''' compare_files - Compare two Files. @param file1 --> a reference file @param file2 --> a file to verify @param shallow --> If shallow is true, files with identical os.stat() signatures are taken to be equal. Otherwise, the contents of the files are compared. @return: True if file1 & file2 seem equal, False otherwise ''' logging.info("Comparing {} to {}".format(file1, file2)) logging.debug("Executing: compare_files(file1 = {}, file2 = {}, shallow = {})".format(file1, file2, shallow)) if sys.version_info > (3,0): filecmp.clear_cache() return filecmp.cmp(file1, file2, shallow=shallow) def compare_caltables( table1, table2, cols=None, rtol=8e-7, atol=1e-8): ''' compare_caltables - Compare two caltables. @param table1 --> a reference table @param table2 --> a table to verify @param cols --> the name of cols to compare (list). Leave Blank For All @param rtol --> The relative tolerance parameter @param atol --> The absolute tolerance parameter @return: True if table1 == table2 else False ''' logging.info("Comparing {} to {}".format(table1, table2)) logging.debug("Executing: compare_caltables(table1 = {}, table2 = {}, cols={}, rtol={}, atol={})".format(table1, table2, cols, rtol, atol)) if cols is None: cols = [] tableVal1 = {} tableVal2 = {} _tb.open(table1) colname1 = _tb.colnames() for col in colname1: try: tableVal1[col] = _tb.getcol(col) except RuntimeError: pass _tb.close() _tb2.open(table2) colname2 = _tb2.colnames() for col in colname2: try: tableVal2[col] = _tb2.getcol(col) except RuntimeError: pass _tb2.close() truthDict = {} for col in tableVal1.keys(): logging.debug("Column: {}, dtype: {}".format(col, tableVal1[col].dtype)) try: if numpy.issubdtype(tableVal1[col].dtype, numpy.number): truthDict[col] = numpy.isclose(tableVal1[col], tableVal2[col], rtol=rtol, atol=atol) else: # Compare Non Numeric Types truthDict[col] = numpy.array_equal(tableVal1[col],tableVal2[col]) except: print(col, 'ERROR in determining the truth value') #casalog.post(message=col+': ERROR in determining the truth value') if len(cols) == 0: truths = [[x, numpy.all(truthDict[x] == True)] for x in truthDict.keys()] else: truths = [[x, numpy.all(truthDict[x] == True)] for x in cols] #Check that All Options are True for key in truthDict.keys(): if isinstance(truthDict[key], bool): if not truthDict[key]: logging.info("{0} in caltables do not match".format(key)) return False elif isinstance(truthDict[key], numpy.ndarray): if not numpy.all(truthDict[key]): return False else: logging.info('ERROR in finding truth value for Column: {}'.format(key)) return False return True def compare_dictionaries( dictionary1, dictionary2, skipkeys = None, rtol=8e-7, atol=1e-8): ''' compare_dictionaries - compare two dictionaries Dictionaries will fail when 1st instance of a failure @param dictionary1 --> the dictionary which is assumed to be correct @param dictionary2 --> the dictionary which is to be compared @param skipkeys --> list of keys which are to be ignored @param rtol --> The relative tolerance parameter @param atol --> The absolute tolerance parameter @return: True if dictionary1 == dictionary2 else False ''' if skipkeys is None: skipkeys = [] if not isinstance(skipkeys, list): logging.error("skipkeys not in correct format") raise TypeError("skipkeys must be a list") key_list_1 = sorted(list(dictionary1.keys())) key_list_2 = sorted(list(dictionary2.keys())) #Checks if Keys are the same if key_list_1 != key_list_2: logging.debug("Keys Do Not Match") return False for key in key_list_1: if key in skipkeys: continue # Compare Numpy Arrays if isinstance(dictionary1[key], numpy.ndarray) and isinstance(dictionary2[key], numpy.ndarray): """ For finite values, isclose uses the following equation to test whether two floating point values are equivalent. absolute(a - b) <= (atol + rtol * absolute(b)) """ if numpy.issubdtype(dictionary1[key].dtype, numpy.number) and numpy.issubdtype(dictionary2[key].dtype, numpy.number): if any( val == False for val in numpy.isclose(dictionary1[key], dictionary2[key], rtol=rtol, atol=atol, equal_nan=False)): logging.info("{0}:{1} != {0}:{2}".format(key,dictionary1[key],dictionary2[key])) return False else: if any( val == False for val in numpy.array_equal(dictionary1[key], dictionary2[key])): logging.info("{0}:{1} != {0}:{2}".format(key,dictionary1[key],dictionary2[key])) return False # Compare Strings elif isinstance(dictionary1[key], str) and isinstance(dictionary2[key], str): if (dictionary1[key] == dictionary2[key]): pass else: logging.info("{0}:{1} != {0}:{2}".format(key,dictionary1[key],dictionary2[key])) return False # Compare lists elif isinstance(dictionary1[key], list) and isinstance(dictionary2[key], list): if dictionary1[key] != dictionary2[key]: logging.info("{0}:{1} != {0}:{2}".format(key,dictionary1[key],dictionary2[key])) return False # Compare Numerics elif isinstance(dictionary1[key], numbers.Number) and isinstance(dictionary2[key], numbers.Number): """ rel_tol is the relative tolerance : it is the maximum allowed difference between a and b, relative to the larger absolute value of a or b. For example, to set a tolerance of 5%, pass rel_tol=0.05. The default tolerance is 1e-09, which assures that the two values are the same within about 9 decimal digits. rel_tol must be greater than zero. abs_tol is the minimum absolute tolerance : useful for comparisons near zero. abs_tol must be at least zero. If no errors occur, the result will be: abs(a-b) <= max(rel_tol * max(abs(a), abs(b)), abs_tol). """ if not numpy.isclose(dictionary1[key],dictionary2[key],rtol = rtol, atol=atol): logging.info("{0}:{1} != {0}:{2}".format(key,dictionary1[key],dictionary2[key])) return False elif isinstance(dictionary1[key], dict) and isinstance(dictionary2[key], dict): # Recursively compare dicts inside this dict res = compare_dictionaries(dictionary1[key], dictionary2[key], skipkeys, rtol, atol) if not res: return res else: try: if dictionary1[key] != dictionary2[key]: return False except: logging.error("Error in Comparing {0}:{1} != {0}:{2}".format(key,dictionary1[key],dictionary2[key])) return False return True def compare_directories( directory1, directory2): ''' Compare two directories recursively. Files in each directory are assumed to be equal if their names and contents are equal. @param directory1: First directory path @param directory2: Second directory path @return: True if the directory trees are the same and there were no errors while accessing the directories or files, False otherwise. ''' dirs_cmp = filecmp.dircmp(directory1, directory2) if len(dirs_cmp.left_only)>0 or len(dirs_cmp.right_only)>0 or \ len(dirs_cmp.funny_files)>0: return False (_, mismatch, errors) = filecmp.cmpfiles( directory1, directory2, dirs_cmp.common_files, shallow=False) if len(mismatch)>0 or len(errors)>0: return False for common_dir in dirs_cmp.common_dirs: new_directory1 = os.path.join(directory1, common_dir) new_directory2 = os.path.join(directory2, common_dir) if not compare_directories(new_directory1, new_directory2): return False return True def compare_pixel_value( imagename=None, refimage=None, loc=None): ''' Compare two images at a certain reference pixel @param imagename: Name of the image to be compared to a reference image @param refimage: Image to be compared against @param loc: The slice or pixel index to compare between the two images @return: True if the pixel values match at the provided index or slice. Returns False otherwise ''' if imagename != None and refimage != None: if isinstance(loc, str): _tb.open(imagename) image1 = _tb.getcol('map') _tb.close() _tb.open(refimage) image2 = _tb.getcol('map') _tb.close() index = [] to_slice = loc.split(',') # get index from the string array for item in to_slice: if ':' not in item: index.append(int(item)) else: item_split = item.split(':') index.append(slice(int(item_split[0]),int(item_split[1]))) selected_slice1 = image1[tuple(index)] selected_slice2 = image2[tuple(index)] isequal = numpy.isclose(selected_slice1, selected_slice2, rtol=1e-05, atol=1e-08) return numpy.all(isequal == True) else: logging.warning('Please give target location in string list format ("20,30,2:4")') else: logging.warning('Please provide both an image and reference image') def compare_pixel_mask( maskname='', refmask=None, refval=None, loc=None): ''' Compare to masks or mask values to a reference value @param maskname: The name of the maskfile to compare to either a reference mask file or value @param refmask: The reference mask image to be compared to @param refval: The reference value to compare the selected pixel(s) of the maskfile to @param loc: The index or slice of the mask image to compare to a refvalue. @return: True if the refmask and mask file are identical or if the selected slice of the mask file matches the refval ''' if os.path.exists(maskname): if refmask == None and refval == None: logging.warning('Please select a mask or region to use for comparison') elif refmask != None and refval == None: # if comparing a refmask compare the values in the table if os.path.exists(refmask): _tb.open(maskname) mask1 = _tb.getcol('PagedArray') _tb.close() _tb.open(refmask) mask2 = _tb.getcol('PagedArray') _tb.close() return numpy.all(mask1 == mask2) else: logging.warning('Invalid refmask file name') elif refmask == None and refval != None: # If using a reference value compare the value/shape to the selected slice if isinstance(loc, str): _tb.open(maskname) image = _tb.getcol('PagedArray') _tb.close() index = [] to_slice = loc.split(',') # get index from the string array for item in to_slice: if ':' not in item: index.append(int(item)) else: item_split = item.split(':') index.append(slice(int(item_split[0]),int(item_split[1]))) selected_slice = image[tuple(index)] # return false if the shapes don't match up if numpy.shape(selected_slice) != numpy.shape(refval): logging.warning('Please check that the shape of the reference and selected slice are the same') return False isequal = numpy.all(selected_slice == refval) return isequal else: logging.warning('Please give target location in string list format ("20,30,2:4")') else: logging.warning('Please provide only a referance value or reference mask, not both') else: logging.warning('Invalid mask file name')