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
def test_four(self): phone_number = '5671235551234' expected = self.destination_four actual = parse_destination(phone_number, self.destinations) self.assertEqual(expected, actual)
def test_none(self): phone_number = '7891235551234' expected = None actual = parse_destination(phone_number, self.destinations) self.assertEqual(expected, actual)
def test_three(self): phone_number = '1231235551234' expected = self.destination_three actual = parse_destination(phone_number, self.destinations) self.assertEqual(expected, actual)