Exemplo n.º 1
0
def initiate_sms_verification_workflow(contact, phone_number):
    # For now this is only applicable to mobile workers
    assert isinstance(contact, CommCareUser)

    logged_event = MessagingEvent.get_current_verification_event(
        contact.domain, contact.get_id, phone_number)

    with CriticalSection(['verifying-phone-number-%s' % phone_number]):
        vn = PhoneNumber.by_phone(phone_number, include_pending=True)
        if vn:
            if vn.owner_id != contact.get_id:
                return VERIFICATION__ALREADY_IN_USE
            if vn.verified:
                return VERIFICATION__ALREADY_VERIFIED
            else:
                result = VERIFICATION__RESENT_PENDING
        else:
            contact.save_verified_number(contact.domain, phone_number, False)
            result = VERIFICATION__WORKFLOW_STARTED
            # Always create a new event when the workflow starts
            if logged_event:
                logged_event.status = MessagingEvent.STATUS_NOT_COMPLETED
                logged_event.save()
            logged_event = MessagingEvent.create_verification_event(
                contact.domain, contact)

        if not logged_event:
            logged_event = MessagingEvent.create_verification_event(
                contact.domain, contact)

        send_verification(contact.domain, contact, phone_number, logged_event)
        return result
Exemplo n.º 2
0
def initiate_sms_verification_workflow(contact, phone_number):
    # For now this is only applicable to mobile workers
    assert isinstance(contact, CommCareUser)

    logged_event = MessagingEvent.get_current_verification_event(
        contact.domain, contact.get_id, phone_number)

    with CriticalSection(['verifying-phone-number-%s' % phone_number]):
        vn = VerifiedNumber.by_phone(phone_number, include_pending=True)
        if vn:
            if vn.owner_id != contact._id:
                return VERIFICATION__ALREADY_IN_USE
            if vn.verified:
                return VERIFICATION__ALREADY_VERIFIED
            else:
                result = VERIFICATION__RESENT_PENDING
        else:
            contact.save_verified_number(contact.domain, phone_number, False)
            result = VERIFICATION__WORKFLOW_STARTED
            # Always create a new event when the workflow starts
            if logged_event:
                logged_event.status = MessagingEvent.STATUS_NOT_COMPLETED
                logged_event.save()
            logged_event = MessagingEvent.create_verification_event(contact.domain, contact)

        if not logged_event:
            logged_event = MessagingEvent.create_verification_event(contact.domain, contact)

        send_verification(contact.domain, contact, phone_number, logged_event)
        return result
Exemplo n.º 3
0
    def handle_current_event(self):
        content = self.memoized_schedule.get_current_event_content(self)

        if isinstance(self, CaseScheduleInstanceMixin):
            content.set_context(case=self.case, schedule_instance=self)
        else:
            content.set_context(schedule_instance=self)

        logged_event = MessagingEvent.create_from_schedule_instance(
            self, content)

        recipient_count = 0
        for recipient in self.expand_recipients():
            recipient_count += 1
            content.send(recipient, logged_event)

        # As a precaution, always explicitly move to the next event after processing the current
        # event to prevent ever getting stuck on the current event.
        self.memoized_schedule.move_to_next_event(self)
        self.memoized_schedule.move_to_next_event_not_in_the_past(self)

        # Update the MessagingEvent for reporting
        if recipient_count == 0:
            logged_event.error(MessagingEvent.ERROR_NO_RECIPIENT)
        else:
            logged_event.completed()
Exemplo n.º 4
0
def forward_sms(msg, domain, verified_number, text, backend_id):
    logged_event = MessagingEvent.create_event_for_adhoc_sms(
        domain,
        recipient=verified_number.owner,
        content_type=MessagingEvent.CONTENT_SMS,
        source=MessagingEvent.SOURCE_FORWARDED)

    inbound_subevent = logged_event.create_subevent_for_single_sms(
        verified_number.owner_doc_type, verified_number.owner_id)
    inbound_meta = MessageMetadata(workflow=WORKFLOW_FORWARD,
                                   messaging_subevent_id=inbound_subevent.pk)
    add_msg_tags(msg, inbound_meta)

    outbound_subevent = logged_event.create_subevent_for_single_sms(
        verified_number.owner_doc_type, verified_number.owner_id)
    outbound_meta = MessageMetadata(workflow=WORKFLOW_FORWARD,
                                    messaging_subevent_id=outbound_subevent.pk)

    send_sms_with_backend(domain,
                          verified_number.phone_number,
                          text,
                          backend_id,
                          metadata=outbound_meta)

    outbound_subevent.completed()
    inbound_subevent.completed()
    logged_event.completed()
Exemplo n.º 5
0
def fallback_handler(verified_number, text, msg):
    domain_obj = Domain.get_by_name(verified_number.domain, strict=True)

    logged_event = MessagingEvent.create_event_for_adhoc_sms(
        verified_number.domain,
        recipient=verified_number.owner,
        content_type=MessagingEvent.CONTENT_SMS,
        source=MessagingEvent.SOURCE_UNRECOGNIZED)

    inbound_subevent = logged_event.create_subevent_for_single_sms(
        verified_number.owner_doc_type, verified_number.owner_id)
    inbound_meta = MessageMetadata(workflow=WORKFLOW_DEFAULT,
                                   messaging_subevent_id=inbound_subevent.pk)
    add_msg_tags(msg, inbound_meta)

    if domain_obj.use_default_sms_response and domain_obj.default_sms_response:
        outbound_subevent = logged_event.create_subevent_for_single_sms(
            verified_number.owner_doc_type, verified_number.owner_id)
        outbound_meta = MessageMetadata(
            workflow=WORKFLOW_DEFAULT,
            location_id=msg.location_id,
            messaging_subevent_id=outbound_subevent.pk)
        send_sms_to_verified_number(verified_number,
                                    domain_obj.default_sms_response,
                                    metadata=outbound_meta)
        outbound_subevent.completed()

    inbound_subevent.completed()
    logged_event.completed()
    return True
Exemplo n.º 6
0
    def add_event_count_info(self, result, days):
        end_date = self.domain_now.date()
        start_date = end_date - timedelta(days=days - 1)
        counts = MessagingEvent.get_counts_by_date(self.domain, start_date, end_date, self.timezone.zone)

        counts_dict = {
            row.date: {'error': row.error_count, 'total': row.total_count}
            for row in counts
        }

        error_values = []
        success_values = []

        for i in range(days):
            dt = start_date + timedelta(days=i)
            error_count = counts_dict.get(dt, {}).get('error', 0)
            total_count = counts_dict.get(dt, {}).get('total', 0)

            error_values.append({
                'x': dt.strftime('%Y-%m-%d'),
                'y': error_count,
            })
            success_values.append({
                'x': dt.strftime('%Y-%m-%d'),
                'y': total_count - error_count,
            })

        result['event_count_data'] = [
            {'key': _("Error"), 'values': error_values},
            {'key': _("Success"), 'values': success_values},
        ]
Exemplo n.º 7
0
def fallback_handler(v, text, msg):
    domain_obj = Domain.get_by_name(v.domain, strict=True)

    logged_event = MessagingEvent.create_event_for_adhoc_sms(
        v.domain, recipient=v.owner, content_type=MessagingEvent.CONTENT_SMS,
        source=MessagingEvent.SOURCE_UNRECOGNIZED)

    inbound_subevent = logged_event.create_subevent_for_single_sms(
        v.owner_doc_type, v.owner_id)
    inbound_meta = MessageMetadata(workflow=WORKFLOW_DEFAULT,
        messaging_subevent_id=inbound_subevent.pk)
    add_msg_tags(msg, inbound_meta)

    if domain_obj.use_default_sms_response and domain_obj.default_sms_response:
        outbound_subevent = logged_event.create_subevent_for_single_sms(
            v.owner_doc_type, v.owner_id)
        outbound_meta = MessageMetadata(workflow=WORKFLOW_DEFAULT,
            location_id=msg.location_id,
            messaging_subevent_id=outbound_subevent.pk)
        send_sms_to_verified_number(v, domain_obj.default_sms_response,
            metadata=outbound_meta)
        outbound_subevent.completed()

    inbound_subevent.completed()
    logged_event.completed()
    return True
Exemplo n.º 8
0
    def add_event_count_info(self, result, days):
        end_date = self.domain_now.date()
        start_date = end_date - timedelta(days=days - 1)
        counts = MessagingEvent.get_counts_by_date(self.domain, start_date, end_date, self.timezone.zone)

        counts_dict = {
            row.date: {'error': row.error_count, 'total': row.total_count}
            for row in counts
        }

        error_values = []
        success_values = []

        for i in range(days):
            dt = start_date + timedelta(days=i)
            error_count = counts_dict.get(dt, {}).get('error', 0)
            total_count = counts_dict.get(dt, {}).get('total', 0)

            error_values.append({
                'x': dt.strftime('%Y-%m-%d'),
                'y': error_count,
            })
            success_values.append({
                'x': dt.strftime('%Y-%m-%d'),
                'y': total_count - error_count,
            })

        result['event_count_data'] = [
            {'key': _("Error"), 'values': error_values},
            {'key': _("Success"), 'values': success_values},
        ]
Exemplo n.º 9
0
def process_verification(v, msg, verification_keywords=None, create_subevent_for_inbound=True):
    verification_keywords = verification_keywords or ['123']

    logged_event = MessagingEvent.get_current_verification_event(
        v.domain, v.owner_id, v.phone_number)

    if not logged_event:
        logged_event = MessagingEvent.create_verification_event(v.domain, v.owner)

    msg.domain = v.domain
    msg.couch_recipient_doc_type = v.owner_doc_type
    msg.couch_recipient = v.owner_id

    if create_subevent_for_inbound:
        subevent = logged_event.create_subevent_for_single_sms(
            v.owner_doc_type,
            v.owner_id
        )
        subevent.completed()
        msg.messaging_subevent_id = subevent.pk

    msg.save()

    if (
        not domain_has_privilege(msg.domain, privileges.INBOUND_SMS) or
        not verification_response_ok(msg.text, verification_keywords)
    ):
        return False

    v.set_two_way()
    v.set_verified()
    v.save()

    logged_event.completed()
    subevent = logged_event.create_subevent_for_single_sms(
        v.owner_doc_type,
        v.owner_id
    )

    message = messages.get_message(
        messages.MSG_VERIFICATION_SUCCESSFUL,
        verified_number=v
    )
    send_sms_to_verified_number(v, message,
        metadata=MessageMetadata(messaging_subevent_id=subevent.pk))
    subevent.completed()
    return True
Exemplo n.º 10
0
def process_verification(phone_number, msg, backend_id=None):
    v = VerifiedNumber.by_phone(phone_number, True)
    if not v:
        return

    logged_event = MessagingEvent.get_current_verification_event(
        v.domain, v.owner_id, phone_number)

    if not logged_event:
        logged_event = MessagingEvent.create_verification_event(
            v.domain, v.owner)

    subevent = logged_event.create_subevent_for_single_sms(
        v.owner_doc_type, v.owner_id)
    subevent.completed()

    msg.domain = v.domain
    msg.couch_recipient_doc_type = v.owner_doc_type
    msg.couch_recipient = v.owner_id
    msg.messaging_subevent_id = subevent.pk
    msg.save()

    if (not domain_has_privilege(msg.domain, privileges.INBOUND_SMS)
            or not verification_response_ok(msg.text)):
        return

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

    assert v.owner_doc_type == 'CommCareUser'
    owner = CommCareUser.get(v.owner_id)

    v = owner.save_verified_number(v.domain, phone_number, True, backend.name)

    logged_event.completed()

    subevent = logged_event.create_subevent_for_single_sms(
        v.owner_doc_type, v.owner_id)

    with localize(owner.language):
        send_sms_to_verified_number(
            v,
            _(CONFIRM),
            metadata=MessageMetadata(messaging_subevent_id=subevent.pk))
        subevent.completed()
Exemplo n.º 11
0
def process_verification(v, msg, verification_keywords=None, create_subevent_for_inbound=True):
    verification_keywords = verification_keywords or ['123']

    logged_event = MessagingEvent.get_current_verification_event(
        v.domain, v.owner_id, v.phone_number)

    if not logged_event:
        logged_event = MessagingEvent.create_verification_event(v.domain, v.owner)

    msg.domain = v.domain
    msg.couch_recipient_doc_type = v.owner_doc_type
    msg.couch_recipient = v.owner_id

    if create_subevent_for_inbound:
        subevent = logged_event.create_subevent_for_single_sms(
            v.owner_doc_type,
            v.owner_id
        )
        subevent.completed()
        msg.messaging_subevent_id = subevent.pk

    msg.save()

    if (
        not domain_has_privilege(msg.domain, privileges.INBOUND_SMS) or
        not verification_response_ok(msg.text, verification_keywords)
    ):
        return False

    v.set_two_way()
    v.set_verified()
    v.save()

    logged_event.completed()
    subevent = logged_event.create_subevent_for_single_sms(
        v.owner_doc_type,
        v.owner_id
    )

    message = messages.get_message(
        messages.MSG_VERIFICATION_SUCCESSFUL,
        verified_number=v
    )
    send_sms_to_verified_number(v, message,
        metadata=MessageMetadata(messaging_subevent_id=subevent.pk))
    subevent.completed()
    return True
Exemplo n.º 12
0
def process_verification(v, msg):
    if not v or v.verified:
        return

    logged_event = MessagingEvent.get_current_verification_event(
        v.domain, v.owner_id, v.phone_number)

    if not logged_event:
        logged_event = MessagingEvent.create_verification_event(v.domain, v.owner)

    subevent = logged_event.create_subevent_for_single_sms(
        v.owner_doc_type,
        v.owner_id
    )
    subevent.completed()

    msg.domain = v.domain
    msg.couch_recipient_doc_type = v.owner_doc_type
    msg.couch_recipient = v.owner_id
    msg.messaging_subevent_id = subevent.pk
    msg.save()

    if (
        not domain_has_privilege(msg.domain, privileges.INBOUND_SMS) or
        not verification_response_ok(msg.text)
    ):
        return

    v.verified = True
    v.save()

    logged_event.completed()
    subevent = logged_event.create_subevent_for_single_sms(
        v.owner_doc_type,
        v.owner_id
    )

    message = messages.get_message(
        messages.MSG_VERIFICATION_SUCCESSFUL,
        verified_number=v
    )
    send_sms_to_verified_number(v, message,
        metadata=MessageMetadata(messaging_subevent_id=subevent.pk))
    subevent.completed()
Exemplo n.º 13
0
    def send_current_event_content_to_recipients(self):
        content = self.memoized_schedule.get_current_event_content(self)

        if isinstance(content, (IVRSurveyContent, SMSCallbackContent)):
            raise TypeError(
                "IVR and Callback use cases are no longer supported. "
                "How did this schedule instance end up as active?")

        if isinstance(self, CaseScheduleInstanceMixin):
            content.set_context(case=self.case, schedule_instance=self)
        else:
            content.set_context(schedule_instance=self)

        logged_event = MessagingEvent.create_from_schedule_instance(
            self, content)

        recipient_count = 0
        for recipient in self.expand_recipients():
            recipient_count += 1

            #   The framework will retry sending a non-processed schedule instance
            # once every hour.

            #   If we are processing a long list of recipients here and an error
            # occurs half-way through, we don't want to reprocess the entire list
            # of recipients again when the framework retries it an hour later.

            #   So we use a non-blocking lock tied to the event due time and recipient
            # to make sure that we don't try resending the same content to the same
            # recipient more than once in the event of a retry.

            #   If we succeed in sending the content, we don't release the lock so
            # that it won't retry later. If we fail in sending the content, we release
            # the lock so that it will retry later.

            lock = self.get_content_send_lock(recipient)
            if lock.acquire(blocking=False):
                try:
                    content.send(recipient, logged_event)
                except:
                    error = sys.exc_info()[1]
                    # Release the lock if an error happened so that we can try sending
                    # to this recipient again later.
                    lock.release()
                    logged_event.error(
                        MessagingEvent.ERROR_INTERNAL_SERVER_ERROR,
                        additional_error_text=str(error),
                    )
                    raise

        # Update the MessagingEvent for reporting
        if recipient_count == 0:
            logged_event.error(MessagingEvent.ERROR_NO_RECIPIENT)
        else:
            logged_event.completed()
Exemplo n.º 14
0
 def randomMessagingSubEventId(self):
     # Just create a dummy event and subevent to generate
     # a valid MessagingSubEvent foreign key
     event = MessagingEvent(
         domain=self.domain,
         date=self.randomDateTime(),
         source=MessagingEvent.SOURCE_KEYWORD,
         content_type=MessagingEvent.CONTENT_SMS,
         status=MessagingEvent.STATUS_COMPLETED,
     )
     event.save()
     subevent = MessagingSubEvent(
         date=self.randomDateTime(),
         parent=event,
         recipient_type=MessagingEvent.RECIPIENT_CASE,
         content_type=MessagingEvent.CONTENT_SMS,
         status=MessagingEvent.STATUS_COMPLETED,
     )
     subevent.save()
     return subevent.pk
Exemplo n.º 15
0
 def randomMessagingSubEventId(self):
     # Just create a dummy event and subevent to generate
     # a valid MessagingSubEvent foreign key
     event = MessagingEvent(
         domain=self.domain,
         date=self.randomDateTime(),
         source=MessagingEvent.SOURCE_KEYWORD,
         content_type=MessagingEvent.CONTENT_SMS,
         status=MessagingEvent.STATUS_COMPLETED,
     )
     event.save()
     subevent = MessagingSubEvent(
         date=self.randomDateTime(),
         parent=event,
         recipient_type=MessagingEvent.RECIPIENT_CASE,
         content_type=MessagingEvent.CONTENT_SMS,
         status=MessagingEvent.STATUS_COMPLETED,
     )
     subevent.save()
     return subevent.pk
Exemplo n.º 16
0
def process_verification(v, msg):
    if not v or v.verified:
        return

    logged_event = MessagingEvent.get_current_verification_event(
        v.domain, v.owner_id, v.phone_number)

    if not logged_event:
        logged_event = MessagingEvent.create_verification_event(v.domain, v.owner)

    subevent = logged_event.create_subevent_for_single_sms(
        v.owner_doc_type,
        v.owner_id
    )
    subevent.completed()

    msg.domain = v.domain
    msg.couch_recipient_doc_type = v.owner_doc_type
    msg.couch_recipient = v.owner_id
    msg.messaging_subevent_id = subevent.pk
    msg.save()

    if (
        not domain_has_privilege(msg.domain, privileges.INBOUND_SMS) or
        not verification_response_ok(msg.text)
    ):
        return

    v.verified = True
    v.save()

    logged_event.completed()
    subevent = logged_event.create_subevent_for_single_sms(
        v.owner_doc_type,
        v.owner_id
    )

    with localize(v.owner.get_language_code()):
        send_sms_to_verified_number(v, _(CONFIRM),
            metadata=MessageMetadata(messaging_subevent_id=subevent.pk))
        subevent.completed()
Exemplo n.º 17
0
def initiate_sms_verification_workflow(contact, phone_number):
    # For now this is only applicable to mobile workers
    assert isinstance(contact, CommCareUser)

    phone_number = apply_leniency(phone_number)

    logged_event = MessagingEvent.get_current_verification_event(
        contact.domain, contact.get_id, phone_number)

    p = PhoneNumber.get_reserved_number(phone_number)
    if p:
        if p.owner_id != contact.get_id:
            return VERIFICATION__ALREADY_IN_USE
        if p.verified:
            return VERIFICATION__ALREADY_VERIFIED
        else:
            result = VERIFICATION__RESENT_PENDING
    else:
        entry = contact.get_or_create_phone_entry(phone_number)
        try:
            entry.set_pending_verification()
        except PhoneNumberInUseException:
            # On the off chance that the phone number was reserved between
            # the check above and now
            return VERIFICATION__ALREADY_IN_USE

        result = VERIFICATION__WORKFLOW_STARTED
        # Always create a new event when the workflow starts
        if logged_event:
            logged_event.status = MessagingEvent.STATUS_NOT_COMPLETED
            logged_event.save()
        logged_event = MessagingEvent.create_verification_event(
            contact.domain, contact)

    if not logged_event:
        logged_event = MessagingEvent.create_verification_event(
            contact.domain, contact)

    send_verification(contact.domain, contact, phone_number, logged_event)
    return result
Exemplo n.º 18
0
def process_verification(v, msg):
    if not v or v.verified:
        return

    logged_event = MessagingEvent.get_current_verification_event(
        v.domain, v.owner_id, v.phone_number)

    if not logged_event:
        logged_event = MessagingEvent.create_verification_event(
            v.domain, v.owner)

    subevent = logged_event.create_subevent_for_single_sms(
        v.owner_doc_type, v.owner_id)
    subevent.completed()

    msg.domain = v.domain
    msg.couch_recipient_doc_type = v.owner_doc_type
    msg.couch_recipient = v.owner_id
    msg.messaging_subevent_id = subevent.pk
    msg.save()

    if (not domain_has_privilege(msg.domain, privileges.INBOUND_SMS)
            or not verification_response_ok(msg.text)):
        return

    v.verified = True
    v.save()

    logged_event.completed()
    subevent = logged_event.create_subevent_for_single_sms(
        v.owner_doc_type, v.owner_id)

    message = messages.get_message(messages.MSG_VERIFICATION_SUCCESSFUL,
                                   verified_number=v)
    send_sms_to_verified_number(
        v,
        message,
        metadata=MessageMetadata(messaging_subevent_id=subevent.pk))
    subevent.completed()
Exemplo n.º 19
0
    def send_current_event_content_to_recipients(self):
        client = get_redis_client()
        content = self.memoized_schedule.get_current_event_content(self)

        if isinstance(content, (IVRSurveyContent, SMSCallbackContent)):
            raise TypeError(
                "IVR and Callback use cases are no longer supported. "
                "How did this schedule instance end up as active?"
            )

        if isinstance(self, CaseScheduleInstanceMixin):
            content.set_context(case=self.case, schedule_instance=self)
        else:
            content.set_context(schedule_instance=self)

        logged_event = MessagingEvent.create_from_schedule_instance(self, content)

        recipient_count = 0
        for recipient in self.expand_recipients():
            recipient_count += 1

            #   The framework will retry sending a non-processed schedule instance
            # once every hour.

            #   If we are processing a long list of recipients here and an error
            # occurs half-way through, we don't want to reprocess the entire list
            # of recipients again when the framework retries it an hour later.

            #   So we use a non-blocking lock tied to the event due time and recipient
            # to make sure that we don't try resending the same content to the same
            # recipient more than once in the event of a retry.

            #   If we succeed in sending the content, we don't release the lock so
            # that it won't retry later. If we fail in sending the content, we release
            # the lock so that it will retry later.

            lock = self.get_content_send_lock(client, recipient)
            if lock.acquire(blocking=False):
                try:
                    content.send(recipient, logged_event)
                except:
                    # Release the lock if an error happened so that we can try sending
                    # to this recipient again later.
                    lock.release()
                    raise

        # Update the MessagingEvent for reporting
        if recipient_count == 0:
            logged_event.error(MessagingEvent.ERROR_NO_RECIPIENT)
        else:
            logged_event.completed()
Exemplo n.º 20
0
def initiate_sms_verification_workflow(contact, phone_number):
    # For now this is only applicable to mobile workers
    assert isinstance(contact, CommCareUser)

    phone_number = apply_leniency(phone_number)

    logged_event = MessagingEvent.get_current_verification_event(
        contact.domain, contact.get_id, phone_number)

    p = PhoneNumber.get_reserved_number(phone_number)
    if p:
        if p.owner_id != contact.get_id:
            return VERIFICATION__ALREADY_IN_USE
        if p.verified:
            return VERIFICATION__ALREADY_VERIFIED
        else:
            result = VERIFICATION__RESENT_PENDING
    else:
        entry = contact.get_or_create_phone_entry(phone_number)
        try:
            entry.set_pending_verification()
        except PhoneNumberInUseException:
            # On the off chance that the phone number was reserved between
            # the check above and now
            return VERIFICATION__ALREADY_IN_USE

        result = VERIFICATION__WORKFLOW_STARTED
        # Always create a new event when the workflow starts
        if logged_event:
            logged_event.status = MessagingEvent.STATUS_NOT_COMPLETED
            logged_event.save()
        logged_event = MessagingEvent.create_verification_event(contact.domain, contact)

    if not logged_event:
        logged_event = MessagingEvent.create_verification_event(contact.domain, contact)

    send_verification(contact.domain, contact, phone_number, logged_event)
    return result
Exemplo n.º 21
0
def process_survey_keyword_actions(verified_number, survey_keyword, text, msg):
    sender = verified_number.owner
    case = None
    args = split_args(text, survey_keyword)

    logged_event = MessagingEvent.create_from_keyword(survey_keyword, sender)

    # Close any open sessions even if it's just an sms that we're
    # responding with.
    SQLXFormsSession.close_all_open_sms_sessions(verified_number.domain,
        verified_number.owner_id)

    if sender.doc_type == "CommCareCase":
        case = sender
        args = args[1:]
Exemplo n.º 22
0
 def _send_fallback_message(self, msg):
     logged_event = MessagingEvent.create_event_for_adhoc_sms(
         self.domain, recipient=msg.recipient)
     logged_subevent = logged_event.create_subevent_for_single_sms(
         recipient_doc_type=msg.recipient.doc_type,
         recipient_id=msg.recipient.get_id)
     metadata = MessageMetadata(
         messaging_subevent_id=logged_subevent.pk,
         custom_metadata={"fallback": "WhatsApp Contact Not Found"},
     )
     if send_sms_with_backend(self.domain, msg.phone_number, msg.text,
                              self.config.fallback_backend_id, metadata):
         logged_subevent.completed()
         logged_event.completed()
     else:
         logged_subevent.error(MessagingEvent.ERROR_INTERNAL_SERVER_ERROR)
Exemplo n.º 23
0
    def send_current_event_content_to_recipients(self):
        client = get_redis_client()
        content = self.memoized_schedule.get_current_event_content(self)

        if isinstance(self, CaseScheduleInstanceMixin):
            content.set_context(case=self.case, schedule_instance=self)
        else:
            content.set_context(schedule_instance=self)

        logged_event = MessagingEvent.create_from_schedule_instance(
            self, content)

        recipient_count = 0
        for recipient in self.expand_recipients():
            recipient_count += 1

            #   The framework will retry sending a non-processed schedule instance
            # once every hour.

            #   If we are processing a long list of recipients here and an error
            # occurs half-way through, we don't want to reprocess the entire list
            # of recipients again when the framework retries it an hour later.

            #   So we use a non-blocking lock tied to the event due time and recipient
            # to make sure that we don't try resending the same content to the same
            # recipient more than once in the event of a retry.

            #   If we succeed in sending the content, we don't release the lock so
            # that it won't retry later. If we fail in sending the content, we release
            # the lock so that it will retry later.

            lock = self.get_content_send_lock(client, recipient)
            if lock.acquire(blocking=False):
                try:
                    content.send(recipient, logged_event)
                except:
                    # Release the lock if an error happened so that we can try sending
                    # to this recipient again later.
                    lock.release()
                    raise

        # Update the MessagingEvent for reporting
        if recipient_count == 0:
            logged_event.error(MessagingEvent.ERROR_NO_RECIPIENT)
        else:
            logged_event.completed()
Exemplo n.º 24
0
    def add_error_count_info(self, result, days):
        end_date = self.domain_now.date()
        start_date = end_date - timedelta(days=days - 1)
        counts = MessagingEvent.get_counts_of_errors(self.domain, start_date, end_date, self.timezone.zone)

        # Consolidate opt-out errors so they show up as one bar in the graph
        if SMS.ERROR_PHONE_NUMBER_OPTED_OUT in counts and MessagingEvent.ERROR_PHONE_OPTED_OUT in counts:
            counts[MessagingEvent.ERROR_PHONE_OPTED_OUT] += counts[SMS.ERROR_PHONE_NUMBER_OPTED_OUT]
            del counts[SMS.ERROR_PHONE_NUMBER_OPTED_OUT]

        sorted_counts = sorted(six.iteritems(counts), key=lambda item: item[1], reverse=True)
        result['error_count_data'] = [
            {
                'values': [
                    {'label': self.get_error_message(error), 'value': count}
                    for error, count in sorted_counts
                ],
            },
        ]
Exemplo n.º 25
0
    def add_error_count_info(self, result, days):
        end_date = self.domain_now.date()
        start_date = end_date - timedelta(days=days - 1)
        counts = MessagingEvent.get_counts_of_errors(self.domain, start_date, end_date, self.timezone.zone)

        # Consolidate opt-out errors so they show up as one bar in the graph
        if SMS.ERROR_PHONE_NUMBER_OPTED_OUT in counts and MessagingEvent.ERROR_PHONE_OPTED_OUT in counts:
            counts[MessagingEvent.ERROR_PHONE_OPTED_OUT] += counts[SMS.ERROR_PHONE_NUMBER_OPTED_OUT]
            del counts[SMS.ERROR_PHONE_NUMBER_OPTED_OUT]

        sorted_counts = sorted(six.iteritems(counts), key=lambda item: item[1], reverse=True)
        result['error_count_data'] = [
            {
                'values': [
                    {'label': self.get_error_message(error), 'value': count}
                    for error, count in sorted_counts
                ],
            },
        ]
Exemplo n.º 26
0
def process_survey_keyword_actions(verified_number, survey_keyword, text, msg):
    sender = verified_number.owner
    case = None
    args = split_args(text, survey_keyword)

    logged_event = MessagingEvent.create_from_keyword(survey_keyword, sender)

    # Log a messaging subevent for the incoming message
    subevent = logged_event.create_subevent_for_single_sms(
        msg.couch_recipient_doc_type, msg.couch_recipient, completed=True)
    add_msg_tags(msg, MessageMetadata(messaging_subevent_id=subevent.pk))

    # Close any open sessions even if it's just an sms that we're
    # responding with.
    SQLXFormsSession.close_all_open_sms_sessions(verified_number.domain,
                                                 verified_number.owner_id)

    if is_commcarecase(sender):
        case = sender
        args = args[1:]
Exemplo n.º 27
0
def forward_sms(msg, domain, verified_number, text, backend_id):
    logged_event = MessagingEvent.create_event_for_adhoc_sms(
        domain, recipient=verified_number.owner,
        content_type=MessagingEvent.CONTENT_SMS,
        source=MessagingEvent.SOURCE_FORWARDED)

    inbound_subevent = logged_event.create_subevent_for_single_sms(
        verified_number.owner_doc_type, verified_number.owner_id)
    inbound_meta = MessageMetadata(workflow=WORKFLOW_FORWARD,
        messaging_subevent_id=inbound_subevent.pk)
    add_msg_tags(msg, inbound_meta)

    outbound_subevent = logged_event.create_subevent_for_single_sms(
        verified_number.owner_doc_type, verified_number.owner_id)
    outbound_meta = MessageMetadata(workflow=WORKFLOW_FORWARD,
        messaging_subevent_id=outbound_subevent.pk)

    send_sms_with_backend(domain, verified_number.phone_number, text,
        backend_id, metadata=outbound_meta)

    outbound_subevent.completed()
    inbound_subevent.completed()
    logged_event.completed()
Exemplo n.º 28
0
def process_survey_keyword_actions(verified_number, survey_keyword, text, msg):
    sender = verified_number.owner
    case = None
    args = split_args(text, survey_keyword)

    logged_event = MessagingEvent.create_from_keyword(survey_keyword, sender)

    # Log a messaging subevent for the incoming message
    subevent = logged_event.create_subevent_for_single_sms(
        msg.couch_recipient_doc_type,
        msg.couch_recipient
    )
    subevent.completed()
    add_msg_tags(msg, MessageMetadata(messaging_subevent_id=subevent.pk))

    # Close any open sessions even if it's just an sms that we're
    # responding with.
    SQLXFormsSession.close_all_open_sms_sessions(verified_number.domain,
        verified_number.owner_id)

    if sender.doc_type == "CommCareCase":
        case = sender
        args = args[1:]
    elif sender.doc_type == "CommCareUser":
        if keyword_uses_form_that_requires_case(survey_keyword):
            if len(args) > 1:
                external_id = args[1]
                case, matches = get_case_by_external_id(verified_number.domain,
                    external_id, sender)
                if matches == 0:
                    send_keyword_response(verified_number, MSG_CASE_NOT_FOUND, logged_event)
                    logged_event.error(MessagingEvent.ERROR_CASE_EXTERNAL_ID_NOT_FOUND)
                    return
                elif matches > 1:
                    send_keyword_response(verified_number, MSG_MULTIPLE_CASES_FOUND, logged_event)
                    logged_event.error(MessagingEvent.ERROR_MULTIPLE_CASES_WITH_EXTERNAL_ID_FOUND)
                    return
            else:
                send_keyword_response(verified_number, MSG_MISSING_EXTERNAL_ID, logged_event)
                logged_event.error(MessagingEvent.ERROR_NO_EXTERNAL_ID_GIVEN)
                return
            args = args[2:]
        else:
            args = args[1:]
    def cmp_fcn(a1, a2):
        a1_ss = (a1.action == METHOD_STRUCTURED_SMS)
        a2_ss = (a2.action == METHOD_STRUCTURED_SMS)
        if a1_ss and a2_ss:
            return 0
        elif a1_ss:
            return -1
        elif a2_ss:
            return 1
        else:
            return 0

    if case:
        subevent.case_id = case.get_id
        subevent.save()

    # Process structured sms actions first
    actions = sorted(survey_keyword.actions, cmp=cmp_fcn)
    for survey_keyword_action in actions:
        if survey_keyword_action.recipient == RECIPIENT_SENDER:
            contact = sender
        elif survey_keyword_action.recipient == RECIPIENT_OWNER:
            if sender.doc_type == "CommCareCase":
                contact = get_wrapped_owner(get_owner_id(sender))
            else:
                contact = None
        elif survey_keyword_action.recipient == RECIPIENT_USER_GROUP:
            try:
                contact = Group.get(survey_keyword_action.recipient_id)
                assert contact.doc_type == "Group"
                assert contact.domain == verified_number.domain
            except Exception:
                contact = None
        else:
            contact = None

        if contact is None:
            continue

        if survey_keyword_action.action == METHOD_SMS:
            create_immediate_reminder(contact, METHOD_SMS, 
                reminder_type=REMINDER_TYPE_KEYWORD_INITIATED,
                message=survey_keyword_action.message_content,
                case=case, logged_event=logged_event)
        elif survey_keyword_action.action == METHOD_SMS_SURVEY:
            create_immediate_reminder(contact, METHOD_SMS_SURVEY,
                reminder_type=REMINDER_TYPE_KEYWORD_INITIATED,
                form_unique_id=survey_keyword_action.form_unique_id,
                case=case, logged_event=logged_event)
        elif survey_keyword_action.action == METHOD_STRUCTURED_SMS:
            res = handle_structured_sms(survey_keyword, survey_keyword_action,
                sender, verified_number, text, send_response=True, msg=msg,
                case=case, text_args=args, logged_event=logged_event)
            if not res:
                # If the structured sms processing wasn't successful, don't
                # process any of the other actions
                return
    logged_event.completed()
Exemplo n.º 29
0
def process_survey_keyword_actions(verified_number, survey_keyword, text, msg):
    sender = verified_number.owner
    case = None
    args = split_args(text, survey_keyword)

    logged_event = MessagingEvent.create_from_keyword(survey_keyword, sender)

    # Log a messaging subevent for the incoming message
    subevent = logged_event.create_subevent_for_single_sms(
        msg.couch_recipient_doc_type,
        msg.couch_recipient,
        completed=True
    )
    add_msg_tags(msg, MessageMetadata(messaging_subevent_id=subevent.pk))

    # Close any open sessions even if it's just an sms that we're
    # responding with.
    SQLXFormsSession.close_all_open_sms_sessions(verified_number.domain,
        verified_number.owner_id)

    if is_commcarecase(sender):
        case = sender
        args = args[1:]
    elif isinstance(sender, CommCareUser):
        if keyword_uses_form_that_requires_case(survey_keyword):
            if len(args) > 1:
                external_id = args[1]
                case, matches = get_case_by_external_id(verified_number.domain,
                    external_id, sender)
                if matches == 0:
                    send_keyword_response(verified_number, MSG_CASE_NOT_FOUND, logged_event)
                    logged_event.error(MessagingEvent.ERROR_CASE_EXTERNAL_ID_NOT_FOUND)
                    return
                elif matches > 1:
                    send_keyword_response(verified_number, MSG_MULTIPLE_CASES_FOUND, logged_event)
                    logged_event.error(MessagingEvent.ERROR_MULTIPLE_CASES_WITH_EXTERNAL_ID_FOUND)
                    return
            else:
                send_keyword_response(verified_number, MSG_MISSING_EXTERNAL_ID, logged_event)
                logged_event.error(MessagingEvent.ERROR_NO_EXTERNAL_ID_GIVEN)
                return
            args = args[2:]
        else:
            args = args[1:]

    def cmp_fcn(a1, a2):
        a1_ss = (a1.action == KeywordAction.ACTION_STRUCTURED_SMS)
        a2_ss = (a2.action == KeywordAction.ACTION_STRUCTURED_SMS)
        if a1_ss and a2_ss:
            return 0
        elif a1_ss:
            return -1
        elif a2_ss:
            return 1
        else:
            return 0

    if case:
        subevent.case_id = case.case_id
        subevent.save()

    # Process structured sms actions first
    actions = sorted(survey_keyword.keywordaction_set.all(), cmp=cmp_fcn)
    for survey_keyword_action in actions:
        if survey_keyword_action.recipient == KeywordAction.RECIPIENT_SENDER:
            contact = sender
        elif survey_keyword_action.recipient == KeywordAction.RECIPIENT_OWNER:
            if is_commcarecase(sender):
                contact = get_wrapped_owner(get_owner_id(sender))
            else:
                contact = None
        elif survey_keyword_action.recipient == KeywordAction.RECIPIENT_USER_GROUP:
            try:
                contact = Group.get(survey_keyword_action.recipient_id)
                assert contact.doc_type == "Group"
                assert contact.domain == verified_number.domain
            except Exception:
                contact = None
        else:
            contact = None

        if contact is None:
            continue

        # contact can be either a user, case, group, or location
        if survey_keyword_action.action in (KeywordAction.ACTION_SMS, KeywordAction.ACTION_SMS_SURVEY):
            if isinstance(contact, Group):
                recipients = list(ScheduleInstance.expand_group(contact))
            elif isinstance(contact, SQLLocation):
                recipients = list(ScheduleInstance.expand_location_ids(contact.domain, [contact.location_id]))
            else:
                recipients = [contact]

            recipient_is_sender = survey_keyword_action.recipient == KeywordAction.RECIPIENT_SENDER

            if survey_keyword_action.action == KeywordAction.ACTION_SMS:
                content = SMSContent(message={'*': survey_keyword_action.message_content})
                content.set_context(case=case)
            elif survey_keyword_action.action == KeywordAction.ACTION_SMS_SURVEY:
                content = SMSSurveyContent(
                    form_unique_id=survey_keyword_action.form_unique_id,
                    expire_after=SQLXFormsSession.MAX_SESSION_LENGTH,
                )
                content.set_context(
                    case=case,
                    critical_section_already_acquired=recipient_is_sender,
                )
            else:
                raise ValueError("Unexpected action %s" % survey_keyword_action.action)

            for recipient in recipients:
                phone_entry = verified_number if recipient_is_sender else None
                content.send(recipient, logged_event, phone_entry=phone_entry)

        elif survey_keyword_action.action == KeywordAction.ACTION_STRUCTURED_SMS:
            res = handle_structured_sms(survey_keyword, survey_keyword_action,
                sender, verified_number, text, send_response=True, msg=msg,
                case=case, text_args=args, logged_event=logged_event)
            if not res:
                # If the structured sms processing wasn't successful, don't
                # process any of the other actions
                return
    logged_event.completed()
Exemplo n.º 30
0
def handle(verified_contact, text, msg):
    domain = msg.domain
    if not domain:
        return False

    if not EWSGhanaConfig.for_domain(domain):
        return False

    if not verified_contact:
        return False

    user = verified_contact.owner

    if not verified_contact.verified:
        if not process_verification(verified_contact, msg, VERIFICATION_KEYWORDS):
            logged_event = MessagingEvent.get_current_verification_event(
                domain, verified_contact.owner_id, verified_contact.phone_number)
            send_verification(domain, user, verified_contact.phone_number, logged_event)
        return True

    args = text.split()
    if not args:
        send_sms_to_verified_number(verified_contact, unicode(INVALID_MESSAGE))
        return True
    keyword = args[0]
    args = args[1:]
    params = {
        'user': user,
        'domain': domain,
        'args': args,
        'msg': msg,
        'verified_contact': verified_contact
    }

    def not_function(word):
        if args and re.match("del", word):
            return NotDeliveredHandler
        elif args and re.match("sub", word):
            return NotSubmittedHandler
        return None

    handlers = {
        ('help', ): HelpHandler,
        ('reminder', ): ReminderOnOffHandler,
        ('language', 'lang', 'lugha'): LanguageHandler,
        ('yes', 'no', 'y', 'n'): RequisitionHandler,
        ('undo', 'replace', 'revoke'): UndoHandler,
        ('soh',): SOHHandler,
        ('not',): not_function(args[0]) if args else None,
        ('rec', 'receipts', 'received'): ReceiptsHandler
    }

    def choose_handler(keyword):
        for k, v in handlers.iteritems():
            if keyword.lower() in k:
                return v
        return None

    handler_class = choose_handler(keyword)
    handler = handler_class(**params) if handler_class else None

    if handler:
        if args:
            return handler.handle()
        else:
            handler.help()
            return True
    else:
        return SOHHandler(**params).handle()