This is the first example for ``n:data``. What happens here is that I
put a thing into the render context, the ``browserURL``. Again, that's
mapped to a python function, which again comes from
``web.metarender.ServiceInfoRenderer``. It looks like this::
def data_browserURL(self, ctx, data):
return self.service.getBrowserURL()
– where the method returns a URL suitable for a web browser, or ``None``
if the service doesn't have a renderer for which a browser will display
something useful (e.g., a service only having an ``ssap.xml`` renderer).
This last part is used in the ``n:render="ifdata"``. This nevow
renderer (if you're curious, it's in web.grend and thus available on all
DaCHS renderers) will return an empty string if the context object
evaluates to false, while otherwise returning the tree below it.
The net effect of this construct is that the "Use this service in your
browser" button that is generated in what's below will only be shown if
the service actualy is usable in a web browser.
The image is put in literally, but the link it points to has to be taken
from the context object (the ``browserURL``). To get it into the
attribute, nevow uses a trick you may know from XSLT::
Essentially, there's an element, ``n:attr``, that, when rendered, will
end up as an attribute on the embedding element (the ``a``). The name
of that attribute is taken from the ``name`` attribute of ``n:attr``,
i.e., ``href`` in this case. The value of that attribute can be
rendered in an arbitrary way, but in this case, I'm just using the
``string`` renderer (provided by nevow itself), which takes the context
object, stringifies it and dumps it into the DOM tree. Had I written::
http://foo.bar/baz
a link
the result would have been::
a link
– while introducing constants in this way doesn't
make much sense, it might be useful if, for
instance, the context object were a relative link, in which case you
could have written::
http://foo.bar/baz
a link
If this seems spooky to you, play around with it for a moment – it's
really not as hard as it might seem.
What follows in ``serviceinfo.html`` is just more of the same, with some
additional nevow renderers explained below. The next new thing – and
the last major concept to understand for nevew renderering – is
this::
Let's take this step by step. First, we put something into the context
object: ``rendAvail``. This is again taken from the DaCHS renderer
(still the ``ServiceInfoRenderer``), which reads::
def data_rendAvail(self, ctx, data):
return [{"rendName": rend,
"rendExpl": RendExplainer.explain(rend, self.service)}
for rend in self.service.allowed]
``RendExplainer.explain`` is a function returning DOM fragments
explaining what a renderer does, with embedded links and similar as
necessary. So, what comes back is a structure looking like this::
[
{"rendName": "form", "rendExpl": "This is browseable"},
{"rendName": "scs.xml", "rendExpl": "This is not browseable"}]
-- a sequence of mappings.
Such sequences you can renderer using the nevow-provided ``sequence``
nevow renderer. This renderer looks for an element that has
``n:pattern="sequence"`` in the DOM tree below it, and renders one copy of
of that each for each element of the context object (which must be a
sequence) with, and that's the brilliant part, the context object set to
that object. You may want to read that sentence again...
So, with example sequene above the::
--
in the body of the ``ul`` element will be rendered twice, first with::
{"rendName": "form", "rendExpl": "This is browseable"}
as the context object and then with::
{"rendName": "scs.xml", "rendExpl": "This is not browseable"}]
These are mappings in python lingo, and so it may not surprise you that
to make something sensible of that, nevow provides a ``mapping`` nevow
renderer. It's a bit like ``sequence`` in that it inspects the tree
below it. Unlike it, it doesn't look for ``item`` patterns but for
``n:slot``, which has a ``name`` attribute – the element is then
replaced by the value of the named key in the mapping.
One last new thing is a bit further down::
Copyright, License, Acknowledgements
rights
Here, the render attribute contains a blank. Nevow interprets that as a
parameterised renderer; in effect, the parameter(s) are passed to the
renderer. It probably helps to see how this is implemented::
def render_ifmeta(self, metaName):
if self.getMeta(metaName) is not None:
return lambda ctx, data: ctx.tag
else:
return lambda ctx, data: ""
Anyway, parameterised nevow renderers need this extra argument, and
you'll get weird errors if you don't provide it.
And that's almost all you need to know about nevow's templating
language, except for what additional render and data functions there
are. The more useful of which are covered in the next chapter.
Common Render and Data Functions
--------------------------------
In DaCHS templates, nevow renderers can come from nevow itself, the
active renderer (that's usually ``form`` for templates or a variety
thereof), or the active service element in the RD (``customRF`` and
``customDF`` for nevow renderers and data functions). Unfortunately,
we've added nevow renderers and data functions in a somewhat ad-hoc
fashion, and so they're not really documented terribly well.
Until we get to imporve that, here's an overview of the nevow renderers
(and data functions) we believe are most useful in practice.
:string:
(from nevow) takes the unicode of a context object and places that in
the DOM
:sequence:
(from nevow) iterates over the context object, rendering ``item``
patterns in its subtree once per item.
:mapping:
(from nevow) fills ``n:slot`` elements in its subtree with values from
its (dict) context object, taking the slot's name attributes as keys
:meta (data function):
(from GavoRenderMixin) puts the named item from the current meta
parent (usually, the service) into the context object. This could
look like this::
(you don't usally need this, prefer the meta render function)
:meta:
(from GavoRenderMixin) renders the meta item named in the text content
as text.
:metahtml:
(from GavoRenderMixin) renders the meta item named in the text content
as HTML; this automatically handles meta sequences and several special
cases, so it's the preferred way of including metadata in templates.
:rootlink:
(from GavoRenderMixin) this was intended for running DaCHS "off-root"
(i.e., as if in a subdirectory of the server). Since that's not how
DaCHS was deployed in practice, it's not used consistently, so there's
no point in using it right now.
:getconfig:
(from GavoRenderMixin) takes a config key from its content and
renders it as the value of this configuration item, e.g.::
Finish up in
[async]defaultExecTime seconds
If you leave out the section, [general] is assumed as usual.
:ifmeta:
(from GavoRenderMixin) a parameterised renderer, i.e. used like
this::
With this, stuff is rendered if a coverage meta item is present in the
service.
:ifownmeta:
(from GavoRenderMixin) like ifmeta, except meta items are not
inherited (e.g., title, which service typically gets from the
embedding resource)
:ifdata:
(from GavoRenderMixin) only renders the embedded subtree if the
context object evaluates to True
:ifnodata:
(from GavoRenderMixin) only renders the embedded subtree if the
context object evaluates to False
:ifslot:
(from GavoRenderMixin) a parameterised renderer. It only renders the
embedded subtree (usually a slot) if the named key exists in the
context object (which must be a dict-like thing)
:ifnoslot:
(from GavoRenderMixin) the reverse of ifslot.
:ifadmin:
(from GavoRenderMixin) renders the embedded subtree if there is a
logged user and that user is gavoadmin.
:explodableMeta:
(from GavoRenderMixin) used in the sidebar – see there for what it
does.
:authinfo:
(from GavoRenderMixin) returns material letting an anonymous user log
in or a logged in user log out.
:prependsite:
(from GavoRenderMixin) for templates intended to be shared between
sites, this prepends the current site's short name (config item
[web]sitename) to the element content.
:withsidebar:
(from GavoRenderMixin) used on the ``body`` element, adds the standard
DaCHS sidebar to a page.
:resulttable:
(from HTMLResultRenderMixin) renders an HTML table out of a ``result``
context object (this is what draws the table on default HTML results)
:resultline:
(from HTMLResultRenderMixin) renders the first line of the result
table in a key-value manner (this is sometimes used with the qp
renderer)
:parpair:
(from HTMLResultRenderMixin) expects a (key, value) pair in the
context and renders it as key:value in the current tag (this is used
to render the current parameters; to avoid showing lots of
non-informative lines, this will render nothing if value is None.
:ifresult:
(from HTMLResultRenderMixin) swallows the child tree if no result is
available
:ifnoresult:
(from HTMLResultRenderMixin) renders its content only if a result
table is available but contains no rows.
:result:
(from HTMLResultRenderMixin) a data function putting the current
result into the context. If no result is available (e.g., because no
query has been sent), ``None`` will be put into the context data.
:queryseq:
(from HTMLResultRenderMixin) a data function putting a sequence of
(key, value) query parameters into the context data.
:param:
(from HTMLResultRenderMixin) a parameterised renderer that takes a
python format string and formats the value of the param named in the
content with it, for instance::
rv
:resultmeta:
(from service.SvcResult) a data function on results (i.e., this
must be in an element with ``n:data="result"``) putting a dictionary
with at least the key ``itemsMatched`` into the context.
:inputRec:
(from service.SvcResult) a data function on results (i.e., this
must be in an element with ``n:data="result"``) putting a the
parameters dictionary of the inputs table (typcially, the input
parameters) into the context.
:table:
(from service.SvcResult) a data function on results (i.e., this
must be in an element with ``n:data="result"``) putting a the
result's primary table into the context
Overriding the Default Response Template
========================================
Sometimes you want to change something in the appearance of a service
that cannot be done with either service/@customCSS or the tricks
described in `How do I add an image to a query form?`_. You will then
need to override the defaultresponse template. As an example, you can
refer to `apfs/res/apfs_new RD`_; it has a custom template sitting next to
the RD as ``response.template``, which is declared as a custom service
template in the service element via::
res/response.template
(the path is relative to the RD's resdir).
What we wanted to do here is change then title depending on input
parameters (sometimes it's "apparent places", sometimes "intermediate
places", and I also wanted to have a warning if users chose a non-single
star for the ephemeris generated, as that is probably inaccurate.
A word of warning: Do not introduce a custom template lightly. We do
change ``defaultresponse.html`` occasionally, and you should probably
follow these changes in your templates. So, before you do a custom,
service-specific template, ask on the dachs-support mailing list if
someone knows a smarter solution to your problem.
In the APFS case, what made us use a custom template is teh varying
title; that's really hard to do in some other way because the title is
service metadata, and the service is persistent within DaCHS, possibly
even used concurrently by concurrent requests. Manipulating its
metadata per-request is therefore a bad idea.
So, what did we do? First, get the default response template to have
something to start with. We recommend to put it into a subdirectory
``/res``, so assuming you are in the RD's resdir, you'd do::
$ mkdir -p res
$ gavo admin dumpDF templates/defaultresponse.html > res/response.html
That done, you can place the template declaration above into the service
definition and start changing the template. Changes should be
immediately visible on reload.
To give you an impression of the interplay between the template and the
RD, here is a closer look at the `apfs/res/apfs_new RD`_ and the
associated template at
https://svn.ari.uni-heidelberg.de/svn/gavo/hdinputs/apfs/res/response.html
There, we first had to add a custom render function rendering the title
to the service::
# wants n:data="result"
if data.queryMeta.get("columnSet")==set(["equ"]):
denom = "Apparent Places"
elif data.queryMeta.get("columnSet")==set(["equ", "cio"]):
denom = "Apparent and Intermediate Places"
else:
denom = "Intermediate Places"
star = str(data.inputTable.getMeta("forStar"))
return ctx.tag[denom+" for star \catname "+star]
We're getting a parameter from ``queryMeta`` here, an ugly piece of
misdesign keeping lots of information on the request. Before you repeat
that stunt, ask on dachs-support, because there are most certainly
better ways do do that. The remaining part is a fairly straightforward
nevow renderer, perhaps except for the call of the catname macro, which
is here because in this particular case the render function is used by
two services and we've put it into a STREAM to allow reuse.
In comparison, the necessary changes in ``response.template`` are
minor::
is all that's needed. The template goes on saying::
ICRS position is α=,
δ=.
This displays the position of the star for which the ephemeris is
computed for. We do this here because the input to this service is
something like a catalog number, and alpha and delta are actually
computed by the service's input data descriptor.
Finally, when the result is in context, we do::
We would do this today by attaching a ``_warning`` meta item on the
result, but since that was not available when we wrote the RD, we added
a custom render function like this::
# wants n:data="result"
if data.inputTable.getMeta("multiple"):
return ctx.tag["Warning: This star"
" is identified by Hipparcos as being a ",
T.strong["multiple star."],
" This means that proper motions given in the Hipparcos"
" catalogue are unreliable when applied to extended periods"
" of time. Thus, the places given below will be severely"
" wrong unless the orbital period of the object is sufficiently"
" long (i.e., hundreds of years)."]
return ""
The ``multiple`` meta is put to the table further up in the computation.
Note how we leave as much as possible to the template; for instance, the
style is set there rather in the render function.
Custom Template, Custom Core
============================
For exotic, custom services you can combine a custom template with a
python (or custom) core. Here is a stripped-down example to get you
started; if you want to run this, you will have to install pyephem
(if you port this to astropy and share your port with us, you'll receive
good Karma).
First, the RD; the thing to watch out for is the creation of the output
table, getting the input params, and setting the output params. Note
that you'd normally generate table rows, which would be dicts added
through addRow::
response.html
1969-06-04
49.4294
11.00417
import ephem
res = rsc.TableForDef(self.outputTable)
n = ephem.Neptune(inputTable.getParam("for_date"))
obs = ephem.Observer()
obs.lat = inputTable.getParam("lat")*DEG
obs.long = inputTable.getParam("long")*DEG
obs.date = inputTable.getParam("for_date")
res.setParam("next_rising", obs.next_rising(n).datetime())
res.setParam("next_setting", obs.next_setting(n).datetime())
return res
And here's the template::
Nepune rising (and setting)
Neptune rising:
next_rising
Neptune setting:
next_setting
Suggested exercise: write two renderers such that you can write::
and get the rising time formatted the way you want it. If you get
stuck, ask back, and we'll provide a solution and a new exercise.
.. _How do I add an image to a query form?: http:///docs.g-vo.org/DaCHS/howDoI.html#add-an-image-to-query-forms
.. _Writing Examples: http://docs.g-vo.org/DaCHS/tutorial.html#writing-examples
.. _apfs/res/apfs_new RD: https://svn.ari.uni-heidelberg.de/svn/gavo/hdinputs/apfs/res/apfs_new.rd
.. [#dtd] One reason *not* to declare a DTD is that careless clients
other than web browsers (on system with misconfigured system
catalogs) might stumble across such a page and try do download the
DTD; the W3C was so pissed with clients doing that that they are now
severely slowing them down by delaying delivery of the DTDs, which
sometimes leads to surprising client behaviour.
.. |date| date::
.. _CC-0: http://creativecommons.org/publicdomain/zero/1.0