diff --git a/.gitignore b/.gitignore index b823e313b4..ed5f6e3706 100644 --- a/.gitignore +++ b/.gitignore @@ -52,7 +52,7 @@ coverage.xml # Django stuff: *.log -local_settings.py +#local_settings.py # Flask stuff: instance/ diff --git a/.travis.yml b/.travis.yml index b72f88a6e0..0b6fb4e691 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,21 +1,28 @@ -dist: xenial - -services: - - postgresql -addons: - postgresql: "9.4" -before_script: - - psql -U postgres -c "create user decide password 'decide'" - - psql -U postgres -c "create database test_decide owner decide" - - psql -U postgres -c "ALTER USER decide CREATEDB" language: python python: - - "3.6" +- '3.6' +branches: + only: + - master + - develop +services: +- postgresql +env: +- DJANGO=2.0 DB=postgres +global: +- PGPORT=5432 +before_install: +- cd decide install: - - pip install -r requirements.txt - - pip install codacy-coverage +- pip install -r ../requirements.txt +before_script: +- cp local_settings.travis.py local_settings.py +- psql -U postgres -c "create user decide password 'decide'" +- psql -U postgres -c "create database test_decide owner decide" +- psql -U postgres -c "ALTER USER decide CREATEDB" +- python manage.py makemigrations +- python manage.py migrate script: - - cd decide - - coverage run --branch --source=. ./manage.py test --keepdb --with-xunit - - coverage xml - - python-codacy-coverage -r coverage.xml +- python manage.py test +addons: + postgresql: '9.6' diff --git a/Procfile b/Procfile new file mode 100644 index 0000000000..818fef7fe3 --- /dev/null +++ b/Procfile @@ -0,0 +1,4 @@ +% prepara el repositorio para su despliegue. +release: sh -c 'cd decide && python manage.py migrate' +% especifica el comando para lanzar Decide +web: sh -c 'cd decide && gunicorn decide.wsgi --log-file -' diff --git a/README.md b/README.md index 1539feb2cb..48af79309d 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -[![Build Status](https://travis-ci.com/wadobo/decide.svg?branch=master)](https://travis-ci.com/wadobo/decide) [![Codacy Badge](https://api.codacy.com/project/badge/Grade/94a85eaa0e974c71af6899ea3b0d27e0)](https://www.codacy.com/app/Wadobo/decide?utm_source=github.com&utm_medium=referral&utm_content=wadobo/decide&utm_campaign=Badge_Grade) [![Codacy Badge](https://api.codacy.com/project/badge/Coverage/94a85eaa0e974c71af6899ea3b0d27e0)](https://www.codacy.com/app/Wadobo/decide?utm_source=github.com&utm_medium=referral&utm_content=wadobo/decide&utm_campaign=Badge_Coverage) +[![Build Status](https://travis-ci.com/RioTinto-Votaciones/decide.svg?branch=develop)](https://travis-ci.com/RioTinto-Votaciones/decide) Plataforma voto electrónico educativa pro ===================================== diff --git a/decide/base/migrations/0001_initial.py b/decide/base/migrations/0001_initial.py index 3130a73e76..f8e411a4bf 100644 --- a/decide/base/migrations/0001_initial.py +++ b/decide/base/migrations/0001_initial.py @@ -1,5 +1,6 @@ -# Generated by Django 2.0 on 2018-06-05 08:42 +# Generated by Django 2.0 on 2021-01-17 21:48 +import base.models from django.db import migrations, models @@ -24,10 +25,10 @@ class Migration(migrations.Migration): name='Key', fields=[ ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('p', models.IntegerField()), - ('g', models.IntegerField()), - ('y', models.IntegerField()), - ('x', models.IntegerField(blank=True, null=True)), + ('p', base.models.BigBigField()), + ('g', base.models.BigBigField()), + ('y', base.models.BigBigField()), + ('x', base.models.BigBigField(blank=True, null=True)), ], ), ] diff --git a/decide/base/migrations/0002_auto_20180921_1056.py b/decide/base/migrations/0002_auto_20180921_1056.py deleted file mode 100644 index e7582f11b2..0000000000 --- a/decide/base/migrations/0002_auto_20180921_1056.py +++ /dev/null @@ -1,33 +0,0 @@ -# Generated by Django 2.0 on 2018-09-21 10:56 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('base', '0001_initial'), - ] - - operations = [ - migrations.AlterField( - model_name='key', - name='g', - field=models.BigIntegerField(), - ), - migrations.AlterField( - model_name='key', - name='p', - field=models.BigIntegerField(), - ), - migrations.AlterField( - model_name='key', - name='x', - field=models.BigIntegerField(blank=True, null=True), - ), - migrations.AlterField( - model_name='key', - name='y', - field=models.BigIntegerField(), - ), - ] diff --git a/decide/base/migrations/0003_auto_20180921_1119.py b/decide/base/migrations/0003_auto_20180921_1119.py deleted file mode 100644 index eef3af603a..0000000000 --- a/decide/base/migrations/0003_auto_20180921_1119.py +++ /dev/null @@ -1,34 +0,0 @@ -# Generated by Django 2.0 on 2018-09-21 11:19 - -import base.models -from django.db import migrations - - -class Migration(migrations.Migration): - - dependencies = [ - ('base', '0002_auto_20180921_1056'), - ] - - operations = [ - migrations.AlterField( - model_name='key', - name='g', - field=base.models.BigBigField(), - ), - migrations.AlterField( - model_name='key', - name='p', - field=base.models.BigBigField(), - ), - migrations.AlterField( - model_name='key', - name='x', - field=base.models.BigBigField(blank=True, null=True), - ), - migrations.AlterField( - model_name='key', - name='y', - field=base.models.BigBigField(), - ), - ] diff --git a/decide/base/tests.py b/decide/base/tests.py index 907964aa95..d2ba15992b 100644 --- a/decide/base/tests.py +++ b/decide/base/tests.py @@ -20,6 +20,10 @@ def setUp(self): user_admin.set_password('qwerty') user_admin.save() + user_admin = User(username='admin-selenium', is_staff=True, is_superuser=True, is_active=True) + user_admin.set_password('qwerty') + user_admin.save() + def tearDown(self): self.client = None self.token = None diff --git a/decide/census/migrations/0001_initial.py b/decide/census/migrations/0001_initial.py index bc5718e987..81e41abc71 100644 --- a/decide/census/migrations/0001_initial.py +++ b/decide/census/migrations/0001_initial.py @@ -1,4 +1,4 @@ -# Generated by Django 2.0 on 2018-04-08 17:16 +# Generated by Django 2.0 on 2021-01-17 21:48 from django.db import migrations, models diff --git a/decide/decide/settings.py b/decide/decide/settings.py index 1d22b67324..6d60cf0e38 100644 --- a/decide/decide/settings.py +++ b/decide/decide/settings.py @@ -70,7 +70,7 @@ 'voting', ] -BASEURL = 'http://localhost:8000' +BASEURL = 'https://votacionegc.herokuapp.com/' MIDDLEWARE = [ 'django.middleware.security.SecurityMiddleware', @@ -161,6 +161,8 @@ # number of bits for the key, all auths should use the same number of bits KEYBITS = 256 +APIS = {} + # Versioning ALLOWED_VERSIONS = ['v1', 'v2'] DEFAULT_VERSION = 'v1' @@ -180,3 +182,6 @@ INSTALLED_APPS = INSTALLED_APPS + MODULES + +# import django_heroku +# django_heroku.settings(locals()) diff --git a/decide/local_settings.py b/decide/local_settings.py new file mode 100644 index 0000000000..8c07e7684d --- /dev/null +++ b/decide/local_settings.py @@ -0,0 +1,45 @@ +ALLOWED_HOSTS = ["*"] + +# Modules in use, commented modules that you won't use +MODULES = [ + 'authentication', + 'base', + 'booth', + 'census', + 'mixnet', + 'postproc', + 'store', + 'visualizer', + 'voting', +] + +APIS = { + 'authentication': 'http://localhost:8000', + 'base': 'http://localhost:8000', + 'booth': 'http://localhost:8000', + 'census': 'http://localhost:8000', + 'mixnet': 'http://localhost:8000', + 'postproc': 'http://localhost:8000', + 'store': 'http://localhost:8000', + 'visualizer': 'http://localhost:8000', + 'voting': 'http://localhost:8000', +} + +BASEURL = 'http://localhost:8000' + +DATABASES = { + 'default': { + 'ENGINE': 'django.db.backends.postgresql', + 'NAME': 'decide', + 'USER': 'decide', + 'PASSWORD': 'decide', + 'HOST': 'localhost', + 'PORT': '5432', + } +} + +# number of bits for the key, all auths should use the same number of bits +KEYBITS = 256 + +STATIC_URL = '/static/' + diff --git a/decide/local_settings.travis.py b/decide/local_settings.travis.py new file mode 100644 index 0000000000..d4162dae92 --- /dev/null +++ b/decide/local_settings.travis.py @@ -0,0 +1,41 @@ +ALLOWED_HOSTS = ["*"] + +# Modules in use, commented modules that you won't use +MODULES = [ + 'authentication', + 'base', + 'booth', + 'census', + 'mixnet', + 'postproc', + 'store', + 'visualizer', + 'voting', +] + +APIS = { + 'authentication': 'http://localhost:8000', + 'base': 'http://localhost:8000', + 'booth': 'http://localhost:8000', + 'census': 'http://localhost:8000', + 'mixnet': 'http://localhost:8000', + 'postproc': 'http://localhost:8000', + 'store': 'http://localhost:8000', + 'visualizer': 'http://localhost:8000', + 'voting': 'http://localhost:8000', +} + +BASEURL = 'http://localhost:8000' + +DATABASES = { + 'default': { + 'ENGINE': 'django.db.backends.postgresql', + 'NAME': 'postgres', + 'USER': 'postgres', + 'HOST': 'localhost', + 'PORT': '5432', + } +} + +# number of bits for the key, all auths should use the same number of bits +KEYBITS = 256 diff --git a/decide/mixnet/migrations/0001_initial.py b/decide/mixnet/migrations/0001_initial.py index 5cde9a643c..be0f74da56 100644 --- a/decide/mixnet/migrations/0001_initial.py +++ b/decide/mixnet/migrations/0001_initial.py @@ -1,4 +1,4 @@ -# Generated by Django 2.0 on 2018-02-16 11:26 +# Generated by Django 2.0 on 2021-01-17 21:48 from django.db import migrations, models import django.db.models.deletion @@ -9,36 +9,19 @@ class Migration(migrations.Migration): initial = True dependencies = [ + ('base', '0001_initial'), ] operations = [ - migrations.CreateModel( - name='Auth', - fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('name', models.CharField(max_length=200)), - ('url', models.URLField()), - ('me', models.BooleanField(default=False)), - ], - ), - migrations.CreateModel( - name='Key', - fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('p', models.IntegerField()), - ('g', models.IntegerField()), - ('y', models.IntegerField()), - ('x', models.IntegerField(blank=True, null=True)), - ], - ), migrations.CreateModel( name='Mixnet', fields=[ ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('vote_id', models.PositiveIntegerField()), - ('auths', models.ManyToManyField(related_name='mixnets', to='mixnet.Auth')), - ('key', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='mixnets', to='mixnet.Key')), - ('pubkey', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='mixnets_pub', to='mixnet.Key')), + ('voting_id', models.PositiveIntegerField()), + ('auth_position', models.PositiveIntegerField(default=0)), + ('auths', models.ManyToManyField(related_name='mixnets', to='base.Auth')), + ('key', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='mixnets', to='base.Key')), + ('pubkey', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='mixnets_pub', to='base.Key')), ], ), ] diff --git a/decide/mixnet/migrations/0002_auto_20180216_1617.py b/decide/mixnet/migrations/0002_auto_20180216_1617.py deleted file mode 100644 index 998f04f623..0000000000 --- a/decide/mixnet/migrations/0002_auto_20180216_1617.py +++ /dev/null @@ -1,18 +0,0 @@ -# Generated by Django 2.0 on 2018-02-16 16:17 - -from django.db import migrations - - -class Migration(migrations.Migration): - - dependencies = [ - ('mixnet', '0001_initial'), - ] - - operations = [ - migrations.RenameField( - model_name='mixnet', - old_name='vote_id', - new_name='voting_id', - ), - ] diff --git a/decide/mixnet/migrations/0003_mixnet_auth_position.py b/decide/mixnet/migrations/0003_mixnet_auth_position.py deleted file mode 100644 index 434ef330c6..0000000000 --- a/decide/mixnet/migrations/0003_mixnet_auth_position.py +++ /dev/null @@ -1,18 +0,0 @@ -# Generated by Django 2.0 on 2018-04-04 10:13 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('mixnet', '0002_auto_20180216_1617'), - ] - - operations = [ - migrations.AddField( - model_name='mixnet', - name='auth_position', - field=models.PositiveIntegerField(default=0), - ), - ] diff --git a/decide/mixnet/migrations/0004_auto_20180605_0842.py b/decide/mixnet/migrations/0004_auto_20180605_0842.py deleted file mode 100644 index 5e81e4a1ea..0000000000 --- a/decide/mixnet/migrations/0004_auto_20180605_0842.py +++ /dev/null @@ -1,36 +0,0 @@ -# Generated by Django 2.0 on 2018-06-05 08:42 - -from django.db import migrations, models -import django.db.models.deletion - - -class Migration(migrations.Migration): - - dependencies = [ - ('voting', '0003_auto_20180605_0842'), - ('mixnet', '0003_mixnet_auth_position'), - ] - - operations = [ - migrations.DeleteModel( - name='Auth', - ), - migrations.AlterField( - model_name='mixnet', - name='auths', - field=models.ManyToManyField(related_name='mixnets', to='base.Auth'), - ), - migrations.AlterField( - model_name='mixnet', - name='key', - field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='mixnets', to='base.Key'), - ), - migrations.AlterField( - model_name='mixnet', - name='pubkey', - field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='mixnets_pub', to='base.Key'), - ), - migrations.DeleteModel( - name='Key', - ), - ] diff --git a/decide/store/migrations/0001_initial.py b/decide/store/migrations/0001_initial.py index cfd46db23d..c561de7338 100644 --- a/decide/store/migrations/0001_initial.py +++ b/decide/store/migrations/0001_initial.py @@ -1,5 +1,6 @@ -# Generated by Django 2.0 on 2018-02-24 07:33 +# Generated by Django 2.0 on 2021-01-17 21:48 +import base.models from django.db import migrations, models @@ -17,8 +18,9 @@ class Migration(migrations.Migration): ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), ('voting_id', models.PositiveIntegerField()), ('voter_id', models.PositiveIntegerField()), - ('a', models.PositiveIntegerField()), - ('b', models.PositiveIntegerField()), + ('a', base.models.BigBigField()), + ('b', base.models.BigBigField()), + ('voted', models.DateTimeField(auto_now=True)), ], ), ] diff --git a/decide/store/migrations/0002_vote_voted.py b/decide/store/migrations/0002_vote_voted.py deleted file mode 100644 index c636eef782..0000000000 --- a/decide/store/migrations/0002_vote_voted.py +++ /dev/null @@ -1,18 +0,0 @@ -# Generated by Django 2.0 on 2018-02-24 08:26 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('store', '0001_initial'), - ] - - operations = [ - migrations.AddField( - model_name='vote', - name='voted', - field=models.DateTimeField(auto_now=True), - ), - ] diff --git a/decide/store/migrations/0003_auto_20180921_1522.py b/decide/store/migrations/0003_auto_20180921_1522.py deleted file mode 100644 index a57789ed68..0000000000 --- a/decide/store/migrations/0003_auto_20180921_1522.py +++ /dev/null @@ -1,24 +0,0 @@ -# Generated by Django 2.0 on 2018-09-21 15:22 - -import base.models -from django.db import migrations - - -class Migration(migrations.Migration): - - dependencies = [ - ('store', '0002_vote_voted'), - ] - - operations = [ - migrations.AlterField( - model_name='vote', - name='a', - field=base.models.BigBigField(), - ), - migrations.AlterField( - model_name='vote', - name='b', - field=base.models.BigBigField(), - ), - ] diff --git a/decide/tally.zip b/decide/tally.zip new file mode 100644 index 0000000000..05e07c28a6 Binary files /dev/null and b/decide/tally.zip differ diff --git a/decide/tallydeVoting1.txt b/decide/tallydeVoting1.txt new file mode 100644 index 0000000000..c2e365f1a4 --- /dev/null +++ b/decide/tallydeVoting1.txt @@ -0,0 +1,3 @@ + + Hola Mundo +[{'option': 'option 1', 'number': 2, 'votes': 2}, {'option': 'option 2', 'number': 3, 'votes': 5}, {'option': 'option 3', 'number': 4, 'votes': 2}, {'option': 'option 4', 'number': 5, 'votes': 5}, {'option': 'option 5', 'number': 6, 'votes': 3}] \ No newline at end of file diff --git a/decide/tallydeVoting2.txt b/decide/tallydeVoting2.txt new file mode 100644 index 0000000000..f9b25a1a83 --- /dev/null +++ b/decide/tallydeVoting2.txt @@ -0,0 +1,3 @@ + + Hola Mundo +[{'option': 'option 1', 'number': 2, 'votes': 5}, {'option': 'option 2', 'number': 3, 'votes': 4}, {'option': 'option 3', 'number': 4, 'votes': 5}, {'option': 'option 4', 'number': 5, 'votes': 4}, {'option': 'option 5', 'number': 6, 'votes': 4}] \ No newline at end of file diff --git a/decide/tallydeVoting9.txt b/decide/tallydeVoting9.txt new file mode 100644 index 0000000000..c6a4c34757 --- /dev/null +++ b/decide/tallydeVoting9.txt @@ -0,0 +1,3 @@ + + Hola Mundo +[{'option': 'option 1', 'number': 2, 'votes': 0}, {'option': 'option 2', 'number': 3, 'votes': 0}, {'option': 'option 3', 'number': 4, 'votes': 0}, {'option': 'option 4', 'number': 5, 'votes': 0}, {'option': 'option 5', 'number': 6, 'votes': 0}] \ No newline at end of file diff --git a/decide/voting/migrations/0001_initial.py b/decide/voting/migrations/0001_initial.py index a64c76ed70..7412e4628a 100644 --- a/decide/voting/migrations/0001_initial.py +++ b/decide/voting/migrations/0001_initial.py @@ -1,5 +1,6 @@ -# Generated by Django 2.0 on 2018-02-23 11:42 +# Generated by Django 2.0 on 2021-01-17 21:48 +import django.contrib.postgres.fields.jsonb from django.db import migrations, models import django.db.models.deletion @@ -9,10 +10,29 @@ class Migration(migrations.Migration): initial = True dependencies = [ - ('mixnet', '0002_auto_20180216_1617'), + ('base', '0001_initial'), ] operations = [ + migrations.CreateModel( + name='OrderQuestion', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('desc', models.TextField()), + ('preference', models.CharField(blank=True, choices=[('B', '1'), ('M', '2'), ('A', '3')], max_length=1)), + ], + ), + migrations.CreateModel( + name='PoliticalParty', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('name', models.CharField(max_length=200)), + ('acronym', models.CharField(max_length=10)), + ('description', models.TextField(blank=True, null=True)), + ('leader', models.CharField(max_length=200)), + ('president', models.CharField(blank=True, max_length=151, null=True)), + ], + ), migrations.CreateModel( name='Question', fields=[ @@ -37,9 +57,26 @@ class Migration(migrations.Migration): ('desc', models.TextField(blank=True, null=True)), ('start_date', models.DateTimeField(blank=True, null=True)), ('end_date', models.DateTimeField(blank=True, null=True)), - ('auths', models.ManyToManyField(related_name='votings', to='mixnet.Auth')), - ('pub_key', models.OneToOneField(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='voting', to='mixnet.Key')), + ('tally', django.contrib.postgres.fields.jsonb.JSONField(blank=True, null=True)), + ('postproc', django.contrib.postgres.fields.jsonb.JSONField(blank=True, null=True)), + ('url', models.CharField(help_text='http://localhost:8000/booth/', max_length=40)), + ('auths', models.ManyToManyField(related_name='votings', to='base.Auth')), + ('order_question', models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, related_name='voting', to='voting.OrderQuestion')), + ('political_party', models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, related_name='voting', to='voting.PoliticalParty')), + ('pub_key', models.OneToOneField(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='voting', to='base.Key')), ('question', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='voting', to='voting.Question')), ], ), + migrations.CreateModel( + name='YesOrNoQuestion', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('desc', models.TextField()), + ('choice', models.CharField(blank=True, choices=[('Y', 'Yes'), ('N', 'No')], max_length=1)), + ], + ), + migrations.AlterUniqueTogether( + name='politicalparty', + unique_together={('name', 'acronym', 'leader')}, + ), ] diff --git a/decide/voting/migrations/0002_auto_20180302_1100.py b/decide/voting/migrations/0002_auto_20180302_1100.py deleted file mode 100644 index 9bb8fa6c52..0000000000 --- a/decide/voting/migrations/0002_auto_20180302_1100.py +++ /dev/null @@ -1,24 +0,0 @@ -# Generated by Django 2.0 on 2018-03-02 11:00 - -import django.contrib.postgres.fields.jsonb -from django.db import migrations - - -class Migration(migrations.Migration): - - dependencies = [ - ('voting', '0001_initial'), - ] - - operations = [ - migrations.AddField( - model_name='voting', - name='postproc', - field=django.contrib.postgres.fields.jsonb.JSONField(blank=True, null=True), - ), - migrations.AddField( - model_name='voting', - name='tally', - field=django.contrib.postgres.fields.jsonb.JSONField(blank=True, null=True), - ), - ] diff --git a/decide/voting/migrations/0002_auto_20210117_2215.py b/decide/voting/migrations/0002_auto_20210117_2215.py new file mode 100644 index 0000000000..3f3a138cda --- /dev/null +++ b/decide/voting/migrations/0002_auto_20210117_2215.py @@ -0,0 +1,29 @@ +# Generated by Django 2.0 on 2021-01-17 22:15 + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('voting', '0001_initial'), + ] + + operations = [ + migrations.AlterField( + model_name='voting', + name='order_question', + field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='voting', to='voting.OrderQuestion'), + ), + migrations.AlterField( + model_name='voting', + name='political_party', + field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='voting', to='voting.PoliticalParty'), + ), + migrations.AlterField( + model_name='voting', + name='question', + field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='voting', to='voting.Question'), + ), + ] diff --git a/decide/voting/migrations/0003_auto_20180605_0842.py b/decide/voting/migrations/0003_auto_20180605_0842.py deleted file mode 100644 index 4a64b26350..0000000000 --- a/decide/voting/migrations/0003_auto_20180605_0842.py +++ /dev/null @@ -1,24 +0,0 @@ -# Generated by Django 2.0 on 2018-06-05 08:42 - -from django.db import migrations, models -import django.db.models.deletion - - -class Migration(migrations.Migration): - - dependencies = [ - ('voting', '0002_auto_20180302_1100'), - ] - - operations = [ - migrations.AlterField( - model_name='voting', - name='auths', - field=models.ManyToManyField(related_name='votings', to='base.Auth'), - ), - migrations.AlterField( - model_name='voting', - name='pub_key', - field=models.OneToOneField(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='voting', to='base.Key'), - ), - ] diff --git a/decide/voting/migrations/0003_voting_yes_or_no_question.py b/decide/voting/migrations/0003_voting_yes_or_no_question.py new file mode 100644 index 0000000000..72323abbf9 --- /dev/null +++ b/decide/voting/migrations/0003_voting_yes_or_no_question.py @@ -0,0 +1,19 @@ +# Generated by Django 2.0 on 2021-01-17 22:24 + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('voting', '0002_auto_20210117_2215'), + ] + + operations = [ + migrations.AddField( + model_name='voting', + name='yes_or_no_question', + field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='voting', to='voting.YesOrNoQuestion'), + ), + ] diff --git a/decide/voting/migrations/0004_voting_url.py b/decide/voting/migrations/0004_voting_url.py deleted file mode 100644 index ca8888b089..0000000000 --- a/decide/voting/migrations/0004_voting_url.py +++ /dev/null @@ -1,19 +0,0 @@ -# Generated by Django 2.0 on 2021-01-01 18:10 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('voting', '0003_auto_20180605_0842'), - ] - - operations = [ - migrations.AddField( - model_name='voting', - name='url', - field=models.CharField(default='_voting_decide_example', max_length=40), - preserve_default=False, - ), - ] diff --git a/decide/voting/migrations/0004_yesorno_yesornooption.py b/decide/voting/migrations/0004_yesorno_yesornooption.py deleted file mode 100644 index 164881b653..0000000000 --- a/decide/voting/migrations/0004_yesorno_yesornooption.py +++ /dev/null @@ -1,29 +0,0 @@ -# Generated by Django 2.0 on 2021-01-06 12:25 - -from django.db import migrations, models -import django.db.models.deletion - - -class Migration(migrations.Migration): - - dependencies = [ - ('voting', '0003_auto_20180605_0842'), - ] - - operations = [ - migrations.CreateModel( - name='YesOrNo', - fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('desc', models.TextField()), - ], - ), - migrations.CreateModel( - name='YesOrNoOption', - fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('option', models.TextField()), - ('yesorno', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='options', to='voting.YesOrNo')), - ], - ), - ] diff --git a/decide/voting/migrations/0005_auto_20210101_1813.py b/decide/voting/migrations/0005_auto_20210101_1813.py deleted file mode 100644 index 984b21cf75..0000000000 --- a/decide/voting/migrations/0005_auto_20210101_1813.py +++ /dev/null @@ -1,18 +0,0 @@ -# Generated by Django 2.0 on 2021-01-01 18:13 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('voting', '0004_voting_url'), - ] - - operations = [ - migrations.AlterField( - model_name='voting', - name='url', - field=models.CharField(help_text='http://localhost:8000/booth/', max_length=40), - ), - ] diff --git a/decide/voting/migrations/0005_auto_20210106_1519.py b/decide/voting/migrations/0005_auto_20210106_1519.py deleted file mode 100644 index e54741adc9..0000000000 --- a/decide/voting/migrations/0005_auto_20210106_1519.py +++ /dev/null @@ -1,26 +0,0 @@ -# Generated by Django 2.0 on 2021-01-06 15:19 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('voting', '0004_yesorno_yesornooption'), - ] - - operations = [ - migrations.RemoveField( - model_name='yesornooption', - name='yesorno', - ), - migrations.AddField( - model_name='yesorno', - name='choice', - field=models.CharField(choices=[('Y', 'Yes'), ('N', 'No')], default='N', max_length=1), - preserve_default=False, - ), - migrations.DeleteModel( - name='YesOrNoOption', - ), - ] diff --git a/decide/voting/migrations/0006_auto_20210106_1523.py b/decide/voting/migrations/0006_auto_20210106_1523.py deleted file mode 100644 index ac5649d592..0000000000 --- a/decide/voting/migrations/0006_auto_20210106_1523.py +++ /dev/null @@ -1,18 +0,0 @@ -# Generated by Django 2.0 on 2021-01-06 15:23 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('voting', '0005_auto_20210106_1519'), - ] - - operations = [ - migrations.AlterField( - model_name='yesorno', - name='choice', - field=models.CharField(blank=True, choices=[('Y', 'Yes'), ('N', 'No')], max_length=1), - ), - ] diff --git a/decide/voting/migrations/0007_auto_20210106_1529.py b/decide/voting/migrations/0007_auto_20210106_1529.py deleted file mode 100644 index 94656b799e..0000000000 --- a/decide/voting/migrations/0007_auto_20210106_1529.py +++ /dev/null @@ -1,17 +0,0 @@ -# Generated by Django 2.0 on 2021-01-06 15:29 - -from django.db import migrations - - -class Migration(migrations.Migration): - - dependencies = [ - ('voting', '0006_auto_20210106_1523'), - ] - - operations = [ - migrations.RenameModel( - old_name='YesOrNo', - new_name='YesOrNoQuestion', - ), - ] diff --git a/decide/voting/migrations/0008_merge_20210106_2217.py b/decide/voting/migrations/0008_merge_20210106_2217.py deleted file mode 100644 index 9615158112..0000000000 --- a/decide/voting/migrations/0008_merge_20210106_2217.py +++ /dev/null @@ -1,14 +0,0 @@ -# Generated by Django 2.0 on 2021-01-06 22:17 - -from django.db import migrations - - -class Migration(migrations.Migration): - - dependencies = [ - ('voting', '0007_auto_20210106_1529'), - ('voting', '0005_auto_20210101_1813'), - ] - - operations = [ - ] diff --git a/decide/voting/migrations/0009_auto_20210106_2332.py b/decide/voting/migrations/0009_auto_20210106_2332.py deleted file mode 100644 index 4af8d8e416..0000000000 --- a/decide/voting/migrations/0009_auto_20210106_2332.py +++ /dev/null @@ -1,34 +0,0 @@ -# Generated by Django 2.0 on 2021-01-06 23:32 - -from django.db import migrations, models -import django.db.models.deletion - - -class Migration(migrations.Migration): - - dependencies = [ - ('voting', '0008_merge_20210106_2217'), - ] - - operations = [ - migrations.CreateModel( - name='PoliticalParty', - fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('name', models.CharField(max_length=200)), - ('acronym', models.CharField(max_length=10)), - ('description', models.TextField(blank=True, null=True)), - ('leader', models.CharField(max_length=200)), - ('president', models.CharField(blank=True, max_length=151, null=True)), - ], - ), - migrations.AlterUniqueTogether( - name='politicalparty', - unique_together={('name', 'acronym', 'leader')}, - ), - migrations.AddField( - model_name='voting', - name='political_party', - field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, related_name='voting', to='voting.PoliticalParty'), - ), - ] diff --git a/decide/voting/models.py b/decide/voting/models.py index 3204b3e894..5bebecb896 100644 --- a/decide/voting/models.py +++ b/decide/voting/models.py @@ -9,6 +9,9 @@ from django.dispatch import receiver from django.utils.translation import gettext_lazy as _ +import zipfile + +from django.http import HttpResponse class Question(models.Model): desc = models.TextField() @@ -77,9 +80,13 @@ def __str__(self): class Voting(models.Model): name = models.CharField(max_length=200) desc = models.TextField(blank=True, null=True) - question = models.ForeignKey(Question, related_name='voting', on_delete=models.CASCADE) - political_party = models.ForeignKey(PoliticalParty, related_name='voting', on_delete=models.CASCADE, null=True) + question = models.ForeignKey(Question, related_name='voting', on_delete=models.CASCADE, null=True, blank=True) + political_party = models.ForeignKey(PoliticalParty, related_name='voting', on_delete=models.CASCADE, null=True, blank=True) # ,blank=True + + order_question = models.ForeignKey(OrderQuestion, related_name='voting', on_delete=models.CASCADE, null= True, blank=True) + + yes_or_no_question = models.ForeignKey(YesOrNoQuestion, related_name='voting', on_delete=models.CASCADE, null=True, blank=True) start_date = models.DateTimeField(blank=True, null=True) end_date = models.DateTimeField(blank=True, null=True) @@ -97,7 +104,9 @@ def clean_fields(self, exclude=None): url = urllib.parse.quote_plus(self.url.encode('utf-8')) - if Voting.objects.filter(url=url).exists(): + print(Voting.objects.filter(url=url)) + + if Voting.objects.filter(url=url).exists() and Voting.objects.filter(url=url)[0] != self: raise ValidationError({'url': "The url already exists."}) def save(self, *args, **kwargs): @@ -164,6 +173,7 @@ def tally_votes(self, token=''): self.do_postproc() + def do_postproc(self): tally = self.tally options = self.question.options.all() @@ -186,6 +196,17 @@ def do_postproc(self): self.postproc = postp self.save() + archivo = open("tallydeVoting"+str((self.id))+".txt","w") + archivo.write("\n Hola Mundo\n") + archivo.write(str((opts))) + archivo.close() + + zip_file=zipfile.ZipFile("tally.zip", mode="w") + zip_file.write("tallydeVoting"+str((self.id))+".txt") + zip_file.close() + + return zip_file + def __str__(self): return self.name diff --git a/decide/voting/tests.py b/decide/voting/tests.py index 063c52e1cc..e8dd35a98d 100644 --- a/decide/voting/tests.py +++ b/decide/voting/tests.py @@ -13,7 +13,7 @@ from mixnet.mixcrypt import ElGamal from mixnet.mixcrypt import MixCrypt from mixnet.models import Auth -from voting.models import Voting, Question, QuestionOption +from voting.models import Voting, Question, QuestionOption, OrderQuestion class VotingTestCase(BaseTestCase): @@ -31,13 +31,18 @@ def encrypt_msg(self, msg, v, bits=settings.KEYBITS): k.k = ElGamal.construct((p, g, y)) return k.encrypt(msg) - def create_voting(self): + def create_voting(self, url=None): q = Question(desc='test question') q.save() for i in range(5): opt = QuestionOption(question=q, option='option {}'.format(i+1)) opt.save() - v = Voting(name='test voting', question=q) + + if url: + v = Voting(name='test voting', question=q, url=url) + else: + v = Voting(name='test voting', question=q) + v.save() a, _ = Auth.objects.get_or_create(url=settings.BASEURL, @@ -55,6 +60,14 @@ def create_voters(self, v): c = Census(voter_id=u.id, voting_id=v.id) c.save() + def create_fiftyvoters(self, v): + for i in range(50): + u, _ = User.objects.get_or_create(username='testvoter{}'.format(i)) + u.is_active = True + u.save() + c = Census(voter_id=u.id, voting_id=v.id) + c.save() + def get_or_create_user(self, pk): user, _ = User.objects.get_or_create(pk=pk) user.username = 'user{}'.format(pk) @@ -83,7 +96,30 @@ def store_votes(self, v): mods.post('store', json=data) return clear - def test_complete_voting(self): + def test_complete_voting_fiftyvoters(self): + v = self.create_voting() + self.create_fiftyvoters(v) + + v.create_pubkey() + v.start_date = timezone.now() + v.save() + + clear = self.store_votes(v) + + self.login() + v.tally_votes(self.token) + + tally = v.tally + tally.sort() + tally = {k: len(list(x)) for k, x in itertools.groupby(tally)} + + for q in v.question.options.all(): + self.assertEqual(tally.get(q.number, 0), clear.get(q.number, 0)) + + for q in v.postproc: + self.assertEqual(tally.get(q["number"], 0), q["votes"]) + + def test_complete_voting_with_voters(self): v = self.create_voting() self.create_voters(v) @@ -93,7 +129,7 @@ def test_complete_voting(self): clear = self.store_votes(v) - self.login() # set token + self.login() v.tally_votes(self.token) tally = v.tally @@ -131,6 +167,82 @@ def test_create_voting_from_api(self): response = self.client.post('/voting/', data, format='json') self.assertEqual(response.status_code, 201) + def test_create_voting_from_api_noadmin(self): + data = {'name': 'Example'} + response = self.client.post('/voting/', data, format='json') + self.assertEqual(response.status_code, 401) + + self.login(user='noadmin') + response = mods.post('voting', params=data, response=True) + self.assertEqual(response.status_code, 403) + + data = { + 'name': 'Example', + 'desc': 'Description example', + 'question': 'I want a ', + 'question_opt': ['cat', 'dog', 'horse'] + } + + response = self.client.post('/voting/', data, format='json') + self.assertEqual(response.status_code, 403 ) + + def test_create_voting_from_api_admin(self): + data = {'name': 'Example'} + response = self.client.post('/voting/', data, format='json') + self.assertEqual(response.status_code, 401) + + self.login() + response = mods.post('voting', params=data, response=True) + self.assertEqual(response.status_code, 400) + + data = { + 'name': 'Example', + 'desc': 'Description example', + 'question': 'I want a ', + 'question_opt': ['cat', 'dog', 'horse'] + } + + response = self.client.post('/voting/', data, format='json') + self.assertEqual(response.status_code, 201 ) + + # def test_create_voting_without_url(self): + # v = self.create_voting() + + # data = { + # 'name': 'Example', + # 'desc': 'Description example', + # 'question': 'Is there any url? ', + # 'question_opt': ['Yes', 'No'] + # } + + # response = self.client.post('/voting/', data, format='json') + # self.assertEqual(response.status_code, 401) + + def test_create_voting_without_url_and_question(self): + v = self.create_voting() + + data = { + 'name': 'Example', + 'desc': 'Description example', + 'question_opt': ['Yes', 'No'] + } + + response = self.client.post('/voting/', data, format='json') + self.assertEqual(response.status_code, 401) + + def test_create_voting_without_url_and_options(self): + v = self.create_voting() + + data = { + 'name': 'Example', + 'desc': 'Description example', + 'question': 'Is there any url? ' + } + + response = self.client.post('/voting/', data, format='json') + self.assertEqual(response.status_code, 401) + + def test_update_voting(self): voting = self.create_voting() @@ -208,3 +320,59 @@ def test_update_voting(self): response = self.client.put('/voting/{}/'.format(voting.pk), data, format='json') self.assertEqual(response.status_code, 400) self.assertEqual(response.json(), 'Voting already tallied') + + def test_create_voting_url_whitespaces(self): + v = self.create_voting(url="_test voting") + self.assertTrue(Voting.objects.filter(url="_test+voting").exists()) + + def test_create_voting_url_exists(self): + v = self.create_voting(url="_test_voting") + + data = { + 'name': 'Example', + 'desc': 'Description example', + 'url': '_test_voting', + 'question': 'Is this a question? ', + 'question_opt': ['Yes', 'No'] + } + + response = self.client.post('/voting/', data, format='json') + self.assertEqual(response.status_code, 401) + + def test_create_voting_without_url(self): + data = { + 'name': 'Example No URL', + 'desc': 'Description example', + 'question': 'Is this a question? ', + 'question_opt': ['Yes', 'No'] + } + + response = self.client.post('/voting/', data, format='json') + self.assertEqual(response.status_code, 401) + + + def test_create_voting_orderquestion(self): + orderquestion = OrderQuestion(desc="Descripción de ejemplo") + orderquestion.save() + + v = Voting(name='test voting', url="_test_voting_orderquestion", order_question=orderquestion) + + v.save() + + a, _ = Auth.objects.get_or_create(url=settings.BASEURL, + defaults={'me': True, 'name': 'test auth'}) + a.save() + v.auths.add(a) + + self.assertTrue(Voting.objects.get(url="_test_voting_orderquestion").order_question==orderquestion) + + def test_create_orderquestion(self): + orderquestion = OrderQuestion(desc="Descripción de ejemplo") + orderquestion.save() + + self.assertTrue(OrderQuestion.objects.filter(desc="Descripción de ejemplo").exists()) + + + + + diff --git a/decide/voting/tests_selenium.py b/decide/voting/tests_selenium.py new file mode 100644 index 0000000000..6577842b58 --- /dev/null +++ b/decide/voting/tests_selenium.py @@ -0,0 +1,200 @@ +import time + +from base.models import Auth +from base.tests import BaseTestCase +from django.contrib.staticfiles.testing import StaticLiveServerTestCase +from django.test import TestCase +from selenium import webdriver +from selenium.webdriver.common.by import By +from selenium.webdriver.common.keys import Keys +from selenium.webdriver.support import expected_conditions as EC +from selenium.webdriver.support.ui import Select, WebDriverWait + +from .models import PoliticalParty, Question, QuestionOption, Voting + + +class VotingAdminTestCase(StaticLiveServerTestCase): + + def setUp(self): + self.base = BaseTestCase() + self.base.setUp() + + question = Question(desc="_Question_Test_Descripción_") + question.save() + + option1 = QuestionOption(question=question, option="_Option_1_Test_") + option1.save() + + option2 = QuestionOption(question=question, option="_Option_2_Test_") + option2.save() + + political_party = PoliticalParty(name="_Political_Party_Test_", acronym="_PPT_", description="_Political_Party_Test_Descripción_", leader="_Leader_") + political_party.save() + + auth = Auth(name="_Auth_Test", url=self.live_server_url, me=True) + auth.save() + + voting = Voting(desc="_Votación_Test_Ya_Existente", name="_Votación_Test_Ya_Existente_Descripción", question=question, political_party=political_party, url="_votacion_test_ejemplo_already_exists") + voting.save() + + voting.auths.add(auth) + + options = webdriver.ChromeOptions() + options.headless = True + self.driver = webdriver.Chrome(options=options) + + super().setUp() + + def tearDown(self): + super().tearDown() + self.driver.quit() + + self.base.tearDown() + + def create_voting(self, url=None): + self.driver.get(f'{self.live_server_url}/admin') + self.driver.set_window_size(924, 1053) + self.driver.find_element(By.ID, "id_username").send_keys("admin-selenium") + self.driver.find_element(By.ID, "id_password").send_keys("qwerty") + self.driver.find_element(By.ID, "id_password").send_keys(Keys.ENTER) + time.sleep(3) + + self.driver.find_element(By.CSS_SELECTOR, ".model-voting .addlink").click() + time.sleep(3) + self.driver.find_element(By.ID, "id_name").send_keys("_Votación_Test_") + self.driver.find_element(By.ID, "id_desc").click() + self.driver.find_element(By.ID, "id_desc").send_keys("_Votación_Test_Descripción_") + + select = Select(self.driver.find_element_by_id('id_question')) + select.select_by_visible_text('_Question_Test_Descripción_') + + select = Select(self.driver.find_element_by_id('id_political_party')) + select.select_by_visible_text('_PPT_ (_Political_Party_Test_) - _Leader_') + + select = Select(self.driver.find_element_by_id('id_auths')) + select.select_by_visible_text(str(self.live_server_url)) + + if url: + self.driver.find_element(By.ID, "id_url").click() + self.driver.find_element(By.ID, "id_url").send_keys(url) + + self.driver.find_element(By.NAME, "_save").click() + + def test_create_voting_with_url(self): + self.create_voting(url="_votacion_test_ejemplo_") + self.assertTrue(len(self.driver.find_elements_by_class_name('success'))==1) + + def test_create_voting_without_url(self): + self.create_voting() + self.assertTrue(len(self.driver.find_elements_by_class_name('errorlist'))==1) + + def test_create_voting_existing_url(self): + self.create_voting(url="_votacion_test_ejemplo_already_exists") + self.assertTrue(len(self.driver.find_elements_by_class_name('errorlist'))==1) + + def create_orderquestionsuccess(self): + self.driver.get(f'{self.live_server_url}/admin') + self.driver.set_window_size(1920, 1031) + self.driver.find_element(By.ID, "id_username").send_keys("admin-selenium") + self.driver.find_element(By.ID, "id_password").click() + self.driver.find_element(By.ID, "id_password").send_keys("qwerty") + self.driver.find_element(By.ID, "id_password").send_keys(Keys.ENTER) + self.driver.find_element(By.ID, "container").click() + self.driver.find_element(By.CSS_SELECTOR, ".model-orderquestion .addlink").click() + self.driver.find_element(By.ID, "id_desc").click() + self.driver.find_element(By.ID, "id_desc").click() + self.driver.find_element(By.ID, "id_desc").click() + self.driver.find_element(By.ID, "id_desc").send_keys("¿Del 1 al 3 cuanto te gusta este partido?") + self.driver.find_element(By.NAME, "_save").click() + self.driver.find_element(By.CSS_SELECTOR, "html").click() + self.driver.find_element(By.CSS_SELECTOR, "html").click() + + def test_create_orderquestion_success(self): + self.create_orderquestionsuccess() + self.assertTrue(len(self.driver.find_elements_by_class_name('success'))==1) + + def test_create_voting_orderquestion(self): + self.create_orderquestionsuccess() + self.driver.find_element(By.LINK_TEXT, "Home").click() + self.driver.find_element(By.CSS_SELECTOR, ".model-voting .addlink").click() + time.sleep(3) + self.driver.find_element(By.ID, "id_name").send_keys("_Votación_Test_") + self.driver.find_element(By.ID, "id_desc").click() + self.driver.find_element(By.ID, "id_desc").send_keys("_Votación_Test_Descripción_") + + select = Select(self.driver.find_element_by_id('id_order_question')) + select.select_by_visible_text('¿Del 1 al 3 cuanto te gusta este partido?') + + select = Select(self.driver.find_element_by_id('id_auths')) + select.select_by_visible_text(str(self.live_server_url)) + + self.driver.find_element(By.ID, "id_url").click() + self.driver.find_element(By.ID, "id_url").send_keys('test') + + self.driver.find_element(By.NAME, "_save").click() + self.assertTrue(len(self.driver.find_elements_by_class_name('success'))==1) + + + def test_orderquestionwitherror(self): + self.driver.get(f'{self.live_server_url}/admin') + self.driver.set_window_size(1920, 1031) + self.driver.find_element(By.ID, "id_username").send_keys("admin-selenium") + self.driver.find_element(By.ID, "id_password").click() + self.driver.find_element(By.ID, "id_password").send_keys("qwerty") + self.driver.find_element(By.ID, "id_password").send_keys(Keys.ENTER) + self.driver.find_element(By.ID, "container").click() + self.driver.find_element(By.CSS_SELECTOR, ".model-orderquestion .addlink").click() + self.driver.find_element(By.NAME, "_save").click() + self.assertTrue(len(self.driver.find_elements_by_class_name('errorlist'))==1) + + + def createYesOrNoQuestion(self): + self.driver.get(f'{self.live_server_url}/admin') + self.driver.set_window_size(1920, 1031) + self.driver.find_element(By.ID, "id_username").send_keys("admin-selenium") + self.driver.find_element(By.ID, "id_password").click() + self.driver.find_element(By.ID, "id_password").send_keys("qwerty") + self.driver.find_element(By.ID, "id_password").send_keys(Keys.ENTER) + self.driver.find_element(By.ID, "container").click() + self.driver.find_element(By.LINK_TEXT, "Yes or no questions").click() + self.driver.find_element(By.CSS_SELECTOR, ".addlink").click() + self.driver.find_element(By.ID, "id_desc").send_keys("Prueba yes or no question") + self.driver.find_element(By.NAME, "_save").click() + + def test_create_yesornoquestion_success(self): + self.createYesOrNoQuestion() + self.assertTrue(len(self.driver.find_elements_by_class_name('success'))==1) + + def test_yesornoquestionwitherror(self): + self.driver.get(f'{self.live_server_url}/admin') + self.driver.set_window_size(1920, 1031) + self.driver.find_element(By.ID, "id_username").send_keys("admin-selenium") + self.driver.find_element(By.ID, "id_password").click() + self.driver.find_element(By.ID, "id_password").send_keys("qwerty") + self.driver.find_element(By.ID, "id_password").send_keys(Keys.ENTER) + self.driver.find_element(By.ID, "container").click() + self.driver.find_element(By.CSS_SELECTOR, ".model-yesornoquestion .addlink").click() + self.driver.find_element(By.NAME, "_save").click() + self.assertTrue(len(self.driver.find_elements_by_class_name('errorlist'))==1) + + def test_votingwithyesornoquestion(self): + self.createYesOrNoQuestion() + self.driver.find_element(By.LINK_TEXT, "Home").click() + self.driver.find_element(By.CSS_SELECTOR, ".model-voting .addlink").click() + time.sleep(3) + self.driver.find_element(By.ID, "id_name").send_keys("_Votación_Test_") + self.driver.find_element(By.ID, "id_desc").click() + self.driver.find_element(By.ID, "id_desc").send_keys("_Votación_Test_Descripción_") + + dropdown = self.driver.find_element(By.ID, "id_yes_or_no_question") + dropdown.find_element(By.XPATH, "//option[. = 'Prueba yes or no question']").click() + self.driver.find_element(By.ID, "id_yes_or_no_question").click() + + select = Select(self.driver.find_element_by_id('id_auths')) + select.select_by_visible_text(str(self.live_server_url)) + + self.driver.find_element(By.ID, "id_url").click() + self.driver.find_element(By.ID, "id_url").send_keys('test') + + self.driver.find_element(By.NAME, "_save").click() + self.assertTrue(len(self.driver.find_elements_by_class_name('success'))==1) diff --git a/decide/voting/views.py b/decide/voting/views.py index 406854d80e..9c3d95f8ef 100644 --- a/decide/voting/views.py +++ b/decide/voting/views.py @@ -10,7 +10,6 @@ from base.perms import UserIsStaff from base.models import Auth - class VotingView(generics.ListCreateAPIView): queryset = Voting.objects.all() serializer_class = VotingSerializer @@ -98,3 +97,4 @@ def put(self, request, voting_id, *args, **kwars): st = status.HTTP_400_BAD_REQUEST return Response(msg, status=st) + diff --git a/docker/Dockerfile b/docker/Dockerfile index 032eed28e2..c33fc47ca8 100644 --- a/docker/Dockerfile +++ b/docker/Dockerfile @@ -1,4 +1,4 @@ -from python:alpine +from python:3.7-alpine RUN apk add --no-cache git postgresql-dev gcc libc-dev RUN apk add --no-cache gcc g++ make libffi-dev python3-dev build-base @@ -10,7 +10,8 @@ RUN pip install ipython WORKDIR /app -RUN git clone https://github.com/wadobo/decide.git . +RUN git clone https://github.com/RioTinto-Votaciones/decide.git . +COPY requirements_docker.txt /app/decide/requirements.txt RUN pip install -r requirements.txt WORKDIR /app/decide diff --git a/docker/docker-compose.yml b/docker/docker-compose.yml index 095583eb02..4689c5ff98 100644 --- a/docker/docker-compose.yml +++ b/docker/docker-compose.yml @@ -4,11 +4,13 @@ services: db: restart: always container_name: decide_db - image: postgres:alpine + image: postgres:10.15-alpine volumes: - db:/var/lib/postgresql/data networks: - decide + environment: + - POSTGRES_PASSWORD=postgres web: restart: always container_name: decide_web diff --git a/docker/docker-settings.py b/docker/docker-settings.py index 01e643d936..f1b6cb5335 100644 --- a/docker/docker-settings.py +++ b/docker/docker-settings.py @@ -5,6 +5,7 @@ 'ENGINE': 'django.db.backends.postgresql', 'NAME': 'postgres', 'USER': 'postgres', + 'PASSWORD': 'postgres', 'HOST': 'db', 'PORT': 5432, } diff --git a/docker/requirements_docker.txt b/docker/requirements_docker.txt new file mode 100644 index 0000000000..9b83fb4496 --- /dev/null +++ b/docker/requirements_docker.txt @@ -0,0 +1,13 @@ +Django==2.0 +pycryptodome==3.6.6 +djangorestframework==3.7.7 +django-cors-headers==2.1.0 +requests==2.18.4 +django-filter==1.1.0 +psycopg2-binary==2.7.4 +django-rest-swagger==2.2.0 +coverage==4.5.2 +django-nose==1.4.6 +jsonnet==0.12.1 +django-heroku +gunicorn diff --git a/requirements.txt b/requirements.txt index 73e5855f29..b2cb3d53cc 100644 --- a/requirements.txt +++ b/requirements.txt @@ -9,3 +9,5 @@ django-rest-swagger==2.2.0 coverage==4.5.2 django-nose==1.4.6 jsonnet==0.12.1 +django-heroku +gunicorn diff --git a/runtime.txt b/runtime.txt new file mode 100644 index 0000000000..8bcbcd4846 --- /dev/null +++ b/runtime.txt @@ -0,0 +1,2 @@ +python-3.7.5 +