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)
|
||||
|
||||
class Meta:
|
||||
ordering = ["-start_date", "-end_date"]
|
||||
|
||||
|
||||
class Question(models.Model):
|
||||
election = models.ForeignKey(
|
||||
|
@ -34,6 +37,9 @@ class Question(models.Model):
|
|||
_("nombre maximal de votes reçus"), default=0
|
||||
)
|
||||
|
||||
class Meta:
|
||||
ordering = ["id"]
|
||||
|
||||
|
||||
class Option(models.Model):
|
||||
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
|
||||
nb_votes = models.PositiveSmallIntegerField(_("nombre de votes reçus"), default=0)
|
||||
|
||||
class Meta:
|
||||
ordering = ["id"]
|
||||
|
|
|
@ -38,14 +38,16 @@
|
|||
<body>
|
||||
<nav class="level has-background-primary">
|
||||
<div class="level-left pl-4">
|
||||
<div class="level-item">
|
||||
<h1 class="has-text-primary-light is-size-1 is-family-secondary">Kadenios</h1>
|
||||
</div>
|
||||
<div class="level-right pr-4">
|
||||
<figure class="image is-64x64">
|
||||
<a href="">
|
||||
<img src="{% static "images/logout.svg" %}" alt="logout">
|
||||
</div>
|
||||
<div class="level-right pr-5">
|
||||
<div class="level-item">
|
||||
<a class="icon is-size-1 has-text-white" href="">
|
||||
<i class="fas fa-sign-out-alt"></i>
|
||||
</a>
|
||||
</figure>
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
{% block layout %}
|
||||
|
|
|
@ -22,54 +22,56 @@
|
|||
</div>
|
||||
|
||||
<div class="level-right">
|
||||
<div class="level-item">
|
||||
{% if election.start_date > current_time %}
|
||||
|
||||
{# Lien pour la modification #}
|
||||
<div class="level-item">
|
||||
<a class="button is-light is-outlined is-primary" href="{% url 'election.update' election.pk %}">
|
||||
<span class="icon">
|
||||
<i class="fas fa-edit"></i>
|
||||
</span>
|
||||
|
||||
{% trans "Modifier" %}
|
||||
<span>{% trans "Modifier" %}</span>
|
||||
</a>
|
||||
</div>
|
||||
{% elif election.end_date < current_time %}
|
||||
{% if not election.tallied %}
|
||||
|
||||
{# Lien pour le dépouillement #}
|
||||
<div class="level-item">
|
||||
<a class="button is-light is-outlined is-primary" href="{% url 'election.tally' election.pk %}">
|
||||
<span class="icon">
|
||||
<i class="fas fa-poll-h"></i>
|
||||
</span>
|
||||
|
||||
{% trans "Dépouiller" %}
|
||||
<span>{% trans "Dépouiller" %}</span>
|
||||
</a>
|
||||
</div>
|
||||
{% else %}
|
||||
|
||||
{# Lien pour la publication des résultats #}
|
||||
<div class="level-item">
|
||||
<a class="button is-outlined is-primary" href="{% url 'election.publish' election.pk %}">
|
||||
<span class="icon">
|
||||
<i class="fas fa-edit"></i>
|
||||
</span>
|
||||
|
||||
{% if not election.results_public %}
|
||||
{% trans "Publier" %}
|
||||
<span>{% trans "Publier" %}</span>
|
||||
{% else %}
|
||||
{% trans "Dépublier" %}
|
||||
<span>{% trans "Dépublier" %}</span>
|
||||
{% endif %}
|
||||
</a>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
{# Lien pour l'archivage #}
|
||||
<div class="level-item">
|
||||
<a class="button is-light is-outlined is-primary" href="{% url 'election.archive' election.pk %}">
|
||||
<span class="icon">
|
||||
<i class="fas fa-edit"></i>
|
||||
</span>
|
||||
|
||||
{% trans "Archiver" %}
|
||||
<span>{% trans "Archiver" %}</span>
|
||||
</a>
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
</div>
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
<hr>
|
||||
|
@ -79,26 +81,23 @@
|
|||
<div class="message-body">{{ election.description|linebreaksbr }}</div>
|
||||
</div>
|
||||
|
||||
<div class="container">
|
||||
|
||||
{# Liste des questions #}
|
||||
{% for q in election.questions.all %}
|
||||
<div class="panel">
|
||||
{# Liste des questions #}
|
||||
{% for q in election.questions.all %}
|
||||
<div class="panel">
|
||||
<div class="panel-heading is-size-6">
|
||||
{{ q.text }}
|
||||
<span>{{ q.text }}</span>
|
||||
{% if election.start_date > current_time %}
|
||||
|
||||
<a class="tag is-small is-outlined is-light is-danger">
|
||||
<span class="icon">
|
||||
<i class="fas fa-times"></i>
|
||||
</span>
|
||||
{% trans "Supprimer" %}
|
||||
<span>{% trans "Supprimer" %}</span>
|
||||
</a>
|
||||
<a class="tag is-small is-outlined is-light is-info">
|
||||
<span class="icon">
|
||||
<i class="fas fa-edit"></i>
|
||||
</span>
|
||||
{% trans "Modifier" %}
|
||||
<span>{% trans "Modifier" %}</span>
|
||||
</a>
|
||||
{% endif %}
|
||||
</div>
|
||||
|
@ -123,8 +122,8 @@
|
|||
<span class="tag {% if o.nb_votes == q.max_votes %}is-success{% else %}is-primary{% endif %}">
|
||||
<span class="icon">
|
||||
<i class="fas fa-vote-yea"></i>
|
||||
</span>
|
||||
{{ o.nb_votes }}
|
||||
</span>
|
||||
<span>{{ o.nb_votes }}</span>
|
||||
</span>
|
||||
{% endif %}
|
||||
{{ o.text }}
|
||||
|
@ -149,12 +148,12 @@
|
|||
</div>
|
||||
</form>
|
||||
{% endif %}
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
{% endfor %}
|
||||
|
||||
{# Rajout d'une question #}
|
||||
{% if election.start_date > current_time %}
|
||||
<div class="columns is-centered">
|
||||
{# Rajout d'une question #}
|
||||
{% if election.start_date > current_time %}
|
||||
<div class="columns is-centered">
|
||||
<div class="column is-two-thirds">
|
||||
<form action="" method="POST">
|
||||
{% csrf_token %}
|
||||
|
@ -172,8 +171,7 @@
|
|||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
{% endblock %}
|
||||
|
|
|
@ -31,8 +31,7 @@
|
|||
<h1 class="title">{% trans "Création d'une élection" %}</h1>
|
||||
<hr>
|
||||
|
||||
<div class="container">
|
||||
<form action="" method="post">
|
||||
<form action="" method="post">
|
||||
{% csrf_token %}
|
||||
|
||||
{% 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>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
{% 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>
|
||||
<hr>
|
||||
|
||||
<div class="container">
|
||||
<form action="" method="post">
|
||||
<form action="" method="post">
|
||||
{% csrf_token %}
|
||||
|
||||
{% include "elections/forms/form.html" with errors=False %}
|
||||
|
||||
<div class="field is-grouped is-centered">
|
||||
<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 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>
|
||||
</form>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
{% endblock %}
|
||||
|
|
|
@ -4,6 +4,7 @@ from . import views
|
|||
|
||||
urlpatterns = [
|
||||
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("update/<int:pk>", views.ElectionUpdateView.as_view(), name="election.update"),
|
||||
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.text import slugify
|
||||
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 .forms import ElectionCreateForm, OptionFormSet
|
||||
from .mixins import CreatorOnlyMixin
|
||||
from .models import Election, Option, Question
|
||||
|
||||
# TODO: access control *everywhere*
|
||||
|
||||
# #############################################################################
|
||||
# Administration Views
|
||||
# #############################################################################
|
||||
|
||||
|
||||
class ElectionCreateView(SuccessMessageMixin, CreateView):
|
||||
model = Election
|
||||
|
@ -35,7 +46,7 @@ class ElectionCreateView(SuccessMessageMixin, CreateView):
|
|||
|
||||
|
||||
# TODO : only the creator can edit the election and view the admin panel
|
||||
class ElectionAdminView(DetailView):
|
||||
class ElectionAdminView(CreatorOnlyMixin, DetailView):
|
||||
model = Election
|
||||
template_name = "elections/election_admin.html"
|
||||
|
||||
|
@ -47,6 +58,11 @@ class ElectionAdminView(DetailView):
|
|||
return super().get_queryset().prefetch_related("questions__options")
|
||||
|
||||
|
||||
class ElectionListView(CreatorOnlyMixin, ListView):
|
||||
model = Election
|
||||
template_name = "elections/election_list.html"
|
||||
|
||||
|
||||
class ElectionUpdateView(SuccessMessageMixin, UpdateView):
|
||||
model = Election
|
||||
fields = ["name", "description", "start_date", "end_date"]
|
||||
|
|
Loading…
Reference in a new issue