Package gavo :: Package formats :: Module texttable
[frames] | no frames]

Source Code for Module gavo.formats.texttable

  1  """ 
  2  Writing data as plain text. 
  3   
  4  Currently, we only do TSV.  It would probably be nice to support "formatted 
  5  ASCII as well, though that may be a bit tricky given that we do not 
  6  really store sane formatting hints for most columns. 
  7  """ 
  8   
  9  #c Copyright 2008-2019, the GAVO project 
 10  #c 
 11  #c This program is free software, covered by the GNU GPL.  See the 
 12  #c COPYING file in the source distribution. 
 13   
 14   
 15  import cStringIO 
 16  import datetime 
 17   
 18  from gavo import base 
 19  from gavo import rsc 
 20  from gavo import stc 
 21  from gavo import utils 
 22  from gavo.formats import common 
 23  from gavo.utils import serializers 
 24   
 25  # A mapper function registry for formats directed at humans 
 26  displayMFRegistry = serializers.ValueMapperFactoryRegistry() 
 27  registerDisplayMF = displayMFRegistry.registerFactory 
 28   
29 -def _defaultMapperFactory(colDesc):
30 def coder(val): 31 if val is None: 32 return "N/A" 33 return unicode(val)
34 return coder 35 registerDisplayMF(_defaultMapperFactory) 36 37 38 39 floatTypes = set(["real", "float", "double", "double precision"]) 40
41 -def _sfMapperFactory(colDesc):
42 if colDesc["dbtype"] not in floatTypes: 43 return 44 if colDesc["displayHint"].get("sf"): 45 fmtStr = "%%.%df"%int(colDesc["displayHint"].get("sf")) 46 def coder(val): 47 if val is None: 48 return "N/A" 49 else: 50 return fmtStr%val
51 return coder 52 registerDisplayMF(_sfMapperFactory) 53 54
55 -def _hmsMapperFactory(colDesc):
56 if colDesc["displayHint"].get("type")!="hms": 57 return 58 colDesc["unit"] = "h:m:s" 59 sepChar = colDesc["displayHint"].get("sepChar", " ") 60 sf = int(colDesc["displayHint"].get("sf", 2)) 61 def coder(val): 62 if val is None: 63 return "N/A" 64 else: 65 return utils.degToHms(val, sepChar, sf)
66 return coder 67 registerDisplayMF(_hmsMapperFactory) 68 69
70 -def _dmsMapperFactory(colDesc):
71 if colDesc["displayHint"].get("type")!="dms": 72 return 73 colDesc["unit"] = "d:m:s" 74 sepChar = colDesc["displayHint"].get("sepChar", " ") 75 sf = int(colDesc["displayHint"].get("sf", 2)) 76 def coder(val): 77 if val is None: 78 return "N/A" 79 return utils.degToDms(val, sepChar, sf)
80 return coder 81 registerDisplayMF(_dmsMapperFactory) 82 83
84 -def _unitMapperFactory(colDesc):
85 """returns a factory that converts between units for fields that have 86 a displayUnit displayHint. 87 88 The stuff done here has to be done for all factories handling unit-based 89 floating point values. Maybe we want to do "decorating" meta-factories? 90 """ 91 if colDesc["displayHint"].get("displayUnit") and \ 92 colDesc["displayHint"]["displayUnit"]!=colDesc["unit"]: 93 try: 94 factor = base.computeConversionFactor(colDesc["unit"], 95 colDesc["displayHint"]["displayUnit"]) 96 except base.BadUnit: 97 # bad unit somewhere; ignore display hint 98 base.ui.notifyError("Bad unit while computing conversion factor.") 99 return None 100 101 colDesc["unit"] = colDesc["displayHint"]["displayUnit"] 102 fmtStr = "%%.%df"%int(colDesc["displayHint"].get("sf", 2)) 103 104 if "[" in colDesc["dbtype"]: 105 def coder(val): 106 if val is None: 107 return "N/A" 108 return "[%s]"%", ".join("N/A" if item is None else fmtStr%(item*factor) 109 for item in val)
110 111 else: 112 def coder(val): 113 return "N/A" if val is None else fmtStr%(val*factor) 114 115 return coder 116 registerDisplayMF(_unitMapperFactory) 117 118
119 -def _stringWrapMF(baseMF):
120 """returns a factory that that stringifies floats and makes N/A from 121 Nones coming out of baseMF and passes everything else through. 122 """ 123 def factory(colDesc): 124 handler = baseMF(colDesc) 125 if colDesc["displayHint"].get("sf", None): 126 fmtstr = "%%.%df"%int(colDesc["displayHint"]["sf"]) 127 fmtstr = "%s" 128 if handler: 129 def realHandler(val): 130 res = handler(val) 131 if isinstance(res, float): 132 return fmtstr%res 133 else: 134 if res is None: 135 return "N/A" 136 else: 137 return res
138 return realHandler 139 return factory 140 141 registerDisplayMF(_stringWrapMF(stc.datetimeMapperFactory)) 142 143
144 -def humanDatesFactory(colDesc):
145 format, unit = {"humanDate": ("%Y-%m-%d %H:%M:%S", ""), 146 "humanDay": ("%Y-%m-%d", "") }.get( 147 colDesc["displayHint"].get("type"), (None, None)) 148 if format and colDesc["dbtype"] in ("date", "timestamp"): 149 colDesc["unit"] = unit 150 def coder(val): 151 if val is None: 152 return "N/A" 153 else: 154 colDesc["datatype"], colDesc["arraysize"] = "char", "*" 155 colDesc["xtype"] = "timestamp" 156 colDesc["unit"] = "" 157 try: 158 return val.strftime(format) 159 except ValueError: # probably too old a date, fall back to a hack 160 return val.isoformat()
161 return coder 162 registerDisplayMF(humanDatesFactory) 163 164
165 -def humanTimesFactory(colDesc):
166 if colDesc["displayHint"].get("type")=="humanTime": 167 sf = int(colDesc["displayHint"].get("sf", 0)) 168 fmtStr = "%%02d:%%02d:%%0%d.%df"%(sf+3, sf) 169 def coder(val): 170 if val is None: 171 return "N/A" 172 else: 173 if isinstance(val, (datetime.time, datetime.datetime)): 174 return fmtStr%(val.hour, val.minute, val.second) 175 elif isinstance(val, datetime.timedelta): 176 hours = val.seconds//3600 177 minutes = (val.seconds-hours*3600)//60 178 seconds = (val.seconds-hours*3600-minutes*60)+val.microseconds/1e6 179 return fmtStr%(hours, minutes, seconds)
180 return coder 181 registerDisplayMF(humanTimesFactory) 182 183
184 -def jdMapperFactory(colDesc):
185 """maps JD, MJD, unix timestamp, and julian year columns to 186 human-readable datetimes. 187 188 MJDs are caught by inspecting the UCD or the name. 189 """ 190 if (colDesc["displayHint"].get("type")=="humanDate" 191 and colDesc["dbtype"] in ("double precision", "real")): 192 193 if colDesc["unit"]=="d": 194 if ("mjd" in colDesc["ucd"].lower() 195 or colDesc["xtype"]=="mjd" 196 or "mjd" in colDesc["name"]): 197 converter = stc.mjdToDateTime 198 else: 199 converter = stc.jdnToDateTime 200 elif colDesc["unit"]=="s": 201 converter = datetime.datetime.utcfromtimestamp 202 elif colDesc["unit"]=="yr": 203 converter = stc.jYearToDateTime 204 else: 205 return None 206 207 def fun(val): 208 if val is None: 209 return "N/A" 210 return utils.formatISODT(converter(val))
211 colDesc["datatype"], colDesc["arraysize"] = "char", "*" 212 colDesc["xtype"] = "timestamp" 213 colDesc["unit"] = "" 214 return fun 215 registerDisplayMF(jdMapperFactory) 216 217
218 -def _sizeMapperFactory(colDesc):
219 """is a factory for formatters for file sizes and similar. 220 """ 221 if colDesc["unit"]!="byte": 222 return 223 sf = int(colDesc["displayHint"].get("sf", 1)) 224 def coder(val): 225 if val is None: 226 return "N/A" 227 else: 228 return utils.formatSize(val, sf)
229 return coder 230 registerDisplayMF(_sizeMapperFactory) 231 232 233 registerDisplayMF(serializers._pgSphereMapperFactory) 234 235
236 -def _makeString(val):
237 # this is a cheap trick to ensure everything non-ascii is escaped. 238 if val is None: 239 return "N/A" 240 if isinstance(val, basestring): 241 return repr(unicode(val))[2:-1] 242 if isinstance(val, (list, tuple)): 243 return "[%s]"%" ".join("%s"%v for v in val) 244 return str(val)
245 246
247 -def renderAsColumns(table, target, acquireSamples=False):
248 """writes a fixed-column representation of table to target. 249 """ 250 if isinstance(table, rsc.Data): 251 table = table.getPrimaryTable() 252 sm = base.SerManager(table, acquireSamples=acquireSamples, 253 mfRegistry=displayMFRegistry) 254 target.write(utils.formatSimpleTable( 255 (_makeString(s) for s in row) 256 for row in sm.getMappedTuples())) 257 return ""
258 259
260 -def renderAsText(table, target, acquireSamples=True):
261 """writes a text (TSV) rendering of table to the file target. 262 """ 263 if isinstance(table, rsc.Data): 264 table = table.getPrimaryTable() 265 sm = base.SerManager(table, acquireSamples=acquireSamples) 266 for row in sm.getMappedTuples(): 267 target.write("\t".join([_makeString(s) for s in row])+"\n")
268 269
270 -def getAsText(data):
271 target = cStringIO.StringIO() 272 renderAsText(data, target) 273 return target.getvalue()
274 275
276 -def readTSV(inFile):
277 """returns a list of tuples for a tab-separated-values file. 278 279 Lines starting with # and lines containing only whitespace are ignored. 280 Whitespace at front and back is stripped. 281 282 No checks are done at this point, i.e., the tuples could be of varying 283 lengths. 284 """ 285 data = [] 286 for ln in inFile: 287 ln = ln.strip() 288 if not ln or ln.startswith("#"): 289 continue 290 data.append(tuple(ln.split("\t"))) 291 return data
292 293 294 # NOTE: This will only serialize the primary table. 295 common.registerDataWriter("tsv", renderAsText, "text/tab-separated-values", 296 "Tab separated values", ".tsv") 297 common.registerDataWriter("txt", renderAsColumns, "text/plain", 298 "Fixed-column plain text", ".txt") 299