diff --git a/elections/forms.py b/elections/forms.py index e1b3b58..9b1c7bc 100644 --- a/elections/forms.py +++ b/elections/forms.py @@ -1,13 +1,27 @@ from django import forms from django.forms.models import inlineformset_factory from django.utils import timezone +from django.utils.functional import keep_lazy_text +from django.utils.text import capfirst from django.utils.translation import gettext_lazy as _ from .models import Election, Option, Question from .utils import check_csv +# En attendant que ce soit merge dans django-translated-fields +def language_code_formfield_callback(db_field, **kwargs): + language_code = getattr(db_field, "_translated_field_language_code", "") + if language_code: + kwargs["label"] = keep_lazy_text(lambda s: "%s [%s]" % (s, language_code))( + capfirst(db_field.verbose_name) + ) + return db_field.formfield(**kwargs) + + class ElectionForm(forms.ModelForm): + formfield_callback = language_code_formfield_callback + def clean(self): cleaned_data = super().clean() if cleaned_data["start_date"] < timezone.now(): @@ -23,16 +37,18 @@ class ElectionForm(forms.ModelForm): class Meta: model = Election fields = [ - "name", - "description", - "vote_restrictions", - "restricted", + *Election.name.fields, "start_date", "end_date", + *Election.description.fields, + "restricted", + *Election.vote_restrictions.fields, ] widgets = { - "description": forms.Textarea(attrs={"rows": 4}), - "vote_restrictions": forms.Textarea(attrs={"rows": 4}), + "description_en": forms.Textarea(attrs={"rows": 4}), + "description_fr": forms.Textarea(attrs={"rows": 4}), + "vote_restrictions_en": forms.Textarea(attrs={"rows": 4}), + "vote_restrictions_fr": forms.Textarea(attrs={"rows": 4}), } @@ -59,17 +75,21 @@ class VoterMailForm(forms.Form): class QuestionForm(forms.ModelForm): + formfield_callback = language_code_formfield_callback + class Meta: model = Question - fields = ["text", "type"] - widgets = {"text": forms.TextInput} + fields = [*Question.text.fields, "type"] + widgets = {"text_fr": forms.TextInput, "text_en": forms.TextInput} class OptionForm(forms.ModelForm): + formfield_callback = language_code_formfield_callback + class Meta: model = Option - fields = ["text"] - widgets = {"text": forms.TextInput} + fields = [*Option.text.fields] + widgets = {"text_fr": forms.TextInput, "text_en": forms.TextInput} class DeleteVoteForm(forms.Form): diff --git a/elections/migrations/0022_auto_20210415_1050.py b/elections/migrations/0022_auto_20210415_1050.py new file mode 100644 index 0000000..590bbc3 --- /dev/null +++ b/elections/migrations/0022_auto_20210415_1050.py @@ -0,0 +1,63 @@ +# Generated by Django 3.2 on 2021-04-15 08:50 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ("elections", "0021_election_vote_restrictions"), + ] + + operations = [ + migrations.RenameField( + model_name="election", + old_name="description", + new_name="description_fr", + ), + migrations.RenameField( + model_name="election", + old_name="name", + new_name="name_fr", + ), + migrations.RenameField( + model_name="election", + old_name="vote_restrictions", + new_name="vote_restrictions_fr", + ), + migrations.RenameField( + model_name="option", + old_name="text", + new_name="text_fr", + ), + migrations.RenameField( + model_name="question", + old_name="text", + new_name="text_fr", + ), + migrations.AddField( + model_name="election", + name="description_en", + field=models.TextField(blank=True, verbose_name="description"), + ), + migrations.AddField( + model_name="election", + name="name_en", + field=models.CharField(blank=True, max_length=255, verbose_name="nom"), + ), + migrations.AddField( + model_name="election", + name="vote_restrictions_en", + field=models.TextField(blank=True, verbose_name="conditions de vote"), + ), + migrations.AddField( + model_name="option", + name="text_en", + field=models.TextField(blank=True, verbose_name="texte"), + ), + migrations.AddField( + model_name="question", + name="text_en", + field=models.TextField(blank=True, verbose_name="question"), + ), + ] diff --git a/elections/models.py b/elections/models.py index 3d07664..5e7c203 100644 --- a/elections/models.py +++ b/elections/models.py @@ -1,3 +1,5 @@ +from translated_fields import TranslatedFieldWithFallback + from django.conf import settings from django.contrib.auth.models import AbstractUser from django.db import models, transaction @@ -25,14 +27,18 @@ from .utils import ( class Election(models.Model): - name = models.CharField(_("nom"), max_length=255) + name = TranslatedFieldWithFallback(models.CharField(_("nom"), max_length=255)) short_name = models.SlugField(_("nom bref"), unique=True) - description = models.TextField(_("description"), blank=True) + description = TranslatedFieldWithFallback( + models.TextField(_("description"), blank=True) + ) start_date = models.DateTimeField(_("date et heure de début")) end_date = models.DateTimeField(_("date et heure de fin")) - vote_restrictions = models.TextField(_("conditions de vote"), blank=True) + vote_restrictions = TranslatedFieldWithFallback( + models.TextField(_("conditions de vote"), blank=True) + ) restricted = models.BooleanField( _("restreint le vote à une liste de personnes"), default=True @@ -69,7 +75,7 @@ class Question(models.Model): election = models.ForeignKey( Election, related_name="questions", on_delete=models.CASCADE ) - text = models.TextField(_("question"), blank=False) + text = TranslatedFieldWithFallback(models.TextField(_("question"), blank=False)) type = models.CharField( _("type de question"), choices=QUESTION_TYPES, @@ -125,7 +131,7 @@ class Option(models.Model): question = models.ForeignKey( Question, related_name="options", on_delete=models.CASCADE ) - text = models.TextField(_("texte"), blank=False) + text = TranslatedFieldWithFallback(models.TextField(_("texte"), blank=False)) winner = models.BooleanField(_("option gagnante"), default=False) voters = models.ManyToManyField( diff --git a/elections/templates/elections/election_admin.html b/elections/templates/elections/election_admin.html index 44c59be..d98b77b 100644 --- a/elections/templates/elections/election_admin.html +++ b/elections/templates/elections/election_admin.html @@ -2,6 +2,24 @@ {% load i18n %} +{% block extra_head %} + +{% endblock %} + + {% block content %}
@@ -217,55 +235,38 @@ {# Rajout d'une option #} {% if election.start_date > current_time %} -
-
- {% csrf_token %} - -
- - - - -
-
- -
-
-
+
+ +
{% endif %}
{% endfor %} {# Rajout d'une question #} {% if election.start_date > current_time %} + +{# Rajout d'une option #} +{% trans "Rajouter une option" as modal_title %} +{% include "forms/modal-form.html" with modal_id="add_option" form=o_form %} + +{# Rajout d'une question #} +{% trans "Rajouter une question" as modal_title %} +{% include "forms/modal-form.html" with modal_id="add_question" form=q_form %} + +
-
- {% csrf_token %} - -
-
- - - - -
- -
- - - -
- -
- -
-
-
+
{% endif %} diff --git a/elections/views.py b/elections/views.py index b661520..f756ea5 100644 --- a/elections/views.py +++ b/elections/views.py @@ -90,8 +90,16 @@ class ElectionAdminView(CreatorOnlyMixin, DetailView): return reverse("election.view", args=[self.object.pk]) def get_context_data(self, **kwargs): + from django.utils.translation import get_language + + print(get_language()) kwargs.update( - {"current_time": timezone.now(), "question_types": QUESTION_TYPES} + { + "current_time": timezone.now(), + "question_types": QUESTION_TYPES, + "o_form": OptionForm, + "q_form": QuestionForm, + } ) return super().get_context_data(**kwargs) diff --git a/requirements.txt b/requirements.txt index 117f3e3..6007652 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,4 +1,5 @@ django==3.2.* +django-translated-fields==0.11 authens>=0.1b2 numpy networkx diff --git a/shared/locale/en/LC_MESSAGES/django.mo b/shared/locale/en/LC_MESSAGES/django.mo index 1dac996..e591006 100644 Binary files a/shared/locale/en/LC_MESSAGES/django.mo and b/shared/locale/en/LC_MESSAGES/django.mo differ diff --git a/shared/locale/en/LC_MESSAGES/django.po b/shared/locale/en/LC_MESSAGES/django.po index 17d4db2..9b3d40f 100644 --- a/shared/locale/en/LC_MESSAGES/django.po +++ b/shared/locale/en/LC_MESSAGES/django.po @@ -7,9 +7,9 @@ msgid "" msgstr "" "Project-Id-Version: 0.1\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2021-04-14 03:31+0200\n" -"PO-Revision-Date: 2021-04-14 20:17+0200\n" -"Last-Translator: Tom Hubrecht \n" +"POT-Creation-Date: 2021-04-15 16:56+0200\n" +"PO-Revision-Date: 2021-04-15 16:57+0200\n" +"Last-Translator: Test Translator \n" "Language-Team: \n" "Language: en\n" "MIME-Version: 1.0\n" @@ -18,125 +18,125 @@ msgstr "" "Plural-Forms: nplurals=2; plural=(n != 1);\n" "X-Generator: Poedit 2.4.1\n" -#: elections/forms.py:15 +#: elections/forms.py:29 msgid "Impossible de faire débuter l'élection dans le passé" msgstr "Impossible to start the election in the past" -#: elections/forms.py:19 +#: elections/forms.py:33 msgid "Impossible de terminer l'élection avant de la commencer" msgstr "Impossible to end the election before it starts" -#: elections/forms.py:40 +#: elections/forms.py:56 msgid "Sélectionnez un fichier .csv" msgstr "Select a .csv file" -#: elections/forms.py:50 +#: elections/forms.py:66 msgid "Extension de fichier invalide, il faut un fichier au format CSV." msgstr "Invalid file extension, a CSV file is required." -#: elections/forms.py:80 +#: elections/forms.py:100 msgid "Supprimer le vote de {} ({}) ?" msgstr "Delete the vote of {} ({}) ?" -#: elections/forms.py:85 elections/templates/elections/election_admin.html:150 -#: elections/templates/elections/election_admin.html:177 +#: elections/forms.py:105 elections/templates/elections/election_admin.html:168 +#: elections/templates/elections/election_admin.html:195 #: elections/templates/elections/election_voters.html:57 msgid "Supprimer" msgstr "Delete" -#: elections/forms.py:85 +#: elections/forms.py:105 msgid "Non" msgstr "No" -#: elections/forms.py:85 +#: elections/forms.py:105 msgid "Oui" msgstr "Yes" -#: elections/models.py:28 +#: elections/models.py:31 msgid "nom" msgstr "name" -#: elections/models.py:29 +#: elections/models.py:32 msgid "nom bref" msgstr "short name" -#: elections/models.py:30 +#: elections/models.py:34 msgid "description" msgstr "description" -#: elections/models.py:32 +#: elections/models.py:37 msgid "date et heure de début" msgstr "start date and time" -#: elections/models.py:33 +#: elections/models.py:38 msgid "date et heure de fin" msgstr "end date and time" -#: elections/models.py:35 +#: elections/models.py:41 msgid "conditions de vote" msgstr "voting requirements" -#: elections/models.py:38 +#: elections/models.py:45 msgid "restreint le vote à une liste de personnes" msgstr "restricts the vote to a list of people" -#: elections/models.py:42 +#: elections/models.py:49 msgid "mail avec les identifiants envoyé" msgstr "mail with credentials sent" -#: elections/models.py:59 +#: elections/models.py:66 msgid "résultats publics" msgstr "results published" -#: elections/models.py:60 +#: elections/models.py:67 msgid "dépouillée" msgstr "counted" -#: elections/models.py:62 +#: elections/models.py:69 msgid "archivée" msgstr "archived" -#: elections/models.py:72 +#: elections/models.py:79 msgid "question" msgstr "question" -#: elections/models.py:74 +#: elections/models.py:81 msgid "type de question" msgstr "type of question" -#: elections/models.py:81 +#: elections/models.py:88 msgid "nombre maximal de votes reçus" msgstr "maximal number of votes received" -#: elections/models.py:128 +#: elections/models.py:135 msgid "texte" msgstr "text" -#: elections/models.py:130 +#: elections/models.py:137 msgid "option gagnante" msgstr "winning option" -#: elections/models.py:138 +#: elections/models.py:145 msgid "nombre de votes reçus" msgstr "number of votes received" -#: elections/models.py:157 +#: elections/models.py:164 msgid "rang de l'option" msgstr "option's ranking" -#: elections/models.py:173 +#: elections/models.py:180 msgid "votes supplémentaires" msgstr "extra votes" -#: elections/models.py:189 +#: elections/models.py:196 msgid "Nom et Prénom" msgstr "Name and surname" -#: elections/models.py:211 elections/tests/test_models.py:57 +#: elections/models.py:218 elections/tests/test_models.py:57 msgid "identifiants spécifiques" msgstr "dedicated credentials" -#: elections/models.py:215 +#: elections/models.py:222 msgid "Peut administrer des élections" msgstr "Can manage elections" @@ -285,52 +285,49 @@ msgstr "Login via CAS" msgid "A voté" msgstr "Voted" -#: elections/templates/elections/election_admin.html:23 +#: elections/templates/elections/election_admin.html:41 msgid "Actions" msgstr "Actions" -#: elections/templates/elections/election_admin.html:36 -#: elections/templates/elections/election_admin.html:159 -#: elections/templates/elections/election_admin.html:182 +#: elections/templates/elections/election_admin.html:54 +#: elections/templates/elections/election_admin.html:177 +#: elections/templates/elections/election_admin.html:200 msgid "Modifier" msgstr "Edit" -#: elections/templates/elections/election_admin.html:45 +#: elections/templates/elections/election_admin.html:63 #: elections/templates/elections/upload_voters.html:25 msgid "Gestion de la liste de votant·e·s" msgstr "Management of the voters' list" -#: elections/templates/elections/election_admin.html:57 +#: elections/templates/elections/election_admin.html:75 #: elections/templates/elections/election_voters.html:27 msgid "Liste des votant·e·s" msgstr "Voters' list" -#: elections/templates/elections/election_admin.html:65 +#: elections/templates/elections/election_admin.html:83 msgid "Dépouiller" msgstr "Count" -#: elections/templates/elections/election_admin.html:76 +#: elections/templates/elections/election_admin.html:94 msgid "Publier" msgstr "Publish" -#: elections/templates/elections/election_admin.html:78 +#: elections/templates/elections/election_admin.html:96 msgid "Dépublier" msgstr "De-publish" -#: elections/templates/elections/election_admin.html:88 +#: elections/templates/elections/election_admin.html:106 msgid "Archiver" msgstr "Archive" -#: elections/templates/elections/election_admin.html:225 +#: elections/templates/elections/election_admin.html:243 +#: elections/templates/elections/election_admin.html:254 msgid "Rajouter une option" msgstr "Add an option" -#: elections/templates/elections/election_admin.html:231 -#: elections/templates/elections/election_admin.html:265 -msgid "Valider" -msgstr "Confirm" - -#: elections/templates/elections/election_admin.html:248 +#: elections/templates/elections/election_admin.html:258 +#: elections/templates/elections/election_admin.html:268 msgid "Rajouter une question" msgstr "Add a question" @@ -542,51 +539,51 @@ msgstr "Invalid e-mail address in line {}: '{}'." msgid "Élection créée avec succès !" msgstr "Election successfully created!" -#: elections/views.py:105 +#: elections/views.py:113 msgid "Liste de votant·e·s importée avec succès !" msgstr "Voters list successfully imported!" -#: elections/views.py:139 +#: elections/views.py:147 msgid "Mail d'annonce envoyé avec succès !" msgstr "Announcement e-mail sent successfully!" -#: elections/views.py:171 +#: elections/views.py:179 msgid "Élection modifiée avec succès !" msgstr "Election successfully modified!" -#: elections/views.py:241 +#: elections/views.py:249 msgid "Élection dépouillée avec succès !" msgstr "Election successfully counted!" -#: elections/views.py:267 +#: elections/views.py:275 msgid "Élection publiée avec succès !" msgstr "Election successfully published!" -#: elections/views.py:268 +#: elections/views.py:276 msgid "Élection dépubliée avec succès !" msgstr "Election successfully de-published!" -#: elections/views.py:280 +#: elections/views.py:288 msgid "Élection archivée avec succès !" msgstr "Election successfully archived!" -#: elections/views.py:312 +#: elections/views.py:320 msgid "Question modifiée avec succès !" msgstr "Question successfully modified!" -#: elections/views.py:324 +#: elections/views.py:332 msgid "Question supprimée !" msgstr "Question deleted!" -#: elections/views.py:362 +#: elections/views.py:370 msgid "Option modifiée avec succès !" msgstr "Option successfully modified!" -#: elections/views.py:374 +#: elections/views.py:382 msgid "Option supprimée !" msgstr "Option deleted!" -#: elections/views.py:514 +#: elections/views.py:522 msgid "Votre choix a bien été enregistré !" msgstr "Your choice has been recorded!" @@ -646,16 +643,16 @@ msgstr "Send an e-mail" msgid "Élections" msgstr "Elections" -#: shared/templates/base.html:163 +#: shared/templates/base.html:125 #, python-format msgid "Connecté·e en tant que %(name)s par %(connection)s" msgstr "Logged in as %(name)s via %(connection)s" -#: shared/templates/base.html:178 +#: shared/templates/base.html:141 msgid "Se connecter" msgstr "Log in" -#: shared/templates/base.html:219 +#: shared/templates/base.html:224 msgid "" "Développé par KDEns. En cas de pépin, contacter - - - {% trans "Se connecter" %} - - - + + {% trans "Se connecter" %} + + @@ -163,8 +161,8 @@