Exemple #1
0
    def testCallLogSync(self):
        self.deleteAllLogs()
        self.assertEqual(self.getCallLogCount(), 0)
        self.assertEqual(self.getCallCount(), 0)

        # Test Create
        calllog = CallLog()
        self.setRandomCallLogValues(calllog)
        calllog.save()

        sleep(1)
        self.assertEqual(self.getCallLogCount(), 1)
        self.assertEqual(self.getCallCount(), 1)

        call = Call.objects.get(couch_id=calllog._id)
        self.checkFieldValues(calllog, call, Call._migration_get_fields())
        self.assertTrue(CallLog.get_db().get_rev(calllog._id).startswith('1-'))

        # Test Update
        self.setRandomCallLogValues(calllog)
        calllog.save()

        sleep(1)
        self.assertEqual(self.getCallLogCount(), 1)
        self.assertEqual(self.getCallCount(), 1)
        call = Call.objects.get(couch_id=calllog._id)
        self.checkFieldValues(calllog, call, Call._migration_get_fields())
        self.assertTrue(CallLog.get_db().get_rev(calllog._id).startswith('2-'))
    def test_401_response(self):
        start_count = Call.by_domain(self.domain).count()

        response = Client().post('/twilio/ivr/xxxxx', {
            'From': self.phone_number,
            'CallSid': 'xyz',
        })
        self.assertEqual(response.status_code, 401)

        end_count = Call.by_domain(self.domain).count()
        self.assertEqual(start_count, end_count)
Exemple #3
0
    def test_401_response(self):
        with self.create_case():
            start_count = Call.by_domain(self.domain).count()

            response = Client().post('/twilio/ivr/xxxxx', {
                'From': self.phone_number,
                'CallSid': 'xyz',
            })
            self.assertEqual(response.status_code, 401)

            end_count = Call.by_domain(self.domain).count()
            self.assertEqual(start_count, end_count)
Exemple #4
0
    def test_log_call(self):
        if self.__class__ == LogCallTestCase:
            # The test runner picks up this base class too, but we only
            # want to run the test on subclasses.
            return

        self.assertEqual(Call.by_domain(self.domain).count(), 0)
        response = self.simulate_inbound_call(self.phone_number)
        self.check_response(response)
        self.assertEqual(Call.by_domain(self.domain).count(), 1)

        call = Call.by_domain(self.domain)[0]
        self.assertEqual(call.couch_recipient_doc_type, 'CommCareCase')
        self.assertEqual(call.couch_recipient, self.case.get_id)
        self.assertEqual(call.direction, INCOMING)
Exemple #5
0
def ivr_finished(request):
    """
    Kookoo invokes this view after a call is finished (whether answered or not)
    with status and some statistics.
    Point Kookoo's 'callback_url' parameter here.
    """
    # Retrieve all parameters
    status = request.POST.get("status", None)
    start_time = request.POST.get("start_time", None)
    caller_id = request.POST.get("caller_id", None)
    phone_no = request.POST.get("phone_no", None)
    sid = request.POST.get("sid", "")
    duration = request.POST.get("duration", None)
    ringing_time = request.POST.get("ringing_time", None)
    status_details = request.POST.get("status_details", None)

    gateway_session_id = "KOOKOO-" + sid

    with CriticalSection([gateway_session_id], timeout=300):
        call = Call.by_gateway_session_id(gateway_session_id)
        if call:
            log_metadata_received(call)
            try:
                duration = int(duration)
            except Exception:
                duration = None
            call.answered = (status == 'answered')
            call.duration = duration
            call.save()

    return HttpResponse('')
Exemple #6
0
def incoming(phone_number,
             gateway_session_id,
             ivr_event,
             backend=None,
             input_data=None,
             duration=None):
    """
    The main entry point for all incoming IVR requests.
    """
    call = Call.by_gateway_session_id(gateway_session_id)
    logged_subevent = None
    if call and call.messaging_subevent_id:
        logged_subevent = MessagingSubEvent.objects.get(
            pk=call.messaging_subevent_id)

    if call:
        add_metadata(call, duration)

    if call and call.form_unique_id is None:
        # If this request is for a call with no form,
        # then just short circuit everything and hang up
        return hang_up_response(gateway_session_id, backend=backend)

    if call and backend:
        return handle_known_call_session(call,
                                         backend,
                                         ivr_event,
                                         input_data=input_data,
                                         logged_subevent=logged_subevent)
    else:
        if not call:
            log_call(phone_number, gateway_session_id, backend=backend)
        return hang_up_response(gateway_session_id, backend=backend)
Exemple #7
0
def incoming(phone_number, gateway_session_id, ivr_event, backend=None, input_data=None,
        duration=None):
    """
    The main entry point for all incoming IVR requests.
    """
    call = Call.by_gateway_session_id(gateway_session_id)
    logged_subevent = None
    if call and call.messaging_subevent_id:
        logged_subevent = MessagingSubEvent.objects.get(
            pk=call.messaging_subevent_id)

    if call:
        add_metadata(call, duration)

    if call and call.form_unique_id is None:
        # If this request is for a call with no form,
        # then just short circuit everything and hang up
        return hang_up_response(gateway_session_id, backend=backend)

    if call and backend:
        return handle_known_call_session(call, backend, ivr_event,
            input_data=input_data, logged_subevent=logged_subevent)
    else:
        if not call:
            log_call(phone_number, gateway_session_id, backend=backend)
        return hang_up_response(gateway_session_id, backend=backend)
Exemple #8
0
def ivr_finished(request):
    """
    Kookoo invokes this view after a call is finished (whether answered or not)
    with status and some statistics.
    Point Kookoo's 'callback_url' parameter here.
    """
    # Retrieve all parameters
    status = request.POST.get("status", None)
    start_time = request.POST.get("start_time", None)
    caller_id = request.POST.get("caller_id", None)
    phone_no = request.POST.get("phone_no", None)
    sid = request.POST.get("sid", "")
    duration = request.POST.get("duration", None)
    ringing_time = request.POST.get("ringing_time", None)
    status_details = request.POST.get("status_details", None)

    gateway_session_id = "KOOKOO-" + sid

    with CriticalSection([gateway_session_id], timeout=300):
        call = Call.by_gateway_session_id(gateway_session_id)
        if call:
            log_metadata_received(call)
            try:
                duration = int(duration)
            except Exception:
                duration = None
            call.answered = status == "answered"
            call.duration = duration
            call.save()

    return HttpResponse("")
Exemple #9
0
    def test_log_call(self):
        with self.create_case() as case:
            if self.__class__ == LogCallTestCase:
                # The test runner picks up this base class too, but we only
                # want to run the test on subclasses.
                return

            self.assertEqual(Call.by_domain(self.domain).count(), 0)
            response = self.simulate_inbound_call(self.phone_number)
            self.check_response(response)
            self.assertEqual(Call.by_domain(self.domain).count(), 1)

            call = Call.by_domain(self.domain)[0]
            self.assertEqual(call.couch_recipient_doc_type, 'CommCareCase')
            self.assertEqual(call.couch_recipient, case.case_id)
            self.assertEqual(call.direction, INCOMING)
Exemple #10
0
def ivr_in(request):
    """
    Handles tropo call requests
    """
    if request.method == "POST":
        data = json.loads(request.body)
        phone_number = data["session"]["from"]["id"]
        # TODO: Implement tropo as an ivr backend. In the meantime, just log the call.

        if phone_number:
            cleaned_number = strip_plus(phone_number)
            v = PhoneNumber.by_extensive_search(cleaned_number)
        else:
            v = None

        # Save the call entry
        msg = Call(
            phone_number=cleaned_number,
            direction=INCOMING,
            date=datetime.utcnow(),
            backend_api=SQLTropoBackend.get_api_id(),
        )
        if v is not None:
            msg.domain = v.domain
            msg.couch_recipient_doc_type = v.owner_doc_type
            msg.couch_recipient = v.owner_id
        msg.save()

        t = Tropo()
        t.reject()
        return HttpResponse(t.RenderJson())
    else:
        return HttpResponseBadRequest("Bad Request")
def fire_ivr_survey_event(reminder, handler, recipients, verified_numbers,
                          logged_event):
    domain_obj = Domain.get_by_name(reminder.domain, strict=True)
    for recipient in recipients:
        initiate_call = True
        if reminder.callback_try_count > 0 and reminder.event_initiation_timestamp:
            initiate_call = not Call.answered_call_exists(
                recipient.doc_type, recipient.get_id,
                reminder.event_initiation_timestamp)

        if initiate_call:
            if (is_commcarecase(recipient)
                    and not handler.force_surveys_to_use_triggered_case):
                case_id = recipient.case_id
            else:
                case_id = reminder.case_id
            verified_number, unverified_number = get_recipient_phone_number(
                reminder, recipient, verified_numbers)
            if verified_number:
                initiate_outbound_call.delay(
                    recipient,
                    reminder.current_event.form_unique_id,
                    handler.submit_partial_forms,
                    handler.include_case_side_effects,
                    handler.max_question_retries,
                    logged_event.pk,
                    verified_number=verified_number,
                    case_id=case_id,
                    case_for_case_submission=handler.
                    force_surveys_to_use_triggered_case,
                    timestamp=CaseReminderHandler.get_now(),
                )
            elif domain_obj.send_to_duplicated_case_numbers and unverified_number:
                initiate_outbound_call.delay(
                    recipient,
                    reminder.current_event.form_unique_id,
                    handler.submit_partial_forms,
                    handler.include_case_side_effects,
                    handler.max_question_retries,
                    logged_event.pk,
                    unverified_number=unverified_number,
                    case_id=case_id,
                    case_for_case_submission=handler.
                    force_surveys_to_use_triggered_case,
                    timestamp=CaseReminderHandler.get_now(),
                )
            else:
                # initiate_outbound_call will create the subevent automatically,
                # so since we're not initiating the call here, we have to create
                # the subevent explicitly in order to log the error.
                logged_subevent = logged_event.create_subevent(
                    handler, reminder, recipient)
                logged_subevent.error(MessagingEvent.ERROR_NO_PHONE_NUMBER)
Exemple #12
0
def ivr_in(request):
    """
    Handles tropo call requests
    """
    if request.method == "POST":
        data = json.loads(request.body)
        phone_number = data["session"]["from"]["id"]
        # TODO: Implement tropo as an ivr backend. In the meantime, just log the call.

        if phone_number:
            cleaned_number = strip_plus(phone_number)
            v = VerifiedNumber.by_extensive_search(cleaned_number)
        else:
            v = None

        # Save the call entry
        msg = Call(
            phone_number=cleaned_number,
            direction=INCOMING,
            date=datetime.utcnow(),
            backend_api=SQLTropoBackend.get_api_id(),
        )
        if v is not None:
            msg.domain = v.domain
            msg.couch_recipient_doc_type = v.owner_doc_type
            msg.couch_recipient = v.owner_id
        msg.save()

        t = Tropo()
        t.reject()
        return HttpResponse(t.RenderJson())
    else:
        return HttpResponseBadRequest("Bad Request")
def fire_ivr_survey_event(reminder, handler, recipients, verified_numbers, logged_event):
    domain_obj = Domain.get_by_name(reminder.domain, strict=True)
    for recipient in recipients:
        initiate_call = True
        if reminder.callback_try_count > 0 and reminder.event_initiation_timestamp:
            initiate_call = not Call.answered_call_exists(
                recipient.doc_type,
                recipient.get_id,
                reminder.event_initiation_timestamp
            )

        if initiate_call:
            if (is_commcarecase(recipient) and
                not handler.force_surveys_to_use_triggered_case):
                case_id = recipient.case_id
            else:
                case_id = reminder.case_id
            verified_number, unverified_number = get_recipient_phone_number(
                reminder, recipient, verified_numbers)
            if verified_number:
                initiate_outbound_call.delay(
                    recipient,
                    reminder.current_event.form_unique_id,
                    handler.submit_partial_forms,
                    handler.include_case_side_effects,
                    handler.max_question_retries,
                    logged_event.pk,
                    verified_number=verified_number,
                    case_id=case_id,
                    case_for_case_submission=handler.force_surveys_to_use_triggered_case,
                    timestamp=CaseReminderHandler.get_now(),
                )
            elif domain_obj.send_to_duplicated_case_numbers and unverified_number:
                initiate_outbound_call.delay(
                    recipient,
                    reminder.current_event.form_unique_id,
                    handler.submit_partial_forms,
                    handler.include_case_side_effects,
                    handler.max_question_retries,
                    logged_event.pk,
                    unverified_number=unverified_number,
                    case_id=case_id,
                    case_for_case_submission=handler.force_surveys_to_use_triggered_case,
                    timestamp=CaseReminderHandler.get_now(),
                )
            else:
                # initiate_outbound_call will create the subevent automatically,
                # so since we're not initiating the call here, we have to create
                # the subevent explicitly in order to log the error.
                logged_subevent = logged_event.create_subevent(handler, reminder, recipient)
                logged_subevent.error(MessagingEvent.ERROR_NO_PHONE_NUMBER)
def fire_sms_callback_event(reminder, handler, recipients, verified_numbers,
                            logged_event):
    current_event = reminder.current_event

    for recipient in recipients:
        send_message = False
        if reminder.callback_try_count > 0:
            if reminder.event_initiation_timestamp:
                event = ExpectedCallback.by_domain_recipient_date(
                    reminder.domain, recipient.get_id,
                    reminder.event_initiation_timestamp)
                if not event:
                    continue
                if event.status == CALLBACK_RECEIVED:
                    continue
                if Call.inbound_entry_exists(
                        recipient.doc_type, recipient.get_id,
                        reminder.event_initiation_timestamp):
                    event.status = CALLBACK_RECEIVED
                    event.save()
                    continue
            else:
                continue

            if (reminder.callback_try_count >= len(
                    current_event.callback_timeout_intervals)):
                # On the last callback timeout, instead of sending the SMS
                # again, log the missed callback
                if event:
                    event.status = CALLBACK_MISSED
                    event.save()
            else:
                send_message = True
        else:
            # It's the first time sending the sms, so create an expected
            # callback event
            send_message = True
            event = ExpectedCallback.objects.create(
                domain=reminder.domain,
                date=reminder.event_initiation_timestamp,
                couch_recipient_doc_type=recipient.doc_type,
                couch_recipient=recipient.get_id,
                status=CALLBACK_PENDING,
            )

        if send_message:
            fire_sms_event(reminder,
                           handler, [recipient],
                           verified_numbers,
                           logged_event,
                           workflow=WORKFLOW_CALLBACK)
def fire_sms_callback_event(reminder, handler, recipients, verified_numbers, logged_event):
    current_event = reminder.current_event

    for recipient in recipients:
        send_message = False
        if reminder.callback_try_count > 0:
            if reminder.event_initiation_timestamp:
                event = ExpectedCallback.by_domain_recipient_date(
                    reminder.domain,
                    recipient.get_id,
                    reminder.event_initiation_timestamp
                )
                if not event:
                    continue
                if event.status == CALLBACK_RECEIVED:
                    continue
                if Call.inbound_entry_exists(
                    recipient.doc_type,
                    recipient.get_id,
                    reminder.event_initiation_timestamp
                ):
                    event.status = CALLBACK_RECEIVED
                    event.save()
                    continue
            else:
                continue

            if (reminder.callback_try_count >=
                len(current_event.callback_timeout_intervals)):
                # On the last callback timeout, instead of sending the SMS
                # again, log the missed callback
                if event:
                    event.status = CALLBACK_MISSED
                    event.save()
            else:
                send_message = True
        else:
            # It's the first time sending the sms, so create an expected
            # callback event
            send_message = True
            event = ExpectedCallback.objects.create(
                domain=reminder.domain,
                date=reminder.event_initiation_timestamp,
                couch_recipient_doc_type=recipient.doc_type,
                couch_recipient=recipient.get_id,
                status=CALLBACK_PENDING,
            )

        if send_message:
            fire_sms_event(reminder, handler, [recipient], verified_numbers,
                logged_event, workflow=WORKFLOW_CALLBACK)
Exemple #16
0
def log_call(phone_number, gateway_session_id, backend=None):
    cleaned_number = strip_plus(phone_number)
    v = PhoneNumber.by_extensive_search(cleaned_number)

    call = Call(
        phone_number=cleaned_number,
        direction=INCOMING,
        date=datetime.utcnow(),
        backend_api=backend.get_api_id() if backend else None,
        backend_id=backend.couch_id if backend else None,
        gateway_session_id=gateway_session_id,
    )
    if v:
        call.domain = v.domain
        call.couch_recipient_doc_type = v.owner_doc_type
        call.couch_recipient = v.owner_id
    call.save()
Exemple #17
0
def log_call(phone_number, gateway_session_id, backend=None):
    cleaned_number = strip_plus(phone_number)
    v = PhoneNumber.by_extensive_search(cleaned_number)

    call = Call(
        phone_number=cleaned_number,
        direction=INCOMING,
        date=datetime.utcnow(),
        backend_api=backend.get_api_id() if backend else None,
        backend_id=backend.couch_id if backend else None,
        gateway_session_id=gateway_session_id,
    )
    if v:
        call.domain = v.domain
        call.couch_recipient_doc_type = v.owner_doc_type
        call.couch_recipient = v.owner_id
    call.save()
Exemple #18
0
 def delete_call_logs(self, domain):
     Call.by_domain(domain).delete()
Exemple #19
0
 def get_last_outbound_call(self, contact):
     return Call.get_last_log_for_recipient(contact.doc_type,
                                            contact.get_id,
                                            direction=OUTGOING)
Exemple #20
0
 def delete_call_logs(self, domain):
     Call.by_domain(domain).delete()
Exemple #21
0
 def get_last_outbound_call(self, contact):
     return Call.get_last_log_for_recipient(
         contact.doc_type,
         contact.get_id,
         direction=OUTGOING
     )
Exemple #22
0
def initiate_outbound_call(recipient, form_unique_id, submit_partial_form,
        include_case_side_effects, max_question_retries, messaging_event_id,
        verified_number=None, unverified_number=None, case_id=None,
        case_for_case_submission=False, timestamp=None):
    """
    Returns False if an error occurred and the call should be retried.
    Returns True if the call should not be retried (either because it was
    queued successfully or because an unrecoverable error occurred).
    """
    call = None
    logged_event = MessagingEvent.objects.get(pk=messaging_event_id)
    logged_subevent = logged_event.create_ivr_subevent(recipient,
        form_unique_id, case_id=case_id)

    if not verified_number and not unverified_number:
        log_error(MessagingEvent.ERROR_NO_PHONE_NUMBER,
            logged_subevent=logged_subevent)
        return True

    backend = get_ivr_backend(recipient, verified_number, unverified_number)
    if not backend:
        log_error(MessagingEvent.ERROR_NO_SUITABLE_GATEWAY,
            logged_subevent=logged_subevent)
        return True

    phone_number = (verified_number.phone_number if verified_number
        else unverified_number)

    call = Call(
        couch_recipient_doc_type=recipient.doc_type,
        couch_recipient=recipient.get_id,
        phone_number='+%s' % str(phone_number),
        direction=OUTGOING,
        date=timestamp or datetime.utcnow(),
        domain=recipient.domain,
        form_unique_id=form_unique_id,
        submit_partial_form=submit_partial_form,
        include_case_side_effects=include_case_side_effects,
        max_question_retries=max_question_retries,
        current_question_retry_count=0,
        case_id=case_id,
        case_for_case_submission=case_for_case_submission,
        messaging_subevent_id=logged_subevent.pk,
    )

    ivr_data = None
    if backend.cache_first_ivr_response():
        ivr_data, error = get_first_ivr_response_data(recipient,
            call, logged_subevent)
        if error:
            return True

    if ivr_data:
        logged_subevent.xforms_session = ivr_data.session
        logged_subevent.save()

    try:
        call.backend_api = backend.get_api_id()
        call.backend_id = backend.couch_id
        result = backend.initiate_outbound_call(call, logged_subevent)
        if ivr_data and not call.error:
            backend.set_first_ivr_response(call, call.gateway_session_id, ivr_data)
        call.save()
        logged_subevent.completed()
        return result
    except GatewayConnectionError:
        log_error(MessagingEvent.ERROR_GATEWAY_ERROR, call, logged_subevent)
        raise
    except Exception:
        log_error(MessagingEvent.ERROR_INTERNAL_SERVER_ERROR, call, logged_subevent)
        raise
Exemple #23
0
    def rows(self):
        data = Call.by_domain(
            self.domain,
            start_date=self.datespan.startdate_utc,
            end_date=self.datespan.enddate_utc).order_by('date')
        result = []

        # Store the results of lookups for faster loading
        contact_cache = {}
        form_map = {}
        xforms_sessions = {}

        direction_map = {
            INCOMING: _("Incoming"),
            OUTGOING: _("Outgoing"),
        }

        # Retrieve message log options
        message_log_options = getattr(settings, "MESSAGE_LOG_OPTIONS", {})
        abbreviated_phone_number_domains = message_log_options.get(
            "abbreviated_phone_number_domains", [])
        abbreviate_phone_number = (self.domain
                                   in abbreviated_phone_number_domains)

        for call in data:
            doc_info = self.get_recipient_info(self.domain,
                                               call.couch_recipient_doc_type,
                                               call.couch_recipient,
                                               contact_cache)

            form_unique_id = call.form_unique_id
            if form_unique_id in [None, ""]:
                form_name = "-"
            elif form_unique_id in form_map:
                form_name = form_map.get(form_unique_id)
            else:
                form_name = get_form_name(form_unique_id)
                form_map[form_unique_id] = form_name

            phone_number = call.phone_number
            if abbreviate_phone_number and phone_number is not None:
                phone_number = phone_number[0:7] if phone_number[
                    0:1] == "+" else phone_number[0:6]

            timestamp = ServerTime(call.date).user_time(self.timezone).done()

            if call.direction == INCOMING:
                answered = "-"
            else:
                answered = _("Yes") if call.answered else _("No")

            if call.xforms_session_id:
                xforms_sessions[call.xforms_session_id] = None

            row = [
                call.xforms_session_id,
                self._fmt_timestamp(timestamp),
                self._fmt_contact_link(call.couch_recipient, doc_info),
                self._fmt(phone_number),
                self._fmt(direction_map.get(call.direction, "-")),
                self._fmt(form_name),
                self._fmt("-"),
                self._fmt(answered),
                self._fmt(call.duration),
                self._fmt(_("Yes") if call.error else _("No")),
                self._fmt(
                    cgi.escape(call.error_message) if call.
                    error_message else None),
            ]

            if self.request.couch_user.is_previewer():
                row.append(self._fmt(call.gateway_session_id))

            result.append(row)

        all_session_ids = list(xforms_sessions)
        session_submission_map = dict(
            SQLXFormsSession.objects.filter(
                session_id__in=all_session_ids).values_list(
                    'session_id', 'submission_id'))
        xforms_sessions.update(session_submission_map)

        # Add into the final result the link to the submission based on the
        # outcome of the above lookups.
        final_result = []
        for row in result:
            final_row = row[1:]
            session_id = row[0]
            if session_id:
                submission_id = xforms_sessions[session_id]
                if submission_id:
                    final_row[5] = self._fmt_submission_link(submission_id)
            final_result.append(final_row)

        return final_result
Exemple #24
0
    def rows(self):
        data = Call.by_domain(
            self.domain,
            start_date=self.datespan.startdate_utc,
            end_date=self.datespan.enddate_utc
        ).order_by('date')
        result = []
        
        # Store the results of lookups for faster loading
        contact_cache = {}
        form_map = {}
        xforms_sessions = {}
        
        direction_map = {
            INCOMING: _("Incoming"),
            OUTGOING: _("Outgoing"),
        }
        
        # Retrieve message log options
        message_log_options = getattr(settings, "MESSAGE_LOG_OPTIONS", {})
        abbreviated_phone_number_domains = message_log_options.get("abbreviated_phone_number_domains", [])
        abbreviate_phone_number = (self.domain in abbreviated_phone_number_domains)
        
        for call in data:
            doc_info = self.get_recipient_info(call.couch_recipient_doc_type,
                call.couch_recipient, contact_cache)

            form_unique_id = call.form_unique_id
            if form_unique_id in [None, ""]:
                form_name = "-"
            elif form_unique_id in form_map:
                form_name = form_map.get(form_unique_id)
            else:
                form_name = get_form_name(form_unique_id)
                form_map[form_unique_id] = form_name
            
            phone_number = call.phone_number
            if abbreviate_phone_number and phone_number is not None:
                phone_number = phone_number[0:7] if phone_number[0:1] == "+" else phone_number[0:6]
            
            timestamp = ServerTime(call.date).user_time(self.timezone).done()
            
            if call.direction == INCOMING:
                answered = "-"
            else:
                answered = _("Yes") if call.answered else _("No")
            
            if call.xforms_session_id:
                xforms_sessions[call.xforms_session_id] = None
            
            row = [
                call.xforms_session_id,
                self._fmt_timestamp(timestamp),
                self._fmt_contact_link(call.couch_recipient, doc_info),
                self._fmt(phone_number),
                self._fmt(direction_map.get(call.direction,"-")),
                self._fmt(form_name),
                self._fmt("-"),
                self._fmt(answered),
                self._fmt(call.duration),
                self._fmt(_("Yes") if call.error else _("No")),
                self._fmt(cgi.escape(call.error_message) if call.error_message else None),
            ]
            
            if self.request.couch_user.is_previewer():
                row.append(self._fmt(call.gateway_session_id))
            
            result.append(row)

        all_session_ids = xforms_sessions.keys()
        session_submission_map = dict(
            SQLXFormsSession.objects.filter(session_id__in=all_session_ids).values_list(
                'session_id', 'submission_id'
            )
        )
        xforms_sessions.update(session_submission_map)

        # Add into the final result the link to the submission based on the
        # outcome of the above lookups.
        final_result = []
        for row in result:
            final_row = row[1:]
            session_id = row[0]
            if session_id:
                submission_id = xforms_sessions[session_id]
                if submission_id:
                    final_row[5] = self._fmt_submission_link(submission_id)
            final_result.append(final_row)

        return final_result
Exemple #25
0
def initiate_outbound_call(recipient,
                           form_unique_id,
                           submit_partial_form,
                           include_case_side_effects,
                           max_question_retries,
                           messaging_event_id,
                           verified_number=None,
                           unverified_number=None,
                           case_id=None,
                           case_for_case_submission=False,
                           timestamp=None):
    """
    Returns False if an error occurred and the call should be retried.
    Returns True if the call should not be retried (either because it was
    queued successfully or because an unrecoverable error occurred).
    """
    call = None
    logged_event = MessagingEvent.objects.get(pk=messaging_event_id)
    logged_subevent = logged_event.create_ivr_subevent(recipient,
                                                       form_unique_id,
                                                       case_id=case_id)

    if not verified_number and not unverified_number:
        log_error(MessagingEvent.ERROR_NO_PHONE_NUMBER,
                  logged_subevent=logged_subevent)
        return True

    backend = get_ivr_backend(recipient, verified_number, unverified_number)
    if not backend:
        log_error(MessagingEvent.ERROR_NO_SUITABLE_GATEWAY,
                  logged_subevent=logged_subevent)
        return True

    phone_number = (verified_number.phone_number
                    if verified_number else unverified_number)

    call = Call(
        couch_recipient_doc_type=recipient.doc_type,
        couch_recipient=recipient.get_id,
        phone_number='+%s' % str(phone_number),
        direction=OUTGOING,
        date=timestamp or datetime.utcnow(),
        domain=recipient.domain,
        form_unique_id=form_unique_id,
        submit_partial_form=submit_partial_form,
        include_case_side_effects=include_case_side_effects,
        max_question_retries=max_question_retries,
        current_question_retry_count=0,
        case_id=case_id,
        case_for_case_submission=case_for_case_submission,
        messaging_subevent_id=logged_subevent.pk,
    )

    ivr_data = None
    if backend.cache_first_ivr_response():
        ivr_data, error = get_first_ivr_response_data(recipient, call,
                                                      logged_subevent)
        if error:
            return True

    if ivr_data:
        logged_subevent.xforms_session = ivr_data.session
        logged_subevent.save()

    try:
        call.backend_api = backend.get_api_id()
        call.backend_id = backend.couch_id
        result = backend.initiate_outbound_call(call, logged_subevent)
        if ivr_data and not call.error:
            backend.set_first_ivr_response(call, call.gateway_session_id,
                                           ivr_data)
        call.save()
        logged_subevent.completed()
        return result
    except GatewayConnectionError:
        log_error(MessagingEvent.ERROR_GATEWAY_ERROR, call, logged_subevent)
        raise
    except Exception:
        log_error(MessagingEvent.ERROR_INTERNAL_SERVER_ERROR, call,
                  logged_subevent)
        raise