예제 #1
0
class OrganizationFilter(FilterSet):
    """ Фильтр категорий товаров и сети для общего списка организаций """
    category = filters.ModelMultipleChoiceFilter(
        queryset=Category.objects.all(),
        field_name='connector__product__category__name',
        to_field_name='name',
        label='Категория товаров/услуг')
    network = filters.ModelMultipleChoiceFilter(queryset=Network.objects.all(),
                                                field_name='network__name',
                                                to_field_name='name',
                                                label='Сеть организаций')

    class Meta:
        model = Organization
        fields = ['category', 'network']
예제 #2
0
class SignalCategoryRemovedAfterFilterSet(FilterSet):
    after = filters.IsoDateTimeFilter(field_name='category_assignment__created_at', lookup_expr='gte')
    before = filters.IsoDateTimeFilter(field_name='category_assignment__created_at', lookup_expr='lte')
    category_slug = filters.ModelMultipleChoiceFilter(
        method='category_filter',
        queryset=Category.objects.all(),
        to_field_name='slug',
        field_name='category_assignment__category__slug',
    )

    def category_filter(self, queryset, name, value):
        # TODO: Get categories from user permissions, can be added after PR
        #       https://github.com/Amsterdam/signals/pull/202 has been approved and merged

        if len(value):
            # We need to check the given categories
            categories_to_check = [v.id for v in value]
        else:
            # A list of category id's that the currently logged in user has permissions for
            categories_to_check = Category.objects.all().values_list('id', flat=True)

        return queryset.filter(
            Q(category_assignment__isnull=False) &
            Q(categories__id__in=categories_to_check) &
            ~Q(category_assignment__category_id__in=categories_to_check)
        )
예제 #3
0
class UserNameListFilterSet(FilterSet):
    username = filters.CharFilter(lookup_expr='icontains', min_length=3)
    is_active = filters.BooleanFilter(field_name='is_active')
    profile_department_code = filters.ModelMultipleChoiceFilter(
        queryset=_get_department_queryset(),
        to_field_name='code',
        field_name='profile__departments__code')
예제 #4
0
class SimpleOffersFilter(FilterSet):
    tags = filters.ModelMultipleChoiceFilter(queryset=Tag.objects.all(),
                                             name="tags__name",
                                             to_field_name='name',
                                             conjoined=True)

    class Meta:
        model = Offer
        fields = ['tags']
예제 #5
0
 def __init__(self, *args, **kwargs):
     super().__init__()
     prod_id = kwargs['queryset'][0].organization.pk
     self.filters['category'] = filters.ModelMultipleChoiceFilter(
         queryset=Category.objects.filter(
             product__connector__organization__id=prod_id).distinct(),
         field_name='product__category__name',
         to_field_name='name',
         label='Категория товаров/услуг')
예제 #6
0
class ProductFilter(FilterSet):
    """ Фильтр категорий товаров, сети, мин. и макс. цены для общего списка товаров """
    category = filters.ModelMultipleChoiceFilter(
        queryset=Category.objects.all(),
        field_name='connector__product__category__name',
        to_field_name='name',
        label='Категория товаров/услуг')
    network = filters.ModelMultipleChoiceFilter(queryset=Network.objects.all(),
                                                field_name='network__name',
                                                to_field_name='name',
                                                label='Сеть организаций')
    min_price = filters.NumberFilter(field_name='price',
                                     lookup_expr='gte',
                                     label='Минимальная цена')
    max_price = filters.NumberFilter(field_name='price',
                                     lookup_expr='lte',
                                     label='Максимальная цена')

    class Meta:
        model = Product
        fields = {
            'min_price': 'gte',
            'max_price': 'lte',
        }
예제 #7
0
class UserFilterSet(FilterSet):
    id = filters.NumberFilter()
    username = filters.CharFilter(lookup_expr='icontains')
    is_active = filters.BooleanFilter(field_name='is_active')

    role = filters.ModelMultipleChoiceFilter(queryset=_get_group_queryset(),
                                             to_field_name='name',
                                             field_name='groups__name')

    profile_department_code = filters.ModelMultipleChoiceFilter(
        queryset=_get_department_queryset(),
        to_field_name='code',
        field_name='profile__departments__code')

    order = OrderingExtraKwargsFilter(
        fields=(
            ('username', 'username'),
            ('is_active', 'is_active'),
        ),
        extra_kwargs={
            'username': {
                'apply': Lower
            }  # Will apply the Lower function when ordering
        })
예제 #8
0
class FunctionFilter(filterset.FilterSet):
    """
    ## Filters

    To filter for exact value matches:

        ?<fieldname>=<value>

    For advanced filtering use lookups:

        ?<fieldname>__<lookup>=<value>

    Possible lookups:

      - `name`: `iexact`, `contains`, `icontains`, `startswith`, `istartswith`, `endswith`, `iendswith`, `regex`, `iregex`
    """

    category = filters.ChoiceFilter(label=_("Category"),
                                    choices=models.Function.CATEGORY_CHOICES)
    persons = filters.ModelMultipleChoiceFilter(
        label=_("Person"), queryset=models.Person.objects.all())

    class Meta:
        model = models.Function
        fields = {
            "name": (
                "exact",
                "iexact",
                "contains",
                "icontains",
                "startswith",
                "istartswith",
                "endswith",
                "iendswith",
                "regex",
                "iregex",
            ),
            "leader": ("exact", ),
        }
예제 #9
0
class SignalFilterSet(FilterSet):
    id = filters.NumberFilter()
    address_text = filters.CharFilter(field_name='location__address_text', lookup_expr='icontains')
    area_code = filters.ChoiceFilter(field_name='location__area_code', choices=area_choices)
    area_type_code = filters.ChoiceFilter(field_name='location__area_type_code', choices=area_type_choices)
    buurt_code = filters.MultipleChoiceFilter(field_name='location__buurt_code', choices=buurt_choices)
    category_id = filters.MultipleChoiceFilter(field_name='category_assignment__category_id',
                                               choices=category_choices)
    category_slug = filters.ModelMultipleChoiceFilter(
        queryset=_get_child_category_queryset(),
        to_field_name='slug',
        field_name='category_assignment__category__slug',
    )
    contact_details = filters.MultipleChoiceFilter(method='contact_details_filter', choices=contact_details_choices)
    created_before = filters.IsoDateTimeFilter(field_name='created_at', lookup_expr='lte')
    directing_department = filters.MultipleChoiceFilter(
        method='directing_department_filter',
        choices=department_choices
    )
    created_after = filters.IsoDateTimeFilter(field_name='created_at', lookup_expr='gte')
    feedback = filters.ChoiceFilter(method='feedback_filter', choices=feedback_choices)
    has_changed_children = filters.MultipleChoiceFilter(method='has_changed_children_filter', choices=boolean_choices)
    kind = filters.MultipleChoiceFilter(method='kind_filter', choices=kind_choices)  # SIG-2636
    incident_date = filters.DateFilter(field_name='incident_date_start', lookup_expr='date')
    incident_date_before = filters.DateFilter(field_name='incident_date_start', lookup_expr='date__gte')
    incident_date_after = filters.DateFilter(field_name='incident_date_start', lookup_expr='date__lte')
    maincategory_slug = filters.ModelMultipleChoiceFilter(
        queryset=_get_parent_category_queryset(),
        to_field_name='slug',
        field_name='category_assignment__category__parent__slug',
    )
    note_keyword = filters.CharFilter(method='note_keyword_filter')
    priority = filters.MultipleChoiceFilter(field_name='priority__priority', choices=Priority.PRIORITY_CHOICES)
    source = filters.MultipleChoiceFilter(choices=source_choices)
    stadsdeel = filters.MultipleChoiceFilter(field_name='location__stadsdeel', choices=stadsdelen_choices)
    status = filters.MultipleChoiceFilter(field_name='status__state', choices=status_choices)
    type = filters.MultipleChoiceFilter(field_name='type_assignment__name', choices=Type.CHOICES)
    updated_before = filters.IsoDateTimeFilter(field_name='updated_at', lookup_expr='lte')
    updated_after = filters.IsoDateTimeFilter(field_name='updated_at', lookup_expr='gte')

    def _cleanup_form_data(self):
        """
        Cleanup the form data
        """
        self.form.cleaned_data.pop('category_slug', None)
        self.form.cleaned_data.pop('maincategory_slug', None)

        if not self.form.cleaned_data.get('area_code', None) or not self.form.cleaned_data.get('area_type_code', None):
            self.form.cleaned_data.pop('area_code', None)
            self.form.cleaned_data.pop('area_type_code', None)

    def filter_queryset(self, queryset):
        """
        Add custom category filtering to the filter_queryset
        """
        if not self.form.cleaned_data['category_id']:
            main_categories = self.form.cleaned_data['maincategory_slug']
            sub_categories = self.form.cleaned_data['category_slug']

            if main_categories or sub_categories:
                queryset = queryset.filter(
                    Q(category_assignment__category__parent_id__in=[c.pk for c in main_categories]) |
                    Q(category_assignment__category_id__in=[c.pk for c in sub_categories])
                )

        self._cleanup_form_data()
        queryset = super(SignalFilterSet, self).filter_queryset(queryset=queryset)
        return queryset.distinct()

    # Custom filter functions

    def contact_details_filter(self, queryset, name, value):
        """
        Filter `signals.Signal` instances according to presence of contact details.
        """
        choices = value  # we have a MultipleChoiceFilter ...

        # Deal with all choices selected, or none selected:
        if len(choices) == len(contact_details_choices()):
            # No filters are present, or ... all filters are present. In that
            # case we want all Signal instances with an email address, or a
            # phone number, or none of those (according to) the UX design.
            # This is the same as not filtering, hence in both cases just
            # return the queryset.
            return queryset

        # Set-up our Q objects for the individual options.
        has_no_email = (Q(reporter__email__isnull=True) | Q(reporter__email=''))
        has_no_phone = (Q(reporter__phone__isnull=True) | Q(reporter__phone=''))
        is_anonymous = has_no_email & has_no_phone

        q_objects = {
            'email': ~has_no_email,
            'phone': ~has_no_phone,
            'none': is_anonymous,
        }

        # The individual choices have to be combined using logical OR:
        q_total = q_objects[choices.pop()]
        while choices:
            q_total |= q_objects[choices.pop()]

        return queryset.filter(q_total)

    def directing_department_filter(self, queryset, name, value):
        """
        Filter Signals on directing department.

        * A directing department can only be set on a Parent Signal
        * When providing the option "null" select all parent Signals without one or more directing departments
        * When providing on or more department codes as options select all parent Signals which match directing
          departments

        Example 1: "?directing_department=ASC" will select all parent Signals where ASC is the directing department
        Example 2: "?directing_department=ASC&directing_department=null" will select all parent Signals without a
                   directing department OR ASC as the directing department
        Example 3: "?directing_department=null" will select all parent Signals without a directing department
        """
        choices = value  # we have a MultipleChoiceFilter ...
        if len(choices) == len(department_choices()):
            # No filters are present, or ... all filters are present. In that case we want all Signal instances
            return queryset

        # Directing departments are only set on parent Signals
        parent_q_filter = (Q(parent__isnull=True) & Q(children__isnull=False))

        if 'null' in choices and len(choices) == 1:
            # "?directing_department=null" will select all parent Signals without a directing department
            return queryset.filter(
                parent_q_filter &
                (
                    Q(directing_departments_assignment__isnull=True) |
                    Q(directing_departments_assignment__departments__isnull=True)
                )
            )
        elif 'null' in choices and len(choices) > 1:
            # "?directing_department=ASC&directing_department=null" will select all parent Signals without a directing
            # department OR ASC as the directing department
            choices.pop(choices.index('null'))
            return queryset.filter(
                parent_q_filter & (
                    Q(directing_departments_assignment__isnull=True) |
                    Q(directing_departments_assignment__departments__isnull=True) |
                    Q(directing_departments_assignment__departments__code__in=choices)
                )
            )
        elif len(choices):
            # "?directing_department=ASC" will select all parent Signals where ASC is the directing department
            return queryset.filter(
                parent_q_filter &
                Q(directing_departments_assignment__departments__code__in=choices)
            )
        return queryset

    def feedback_filter(self, queryset, name, value):
        # Only signals that have feedback
        queryset = queryset.annotate(feedback_count=Count('feedback')).filter(feedback_count__gte=1)

        if value in ['satisfied', 'not_satisfied']:
            is_satisfied = True if value == 'satisfied' else False
            queryset = queryset.annotate(
                feedback_max_created_at=Max('feedback__created_at'),
                feedback_max_submitted_at=Max('feedback__submitted_at')
            ).filter(
                feedback__is_satisfied=is_satisfied,
                feedback__submitted_at__isnull=False,
                feedback__created_at=F('feedback_max_created_at'),
                feedback__submitted_at=F('feedback_max_submitted_at')
            )
        elif value == 'not_received':
            queryset = queryset.annotate(
                feedback_max_created_at=Max('feedback__created_at')
            ).filter(
                feedback__submitted_at__isnull=True,
                feedback__created_at=F('feedback_max_created_at')
            )

        return queryset

    def kind_filter(self, queryset, name, value):
        choices = value  # we have a MultipleChoiceFilter ...

        if (len(choices) == len(kind_choices())
                or {'signal', 'parent_signal', 'child_signal'} == set(choices)
                or {'parent_signal', 'exclude_parent_signal'} == set(choices)):
            return queryset

        q_objects = {
            'signal': (Q(parent__isnull=True) & Q(children__isnull=True)),
            'parent_signal': (Q(parent__isnull=True) & Q(children__isnull=False)),
            'exclude_parent_signal': ~(Q(parent__isnull=True) & Q(children__isnull=False)),
            'child_signal': (Q(parent__isnull=False)),
        }

        q_filter = q_objects[choices.pop()]
        while choices:
            q_filter |= q_objects[choices.pop()]

        return queryset.filter(q_filter) if q_filter else queryset

    def note_keyword_filter(self, queryset, name, value):
        return queryset.filter(notes__text__icontains=value)

    def has_changed_children_filter(self, queryset, name, value):
        # we have a MultipleChoiceFilter ...
        choices = list(set(map(lambda x: True if x in [True, 'True', 'true', 1] else False, value)))
        q_filter = Q(children__isnull=False)
        if len(choices) == 2:
            return queryset.filter(q_filter)

        if True in choices:
            q_filter &= Q(updated_at__lt=F('children__updated_at'))  # noqa Selects all parent signals with changes in the child signals

        if False in choices:
            q_filter &= Q(updated_at__gt=F('children__updated_at'))  # noqa Selects all parent signals with NO changes in the child signals

        return queryset.filter(q_filter)
예제 #10
0
class SignalFilter(FilterSet):
    """
    !!! This is the filter used in the V0 version of the API. V0 will be deprecated soon !!!
    - V1 filters can be found in signals/apps/api/v1/filters.py
    """
    id = IntegerFilter()
    in_bbox = filters.CharFilter(method='in_bbox_filter', label='bbox')
    geo = filters.CharFilter(method="locatie_filter", label='x,y,r')

    location__stadsdeel = filters.MultipleChoiceFilter(choices=STADSDELEN)
    location__buurt_code = filters.MultipleChoiceFilter(choices=buurt_choices)
    location__address_text = filters.CharFilter(lookup_expr='icontains')

    created_at = filters.DateFilter(field_name='created_at',
                                    lookup_expr='date')
    created_at__gte = filters.DateFilter(field_name='created_at',
                                         lookup_expr='date__gte')
    created_at__lte = filters.DateFilter(field_name='created_at',
                                         lookup_expr='date__lte')

    updated_at = filters.DateFilter(field_name='updated_at',
                                    lookup_expr='date')
    updated_at__gte = filters.DateFilter(field_name='updated_at',
                                         lookup_expr='date__gte')
    updated_at__lte = filters.DateFilter(field_name='updated_at',
                                         lookup_expr='date__lte')

    incident_date_start = filters.DateFilter(field_name='incident_date_start',
                                             lookup_expr='date')
    incident_date_start__gte = filters.DateFilter(
        field_name='incident_date_start', lookup_expr='date__gte')
    incident_date_start__lte = filters.DateFilter(
        field_name='incident_date_start', lookup_expr='date__lte')

    incident_date_end = filters.DateFilter(field_name='incident_date_end',
                                           lookup_expr='date')

    operational_date = filters.DateFilter(field_name='operational_date',
                                          lookup_expr='date')
    expire_date = filters.DateFilter(field_name='expire_date',
                                     lookup_expr='date')
    expire_date__gte = filters.DateFilter(field_name='expire_date',
                                          lookup_expr='date__gte')
    expire_date__lte = filters.DateFilter(field_name='expire_date',
                                          lookup_expr='date__lte')

    status__state = filters.MultipleChoiceFilter(choices=status_choices)
    # TODO: these filter (category__main, category__sub) should be removed
    category__main = filters.ModelMultipleChoiceFilter(
        queryset=Category.objects.filter(parent__isnull=True),
        to_field_name='name',
        field_name='category_assignment__category__parent__name')
    category__sub = filters.ModelMultipleChoiceFilter(
        queryset=Category.objects.all(),
        to_field_name='name',
        field_name='category_assignment__category__name')
    # category__main and category__sub filters will be replaced with main_slug and sub_slug
    main_slug = filters.ModelMultipleChoiceFilter(
        queryset=Category.objects.filter(parent__isnull=True).select_related(),
        to_field_name='slug',
        field_name='category_assignment__category__parent__slug',
    )
    sub_slug = filters.ModelMultipleChoiceFilter(
        queryset=Category.objects.all().select_related(),
        to_field_name='slug',
        field_name='category_assignment__category__slug',
    )

    priority__priority = filters.MultipleChoiceFilter(
        choices=Priority.PRIORITY_CHOICES)

    class Meta(object):
        model = Signal
        fields = (
            'id',
            'signal_id',
            'status__state',
            'category__main',
            'category__sub',
            'main_slug',
            'sub_slug',
            'location__buurt_code',
            'location__stadsdeel',
            'location__address_text',
            'reporter__email',
            'in_bbox',
            'geo',
        )

    def in_bbox_filter(self, qs, name, value):
        bbox_values, err = bbox.valid_bbox(value)
        lon1, lat1, lon2, lat2 = bbox_values
        poly_bbox = Polygon.from_bbox((lon1, lat1, lon2, lat2))

        if err:
            raise ValidationError(f"bbox invalid {err}:{bbox_values}")
        return qs.filter(location__geometrie__bboverlaps=poly_bbox)

    def locatie_filter(self, qs, name, value):
        lon, lat, radius = parse_xyr(value)
        point = Point(lon, lat)

        return qs.filter(
            location__geometrie__dwithin=(point,
                                          bbox.dist_to_deg(radius, lat)))
예제 #11
0
class PersonFilter(filterset.FilterSet):
    """
    ## Filters

    To filter for exact value matches:

        ?<fieldname>=<value>

    For advanced filtering use lookups:

        ?<fieldname>__<lookup>=<value>

    Possible lookups:

      - `first_name`: `iexact`, `contains`, `icontains`, `startswith`, `istartswith`, `endswith`, `iendswith`, `isnull`, `regex`, `iregex`
      - `last_name`: `iexact`, `contains`, `icontains`, `startswith`, `istartswith`, `endswith`, `iendswith`, `isnull`, `regex`, `iregex`
      - `title`: `iexact`, `contains`, `icontains`, `isnull`, `regex`, `iregex`
      - `consultation`: `contains`, `icontains`, `isnull`, `regex`, `iregex`
      - `appendix`: `contains`, `icontains`, `isnull`, `regex`, `iregex`
    """

    sex = filters.ChoiceFilter(label=_("Sex"),
                               choices=models.Person.GENDER_CHOICES)
    functions = filters.ModelMultipleChoiceFilter(
        label=_("Function"), queryset=models.Function.objects.all())

    class Meta:
        model = models.Person
        fields = {
            "first_name": (
                "exact",
                "iexact",
                "contains",
                "icontains",
                "startswith",
                "istartswith",
                "endswith",
                "iendswith",
                "isnull",
                "regex",
                "iregex",
            ),
            "last_name": (
                "exact",
                "iexact",
                "contains",
                "icontains",
                "startswith",
                "istartswith",
                "endswith",
                "iendswith",
                "isnull",
                "regex",
                "iregex",
            ),
            "title": (
                "exact",
                "iexact",
                "contains",
                "icontains",
                "isnull",
                "regex",
                "iregex",
            ),
            "consultation":
            ("contains", "icontains", "isnull", "regex", "iregex"),
            "appendix": ("contains", "icontains", "isnull", "regex", "iregex"),
        }
예제 #12
0
파일: filters.py 프로젝트: houcy/cyphon
class AlertFilter(FilterSet):
    """
    Filters Alerts.
    """
    def __init__(self, *args, **kwargs):
        super(AlertFilter, self).__init__(*args, **kwargs)

        # add a blank choice to ChoiceFilter options
        for (dummy_name, field) in self.filters.items():
            if isinstance(field, django_filters.ChoiceFilter):
                field.extra['choices'] = tuple([('', '---------'), ] + \
                    list(field.extra['choices']))

    collection = django_filters.ModelMultipleChoiceFilter(
        name='distillery',
        label='Collections',
        queryset=Distillery.objects.have_alerts())
    warehouse = django_filters.ModelMultipleChoiceFilter(
        name='distillery__collection__warehouse',
        label='Warehouses',
        queryset=Warehouse.objects.all())
    after = django_filters.DateTimeFilter(name='created_date',
                                          lookup_expr='gt')
    before = django_filters.DateTimeFilter(name='created_date',
                                           lookup_expr='lte')
    level = django_filters.MultipleChoiceFilter(choices=ALERT_LEVEL_CHOICES)
    status = django_filters.MultipleChoiceFilter(choices=ALERT_STATUS_CHOICES)
    assigned_user = django_filters.ModelChoiceFilter(
        name='assigned_user', queryset=AppUser.objects.all())
    content = django_filters.CharFilter(name='data',
                                        label='Content',
                                        method='filter_by_content')
    categories = django_filters.ModelMultipleChoiceFilter(
        name='distillery__categories',
        label='Collection categories',
        queryset=Category.objects.all())
    tags = django_filters.ModelMultipleChoiceFilter(name='tags',
                                                    queryset=Tag.objects.all())

    class Meta:
        model = Alert

        # List content field last so it will have fewer Alerts to
        # filter. The content filter requires a query to the Distillery
        # associated with the Alert. It's best to filter out as many
        # records as possible before constructing that query.
        fields = [
            'collection', 'after', 'before', 'level', 'status',
            'assigned_user', 'content'
        ]

    @staticmethod
    def _get_data_query(distillery, value):
        """

        """
        text_fields = distillery.get_text_fields()
        field_names = [field.field_name for field in text_fields]
        field_keys = [name.replace('.', '__') for name in field_names]
        queries = []

        for key in field_keys:
            query_exp = 'data__%s' % key
            kwarg = {query_exp: value}
            queries.append(Q(**kwarg))

        field_query = join_query(queries, 'OR')
        return Q(distillery=distillery) & field_query

    @staticmethod
    def _get_title_query(value):
        """

        """
        return Q(title__icontains=value)

    def _filter_by_value(self, queryset, value):
        """

        """
        distilleries = Distillery.objects.filter(
            alerts__in=queryset).distinct()

        if distilleries:
            queries = [self._get_data_query(distillery, value) \
                       for distillery in distilleries]
            data_query = join_query(queries, 'OR')
            title_query = self._get_title_query(value)
            return queryset.filter(title_query | data_query)
        else:
            return queryset.none()

    # @timeit
    def filter_by_content(self, queryset, name, value):
        """
        Takes a QuerySet of Alerts and a string value. Returns a filtered
        QuerySet of Alerts whose data includes the given value.
        """
        if not value:
            return queryset

        try:
            return self._filter_by_value(queryset, value)

        except ValueError:
            LOGGER.error('An error occurred while filtering Alerts')
            return queryset
예제 #13
0
class SignalFilter(FilterSet):
    id = filters.NumberFilter()

    created_before = filters.IsoDateTimeFilter(field_name='created_at', lookup_expr='lte')
    created_after = filters.IsoDateTimeFilter(field_name='created_at', lookup_expr='gte')

    updated_before = filters.IsoDateTimeFilter(field_name='updated_at', lookup_expr='lte')
    updated_after = filters.IsoDateTimeFilter(field_name='updated_at', lookup_expr='gte')

    status = filters.MultipleChoiceFilter(field_name='status__state', choices=status_choices)

    maincategory_slug = filters.ModelMultipleChoiceFilter(
        queryset=_get_parent_category_queryset(),
        to_field_name='slug',
        field_name='category_assignment__category__parent__slug',
    )

    # category_slug, because we will soon be using one type of category, instead of main vs sub
    # categories. This way the naming in the API will remain consistent
    category_slug = filters.ModelMultipleChoiceFilter(
        queryset=_get_child_category_queryset(),
        to_field_name='slug',
        field_name='category_assignment__category__slug',
    )

    priority = filters.ChoiceFilter(field_name='priority__priority',
                                    choices=Priority.PRIORITY_CHOICES)

    stadsdeel = filters.MultipleChoiceFilter(field_name='location__stadsdeel',
                                             choices=STADSDELEN)
    buurt_code = filters.MultipleChoiceFilter(field_name='location__buurt_code',
                                              choices=buurt_choices)
    address_text = filters.CharFilter(field_name='location__address_text',
                                      lookup_expr='icontains')

    incident_date = filters.DateFilter(field_name='incident_date_start', lookup_expr='date')
    incident_date_before = filters.DateFilter(field_name='incident_date_start',
                                              lookup_expr='date__gte')
    incident_date_after = filters.DateFilter(field_name='incident_date_start',
                                             lookup_expr='date__lte')

    source = filters.MultipleChoiceFilter(choices=source_choices)

    feedback = filters.ChoiceFilter(method='feedback_filter', choices=feedback_choices)

    def feedback_filter(self, queryset, name, value):
        # Only signals that have feedback
        queryset = queryset.annotate(feedback_count=Count('feedback')).filter(feedback_count__gte=1)

        if value in ['satisfied', 'not_satisfied']:
            is_satisfied = True if value == 'satisfied' else False
            queryset = queryset.annotate(
                feedback_max_created_at=Max('feedback__created_at'),
                feedback_max_submitted_at=Max('feedback__submitted_at')
            ).filter(
                feedback__is_satisfied=is_satisfied,
                feedback__submitted_at__isnull=False,
                feedback__created_at=F('feedback_max_created_at'),
                feedback__submitted_at=F('feedback_max_submitted_at')
            )
        elif value == 'not_received':
            queryset = queryset.annotate(
                feedback_max_created_at=Max('feedback__created_at')
            ).filter(
                feedback__submitted_at__isnull=True,
                feedback__created_at=F('feedback_max_created_at')
            )

        return queryset

    def _categories_filter(self, queryset, main_categories, sub_categories):
        if not main_categories and not sub_categories:
            return queryset

        queryset = queryset.filter(
            Q(category_assignment__category__parent_id__in=[c.pk for c in main_categories]) |
            Q(category_assignment__category_id__in=[c.pk for c in sub_categories])
        )
        return queryset

    def filter_queryset(self, queryset):
        main_categories = []
        sub_categories = []

        for name, value in self.form.cleaned_data.items():
            if name.lower() == 'maincategory_slug':
                main_categories = value
            elif name.lower() == 'category_slug':
                sub_categories = value
            else:
                queryset = self.filters[name].filter(queryset, value)

        return self._categories_filter(queryset=queryset,
                                       main_categories=main_categories,
                                       sub_categories=sub_categories)
예제 #14
0
class SignalFilter(FilterSet):
    id = filters.NumberFilter()

    country = filters.MultipleChoiceFilter(field_name='country')
    city = filters.MultipleChoiceFilter(field_name='city')

    created_before = filters.IsoDateTimeFilter(field_name='created_at',
                                               lookup_expr='lte')
    created_after = filters.IsoDateTimeFilter(field_name='created_at',
                                              lookup_expr='gte')

    updated_before = filters.IsoDateTimeFilter(field_name='updated_at',
                                               lookup_expr='lte')
    updated_after = filters.IsoDateTimeFilter(field_name='updated_at',
                                              lookup_expr='gte')

    status = filters.MultipleChoiceFilter(field_name='status__state',
                                          choices=status_choices)

    maincategory_slug = filters.ModelMultipleChoiceFilter(
        queryset=_get_parent_category_queryset(),
        to_field_name='slug',
        field_name='category_assignment__category__parent__slug',
    )

    # category_slug, because we will soon be using one type of category, instead of main vs sub
    # categories. This way the naming in the API will remain consistent
    category_slug = filters.ModelMultipleChoiceFilter(
        queryset=_get_child_category_queryset(),
        to_field_name='slug',
        field_name='category_assignment__category__slug',
    )

    category_filter_label = filters.ModelMultipleChoiceFilter(
        queryset=_get_parent_category_queryset(),
        to_field_name='filter_label',
        field_name='category_assignment__category__filter_label',
    )

    priority = filters.MultipleChoiceFilter(field_name='priority__priority',
                                            choices=Priority.PRIORITY_CHOICES)

    stadsdeel = filters.MultipleChoiceFilter(field_name='location__stadsdeel',
                                             choices=stadsdelen)
    buurt_code = filters.MultipleChoiceFilter(
        field_name='location__buurt_code', choices=buurt_choices)
    address_text = filters.CharFilter(field_name='location__address_text',
                                      lookup_expr='icontains')

    incident_date = filters.DateFilter(field_name='incident_date_start',
                                       lookup_expr='date')
    incident_date_before = filters.DateFilter(field_name='incident_date_start',
                                              lookup_expr='date__gte')
    incident_date_after = filters.DateFilter(field_name='incident_date_start',
                                             lookup_expr='date__lte')

    source = filters.MultipleChoiceFilter(choices=source_choices)

    feedback = filters.ChoiceFilter(method='feedback_filter',
                                    choices=feedback_choices)

    contact_details = filters.MultipleChoiceFilter(
        method='contact_details_filter',
        choices=contact_details_choices,
    )

    # SIG-2148 Filter on Signal Type
    type = filters.MultipleChoiceFilter(method='type_filter',
                                        choices=Type.CHOICES)

    note_keyword = filters.CharFilter(method='note_keyword_filter', )

    directing_department = filters.MultipleChoiceFilter(
        field_name='directing_departments_assignment__departments__code',
        choices=department_choices)

    def feedback_filter(self, queryset, name, value):
        # Only signals that have feedback
        queryset = queryset.annotate(feedback_count=Count('feedback')).filter(
            feedback_count__gte=1)

        if value in ['satisfied', 'not_satisfied']:
            is_satisfied = True if value == 'satisfied' else False
            queryset = queryset.annotate(
                feedback_max_created_at=Max('feedback__created_at'),
                feedback_max_submitted_at=Max(
                    'feedback__submitted_at')).filter(
                        feedback__is_satisfied=is_satisfied,
                        feedback__submitted_at__isnull=False,
                        feedback__created_at=F('feedback_max_created_at'),
                        feedback__submitted_at=F('feedback_max_submitted_at'))
        elif value == 'not_received':
            queryset = queryset.annotate(
                feedback_max_created_at=Max('feedback__created_at')).filter(
                    feedback__submitted_at__isnull=True,
                    feedback__created_at=F('feedback_max_created_at'))

        return queryset

    def _categories_filter(self, queryset, main_categories, sub_categories):
        if not main_categories and not sub_categories:
            return queryset

        queryset = queryset.filter(
            Q(category_assignment__category__parent_id__in=[
                c.pk for c in main_categories
            ]) | Q(category_assignment__category_id__in=[
                c.pk for c in sub_categories
            ]))
        return queryset

    def filter_queryset(self, queryset):
        main_categories = []
        sub_categories = []

        for name, value in self.form.cleaned_data.items():
            if name.lower() == 'maincategory_slug':
                main_categories = value
            elif name.lower() == 'category_slug':
                sub_categories = value
            else:
                queryset = self.filters[name].filter(queryset, value)

        return self._categories_filter(queryset=queryset,
                                       main_categories=main_categories,
                                       sub_categories=sub_categories)

    def contact_details_filter(self, queryset, name, value):
        """
        Filter `signals.Signal` instances according to presence of contact details.
        """
        # Set-up our Q objects for the individual options.
        has_no_email = (Q(reporter__email__isnull=True)
                        | Q(reporter__email=''))
        has_no_phone = (Q(reporter__phone__isnull=True)
                        | Q(reporter__phone=''))
        is_anonymous = has_no_email & has_no_phone

        q_objects = {
            'email': ~has_no_email,
            'phone': ~has_no_phone,
            'none': is_anonymous,
        }

        choices = value  # we have a MultipleChoiceFilter ...

        # Deal with all choices selected, or none selected:
        if len(choices) == len(contact_details_choices):
            # No filters are present, or ... all filters are present. In that
            # case we want all Signal instances with an email address, or a
            # phone number, or none of those (according to) the UX design.
            # This is the same as not filtering, hence in both cases just
            # return the queryset.
            return queryset

        # The individual choices have to be combined using logical OR:
        q_total = q_objects[choices.pop()]
        while choices:
            q_total |= q_objects[choices.pop()]

        return queryset.filter(q_total)

    def type_filter(self, queryset, name, value):
        return queryset.annotate(type_assignment_id=Max('types__id')).filter(
            types__id=F('type_assignment_id'), types__name__in=value)

    def note_keyword_filter(self, queryset, name, value):
        return queryset.filter(notes__text__icontains=value).distinct()
예제 #15
0
파일: signal.py 프로젝트: Signalen/backend
class PublicSignalGeographyFilter(FilterSet):

    bbox = filters.CharFilter()  # min_lon, min_lat, max_lon, max_lat
    lat = filters.NumberFilter()
    lon = filters.NumberFilter()

    maincategory_slug = filters.ModelMultipleChoiceFilter(
        queryset=_get_parent_category_queryset(),
        to_field_name='slug',
        field_name='category_assignment__category__parent__slug',
    )  # Only parent categories are allowed

    category_slug = filters.ModelMultipleChoiceFilter(
        queryset=_get_child_category_queryset().filter(
            is_public_accessible=True),
        to_field_name='slug',
        field_name='category_assignment__category__slug'
    )  # Only child categories that are public accessible are allowed

    def is_valid(self):
        """
        Validate if the bbox or lon/lat variable are filled in
        """
        data = self.form.data
        if data.get('bbox') or (data.get('lon') and data.get('lat')):
            return self.form.is_valid()

        raise ValidationError(
            {"non_field_errors": ["Either bbox or lon/lat must be filled in"]})

    def filter_queryset(self, queryset):
        """
        Filters Signal's in a given bbox and category
        """
        bbox = self.form.cleaned_data.pop('bbox', None)
        bbox = bbox.split(',') if bbox else None
        main_categories = self.form.cleaned_data.pop('maincategory_slug')
        sub_categories = self.form.cleaned_data.pop('category_slug')

        lat = self.form.cleaned_data.pop('lat', None)
        lon = self.form.cleaned_data.pop('lon', None)

        geometrie_filter = Q(location__geometrie__within=Polygon.from_bbox(bbox)) if bbox \
            else Q(location__geometrie=Point(float(lon), float(lat), srid=4326))

        category_filter = Q()

        if main_categories:
            category_filter &= Q(category_assignment__category__parent_id__in=[
                c.pk for c in main_categories
            ])

        if sub_categories:
            category_filter &= Q(category_assignment__category_id__in=[
                c.pk for c in sub_categories
            ])

        return super().filter_queryset(queryset=queryset.filter(
            category_filter &
            # Filter Signal's in the given bounding box
            geometrie_filter))