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.")
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))
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)
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)
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)
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()
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
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)
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)
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})
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
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)
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)
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)
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})