diff --git a/.gitignore b/.gitignore index 501d233..c44c296 100644 --- a/.gitignore +++ b/.gitignore @@ -4,3 +4,4 @@ __pycache__/ .#* .ipynb_checkpoints/ .env.local +backup diff --git a/docker/Dockerfile b/docker/Dockerfile index 89872e3..a1e5207 100644 --- a/docker/Dockerfile +++ b/docker/Dockerfile @@ -4,6 +4,7 @@ RUN mkdir /code WORKDIR /code RUN apt-get update && apt-get install -y \ gettext \ - python3-pip + python3-pip \ + postgresql-client COPY requirements.txt /code/ RUN pip install -r requirements.txt diff --git a/docker/backup.sh b/docker/backup.sh new file mode 100755 index 0000000..a1fdfa8 --- /dev/null +++ b/docker/backup.sh @@ -0,0 +1 @@ +docker exec -it mvapi_web_1 python manage.py dbbackup diff --git a/election/migrations/0004_election_send_mail.py b/election/migrations/0004_election_send_mail.py new file mode 100644 index 0000000..c9108d2 --- /dev/null +++ b/election/migrations/0004_election_send_mail.py @@ -0,0 +1,18 @@ +# Generated by Django 3.1.1 on 2021-04-10 15:13 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('election', '0003_remove_token_email'), + ] + + operations = [ + migrations.AddField( + model_name='election', + name='send_mail', + field=models.BooleanField(default=True), + ), + ] diff --git a/election/models.py b/election/models.py index cab2c9f..993d018 100644 --- a/election/models.py +++ b/election/models.py @@ -16,6 +16,7 @@ class Election(RandomPrimaryIdModel): num_grades = models.PositiveSmallIntegerField("Num. grades", null=False) start_at = models.IntegerField("Start date", default=time) finish_at = models.IntegerField("End date",default=time) + send_mail = models.BooleanField(default=True) # Language preference is used for emailing voters select_language = models.CharField("Language", max_length=2,default="en") # If results are restricted, one can see them only when the election is finished diff --git a/election/serializers.py b/election/serializers.py index 2984745..0976f6a 100644 --- a/election/serializers.py +++ b/election/serializers.py @@ -1,20 +1,12 @@ from django.utils.text import slugify from rest_framework import serializers -from election.models import Election, Vote +from election.models import Election, Vote, Token from django.conf import settings -class ElectionViewMixin: - - def to_representation(self, instance): - ret = super().to_representation(instance) - ret["slug"] = slugify(instance.title) - ret["id"] = instance.id - return ret - -class ElectionCreateSerializer(ElectionViewMixin, serializers.ModelSerializer): +class ElectionCreateSerializer(serializers.ModelSerializer): elector_emails = serializers.ListField( child=serializers.EmailField(), @@ -43,16 +35,32 @@ class ElectionCreateSerializer(ElectionViewMixin, serializers.ModelSerializer): 'start_at', 'finish_at', 'select_language', - 'restrict_results' + 'restrict_results', + 'send_mail', ) + def to_representation(self, instance): + ret = super().to_representation(instance) + ret["slug"] = slugify(instance.title) + ret["id"] = instance.id + ret["tokens"] = [token.id for token in Token.objects.filter(election=instance)] + return ret + + -class ElectionViewSerializer(ElectionViewMixin, serializers.ModelSerializer): + +class ElectionViewSerializer(serializers.ModelSerializer): class Meta: model = Election fields = '__all__' + def to_representation(self, instance): + ret = super().to_representation(instance) + ret["slug"] = slugify(instance.title) + ret["id"] = instance.id + return ret + class VoteSerializer(serializers.ModelSerializer): diff --git a/election/views.py b/election/views.py index ffc43fc..14fd481 100644 --- a/election/views.py +++ b/election/views.py @@ -145,10 +145,11 @@ class ElectionCreateAPIView(CreateAPIView): ) list_email_token.append([email,token.id]) - if settings.EMAIL_TYPE == "API": - send_mails_invitation_api(list_email_token, election) - else: - send_mails_invitation_smtp(list_email_token, election) + if election.send_mail: + if settings.EMAIL_TYPE == "API": + send_mails_invitation_api(list_email_token, election) + else: + send_mails_invitation_smtp(list_email_token, election) headers = self.get_success_headers(serializer.data) return Response( @@ -297,7 +298,6 @@ class ResultAPIView(APIView): merit_profiles[idx], value.grade, ) - # for idx in sorted_indexes for idx, value in indexed_values ] serializer = serializers.CandidateSerializer(candidates, many=True) @@ -347,4 +347,4 @@ class LinkAPIView(CreateAPIView): send_status = send_mail_api(emails,text_body,html_body,election.title) - return Response(status=send_status) \ No newline at end of file + return Response(status=send_status) diff --git a/locale/de/LC_MESSAGES/django.po b/locale/de/LC_MESSAGES/django.po index df116eb..f863ad0 100644 --- a/locale/de/LC_MESSAGES/django.po +++ b/locale/de/LC_MESSAGES/django.po @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2020-09-11 10:12+0000\n" +"POT-Creation-Date: 2021-03-17 18:15+0000\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" diff --git a/locale/en/LC_MESSAGES/django.po b/locale/en/LC_MESSAGES/django.po index 57f46e6..650ae39 100644 --- a/locale/en/LC_MESSAGES/django.po +++ b/locale/en/LC_MESSAGES/django.po @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2020-09-16 10:37+0000\n" +"POT-Creation-Date: 2021-03-17 18:15+0000\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" diff --git a/locale/es/LC_MESSAGES/django.po b/locale/es/LC_MESSAGES/django.po index fa42107..13401ac 100644 --- a/locale/es/LC_MESSAGES/django.po +++ b/locale/es/LC_MESSAGES/django.po @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2020-09-11 10:12+0000\n" +"POT-Creation-Date: 2021-03-17 18:15+0000\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" diff --git a/locale/fr/LC_MESSAGES/django.po b/locale/fr/LC_MESSAGES/django.po index 95ca2cb..1d9dcfc 100644 --- a/locale/fr/LC_MESSAGES/django.po +++ b/locale/fr/LC_MESSAGES/django.po @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2020-12-08 11:16+0000\n" +"POT-Creation-Date: 2021-03-17 18:15+0000\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" diff --git a/mvapi/settings.py b/mvapi/settings.py index e83383b..3e7d5a7 100644 --- a/mvapi/settings.py +++ b/mvapi/settings.py @@ -20,8 +20,8 @@ DEBUG = os.environ['DJANGO_DEBUG'] == 'True' SECRET_KEY = os.environ['DJANGO_SECRET_KEY'] ALLOWED_HOSTS = os.environ['DJANGO_ALLOWED_HOSTS'].split(',') MAX_NUM_GRADES = int(os.environ['MAX_NUM_GRADES']) -LANGUAGE_AVAILABLE = os.environ['LANGUAGE_AVAILABLE'] DEFAULT_LANGUAGE = "en" +LANGUAGE_AVAILABLE = os.environ.get('LANGUAGE_AVAILABLE', DEFAULT_LANGUAGE) # Application definition @@ -33,11 +33,15 @@ INSTALLED_APPS = [ 'django.contrib.messages', 'django.contrib.staticfiles', 'django_extensions', + 'dbbackup', # django-dbbackup 'rest_framework', 'corsheaders', 'election' ] +DBBACKUP_STORAGE = 'django.core.files.storage.FileSystemStorage' +DBBACKUP_STORAGE_OPTIONS = {'location': 'backup'} + MIDDLEWARE = [ 'corsheaders.middleware.CorsMiddleware', 'django.middleware.security.SecurityMiddleware', @@ -155,4 +159,4 @@ elif EMAIL_TYPE == "SMTP": EMAIL_HOST=os.environ['EMAIL_HOST'] else: - raise ValueError('API and SMTP are only available') \ No newline at end of file + raise ValueError('API and SMTP are only available') diff --git a/requirements.txt b/requirements.txt index 944d6ff..64466b0 100644 --- a/requirements.txt +++ b/requirements.txt @@ -4,8 +4,9 @@ django-nose==1.4.7 django-extensions==3.0.8 django-cors-headers==3.5 djangorestframework==3.11.1 +django-dbbackup==3.3.0 nose==1.3.7 psycopg2==2.8.6 dash==1.11.0 dash_daq==0.5.0 -dash-bootstrap-components==0.9.2 \ No newline at end of file +dash-bootstrap-components==0.9.2 diff --git a/scripts/add_voters.py b/scripts/add_voters.py new file mode 100644 index 0000000..082ec84 --- /dev/null +++ b/scripts/add_voters.py @@ -0,0 +1,43 @@ +""" +Add voters to a started election. +""" +from typing import List, Dict +import os +import pathlib +import argparse +import django + + +def load_mvapi(): + import os + import sys + sys.path.append('../') + os.environ.setdefault("DJANGO_SETTINGS_MODULE", "mvapi.settings") + django.setup() + +load_mvapi() +from election.models import Election, Vote, Token + + +if __name__ == "__main__": + parser = argparse.ArgumentParser() + parser.add_argument("--election_id", type=str) + parser.add_argument("--num_tokens", type=int) + parser.add_argument("--output", type=str, required=True) + args = parser.parse_args() + print(args) + + try: + election = Election.objects.get(id=args.election_id) + except Election.DoesNotExist: + raise ValueError(f"The election {election} does not exist") + + tokens = [] + for email in range(args.num_tokens): + token = Token.objects.create(election=election) + tokens.append(token.id) + # print(token) + # send_mail_invitation(email, election, token.id) + + with open(args.output, "w") as fid: + fid.write("\n".join([f"https://app.mieuxvoter.fr/vote/{args.election_id}/?token={t}" for t in tokens]))