casa
$Rev:20696$
|
00001 import numpy 00002 import os 00003 import shutil 00004 import tempfile 00005 from taskinit import tbtool, me 00006 00007 def dict_to_table(indict, tablepath, kwkeys=[], colkeys=[], info=None, keepcolorder=False): 00008 """ 00009 Converts a dictionary to a CASA table, and attempts to 00010 save it to tablepath. Returns whether or not it was successful. 00011 00012 kwkeys is a list of keys in dict that should be treated as table keywords, 00013 and colkeys is a list of keys to be treated as table columns. If a key in 00014 indict is not in either kwkeys or colkeys, it will be appended to colkeys 00015 if it refers to a list, array, or specially formed dict with the right 00016 number of rows, or kwkeys otherwise. 00017 00018 "Specially formed dict" means a python dictionary with the right keys to 00019 provide a comment and/or keywords to specify a (measure) frame or 00020 (quantity) unit for the column. 00021 00022 The number of rows is set by the first column. The order of the columns is 00023 the order of colkeys, followed by the remaining columns in alphabetical 00024 order. 00025 00026 Example: 00027 mydict = {'delta': [1.2866, 1.2957, 1.3047], 00028 'obs_code': ['*', 'U', 't'], 00029 'date': {'m0': {'unit': 'd', 00030 'value': [55317.0, 55318.0, 55319.0]}, 00031 'refer': 'UT1', 00032 'type': 'epoch'}, 00033 'phang': {'comment': 'phase angle', 00034 'data': {'unit': 'deg', 00035 'value': array([37.30, 37.33, 37.36])}}} 00036 00037 # Produces a table with, in order, a measure column (date), two bare 00038 # columns (delta and obs_code), and a commented quantity column (phang). 00039 # The comment goes in the 'comment' field of the column description. 00040 # Measure and straight array columns can also be described by using a 00041 # {'comment': (description), 'data': (measure, quantity, numpy.array or 00042 # list)} dict. 00043 dict_to_table(mydict, 'd_vs_phang.tab') 00044 00045 TODO: detect non-float data types, including array cells. 00046 """ 00047 nrows = 0 00048 dkeys = indict.keys() 00049 keywords = [] 00050 cols = [] 00051 00052 def get_bare_col(col): 00053 """ 00054 Given a col that could be a bare column (list or array), or measure or 00055 quantity containing a bare column, return the bare column. 00056 """ 00057 barecol = col 00058 if hasattr(barecol, 'has_key'): 00059 if barecol.has_key('comment'): 00060 barecol = barecol.get('data') 00061 if me.ismeasure(barecol): 00062 barecol = barecol['m0'] 00063 # if qa.isquantity(data) can't be trusted. 00064 if hasattr(barecol, 'has_key') and barecol.has_key('unit') and barecol.has_key('value'): 00065 barecol = barecol['value'] 00066 return barecol 00067 00068 # Divvy up the known keywords and columns, if present, preserving the 00069 # requested order. 00070 for kw in kwkeys: 00071 if kw in dkeys: 00072 # Take kw out of dkeys and put it in keywords. 00073 keywords.append(dkeys.pop(dkeys.index(kw))) 00074 for c in colkeys: 00075 if c in dkeys: 00076 cols.append(dkeys.pop(dkeys.index(c))) 00077 if nrows == 0: 00078 nrows = len(get_bare_col(indict[c])) 00079 print "Got nrows =", nrows, "from", c 00080 00081 # Go through what's left of dkeys and assign them to either keywords or 00082 # cols. 00083 dkeys.sort() 00084 for d in dkeys: 00085 used_as_col = False 00086 colcand = get_bare_col(indict[d]) 00087 # Treat it as a column if it has the right number of rows. 00088 if type(colcand) in (list, numpy.ndarray): 00089 if nrows == 0: 00090 nrows = len(colcand) 00091 if len(colcand) == nrows: 00092 cols.append(d) 00093 used_as_col = True 00094 if not used_as_col: 00095 keywords.append(d) 00096 00097 # Make the table's description. 00098 tabdesc = {} 00099 # Initialize the column descriptor with defaults (these come from 00100 # data/ephemerides/DE200, but I replaced IncrementalStMan with StandardStMan). 00101 coldesc = {'comment': '', 00102 'dataManagerGroup': '', 00103 'dataManagerType': 'StandardStMan', 00104 'maxlen': 0, 00105 'option': 0, 00106 'valueType': 'double'} # Use double (not float!) for columns 00107 # that will be read by MeasIERS. 00108 for c in cols: 00109 #print "Setting coldesc for", c 00110 data = indict[c] # Place to find the valueType. 00111 00112 if hasattr(data, 'has_key'): 00113 #print "comment =", data.get('comment', '') 00114 coldesc['comment'] = data.get('comment', '') 00115 00116 data = get_bare_col(data) 00117 valtype = str(type(data[0]))[7:-2] 00118 if valtype == 'str': 00119 valtype = 'string' 00120 valtype = valtype.replace('64', '') # Table uses 'float', not 'float64'. 00121 valtype = valtype.replace('numpy.', '') # or 'numpy.float'. 00122 00123 # Use double (not float!) for columns that will be read by MeasIERS. 00124 if valtype == 'float': 00125 valtype = 'double' 00126 00127 coldesc['valueType'] = valtype 00128 00129 tabdesc[c] = coldesc.copy() 00130 00131 # Since tables are directories, it saves a lot of grief if we first check 00132 # whether the table exists and is under svn control. 00133 svndir = None 00134 if os.path.isdir(tablepath): 00135 if os.path.isdir(tablepath + '/.svn'): 00136 # tempfile is liable to use /tmp, which can be too small and/or slow. 00137 # Use the directory that tablepath is in, since we know the user 00138 # approves of writing to it. 00139 workingdir = os.path.abspath(os.path.dirname(tablepath.rstrip('/'))) 00140 00141 svndir = tempfile.mkdtemp(dir=workingdir) 00142 shutil.move(tablepath + '/.svn', svndir) 00143 print "Removing %s directory" % tablepath 00144 shutil.rmtree(tablepath) 00145 00146 # Create and fill the table. 00147 retval = True 00148 try: 00149 mytb = tbtool() 00150 tmpfname='_tmp_fake.dat' 00151 if keepcolorder: 00152 # try to keep order of cols 00153 # Ugly, but since tb.create() cannot accept odered dictionary 00154 # for tabledesc, I cannot find any other way to keep column order. 00155 # * comment for each column will not be filled 00156 f = open(tmpfname,'w') 00157 zarr=numpy.zeros(len(cols)) 00158 szarr=str(zarr.tolist()) 00159 szarr=szarr.replace('[','') 00160 szarr=szarr.replace(']','') 00161 szarr=szarr.replace(',','') 00162 scollist='' 00163 sdtypes='' 00164 for c in cols: 00165 scollist+=c+' ' 00166 vt=tabdesc[c]['valueType'] 00167 if vt=='string': 00168 sdtypes+='A ' 00169 elif vt=='integer': 00170 sdtypes+='I ' 00171 elif vt=='double': 00172 sdtypes+='D ' 00173 elif vt=='float': 00174 sdtypes+='R ' 00175 f.write(scollist+'\n') 00176 f.write(sdtypes+'\n') 00177 f.write(szarr) 00178 f.close() 00179 mytb.fromascii(tablepath,tmpfname,sep=' ') 00180 # close and re-open since tb.fromascii(nomodify=False) has not 00181 # implemented yet 00182 mytb.close() 00183 os.remove(tmpfname) 00184 mytb.open(tablepath, nomodify=False) 00185 mytb.removerows(0) 00186 else: 00187 mytb.create(tablepath, tabdesc) 00188 if type(info) == dict: 00189 mytb.putinfo(info) 00190 mytb.addrows(nrows) # Must be done before putting the columns. 00191 except Exception, e: 00192 print "Error", e, "trying to create", tablepath 00193 retval = False 00194 for c in cols: 00195 try: 00196 #print "tabdesc[%s] =" % c, tabdesc[c] 00197 data = indict[c] # Note the trickle-down nature below. 00198 if hasattr(indict[c], 'has_key') and indict[c].has_key('comment'): 00199 data = data['data'] 00200 if me.ismeasure(data): 00201 mytb.putcolkeyword(c, 'MEASINFO', {'Ref': data['refer'], 00202 'type': data['type']}) 00203 data = data['m0'] # = quantity 00204 # if qa.isquantity(data) can't be trusted. 00205 if hasattr(data, 'has_key') and data.has_key('unit') and data.has_key('value'): 00206 mytb.putcolkeyword(c, 'QuantumUnits', 00207 numpy.array([data['unit']])) 00208 data = data['value'] 00209 mytb.putcol(c, data) 00210 except Exception, e: 00211 print "Error", e, "trying to put column", c, "in", tablepath 00212 print "data[0] =", data[0] 00213 print "tabdesc[c] =", tabdesc[c] 00214 retval = False 00215 for k in keywords: 00216 try: 00217 mytb.putkeyword(k, indict[k]) 00218 except Exception, e: 00219 print "Error", e, "trying to put keyword", k, "in", tablepath 00220 retval = False 00221 mytb.close() 00222 00223 if svndir: 00224 shutil.move(svndir, tablepath + '/.svn') 00225 return retval