From 93295d31e1d338967cbc0d0b6233c898fe1c2052 Mon Sep 17 00:00:00 2001 From: MartaInes Date: Sun, 10 Dec 2023 19:43:29 +0100 Subject: [PATCH 01/11] feat: show different types of voting views --- decide/base/static/base/style.css | 6 +- decide/booth/static/booth/style.css | 48 +++++- decide/booth/templates/booth/booth.html | 186 ++++-------------------- decide/booth/views.py | 19 ++- 4 files changed, 93 insertions(+), 166 deletions(-) diff --git a/decide/base/static/base/style.css b/decide/base/static/base/style.css index 63e30ca5c..18f23de96 100644 --- a/decide/base/static/base/style.css +++ b/decide/base/static/base/style.css @@ -1,11 +1,11 @@ :root { - --primary-color: #3498db; - --primary-dark-color: #2980b9; + --primary-color: #be8c76; + --primary-dark-color: #af836f; --green-dark-color: #2ecc71; --green-color: #69dd9a; --red-dark-color: #e74c3c; --red-color: #ef8b80; - --neutral-background: #f5f5f5; + --neutral-background: #f9f7f6; --card-background: #ffffff; --card-semi-transparent: rgba(255, 255, 255, 0.9); --text-color: #333333 ; diff --git a/decide/booth/static/booth/style.css b/decide/booth/static/booth/style.css index b9f92d446..04ead024c 100644 --- a/decide/booth/static/booth/style.css +++ b/decide/booth/static/booth/style.css @@ -60,8 +60,20 @@ table { height: fit-content; width: fit-content; padding: 1em; - background-color: #efefef; + background-color: #ead4ca; + border-radius: 0.5em; +} + +.card-voting { + height: 25em; + width:50em; + align-self: center; + background-color: #ead4ca; + padding: 1em; + margin: 50px; border-radius: 0.5em; + gap: 20px; + position: relative; } .voting-card { @@ -76,7 +88,7 @@ table { } .voting-card:hover { - background-color: #e0e0e0; + background-color: #e5cfc6; cursor: pointer; transform: scale(1.01); } @@ -125,4 +137,36 @@ table { .no-votings { width: 100%; +} + +.btn-vote { + background-color: var(--primary-color); + position: absolute; + bottom: 0px; + right: 0px; + margin-bottom: 20px; + margin-right: 20px; +} + +.btn-vote:hover { + background-color: var(--primary-dark-color); +} + +.voting-title { + font-weight: bold; + margin-left: 20px; +} + +.voting-desc { + margin-left: 20px; + margin-top: 10px; +} + +.voting-options { + margin-left: 20px; + margin-top: 20px; +} + +.form-options { + margin-bottom: 15px; } \ No newline at end of file diff --git a/decide/booth/templates/booth/booth.html b/decide/booth/templates/booth/booth.html index e534acd34..533838756 100644 --- a/decide/booth/templates/booth/booth.html +++ b/decide/booth/templates/booth/booth.html @@ -2,138 +2,42 @@ {% load i18n static %} {% block extrahead %} - - - + + Decide | {% block title %} Vote {% endblock %} + + + {% endblock %} {% block content %} -
- - - - - - - - - - - - - - - - - - - -
- -
- - -
- - -
-

[[ voting.id ]] - [[ voting.name ]]

-

[[ voting.question.desc ]]

-
-
- - [[ opt.option ]] +
+
+
+

{{ voting.id }} - {{ voting.name }}

+

{{ voting.question.desc }}

+ +
+ {% for option in options %} + {% if question_type == "S" %} +
+ + {{ option.option }} +
+ {% else %} +
+ + {{ option.option }} +
+ {% endif %} + {% endfor %}
-
-
-
{% endblock %} @@ -151,7 +55,7 @@

[[ voting.question.desc ]]

- @@ -204,6 +108,7 @@

[[ voting.question.desc ]]

body: JSON.stringify(data), headers: { 'content-type': 'application/json', + 'X-CSRFToken': '{{ csrf_token }}', }, method: 'POST', }; @@ -221,41 +126,6 @@

[[ voting.question.desc ]]

} }); }, - onSubmitLogin(evt) { - evt.preventDefault(); - this.postData("{% url "gateway" "authentication" "/login/" %}", this.form) - .then(data => { - document.cookie = 'decide='+data.token+';'; - this.token = data.token; - this.getUser(); - this.alertShow = false; - }) - .catch(error => { - this.showAlert("danger", '{% trans "Error: " %}' + error); - }); - }, - getUser(evt) { - var data = {token: this.token}; - this.postData("{% url "gateway" "authentication" "/getuser/" %}", data) - .then(data => { - this.user = data; - this.signup = false; - this.alertShow = false; - }).catch(error => { - this.showAlert("danger", '{% trans "Error: " %}' + error); - }); - }, - decideLogout(evt) { - evt.preventDefault(); - var data = {token: this.token}; - this.postData("{% url "gateway" "authentication" "/logout/" %}", data); - this.token = null; - this.user = null; - this.alertShow = false; - document.cookie = 'decide=;'; - this.signup = true; - this.successVote = false; - }, decideEncrypt(s) { var bigmsg = BigInt.fromJSONObject(s); var cipher = ElGamal.encrypt(this.bigpk, bigmsg); diff --git a/decide/booth/views.py b/decide/booth/views.py index 59b1f359a..f23cd6111 100644 --- a/decide/booth/views.py +++ b/decide/booth/views.py @@ -4,10 +4,11 @@ from census.models import Census from django.conf import settings from django.contrib.auth.decorators import login_required +from django.contrib.auth.mixins import LoginRequiredMixin from django.http import Http404 -from django.shortcuts import render +from django.shortcuts import render, get_object_or_404, get_list_or_404 from django.views.generic import TemplateView -from voting.models import Voting +from voting.models import Voting, QuestionOption, Question def index(request): @@ -26,7 +27,8 @@ def voting_list(request): return render(request, "booth/voting-list.html", {"user_votings": user_votings}) -class BoothView(TemplateView): +class BoothView(LoginRequiredMixin, TemplateView): + login_url = '/signin' template_name = "booth/booth.html" def get_context_data(self, **kwargs): @@ -47,3 +49,14 @@ def get_context_data(self, **kwargs): context["KEYBITS"] = settings.KEYBITS return context + + def get(self, request, voting_id, **kwargs): + user = request.user + voting = get_object_or_404(Voting, pk=voting_id) + question_type = voting.question.question_type + options = voting.question.options.all() + try: + Census.objects.get(voting_id=voting_id, voter_id=user.id) + except Census.DoesNotExist: + raise Http404 + return render(request, "booth/booth.html", {"voting": voting, "options": options, "question_type": question_type}) From 6804ad8b543cd010b37d1711e5ac516771156049 Mon Sep 17 00:00:00 2001 From: MartaInes Date: Mon, 11 Dec 2023 08:47:23 +0100 Subject: [PATCH 02/11] feat: change of colors of the voting list --- decide/booth/static/booth/style.css | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/decide/booth/static/booth/style.css b/decide/booth/static/booth/style.css index 04ead024c..3c4446a60 100644 --- a/decide/booth/static/booth/style.css +++ b/decide/booth/static/booth/style.css @@ -60,7 +60,7 @@ table { height: fit-content; width: fit-content; padding: 1em; - background-color: #ead4ca; + background-color: #eedbd3; border-radius: 0.5em; } From 2ce26f53bdf43159001a2a6f4f21a80f9a37953d Mon Sep 17 00:00:00 2001 From: MartaInes Date: Thu, 14 Dec 2023 18:41:30 +0100 Subject: [PATCH 03/11] fix: voting form --- decide/booth/templates/booth/booth.html | 119 ++++++++++++------------ 1 file changed, 61 insertions(+), 58 deletions(-) diff --git a/decide/booth/templates/booth/booth.html b/decide/booth/templates/booth/booth.html index 533838756..25c02498e 100644 --- a/decide/booth/templates/booth/booth.html +++ b/decide/booth/templates/booth/booth.html @@ -2,42 +2,43 @@ {% load i18n static %} {% block extrahead %} - - Decide | {% block title %} Vote {% endblock %} - - - + + Decide | {% block title %} Vote {% endblock %} + + + {% endblock %} {% block content %} -
-
-
-

{{ voting.id }} - {{ voting.name }}

-

{{ voting.question.desc }}

-
-
- {% for option in options %} - {% if question_type == "S" %} -
- - {{ option.option }} -
- {% else %} -
- - {{ option.option }} -
- {% endif %} - {% endfor %} -
-
- +
+ +
+ +
+
+

[[ voting.name ]]

+

[[ voting.question.desc ]]

+
+
+ + + [[ opt.option ]] +
+
+ +
+
{% endblock %} @@ -62,53 +63,38 @@

{{ voting.question.desc }}

-{% endblock %} +{% endblock %} \ No newline at end of file From 01b51b1011d9c6b1fb0d333a2611f00cd4801f81 Mon Sep 17 00:00:00 2001 From: MartaInes Date: Thu, 14 Dec 2023 21:48:07 +0100 Subject: [PATCH 04/11] fix: mutioption vote --- decide/booth/templates/booth/booth.html | 155 +++++++++++++++++++++++- 1 file changed, 149 insertions(+), 6 deletions(-) diff --git a/decide/booth/templates/booth/booth.html b/decide/booth/templates/booth/booth.html index 25c02498e..8a903decc 100644 --- a/decide/booth/templates/booth/booth.html +++ b/decide/booth/templates/booth/booth.html @@ -14,6 +14,99 @@
+ + + + + + + + + + + + + + + + + + + +
+ + +
+ +
@@ -72,10 +165,15 @@

[[ voting.question.desc ]]

selected: [], successVote: false, alertShow: false, + signup: true, alertMsg: "", alertLvl: "info", - token: '{{ csrf_token }}', - user_id: '{{user.id}}', + token: null, + user: null, + form: { + username: '', + password: '' + }, bigpk: { p: BigInt.fromJSONObject(voting.pub_key.p.toString()), g: BigInt.fromJSONObject(voting.pub_key.g.toString()), @@ -86,11 +184,21 @@

[[ voting.question.desc ]]

} }, beforeMount() { + this.init() ElGamal.BITS = this.keybits; }, methods: { + init() { + var cookies = document.cookie.split("; "); + cookies.forEach((c) => { + var cs = c.split("="); + if (cs[0] == 'decide' && cs[1]) { + this.token = cs[1]; + this.getUser(); + } + }); + }, postData(url, data) { - // Default options are marked with * var fdata = { body: JSON.stringify(data), headers: { @@ -112,6 +220,41 @@

[[ voting.question.desc ]]

} }); }, + onSubmitLogin(evt) { + evt.preventDefault(); + this.postData("{% url "gateway" "authentication" "/login/" %}", this.form) + .then(data => { + document.cookie = 'decide='+data.token+';'; + this.token = data.token; + this.getUser(); + this.alertShow = false; + }) + .catch(error => { + this.showAlert("danger", '{% trans "Error: " %}' + error); + }); + }, + getUser(evt) { + var data = {token: this.token}; + this.postData("{% url "gateway" "authentication" "/getuser/" %}", data) + .then(data => { + this.user = data; + this.signup = false; + this.alertShow = false; + }).catch(error => { + this.showAlert("danger", '{% trans "Error: " %}' + error); + }); + }, + decideLogout(evt) { + evt.preventDefault(); + var data = {token: this.token}; + this.postData("{% url "gateway" "authentication" "/logout/" %}", data); + this.token = null; + this.user = null; + this.alertShow = false; + document.cookie = 'decide=;'; + this.signup = true; + this.successVote = false; + }, decideEncrypt(s) { var bigmsg = BigInt.fromJSONObject(s); var cipher = ElGamal.encrypt(this.bigpk, bigmsg); @@ -124,14 +267,14 @@

[[ voting.question.desc ]]

var data = { vote: {a: v.alpha.toString(), b: v.beta.toString()}, voting: this.voting.id, - voter: this.user_id, + voter: this.user.id, token: this.token } } else if (this.questionType === 'M') { - var selected = [] + var selected = [2,1] var vote = [] - for (i=0; i < selected.lenght; i++){ + for (i=0; i < selected.length; i++){ var v = this.decideEncrypt(selected[i].toString()) vote.push({a: v.alpha.toString(), b: v.beta.toString()}) } From eb8e8786efbf4c032871726fdfa7b1f284113908 Mon Sep 17 00:00:00 2001 From: MartaInes Date: Fri, 15 Dec 2023 08:29:16 +0100 Subject: [PATCH 05/11] fix: change in the BoothView --- decide/booth/views.py | 23 ++++++++--------------- 1 file changed, 8 insertions(+), 15 deletions(-) diff --git a/decide/booth/views.py b/decide/booth/views.py index f23cd6111..80fb84f7e 100644 --- a/decide/booth/views.py +++ b/decide/booth/views.py @@ -6,9 +6,9 @@ from django.contrib.auth.decorators import login_required from django.contrib.auth.mixins import LoginRequiredMixin from django.http import Http404 -from django.shortcuts import render, get_object_or_404, get_list_or_404 +from django.shortcuts import render, get_object_or_404 from django.views.generic import TemplateView -from voting.models import Voting, QuestionOption, Question +from voting.models import Voting def index(request): @@ -27,8 +27,7 @@ def voting_list(request): return render(request, "booth/voting-list.html", {"user_votings": user_votings}) -class BoothView(LoginRequiredMixin, TemplateView): - login_url = '/signin' +class BoothView(TemplateView): template_name = "booth/booth.html" def get_context_data(self, **kwargs): @@ -36,6 +35,9 @@ def get_context_data(self, **kwargs): vid = kwargs.get("voting_id", 0) try: + vote = get_object_or_404(Voting, pk=vid) + question_type = vote.question.question_type + r = mods.get("voting", params={"id": vid}) # Casting numbers to string to manage in javascript with BigInt # and avoid problems with js and big number conversion @@ -43,20 +45,11 @@ def get_context_data(self, **kwargs): r[0]["pub_key"][k] = str(v) context["voting"] = json.dumps(r[0]) + except: raise Http404 context["KEYBITS"] = settings.KEYBITS + context["question_type"] = question_type return context - - def get(self, request, voting_id, **kwargs): - user = request.user - voting = get_object_or_404(Voting, pk=voting_id) - question_type = voting.question.question_type - options = voting.question.options.all() - try: - Census.objects.get(voting_id=voting_id, voter_id=user.id) - except Census.DoesNotExist: - raise Http404 - return render(request, "booth/booth.html", {"voting": voting, "options": options, "question_type": question_type}) From 63a72150f229f746384759214b6397220ac454e7 Mon Sep 17 00:00:00 2001 From: MartaInes Date: Sat, 16 Dec 2023 23:49:41 +0100 Subject: [PATCH 06/11] style: add question type in list voting --- decide/booth/templates/booth/voting-list.html | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/decide/booth/templates/booth/voting-list.html b/decide/booth/templates/booth/voting-list.html index e11031477..bc1fc3080 100644 --- a/decide/booth/templates/booth/voting-list.html +++ b/decide/booth/templates/booth/voting-list.html @@ -43,6 +43,13 @@

{{ voting.name }}

Voting Open

{% endif %}
+
+ {% if voting.question.question_type == 'S' %} +

Single

+ {% elif voting.question.question_type == 'M' %} +

Multiple

+ {% endif %} +

{{ voting.desc }}

{% endfor %} From b639fa474fc651252e8e461017a8f6b703bbaf76 Mon Sep 17 00:00:00 2001 From: JMGarCas Date: Sun, 17 Dec 2023 00:01:29 +0100 Subject: [PATCH 07/11] tests: simple and multiple voting selenium --- decide/booth/test-selenium.py | 90 ++++++++++++++++++++++++++++++++++- 1 file changed, 89 insertions(+), 1 deletion(-) diff --git a/decide/booth/test-selenium.py b/decide/booth/test-selenium.py index 593f780a8..9eb0282d7 100644 --- a/decide/booth/test-selenium.py +++ b/decide/booth/test-selenium.py @@ -14,7 +14,8 @@ from selenium import webdriver from selenium.webdriver.common.by import By from authentication.models import EmailCheck -from voting.models import Question, Voting +from voting.models import Question, QuestionOption, Voting +from django.test import Client class HomepageTestCase(StaticLiveServerTestCase): @@ -317,3 +318,90 @@ def test_form_opinions(self): # Verifica que hay un boton de enviar self.assertTrue(len(self.driver.find_elements(By.ID, "submit-opinion")) == 1) + + +class VotingListViewTestCase(StaticLiveServerTestCase): + def setUp(self): + self.client = Client() + + # Crea un usuario admin y otro no admin + self.base = BaseTestCase() + self.base.setUp() + + user = User.objects.get(username="noadmin") + EmailCheck.objects.create(user=user, emailChecked=True) + + # Crea votacion simple + question1 = Question.objects.create(desc="Test question", question_type="S") + for i in range(5): + opt = QuestionOption(question=question1, option="option {}".format(i + 1)) + opt.save() + voting1 = Voting.objects.create( + name="Simple Voting", + desc="Test", + start_date=timezone.make_aware(datetime.datetime(2023, 10, 11)), + question_id=question1.id, + ) + auth = Auth.objects.get_or_create( + url=self.live_server_url, defaults={"me": True, "name": "test auth"} + )[0] + voting1.auths.add(auth) + Census.objects.create(voter_id=user.id, voting_id=voting1.id) + + # Crea votacion multiple + question2 = Question.objects.create(desc="Test question", question_type="M") + for i in range(5): + opt = QuestionOption(question=question2, option="option {}".format(i + 1)) + opt.save() + voting1 = Voting.objects.create( + name="Multiple Voting", + desc="Test", + start_date=timezone.make_aware(datetime.datetime(2023, 10, 11)), + question_id=question2.id, + ) + auth = Auth.objects.get_or_create( + url=self.live_server_url, defaults={"me": True, "name": "test auth"} + )[0] + voting1.auths.add(auth) + Census.objects.create(voter_id=user.id, voting_id=voting1.id) + + app = SocialApp.objects.create( + provider="google", + name="Google", + client_id="test", + secret="test", + ) + + # Add the current site to the SocialApp's sites + app.sites.add(Site.objects.get_current()) + + # Opciones de Chrome + options = webdriver.ChromeOptions() + options.headless = True + options.add_argument("--no-sandbox") + self.driver = webdriver.Chrome(options=options) + + # Disable recaptcha + os.environ["DISABLE_RECAPTCHA"] = "1" + + super().setUp() + + def tearDown(self): + super().tearDown() + self.driver.quit() + + self.base.tearDown() + + def test_simple_voting(self): + self.driver.get(f"{self.live_server_url}/signin") + self.driver.find_element(By.ID, "id_username").send_keys("noadmin") + self.driver.find_element(By.ID, "id_password").send_keys("qwerty") + self.driver.find_element(By.CSS_SELECTOR, ".btn").click() + + self.driver.get(f"{self.live_server_url}/voting-list") + + elements = self.driver.find_elements(By.CSS_SELECTOR, ".question_type") + + self.assertEqual(len(elements), 2) + self.assertEqual(elements[0].text, "Single") + self.assertEqual(elements[1].text, "Multiple") From f1a05eda96c1c8474ec9304a641cbb1ac22259d0 Mon Sep 17 00:00:00 2001 From: JMGarCas Date: Sun, 17 Dec 2023 00:52:06 +0100 Subject: [PATCH 08/11] fix: selenium booth tests now works properly --- decide/booth/test-selenium.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/decide/booth/test-selenium.py b/decide/booth/test-selenium.py index 9eb0282d7..145ef9433 100644 --- a/decide/booth/test-selenium.py +++ b/decide/booth/test-selenium.py @@ -295,9 +295,10 @@ def test_opinions(self): ) def test_form_opinions(self): + self.driver.set_window_size(1280, 720) + # Abre la ruta del navegador self.driver.get(f"{self.live_server_url}") - self.driver.find_element(By.LINK_TEXT, "Votaciones").click() # Ingresa el usuario y contraseña @@ -308,7 +309,7 @@ def test_form_opinions(self): self.driver.find_element(By.CLASS_NAME, "btn-primary").click() # Verifica que existe el boton de Opiniones - self.driver.find_element(By.LINK_TEXT, "Opiniones").click() + self.driver.find_element(By.CLASS_NAME, "opinions-card").click() # Verifica que hay dos h2 self.assertTrue(len(self.driver.find_elements(By.TAG_NAME, "h2")) == 2) @@ -320,7 +321,7 @@ def test_form_opinions(self): self.assertTrue(len(self.driver.find_elements(By.ID, "submit-opinion")) == 1) -class VotingListViewTestCase(StaticLiveServerTestCase): +class SingleMultipleVotingListViewTestCase(StaticLiveServerTestCase): def setUp(self): self.client = Client() From a351b441d3647f9be288fa4532e8b68dd7254d13 Mon Sep 17 00:00:00 2001 From: MartaInes Date: Sun, 17 Dec 2023 12:15:46 +0100 Subject: [PATCH 09/11] fix: change styles for the log in inside to votation --- decide/booth/static/booth/style.css | 35 ++++++++++ decide/booth/templates/booth/booth.html | 88 ++++++++----------------- 2 files changed, 64 insertions(+), 59 deletions(-) diff --git a/decide/booth/static/booth/style.css b/decide/booth/static/booth/style.css index d073e6b5d..8ed9264f7 100644 --- a/decide/booth/static/booth/style.css +++ b/decide/booth/static/booth/style.css @@ -217,4 +217,39 @@ textarea { .verde { background-color: #4CAF50; /* Color verde */ color: white; /* Texto blanco */ +} + +.form-title { + font-family: var(--fuente1); + font-size: 1.5em; + font-weight: bold; + color: var(--text-color); + } + +.form-content { + display: flex; + flex-direction: column; + align-items: flex-end; + gap: 0.5em; +} + +.input-group { + display: flex; + flex-direction: row; + align-items: center; + gap: 0.5em; +} + +.input-group input { + width: 20em; + padding: 8px; + margin-top: 5px; + border-radius: 0.25em; + box-shadow: 0 0 2px rgba(0, 0, 0, 0.5); + color: var(--input-text); + font-family: var(--fuente2); +} + +.input-group i { + font-size: 1.5em; } \ No newline at end of file diff --git a/decide/booth/templates/booth/booth.html b/decide/booth/templates/booth/booth.html index 1c32f736b..9aa63f3e5 100644 --- a/decide/booth/templates/booth/booth.html +++ b/decide/booth/templates/booth/booth.html @@ -14,29 +14,7 @@
- - - + @@ -66,47 +44,39 @@
-
+
-