def __init__(self, user, *args, **kwargs): super(DuplicateCampaignForm, self).__init__(*args, **kwargs) self.helper = FormHelper() self.helper.form_tag = False css_class = 'col-md-12' self.helper.layout = Layout( Field('campaign_code'), Div(Div('name', css_class=css_class), Div('phonebook', css_class=css_class), css_class='row')) self.fields['campaign_code'].initial = get_unique_code(length=5) if user: phonebook_list = get_phonebook_list(user) self.fields['phonebook'].choices = phonebook_list self.fields['phonebook'].initial = str(phonebook_list[0][0])
def __init__(self, user, *args, **kwargs): super(DuplicateCampaignForm, self).__init__(*args, **kwargs) self.helper = FormHelper() self.helper.form_tag = False css_class = 'col-md-12' self.helper.layout = Layout( Field('campaign_code'), Div( Div('name', css_class=css_class), Div('phonebook', css_class=css_class), css_class='row' ) ) self.fields['campaign_code'].initial = get_unique_code(length=5) if user: phonebook_list = get_phonebook_list(user) self.fields['phonebook'].choices = phonebook_list self.fields['phonebook'].initial = str(phonebook_list[0][0])
def set_campaign_code(): return get_unique_code(length=5)
def __init__(self, user, *args, **kwargs): super(CampaignForm, self).__init__(*args, **kwargs) self.user = user self.helper = FormHelper() if self.instance.id: form_action = common_submit_buttons(default_action='update') else: form_action = common_submit_buttons(default_action='add') week_days = ['monday', 'tuesday', 'wednesday', 'thursday', 'friday', 'saturday', 'sunday'] week_days_html = """<div class="row"><div class="col-md-12 col-xs-6">""" for i in week_days: week_days_html += """ <div class="col-md-3"> <div class="btn-group" data-toggle="buttons"> <label for="{{ form.%s.auto_id }}">{{ form.%s.label }}</label><br/> <div class="make-switch switch-small"> {{ form.%s }} </div> </div> </div> """ % (i, i, i) week_days_html += """</div></div>""" css_class = 'col-md-6' self.helper.layout = Layout( Field('campaign_code'), TabHolder( Tab(_('general').title(), Div( Div(Fieldset(_('general settings').title()), css_class='col-md-12'), Div('name', css_class=css_class), Div('callerid', css_class=css_class), Div('caller_name', css_class=css_class), Div('content_object', css_class=css_class), css_class='row' ), Div( Div('extra_data', css_class=css_class), Div('dnc', css_class=css_class), Div('description', css_class=css_class), Div('phonebook', css_class=css_class), css_class='row' ), form_action, css_class='well' ), Tab('Dialer', Div( Div(Fieldset(_('dialer settings').title()), css_class='col-md-12'), Div('aleg_gateway', css_class=css_class), Div('frequency', css_class=css_class), Div('callmaxduration', css_class=css_class), Div('maxretry', css_class=css_class), Div('intervalretry', css_class=css_class), Div('calltimeout', css_class=css_class), Div(Fieldset(_('dialer completion settings').title()), css_class='col-md-12'), Div('completion_maxretry', css_class=css_class), Div('completion_intervalretry', css_class=css_class), Div('sms_gateway', css_class=css_class), css_class='row' ), form_action, css_class='well' ), Tab('schedule', Div( Div(Fieldset(_('schedule settings').title()), css_class='col-md-12'), Div(HTML("""<label>%s<label>""" % (_('week days').capitalize())), css_class="col-md-3"), HTML(week_days_html), HTML("""<div> </div>"""), Div('startingdate', css_class=css_class), Div('expirationdate', css_class=css_class), Div('daily_start_time', css_class=css_class), Div('daily_stop_time', css_class=css_class), css_class='row' ), form_action, css_class='well' ), ), ) if settings.AMD: amd_layot = Tab(_('voicemail').capitalize(), Div( Div(Fieldset(_('voicemail settings').title()), css_class='col-md-12'), Div(HTML(""" <div class="btn-group" data-toggle="buttons"> <label for="{{ form.voicemail.auto_id }}">{{ form.voicemail.label }}</label> <br/> <div class="make-switch switch-small"> {{ form.voicemail }} </div> </div> """), css_class='col-md-12 col-xs-10'), HTML("""<div> </div>"""), Div('amd_behavior', css_class=css_class), Div('voicemail_audiofile', css_class=css_class), css_class='row' ), form_action, css_class='well' ) self.helper.layout[1].insert(2, amd_layot) # hidden var self.helper.layout.append(Field('selected_phonebook')) self.helper.layout.append(Field('selected_content_object')) instance = getattr(self, 'instance', None) self.fields['campaign_code'].initial = get_unique_code(length=5) if user: list_gw = [] dnc_list = [] phonebook_list = get_phonebook_list(user) if not phonebook_list: phonebook_list = [] phonebook_list.append(('', '---')) self.fields['phonebook'].choices = phonebook_list self.fields['phonebook'].initial = str(phonebook_list[0][0]) gateway_list = UserProfile.objects.get(user=user).userprofile_gateway.all() gw_list = ((l.id, l.name) for l in gateway_list) dnc_list.append(('', '---')) dnc_obj_list = DNC.objects.values_list('id', 'name').filter(user=user).order_by('id') for l in dnc_obj_list: dnc_list.append((l[0], l[1])) self.fields['dnc'].choices = dnc_list for i in gw_list: list_gw.append((i[0], i[1])) self.fields['aleg_gateway'].choices = UserProfile.objects.get(user=user)\ .userprofile_gateway.all().values_list('id', 'name') if instance.has_been_duplicated: from survey.models import Survey available_objects = Survey.objects.filter(user=user, campaign=instance) object_choices = get_object_choices(available_objects) self.fields['content_object'].widget.attrs['readonly'] = True else: from survey.models import Survey_template available_objects = Survey_template.objects.filter(user=user) object_choices = get_object_choices(available_objects) self.fields['content_object'].choices = object_choices # Voicemail setting is not enabled by default if settings.AMD: from survey.forms import get_audiofile_list self.fields['voicemail_audiofile'].choices = get_audiofile_list(user) # If campaign is running or has been started if instance.status == CAMPAIGN_STATUS.START or instance.has_been_started: self.fields['name'].widget.attrs['readonly'] = True self.fields['caller_name'].widget.attrs['readonly'] = True self.fields['callerid'].widget.attrs['readonly'] = True self.fields['extra_data'].widget.attrs['readonly'] = True self.fields['phonebook'].widget.attrs['readonly'] = True self.fields['lead_disposition'].widget.attrs['readonly'] = True self.fields['dnc'].widget.attrs['readonly'] = True self.fields['aleg_gateway'].widget.attrs['readonly'] = True self.fields['sms_gateway'].widget.attrs['readonly'] = True self.fields['voicemail'].widget.attrs['readonly'] = True self.fields['amd_behavior'].widget.attrs['readonly'] = True self.fields['voicemail_audiofile'].widget.attrs['readonly'] = True selected_phonebook = '' if instance.phonebook.all(): selected_phonebook = ",".join(["%s" % (i.id) for i in instance.phonebook.all()]) self.fields['selected_phonebook'].initial = selected_phonebook self.fields['content_object'].widget.attrs['disabled'] = 'disabled' self.fields['content_object'].required = False self.fields['selected_content_object'].initial = "type:%s-id:%s" % \ (instance.content_type.id, instance.object_id)
def __init__(self, *args, **kwargs): super(CampaignAdminForm, self).__init__(*args, **kwargs) self.fields['campaign_code'].widget.attrs['readonly'] = True self.fields['campaign_code'].initial = get_unique_code(length=5)
class Campaign(Model): """This defines the Campaign **Attributes**: * ``campaign_code`` - Auto-generated campaign code to identify the campaign * ``name`` - Campaign name * ``description`` - Description about the Campaign * ``status`` - Campaign status * ``callerid`` - Caller ID * ``startingdate`` - Starting date of the Campaign * ``expirationdate`` - Expiry date of the Campaign * ``daily_start_time`` - Start time * ``daily_stop_time`` - End time * ``week_day_setting`` (monday, tuesday, wednesday, thursday, friday, \ saturday, sunday) * ``frequency`` - Frequency, speed of the campaign. number of calls/min * ``callmaxduration`` - Max retry allowed per user * ``maxretry`` - Max retry allowed per user * ``intervalretry`` - Time to wait between retries in seconds * ``completion_maxretry`` - Number of retries until a contact completes survey * ``completion_intervalretry`` - Time delay in seconds before retrying contact \ to complete survey * ``calltimeout`` - Number of seconds to timeout on calls * ``aleg_gateway`` - Gateway to use to reach the contact * ``extra_data`` - Additional data to pass to the application * ``totalcontact`` - Total Contact for this campaign * ``completed`` - Total Contact that completed Call / Survey * ``has_been_started`` - campaign started flag * ``has_been_duplicated`` - campaign duplicated flag * ``voicemail`` - Enable Voicemail Detection * ``amd_behavior`` - Detection Behaviour * ``sms_gateway`` - Gateway to transport the SMS **Relationships**: * ``content_type`` - Defines the application (``survey``) \ to use when the call is established on the A-Leg * ``object_id`` - Defines the object of content_type application * ``content_object`` - Used to define the Voice App or the Survey with generic ForeignKey * ``phonebook`` - Many-To-Many relationship to the Phonebook model. * ``user`` - Foreign key relationship to the a User model. \ Each campaign assigned to a User * ``voicemail_audiofile`` - Foreign key relationship to the a AudioFile model. * ``dnc`` - Foreign key relationship to the a DNC model. **Name of DB table**: dialer_campaign """ campaign_code = models.CharField( unique=True, max_length=20, blank=True, verbose_name=_("campaign code"), help_text=_('this code is auto-generated by the platform, this is used to identify the campaign'), default=(lambda: get_unique_code(length=5))) name = models.CharField(max_length=100, verbose_name=_('name')) description = models.TextField(verbose_name=_('description'), blank=True, null=True, help_text=_("campaign description")) user = models.ForeignKey('auth.User', related_name='Campaign owner') status = models.IntegerField(choices=list(CAMPAIGN_STATUS), default=CAMPAIGN_STATUS.PAUSE, verbose_name=_("status"), blank=True, null=True) callerid = models.CharField(max_length=80, blank=True, verbose_name=_("Caller ID Number"), help_text=_("outbound Caller ID")) caller_name = models.CharField(max_length=80, blank=True, verbose_name=_("Caller Name"), help_text=_("outbound Caller Name")) #General Starting & Stopping date startingdate = models.DateTimeField(default=(lambda: datetime.utcnow().replace(tzinfo=utc)), verbose_name=_('start')) expirationdate = models.DateTimeField( default=(lambda: datetime.utcnow().replace(tzinfo=utc) + relativedelta(days=+1)), verbose_name=_('finish')) #Per Day Starting & Stopping Time daily_start_time = models.TimeField(default='00:00:00', verbose_name=_('daily start time')) daily_stop_time = models.TimeField(default='23:59:59', verbose_name=_('daily stop time')) monday = models.BooleanField(default=True, verbose_name=_('monday')) tuesday = models.BooleanField(default=True, verbose_name=_('tuesday')) wednesday = models.BooleanField(default=True, verbose_name=_('wednesday')) thursday = models.BooleanField(default=True, verbose_name=_('thursday')) friday = models.BooleanField(default=True, verbose_name=_('friday')) saturday = models.BooleanField(default=True, verbose_name=_('saturday')) sunday = models.BooleanField(default=True, verbose_name=_('sunday')) #Campaign Settings frequency = models.IntegerField(default='10', blank=True, null=True, verbose_name=_('frequency'), help_text=_("calls per minute")) callmaxduration = models.IntegerField(default='1800', blank=True, null=True, verbose_name=_('max call duration'), help_text=_("maximum call duration in seconds")) #max retry on failure - Note that the answered call not completed are counted maxretry = models.IntegerField(default='0', blank=True, null=True, verbose_name=_('max retries'), help_text=_("maximum retries per contact")) intervalretry = models.IntegerField(default='300', blank=True, null=True, verbose_name=_('time between retries'), help_text=_("time delay in seconds before retrying contact")) completion_maxretry = models.IntegerField(default='0', blank=True, null=True, verbose_name=_('completion max retries'), help_text=_("number of retries until a contact completes survey")) completion_intervalretry = models.IntegerField( default='900', blank=True, null=True, verbose_name=_('completion time between retries'), help_text=_("time delay in seconds before retrying contact to complete survey")) calltimeout = models.IntegerField(default='45', blank=True, null=True, verbose_name=_('timeout on call'), help_text=_("connection timeout in seconds")) aleg_gateway = models.ForeignKey(Gateway, verbose_name=_("A-Leg gateway"), related_name="A-Leg Gateway", help_text=_("select outbound gateway")) sms_gateway = models.ForeignKey(SMS_Gateway, verbose_name=_("sms gateway"), null=True, blank=True, related_name="campaign_sms_gateway", help_text=_("select SMS gateway")) content_type = models.ForeignKey(ContentType, verbose_name=_("type"), limit_choices_to={"model__in": ["survey_template", "survey"]}) object_id = models.PositiveIntegerField(verbose_name=_("application")) content_object = generic.GenericForeignKey('content_type', 'object_id') extra_data = models.CharField(max_length=120, blank=True, verbose_name=_("extra parameters"), help_text=_("additional application parameters.")) phonebook = models.ManyToManyField(Phonebook, blank=True, null=True) imported_phonebook = models.CharField(max_length=500, default='', blank=True, verbose_name=_('imported phonebook')) totalcontact = models.IntegerField(default=0, blank=True, null=True, verbose_name=_('total contact'), help_text=_("total contact for this campaign")) completed = models.IntegerField(default=0, blank=True, null=True, verbose_name=_('completed'), help_text=_("total contact that completed call / survey")) #Flags has_been_started = models.BooleanField(default=False, verbose_name=_('has been started')) has_been_duplicated = models.BooleanField(default=False, verbose_name=_('has been duplicated')) dnc = models.ForeignKey(DNC, null=True, blank=True, verbose_name=_("DNC"), help_text=_("do not call list"), related_name='DNC') #Voicemail Detection voicemail = models.BooleanField(default=False, verbose_name=_('voicemail detection')) amd_behavior = models.IntegerField(choices=list(AMD_BEHAVIOR), blank=True, null=True, default=AMD_BEHAVIOR.ALWAYS, verbose_name=_("detection behaviour")) voicemail_audiofile = models.ForeignKey(AudioFile, null=True, blank=True, verbose_name=_("voicemail audio file")) #Callcenter agent_script = models.TextField(verbose_name=_('agent script'), blank=True, null=True) lead_disposition = models.TextField(verbose_name=_('lead disposition'), blank=True, null=True) external_link = jsonfield.JSONField( null=True, blank=True, verbose_name=_('additional parameters (JSON)'), help_text=_("enter the list of parameters in Json format, e.g. {\"title\": [\"tab-1\", \"tab-2\"], \"url\": [\"https://duckduckgo.com/\", \"http://www.newfies-dialer.org/\"]}")) created_date = models.DateTimeField(auto_now_add=True, verbose_name=_('date')) updated_date = models.DateTimeField(auto_now=True) objects = CampaignManager() def __unicode__(self): return u"%s" % (self.name) class Meta: permissions = ( ("view_campaign", _('can see campaign')), ("view_dashboard", _('can see campaign dashboard')) ) db_table = u'dialer_campaign' verbose_name = _("campaign") verbose_name_plural = _("campaigns") def update_campaign_status(self): """Update the campaign's status For example, If campaign is active, you can change status to 'Pause' or 'Stop' """ if self.status == CAMPAIGN_STATUS.START: return "<a href='%s'>Pause</a> | <a href='%s'>Abort</a> | <a href='%s'>Stop</a>" % \ (reverse('dialer_campaign.views.update_campaign_status_admin', args=[self.pk, CAMPAIGN_STATUS.PAUSE]), reverse('dialer_campaign.views.update_campaign_status_admin', args=[self.pk, CAMPAIGN_STATUS.ABORT]), reverse('dialer_campaign.views.update_campaign_status_admin', args=[self.pk, CAMPAIGN_STATUS.END])) if self.status == CAMPAIGN_STATUS.PAUSE: return "<a href='%s'>Start</a> | <a href='%s'>Abort</a> | <a href='%s'>Stop</a>" % \ (reverse('dialer_campaign.views.update_campaign_status_admin', args=[self.pk, CAMPAIGN_STATUS.START]), reverse('dialer_campaign.views.update_campaign_status_admin', args=[self.pk, CAMPAIGN_STATUS.ABORT]), reverse('dialer_campaign.views.update_campaign_status_admin', args=[self.pk, CAMPAIGN_STATUS.END])) if self.status == CAMPAIGN_STATUS.ABORT: return "<a href='%s'>Start</a> | <a href='%s'>Pause</a> | <a href='%s'>Stop</a>" % \ (reverse('dialer_campaign.views.update_campaign_status_admin', args=[self.pk, CAMPAIGN_STATUS.START]), reverse('dialer_campaign.views.update_campaign_status_admin', args=[self.pk, CAMPAIGN_STATUS.PAUSE]), reverse('dialer_campaign.views.update_campaign_status_admin', args=[self.pk, CAMPAIGN_STATUS.END])) if self.status == CAMPAIGN_STATUS.END: return "<a href='%s'>Start</a> | <a href='%s'>Pause</a> | <a href='%s'>Abort</a>" % \ (reverse('dialer_campaign.views.update_campaign_status_admin', args=[self.pk, CAMPAIGN_STATUS.START]), reverse('dialer_campaign.views.update_campaign_status_admin', args=[self.pk, CAMPAIGN_STATUS.PAUSE]), reverse('dialer_campaign.views.update_campaign_status_admin', args=[self.pk, CAMPAIGN_STATUS.ABORT])) update_campaign_status.allow_tags = True update_campaign_status.short_description = _('action') def is_authorized_contact(self, dialersetting, str_contact): """Check if a contact is authorized""" return common_contact_authorization(dialersetting, str_contact) def get_campaign_type(self): """Get campaign type""" if self.content_type.model == 'survey': return ugettext('survey') return ugettext('voice app') def get_active_max_frequency(self): """Get the active max frequency""" try: obj_userprofile = UserProfile.objects.get(user=self.user) except UserProfile.DoesNotExist: return self.frequency max_frequency = obj_userprofile.dialersetting.max_frequency if max_frequency < self.frequency: return max_frequency return self.frequency def get_active_callmaxduration(self): """Get the active call max duration""" try: obj_userprofile = UserProfile.objects.get(user=self.user) except UserProfile.DoesNotExist: return self.frequency callmaxduration = obj_userprofile.dialersetting.callmaxduration if callmaxduration < self.callmaxduration: return callmaxduration return self.callmaxduration def get_active_contact(self): """Get all the active Contacts from the phonebook""" list_contact = Contact.objects.filter(phonebook__campaign=self.id, status=CONTACT_STATUS.ACTIVE).all() if not list_contact: return False return list_contact def progress_bar(self): """Progress bar generated based on no of contacts""" # Cache subscriber_count count_contact = Contact.objects.filter(phonebook__campaign=self.id).count() # Cache need to be set per campaign # subscriber_count_key_campaign_id_1 subscriber_count = cache.get('subscriber_count_key_campaign_id_' + str(self.id)) if subscriber_count is None: list_contact = Contact.objects.values_list('id', flat=True)\ .filter(phonebook__campaign=self.id) subscriber_count = 0 try: subscriber_count += Subscriber.objects\ .filter(contact__in=list_contact, campaign=self.id, status=SUBSCRIBER_STATUS.SENT)\ .count() except: pass cache.set("subscriber_count_key_campaign_id_%s" % str(self.id), subscriber_count, 5) subscriber_count = int(subscriber_count) count_contact = int(count_contact) if count_contact > 0: percentage_pixel = int(percentage(subscriber_count, count_contact)) else: percentage_pixel = 0 subscriber_count_string = "subscribers (" + str(subscriber_count) + ")" return "<div title='%s' style='width: 100px; border: 1px solid #ccc;'><div style='height: 4px; width: %dpx; background: #555; '></div></div>" % \ (subscriber_count_string, percentage_pixel) progress_bar.allow_tags = True progress_bar.short_description = _('progress') def subscriber_detail(self): """This will link to subscribers who are associated with the campaign""" model_name = Subscriber._meta.object_name.lower() app_label = self._meta.app_label link = '/admin/%s/%s/' % (app_label, model_name) link += '?campaign__id=%d' % self.id display_link = _("<a href='%(link)s'>%(name)s</a>") % {'link': link, 'name': _('details')} return display_link subscriber_detail.allow_tags = True subscriber_detail.short_description = _('subscriber') # OPTIMIZATION - GOOD @transaction.atomic def get_pending_subscriber_update(self, limit, status): """Get all the pending subscribers from the campaign""" #TODO: Improve this part with a PL/SQL #We cannot use select_related here as it's not compliant with locking the rows list_subscriber = Subscriber.objects.select_for_update()\ .filter(campaign=self.id, status=SUBSCRIBER_STATUS.PENDING)\ .all()[:limit] if not list_subscriber: return (False, 0) id_list_sb = [] count = 0 for elem_subscriber in list_subscriber: count = count + 1 id_list_sb.append(elem_subscriber.id) #Update in bulk Subscriber.objects.filter(id__in=id_list_sb).update(status=status) return (list_subscriber, count)
def __init__(self, *args, **kwargs): super(CampaignAdminForm, self).__init__(*args, **kwargs) self.fields['campaign_code'].widget.attrs['readonly'] = True self.fields['campaign_code'].initial = get_unique_code(length=5)
def __init__(self, user, *args, **kwargs): super(CampaignForm, self).__init__(*args, **kwargs) self.user = user self.helper = FormHelper() if self.instance.id: form_action = common_submit_buttons(default_action='update') else: form_action = common_submit_buttons(default_action='add') week_days = [ 'monday', 'tuesday', 'wednesday', 'thursday', 'friday', 'saturday', 'sunday' ] week_days_html = """<div class="row"><div class="col-md-12 col-xs-6">""" for i in week_days: week_days_html += """ <div class="col-md-3"> <div class="btn-group" data-toggle="buttons"> <label for="{{ form.%s.auto_id }}">{{ form.%s.label }}</label><br/> <div class="make-switch switch-small"> {{ form.%s }} </div> </div> </div> """ % (i, i, i) week_days_html += """</div></div>""" css_class = 'col-md-6' self.helper.layout = Layout( Field('campaign_code'), TabHolder( Tab(_('general').title(), Div(Div(Fieldset(_('general settings').title()), css_class='col-md-12'), Div('name', css_class=css_class), Div('callerid', css_class=css_class), Div('caller_name', css_class=css_class), Div('content_object', css_class=css_class), css_class='row'), Div(Div('extra_data', css_class=css_class), Div('dnc', css_class=css_class), Div('description', css_class=css_class), Div('phonebook', css_class=css_class), css_class='row'), form_action, css_class='well'), Tab('Dialer', Div(Div(Fieldset(_('dialer settings').title()), css_class='col-md-12'), Div('aleg_gateway', css_class=css_class), Div('frequency', css_class=css_class), Div('callmaxduration', css_class=css_class), Div('maxretry', css_class=css_class), Div('intervalretry', css_class=css_class), Div('calltimeout', css_class=css_class), Div(Fieldset(_('dialer completion settings').title()), css_class='col-md-12'), Div('completion_maxretry', css_class=css_class), Div('completion_intervalretry', css_class=css_class), Div('sms_gateway', css_class=css_class), css_class='row'), form_action, css_class='well'), Tab('schedule', Div(Div(Fieldset(_('schedule settings').title()), css_class='col-md-12'), Div(HTML("""<label>%s<label>""" % (_('week days').capitalize())), css_class="col-md-3"), HTML(week_days_html), HTML("""<div> </div>"""), Div('startingdate', css_class=css_class), Div('expirationdate', css_class=css_class), Div('daily_start_time', css_class=css_class), Div('daily_stop_time', css_class=css_class), css_class='row'), form_action, css_class='well'), ), ) if settings.AMD: amd_layot = Tab(_('voicemail').capitalize(), Div(Div(Fieldset(_('voicemail settings').title()), css_class='col-md-12'), Div(HTML(""" <div class="btn-group" data-toggle="buttons"> <label for="{{ form.voicemail.auto_id }}">{{ form.voicemail.label }}</label> <br/> <div class="make-switch switch-small"> {{ form.voicemail }} </div> </div> """), css_class='col-md-12 col-xs-10'), HTML("""<div> </div>"""), Div('amd_behavior', css_class=css_class), Div('voicemail_audiofile', css_class=css_class), css_class='row'), form_action, css_class='well') self.helper.layout[1].insert(2, amd_layot) # hidden var self.helper.layout.append(Field('selected_phonebook')) self.helper.layout.append(Field('selected_content_object')) instance = getattr(self, 'instance', None) self.fields['campaign_code'].initial = get_unique_code(length=5) if user: list_gw = [] dnc_list = [] phonebook_list = get_phonebook_list(user) if not phonebook_list: phonebook_list = [] phonebook_list.append(('', '---')) self.fields['phonebook'].choices = phonebook_list self.fields['phonebook'].initial = str(phonebook_list[0][0]) gateway_list = UserProfile.objects.get( user=user).userprofile_gateway.all() gw_list = ((l.id, l.name) for l in gateway_list) dnc_list.append(('', '---')) dnc_obj_list = DNC.objects.values_list( 'id', 'name').filter(user=user).order_by('id') for l in dnc_obj_list: dnc_list.append((l[0], l[1])) self.fields['dnc'].choices = dnc_list for i in gw_list: list_gw.append((i[0], i[1])) self.fields['aleg_gateway'].choices = UserProfile.objects.get(user=user)\ .userprofile_gateway.all().values_list('id', 'name') if instance.has_been_duplicated: from survey.models import Survey available_objects = Survey.objects.filter(user=user, campaign=instance) object_choices = get_object_choices(available_objects) self.fields['content_object'].widget.attrs['readonly'] = True else: from survey.models import Survey_template available_objects = Survey_template.objects.filter(user=user) object_choices = get_object_choices(available_objects) self.fields['content_object'].choices = object_choices # Voicemail setting is not enabled by default if settings.AMD: from survey.forms import get_audiofile_list self.fields[ 'voicemail_audiofile'].choices = get_audiofile_list(user) # If campaign is running or has been started if instance.status == CAMPAIGN_STATUS.START or instance.has_been_started: self.fields['name'].widget.attrs['readonly'] = True self.fields['caller_name'].widget.attrs['readonly'] = True self.fields['callerid'].widget.attrs['readonly'] = True self.fields['extra_data'].widget.attrs['readonly'] = True self.fields['phonebook'].widget.attrs['readonly'] = True self.fields['lead_disposition'].widget.attrs['readonly'] = True self.fields['dnc'].widget.attrs['readonly'] = True self.fields['aleg_gateway'].widget.attrs['readonly'] = True self.fields['sms_gateway'].widget.attrs['readonly'] = True self.fields['voicemail'].widget.attrs['readonly'] = True self.fields['amd_behavior'].widget.attrs['readonly'] = True self.fields['voicemail_audiofile'].widget.attrs['readonly'] = True selected_phonebook = '' if instance.phonebook.all(): selected_phonebook = ",".join( ["%s" % (i.id) for i in instance.phonebook.all()]) self.fields['selected_phonebook'].initial = selected_phonebook self.fields['content_object'].widget.attrs['disabled'] = 'disabled' self.fields['content_object'].required = False self.fields['selected_content_object'].initial = "type:%s-id:%s" % \ (instance.content_type.id, instance.object_id)
def set_campaign_code(): return get_unique_code(length=5)
class SMSCampaign(Model): """This defines the SMSCampaign **Attributes**: * ``campaign_code`` - Auto-generated campaign code to identify the campaign * ``name`` - Campaign name * ``description`` - Description about the Campaign * ``status`` - Campaign status * ``callerid`` - Caller ID * ``startingdate`` - Starting date of the Campaign * ``expirationdate`` - Expiry date of the Campaign * ``daily_start_time`` - Start time * ``daily_stop_time`` - End time * ``week_day_setting`` (monday, tuesday, wednesday, thursday, friday, \ saturday, sunday) * ``frequency`` - Frequency, speed of the campaign. number of calls/min * ``maxretry`` - Max retry allowed per user * ``intervalretry`` - Time to wait between retries in seconds * ``sms_gateway`` - Gateway to transport the SMS * ``extra_data`` - Additional data to pass to the application **Relationships**: * ``content_type`` - Defines the application (``voice_app`` or ``survey``) \ to use when the call is established on the A-Leg * ``object_id`` - Defines the object of content_type application * ``content_object`` - Used to define the Voice App or the Survey with generic ForeignKey * ``phonebook`` - Many-To-Many relationship to the Phonebook model. * ``user`` - Foreign key relationship to the a User model. \ Each campaign assigned to a User **Name of DB table**: sms_campaign """ campaign_code = models.CharField( unique=True, max_length=20, blank=True, verbose_name=_("SMS campaign code"), help_text=_('this code is auto-generated by the platform, \ this is used to identify the campaign'), default=(lambda: get_unique_code(length=5))) name = models.CharField(max_length=100, verbose_name=_('name')) description = models.TextField(verbose_name=_('description'), blank=True, null=True, help_text=_("campaign description")) user = models.ForeignKey('auth.User', related_name='SMSCampaign owner') status = models.IntegerField(choices=list(SMS_CAMPAIGN_STATUS), default=SMS_CAMPAIGN_STATUS.PAUSE, verbose_name=_("status")) callerid = models.CharField(max_length=80, blank=True, verbose_name=_("Caller ID Number"), help_text=_("outbound Caller ID")) #General Starting & Stopping date startingdate = models.DateTimeField( default=(lambda: datetime.utcnow().replace(tzinfo=utc)), verbose_name=_('start')) expirationdate = models.DateTimeField(default=(lambda: datetime.utcnow( ).replace(tzinfo=utc) + relativedelta(months=+1)), verbose_name=_('finish')) #Per Day Starting & Stopping Time daily_start_time = models.TimeField(default='00:00:00') daily_stop_time = models.TimeField(default='23:59:59') monday = models.BooleanField(default=True, verbose_name=_('monday')) tuesday = models.BooleanField(default=True, verbose_name=_('tuesday')) wednesday = models.BooleanField(default=True, verbose_name=_('wednesday')) thursday = models.BooleanField(default=True, verbose_name=_('thursday')) friday = models.BooleanField(default=True, verbose_name=_('friday')) saturday = models.BooleanField(default=True, verbose_name=_('saturday')) sunday = models.BooleanField(default=True, verbose_name=_('sunday')) #Campaign Settings frequency = models.IntegerField(default='10', blank=True, null=True, verbose_name=_('frequency'), help_text=_("SMS per minute")) maxretry = models.IntegerField(default='0', blank=True, null=True, verbose_name=_('max retries'), help_text=_("maximum retries per contact")) intervalretry = models.IntegerField( default='300', blank=True, null=True, verbose_name=_('time between Retries'), help_text=_("time delay in seconds before retrying contact")) sms_gateway = models.ForeignKey(Gateway, verbose_name=_("sms gateway"), related_name="SMS Gateway", help_text=_("select SMS gateway")) text_message = models.TextField(verbose_name=_('text Message'), blank=False, null=False) extra_data = models.CharField( max_length=120, blank=True, verbose_name=_("extra parameters"), help_text=_("additional application parameters.")) created_date = models.DateTimeField(auto_now_add=True, verbose_name='Date') updated_date = models.DateTimeField(auto_now=True) phonebook = models.ManyToManyField(Phonebook, blank=True, null=True) imported_phonebook = models.CharField( max_length=500, default='', verbose_name=_('list of imported phonebook')) totalcontact = models.IntegerField( default=0, blank=True, null=True, verbose_name=_('total contact'), help_text=_("total contact for this campaign")) objects = SMSCampaignManager() def __unicode__(self): return u"%s" % (self.name) class Meta: permissions = (("view_smscampaign", _('can see SMS campaign')), ("view_sms_dashboard", _('can see SMS campaign dashboard'))) db_table = u'sms_campaign' verbose_name = _("SMS campaign") verbose_name_plural = _("SMS campaigns") def update_sms_campaign_status(self): """Update the sms_campaign's status For example, If campaign is active, you can change status to 'Pause' or 'Stop' """ # active - 1 | pause - 2 | abort - 3 | stop - 4 if self.status == SMS_CAMPAIGN_STATUS.START: return "<a href='%s'>Pause</a> | <a href='%s'>Abort</a> | <a href='%s'>Stop</a>" % ( reverse('mod_sms.views.update_sms_campaign_status_admin', args=[self.pk, SMS_CAMPAIGN_STATUS.PAUSE]), reverse('mod_sms.views.update_sms_campaign_status_admin', args=[self.pk, SMS_CAMPAIGN_STATUS.ABORT]), reverse('mod_sms.views.update_sms_campaign_status_admin', args=[self.pk, SMS_CAMPAIGN_STATUS.END])) if self.status == SMS_CAMPAIGN_STATUS.PAUSE: return "<a href='%s'>Start</a> | <a href='%s'>Abort</a> | <a href='%s'>Stop</a>" % ( reverse('mod_sms.views.update_sms_campaign_status_admin', args=[self.pk, SMS_CAMPAIGN_STATUS.START]), reverse('mod_sms.views.update_sms_campaign_status_admin', args=[self.pk, SMS_CAMPAIGN_STATUS.ABORT]), reverse('mod_sms.views.update_sms_campaign_status_admin', args=[self.pk, SMS_CAMPAIGN_STATUS.END])) if self.status == SMS_CAMPAIGN_STATUS.ABORT: return "<a href='%s'>Start</a> | <a href='%s'>Pause</a> | <a href='%s'>Stop</a>" % ( reverse('mod_sms.views.update_sms_campaign_status_admin', args=[self.pk, SMS_CAMPAIGN_STATUS.START]), reverse('mod_sms.views.update_sms_campaign_status_admin', args=[self.pk, SMS_CAMPAIGN_STATUS.PAUSE]), reverse('mod_sms.views.update_sms_campaign_status_admin', args=[self.pk, SMS_CAMPAIGN_STATUS.END])) if self.status == SMS_CAMPAIGN_STATUS.END: return "<a href='%s'>Start</a> | <a href='%s'>Pause</a> | <a href='%s'>Abort</a>" % ( reverse('mod_sms.views.update_sms_campaign_status_admin', args=[self.pk, SMS_CAMPAIGN_STATUS.START]), reverse('mod_sms.views.update_sms_campaign_status_admin', args=[self.pk, SMS_CAMPAIGN_STATUS.PAUSE]), reverse('mod_sms.views.update_sms_campaign_status_admin', args=[self.pk, SMS_CAMPAIGN_STATUS.ABORT])) update_sms_campaign_status.allow_tags = True update_sms_campaign_status.short_description = _('action') def count_contact_of_phonebook(self, status=None): """Count the no. of Contacts in a phonebook""" if status == CONTACT_STATUS.ACTIVE: count_contact = Contact.objects.filter( status=CONTACT_STATUS.ACTIVE, phonebook__smscampaign=self.id).count() else: count_contact = Contact.objects.filter( phonebook__smscampaign=self.id).count() if not count_contact: return _("Phonebook Empty") return count_contact count_contact_of_phonebook.allow_tags = True count_contact_of_phonebook.short_description = _('contact') def is_authorized_contact(self, str_contact): """Check if a contact is authorized""" try: dialersetting = UserProfile.objects.get( user=self.user).dialersetting return common_contact_authorization(dialersetting, str_contact) except UserProfile.DoesNotExist: return False def get_active_max_frequency(self): """Get the active max frequency""" try: sms_dialersetting = UserProfile.objects.get( user=self.user).dialersetting except UserProfile.DoesNotExist: return self.frequency # sms_max_frequency max_frequency = sms_dialersetting.sms_max_frequency if max_frequency < self.frequency: return max_frequency return self.frequency def get_active_contact(self): """Get all the active Contacts from the phonebook""" list_contact = Contact.objects.filter( phonebook__smscampaign=self.id, status=CONTACT_STATUS.ACTIVE).all() if not list_contact: return False return list_contact def get_active_contact_no_subscriber(self): """List of active contacts that do not exist in Campaign Subscriber""" # The list of active contacts that doesn't # exist in SMSCampaignSubscriber #TODO : This might kill performance on huge phonebook... query = \ 'SELECT dc.id, dc.phonebook_id, dc.contact, dc.last_name, \ dc.first_name, dc.email, dc.city, dc.description, \ dc.status, dc.additional_vars, dc.created_date, dc.updated_date \ FROM dialer_contact as dc \ INNER JOIN dialer_phonebook ON \ (dc.phonebook_id = dialer_phonebook.id) \ INNER JOIN sms_campaign_phonebook ON \ (dialer_phonebook.id = sms_campaign_phonebook.phonebook_id) \ WHERE sms_campaign_phonebook.smscampaign_id = %s \ AND dc.status = 1 \ AND dc.id NOT IN \ (SELECT sms_campaign_subscriber.contact_id \ FROM sms_campaign_subscriber \ WHERE sms_campaign_subscriber.sms_campaign_id = %s)' % \ (str(self.id), str(self.id),) raw_contact_list = Contact.objects.raw(query) return raw_contact_list def progress_bar(self): """Progress bar generated based on no of contacts""" # Cache campaignsubscriber_count count_contact = Contact.objects.filter( phonebook__smscampaign=self.id).count() # Cache need to be set per campaign # sms_campaignsubscriber_count_key_campaign_id_1 sms_campaignsubscriber_count = cache.get( 'sms_campaignsubscriber_count_key_campaign_id_' + str(self.id)) #sms_campaignsubscriber_count = None if sms_campaignsubscriber_count is None: list_contact = Contact.objects.values_list('id', flat=True)\ .filter(phonebook__smscampaign=self.id) sms_campaignsubscriber_count = 0 try: sms_campaignsubscriber_count += SMSCampaignSubscriber.objects.filter( contact__in=list_contact, sms_campaign=self.id, status=SMS_SUBSCRIBER_STATUS.COMPLETE).count() except: pass cache.set( "sms_campaignsubscriber_count_key_campaign_id_" + str(self.id), sms_campaignsubscriber_count, 5) sms_campaignsubscriber_count = int(sms_campaignsubscriber_count) count_contact = int(count_contact) if count_contact > 0: percentage_pixel = (float(sms_campaignsubscriber_count) / count_contact) * 100 percentage_pixel = int(percentage_pixel) else: percentage_pixel = 0 sms_campaignsubscriber_count_string = "sms_campaign-subscribers (" + \ str(sms_campaignsubscriber_count) + ")" return "<div title='%s' style='width: 100px; border: 1px solid #ccc;'>\ <div style='height: 4px; width: %dpx; background: #555; '>\ </div></div>" % (sms_campaignsubscriber_count_string, percentage_pixel) progress_bar.allow_tags = True progress_bar.short_description = _('progress') def sms_campaignsubscriber_detail(self): """This will link to sms_campaign subscribers who are associated with the sms_campaign""" model_name = SMSCampaignSubscriber._meta.object_name.lower() app_label = self._meta.app_label link = '/admin/%s/%s/' % (app_label, model_name) link += '?sms_campaign__id=%d' % self.id display_link = _("<a href='%(link)s'>%(name)s</a>") %\ {'link': link, 'name': _('details')} return display_link sms_campaignsubscriber_detail.allow_tags = True sms_campaignsubscriber_detail.short_description = _( 'SMSCampaign Subscriber') def get_pending_subscriber(self, limit=1000): """Get all the pending subscribers from the sms_campaign""" list_subscriber = SMSCampaignSubscriber.objects.filter( sms_campaign=self.id, status=SMS_SUBSCRIBER_STATUS.PENDING)\ .all()[:limit] if not list_subscriber: return False return list_subscriber def get_pending_subscriber_update(self, limit=1000, status=6): """Get all the pending subscribers from the campaign""" # TODO in django 1.4 : replace by SELECT FOR UPDATE list_subscriber = SMSCampaignSubscriber.objects.filter( sms_campaign=self.id, status=SMS_SUBSCRIBER_STATUS.PENDING)\ .all()[:limit] if not list_subscriber: return False for elem_subscriber in list_subscriber: elem_subscriber.status = status elem_subscriber.save() return list_subscriber def common_sms_campaign_status(self, status): """SMS Campaign Status (e.g. start | stop | abort | pause) needs to be changed. It is a common function for the admin and customer UI's **Attributes**: * ``status`` - selected status for the sms campaign record **Logic Description**: * Selected SMS Campaign's status needs to be changed. Changed status can be start, stop or pause. * This function is used by ``update_sms_campaign_status_admin()`` & ``update_sms_campaign_status_cust()`` """ previous_status = self.status self.status = status self.save() # Start tasks to import subscriber if status == SMS_CAMPAIGN_STATUS.START and previous_status != SMS_CAMPAIGN_STATUS.START: from mod_sms.tasks import sms_collect_subscriber sms_collect_subscriber.delay(self.id) return self.user