Tweaks on templates and foundation for uploading a file
This commit is contained in:
parent
e7db47116e
commit
5c7e2238d8
9 changed files with 140 additions and 18 deletions
|
@ -24,6 +24,10 @@ class ElectionForm(forms.ModelForm):
|
||||||
fields = ["name", "description", "restricted", "start_date", "end_date"]
|
fields = ["name", "description", "restricted", "start_date", "end_date"]
|
||||||
|
|
||||||
|
|
||||||
|
class UploadVotersForm(forms.Form):
|
||||||
|
csv_file = forms.FileField(label=_("Sélectionnez un fichier .csv"))
|
||||||
|
|
||||||
|
|
||||||
class QuestionForm(forms.ModelForm):
|
class QuestionForm(forms.ModelForm):
|
||||||
class Meta:
|
class Meta:
|
||||||
model = Question
|
model = Question
|
||||||
|
|
|
@ -22,9 +22,9 @@
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="level-right">
|
<div class="level-right">
|
||||||
{% if election.start_date > current_time %}
|
|
||||||
|
|
||||||
{# Lien pour la modification #}
|
{# Lien pour la modification et l'upload des votant·e·s #}
|
||||||
|
{% if election.start_date > current_time %}
|
||||||
<div class="level-item">
|
<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">
|
||||||
|
@ -33,10 +33,22 @@
|
||||||
<span>{% trans "Modifier" %}</span>
|
<span>{% trans "Modifier" %}</span>
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
{% if election.restricted %}
|
||||||
|
<div class="level-item">
|
||||||
|
<a class="button is-light is-outlined is-primary" href="{% url 'election.upload-voters' election.pk %}">
|
||||||
|
<span class="icon">
|
||||||
|
<i class="fas fa-file-import"></i>
|
||||||
|
</span>
|
||||||
|
<span>{% trans "Importer une liste de votant·e·s" %}</span>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
{% elif election.end_date < current_time %}
|
{% elif election.end_date < current_time %}
|
||||||
{% if not election.tallied %}
|
|
||||||
|
|
||||||
{# Lien pour le dépouillement #}
|
{# Lien pour le dépouillement #}
|
||||||
|
{% if not election.tallied %}
|
||||||
<div class="level-item">
|
<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">
|
||||||
|
@ -45,9 +57,9 @@
|
||||||
<span>{% trans "Dépouiller" %}</span>
|
<span>{% trans "Dépouiller" %}</span>
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
{% else %}
|
|
||||||
|
|
||||||
{# Lien pour la publication des résultats #}
|
{# Lien pour la publication des résultats #}
|
||||||
|
{% else %}
|
||||||
<div class="level-item">
|
<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">
|
||||||
|
|
60
elections/templates/elections/upload_voters.html
Normal file
60
elections/templates/elections/upload_voters.html
Normal file
|
@ -0,0 +1,60 @@
|
||||||
|
{% extends "base.html" %}
|
||||||
|
{% load i18n %}
|
||||||
|
|
||||||
|
|
||||||
|
{% block extra_head %}
|
||||||
|
<script>
|
||||||
|
$(document).ready(function($) {
|
||||||
|
const fileInput = document.querySelector('#import-voters input[type=file]');
|
||||||
|
fileInput.onchange = () => {
|
||||||
|
if (fileInput.files.length > 0) {
|
||||||
|
const fileName = document.querySelector('#import-voters .file-name');
|
||||||
|
fileName.textContent = fileInput.files[0].name;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
</script>
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
|
||||||
|
<h1 class="title">{% trans "Importer une liste de votant·e·s" %}</h1>
|
||||||
|
<hr>
|
||||||
|
|
||||||
|
<div class="message is-warning">
|
||||||
|
<div class="message-body">
|
||||||
|
{% trans "Importez un fichier au format CSV, avec sur la première colonne les prénoms, sur la deuxième le nom et la troisième l'adresse email. Soit :<br><br><pre>Prénom_1,Nom_1,mail_1@machin.test<br>Prénom_2,Nom_2,mail_2@bidule.test<br>...</pre>" %}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="columns is-centered">
|
||||||
|
<div class="column is-two-thirds">
|
||||||
|
<form action="" method="post" id="import-voters">
|
||||||
|
{% csrf_token %}
|
||||||
|
|
||||||
|
{% include "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">
|
||||||
|
<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 %}">
|
||||||
|
<span class="icon is-small">
|
||||||
|
<i class="fas fa-undo-alt"></i>
|
||||||
|
</span>
|
||||||
|
<span>{% trans "Retour" %}</span>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{% endblock %}
|
|
@ -7,6 +7,11 @@ 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("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(
|
||||||
|
"upload-voters/<int:pk>",
|
||||||
|
views.ElectionUploadVotersView.as_view(),
|
||||||
|
name="election.upload-voters",
|
||||||
|
),
|
||||||
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"),
|
||||||
path(
|
path(
|
||||||
|
|
|
@ -10,12 +10,19 @@ from django.utils.translation import gettext_lazy as _
|
||||||
from django.views.generic import (
|
from django.views.generic import (
|
||||||
CreateView,
|
CreateView,
|
||||||
DetailView,
|
DetailView,
|
||||||
|
FormView,
|
||||||
ListView,
|
ListView,
|
||||||
RedirectView,
|
RedirectView,
|
||||||
UpdateView,
|
UpdateView,
|
||||||
)
|
)
|
||||||
|
|
||||||
from .forms import ElectionForm, OptionForm, OptionFormSet, QuestionForm
|
from .forms import (
|
||||||
|
ElectionForm,
|
||||||
|
OptionForm,
|
||||||
|
OptionFormSet,
|
||||||
|
QuestionForm,
|
||||||
|
UploadVotersForm,
|
||||||
|
)
|
||||||
from .mixins import CreatorOnlyEditMixin, CreatorOnlyMixin, OpenElectionOnlyMixin
|
from .mixins import CreatorOnlyEditMixin, CreatorOnlyMixin, OpenElectionOnlyMixin
|
||||||
from .models import Election, Option, Question
|
from .models import Election, Option, Question
|
||||||
|
|
||||||
|
@ -63,11 +70,13 @@ class ElectionCreateView(SuccessMessageMixin, CreateView):
|
||||||
return super().form_valid(form)
|
return super().form_valid(form)
|
||||||
|
|
||||||
|
|
||||||
# TODO : only the creator can edit the election and view the admin panel
|
|
||||||
class ElectionAdminView(CreatorOnlyMixin, DetailView):
|
class ElectionAdminView(CreatorOnlyMixin, DetailView):
|
||||||
model = Election
|
model = Election
|
||||||
template_name = "elections/election_admin.html"
|
template_name = "elections/election_admin.html"
|
||||||
|
|
||||||
|
def get_next_url(self):
|
||||||
|
return reverse("election.view", args=[self.object.pk])
|
||||||
|
|
||||||
def get_context_data(self, **kwargs):
|
def get_context_data(self, **kwargs):
|
||||||
kwargs.update({"current_time": timezone.now()})
|
kwargs.update({"current_time": timezone.now()})
|
||||||
return super().get_context_data(**kwargs)
|
return super().get_context_data(**kwargs)
|
||||||
|
@ -76,6 +85,19 @@ class ElectionAdminView(CreatorOnlyMixin, DetailView):
|
||||||
return super().get_queryset().prefetch_related("questions__options")
|
return super().get_queryset().prefetch_related("questions__options")
|
||||||
|
|
||||||
|
|
||||||
|
class ElectionUploadVotersView(CreatorOnlyEditMixin, FormView):
|
||||||
|
model = Election
|
||||||
|
form_class = UploadVotersForm
|
||||||
|
template_name = "elections/upload_voters.html"
|
||||||
|
|
||||||
|
def get(self, request, *args, **kwargs):
|
||||||
|
self.object = self.get_object()
|
||||||
|
return super().get(request, *args, **kwargs)
|
||||||
|
|
||||||
|
def form_valid(self, form):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
class ElectionListView(CreatorOnlyMixin, ListView):
|
class ElectionListView(CreatorOnlyMixin, ListView):
|
||||||
model = Election
|
model = Election
|
||||||
template_name = "elections/election_list.html"
|
template_name = "elections/election_list.html"
|
||||||
|
@ -290,7 +312,7 @@ class VoteView(OpenElectionOnlyMixin, DetailView):
|
||||||
model = Question
|
model = Question
|
||||||
template_name = "elections/vote.html"
|
template_name = "elections/vote.html"
|
||||||
|
|
||||||
def get_newt_url(self):
|
def get_next_url(self):
|
||||||
return reverse("election.view", args=[self.object.election.pk])
|
return reverse("election.view", args=[self.object.election.pk])
|
||||||
|
|
||||||
def get_success_url(self):
|
def get_success_url(self):
|
||||||
|
|
|
@ -94,6 +94,8 @@ class LogoutView(auth_views.LogoutView):
|
||||||
logged in via CAS.
|
logged in via CAS.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
template_name = "auth/logout.html"
|
||||||
|
|
||||||
def setup(self, request):
|
def setup(self, request):
|
||||||
super().setup(request)
|
super().setup(request)
|
||||||
if "CASCONNECTED" in request.session:
|
if "CASCONNECTED" in request.session:
|
||||||
|
|
12
shared/templates/auth/logout.html
Normal file
12
shared/templates/auth/logout.html
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
{% extends "base.html" %}
|
||||||
|
{% load i18n %}
|
||||||
|
|
||||||
|
|
||||||
|
{% block auth %}{% endblock %}
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
|
||||||
|
<h1 class="title">{% trans "Vous avez bien été déconnecté." %}</h1>
|
||||||
|
<hr>
|
||||||
|
|
||||||
|
{% endblock %}
|
|
@ -55,7 +55,7 @@
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="level-item ml-3">
|
<div class="level-item ml-3">
|
||||||
<a class="icon is-size-1 has-text-white" href="{% url 'auth.logout' %}?next=/{{ view.get_next_url }}">
|
<a class="icon is-size-1 has-text-white" href="{% url 'auth.logout' %}?next={% if view.get_next_url %}{{ view.get_next_url }}{% else %}/{% endif %}">
|
||||||
<i class="fas fa-sign-out-alt"></i>
|
<i class="fas fa-sign-out-alt"></i>
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -1,22 +1,27 @@
|
||||||
{% load bulma_utils i18n %}
|
{% load bulma_utils i18n %}
|
||||||
|
|
||||||
<label class="label {% if field.field.required %}{{ form.required_css_class }}{% endif %}">
|
<label class="label {% if field.field.required %}{{ form.required_css_class }}{% endif %}">
|
||||||
{{ field.label }}
|
{{ field.label_tag }}
|
||||||
</label>
|
</label>
|
||||||
|
|
||||||
<div class="control">
|
<div class="control">
|
||||||
|
|
||||||
<label class="file-label">
|
<div class="file has-name">
|
||||||
{{ field|bulmafy:'file-input' }}
|
<label class="file-label">
|
||||||
<span class="file-cta">
|
{{ field|bulmafy:'file-input' }}
|
||||||
<span class="file-icon">
|
<span class="file-cta">
|
||||||
<i class="fas fa-upload"></i>
|
<span class="file-icon">
|
||||||
|
<i class="fas fa-upload"></i>
|
||||||
|
</span>
|
||||||
|
<span class="file-label">
|
||||||
|
{% trans "Choisissez un fichier..." %}
|
||||||
|
</span>
|
||||||
</span>
|
</span>
|
||||||
<span class="file-label">
|
<span class="file-name">
|
||||||
{% trans "Choisissez un fichier..." %}
|
{% trans "Aucun fichier sélectionné" %}
|
||||||
</span>
|
</span>
|
||||||
</span>
|
</label>
|
||||||
</label>
|
</div>
|
||||||
|
|
||||||
{% for error in field.errors %}
|
{% for error in field.errors %}
|
||||||
<span class="help is-danger {{ form.error_css_class }}">{{ error }}</span>
|
<span class="help is-danger {{ form.error_css_class }}">{{ error }}</span>
|
||||||
|
|
Loading…
Reference in a new issue