Exemple #1
0
def poll_vote_reminder():
    """Send an email reminder every 8 hours to
    remind valid users to cast their vote.

    """
    now = datetime2pdt()
    polls = Poll.objects.filter(start__lte=now, end__gt=now)

    for poll in polls:
        last_notification = (poll.last_nofication if poll.last_notification
                             else poll.created_on)

        time_diff = (time.mktime(now.timetuple()) -
                     time.mktime(last_notification.timetuple()))
        if time_diff > NOTIFICATION_INTERVAL:
            valid_users = User.objects.filter(groups=poll.valid_groups)
            recipients = (valid_users.exclude(pk__in=poll.users_voted.all())
                                     .values_list('id', flat=True))
            subject = ('[Reminder][Voting] Please cast your vote '
                       'for "%s" now!' % poll.name)
            template_reminder = 'emails/voting_vote_reminder.txt'
            ctx_data = {'poll': poll}
            send_remo_mail.delay(recipients, subject,
                                 template_reminder, ctx_data)
            Poll.objects.filter(pk=poll.pk).update(last_notification=now)
Exemple #2
0
    def clean(self):
        """Clean form."""
        super(PollAddForm, self).clean()

        cdata = self.cleaned_data
        date_now = datetime2pdt()

        # Check if key exists
        if not 'start_form' in cdata:
            raise ValidationError('Please correct the form errors.')

        cdata['start'] = cdata['start_form'].replace(tzinfo=utc)

        if cdata['start_form'] >= cdata['end_form']:
            msg = 'Start date should come before end date.'
            self._errors['start_form'] = self.error_class([msg])
        if cdata['start'] < date_now:
            msg = 'Start date should not be in the past.'
            self._errors['start_form'] = self.error_class([msg])

        # Check that there is at least one radio or range poll
        if ((not self.range_poll_formset._count_filled_forms()) and
                (not self.radio_poll_formset._count_filled_forms())):
            msg = u'You must fill at least one radio or range poll.'
            raise ValidationError(msg)

        return cdata
Exemple #3
0
def automated_poll(sender, instance, **kwargs):
    """Create a radio poll automatically.

    If a bug lands in our database with council_vote_requested, create
    a new Poll and let Council members vote.

    """
    if ((not instance.council_vote_requested
         or Poll.objects.filter(bug=instance).exists())):
        return

    date_now = datetime2pdt()
    remobot = User.objects.get(username='******')

    with transaction.commit_on_success():
        poll = (Poll.objects.create(
            name=instance.summary,
            description=instance.first_comment,
            valid_groups=Group.objects.get(name='Council'),
            start=date_now,
            end=(date_now + timedelta(days=VOTING_PERIOD_AUTOMATED_POLLS)),
            bug=instance,
            created_by=remobot,
            automated_poll=True))

        radio_poll = RadioPoll.objects.create(poll=poll,
                                              question='Budget Approval')

        RadioPollChoice.objects.create(answer='Approved',
                                       radio_poll=radio_poll)
        RadioPollChoice.objects.create(answer='Denied', radio_poll=radio_poll)

        statsd.incr('voting.create_automated_poll')
Exemple #4
0
def edit_voting(request, slug=None):
    """Create/Edit voting view."""
    poll, created = get_or_create_instance(Poll, slug=slug)

    can_delete_voting = False
    extra = 0
    current_voting_edit = False
    range_poll_formset = None
    radio_poll_formset = None
    if created:
        poll.created_by = request.user
        extra = 1
    else:
        if (RangePoll.objects.filter(poll=poll).count() or
                RadioPoll.objects.filter(poll=poll).count()) == 0:
            extra = 1
        can_delete_voting = True
        date_now = datetime2pdt()
        if poll.start < date_now and poll.end > date_now:
            current_voting_edit = True

    if current_voting_edit:
        poll_form = forms.PollEditForm(request.POST or None, instance=poll)
    else:
        RangePollFormset = (inlineformset_factory(Poll, RangePoll,
                            formset=forms.BaseRangePollInlineFormSet,
                            extra=extra, can_delete=True))
        RadioPollFormset = (inlineformset_factory(Poll, RadioPoll,
                            formset=forms.BaseRadioPollInlineFormSet,
                            extra=extra, can_delete=True))

        nominee_list = User.objects.filter(
            groups__name='Rep', userprofile__registration_complete=True)
        range_poll_formset = RangePollFormset(request.POST or None,
                                              instance=poll,
                                              user_list=nominee_list)
        radio_poll_formset = RadioPollFormset(request.POST or None,
                                              instance=poll)
        poll_form = forms.PollAddForm(request.POST or None, instance=poll,
                                      radio_poll_formset=radio_poll_formset,
                                      range_poll_formset=range_poll_formset)

    if poll_form.is_valid():
        poll_form.save()

        if created:
            messages.success(request, 'Voting successfully created.')
        else:
            messages.success(request, 'Voting successfully edited.')

        return redirect('voting_edit_voting', slug=poll.slug)

    return render(request, 'edit_voting.html',
                  {'creating': created,
                   'poll': poll,
                   'poll_form': poll_form,
                   'range_poll_formset': range_poll_formset,
                   'radio_poll_formset': radio_poll_formset,
                   'can_delete_voting': can_delete_voting,
                   'current_voting_edit': current_voting_edit})
Exemple #5
0
def create_radio_poll(sender, instance, **kwargs):
    """Create a radio poll automatically when a new budget or
    swag bug is submitted.
    """
    # Avoid circular dependencies
    from remo.voting.models import Poll, RadioPoll, RadioPollChoice

    if (instance.flag_status == '?' and
        instance.flag_name == 'remo-review' and
            instance.component in ('Budget Requests', 'Swag Requests')):
        if not Poll.objects.filter(bug=instance).exists():
            date_now = datetime2pdt()
            remobot = User.objects.get(username='******')

            poll = (Poll.objects
                    .create(name=instance.summary,
                            description=instance.first_comment,
                            valid_groups=Group.objects.get(name='Council'),
                            start=date_now,
                            end=(date_now + timedelta(days=3)),
                            bug=instance,
                            created_by=remobot,
                            automated_poll=True))

            radio_poll = RadioPoll.objects.create(poll=poll,
                                                  question='Budget Approval')

            for answer in ('Approved', 'Denied'):
                RadioPollChoice.objects.create(answer=answer,
                                               radio_poll=radio_poll)
Exemple #6
0
    def test_view_post_a_comment(self, fake_messages):
        """Post a comment on poll."""
        poll_start = datetime2pdt() - timedelta(days=5)
        poll_user = UserFactory.create(groups=['Council'])
        poll_group = Group.objects.get(name='Council')
        swag_poll = PollFactory.create(name='swag poll',
                                       start=poll_start,
                                       end=poll_start + timedelta(days=15),
                                       created_by=poll_user,
                                       valid_groups=poll_group,
                                       automated_poll=True,
                                       description='Swag poll description.',
                                       slug='swag-poll')
        vote_url = reverse('voting_view_voting', kwargs={'slug': 'swag-poll'})
        factory = RequestFactory()
        request = factory.post(vote_url, {'comment': 'This is a comment'},
                               follow=True)
        request.user = poll_user

        response = view_voting(request, slug=swag_poll.slug)
        self.assertTemplateUsed(response, 'list_votings.html')
        poll_comment = PollComment.objects.get(poll=swag_poll)
        eq_(poll_comment.user, poll_user)
        eq_(poll_comment.comment, 'This is a comment')
        fake_messages.success.assert_called_once_with(
            mock.ANY, 'Your vote has been successfully registered.')
Exemple #7
0
def automated_poll(sender, instance, **kwargs):
    """Create a radio poll automatically.

    If a bug lands in our database with council_vote_requested, create
    a new Poll and let Council members vote.

    """
    if ((not instance.council_vote_requested
         or Poll.objects.filter(bug=instance).exists())):
        return

    date_now = datetime2pdt()
    remobot = User.objects.get(username='******')

    with transaction.commit_on_success():
        poll = (Poll.objects
                .create(name=instance.summary,
                        description=instance.first_comment,
                        valid_groups=Group.objects.get(name='Council'),
                        start=date_now,
                        end=(date_now +
                             timedelta(days=VOTING_PERIOD_AUTOMATED_POLLS)),
                        bug=instance,
                        created_by=remobot,
                        automated_poll=True))

        radio_poll = RadioPoll.objects.create(poll=poll,
                                              question='Budget Approval')

        RadioPollChoice.objects.create(answer='Approved',
                                       radio_poll=radio_poll)
        RadioPollChoice.objects.create(answer='Denied', radio_poll=radio_poll)

        statsd.incr('voting.create_automated_poll')
Exemple #8
0
    def test_view_post_a_comment(self, fake_messages):
        """Post a comment on poll."""
        poll_start = datetime2pdt() - timedelta(days=5)
        poll_user = UserFactory.create(groups=["Council"])
        poll_group = Group.objects.get(name="Council")
        swag_poll = PollFactory.create(
            name="swag poll",
            start=poll_start,
            end=poll_start + timedelta(days=15),
            created_by=poll_user,
            valid_groups=poll_group,
            automated_poll=True,
            description="Swag poll description.",
            slug="swag-poll",
        )
        vote_url = reverse("voting_view_voting", kwargs={"slug": "swag-poll"})
        factory = RequestFactory()
        request = factory.post(vote_url, {"comment": "This is a comment"}, follow=True)
        request.user = poll_user

        response = view_voting(request, slug=swag_poll.slug)
        self.assertTemplateUsed(response, "list_votings.html")
        poll_comment = PollComment.objects.get(poll=swag_poll)
        eq_(poll_comment.user, poll_user)
        eq_(poll_comment.comment, "This is a comment")
        fake_messages.success.assert_called_once_with(mock.ANY, "Your vote has been successfully registered.")
Exemple #9
0
def poll_vote_reminder():
    """Send an email reminder every 8 hours to
    remind valid users to cast their vote.

    """
    now = datetime2pdt()
    polls = Poll.objects.filter(start__lte=now, end__gt=now)

    for poll in polls:
        last_notification = (poll.last_nofication
                             if poll.last_notification else poll.created_on)

        time_diff = (time.mktime(now.timetuple()) -
                     time.mktime(last_notification.timetuple()))
        if time_diff > NOTIFICATION_INTERVAL:
            valid_users = User.objects.filter(groups=poll.valid_groups)
            recipients = (valid_users.exclude(
                pk__in=poll.users_voted.all()).values_list('id', flat=True))
            subject = ('[Reminder][Voting] Please cast your vote '
                       'for "%s" now!' % poll.name)
            template_reminder = 'emails/voting_vote_reminder.txt'
            ctx_data = {'poll': poll}
            send_remo_mail.delay(recipients, subject, template_reminder,
                                 ctx_data)
            Poll.objects.filter(pk=poll.pk).update(last_notification=now)
Exemple #10
0
    def clean(self):
        """Clean form."""
        super(PollAddForm, self).clean()

        cdata = self.cleaned_data
        date_now = datetime2pdt()

        # Check if key exists
        if not 'start_form' in cdata:
            raise ValidationError('Please correct the form errors.')

        cdata['start'] = cdata['start_form'].replace(tzinfo=utc)

        if cdata['start_form'] >= cdata['end_form']:
            msg = 'Start date should come before end date.'
            self._errors['start_form'] = self.error_class([msg])
        if cdata['start'] < date_now:
            msg = 'Start date should not be in the past.'
            self._errors['start_form'] = self.error_class([msg])

        # Check that there is at least one radio or range poll
        if ((not self.range_poll_formset._count_filled_forms())
                and (not self.radio_poll_formset._count_filled_forms())):
            msg = u'You must fill at least one radio or range poll.'
            raise ValidationError(msg)

        return cdata
Exemple #11
0
 def setUp(self):
     """Initial data for the tests."""
     self.user = User.objects.get(username="******")
     self.group = Group.objects.get(name="Council")
     self._now = datetime2pdt()
     self.now = self._now.replace(microsecond=0)
     self.start = self.now
     self.end = self.now + datetime.timedelta(hours=5 * 24)
     self.voting = Poll(name="poll", start=self.start, end=self.end, valid_groups=self.group, created_by=self.user)
     self.voting.save()
Exemple #12
0
 def setUp(self):
     """Initial data for the tests."""
     self.user = User.objects.get(username='******')
     self.group = Group.objects.get(name='Admin')
     self._now = datetime2pdt()
     self.now = self._now.replace(microsecond=0)
     self.start = self.now
     self.end = self.now + datetime.timedelta(days=5)
     self.voting = Poll(name='poll', start=self.start, end=self.end,
                        valid_groups=self.group, created_by=self.user)
     self.voting.save()
Exemple #13
0
    def test_email_users_without_a_vote(self, fake_datetime2pdt):
        """Test sending an email to users who have not cast
        their vote yet.

        """
        # act like it's today + 1 day
        fake_datetime2pdt.return_value = datetime2pdt() + datetime.timedelta(days=1)
        args = ["poll_vote_reminder"]
        management.call_command("cron", *args)
        eq_(len(mail.outbox), 3)
        for email in mail.outbox:
            eq_(email.to, ["*****@*****.**"])
Exemple #14
0
    def test_email_users_without_a_vote(self, fake_datetime2pdt):
        """Test sending an email to users who have not cast
        their vote yet.

        """
        # act like it's today + 1 day
        fake_datetime2pdt.return_value = (datetime2pdt() +
                                          datetime.timedelta(days=1))
        args = ['poll_vote_reminder']
        management.call_command('cron', *args)
        eq_(len(mail.outbox), 3)
        for email in mail.outbox:
            eq_(email.to, ['*****@*****.**'])
Exemple #15
0
 def setUp(self):
     """Initial data for the tests."""
     UserFactory.create(username='******', email='*****@*****.**',
                        first_name='ReMo', last_name='bot')
     self.user = User.objects.get(username='******')
     self.group = Group.objects.get(name='Council')
     self._now = datetime2pdt()
     self.now = self._now.replace(microsecond=0)
     self.start = self.now
     self.end = self.now + datetime.timedelta(hours=5*24)
     self.voting = Poll(name='poll', start=self.start, end=self.end,
                        valid_groups=self.group, created_by=self.user)
     self.voting.save()
Exemple #16
0
    def test_email_users_without_a_vote(self, fake_datetime2pdt):
        """Test sending an email to users who have not cast
        their vote yet.

        """
        # act like it's today + 1 day
        fake_datetime2pdt.return_value = (datetime2pdt() +
                                          datetime.timedelta(days=1))
        args = ['poll_vote_reminder']
        management.call_command('cron', *args)
        recipients = map(lambda x: '%s' % x.email,
                         User.objects.filter(groups=self.group))
        eq_(len(mail.outbox), 3)
        eq_(mail.outbox[2].to, recipients)
Exemple #17
0
    def clean(self):
        """Clean form."""
        super(PollEditForm, self).clean()

        cdata = self.cleaned_data
        date_now = datetime2pdt()

        # Check if key exists
        if not 'end_form' in cdata:
            raise ValidationError('Please correct the form errors.')

        cdata['end'] = cdata['end_form'].replace(tzinfo=utc)

        if cdata['end'] < date_now:
            msg = 'End date should not be in the past.'
            self._errors['end_form'] = self.error_class([msg])

        return cdata
Exemple #18
0
    def test_extend_voting_period_by_24hours(self, fake_datetime2pdt):
        """Test extending voting period by 24hours if less than
        50% of the valid users have voted and the poll ends in less than
        8 hours.

        """
        automated_poll = Poll(name='poll', start=self.start, end=self.end,
                              valid_groups=self.group, created_by=self.user,
                              automated_poll=True)
        automated_poll.save()

        # act like it's 4 hours before the end of the poll
        fake_datetime2pdt.return_value = (datetime2pdt() +
                                          datetime.timedelta(hours=116))
        args = ['extend_voting_period']
        management.call_command('cron', *args)
        poll = Poll.objects.get(pk=automated_poll.id)
        eq_(poll.end - automated_poll.end, datetime.timedelta(hours=24))
Exemple #19
0
    def clean(self):
        """Clean form."""
        super(PollEditForm, self).clean()

        cdata = self.cleaned_data
        date_now = datetime2pdt()

        # Check if key exists
        if not 'end_form' in cdata:
            raise ValidationError('Please correct the form errors.')

        cdata['end'] = cdata['end_form'].replace(tzinfo=utc)

        if cdata['end'] < date_now:
            msg = 'End date should not be in the past.'
            self._errors['end_form'] = self.error_class([msg])

        return cdata
Exemple #20
0
    def __init__(self, *args, **kwargs):
        """Initialize form.

        Dynamically set some fields of the form.
        """
        super(PollEditForm, self).__init__(*args, **kwargs)

        instance = self.instance
        # Set the year portion of the widget
        now = datetime.utcnow()
        end_year = min(getattr(self.instance.end, 'year', now.year),
                       now.year - 1)
        self.fields['end_form'] = forms.DateTimeField(
            widget=SplitSelectDateTimeWidget(
                years=range(end_year, now.year + 10), minute_step=5),
            validators=[validate_datetime])
        if self.instance.end:
            # Convert to server timezone
            naive_time = make_naive(instance.end, pytz.UTC)
            server_time = datetime2pdt(naive_time)
            self.fields['end_form'].initial = server_time
Exemple #21
0
def extend_voting_period():
    """Extend the voting period by 24hours if
    less than 50% of the Council members has voted
    and the poll ends in less than NOTIFICATION_INTERVAL.

    """
    now = datetime2pdt()
    polls = Poll.objects.filter(start__lte=now, end__gt=now,
                                automated_poll=True)

    for poll in polls:
        vote_count = poll.users_voted.all().count()
        missing_vote_count = (User.objects
                                  .filter(groups=poll.valid_groups)
                                  .exclude(pk__in=poll.users_voted.all())
                                  .count())
        half_voted = vote_count < missing_vote_count
        time_diff = (time.mktime(now.timetuple()) -
                     time.mktime(poll.end.timetuple()))
        if time_diff < NOTIFICATION_INTERVAL and half_voted:
            poll.end += datetime.timedelta(seconds=EXTEND_VOTING_PERIOD)
            poll.save()
Exemple #22
0
    def __init__(self, *args, **kwargs):
        """Initialize form.

        Dynamically set some fields of the form.
        """
        self.range_poll_formset = kwargs.pop('range_poll_formset')
        self.radio_poll_formset = kwargs.pop('radio_poll_formset')
        super(PollAddForm, self).__init__(*args, **kwargs)

        instance = self.instance
        # Set the year portion of the widget
        now = datetime.utcnow()
        start_year = min(getattr(self.instance.start, 'year', now.year),
                         now.year - 1)
        self.fields['start_form'] = forms.DateTimeField(
            widget=SplitSelectDateTimeWidget(
                years=range(start_year, now.year + 10), minute_step=5),
            validators=[validate_datetime])
        if self.instance.start:
            naive_time = make_naive(instance.start, pytz.UTC)
            server_time = datetime2pdt(naive_time)
            self.fields['start_form'].initial = server_time
Exemple #23
0
    def __init__(self, *args, **kwargs):
        """Initialize form.

        Dynamically set some fields of the form.
        """
        super(PollEditForm, self).__init__(*args, **kwargs)

        instance = self.instance
        # Set the year portion of the widget
        now = datetime.utcnow()
        end_year = min(getattr(self.instance.end, 'year', now.year),
                       now.year - 1)
        self.fields['end_form'] = forms.DateTimeField(
            widget=SplitSelectDateTimeWidget(years=range(
                end_year, now.year + 10),
                                             minute_step=5),
            validators=[validate_datetime])
        if self.instance.end:
            # Convert to server timezone
            naive_time = make_naive(instance.end, pytz.UTC)
            server_time = datetime2pdt(naive_time)
            self.fields['end_form'].initial = server_time
Exemple #24
0
def extend_voting_period():
    """Extend the voting period by 24hours if
    less than 50% of the Council members has voted
    and the poll ends in less than NOTIFICATION_INTERVAL.

    """
    now = datetime2pdt()
    polls = Poll.objects.filter(start__lte=now,
                                end__gt=now,
                                automated_poll=True)

    for poll in polls:
        vote_count = poll.users_voted.all().count()
        missing_vote_count = (User.objects.filter(
            groups=poll.valid_groups).exclude(
                pk__in=poll.users_voted.all()).count())
        half_voted = vote_count < missing_vote_count
        time_diff = (time.mktime(now.timetuple()) -
                     time.mktime(poll.end.timetuple()))
        if time_diff < NOTIFICATION_INTERVAL and half_voted:
            poll.end += datetime.timedelta(seconds=EXTEND_VOTING_PERIOD)
            poll.save()
Exemple #25
0
    def __init__(self, *args, **kwargs):
        """Initialize form.

        Dynamically set some fields of the form.
        """
        self.range_poll_formset = kwargs.pop('range_poll_formset')
        self.radio_poll_formset = kwargs.pop('radio_poll_formset')
        super(PollAddForm, self).__init__(*args, **kwargs)

        instance = self.instance
        # Set the year portion of the widget
        now = datetime.utcnow()
        start_year = min(getattr(self.instance.start, 'year', now.year),
                         now.year - 1)
        self.fields['start_form'] = forms.DateTimeField(
            widget=SplitSelectDateTimeWidget(years=range(
                start_year, now.year + 10),
                                             minute_step=5),
            validators=[validate_datetime])
        if self.instance.start:
            naive_time = make_naive(instance.start, pytz.UTC)
            server_time = datetime2pdt(naive_time)
            self.fields['start_form'].initial = server_time
Exemple #26
0
def view_voting(request, slug):
    """View voting and cast a vote view."""
    user = request.user
    now = datetime2pdt()
    poll = get_object_or_404(Poll, slug=slug)
    # If the user does not belong to a valid poll group
    if not (user.groups.filter(Q(id=poll.valid_groups.id) |
                               Q(name='Admin')).exists()):
        messages.error(request, ('You do not have the permissions to '
                                 'vote on this voting.'))
        return redirect('voting_list_votings')

    range_poll_choice_forms = {}
    radio_poll_choice_forms = {}

    data = {'poll': poll}

    # if the voting period has ended, display the results
    if now > poll.end:
        return render(request, 'view_voting.html', data)
    if now < poll.start:
        # Admin can edit future votings
        if user.groups.filter(name='Admin').exists():
            return redirect('voting_edit_voting', slug=poll.slug)
        else:
            messages.warning(request, ('This vote has not yet begun. '
                                       'You can cast your vote on %s PDT.' %
                                       poll.start.strftime('%Y %B %d, %H:%M')))
            return redirect('voting_list_votings')

    # avoid multiple votes from the same user
    if Vote.objects.filter(poll=poll, user=user).exists():
        messages.warning(request, ('You have already cast your vote for this '
                                   'voting. Come back to see the results on '
                                   '%s PDT.'
                                   % poll.end.strftime('%Y %B %d, %H:%M')))
        return redirect('voting_list_votings')

    # pack the forms for rendering
    for item in poll.range_polls.all():
        range_poll_choice_forms[item] = forms.RangePollChoiceVoteForm(
            data=request.POST or None, choices=item.choices.all())

    for item in poll.radio_polls.all():
        radio_poll_choice_forms[item] = forms.RadioPollChoiceVoteForm(
            data=request.POST or None, radio_poll=item)

    if request.method == 'POST':
        forms_valid = True
        # validate all forms
        for item in (range_poll_choice_forms.values()
                     + radio_poll_choice_forms.values()):
            if not item.is_valid():
                forms_valid = False
                break

        if forms_valid:
            for range_poll_form in range_poll_choice_forms.values():
                range_poll_form.save()
            for radio_poll_form in radio_poll_choice_forms.values():
                radio_poll_form.save()
            Vote.objects.create(user=user, poll=poll)
            messages.success(request, ('Your vote has been '
                                       'successfully registered.'))
            return redirect('voting_list_votings')

    data['range_poll_choice_forms'] = range_poll_choice_forms
    data['radio_poll_choice_forms'] = radio_poll_choice_forms

    return render(request, 'vote_voting.html', data)