From fdbd5ee6db2a2ea88c670a2b7b4c94539eebb9eb Mon Sep 17 00:00:00 2001 From: Ben Adida <ben@adida.net> Date: Sun, 19 Jan 2014 15:33:42 -0800 Subject: [PATCH] implemented answer reordering and enabled the field in the election form --- helios/forms.py | 2 +- heliosbooth/templates/question.html | 6 ++-- heliosbooth/vote.html | 49 +++++++++++++++++++++++++++-- 3 files changed, 50 insertions(+), 7 deletions(-) diff --git a/helios/forms.py b/helios/forms.py index c1ccea3..a5263fa 100644 --- a/helios/forms.py +++ b/helios/forms.py @@ -14,7 +14,7 @@ class ElectionForm(forms.Form): election_type = forms.ChoiceField(label="type", choices = Election.ELECTION_TYPES) use_voter_aliases = forms.BooleanField(required=False, initial=False, help_text='If selected, voter identities will be replaced with aliases, e.g. "V12", in the ballot tracking center') #use_advanced_audit_features = forms.BooleanField(required=False, initial=True, help_text='disable this only if you want a simple election with reduced security but a simpler user interface') - #randomize_answer_order = forms.BooleanField(required=False, initial=False, help_text='enable this if you want the answers to questions to appear in random order for each voter') + randomize_answer_order = forms.BooleanField(required=False, initial=False, help_text='enable this if you want the answers to questions to appear in random order for each voter') private_p = forms.BooleanField(required=False, initial=False, label="Private?", help_text='A private election is only visible to registered voters.') help_email = forms.CharField(required=False, initial="", label="Help Email Address", help_text='An email address voters should contact if they need help.') diff --git a/heliosbooth/templates/question.html b/heliosbooth/templates/question.html index 1740631..4d9af47 100644 --- a/heliosbooth/templates/question.html +++ b/heliosbooth/templates/question.html @@ -20,12 +20,12 @@ as many of the choices as you approve of </p> {#foreach $T.question.answers as answer} -<div id="answer_label_{$T.question_num}_{$T.answer$index}"><input type="checkbox" class="ballot_answer" id="answer_{$T.question_num}_{$T.answer$index}" name="answer_{$T.question_num}_{$T.answer$index}" value="yes" onclick="BOOTH.click_checkbox({$T.question_num}, {$T.answer$index}, this.checked);" /> {$T.answer} +<div id="answer_label_{$T.question_num}_{$T.answer_ordering[$T.answer$index]}"><input type="checkbox" class="ballot_answer" id="answer_{$T.question_num}_{$T.answer_ordering[$T.answer$index]}" name="answer_{$T.question_num}_{$T.answer_ordering[$T.answer$index]}" value="yes" onclick="BOOTH.click_checkbox({$T.question_num}, {$T.answer_ordering[$T.answer$index]}, this.checked);" /> {$T.question.answers[$T.answer_ordering[$T.answer$index]]} -{#if $T.question.answer_urls && $T.question.answer_urls[$T.answer$index] && $T.question.answer_urls[$T.answer$index] != ""} +{#if $T.question.answer_urls && $T.question.answer_urls[$T.answer_ordering[$T.answer$index]] && $T.question.answer_urls[$T.answer_ordering[$T.answer$index]] != ""} <span style="font-size: 12pt;"> -[<a target="_blank" href="{$T.question.answer_urls[$T.answer$index]}">more info</a>] +[<a target="_blank" href="{$T.question.answer_urls[$T.answer_ordering[$T.answer$index]]}">more info</a>] </span> {#/if} </div> diff --git a/heliosbooth/vote.html b/heliosbooth/vote.html index 38f40c3..2667fa9 100644 --- a/heliosbooth/vote.html +++ b/heliosbooth/vote.html @@ -159,7 +159,7 @@ function escape_html(content) { return $('<div/>').text(content).html(); } -BOOTH.setup_election = function(raw_json) { +BOOTH.setup_election = function(raw_json, election_metadata) { // IMPORTANT: we use the raw JSON for safer hash computation // so that we are using the JSON serialization of the SERVER // to compute the hash, not the JSON serialization in JavaScript. @@ -182,6 +182,26 @@ BOOTH.setup_election = function(raw_json) { BOOTH.election[field] = escape_html(BOOTH.election[field]); }); + // TODO: escape question and answers + + // whether the election wants candidate order randomization or not + // we set up an ordering array so that the rest of the code is + // less error-prone. + BOOTH.election.question_answer_orderings = []; + $(BOOTH.election.questions).each(function(i, question) { + var ordering = new Array(question.answers.length); + + // initialize array so it is the identity permutation + $(ordering).each(function(j, answer) {ordering[j]=j;}); + + // if we want reordering, then we shuffle the array + if (election_metadata && election_metadata.randomize_answer_order) { + shuffleArray(ordering); + } + + BOOTH.election.question_answer_orderings[i] = ordering; + }); + $('#header').processTemplate({'election' : BOOTH.election, 'election_metadata': BOOTH.election_metadata}); $('#footer').processTemplate({'election' : BOOTH.election, 'election_metadata': BOOTH.election_metadata}); BOOTH.setup_ballot(); @@ -246,6 +266,28 @@ BOOTH.previous = function(question_num) { } }; +// http://stackoverflow.com/questions/2450954/how-to-randomize-shuffle-a-javascript-array +function shuffleArray(array) { + var currentIndex = array.length + , temporaryValue + , randomIndex + ; + + // While there remain elements to shuffle... + while (0 !== currentIndex) { + // Pick a remaining element... + randomIndex = Math.floor(Math.random() * currentIndex); + currentIndex -= 1; + + // And swap it with the current element. + temporaryValue = array[currentIndex]; + array[currentIndex] = array[randomIndex]; + array[randomIndex] = temporaryValue; + } + + return array; +} + BOOTH.show_question = function(question_num) { BOOTH.started_p = true; @@ -256,7 +298,8 @@ BOOTH.show_question = function(question_num) { BOOTH.show_progress('1'); BOOTH.show($('#question_div')).processTemplate({'question_num' : question_num, 'last_question_num' : BOOTH.election.questions.length - 1, - 'question' : BOOTH.election.questions[question_num], 'show_reviewall' : BOOTH.all_questions_seen + 'question' : BOOTH.election.questions[question_num], 'show_reviewall' : BOOTH.all_questions_seen, + 'answer_ordering': BOOTH.election.question_answer_orderings[question_num] }); // fake clicking through the answers, to trigger the disabling if need be @@ -330,7 +373,7 @@ BOOTH.load_and_setup_election = function(election_url) { // let's also get the metadata $.getJSON(election_url + "/meta", {}, function(election_metadata) { BOOTH.election_metadata = election_metadata; - BOOTH.setup_election(raw_json); + BOOTH.setup_election(raw_json, election_metadata); BOOTH.show_election(); BOOTH.election_url = election_url; }); -- GitLab