Profile list

This commit is contained in:
Yohann D'ANELLO 2020-04-30 21:07:12 +02:00
parent f70f6f16f0
commit 03f47f996b
8 changed files with 124 additions and 52 deletions

View File

@ -127,10 +127,10 @@ class TFJMUser(AbstractUser):
role = models.CharField( role = models.CharField(
max_length=16, max_length=16,
choices=[ choices=[
("0admin", _("admin")), ("0admin", _("Admin")),
("1volunteer", _("organizer")), ("1volunteer", _("Organizer")),
("2coach", _("coach")), ("2coach", _("Coach")),
("3participant", _("participant")), ("3participant", _("Participant")),
] ]
) )

View File

@ -6,4 +6,7 @@ from member.models import TFJMUser
class UserTable(tables.Table): class UserTable(tables.Table):
class Meta: class Meta:
model = TFJMUser model = TFJMUser
fields = ("last_name", "first_name", "role",) fields = ("last_name", "first_name", "role", "date_joined", )
attrs = {
'class': 'table table-condensed table-striped table-hover'
}

View File

@ -1,7 +1,7 @@
from django.urls import path from django.urls import path
from django.views.generic import RedirectView from django.views.generic import RedirectView
from .views import CreateUserView, DocumentView from .views import CreateUserView, ProfileListView, OrphanedProfileListView, OrganizersListView
app_name = "member" app_name = "member"
@ -12,7 +12,7 @@ urlpatterns = [
path("join-team/", RedirectView.as_view(pattern_name="index"), name="join_team"), path("join-team/", RedirectView.as_view(pattern_name="index"), name="join_team"),
path("my-team/", RedirectView.as_view(pattern_name="index"), name="my_team"), path("my-team/", RedirectView.as_view(pattern_name="index"), name="my_team"),
path("my-team/update/", RedirectView.as_view(pattern_name="index"), name="update_my_team"), path("my-team/update/", RedirectView.as_view(pattern_name="index"), name="update_my_team"),
path("profiles/", RedirectView.as_view(pattern_name="index"), name="all_profiles"), path("profiles/", ProfileListView.as_view(), name="all_profiles"),
path("orphaned-profiles/", RedirectView.as_view(pattern_name="index"), name="orphaned_profiles"), path("orphaned-profiles/", OrphanedProfileListView.as_view(), name="orphaned_profiles"),
path("organizers/", RedirectView.as_view(pattern_name="index"), name="organizers"), path("organizers/", OrganizersListView.as_view(), name="organizers"),
] ]

View File

@ -1,11 +1,16 @@
from django.contrib.auth.mixins import LoginRequiredMixin from django.contrib.auth.mixins import LoginRequiredMixin
from django.core.exceptions import PermissionDenied from django.core.exceptions import PermissionDenied
from django.db.models import Q
from django.http import FileResponse from django.http import FileResponse
from django.utils.translation import gettext_lazy as _
from django.views import View from django.views import View
from django.views.generic import CreateView from django.views.generic import CreateView
from django_tables2 import SingleTableView
from tournament.views import AdminMixin
from .forms import SignUpForm from .forms import SignUpForm
from .models import TFJMUser, Document from .models import TFJMUser, Document
from .tables import UserTable
class CreateUserView(CreateView): class CreateUserView(CreateView):
@ -22,3 +27,29 @@ class DocumentView(LoginRequiredMixin, View):
raise PermissionDenied raise PermissionDenied
return FileResponse(doc.file, content_type="application/pdf") return FileResponse(doc.file, content_type="application/pdf")
class ProfileListView(LoginRequiredMixin, AdminMixin, SingleTableView):
model = TFJMUser
queryset = TFJMUser.objects.order_by("role", "last_name", "first_name")
table_class = UserTable
template_name = "member/profile_list.html"
extra_context = dict(title=_("All profiles"))
class OrphanedProfileListView(LoginRequiredMixin, AdminMixin, SingleTableView):
model = TFJMUser
queryset = TFJMUser.objects.filter((Q(role="2coach") | Q(role="3participant")) & Q(team__isnull=True))\
.order_by("role", "last_name", "first_name")
table_class = UserTable
template_name = "member/profile_list.html"
extra_context = dict(title=_("Orphaned profiles"))
class OrganizersListView(LoginRequiredMixin, AdminMixin, SingleTableView):
model = TFJMUser
queryset = TFJMUser.objects.filter(Q(role="0admin") | Q(role="1volunteer"))\
.order_by("role", "last_name", "first_name")
table_class = UserTable
template_name = "member/profile_list.html"
extra_context = dict(title=_("Organizers"))

View File

@ -74,6 +74,32 @@ class TournamentDetailView(DetailView):
class TeamDetailView(LoginRequiredMixin, DetailView): class TeamDetailView(LoginRequiredMixin, DetailView):
model = Team model = Team
def dispatch(self, request, *args, **kwargs):
if not request.user.admin and self.request.user not in self.get_object().tournament.organizers:
raise PermissionDenied
return super().dispatch(request, *args, **kwargs)
def post(self, request, *args, **kwargs):
team = self.get_object()
if "zip" in request.POST:
solutions = team.solutions.all()
out = BytesIO()
zf = zipfile.ZipFile(out, "w")
for solution in solutions:
zf.write(solution.file.path, str(solution) + ".pdf")
zf.close()
resp = HttpResponse(out.getvalue(), content_type="application/x-zip-compressed")
resp['Content-Disposition'] = 'attachment; filename={}'\
.format(_("Solutions for team {team}.zip")
.format(team=str(team)).replace(" ", "%20"))
return resp
return self.get(request, *args, **kwargs)
def get_context_data(self, **kwargs): def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs) context = super().get_context_data(**kwargs)

View File

@ -8,7 +8,7 @@ msgid ""
msgstr "" msgstr ""
"Project-Id-Version: TFJM2\n" "Project-Id-Version: TFJM2\n"
"Report-Msgid-Bugs-To: \n" "Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2020-04-30 18:11+0000\n" "POT-Creation-Date: 2020-04-30 19:04+0000\n"
"PO-Revision-Date: 2020-04-29 02:30+0000\n" "PO-Revision-Date: 2020-04-29 02:30+0000\n"
"Last-Translator: Yohann D'ANELLO <yohann.danello@animath.fr>\n" "Last-Translator: Yohann D'ANELLO <yohann.danello@animath.fr>\n"
"Language-Team: fr <LL@li.org>\n" "Language-Team: fr <LL@li.org>\n"
@ -30,7 +30,7 @@ msgstr "membre"
msgid "Choose a role..." msgid "Choose a role..."
msgstr "Choisir un rôle ..." msgstr "Choisir un rôle ..."
#: apps/member/forms.py:15 #: apps/member/forms.py:15 apps/member/models.py:133
msgid "Participant" msgid "Participant"
msgstr "Participant" msgstr "Participant"
@ -122,20 +122,16 @@ msgid "description"
msgstr "description" msgstr "description"
#: apps/member/models.py:130 #: apps/member/models.py:130
msgid "admin" msgid "Admin"
msgstr "administrateur" msgstr "Administrateur"
#: apps/member/models.py:131 #: apps/member/models.py:131
msgid "organizer" msgid "Organizer"
msgstr "oragnisateur" msgstr "Organisateur"
#: apps/member/models.py:132 #: apps/member/models.py:132
msgid "coach" msgid "Coach"
msgstr "encadrant" msgstr "Encadrant"
#: apps/member/models.py:133
msgid "participant"
msgstr "participant"
#: apps/member/models.py:139 apps/tournament/models.py:63 #: apps/member/models.py:139 apps/tournament/models.py:63
#: apps/tournament/models.py:129 #: apps/tournament/models.py:129
@ -269,7 +265,8 @@ msgstr "synthèses"
#: apps/member/models.py:286 #: apps/member/models.py:286
#, python-brace-format #, python-brace-format
msgid "Synthesis of team {trigram} that is {dest} for problem {problem}" msgid "Synthesis of team {trigram} that is {dest} for problem {problem}"
msgstr "Synthèse de l'équipe {trigram} qui est {dest} pour le problème {problem}" msgstr ""
"Synthèse de l'équipe {trigram} qui est {dest} pour le problème {problem}"
#: apps/member/models.py:294 #: apps/member/models.py:294
msgid "key" msgid "key"
@ -287,6 +284,18 @@ msgstr "configuration"
msgid "configurations" msgid "configurations"
msgstr "configurations" msgstr "configurations"
#: apps/member/views.py:37 templates/base.html:81
msgid "All profiles"
msgstr "Tous les profils"
#: apps/member/views.py:46 templates/base.html:80
msgid "Orphaned profiles"
msgstr "Profils orphelins"
#: apps/member/views.py:55 templates/base.html:83
msgid "Organizers"
msgstr "Organisateurs"
#: apps/tournament/apps.py:7 apps/tournament/models.py:76 #: apps/tournament/apps.py:7 apps/tournament/models.py:76
#: apps/tournament/models.py:98 templates/tournament/team_detail.html:18 #: apps/tournament/models.py:98 templates/tournament/team_detail.html:18
msgid "tournament" msgid "tournament"
@ -432,29 +441,27 @@ msgid "Tournaments list"
msgstr "Liste des tournois" msgstr "Liste des tournois"
#: apps/tournament/views.py:56 #: apps/tournament/views.py:56
#, fuzzy, python-brace-format
#| msgid "Payment of {user}"
msgid "Tournament of {name}" msgid "Tournament of {name}"
msgstr "Paiement de {user}" msgstr "Tournoi de {user}"
#: apps/tournament/views.py:80 #: apps/tournament/views.py:97 apps/tournament/views.py:131
msgid "Information about team"
msgstr "Informations sur l'équipe"
#: apps/tournament/views.py:89 templates/base.html:108 templates/base.html:126
msgid "Solutions"
msgstr "Solutions"
#: apps/tournament/views.py:105
#, python-brace-format #, python-brace-format
msgid "Solutions for team {team}.zip" msgid "Solutions for team {team}.zip"
msgstr "Solutions pour l'équipe {team}.zip" msgstr "Solutions pour l'équipe {team}.zip"
#: apps/tournament/views.py:130 #: apps/tournament/views.py:106
msgid "Information about team"
msgstr "Informations sur l'équipe"
#: apps/tournament/views.py:115 templates/base.html:108 templates/base.html:126
msgid "Solutions"
msgstr "Solutions"
#: apps/tournament/views.py:156
msgid "All solutions" msgid "All solutions"
msgstr "Toutes les solutions" msgstr "Toutes les solutions"
#: apps/tournament/views.py:149 #: apps/tournament/views.py:175
#, python-brace-format #, python-brace-format
msgid "Solutions for tournament {tournament}.zip" msgid "Solutions for tournament {tournament}.zip"
msgstr "Solutions pour le tournoi {tournament}.zip" msgstr "Solutions pour le tournoi {tournament}.zip"
@ -471,18 +478,6 @@ msgstr "Accueil"
msgid "Tournament list" msgid "Tournament list"
msgstr "Liste des tournois" msgstr "Liste des tournois"
#: templates/base.html:80
msgid "Orphaned profiles"
msgstr "Profils orphelins"
#: templates/base.html:81
msgid "All profiles"
msgstr "Tous les profils"
#: templates/base.html:83
msgid "Organizers"
msgstr "Organisateurs"
#: templates/base.html:89 #: templates/base.html:89
msgid "My account" msgid "My account"
msgstr "Mon compte" msgstr "Mon compte"
@ -671,7 +666,7 @@ msgid "Reset my password"
msgstr "Réinitialiser mon mot de passe" msgstr "Réinitialiser mon mot de passe"
#: templates/tournament/solutions_orga_list.html:14 #: templates/tournament/solutions_orga_list.html:14
#| msgid "tournaments" #, python-format
msgid "%(tournament)s — ZIP" msgid "%(tournament)s — ZIP"
msgstr "%(tournament)s — ZIP" msgstr "%(tournament)s — ZIP"
@ -700,10 +695,13 @@ msgid "Documents"
msgstr "Documents" msgstr "Documents"
#: templates/tournament/team_detail.html:45 #: templates/tournament/team_detail.html:45
#, python-format
msgid "Motivation letter:" msgid "Motivation letter:"
msgstr "Lettre de motivation :" msgstr "Lettre de motivation :"
#: templates/tournament/team_detail.html:61
msgid "Download solutions as ZIP"
msgstr "Télécharger les solutions en archive ZIP"
#: templates/tournament/tournament_detail.html:22 #: templates/tournament/tournament_detail.html:22
msgid "Free" msgid "Free"
msgstr "Gratuit" msgstr "Gratuit"
@ -722,7 +720,8 @@ msgstr "Envoyer un mail à toutes les personnes du tournoi"
#: templates/tournament/tournament_detail.html:43 #: templates/tournament/tournament_detail.html:43
msgid "Send a mail to all people in this tournament that are in a valid team" msgid "Send a mail to all people in this tournament that are in a valid team"
msgstr "Envoyer un mail à toutes les personnes du tournoi dans une équipe valide" msgstr ""
"Envoyer un mail à toutes les personnes du tournoi dans une équipe valide"
#: templates/tournament/tournament_detail.html:50 #: templates/tournament/tournament_detail.html:50
msgid "Edit tournament" msgid "Edit tournament"

View File

@ -0,0 +1,7 @@
{% extends "base.html" %}
{% load django_tables2 %}
{% block content %}
{% render_table table %}
{% endblock %}

View File

@ -51,9 +51,15 @@
<div class="alert alert-info"> <div class="alert alert-info">
<ul> <ul>
{% for solution in team.solutions.all %} {% for solution in team.solutions.all %}
<li><strong>{{ solution }} :</strong> <a data-turbolinks="false" href="{% url "member:document" file=solution.file %}">{% trans "Download" %}</a></li> <li><strong>{{ solution }} :</strong> <a data-turbolinks="false" href="{% url "document" file=solution.file %}">{% trans "Download" %}</a></li>
{% endfor %} {% endfor %}
</ul> </ul>
</div> </div>
<div class="text-center">
<form method="post">
{% csrf_token %}
<button class="btn btn-success" name="zip">{% trans "Download solutions as ZIP" %}</button>
</form>
</div>
{% endif %} {% endif %}
{% endblock %} {% endblock %}