def run(self, **kwargs): logger = init_call_retry.get_logger() logger.info("TASK :: init_call_retry") try: # get callrequest which are failed callreq_retry_list = Callrequest.objects.filter(status=2, call_type=1) for callreq in callreq_retry_list: try: # Call type => Retry Done = 3 callreq.call_type = 3 callreq.save() campaign_obj = Campaign.objects.get(id=callreq.campaign_id) if campaign_obj: if callreq.num_attempt >= campaign_obj.maxretry: logger.error("Not allowed retry") break dialer_set = user_dialer_setting(callreq.user) if dialer_set: if callreq.num_attempt >= dialer_set.maxretry: logger.error("Not allowed retry") break except: # Call type => Can Not Retry = 2 callreq.call_type = 2 callreq.save() logger.error("Can't find dialer setting for user of the campaign : %s" \ % callreq.campaign_id) break # TODO : Review Logic # Crete new callrequest, Assign parent_callrequest, Change callrequest_type # & num_attempt obj = Callrequest(request_uuid=uuid1(), parent_callrequest_id=callreq.id, call_type=1, num_attempt=callreq.num_attempt+1, user=callreq.user, campaign_id=callreq.campaign_id, aleg_gateway_id=callreq.aleg_gateway_id, content_type=callreq.content_type, object_id=callreq.object_id, phone_number=callreq.phone_number) obj.save() # TODO : perform retry init_callrequest.delay(obj.id, obj.campaign_id) except: logger.error("Can't find failed callrequest") return False return True
def check_retrycall_completion(callrequest): """ We will check if the callrequest need to be restarted in order to achieve completion """ # Check if subscriber is not completed and check if # subscriber.completion_count_attempt < campaign.completion_maxretry if (callrequest.subscriber.status == SUBSCRIBER_STATUS.COMPLETED or callrequest.subscriber.completion_count_attempt >= callrequest.campaign.completion_maxretry or not callrequest.campaign.completion_maxretry or callrequest.campaign.completion_maxretry == 0): logger.debug("Subscriber completed or limit reached!") else: # Increment subscriber.completion_count_attempt if callrequest.subscriber.completion_count_attempt: callrequest.subscriber.completion_count_attempt = callrequest.subscriber.completion_count_attempt + 1 else: callrequest.subscriber.completion_count_attempt = 1 callrequest.subscriber.save() # TODO: Add method in models.Callrequest to create copy # Init new callrequest -> delay at completion_intervalretry new_callrequest = Callrequest( request_uuid=uuid1(), parent_callrequest_id=callrequest.id, call_type=CALLREQUEST_TYPE.ALLOW_RETRY, num_attempt=callrequest.num_attempt + 1, user=callrequest.user, campaign_id=callrequest.campaign_id, aleg_gateway_id=callrequest.aleg_gateway_id, content_type=callrequest.content_type, object_id=callrequest.object_id, phone_number=callrequest.phone_number, timelimit=callrequest.timelimit, callerid=callrequest.callerid, caller_name=callrequest.caller_name, timeout=callrequest.timeout, content_object=callrequest.content_object, subscriber=callrequest.subscriber) new_callrequest.save() # NOTE : implement a PID algorithm second_towait = callrequest.campaign.completion_intervalretry logger.info("Init Completion Retry CallRequest %d in %d seconds" % (new_callrequest.id, second_towait)) init_callrequest.apply_async(args=[ new_callrequest.id, callrequest.campaign.id, callrequest.campaign.callmaxduration ], countdown=second_towait)
def check_retrycall_completion(callrequest): """ We will check if the callrequest need to be restarted in order to achieve completion """ # Check if subscriber is not completed and check if # subscriber.completion_count_attempt < campaign.completion_maxretry if ( callrequest.subscriber.status == SUBSCRIBER_STATUS.COMPLETED or callrequest.subscriber.completion_count_attempt >= callrequest.campaign.completion_maxretry or not callrequest.campaign.completion_maxretry or callrequest.campaign.completion_maxretry == 0 ): logger.debug("Subscriber completed or limit reached!") else: # Let's Init a new callrequest # Increment subscriber.completion_count_attempt if callrequest.subscriber.completion_count_attempt: callrequest.subscriber.completion_count_attempt = callrequest.subscriber.completion_count_attempt + 1 else: callrequest.subscriber.completion_count_attempt = 1 callrequest.subscriber.save() # init_callrequest -> delay at completion_intervalretry new_callrequest = Callrequest( request_uuid=uuid1(), parent_callrequest_id=callrequest.id, call_type=CALLREQUEST_TYPE.ALLOW_RETRY, num_attempt=callrequest.num_attempt + 1, user=callrequest.user, campaign_id=callrequest.campaign_id, aleg_gateway_id=callrequest.aleg_gateway_id, content_type_id=callrequest.content_type_id, object_id=callrequest.object_id, phone_number=callrequest.phone_number, timelimit=callrequest.timelimit, callerid=callrequest.callerid, timeout=callrequest.timeout, content_object=callrequest.content_object, subscriber=callrequest.subscriber, ) new_callrequest.save() # NOTE : implement a PID algorithm second_towait = callrequest.campaign.completion_intervalretry logger.debug("Init Completion Retry CallRequest in %d seconds" % second_towait) init_callrequest.apply_async( args=[new_callrequest.id, callrequest.campaign.id, callrequest.campaign.callmaxduration], countdown=second_towait, )
def setUp(self): self.user = User.objects.get(username='******') VoipSearchForm(self.user) try: content_type_id = ContentType.objects.get(model='survey').id except: content_type_id = 1 # Callrequest model self.callrequest = Callrequest( call_type=1, status=1, user=self.user, phone_number='123456', subscriber_id=1, campaign_id=1, aleg_gateway_id=1, content_type_id=content_type_id, object_id=1, ) self.callrequest.save() # VoIPCall model self.voipcall = VoIPCall( user=self.user, used_gateway_id=1, callrequest=self.callrequest, callid='Top Gun', phone_number='123456', leg_type=1, duration=20, ) self.voipcall.save() self.assertEqual(self.voipcall.__unicode__(), u'1 - Top Gun') # Test mgt command call_command("create_callrequest_cdr", "1|1") call_command("create_callrequest_cdr", "3|1")
def setUp(self): self.user = User.objects.get(username='******') VoipSearchForm(self.user) try: content_type_id = ContentType.objects.get(model='survey').id except: content_type_id = 1 # Callrequest model self.callrequest = Callrequest( call_type=1, status=1, user=self.user, phone_number='123456', subscriber_id=1, campaign_id=1, aleg_gateway_id=1, content_type_id=content_type_id, object_id=1, ) self.callrequest.save() # VoIPCall model self.voipcall = VoIPCall( user=self.user, used_gateway_id=1, callrequest=self.callrequest, callid='Top Gun', phone_number='123456', leg_type=1, duration=20, ) self.voipcall.save() self.assertEqual(self.voipcall.__unicode__(), u'2 - Top Gun') # Test mgt command call_command("create_callrequest_cdr", "1|1") call_command("create_callrequest_cdr", "3|1")
def run(self, **kwargs): logger.info("TASK :: alarmrequest_dispatcher") # Select AlarmRequest where date >= now() - 60 minutes start_time = datetime.utcnow().replace(tzinfo=utc) + relativedelta(minutes=-60) alarmreq_list = AlarmRequest.objects.filter(date__gte=start_time, status=ALARMREQUEST_STATUS.PENDING) no_alarmreq = alarmreq_list.count() if no_alarmreq == 0: logger.error("alarmrequest_dispatcher - no alarmreq found!") return False # Set time to wait for balanced dispatching of calls #time_to_wait = int(60 / DIV_MIN) / no_subscriber time_to_wait = 6.0 / no_alarmreq count = 0 # Browse all the AlarmRequest found for obj_alarmreq in alarmreq_list: # Loop on AlarmRequest and start to the initcall's task count = count + 1 second_towait = floor(count * time_to_wait) ms_addtowait = (count * time_to_wait) - second_towait logger.info("Init CallRequest for AlarmRequest in %d seconds (alarmreq:%d)" % (second_towait, obj_alarmreq.id)) if obj_alarmreq.alarm.maxretry == 0: call_type = CALLREQUEST_TYPE.CANNOT_RETRY else: call_type = CALLREQUEST_TYPE.ALLOW_RETRY try: caluser_profile = CalendarUserProfile.objects.get(user=obj_alarmreq.alarm.event.creator) except CalendarUserProfile.DoesNotExist: logger.error("Error retrieving CalendarUserProfile") return False #manager_profile = UserProfile.objects.get(user=caluser_profile.manager) # manager_profile = caluser_profile.manager.get_profile() # manager_profile.dialersetting # Use manager_profile.dialersetting to retrieve some settings # TODO: build settings for this calltimeout = caluser_profile.calendar_setting.call_timeout callmaxduration = 60 * 60 callerid = caluser_profile.calendar_setting.callerid caller_name = caluser_profile.calendar_setting.caller_name aleg_gateway = caluser_profile.calendar_setting.aleg_gateway content_type = ContentType.objects.get(model__in=["survey"]) object_id = caluser_profile.calendar_setting.survey_id # Create Callrequest to track the call task new_callrequest = Callrequest( status=CALLREQUEST_STATUS.PENDING, call_type=call_type, call_time=datetime.utcnow().replace(tzinfo=utc), timeout=calltimeout, callerid=callerid, caller_name=caller_name, phone_number=obj_alarmreq.alarm.alarm_phonenumber, alarm_request_id=obj_alarmreq.id, aleg_gateway=aleg_gateway, content_type=content_type, object_id=object_id, user=caluser_profile.manager, extra_data='', timelimit=callmaxduration) new_callrequest.save() init_callrequest.apply_async( args=[new_callrequest.id, None, callmaxduration, ms_addtowait, obj_alarmreq.id], countdown=second_towait) obj_alarmreq.callrequest = new_callrequest obj_alarmreq.status = ALARMREQUEST_STATUS.IN_PROCESS obj_alarmreq.save() # Increment num_attempt obj_alarmreq.alarm.num_attempt = obj_alarmreq.alarm.num_attempt + 1 obj_alarmreq.alarm.save()
def process_callevent(record): """ Process the callevent, this tasks will: - Retrieve the callrequest using either callrequest_id or request_uuid - create the voipcall, and save different data """ # TODO: add method in utils parse_callevent app_type = 'campaign' event_name = record[1] body = record[2] job_uuid = record[3] call_uuid = record[4] # used_gateway_id = record[5] callrequest_id = record[6] alarm_request_id = record[7] callerid = record[8] phonenumber = record[9] duration = record[10] billsec = record[11] hangup_cause = record[12] hangup_cause_q850 = record[13] starting_date = record[14] amd_status = record[17] leg = record[18] if event_name == 'BACKGROUND_JOB': # hangup cause come from body hangup_cause = body[5:] if hangup_cause == '': hangup_cause = body[5:] request_uuid = job_uuid opt_hangup_cause = hangup_cause debug_query(22) try: if callrequest_id == 0: callrequest = Callrequest.objects \ .select_related('aleg_gateway', 'subscriber', 'campaign') \ .get(request_uuid=request_uuid.strip(' \t\n\r')) else: # mainly coming here callrequest = Callrequest.objects \ .select_related('aleg_gateway', 'subscriber', 'campaign') \ .get(id=callrequest_id) except: logger.error("Cannot find Callrequest job_uuid : %s" % job_uuid) return True if callrequest.alarm_request_id: app_type = 'alarm' alarm_req = AlarmRequest.objects.get(pk=callrequest.alarm_request_id) # Overwrite alarm_request_id as this is equal to 0 when call fails alarm_request_id = callrequest.alarm_request_id logger.debug("Find Callrequest id : %d" % callrequest.id) debug_query(23) if leg == 'aleg' and app_type == 'campaign': # Update callrequest # update_callrequest.delay(callrequest, opt_hangup_cause) # Disabled above tasks to reduce amount of tasks # Only the aleg will update the subscriber status / Bleg is only recorded # Update Callrequest Status if opt_hangup_cause == 'NORMAL_CLEARING': callrequest.status = CALLREQUEST_STATUS.SUCCESS if callrequest.subscriber.status != SUBSCRIBER_STATUS.COMPLETED: callrequest.subscriber.status = SUBSCRIBER_STATUS.SENT else: callrequest.status = CALLREQUEST_STATUS.FAILURE callrequest.subscriber.status = SUBSCRIBER_STATUS.FAIL callrequest.hangup_cause = opt_hangup_cause # ... callrequest.save() callrequest.subscriber.save() debug_query(24) elif leg == 'aleg' and app_type == 'alarm': try: caluser_profile = CalendarUserProfile.objects.get( user=alarm_req.alarm.event.creator) except CalendarUserProfile.DoesNotExist: logger.error("Error retrieving CalendarUserProfile") return False if opt_hangup_cause == 'NORMAL_CLEARING' and \ amd_status == 'machine' and \ caluser_profile.calendar_setting.voicemail and \ caluser_profile.calendar_setting.amd_behavior == AMD_BEHAVIOR.HUMAN_ONLY: # Call reached AMD callrequest.status = CALLREQUEST_STATUS.FAILURE alarm_req.status = ALARMREQUEST_STATUS.FAILURE alarm_req.alarm.status = ALARM_STATUS.FAILURE elif opt_hangup_cause == 'NORMAL_CLEARING': # Call is successful callrequest.status = CALLREQUEST_STATUS.SUCCESS alarm_req.status = ALARMREQUEST_STATUS.SUCCESS alarm_req.duration = duration alarm_req.alarm.status = ALARM_STATUS.SUCCESS else: # Call failed callrequest.status = CALLREQUEST_STATUS.FAILURE alarm_req.status = ALARMREQUEST_STATUS.FAILURE alarm_req.alarm.status = ALARM_STATUS.FAILURE callrequest.hangup_cause = opt_hangup_cause callrequest.save() alarm_req.save() alarm_req.alarm.save() debug_query(24) if call_uuid == '': call_uuid = job_uuid if callerid == '': callerid = callrequest.callerid if phonenumber == '': phonenumber = callrequest.phone_number # Create those in Bulk - add in a buffer until reach certain number # buff_voipcall.save( # obj_callrequest=callrequest, # request_uuid=request_uuid, # leg=leg, # hangup_cause=opt_hangup_cause, # hangup_cause_q850=hangup_cause_q850, # callerid=callerid, # phonenumber=phonenumber, # starting_date=starting_date, # call_uuid=call_uuid, # duration=duration, # billsec=billsec, # amd_status=amd_status) # debug_query(25) voipcall_save(callrequest=callrequest, request_uuid=request_uuid, leg=leg, hangup_cause=opt_hangup_cause, hangup_cause_q850=hangup_cause_q850, callerid=callerid, phonenumber=phonenumber, starting_date=starting_date, call_uuid=call_uuid, duration=duration, billsec=billsec, amd_status=amd_status) # If the call failed we will check if we want to make a retry call # Add condition to retry when it s machine and we want to reach a human if (app_type == 'campaign' and opt_hangup_cause != 'NORMAL_CLEARING' and callrequest.call_type == CALLREQUEST_TYPE.ALLOW_RETRY) or \ (app_type == 'campaign' and amd_status == 'machine' and callrequest.campaign.voicemail and callrequest.campaign.amd_behavior == AMD_BEHAVIOR.HUMAN_ONLY): # Update to Retry Done callrequest.call_type = CALLREQUEST_TYPE.RETRY_DONE callrequest.save() debug_query(26) # check if we are allowed to retry on failure if ((callrequest.subscriber.count_attempt - 1) >= callrequest.campaign.maxretry or not callrequest.campaign.maxretry): logger.error("Not allowed retry - Maxretry (%d)" % callrequest.campaign.maxretry) # Check here if we should try for completion check_retrycall_completion(callrequest) debug_query(28) else: # Allowed Retry logger.error("Allowed Retry - Maxretry (%d)" % callrequest.campaign.maxretry) # Create new callrequest, Assign parent_callrequest, # Change callrequest_type & num_attempt new_callrequest = Callrequest( request_uuid=uuid1(), parent_callrequest_id=callrequest.id, call_type=CALLREQUEST_TYPE.ALLOW_RETRY, num_attempt=callrequest.num_attempt + 1, user=callrequest.user, campaign_id=callrequest.campaign_id, aleg_gateway_id=callrequest.aleg_gateway_id, content_type=callrequest.content_type, object_id=callrequest.object_id, phone_number=callrequest.phone_number, timelimit=callrequest.timelimit, callerid=callrequest.callerid, timeout=callrequest.timeout, subscriber_id=callrequest.subscriber_id) new_callrequest.save() # NOTE : implement a PID algorithm second_towait = callrequest.campaign.intervalretry debug_query(29) logger.debug("Init Retry CallRequest in %d seconds" % second_towait) init_callrequest.apply_async(args=[ new_callrequest.id, callrequest.campaign.id, callrequest.campaign.callmaxduration ], countdown=second_towait) elif app_type == 'campaign': # The Call is Answered and it's a campaign call logger.info("Check for completion call") # Check if we should relaunch a new call to achieve completion check_retrycall_completion(callrequest) elif (opt_hangup_cause != 'NORMAL_CLEARING' and app_type == 'alarm') or \ (amd_status == 'machine' and app_type == 'alarm' and caluser_profile.calendar_setting.voicemail and caluser_profile.calendar_setting.amd_behavior == AMD_BEHAVIOR.HUMAN_ONLY): # Alarm callrequest failed or Alarm callrequest reached voicemail logger.info("Check to retry alarm") check_retry_alarm(alarm_request_id) else: logger.info("Retry: No matching conditions")
def run(self, campaign_id): """ This task retrieves the next outbound call to be made for a given campaign, and will create a new callrequest and schedule a task to process those calls **Attributes**: * ``campaign_id`` - Campaign ID """ logger = self.get_logger() logger.info("TASK :: pending_call_processing = %d" % campaign_id) debug_query(0) try: obj_campaign = Campaign.objects\ .select_related('user__userprofile__dialersetting', 'aleg_gateway', 'content_type')\ .get(id=campaign_id) except: logger.error("Can't find this campaign") return False # TODO : Control the Speed # if there is many task pending we should slow down frequency = obj_campaign.frequency # default 10 calls per minutes debug_query(1) #TODO: move this logic of setting call_type after CallRequest post_save # Default call_type call_type = CALLREQUEST_TYPE.ALLOW_RETRY # Check campaign's maxretry if obj_campaign.maxretry == 0: call_type = CALLREQUEST_TYPE.CANNOT_RETRY # Check user's dialer setting maxretry try: obj_campaign.user.userprofile.dialersetting if obj_campaign.user.userprofile.dialersetting.maxretry == 0: call_type = CALLREQUEST_TYPE.CANNOT_RETRY except ObjectDoesNotExist: logger.error("Can't find user's dialersetting") return False debug_query(2) # Speed # Check if the other tasks send for this campaign finished to be ran # Get the subscriber of this campaign # get_pending_subscriber get Max 1000 records if settings.HEARTBEAT_MIN == 1: # 1 task per minute callfrequency = frequency # task run only once per minute, so we can assign frequency else: callfrequency = int(frequency / settings.HEARTBEAT_MIN) + 1 # 1000 per minutes #callfrequency = int(frequency) + 1 # 1000 per minutes (list_subscriber, no_subscriber) = obj_campaign\ .get_pending_subscriber_update(callfrequency, SUBSCRIBER_STATUS.IN_PROCESS) logger.info("##subscriber=%d campaign_id=%d callfreq=%d freq=%d" % (no_subscriber, campaign_id, callfrequency, frequency)) debug_query(3) if no_subscriber == 0: return False # Set time to wait for balanced dispatching of calls time_to_wait = (60.0 / settings.HEARTBEAT_MIN) / no_subscriber count = 0 loopnow = datetime.utcnow() loopnow + timedelta(seconds=1.55) for elem_camp_subscriber in list_subscriber: # Loop on Subscriber and start the initcall's task count = count + 1 second_towait = floor(count * time_to_wait) # ms_addtowait now used anymore, replaced by async eta ms_addtowait = (count * time_to_wait) - second_towait eta_delta = loopnow + timedelta(seconds=(count * time_to_wait)) # as we use eta_delta ms_addtowait is set to 0 ms_addtowait = 0 logger.info("Init CallRequest in %d seconds (cmpg:%d,subscr:%d:eta_delta:%s)" % (second_towait, campaign_id, elem_camp_subscriber.id, eta_delta)) phone_number = elem_camp_subscriber.duplicate_contact debug_query(4) #Verify that the contact is authorized if not obj_campaign.is_authorized_contact(obj_campaign.user.userprofile.dialersetting, phone_number): logger.error("Error : Contact not authorized") elem_camp_subscriber.status = SUBSCRIBER_STATUS.NOT_AUTHORIZED elem_camp_subscriber.save() continue #Verify that the contact is not in the DNC list if obj_campaign.dnc: res_dnc = DNCContact.objects.filter(dnc_id=obj_campaign.dnc_id, phone_number=phone_number) if res_dnc: logger.error("Contact (%s) in DNC list" % phone_number) elem_camp_subscriber.status = SUBSCRIBER_STATUS.NOT_AUTHORIZED elem_camp_subscriber.save() continue else: logger.debug("Contact (%s) not in DNC list" % phone_number) debug_query(5) #TODO: idea to speed up, create bluck of 10(Y) and then send a list # of callrequest_id to init_callrequest # Create Callrequest new_callrequest = Callrequest( status=CALLREQUEST_STATUS.PENDING, call_type=call_type, call_time=datetime.utcnow().replace(tzinfo=utc), timeout=obj_campaign.calltimeout, callerid=obj_campaign.callerid, caller_name=obj_campaign.caller_name, phone_number=phone_number, campaign=obj_campaign, aleg_gateway=obj_campaign.aleg_gateway, content_type=obj_campaign.content_type, object_id=obj_campaign.object_id, user=obj_campaign.user, extra_data=obj_campaign.extra_data, timelimit=obj_campaign.callmaxduration, subscriber=elem_camp_subscriber) new_callrequest.save() debug_query(6) second_towait = second_towait + settings.DELAY_OUTBOUND init_callrequest.apply_async( args=[new_callrequest.id, obj_campaign.id, obj_campaign.callmaxduration, ms_addtowait], # countdown=second_towait) eta=eta_delta) # Shell_plus # from dialer_cdr.tasks import init_callrequest # from datetime import datetime # new_callrequest_id = 112 # obj_campaign_id = 3 # countdown = 1 # init_callrequest.apply_async(args=[new_callrequest.id, obj_campaign.id, obj_campaign.callmaxduration, ms_addtowait], countdown=1) debug_query(7) return True
class DialerCdrModel(TestCase): """Test Callrequest, VoIPCall models""" fixtures = ['auth_user.json', 'gateway.json', 'dialer_setting.json', 'user_profile.json', 'phonebook.json', 'contact.json', 'dnc_list.json', 'dnc_contact.json', 'survey.json', 'campaign.json', 'subscriber.json', 'callrequest.json', 'voipcall.json', 'user_profile.json'] def setUp(self): self.user = User.objects.get(username='******') VoipSearchForm(self.user) try: content_type_id = ContentType.objects.get(model='survey').id except: content_type_id = 1 # Callrequest model self.callrequest = Callrequest( call_type=1, status=1, user=self.user, phone_number='123456', subscriber_id=1, campaign_id=1, aleg_gateway_id=1, content_type_id=content_type_id, object_id=1, ) self.callrequest.save() # VoIPCall model self.voipcall = VoIPCall( user=self.user, used_gateway_id=1, callrequest=self.callrequest, callid='Top Gun', phone_number='123456', leg_type=1, duration=20, ) self.voipcall.save() self.assertEqual(self.voipcall.__unicode__(), u'2 - Top Gun') # Test mgt command call_command("create_callrequest_cdr", "1|1") call_command("create_callrequest_cdr", "3|1") def test_name(self): self.assertEqual(self.callrequest.phone_number, "123456") #self.assertEqual(self.callrequest.__unicode__(), u'Top Gun') self.assertEqual(self.voipcall.phone_number, "123456") Callrequest.objects.get_pending_callrequest() self.voipcall.destination_name() self.voipcall.duration = '' self.voipcall.min_duration() self.voipcall.duration = 12 self.voipcall.min_duration() def teardown(self): self.callrequest.delete() self.voipcall.delete()
def create(self, request=None, **kwargs): """POST method of Hangupcall API""" logger.debug('Hangupcall API authentication called!') auth_result = self._meta.authentication.is_authenticated(request) if not auth_result is True: raise ImmediateHttpResponse(response=http.HttpUnauthorized()) auth_result = self._meta.authorization.is_authorized(request, object) errors = self._meta.validation.is_valid(request) if not errors: opt_request_uuid = request.POST.get('RequestUUID') opt_hangup_cause = request.POST.get('HangupCause') try: callrequest = Callrequest.objects.get( request_uuid=opt_request_uuid) except: logger.debug('Hangupcall Error cannot find the Callrequest!') try: obj_subscriber = CampaignSubscriber.objects.get( id=callrequest.campaign_subscriber.id) if opt_hangup_cause == 'NORMAL_CLEARING': obj_subscriber.status = 5 # Complete else: obj_subscriber.status = 4 # Fail obj_subscriber.save() except: logger.debug('Hangupcall Error cannot find the ' 'Campaignsubscriber!') # 2 / FAILURE ; 3 / RETRY ; 4 / SUCCESS if opt_hangup_cause == 'NORMAL_CLEARING': callrequest.status = 4 # Success else: callrequest.status = 2 # Failure callrequest.hangup_cause = opt_hangup_cause #save callrequest & campaignsubscriber callrequest.save() data = {} for element in CDR_VARIABLES: if not request.POST.get('variable_%s' % element): data[element] = None else: data[element] = request.POST.get('variable_%s' % element) from_plivo = request.POST.get('From') to_plivo = request.POST.get('To') create_voipcall(obj_callrequest=callrequest, plivo_request_uuid=opt_request_uuid, data=data, data_prefix='', leg='a', hangup_cause=opt_hangup_cause, from_plivo=from_plivo, to_plivo=to_plivo) object_list = [{'result': 'OK'}] logger.debug('Hangupcall API : Result 200!') obj = CustomXmlEmitter() #We will manage the retry directly from the API if opt_hangup_cause != 'NORMAL_CLEARING'\ and callrequest.call_type == 1: # Allow retry #Update to Retry Done callrequest.call_type = 3 callrequest.save() dialer_set = user_dialer_setting(callrequest.user) if callrequest.num_attempt >= callrequest.campaign.maxretry\ or callrequest.num_attempt >= dialer_set.maxretry: logger.error("Not allowed retry - Maxretry (%d)" %\ callrequest.campaign.maxretry) else: #Allowed Retry # TODO : Review Logic # Create new callrequest, Assign parent_callrequest, # Change callrequest_type & num_attempt new_callrequest = Callrequest( request_uuid=uuid1(), parent_callrequest_id=callrequest.id, call_type=1, num_attempt=callrequest.num_attempt + 1, user=callrequest.user, campaign_id=callrequest.campaign_id, aleg_gateway_id=callrequest.aleg_gateway_id, content_type=callrequest.content_type, object_id=callrequest.object_id, phone_number=callrequest.phone_number) new_callrequest.save() #Todo Check if it's a good practice #implement a PID algorithm second_towait = callrequest.campaign.intervalretry launch_date = datetime.now() + \ timedelta(seconds=second_towait) logger.info("Init Retry CallRequest at %s" %\ (launch_date.strftime("%b %d %Y %I:%M:%S"))) init_callrequest.apply_async( args=[new_callrequest.id, callrequest.campaign.id], eta=launch_date) return self.create_response(request, obj.render(request, object_list)) else: if len(errors): if request: desired_format = self.determine_format(request) else: desired_format = self._meta.default_format serialized = self.serialize(request, errors, desired_format) response = http.HttpBadRequest(content=serialized, content_type=desired_format) raise ImmediateHttpResponse(response=response)
class DialerCdrModel(TestCase): """Test Callrequest, VoIPCall models""" fixtures = [ 'auth_user.json', 'gateway.json', 'dialer_setting.json', 'contenttype.json', 'user_profile.json', 'phonebook.json', 'contact.json', 'dnc_list.json', 'dnc_contact.json', 'campaign.json', 'subscriber.json', 'callrequest.json', 'survey.json', 'section.json' ] def setUp(self): self.user = User.objects.get(username='******') VoipSearchForm(self.user) try: content_type_id = ContentType.objects.get(model='survey').id except: content_type_id = 1 # Callrequest model self.callrequest = Callrequest( call_type=1, status=1, user=self.user, phone_number='123456', subscriber_id=1, campaign_id=1, aleg_gateway_id=1, content_type_id=content_type_id, object_id=1, ) self.callrequest.save() # VoIPCall model self.voipcall = VoIPCall( user=self.user, used_gateway_id=1, callrequest=self.callrequest, callid='Top Gun', phone_number='123456', leg_type=1, duration=20, ) self.voipcall.save() self.assertEqual(self.voipcall.__unicode__(), u'1 - Top Gun') # Test mgt command call_command("create_callrequest_cdr", "1|1") call_command("create_callrequest_cdr", "3|1") def test_name(self): self.assertEqual(self.callrequest.phone_number, "123456") #self.assertEqual(self.callrequest.__unicode__(), u'Top Gun') self.assertEqual(self.voipcall.phone_number, "123456") Callrequest.objects.get_pending_callrequest() self.voipcall.destination_name() self.voipcall.duration = '' self.voipcall.min_duration() self.voipcall.duration = 12 self.voipcall.min_duration() def teardown(self): self.callrequest.delete() self.voipcall.delete()
def run(self, campaign_id): """ This task retrieves the next outbound call to be made for a given campaign, and will create a new callrequest and schedule a task to process those calls **Attributes**: * ``campaign_id`` - Campaign ID """ logger = self.get_logger() logger.info("TASK :: pending_call_processing = %d" % campaign_id) debug_query(0) try: obj_campaign = Campaign.objects\ .select_related('user__userprofile__dialersetting', 'aleg_gateway', 'content_type')\ .get(id=campaign_id) except: logger.error("Can't find this campaign") return False # Ensure the content_type become "survey" when campagin starts if not obj_campaign.has_been_started: # change has_been_started flag obj_campaign.has_been_started = True obj_campaign.save() if obj_campaign.content_type.model == 'survey_template': # Copy survey survey_template = Survey_template.objects.get( user=obj_campaign.user, pk=obj_campaign.object_id) survey_template.copy_survey_template(obj_campaign.id) collect_subscriber.delay(obj_campaign.id) # TODO : Control the Speed # if there is many task pending we should slow down frequency = obj_campaign.frequency # default 10 calls per minutes debug_query(1) # TODO: move this logic of setting call_type after CallRequest post_save # Default call_type call_type = CALLREQUEST_TYPE.ALLOW_RETRY # Check campaign's maxretry if obj_campaign.maxretry == 0: call_type = CALLREQUEST_TYPE.CANNOT_RETRY # Check user's dialer setting maxretry try: obj_campaign.user.userprofile.dialersetting if obj_campaign.user.userprofile.dialersetting.maxretry == 0: call_type = CALLREQUEST_TYPE.CANNOT_RETRY except ObjectDoesNotExist: logger.error("Can't find user's dialersetting") return False debug_query(2) # Speed # Check if the other tasks send for this campaign finished to be ran # Get the subscriber of this campaign # get_pending_subscriber get Max 1000 records if settings.HEARTBEAT_MIN == 1: # 1 task per minute callfrequency = frequency # task run only once per minute, so we can assign frequency else: callfrequency = int( frequency / settings.HEARTBEAT_MIN) + 1 # 1000 per minutes # callfrequency = int(frequency) + 1 # 1000 per minutes (list_subscriber, no_subscriber) = obj_campaign\ .get_pending_subscriber_update(callfrequency, SUBSCRIBER_STATUS.IN_PROCESS) logger.info("##subscriber=%d campaign_id=%d callfreq=%d freq=%d" % (no_subscriber, campaign_id, callfrequency, frequency)) debug_query(3) if no_subscriber == 0: return False list_cr = [] bulk_record = [] # this is used to tag and retrieve the id that are inserted bulk_uuid = str(uuid1()) for elem_camp_subscriber in list_subscriber: phone_number = elem_camp_subscriber.duplicate_contact debug_query(4) # Verify that the contact is authorized if not obj_campaign.is_authorized_contact( obj_campaign.user.userprofile.dialersetting, phone_number): logger.error("Error : Contact not authorized") elem_camp_subscriber.status = SUBSCRIBER_STATUS.NOT_AUTHORIZED elem_camp_subscriber.save() continue # Verify that the contact is not in the DNC list if obj_campaign.dnc: res_dnc = DNCContact.objects.filter(dnc_id=obj_campaign.dnc_id, phone_number=phone_number) if res_dnc: logger.error("Contact (%s) in DNC list" % phone_number) elem_camp_subscriber.status = SUBSCRIBER_STATUS.NOT_AUTHORIZED elem_camp_subscriber.save() continue else: logger.debug("Contact (%s) not in DNC list" % phone_number) debug_query(5) bulk_record.append( Callrequest(status=CALLREQUEST_STATUS.PENDING, call_type=call_type, call_time=datetime.utcnow().replace(tzinfo=utc), timeout=obj_campaign.calltimeout, callerid=obj_campaign.callerid, caller_name=obj_campaign.caller_name, phone_number=phone_number, campaign=obj_campaign, aleg_gateway=obj_campaign.aleg_gateway, content_type=obj_campaign.content_type, object_id=obj_campaign.object_id, user=obj_campaign.user, extra_data=obj_campaign.extra_data, timelimit=obj_campaign.callmaxduration, subscriber=elem_camp_subscriber, request_uuid=bulk_uuid)) debug_query(6) # Create Callrequests in Bulk logger.info("Bulk Create CallRequest => %d" % (len(bulk_record))) Callrequest.objects.bulk_create(bulk_record) # Set time to wait for balanced dispatching of calls time_to_wait = (60.0 / settings.HEARTBEAT_MIN) / no_subscriber count = 0 loopnow = datetime.utcnow() loopnow + timedelta(seconds=1.55) # Retrienve the one we just created list_cr = Callrequest.objects.filter(request_uuid=bulk_uuid).all() for cr in list_cr: # Loop on Subscriber and start the initcall's task count = count + 1 second_towait = floor(count * time_to_wait) # ms_addtowait now used anymore, replaced by async eta ms_addtowait = (count * time_to_wait) - second_towait eta_delta = loopnow + timedelta(seconds=(count * time_to_wait)) # as we use eta_delta ms_addtowait is set to 0 ms_addtowait = 0 logger.info( "Init CallRequest in %d seconds (cmpg:%d,subscr:%d:eta_delta:%s)" % (second_towait, campaign_id, elem_camp_subscriber.id, eta_delta)) init_callrequest.apply_async( args=[ cr.id, obj_campaign.id, obj_campaign.callmaxduration, ms_addtowait ], # countdown=second_towait) eta=eta_delta) second_towait = second_towait + settings.DELAY_OUTBOUND # Shell_plus # from dialer_cdr.tasks import init_callrequest # from datetime import datetime # new_callrequest_id = 112 # obj_campaign_id = 3 # countdown = 1 # init_callrequest.apply_async( # args=[new_callrequest.id, obj_campaign.id, obj_campaign.callmaxduration, ms_addtowait], # countdown=1) debug_query(7) return True
def handle_callevent(record): """ Handle the callevent, create the voipcall, and save different data """ event_name = record[1] body = record[2] job_uuid = record[3] call_uuid = record[4] #used_gateway_id = record[5] callrequest_id = record[6] callerid = record[7] phonenumber = record[8] duration = record[9] billsec = record[10] hangup_cause = record[11] hangup_cause_q850 = record[12] starting_date = record[13] amd_status = record[16] leg = record[17] if event_name == 'BACKGROUND_JOB': #hangup cause come from body hangup_cause = body[5:] # if event_name == 'CHANNEL_HANGUP_COMPLETE': # #hangup cause come from body # print(event_name) if hangup_cause == '': hangup_cause = body[5:] request_uuid = job_uuid opt_hangup_cause = hangup_cause debug_query(22) try: if callrequest_id == 0: callrequest = Callrequest.objects\ .select_related('aleg_gateway', 'subscriber', 'campaign')\ .get(request_uuid=request_uuid.strip(' \t\n\r')) else: #mainly coming here callrequest = Callrequest.objects\ .select_related('aleg_gateway', 'subscriber', 'campaign')\ .get(id=callrequest_id) except: logger.error("Cannot find Callrequest job_uuid : %s" % job_uuid) return True logger.debug("Find Callrequest id : %d" % callrequest.id) debug_query(23) if leg == 'aleg': #Update callrequest #update_callrequest.delay(callrequest, opt_hangup_cause) #Disabled above tasks to reduce amount of tasks #Only the aleg will update the subscriber status / Bleg is only recorded #Update Callrequest Status if opt_hangup_cause == 'NORMAL_CLEARING': callrequest.status = CALLREQUEST_STATUS.SUCCESS if callrequest.subscriber.status != SUBSCRIBER_STATUS.COMPLETED: callrequest.subscriber.status = SUBSCRIBER_STATUS.SENT else: callrequest.status = CALLREQUEST_STATUS.FAILURE callrequest.subscriber.status = SUBSCRIBER_STATUS.FAIL callrequest.hangup_cause = opt_hangup_cause callrequest.save() callrequest.subscriber.save() debug_query(24) if call_uuid == '': call_uuid = job_uuid if callerid == '': callerid = callrequest.callerid if phonenumber == '': phonenumber = callrequest.phone_number #Create those in Bulk - add in a buffer until reach certain number # buff_voipcall.save( # obj_callrequest=callrequest, # request_uuid=request_uuid, # leg=leg, # hangup_cause=opt_hangup_cause, # hangup_cause_q850=hangup_cause_q850, # callerid=callerid, # phonenumber=phonenumber, # starting_date=starting_date, # call_uuid=call_uuid, # duration=duration, # billsec=billsec, # amd_status=amd_status) # debug_query(25) voipcall_save.delay(callrequest=callrequest, request_uuid=request_uuid, leg=leg, hangup_cause=opt_hangup_cause, hangup_cause_q850=hangup_cause_q850, callerid=callerid, phonenumber=phonenumber, starting_date=starting_date, call_uuid=call_uuid, duration=duration, billsec=billsec, amd_status=amd_status) #TODO: Move this to tasks? #If the call failed we will check if we want to make a retry call #Add condition to retry when it s machine and we want to reach a human if (opt_hangup_cause != 'NORMAL_CLEARING' and callrequest.call_type == CALLREQUEST_TYPE.ALLOW_RETRY) or \ (amd_status == 'machine' and callrequest.campaign.voicemail and callrequest.campaign.amd_behavior == AMD_BEHAVIOR.HUMAN_ONLY): #Update to Retry Done callrequest.call_type = CALLREQUEST_TYPE.RETRY_DONE callrequest.save() debug_query(26) #check if we are allowed to retry on failure if ((callrequest.subscriber.count_attempt - 1) >= callrequest.campaign.maxretry or not callrequest.campaign.maxretry): logger.error("Not allowed retry - Maxretry (%d)" % callrequest.campaign.maxretry) #Check here if we should try for completion check_retrycall_completion(callrequest) debug_query(28) else: #Allowed Retry logger.error("Allowed Retry - Maxretry (%d)" % callrequest.campaign.maxretry) # Create new callrequest, Assign parent_callrequest, # Change callrequest_type & num_attempt new_callrequest = Callrequest( request_uuid=uuid1(), parent_callrequest_id=callrequest.id, call_type=CALLREQUEST_TYPE.ALLOW_RETRY, num_attempt=callrequest.num_attempt + 1, user=callrequest.user, campaign_id=callrequest.campaign_id, aleg_gateway_id=callrequest.aleg_gateway_id, content_type=callrequest.content_type, object_id=callrequest.object_id, phone_number=callrequest.phone_number, timelimit=callrequest.timelimit, callerid=callrequest.callerid, timeout=callrequest.timeout, subscriber_id=callrequest.subscriber_id) new_callrequest.save() #NOTE : implement a PID algorithm second_towait = callrequest.campaign.intervalretry debug_query(29) logger.debug("Init Retry CallRequest in %d seconds" % second_towait) init_callrequest.apply_async(args=[ new_callrequest.id, callrequest.campaign.id, callrequest.campaign.callmaxduration ], countdown=second_towait) else: #The Call is Answered logger.debug("Check for completion call") #Check if we should relaunch a new call to achieve completion check_retrycall_completion(callrequest)
def check_campaign_pendingcall(campaign_id): """This will execute the outbound calls in the campaign **Attributes**: * ``campaign_id`` - Campaign ID """ logger = check_campaign_pendingcall.get_logger() logger.info("TASK :: check_campaign_pendingcall = %s" % str(campaign_id)) try: obj_campaign = Campaign.objects.get(id=campaign_id) except: logger.error('Can\'t find this campaign') return False #TODO: Control the Speed #if there is many task pending we should slow down frequency = obj_campaign.frequency # default 10 calls per minutes dialer_set = user_dialer_setting(obj_campaign.user) # default call_type call_type = 1 # Check campaign's maxretry if obj_campaign.maxretry == 0: call_type = 2 # Check user's dialer setting maxretry if dialer_set: if dialer_set.maxretry == 0: call_type = 2 # check frequency to control the Speed #if dialer_set.frequency: # frequency = 20 #Speed #check if the other tasks send for this campaign finished to be ran #Get the subscriber of this campaign # get_pending_subscriber get Max 1000 records list_subscriber = obj_campaign.get_pending_subscriber_update( frequency, 6 # Update to In Process ) if list_subscriber: logger.debug("Number of subscriber found : %d" % len(list_subscriber)) try: no_subscriber = list_subscriber.count() except AttributeError: no_subscriber = 0 if no_subscriber == 0: logger.info("No Subscriber to proceed on this campaign") return False #find how to dispatch them in the current minutes time_to_wait = 60.0 / no_subscriber count = 0 for elem_camp_subscriber in list_subscriber: """Loop on Subscriber and start the initcall task""" count = count + 1 logger.info("Add CallRequest for Subscriber (%s) & wait (%s) " % (str(elem_camp_subscriber.id), str(time_to_wait))) #Check if the contact is authorized if not obj_campaign.is_authorized_contact( elem_camp_subscriber.contact.contact): logger.error("Error : Contact not authorized") elem_camp_subscriber.status = 7 # Update to Not Authorized elem_camp_subscriber.save() return True #Create a Callrequest Instance to track the call task new_callrequest = Callrequest( status=1, # PENDING call_type=call_type, call_time=datetime.now(), timeout=obj_campaign.calltimeout, callerid=obj_campaign.callerid, phone_number=elem_camp_subscriber.contact.contact, campaign=obj_campaign, aleg_gateway=obj_campaign.aleg_gateway, content_type=obj_campaign.content_type, object_id=obj_campaign.object_id, user=obj_campaign.user, extra_data=obj_campaign.extra_data, timelimit=obj_campaign.callmaxduration, campaign_subscriber=elem_camp_subscriber) new_callrequest.save() #Todo Check if it's a good practice / implement a PID algorithm second_towait = ceil(count * time_to_wait) launch_date = datetime.now() + timedelta(seconds=second_towait) logger.info("Init CallRequest at %s" % \ (launch_date.strftime("%b %d %Y %I:%M:%S"))) init_callrequest.apply_async( args=[new_callrequest.id, obj_campaign.id], eta=launch_date)
def run(self, campaign_id): """ This will execute the outbound calls in the campaign **Attributes**: * ``campaign_id`` - Campaign ID """ logger = self.get_logger() logger.info("TASK :: spool_pending_call = %d" % campaign_id) debug_query(0) try: obj_campaign = Campaign.objects.select_related( 'user__userprofile__dialersetting', 'aleg_gateway', 'content_type').get(id=campaign_id) except: logger.error("Can't find this campaign") return False # TODO : Control the Speed # if there is many task pending we should slow down frequency = obj_campaign.frequency # default 10 calls per minutes debug_query(1) # Default call_type call_type = CALLREQUEST_TYPE.ALLOW_RETRY # Check campaign's maxretry if obj_campaign.maxretry == 0: call_type = CALLREQUEST_TYPE.CANNOT_RETRY # Check user's dialer setting maxretry if obj_campaign.user.userprofile.dialersetting: if obj_campaign.user.userprofile.dialersetting.maxretry == 0: call_type = CALLREQUEST_TYPE.CANNOT_RETRY debug_query(2) # Speed # Check if the other tasks send for this campaign finished to be ran # Get the subscriber of this campaign # get_pending_subscriber get Max 1000 records if frequency >= 10: callfrequency = int( frequency / DIV_MIN) + 1 # 1000 per minutes 101 #callfrequency = int(frequency) + 1 # 1000 per minutes 101 else: callfrequency = frequency (list_subscriber, no_subscriber) = obj_campaign.get_pending_subscriber_update( callfrequency, SUBSCRIBER_STATUS.IN_PROCESS) logger.info( "##subscriber=%d campaign_id=%d callfrequency=%d frequency=%d" % (no_subscriber, campaign_id, callfrequency, frequency)) debug_query(3) if no_subscriber == 0: return False # Set time to wait for balanced dispatching of calls #time_to_wait = int(60 / DIV_MIN) / no_subscriber time_to_wait = 6.0 / no_subscriber count = 0 for elem_camp_subscriber in list_subscriber: """Loop on Subscriber and start the initcall task""" count = count + 1 second_towait = floor(count * time_to_wait) ms_addtowait = (count * time_to_wait) - second_towait logger.info( "Init CallRequest in %d seconds (cmpg:%d,subscriber:%d)" % (second_towait, campaign_id, elem_camp_subscriber.id)) phone_number = elem_camp_subscriber.duplicate_contact debug_query(4) #Verify that the contact is authorized if not obj_campaign.is_authorized_contact( obj_campaign.user.userprofile.dialersetting, phone_number): logger.error("Error : Contact not authorized") elem_camp_subscriber.status = SUBSCRIBER_STATUS.NOT_AUTHORIZED elem_camp_subscriber.save() return True #Verify that the contact is not in the DNC list if obj_campaign.dnc: res_dnc = DNCContact.objects.filter(dnc_id=obj_campaign.dnc_id, phone_number=phone_number) if res_dnc: logger.error("Contact (%s) in DNC list" % phone_number) elem_camp_subscriber.status = SUBSCRIBER_STATUS.NOT_AUTHORIZED elem_camp_subscriber.save() return True else: logger.debug("Contact (%s) not in DNC list" % phone_number) debug_query(5) #TODO: idea to speed up, create bluck of 10(Y) and then send a list of callrequest_id to init_callrequest # Create a Callrequest Instance to track the call task new_callrequest = Callrequest( status=CALLREQUEST_STATUS.PENDING, call_type=call_type, call_time=datetime.now(), timeout=obj_campaign.calltimeout, callerid=obj_campaign.callerid, phone_number=phone_number, campaign=obj_campaign, aleg_gateway=obj_campaign.aleg_gateway, content_type=obj_campaign.content_type, object_id=obj_campaign.object_id, user=obj_campaign.user, extra_data=obj_campaign.extra_data, timelimit=obj_campaign.callmaxduration, subscriber=elem_camp_subscriber) new_callrequest.save() debug_query(6) init_callrequest.apply_async(args=[ new_callrequest.id, obj_campaign.id, obj_campaign.callmaxduration, ms_addtowait ], countdown=second_towait) # Shell_plus # from dialer_cdr.tasks import init_callrequest # from datetime import datetime # new_callrequest_id = 112 # obj_campaign_id = 3 # countdown = 1 # init_callrequest.apply_async(args=[new_callrequest_id, obj_campaign_id], countdown=1) debug_query(7) return True