Basic ticket dragging #7

This commit is contained in:
RunasSudo 2017-12-11 10:55:01 +10:30
parent c0752f71ed
commit 6bb2dfddcb
Signed by: RunasSudo
GPG Key ID: 7234E476BF21C61A
8 changed files with 107 additions and 25 deletions

View File

@ -110,14 +110,16 @@ class Result(EmbeddedObject):
pass
class ListChoiceQuestion(Question):
choices = ListField(StringField())
_ver = StringField(default='0.4')
choices = EmbeddedObjectListField()
min_choices = IntField()
max_choices = IntField()
def pretty_answer(self, answer):
if len(answer.choices) == 0:
return '(blank votes)'
return ', '.join([self.choices[choice] for choice in answer.choices])
return ', '.join([self.choices[choice].name for choice in answer.choices])
def max_bits(self):
answer = self.answer_type(choices=list(range(len(self.choices))))
@ -135,6 +137,14 @@ class PreferentialAnswer(Answer):
class PreferentialQuestion(ListChoiceQuestion):
answer_type = PreferentialAnswer
class Choice(EmbeddedObject):
name = StringField()
party = StringField(default=None)
class Ticket(EmbeddedObject):
name = StringField()
choices = EmbeddedObjectListField()
class RawResult(Result):
plaintexts = ListField(EmbeddedObjectListField())
answers = EmbeddedObjectListField()

View File

@ -56,10 +56,10 @@ class ElectionTestCase(EosTestCase):
# Check _instance
self.assertEqual(voter._instance, (election.voters, i))
question = ApprovalQuestion(prompt='President', choices=['John Smith', 'Joe Bloggs', 'John Q. Public'])
question = ApprovalQuestion(prompt='President', choices=[Choice(name='John Smith'), Choice(name='Joe Bloggs'), Choice(name='John Q. Public')])
election.questions.append(question)
question = ApprovalQuestion(prompt='Chairman', choices=['John Doe', 'Andrew Citizen'])
question = ApprovalQuestion(prompt='Chairman', choices=[Choice(name='John Doe'), Choice(name='Andrew Citizen')])
election.questions.append(question)
election.save()

View File

@ -252,10 +252,10 @@ class ElectionTestCase(EosTestCase):
election.sk = EGPrivateKey.generate()
election.public_key = election.sk.public_key
question = ApprovalQuestion(prompt='President', choices=['John Smith', 'Joe Bloggs', 'John Q. Public'])
question = ApprovalQuestion(prompt='President', choices=[Choice(name='John Smith'), Choice(name='Joe Bloggs'), Choice(name='John Q. Public')])
election.questions.append(question)
question = ApprovalQuestion(prompt='Chairman', choices=['John Doe', 'Andrew Citizen'])
question = ApprovalQuestion(prompt='Chairman', choices=[Choice(name='John Doe'), Choice(name='Andrew Citizen')])
election.questions.append(question)
election.save()

View File

@ -123,10 +123,16 @@ def setup_test_election():
election.sk = EGPrivateKey.generate()
election.public_key = election.sk.public_key
question = PreferentialQuestion(prompt='President', choices=['John Smith', 'Joe Bloggs', 'John Q. Public'], min_choices=0, max_choices=3)
question = PreferentialQuestion(prompt='President', choices=[
Ticket(name='ACME Party', choices=[
Choice(name='John Smith'),
Choice(name='Joe Bloggs', party='Independent ACME')
]),
Choice(name='John Q. Public')
], min_choices=0, max_choices=3)
election.questions.append(question)
question = ApprovalQuestion(prompt='Chairman', choices=['John Doe', 'Andrew Citizen'], min_choices=0, max_choices=1)
question = ApprovalQuestion(prompt='Chairman', choices=[Choice(name='John Doe'), Choice(name='Andrew Citizen')], min_choices=0, max_choices=1)
election.questions.append(question)
election.save()

View File

@ -51,13 +51,13 @@
}
.preferential-choices .dragarea {
min-height: 3em;
min-height: 2em;
margin-top: 0.5em;
}
.preferential-choices .dragarea-hint:first-child:last-child {
width: calc(100% - 1em);
height: 3em;
height: 2em;
box-sizing: border-box;
z-index: -100;
border: 1px dashed #555;
@ -71,11 +71,13 @@
.preferential-choice {
background-color: #eee;
font-size: 1.1rem;
margin-top: 0.5em;
display: flex;
align-items: center;
min-height: 3em;
min-height: 2em;
}
.ticket-choices .preferential-choice {
margin-top: 0em;
}
.preferential-choice:first-child {
@ -94,11 +96,23 @@
background-color: #eee;
}
.preferential-choice .number, .preferential-choice .name {
.preferential-choice .number, .preferential-choice .content {
padding: 0.5em 0 0.5em 0.5em;
}
.ticket-choices .number, .ticket-choices .content {
padding: 0 0 0 0.5em;
}
.preferential-choice .number {
width: 2em;
text-align: center;
}
.preferential-choice .party-name {
font-style: italic;
}
.ticket > .content > .party-name {
min-height: 2em;
}

View File

@ -30,16 +30,41 @@
<div class="ui hidden message" id="message-max-choices">
<p>You have now selected the maximum allowed number of choices. If you wish to add different choices, you must deselect some choices by dragging them from the blue box back to the grey box.</p>
</div>
<div class="ui hidden error message" id="message-too-many-choices">
<p>You have selected more than the maximum allowed number of choices. To proceed, you must deselect some choices by dragging them from the blue box back to the grey box.</p>
</div>
{% macro printchoice(choice, ticket=None) %}
<div class="preferential-choice" data-choiceno="{{ loop.index0 }}">
<div class="number"></div>
<div class="content">
<div class="candidate-name">{{ choice.name }}</div>
{% if (ticket and choice.party and choice.party != ticket.name) or (not ticket and choice.party) %}<div class="party-name">{{ choice.party }}</div>{% endif %}
</div>
</div>
{% endmacro %}
<div id="question-choices-remaining" class="preferential-choices">
<div>Options not yet voted for:</div>
<div class="dragarea">
<div class="dragarea-hint"></div>
{% for choice in election.questions.__getitem__(questionNum).choices.impl %}
<div class="preferential-choice" data-choiceno="{{ loop.index0 }}">
<div class="number"></div>
<div class="name">{{ choice }}</div>
</div>
{% if choice.choices %}
{# Ticket #}
<div class="preferential-choice ticket" data-choiceno="{{ loop.index0 }}">
<div class="number"></div>
<div class="content">
<div class="party-name">{{ choice.name }}</div>
<div class="ticket-choices">
{% for choice2 in choice.choices.impl %}
{{ printchoice(choice2, choice) }}
{% endfor %}
</div>
</div>
</div>
{% else %}
{{ printchoice(choice) }}
{% endif %}
{% endfor %}
</div>
</div>
@ -52,17 +77,28 @@
$(".preferential-choices .preferential-choice .number").each(function(i, el) {
$(el).text("");
});
var selectedChoices = $("#question-choices-selected .preferential-choice .number");
var selectedChoices = $("#question-choices-selected .dragarea > .preferential-choice > .number");
selectedChoices.each(function(i, el) {
$(el).text(i + 1);
});
if (selectedChoices.length >= election.questions.__getitem__(booth.questionNum).max_choices) {
var selectedCandidates = $("#question-choices-selected .preferential-choice:not(.ticket) .number");
if (selectedCandidates.length >= election.questions.__getitem__(booth.questionNum).max_choices) {
// Prevent making any more selections
allowAdding = false;
$("#message-max-choices").removeClass("hidden");
if (selectedCandidates.length > election.questions.__getitem__(booth.questionNum).max_choices) {
// Prevent progression
$(".primary.button").addClass("disabled");
$("#message-too-many-choices").removeClass("hidden");
} else {
$(".primary.button").removeClass("disabled");
$("#message-max-choices").removeClass("hidden");
}
} else {
allowAdding = true;
$("#message-max-choices").addClass("hidden");
$("#message-too-many-choices").addClass("hidden");
$(".primary.button").removeClass("disabled");
}
}
@ -75,17 +111,21 @@
}
var dragulaChoices = dragula(
[document.querySelector("#question-choices-selected .dragarea"), document.querySelector("#question-choices-remaining .dragarea")],
[document.querySelector("#question-choices-selected .dragarea"), document.querySelector("#question-choices-remaining .dragarea")].concat([].slice.apply(document.querySelectorAll(".ticket-choices"))),
{
moves: function(el, source, handle, sibling) {
if ("dragarea-hint" in el.classList) {
return false;
}
//if ("ticket" in el.classList && !("party-name" in handle.classList)) {
// return false;
//}
if ($.contains(document.querySelector("#question-choices-remaining"), el)) {
return allowAdding;
}
return true;
}
},
mirrorContainer: document.querySelector("#question-choices-remaining")
}
);

View File

@ -20,6 +20,6 @@
<ul class="ui list">
{% for choice in question.choices %}
<li>{{ choice }}</li>
<li>{{ choice.name }}{% if choice.party %} – {{ choice.party }}{% endif %}</li>
{% endfor %}
</ul>

View File

@ -20,6 +20,18 @@
<ul class="ui list">
{% for choice in question.choices %}
<li>{{ choice }}</li>
{% if choice.choices %}
{# Ticket #}
<li>
{{ choice.name }}
<ul>
{% for choice2 in choice.choices %}
<li>{{ choice2.name }}{% if choice2.party and choice2.party != choice.name %} – {{ choice2.party }}{% endif %}</li>
{% endfor %}
</ul>
</li>
{% else %}
<li>{{ choice.name }}{% if choice.party %} – {{ choice.party }}{% endif %}</li>
{% endif %}
{% endfor %}
</ul>