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)
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)
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)
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)
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'))
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)
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)
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)
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)
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")
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}))
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)
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'))
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'))
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)
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)
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)