Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature/priority voting #61

Merged
merged 14 commits into from
Dec 18, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion decide/booth/templates/booth/booth.html
Original file line number Diff line number Diff line change
Expand Up @@ -266,7 +266,7 @@ <h2 class="voting-desc">[[ voting.question.desc ]]</h2>
this.succesVote = false;
this.showAlert("danger", '{% trans "Error: " %}' + error);
});
},
},
showAlert(lvl, msg) {
this.alertLvl = lvl;
this.alertMsg = msg;
Expand Down
67 changes: 46 additions & 21 deletions decide/mixnet/mixcrypt.py
Original file line number Diff line number Diff line change
Expand Up @@ -140,27 +140,47 @@ def decrypt(self, c):

def multiple_decrypt(self, msgs, last=True):
msgs2 = []
for a, b in msgs:
clear = self.decrypt((a, b))
if last:
msg = clear
else:
msg = (a, clear)
msgs2.append(msg)
if len(msgs[0]) == 2:
for a, b in msgs:
clear = self.decrypt((a, b))
if last:
msg = clear
else:
msg = (a, clear)
msgs2.append(msg)
elif len(msgs[0]) == 3:
for a, b, priority in msgs:
clear = self.decrypt((a, b))
if last:
msg = clear
else:
msg = (a, clear, priority)
msgs2.append(msg)
return msgs2

def shuffle_decrypt(self, msgs, last=True):
msgs2 = msgs.copy()
msgs3 = []
while msgs2:
n = random.StrongRandom().randint(0, len(msgs2) - 1)
a, b = msgs2.pop(n)
clear = self.decrypt((a, b))
if last:
msg = clear
else:
msg = (a, clear)
msgs3.append(msg)
if len(msgs2[0]) == 3:
while msgs2:
n = random.StrongRandom().randint(0, len(msgs2) - 1)
a, b, priority = msgs2.pop(n)
clear = self.decrypt((a, b))
if last:
msg = (clear, priority) # Return a list of two elements
else:
msg = (a, clear, priority) # Return a list of three elements
msgs3.append(msg)
else:
while msgs2:
n = random.StrongRandom().randint(0, len(msgs2) - 1)
a, b = msgs2.pop(n)
clear = self.decrypt((a, b))
if last:
msg = clear
else:
msg = (a, clear)
msgs3.append(msg)

return msgs3

Expand All @@ -185,11 +205,16 @@ def reencrypt(self, cipher, pubkey=None):
else:
k = self.k

a, b = map(int, cipher)
a1, b1 = map(int, self.encrypt(1, k=k))
p = int(k.p)

return ((a * a1) % p, (b * b1) % p)
if len(cipher) == 2:
a, b = map(int, cipher)
a1, b1 = map(int, self.encrypt(1, k=k))
p = int(k.p)
return ((a * a1) % p, (b * b1) % p)
elif len(cipher) == 3:
a, b, priority = map(int, cipher)
a1, b1 = map(int, self.encrypt(1, k=k))
p = int(k.p)
return ((a * a1) % p, (b * b1) % p, priority)

def gen_perm(self, l):
x = list(range(l))
Expand Down
17 changes: 17 additions & 0 deletions decide/store/migrations/0006_voteoption_p.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
# Generated by Django 4.1 on 2023-11-23 11:56

from django.db import migrations, models


class Migration(migrations.Migration):
dependencies = [
("store", "0005_rename_vote_id_voteoption_vote"),
]

operations = [
migrations.AddField(
model_name="voteoption",
name="p",
field=models.PositiveIntegerField(blank=True, null=True),
),
]
1 change: 1 addition & 0 deletions decide/store/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,3 +16,4 @@ class VoteOption(models.Model):
vote = models.ForeignKey(Vote, related_name="options", on_delete=models.CASCADE)
a = BigBigField()
b = BigBigField()
p = models.PositiveIntegerField(blank=True, null=True)
2 changes: 1 addition & 1 deletion decide/store/serializers.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
class VoteOptionSerializer(serializers.HyperlinkedModelSerializer):
class Meta:
model = VoteOption
fields = ("a", "b")
fields = ("a", "b", "p")


class VoteSerializer(serializers.HyperlinkedModelSerializer):
Expand Down
52 changes: 50 additions & 2 deletions decide/store/tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ def gen_votes_single(self):
self.logout()
return votings, users

def gen_voting_multiple_options(self, pk):
"""def gen_voting_multiple_options(self, pk):
question = Question(desc="multiple_options_question", question_type="M")
question.save()
voting = Voting(
Expand All @@ -73,6 +73,18 @@ def gen_voting_multiple_options(self, pk):
start_date=timezone.now(),
end_date=timezone.now() + datetime.timedelta(days=1),
)
voting.save()"""

def gen_voting_priority(self, pk):
question = Question(desc="priority_question", question_type="P")
question.save()
voting = Voting(
pk=pk,
name="voting_priority",
question=question,
start_date=timezone.now(),
end_date=timezone.now() + datetime.timedelta(days=1),
)
voting.save()

def test_gen_vote_invalid_single(self):
Expand Down Expand Up @@ -100,7 +112,7 @@ def test_store_vote_single(self):
self.assertEqual(Vote.objects.first().options.first().a, CTE_A)
self.assertEqual(Vote.objects.first().options.first().b, CTE_B)

def test_store_vote_multiple_options(self):
"""def test_store_vote_multiple_options(self):
VOTING_PK = 346
census = Census(voting_id=VOTING_PK, voter_id=2)
census.save()
Expand All @@ -127,7 +139,43 @@ def test_store_vote_multiple_options(self):
self.assertEqual(options[1].a, 40)
self.assertEqual(options[1].b, 50)
self.assertEqual(options[2].a, 60)
self.assertEqual(options[2].b, 70)"""

def test_store_vote_priority(self):
VOTING_PK = 347
census = Census(voting_id=VOTING_PK, voter_id=2)
census.save()
self.gen_voting_priority(VOTING_PK)
data = {
"voting": VOTING_PK,
"voter": 2,
"vote": [
{"a": 20, "b": 30, "p": 2},
{"a": 40, "b": 50, "p": 1},
{"a": 60, "b": 70, "p": 3},
],
}
user = self.get_or_create_user(2)
self.login(user=user.username)
response = self.client.post("/store/", data, format="json")
self.assertEqual(response.status_code, 200)

self.assertEqual(Vote.objects.count(), 1)
self.assertEqual(Vote.objects.first().voting_id, VOTING_PK)
self.assertEqual(Vote.objects.first().voter_id, 2)
self.assertEqual(Vote.objects.first().options.count(), 3)

# Validate the options
options = Vote.objects.first().options.all()
self.assertEqual(options[0].a, 20)
self.assertEqual(options[0].b, 30)
self.assertEqual(options[0].p, 2)
self.assertEqual(options[1].a, 40)
self.assertEqual(options[1].b, 50)
self.assertEqual(options[1].p, 1)
self.assertEqual(options[2].a, 60)
self.assertEqual(options[2].b, 70)
self.assertEqual(options[2].p, 3)

def test_vote(self):
self.gen_votes_single()
Expand Down
21 changes: 20 additions & 1 deletion decide/store/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,10 @@ def post(self, request):
uid = request.data.get("voter")
vote = request.data.get("vote")

if voting[0]["question"]["question_type"] == "M" and not isinstance(vote, list):
if (
voting[0]["question"]["question_type"] == "M"
or voting[0]["question"]["question_type"] == "P"
) and not isinstance(vote, list):
return Response({}, status=status.HTTP_400_BAD_REQUEST)

if not vid or not uid or not vote:
Expand Down Expand Up @@ -106,6 +109,22 @@ def post(self, request):
vote_option.a = a
vote_option.b = b

vote_option.save()
v.save()
elif voting[0]["question"]["question_type"] == "P":
v, _ = Vote.objects.get_or_create(voting_id=vid, voter_id=uid)
# Delete previous options
VoteOption.objects.filter(vote=v).delete()
for option in vote:
a = option.get("a")
b = option.get("b")
p = option.get("p")

vote_option = VoteOption(vote=v)
vote_option.a = a
vote_option.b = b
vote_option.p = p

vote_option.save()
v.save()

Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# Generated by Django 4.1 on 2023-11-12 16:21
# Generated by Django 4.1 on 2023-11-23 10:53

from django.db import migrations, models

Expand Down
21 changes: 21 additions & 0 deletions decide/voting/migrations/0007_alter_question_question_type.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
# Generated by Django 4.1 on 2023-11-23 10:59

from django.db import migrations, models


class Migration(migrations.Migration):
dependencies = [
("voting", "0006_remove_questionoption_question_type_and_more"),
]

operations = [
migrations.AlterField(
model_name="question",
name="question_type",
field=models.CharField(
choices=[("S", "Single"), ("M", "Multiple"), ("P", "Priority")],
default="S",
max_length=1,
),
),
]
12 changes: 12 additions & 0 deletions decide/voting/migrations/0012_merge_20231217_1712.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
# Generated by Django 4.1 on 2023-12-17 17:12

from django.db import migrations


class Migration(migrations.Migration):
dependencies = [
("voting", "0007_alter_question_question_type"),
("voting", "0011_alter_question_question_type"),
]

operations = []
Loading