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
10
11
12
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
26 displayMFRegistry = serializers.ValueMapperFactoryRegistry()
27 registerDisplayMF = displayMFRegistry.registerFactory
28
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
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
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
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
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
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
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
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:
160 return val.isoformat()
161 return coder
162 registerDisplayMF(humanDatesFactory)
163
164
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
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
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
237
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
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
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
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