casa  $Rev:20696$
 All Classes Namespaces Files Functions Variables
dict_to_table.py
Go to the documentation of this file.
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