Ejemplo n.º 1
0
def handle_event(bts, event, destinations=None):
    """Handles a usage event from a BTS.

    Nothing happens if the event has a lower seqno than the max we've seen
    already (it gets ignored).  Otherwise, we create the UsageEvent, and
    associate it with the relevant subscriber.  Also updates the BTS' max
    seqno if we see a new one.

    Args:
      event: a usage event from the BTS (dict)
      destinations: ???

    Returns:
      the max_seqno
    """
    if event['seq'] <= bts.max_seqno:
        logging.warn("ignoring event (%d) from BTS %s" %
                     (event['seq'], bts.uuid))
        return bts.max_seqno
    try:
        sub = Subscriber.objects.get(imsi=event['imsi'])
    except Subscriber.DoesNotExist:
        logging.warn('Subscriber with IMSI "%s" does not exist.  BTS: %s' %
                     (event['imsi'], bts.uuid))
        return bts.max_seqno
    date = datetime.datetime.strptime(event['date'], '%Y-%m-%d %H:%M:%S')
    # Note that the default timezone should be UTC, no matter what the
    # UserProfile timezone settings are.
    date = django.utils.timezone.make_aware(
        date, django.utils.timezone.get_default_timezone())
    usage_event = UsageEvent(date=date,
                             kind=event['kind'],
                             oldamt=event['oldamt'],
                             newamt=event['newamt'],
                             change=event['change'],
                             reason=event['reason'][:500],
                             subscriber=sub,
                             bts=bts)
    # Try to get a valid call duration.  This either comes from the
    # 'call_duration' key in new events or can be parsed from the reason.
    # If we can't figure it out, just set the default to zero from None.
    # (None is used if the usage event was not a call.)
    duration = None
    if 'sec call' in event['reason'][:500]:
        try:
            duration = int(event['reason'][:500].split()[0])
        except Exception:
            duration = 0
    usage_event.call_duration = event.get('call_duration', duration)
    usage_event.billsec = event.get('billsec', duration)
    usage_event.from_imsi = event.get('from_imsi')
    usage_event.from_number = event.get('from_number')
    usage_event.to_imsi = event.get('to_imsi')
    # Set the to_number and, if there is a to_number, set the Destination.
    usage_event.to_number = event.get('to_number')
    if event.get('to_number', None):
        if not destinations:
            destinations = list(Destination.objects.all())
        usage_event.destination = parse_destination(event.get('to_number'),
                                                    destinations)
    usage_event.tariff = event.get('tariff')
    usage_event.uploaded_bytes = event.get('up_bytes')
    usage_event.downloaded_bytes = event.get('down_bytes')
    usage_event.timespan = event.get('timespan')
    # balance is updated in the subscribers_handler above -kurtis
    bts.max_seqno = event['seq']
    # Bill the operator for local traffic.  Billing for voice occurs in the
    # internal API, and billing for outgoing and incoming SMS occurs near
    # calls to the Nexmo API.
    receive_kinds = ('local_recv_call', 'local_recv_sms')
    send_kinds = ('local_call', 'local_sms')
    if event['kind'] in receive_kinds + send_kinds:
        # The django-pylint plugin is confused below because we define the
        # Network ForeignKey by name (with quotes) instead of by reference.
        # So we'll disable that check.
        # pylint: disable=no-member
        if event['kind'] in receive_kinds:
            directionality = 'on_network_receive'
        elif event['kind'] in send_kinds:
            directionality = 'on_network_send'
        if 'sms' in event['kind']:
            cost = bts.network.calculate_operator_cost(directionality, 'sms')
            bts.network.bill_for_sms(cost, event['kind'])
        elif 'call' in event['kind']:
            billable_seconds = int(event.get('billsec', duration))
            cost = bts.network.calculate_operator_cost(directionality, 'call')
            bts.network.bill_for_call(cost, billable_seconds, event['kind'])
    # Persist.
    usage_event.save()
    sub.save()
    bts.save()
    return bts.max_seqno
Ejemplo n.º 2
0
 def test_four(self):
     phone_number = '5671235551234'
     expected = self.destination_four
     actual = parse_destination(phone_number, self.destinations)
     self.assertEqual(expected, actual)
Ejemplo n.º 3
0
 def test_none(self):
     phone_number = '7891235551234'
     expected = None
     actual = parse_destination(phone_number, self.destinations)
     self.assertEqual(expected, actual)
Ejemplo n.º 4
0
 def test_three(self):
     phone_number = '1231235551234'
     expected = self.destination_three
     actual = parse_destination(phone_number, self.destinations)
     self.assertEqual(expected, actual)