コード例 #1
0
def voter_delete(request, election, poll, voter_uuid):
    voter = get_object_or_404(Voter,
                              uuid=voter_uuid,
                              poll__in=poll.linked_polls)
    voter_id = voter.voter_login_id
    unlink = request.GET.get('unlink', False)

    linked_polls = poll.linked_polls
    if unlink:
        linked_polls = linked_polls.filter(pk=poll.pk)

    for poll in linked_polls:
        voter = None
        try:
            voter = Voter.objects.get(poll=poll, voter_login_id=voter_id)
        except Voter.DoesNotExist:
            poll.logger.error("Cannot remove voter '%s'. Does not exist.",
                              voter_uuid)
        if voter and voter.voted:
            raise PermissionDenied('36')
        if voter:
            voter.delete()
            poll.logger.info("Poll voter '%s' removed", voter.voter_login_id)
            message = _("Voter removed successfully")
            messages.success(request, message)

    url = poll_reverse(poll, 'voters')
    return HttpResponseRedirect(url)
コード例 #2
0
    def questions_update_view(self, request, election, poll):
        from zeus.utils import poll_reverse
        from zeus.forms import PartyForm, DEFAULT_ANSWERS_COUNT, \
                MAX_QUESTIONS_LIMIT

        extra = 1
        if poll.questions_data:
            extra = 0

        questions_formset = formset_factory(PartyForm,
                                            extra=extra,
                                            can_delete=True,
                                            can_order=True)
        if request.method == 'POST':
            formset = questions_formset(request.POST)
            if formset.is_valid():
                questions_data = []
                for question in formset.cleaned_data:
                    if not question:
                        continue

                    # force sort of answers by extracting index from answer key.
                    # cast answer index to integer, otherwise answer_10 would
                    # be placed before answer_2
                    answer_index = lambda a: int(a[0].replace('answer_', ''))
                    isanswer = lambda a: a[0].startswith('answer_')
                    answer_values = list(
                        filter(isanswer, iter(question.items())))
                    sorted_answers = sorted(answer_values, key=answer_index)

                    answers = [x[1] for x in sorted_answers]
                    question['answers'] = answers
                    for k in list(question.keys()):
                        if k in ['DELETE', 'ORDER']:
                            del question[k]

                    questions_data.append(question)

                poll.questions_data = questions_data
                poll.update_answers()
                poll.logger.info("Poll ballot updated")
                poll.save()

                url = poll_reverse(poll, 'questions')
                return HttpResponseRedirect(url)
        else:
            formset = questions_formset(initial=poll.questions_data)

        context = {
            'default_answers_count': DEFAULT_ANSWERS_COUNT,
            'formset': formset,
            'max_questions_limit': MAX_QUESTIONS_LIMIT,
            'election': election,
            'poll': poll,
            'module': self
        }
        set_menu('questions', context)
        tpl = 'election_modules/parties/election_poll_questions_manage'
        return render_template(request, tpl, context)
コード例 #3
0
    def questions_update_view(self, request, election, poll):
        from zeus.utils import poll_reverse
        from zeus.forms import PartyForm, DEFAULT_ANSWERS_COUNT, \
                MAX_QUESTIONS_LIMIT

        extra = 1
        if poll.questions_data:
            extra = 0

        questions_formset = formset_factory(PartyForm, extra=extra,
                                            can_delete=True, can_order=True)
        if request.method == 'POST':
            formset = questions_formset(request.POST)
            if formset.is_valid():
                questions_data = []
                for question in formset.cleaned_data:
                    if not question:
                        continue

                    # force sort of answers by extracting index from answer key.
                    # cast answer index to integer, otherwise answer_10 would
                    # be placed before answer_2
                    answer_index = lambda a: int(a[0].replace('answer_', ''))
                    isanswer = lambda a: a[0].startswith('answer_')
                    answer_values = filter(isanswer, question.iteritems())
                    sorted_answers = sorted(answer_values, key=answer_index)

                    answers = [x[1] for x in sorted_answers]
                    question['answers'] = answers
                    for k in question.keys():
                        if k in ['DELETE', 'ORDER']:
                            del question[k]

                    questions_data.append(question)

                poll.questions_data = questions_data
                poll.update_answers()
                poll.logger.info("Poll ballot updated")
                poll.save()

                url = poll_reverse(poll, 'questions')
                return HttpResponseRedirect(url)
        else:
            formset = questions_formset(initial=poll.questions_data)

        context = {
            'default_answers_count': DEFAULT_ANSWERS_COUNT,
            'formset': formset,
            'max_questions_limit': MAX_QUESTIONS_LIMIT,
            'election': election,
            'poll': poll,
            'module': self
        }
        set_menu('questions', context)
        tpl = 'election_modules/parties/election_poll_questions_manage'
        return render_template(request, tpl, context)
コード例 #4
0
def voters_upload_cancel(request, election, poll):
    voter_file_id = request.session.get('voter_file_id', None)
    if voter_file_id:
        vf = VoterFile.objects.get(id=voter_file_id)
        vf.delete()
    if 'voter_file_id' in request.session:
        del request.session['voter_file_id']

    url = poll_reverse(poll, 'voters_upload')
    return HttpResponseRedirect(url)
コード例 #5
0
def voter_exclude(request, election, poll, voter_uuid):
    polls = poll.linked_polls
    voter = get_object_or_404(Voter, uuid=voter_uuid, poll__in=polls)
    for p in polls:
        linked_voter = voter.linked_voters.get(poll=p)
        if not linked_voter.excluded_at:
            reason = request.POST.get('reason', '')
            try:
                p.zeus.exclude_voter(linked_voter.uuid, reason)
                p.logger.info("Poll voter '%s' excluded", linked_voter.voter_login_id)
            except Exception as e:
                pass
    return HttpResponseRedirect(poll_reverse(poll, 'voters'))
コード例 #6
0
ファイル: auth.py プロジェクト: teloniusz/zeus
def voter_login(request):
    form = VoterLoginForm()
    if request.method == 'POST':
        form = VoterLoginForm(request.POST)
        if form.is_valid():
            poll = form._voter.poll
            user = auth.ZeusUser(form._voter)
            user.authenticate(request)
            poll.logger.info("Poll voter '%s' logged in (global login view)",
                             form._voter.voter_login_id)
            return HttpResponseRedirect(poll_reverse(poll, 'index'))

    cxt = {'form': form}
    return render_template(request, 'voter_login', cxt)
コード例 #7
0
def questions(request, election, poll):
    module = poll.get_module()
    if request.zeususer.is_admin:
        if not module.questions_set() and poll.feature_can_manage_questions:
            url = poll_reverse(poll, 'questions_manage')
            return HttpResponseRedirect(url)

    preview_booth_url = poll.get_booth_url(request, preview=True)
    context = {
        'election': election,
        'poll': poll,
        'questions': questions,
        'module': poll.get_module(),
        'preview_booth_url': preview_booth_url
    }
    set_menu('questions', context)
    tpl = getattr(module, 'questions_list_template', 'election_poll_questions')
    return render_template(request, tpl, context)
コード例 #8
0
def voters_clear(request, election, poll):
    polls = poll.linked_polls
    q_param = request.POST.get('q_param', None)
    process_linked = request.GET.get('no-linked', False) is False
    if not process_linked:
        polls = polls.filter(pk=poll.pk)

    for p in polls:
        voters = p.voters.all()
        if q_param:
            voters = election.get_module().filter_voters(voters, q_param, request)

        for voter in voters:
            if not voter.cast_votes.count():
                voter.delete()
            p.logger.info("Poll voters cleared")

    url = poll_reverse(poll, 'voters')
    return HttpResponseRedirect(url)
コード例 #9
0
ファイル: __init__.py プロジェクト: teloniusz/zeus
    def questions_update_view(self, request, election, poll):
        from zeus.utils import poll_reverse
        from zeus.forms import DEFAULT_ANSWERS_COUNT, \
                MAX_QUESTIONS_LIMIT

        extra = 1
        if poll.questions_data:
            extra = 0

        questions_formset = self.questions_formset(extra)
        if request.method == 'POST':
            formset = questions_formset(request.POST,
                                        request.FILES,
                                        initial=poll.questions_data)
            should_submit = not request.FILES and formset.is_valid()
            if should_submit:
                cleaned_data = formset.cleaned_data
                questions_data = self.extract_question_data(cleaned_data)

                poll.questions_data = questions_data
                poll.update_answers()
                poll.logger.info("Poll ballot updated")
                self.update_poll_params(poll, formset.cleaned_data)
                poll.save()

                url = poll_reverse(poll, 'questions')
                return HttpResponseRedirect(url)
        else:
            formset = questions_formset(initial=poll.questions_data)

        context = {
            'default_answers_count': DEFAULT_ANSWERS_COUNT,
            'formset': formset,
            'max_questions_limit': self.max_questions_limit
            or MAX_QUESTIONS_LIMIT,
            'election': election,
            'poll': poll,
            'module': self
        }
        set_menu('questions', context)
        tpl = f'election_modules/{self.module_id}/election_poll_questions_manage'
        return render_template(request, tpl, context)
コード例 #10
0
def cast(request, election, poll):
    voter = request.voter
    encrypted_vote = request.POST['encrypted_vote']
    vote = datatypes.LDObject.fromDict(
        crypto_utils.from_json(encrypted_vote),
        type_hint='phoebus/EncryptedVote').wrapped_obj
    audit_password = request.POST.get('audit_password', None)

    cursor = connection.cursor()
    try:
        cursor.execute("SELECT pg_advisory_lock(1)")
        with transaction.atomic():
            cast_result = poll.cast_vote(voter, vote, audit_password)
            poll.logger.info("Poll cast")
    finally:
        cursor.execute("SELECT pg_advisory_unlock(1)")

    signature = {'signature': cast_result}

    if 'audit_request' in request.session:
        poll.logger.info("Poll cast audit request")
        del request.session['audit_request']
    else:
        poll.logger.info("Poll cast")

    if signature['signature'].startswith("AUDIT REQUEST"):
        request.session['audit_request'] = encrypted_vote
        request.session['audit_password'] = audit_password
        token = request.session.get('csrf_token')
        return HttpResponse('{"audit": 1, "token":"%s"}' % token,
                            content_type="application/json")
    else:
        # notify user
        fingerprint = voter.cast_votes.filter()[0].fingerprint
        tasks.send_cast_vote_email.delay(poll.pk, voter.pk, signature,
                                         fingerprint)
        url = "%s%s?f=%s" % (settings.SECURE_URL_HOST,
                             poll_reverse(poll, 'cast_done'), fingerprint)

        return HttpResponse('{"cast_url": "%s"}' % url,
                            content_type="application/json")
コード例 #11
0
ファイル: auth.py プロジェクト: miarka/zeus
def oauth2_login(request):
    poll_uuid = request.GET.get('state')
    try:
        poll = Poll.objects.get(uuid=poll_uuid)
    except Poll.DoesNotExist:
        return HttpResponseBadRequest(400)
    oauth2 = poll.get_oauth2_module
    if oauth2.can_exchange(request):
        oauth2.exchange(oauth2.get_exchange_url())
        try:
            confirmed, data = oauth2.confirm_email()
            if confirmed:
                voter = Voter.objects.get(poll__uuid=poll_uuid,
                                          uuid=oauth2.voter_uuid)
                user = auth.ZeusUser(voter)
                user.authenticate(request)
                poll.logger.info("Poll voter '%s' logged in",
                                 voter.voter_login_id)
                del request.session['oauth2_voter_uuid']
                del request.session['oauth2_voter_email']
                return HttpResponseRedirect(poll_reverse(poll, 'index'))
            else:
                poll.logger.info("[thirdparty] %s cannot resolve email from %r",
                                 poll.remote_login_display, data)
                messages.error(request, 'oauth2 user does not match voter')
                return HttpResponseRedirect(reverse('error',
                                                    kwargs={'code': 400}))
        except six.moves.urllib.error.HTTPError as e:
            poll.logger.exception(e)
            messages.error(request, 'oauth2 error')
            return HttpResponseRedirect(reverse('error',
                                                kwargs={'code': 400}))
    else:
        poll.logger.info("[thirdparty] oauth2 '%s' can_exchange failed",
                         poll.remote_login_display)
        messages.error(request, 'oauth2 exchange failed')
        return HttpResponseRedirect(reverse('error', kwargs={'code': 400}))
コード例 #12
0
ファイル: __init__.py プロジェクト: grnet/zeus
    def questions_update_view(self, request, election, poll):
        from zeus.utils import poll_reverse
        from zeus.forms import QuestionForm, DEFAULT_ANSWERS_COUNT, \
                MAX_QUESTIONS_LIMIT

        extra = 1
        if poll.questions_data:
            extra = 0

        questions_formset = self.questions_formset(extra)
        if request.method == 'POST':
            formset = questions_formset(request.POST)
            if formset.is_valid():
                cleaned_data = formset.cleaned_data
                questions_data = self.extract_question_data(cleaned_data)

                poll.questions_data = questions_data
                poll.update_answers()
                poll.logger.info("Poll ballot updated")
                poll.save()

                url = poll_reverse(poll, 'questions')
                return HttpResponseRedirect(url)
        else:
            formset = questions_formset(initial=poll.questions_data)

        context = {
            'default_answers_count': DEFAULT_ANSWERS_COUNT,
            'formset': formset,
            'max_questions_limit': MAX_QUESTIONS_LIMIT,
            'election': election,
            'poll': poll,
            'module': self
        }
        set_menu('questions', context)
        tpl = 'election_modules/simple/election_poll_questions_manage'
        return render_template(request, tpl, context)
コード例 #13
0
ファイル: auth.py プロジェクト: miarka/zeus
def shibboleth_login(request, endpoint):
    voter_uuid = request.session.get('shibboleth_voter_uuid', None)
    email = request.session.get('shibboleth_voter_email', None)

    if voter_uuid is not None:
        del request.session['shibboleth_voter_uuid']
    if email is not None:
        del request.session['shibboleth_voter_email']

    if not all([voter_uuid, email]):
        messages.error(request, _('Uninitialized shibboleth session.'))
        return HttpResponseRedirect(reverse('error',
                                            kwargs={'code': 400}))

    voter = get_object_or_404(Voter, uuid=voter_uuid)
    assert voter.voter_email == email

    poll = voter.poll
    constraints = poll.get_shibboleth_constraints()

    common_fields = ['HTTP_EPPN', 'HTTP_REMOTE_USER', 'HTTP_MAIL']
    meta = request.META
    shibboleth = {}
    for key, value in meta.items():
        if key in common_fields:
            shibboleth[key.replace('HTTP_', '', 1)] = value
        if key.startswith('HTTP_SHIB_'):
            shibboleth[key.replace('HTTP_SHIB_', '', 1)] = value

    poll.logger.info("[thirdparty] Voter (%s, %s) shibboleth data: %r" % (voter.uuid, voter.voter_email, shibboleth))
    error = False

    if constraints.get('endpoint') != endpoint:
        poll.logger.error('[thirdparty] invalid login endpoint %s', endpoint)
        error = 403
        messages.error(request, _("Invalid shibboleth endpoint"))

    if not error:
        for key in constraints.get('required_fields'):
            if key not in shibboleth:
                error = 403
                poll.logger.error('[thirdparty] %s field not found in shibboleth data', key)
                messages.error(request, _('Invalid shibboleth data resolved.'))

    idp_field_key = constraints.get('assert_idp_key')
    if not error and idp_field_key not in shibboleth:
        error = 403
        poll.logger.error('[thirdparty] %s field not found in shibboleth data', idp_field_key)
        messages.error(request, _('Invalid shibboleth data resolved.'))

    idp_field = None
    voter_field = None
    voter_field_key = None

    if not error and idp_field_key in shibboleth:
        idp_field = shibboleth[idp_field_key]
        voter_field_key = constraints.get('assert_voter_key')
        voter_field = getattr(voter, 'voter_%s' % voter_field_key, None)

    if not error and voter_field is None:
        error = 403
        poll.logger.error('[thirdparty] invalid assert_voter_key set %s' % voter_field_key)

    idp_field_arr = []
    if idp_field and ":" in idp_field:
        idp_field_arr = [x.strip() for x in idp_field.split(":")]

    if (not error and not idp_field == voter_field) and (not error and voter_field not in idp_field_arr):
        error = 403
        err_fields = [idp_field, idp_field_key, voter_field_key, voter_field]
        poll.logger.error('[thirdparty] assertion failed (%r=%s != %r=%s)', *err_fields)
        messages.error(request, _('Shibboleth voter info assertion failed.'))

    if error:
        return HttpResponseRedirect(reverse('error',
                                            kwargs={'code': error}))
    else:
        user = auth.ZeusUser(voter)
        user.authenticate(request)
        poll.logger.info("[thirdparty] Shibboleth login for %s", voter.voter_login_id)
        poll.logger.info("Poll voter '%s' logged in", voter.voter_login_id)
        return HttpResponseRedirect(poll_reverse(poll, 'index'))
コード例 #14
0
def voter_booth_login(request, election, poll, voter_uuid, voter_secret):
    voter = None

    if poll.jwt_auth:
        messages.error(request, _("Poll does not support voter url login."))
        return HttpResponseRedirect(reverse('error', kwargs={'code': 403}))

    try:
        voter = Voter.objects.get(poll=poll, uuid=voter_uuid)
        if voter.excluded_at:
            raise PermissionDenied('37')
    except Voter.DoesNotExist:
        raise PermissionDenied("Invalid election")

    if request.zeususer.is_authenticated() and request.zeususer.is_voter:
        return HttpResponseRedirect(
            reverse('election_poll_index',
                    kwargs={
                        'election_uuid':
                        request.zeususer._user.poll.election.uuid,
                        'poll_uuid': request.zeususer._user.poll.uuid
                    }))

    if request.zeususer.is_authenticated() and (
            not request.zeususer.is_voter
            or request.zeususer._user.pk != voter.pk):
        messages.error(
            request,
            _("You need to logout from your current account "
              "to access this view."))
        return HttpResponseRedirect(reverse('error', kwargs={'code': 403}))

    if voter.voter_password != str(voter_secret):
        raise PermissionDenied("Invalid secret")

    if poll.oauth2_thirdparty:
        oauth2 = poll.get_oauth2_module
        if oauth2.type_id == 'google':
            oauth2.set_login_hint(voter.voter_email)
        poll.logger.info(
            "[thirdparty] setting thirdparty voter " + "session data (%s, %s)",
            voter.voter_email, voter.uuid)
        request.session['oauth2_voter_email'] = voter.voter_email
        request.session['oauth2_voter_uuid'] = voter.uuid
        url = oauth2.get_code_url()
        poll.logger.info("[thirdparty] code handshake from %s", url)
        context = {'url': url}
        tpl = 'voter_redirect'
        return render_template(request, tpl, context)
    elif poll.shibboleth_auth:
        poll.logger.info("[thirdparty] shibboleth redirect for voter (%s, %s)",
                         voter.voter_email, voter.uuid)
        constraints = poll.get_shibboleth_constraints()
        endpoint = constraints.get('endpoint')
        request.session['shibboleth_voter_email'] = voter.voter_email
        request.session['shibboleth_voter_uuid'] = voter.uuid
        url = auth.make_shibboleth_login_url(endpoint)
        context = {'url': url}
        tpl = 'voter_redirect'
        return render_template(request, tpl, context)
    else:
        user = auth.ZeusUser(voter)
        user.authenticate(request)
        poll.logger.info("Poll voter '%s' logged in", voter.voter_login_id)
        return HttpResponseRedirect(poll_reverse(poll, 'index'))
コード例 #15
0
def voters_email(request, election, poll=None, voter_uuid=None):
    user = request.admin

    TEMPLATES = [
        ('vote', _('Time to Vote')),
        ('info', _('Additional Info')),
    ]

    default_template = 'vote'

    if not election.any_poll_feature_can_send_voter_mail:
        raise PermissionDenied('34')

    if not election.any_poll_feature_can_send_voter_booth_invitation:
        TEMPLATES.pop(0)
        default_template = 'info'

    polls = [poll]
    if not poll:
        polls = election.polls_by_link_id

    voter = None
    if voter_uuid:
        try:
            if poll:
                voter = get_object_or_404(Voter, uuid=voter_uuid, poll=poll)
            else:
                voter = get_object_or_404(Voter,
                                          uuid=voter_uuid,
                                          election=election)
        except Voter.DoesNotExist:
            raise PermissionDenied('35')
        if not voter:
            url = election_reverse(election, 'index')
            return HttpResponseRedirect(url)

        if voter.excluded_at:
            TEMPLATES.pop(0)
            default_template = 'info'

    if election.voting_extended_until and not election.voting_ended_at:
        if not voter or (voter and not voter.excluded_at):
            TEMPLATES.append(('extension', _('Voting end date extended')))

    if request.method == 'POST':
        template = request.POST.get('template', default_template)
    else:
        template = request.GET.get('template', default_template)

    if template not in [t[0] for t in TEMPLATES]:
        raise Exception("bad template")

    election_url = election.get_absolute_url()

    default_subject = render_to_string('email/%s_subject.txt' % template,
                                       {'custom_subject': "<SUBJECT>"})

    tpl_context = {
        'election': election,
        'election_url': election_url,
        'custom_subject': default_subject,
        'custom_message': '<BODY>',
        'custom_message_sms': '<SMS_BODY>',
        'SECURE_URL_HOST': settings.SECURE_URL_HOST,
        'voter': {
            'vote_hash': '<SMART_TRACKER>',
            'name': '<VOTER_NAME>',
            'voter_name': '<VOTER_NAME>',
            'voter_surname': '<VOTER_SURNAME>',
            'voter_login_id': '<VOTER_LOGIN_ID>',
            'voter_password': '******',
            'login_code': '<VOTER_LOGIN_CODE>',
            'audit_passwords': '1',
            'get_audit_passwords': ['pass1', 'pass2', '...'],
            'get_quick_login_url': '<VOTER_LOGIN_URL>',
            'poll': poll,
            'election': election
        }
    }

    default_body = render_to_string('email/%s_body.txt' % template,
                                    tpl_context)

    default_sms_body = render_to_string('sms/%s_body.txt' % template,
                                        tpl_context)

    q_param = request.GET.get('q', None)

    filtered_voters = election.voters.filter()
    if poll:
        filtered_voters = poll.voters.filter()

    if not q_param:
        filtered_voters = filtered_voters.none()
    else:
        filtered_voters = election.get_module().filter_voters(
            filtered_voters, q_param, request)

        if not filtered_voters.count():
            message = _("No voters were found.")
            messages.error(request, message)
            url = election_reverse(election, 'polls_list')
            return HttpResponseRedirect(url)

    if request.method == "GET":
        email_form = EmailVotersForm(election, template)
        email_form.fields['email_subject'].initial = dict(TEMPLATES)[template]
        if voter:
            email_form.fields['send_to'].widget = \
                email_form.fields['send_to'].hidden_widget()
    else:
        email_form = EmailVotersForm(election, template, request.POST)
        if email_form.is_valid():
            # the client knows to submit only once with a specific voter_id
            voter_constraints_include = None
            voter_constraints_exclude = None
            update_booth_invitation_date = False
            if template == 'vote':
                update_booth_invitation_date = True

            if voter:
                voter_constraints_include = {'uuid': voter.uuid}

            # exclude those who have not voted
            if email_form.cleaned_data['send_to'] == 'voted':
                voter_constraints_exclude = {'vote_hash': None}

            # include only those who have not voted
            if email_form.cleaned_data['send_to'] == 'not-voted':
                voter_constraints_include = {'vote_hash': None}

            for _poll in polls:
                if not _poll.feature_can_send_voter_mail:
                    continue

                if template == 'vote' and not \
                        _poll.feature_can_send_voter_booth_invitation:
                    continue

                subject_template = 'email/%s_subject.txt' % template
                body_template = 'email/%s_body.txt' % template
                body_template_sms = 'sms/%s_body.txt' % template
                contact_method = email_form.cleaned_data['contact_method']

                extra_vars = {
                    'SECURE_URL_HOST': settings.SECURE_URL_HOST,
                    'custom_subject': email_form.cleaned_data['email_subject'],
                    'custom_message': email_form.cleaned_data['email_body'],
                    'custom_message_sms': email_form.cleaned_data['sms_body'],
                    'election_url': election_url,
                }
                task_kwargs = {
                    'contact_id': template,
                    'notify_once': email_form.cleaned_data.get('notify_once'),
                    'subject_template_email': subject_template,
                    'body_template_email': body_template,
                    'body_template_sms': body_template_sms,
                    'contact_methods': contact_method.split(":"),
                    'template_vars': extra_vars,
                    'voter_constraints_include': voter_constraints_include,
                    'voter_constraints_exclude': voter_constraints_exclude,
                    'update_date': True,
                    'update_booth_invitation_date':
                    update_booth_invitation_date,
                    'q_param': q_param,
                }
                log_obj = election
                if poll:
                    log_obj = poll
                if voter:
                    log_obj.logger.info(
                        "Notifying single voter %s, [template: %s, filter: %s]",
                        voter.voter_login_id, template, q_param)
                else:
                    log_obj.logger.info(
                        "Notifying voters, [template: %s, filter: %r]",
                        template, q_param)
                tasks.voters_email.delay(_poll.pk, **task_kwargs)

            filters = get_voters_filters_with_constraints(
                q_param, voter_constraints_include, voter_constraints_exclude)
            send_to = filtered_voters.filter(filters)
            if q_param and not send_to.filter(filters).count():
                msg = "No voters matched your filters. No emails were sent."
                messages.error(request, _(msg))
            else:
                messages.info(request, _("Email sending started"))

            url = election_reverse(election, 'polls_list')
            if poll:
                url = poll_reverse(poll, 'voters')
            if q_param:
                url += '?q=%s' % six.moves.urllib.parse.quote_plus(q_param)
            return HttpResponseRedirect(url)
        else:
            message = _("Something went wrong")
            messages.error(request, message)

    if election.sms_enabled and election.sms_data.left <= 0:
        messages.warning(request, _("No SMS deliveries left."))

    context = {
        'email_form': email_form,
        'election': election,
        'poll': poll,
        'voter_o': voter,
        'default_subject': default_subject,
        'default_body': default_body,
        'default_sms_body': default_sms_body,
        'sms_enabled': election.sms_enabled,
        'template': template,
        'filtered_voters': filtered_voters,
        'templates': TEMPLATES
    }
    set_menu('voters', context)
    if not poll:
        set_menu('polls', context)
    return render_template(request, "voters_email", context)
コード例 #16
0
def voters_upload(request, election, poll):
    common_context = {
        'election': election,
        'poll': poll,
        'encodings': ENCODINGS
    }

    set_menu('voters', common_context)
    if request.method == "POST":
        preferred_encoding = request.POST.get('encoding', None)
        if preferred_encoding not in dict(ENCODINGS):
            messages.error(request, _("Invalid encoding"))
            url = poll_reverse(poll, 'voters_upload')
            return HttpResponseRedirect(url)
        else:
            common_context['preferred_encoding'] = preferred_encoding

        if bool(request.POST.get('confirm_p', 0)):
            # launch the background task to parse that file
            voter_file_id = request.session.get('voter_file_id', None)
            process_linked = request.session.get('no_link', False) is False
            if not voter_file_id:
                messages.error(request, _("Invalid voter file id"))
                url = poll_reverse(poll, 'voters')
                return HttpResponseRedirect(url)
            try:
                voter_file = VoterFile.objects.get(pk=voter_file_id)
                try:
                    voter_file.process(process_linked,
                                       preferred_encoding=preferred_encoding)
                except (exceptions.VoterLimitReached,
                        exceptions.DuplicateVoterID, ValidationError) as e:
                    messages.error(request, e.message)
                    voter_file.delete()
                    url = poll_reverse(poll, 'voters')
                    return HttpResponseRedirect(url)

                poll.logger.info("Processing voters upload")
            except VoterFile.DoesNotExist:
                pass
            except KeyError:
                pass
            if 'no_link' in request.session:
                del request.session['no_link']
            if 'voter_file_id' in request.session:
                del request.session['voter_file_id']
            url = poll_reverse(poll, 'voters')
            return HttpResponseRedirect(url)
        else:
            if 'voter_file_id' in request.session:
                del request.session['voter_file_id']
            # we need to confirm
            voters = []
            error = None
            invalid_emails = []

            def _email_validate(eml, line):
                try:
                    validate_email(eml)
                except ValidationError:
                    invalid_emails.append((eml, line))
                return True

            if 'voters_file' in request.FILES:
                voters_file = request.FILES['voters_file']
                voter_file_obj = poll.add_voters_file(voters_file)

                # import the first few lines to check
                invalid_emails = []
                try:
                    voters = [
                        v for v in voter_file_obj.itervoters(
                            email_validator=_email_validate,
                            preferred_encoding=preferred_encoding)
                    ]
                except ValidationError as e:
                    if hasattr(e, 'messages') and e.messages:
                        error = "".join(e.messages)
                    else:
                        error = "error."
                except Exception as e:
                    logging.exception('error reading voter file')
                    voter_file_obj.delete()
                    error = str(e)
                    if 'voter_file_id' in request.session:
                        del request.session['voter_file_id']
                    messages.error(request, error)
                    url = poll_reverse(poll, 'voters_upload')
                    return HttpResponseRedirect(url)

                if len(invalid_emails):
                    error = _("Enter a valid email address. " "<br />")
                    for email, line in invalid_emails:
                        error += "<br />" + "line %d: %s " % (line,
                                                              escape(email))

                    error = mark_safe(error)
            else:
                error = _("No file uploaded")
            if not error:
                request.session['voter_file_id'] = voter_file_obj.id
            count = len(voters)
            context = common_context
            context.update({'voters': voters, 'count': count, 'error': error})
            return render_template(request,
                                   'election_poll_voters_upload_confirm',
                                   context)
    else:
        if 'voter_file_id' in request.session:
            del request.session['voter_file_id']
        no_link = bool(request.GET.get("no-link", False))
        request.session['no_link'] = no_link
        return render_template(request, 'election_poll_voters_upload',
                               common_context)
コード例 #17
0
    def questions_update_view(self, request, election, poll):
        from zeus.utils import poll_reverse
        from zeus.forms import StvForm, DEFAULT_ANSWERS_COUNT

        if not poll.questions_data:
            poll.questions_data = [{}]

        poll.questions_data[0]['departments_data'] = election.departments
        initial = poll.questions_data

        extra = 1
        if poll.questions_data:
            extra = 0

        questions_formset = formset_factory(StvForm,
                                            extra=extra,
                                            can_delete=True,
                                            can_order=True)

        if request.method == 'POST':
            formset = questions_formset(request.POST, initial=initial)

            if formset.is_valid():
                questions_data = []

                for question in formset.cleaned_data:
                    if not question:
                        continue
                    # force sort of answers by extracting index from answer key.
                    # cast answer index to integer, otherwise answer_10 would
                    # be placed before answer_2
                    answer_index = lambda a: int(a[0].replace('answer_', ''))
                    isanswer = lambda a: a[0].startswith('answer_')

                    answer_values = list(
                        filter(isanswer, iter(question.items())))
                    sorted_answers = sorted(answer_values, key=answer_index)

                    answers = [json.loads(x[1])[0] for x in sorted_answers]
                    departments = [json.loads(x[1])[1] for x in sorted_answers]

                    final_answers = []
                    for a, d in zip(answers, departments):
                        final_answers.append(a + ':' + d)
                    question['answers'] = final_answers
                    for k in list(question.keys()):
                        if k in ['DELETE', 'ORDER']:
                            del question[k]

                    questions_data.append(question)

                poll.questions_data = questions_data
                poll.update_answers()
                poll.logger.info("Poll ballot updated")
                poll.eligibles_count = int(
                    formset.cleaned_data[0]['eligibles'])
                poll.has_department_limit = formset.cleaned_data[0][
                    'has_department_limit']
                poll.department_limit = int(
                    formset.cleaned_data[0]['department_limit'])
                poll.save()

                url = poll_reverse(poll, 'questions')
                return HttpResponseRedirect(url)
        else:
            formset = questions_formset(initial=initial)

        context = {
            'default_answers_count': DEFAULT_ANSWERS_COUNT,
            'formset': formset,
            'max_questions_limit': 1,
            'election': election,
            'poll': poll,
            'module': self
        }
        set_menu('questions', context)

        tpl = 'election_modules/stv/election_poll_questions_manage'
        return render_template(request, tpl, context)