changeset 7618 | 5395007c415c |
parent 7615 | e5ad51352eb3 |
child 7624 | ce020f90fb8e |
7617:be5f68f9314e | 7618:5395007c415c |
---|---|
28 -------------------------- |
28 -------------------------- |
29 .. autoclass:: cubicweb.web.facet.RelationFacet |
29 .. autoclass:: cubicweb.web.facet.RelationFacet |
30 .. autoclass:: cubicweb.web.facet.RelationAttributeFacet |
30 .. autoclass:: cubicweb.web.facet.RelationAttributeFacet |
31 .. autoclass:: cubicweb.web.facet.HasRelationFacet |
31 .. autoclass:: cubicweb.web.facet.HasRelationFacet |
32 .. autoclass:: cubicweb.web.facet.AttributeFacet |
32 .. autoclass:: cubicweb.web.facet.AttributeFacet |
33 .. autoclass:: cubicweb.web.facet.RQLPathFacet |
|
33 .. autoclass:: cubicweb.web.facet.RangeFacet |
34 .. autoclass:: cubicweb.web.facet.RangeFacet |
34 .. autoclass:: cubicweb.web.facet.DateRangeFacet |
35 .. autoclass:: cubicweb.web.facet.DateRangeFacet |
35 |
36 |
36 Classes for facets implementor |
37 Classes for facets implementor |
37 ------------------------------ |
38 ------------------------------ |
148 * add the new variable to GROUPBY clause if necessary |
149 * add the new variable to GROUPBY clause if necessary |
149 * add the new variable to the selection |
150 * add the new variable to the selection |
150 """ |
151 """ |
151 newvar = _add_rtype_relation(select, filtered_variable, rtype, role)[0] |
152 newvar = _add_rtype_relation(select, filtered_variable, rtype, role)[0] |
152 if select_target_entity: |
153 if select_target_entity: |
153 if select.groupby: |
154 # if select.groupby: XXX we remove groupby now |
154 select.add_group_var(newvar) |
155 # select.add_group_var(newvar) |
155 select.add_selected(newvar) |
156 select.add_selected(newvar) |
156 # add is restriction if necessary |
157 # add is restriction if necessary |
157 if filtered_variable.stinfo['typerel'] is None: |
158 if filtered_variable.stinfo['typerel'] is None: |
158 etypes = frozenset(sol[filtered_variable.name] for sol in select.solutions) |
159 etypes = frozenset(sol[filtered_variable.name] for sol in select.solutions) |
159 select.add_type_restriction(filtered_variable, etypes) |
160 select.add_type_restriction(filtered_variable, etypes) |
298 """ |
299 """ |
299 newvar, newrel = _make_relation(select, variable, rtype, role) |
300 newvar, newrel = _make_relation(select, variable, rtype, role) |
300 select.add_restriction(newrel) |
301 select.add_restriction(newrel) |
301 return newvar, newrel |
302 return newvar, newrel |
302 |
303 |
303 def _add_eid_restr(rel, restrvar, value): |
|
304 rrel = nodes.make_constant_restriction(restrvar, 'eid', value, 'Int') |
|
305 rel.parent.replace(rel, nodes.And(rel, rrel)) |
|
306 |
|
307 def _remove_relation(select, rel, var): |
304 def _remove_relation(select, rel, var): |
308 """remove a constraint relation from the syntax tree""" |
305 """remove a constraint relation from the syntax tree""" |
309 # remove the relation |
306 # remove the relation |
310 select.remove_node(rel) |
307 select.remove_node(rel) |
311 # remove relations where the filtered variable appears on the |
308 # remove relations where the filtered variable appears on the |
328 vref.register_reference() |
325 vref.register_reference() |
329 sortfunc = nodes.Function(sortfuncname) |
326 sortfunc = nodes.Function(sortfuncname) |
330 sortfunc.append(vref) |
327 sortfunc.append(vref) |
331 term = nodes.SortTerm(sortfunc, sortasc) |
328 term = nodes.SortTerm(sortfunc, sortasc) |
332 select.add_sort_term(term) |
329 select.add_sort_term(term) |
330 |
|
331 def _get_var(select, varname, varmap): |
|
332 try: |
|
333 return varmap[varname] |
|
334 except KeyError: |
|
335 varmap[varname] = var = select.make_variable() |
|
336 return var |
|
333 |
337 |
334 |
338 |
335 _prepare_vocabulary_rqlst = deprecated('[3.13] renamed prepare_vocabulary_select')( |
339 _prepare_vocabulary_rqlst = deprecated('[3.13] renamed prepare_vocabulary_select')( |
336 prepare_vocabulary_select) |
340 prepare_vocabulary_select) |
337 _cleanup_rqlst = deprecated('[3.13] renamed to cleanup_select')(cleanup_select) |
341 _cleanup_rqlst = deprecated('[3.13] renamed to cleanup_select')(cleanup_select) |
402 self.filtered_variable = filtered_variable |
406 self.filtered_variable = filtered_variable |
403 |
407 |
404 def __repr__(self): |
408 def __repr__(self): |
405 return '<%s>' % self.__class__.__name__ |
409 return '<%s>' % self.__class__.__name__ |
406 |
410 |
411 def get_widget(self): |
|
412 """Return the widget instance to use to display this facet, or None if |
|
413 the facet can't do anything valuable (only one value in the vocabulary |
|
414 for instance). |
|
415 """ |
|
416 raise NotImplementedError |
|
417 |
|
418 def add_rql_restrictions(self): |
|
419 """When some facet criteria has been updated, this method is called to |
|
420 add restriction for this facet into the rql syntax tree. It should get |
|
421 back its value in form parameters, and modify the syntax tree |
|
422 (`self.select`) accordingly. |
|
423 """ |
|
424 raise NotImplementedError |
|
425 |
|
407 @property |
426 @property |
408 def operator(self): |
427 def operator(self): |
409 """Return the operator (AND or OR) to use for this facet when multiple |
428 """Return the operator (AND or OR) to use for this facet when multiple |
410 values are selected. |
429 values are selected. |
411 """ |
430 """ |
418 """ |
437 """ |
419 try: |
438 try: |
420 return self._cw.execute(rql, args) |
439 return self._cw.execute(rql, args) |
421 except Unauthorized: |
440 except Unauthorized: |
422 return [] |
441 return [] |
423 |
|
424 def get_widget(self): |
|
425 """Return the widget instance to use to display this facet, or None if |
|
426 the facet can't do anything valuable (only one value in the vocabulary |
|
427 for instance). |
|
428 """ |
|
429 raise NotImplementedError |
|
430 |
|
431 def add_rql_restrictions(self): |
|
432 """When some facet criteria has been updated, this method is called to |
|
433 add restriction for this facet into the rql syntax tree. It should get |
|
434 back its value in form parameters, and modify the syntax tree |
|
435 (`self.select`) accordingly. |
|
436 """ |
|
437 raise NotImplementedError |
|
438 |
442 |
439 @property |
443 @property |
440 def wdgclass(self): |
444 def wdgclass(self): |
441 raise NotImplementedError |
445 raise NotImplementedError |
442 |
446 |
513 |
517 |
514 When no `label_vid` is set, you will get translated value if `i18nable` is |
518 When no `label_vid` is set, you will get translated value if `i18nable` is |
515 set. By default, `i18nable` will be set according to the schema, but you can |
519 set. By default, `i18nable` will be set according to the schema, but you can |
516 force its value by setting it has a class attribute. |
520 force its value by setting it has a class attribute. |
517 |
521 |
518 You can filter out target entity types by specifying `target_type` |
522 You can filter out target entity types by specifying `target_type`. |
519 |
523 |
520 By default, vocabulary will be displayed sorted on `target_attr` value in an |
524 By default, vocabulary will be displayed sorted on `target_attr` value in an |
521 ascending way. You can control sorting with: |
525 ascending way. You can control sorting with: |
522 |
526 |
523 * `sortfunc`: set this to a stored procedure name if you want to sort on the |
527 * `sortfunc`: set this to a stored procedure name if you want to sort on the |
559 """ |
563 """ |
560 __select__ = partial_relation_possible() & match_context_prop() |
564 __select__ = partial_relation_possible() & match_context_prop() |
561 # class attributes to configure the relation facet |
565 # class attributes to configure the relation facet |
562 rtype = None |
566 rtype = None |
563 role = 'subject' |
567 role = 'subject' |
568 target_type = None |
|
564 target_attr = 'eid' |
569 target_attr = 'eid' |
565 target_type = None |
570 # for subclasses parametrization, should not change if you want a |
571 # RelationFacet |
|
572 target_attr_type = 'Int' |
|
573 restr_attr = 'eid' |
|
574 restr_attr_type = 'Int' |
|
575 comparator = '=' # could be '<', '<=', '>', '>=' |
|
566 # set this to a stored procedure name if you want to sort on the result of |
576 # set this to a stored procedure name if you want to sort on the result of |
567 # this function's result instead of direct value |
577 # this function's result instead of direct value |
568 sortfunc = None |
578 sortfunc = None |
569 # ascendant/descendant sorting |
579 # ascendant/descendant sorting |
570 sortasc = True |
580 sortasc = True |
598 self._select_target_entity) |
608 self._select_target_entity) |
599 if self.target_type is not None: |
609 if self.target_type is not None: |
600 select.add_type_restriction(var, self.target_type) |
610 select.add_type_restriction(var, self.target_type) |
601 try: |
611 try: |
602 rset = self.rqlexec(select.as_string(), self.cw_rset.args) |
612 rset = self.rqlexec(select.as_string(), self.cw_rset.args) |
603 except: |
613 except Exception: |
604 self.exception('error while getting vocabulary for %s, rql: %s', |
614 self.exception('error while getting vocabulary for %s, rql: %s', |
605 self, select.as_string()) |
615 self, select.as_string()) |
606 return () |
616 return () |
607 finally: |
617 finally: |
608 select.recover() |
618 select.recover() |
624 if self._select_target_entity: |
634 if self._select_target_entity: |
625 prepare_vocabulary_select(select, self.filtered_variable, self.rtype, |
635 prepare_vocabulary_select(select, self.filtered_variable, self.rtype, |
626 self.role, select_target_entity=True) |
636 self.role, select_target_entity=True) |
627 else: |
637 else: |
628 insert_attr_select_relation( |
638 insert_attr_select_relation( |
629 select, self.filtered_variable, self.rtype, self.role, self.target_attr, |
639 select, self.filtered_variable, self.rtype, self.role, |
630 select_target_entity=False) |
640 self.target_attr, select_target_entity=False) |
631 values = [unicode(x) for x, in self.rqlexec(select.as_string())] |
641 values = [unicode(x) for x, in self.rqlexec(select.as_string())] |
632 except: |
642 except Exception: |
633 self.exception('while computing values for %s', self) |
643 self.exception('while computing values for %s', self) |
634 return [] |
644 return [] |
635 finally: |
645 finally: |
636 select.recover() |
646 select.recover() |
637 if self._include_no_relation(): |
647 if self._include_no_relation(): |
706 DeprecationWarning) |
716 DeprecationWarning) |
707 support = support() |
717 support = support() |
708 return support |
718 return support |
709 |
719 |
710 def value_restriction(self, restrvar, rel, value): |
720 def value_restriction(self, restrvar, rel, value): |
721 if self.restr_attr != 'eid': |
|
722 self.select.set_distinct(True) |
|
711 if isinstance(value, basestring): |
723 if isinstance(value, basestring): |
712 # only one value selected |
724 # only one value selected |
713 if value: |
725 if value: |
714 self.select.add_eid_restriction(restrvar, value) |
726 self.select.add_constant_restriction( |
727 restrvar, self.restr_attr, value, |
|
728 self.restr_attr_type) |
|
715 else: |
729 else: |
716 rel.parent.replace(rel, nodes.Not(rel)) |
730 rel.parent.replace(rel, nodes.Not(rel)) |
717 elif self.operator == 'OR': |
731 elif self.operator == 'OR': |
718 # set_distinct only if rtype cardinality is > 1 |
732 # set_distinct only if rtype cardinality is > 1 |
719 if self._support_and_compat(): |
733 if self._support_and_compat(): |
720 self.select.set_distinct(True) |
734 self.select.set_distinct(True) |
721 # multiple ORed values: using IN is fine |
735 # multiple ORed values: using IN is fine |
722 if '' in value: |
736 if '' in value: |
723 value.remove('') |
737 value.remove('') |
724 self._add_not_rel_restr(rel) |
738 self._add_not_rel_restr(rel) |
725 _add_eid_restr(rel, restrvar, value) |
739 self._and_restriction(rel, restrvar, value) |
726 else: |
740 else: |
727 # multiple values with AND operator |
741 # multiple values with AND operator |
728 if '' in value: |
742 if '' in value: |
729 value.remove('') |
743 value.remove('') |
730 self._add_not_rel_restr(rel) |
744 self._add_not_rel_restr(rel) |
731 _add_eid_restr(rel, restrvar, value.pop()) |
745 self._and_restriction(rel, restrvar, value.pop()) |
732 while value: |
746 while value: |
733 restrvar, rtrel = _make_relation(self.select, filtered_variable, |
747 restrvar, rtrel = _make_relation(self.select, filtered_variable, |
734 self.rtype, self.role) |
748 self.rtype, self.role) |
735 _add_eid_restr(rel, restrvar, value.pop()) |
749 self._and_restriction(rel, restrvar, value.pop()) |
750 |
|
751 def _and_restriction(self, rel, restrvar, value): |
|
752 rrel = nodes.make_constant_restriction(restrvar, self.restr_attr, |
|
753 value, self.restr_attr_type) |
|
754 rel.parent.replace(rel, nodes.And(rel, rrel)) |
|
755 |
|
736 |
756 |
737 @cached |
757 @cached |
738 def _search_card(self, cards): |
758 def _search_card(self, cards): |
739 for rdef in self._iter_rdefs(): |
759 for rdef in self._iter_rdefs(): |
740 if rdef.role_cardinality(self.role) in cards: |
760 if rdef.role_cardinality(self.role) in cards: |
785 restrictions = '' |
805 restrictions = '' |
786 rql = 'Any %s LIMIT 1 WHERE NOT %s %s %s%s' % ( |
806 rql = 'Any %s LIMIT 1 WHERE NOT %s %s %s%s' % ( |
787 self.filtered_variable.name, subj, self.rtype, obj, restrictions) |
807 self.filtered_variable.name, subj, self.rtype, obj, restrictions) |
788 try: |
808 try: |
789 return bool(self.rqlexec(rql, self.cw_rset and self.cw_rset.args)) |
809 return bool(self.rqlexec(rql, self.cw_rset and self.cw_rset.args)) |
790 except: |
810 except Exception: |
791 # catch exception on executing rql, work-around #1356884 until a |
811 # catch exception on executing rql, work-around #1356884 until a |
792 # proper fix |
812 # proper fix |
793 self.exception('cant handle rql generated by %s', self) |
813 self.exception('cant handle rql generated by %s', self) |
794 return False |
814 return False |
795 |
815 |
804 entities to which they are related. Most things work similarly as |
824 entities to which they are related. Most things work similarly as |
805 :class:`RelationFacet`, except that: |
825 :class:`RelationFacet`, except that: |
806 |
826 |
807 * `label_vid` doesn't make sense here |
827 * `label_vid` doesn't make sense here |
808 |
828 |
809 * you should specify the attribute type using `attrtype` if it's not a |
829 * you should specify the attribute type using `target_attr_type` if it's not a |
810 String |
830 String |
811 |
831 |
812 * you can specify a comparison operator using `comparator` |
832 * you can specify a comparison operator using `comparator` |
813 |
833 |
814 |
834 |
837 # we want to search according to address 'postal_code' attribute |
857 # we want to search according to address 'postal_code' attribute |
838 target_attr = 'postalcode' |
858 target_attr = 'postalcode' |
839 """ |
859 """ |
840 _select_target_entity = False |
860 _select_target_entity = False |
841 # attribute type |
861 # attribute type |
842 attrtype = 'String' |
862 target_attr_type = 'String' |
843 # type of comparison: default is an exact match on the attribute value |
863 # type of comparison: default is an exact match on the attribute value |
844 comparator = '=' # could be '<', '<=', '>', '>=' |
864 comparator = '=' # could be '<', '<=', '>', '>=' |
865 |
|
866 @property |
|
867 def restr_attr(self): |
|
868 return self.target_attr |
|
869 |
|
870 @property |
|
871 def restr_attr_type(self): |
|
872 return self.target_attr_type |
|
845 |
873 |
846 def rset_vocabulary(self, rset): |
874 def rset_vocabulary(self, rset): |
847 if self.i18nable: |
875 if self.i18nable: |
848 _ = self._cw._ |
876 _ = self._cw._ |
849 else: |
877 else: |
852 return [(_(value), value) for value, in rset] |
880 return [(_(value), value) for value, in rset] |
853 values = [(_(value), value) for value, in rset] |
881 values = [(_(value), value) for value, in rset] |
854 if self.sortasc: |
882 if self.sortasc: |
855 return sorted(values) |
883 return sorted(values) |
856 return reversed(sorted(values)) |
884 return reversed(sorted(values)) |
857 |
|
858 def add_rql_restrictions(self): |
|
859 """add restriction for this facet into the rql syntax tree""" |
|
860 value = self._cw.form.get(self.__regid__) |
|
861 if not value: |
|
862 return |
|
863 filtered_variable = self.filtered_variable |
|
864 restrvar = _add_rtype_relation(self.select, filtered_variable, self.rtype, |
|
865 self.role)[0] |
|
866 self.select.set_distinct(True) |
|
867 if isinstance(value, basestring) or self.operator == 'OR': |
|
868 # only one value selected or multiple ORed values: using IN is fine |
|
869 self.select.add_constant_restriction( |
|
870 restrvar, self.target_attr, value, |
|
871 self.attrtype, self.comparator) |
|
872 else: |
|
873 # multiple values with AND operator |
|
874 self.select.add_constant_restriction( |
|
875 restrvar, self.target_attr, value.pop(), |
|
876 self.attrtype, self.comparator) |
|
877 while value: |
|
878 restrvar = _add_rtype_relation(self.select, filtered_variable, self.rtype, |
|
879 self.role)[0] |
|
880 self.select.add_constant_restriction( |
|
881 restrvar, self.target_attr, value.pop(), |
|
882 self.attrtype, self.comparator) |
|
883 |
885 |
884 |
886 |
885 class AttributeFacet(RelationAttributeFacet): |
887 class AttributeFacet(RelationAttributeFacet): |
886 """Base facet to filter some entities according one of their attribute. |
888 """Base facet to filter some entities according one of their attribute. |
887 Configuration is mostly similarly as :class:`RelationAttributeFacet`, except that: |
889 Configuration is mostly similarly as :class:`RelationAttributeFacet`, except that: |
937 cleanup_select(select, filtered_variable) |
939 cleanup_select(select, filtered_variable) |
938 newvar = prepare_vocabulary_select(select, filtered_variable, self.rtype, self.role) |
940 newvar = prepare_vocabulary_select(select, filtered_variable, self.rtype, self.role) |
939 _set_orderby(select, newvar, self.sortasc, self.sortfunc) |
941 _set_orderby(select, newvar, self.sortasc, self.sortfunc) |
940 try: |
942 try: |
941 rset = self.rqlexec(select.as_string(), self.cw_rset.args) |
943 rset = self.rqlexec(select.as_string(), self.cw_rset.args) |
942 except: |
944 except Exception: |
943 self.exception('error while getting vocabulary for %s, rql: %s', |
945 self.exception('error while getting vocabulary for %s, rql: %s', |
944 self, select.as_string()) |
946 self, select.as_string()) |
945 return () |
947 return () |
946 finally: |
948 finally: |
947 select.recover() |
949 select.recover() |
954 value = self._cw.form.get(self.__regid__) |
956 value = self._cw.form.get(self.__regid__) |
955 if not value: |
957 if not value: |
956 return |
958 return |
957 filtered_variable = self.filtered_variable |
959 filtered_variable = self.filtered_variable |
958 self.select.add_constant_restriction(filtered_variable, self.rtype, value, |
960 self.select.add_constant_restriction(filtered_variable, self.rtype, value, |
959 self.attrtype, self.comparator) |
961 self.target_attr_type, self.comparator) |
962 |
|
963 |
|
964 class RQLPathFacet(RelationFacet): |
|
965 """Base facet to filter some entities according to an arbitrary rql |
|
966 path. Path should be specified as a list of 3-uples or triplet string, where |
|
967 'X' represent the filtered variable. You should specify using |
|
968 `filter_variable` the snippet variable that will be used to filter out |
|
969 results. You may also specify a `label_variable`. If you want to filter on |
|
970 an attribute value, you usually don't want to specify the later since it's |
|
971 the same as the filter variable, though you may have to specify the attribute |
|
972 type using `restr_attr_type` if there are some type ambiguity in the schema |
|
973 for the attribute. |
|
974 |
|
975 Using this facet, we can rewrite facets we defined previously: |
|
976 |
|
977 .. sourcecode:: python |
|
978 |
|
979 class AgencyFacet(RQLPathFacet): |
|
980 __regid__ = 'agency' |
|
981 # this facet should only be selected when visualizing offices |
|
982 __select__ = RelationFacet.__select__ & is_instance('Office') |
|
983 # this facet is a filter on the 'Agency' entities linked to the office |
|
984 # through the 'proposed_by' relation, where the office is the subject |
|
985 # of the relation |
|
986 path = ['X has_address O', 'O name N'] |
|
987 filter_variable = 'O' |
|
988 label_variable = 'N' |
|
989 |
|
990 class PostalCodeFacet(RQLPathFacet): |
|
991 __regid__ = 'postalcode' |
|
992 # this facet should only be selected when visualizing offices |
|
993 __select__ = RelationAttributeFacet.__select__ & is_instance('Office') |
|
994 # this facet is a filter on the PostalAddress entities linked to the |
|
995 # office through the 'has_address' relation, where the office is the |
|
996 # subject of the relation |
|
997 path = ['X has_address O', 'O postal_code PC'] |
|
998 filter_variable = 'PC' |
|
999 |
|
1000 Though some features, such as 'no value' or automatic internationalization, |
|
1001 won't work. This facet class is designed to be used for cases where |
|
1002 :class:`RelationFacet` or :class:`RelationAttributeFacet` can't do the trick |
|
1003 (e.g when you want to filter on entities where are not directly linked to |
|
1004 the filtered entities). |
|
1005 """ |
|
1006 # must be specified |
|
1007 path = None |
|
1008 filter_variable = None |
|
1009 # may be specified |
|
1010 label_variable = None |
|
1011 # usually guessed, but may be explicitly specified |
|
1012 restr_attr = None |
|
1013 restr_attr_type = None |
|
1014 |
|
1015 # XXX disabled features |
|
1016 i18nable = False |
|
1017 no_relation = False |
|
1018 support_and = False |
|
1019 |
|
1020 def __init__(self, *args, **kwargs): |
|
1021 super(RQLPathFacet, self).__init__(*args, **kwargs) |
|
1022 assert self.path and isinstance(self.path, (list, tuple)), \ |
|
1023 'path should be a list of 3-uples, not %s' % self.path |
|
1024 for part in self.path: |
|
1025 if isinstance(part, basestring): |
|
1026 part = part.split() |
|
1027 assert len(part) == 3, \ |
|
1028 'path should be a list of 3-uples, not %s' % part |
|
1029 |
|
1030 def __repr__(self): |
|
1031 return '<%s %s>' % (self.__class__.__name__, |
|
1032 ','.join(str(p) for p in self.path)) |
|
1033 |
|
1034 def vocabulary(self): |
|
1035 """return vocabulary for this facet, eg a list of 2-uple (label, value) |
|
1036 """ |
|
1037 select = self.select |
|
1038 select.save_state() |
|
1039 if self.rql_sort: |
|
1040 sort = self.sortasc |
|
1041 else: |
|
1042 sort = None # will be sorted on label |
|
1043 try: |
|
1044 cleanup_select(select, self.filtered_variable) |
|
1045 varmap, restrvar = self.add_path_to_select() |
|
1046 select.append_selected(nodes.VariableRef(restrvar)) |
|
1047 if self.label_variable: |
|
1048 attrvar = varmap[self.label_variable] |
|
1049 else: |
|
1050 attrvar = restrvar |
|
1051 select.append_selected(nodes.VariableRef(attrvar)) |
|
1052 if sort is not None: |
|
1053 _set_orderby(select, attrvar, sort, self.sortfunc) |
|
1054 try: |
|
1055 rset = self.rqlexec(select.as_string(), self.cw_rset.args) |
|
1056 except Exception: |
|
1057 self.exception('error while getting vocabulary for %s, rql: %s', |
|
1058 self, select.as_string()) |
|
1059 return () |
|
1060 finally: |
|
1061 select.recover() |
|
1062 # don't call rset_vocabulary on empty result set, it may be an empty |
|
1063 # *list* (see rqlexec implementation) |
|
1064 values = rset and self.rset_vocabulary(rset) or [] |
|
1065 if self._include_no_relation(): |
|
1066 values.insert(0, (self._cw._(self.no_relation_label), '')) |
|
1067 return values |
|
1068 |
|
1069 def possible_values(self): |
|
1070 """return a list of possible values (as string since it's used to |
|
1071 compare to a form value in javascript) for this facet |
|
1072 """ |
|
1073 select = self.select |
|
1074 select.save_state() |
|
1075 try: |
|
1076 cleanup_select(select, self.filtered_variable) |
|
1077 varmap, restrvar = self.add_path_to_select(skiplabel=True) |
|
1078 select.append_selected(nodes.VariableRef(restrvar)) |
|
1079 values = [unicode(x) for x, in self.rqlexec(select.as_string())] |
|
1080 except Exception: |
|
1081 self.exception('while computing values for %s', self) |
|
1082 return [] |
|
1083 finally: |
|
1084 select.recover() |
|
1085 if self._include_no_relation(): |
|
1086 values.append('') |
|
1087 return values |
|
1088 |
|
1089 def add_rql_restrictions(self): |
|
1090 """add restriction for this facet into the rql syntax tree""" |
|
1091 value = self._cw.form.get(self.__regid__) |
|
1092 if value is None: |
|
1093 return |
|
1094 varmap, restrvar = self.add_path_to_select( |
|
1095 skiplabel=True, skipattrfilter=True) |
|
1096 self.value_restriction(restrvar, None, value) |
|
1097 |
|
1098 def add_path_to_select(self, skiplabel=False, skipattrfilter=False): |
|
1099 varmap = {'X': self.filtered_variable} |
|
1100 actual_filter_variable = None |
|
1101 for part in self.path: |
|
1102 if isinstance(part, basestring): |
|
1103 part = part.split() |
|
1104 subject, rtype, object = part |
|
1105 if skiplabel and object == self.label_variable: |
|
1106 continue |
|
1107 if object == self.filter_variable: |
|
1108 rschema = self._cw.vreg.schema.rschema(rtype) |
|
1109 if rschema.final: |
|
1110 # filter variable is an attribute variable |
|
1111 if self.restr_attr is None: |
|
1112 self.restr_attr = rtype |
|
1113 if self.restr_attr_type is None: |
|
1114 attrtypes = set(obj for subj,obj in rschema.rdefs) |
|
1115 if len(attrtypes) > 1: |
|
1116 raise Exception('ambigous attribute %s, specify attrtype on %s' |
|
1117 % (rtype, self.__class__)) |
|
1118 self.restr_attr_type = iter(attrtypes).next() |
|
1119 if skipattrfilter: |
|
1120 actual_filter_variable = subject |
|
1121 continue |
|
1122 subjvar = _get_var(self.select, subject, varmap) |
|
1123 objvar = _get_var(self.select, object, varmap) |
|
1124 rel = nodes.make_relation(subjvar, rtype, (objvar,), |
|
1125 nodes.VariableRef) |
|
1126 self.select.add_restriction(rel) |
|
1127 if self.restr_attr is None: |
|
1128 self.restr_attr = 'eid' |
|
1129 if self.restr_attr_type is None: |
|
1130 self.restr_attr_type = 'Int' |
|
1131 if actual_filter_variable: |
|
1132 restrvar = varmap[actual_filter_variable] |
|
1133 else: |
|
1134 restrvar = varmap[self.filter_variable] |
|
1135 return varmap, restrvar |
|
960 |
1136 |
961 |
1137 |
962 class RangeFacet(AttributeFacet): |
1138 class RangeFacet(AttributeFacet): |
963 """This class allows to filter entities according to an attribute of |
1139 """This class allows to filter entities according to an attribute of |
964 numerical type. |
1140 numerical type. |
986 |
1162 |
987 .. image:: ../images/facet_range.png |
1163 .. image:: ../images/facet_range.png |
988 |
1164 |
989 .. _jquery: http://www.jqueryui.com/ |
1165 .. _jquery: http://www.jqueryui.com/ |
990 """ |
1166 """ |
991 attrtype = 'Float' # only numerical types are supported |
1167 target_attr_type = 'Float' # only numerical types are supported |
992 needs_update = False # not supported actually |
1168 needs_update = False # not supported actually |
993 |
1169 |
994 @property |
1170 @property |
995 def wdgclass(self): |
1171 def wdgclass(self): |
996 return FacetRangeWidget |
1172 return FacetRangeWidget |
1031 |
1207 |
1032 def _add_restriction(self, value, operator): |
1208 def _add_restriction(self, value, operator): |
1033 self.select.add_constant_restriction(self.filtered_variable, |
1209 self.select.add_constant_restriction(self.filtered_variable, |
1034 self.rtype, |
1210 self.rtype, |
1035 self.formatvalue(value), |
1211 self.formatvalue(value), |
1036 self.attrtype, operator) |
1212 self.target_attr_type, operator) |
1037 |
1213 |
1038 |
1214 |
1039 |
1215 |
1040 class DateRangeFacet(RangeFacet): |
1216 class DateRangeFacet(RangeFacet): |
1041 """This class works similarly as the :class:`RangeFacet` but for attribute |
1217 """This class works similarly as the :class:`RangeFacet` but for attribute |
1043 |
1219 |
1044 The image below display the rendering of the slider for a date range: |
1220 The image below display the rendering of the slider for a date range: |
1045 |
1221 |
1046 .. image:: ../images/facet_date_range.png |
1222 .. image:: ../images/facet_date_range.png |
1047 """ |
1223 """ |
1048 attrtype = 'Date' # only date types are supported |
1224 target_attr_type = 'Date' # only date types are supported |
1049 |
1225 |
1050 @property |
1226 @property |
1051 def wdgclass(self): |
1227 def wdgclass(self): |
1052 return DateFacetRangeWidget |
1228 return DateFacetRangeWidget |
1053 |
1229 |