Exemple #1
0
    def clean_name(self):
        value = self.cleaned_data.get("name")
        if value is not None:
            value = value.strip().upper()
        if value is None or value == "":
            raise ValidationError(_("This field is required."))
        if re.compile("\s").search(value) is not None:
            raise ValidationError(_("Name may not contain any spaces."))

        if self.is_global_backend:
            # We're using the form to create a global backend, so
            # ensure name is not duplicated among other global backends
            is_unique = SQLMobileBackend.name_is_unique(
                value,
                backend_id=self._cchq_backend_id
            )
        else:
            # We're using the form to create a domain-level backend, so
            # ensure name is not duplicated among other backends owned by this domain
            is_unique = SQLMobileBackend.name_is_unique(
                value,
                domain=self._cchq_domain,
                backend_id=self._cchq_backend_id
            )

        if not is_unique:
            raise ValidationError(_("Name is already in use."))

        return value
 def tearDown(self):
     SmsBillable.objects.all().delete()
     SmsUsageFee.objects.all().delete()
     SmsUsageFeeCriteria.objects.all().delete()
     SmsGatewayFee.objects.all().delete()
     self.currency_usd.delete()
     for api_id, backend_id in self.backend_ids.iteritems():
         SQLMobileBackend.load(backend_id, is_couch_id=True).delete()
    def tearDown(self):
        SmsBillable.objects.all().delete()
        SmsUsageFee.objects.all().delete()
        SmsUsageFeeCriteria.objects.all().delete()
        SmsGatewayFee.objects.all().delete()
        self.currency_usd.delete()
        for api_id, backend_id in six.iteritems(self.backend_ids):
            SQLMobileBackend.load(backend_id, is_couch_id=True).delete()

        FakeTwilioMessageFactory.backend_message_id_to_price = {}

        super(TestUsageFee, self).tearDown()
    def tearDown(self):
        SmsBillable.objects.all().delete()
        SmsGatewayFee.objects.all().delete()
        SmsGatewayFeeCriteria.objects.all().delete()
        SmsUsageFee.objects.all().delete()
        SmsUsageFeeCriteria.objects.all().delete()

        self.currency_usd.delete()
        self.other_currency.delete()
        SMS.by_domain(generator.TEST_DOMAIN).delete()

        for api_id, backend_id in self.backend_ids.iteritems():
            SQLMobileBackend.load(backend_id, is_couch_id=True).delete()
Exemple #5
0
 def backend(self):
     from corehq.apps.sms.models import SQLMobileBackend
     from corehq.apps.sms.util import clean_phone_number
     if isinstance(self.backend_id, basestring) and self.backend_id.strip() != '':
         return SQLMobileBackend.load_by_name(
             SQLMobileBackend.SMS,
             self.domain,
             self.backend_id
         )
     else:
         return SQLMobileBackend.load_default_by_phone_and_domain(
             SQLMobileBackend.SMS,
             clean_phone_number(self.phone_number),
             domain=self.domain
         )
Exemple #6
0
    def general_fields(self):
        fields = [
            crispy.Field('name', css_class='input-xxlarge'),
            crispy.Field('description', css_class='input-xxlarge', rows="3"),
            crispy.Field('reply_to_phone_number', css_class='input-xxlarge'),
        ]

        if not self.is_global_backend:
            fields.extend([
                crispy.Field(
                    twbscrispy.PrependedText(
                        'give_other_domains_access', '', data_bind="checked: share_backend"
                    )
                ),
                crispy.Div(
                    'authorized_domains',
                    data_bind="visible: showAuthorizedDomains",
                ),
            ])

        if self._cchq_backend_id:
            backend = SQLMobileBackend.load(self._cchq_backend_id)
            if backend.show_inbound_api_key_during_edit:
                self.fields['inbound_api_key'].initial = backend.inbound_api_key
                fields.append(crispy.Field('inbound_api_key'))

        return fields
Exemple #7
0
    def __init__(self, domain, *args, **kwargs):
        super(SMSRateCalculatorForm, self).__init__(*args, **kwargs)
        backends = SQLMobileBackend.get_domain_backends(SQLMobileBackend.SMS, domain)

        def _get_backend_info(backend):
            return backend.couch_id, "%s (%s)" % (backend.name, backend.hq_api_id)

        backends = [_get_backend_info(g) for g in backends]
        self.fields['gateway'].choices = backends

        self.helper = FormHelper()
        self.helper.form_class = "form-horizontal"
        self.helper.layout = crispy.Layout(
            crispy.Field(
                'gateway',
                data_bind="value: gateway, events: {change: clearSelect2}",
                css_class="input-xxlarge",
            ),
            crispy.Field(
                'direction', data_bind="value: direction, "
                                       "event: {change: clearSelect2}",
            ),
            crispy.Field(
                'country_code',
                css_class="input-xxlarge",
                data_bind="value: select2CountryCode.value, "
                          "event: {change: updateRate}",
                placeholder=_("Please Select a Country Code"),
            ),
        )
    def get_rate_response(self):
        gateway = self.data.get('gateway')
        try:
            backend_api_id = SQLMobileBackend.get_backend_api_id(gateway, is_couch_id=True)
        except Exception as e:
            log_smsbillables_error(
                "Failed to get backend for calculating an sms rate due to: %s"
                % e
            )
            raise SMSRateCalculatorError("Could not obtain connection information.")

        country_code = self.data.get('country_code')
        if country_code == NONMATCHING_COUNTRY:
            country_code = None
        direction = self.data.get('direction')

        gateway_fee = SmsGatewayFee.get_by_criteria(
            backend_api_id, direction, backend_instance=gateway,
            country_code=country_code,
        )
        usage_fee = SmsUsageFee.get_by_criteria(direction, self.request.domain)
        usd_gateway_fee = gateway_fee.amount / gateway_fee.currency.rate_to_default
        usd_total = usage_fee.amount + usd_gateway_fee

        return {
            'rate': _("%s per 160 character SMS") % fmt_dollar_amount(usd_total),
        }
Exemple #9
0
    def page_context(self):
        # The webhook secret is a piece of data that is sent to hq on each
        # Telerivet inbound request. It's used to tie an inbound request to
        # a Telerivet backend.
        webhook_secret = uuid.uuid4().hex

        # The request token is only used for the purposes of using this UI to
        # setup a Telerivet backend. We need a way to post the webhook_secret
        # to create the backend, but we want hq to be the origin of the secret
        # generation. So instead, the request_token resolves to the webhook_secret
        # via a redis lookup which expires in 1 week.
        request_token = uuid.uuid4().hex

        self.set_cached_webhook_secret(request_token, webhook_secret)
        domain_has_default_gateway = SQLMobileBackend.get_domain_default_backend(
            SQLMobileBackend.SMS,
            self.domain,
            id_only=True
        ) is not None

        return {
            'outgoing_sms_form': TelerivetOutgoingSMSForm(),
            'test_sms_form': TelerivetPhoneNumberForm(),
            'finalize_gateway_form': FinalizeGatewaySetupForm(
                initial={
                    'set_as_default': (FinalizeGatewaySetupForm.NO
                                       if domain_has_default_gateway
                                       else FinalizeGatewaySetupForm.YES),
                }
            ),
            'webhook_url': absolute_reverse('telerivet_in'),
            'webhook_secret': webhook_secret,
            'request_token': request_token,
        }
Exemple #10
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()
    def country_code_response(self):
        gateway = self.data.get('gateway')
        try:
            backend_api_id = SQLMobileBackend.get_backend_api_id(gateway, is_couch_id=True)
        except Exception:
            return []
        direction = self.data.get('direction')
        criteria_query = SmsGatewayFeeCriteria.objects.filter(
            direction=direction, backend_api_id=backend_api_id
        )
        country_codes = criteria_query.exclude(
            country_code__exact=None
        ).values_list('country_code', flat=True).distinct()
        final_codes = []
        for code in country_codes:
            country_name = country_name_from_isd_code_or_empty(code)
            final_codes.append((code, country_name))

        search_term = self.data.get('searchString')
        if search_term:
            search_term = search_term.lower().replace('+', '')
            final_codes = filter(
                lambda x: (str(x[0]).startswith(search_term)
                           or x[1].lower().startswith(search_term)),
                final_codes
            )
        final_codes = [(c[0], "+%s%s" % (c[0], " (%s)" % c[1] if c[1] else '')) for c in final_codes]
        if criteria_query.filter(country_code__exact=None).exists():
            final_codes.append((
                NONMATCHING_COUNTRY,
                _('Any Country (Delivery not guaranteed via connection)')
            ))
        return final_codes
Exemple #12
0
    def test_load(self):
        backend = SQLTestSMSBackend.objects.create(
            name='BACKEND',
            is_global=True,
            hq_api_id=SQLTestSMSBackend.get_api_id(),
        )

        self.assertBackendsEqual(
            SQLMobileBackend.load(backend.pk),
            backend
        )

        self.assertBackendsEqual(
            SQLMobileBackend.load(backend.pk, api_id=SQLTestSMSBackend.get_api_id()),
            backend
        )

        self.assertBackendsEqual(
            SQLMobileBackend.load(backend.couch_id, is_couch_id=True),
            backend
        )

        self.assertBackendsEqual(
            SQLMobileBackend.load(
                backend.couch_id,
                api_id=SQLTestSMSBackend.get_api_id(),
                is_couch_id=True
            ),
            backend
        )

        backend.soft_delete()

        with self.assertRaises(SQLMobileBackend.DoesNotExist):
            SQLMobileBackend.load(backend.pk, api_id=SQLTestSMSBackend.get_api_id())

        with self.assertRaises(SQLMobileBackend.DoesNotExist):
            SQLMobileBackend.load(
                backend.couch_id,
                api_id=SQLTestSMSBackend.get_api_id(),
                is_couch_id=True
            )

        with self.assertRaises(BadSMSConfigException):
            SQLMobileBackend.load(backend.pk, api_id='this-api-id-does-not-exist')

        backend.delete()
Exemple #13
0
def _get_twilio_client(backend_instance):
    from corehq.messaging.smsbackends.twilio.models import SQLTwilioBackend

    twilio_backend = SQLMobileBackend.load(
        backend_instance, api_id=SQLTwilioBackend.get_api_id(), is_couch_id=True, include_deleted=True
    )
    config = twilio_backend.config
    return TwilioRestClient(config.account_sid, config.auth_token)
Exemple #14
0
    def tearDown(self):
        SmsBillable.objects.all().delete()
        SmsGatewayFee.objects.all().delete()
        SmsGatewayFeeCriteria.objects.all().delete()
        SmsUsageFee.objects.all().delete()
        SmsUsageFeeCriteria.objects.all().delete()

        self.currency_usd.delete()
        self.other_currency.delete()
        SMS.by_domain(generator.TEST_DOMAIN).delete()

        for api_id, backend_id in self.backend_ids.iteritems():
            SQLMobileBackend.load(backend_id, is_couch_id=True).delete()

        FakeTwilioMessageFactory.backend_message_id_to_price = {}

        super(TestGatewayFee, self).tearDown()
Exemple #15
0
def set_domain_default_backend_to_test_backend(domain):
    """
    Pass in the name of the domain to set the domain's default
    sms backend to be the test backend.
    """
    from corehq.apps.sms.models import SQLMobileBackend, SQLMobileBackendMapping

    test_backend = SQLMobileBackend.get_global_backend_by_name(SQLMobileBackend.SMS, "MOBILE_BACKEND_TEST")
    if not test_backend:
        raise Exception("Expected MOBILE_BACKEND_TEST to be created")
    SQLMobileBackendMapping.set_default_domain_backend(domain, test_backend)
Exemple #16
0
def get_ivr_backend(recipient, verified_number=None, unverified_number=None):
    if verified_number and verified_number.ivr_backend_id:
        return SQLMobileBackend.load_by_name(
            SQLMobileBackend.IVR,
            verified_number.domain,
            verified_number.ivr_backend_id
        )
    else:
        phone_number = (verified_number.phone_number if verified_number
            else unverified_number)
        phone_number = strip_plus(str(phone_number))
        prefixes = settings.IVR_BACKEND_MAP.keys()
        prefixes = sorted(prefixes, key=lambda x: len(x), reverse=True)
        for prefix in prefixes:
            if phone_number.startswith(prefix):
                return SQLMobileBackend.get_global_backend_by_name(
                    SQLMobileBackend.IVR,
                    settings.IVR_BACKEND_MAP[prefix]
                )
    return None
Exemple #17
0
    def test_get_backend_api_id(self):
        backend = SQLTestSMSBackend.objects.create(
            name='BACKEND',
            is_global=True,
            hq_api_id=SQLTestSMSBackend.get_api_id(),
        )

        self.assertEquals(
            SQLMobileBackend.get_backend_api_id(backend.pk),
            SQLTestSMSBackend.get_api_id()
        )

        self.assertEquals(
            SQLMobileBackend.get_backend_api_id(backend.couch_id, is_couch_id=True),
            SQLTestSMSBackend.get_api_id()
        )

        backend.soft_delete()
        with self.assertRaises(SQLMobileBackend.DoesNotExist):
            SQLMobileBackend.get_backend_api_id(backend.pk)

        with self.assertRaises(SQLMobileBackend.DoesNotExist):
            SQLMobileBackend.get_backend_api_id(backend.couch_id, is_couch_id=True)

        backend.delete()
Exemple #18
0
 def test_multiple_country_prefixes(self):
     self.assertEqual(
         SQLMobileBackend.load_default_by_phone_and_domain(
             SQLMobileBackend.SMS,
             '256800000000'
         ).pk,
         self.backend5.pk
     )
     self.assertEqual(
         SQLMobileBackend.load_default_by_phone_and_domain(
             SQLMobileBackend.SMS,
             '256700000000'
         ).pk,
         self.backend6.pk
     )
     self.assertEqual(
         SQLMobileBackend.load_default_by_phone_and_domain(
             SQLMobileBackend.SMS,
             '256750000000'
         ).pk,
         self.backend7.pk
     )
Exemple #19
0
def get_backend_name(backend_id):
    """
    Returns None if the backend is not found, otherwise
    returns the backend's name.
    """
    if not backend_id:
        return None

    from corehq.apps.sms.models import SQLMobileBackend
    try:
        return SQLMobileBackend.load(backend_id, is_couch_id=True).name
    except:
        return None
Exemple #20
0
def get_inbound_phone_entry(msg):
    if msg.backend_id:
        backend = SQLMobileBackend.load(msg.backend_id, is_couch_id=True)
        if not backend.is_global and toggles.INBOUND_SMS_LENIENCY.enabled(backend.domain):
            p = PhoneNumber.get_two_way_number_with_domain_scope(msg.phone_number, backend.domains_with_access)
            return (
                p,
                p is not None
            )

    return (
        PhoneNumber.get_reserved_number(msg.phone_number),
        False
    )
Exemple #21
0
def send_sms_with_backend_name(domain, phone_number, text, backend_name, metadata=None):
    phone_number = clean_phone_number(phone_number)
    backend = SQLMobileBackend.load_by_name(SQLMobileBackend.SMS, domain, backend_name)
    msg = get_sms_class()(
        domain=domain,
        phone_number=phone_number,
        direction=OUTGOING,
        date=get_utcnow(),
        backend_id=backend.couch_id,
        text=text
    )
    add_msg_tags(msg, metadata)

    return queue_outgoing_sms(msg)
Exemple #22
0
def _get_backend_tag(backend=None, backend_id=None):
    assert not (backend_id and backend)
    if backend_id:
        try:
            backend = SQLMobileBackend.load(backend_id, is_couch_id=True)
        except Exception:
            backend = None

    if not backend:
        return 'unknown'
    elif backend.is_global:
        return backend.name
    else:
        return f'{backend.domain}/{backend.name}'
Exemple #23
0
def get_inbound_phone_entry(msg):
    if msg.backend_id:
        backend = SQLMobileBackend.load(msg.backend_id, is_couch_id=True)
        if not backend.is_global and toggles.INBOUND_SMS_LENIENCY.enabled(backend.domain):
            p = PhoneNumber.get_two_way_number_with_domain_scope(msg.phone_number, backend.domains_with_access)
            return (
                p,
                p is not None
            )

    return (
        PhoneNumber.get_reserved_number(msg.phone_number),
        False
    )
Exemple #24
0
def send_sms_with_backend_name(domain, phone_number, text, backend_name, metadata=None):
    phone_number = clean_phone_number(phone_number)
    backend = SQLMobileBackend.load_by_name(SQLMobileBackend.SMS, domain, backend_name)
    msg = get_sms_class()(
        domain=domain,
        phone_number=phone_number,
        direction=OUTGOING,
        date=get_utcnow(),
        backend_id=backend.couch_id,
        text=text
    )
    add_msg_tags(msg, metadata)

    return queue_outgoing_sms(msg)
    def get_rate_table(self, country_code):
        backends = SQLMobileBackend.get_global_backends(SQLMobileBackend.SMS)

        def _directed_fee(direction, backend_api_id, backend_instance_id):
            gateway_fee = SmsGatewayFee.get_by_criteria(
                backend_api_id,
                direction,
                backend_instance=backend_instance_id,
                country_code=country_code
            )
            if not gateway_fee:
                return None
            usd_gateway_fee = gateway_fee.amount / gateway_fee.currency.rate_to_default
            usage_fee = SmsUsageFee.get_by_criteria(direction)
            return fmt_dollar_amount(usage_fee.amount + usd_gateway_fee)

        rate_table = []

        from corehq.messaging.smsbackends.test.models import SQLTestSMSBackend

        for backend_instance in backends:
            # Skip Testing backends
            if isinstance(backend_instance, SQLTestSMSBackend):
                continue

            # skip if country is not in supported countries
            if backend_instance.supported_countries:
                if ('*' not in backend_instance.supported_countries and
                   str(country_code) not in backend_instance.supported_countries):
                    continue

            gateway_fee_incoming = _directed_fee(
                INCOMING,
                backend_instance.hq_api_id,
                backend_instance.couch_id
            )
            gateway_fee_outgoing = _directed_fee(
                OUTGOING,
                backend_instance.hq_api_id,
                backend_instance.couch_id
            )

            if gateway_fee_outgoing or gateway_fee_incoming:
                rate_table.append({
                    'gateway': backend_instance.display_name,
                    'inn': gateway_fee_incoming or 'NA',  # 'in' is reserved
                    'out': gateway_fee_outgoing or 'NA'
                })
        return rate_table
Exemple #26
0
    def get_rate_table(self, country_code):
        backends = SQLMobileBackend.get_global_backends(SQLMobileBackend.SMS)

        def _directed_fee(direction, backend_api_id, backend_instance_id):
            gateway_fee = SmsGatewayFee.get_by_criteria(
                backend_api_id,
                direction,
                backend_instance=backend_instance_id,
                country_code=country_code
            )
            if not gateway_fee or gateway_fee.amount is None:
                return None
            usd_gateway_fee = gateway_fee.amount / gateway_fee.currency.rate_to_default
            usage_fee = SmsUsageFee.get_by_criteria(direction)
            return fmt_dollar_amount(usage_fee.amount + usd_gateway_fee)

        rate_table = []

        from corehq.messaging.smsbackends.test.models import SQLTestSMSBackend

        for backend_instance in backends:
            # Skip Testing backends
            if isinstance(backend_instance, SQLTestSMSBackend):
                continue

            # skip if country is not in supported countries
            if backend_instance.supported_countries:
                if ('*' not in backend_instance.supported_countries and
                   str(country_code) not in backend_instance.supported_countries):
                    continue

            gateway_fee_incoming = _directed_fee(
                INCOMING,
                backend_instance.hq_api_id,
                backend_instance.couch_id
            )
            gateway_fee_outgoing = _directed_fee(
                OUTGOING,
                backend_instance.hq_api_id,
                backend_instance.couch_id
            )

            if gateway_fee_outgoing or gateway_fee_incoming:
                rate_table.append({
                    'gateway': backend_instance.display_name,
                    'inn': gateway_fee_incoming or 'NA',  # 'in' is reserved
                    'out': gateway_fee_outgoing or 'NA'
                })
        return rate_table
Exemple #27
0
    def clean_name(self):
        value = self.cleaned_data.get("name")
        if value is not None:
            value = value.strip().upper()
        if value is None or value == "":
            raise ValidationError(_("This field is required."))
        if re.compile(r"\s").search(value) is not None:
            raise ValidationError(_("Name may not contain any spaces."))

        if self.is_global_backend:
            # We're using the form to create a global backend, so
            # ensure name is not duplicated among other global backends
            is_unique = SQLMobileBackend.name_is_unique(
                value, backend_id=self.backend_id)
        else:
            # We're using the form to create a domain-level backend, so
            # ensure name is not duplicated among other backends owned by this domain
            is_unique = SQLMobileBackend.name_is_unique(
                value, domain=self.domain, backend_id=self.backend_id)

        if not is_unique:
            raise ValidationError(_("Name is already in use."))

        return value
Exemple #28
0
def send_sms(domain,
             contact,
             phone_number,
             text,
             metadata=None,
             logged_subevent=None):
    """
    Sends an outbound SMS. Returns false if it fails.
    """
    if phone_number is None:
        return False
    if isinstance(phone_number, six.integer_types):
        phone_number = str(phone_number)
    phone_number = clean_phone_number(phone_number)

    msg = get_sms_class()(domain=domain,
                          phone_number=phone_number,
                          direction=OUTGOING,
                          date=get_utcnow(),
                          backend_id=None,
                          location_id=get_location_id_by_contact(
                              domain, contact),
                          text=text)
    if contact:
        msg.couch_recipient = contact.get_id
        msg.couch_recipient_doc_type = contact.doc_type

    if domain and contact and is_commcarecase(contact):
        backend_name = contact.get_case_property('contact_backend_id')
        backend_name = backend_name.strip() if isinstance(
            backend_name, six.string_types) else ''
        soft_assert_type_text(backend_name)
        if backend_name:
            try:
                backend = SQLMobileBackend.load_by_name(
                    SQLMobileBackend.SMS, domain, backend_name)
            except BadSMSConfigException as e:
                if logged_subevent:
                    logged_subevent.error(
                        MessagingEvent.ERROR_GATEWAY_NOT_FOUND,
                        additional_error_text=six.text_type(e))
                return False

            msg.backend_id = backend.couch_id

    add_msg_tags(msg, metadata)

    return queue_outgoing_sms(msg)
Exemple #29
0
def set_domain_default_backend_to_test_backend(domain):
    """
    Pass in the name of the domain to set the domain's default
    sms backend to be the test backend.
    """
    from corehq.apps.sms.models import SQLMobileBackend, SQLMobileBackendMapping
    test_backend = SQLMobileBackend.get_global_backend_by_name(
        SQLMobileBackend.SMS,
        'MOBILE_BACKEND_TEST'
    )
    if not test_backend:
        raise Exception("Expected MOBILE_BACKEND_TEST to be created")
    SQLMobileBackendMapping.set_default_domain_backend(
        domain,
        test_backend
    )
Exemple #30
0
    def _clean_backend_id(self, backend_id):
        try:
            backend_id = int(backend_id)
        except (ValueError, TypeError):
            raise ValidationError(_("Invalid Backend Specified."))

        try:
            backend = SQLMobileBackend.load(backend_id)
        except:
            raise ValidationError(_("Invalid Backend Specified."))

        if (backend.deleted or not backend.is_global
                or backend.backend_type != SQLMobileBackend.SMS):
            raise ValidationError(_("Invalid Backend Specified."))

        return backend_id
Exemple #31
0
    def _get_gateway_fee(cls, backend_api_id, backend_id, phone_number,
                         direction, couch_id, backend_message_id, domain):
        country_code, national_number = get_country_code_and_national_number(
            phone_number)

        backend_instance = None
        if backend_id is not None:
            backend_instance = SQLMobileBackend.load(
                backend_id,
                api_id=backend_api_id,
                is_couch_id=True,
                include_deleted=True,
            )

        is_gateway_billable = (
            backend_id is None or backend_instance.is_global
            or toggles.ENABLE_INCLUDE_SMS_GATEWAY_CHARGING.enabled(domain))

        direct_gateway_fee = gateway_fee = multipart_count = conversion_rate = None

        if is_gateway_billable:
            if backend_instance and backend_instance.using_api_to_get_fees:
                if backend_message_id:
                    direct_gateway_fee, multipart_count = \
                        cls.get_charge_details_through_api(backend_instance, backend_message_id)

                else:
                    log_smsbillables_error(
                        "Could not create direct gateway fee for message %s: no backend_message_id"
                        % couch_id)

            # always grab the gateway fee even if using an api
            gateway_fee = SmsGatewayFee.get_by_criteria(
                backend_api_id,
                direction,
                backend_instance=backend_id,
                country_code=country_code,
                national_number=national_number,
            )
            if gateway_fee:
                conversion_rate = cls.get_conversion_rate(gateway_fee)
            else:
                log_smsbillables_error(
                    "No matching gateway fee criteria for SMS %s" % couch_id)
        return _ProviderChargeInfo(direct_gateway_fee, gateway_fee,
                                   multipart_count, conversion_rate)
Exemple #32
0
 def gateway_specific_fields(self):
     domain_backends = SQLMobileBackend.get_domain_backends(
         SQLMobileBackend.SMS,
         self.domain,
     )
     backend_choices = [('', _("No Fallback Backend"))]
     backend_choices.extend([(backend.couch_id, backend.name)
                             for backend in domain_backends
                             if backend.id != self.backend_id])
     self.fields['fallback_backend_id'].choices = backend_choices
     return crispy.Fieldset(
         _("Turn Settings"),
         "client_auth_token",
         "business_id",
         "template_namespace",
         "business_auth_token",
         "fallback_backend_id",
     )
Exemple #33
0
    def page_context(self):
        # The webhook secret is a piece of data that is sent to hq on each
        # Telerivet inbound request. It's used to tie an inbound request to
        # a Telerivet backend.
        webhook_secret = uuid.uuid4().hex

        # The request token is only used for the purposes of using this UI to
        # setup a Telerivet backend. We need a way to post the webhook_secret
        # to create the backend, but we want hq to be the origin of the secret
        # generation. So instead, the request_token resolves to the webhook_secret
        # via a redis lookup which expires in 1 week.
        request_token = uuid.uuid4().hex

        TelerivetSetupView.set_cached_webhook_secret(request_token,
                                                     webhook_secret)
        domain_has_default_gateway = SQLMobileBackend.get_domain_default_backend(
            SQLMobileBackend.SMS, self.domain, id_only=True) is not None

        webhook_url = absolute_reverse('telerivet_in')
        return {
            'outgoing_sms_form':
            TelerivetOutgoingSMSForm(),
            'test_sms_form':
            TelerivetPhoneNumberForm(),
            'finalize_gateway_form':
            FinalizeGatewaySetupForm(
                initial={
                    'name':
                    'TELERIVET',
                    'set_as_default':
                    (FinalizeGatewaySetupForm.NO if domain_has_default_gateway
                     else FinalizeGatewaySetupForm.YES),
                }),
            'webhook_url':
            webhook_url,
            'include_https_notice':
            webhook_url.startswith('https'),
            'webhook_secret':
            webhook_secret,
            'request_token':
            request_token,
            'gateway_list_url':
            reverse(DomainSmsGatewayListView.urlname, args=[self.domain]),
        }
Exemple #34
0
def get_active_dimagi_owned_gateway_projects(domains, datespan, interval, datefield="date"):
    """
    Returns list of timestamps and how many domains used a Dimagi owned gateway
    in the past thrity days before each timestamp
    """
    dimagi_owned_backend_ids = SQLMobileBackend.get_global_backend_ids(SQLMobileBackend.SMS, couch_id=True)
    backend_filter = {"terms": {"backend_id": dimagi_owned_backend_ids}}

    histo_data = []
    for timestamp in daterange(interval, datespan.startdate, datespan.enddate):
        t = timestamp
        f = timestamp - relativedelta(days=30)
        sms_query = get_sms_query(f, t, "domains", "domain", domains)
        d = sms_query.filter(backend_filter).run()
        c = len(d.aggregations.domains.keys)
        if c > 0:
            histo_data.append(get_data_point(c, timestamp))

    return format_return_data(histo_data, 0, datespan)
Exemple #35
0
    def _clean_backend_id(self, backend_id):
        try:
            backend_id = int(backend_id)
        except (ValueError, TypeError):
            raise ValidationError(_("Invalid Backend Specified."))

        try:
            backend = SQLMobileBackend.load(backend_id)
        except:
            raise ValidationError(_("Invalid Backend Specified."))

        if (
            backend.deleted or
            not backend.is_global or
            backend.backend_type != SQLMobileBackend.SMS
        ):
            raise ValidationError(_("Invalid Backend Specified."))

        return backend_id
Exemple #36
0
def ivr(request):
    """
    Kookoo invokes this view for its main communication with HQ.
    Point Kookoo's 'url' parameter here.
    """
    # Retrieve all parameters
    called_number = request.GET.get("called_number", None)
    outbound_sid = request.GET.get("outbound_sid", None)
    cid = request.GET.get(
        "cid", None)  # This is the caller id, format being 0..., not 91...
    sid = request.GET.get("sid", None)
    operator = request.GET.get("operator", None)
    circle = request.GET.get("circle", None)
    event = request.GET.get("event", None)
    data = request.GET.get("data", None)
    total_call_duration = request.GET.get("total_call_duration", None)

    phone_number = cid
    if phone_number is not None and phone_number.startswith("0"):
        phone_number = "91" + phone_number[1:]

    gateway_session_id = "KOOKOO-" + sid

    # Process the event
    if event == "NewCall":
        ivr_event = IVR_EVENT_NEW_CALL
    elif event == "GotDTMF":
        ivr_event = IVR_EVENT_INPUT
    elif event == "Disconnect":
        ivr_event = IVR_EVENT_DISCONNECT
    else:
        ivr_event = IVR_EVENT_DISCONNECT

    backend = SQLMobileBackend.get_global_backend_by_name(
        SQLMobileBackend.IVR, 'MOBILE_BACKEND_KOOKOO')
    with CriticalSection([gateway_session_id], timeout=300):
        result = incoming(phone_number,
                          gateway_session_id,
                          ivr_event,
                          backend=backend,
                          input_data=data,
                          duration=total_call_duration)
    return result
Exemple #37
0
def get_inbound_phone_entry(phone_number, backend_id=None):
    if backend_id:
        backend = SQLMobileBackend.load(backend_id, is_couch_id=True)
        if toggles.INBOUND_SMS_LENIENCY.enabled(backend.domain):
            p = None
            if toggles.ONE_PHONE_NUMBER_MULTIPLE_CONTACTS.enabled(backend.domain):
                running_session_info = XFormsSessionSynchronization.get_running_session_info_for_channel(
                    SMSChannel(backend_id=backend_id, phone_number=phone_number)
                )
                contact_id = running_session_info.contact_id
                if contact_id:
                    p = PhoneNumber.get_phone_number_for_owner(contact_id, phone_number)
                if p is not None:
                    return (
                        p,
                        True
                    )
                elif running_session_info.session_id:
                    # This would be very unusual, as it would mean the supposedly running form session
                    # is linked to a phone number, contact pair that doesn't exist in the PhoneNumber table
                    notify_error(
                        "Contact from running session has no match in PhoneNumber table. "
                        "Only known way for this to happen is if you "
                        "unregister a phone number for a contact "
                        "while they are in an active session.",
                        details={
                            'running_session_info': running_session_info
                        }
                    )

            # NOTE: I don't think the backend could ever be global here since global backends
            # don't have a 'domain' and so the toggles would never be activated
            if not backend.is_global:
                p = PhoneNumber.get_two_way_number_with_domain_scope(phone_number, backend.domains_with_access)
                return (
                    p,
                    p is not None
                )

    return (
        PhoneNumber.get_reserved_number(phone_number),
        False
    )
Exemple #38
0
def send_sms(domain, contact, phone_number, text, metadata=None, logged_subevent=None):
    """
    Sends an outbound SMS. Returns false if it fails.
    """
    if phone_number is None:
        return False
    if isinstance(phone_number, six.integer_types):
        phone_number = str(phone_number)
    phone_number = clean_phone_number(phone_number)

    msg = get_sms_class()(
        domain=domain,
        phone_number=phone_number,
        direction=OUTGOING,
        date=get_utcnow(),
        backend_id=None,
        location_id=get_location_id_by_contact(domain, contact),
        text = text
    )
    if contact:
        msg.couch_recipient = contact.get_id
        msg.couch_recipient_doc_type = contact.doc_type

    if domain and contact and is_commcarecase(contact):
        backend_name = contact.get_case_property('contact_backend_id')
        backend_name = backend_name.strip() if isinstance(backend_name, six.string_types) else ''
        soft_assert_type_text(backend_name)
        if backend_name:
            try:
                backend = SQLMobileBackend.load_by_name(SQLMobileBackend.SMS, domain, backend_name)
            except BadSMSConfigException as e:
                if logged_subevent:
                    logged_subevent.error(MessagingEvent.ERROR_GATEWAY_NOT_FOUND,
                        additional_error_text=six.text_type(e))
                return False

            msg.backend_id = backend.couch_id

    add_msg_tags(msg, metadata)

    return queue_outgoing_sms(msg)
Exemple #39
0
def get_active_dimagi_owned_gateway_projects(domains, datespan, interval,
        datefield='date'):
    """
    Returns list of timestamps and how many domains used a Dimagi owned gateway
    in the past thrity days before each timestamp
    """
    dimagi_owned_backend_ids = SQLMobileBackend.get_global_backend_ids(couch_id=True)
    backend_filter = {'terms': {'backend_id': dimagi_owned_backend_ids}}

    histo_data = []
    for timestamp in daterange(interval, datespan.startdate, datespan.enddate):
        t = timestamp
        f = timestamp - relativedelta(days=30)
        sms_query = get_sms_query(f, t, 'domains', 'domain', domains,
                                  DOMAIN_COUNT_UPPER_BOUND)
        d = sms_query.filter(backend_filter).run()
        c = len(d.facet('domains', 'terms'))
        if c > 0:
            histo_data.append(get_data_point(c, timestamp))

    return format_return_data(histo_data, 0, datespan)
Exemple #40
0
def ivr(request):
    """
    Kookoo invokes this view for its main communication with HQ.
    Point Kookoo's 'url' parameter here.
    """
    # Retrieve all parameters
    called_number = request.GET.get("called_number", None)
    outbound_sid = request.GET.get("outbound_sid", None)
    cid = request.GET.get("cid", None) # This is the caller id, format being 0..., not 91...
    sid = request.GET.get("sid", None)
    operator = request.GET.get("operator", None)
    circle = request.GET.get("circle", None)
    event = request.GET.get("event", None)
    data = request.GET.get("data", None)
    total_call_duration = request.GET.get("total_call_duration", None)
    
    phone_number = cid
    if phone_number is not None and phone_number.startswith("0"):
        phone_number = "91" + phone_number[1:]
    
    gateway_session_id = "KOOKOO-" + sid
    
    # Process the event
    if event == "NewCall":
        ivr_event = IVR_EVENT_NEW_CALL
    elif event == "GotDTMF":
        ivr_event = IVR_EVENT_INPUT
    elif event == "Disconnect":
        ivr_event = IVR_EVENT_DISCONNECT
    else:
        ivr_event = IVR_EVENT_DISCONNECT

    backend = SQLMobileBackend.get_global_backend_by_name(
        SQLMobileBackend.IVR,
        'MOBILE_BACKEND_KOOKOO'
    )
    with CriticalSection([gateway_session_id], timeout=300):
        result = incoming(phone_number, gateway_session_id, ivr_event,
            backend=backend, input_data=data, duration=total_call_duration)
    return result
Exemple #41
0
def get_active_dimagi_owned_gateway_projects(domains,
                                             datespan,
                                             interval,
                                             datefield='date'):
    """
    Returns list of timestamps and how many domains used a Dimagi owned gateway
    in the past thrity days before each timestamp
    """
    dimagi_owned_backend_ids = SQLMobileBackend.get_global_backend_ids(
        SQLMobileBackend.SMS, couch_id=True)
    backend_filter = {'terms': {'backend_id': dimagi_owned_backend_ids}}

    histo_data = []
    for timestamp in daterange(interval, datespan.startdate, datespan.enddate):
        t = timestamp
        f = timestamp - relativedelta(days=30)
        sms_query = get_sms_query(f, t, 'domains', 'domain', domains)
        d = sms_query.filter(backend_filter).run()
        c = len(d.aggregations.domains.keys)
        if c > 0:
            histo_data.append(get_data_point(c, timestamp))

    return format_return_data(histo_data, 0, datespan)
Exemple #42
0
    def page_context(self):
        # The webhook secret is a piece of data that is sent to hq on each
        # Telerivet inbound request. It's used to tie an inbound request to
        # a Telerivet backend.
        webhook_secret = uuid.uuid4().hex

        # The request token is only used for the purposes of using this UI to
        # setup a Telerivet backend. We need a way to post the webhook_secret
        # to create the backend, but we want hq to be the origin of the secret
        # generation. So instead, the request_token resolves to the webhook_secret
        # via a redis lookup which expires in 1 week.
        request_token = uuid.uuid4().hex

        self.set_cached_webhook_secret(request_token, webhook_secret)
        domain_has_default_gateway = (
            SQLMobileBackend.get_domain_default_backend(SQLMobileBackend.SMS, self.domain, id_only=True) is not None
        )

        webhook_url = absolute_reverse("telerivet_in")
        return {
            "outgoing_sms_form": TelerivetOutgoingSMSForm(),
            "test_sms_form": TelerivetPhoneNumberForm(),
            "finalize_gateway_form": FinalizeGatewaySetupForm(
                initial={
                    "name": "TELERIVET",
                    "set_as_default": (
                        FinalizeGatewaySetupForm.NO if domain_has_default_gateway else FinalizeGatewaySetupForm.YES
                    ),
                }
            ),
            "webhook_url": webhook_url,
            "include_https_notice": webhook_url.startswith("https"),
            "webhook_secret": webhook_secret,
            "request_token": request_token,
            "gateway_list_url": reverse(DomainSmsGatewayListView.urlname, args=[self.domain]),
        }
Exemple #43
0
def num_telerivet_backends(domain):
    from corehq.messaging.smsbackends.telerivet.models import SQLTelerivetBackend
    backends = SQLMobileBackend.get_domain_backends(SQLMobileBackend.SMS, domain)
    return len([b for b in backends if isinstance(b, SQLTelerivetBackend)])
Exemple #44
0
def _sms_backend_is_global(sms_backend_id):
    return SQLMobileBackend.load(
        sms_backend_id,
        is_couch_id=True,
        include_deleted=True
    ).is_global
Exemple #45
0
def num_telerivet_backends(domain):
    from corehq.messaging.smsbackends.telerivet.models import SQLTelerivetBackend
    backends = SQLMobileBackend.get_domain_backends(SQLMobileBackend.SMS,
                                                    domain)
    return len([b for b in backends if isinstance(b, SQLTelerivetBackend)])
Exemple #46
0
def _sms_backend_is_global(sms_backend_id):
    return SQLMobileBackend.load(
        sms_backend_id,
        is_couch_id=True,
        include_deleted=True
    ).is_global