web/views/sparql.py
changeset 11057 0b59724cb3f2
parent 11052 058bb3dc685f
child 11058 23eb30449fe5
equal deleted inserted replaced
11052:058bb3dc685f 11057:0b59724cb3f2
     1 # copyright 2003-2011 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
       
     2 # contact http://www.logilab.fr/ -- mailto:contact@logilab.fr
       
     3 #
       
     4 # This file is part of CubicWeb.
       
     5 #
       
     6 # CubicWeb is free software: you can redistribute it and/or modify it under the
       
     7 # terms of the GNU Lesser General Public License as published by the Free
       
     8 # Software Foundation, either version 2.1 of the License, or (at your option)
       
     9 # any later version.
       
    10 #
       
    11 # CubicWeb is distributed in the hope that it will be useful, but WITHOUT
       
    12 # ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
       
    13 # FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more
       
    14 # details.
       
    15 #
       
    16 # You should have received a copy of the GNU Lesser General Public License along
       
    17 # with CubicWeb.  If not, see <http://www.gnu.org/licenses/>.
       
    18 """SPARQL integration"""
       
    19 
       
    20 __docformat__ = "restructuredtext en"
       
    21 from cubicweb import _
       
    22 
       
    23 from six.moves import range
       
    24 
       
    25 from yams import xy
       
    26 from rql import TypeResolverException
       
    27 
       
    28 from lxml import etree
       
    29 from lxml.builder import E
       
    30 
       
    31 from cubicweb.view import StartupView, AnyRsetView
       
    32 from cubicweb.web import Redirect, form, formfields, formwidgets as fwdgs
       
    33 from cubicweb.web.views import forms
       
    34 try:
       
    35     from cubicweb.spa2rql import Sparql2rqlTranslator, UnsupportedQuery
       
    36 except ImportError:
       
    37     # fyzz not available (only a recommends)
       
    38     Sparql2rqlTranslator = None
       
    39 
       
    40 class SparqlForm(forms.FieldsForm):
       
    41     __regid__ = 'sparql'
       
    42     sparql = formfields.StringField(help=_('type here a sparql query'))
       
    43     resultvid = formfields.StringField(choices=((_('table'), 'table'),
       
    44                                                 (_('sparql xml'), 'sparqlxml')),
       
    45                                        widget=fwdgs.Radio,
       
    46                                        value='table')
       
    47     form_buttons = [fwdgs.SubmitButton()]
       
    48     @property
       
    49     def action(self):
       
    50         return self._cw.url()
       
    51 
       
    52 
       
    53 class SparqlFormView(form.FormViewMixIn, StartupView):
       
    54     __regid__ = 'sparql'
       
    55     def call(self):
       
    56         form = self._cw.vreg['forms'].select('sparql', self._cw)
       
    57         form.render(w=self.w)
       
    58         sparql = self._cw.form.get('sparql')
       
    59         vid = self._cw.form.get('resultvid', 'table')
       
    60         if sparql:
       
    61             try:
       
    62                 qinfo = Sparql2rqlTranslator(self._cw.vreg.schema).translate(sparql)
       
    63             except TypeResolverException as exc:
       
    64                 self.w(self._cw._('can not resolve entity types:') + u' ' + unicode(exc))
       
    65             except UnsupportedQuery:
       
    66                 self.w(self._cw._('we are not yet ready to handle this query'))
       
    67             except xy.UnsupportedVocabulary as exc:
       
    68                 self.w(self._cw._('unknown vocabulary:') + u' ' + unicode(exc))
       
    69             else:
       
    70                 rql, args = qinfo.finalize()
       
    71                 if vid == 'sparqlxml':
       
    72                     url = self._cw.build_url('view', rql=rql % args, vid=vid)
       
    73                     raise Redirect(url)
       
    74                 rset = self._cw.execute(rql, args)
       
    75                 self.wview(vid, rset, 'null')
       
    76 
       
    77 
       
    78 ## sparql resultset views #####################################################
       
    79 
       
    80 YAMS_XMLSCHEMA_MAPPING = {
       
    81     'String': 'string',
       
    82 
       
    83     'Boolean': 'boolean',
       
    84     'Int': 'integer',
       
    85     'BigInt': 'integer',
       
    86     'Float': 'float',
       
    87 
       
    88     'Datetime': 'dateTime',
       
    89     'TZDatetime': 'dateTime',
       
    90     'Date': 'date',
       
    91     'Time': 'time',
       
    92     'TZTime': 'time',
       
    93 
       
    94     # XXX the following types don't have direct mapping
       
    95     'Decimal': 'string',
       
    96     'Interval': 'duration',
       
    97     'Bytes': 'base64Binary',
       
    98     'Password': 'string',
       
    99     }
       
   100 
       
   101 def xmlschema(yamstype):
       
   102     return 'http://www.w3.org/2001/XMLSchema#%s' % YAMS_XMLSCHEMA_MAPPING[yamstype]
       
   103 
       
   104 class SparqlResultXmlView(AnyRsetView):
       
   105     """The spec can be found here: http://www.w3.org/TR/rdf-sparql-XMLres/
       
   106     """
       
   107     __regid__ = 'sparqlxml'
       
   108     content_type = 'application/sparql-results+xml'
       
   109     templatable = False
       
   110 
       
   111     def call(self):
       
   112         # XXX handle UNION
       
   113         rqlst = self.cw_rset.syntax_tree().children[0]
       
   114         varnames = [var.name for var in rqlst.selection]
       
   115         results = E.results()
       
   116         for rowidx in range(len(self.cw_rset)):
       
   117             result = E.result()
       
   118             for colidx, varname in enumerate(varnames):
       
   119                 result.append(self.cell_binding(rowidx, colidx, varname))
       
   120             results.append(result)
       
   121         sparql = E.sparql(E.head(*(E.variable(name=name) for name in varnames)),
       
   122                           results)
       
   123         self.w(u'<?xml version="1.0"?>\n')
       
   124         self.w(etree.tostring(sparql, encoding=unicode, pretty_print=True))
       
   125 
       
   126     def cell_binding(self, row, col, varname):
       
   127         celltype = self.cw_rset.description[row][col]
       
   128         if self._cw.vreg.schema.eschema(celltype).final:
       
   129             cellcontent = self._cw.view('cell', self.cw_rset, row=row, col=col)
       
   130             return E.binding(E.literal(cellcontent,
       
   131                                        datatype=xmlschema(celltype)),
       
   132                              name=varname)
       
   133         else:
       
   134             entity = self.cw_rset.get_entity(row, col)
       
   135             return E.binding(E.uri(entity.absolute_url()), name=varname)
       
   136 
       
   137     def set_request_content_type(self):
       
   138         """overriden to set the correct filetype and filename"""
       
   139         self._cw.set_content_type(self.content_type,
       
   140                                   filename='sparql.xml',
       
   141                                   encoding=self._cw.encoding)
       
   142 
       
   143 def registration_callback(vreg):
       
   144     if Sparql2rqlTranslator is not None:
       
   145         vreg.register_all(globals().values(), __name__)