Source code for gavo.web.qprenderer

"""
A renderer that queries a single field in a service.
"""

#c Copyright 2008-2023, the GAVO project <gavo@ari.uni-heidelberg.de>
#c
#c This program is free software, covered by the GNU GPL.  See the
#c COPYING file in the source distribution.


from twisted.web import resource
from twisted.web import server
from twisted.web.template import tags as T

from gavo import base
from gavo import formal
from gavo import svcs
from gavo.svcs import streaming
from gavo.web import common
from gavo.web import grend
from gavo.web import weberrors


NT = formal.addNevowAttributes


[docs]class VOTableResource(resource.Resource): # A quick hack to support VOTable responses. # Kill this in favour of serviceresults. def __init__(self, result, queryMeta): resource.Resource.__init__(self) self.result, self.queryMeta = result, queryMeta
[docs] def render(self, request): if base.getMetaText(self.result.getPrimaryTable(), "_queryStatus" )=="OVERFLOW": fName = "truncated_votable.xml" else: fName = "votable.xml" request.setHeader("content-type", base.votableType) request.setHeader('content-disposition', 'attachment; filename=%s'%fName) return streaming.streamVOTable(request, self.result, self.queryMeta)
[docs]class QPRenderer(grend.HTMLResultRenderMixin, grend.CustomTemplateMixin, grend.ServiceBasedPage): """The Query Path renderer extracts a query argument from the query path. Basically, whatever segments are left after the path to the renderer are taken and fed into the service. The service must cooperate by setting a queryField property which is the key the parameter is assigned to. QPRenderers cannot do forms, of course, but they can nicely share a service with the form renderer. To adjust the results' appreance, you can override resultline (for when there's just one result row) and resulttable (for when there is more than one result row) templates. """ name = "qp" queryValue = None defaultQueryFormat = "HTML"
[docs] @classmethod def isCacheable(self, segments, request): return False # That's the default, but let's be sure here...
[docs] def render(self, request): if not self.queryValue: raise svcs.UnknownURI("This page is a root page for a" " query-based service. You have to give a valid value in the" " path.") args = request.strargs.copy() args[self.service.getProperty("queryField")] = [self.queryValue] self.runAsync(args ).addCallback(self._formatOutput, request ).addErrback(self._handleError, request ).addErrback(weberrors.renderDCErrorPage, request) return server.NOT_DONE_YET
def _formatOutput(self, res, request): self.result = res # Hm... can we get rid of this hack? Just support proper RESPONSEFORMAT, # perhaps? if "vot" in self.queryMeta.get("format", "").lower(): return VOTableResource(self.result, self.queryMeta).render(request) # Hm... I suppose this and the corresponding stuff in # UnifiedDALRenderer should be merged. if isinstance(self.result, tuple): # core returned a complete document (mime and string) mime, payload = self.result request.setHeader("content-type", mime) return streaming.streamOut( lambda f: f.write(payload), request, self.queryMeta) nMatched = self.queryMeta.get("Matched") if nMatched==0: raise svcs.UnknownURI("No record matching %s."%( self.queryValue)) elif nMatched==1: self.customTemplate = self.getTemplate("resultline") else: self.customTemplate = self.getTemplate("resulttable") return super(QPRenderer, self).render(request) def _handleError(self, flr, request): # all errors except svcs.Errors (which are handled by weberror) # are translated to 404s (and logged). if isinstance(flr.value, base.ExecutiveAction): return flr base.ui.notifyFailure(flr, "A core raised '%s' into a qp renderer." " Translating into a 404."%(flr.value)) raise svcs.UnknownURI("The query initiated by your URL failed," " yielding a message '%s'."%flr.getErrorMessage())
[docs] def getChild(self, name, request): # if we're here, we are the responsible resource and just stuff # the remaining segments into the query value self.queryValue = "/".join(request.popSegments(name)) return self
[docs] def getTemplate(self, resultFormat): if resultFormat in self.service.templates: return self.service.getTemplate(resultFormat) return common.doctypedStan( T.html[ T.head(render="commonhead")[ T.title(render="meta")['title'], T.style(type="text/css", render="servicestyle"),], T.body(render="withsidebar")[ T.h1(render="meta")['title'], NT(T.div, data="result")(class_="result") [ T.transparent(render=resultFormat)]]])