Пример #1
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])
Пример #2
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])
Пример #3
0
def set_campaign_code():
    return get_unique_code(length=5)
Пример #4
0
    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>&nbsp;</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>&nbsp;</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)
Пример #5
0
 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)
Пример #6
0
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)
Пример #7
0
 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)
Пример #8
0
    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>&nbsp;</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>&nbsp;</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)
Пример #9
0
def set_campaign_code():
    return get_unique_code(length=5)
Пример #10
0
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