Exemple #1
0
 def test_add_credit(self):
     """ We can add credit to a subscriber's balance. """
     increment = randrange(1, 1000)
     prior = subscriber.get_account_balance(self.TEST_IMSI)
     subscriber.add_credit(self.TEST_IMSI, increment)
     after = subscriber.get_account_balance(self.TEST_IMSI)
     self.assertEqual(prior + increment, after)
def process_confirm(from_imsi, code):
    """Process a confirmation request.

    Args:
      from_imsi: sender's IMSI
      code: the input confirmation code string
    """
    # Step one: delete all the confirm codes older than some time.
    db = sqlite3.connect(config_db['pending_transfer_db_path'])
    db.execute("DELETE FROM pending_transfers"
               " WHERE time - ? > 600", (time.time(),))
    db.commit()

    # Step two: check if this (from_imsi, code) combo is valid.
    r = db.execute("SELECT from_acct, to_acct, amount FROM pending_transfers"
                   " WHERE code=? AND from_acct=?", (code, from_imsi))
    res = r.fetchone()
    if res and len(res) == 3:
        from_imsi, to_imsi, amount = res
        from_num = subscriber.get_numbers_from_imsi(from_imsi)[0]
        to_num = subscriber.get_numbers_from_imsi(to_imsi)[0]
        reason = "SMS transfer from %s to %s" % (from_num, to_num)
        # Deduct credit from the sender.
        from_imsi_old_credit = subscriber.get_account_balance(from_imsi)
        from_imsi_new_credit = int(from_imsi_old_credit) - int(amount)
        events.create_transfer_event(from_imsi, from_imsi_old_credit,
                                     from_imsi_new_credit, reason,
                                     from_number=from_num, to_number=to_num)
        subscriber.subtract_credit(from_imsi, str(int(amount)))
        # Add credit to the recipient.
        to_imsi_old_credit = subscriber.get_account_balance(to_imsi)
        to_imsi_new_credit = int(to_imsi_old_credit) + int(amount)
        events.create_transfer_event(to_imsi, to_imsi_old_credit,
                                     to_imsi_new_credit, reason,
                                     from_number=from_num, to_number=to_num)
        subscriber.add_credit(to_imsi, str(int(amount)))
        # Humanize credit strings
        amount_str = freeswitch_strings.humanize_credits(amount)
        to_balance_str = freeswitch_strings.humanize_credits(
                to_imsi_new_credit)
        from_balance_str = freeswitch_strings.humanize_credits(
                from_imsi_new_credit)
        # Let the recipient know they got credit.
        message = gt("You've received %(amount)s credits from %(from_num)s!"
                     " Your new balance is %(new_balance)s.") % {
                     'amount': amount_str, 'from_num': from_num,
                     'new_balance': to_balance_str}
        sms.send(str(to_num), str(config_db['app_number']), str(message))
        # Remove this particular the transfer as it's no longer pending.
        db.execute("DELETE FROM pending_transfers WHERE code=?"
                   " AND from_acct=?", (code, from_imsi))
        db.commit()
        # Tell the sender that the operation succeeded.
        return True, gt("You've transferred %(amount)s to %(to_num)s. "
                        "Your new balance is %(new_balance)s.") % {
                                'amount': amount_str, 'to_num': to_num,
                                'new_balance': from_balance_str}
    return False, gt("That transfer confirmation code doesn't exist"
                     " or has expired.")
Exemple #3
0
    def test_negative_balance(self):
        """Sub balances are clamped at a min of zero.

        If they go lower, we have made a mistake in the billing system as the
        procedure is to first check if a sub has the funds to complete an
        operation and then, if so, to complete the operation and bill for it.
        When we previously allowed negative sub balances it created some
        confusing displays in the dashboard.
        """
        prior = subscriber.get_account_balance(self.TEST_IMSI)
        decrement = prior + randrange(1, 1000)
        subscriber.subtract_credit(self.TEST_IMSI, decrement)
        self.assertEqual(0, subscriber.get_account_balance(self.TEST_IMSI))
Exemple #4
0
def purge_entry(pk):
    """
        Once a subscriber's promo subscription expires, this task will remove
        the entry from pcari_promo_subscriptions table
        Also, it sends an SMS to inform the subscriber that the
        promo has expired
    Args:
        pk: primary key of PromoSubscriptions entry

    Returns: None
    """

    subscription = PromoSubscription.objects.get(pk=pk)
    promoname = subscription.promo.keyword
    callerid = subscription.contact.callerid

    # we should also create an event
    balance = endaga_sub.get_account_balance(subscription.contact.imsi)
    reason = "Promo Expiration: %s" % subscription.promo.keyword
    events.create_sms_event(subscription.contact.imsi, balance, 0, reason,
                            '555')

    subscription.delete()

    # inform user that promo has expired
    msg = "Your %s promo subscription has already expired." % promoname
    send_sms(callerid, '0000', msg)
Exemple #5
0
    def post(self, request, format=None):
        needed_fields = ["imsi", "keyword"]
        if not all(i in request.POST for i in needed_fields):
            return Response("Missing Args", status=status.HTTP_400_BAD_REQUEST)

        imsi = request.data['imsi']
        keyword = request.data['keyword']
        callerid = endaga_sub.get_numbers_from_imsi(imsi)[0]
        subscriptions = PromoSubscription.objects.filter(
            contact__imsi__exact=imsi, promo__keyword=keyword)
        if not subscriptions:
            send_sms.delay(callerid, '0000',
                           _("You have no %s subscriptions.") % keyword)
            ret = 'FAIL UNSUBSCRIBE'
        else:
            for item in subscriptions:
                app.control.revoke(str(item.id), terminate=True)
            subscriptions.delete()
            msg = _("You are now unsubscribed from your %s promos.") % keyword
            send_sms.delay(callerid, '0000', msg)
            ret = 'OK UNSUBSCRIBE'

            # we should also create an event
            balance = endaga_sub.get_account_balance(imsi)
            reason = "Promo Cancel Subscription: %s" % keyword
            events.create_sms_event(imsi, balance, 0, reason, '555')

        return Response(ret, status=status.HTTP_200_OK)
Exemple #6
0
 def test_subtract_credit(self):
     """ We can subtract credit from a subscriber's balance. """
     prior = randrange(1, 1000)  # add some credit first
     subscriber.add_credit(self.TEST_IMSI, prior)
     decrement = randrange(0, prior)
     subscriber.subtract_credit(self.TEST_IMSI, decrement)
     after = subscriber.get_account_balance(self.TEST_IMSI)
     self.assertEqual(prior - int(decrement), after)
Exemple #7
0
    def adjust_credits(self, data):
        required_fields = ["imsi", "change"]
        if not all([_ in data for _ in required_fields]):
            return web.BadRequest()
        imsi = data["imsi"]
        try:
            change = int(data["change"])
        except ValueError:
            return web.BadRequest()
        old_credit = subscriber.get_account_balance(imsi)
        if change > 0:
            subscriber.add_credit(imsi, str(abs(change)))
        elif change < 0:
            subscriber.subtract_credit(imsi, str(abs(change)))

        new_credit = subscriber.get_account_balance(imsi)

        # Codeship is stupid. These imports break CI and this is an untested
        # method :)
        from core import freeswitch_interconnect, freeswitch_strings

        # Send a confirmation to the subscriber
        number = subscriber.get_caller_id(imsi)
        change_frmt = freeswitch_strings.humanize_credits(change)
        balance_frmt = freeswitch_strings.humanize_credits(new_credit)

        fs_ic = freeswitch_interconnect.freeswitch_ic(self.conf)
        fs_ic.send_to_number(
            number, '000',
            freeswitch_strings.gt(
                "The network operator adjusted your credit by %(change)s. "
                "Your balance is now %(balance)s.") % {
                    'change': change_frmt,
                    'balance': balance_frmt
                })

        # TODO(matt): all credit adjustments are of the kind "add_money," even
        #             if they actually subtract credit.
        reason = 'Update from web UI (add_money)'
        events.create_add_money_event(imsi,
                                      old_credit,
                                      new_credit,
                                      reason,
                                      to_number=number)
        return web.ok()
Exemple #8
0
def process_transfer(from_imsi, to_imsi, amount):
    """Process a transfer request.

    Args:
      from_imsi: the sender's IMSI
      to_imsi: the recipient's IMSI
      amount: an amount of credit to add (type?)

    Returns:
      boolean indicating success
    """
    # Error when user tries to transfer to his own account
    if from_imsi == to_imsi:
        return False, gt("Transaction Failed. Sharing load to "
                         "your own account is not allowed.")
    from_balance = int(subscriber.get_account_balance(from_imsi))
    # Error when user tries to transfer more credit than they have.
    if not from_balance or from_balance < amount:
        return False, gt("Your account doesn't have sufficient funds for"
                         " the transfer.")
    # Error when user tries to transfer to a non-existent user.
    #       Could be 0!  Need to check if doesn't exist.
    if not to_imsi or (subscriber.get_account_balance(to_imsi) == None):
        return False, gt("The number you're sending to doesn't exist."
                         " Try again.")
    # Add the pending transfer.
    code = ''
    for _ in range(int(config_db['code_length'])):
        code += str(random.randint(0, 9))
    db = sqlite3.connect(config_db['pending_transfer_db_path'])
    db.execute("INSERT INTO pending_transfers VALUES (?, ?, ?, ?, ?)",
               (code, time.time(), from_imsi, to_imsi, amount))
    db.commit()
    db.close()
    to_num = subscriber.get_numbers_from_imsi(to_imsi)[0]
    amount_str = freeswitch_strings.humanize_credits(amount)
    response = gt("Reply to this message with %(code)s to confirm your"
                  " transfer of %(amount)s to %(to_num)s. Code expires in ten"
                  " minutes.") % {
                      'code': code,
                      'amount': amount_str,
                      'to_num': to_num
                  }
    return True, response
Exemple #9
0
def add_promo(request, pk=None, template_name='contacts/addpromo_form.html'):
    """
        This function ties a promo (creates a promo subscription)
        to a SIM card (Contact)
    Args:
        request:
        pk: primary key passed is the SIM card's callerid
        template_name:

    Returns:

    """
    if pk:
        contact = get_object_or_404(Contact, callerid=pk)
    else:
        contact = None
    form = ContactAddPromoForm(request.POST or None, contact=contact)

    if form.is_valid():
        instance = form.save(commit=False)
        promo = form.cleaned_data.get('promo')
        contact = form.cleaned_data.get('contact')
        instance.local_sms = promo.local_sms
        instance.local_call = promo.local_call
        instance.outside_sms = promo.outside_sms
        instance.outside_call = promo.outside_call
        instance.date_expiration = timezone.now() + timedelta(promo.validity)
        instance.save()

        send_sms.delay(
            contact.callerid, '0000',
            _('You are automatically subscribed to %(promo)s promo. '
              'To opt out, text REMOVE %(keyword)s to 555.') %
            ({
                'promo': promo.name,
                'keyword': promo.keyword
            }))
        purge_entry.apply_async(eta=instance.date_expiration,
                                args=[instance.pk],
                                task_id=str(instance.pk))

        # we should also create an event
        balance = endaga_sub.get_account_balance(contact.imsi)
        reason = "Promo Auto Subscription: %s" % promo.keyword
        events.create_sms_event(contact.imsi, balance, 0, reason, '555')

        alerts.success(
            request,
            _("You've successfully added '%(promo)s' promo to "
              "contact '%(contact)s.'") % ({
                  'promo': promo,
                  'contact': contact
              }))
        return contact_list(request)
    return render(request, template_name, {'form': form})
 def POST(self):
     """Handles POST requests."""
     data = web.input()
     if ('from_name' not in data or 'service_type' not in data or
             'destination' not in data):
         raise web.BadRequest()
     # Process the CDR data.
     cost_in_credits = billing.get_sms_cost(
         data.service_type, destination_number=data.destination)
     try:
         old_balance = subscriber.get_account_balance(data.from_name)
         subscriber.subtract_credit(data.from_name, str(cost_in_credits))
     except SubscriberNotFound:
         # The subscriber does not exist yet but has sent an SMS
         if data.service_type == 'free_sms':
             # But that is OK for a free service like provisioning
             old_balance = 0
         else:
             raise
     reason = "SMS sent to %s (%s)" % (data.destination, data.service_type)
     events.create_sms_event(
         data.from_name, old_balance, cost_in_credits, reason,
         data.destination, from_imsi=data.from_name,
         from_number=data.from_number)
     # If this was an in-network event, find the cost for the recipient.  We
     # can lookup the recipient by data.destination (the "to number").
     if 'local' in data.service_type:
         recipient_imsi = subscriber.get_imsi_from_number(data.destination)
         old_balance = subscriber.get_account_balance(recipient_imsi)
         cost_in_credits = billing.get_sms_cost(
             'local_recv_sms', destination_number=data.destination)
         subscriber.subtract_credit(recipient_imsi, str(cost_in_credits))
         reason = "SMS received from %s (local_recv_sms)" % data.from_name
         events.create_sms_event(
             recipient_imsi, old_balance, cost_in_credits, reason,
             data.destination, from_imsi=data.from_name,
             from_number=data.from_number)
     # Return 200 OK.
     headers = {
         'Content-type': 'text/plain'
     }
     raise web.OK(None, headers)
def chat(message, imsi):
    """Handle chat requests.

    Args:
      imsi: a subscriber's IMSI
    """
    try:
        account_balance = str(subscriber.get_account_balance(imsi))
    except SubscriberNotFound:
        account_balance = ''
    consoleLog('info', "Returned Chat: " + account_balance + "\n")
    message.chat_execute('set', '_openbts_ret=%s' % account_balance)
def fsapi(session, stream, env, imsi):
    """Handle FS API requests.

    Args:
      imsi: a subscriber's IMSI
    """
    try:
        account_balance = str(subscriber.get_account_balance(imsi))
    except SubscriberNotFound:
        account_balance = ''
    consoleLog('info', "Returned FSAPI: " + account_balance + "\n")
    stream.write(account_balance)
Exemple #13
0
 def bill(self, to_number, from_number):
     try:
         if from_number == DASHBOARD_FROM_NUMBER:
             self.tariff_type = 'free_sms'
         tariff = billing.get_sms_cost(self.tariff_type,
                                       destination_number=to_number)
         username = subscriber.get_imsi_from_number(to_number)
         if username:
             reason = 'SMS from %s to %s (incoming_sms)' % (
                 from_number, username)
             old_balance = subscriber.get_account_balance(username)
             subscriber.subtract_credit(username, str(int(tariff)))
             events.create_sms_event(username, old_balance, tariff, reason,
                                     to_number, from_number=from_number)
     except Exception as e:
         logger.error("Endaga bill error:" + traceback.format_exc(e))
def create_gprs_event(imsi, cost, reason, up_bytes, down_bytes, timespan):
    """Creates a GPRS event.

    Args:
      up_bytes: integer amount of data uploaded during the timespan
      down_bytes: integer amount of data downloaded during the timespan
      timsespan: number of seconds over which this measurement was taken
    """
    old_balance = int(subscriber.get_account_balance(imsi))
    new_balance = max(0, old_balance - int(cost))
    _create_event(imsi,
                  old_balance,
                  new_balance,
                  reason,
                  up_bytes=up_bytes,
                  down_bytes=down_bytes,
                  timespan=timespan)
Exemple #15
0
def promo_delete_subscription(request,
                              pk,
                              template_name='promos/subscription_'
                              'confirm_delete.html'):
    subscription = get_object_or_404(PromoSubscription, pk=pk)
    if request.method == 'POST':
        revoke(str(pk), terminate=True)
        msg = _("You were automatically unsubscribed from your %s promo."
                ) % subscription.promo.keyword
        send_sms.delay(subscription.contact.callerid, '0000', msg)

        # we should also create an event
        balance = endaga_sub.get_account_balance(subscription.contact.imsi)
        reason = "Promo Cancel Subscription: %s" % subscription.promo.keyword
        events.create_sms_event(subscription.contact.imsi, balance, 0, reason,
                                '555')

        subscription.delete()
        return redirect(request.GET.get('from', 'promos'))
    return render(request, template_name, {'subscription': subscription})
Exemple #16
0
def push_cmd_routine(service_pk):
    """
        For push-based services, this handles content delivery to subscribers
    Args:
        service_pk: primary key of the service i
    Returns:
        None
    """
    # Execute code defined by service.scripts or service.plugin
    # and then get result

    try:
        subscriptions = ServiceSubscribers.objects.filter(service=service_pk)
        service = Service.objects.get(pk=service_pk)
        # cmd = "fs_cli -x 'python %s %s' " % (service.script.fs_script,
        #                                      service.script_arguments.value)
        # result = subprocess.check_output(cmd, shell=True)
        result = 'You received a dummy output.'
        for item in subscriptions:
            # Check balance of subscriber first
            balance = endaga_sub.get_account_balance(item.subscriber.imsi)
            if balance - item.service.price < 0:
                # not enough balance, TODO: determine proper action here
                pass
            else:
                send_sms.delay(item.subscriber.callerid, '0000', result)
                endaga_sub.subtract_credit(item.subscriber.imsi,
                                           str(item.service.price))
                event = "Received pushed content from '%s' service" % \
                        item.service.keyword
                ServiceEvents.objects.create(service=item.service,
                                             subscriber=item.subscriber,
                                             event=event)
    except BaseException:
        pass
        # no subscribers, nothing to do.
    return result
Exemple #17
0
    def process_cdr(self, cdr_xml):
        """Processes the XML CDR for the caller."""
        cdr_dom = xml.parseString(cdr_xml)
        # Handle only b-legs for billing.
        origin = cdr_dom.getElementsByTagName("origination")
        if origin:
            return
        # Handle only b-legs for billing.
        # For our purposes, billsec is how long the call lasted. call_duration
        # captures the amount of time spent ringing, which we don't charge for,
        # so don't include here. Caller and callee are just used for logging
        # and reason statements.
        # TODO(matt): what happens if the tag does not exist?
        call_duration = int(
            get_tag_text(
                cdr_dom.getElementsByTagName("duration")[0].childNodes))
        billsec = int(
            get_tag_text(
                cdr_dom.getElementsByTagName("billsec")[0].childNodes))
        # In b-leg cdrs, there are multiple destinations -- the sip one (IMSI)
        # and the dialed one (MSISDN).  We want the latter.
        callees = cdr_dom.getElementsByTagName("destination_number")
        callee = ''
        for c in callees:
            c = get_tag_text(c.childNodes)
            # NOT THE IMSI
            if c[0:4] != IMSI_PREFIX:
                callee = c
                break
        if callee[0] == "+":
            callee = callee[1:]
        hangupcause = get_hangup_cause(cdr_dom)
        # This is where we get the info we need to do billing.
        if len(cdr_dom.getElementsByTagName("service_type")) > 0:
            service_type = get_tag_text(
                cdr_dom.getElementsByTagName("service_type")[0].childNodes)
            # Get caller / callee info.  See the 'CDR notes' doc in drive for
            # more info.
            from_imsi, from_number, to_imsi, to_number = 4 * [None]
            # We always get 'from_imsi' from the <username> tag with the
            # <caller_profile> parent element. If it's a BTS-originated call,
            # this will be an IMSI; otherwise, it'll be an MSISDN.
            if service_type not in ['incoming_call']:
                elements = cdr_dom.getElementsByTagName('username')
                for element in elements:
                    if element.parentNode.nodeName == 'caller_profile':
                        username = get_tag_text(element.childNodes)
                        from_imsi = subscriber.get_imsi_from_username(username)
                        break
            # Get 'from_number' (only available for outside and local calls).
            if service_type in ['outside_call', 'local_call', 'incoming_call']:
                elements = cdr_dom.getElementsByTagName('caller_id_name')
                for element in elements:
                    if element.parentNode.nodeName == 'caller_profile':
                        from_number = get_tag_text(element.childNodes)
                        break
            # Get 'to_imsi' (only available for local/incoming calls).
            if service_type in ['local_call', 'incoming_call']:
                elements = cdr_dom.getElementsByTagName('callee_id_number')
                for element in elements:
                    if element.parentNode.nodeName == 'caller_profile':
                        callee_id = get_tag_text(element.childNodes)
                        if callee_id[0:4] == IMSI_PREFIX:
                            to_imsi = callee_id
                        else:
                            # callee_id_number in the CDR is MSISDN.
                            to_imsi = subscriber.get_imsi_from_number(
                                callee_id)
                        break

            # Get 'to_number' (slightly different for local/incoming calls).
            if service_type in ['outside_call', 'free_call', 'error_call']:
                elements = cdr_dom.getElementsByTagName('destination_number')
                for element in elements:
                    if element.parentNode.nodeName == 'caller_profile':
                        to_number = get_tag_text(element.childNodes)
                        break
            elif service_type in ['local_call', 'incoming_call']:
                elements = cdr_dom.getElementsByTagName('destination_number')
                for element in elements:
                    if (element.parentNode.nodeName ==
                            'originator_caller_profile'):
                        to_number = get_tag_text(element.childNodes)
                        break
            # Generate billing information for the caller, if the caller is
            # local to the BTS.
            if service_type != 'incoming_call':
                tariff = billing.get_service_tariff(
                    service_type, 'call', destination_number=to_number)
                cost = billing.get_call_cost(billsec,
                                             service_type,
                                             destination_number=to_number)
                reason = "%s sec call to %s (%s/%s)" % (
                    billsec, to_number, service_type, hangupcause)
                old_balance = subscriber.get_account_balance(from_imsi)
                subscriber.subtract_credit(from_imsi, str(cost))
                owner_imsi = from_imsi
                kind = service_type
                events.create_call_event(owner_imsi, from_imsi, from_number,
                                         to_imsi, to_number, old_balance, cost,
                                         kind, reason, tariff, call_duration,
                                         billsec)

            # Create a call record for the callee, if applicable.
            if service_type in ['local_call', 'incoming_call']:
                if service_type == 'local_call':
                    service_type = 'local_recv_call'
                tariff = billing.get_service_tariff(service_type, 'call')
                cost = billing.get_call_cost(billsec, service_type)
                reason = "%d sec call from %s (%s/%s)" % (
                    billsec, from_number, service_type, hangupcause)
                # Note! This is different than how we bill for a caller --
                # we're deducting from the 'to_imsi' (the callee) instead.
                old_balance = subscriber.get_account_balance(to_imsi)
                subscriber.subtract_credit(to_imsi, str(cost))
                owner_imsi = to_imsi
                kind = service_type
                events.create_call_event(owner_imsi, from_imsi, from_number,
                                         to_imsi, to_number, old_balance, cost,
                                         kind, reason, tariff, call_duration,
                                         billsec)

        else:
            username = get_tag_text(
                cdr_dom.getElementsByTagName("username")[0].childNodes)
            from_imsi = subscriber.get_imsi_from_username(username)
            message = "No rate info for this call. (from: %s, billsec: %s)" % (
                from_imsi, billsec)
            logger.error(message)
Exemple #18
0
    def post(self, request):
        """ POST method for subscribing to a service """
        needed_fields = ["imsi", "keyword"]
        if not all(i in request.POST for i in needed_fields):
            return Response("ERROR: Missing arguments.",
                            status=status.HTTP_400_BAD_REQUEST)

        imsi = request.data['imsi']

        try:
            subscriber = Contact.objects.get(imsi__exact=imsi)
        except BaseException:
            send_sms.delay(
                endaga_sub.get_numbers_from_imsi(imsi)[0], '0000',
                _("You are currently not registered to the BTS. "
                  "Please register."))
            return Response("ERROR: Not registered subscriber.",
                            status=status.HTTP_400_BAD_REQUEST)

        try:
            service = Service.objects.filter(
                keyword=request.data['keyword'].upper(),
                service_type='P',
                status='P')[0]
        except BaseException:
            send_sms.delay(
                subscriber.callerid, '0000',
                _("Sorry we can't process your request. "
                  "Invalid keyword."))
            return Response("ERROR: Invalid keyword.",
                            status=status.HTTP_400_BAD_REQUEST)

        is_duplicate = ServiceSubscribers.objects. \
            filter(service=service, subscriber=subscriber).exists()

        if is_duplicate:
            send_sms.delay(
                subscriber.callerid, '0000',
                _("You are already subscribed to %s.") % service.name)
            return Response('OK ALREADY SUBSCRIBED', status=status.HTTP_200_OK)

        else:
            # check if subscriber has enough balance
            balance = endaga_sub.get_account_balance(imsi)
            if balance - service.price < 0:
                send_sms.delay(
                    subscriber.callerid, '0000',
                    _("You do not have sufficient balance to "
                      "subscribe to %s.") % service.name)
                return Response("Insufficient balance",
                                status=status.HTTP_402_PAYMENT_REQUIRED)

            # user passes above check, has enough balance, so sign him up!
            new_subscription = ServiceSubscribers(subscriber=subscriber,
                                                  service=service)
            new_subscription.date_joined = timezone.now()
            new_subscription.save()
            # finally, deduct service.price from subscriber's balance
            # price is expressed in millicents
            endaga_sub.subtract_credit(imsi, str(service.price))

            send_sms.delay(
                subscriber.callerid, '0000',
                _("You have successfully subscribed to %s.") % service.name)
            return Response('OK SUBSCRIBED', status=status.HTTP_200_OK)
Exemple #19
0
    def post(self, request):
        needed_fields = ["imsi", "keyword"]
        if not all(i in request.POST for i in needed_fields):
            return Response("Missing Args", status=status.HTTP_400_BAD_REQUEST)

        imsi = request.data['imsi']
        keyword = request.data['keyword']

        try:
            subscriber = Contact.objects.get(imsi__exact=imsi)
        except BaseException:  # subscriber not found in Contacts table
            send_sms.delay(
                endaga_sub.get_numbers_from_imsi(imsi)[0], '0000',
                _("You are not listed in the PCARI-VBTS database. "
                  "Please register."))
            return Response("Not Found", status=status.HTTP_404_NOT_FOUND)

        # We put code to optionally limit promo subscriptions
        # this depends on the limit type declared in the configs

        try:
            limit_type = Config.objects.get(key='promo_limit_type').value
        except Config.DoesNotExist:
            limit_type = 'NA'

        try:
            max_promo_subscription = int(
                Config.objects.get(key='max_promo_subscription').value)
        except Config.DoesNotExist:
            max_promo_subscription = 1

        try:
            min_balance_required = float(
                Config.objects.get(key='min_balance_required').value)
        except Config.DoesNotExist:
            min_balance_required = 0

        # type A: Limit number of subscription per promo
        if limit_type == 'A':
            count = PromoSubscription.objects.filter(
                promo__keyword__exact=keyword, contact=subscriber).count()
            if count >= max_promo_subscription:
                send_sms.delay(subscriber.callerid, '0000',
                               _("You have to many promo subscriptions."))
                return Response("Too Many Subscriptions",
                                status=status.HTTP_403_FORBIDDEN)

        # type B: Limit number of subscription for all promos
        elif limit_type == 'B':
            count = PromoSubscription.objects.filter(
                contact=subscriber).count()
            if count >= max_promo_subscription:
                send_sms.delay(subscriber.callerid, '0000',
                               _("You have to many promo subscriptions."))
                return Response("Too Many Subscriptions",
                                status=status.HTTP_403_FORBIDDEN)
        else:
            pass  # proceed as usual

        try:
            promo = Promo.objects.get(keyword__exact=keyword)
        except BaseException:  # bad promo keyword
            send_sms.delay(subscriber.callerid, '0000',
                           _("You made a bad promo request."))
            return Response("Bad promo request",
                            status=status.HTTP_400_BAD_REQUEST)

        # check account balance first
        balance = endaga_sub.get_account_balance(imsi)
        if balance - promo.price < min_balance_required:
            send_sms.delay(
                subscriber.callerid, '0000',
                _("You do not have sufficient balance to subscribe "
                  "to the %s promo.") % promo.keyword)
            return Response("Insufficient balance",
                            status=status.HTTP_402_PAYMENT_REQUIRED)

        # user passes above check, has enough balance, so sign him up!
        new_subscription = PromoSubscription()
        new_subscription.promo = promo
        new_subscription.contact = subscriber
        # save time as UTC in database
        new_subscription.date_expiration = timezone.now() + timedelta(
            promo.validity)
        new_subscription.local_sms = promo.local_sms
        new_subscription.local_call = promo.local_call
        new_subscription.globe_sms = promo.globe_sms
        new_subscription.globe_call = promo.globe_call
        new_subscription.outside_sms = promo.outside_sms
        new_subscription.outside_call = promo.outside_call
        new_subscription.save()

        # finally, deduct promo.price from subscriber's balance
        # price is expressed in millicents
        endaga_sub.subtract_credit(imsi, str(promo.price))

        try:
            tz = Config.objects.get(key='timezone').value
        except Config.DoesNotExist:
            tz = pytz_timezone('Asia/Manila')

        # present time to subscriber according to defined timezone
        expiry = new_subscription.date_expiration.astimezone(tz). \
            strftime("%m/%d/%y %I:%M%p")

        # and then lets inform the subscriber
        send_sms.delay(
            subscriber.callerid, '0000',
            _("You are now subscribed to %(keyword)s valid "
              "until %(expiry)s.") % ({
                  'keyword': promo.keyword,
                  'expiry': expiry
              }))

        # and then create a purge task
        # let's use the PK as the task_id, so that we dont have to create
        # a new field to store the id. this might be OK for now.
        purge_entry.apply_async(eta=new_subscription.date_expiration,
                                args=[new_subscription.pk],
                                task_id=str(new_subscription.pk))

        # we should also create an event
        reason = "Promo subscription: %s" % promo.keyword
        events.create_sms_event(subscriber.imsi, balance, promo.price, reason,
                                '555')

        return Response('OK SUBSCRIBE', status=status.HTTP_200_OK)
Exemple #20
0
def simple_upload(request, template_name='promos/upload_csv.html'):
    csv_subs_len = 2
    csv_promo_len = 12
    fail = False
    promo = True
    contact = True
    page = 'promo_upload_csv'

    if request.method == 'POST':
        form = DocumentForm(request.POST, request.FILES)
        if form.is_valid():
            myfile = request.FILES['docfile']
            actobj = form.cleaned_data.get(
                'action') + form.cleaned_data.get('object')

            reader = csv.reader(io.StringIO(myfile.read().decode('utf-8')))
            headers = next(reader)  # handle row header
            if actobj == '11':  # Create promos
                if len(headers) < csv_promo_len:
                    fail = True
                    alerts.warning(request, _(
                        "Missing required fields. Check your CSV file again."
                    ))
                for row in reader:
                    if len(row) < csv_promo_len:
                        break  # do nothing
                    try:
                        Promo.objects.create(
                            author=request.user,
                            name=row[0],
                            description=row[1],
                            price=row[2],
                            promo_type=row[3],
                            keyword=row[4],
                            validity=row[5],
                            local_sms=row[6],
                            local_call=row[7],
                            globe_sms=row[8],
                            globe_call=row[9],
                            outside_sms=row[10],
                            outside_call=row[11])
                    except Exception as e:
                        fail = True
                        alerts.warning(
                            request, _(
                                'Duplicate entries found in CSV file. %s' %
                                e))

            elif actobj == '12':  # Create promo subscriptions
                if len(headers) < csv_subs_len:
                    fail = True
                    alerts.warning(request, _(
                        "Missing required fields. Check your CSV file again."
                    ))
                for row in reader:
                    if len(row) < csv_subs_len:
                        break
                    try:
                        promo = Promo.objects.get(keyword=row[0])
                    except Promo.DoesNotExist:
                        promo = None
                    try:
                        contact = Contact.objects.get(callerid=row[1])
                    except Contact.DoesNotExist:
                        fail = True
                        contact = None

                    if promo and contact:
                        try:
                            instance = PromoSubscription.objects.create(
                                promo=promo,
                                contact=contact,
                                date_expiration=timezone.now() + timedelta(
                                    promo.validity),
                                local_sms=promo.local_sms,
                                local_call=promo.local_call,
                                globe_sms=promo.globe_sms,
                                globe_call=promo.globe_call,
                                outside_sms=promo.outside_sms,
                                outside_call=promo.outside_call)
                            purge_entry.apply_async(
                                eta=instance.date_expiration,
                                args=[instance.pk],
                                task_id=str(instance.pk))
                            send_sms.delay(
                                contact.callerid,
                                '0000',
                                _(
                                    'You are automatically subscribed to %(promo)s promo '
                                    'valid for %(validity)s day(s). '
                                    'To opt out, text REMOVE %(keyword)s to 555. '
                                    'For more info, text INFO %(keyword)s to 555.') % ({
                                        'promo': promo.name,
                                        'validity': promo.validity,
                                        'keyword': promo.keyword}))
                            # we should also create an event
                            balance = endaga_sub.get_account_balance(
                                contact.imsi)
                            reason = "Promo Auto Subscription: %s" % promo.keyword
                            events.create_sms_event(contact.imsi, balance,
                                                    0, reason, '555')
                        except BaseException:
                            pass

            elif actobj == '21':  # Delete promo
                pass
                # for row in reader:
                #     Promo.objects.filter(keyword=row[6]).delete()
                # # TODO: Do we also delete exisiting subscriptions?
            elif actobj == '22':  # Delete promo subscriptions
                for row in reader:
                    subscriptions = PromoSubscription.objects.filter(
                        promo__keyword__exact=row[0],
                        contact__callerid__exact=row[1])
                    for item in subscriptions:
                        app.control.revoke(str(item.id), terminate=True)
                    subscriptions.delete()
            else:
                pass

            myfile.close()
            if not fail:
                alerts.success(request, _(
                    "Batch operation completed successfully."))
                page = 'promos'
            else:
                if not contact:
                    alerts.warning(
                        request,
                        _("Operation failed. Contact does not exist.")
                    )
                elif not promo:
                    alerts.warning(
                        request,
                        _("Operation failed. Promo does not exist.")
                    )
                else:
                    alerts.warning(
                        request,
                        _("Operation failed. Unknown error.")
                    )
            return redirect(page)
    else:
        form = DocumentForm()
    return render(request, template_name, {'form': form})