Liste des élections créées
This commit is contained in:
parent
42bf59bf1a
commit
9281c76ddc
9 changed files with 243 additions and 130 deletions
|
@ -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"""
|
|
@ -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"]
|
||||||
|
|
|
@ -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 %}
|
||||||
|
|
|
@ -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>
|
||||||
|
<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>
|
||||||
|
<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>
|
||||||
|
|
||||||
{% 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>
|
</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>
|
||||||
|
<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 #}
|
||||||
|
{% for q in election.questions.all %}
|
||||||
{# Liste des questions #}
|
<div class="panel">
|
||||||
{% for q in election.questions.all %}
|
|
||||||
<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 %}
|
||||||
|
|
||||||
<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>
|
</span>
|
||||||
{{ o.nb_votes }}
|
<span>{{ o.nb_votes }}</span>
|
||||||
</span>
|
</span>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{{ o.text }}
|
{{ o.text }}
|
||||||
|
@ -149,12 +148,12 @@
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</div>
|
</div>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
|
|
||||||
{# Rajout d'une question #}
|
{# Rajout d'une question #}
|
||||||
{% if election.start_date > current_time %}
|
{% if election.start_date > current_time %}
|
||||||
<div class="columns is-centered">
|
<div class="columns is-centered">
|
||||||
<div class="column is-two-thirds">
|
<div class="column is-two-thirds">
|
||||||
<form action="" method="POST">
|
<form action="" method="POST">
|
||||||
{% csrf_token %}
|
{% csrf_token %}
|
||||||
|
@ -172,8 +171,7 @@
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
{% endif %}
|
|
||||||
</div>
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
|
@ -31,8 +31,7 @@
|
||||||
<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 %}
|
||||||
|
|
||||||
{% include "elections/forms/form.html" with errors=False %}
|
{% include "elections/forms/form.html" with errors=False %}
|
||||||
|
@ -42,7 +41,6 @@
|
||||||
<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">{% trans "Enregistrer" %}</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
</div>
|
|
||||||
|
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
65
elections/templates/elections/election_list.html
Normal file
65
elections/templates/elections/election_list.html
Normal 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 %}
|
||||||
|
|
||||||
|
<span class="tag is-success is-light">{% trans "Élection dépouillée" %}</span>
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
{% if e.results_public %}
|
||||||
|
|
||||||
|
<span class="tag is-info is-light">{% trans "Élection publiée" %}</span>
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
{% if e.archived %}
|
||||||
|
|
||||||
|
<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 %}
|
|
@ -31,21 +31,29 @@
|
||||||
<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 %}
|
||||||
|
|
||||||
{% include "elections/forms/form.html" with errors=False %}
|
{% include "elections/forms/form.html" with errors=False %}
|
||||||
|
|
||||||
<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 %}
|
||||||
|
|
|
@ -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"),
|
||||||
|
|
|
@ -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"]
|
||||||
|
|
Loading…
Reference in a new issue