267 specified |
267 specified |
268 |
268 |
269 When there are several classes to be evaluated, return the sum of scores for |
269 When there are several classes to be evaluated, return the sum of scores for |
270 each entity class unless: |
270 each entity class unless: |
271 |
271 |
272 - `once_is_enough` is False (the default) and some entity class is scored |
272 - `mode` == 'all' (the default) and some entity class is scored |
273 to 0, in which case 0 is returned |
273 to 0, in which case 0 is returned |
274 |
274 |
275 - `once_is_enough` is True, in which case the first non-zero score is |
275 - `mode` == 'any', in which case the first non-zero score is |
276 returned |
276 returned |
277 |
277 |
278 - `accept_none` is False and some cell in the column has a None value |
278 - `accept_none` is False and some cell in the column has a None value |
279 (this may occurs with outer join) |
279 (this may occurs with outer join) |
280 """ |
280 """ |
281 def __init__(self, once_is_enough=False, accept_none=True): |
281 def __init__(self, once_is_enough=None, accept_none=True, mode='all'): |
282 self.once_is_enough = once_is_enough |
282 if once_is_enough is not None: |
|
283 warn("[3.14] once_is_enough is deprecated, use mode='any'", |
|
284 DeprecationWarning, stacklevel=2) |
|
285 if once_is_enough: |
|
286 mode = 'any' |
|
287 assert mode in ('any', 'all'), 'bad mode %s' % mode |
|
288 self.once_is_enough = mode == 'any' |
283 self.accept_none = accept_none |
289 self.accept_none = accept_none |
284 |
290 |
285 @lltrace |
291 @lltrace |
286 def __call__(self, cls, req, rset=None, row=None, col=0, entity=None, |
292 def __call__(self, cls, req, rset=None, row=None, col=0, entity=None, |
287 select=None, filtered_variable=None, |
293 select=None, filtered_variable=None, |
399 |
405 |
400 class ExpectedValueSelector(Selector): |
406 class ExpectedValueSelector(Selector): |
401 """Take a list of expected values as initializer argument and store them |
407 """Take a list of expected values as initializer argument and store them |
402 into the :attr:`expected` set attribute. You may also give a set as single |
408 into the :attr:`expected` set attribute. You may also give a set as single |
403 argument, which will be then be referenced as set of expected values, |
409 argument, which will be then be referenced as set of expected values, |
404 allowing modification to the given set to be considered. |
410 allowing modifications to the given set to be considered. |
405 |
411 |
406 You should implement the :meth:`_get_value(cls, req, **kwargs)` method |
412 You should implement one of :meth:`_values_set(cls, req, **kwargs)` or |
407 which should return the value for the given context. The selector will then |
413 :meth:`_get_value(cls, req, **kwargs)` method which should respectivly |
408 return 1 if the value is expected, else 0. |
414 return the set of values or the unique possible value for the given context. |
409 """ |
415 |
410 def __init__(self, *expected): |
416 You may also specify a `mode` behaviour as argument, as explained below. |
|
417 |
|
418 Returned score is: |
|
419 |
|
420 - 0 if `mode` == 'all' (the default) and at least one expected |
|
421 values isn't found |
|
422 |
|
423 - 0 if `mode` == 'any' and no expected values isn't found at all |
|
424 |
|
425 - else the number of matching values |
|
426 |
|
427 Notice `mode`='any' with a single expected value has no effect at all. |
|
428 """ |
|
429 def __init__(self, *expected, **kwargs): |
411 assert expected, self |
430 assert expected, self |
412 if len(expected) == 1 and isinstance(expected[0], set): |
431 if len(expected) == 1 and isinstance(expected[0], set): |
413 self.expected = expected[0] |
432 self.expected = expected[0] |
414 else: |
433 else: |
415 self.expected = frozenset(expected) |
434 self.expected = frozenset(expected) |
|
435 mode = kwargs.pop('mode', 'all') |
|
436 assert mode in ('any', 'all'), 'bad mode %s' % mode |
|
437 self.once_is_enough = mode == 'any' |
|
438 assert not kwargs, 'unexpected arguments %s' % kwargs |
416 |
439 |
417 def __str__(self): |
440 def __str__(self): |
418 return '%s(%s)' % (self.__class__.__name__, |
441 return '%s(%s)' % (self.__class__.__name__, |
419 ','.join(sorted(str(s) for s in self.expected))) |
442 ','.join(sorted(str(s) for s in self.expected))) |
420 |
443 |
421 @lltrace |
444 @lltrace |
422 def __call__(self, cls, req, **kwargs): |
445 def __call__(self, cls, req, **kwargs): |
423 if self._get_value(cls, req, **kwargs) in self.expected: |
446 values = self._values_set(cls, req, **kwargs) |
424 return 1 |
447 matching = len(values & self.expected) |
|
448 if self.once_is_enough: |
|
449 return matching |
|
450 if matching == len(self.expected): |
|
451 return matching |
425 return 0 |
452 return 0 |
|
453 |
|
454 def _values_set(self, cls, req, **kwargs): |
|
455 return frozenset( (self._get_value(cls, req, **kwargs),) ) |
426 |
456 |
427 def _get_value(self, cls, req, **kwargs): |
457 def _get_value(self, cls, req, **kwargs): |
428 raise NotImplementedError() |
458 raise NotImplementedError() |
429 |
459 |
430 |
460 |
431 # bare selectors ############################################################## |
461 # bare selectors ############################################################## |
432 |
462 |
433 class match_kwargs(ExpectedValueSelector): |
463 class match_kwargs(ExpectedValueSelector): |
434 """Return non-zero score if parameter names specified as initializer |
464 """Return non-zero score if parameter names specified as initializer |
435 arguments are specified in the input context. When multiple parameters are |
465 arguments are specified in the input context. |
436 specified, all of them should be specified in the input context. Return a |
466 |
437 score corresponding to the number of expected parameters. |
467 |
438 """ |
468 Return a score corresponding to the number of expected parameters. |
439 |
469 |
440 @lltrace |
470 When multiple parameters are expected, all of them should be found in |
441 def __call__(self, cls, req, **kwargs): |
471 the input context unless `mode` keyword argument is given to 'any', |
442 for arg in self.expected: |
472 in which case a single matching parameter is enough. |
443 if not arg in kwargs: |
473 """ |
444 return 0 |
474 |
445 return len(self.expected) |
475 def _values_set(self, cls, req, **kwargs): |
|
476 return frozenset(kwargs) |
446 |
477 |
447 |
478 |
448 class appobject_selectable(Selector): |
479 class appobject_selectable(Selector): |
449 """Return 1 if another appobject is selectable using the same input context. |
480 """Return 1 if another appobject is selectable using the same input context. |
450 |
481 |
840 value at the end. |
871 value at the end. |
841 |
872 |
842 See :class:`~cubicweb.selectors.EntitySelector` documentation for entity |
873 See :class:`~cubicweb.selectors.EntitySelector` documentation for entity |
843 lookup / score rules according to the input context. |
874 lookup / score rules according to the input context. |
844 """ |
875 """ |
845 def __init__(self, scorefunc, once_is_enough=False): |
876 def __init__(self, scorefunc, once_is_enough=None, mode='all'): |
846 super(score_entity, self).__init__(once_is_enough) |
877 super(score_entity, self).__init__(mode=mode, once_is_enough=once_is_enough) |
847 def intscore(*args, **kwargs): |
878 def intscore(*args, **kwargs): |
848 score = scorefunc(*args, **kwargs) |
879 score = scorefunc(*args, **kwargs) |
849 if not score: |
880 if not score: |
850 return 0 |
881 return 0 |
851 if isinstance(score, (int, long)): |
882 if isinstance(score, (int, long)): |
858 """Return 1 if the entity adapt to IDownloadable and has the given MIME type. |
889 """Return 1 if the entity adapt to IDownloadable and has the given MIME type. |
859 |
890 |
860 You can give 'image/' to match any image for instance, or 'image/png' to match |
891 You can give 'image/' to match any image for instance, or 'image/png' to match |
861 only PNG images. |
892 only PNG images. |
862 """ |
893 """ |
863 def __init__(self, mimetype, once_is_enough=False): |
894 def __init__(self, mimetype, once_is_enough=None, mode='all'): |
864 super(has_mimetype, self).__init__(once_is_enough) |
895 super(has_mimetype, self).__init__(mode=mode, once_is_enough=once_is_enough) |
865 self.mimetype = mimetype |
896 self.mimetype = mimetype |
866 |
897 |
867 def score_entity(self, entity): |
898 def score_entity(self, entity): |
868 idownloadable = entity.cw_adapt_to('IDownloadable') |
899 idownloadable = entity.cw_adapt_to('IDownloadable') |
869 if idownloadable is None: |
900 if idownloadable is None: |
1171 benefit from the ORM's request entities cache. |
1202 benefit from the ORM's request entities cache. |
1172 |
1203 |
1173 See :class:`~cubicweb.selectors.EntitySelector` documentation for entity |
1204 See :class:`~cubicweb.selectors.EntitySelector` documentation for entity |
1174 lookup / score rules according to the input context. |
1205 lookup / score rules according to the input context. |
1175 """ |
1206 """ |
1176 def __init__(self, expression, once_is_enough=False, user_condition=False): |
1207 def __init__(self, expression, once_is_enough=None, mode='all', user_condition=False): |
1177 super(rql_condition, self).__init__(once_is_enough) |
1208 super(rql_condition, self).__init__(mode=mode, once_is_enough=once_is_enough) |
1178 self.user_condition = user_condition |
1209 self.user_condition = user_condition |
1179 if user_condition: |
1210 if user_condition: |
1180 rql = 'Any COUNT(U) WHERE U eid %%(u)s, %s' % expression |
1211 rql = 'Any COUNT(U) WHERE U eid %%(u)s, %s' % expression |
1181 elif 'U' in frozenset(split_expression(expression)): |
1212 elif 'U' in frozenset(split_expression(expression)): |
1182 rql = 'Any COUNT(X) WHERE X eid %%(x)s, U eid %%(u)s, %s' % expression |
1213 rql = 'Any COUNT(X) WHERE X eid %%(x)s, U eid %%(u)s, %s' % expression |
1472 return 1 |
1500 return 1 |
1473 |
1501 |
1474 |
1502 |
1475 class match_form_params(ExpectedValueSelector): |
1503 class match_form_params(ExpectedValueSelector): |
1476 """Return non-zero score if parameter names specified as initializer |
1504 """Return non-zero score if parameter names specified as initializer |
1477 arguments are specified in request's form parameters. When multiple |
1505 arguments are specified in request's form parameters. |
1478 parameters are specified, all of them should be found in req.form. Return a |
1506 |
1479 score corresponding to the number of expected parameters. |
1507 Return a score corresponding to the number of expected parameters. |
1480 """ |
1508 |
1481 |
1509 When multiple parameters are expected, all of them should be found in |
1482 @lltrace |
1510 the input context unless `mode` keyword argument is given to 'any', |
1483 def __call__(self, cls, req, **kwargs): |
1511 in which case a single matching parameter is enough. |
1484 for param in self.expected: |
1512 """ |
1485 if not param in req.form: |
1513 |
1486 return 0 |
1514 def _values_set(self, cls, req, **kwargs): |
1487 return len(self.expected) |
1515 return frozenset(req.form) |
1488 |
1516 |
1489 |
1517 |
1490 class specified_etype_implements(is_instance): |
1518 class specified_etype_implements(is_instance): |
1491 """Return non-zero score if the entity type specified by an 'etype' key |
1519 """Return non-zero score if the entity type specified by an 'etype' key |
1492 searched in (by priority) input context kwargs and request form parameters |
1520 searched in (by priority) input context kwargs and request form parameters |
1535 which will not score at edit time:: |
1563 which will not score at edit time:: |
1536 |
1564 |
1537 is_instance('Version') & (match_transition('ready') | |
1565 is_instance('Version') & (match_transition('ready') | |
1538 attribute_edited('publication_date')) |
1566 attribute_edited('publication_date')) |
1539 """ |
1567 """ |
1540 def __init__(self, attribute, once_is_enough=False): |
1568 def __init__(self, attribute, once_is_enough=None, mode='all'): |
1541 super(attribute_edited, self).__init__(once_is_enough) |
1569 super(attribute_edited, self).__init__(mode=mode, once_is_enough=once_is_enough) |
1542 self._attribute = attribute |
1570 self._attribute = attribute |
1543 |
1571 |
1544 def score_entity(self, entity): |
1572 def score_entity(self, entity): |
1545 return eid_param(role_name(self._attribute, 'subject'), entity.eid) in entity._cw.form |
1573 return eid_param(role_name(self._attribute, 'subject'), entity.eid) in entity._cw.form |
1546 |
1574 |
1547 |
1575 |
1548 # Other selectors ############################################################## |
1576 # Other selectors ############################################################## |
1549 |
1577 |
1550 |
|
1551 class match_exception(ExpectedValueSelector): |
1578 class match_exception(ExpectedValueSelector): |
1552 """Return 1 if a view is specified an as its registry id is in one of the |
1579 """Return 1 if exception given as `exc` in the input context is an instance |
1553 expected view id given to the initializer. |
1580 of one of the class given on instanciation of this predicate. |
1554 """ |
1581 """ |
1555 def __init__(self, *expected): |
1582 def __init__(self, *expected): |
1556 assert expected, self |
1583 assert expected, self |
|
1584 # we want a tuple, not a set as done in the parent class |
1557 self.expected = expected |
1585 self.expected = expected |
1558 |
1586 |
1559 @lltrace |
1587 @lltrace |
1560 def __call__(self, cls, req, exc=None, **kwargs): |
1588 def __call__(self, cls, req, exc=None, **kwargs): |
1561 if exc is not None and isinstance(exc, self.expected): |
1589 if exc is not None and isinstance(exc, self.expected): |