diff --git a/elections/templates/elections/option_update.html b/elections/templates/elections/option_update.html
new file mode 100644
index 0000000..92f44aa
--- /dev/null
+++ b/elections/templates/elections/option_update.html
@@ -0,0 +1,41 @@
+{% extends "elections/base.html" %}
+{% load i18n static %}
+
+
+{% block content %}
+
+{% for error in form.non_field_errors %}
+
+ {{ error }}
+
+{% endfor %}
+
+
{% trans "Modification d'une option" %}
+
+
+
+
+{% endblock %}
diff --git a/elections/templates/elections/question_update.html b/elections/templates/elections/question_update.html
new file mode 100644
index 0000000..139ae1a
--- /dev/null
+++ b/elections/templates/elections/question_update.html
@@ -0,0 +1,41 @@
+{% extends "elections/base.html" %}
+{% load i18n static %}
+
+
+{% block content %}
+
+{% for error in form.non_field_errors %}
+
+ {{ error }}
+
+{% endfor %}
+
+
{% trans "Modification d'une question" %}
+
+
+
+
+{% endblock %}
diff --git a/elections/urls.py b/elections/urls.py
index 1de4200..24ca25e 100644
--- a/elections/urls.py
+++ b/elections/urls.py
@@ -3,6 +3,7 @@ from django.urls import path
from . import views
urlpatterns = [
+ # Admin views
path("create/", views.ElectionCreateView.as_view(), name="election.create"),
path("created/", views.ElectionListView.as_view(), name="election.created"),
path("admin/
", views.ElectionAdminView.as_view(), name="election.admin"),
@@ -16,6 +17,39 @@ urlpatterns = [
path(
"archive/", views.ElectionArchiveView.as_view(), name="election.archive"
),
+ # Question views
+ path(
+ "add-question/",
+ views.AddQuestionView.as_view(),
+ name="election.add-question",
+ ),
+ path(
+ "mod-question/",
+ views.ModQuestionView.as_view(),
+ name="election.mod-question",
+ ),
+ path(
+ "del-question/",
+ views.DelQuestionView.as_view(),
+ name="election.del-question",
+ ),
+ # Option views
+ path(
+ "add-option/",
+ views.AddOptionView.as_view(),
+ name="election.add-option",
+ ),
+ path(
+ "mod-option/",
+ views.ModOptionView.as_view(),
+ name="election.mod-option",
+ ),
+ path(
+ "del-option/",
+ views.DelOptionView.as_view(),
+ name="election.del-option",
+ ),
+ # Common views
path("view/", views.ElectionView.as_view(), name="election.view"),
path("vote/", views.VoteView.as_view(), name="election.vote"),
]
diff --git a/elections/views.py b/elections/views.py
index 07dfe4a..382db22 100644
--- a/elections/views.py
+++ b/elections/views.py
@@ -13,14 +13,31 @@ from django.views.generic import (
RedirectView,
UpdateView,
)
-from django.views.generic.detail import SingleObjectMixin
-from .forms import ElectionCreateForm, OptionFormSet
-from .mixins import CreatorOnlyMixin
+from .forms import ElectionForm, OptionForm, OptionFormSet, QuestionForm
+from .mixins import CreatorOnlyEditMixin, CreatorOnlyMixin
from .models import Election, Option, Question
# TODO: access control *everywhere*
+# #############################################################################
+# Utils Views
+# #############################################################################
+
+
+class BackgroundUpdateView(RedirectView):
+ success_message = ""
+
+ def get_success_message(self):
+ return self.success_message
+
+ def get(self, request, *args, **kwargs):
+ success_message = self.get_success_message()
+ if success_message:
+ messages.success(self.request, success_message)
+ return super().get(self, request, *args, **kwargs)
+
+
# #############################################################################
# Administration Views
# #############################################################################
@@ -28,7 +45,7 @@ from .models import Election, Option, Question
class ElectionCreateView(SuccessMessageMixin, CreateView):
model = Election
- form_class = ElectionCreateForm
+ form_class = ElectionForm
template_name = "elections/election_create.html"
success_message = _("Élection créée avec succès !")
@@ -63,23 +80,20 @@ class ElectionListView(CreatorOnlyMixin, ListView):
template_name = "elections/election_list.html"
-class ElectionUpdateView(SuccessMessageMixin, UpdateView):
+class ElectionUpdateView(CreatorOnlyEditMixin, SuccessMessageMixin, UpdateView):
model = Election
- fields = ["name", "description", "start_date", "end_date"]
+ form_class = ElectionForm
success_message = _("Élection modifiée avec succès !")
template_name = "elections/election_update.html"
def get_success_url(self):
return reverse("election.admin", args=[self.object.pk])
- def get_queryset(self):
- # On ne peut plus modifier une élection qui a déjà commencé
- return super().get_queryset().filter(start_date__gt=timezone.now())
-
-class ElectionTallyView(SuccessMessageMixin, SingleObjectMixin, RedirectView):
+class ElectionTallyView(CreatorOnlyEditMixin, BackgroundUpdateView):
model = Election
pattern_name = "election.admin"
+ success_message = _("Élection dépouillée avec succès !")
def get_queryset(self):
return (
@@ -106,35 +120,32 @@ class ElectionTallyView(SuccessMessageMixin, SingleObjectMixin, RedirectView):
Question.objects.bulk_update(questions, ["max_votes"])
election.tallied = True
election.save()
- messages.success(request, _("Élection dépouillée avec succès !"))
return super().get(request, *args, **kwargs)
-class ElectionChangePublicationView(
- SuccessMessageMixin, SingleObjectMixin, RedirectView
-):
+class ElectionChangePublicationView(CreatorOnlyEditMixin, BackgroundUpdateView):
model = Election
pattern_name = "election.admin"
+ def get_success_message(self):
+ if self.election.results_public:
+ return _("Élection publiée avec succès !")
+ return _("Élection dépubliée avec succès !")
+
def get_queryset(self):
return super().get_queryset().filter(end_date__lt=timezone.now())
def get(self, request, *args, **kwargs):
- election = self.get_object()
- election.results_public = not election.results_public
- election.save()
- messages.success(
- request,
- _("Élection publiée avec succès !")
- if election.results_public
- else _("Élection dépubliée avec succès !"),
- )
+ self.election = self.get_object()
+ self.election.results_public = not self.election.results_public
+ self.election.save()
return super().get(request, *args, **kwargs)
-class ElectionArchiveView(SuccessMessageMixin, SingleObjectMixin, RedirectView):
+class ElectionArchiveView(CreatorOnlyEditMixin, BackgroundUpdateView):
model = Election
pattern_name = "election.admin"
+ success_message = _("Élection archivée avec succès !")
def get_queryset(self):
return super().get_queryset().filter(end_date__lt=timezone.now())
@@ -143,10 +154,112 @@ class ElectionArchiveView(SuccessMessageMixin, SingleObjectMixin, RedirectView):
election = self.get_object()
election.archived = True
election.save()
- messages.success(request, _("Élection archivée avec succès !"))
return super().get(request, *args, **kwargs)
+# #############################################################################
+# Question Views
+# #############################################################################
+
+
+class AddQuestionView(CreatorOnlyEditMixin, CreateView):
+ model = Election
+ form_class = QuestionForm
+
+ def get_success_url(self):
+ return reverse("election.admin", args=[self.election.pk]) + "#q_add"
+
+ def form_valid(self, form):
+ self.election = self.get_object()
+ # On ajoute l'élection voulue à la question créée
+ form.instance.election = self.election
+ return super().form_valid(form)
+
+
+class ModQuestionView(CreatorOnlyEditMixin, SuccessMessageMixin, UpdateView):
+ model = Question
+ form_class = QuestionForm
+ success_message = _("Question modifiée avec succès !")
+ template_name = "elections/question_update.html"
+
+ def get_success_url(self):
+ return (
+ reverse("election.admin", args=[self.object.election.pk])
+ + f"#q_{self.object.pk}"
+ )
+
+
+class DelQuestionView(CreatorOnlyEditMixin, BackgroundUpdateView):
+ model = Question
+ success_message = _("Question supprimée !")
+
+ def get_redirect_url(self, *args, **kwargs):
+ return reverse("election.admin", args=[self.election.pk]) + "#q_add"
+
+ def get(self, request, *args, **kwargs):
+ question = self.get_object()
+ self.election = question.election
+ question.delete()
+ return super().get(request, *args, **kwargs)
+
+
+# #############################################################################
+# Option Views
+# #############################################################################
+
+
+class AddOptionView(CreatorOnlyEditMixin, CreateView):
+ model = Question
+ form_class = OptionForm
+
+ def get_queryset(self):
+ return super().get_queryset()
+
+ def get_success_url(self):
+ return (
+ reverse("election.admin", args=[self.question.election.pk])
+ + f"#q_{self.question.pk}"
+ )
+
+ def form_valid(self, form):
+ self.question = self.get_object()
+ # On ajoute l'élection voulue à la question créée
+ form.instance.question = self.question
+ return super().form_valid(form)
+
+
+class ModOptionView(CreatorOnlyEditMixin, SuccessMessageMixin, UpdateView):
+ model = Option
+ form_class = OptionForm
+ success_message = _("Option modifiée avec succès !")
+ template_name = "elections/option_update.html"
+
+ def get_success_url(self):
+ return (
+ reverse("election.admin", args=[self.object.question.election.pk])
+ + f"#o_{self.object.pk}"
+ )
+
+
+class DelOptionView(CreatorOnlyEditMixin, BackgroundUpdateView):
+ model = Option
+ success_message = _("Option supprimée !")
+
+ def get_redirect_url(self, *args, **kwargs):
+ return reverse("election.admin", args=[self.election.pk]) + "#q_add"
+
+ def get(self, request, *args, **kwargs):
+ option = self.get_object()
+ self.election = option.question.election
+ option.delete()
+ return super().get(request, *args, **kwargs)
+
+
+# #############################################################################
+# Common Views
+# #############################################################################
+
+
class ElectionView(DetailView):
model = Election
template_name = "elections/election.html"