Skip to content

Commit

Permalink
Merge branch 'develop' into feature/multivoting-visualization
Browse files Browse the repository at this point in the history
  • Loading branch information
AntonioRodriguezRuiz committed Dec 17, 2023
2 parents a351b44 + d50e29c commit 8933b1f
Show file tree
Hide file tree
Showing 9 changed files with 223 additions and 43 deletions.
3 changes: 2 additions & 1 deletion decide/authentication/views.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import socket
from django.contrib.auth import authenticate, login, logout
from django.contrib.auth.views import PasswordChangeView
from django.shortcuts import get_object_or_404, redirect, render
Expand Down Expand Up @@ -90,7 +91,7 @@ def post(self, request):
encoded = base64.b64encode(
bytes(usernameToEncode, encoding="utf-8")
).decode("utf-8")
urlVerificar = f"{Site.objects.get_current().domain}/verificar/{encoded}"
urlVerificar = f"{request._get_raw_host()}/verificar/{encoded}"
mailMessage.dynamic_template_data = {
"urlVerificar": urlVerificar,
"username": f'{form.cleaned_data.get("username")}',
Expand Down
9 changes: 9 additions & 0 deletions decide/local_settings.deploy.py
Original file line number Diff line number Diff line change
Expand Up @@ -46,9 +46,18 @@
STATICFILES_STORAGE = "whitenoise.storage.CompressedManifestStaticFilesStorage"

BASEURL = "https://{}".format(os.environ.get("RENDER_EXTERNAL_HOSTNAME"))

SITE_ID = int(os.getenv("DJANGO_SITE_ID", "2"))
SOCIAL_AUTH_GITHUB_KEY = os.getenv("GITHUB_KEY", "")
SOCIAL_AUTH_GITHUB_SECRET = os.getenv("GITHUB_SECRET", "")
RECAPTCHA_PUBLIC_KEY = os.getenv("RECAPTCHA_PUBLIC_KEY", "")
RECAPTCHA_PRIVATE_KEY = os.getenv("RECAPTCHA_PRIVATE_KEY", "")
os.environ["RECAPTCHA_PUBLIC_KEY"] = (
RECAPTCHA_PUBLIC_KEY or "6LeIxAcTAAAAAJcZVRqyHh71UMIEGNQ_MXjiZKhI"
)
os.environ["RECAPTCHA_PRIVATE_KEY"] = (
RECAPTCHA_PRIVATE_KEY or "6LeIxAcTAAAAAGG-vFI1TnRWxMZNFuojJ4WifJWe"
)

APIS = {
"authentication": BASEURL,
Expand Down
5 changes: 4 additions & 1 deletion decide/store/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,10 @@ def post(self, request):
return Response({}, status=status.HTTP_401_UNAUTHORIZED)

with transaction.atomic():
if voting[0]["question"]["question_type"] == "S":
if (
voting[0]["question"]["question_type"] == "S"
or voting[0]["question"]["question_type"] == "B"
):
v, _ = Vote.objects.get_or_create(voting_id=vid, voter_id=uid)
# Delete previous options
VoteOption.objects.filter(vote=v).delete()
Expand Down
21 changes: 21 additions & 0 deletions decide/voting/migrations/0011_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-12-16 13:56

from django.db import migrations, models


class Migration(migrations.Migration):
dependencies = [
("voting", "0010_question_voteblank"),
]

operations = [
migrations.AlterField(
model_name="question",
name="question_type",
field=models.CharField(
choices=[("S", "Single"), ("M", "Multiple"), ("B", "Boolean")],
default="S",
max_length=1,
),
),
]
55 changes: 51 additions & 4 deletions decide/voting/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,11 @@
from django.db.models import JSONField
from django.db.models.signals import post_save
from django.dispatch import receiver
from django.core.exceptions import BadRequest


class Question(models.Model):
QUESTION_TYPES = (
("S", "Single"),
("M", "Multiple"),
)
QUESTION_TYPES = (("S", "Single"), ("M", "Multiple"), ("B", "Boolean"))

question_type = models.CharField(max_length=1, choices=QUESTION_TYPES, default="S")
desc = models.TextField()
Expand All @@ -32,6 +30,43 @@ def save(self, **kwargs):
)
enBlanco.save()
self.options.add(enBlanco)
if self.question_type == "B":
if (
QuestionOption.objects.filter(
question__id=self.id, option__startswith="Sí"
).count()
== 0
):
op1 = QuestionOption(
question=self, number=self.options.count() + 1, option="Sí"
)
op1.save()
self.options.add(op1)
if (
QuestionOption.objects.filter(
question__id=self.id, option__startswith="No"
).count()
== 0
):
op2 = QuestionOption(
question=self, number=self.options.count() + 1, option="No"
)
op2.save()
self.options.add(op2)
if (
self.voteBlank
and QuestionOption.objects.filter(
question_id=self.id, option__startswith="Voto En Blanco"
).count()
== 0
):
enBlanco = QuestionOption(
question=self,
number=self.options.count() + 1,
option="Voto En Blanco",
)
enBlanco.save()
self.options.add(enBlanco)
return super().save()

def __str__(self):
Expand All @@ -47,6 +82,18 @@ class QuestionOption(models.Model):
hidden = models.BooleanField(default=False)

def save(self):
if self.question.question_type == "B" and self.question.options.count() > 1:
if self.question.voteBlank:
if (
self.option != "Sí"
and self.option != "No"
and self.option != "Voto En Blanco"
):
raise BadRequest(
"Boolean questions with white votes can only have 'Sí', 'No', or 'Voto En Blanco' options."
)
else:
raise BadRequest("Boolean questions cannot have any options.")
if not self.number:
self.number = self.question.options.count() + 1
else:
Expand Down
81 changes: 81 additions & 0 deletions decide/voting/tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
from selenium.webdriver.common.by import By
from voting.models import Question, QuestionOption, Voting
from selenium.webdriver.chrome.webdriver import WebDriver
from django.core.exceptions import BadRequest


class VotingTestCase(BaseTestCase):
Expand Down Expand Up @@ -238,6 +239,32 @@ def test_create_voting_from_api(self):
response = self.client.post("/voting/", data, format="json")
self.assertEqual(response.status_code, 201)

def test_create_boolean_voting_from_api(self):
data = {"name": "Example boolean"}
response = self.client.post("/voting/", data, format="json")
self.assertEqual(response.status_code, 401)

# login with user no admin
self.login(user="noadmin")
response = mods.post("voting", params=data, response=True)
self.assertEqual(response.status_code, 403)

# login with user admin
self.login()
response = mods.post("voting", params=data, response=True)
self.assertEqual(response.status_code, 400)

data = {
"name": "Example boolean",
"desc": "Description example boolean",
"question_type": "B",
"question": "Do you agree?",
"question_opt": ["Sí", "No"],
}

response = self.client.post("/voting/", data, format="json")
self.assertEqual(response.status_code, 201)

def test_update_voting(self):
voting = self.create_voting()

Expand Down Expand Up @@ -591,3 +618,57 @@ def createCensusEmptyError(self):
self.cleaner.current_url
== self.live_server_url + "/admin/voting/question/add/"
)


class BooleanQuestionTestCase(BaseTestCase):
def setUp(self):
self.question = Question.objects.create(
question_type="B", desc="What is your favorite color?", voteBlank=False
)
self.question.save()

def test_save_boolean_question(self):
self.assertEqual(QuestionOption.objects.count(), 2)
self.assertEqual(QuestionOption.objects.first().option, "Sí")
self.assertEqual(QuestionOption.objects.last().option, "No")

def test_str_representation(self):
self.assertEqual(str(self.question), "What is your favorite color?")

def test_one_option_boolean_question(self):
self.question = Question.objects.create(
question_type="B", desc="What is your favorite color?", voteBlank=False
)
self.question.save()

opt = QuestionOption(question=self.question, option="option {}".format(1))
with self.assertRaises(BadRequest):
opt.save()

def test_multiple_option_boolean_question(self):
self.question = Question.objects.create(
question_type="B", desc="What is your favorite color?", voteBlank=False
)
self.question.save()

opt1 = QuestionOption(question=self.question, option="option {}".format(1))
opt2 = QuestionOption(question=self.question, option="option {}".format(2))
opt3 = QuestionOption(question=self.question, option="option {}".format(3))
with self.assertRaises(BadRequest):
opt1.save()
opt2.save()
opt3.save()


class BooleanWhiteTestCase(BaseTestCase):
def setUp(self):
self.question = Question.objects.create(
question_type="B", desc="What is your favorite color?", voteBlank=True
)
self.question.save()

def test_boolean_question_and_white(self):
self.assertEqual(QuestionOption.objects.count(), 3)
self.assertEqual(QuestionOption.objects.first().option, "Sí")
self.assertEqual(QuestionOption.objects.all()[1].option, "No")
self.assertEqual(QuestionOption.objects.last().option, "Voto En Blanco")
4 changes: 2 additions & 2 deletions loadtest/gen_census.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@


HOST = "http://localhost:8000"
USER = "admin"
PASS = "admin"
USER = "decideuser"
PASS = "decidepass123"
VOTING = 1


Expand Down
85 changes: 51 additions & 34 deletions loadtest/locustfile.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import json
import requests

from random import choice

Expand All @@ -7,6 +8,14 @@

HOST = "http://localhost:8000"
VOTING = 1
USER = "decideuser"
PASS = "decidepass123"


class LoadHomepage(TaskSet):
@task
def load_homepage(self):
self.client.get("/")


class DefVisualizer(TaskSet):
Expand All @@ -15,49 +24,51 @@ def index(self):
self.client.get("/visualizer/{0}/".format(VOTING))


class DefVoters(SequentialTaskSet):
class DefVotingCreation(TaskSet):
def on_start(self):
with open("voters.json") as f:
self.voters = json.loads(f.read())
self.voter = choice(list(self.voters.items()))
self.client.post("/authentication/login/", {"username": USER, "password": PASS})
response = self.client.get("/admin/voting/voting/add/")
self.csrftoken = response.cookies["csrftoken"]

@task
def login(self):
username, pwd = self.voter
self.token = self.client.post(
"/authentication/login/",
def create_voting(self):
self.client.post(
"/admin/voting/voting/add/",
{
"username": username,
"password": pwd,
"csrfmiddlewaretoken": self.csrftoken,
"title": "Test Voting locust",
"description": "This is a test voting",
"question": "1",
"auths": "1",
},
).json()
headers={"X-CSRFToken": self.csrftoken},
)

@task
def getuser(self):
self.usr = self.client.post("/authentication/getuser/", self.token).json()
print(str(self.user))

class DefUserCreation(TaskSet):
counter = 0

@task
def voting(self):
headers = {
"Authorization": "Token " + self.token.get("token"),
"content-type": "application/json",
}
def create_user(self):
DefUserCreation.counter += 1
self.client.post(
"/store/",
json.dumps(
{
"token": self.token.get("token"),
"vote": {"a": "12", "b": "64"},
"voter": self.usr.get("id"),
"voting": VOTING,
}
),
headers=headers,
"/register/",
{
"first_name": f"Pepito{DefUserCreation.counter}",
"last_name": f"Gonzalez{DefUserCreation.counter}",
"email": f"test{DefUserCreation.counter}@test.com",
"username": f"gonsale{DefUserCreation.counter}",
"password1": "decidepass123",
"password2": "decidepass123",
"g-recaptcha-response": "test",
},
)

def on_quit(self):
self.voter = None

class HomepageUser(HttpUser):
host = HOST
tasks = [LoadHomepage]
wait_time = between(3, 5)


class Visualizer(HttpUser):
Expand All @@ -66,7 +77,13 @@ class Visualizer(HttpUser):
wait_time = between(3, 5)


class Voters(HttpUser):
class VotingCreation(HttpUser):
host = HOST
tasks = [DefVotingCreation]
wait_time = between(3, 5)


class UserCreation(HttpUser):
host = HOST
tasks = [DefVoters]
tasks = [DefUserCreation]
wait_time = between(3, 5)
3 changes: 2 additions & 1 deletion requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -19,4 +19,5 @@ dj-database-url == 2.1.0
whitenoise==6.5.0
gunicorn==21.2.0
social-auth-app-django
sendgrid
locust==2.20.0
sendgrid

0 comments on commit 8933b1f

Please sign in to comment.