web/facet.py
changeset 9562 0509880fec01
parent 9492 c7fc56eecd1a
child 9892 928732ec00dd
child 9984 793377697c81
equal deleted inserted replaced
9561:3bdf85279c67 9562:0509880fec01
    32 .. autoclass:: cubicweb.web.facet.AttributeFacet
    32 .. autoclass:: cubicweb.web.facet.AttributeFacet
    33 .. autoclass:: cubicweb.web.facet.RQLPathFacet
    33 .. autoclass:: cubicweb.web.facet.RQLPathFacet
    34 .. autoclass:: cubicweb.web.facet.RangeFacet
    34 .. autoclass:: cubicweb.web.facet.RangeFacet
    35 .. autoclass:: cubicweb.web.facet.DateRangeFacet
    35 .. autoclass:: cubicweb.web.facet.DateRangeFacet
    36 .. autoclass:: cubicweb.web.facet.BitFieldFacet
    36 .. autoclass:: cubicweb.web.facet.BitFieldFacet
       
    37 .. autoclass:: cubicweb.web.facet.AbstractRangeRQLPathFacet
       
    38 .. autoclass:: cubicweb.web.facet.RangeRQLPathFacet
       
    39 .. autoclass:: cubicweb.web.facet.DateRangeRQLPathFacet
    37 
    40 
    38 Classes for facets implementor
    41 Classes for facets implementor
    39 ------------------------------
    42 ------------------------------
    40 Unless you didn't find the class that does the job you want above, you may want
    43 Unless you didn't find the class that does the job you want above, you may want
    41 to skip those classes...
    44 to skip those classes...
  1299                                              self.rtype,
  1302                                              self.rtype,
  1300                                              self.formatvalue(value),
  1303                                              self.formatvalue(value),
  1301                                              self.target_attr_type, operator)
  1304                                              self.target_attr_type, operator)
  1302 
  1305 
  1303 
  1306 
  1304 
       
  1305 class DateRangeFacet(RangeFacet):
  1307 class DateRangeFacet(RangeFacet):
  1306     """This class works similarly as the :class:`RangeFacet` but for attribute
  1308     """This class works similarly as the :class:`RangeFacet` but for attribute
  1307     of date type.
  1309     of date type.
  1308 
  1310 
  1309     The image below display the rendering of the slider for a date range:
  1311     The image below display the rendering of the slider for a date range:
  1321         try:
  1323         try:
  1322             date_value = ticks2datetime(float(value))
  1324             date_value = ticks2datetime(float(value))
  1323         except (ValueError, OverflowError):
  1325         except (ValueError, OverflowError):
  1324             return u'"date out-of-range"'
  1326             return u'"date out-of-range"'
  1325         return '"%s"' % ustrftime(date_value, '%Y/%m/%d')
  1327         return '"%s"' % ustrftime(date_value, '%Y/%m/%d')
       
  1328 
       
  1329 
       
  1330 class AbstractRangeRQLPathFacet(RQLPathFacet):
       
  1331     """
       
  1332     The :class:`AbstractRangeRQLPathFacet` is the base class for
       
  1333     RQLPathFacet-type facets allowing the use of RangeWidgets-like
       
  1334     widgets (such as (:class:`FacetRangeWidget`,
       
  1335     class:`DateFacetRangeWidget`) on the parent :class:`RQLPathFacet`
       
  1336     target attribute.
       
  1337     """
       
  1338     __abstract__ = True
       
  1339 
       
  1340     def vocabulary(self):
       
  1341         """return vocabulary for this facet, eg a list of (label,
       
  1342         value)"""
       
  1343         select = self.select
       
  1344         select.save_state()
       
  1345         try:
       
  1346             filtered_variable = self.filtered_variable
       
  1347             cleanup_select(select, filtered_variable)
       
  1348             varmap, restrvar = self.add_path_to_select()
       
  1349             if self.label_variable:
       
  1350                 attrvar = varmap[self.label_variable]
       
  1351             else:
       
  1352                 attrvar = restrvar
       
  1353             # start RangeRQLPathFacet
       
  1354             minf = nodes.Function('MIN')
       
  1355             minf.append(nodes.VariableRef(restrvar))
       
  1356             select.add_selected(minf)
       
  1357             maxf = nodes.Function('MAX')
       
  1358             maxf.append(nodes.VariableRef(restrvar))
       
  1359             select.add_selected(maxf)
       
  1360             # add is restriction if necessary
       
  1361             if filtered_variable.stinfo['typerel'] is None:
       
  1362                 etypes = frozenset(sol[filtered_variable.name] for sol in select.solutions)
       
  1363                 select.add_type_restriction(filtered_variable, etypes)
       
  1364             # end RangeRQLPathFacet
       
  1365             try:
       
  1366                 rset = self.rqlexec(select.as_string(), self.cw_rset.args)
       
  1367             except Exception:
       
  1368                 self.exception('error while getting vocabulary for %s, rql: %s',
       
  1369                                self, select.as_string())
       
  1370                 return ()
       
  1371         finally:
       
  1372             select.recover()
       
  1373         # don't call rset_vocabulary on empty result set, it may be an empty
       
  1374         # *list* (see rqlexec implementation)
       
  1375         if rset:
       
  1376             minv, maxv = rset[0]
       
  1377             return [(unicode(minv), minv), (unicode(maxv), maxv)]
       
  1378         return []
       
  1379 
       
  1380 
       
  1381     def possible_values(self):
       
  1382         """return a list of possible values (as string since it's used to
       
  1383         compare to a form value in javascript) for this facet
       
  1384         """
       
  1385         return [strval for strval, val in self.vocabulary()]
       
  1386 
       
  1387     def add_rql_restrictions(self):
       
  1388         infvalue = self.infvalue()
       
  1389         supvalue = self.supvalue()
       
  1390         if infvalue is None or supvalue is None: # nothing sent
       
  1391             return
       
  1392         varmap, restrvar = self.add_path_to_select(
       
  1393             skiplabel=True, skipattrfilter=True)
       
  1394         restrel = None
       
  1395         for part in self.path:
       
  1396             if isinstance(part, basestring):
       
  1397                 part = part.split()
       
  1398             subject, rtype, object = part
       
  1399             if object == self.filter_variable:
       
  1400                 restrel = rtype
       
  1401         assert restrel
       
  1402         # when a value is equal to one of the limit, don't add the restriction,
       
  1403         # else we filter out NULL values implicitly
       
  1404         if infvalue != self.infvalue(min=True):
       
  1405 
       
  1406             self._add_restriction(infvalue, '>=', restrvar, restrel)
       
  1407         if supvalue != self.supvalue(max=True):
       
  1408             self._add_restriction(supvalue, '<=', restrvar, restrel)
       
  1409 
       
  1410     def _add_restriction(self, value, operator, restrvar, restrel):
       
  1411         self.select.add_constant_restriction(restrvar,
       
  1412                                              restrel,
       
  1413                                              self.formatvalue(value),
       
  1414                                              self.target_attr_type, operator)
       
  1415 
       
  1416 
       
  1417 class RangeRQLPathFacet(AbstractRangeRQLPathFacet, RQLPathFacet):
       
  1418     """
       
  1419     The :class:`RangeRQLPathFacet` uses the :class:`FacetRangeWidget`
       
  1420     on the :class:`AbstractRangeRQLPathFacet` target attribute
       
  1421     """
       
  1422     pass
       
  1423 
       
  1424 
       
  1425 class DateRangeRQLPathFacet(AbstractRangeRQLPathFacet, DateRangeFacet):
       
  1426     """
       
  1427     The :class:`DateRangeRQLPathFacet` uses the
       
  1428     :class:`DateFacetRangeWidget` on the
       
  1429     :class:`AbstractRangeRQLPathFacet` target attribute
       
  1430     """
       
  1431     pass
  1326 
  1432 
  1327 
  1433 
  1328 class HasRelationFacet(AbstractFacet):
  1434 class HasRelationFacet(AbstractFacet):
  1329     """This class simply filter according to the presence of a relation
  1435     """This class simply filter according to the presence of a relation
  1330     (whatever the entity at the other end). It display a simple checkbox that
  1436     (whatever the entity at the other end). It display a simple checkbox that