web/views/sparql.py
author Sylvain Thénault <sylvain.thenault@logilab.fr>
Mon, 30 Nov 2009 10:24:01 +0100
branchstable
changeset 3947 8d06bce45c02
parent 3689 deb13e88e037
child 3720 5376aaadd16b
child 3953 19aefd78f61b
permissions -rw-r--r--
when one is adding an inline entity for a relation of a single card, the 'add a new xxx' link disappears. If the user then cancel the addition, we have to make this link appears back. This is done by giving add new link id to removeInlineForm.

"""SPARQL integration

:organization: Logilab
:copyright: 2009 LOGILAB S.A. (Paris, FRANCE), license is LGPL v2.
:contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
:license: GNU Lesser General Public License, v2.1 - http://www.gnu.org/licenses
"""
v__docformat__ = "restructuredtext en"

import rql
from yams import xy

from lxml import etree
from lxml.builder import E

from cubicweb.view import StartupView, AnyRsetView
from cubicweb.web import Redirect, form, formfields, formwidgets as fwdgs
from cubicweb.web.views import forms, urlrewrite
try:
    from cubicweb.spa2rql import Sparql2rqlTranslator
except ImportError:
    # fyzz not available (only a recommends)
    Sparql2rqlTranslator = None

class SparqlForm(forms.FieldsForm):
    id = 'sparql'
    sparql = formfields.StringField(help=_('type here a sparql query'))
    resultvid = formfields.StringField(choices=((_('table'), 'table'),
                                                (_('sparql xml'), 'sparqlxml')),
                                       widget=fwdgs.Radio,
                                       initial='table')
    form_buttons = [fwdgs.SubmitButton()]
    @property
    def action(self):
        return self.req.url()


class SparqlFormView(form.FormViewMixIn, StartupView):
    id = 'sparql'
    def call(self):
        form = self.vreg.select('forms', 'sparql', self.req)
        self.w(form.form_render())
        sparql = self.req.form.get('sparql')
        vid = self.req.form.get('resultvid', 'table')
        if sparql:
            try:
                qinfo = Sparql2rqlTranslator(self.schema).translate(sparql)
            except rql.TypeResolverException, ex:
                self.w(self.req._('can not resolve entity types:') + u' ' + unicode('ex'))
            except UnsupportedQuery:
                self.w(self.req._('we are not yet ready to handle this query'))
            except xy.UnsupportedVocabulary, ex:
                self.w(self.req._('unknown vocabulary:') + u' ' + unicode('ex'))
            if vid == 'sparqlxml':
                url = self.build_url('view', rql=qinfo.finalize(), vid=vid)
                raise Redirect(url)
            rset = self.req.execute(qinfo.finalize())
            self.wview(vid, rset, 'null')


## sparql resultset views #####################################################

YAMS_XMLSCHEMA_MAPPING = {
    'String': 'string',
    'Int': 'integer',
    'Float': 'float',
    'Boolean': 'boolean',
    'Datetime': 'dateTime',
    'Date': 'date',
    'Time': 'time',
    # XXX the following types don't have direct mapping
    'Decimal': 'string',
    'Interval': 'duration',
    'Password': 'string',
    'Bytes': 'base64Binary',
    }

def xmlschema(yamstype):
    return 'http://www.w3.org/2001/XMLSchema#%s' % YAMS_XMLSCHEMA_MAPPING[yamstype]

class SparqlResultXmlView(AnyRsetView):
    """The spec can be found here: http://www.w3.org/TR/rdf-sparql-XMLres/
    """
    id = 'sparqlxml'
    content_type = 'application/sparql-results+xml'
    templatable = False

    def call(self):
        # XXX handle UNION
        rqlst = self.rset.syntax_tree().children[0]
        varnames = [var.name for var in rqlst.selection]
        results = E.results()
        for rowidx in xrange(len(self.rset)):
            result = E.result()
            for colidx, varname in enumerate(varnames):
                result.append(self.cell_binding(rowidx, colidx, varname))
            results.append(result)
        sparql = E.sparql(E.head(*(E.variable(name=name) for name in varnames)),
                          results)
        self.w(u'<?xml version="1.0"?>\n')
        self.w(etree.tostring(sparql, encoding=unicode, pretty_print=True))

    def cell_binding(self, row, col, varname):
        celltype = self.rset.description[row][col]
        if self.schema.eschema(celltype).final:
            cellcontent = self.view('cell', self.rset, row=row, col=col)
            return E.binding(E.literal(cellcontent,
                                       datatype=xmlschema(celltype)),
                             name=varname)
        else:
            entity = self.entity(row, col)
            return E.binding(E.uri(entity.absolute_url()), name=varname)

    def set_request_content_type(self):
        """overriden to set the correct filetype and filename"""
        self.req.set_content_type(self.content_type,
                                  filename='sparql.xml',
                                  encoding=self.req.encoding)

def registration_callback(vreg):
    if Sparql2rqlTranslator is not None:
        vreg.register_all(globals().values(), __name__)