Пример #1
0
    def _get_queryset(self, cl, form_data):
        cqs = Checkin.objects.filter(
            position_id=OuterRef('pk'),
            list_id=cl.pk
        ).order_by().values('position_id').annotate(
            m=Max('datetime')
        ).values('m')

        qs = OrderPosition.objects.filter(
            order__event=self.event,
        ).annotate(
            last_checked_in=Subquery(cqs),
            auto_checked_in=Exists(
                Checkin.objects.filter(position_id=OuterRef('pk'), list_id=cl.pk, auto_checked_in=True)
            )
        ).prefetch_related(
            'answers', 'answers__question', 'addon_to__answers', 'addon_to__answers__question'
        ).select_related('order', 'item', 'variation', 'addon_to', 'order__invoice_address', 'voucher', 'seat')

        if not cl.all_products:
            qs = qs.filter(item__in=cl.limit_products.values_list('id', flat=True))

        if cl.subevent:
            qs = qs.filter(subevent=cl.subevent)

        if form_data['sort'] == 'name':
            qs = qs.order_by(
                Coalesce(
                    NullIf('attendee_name_cached', Value('')),
                    NullIf('addon_to__attendee_name_cached', Value('')),
                    NullIf('order__invoice_address__name_cached', Value('')),
                    'order__code'
                )
            )
        elif form_data['sort'] == 'code':
            qs = qs.order_by('order__code')
        elif form_data['sort'].startswith('name:'):
            part = form_data['sort'][5:]
            qs = qs.annotate(
                resolved_name=Case(
                    When(attendee_name_cached__ne='', then='attendee_name_parts'),
                    When(addon_to__attendee_name_cached__isnull=False, addon_to__attendee_name_cached__ne='', then='addon_to__attendee_name_parts'),
                    default='order__invoice_address__name_parts',
                )
            ).annotate(
                resolved_name_part=JSONExtract('resolved_name', part)
            ).order_by(
                'resolved_name_part'
            )

        if form_data.get('attention_only'):
            qs = qs.filter(Q(item__checkin_attention=True) | Q(order__checkin_attention=True))

        if not cl.include_pending:
            qs = qs.filter(order__status=Order.STATUS_PAID)
        else:
            qs = qs.filter(order__status__in=(Order.STATUS_PAID, Order.STATUS_PENDING))

        return qs
Пример #2
0
class SearchContactExportAPIView(SearchContactAPIViewMixin,
                                 SearchExportAPIView):
    """Company search export view."""

    db_sort_by_remappings = {
        'address_country.name': 'computed_country_name',
    }
    queryset = DBContact.objects.annotate(
        name=get_full_name_expression(),
        link=get_front_end_url_expression('contact', 'pk'),
        company_sector_name=get_sector_name_subquery('company__sector'),
        company_link=get_front_end_url_expression('company', 'company__pk'),
        computed_country_name=Case(
            When(address_same_as_company=True,
                 then='company__address_country__name'),
            default='address_country__name',
        ),
        computed_postcode=Case(
            When(address_same_as_company=True,
                 then='company__address_postcode'),
            default='address_postcode',
        ),
        full_telephone_number=ConcatWS(
            Value(' '),
            NullIf('telephone_countrycode', Value('')),
            NullIf('telephone_number', Value('')),
        ),
        date_of_latest_interaction=get_aggregate_subquery(
            DBContact,
            Max('interactions__date'),
        ),
        team_of_latest_interaction=get_top_related_expression_subquery(
            DBInteraction.contacts.field,
            F('dit_team__name'),
            ('-date', ),
        ),
    )
    field_titles = {
        'name': 'Name',
        'job_title': 'Job title',
        'created_on': 'Date created',
        'archived': 'Archived',
        'link': 'Link',
        'company__name': 'Company',
        'company_sector_name': 'Company sector',
        'company_link': 'Company link',
        'company__uk_region__name': 'Company UK region',
        'computed_country_name': 'Country',
        'computed_postcode': 'Postcode',
        'full_telephone_number': 'Phone number',
        'email': 'Email address',
        'accepts_dit_email_marketing': 'Accepts DIT email marketing',
        'date_of_latest_interaction': 'Date of latest interaction',
        'team_of_latest_interaction': 'Team of latest interaction',
        'created_by__dit_team__name': 'Created by team',
    }
Пример #3
0
def get_bracketed_concat_expression(*expressions, expression_to_bracket=None):
    """
    Gets an SQL expression that concatenates a number of expressions and optionally another
    field surrounded by brackets.

    For simple annotation of full names of contacts or advisers, get_full_name_expression() should
    be preferred. However, this function can handle other or more complex scenarios.

    Usage examples:

        # Effectively '{Contact.first_name} {Contact.last_name}'
        Contact.objects.annotate(
            name=get_bracketed_concat_expression('first_name', 'last_name'),
        )

        # Effectively '{Contact.first_name} {Contact.last_name} ({Contact.job_title})'
        # (but the job title would be omitted if blank or NULL)
        Contact.objects.annotate(
            name=get_bracketed_concat_expression(
                'first_name',
                'last_name',
                expression_to_bracket='job_title',
            ),
        )

        # Effectively '{Interaction.dit_adviser.first_name} {Interaction.dit_adviser.last_name}'
        Interaction.objects.annotate(
            dit_adviser_name=get_bracketed_concat_expression(
                'dit_adviser__first_name',
                'dit_adviser__last_name',
            ),
        )
    """
    parts = [
        NullIf(field, Value('', output_field=CharField()))
        for field in expressions
    ]

    if expression_to_bracket:
        bracketed_expression = PreferNullConcat(
            Value('('),
            NullIf(expression_to_bracket, Value('', output_field=CharField())),
            Value(')'),
        )
        parts.append(bracketed_expression)

    return ConcatWS(Value(' ', output_field=CharField()),
                    *parts,
                    output_field=CharField())
def _full_name_concat(first_name_field, last_name_field, bracketed_field=None):
    parts = [
        NullIf(first_name_field, Value('')),
        NullIf(last_name_field, Value('')),
    ]

    if bracketed_field:
        bracketed_expression = PreferNullConcat(
            Value('('),
            NullIf(bracketed_field, Value('')),
            Value(')'),
        )
        parts.append(bracketed_expression)

    return ConcatWS(Value(' '), *parts)
Пример #5
0
 def get_field(self):
     return Coalesce(
         Cast('debateteam__teamscore__votes_given', FloatField()) /
         NullIf('debateteam__teamscore__votes_possible',
                0,
                output_field=FloatField()) * self.adjs_per_debate,
         Value(0.0))
Пример #6
0
    def annotate_bookmarks(self, obj_list):
        """Hoist up bookmark annoations."""
        ub_filter = self.get_userbookmark_filter()

        # Hoist up: are the children finished or unfinished?
        finished_aggregate = Cast(
            NullIf(
                Coalesce(
                    Avg(  # distinct average of user's finished values
                        "comic__userbookmark__finished",
                        filter=ub_filter,
                        distinct=True,
                        output_field=DecimalField(),
                    ),
                    False,  # Null db values counted as False
                ),
                Value(0.5),  # Null result if mixed true & false
            ),
            BooleanField(),  # Finally ends up as a ternary boolean
        )

        # Hoist up the bookmark
        bookmark_sum = Sum("comic__userbookmark__bookmark", filter=ub_filter)

        obj_list = obj_list.annotate(finished=finished_aggregate, bookmark=bookmark_sum)

        return obj_list
Пример #7
0
 def test_null_literal(self):
     msg = "Oracle does not allow Value(None) for expression1."
     with self.assertRaisesMessage(ValueError, msg):
         list(
             Author.objects.annotate(nullif=NullIf(Value(None), "name")).values_list(
                 "nullif"
             )
         )
Пример #8
0
 def filter(self, qs, value):
     if value and len(value) == 1 and value[0] in self.null_last_fields:
         qs = qs.annotate(
             **{
                 f'{value[0]}__nullif': NullIf(f'{value[0]}', Value(''))
             }).order_by(f'{value[0]}__nullif')
         return qs
     return super().filter(qs, value)
Пример #9
0
 def test_basic(self):
     authors = Author.objects.annotate(
         nullif=NullIf('alias', 'name')).values_list('nullif')
     self.assertSequenceEqual(
         authors,
         [('smithj', ),
          ('' if connection.features.interprets_empty_strings_as_nulls else
           None, )])
Пример #10
0
 def form_filter_queryset(self):
     """
     In our filter forms, we want to display the sectors by group.
     """
     return (self.select_related("group").exclude(
         group=None)  # sector must have a group !
             .annotate(
                 sector_is_autre=NullIf("name", Value("Autre"))).order_by(
                     "group__id", "sector_is_autre"))
Пример #11
0
def get_other_field_if_null_or_empty_expression(field_a, field_b, default=None):
    """
    Get other field_b value if field_a is null or empty.

    If field_b is null, return empty string.
    """
    return Coalesce(
        NullIf(field_a, Value('')),
        field_b,
        default,
    )
Пример #12
0
 def test_basic(self):
     authors = Author.objects.annotate(nullif=NullIf("alias", "name")).values_list(
         "nullif"
     )
     self.assertCountEqual(
         authors,
         [
             ("smithj",),
             (
                 ""
                 if connection.features.interprets_empty_strings_as_nulls
                 else None,
             ),
         ],
     )
Пример #13
0
 def _add_formal_type_ids(self, qs):
     # define the integer pubchem_cid field as CharField
     cast = Cast(
         "measurement__measurement_type__metabolite__pubchem_cid",
         output_field=CharField(),
     )
     prefix = Value("cid:", output_field=CharField())
     # instruct database to give PubChem ID in the cid:N format, or None
     qs = qs.annotate(anno_pubchem=NullIf(Concat(prefix, cast), prefix))
     # grab formal type IDs if able, otherwise empty string
     qs = qs.annotate(anno_formal_type=Coalesce(
         "anno_pubchem",
         "measurement__measurement_type__proteinidentifier__accession_id",
         Value("", output_field=CharField()),
         output_field=CharField(),
     ))
     return qs
Пример #14
0
    def as_sql(self, compiler, connection):
        language = translation.get_language()
        fallback_config = getattr(settings, "LOCALIZED_FIELDS_FALLBACKS", {})
        target_languages = fallback_config.get(language, [])
        if not target_languages and language != settings.LANGUAGE_CODE:
            target_languages.append(settings.LANGUAGE_CODE)

        if language:
            target_languages.insert(0, language)

        if len(target_languages) > 1:
            return Coalesce(*[
                NullIf(KeyTransform(language, self.lhs), Value(""))
                for language in target_languages
            ]).as_sql(compiler, connection)

        return KeyTransform(target_languages[0],
                            self.lhs).as_sql(compiler, connection)
Пример #15
0
    def employer_median_salaries(self):
        if not hasattr(self, '_employer_median_salaries'):
            median_base_pay = Percentile('positions__jobs__salaries__amount',
                                         0.5,
                                         filter=self.salary_q,
                                         output_field=FloatField())
            median_extra_pay = Percentile(
                'positions__jobs__salaries__extra_pay',
                0.5,
                filter=self.salary_q,
                output_field=FloatField())
            median_total_pay = Percentile(NullIf(
                Coalesce('positions__jobs__salaries__amount', 0) +
                Coalesce('positions__jobs__salaries__extra_pay', 0), 0),
                                          0.5,
                                          filter=self.salary_q,
                                          output_field=FloatField())

            self._employer_median_salaries = self.employer_queryset.aggregate(
                median_base_pay=Coalesce(median_base_pay, 0),
                median_extra_pay=Coalesce(median_extra_pay, 0),
                median_total_pay=Coalesce(median_total_pay, 0))

        return self._employer_median_salaries
Пример #16
0
 def test_too_few_args(self):
     msg = "'NullIf' takes exactly 2 arguments (1 given)"
     with self.assertRaisesMessage(TypeError, msg):
         NullIf("name")
Пример #17
0
    def get(self, request, *args, **kwargs):

        # -----> ACTUAL SIZE <-------
        base = BasePart.objects.values('program__pk').filter(
            program=OuterRef("pk")).annotate(total=Sum('lines_current_base'))
        added = BasePart.objects.values('program__pk').filter(
            program=OuterRef("pk")).annotate(total=Sum('lines_current_added'))
        deleted = BasePart.objects.values('program__pk').filter(
            program=OuterRef("pk")).annotate(
                total=Sum('lines_current_deleted'))
        reused = ReusedPart.objects.values('program__pk').filter(
            program=OuterRef("pk")).annotate(total=Sum('current_lines'))

        self.data["actual_size"] = Program.objects.values('pk', 'name').filter(
            programmer=self.user,
            finish_date__isnull=False).annotate(total=Coalesce(
                Subquery(base.values('total')) -
                Subquery(deleted.values('total')) +
                Subquery(reused.values('total')) +
                Subquery(added.values('total')), 0))
        # -----> END ACTUAL SIZE <------

        # -----> Defects Removed <--------
        self.data["defects_removed"] = Program.objects.filter(
            programmer=self.user, finish_date__isnull=False).annotate(
                planning=Count('name',
                               Q(get_defects__phase_removed__name='Planning')),
                design=Count('name',
                             Q(get_defects__phase_removed__name='Design')),
                design_review=Count(
                    'name',
                    Q(get_defects__phase_removed__name='Design Review')),
                codification=Count(
                    'name',
                    Q(get_defects__phase_removed__name='Codification')),
                codification_review=Count(
                    'name',
                    Q(get_defects__phase_removed__name='Codification Review')),
                compilation=Count(
                    'name', Q(get_defects__phase_removed__name='Compilation')),
                unit_test=Count(
                    'name', Q(get_defects__phase_removed__name='Unit Test')),
                postmortem=Count(
                    'name',
                    Q(get_defects__phase_removed__name='Postmortem'))).values(
                        'pk', 'name', 'planning', 'design', 'design_review',
                        'codification', 'codification_review', 'compilation',
                        'unit_test', 'postmortem').order_by('created_at')
        # -----> END Defects removed <------

        # -----> Defects Injected <-------
        self.data["defects_injected"] = Program.objects.filter(
            programmer=self.user, finish_date__isnull=False
        ).annotate(
            planning=Count('name',
                           Q(get_defects__phase_injected__name='Planning')),
            design=Count('name',
                         Q(get_defects__phase_injected__name='Design')),
            design_review=Count(
                'name', Q(get_defects__phase_injected__name='Design Review')),
            codification=Count(
                'name', Q(get_defects__phase_injected__name='Codification')),
            codification_review=Count(
                'name',
                Q(get_defects__phase_injected__name='Codification Review')),
            compilation=Count(
                'name', Q(get_defects__phase_injected__name='Compilation')),
            unit_test=Count('name',
                            Q(get_defects__phase_injected__name='Unit Test')),
            postmortem=Count(
                'name',
                Q(get_defects__phase_injected__name='Postmortem'))).values(
                    'pk', 'name', 'planning', 'design', 'design_review',
                    'codification', 'codification_review', 'compilation',
                    'unit_test', 'postmortem').order_by('created_at')
        # ------> END Defects injected <------

        # -----> Total time <--------
        self.data["total_time"] = Program.objects.filter(
            programmer=self.user, finish_date__isnull=False).annotate(
                total=TIME_TOTAL_BY_PROGRAM).values(
                    'pk', 'name', 'total').order_by('created_at')
        # -----> END total time <------

        # -----> Failure COQ <--------
        # Hago dos subconsultas que retornan el tiempo dedicado en la fase de compilación y de pruebas
        timelogComp = TimeLog.objects.filter(
            program__pk=OuterRef("pk"), phase__name='Compilation').annotate(
                time=Ceil(F('delta_time') / 60.0)).values('time')[:1]
        timelogUnit = TimeLog.objects.filter(
            program__pk=OuterRef("pk"), phase__name='Unit Test').annotate(
                time=Ceil(F('delta_time') / 60.0)).values('time')[:1]

        self.data["failure_COQ"] = Program.objects.filter(
            programmer=self.user,
            finish_date__isnull=False).annotate(total=Coalesce(
                100 * ((Coalesce(Subquery(timelogComp), 0) +
                        Coalesce(Subquery(timelogUnit), 0)) /
                       NullIf(TIME_TOTAL_BY_PROGRAM, 0)), 0)).values(
                           'pk', 'name', 'total').order_by('created_at')
        # ------> END Failure COQ <------

        # ------> Appraisal Cost Of Quality <---------
        # Hago dos subconsultas que retornan el tiempo dedicado en la fase de revisión de diseño y revisión de código
        timelog_design_review = TimeLog.objects.filter(
            program__pk=OuterRef("pk"), phase__name='Design Review').annotate(
                time=Ceil(F('delta_time') / 60.0)).values('time')[:1]
        timelog_code_review = TimeLog.objects.filter(
            program__pk=OuterRef("pk"),
            phase__name='Codification Review').annotate(
                time=Ceil(F('delta_time') / 60.0)).values('time')[:1]

        self.data["appraisal_COQ"] = Program.objects.filter(
            programmer=self.user,
            finish_date__isnull=False).annotate(total=Coalesce(
                100 * ((Coalesce(Subquery(timelog_design_review), 0) +
                        Coalesce(Subquery(timelog_code_review), 0)) /
                       NullIf(TIME_TOTAL_BY_PROGRAM, 0)), 0)).values(
                           'pk', 'name', 'total').order_by('created_at')
        # -------> END Total defects <-------

        # ------> Total defects <---------
        self.data["total_defects"] = Program.objects.filter(
            programmer=self.user, finish_date__isnull=False).annotate(
                total=Count('get_defects')).values('pk', 'name', 'total')
        # ------> END total defects <-------

        return Response(data=self.data, status=status.HTTP_200_OK)
Пример #18
0
 def test_null_argument(self):
     authors = Author.objects.annotate(
         nullif=NullIf('name', Value(None))).values_list('nullif')
     self.assertSequenceEqual(authors, [('John Smith', ), ('Rhonda', )])
Пример #19
0
class SearchContactExportAPIView(SearchContactAPIViewMixin,
                                 SearchExportAPIView):
    """Company search export view."""
    def _is_valid_email(self, value):
        """Validate if emails are valid and return a boolean flag."""
        try:
            validate_email(value)
            return True
        except ValidationError:
            return False

    consent_page_size = 100

    db_sort_by_remappings = {
        'address_country.name': 'computed_country_name',
    }
    queryset = DBContact.objects.annotate(
        name=get_full_name_expression(),
        link=get_front_end_url_expression('contact', 'pk'),
        company_sector_name=get_sector_name_subquery('company__sector'),
        company_link=get_front_end_url_expression('company', 'company__pk'),
        computed_country_name=Case(
            When(address_same_as_company=True,
                 then='company__address_country__name'),
            default='address_country__name',
        ),
        computed_postcode=Case(
            When(address_same_as_company=True,
                 then='company__address_postcode'),
            default='address_postcode',
        ),
        full_telephone_number=ConcatWS(
            Value(' '),
            NullIf('telephone_countrycode', Value('')),
            NullIf('telephone_number', Value('')),
        ),
        date_of_latest_interaction=get_aggregate_subquery(
            DBContact,
            Max('interactions__date'),
        ),
        teams_of_latest_interaction=get_top_related_expression_subquery(
            DBInteraction.contacts.field,
            get_string_agg_subquery(DBInteraction,
                                    'dit_participants__team__name',
                                    distinct=True),
            ('-date', ),
        ),
    )
    field_titles = {
        'name': 'Name',
        'job_title': 'Job title',
        'created_on': 'Date created',
        'archived': 'Archived',
        'link': 'Link',
        'company__name': 'Company',
        'company_sector_name': 'Company sector',
        'company_link': 'Company link',
        'company__uk_region__name': 'Company UK region',
        'computed_country_name': 'Country',
        'computed_postcode': 'Postcode',
        'full_telephone_number': 'Phone number',
        'email': 'Email address',
        'accepts_dit_email_marketing': 'Accepts DIT email marketing',
        'date_of_latest_interaction': 'Date of latest interaction',
        'teams_of_latest_interaction': 'Teams of latest interaction',
        'created_by__dit_team__name': 'Created by team',
    }

    def _add_consent_response(self, rows):
        """
        Transforms iterable to add user consent from the consent service.

        The consent lookup makes an external API call to return consent.
        For perfromance reasons the consent amount is limited by consent_page_size.
        Due to this limitaion the iterable are sliced into chunks requesting consent for 100 rows
        at a time.
        """
        # Slice iterable into chunks
        row_chunks = slice_iterable_into_chunks(rows, self.consent_page_size)
        for chunk in row_chunks:
            """
            Loop over the chunks and extract the email and item.
            Save the item because the iterator cannot be used twice.
            """
            rows = list(chunk)
            # Peform constent lookup on emails POST request
            consent_lookups = consent.get_many([
                row['email']
                for row in rows if self._is_valid_email(row['email'])
            ], )
            for row in rows:
                # Assign contact consent boolean to accepts_dit_email_marketing
                # and yield modified result.
                row['accepts_dit_email_marketing'] = consent_lookups.get(
                    row['email'], False)
                yield row

    def _get_rows(self, ids, search_ordering):
        """
        Get row queryset for constent service.

        This populates accepts_dit_email_marketing field from the consent service and
        removes the accepts_dit_email_marketing from the field query because the field is not in
        the db.
        """
        db_ordering = self._translate_search_ordering_to_django_ordering(
            search_ordering)
        field_titles = self.field_titles.copy()
        del field_titles['accepts_dit_email_marketing']
        rows = self.queryset.filter(pk__in=ids, ).order_by(
            *db_ordering, ).values(*field_titles, ).iterator()

        return self._add_consent_response(rows)
Пример #20
0
 def test_null_argument(self):
     authors = Author.objects.annotate(
         nullif=NullIf("name", Value(None))
     ).values_list("nullif")
     self.assertCountEqual(authors, [("John Smith",), ("Rhonda",)])
Пример #21
0
    def _get_queryset(self, cl, form_data):
        cqs = Checkin.objects.filter(
            position_id=OuterRef('pk'),
            list_id=cl.pk).order_by().values('position_id').annotate(
                m=Max('datetime')).values('m')

        cqsin = cqs.filter(type=Checkin.TYPE_ENTRY)
        cqsout = cqs.filter(type=Checkin.TYPE_EXIT)

        qs = OrderPosition.objects.filter(order__event=self.event, ).annotate(
            last_checked_in=Subquery(cqsin),
            last_checked_out=Subquery(cqsout),
            auto_checked_in=Exists(
                Checkin.objects.filter(
                    position_id=OuterRef('pk'),
                    list_id=cl.pk,
                    auto_checked_in=True))).prefetch_related(
                        'answers', 'answers__question', 'addon_to__answers',
                        'addon_to__answers__question').select_related(
                            'order', 'item', 'variation', 'addon_to',
                            'order__invoice_address', 'voucher', 'seat')

        if not cl.all_products:
            qs = qs.filter(
                item__in=cl.limit_products.values_list('id', flat=True))

        if cl.subevent:
            qs = qs.filter(subevent=cl.subevent)

        if form_data.get('date_from'):
            dt = make_aware(
                datetime.combine(
                    dateutil.parser.parse(form_data['date_from']).date(),
                    time(hour=0, minute=0, second=0)), self.event.timezone)
            qs = qs.filter(subevent__date_from__gte=dt)

        if form_data.get('date_to'):
            dt = make_aware(
                datetime.combine(
                    dateutil.parser.parse(form_data['date_to']).date() +
                    timedelta(days=1), time(hour=0, minute=0, second=0)),
                self.event.timezone)
            qs = qs.filter(subevent__date_from__lt=dt)

        o = ()
        if self.event.has_subevents and not cl.subevent:
            o = ('subevent__date_from', 'subevent__name')

        sort = form_data.get('sort') or 'name'
        if sort == 'name':
            qs = qs.order_by(
                *o,
                Coalesce(
                    NullIf('attendee_name_cached', Value('')),
                    NullIf('addon_to__attendee_name_cached', Value('')),
                    NullIf('order__invoice_address__name_cached', Value('')),
                    'order__code'))
        elif sort == 'code':
            qs = qs.order_by(*o, 'order__code')
        elif sort.startswith('name:'):
            part = sort[5:]
            qs = qs.annotate(resolved_name=Case(
                When(attendee_name_cached__ne='', then='attendee_name_parts'),
                When(addon_to__attendee_name_cached__isnull=False,
                     addon_to__attendee_name_cached__ne='',
                     then='addon_to__attendee_name_parts'),
                default='order__invoice_address__name_parts',
            )).annotate(resolved_name_part=JSONExtract(
                'resolved_name', part)).order_by(*o, 'resolved_name_part')

        if form_data.get('attention_only'):
            qs = qs.filter(
                Q(item__checkin_attention=True)
                | Q(order__checkin_attention=True))

        if not cl.include_pending:
            qs = qs.filter(order__status=Order.STATUS_PAID)
        else:
            qs = qs.filter(order__status__in=(Order.STATUS_PAID,
                                              Order.STATUS_PENDING))

        return qs