Liste des élections créées

This commit is contained in:
Tom Hubrecht 2020-12-19 23:48:18 +01:00
parent 42bf59bf1a
commit 9281c76ddc
9 changed files with 243 additions and 130 deletions

View file

@ -0,0 +1,16 @@
# TODO:
class CreatorOnlyMixin:
"""Restreint l'accès au créateurice de l'élection"""
def get_queryset(self):
# TODO: change the way we collect the user according to the model used
user = self.request.user
return super().get_queryset().filter(created_by=user)
class AdministratorOnlyMixin:
"""Restreint l'accès aux admins"""
class VoterOnlyMixin:
"""Restreint l'accès aux voteureuses de l'élection"""

View file

@ -23,6 +23,9 @@ class Election(models.Model):
archived = models.BooleanField(_("archivée"), default=False) archived = models.BooleanField(_("archivée"), default=False)
class Meta:
ordering = ["-start_date", "-end_date"]
class Question(models.Model): class Question(models.Model):
election = models.ForeignKey( election = models.ForeignKey(
@ -34,6 +37,9 @@ class Question(models.Model):
_("nombre maximal de votes reçus"), default=0 _("nombre maximal de votes reçus"), default=0
) )
class Meta:
ordering = ["id"]
class Option(models.Model): class Option(models.Model):
question = models.ForeignKey( question = models.ForeignKey(
@ -46,3 +52,6 @@ class Option(models.Model):
) )
# For now, we store the amount of votes received after the election is tallied # For now, we store the amount of votes received after the election is tallied
nb_votes = models.PositiveSmallIntegerField(_("nombre de votes reçus"), default=0) nb_votes = models.PositiveSmallIntegerField(_("nombre de votes reçus"), default=0)
class Meta:
ordering = ["id"]

View file

@ -38,14 +38,16 @@
<body> <body>
<nav class="level has-background-primary"> <nav class="level has-background-primary">
<div class="level-left pl-4"> <div class="level-left pl-4">
<div class="level-item">
<h1 class="has-text-primary-light is-size-1 is-family-secondary">Kadenios</h1> <h1 class="has-text-primary-light is-size-1 is-family-secondary">Kadenios</h1>
</div> </div>
<div class="level-right pr-4"> </div>
<figure class="image is-64x64"> <div class="level-right pr-5">
<a href=""> <div class="level-item">
<img src="{% static "images/logout.svg" %}" alt="logout"> <a class="icon is-size-1 has-text-white" href="">
<i class="fas fa-sign-out-alt"></i>
</a> </a>
</figure> </div>
</div> </div>
</nav> </nav>
{% block layout %} {% block layout %}

View file

@ -22,54 +22,56 @@
</div> </div>
<div class="level-right"> <div class="level-right">
<div class="level-item">
{% if election.start_date > current_time %} {% if election.start_date > current_time %}
{# Lien pour la modification #} {# Lien pour la modification #}
<div class="level-item">
<a class="button is-light is-outlined is-primary" href="{% url 'election.update' election.pk %}"> <a class="button is-light is-outlined is-primary" href="{% url 'election.update' election.pk %}">
<span class="icon"> <span class="icon">
<i class="fas fa-edit"></i> <i class="fas fa-edit"></i>
</span> </span>
&nbsp; <span>{% trans "Modifier" %}</span>
{% trans "Modifier" %}
</a> </a>
</div>
{% elif election.end_date < current_time %} {% elif election.end_date < current_time %}
{% if not election.tallied %} {% if not election.tallied %}
{# Lien pour le dépouillement #} {# Lien pour le dépouillement #}
<div class="level-item">
<a class="button is-light is-outlined is-primary" href="{% url 'election.tally' election.pk %}"> <a class="button is-light is-outlined is-primary" href="{% url 'election.tally' election.pk %}">
<span class="icon"> <span class="icon">
<i class="fas fa-poll-h"></i> <i class="fas fa-poll-h"></i>
</span> </span>
&nbsp; <span>{% trans "Dépouiller" %}</span>
{% trans "Dépouiller" %}
</a> </a>
</div>
{% else %} {% else %}
{# Lien pour la publication des résultats #} {# Lien pour la publication des résultats #}
<div class="level-item">
<a class="button is-outlined is-primary" href="{% url 'election.publish' election.pk %}"> <a class="button is-outlined is-primary" href="{% url 'election.publish' election.pk %}">
<span class="icon"> <span class="icon">
<i class="fas fa-edit"></i> <i class="fas fa-edit"></i>
</span> </span>
&nbsp;
{% if not election.results_public %} {% if not election.results_public %}
{% trans "Publier" %} <span>{% trans "Publier" %}</span>
{% else %} {% else %}
{% trans "Dépublier" %} <span>{% trans "Dépublier" %}</span>
{% endif %} {% endif %}
</a>&nbsp; </a>
</div>
{# Lien pour l'archivage #} {# Lien pour l'archivage #}
<div class="level-item">
<a class="button is-light is-outlined is-primary" href="{% url 'election.archive' election.pk %}"> <a class="button is-light is-outlined is-primary" href="{% url 'election.archive' election.pk %}">
<span class="icon"> <span class="icon">
<i class="fas fa-edit"></i> <i class="fas fa-edit"></i>
</span> </span>
&nbsp; <span>{% trans "Archiver" %}</span>
{% trans "Archiver" %}
</a> </a>
{% endif %}
{% endif %}
</div> </div>
{% endif %}
{% endif %}
</div> </div>
</div> </div>
<hr> <hr>
@ -79,26 +81,23 @@
<div class="message-body">{{ election.description|linebreaksbr }}</div> <div class="message-body">{{ election.description|linebreaksbr }}</div>
</div> </div>
<div class="container">
{# Liste des questions #} {# Liste des questions #}
{% for q in election.questions.all %} {% for q in election.questions.all %}
<div class="panel"> <div class="panel">
<div class="panel-heading is-size-6"> <div class="panel-heading is-size-6">
{{ q.text }} <span>{{ q.text }}</span>
{% if election.start_date > current_time %} {% if election.start_date > current_time %}
&nbsp;
<a class="tag is-small is-outlined is-light is-danger"> <a class="tag is-small is-outlined is-light is-danger">
<span class="icon"> <span class="icon">
<i class="fas fa-times"></i> <i class="fas fa-times"></i>
</span> </span>
{% trans "Supprimer" %} <span>{% trans "Supprimer" %}</span>
</a> </a>
<a class="tag is-small is-outlined is-light is-info"> <a class="tag is-small is-outlined is-light is-info">
<span class="icon"> <span class="icon">
<i class="fas fa-edit"></i> <i class="fas fa-edit"></i>
</span> </span>
{% trans "Modifier" %} <span>{% trans "Modifier" %}</span>
</a> </a>
{% endif %} {% endif %}
</div> </div>
@ -123,8 +122,8 @@
<span class="tag {% if o.nb_votes == q.max_votes %}is-success{% else %}is-primary{% endif %}"> <span class="tag {% if o.nb_votes == q.max_votes %}is-success{% else %}is-primary{% endif %}">
<span class="icon"> <span class="icon">
<i class="fas fa-vote-yea"></i> <i class="fas fa-vote-yea"></i>
</span>&nbsp; </span>
{{ o.nb_votes }} <span>{{ o.nb_votes }}</span>
</span>&nbsp;&nbsp; </span>&nbsp;&nbsp;
{% endif %} {% endif %}
{{ o.text }} {{ o.text }}
@ -174,6 +173,5 @@
</div> </div>
</div> </div>
{% endif %} {% endif %}
</div>
{% endblock %} {% endblock %}

View file

@ -31,7 +31,6 @@
<h1 class="title">{% trans "Création d'une élection" %}</h1> <h1 class="title">{% trans "Création d'une élection" %}</h1>
<hr> <hr>
<div class="container">
<form action="" method="post"> <form action="" method="post">
{% csrf_token %} {% csrf_token %}
@ -43,6 +42,5 @@
</div> </div>
</div> </div>
</form> </form>
</div>
{% endblock %} {% endblock %}

View file

@ -0,0 +1,65 @@
{% extends "elections/base.html" %}
{% load i18n %}
{% block content %}
<div class="level">
<div class="level-left">
<div class="level-item">
<h1 class="title">{% trans "Liste des élections créées" %}</h1>
</div>
</div>
<div class="level-right">
<div class="level-item">
<a class="button is-light is-outlined is-primary" href={% url 'election.create' %}>
<span class="icon">
<i class="fas fa-plus"></i>
</span>
<span>{% trans "Créer une élection" %}</span>
</a>
</div>
</div>
</div>
<hr>
{% for e in election_list %}
<div class="message is-primary">
<div class="message-header is-size-6 is-radiusless">
<div>
<span class="mr-2">{{ e.name }}</span>
<span class="tag is-primary is-light">{{ e.start_date|date:"d/m/Y H:i" }}</span>
<span class="icon has-text-primary-light">
<i class="fas fa-long-arrow-alt-right"></i>
</span>
<span class="tag is-primary is-light">{{ e.end_date|date:"d/m/Y H:i" }}</span>
{% if e.tallied %}
&nbsp;
<span class="tag is-success is-light">{% trans "Élection dépouillée" %}</span>
{% endif %}
{% if e.results_public %}
&nbsp;
<span class="tag is-info is-light">{% trans "Élection publiée" %}</span>
{% endif %}
{% if e.archived %}
&nbsp;
<span class="tag is-danger is-light">{% trans "Élection archivée" %}</span>
{% endif %}
</div>
<a class="has-text-primary-light" href="{% url 'election.admin' e.pk %}">
<span class="icon">
<i class="fas fa-tools"></i>
</span>
<span>{% trans "" %}</span>
</a>
</div>
<p class="message-body">
{{ e.description|linebreaksbr }}
</p>
</div>
{% endfor %}
{% endblock %}

View file

@ -31,7 +31,6 @@
<h1 class="title">{% trans "Modification d'une élection" %}</h1> <h1 class="title">{% trans "Modification d'une élection" %}</h1>
<hr> <hr>
<div class="container">
<form action="" method="post"> <form action="" method="post">
{% csrf_token %} {% csrf_token %}
@ -39,13 +38,22 @@
<div class="field is-grouped is-centered"> <div class="field is-grouped is-centered">
<div class="control is-expanded"> <div class="control is-expanded">
<button class="button is-fullwidth is-outlined is-primary is-light">{% trans "Enregistrer" %}</button> <button class="button is-fullwidth is-outlined is-primary is-light">
<span class="icon is-small">
<i class="fas fa-check"></i>
</span>
<span>{% trans "Enregistrer" %}</span>
</button>
</div> </div>
<div class="control"> <div class="control">
<a class="button is-primary" href="{% url 'election.admin' election.pk %}">{% trans "Retour" %}</a> <a class="button is-primary" href="{% url 'election.admin' election.pk %}">
<span class="icon is-small">
<i class="fas fa-undo-alt"></i>
</span>
<span>{% trans "Retour" %}</span>
</a>
</div> </div>
</div> </div>
</form> </form>
</div>
{% endblock %} {% endblock %}

View file

@ -4,6 +4,7 @@ from . import views
urlpatterns = [ urlpatterns = [
path("create/", views.ElectionCreateView.as_view(), name="election.create"), path("create/", views.ElectionCreateView.as_view(), name="election.create"),
path("created/", views.ElectionListView.as_view(), name="election.created"),
path("admin/<int:pk>", views.ElectionAdminView.as_view(), name="election.admin"), path("admin/<int:pk>", views.ElectionAdminView.as_view(), name="election.admin"),
path("update/<int:pk>", views.ElectionUpdateView.as_view(), name="election.update"), path("update/<int:pk>", views.ElectionUpdateView.as_view(), name="election.update"),
path("tally/<int:pk>", views.ElectionTallyView.as_view(), name="election.tally"), path("tally/<int:pk>", views.ElectionTallyView.as_view(), name="election.tally"),

View file

@ -6,14 +6,25 @@ from django.urls import reverse
from django.utils import timezone from django.utils import timezone
from django.utils.text import slugify from django.utils.text import slugify
from django.utils.translation import gettext_lazy as _ from django.utils.translation import gettext_lazy as _
from django.views.generic import CreateView, DetailView, RedirectView, UpdateView from django.views.generic import (
CreateView,
DetailView,
ListView,
RedirectView,
UpdateView,
)
from django.views.generic.detail import SingleObjectMixin from django.views.generic.detail import SingleObjectMixin
from .forms import ElectionCreateForm, OptionFormSet from .forms import ElectionCreateForm, OptionFormSet
from .mixins import CreatorOnlyMixin
from .models import Election, Option, Question from .models import Election, Option, Question
# TODO: access control *everywhere* # TODO: access control *everywhere*
# #############################################################################
# Administration Views
# #############################################################################
class ElectionCreateView(SuccessMessageMixin, CreateView): class ElectionCreateView(SuccessMessageMixin, CreateView):
model = Election model = Election
@ -35,7 +46,7 @@ class ElectionCreateView(SuccessMessageMixin, CreateView):
# TODO : only the creator can edit the election and view the admin panel # TODO : only the creator can edit the election and view the admin panel
class ElectionAdminView(DetailView): class ElectionAdminView(CreatorOnlyMixin, DetailView):
model = Election model = Election
template_name = "elections/election_admin.html" template_name = "elections/election_admin.html"
@ -47,6 +58,11 @@ class ElectionAdminView(DetailView):
return super().get_queryset().prefetch_related("questions__options") return super().get_queryset().prefetch_related("questions__options")
class ElectionListView(CreatorOnlyMixin, ListView):
model = Election
template_name = "elections/election_list.html"
class ElectionUpdateView(SuccessMessageMixin, UpdateView): class ElectionUpdateView(SuccessMessageMixin, UpdateView):
model = Election model = Election
fields = ["name", "description", "start_date", "end_date"] fields = ["name", "description", "start_date", "end_date"]