예제 #1
0
def send_messages_for_config(config, actually_send=True):
    query_engine = QueryEngine(template_vars=config.template_variables)
    params = get_parsed_params(config.template)
    sent_messages = []
    for user in config.group.get_users():
        phone_number = get_preferred_phone_number_for_recipient(user)
        if phone_number:
            query_context = QueryContext(
                user, config.group, template_vars=config.template_variables)
            message_context = query_engine.get_context(params, query_context)
            message = config.template.format(**message_context)
            if actually_send:
                metadata = MessageMetadata(workflow=WORKFLOW_PERFORMANCE)
                if isinstance(phone_number, PhoneNumber):
                    send_sms_to_verified_number(phone_number,
                                                message,
                                                metadata=metadata)
                else:
                    send_sms(config.domain,
                             user,
                             phone_number,
                             message,
                             metadata=metadata)

            sent_messages.append(MessageResult(user, message))
    return sent_messages
예제 #2
0
def send_verification(domain, user, phone_number, logged_event):
    backend = SQLMobileBackend.load_default_by_phone_and_domain(
        SQLMobileBackend.SMS,
        phone_number,
        domain=domain
    )
    reply_phone = backend.reply_to_phone_number

    subevent = logged_event.create_subevent_for_single_sms(
        user.doc_type,
        user.get_id
    )

    if reply_phone:
        message = messages.get_message(
            messages.MSG_VERIFICATION_START_WITH_REPLY,
            context=(user.raw_username, reply_phone),
            domain=domain,
            language=user.get_language_code()
        )
    else:
        message = messages.get_message(
            messages.MSG_VERIFICATION_START_WITHOUT_REPLY,
            context=(user.raw_username,),
            domain=domain,
            language=user.get_language_code()
        )
    send_sms(domain, user, phone_number, message,
        metadata=MessageMetadata(messaging_subevent_id=subevent.pk))
    subevent.completed()
예제 #3
0
    def __test_global_backend_map(self):
        with patch(
            'corehq.messaging.smsbackends.test.models.SQLTestSMSBackend.send',
            autospec=True
        ) as mock_send:
            self.assertTrue(send_sms(self.domain, None, '15551234567', 'Test for BACKEND2'))
        self.assertEqual(mock_send.call_count, 1)
        self.assertEqual(mock_send.call_args[0][0].pk, self.backend2.pk)

        with patch(
            'corehq.messaging.smsbackends.test.models.SQLTestSMSBackend.send',
            autospec=True
        ) as mock_send:
            self.assertTrue(send_sms(self.domain, None, '9100000000', 'Test for BACKEND3'))
        self.assertEqual(mock_send.call_count, 1)
        self.assertEqual(mock_send.call_args[0][0].pk, self.backend3.pk)

        with patch(
            'corehq.messaging.smsbackends.test.models.SQLTestSMSBackend.send',
            autospec=True
        ) as mock_send:
            self.assertTrue(send_sms(self.domain, None, '26500000000', 'Test for BACKEND4'))
        self.assertEqual(mock_send.call_count, 1)
        self.assertEqual(mock_send.call_args[0][0].pk, self.backend4.pk)

        with patch(
            'corehq.messaging.smsbackends.test.models.SQLTestSMSBackend.send',
            autospec=True
        ) as mock_send:
            self.assertTrue(send_sms(self.domain, None, '25800000000', 'Test for BACKEND1'))
        self.assertEqual(mock_send.call_count, 1)
        self.assertEqual(mock_send.call_args[0][0].pk, self.backend1.pk)
예제 #4
0
    def test_outgoing_with_error(self, process_sms_delay_mock,
                                 enqueue_directly_mock):
        send_sms(self.domain, None, '+999123', 'test outgoing')

        self.assertEqual(enqueue_directly_mock.call_count, 1)
        self.assertEqual(self.queued_sms_count, 1)
        self.assertEqual(self.reporting_sms_count, 0)

        queued_sms = self.get_queued_sms()
        self.assertEqual(queued_sms.processed, False)
        self.assertEqual(queued_sms.error, False)
        couch_id = queued_sms.couch_id
        self.assertIsNotNone(couch_id)

        with patch_error_send() as send_mock:
            process_sms(queued_sms.pk)

        self.assertEqual(send_mock.call_count, 1)
        self.assertEqual(self.queued_sms_count, 0)
        self.assertEqual(self.reporting_sms_count, 1)

        reporting_sms = self.get_reporting_sms()
        self.assertEqual(reporting_sms.processed, False)
        self.assertEqual(reporting_sms.error, True)
        self.assertEqual(reporting_sms.couch_id, couch_id)
        self.assertEqual(reporting_sms.backend_api, self.backend.get_api_id())
        self.assertEqual(reporting_sms.backend_id, self.backend.couch_id)

        self.assertEqual(process_sms_delay_mock.call_count, 0)
        self.assertBillableDoesNotExist(couch_id)
예제 #5
0
    def test_outgoing_with_error(self, process_sms_delay_mock, enqueue_directly_mock):
        send_sms(self.domain, None, '+999123', 'test outgoing')

        self.assertEqual(enqueue_directly_mock.call_count, 1)
        self.assertEqual(self.queued_sms_count, 1)
        self.assertEqual(self.reporting_sms_count, 0)

        queued_sms = self.get_queued_sms()
        self.assertEqual(queued_sms.processed, False)
        self.assertEqual(queued_sms.error, False)
        couch_id = queued_sms.couch_id
        self.assertIsNotNone(couch_id)

        with patch_error_send() as send_mock:
            process_sms(queued_sms.pk)

        self.assertEqual(send_mock.call_count, 1)
        self.assertEqual(self.queued_sms_count, 0)
        self.assertEqual(self.reporting_sms_count, 1)

        reporting_sms = self.get_reporting_sms()
        self.assertEqual(reporting_sms.processed, False)
        self.assertEqual(reporting_sms.error, True)
        self.assertEqual(reporting_sms.couch_id, couch_id)
        self.assertEqual(reporting_sms.backend_api, self.backend.get_api_id())
        self.assertEqual(reporting_sms.backend_id, self.backend.couch_id)

        self.assertEqual(process_sms_delay_mock.call_count, 0)
        self.assertBillableDoesNotExist(couch_id)
예제 #6
0
def fire_sms_event(reminder,
                   handler,
                   recipients,
                   verified_numbers,
                   logged_event,
                   workflow=None):
    current_event = reminder.current_event
    case = reminder.case
    template_params = get_message_template_params(case)

    uses_custom_content_handler, content_handler = get_custom_content_handler(
        handler, logged_event)
    if uses_custom_content_handler and not content_handler:
        return

    domain_obj = Domain.get_by_name(reminder.domain, strict=True)
    for recipient in recipients:
        logged_subevent = logged_event.create_subevent(handler, reminder,
                                                       recipient)

        try:
            lang = recipient.get_language_code()
        except Exception:
            lang = None

        if content_handler:
            message = content_handler(reminder, handler, recipient)
        else:
            message = current_event.message.get(
                lang, current_event.message[handler.default_lang])
            try:
                message = Message.render(message, **template_params)
            except Exception:
                logged_subevent.error(
                    MessagingEvent.ERROR_CANNOT_RENDER_MESSAGE)
                continue

        verified_number, unverified_number = get_recipient_phone_number(
            reminder, recipient, verified_numbers)

        if message:
            metadata = MessageMetadata(
                workflow=workflow or get_workflow(handler),
                reminder_id=reminder._id,
                messaging_subevent_id=logged_subevent.pk,
            )
            if verified_number is not None:
                send_sms_to_verified_number(verified_number, message, metadata)
            elif isinstance(recipient, CouchUser) and unverified_number:
                send_sms(reminder.domain, recipient, unverified_number,
                         message, metadata)
            elif (isinstance(recipient, CommCareCase) and unverified_number
                  and domain_obj.send_to_duplicated_case_numbers):
                send_sms(reminder.domain, recipient, unverified_number,
                         message, metadata)
            else:
                logged_subevent.error(MessagingEvent.ERROR_NO_PHONE_NUMBER)
                continue

        logged_subevent.completed()
예제 #7
0
    def send_sms_message(self, domain, recipient, phone_number, message,
                         logged_subevent):
        if not message:
            return

        metadata = self.get_sms_message_metadata(logged_subevent)
        send_sms(domain, recipient, phone_number, message, metadata=metadata)
예제 #8
0
def run_indicator(domain, user_id, indicator_class):
    """
    Runs the given indicator for the given user and sends the SMS if needed.

    :param domain: The domain the indicator is being run for
    :param user_id: The id of either an AWW or LS CommCareUser
    :param indicator_class: a subclass of AWWIndicator or LSIndicator
    """
    user = CommCareUser.get_by_user_id(user_id, domain=domain)
    indicator = indicator_class(domain, user)
    # The user's phone number and preferred language is stored on the usercase
    usercase = user.get_usercase()
    messages = indicator.get_messages(
        language_code=usercase.get_language_code())

    if not isinstance(messages, list):
        raise ValueError("Expected a list of messages")

    if messages:
        phone_number = get_one_way_number_for_recipient(usercase)
        if not phone_number:
            return

        for message in messages:
            send_sms(domain, usercase, phone_number, message)
예제 #9
0
def send_verification(domain, user, phone_number, logged_event):
    backend = SQLMobileBackend.load_default_by_phone_and_domain(
        SQLMobileBackend.SMS, phone_number, domain=domain)
    reply_phone = backend.reply_to_phone_number

    subevent = logged_event.create_subevent_for_single_sms(
        user.doc_type, user.get_id)

    if reply_phone:
        message = messages.get_message(
            messages.MSG_VERIFICATION_START_WITH_REPLY,
            context=(user.raw_username, reply_phone),
            domain=domain,
            language=user.get_language_code())
    else:
        message = messages.get_message(
            messages.MSG_VERIFICATION_START_WITHOUT_REPLY,
            context=(user.raw_username, ),
            domain=domain,
            language=user.get_language_code())
    send_sms(domain,
             user,
             phone_number,
             message,
             metadata=MessageMetadata(messaging_subevent_id=subevent.pk))
    subevent.completed()
예제 #10
0
def run_indicator(domain, user_id, indicator_class, language_code=None):
    """
    Runs the given indicator for the given user and sends the SMS if needed.

    :param domain: The domain the indicator is being run for
    :param user_id: The id of either an AWW or LS CommCareUser
    :param indicator_class: a subclass of AWWIndicator or LSIndicator
    """
    user = CommCareUser.get_by_user_id(user_id, domain=domain)

    # The user's phone number and preferred language is stored on the usercase
    usercase = user.get_usercase()

    phone_number = get_one_way_number_for_recipient(usercase)
    if not phone_number or phone_number == '91':
        # If there is no phone number, don't bother calculating the indicator
        return

    if not language_code:
        language_code = usercase.get_language_code()

    indicator = indicator_class(domain, user)
    messages = indicator.get_messages(language_code=language_code)

    if not isinstance(messages, list):
        raise ValueError("Expected a list of messages")

    metadata = MessageMetadata(custom_metadata={
        'icds_indicator': indicator_class.slug,
    })

    for message in messages:
        send_sms(domain, usercase, phone_number, message, metadata=metadata)
예제 #11
0
    def test_outgoing_failure_indefinite_retry(self, process_sms_delay_mock, enqueue_directly_mock):
        timestamp = datetime(2016, 1, 1, 12, 0)
        RETRY_SMS_INDEFINITELY.set(self.domain, True, NAMESPACE_DOMAIN)

        with patch_datetime_api(timestamp):
            send_sms(self.domain, None, '+999123', 'test outgoing')

        self.assertEqual(enqueue_directly_mock.call_count, 1)
        self.assertEqual(self.queued_sms_count, 1)
        self.assertEqual(self.reporting_sms_count, 0)

        for i in range(settings.SMS_QUEUE_MAX_PROCESSING_ATTEMPTS):
            queued_sms = self.get_queued_sms()
            self.assertEqual(queued_sms.domain, self.domain)
            self.assertEqual(queued_sms.phone_number, '+999123')
            self.assertEqual(queued_sms.text, 'test outgoing')
            self.assertEqual(queued_sms.datetime_to_process, timestamp)
            self.assertEqual(queued_sms.processed, False)
            self.assertEqual(queued_sms.error, False)
            self.assertEqual(queued_sms.num_processing_attempts, i)

            with patch_failed_send() as send_mock, patch_datetime_tasks(timestamp + timedelta(seconds=1)):
                process_sms(queued_sms.pk)

            self.assertEqual(process_sms_delay_mock.call_count, 0)
            self.assertBillableDoesNotExist(queued_sms.couch_id)

            self.assertEqual(send_mock.call_count, 1)
            self.assertEqual(self.queued_sms_count, 1)
            self.assertEqual(self.reporting_sms_count, 0)
            timestamp += timedelta(minutes=settings.SMS_QUEUE_REPROCESS_INTERVAL)

        timestamp += (
            timedelta(minutes=settings.SMS_QUEUE_REPROCESS_INDEFINITELY_INTERVAL)
            - timedelta(minutes=settings.SMS_QUEUE_REPROCESS_INTERVAL)
        )
        queued_sms = self.get_queued_sms()
        self.assertEqual(queued_sms.domain, self.domain)
        self.assertEqual(queued_sms.phone_number, '+999123')
        self.assertEqual(queued_sms.text, 'test outgoing')
        self.assertEqual(queued_sms.datetime_to_process, timestamp)
        self.assertEqual(queued_sms.processed, False)
        self.assertEqual(queued_sms.error, False)
        self.assertEqual(queued_sms.num_processing_attempts, settings.SMS_QUEUE_MAX_PROCESSING_ATTEMPTS)

        with patch_successful_send() as send_mock, patch_datetime_tasks(timestamp + timedelta(seconds=1)):
            process_sms(queued_sms.pk)

        self.assertEqual(send_mock.call_count, 1)
        self.assertEqual(self.queued_sms_count, 0)
        self.assertEqual(self.reporting_sms_count, 1)

        reporting_sms = self.get_reporting_sms()
        self.assertEqual(reporting_sms.processed, True)
        self.assertEqual(reporting_sms.error, False)
        self.assertEqual(reporting_sms.num_processing_attempts, settings.SMS_QUEUE_MAX_PROCESSING_ATTEMPTS + 1)

        self.assertEqual(process_sms_delay_mock.call_count, 0)
        self.assertBillableExists(reporting_sms.couch_id)
예제 #12
0
 def respond(self, message, **kwargs):
     if self.verified_contact:
         with localize(self.user.get_language_code()):
             send_sms_to_verified_number(self.verified_contact,
                                         str(message % kwargs))
     else:
         send_sms(self.domain, None, self.msg.phone_number,
                  str(message % kwargs))
예제 #13
0
def send_first_message(domain, recipient, phone_entry_or_number, session, responses, logged_subevent, workflow):
    # This try/except section is just here (temporarily) to support future refactors
    # If any of these notify, they should be replaced with a comment as to why the two are different
    # so that someone refactoring in the future will know that this or that param is necessary.
    try:
        if session.workflow != workflow:
            # see if we can eliminate the workflow arg
            notify_error('Exploratory: session.workflow != workflow', details={
                'session.workflow': session.workflow, 'workflow': workflow})
        if session.connection_id != recipient.get_id:
            # see if we can eliminate the recipient arg
            notify_error('Exploratory: session.connection_id != recipient.get_id', details={
                'session.connection_id': session.connection_id, 'recipient.get_id': recipient.get_id,
                'recipient': recipient
            })
        if session.related_subevent != logged_subevent:
            # see if we can eliminate the logged_subevent arg
            notify_error('Exploratory: session.related_subevent != logged_subevent', details={
                'session.connection_id': session.connection_id, 'logged_subevent': logged_subevent})
    except Exception:
        # The above running is not mission critical, so if it errors just leave a message in the log
        # for us to follow up on.
        # Absence of the message below and messages above ever notifying
        # will indicate that we can remove these args.
        notify_exception(None, "Error in section of code that's just supposed help inform future refactors")

    if toggles.ONE_PHONE_NUMBER_MULTIPLE_CONTACTS.enabled(domain):
        if not XFormsSessionSynchronization.claim_channel_for_session(session):
            send_first_message.apply_async(
                args=(domain, recipient, phone_entry_or_number, session, responses, logged_subevent, workflow),
                countdown=60
            )
            return

    metrics_counter('commcare.smsforms.session_started', 1, tags={'domain': domain, 'workflow': workflow})

    if len(responses) > 0:
        message = format_message_list(responses)
        metadata = MessageMetadata(
            workflow=workflow,
            xforms_session_couch_id=session.couch_id,
        )
        if isinstance(phone_entry_or_number, PhoneNumber):
            send_sms_to_verified_number(
                phone_entry_or_number,
                message,
                metadata,
                logged_subevent=logged_subevent
            )
        else:
            send_sms(
                domain,
                recipient,
                phone_entry_or_number,
                message,
                metadata
            )
    logged_subevent.completed()
예제 #14
0
def send_verification(domain, user, phone_number):
    module = MobileBackend.auto_load(phone_number, domain).backend_module
    reply_phone = getattr(module, 'receive_phone_number', lambda: None)()

    message = OUTGOING % {
        'name': user.username.split('@')[0],
        'replyto': ' to %s' % util.clean_phone_number(reply_phone) if reply_phone else '',
    }
    api.send_sms(domain, user._id, phone_number, message)
예제 #15
0
def send_verification(domain, user, phone_number):
    backend = MobileBackend.auto_load(phone_number, domain)
    reply_phone = backend.reply_to_phone_number

    message = OUTGOING % {
        "name": user.username.split("@")[0],
        "replyto": " to %s" % util.clean_phone_number(reply_phone) if reply_phone else "",
    }
    send_sms(domain, user, phone_number, message)
예제 #16
0
    def test_sending_to_opted_out_number(self):
        self.assertEqual(PhoneNumber.objects.count(), 0)
        self.assertTrue(send_sms(self.domain, None, "999123456789", "hello"))

        incoming("999123456789", "stop", "GVI")
        self.assertEqual(PhoneNumber.objects.count(), 1)
        phone_number = PhoneNumber.objects.get(phone_number="999123456789")
        self.assertFalse(phone_number.send_sms)

        self.assertFalse(send_sms(self.domain, None, "999123456789", "hello"))
예제 #17
0
def send_verification(domain, user, phone_number):
    backend = MobileBackend.auto_load(phone_number, domain)
    reply_phone = backend.reply_to_phone_number

    with localize(user.language):
        message = _(OUTGOING) % {
            'name': user.username.split('@')[0],
            'replyto': ' to %s' % util.clean_phone_number(reply_phone) if reply_phone else '',
        }
        send_sms(domain, user, phone_number, message)
예제 #18
0
    def test_sending_to_opted_out_number(self):
        self.assertEqual(PhoneNumber.objects.count(), 0)
        self.assertTrue(send_sms(self.domain, None, "999123456789", "hello"))

        incoming("999123456789", "stop", "GVI")
        self.assertEqual(PhoneNumber.objects.count(), 1)
        phone_number = PhoneNumber.objects.get(phone_number="999123456789")
        self.assertFalse(phone_number.send_sms)

        self.assertFalse(send_sms(self.domain, None, "999123456789", "hello"))
예제 #19
0
    def send_sms_message(self, domain, recipient, phone_entry_or_number, message, logged_subevent):
        if not message:
            return

        metadata = self.get_sms_message_metadata(logged_subevent)

        if isinstance(phone_entry_or_number, PhoneNumber):
            send_sms_to_verified_number(phone_entry_or_number, message, metadata=metadata,
                logged_subevent=logged_subevent)
        else:
            send_sms(domain, recipient, phone_entry_or_number, message, metadata=metadata)
예제 #20
0
def fire_sms_event(reminder, handler, recipients, verified_numbers, logged_event, workflow=None):
    current_event = reminder.current_event
    case = reminder.case
    template_params = get_message_template_params(case)

    uses_custom_content_handler, content_handler = get_custom_content_handler(handler, logged_event)
    if uses_custom_content_handler and not content_handler:
        return

    domain_obj = Domain.get_by_name(reminder.domain, strict=True)
    for recipient in recipients:
        logged_subevent = logged_event.create_subevent(handler, reminder, recipient)

        try:
            lang = recipient.get_language_code()
        except Exception:
            lang = None

        if content_handler:
            message = content_handler(reminder, handler, recipient)
        else:
            message = current_event.message.get(lang, current_event.message[handler.default_lang])
            try:
                message = Message.render(message, **template_params)
            except Exception:
                logged_subevent.error(MessagingEvent.ERROR_CANNOT_RENDER_MESSAGE)
                continue

        verified_number, unverified_number = get_recipient_phone_number(
            reminder, recipient, verified_numbers)

        if message:
            metadata = MessageMetadata(
                workflow=workflow or get_workflow(handler),
                reminder_id=reminder._id,
                messaging_subevent_id=logged_subevent.pk,
            )
            if verified_number is not None:
                send_sms_to_verified_number(verified_number,
                    message, metadata, logged_subevent=logged_subevent)
            elif isinstance(recipient, CouchUser) and unverified_number:
                send_sms(reminder.domain, recipient, unverified_number,
                    message, metadata)
            elif (is_commcarecase(recipient) and unverified_number and
                    domain_obj.send_to_duplicated_case_numbers):
                send_sms(reminder.domain, recipient, unverified_number,
                    message, metadata)
            else:
                logged_subevent.error(MessagingEvent.ERROR_NO_PHONE_NUMBER)
                continue

        logged_subevent.completed()
예제 #21
0
def send_verification(domain, user, phone_number):
    backend = MobileBackend.auto_load(phone_number, domain)
    reply_phone = backend.reply_to_phone_number

    with localize(user.language):
        message = _(OUTGOING) % {
            'name':
            user.username.split('@')[0],
            'replyto':
            ' to %s' %
            util.clean_phone_number(reply_phone) if reply_phone else '',
        }
        send_sms(domain, user, phone_number, message)
예제 #22
0
    def _test_outbound_backend(self, backend, msg_text, mock_send):
        self.domain_obj.default_sms_backend_id = backend._id
        self.domain_obj.save()

        send_sms(self.domain_obj.name, None, self.test_phone_number, msg_text)
        sms = SMS.objects.get(
            domain=self.domain_obj.name,
            direction='O',
            text=msg_text
        )

        self.assertTrue(mock_send.called)
        msg_arg = mock_send.call_args[0][0]
        self.assertEqual(msg_arg.date, sms.date)
예제 #23
0
    def test_outgoing_failure_recovery(self, process_sms_delay_mock,
                                       enqueue_directly_mock):
        timestamp = datetime(2016, 1, 1, 12, 0)

        with patch_datetime_api(timestamp):
            send_sms(self.domain, None, '+999123', 'test outgoing')

        self.assertEqual(enqueue_directly_mock.call_count, 1)
        self.assertEqual(self.queued_sms_count, 1)
        self.assertEqual(self.reporting_sms_count, 0)

        queued_sms = self.get_queued_sms()
        self.assertEqual(queued_sms.datetime_to_process, timestamp)
        self.assertEqual(queued_sms.processed, False)
        self.assertEqual(queued_sms.error, False)
        self.assertEqual(queued_sms.num_processing_attempts, 0)

        with patch_failed_send() as send_mock, patch_datetime_tasks(
                timestamp + timedelta(seconds=1)):
            process_sms(queued_sms.pk)

        self.assertEqual(process_sms_delay_mock.call_count, 0)
        self.assertBillableDoesNotExist(queued_sms.couch_id)
        self.assertEqual(send_mock.call_count, 1)
        self.assertEqual(self.queued_sms_count, 1)
        self.assertEqual(self.reporting_sms_count, 0)
        timestamp += timedelta(minutes=settings.SMS_QUEUE_REPROCESS_INTERVAL)

        queued_sms = self.get_queued_sms()
        self.assertEqual(queued_sms.datetime_to_process, timestamp)
        self.assertEqual(queued_sms.processed, False)
        self.assertEqual(queued_sms.error, False)
        self.assertEqual(queued_sms.num_processing_attempts, 1)

        with patch_successful_send() as send_mock, patch_datetime_tasks(
                timestamp + timedelta(seconds=1)):
            process_sms(queued_sms.pk)

        self.assertEqual(send_mock.call_count, 1)
        self.assertEqual(self.queued_sms_count, 0)
        self.assertEqual(self.reporting_sms_count, 1)

        reporting_sms = self.get_reporting_sms()
        self.assertEqual(reporting_sms.processed, True)
        self.assertEqual(reporting_sms.error, False)
        self.assertEqual(reporting_sms.num_processing_attempts, 2)

        self.assertEqual(process_sms_delay_mock.call_count, 0)
        self.assertBillableExists(reporting_sms.couch_id)
예제 #24
0
    def _test_outbound_backend(self, backend, msg_text):
        from corehq.apps.sms.tests import BackendInvocationDoc
        self.domain_obj.default_sms_backend_id = backend._id
        self.domain_obj.save()

        send_sms(self.domain_obj.name, None, self.test_phone_number, msg_text)
        sms = SMS.objects.get(
            domain=self.domain_obj.name,
            direction='O',
            text=msg_text
        )

        invoke_doc_id = '%s-%s' % (backend.__class__.__name__, json_format_datetime(sms.date))
        invoke_doc = BackendInvocationDoc.get(invoke_doc_id)
        self.assertIsNotNone(invoke_doc)
예제 #25
0
    def _test_outbound_backend(self, backend, msg_text, mock_send):
        SQLMobileBackendMapping.set_default_domain_backend(self.domain_obj.name, backend)

        send_sms(self.domain_obj.name, None, self.test_phone_number, msg_text)
        sms = SMS.objects.get(
            domain=self.domain_obj.name,
            direction='O',
            text=msg_text
        )

        self.assertTrue(mock_send.called)
        msg_arg = mock_send.call_args[0][0]
        self.assertEqual(msg_arg.date, sms.date)
        self.assertEqual(sms.backend_api, backend.hq_api_id)
        self.assertEqual(sms.backend_id, backend.couch_id)
예제 #26
0
파일: views.py 프로젝트: wpride/commcare-hq
def api_send_sms(request, domain):
    """
    An API to send SMS.
    Expected post parameters:
        phone_number - the phone number to send to
        contact_id - the _id of a contact to send to (overrides phone_number)
        text - the text of the message
        backend_id - the name of the MobileBackend to use while sending
    """
    if request.method == "POST":
        phone_number = request.POST.get("phone_number", None)
        contact_id = request.POST.get("contact_id", None)
        text = request.POST.get("text", None)
        backend_id = request.POST.get("backend_id", None)
        chat = request.POST.get("chat", None)

        if (phone_number is None and contact_id is None) or (text is None):
            return HttpResponseBadRequest("Not enough arguments.")

        vn = None
        if contact_id is not None:
            try:
                contact = get_contact(contact_id)
                assert contact is not None
                assert contact.domain == domain
            except Exception:
                return HttpResponseBadRequest("Contact not found.")
            try:
                vn = contact.get_verified_number()
                assert vn is not None
                phone_number = vn.phone_number
            except Exception:
                return HttpResponseBadRequest("Contact has no phone number.")

        try:
            chat_workflow = string_to_boolean(chat)
        except Exception:
            chat_workflow = False

        if chat_workflow:
            chat_user_id = request.couch_user._id
        else:
            chat_user_id = None

        metadata = MessageMetadata(
            chat_user_id=chat_user_id
        )
        if backend_id is not None:
            success = send_sms_with_backend_name(domain, phone_number, text, backend_id, metadata)
        elif vn is not None:
            success = send_sms_to_verified_number(vn, text, metadata)
        else:
            success = send_sms(domain, None, phone_number, text, metadata)

        if success:
            return HttpResponse("OK")
        else:
            return HttpResponse("ERROR")
    else:
        return HttpResponseBadRequest("POST Expected.")
예제 #27
0
 def send_first_message(self, domain, recipient, phone_entry_or_number,
                        session, responses, logged_subevent, workflow):
     if len(responses) > 0:
         message = format_message_list(responses)
         metadata = MessageMetadata(
             workflow=workflow,
             xforms_session_couch_id=session.couch_id,
         )
         if isinstance(phone_entry_or_number, PhoneNumber):
             send_sms_to_verified_number(phone_entry_or_number,
                                         message,
                                         metadata,
                                         logged_subevent=logged_subevent)
         else:
             send_sms(domain, recipient, phone_entry_or_number, message,
                      metadata)
예제 #28
0
    def test_outgoing_failure(self, process_sms_delay_mock,
                              enqueue_directly_mock):
        timestamp = datetime(2016, 1, 1, 12, 0)

        with patch_datetime_api(timestamp):
            send_sms(self.domain, None, '+999123', 'test outgoing')

        self.assertEqual(enqueue_directly_mock.call_count, 1)
        self.assertEqual(self.queued_sms_count, 1)
        self.assertEqual(self.reporting_sms_count, 0)

        for i in range(settings.SMS_QUEUE_MAX_PROCESSING_ATTEMPTS):
            queued_sms = self.get_queued_sms()
            self.assertEqual(queued_sms.domain, self.domain)
            self.assertEqual(queued_sms.phone_number, '+999123')
            self.assertEqual(queued_sms.text, 'test outgoing')
            self.assertEqual(queued_sms.datetime_to_process, timestamp)
            self.assertEqual(queued_sms.processed, False)
            self.assertEqual(queued_sms.error, False)
            self.assertEqual(queued_sms.num_processing_attempts, i)

            with patch_failed_send() as send_mock, patch_datetime_tasks(
                    timestamp + timedelta(seconds=1)):
                process_sms(queued_sms.pk)

            self.assertEqual(process_sms_delay_mock.call_count, 0)
            self.assertBillableDoesNotExist(queued_sms.couch_id)

            self.assertEqual(send_mock.call_count, 1)
            if i < (settings.SMS_QUEUE_MAX_PROCESSING_ATTEMPTS - 1):
                self.assertEqual(self.queued_sms_count, 1)
                self.assertEqual(self.reporting_sms_count, 0)
                timestamp += timedelta(
                    minutes=settings.SMS_QUEUE_REPROCESS_INTERVAL)
            else:
                self.assertEqual(self.queued_sms_count, 0)
                self.assertEqual(self.reporting_sms_count, 1)

        reporting_sms = self.get_reporting_sms()
        self.assertEqual(reporting_sms.domain, self.domain)
        self.assertEqual(reporting_sms.phone_number, '+999123')
        self.assertEqual(reporting_sms.text, 'test outgoing')
        self.assertEqual(reporting_sms.processed, False)
        self.assertEqual(reporting_sms.error, True)
        self.assertEqual(reporting_sms.num_processing_attempts,
                         settings.SMS_QUEUE_MAX_PROCESSING_ATTEMPTS)
        self.assertBillableDoesNotExist(reporting_sms.couch_id)
예제 #29
0
def send_verification(domain, user, phone_number, logged_event):
    backend = MobileBackend.auto_load(phone_number, domain)
    reply_phone = backend.reply_to_phone_number

    subevent = logged_event.create_subevent_for_single_sms(
        user.doc_type,
        user.get_id
    )

    with localize(user.language):
        message = _(OUTGOING) % {
            'name': user.username.split('@')[0],
            'replyto': ' to %s' % util.clean_phone_number(reply_phone) if reply_phone else '',
        }
        send_sms(domain, user, phone_number, message,
            metadata=MessageMetadata(messaging_subevent_id=subevent.pk))
        subevent.completed()
예제 #30
0
    def test_outgoing_failure_recovery(self, process_sms_delay_mock, enqueue_directly_mock):
        timestamp = datetime(2016, 1, 1, 12, 0)

        with patch_datetime_api(timestamp):
            send_sms(self.domain, None, '+999123', 'test outgoing')

        self.assertEqual(enqueue_directly_mock.call_count, 1)
        self.assertEqual(self.queued_sms_count, 1)
        self.assertEqual(self.reporting_sms_count, 0)

        queued_sms = self.get_queued_sms()
        self.assertEqual(queued_sms.datetime_to_process, timestamp)
        self.assertEqual(queued_sms.processed, False)
        self.assertEqual(queued_sms.error, False)
        self.assertEqual(queued_sms.num_processing_attempts, 0)

        with patch_failed_send() as send_mock, patch_datetime_tasks(timestamp + timedelta(seconds=1)):
            process_sms(queued_sms.pk)

        self.assertEqual(process_sms_delay_mock.call_count, 0)
        self.assertBillableDoesNotExist(queued_sms.couch_id)
        self.assertEqual(send_mock.call_count, 1)
        self.assertEqual(self.queued_sms_count, 1)
        self.assertEqual(self.reporting_sms_count, 0)
        timestamp += timedelta(minutes=settings.SMS_QUEUE_REPROCESS_INTERVAL)

        queued_sms = self.get_queued_sms()
        self.assertEqual(queued_sms.datetime_to_process, timestamp)
        self.assertEqual(queued_sms.processed, False)
        self.assertEqual(queued_sms.error, False)
        self.assertEqual(queued_sms.num_processing_attempts, 1)

        with patch_successful_send() as send_mock, patch_datetime_tasks(timestamp + timedelta(seconds=1)):
            process_sms(queued_sms.pk)

        self.assertEqual(send_mock.call_count, 1)
        self.assertEqual(self.queued_sms_count, 0)
        self.assertEqual(self.reporting_sms_count, 1)

        reporting_sms = self.get_reporting_sms()
        self.assertEqual(reporting_sms.processed, True)
        self.assertEqual(reporting_sms.error, False)
        self.assertEqual(reporting_sms.num_processing_attempts, 2)

        self.assertEqual(process_sms_delay_mock.call_count, 0)
        self.assertBillableExists(reporting_sms.couch_id)
예제 #31
0
def fire_sms_event(reminder, handler, recipients, verified_numbers):
    current_event = reminder.current_event
    if handler.method in [METHOD_SMS, METHOD_SMS_CALLBACK]:
        for recipient in recipients:
            try:
                lang = recipient.get_language_code()
            except Exception:
                lang = None
            
            message = current_event.message.get(lang, current_event.message[handler.default_lang])
            try:
                message = Message.render(message, case=reminder.case.case_properties())
            except Exception:
                if len(recipients) == 1:
                    raise_error(reminder, ERROR_RENDERING_MESSAGE % lang)
                    return False
                else:
                    raise_warning() # ERROR_RENDERING_MESSAGE
                    continue
            
            verified_number = verified_numbers[recipient.get_id]
            if verified_number is not None:
                result = send_sms_to_verified_number(verified_number, message)
                if not result:
                    raise_warning() # Could not send SMS
            elif isinstance(recipient, CouchUser):
                # If there is no verified number, but the recipient is a CouchUser, still try to send it
                try:
                    phone_number = recipient.phone_number
                except Exception:
                    phone_number = None
                
                if phone_number is None:
                    result = False
                    if len(recipients) == 1:
                        raise_error(reminder, ERROR_NO_OTHER_NUMBERS)
                    else:
                        raise_warning() # ERROR_NO_OTHER_NUMBERS
                else:
                    result = send_sms(reminder.domain, recipient.get_id, phone_number, message)
                    if not result:
                        raise_warning() # Could not send SMS
            else:
                if len(recipients) == 1:
                    raise_error(reminder, ERROR_NO_VERIFIED_NUMBER)
                else:
                    raise_warning() # ERROR_NO_VERIFIED_NUMBER
                result = False
            
            if len(recipients) == 1:
                return result
        
        # For multiple recipients, always move to the next event
        return True
    
    elif handler.method in [METHOD_TEST, METHOD_SMS_CALLBACK_TEST]:
        # Used for automated tests
        return True
예제 #32
0
def send_messages_for_config(config, actually_send=True):
    query_engine = QueryEngine(template_vars=config.template_variables)
    params = get_parsed_params(config.template)
    sent_messages = []
    for user in config.group.get_users():
        phone_number = get_preferred_phone_number_for_recipient(user)
        if phone_number:
            query_context = QueryContext(user, config.group, template_vars=config.template_variables)
            message_context = query_engine.get_context(params, query_context)
            message = config.template.format(**message_context)
            if actually_send:
                metadata = MessageMetadata(workflow=WORKFLOW_PERFORMANCE)
                if isinstance(phone_number, PhoneNumber):
                    send_sms_to_verified_number(phone_number, message, metadata=metadata)
                else:
                    send_sms(config.domain, user, phone_number, message, metadata=metadata)

            sent_messages.append(MessageResult(user, message))
    return sent_messages
예제 #33
0
    def __test_domain_default(self):
        # Test overriding with domain-level backend
        SQLMobileBackendMapping.set_default_domain_backend(self.domain, self.backend5)

        with patch(
            'corehq.messaging.smsbackends.test.models.SQLTestSMSBackend.send',
            autospec=True
        ) as mock_send:
            self.assertTrue(send_sms(self.domain, None, '15551234567', 'Test for BACKEND5'))
        self.assertEqual(mock_send.call_count, 1)
        self.assertEqual(mock_send.call_args[0][0].pk, self.backend5.pk)
예제 #34
0
def send_sms_for_schedule_instance(schedule_instance, recipient, phone_number,
                                   message):
    if not message:
        return

    metadata = MessageMetadata(
        custom_metadata=get_sms_custom_metadata(schedule_instance), )

    if schedule_instance.memoized_schedule.is_test:
        send_sms_with_backend_name(schedule_instance.domain,
                                   phone_number,
                                   message,
                                   'TEST',
                                   metadata=metadata)
    else:
        send_sms(schedule_instance.domain,
                 recipient,
                 phone_number,
                 message,
                 metadata=metadata)
예제 #35
0
    def __test_shared_backend(self):
        # Test use of backend that another domain owns but has granted access
        SQLMobileBackendMapping.set_default_domain_backend(self.domain, self.backend6)

        with patch(
            'corehq.messaging.smsbackends.test.models.SQLTestSMSBackend.send',
            autospec=True
        ) as mock_send:
            self.assertTrue(send_sms(self.domain, None, '25800000000', 'Test for BACKEND6'))
        self.assertEqual(mock_send.call_count, 1)
        self.assertEqual(mock_send.call_args[0][0].pk, self.backend6.pk)

        # Test trying to use a backend that another domain owns but has not granted access
        SQLMobileBackendMapping.set_default_domain_backend(self.domain, self.backend7)

        with patch(
            'corehq.messaging.smsbackends.test.models.SQLTestSMSBackend.send',
            autospec=True
        ) as mock_send:
            self.assertFalse(send_sms(self.domain, None, '25800000000', 'Test Unauthorized'))
        self.assertEqual(mock_send.call_count, 0)
예제 #36
0
def process_verification(phone_number, text, backend_id=None):
    v = VerifiedNumber.by_phone(phone_number, True)
    if not v:
        return

    if not verification_response_ok(text):
        return
    
    if backend_id:
        backend = MobileBackend.load(backend_id)
    else:
        backend = MobileBackend.auto_load(phone_number, v.domain)

    # i don't know how to dynamically instantiate this object, which may be any number of doc types...
    #owner = CommCareMobileContactMixin.get(v.owner_id)
    assert v.owner_doc_type == 'CommCareUser'
    owner = CommCareUser.get(v.owner_id)

    owner.save_verified_number(v.domain, phone_number, True, backend._id)

    api.send_sms(v.domain, owner._id, phone_number, CONFIRM)
예제 #37
0
def send_verification(domain, user, phone_number, logged_event):
    backend = MobileBackend.auto_load(phone_number, domain)
    reply_phone = backend.reply_to_phone_number

    subevent = logged_event.create_subevent_for_single_sms(
        user.doc_type, user.get_id)

    with localize(user.language):
        message = _(OUTGOING) % {
            'name':
            user.username.split('@')[0],
            'replyto':
            ' to %s' %
            util.clean_phone_number(reply_phone) if reply_phone else '',
        }
        send_sms(domain,
                 user,
                 phone_number,
                 message,
                 metadata=MessageMetadata(messaging_subevent_id=subevent.pk))
        subevent.completed()
예제 #38
0
 def send_first_message(self, domain, recipient, phone_entry_or_number, session, responses, logged_subevent,
         workflow):
     if len(responses) > 0:
         message = format_message_list(responses)
         metadata = MessageMetadata(
             workflow=workflow,
             xforms_session_couch_id=session.couch_id,
         )
         if isinstance(phone_entry_or_number, PhoneNumber):
             send_sms_to_verified_number(
                 phone_entry_or_number,
                 message,
                 metadata,
                 logged_subevent=logged_subevent
             )
         else:
             send_sms(
                 domain,
                 recipient,
                 phone_entry_or_number,
                 message,
                 metadata
             )
예제 #39
0
def fire_sms_survey_event(reminder, handler, recipients, verified_numbers,
                          logged_event):
    if reminder.callback_try_count > 0:
        # Handle timeouts
        if handler.submit_partial_forms and (
                reminder.callback_try_count == len(
                    reminder.current_event.callback_timeout_intervals)):
            # Submit partial form completions
            for session_id in reminder.xforms_session_ids:
                submit_unfinished_form(session_id,
                                       handler.include_case_side_effects)
        else:
            # Resend current question
            for session_id in reminder.xforms_session_ids:
                session = get_session_by_session_id(session_id)
                if session.end_time is None:
                    vn = VerifiedNumber.view("sms/verified_number_by_owner_id",
                                             key=session.connection_id,
                                             include_docs=True).first()
                    if vn is not None:
                        metadata = MessageMetadata(
                            workflow=get_workflow(handler),
                            reminder_id=reminder._id,
                            xforms_session_couch_id=session._id,
                        )
                        resp = current_question(session_id)
                        send_sms_to_verified_number(vn, resp.event.text_prompt,
                                                    metadata)
    else:
        reminder.xforms_session_ids = []
        domain_obj = Domain.get_by_name(reminder.domain, strict=True)

        # Get the app, module, and form
        try:
            form_unique_id = reminder.current_event.form_unique_id
            form = Form.get_form(form_unique_id)
            app = form.get_app()
            module = form.get_module()
        except Exception:
            logged_event.error(MessagingEvent.ERROR_CANNOT_FIND_FORM)
            return

        # Start a touchforms session for each recipient
        for recipient in recipients:
            logged_subevent = logged_event.create_subevent(
                handler, reminder, recipient)

            verified_number, unverified_number = get_recipient_phone_number(
                reminder, recipient, verified_numbers)

            no_verified_number = verified_number is None
            cant_use_unverified_number = (
                unverified_number is None
                or not domain_obj.send_to_duplicated_case_numbers
                or form_requires_input(form))
            if no_verified_number and cant_use_unverified_number:
                logged_subevent.error(
                    MessagingEvent.ERROR_NO_TWO_WAY_PHONE_NUMBER)
                continue

            key = "start-sms-survey-for-contact-%s" % recipient.get_id
            with CriticalSection([key], timeout=60):
                # Get the case to submit the form against, if any
                if (isinstance(recipient, CommCareCase)
                        and not handler.force_surveys_to_use_triggered_case):
                    case_id = recipient.get_id
                else:
                    case_id = reminder.case_id

                if form.requires_case() and not case_id:
                    logged_subevent.error(MessagingEvent.ERROR_NO_CASE_GIVEN)
                    continue

                # Close all currently open sessions
                SQLXFormsSession.close_all_open_sms_sessions(
                    reminder.domain, recipient.get_id)

                # Start the new session
                try:
                    session, responses = start_session(
                        reminder.domain,
                        recipient,
                        app,
                        module,
                        form,
                        case_id,
                        case_for_case_submission=handler.
                        force_surveys_to_use_triggered_case)
                except TouchformsError as e:
                    human_readable_message = e.response_data.get(
                        'human_readable_message', None)

                    logged_subevent.error(
                        MessagingEvent.ERROR_TOUCHFORMS_ERROR,
                        additional_error_text=human_readable_message)

                    if touchforms_error_is_config_error(e):
                        # Don't reraise the exception because this means there are configuration
                        # issues with the form that need to be fixed
                        continue
                    else:
                        # Reraise the exception so that the framework retries it again later
                        raise
                except Exception as e:
                    logged_subevent.error(
                        MessagingEvent.ERROR_TOUCHFORMS_ERROR)
                    # Reraise the exception so that the framework retries it again later
                    raise
                session.survey_incentive = handler.survey_incentive
                session.workflow = get_workflow(handler)
                session.reminder_id = reminder._id
                session.save()

            reminder.xforms_session_ids.append(session.session_id)
            logged_subevent.xforms_session = session
            logged_subevent.save()

            # Send out first message
            if len(responses) > 0:
                message = format_message_list(responses)
                metadata = MessageMetadata(
                    workflow=get_workflow(handler),
                    reminder_id=reminder._id,
                    xforms_session_couch_id=session._id,
                )
                if verified_number:
                    send_sms_to_verified_number(verified_number, message,
                                                metadata)
                else:
                    send_sms(reminder.domain, recipient, unverified_number,
                             message, metadata)

            logged_subevent.completed()
예제 #40
0
def send_to_recipients(request, domain):
    recipients = request.POST.get('recipients')
    message = request.POST.get('message')
    if not recipients:
        messages.error(request, _("You didn't specify any recipients"))
    elif not message:
        messages.error(request, _("You can't send an empty message"))
    else:
        recipients = [x.strip() for x in recipients.split(',') if x.strip()]
        phone_numbers = []
        # formats: GroupName (group), "Username", +15555555555
        group_names = []
        usernames = []
        phone_numbers = []

        unknown_usernames = []
        GROUP = "[group]"
        send_to_all_checked = False

        for recipient in recipients:
            if recipient == "[send to all]":
                send_to_all_checked = True
                phone_users = CouchUser.view("users/phone_users_by_domain",
                    startkey=[domain], endkey=[domain, {}], include_docs=True
                )
                for user in phone_users:
                    usernames.append(user.username)
                group_names = []
                break
            elif (not send_to_all_checked) and recipient.endswith(GROUP):
                name = recipient[:-len(GROUP)].strip()
                group_names.append(name)
            elif re.match(r'^\+\d+', recipient): # here we expect it to have a plus sign
                def wrap_user_by_type(u):
                    return getattr(user_models, u['doc']['doc_type']).wrap(u['doc'])

                phone_users = CouchUser.view("users/by_default_phone", # search both with and w/o the plus
                    keys=[recipient, recipient[1:]], include_docs=True,
                    wrapper=wrap_user_by_type).all()

                phone_users = filter(lambda u: u.is_member_of(domain), phone_users)
                if len(phone_users) > 0:
                    phone_numbers.append((phone_users[0], recipient))
                else:
                    phone_numbers.append((None, recipient))
            elif (not send_to_all_checked) and re.match(r'[\w\.]+', recipient):
                usernames.append(recipient)
            else:
                unknown_usernames.append(recipient)


        login_ids = dict([(r['key'], r['id']) for r in get_db().view("users/by_username", keys=usernames).all()])
        for username in usernames:
            if username not in login_ids:
                unknown_usernames.append(username)
        login_ids = login_ids.values()

        users = []
        empty_groups = []
        if len(group_names) > 0:
            users.extend(CouchUser.view('users/by_group', keys=[[domain, gn] for gn in group_names],
                                        include_docs=True).all())
            if len(users) == 0:
                empty_groups = group_names

        users.extend(CouchUser.view('_all_docs', keys=login_ids, include_docs=True).all())
        users = [user for user in users if user.is_active and not user.is_deleted()]

        phone_numbers.extend([(user, user.phone_number) for user in users])

        failed_numbers = []
        no_numbers = []
        sent = []
        for user, number in phone_numbers:
            if not number:
                no_numbers.append(user.raw_username)
            elif send_sms(domain, user.user_id if user else "", number, message):
                sent.append("%s" % (user.raw_username if user else number))
            else:
                failed_numbers.append("%s (%s)" % (
                    number,
                    user.raw_username if user else "<no username>"
                ))

        if empty_groups or failed_numbers or unknown_usernames or no_numbers:
            if empty_groups:
                messages.error(request, _("The following groups don't exist: ") + (', '.join(empty_groups)))
            if no_numbers:
                messages.error(request, _("The following users don't have phone numbers: ") + (', '.join(no_numbers)))
            if failed_numbers:
                messages.error(request, _("Couldn't send to the following number(s): ") + (', '.join(failed_numbers)))
            if unknown_usernames:
                messages.error(request, _("Couldn't find the following user(s): ") + (', '.join(unknown_usernames)))
            if sent:
                messages.success(request, _("Successfully sent: ") + (', '.join(sent)))
            else:
                messages.info(request, _("No messages were sent."))
        else:
            messages.success(request, _("Message sent"))

    return HttpResponseRedirect(
        request.META.get('HTTP_REFERER') or
        reverse(compose_message, args=[domain])
    )
예제 #41
0
def fire_sms_survey_event(reminder, handler, recipients, verified_numbers):
    if reminder.callback_try_count > 0:
        # Handle timeouts
        if handler.submit_partial_forms and (reminder.callback_try_count == len(reminder.current_event.callback_timeout_intervals)):
            # Submit partial form completions
            for session_id in reminder.xforms_session_ids:
                submit_unfinished_form(session_id, handler.include_case_side_effects)
        else:
            # Resend current question
            for session_id in reminder.xforms_session_ids:
                session = XFormsSession.view("smsforms/sessions_by_touchforms_id",
                                             startkey=[session_id],
                                             endkey=[session_id, {}],
                                             include_docs=True).one()
                if session.end_time is None:
                    vn = VerifiedNumber.view("sms/verified_number_by_owner_id",
                                             key=session.connection_id,
                                             include_docs=True).first()
                    if vn is not None:
                        metadata = MessageMetadata(
                            workflow=get_workflow(handler),
                            reminder_id=reminder._id,
                            xforms_session_couch_id=session._id,
                        )
                        resp = current_question(session_id)
                        send_sms_to_verified_number(vn, resp.event.text_prompt, metadata)
        return True
    else:
        reminder.xforms_session_ids = []

        # Get the app, module, and form
        try:
            form_unique_id = reminder.current_event.form_unique_id
            form = Form.get_form(form_unique_id)
            app = form.get_app()
            module = form.get_module()
        except Exception as e:
            raise_error(reminder, ERROR_FORM)
            return False

        # Start a touchforms session for each recipient
        for recipient in recipients:

            verified_number, unverified_number = get_recipient_phone_number(
                reminder, recipient, verified_numbers)

            domain_obj = Domain.get_by_name(reminder.domain, strict=True)
            no_verified_number = verified_number is None
            cant_use_unverified_number = (unverified_number is None or
                not domain_obj.send_to_duplicated_case_numbers or
                form_requires_input(form))
            if no_verified_number and cant_use_unverified_number:
                if len(recipients) == 1:
                    raise_error(reminder, ERROR_NO_VERIFIED_NUMBER)
                    return False
                else:
                    continue

            # Close all currently open sessions
            XFormsSession.close_all_open_sms_sessions(reminder.domain, recipient.get_id)

            # Start the new session
            if isinstance(recipient, CommCareCase) and not handler.force_surveys_to_use_triggered_case:
                case_id = recipient.get_id
            else:
                case_id = reminder.case_id
            session, responses = start_session(reminder.domain, recipient, app, module, form, case_id, case_for_case_submission=handler.force_surveys_to_use_triggered_case)
            session.survey_incentive = handler.survey_incentive
            session.workflow = get_workflow(handler)
            session.reminder_id = reminder._id
            session.save()
            reminder.xforms_session_ids.append(session.session_id)

            # Send out first message
            if len(responses) > 0:
                message = format_message_list(responses)
                metadata = MessageMetadata(
                    workflow=get_workflow(handler),
                    reminder_id=reminder._id,
                    xforms_session_couch_id=session._id,
                )
                if verified_number:
                    result = send_sms_to_verified_number(verified_number, message, metadata)
                else:
                    result = send_sms(reminder.domain, recipient, unverified_number,
                        message, metadata)

                if len(recipients) == 1:
                    return result

        return True
예제 #42
0
def fire_sms_event(reminder,
                   handler,
                   recipients,
                   verified_numbers,
                   workflow=None):
    metadata = MessageMetadata(
        workflow=workflow or get_workflow(handler),
        reminder_id=reminder._id,
    )
    current_event = reminder.current_event
    if handler.method in [METHOD_SMS, METHOD_SMS_CALLBACK]:
        template_params = {}
        case = reminder.case
        if case is not None:
            template_params["case"] = case.case_properties()
        for recipient in recipients:
            try:
                lang = recipient.get_language_code()
            except Exception:
                lang = None

            if handler.custom_content_handler is not None:
                if handler.custom_content_handler in settings.ALLOWED_CUSTOM_CONTENT_HANDLERS:
                    try:
                        content_handler = to_function(
                            settings.ALLOWED_CUSTOM_CONTENT_HANDLERS[
                                handler.custom_content_handler])
                    except Exception:
                        raise_error(reminder,
                                    ERROR_FINDING_CUSTOM_CONTENT_HANDLER)
                        return False
                    message = content_handler(reminder, handler, recipient)
                    # If the content handler returns None or empty string,
                    # don't send anything
                    if not message:
                        return True
                else:
                    raise_error(reminder, ERROR_INVALID_CUSTOM_CONTENT_HANDLER)
                    return False
            else:
                message = current_event.message.get(
                    lang, current_event.message[handler.default_lang])
                try:
                    message = Message.render(message, **template_params)
                except Exception:
                    if len(recipients) == 1:
                        raise_error(reminder, ERROR_RENDERING_MESSAGE % lang)
                        return False
                    else:
                        raise_warning()  # ERROR_RENDERING_MESSAGE
                        continue

            verified_number, unverified_number = get_recipient_phone_number(
                reminder, recipient, verified_numbers)

            domain_obj = Domain.get_by_name(reminder.domain, strict=True)
            if verified_number is not None:
                result = send_sms_to_verified_number(verified_number, message,
                                                     metadata)
            elif isinstance(recipient, CouchUser) and unverified_number:
                result = send_sms(reminder.domain, recipient,
                                  unverified_number, message, metadata)
            elif (isinstance(recipient, CommCareCase) and unverified_number
                  and domain_obj.send_to_duplicated_case_numbers):
                result = send_sms(reminder.domain, recipient,
                                  unverified_number, message, metadata)
            else:
                if len(recipients) == 1:
                    raise_error(reminder, ERROR_NO_VERIFIED_NUMBER)
                result = False

            if len(recipients) == 1:
                return result

        # For multiple recipients, always move to the next event
        return True
예제 #43
0
def fire_sms_event(reminder, handler, recipients, verified_numbers, workflow=None):
    message_tags = {
        "workflow" : workflow or get_workflow(handler),
        "reminder_id" : reminder._id,
    }
    current_event = reminder.current_event
    if handler.method in [METHOD_SMS, METHOD_SMS_CALLBACK]:
        template_params = {}
        case = reminder.case
        if case is not None:
            template_params["case"] = case.case_properties()
        for recipient in recipients:
            try:
                lang = recipient.get_language_code()
            except Exception:
                lang = None
            
            if handler.custom_content_handler is not None:
                if handler.custom_content_handler in settings.ALLOWED_CUSTOM_CONTENT_HANDLERS:
                    try:
                        content_handler = to_function(settings.ALLOWED_CUSTOM_CONTENT_HANDLERS[handler.custom_content_handler])
                    except Exception:
                        raise_error(reminder, ERROR_FINDING_CUSTOM_CONTENT_HANDLER)
                        return False
                    message = content_handler(reminder, handler, recipient)
                    # If the content handler returns None or empty string,
                    # don't send anything
                    if not message:
                        return True
                else:
                    raise_error(reminder, ERROR_INVALID_CUSTOM_CONTENT_HANDLER)
                    return False
            else:
                message = current_event.message.get(lang, current_event.message[handler.default_lang])
                try:
                    message = Message.render(message, **template_params)
                except Exception:
                    if len(recipients) == 1:
                        raise_error(reminder, ERROR_RENDERING_MESSAGE % lang)
                        return False
                    else:
                        raise_warning() # ERROR_RENDERING_MESSAGE
                        continue
            
            verified_number = verified_numbers[recipient.get_id]
            if verified_number is not None:
                result = send_sms_to_verified_number(verified_number, message, **message_tags)
                if not result:
                    raise_warning() # Could not send SMS
            elif isinstance(recipient, CouchUser):
                # If there is no verified number, but the recipient is a CouchUser, still try to send it
                try:
                    phone_number = recipient.phone_number
                except Exception:
                    phone_number = None
                
                if phone_number is None:
                    result = False
                    if len(recipients) == 1:
                        raise_error(reminder, ERROR_NO_OTHER_NUMBERS)
                    else:
                        raise_warning() # ERROR_NO_OTHER_NUMBERS
                else:
                    result = send_sms(reminder.domain, recipient, phone_number, message, **message_tags)
                    if not result:
                        raise_warning() # Could not send SMS
            else:
                if len(recipients) == 1:
                    raise_error(reminder, ERROR_NO_VERIFIED_NUMBER)
                else:
                    raise_warning() # ERROR_NO_VERIFIED_NUMBER
                result = False
            
            if len(recipients) == 1:
                return result
        
        # For multiple recipients, always move to the next event
        return True
    
    elif handler.method in [METHOD_TEST, METHOD_SMS_CALLBACK_TEST]:
        # Used for automated tests
        return True
예제 #44
0
def fire_sms_event(reminder, handler, recipients, verified_numbers, workflow=None):
    metadata = MessageMetadata(
        workflow=workflow or get_workflow(handler),
        reminder_id=reminder._id,
    )
    current_event = reminder.current_event
    template_params = {}
    case = reminder.case
    if case is not None:
        template_params["case"] = case.case_properties()
    for recipient in recipients:
        try:
            lang = recipient.get_language_code()
        except Exception:
            lang = None

        if handler.custom_content_handler is not None:
            if handler.custom_content_handler in settings.ALLOWED_CUSTOM_CONTENT_HANDLERS:
                try:
                    content_handler = to_function(settings.ALLOWED_CUSTOM_CONTENT_HANDLERS[handler.custom_content_handler])
                except Exception:
                    raise_error(reminder, ERROR_FINDING_CUSTOM_CONTENT_HANDLER)
                    return False
                message = content_handler(reminder, handler, recipient)
                # If the content handler returns None or empty string,
                # don't send anything
                if not message:
                    return True
            else:
                raise_error(reminder, ERROR_INVALID_CUSTOM_CONTENT_HANDLER)
                return False
        else:
            message = current_event.message.get(lang, current_event.message[handler.default_lang])
            try:
                message = Message.render(message, **template_params)
            except Exception:
                if len(recipients) == 1:
                    raise_error(reminder, ERROR_RENDERING_MESSAGE % lang)
                    return False
                else:
                    raise_warning() # ERROR_RENDERING_MESSAGE
                    continue

        verified_number, unverified_number = get_recipient_phone_number(
            reminder, recipient, verified_numbers)

        domain_obj = Domain.get_by_name(reminder.domain, strict=True)
        if verified_number is not None:
            result = send_sms_to_verified_number(verified_number,
                message, metadata)
        elif isinstance(recipient, CouchUser) and unverified_number:
            result = send_sms(reminder.domain, recipient, unverified_number,
                message, metadata)
        elif (isinstance(recipient, CommCareCase) and unverified_number and
            domain_obj.send_to_duplicated_case_numbers):
            result = send_sms(reminder.domain, recipient, unverified_number,
                message, metadata)
        else:
            if len(recipients) == 1:
                raise_error(reminder, ERROR_NO_VERIFIED_NUMBER)
            result = False

        if len(recipients) == 1:
            return result

    # For multiple recipients, always move to the next event
    return True
예제 #45
0
def fire_sms_survey_event(reminder, handler, recipients, verified_numbers):
    if reminder.callback_try_count > 0:
        # Handle timeouts
        if handler.submit_partial_forms and (
                reminder.callback_try_count == len(
                    reminder.current_event.callback_timeout_intervals)):
            # Submit partial form completions
            for session_id in reminder.xforms_session_ids:
                submit_unfinished_form(session_id,
                                       handler.include_case_side_effects)
        else:
            # Resend current question
            for session_id in reminder.xforms_session_ids:
                session = XFormsSession.view(
                    "smsforms/sessions_by_touchforms_id",
                    startkey=[session_id],
                    endkey=[session_id, {}],
                    include_docs=True).one()
                if session.end_time is None:
                    vn = VerifiedNumber.view("sms/verified_number_by_owner_id",
                                             key=session.connection_id,
                                             include_docs=True).first()
                    if vn is not None:
                        metadata = MessageMetadata(
                            workflow=get_workflow(handler),
                            reminder_id=reminder._id,
                            xforms_session_couch_id=session._id,
                        )
                        resp = current_question(session_id)
                        send_sms_to_verified_number(vn, resp.event.text_prompt,
                                                    metadata)
        return True
    else:
        reminder.xforms_session_ids = []

        # Get the app, module, and form
        try:
            form_unique_id = reminder.current_event.form_unique_id
            form = Form.get_form(form_unique_id)
            app = form.get_app()
            module = form.get_module()
        except Exception as e:
            raise_error(reminder, ERROR_FORM)
            return False

        # Start a touchforms session for each recipient
        for recipient in recipients:

            verified_number, unverified_number = get_recipient_phone_number(
                reminder, recipient, verified_numbers)

            domain_obj = Domain.get_by_name(reminder.domain, strict=True)
            no_verified_number = verified_number is None
            cant_use_unverified_number = (
                unverified_number is None
                or not domain_obj.send_to_duplicated_case_numbers
                or form_requires_input(form))
            if no_verified_number and cant_use_unverified_number:
                if len(recipients) == 1:
                    raise_error(reminder, ERROR_NO_VERIFIED_NUMBER)
                    return False
                else:
                    continue

            # Close all currently open sessions
            XFormsSession.close_all_open_sms_sessions(reminder.domain,
                                                      recipient.get_id)

            # Start the new session
            if isinstance(
                    recipient, CommCareCase
            ) and not handler.force_surveys_to_use_triggered_case:
                case_id = recipient.get_id
            else:
                case_id = reminder.case_id
            session, responses = start_session(
                reminder.domain,
                recipient,
                app,
                module,
                form,
                case_id,
                case_for_case_submission=handler.
                force_surveys_to_use_triggered_case)
            session.survey_incentive = handler.survey_incentive
            session.workflow = get_workflow(handler)
            session.reminder_id = reminder._id
            session.save()
            reminder.xforms_session_ids.append(session.session_id)

            # Send out first message
            if len(responses) > 0:
                message = format_message_list(responses)
                metadata = MessageMetadata(
                    workflow=get_workflow(handler),
                    reminder_id=reminder._id,
                    xforms_session_couch_id=session._id,
                )
                if verified_number:
                    result = send_sms_to_verified_number(
                        verified_number, message, metadata)
                else:
                    result = send_sms(reminder.domain, recipient,
                                      unverified_number, message, metadata)

                if len(recipients) == 1:
                    return result

        return True
예제 #46
0
    def test_outgoing_failure_indefinite_retry(self, process_sms_delay_mock,
                                               enqueue_directly_mock):
        timestamp = datetime(2016, 1, 1, 12, 0)
        update_toggle_cache(RETRY_SMS_INDEFINITELY.slug, self.domain, True,
                            NAMESPACE_DOMAIN)

        with patch_datetime_api(timestamp):
            send_sms(self.domain, None, '+999123', 'test outgoing')

        self.assertEqual(enqueue_directly_mock.call_count, 1)
        self.assertEqual(self.queued_sms_count, 1)
        self.assertEqual(self.reporting_sms_count, 0)

        for i in range(settings.SMS_QUEUE_MAX_PROCESSING_ATTEMPTS):
            queued_sms = self.get_queued_sms()
            self.assertEqual(queued_sms.domain, self.domain)
            self.assertEqual(queued_sms.phone_number, '+999123')
            self.assertEqual(queued_sms.text, 'test outgoing')
            self.assertEqual(queued_sms.datetime_to_process, timestamp)
            self.assertEqual(queued_sms.processed, False)
            self.assertEqual(queued_sms.error, False)
            self.assertEqual(queued_sms.num_processing_attempts, i)

            with patch_failed_send() as send_mock, patch_datetime_tasks(
                    timestamp + timedelta(seconds=1)):
                process_sms(queued_sms.pk)

            self.assertEqual(process_sms_delay_mock.call_count, 0)
            self.assertBillableDoesNotExist(queued_sms.couch_id)

            self.assertEqual(send_mock.call_count, 1)
            self.assertEqual(self.queued_sms_count, 1)
            self.assertEqual(self.reporting_sms_count, 0)
            timestamp += timedelta(
                minutes=settings.SMS_QUEUE_REPROCESS_INTERVAL)

        timestamp += (timedelta(
            minutes=settings.SMS_QUEUE_REPROCESS_INDEFINITELY_INTERVAL) -
                      timedelta(minutes=settings.SMS_QUEUE_REPROCESS_INTERVAL))
        queued_sms = self.get_queued_sms()
        self.assertEqual(queued_sms.domain, self.domain)
        self.assertEqual(queued_sms.phone_number, '+999123')
        self.assertEqual(queued_sms.text, 'test outgoing')
        self.assertEqual(queued_sms.datetime_to_process, timestamp)
        self.assertEqual(queued_sms.processed, False)
        self.assertEqual(queued_sms.error, False)
        self.assertEqual(queued_sms.num_processing_attempts,
                         settings.SMS_QUEUE_MAX_PROCESSING_ATTEMPTS)

        with patch_successful_send() as send_mock, patch_datetime_tasks(
                timestamp + timedelta(seconds=1)):
            process_sms(queued_sms.pk)

        self.assertEqual(send_mock.call_count, 1)
        self.assertEqual(self.queued_sms_count, 0)
        self.assertEqual(self.reporting_sms_count, 1)

        reporting_sms = self.get_reporting_sms()
        self.assertEqual(reporting_sms.processed, True)
        self.assertEqual(reporting_sms.error, False)
        self.assertEqual(reporting_sms.num_processing_attempts,
                         settings.SMS_QUEUE_MAX_PROCESSING_ATTEMPTS + 1)

        self.assertEqual(process_sms_delay_mock.call_count, 0)
        self.assertBillableExists(reporting_sms.couch_id)
예제 #47
0
 def fire(self, reminder):
     """
     Sends the message associated with the given CaseReminder's current event.
     
     reminder    The CaseReminder which to fire.
     
     return      True on success, False on failure
     """
     # Get the proper recipient
     recipient = reminder.recipient
     
     # Retrieve the VerifiedNumber entry for the recipient
     try:
         verified_number = recipient.get_verified_number()
     except Exception:
         verified_number = None
         
     # Get the language of the recipient
     try:
         lang = recipient.get_language_code()
     except Exception:
         lang = None
     
     if reminder.method == "survey":
         # Close all currently open sessions
         sessions = XFormsSession.view("smsforms/open_sessions_by_connection",
                                      key=[reminder.domain, recipient.get_id],
                                      include_docs=True).all()
         for session in sessions:
             session.end(False)
             session.save()
         
         # Start the new session
         try:
             form_unique_id = reminder.current_event.form_unique_id
             form = Form.get_form(form_unique_id)
             app = form.get_app()
             module = form.get_module()
         except Exception as e:
             print e
             print "ERROR: Could not load survey form for handler " + reminder.handler_id + ", event " + str(reminder.current_event_sequence_num)
             return False
         session, responses = start_session(reminder.domain, recipient, app, module, form, reminder.case_id)
         
         # Send out first message
         if len(responses) > 0:
             message = format_message_list(responses)
             if verified_number is not None:
                 return send_sms_to_verified_number(verified_number, message)
             else:
                 return True
     else:
         # If it is a callback reminder and the callback has been received, skip sending the next timeout message
         if (reminder.method == "callback" or reminder.method == "callback_test") and len(reminder.current_event.callback_timeout_intervals) > 0 and (reminder.callback_try_count > 0):
             if CallLog.inbound_call_exists(recipient.doc_type, recipient._id, reminder.last_fired):
                 reminder.callback_received = True
                 return True
             elif len(reminder.current_event.callback_timeout_intervals) == reminder.callback_try_count:
                 # On the last callback timeout, instead of sending the SMS again, log the missed callback
                 event = EventLog(
                     domain          = reminder.domain,
                     date            = self.get_now(),
                     event_type      = MISSED_EXPECTED_CALLBACK
                 )
                 if verified_number is not None:
                     event.couch_recipient_doc_type = verified_number.owner_doc_type
                     event.couch_recipient = verified_number.owner_id
                 event.save()
                 return True
         reminder.last_fired = self.get_now()
         message = reminder.current_event.message.get(lang, reminder.current_event.message[self.default_lang])
         message = Message.render(message, case=reminder.case.case_properties())
         if reminder.method == "sms" or reminder.method == "callback":
             if verified_number is not None:
                 return send_sms_to_verified_number(verified_number, message)
             elif self.recipient == RECIPIENT_USER:
                 # If there is no verified number, but the recipient is a CommCareUser, still try to send it
                 try:
                     phone_number = reminder.user.phone_number
                 except Exception:
                     # If the user has no phone number, we cannot send any SMS
                     return False
                 return send_sms(reminder.domain, reminder.user_id, phone_number, message)
             else:
                 return False
         elif reminder.method == "test" or reminder.method == "callback_test":
             print(message)
             return True
예제 #48
0
def fire_sms_survey_event(reminder, handler, recipients, verified_numbers, logged_event):
    if reminder.callback_try_count > 0:
        # Handle timeouts
        if handler.submit_partial_forms and (
            reminder.callback_try_count == len(reminder.current_event.callback_timeout_intervals)
        ):
            # Submit partial form completions
            for session_id in reminder.xforms_session_ids:
                submit_unfinished_form(session_id, handler.include_case_side_effects)
        else:
            # Resend current question
            for session_id in reminder.xforms_session_ids:
                session = get_session_by_session_id(session_id)
                if session.end_time is None:
                    vn = VerifiedNumber.view(
                        "sms/verified_number_by_owner_id", key=session.connection_id, include_docs=True
                    ).first()
                    if vn is not None:
                        metadata = MessageMetadata(
                            workflow=get_workflow(handler),
                            reminder_id=reminder._id,
                            xforms_session_couch_id=session._id,
                        )
                        resp = current_question(session_id)
                        send_sms_to_verified_number(vn, resp.event.text_prompt, metadata)
    else:
        reminder.xforms_session_ids = []
        domain_obj = Domain.get_by_name(reminder.domain, strict=True)

        # Get the app, module, and form
        try:
            form_unique_id = reminder.current_event.form_unique_id
            form = Form.get_form(form_unique_id)
            app = form.get_app()
            module = form.get_module()
        except Exception:
            logged_event.error(MessagingEvent.ERROR_CANNOT_FIND_FORM)
            return

        # Start a touchforms session for each recipient
        for recipient in recipients:
            logged_subevent = logged_event.create_subevent(handler, reminder, recipient)

            verified_number, unverified_number = get_recipient_phone_number(reminder, recipient, verified_numbers)

            no_verified_number = verified_number is None
            cant_use_unverified_number = (
                unverified_number is None or not domain_obj.send_to_duplicated_case_numbers or form_requires_input(form)
            )
            if no_verified_number and cant_use_unverified_number:
                logged_subevent.error(MessagingEvent.ERROR_NO_TWO_WAY_PHONE_NUMBER)
                continue

            key = "start-sms-survey-for-contact-%s" % recipient.get_id
            with CriticalSection([key], timeout=60):
                # Get the case to submit the form against, if any
                if isinstance(recipient, CommCareCase) and not handler.force_surveys_to_use_triggered_case:
                    case_id = recipient.get_id
                else:
                    case_id = reminder.case_id

                if form.requires_case() and not case_id:
                    logged_subevent.error(MessagingEvent.ERROR_NO_CASE_GIVEN)
                    continue

                # Close all currently open sessions
                SQLXFormsSession.close_all_open_sms_sessions(reminder.domain, recipient.get_id)

                # Start the new session
                try:
                    session, responses = start_session(
                        reminder.domain,
                        recipient,
                        app,
                        module,
                        form,
                        case_id,
                        case_for_case_submission=handler.force_surveys_to_use_triggered_case,
                    )
                except TouchformsError as e:
                    human_readable_message = e.response_data.get("human_readable_message", None)

                    logged_subevent.error(
                        MessagingEvent.ERROR_TOUCHFORMS_ERROR, additional_error_text=human_readable_message
                    )

                    if touchforms_error_is_config_error(e):
                        # Don't reraise the exception because this means there are configuration
                        # issues with the form that need to be fixed
                        continue
                    else:
                        # Reraise the exception so that the framework retries it again later
                        raise
                except Exception as e:
                    logged_subevent.error(MessagingEvent.ERROR_TOUCHFORMS_ERROR)
                    # Reraise the exception so that the framework retries it again later
                    raise
                session.survey_incentive = handler.survey_incentive
                session.workflow = get_workflow(handler)
                session.reminder_id = reminder._id
                session.save()

            reminder.xforms_session_ids.append(session.session_id)
            logged_subevent.xforms_session = session
            logged_subevent.save()

            # Send out first message
            if len(responses) > 0:
                message = format_message_list(responses)
                metadata = MessageMetadata(
                    workflow=get_workflow(handler), reminder_id=reminder._id, xforms_session_couch_id=session._id
                )
                if verified_number:
                    send_sms_to_verified_number(verified_number, message, metadata)
                else:
                    send_sms(reminder.domain, recipient, unverified_number, message, metadata)

            logged_subevent.completed()
예제 #49
0
                if len(recipients) == 1:
                    raise_error(reminder, ERROR_RENDERING_MESSAGE % lang)
                    return False
                else:
                    raise_warning()  # ERROR_RENDERING_MESSAGE
                    continue

        verified_number, unverified_number = get_recipient_phone_number(
            reminder, recipient, verified_numbers)

        domain_obj = Domain.get_by_name(reminder.domain, strict=True)
        if verified_number is not None:
            result = send_sms_to_verified_number(verified_number, message,
                                                 metadata)
        elif isinstance(recipient, CouchUser) and unverified_number:
            result = send_sms(reminder.domain, recipient, unverified_number,
                              message, metadata)
        elif (isinstance(recipient, CommCareCase) and unverified_number
              and domain_obj.send_to_duplicated_case_numbers):
            result = send_sms(reminder.domain, recipient, unverified_number,
                              message, metadata)
        else:
            if len(recipients) == 1:
                raise_error(reminder, ERROR_NO_VERIFIED_NUMBER)
            result = False

        if len(recipients) == 1:
            return result

    # For multiple recipients, always move to the next event
    return True
예제 #50
0
def fire_sms_event(reminder, handler, recipients, verified_numbers):
    current_event = reminder.current_event
    if reminder.method in [METHOD_SMS, METHOD_SMS_CALLBACK]:
        for recipient in recipients:
            try:
                lang = recipient.get_language_code()
            except Exception:
                lang = None

            message = current_event.message.get(
                lang, current_event.message[handler.default_lang])
            try:
                message = Message.render(message,
                                         case=reminder.case.case_properties())
            except Exception:
                if len(recipients) == 1:
                    raise_error(reminder, ERROR_RENDERING_MESSAGE % lang)
                    return False
                else:
                    raise_warning()  # ERROR_RENDERING_MESSAGE
                    continue

            verified_number = verified_numbers[recipient.get_id]
            if verified_number is not None:
                result = send_sms_to_verified_number(verified_number, message)
                if not result:
                    raise_warning()  # Could not send SMS
            elif isinstance(recipient, CouchUser):
                # If there is no verified number, but the recipient is a CouchUser, still try to send it
                try:
                    phone_number = recipient.phone_number
                except Exception:
                    phone_number = None

                if phone_number is None:
                    result = False
                    if len(recipients) == 1:
                        raise_error(reminder, ERROR_NO_OTHER_NUMBERS)
                    else:
                        raise_warning()  # ERROR_NO_OTHER_NUMBERS
                else:
                    result = send_sms(reminder.domain, recipient.get_id,
                                      phone_number, message)
                    if not result:
                        raise_warning()  # Could not send SMS
            else:
                if len(recipients) == 1:
                    raise_error(reminder, ERROR_NO_VERIFIED_NUMBER)
                else:
                    raise_warning()  # ERROR_NO_VERIFIED_NUMBER
                result = False

            if len(recipients) == 1:
                return result

        # For multiple recipients, always move to the next event
        return True

    elif reminder.method in [METHOD_TEST, METHOD_SMS_CALLBACK_TEST]:
        # Used for automated tests
        return True
예제 #51
0
    def test_backend(self):
        # Test the backend map

        self.assertTrue(
            send_sms(self.domain, None, "15551234567", "Test for BACKEND2"))
        self.assertFalse(self.backend1.invoke_doc_exists())
        self.assertTrue(self.backend2.invoke_doc_exists())
        self.assertFalse(self.backend3.invoke_doc_exists())
        self.assertFalse(self.backend4.invoke_doc_exists())
        self.assertFalse(self.backend5.invoke_doc_exists())
        self.assertFalse(self.backend6.invoke_doc_exists())
        self.assertFalse(self.backend7.invoke_doc_exists())
        self.assertFalse(self.backend8.invoke_doc_exists())
        self.assertFalse(self.backend9.invoke_doc_exists())
        self.assertFalse(self.backend10.invoke_doc_exists())
        self.backend2.delete_invoke_doc()
        self.assertFalse(self.backend2.invoke_doc_exists())

        self.assertTrue(
            send_sms(self.domain, None, "9100000000", "Test for BACKEND3"))
        self.assertFalse(self.backend1.invoke_doc_exists())
        self.assertFalse(self.backend2.invoke_doc_exists())
        self.assertTrue(self.backend3.invoke_doc_exists())
        self.assertFalse(self.backend4.invoke_doc_exists())
        self.assertFalse(self.backend5.invoke_doc_exists())
        self.assertFalse(self.backend6.invoke_doc_exists())
        self.assertFalse(self.backend7.invoke_doc_exists())
        self.assertFalse(self.backend8.invoke_doc_exists())
        self.assertFalse(self.backend9.invoke_doc_exists())
        self.assertFalse(self.backend10.invoke_doc_exists())
        self.backend3.delete_invoke_doc()
        self.assertFalse(self.backend3.invoke_doc_exists())

        self.assertTrue(
            send_sms(self.domain, None, "26500000000", "Test for BACKEND4"))
        self.assertFalse(self.backend1.invoke_doc_exists())
        self.assertFalse(self.backend2.invoke_doc_exists())
        self.assertFalse(self.backend3.invoke_doc_exists())
        self.assertTrue(self.backend4.invoke_doc_exists())
        self.assertFalse(self.backend5.invoke_doc_exists())
        self.assertFalse(self.backend6.invoke_doc_exists())
        self.assertFalse(self.backend7.invoke_doc_exists())
        self.assertFalse(self.backend8.invoke_doc_exists())
        self.assertFalse(self.backend9.invoke_doc_exists())
        self.assertFalse(self.backend10.invoke_doc_exists())
        self.backend4.delete_invoke_doc()
        self.assertFalse(self.backend4.invoke_doc_exists())

        self.assertTrue(
            send_sms(self.domain, None, "25800000000", "Test for BACKEND1"))
        self.assertTrue(self.backend1.invoke_doc_exists())
        self.assertFalse(self.backend2.invoke_doc_exists())
        self.assertFalse(self.backend3.invoke_doc_exists())
        self.assertFalse(self.backend4.invoke_doc_exists())
        self.assertFalse(self.backend5.invoke_doc_exists())
        self.assertFalse(self.backend6.invoke_doc_exists())
        self.assertFalse(self.backend7.invoke_doc_exists())
        self.assertFalse(self.backend8.invoke_doc_exists())
        self.assertFalse(self.backend9.invoke_doc_exists())
        self.assertFalse(self.backend10.invoke_doc_exists())
        self.backend1.delete_invoke_doc()
        self.assertFalse(self.backend1.invoke_doc_exists())

        # Test overriding with a domain-level backend

        self.domain_obj.default_sms_backend_id = self.backend5._id
        self.domain_obj.save()

        self.assertTrue(
            send_sms(self.domain, None, "15551234567", "Test for BACKEND5"))
        self.assertFalse(self.backend1.invoke_doc_exists())
        self.assertFalse(self.backend2.invoke_doc_exists())
        self.assertFalse(self.backend3.invoke_doc_exists())
        self.assertFalse(self.backend4.invoke_doc_exists())
        self.assertTrue(self.backend5.invoke_doc_exists())
        self.assertFalse(self.backend6.invoke_doc_exists())
        self.assertFalse(self.backend7.invoke_doc_exists())
        self.assertFalse(self.backend8.invoke_doc_exists())
        self.assertFalse(self.backend9.invoke_doc_exists())
        self.assertFalse(self.backend10.invoke_doc_exists())
        self.backend5.delete_invoke_doc()
        self.assertFalse(self.backend5.invoke_doc_exists())

        # Test use of backend that another domain owns but has granted access

        self.domain_obj.default_sms_backend_id = self.backend6._id
        self.domain_obj.save()

        self.assertTrue(
            send_sms(self.domain, None, "25800000000", "Test for BACKEND6"))
        self.assertFalse(self.backend1.invoke_doc_exists())
        self.assertFalse(self.backend2.invoke_doc_exists())
        self.assertFalse(self.backend3.invoke_doc_exists())
        self.assertFalse(self.backend4.invoke_doc_exists())
        self.assertFalse(self.backend5.invoke_doc_exists())
        self.assertTrue(self.backend6.invoke_doc_exists())
        self.assertFalse(self.backend7.invoke_doc_exists())
        self.assertFalse(self.backend8.invoke_doc_exists())
        self.assertFalse(self.backend9.invoke_doc_exists())
        self.assertFalse(self.backend10.invoke_doc_exists())
        self.backend6.delete_invoke_doc()
        self.assertFalse(self.backend6.invoke_doc_exists())

        # Test backend access control

        self.domain_obj.default_sms_backend_id = self.backend7._id
        self.domain_obj.save()

        self.assertFalse(
            send_sms(self.domain, None, "25800000000", "Test for BACKEND7"))
        self.assertFalse(self.backend1.invoke_doc_exists())
        self.assertFalse(self.backend2.invoke_doc_exists())
        self.assertFalse(self.backend3.invoke_doc_exists())
        self.assertFalse(self.backend4.invoke_doc_exists())
        self.assertFalse(self.backend5.invoke_doc_exists())
        self.assertFalse(self.backend6.invoke_doc_exists())
        self.assertFalse(self.backend7.invoke_doc_exists())
        self.assertFalse(self.backend8.invoke_doc_exists())
        self.assertFalse(self.backend9.invoke_doc_exists())
        self.assertFalse(self.backend10.invoke_doc_exists())

        # Test sending to verified number with backend map

        self.domain_obj.default_sms_backend_id = None
        self.domain_obj.save()

        verified_number = self.contact.get_verified_number()
        self.assertTrue(verified_number is not None)
        self.assertTrue(verified_number.backend_id is None)
        self.assertEqual(verified_number.phone_number, "15551234567")

        self.assertTrue(
            send_sms_to_verified_number(verified_number, "Test for BACKEND2"))
        self.assertFalse(self.backend1.invoke_doc_exists())
        self.assertTrue(self.backend2.invoke_doc_exists())
        self.assertFalse(self.backend3.invoke_doc_exists())
        self.assertFalse(self.backend4.invoke_doc_exists())
        self.assertFalse(self.backend5.invoke_doc_exists())
        self.assertFalse(self.backend6.invoke_doc_exists())
        self.assertFalse(self.backend7.invoke_doc_exists())
        self.assertFalse(self.backend8.invoke_doc_exists())
        self.assertFalse(self.backend9.invoke_doc_exists())
        self.assertFalse(self.backend10.invoke_doc_exists())
        self.backend2.delete_invoke_doc()
        self.assertFalse(self.backend2.invoke_doc_exists())

        # Test sending to verified number with default domain backend

        self.domain_obj.default_sms_backend_id = self.backend5._id
        self.domain_obj.save()

        self.assertTrue(
            send_sms_to_verified_number(verified_number, "Test for BACKEND5"))
        self.assertFalse(self.backend1.invoke_doc_exists())
        self.assertFalse(self.backend2.invoke_doc_exists())
        self.assertFalse(self.backend3.invoke_doc_exists())
        self.assertFalse(self.backend4.invoke_doc_exists())
        self.assertTrue(self.backend5.invoke_doc_exists())
        self.assertFalse(self.backend6.invoke_doc_exists())
        self.assertFalse(self.backend7.invoke_doc_exists())
        self.assertFalse(self.backend8.invoke_doc_exists())
        self.assertFalse(self.backend9.invoke_doc_exists())
        self.assertFalse(self.backend10.invoke_doc_exists())
        self.backend5.delete_invoke_doc()
        self.assertFalse(self.backend5.invoke_doc_exists())

        # Test sending to verified number with a contact-level backend owned by the domain

        self.case.set_case_property("contact_backend_id", "BACKEND")
        self.case.save()
        self.contact = CommConnectCase.wrap(self.case.to_json())
        verified_number = self.contact.get_verified_number()
        self.assertTrue(verified_number is not None)
        self.assertEqual(verified_number.backend_id, "BACKEND")
        self.assertEqual(verified_number.phone_number, "15551234567")

        self.assertTrue(
            send_sms_to_verified_number(verified_number, "Test for BACKEND"))
        self.assertFalse(self.backend1.invoke_doc_exists())
        self.assertFalse(self.backend2.invoke_doc_exists())
        self.assertFalse(self.backend3.invoke_doc_exists())
        self.assertFalse(self.backend4.invoke_doc_exists())
        self.assertFalse(self.backend5.invoke_doc_exists())
        self.assertFalse(self.backend6.invoke_doc_exists())
        self.assertFalse(self.backend7.invoke_doc_exists())
        self.assertTrue(self.backend8.invoke_doc_exists())
        self.assertFalse(self.backend9.invoke_doc_exists())
        self.assertFalse(self.backend10.invoke_doc_exists())
        self.backend8.delete_invoke_doc()
        self.assertFalse(self.backend8.invoke_doc_exists())

        # Test sending to verified number with a contact-level backend granted to the domain by another domain

        self.backend8.delete()

        self.assertTrue(
            send_sms_to_verified_number(verified_number, "Test for BACKEND"))
        self.assertFalse(self.backend1.invoke_doc_exists())
        self.assertFalse(self.backend2.invoke_doc_exists())
        self.assertFalse(self.backend3.invoke_doc_exists())
        self.assertFalse(self.backend4.invoke_doc_exists())
        self.assertFalse(self.backend5.invoke_doc_exists())
        self.assertFalse(self.backend6.invoke_doc_exists())
        self.assertFalse(self.backend7.invoke_doc_exists())
        self.assertTrue(self.backend9.invoke_doc_exists())
        self.assertFalse(self.backend10.invoke_doc_exists())
        self.backend9.delete_invoke_doc()
        self.assertFalse(self.backend9.invoke_doc_exists())

        # Test sending to verified number with a contact-level global backend

        self.backend9.delete()

        self.assertTrue(
            send_sms_to_verified_number(verified_number, "Test for BACKEND"))
        self.assertFalse(self.backend1.invoke_doc_exists())
        self.assertFalse(self.backend2.invoke_doc_exists())
        self.assertFalse(self.backend3.invoke_doc_exists())
        self.assertFalse(self.backend4.invoke_doc_exists())
        self.assertFalse(self.backend5.invoke_doc_exists())
        self.assertFalse(self.backend6.invoke_doc_exists())
        self.assertFalse(self.backend7.invoke_doc_exists())
        self.assertTrue(self.backend10.invoke_doc_exists())
        self.backend10.delete_invoke_doc()
        self.assertFalse(self.backend10.invoke_doc_exists())

        # Test raising exception if contact-level backend is not found

        self.backend10.delete()

        try:
            self.assertTrue(
                send_sms_to_verified_number(verified_number,
                                            "Test for BACKEND"))
        except BadSMSConfigException:
            pass
        else:
            self.assertTrue(False)

        # Test send_sms_with_backend

        self.assertTrue(
            send_sms_with_backend(self.domain, "+15551234567",
                                  "Test for BACKEND3", self.backend3._id))
        self.assertFalse(self.backend1.invoke_doc_exists())
        self.assertFalse(self.backend2.invoke_doc_exists())
        self.assertTrue(self.backend3.invoke_doc_exists())
        self.assertFalse(self.backend4.invoke_doc_exists())
        self.assertFalse(self.backend5.invoke_doc_exists())
        self.assertFalse(self.backend6.invoke_doc_exists())
        self.assertFalse(self.backend7.invoke_doc_exists())
        self.backend3.delete_invoke_doc()
        self.assertFalse(self.backend3.invoke_doc_exists())

        # Test send_sms_with_backend_name

        self.assertTrue(
            send_sms_with_backend_name(self.domain, "+15551234567",
                                       "Test for BACKEND3", "BACKEND3"))
        self.assertFalse(self.backend1.invoke_doc_exists())
        self.assertFalse(self.backend2.invoke_doc_exists())
        self.assertTrue(self.backend3.invoke_doc_exists())
        self.assertFalse(self.backend4.invoke_doc_exists())
        self.assertFalse(self.backend5.invoke_doc_exists())
        self.assertFalse(self.backend6.invoke_doc_exists())
        self.assertFalse(self.backend7.invoke_doc_exists())
        self.backend3.delete_invoke_doc()
        self.assertFalse(self.backend3.invoke_doc_exists())
예제 #52
0
    def test_backend(self):
        # Test the backend map

        self.assertTrue(send_sms(self.domain, None, "15551234567", "Test for BACKEND2"))
        self.assertFalse(self.backend1.invoke_doc_exists())
        self.assertTrue(self.backend2.invoke_doc_exists())
        self.assertFalse(self.backend3.invoke_doc_exists())
        self.assertFalse(self.backend4.invoke_doc_exists())
        self.assertFalse(self.backend5.invoke_doc_exists())
        self.assertFalse(self.backend6.invoke_doc_exists())
        self.assertFalse(self.backend7.invoke_doc_exists())
        self.assertFalse(self.backend8.invoke_doc_exists())
        self.assertFalse(self.backend9.invoke_doc_exists())
        self.assertFalse(self.backend10.invoke_doc_exists())
        self.backend2.delete_invoke_doc()
        self.assertFalse(self.backend2.invoke_doc_exists())

        self.assertTrue(send_sms(self.domain, None, "9100000000", "Test for BACKEND3"))
        self.assertFalse(self.backend1.invoke_doc_exists())
        self.assertFalse(self.backend2.invoke_doc_exists())
        self.assertTrue(self.backend3.invoke_doc_exists())
        self.assertFalse(self.backend4.invoke_doc_exists())
        self.assertFalse(self.backend5.invoke_doc_exists())
        self.assertFalse(self.backend6.invoke_doc_exists())
        self.assertFalse(self.backend7.invoke_doc_exists())
        self.assertFalse(self.backend8.invoke_doc_exists())
        self.assertFalse(self.backend9.invoke_doc_exists())
        self.assertFalse(self.backend10.invoke_doc_exists())
        self.backend3.delete_invoke_doc()
        self.assertFalse(self.backend3.invoke_doc_exists())

        self.assertTrue(send_sms(self.domain, None, "26500000000", "Test for BACKEND4"))
        self.assertFalse(self.backend1.invoke_doc_exists())
        self.assertFalse(self.backend2.invoke_doc_exists())
        self.assertFalse(self.backend3.invoke_doc_exists())
        self.assertTrue(self.backend4.invoke_doc_exists())
        self.assertFalse(self.backend5.invoke_doc_exists())
        self.assertFalse(self.backend6.invoke_doc_exists())
        self.assertFalse(self.backend7.invoke_doc_exists())
        self.assertFalse(self.backend8.invoke_doc_exists())
        self.assertFalse(self.backend9.invoke_doc_exists())
        self.assertFalse(self.backend10.invoke_doc_exists())
        self.backend4.delete_invoke_doc()
        self.assertFalse(self.backend4.invoke_doc_exists())

        self.assertTrue(send_sms(self.domain, None, "25800000000", "Test for BACKEND1"))
        self.assertTrue(self.backend1.invoke_doc_exists())
        self.assertFalse(self.backend2.invoke_doc_exists())
        self.assertFalse(self.backend3.invoke_doc_exists())
        self.assertFalse(self.backend4.invoke_doc_exists())
        self.assertFalse(self.backend5.invoke_doc_exists())
        self.assertFalse(self.backend6.invoke_doc_exists())
        self.assertFalse(self.backend7.invoke_doc_exists())
        self.assertFalse(self.backend8.invoke_doc_exists())
        self.assertFalse(self.backend9.invoke_doc_exists())
        self.assertFalse(self.backend10.invoke_doc_exists())
        self.backend1.delete_invoke_doc()
        self.assertFalse(self.backend1.invoke_doc_exists())

        # Test overriding with a domain-level backend

        self.domain_obj.default_sms_backend_id = self.backend5._id
        self.domain_obj.save()

        self.assertTrue(send_sms(self.domain, None, "15551234567", "Test for BACKEND5"))
        self.assertFalse(self.backend1.invoke_doc_exists())
        self.assertFalse(self.backend2.invoke_doc_exists())
        self.assertFalse(self.backend3.invoke_doc_exists())
        self.assertFalse(self.backend4.invoke_doc_exists())
        self.assertTrue(self.backend5.invoke_doc_exists())
        self.assertFalse(self.backend6.invoke_doc_exists())
        self.assertFalse(self.backend7.invoke_doc_exists())
        self.assertFalse(self.backend8.invoke_doc_exists())
        self.assertFalse(self.backend9.invoke_doc_exists())
        self.assertFalse(self.backend10.invoke_doc_exists())
        self.backend5.delete_invoke_doc()
        self.assertFalse(self.backend5.invoke_doc_exists())

        # Test use of backend that another domain owns but has granted access

        self.domain_obj.default_sms_backend_id = self.backend6._id
        self.domain_obj.save()

        self.assertTrue(send_sms(self.domain, None, "25800000000", "Test for BACKEND6"))
        self.assertFalse(self.backend1.invoke_doc_exists())
        self.assertFalse(self.backend2.invoke_doc_exists())
        self.assertFalse(self.backend3.invoke_doc_exists())
        self.assertFalse(self.backend4.invoke_doc_exists())
        self.assertFalse(self.backend5.invoke_doc_exists())
        self.assertTrue(self.backend6.invoke_doc_exists())
        self.assertFalse(self.backend7.invoke_doc_exists())
        self.assertFalse(self.backend8.invoke_doc_exists())
        self.assertFalse(self.backend9.invoke_doc_exists())
        self.assertFalse(self.backend10.invoke_doc_exists())
        self.backend6.delete_invoke_doc()
        self.assertFalse(self.backend6.invoke_doc_exists())

        # Test backend access control

        self.domain_obj.default_sms_backend_id = self.backend7._id
        self.domain_obj.save()

        self.assertFalse(send_sms(self.domain, None, "25800000000", "Test for BACKEND7"))
        self.assertFalse(self.backend1.invoke_doc_exists())
        self.assertFalse(self.backend2.invoke_doc_exists())
        self.assertFalse(self.backend3.invoke_doc_exists())
        self.assertFalse(self.backend4.invoke_doc_exists())
        self.assertFalse(self.backend5.invoke_doc_exists())
        self.assertFalse(self.backend6.invoke_doc_exists())
        self.assertFalse(self.backend7.invoke_doc_exists())
        self.assertFalse(self.backend8.invoke_doc_exists())
        self.assertFalse(self.backend9.invoke_doc_exists())
        self.assertFalse(self.backend10.invoke_doc_exists())

        # Test sending to verified number with backend map

        self.domain_obj.default_sms_backend_id = None
        self.domain_obj.save()

        verified_number = self.contact.get_verified_number()
        self.assertTrue(verified_number is not None)
        self.assertTrue(verified_number.backend_id is None)
        self.assertEqual(verified_number.phone_number, "15551234567")

        self.assertTrue(send_sms_to_verified_number(verified_number, "Test for BACKEND2"))
        self.assertFalse(self.backend1.invoke_doc_exists())
        self.assertTrue(self.backend2.invoke_doc_exists())
        self.assertFalse(self.backend3.invoke_doc_exists())
        self.assertFalse(self.backend4.invoke_doc_exists())
        self.assertFalse(self.backend5.invoke_doc_exists())
        self.assertFalse(self.backend6.invoke_doc_exists())
        self.assertFalse(self.backend7.invoke_doc_exists())
        self.assertFalse(self.backend8.invoke_doc_exists())
        self.assertFalse(self.backend9.invoke_doc_exists())
        self.assertFalse(self.backend10.invoke_doc_exists())
        self.backend2.delete_invoke_doc()
        self.assertFalse(self.backend2.invoke_doc_exists())

        # Test sending to verified number with default domain backend

        self.domain_obj.default_sms_backend_id = self.backend5._id
        self.domain_obj.save()

        self.assertTrue(send_sms_to_verified_number(verified_number, "Test for BACKEND5"))
        self.assertFalse(self.backend1.invoke_doc_exists())
        self.assertFalse(self.backend2.invoke_doc_exists())
        self.assertFalse(self.backend3.invoke_doc_exists())
        self.assertFalse(self.backend4.invoke_doc_exists())
        self.assertTrue(self.backend5.invoke_doc_exists())
        self.assertFalse(self.backend6.invoke_doc_exists())
        self.assertFalse(self.backend7.invoke_doc_exists())
        self.assertFalse(self.backend8.invoke_doc_exists())
        self.assertFalse(self.backend9.invoke_doc_exists())
        self.assertFalse(self.backend10.invoke_doc_exists())
        self.backend5.delete_invoke_doc()
        self.assertFalse(self.backend5.invoke_doc_exists())

        # Test sending to verified number with a contact-level backend owned by the domain

        self.case.set_case_property("contact_backend_id", "BACKEND")
        self.case.save()
        self.contact = CommConnectCase.wrap(self.case.to_json())
        verified_number = self.contact.get_verified_number()
        self.assertTrue(verified_number is not None)
        self.assertEqual(verified_number.backend_id, "BACKEND")
        self.assertEqual(verified_number.phone_number, "15551234567")

        self.assertTrue(send_sms_to_verified_number(verified_number, "Test for BACKEND"))
        self.assertFalse(self.backend1.invoke_doc_exists())
        self.assertFalse(self.backend2.invoke_doc_exists())
        self.assertFalse(self.backend3.invoke_doc_exists())
        self.assertFalse(self.backend4.invoke_doc_exists())
        self.assertFalse(self.backend5.invoke_doc_exists())
        self.assertFalse(self.backend6.invoke_doc_exists())
        self.assertFalse(self.backend7.invoke_doc_exists())
        self.assertTrue(self.backend8.invoke_doc_exists())
        self.assertFalse(self.backend9.invoke_doc_exists())
        self.assertFalse(self.backend10.invoke_doc_exists())
        self.backend8.delete_invoke_doc()
        self.assertFalse(self.backend8.invoke_doc_exists())

        # Test sending to verified number with a contact-level backend granted to the domain by another domain

        self.backend8.delete()

        self.assertTrue(send_sms_to_verified_number(verified_number, "Test for BACKEND"))
        self.assertFalse(self.backend1.invoke_doc_exists())
        self.assertFalse(self.backend2.invoke_doc_exists())
        self.assertFalse(self.backend3.invoke_doc_exists())
        self.assertFalse(self.backend4.invoke_doc_exists())
        self.assertFalse(self.backend5.invoke_doc_exists())
        self.assertFalse(self.backend6.invoke_doc_exists())
        self.assertFalse(self.backend7.invoke_doc_exists())
        self.assertTrue(self.backend9.invoke_doc_exists())
        self.assertFalse(self.backend10.invoke_doc_exists())
        self.backend9.delete_invoke_doc()
        self.assertFalse(self.backend9.invoke_doc_exists())

        # Test sending to verified number with a contact-level global backend

        self.backend9.delete()

        self.assertTrue(send_sms_to_verified_number(verified_number, "Test for BACKEND"))
        self.assertFalse(self.backend1.invoke_doc_exists())
        self.assertFalse(self.backend2.invoke_doc_exists())
        self.assertFalse(self.backend3.invoke_doc_exists())
        self.assertFalse(self.backend4.invoke_doc_exists())
        self.assertFalse(self.backend5.invoke_doc_exists())
        self.assertFalse(self.backend6.invoke_doc_exists())
        self.assertFalse(self.backend7.invoke_doc_exists())
        self.assertTrue(self.backend10.invoke_doc_exists())
        self.backend10.delete_invoke_doc()
        self.assertFalse(self.backend10.invoke_doc_exists())

        # Test raising exception if contact-level backend is not found

        self.backend10.delete()

        try:
            self.assertTrue(send_sms_to_verified_number(verified_number, "Test for BACKEND"))
        except BadSMSConfigException:
            pass
        else:
            self.assertTrue(False)

        # Test send_sms_with_backend

        self.assertTrue(send_sms_with_backend(self.domain, "+15551234567", "Test for BACKEND3", self.backend3._id))
        self.assertFalse(self.backend1.invoke_doc_exists())
        self.assertFalse(self.backend2.invoke_doc_exists())
        self.assertTrue(self.backend3.invoke_doc_exists())
        self.assertFalse(self.backend4.invoke_doc_exists())
        self.assertFalse(self.backend5.invoke_doc_exists())
        self.assertFalse(self.backend6.invoke_doc_exists())
        self.assertFalse(self.backend7.invoke_doc_exists())
        self.backend3.delete_invoke_doc()
        self.assertFalse(self.backend3.invoke_doc_exists())

        # Test send_sms_with_backend_name

        self.assertTrue(send_sms_with_backend_name(self.domain, "+15551234567", "Test for BACKEND3", "BACKEND3"))
        self.assertFalse(self.backend1.invoke_doc_exists())
        self.assertFalse(self.backend2.invoke_doc_exists())
        self.assertTrue(self.backend3.invoke_doc_exists())
        self.assertFalse(self.backend4.invoke_doc_exists())
        self.assertFalse(self.backend5.invoke_doc_exists())
        self.assertFalse(self.backend6.invoke_doc_exists())
        self.assertFalse(self.backend7.invoke_doc_exists())
        self.backend3.delete_invoke_doc()
        self.assertFalse(self.backend3.invoke_doc_exists())
예제 #53
0
def send_to_recipients(request, domain):
    recipients = request.POST.get('recipients')
    message = request.POST.get('message')
    if not recipients:
        messages.error(request, "You didn't specify any recipients")
    elif not message:
        messages.error(request, "You can't send an empty message")
    else:
        recipients = [x.strip() for x in recipients.split(',') if x.strip()]
        phone_numbers = []
        # formats: GroupName (group), "Username", +15555555555
        group_names = []
        usernames = []
        phone_numbers = []

        unknown_usernames = []
        GROUP = "[group]"
        send_to_all_checked = False

        for recipient in recipients:
            if recipient == "[send to all]":
                send_to_all_checked = True
                phone_users = CouchUser.view("users/phone_users_by_domain",
                                             startkey=[domain],
                                             endkey=[domain, {}],
                                             include_docs=True)
                for user in phone_users:
                    usernames.append(user.username)
                group_names = []
                break
            elif (not send_to_all_checked) and recipient.endswith(GROUP):
                name = recipient[:-len(GROUP)].strip()
                group_names.append(name)
            elif re.match(r'^\+\d+',
                          recipient):  # here we expect it to have a plus sign

                def wrap_user_by_type(u):
                    return getattr(user_models,
                                   u['doc']['doc_type']).wrap(u['doc'])

                phone_users = CouchUser.view(
                    "users/by_default_phone",  # search both with and w/o the plus
                    keys=[recipient, recipient[1:]],
                    include_docs=True,
                    wrapper=wrap_user_by_type).all()

                phone_users = filter(lambda u: u.is_member_of(domain),
                                     phone_users)
                if len(phone_users) > 0:
                    phone_numbers.append((phone_users[0], recipient))
                else:
                    phone_numbers.append((None, recipient))
            elif (not send_to_all_checked) and re.match(r'[\w\.]+', recipient):
                usernames.append(recipient)
            else:
                unknown_usernames.append(recipient)

        login_ids = dict([
            (r['key'], r['id'])
            for r in get_db().view("users/by_username", keys=usernames).all()
        ])
        for username in usernames:
            if username not in login_ids:
                unknown_usernames.append(username)
        login_ids = login_ids.values()

        users = []
        empty_groups = []
        if len(group_names) > 0:
            users.extend(
                CouchUser.view('users/by_group',
                               keys=[[domain, gn] for gn in group_names],
                               include_docs=True).all())
            if len(users) == 0:
                empty_groups = group_names

        users.extend(
            CouchUser.view('_all_docs', keys=login_ids,
                           include_docs=True).all())
        users = [
            user for user in users if user.is_active and not user.is_deleted()
        ]

        phone_numbers.extend([(user, user.phone_number) for user in users])

        failed_numbers = []
        no_numbers = []
        sent = []
        for user, number in phone_numbers:
            if not number:
                no_numbers.append(user.raw_username)
            elif send_sms(domain, user.user_id if user else "", number,
                          message):
                sent.append("%s" % (user.raw_username if user else number))
            else:
                failed_numbers.append(
                    "%s (%s)" %
                    (number, user.raw_username if user else "<no username>"))

        if empty_groups or failed_numbers or unknown_usernames or no_numbers:
            if empty_groups:
                messages.error(
                    request, "The following groups don't exist: %s" %
                    (', '.join(empty_groups)))
            if no_numbers:
                messages.error(
                    request,
                    "The following users don't have phone numbers: %s" %
                    (', '.join(no_numbers)))
            if failed_numbers:
                messages.error(
                    request, "Couldn't send to the following number(s): %s" %
                    (', '.join(failed_numbers)))
            if unknown_usernames:
                messages.error(
                    request, "Couldn't find the following user(s): %s" %
                    (', '.join(unknown_usernames)))
            if sent:
                messages.success(request,
                                 "Successfully sent: %s" % (', '.join(sent)))
            else:
                messages.info(request, "No messages were sent.")
        else:
            messages.success(request, "Message sent")

    return HttpResponseRedirect(
        request.META.get('HTTP_REFERER')
        or reverse(compose_message, args=[domain]))
예제 #54
0
def fire_sms_survey_event(reminder, handler, recipients, verified_numbers,
                          logged_event):
    current_event = reminder.current_event
    if reminder.callback_try_count > 0:
        # Leaving this as an explicit reminder that all survey related actions now happen
        # in a different process. Eventually all of this code will be removed when we move
        # to the new reminders framework.
        pass
    else:
        reminder.xforms_session_ids = []
        domain_obj = Domain.get_by_name(reminder.domain, strict=True)

        # Get the app, module, and form
        try:
            form_unique_id = current_event.form_unique_id
            form = Form.get_form(form_unique_id)
            app = form.get_app()
            module = form.get_module()
        except Exception:
            logged_event.error(MessagingEvent.ERROR_CANNOT_FIND_FORM)
            return

        # Start a touchforms session for each recipient
        for recipient in recipients:
            logged_subevent = logged_event.create_subevent(
                handler, reminder, recipient)

            verified_number, unverified_number = get_recipient_phone_number(
                reminder, recipient, verified_numbers)

            no_verified_number = verified_number is None
            cant_use_unverified_number = (unverified_number is None
                                          or form_requires_input(form))
            if no_verified_number and cant_use_unverified_number:
                logged_subevent.error(
                    MessagingEvent.ERROR_NO_TWO_WAY_PHONE_NUMBER)
                continue

            if verified_number:
                pb = PhoneBlacklist.get_by_phone_number_or_none(
                    verified_number.phone_number)
            else:
                pb = PhoneBlacklist.get_by_phone_number_or_none(
                    unverified_number)

            if pb and not pb.send_sms:
                logged_subevent.error(MessagingEvent.ERROR_PHONE_OPTED_OUT)
                continue

            with critical_section_for_smsforms_sessions(recipient.get_id):
                # Get the case to submit the form against, if any
                if (is_commcarecase(recipient)
                        and not handler.force_surveys_to_use_triggered_case):
                    case_id = recipient.case_id
                else:
                    case_id = reminder.case_id

                if form.requires_case() and not case_id:
                    logged_subevent.error(MessagingEvent.ERROR_NO_CASE_GIVEN)
                    continue

                # Close all currently open sessions
                SQLXFormsSession.close_all_open_sms_sessions(
                    reminder.domain, recipient.get_id)

                # Start the new session
                try:
                    if current_event.callback_timeout_intervals:
                        if handler.submit_partial_forms:
                            expire_after = sum(
                                current_event.callback_timeout_intervals)
                            reminder_intervals = current_event.callback_timeout_intervals[:
                                                                                          -1]
                        else:
                            expire_after = SQLXFormsSession.MAX_SESSION_LENGTH
                            reminder_intervals = current_event.callback_timeout_intervals

                        submit_partially_completed_forms = handler.submit_partial_forms
                        include_case_updates_in_partial_submissions = handler.include_case_side_effects
                    else:
                        expire_after = SQLXFormsSession.MAX_SESSION_LENGTH
                        reminder_intervals = []
                        submit_partially_completed_forms = False
                        include_case_updates_in_partial_submissions = False

                    session, responses = start_session(
                        SQLXFormsSession.create_session_object(
                            reminder.domain,
                            recipient,
                            verified_number.phone_number
                            if verified_number else unverified_number,
                            app,
                            form,
                            expire_after=expire_after,
                            reminder_intervals=reminder_intervals,
                            submit_partially_completed_forms=
                            submit_partially_completed_forms,
                            include_case_updates_in_partial_submissions=
                            include_case_updates_in_partial_submissions),
                        reminder.domain,
                        recipient,
                        app,
                        module,
                        form,
                        case_id,
                        case_for_case_submission=handler.
                        force_surveys_to_use_triggered_case)
                except TouchformsError as e:
                    human_readable_message = get_formplayer_exception(
                        reminder.domain, e)

                    logged_subevent.error(
                        MessagingEvent.ERROR_TOUCHFORMS_ERROR,
                        additional_error_text=human_readable_message)

                    if touchforms_error_is_config_error(reminder.domain, e):
                        # Don't reraise the exception because this means there are configuration
                        # issues with the form that need to be fixed
                        continue
                    else:
                        # Reraise the exception so that the framework retries it again later
                        raise
                except Exception as e:
                    logged_subevent.error(
                        MessagingEvent.ERROR_TOUCHFORMS_ERROR)
                    # Reraise the exception so that the framework retries it again later
                    raise
                session.survey_incentive = handler.survey_incentive
                session.workflow = get_workflow(handler)
                session.reminder_id = reminder._id
                session.save()

            reminder.xforms_session_ids.append(session.session_id)
            logged_subevent.xforms_session = session
            logged_subevent.save()

            # Send out first message
            if len(responses) > 0:
                message = format_message_list(responses)
                metadata = MessageMetadata(
                    workflow=get_workflow(handler),
                    reminder_id=reminder._id,
                    xforms_session_couch_id=session._id,
                )
                if verified_number:
                    send_sms_to_verified_number(
                        verified_number,
                        message,
                        metadata,
                        logged_subevent=logged_subevent)
                else:
                    send_sms(reminder.domain, recipient, unverified_number,
                             message, metadata)

            logged_subevent.completed()
예제 #55
0
 def respond(self, message, **kwargs):
     if self.verified_contact:
         with localize(self.user.get_language_code()):
             send_sms_to_verified_number(self.verified_contact, unicode(message % kwargs))
     else:
         send_sms(self.domain, None, self.msg.phone_number, unicode(message % kwargs))