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... |
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 |