1 """
2 A renderer for Data to HTML/stan
3 """
4
5
6
7
8
9
10
11 import itertools
12 import os
13 import re
14 import urlparse
15 import urllib
16
17 from nevow import flat
18 from nevow import loaders
19 from nevow import rend
20 from nevow import tags as T
21
22 from gavo import base
23 from gavo import formats
24 from gavo import rsc
25 from gavo import svcs
26 from gavo import utils
27 from gavo.base import valuemappers
28 from gavo.formats import texttable
29 from gavo.protocols import products
30 from gavo.rscdef import rmkfuncs
31 from gavo.utils import serializers
32 from gavo.utils import typeconversions
33 from gavo.web import common
34
35
36 _htmlMFRegistry = texttable.displayMFRegistry.clone()
37 _registerHTMLMF = _htmlMFRegistry.registerFactory
38
39
41 if colDesc["displayHint"].get("type")!="bar":
42 return
43 def coder(val):
44 if val:
45 return T.hr(style="width: %dpx"%int(val), title="%.2f"%val,
46 class_="scoreBar")
47 return ""
48 return coder
49 _registerHTMLMF(_barMapperFactory)
50
51
53 if colDesc["displayHint"].get("type")!="product":
54 return
55 if colDesc["displayHint"].get("nopreview"):
56 mouseoverHandler = None
57 else:
58 mouseoverHandler = "insertPreview(this, null)"
59 fixedArgs = ""
60 def coder(val):
61 if val:
62 anchor = re.sub(r"\?.*", "",
63 os.path.basename(urllib.unquote_plus(str(val)[4:])))
64 if not anchor:
65 anchor = "File"
66
67 if isinstance(val, basestring) and utils.looksLikeURLPat.match(val):
68
69
70 return T.a(href=val)[anchor]
71
72 else:
73 return T.a(href=products.makeProductLink(val)+fixedArgs,
74 onmouseover=mouseoverHandler,
75 class_="productlink")[anchor]
76 else:
77 return ""
78 return coder
79 _registerHTMLMF(_productMapperFactory)
80
81
83 """is a mapper yielding links to simbad.
84
85 To make this work, you need to furnish the OutputField with a
86 select="array[alphaFloat, deltaFloat]" or similar.
87
88 You can give a coneMins displayHint to specify the search radius in
89 minutes.
90 """
91 if colDesc["displayHint"].get("type")!="simbadlink":
92 return
93 radius = float(colDesc["displayHint"].get("coneMins", "1"))
94 def coder(data):
95 alpha, delta = data[0], data[1]
96 if alpha and delta:
97 return T.a(href="http://simbad.u-strasbg.fr/simbad/sim-coo?Coord=%s"
98 "&Radius=%f"%(urllib.quote("%.5fd%+.5fd"%(alpha, delta)),
99 radius))["[Simbad]"]
100 else:
101 return ""
102 return coder
103 _registerHTMLMF(_simbadMapperFactory)
104
105
107 if colDesc["displayHint"].get("type")!="bibcode":
108 return
109 def coder(data):
110 if data:
111 for item in data.split(","):
112 yield T.a(href=base.getConfig("web", "adsMirror")+
113 "/abs/"+urllib.quote(item.strip()))[
114 item.strip()]
115 yield ", "
116 else:
117 yield ""
118 return coder
119 _registerHTMLMF(_bibcodeMapperFactory)
120
121
123 if colDesc["displayHint"].get("type")!="keephtml":
124 return
125 def coder(data):
126 if data:
127 return T.raw(data)
128 return ""
129 return coder
130 _registerHTMLMF(_keepHTMLMapperFactory)
131
132
134 if colDesc["displayHint"].get("type")!="imageURL":
135 return
136 width = colDesc["displayHint"].get("width")
137 def coder(data):
138 if data:
139 res = T.img(src=data, alt="Image at %s"%data)
140 if width:
141 res(width=width)
142 return res
143 return ""
144 return coder
145 _registerHTMLMF(_imageURLMapperFactory)
146
147
149 if colDesc["displayHint"].get("type")!="url":
150 return
151
152 anchorText = colDesc.original.getProperty("anchorText", None)
153 if anchorText:
154 def makeAnchor(data):
155 return anchorText
156 else:
157 def makeAnchor(data):
158 return urllib.unquote(
159 urlparse.urlparse(data)[2].split("/")[-1])
160
161 def coder(data):
162 if data:
163 return T.a(href=data)[makeAnchor(data)]
164 return ""
165 return coder
166 _registerHTMLMF(_urlMapperFactory)
167
168
170 """inserts mappers for values with displayHint type=checkmark.
171
172 These render a check mark if the value is python-true, else nothing.
173 """
174 if colDesc["displayHint"].get("type")!="checkmark":
175 return
176 def coder(data):
177 if data:
178 return u"\u2713"
179 return ""
180 return coder
181 _registerHTMLMF(_booleanCheckmarkFactory)
182
183
185 """do a reasonable representation of arrays in HTML:
186 """
187 if not colDesc["dbtype"] in serializers.GEOMETRY_ARRAY_TYPES:
188 return
189
190 def mapper(val):
191 if val is None:
192 return None
193 return T.span(class_="array")["[%s]"%" ".join(
194 "%s"%v for v in val.asDALI())]
195
196 colDesc["datatype"], colDesc["arraysize"], colDesc["xtype"
197 ] = typeconversions.sqltypeToVOTable(colDesc["dbtype"])
198
199 return mapper
200 _registerHTMLMF(_pgSphereMapperFactory)
201
202
203
204
205
207 """A mixin providing renders for table headings.
208
209 The class mixing in must give the SerManager used in a serManager
210 attribute.
211 """
213 return self.serManager.table.tableDef.columns
214
216 cd = self.serManager.getColumnByName(colDef.key)
217 cont = colDef.getLabel()
218 desc = cd["description"]
219 if not desc:
220 desc = cont
221 tag = ctx.tag(title=desc)[T.xml(cont)]
222 if cd["unit"]:
223 tag[T.br, "[%s]"%cd["unit"]]
224 note = cd["note"]
225 if note:
226 noteURL = "#note-%s"%note.tag
227 ctx.tag[T.sup[T.a(href=noteURL)[note.tag]]]
228 return tag
229
230
233 self.serManager = serManager
234
235 docFactory = loaders.stan(
236 T.tr(data=T.directive("fielddefs"), render=rend.sequence) [
237 T.th(pattern="item", render=T.directive("headCell"),
238 class_="thVertical")
239 ])
240
241
242 _htmlMetaBuilder = common.HTMLMetaBuilder()
243
244
246 """returns a function object from source.
247
248 Source must be the function body of a renderer. The variable data
249 contains the entire row, and the thing must return a string or at
250 least stan (it can use T.tag).
251 """
252 code = ("def format(data):\n"+
253 utils.fixIndentation(source, " ")+"\n")
254 return rmkfuncs.makeProc("format", code, "", None,
255 queryMeta=queryMeta, source=source, T=T, rd=rd)
256
257
259 """A base class for rendering tables and table lines.
260
261 Both HTMLTableFragment (for complete tables) and HTMLKeyValueFragment
262 (for single rows) inherit from this.
263 """
269
271 """creates the serialization manager and the formatter sequence.
272
273 These are in the attributes serManager and formatterSeq, respectively.
274 formatterSeq consists of triples of (name, formatter, fullRow), where
275 fullRow is true if the formatter wants to be passed the full row rather
276 than just the column value.
277 """
278 self.serManager = valuemappers.SerManager(self.table, withRanges=False,
279 mfRegistry=_htmlMFRegistry, acquireSamples=False)
280 self.formatterSeq = []
281 for index, (desc, field) in enumerate(
282 zip(self.serManager, self.table.tableDef)):
283 formatter = self.serManager.mappers[index]
284 if isinstance(field, svcs.OutputField):
285 if field.wantsRow:
286 desc["wantsRow"] = True
287 if field.formatter:
288 formatter = _compileRenderer(
289 field.formatter,
290 self.queryMeta,
291 self.table.tableDef.rd)
292 self.formatterSeq.append(
293 (desc["name"], formatter, desc.get("wantsRow", False)))
294
296 """leaves a sequence of children for each row in the
297 defaultTds attribute.
298
299 This calls _computeSerializationRules. The function was
300 (and can still be) used for stan-based serialization of HTML tables,
301 but beware that that is dead slow. The normal rendering doesn't
302 use defaultTds any more.
303 """
304 self._computeSerializationRules()
305 self.defaultTds = []
306 for (name, formatter, wantsRow) in self.formatterSeq:
307 if wantsRow:
308 self.defaultTds.append(
309 T.td(formatter=formatter, render=T.directive("useformatter")))
310 else:
311 self.defaultTds.append(T.td(
312 data=T.slot(unicode(name)),
313 formatter=formatter,
314 render=T.directive("useformatter")))
315
325
336
338 self.headCells = HeadCells(self.serManager)
339 self.headCellsStan = T.xml(self.headCells.renderSynchronously())
340
342 """returns the header line for this table as an XML string.
343 """
344
345
346 return ctx.tag[self.headCellsStan]
347
349 return self.table.tableDef.columns
350
359
360
362 """A nevow renderer for result tables.
363 """
364 rowsPerDivision = 25
365
395
397
398 return ctx.tag(render=rend.mapping)[self.defaultTds]
399
400 - def render_tableBody(self, ctx, data):
401 """returns HTML-rendered table rows in chunks of rowsPerDivision.
402
403 We don't use stan here since we can concat all those tr/td much faster
404 ourselves.
405 """
406 rowAttrsIterator = itertools.cycle([' class="data"', ' class="data even"'])
407 formatRow = self._getRowFormatter()
408 rendered = []
409 yield T.xml("<tbody>")
410 for row in self.table:
411 rendered.append(formatRow(row, rowAttrsIterator.next()))
412 if len(rendered)>=self.rowsPerDivision:
413 yield T.xml("\n".join(rendered))
414 yield self.headCellsStan
415 rendered = []
416 yield T.xml("\n".join(rendered)+"\n</tbody>")
417
418 docFactory = loaders.stan(T.div(class_="tablewrap")[
419 T.div(render=T.directive("meta"), class_="warning")["_warning"],
420 T.table(class_="results") [
421 T.thead(render=T.directive("headCells")),
422 T.tbody(render=T.directive("tableBody"))],
423 T.invisible(render=T.directive("footnotes")),
424 ]
425 )
426
427
429 """A nevow renderer for single-row result tables.
430 """
433
435 return loaders.stan([
436 T.div(render=T.directive("meta"), class_="warning")["_warning"],
437 T.table(class_="keyvalue", render=rend.mapping,
438 data=T.directive("firstrow")) [
439 [[T.tr[
440 T.th(data=colDef, render=T.directive("headCell"),
441 class_="thHorizontal"),
442 td],
443 T.tr(class_="keyvaluedesc")[T.td(colspan=2)[
444 colDef.description]]]
445 for colDef, td in zip(self.serManager.table.tableDef.columns,
446 self.defaultTds)]],
447 T.invisible(render=T.directive("footnotes")),
448 ])
449
450 docFactory = property(makeDocFactory)
451
452
463
464
465 formats.registerDataWriter("html", writeDataAsHTML, "text/html", "HTML",
466 ".html")
467