Add some forms

This commit is contained in:
Yohann D'ANELLO 2020-05-04 20:21:53 +02:00
parent 97e5a0dfdd
commit 5ebf258eab
17 changed files with 285 additions and 26 deletions

View File

@ -1,4 +1,5 @@
from django.contrib.auth.forms import UserCreationForm
from django import forms
from django.utils.translation import gettext_lazy as _
from member.models import TFJMUser
@ -37,3 +38,9 @@ class SignUpForm(UserCreationForm):
'responsible_email',
'description',
)
class TFJMUserForm(forms.ModelForm):
class Meta:
model = TFJMUser
fields = '__all__'

View File

@ -1,13 +1,15 @@
from django.urls import path
from django.views.generic import RedirectView
from .views import CreateUserView, ProfileListView, OrphanedProfileListView, OrganizersListView
from .views import CreateUserView, MyAccountView, UserDetailView,\
ProfileListView, OrphanedProfileListView, OrganizersListView
app_name = "member"
urlpatterns = [
path('signup/', CreateUserView.as_view(), name="signup"),
path("my-account/", RedirectView.as_view(pattern_name="index"), name="my_account"),
path("my-account/", MyAccountView.as_view(), name="my_account"),
path("information/<int:pk>/", UserDetailView.as_view(), name="information"),
path("add-team/", RedirectView.as_view(pattern_name="index"), name="add_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"),

View File

@ -4,11 +4,11 @@ from django.db.models import Q
from django.http import FileResponse
from django.utils.translation import gettext_lazy as _
from django.views import View
from django.views.generic import CreateView
from django.views.generic import CreateView, UpdateView, DetailView
from django_tables2 import SingleTableView
from tournament.views import AdminMixin
from .forms import SignUpForm
from .forms import SignUpForm, TFJMUserForm
from .models import TFJMUser, Document
from .tables import UserTable
@ -19,6 +19,35 @@ class CreateUserView(CreateView):
template_name = "registration/signup.html"
class MyAccountView(LoginRequiredMixin, UpdateView):
model = TFJMUser
form_class = TFJMUserForm
template_name = "member/my_account.html"
def get_object(self, queryset=None):
return self.request.user
class UserDetailView(LoginRequiredMixin, DetailView):
model = TFJMUser
form_class = TFJMUserForm
context_object_name = "user"
def dispatch(self, request, *args, **kwargs):
if not request.user.admin \
and (self.object.team is not None and request.user not in self.object.team.tournament.organizers)\
and self.request.user != self.object:
raise PermissionDenied
return super().dispatch(request, *args, **kwargs)
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context["title"] = str(self.object)
return context
class DocumentView(LoginRequiredMixin, View):
def get(self, request, *args, **kwargs):
doc = Document.objects.get(file=self.kwargs["file"])
@ -29,7 +58,7 @@ class DocumentView(LoginRequiredMixin, View):
return FileResponse(doc.file, content_type="application/pdf")
class ProfileListView(LoginRequiredMixin, AdminMixin, SingleTableView):
class ProfileListView(AdminMixin, SingleTableView):
model = TFJMUser
queryset = TFJMUser.objects.order_by("role", "last_name", "first_name")
table_class = UserTable
@ -37,7 +66,7 @@ class ProfileListView(LoginRequiredMixin, AdminMixin, SingleTableView):
extra_context = dict(title=_("All profiles"))
class OrphanedProfileListView(LoginRequiredMixin, AdminMixin, SingleTableView):
class OrphanedProfileListView(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")
@ -46,7 +75,7 @@ class OrphanedProfileListView(LoginRequiredMixin, AdminMixin, SingleTableView):
extra_context = dict(title=_("Orphaned profiles"))
class OrganizersListView(LoginRequiredMixin, AdminMixin, SingleTableView):
class OrganizersListView(AdminMixin, SingleTableView):
model = TFJMUser
queryset = TFJMUser.objects.filter(Q(role="0admin") | Q(role="1volunteer"))\
.order_by("role", "last_name", "first_name")

44
apps/tournament/forms.py Normal file
View File

@ -0,0 +1,44 @@
from django import forms
from django.utils.translation import gettext_lazy as _
from member.models import TFJMUser
from tfjm.inputs import DatePickerInput, DateTimePickerInput, AmountInput
from tournament.models import Tournament, Team
class TournamentForm(forms.ModelForm):
class Meta:
model = Tournament
fields = '__all__'
widgets = {
"price": AmountInput(),
"date_start": DatePickerInput(),
"date_end": DatePickerInput(),
"date_inscription": DateTimePickerInput(),
"date_solutions": DateTimePickerInput(),
"date_syntheses": DateTimePickerInput(),
}
class OrganizerForm(forms.ModelForm):
class Meta:
model = TFJMUser
fields = ('last_name', 'first_name', 'email', 'is_superuser',)
def save(self, commit=True):
user = self.instance
user.role = '0admin' if user.is_superuser else '1organizer'
super().save(commit)
class TeamForm(forms.ModelForm):
class Meta:
model = Team
fields = ('name', 'trigram', 'tournament',)
class JoinTeam(forms.Form):
access_code = forms.CharField(
label=_("Access code"),
max_length=6,
)

View File

@ -2,6 +2,7 @@ import os
from datetime import date
from django.db import models
from django.urls import reverse_lazy
from django.utils.translation import gettext_lazy as _
@ -145,10 +146,20 @@ class Team(models.Model):
def encadrants(self):
return self.users.all().filter(role="2coach")
@property
def linked_encadrants(self):
return ['<a href="{url}">'.format(url=reverse_lazy("member:information", args=(user.pk,))) + str(user) + '</a>'
for user in self.encadrants]
@property
def participants(self):
return self.users.all().filter(role="3participant")
@property
def linked_participants(self):
return ['<a href="{url}">'.format(url=reverse_lazy("member:information", args=(user.pk,))) + str(user) + '</a>'
for user in self.participants]
class Meta:
verbose_name = _("team")
verbose_name_plural = _("teams")

View File

@ -1,16 +1,19 @@
from django.urls import path
from django.views.generic import RedirectView
from .views import TournamentListView, TournamentDetailView, TeamDetailView, SolutionsView, SolutionsOrgaListView
from .views import TournamentListView, TournamentCreateView, TournamentDetailView, TournamentUpdateView,\
TeamDetailView, TeamUpdateView, AddOrganizerView, SolutionsView, SolutionsOrgaListView
app_name = "tournament"
urlpatterns = [
path('list/', TournamentListView.as_view(), name="list"),
path("add/", RedirectView.as_view(pattern_name="index"), name="add"),
path("add/", TournamentCreateView.as_view(), name="add"),
path('<int:pk>/', TournamentDetailView.as_view(), name="detail"),
path('<int:pk>/update/', TournamentUpdateView.as_view(), name="update"),
path('team/<int:pk>/', TeamDetailView.as_view(), name="team_detail"),
path("add-organizer/", RedirectView.as_view(pattern_name="index"), name="add_organizer"),
path('team/<int:pk>/update/', TeamUpdateView.as_view(), name="team_update"),
path("add-organizer/", AddOrganizerView.as_view(), name="add_organizer"),
path("solutions/", SolutionsView.as_view(), name="solutions"),
path("all-solutions/", SolutionsOrgaListView.as_view(), name="all_solutions"),
path("syntheses/", RedirectView.as_view(pattern_name="index"), name="syntheses"),

View File

@ -5,23 +5,26 @@ from django.contrib.auth.mixins import LoginRequiredMixin
from django.core.exceptions import PermissionDenied
from django.db.models import Q
from django.http import HttpResponse
from django.shortcuts import redirect
from django.urls import reverse_lazy
from django.utils.translation import gettext_lazy as _
from django.views.generic import DetailView
from django.views.generic import DetailView, CreateView, UpdateView
from django_tables2.views import SingleTableView
from member.models import TFJMUser, Solution
from .forms import TournamentForm, OrganizerForm, TeamForm
from .models import Tournament, Team
from .tables import TournamentTable, TeamTable, SolutionTable
class AdminMixin(object):
class AdminMixin(LoginRequiredMixin):
def dispatch(self, request, *args, **kwargs):
if not request.user.admin:
raise PermissionDenied
return super().dispatch(request, *args, **kwargs)
class TeamMixin(object):
class TeamMixin(LoginRequiredMixin):
def dispatch(self, request, *args, **kwargs):
if not request.user.team:
raise PermissionDenied
@ -47,6 +50,15 @@ class TournamentListView(SingleTableView):
return context
class TournamentCreateView(AdminMixin, CreateView):
model = Tournament
form_class = TournamentForm
extra_context = dict(title=_("Add tournament"),)
def get_success_url(self):
return reverse_lazy('tournament:detail', args=(self.object.pk,))
class TournamentDetailView(DetailView):
model = Tournament
@ -71,6 +83,15 @@ class TournamentDetailView(DetailView):
return context
class TournamentUpdateView(AdminMixin, UpdateView):
model = Tournament
form_class = TournamentForm
extra_context = dict(title=_("Update tournament"),)
def get_success_url(self):
return reverse_lazy('tournament:detail', args=(self.object.pk,))
class TeamDetailView(LoginRequiredMixin, DetailView):
model = Team
@ -97,6 +118,9 @@ class TeamDetailView(LoginRequiredMixin, DetailView):
.format(_("Solutions for team {team}.zip")
.format(team=str(team)).replace(" ", "%20"))
return resp
elif "delete" in request.POST:
team.delete()
return redirect('tournament:detail', pk=team.tournament.pk)
return self.get(request, *args, **kwargs)
@ -108,7 +132,25 @@ class TeamDetailView(LoginRequiredMixin, DetailView):
return context
class SolutionsView(LoginRequiredMixin, TeamMixin, SingleTableView):
class TeamUpdateView(LoginRequiredMixin, UpdateView):
model = Team
form_class = TeamForm
extra_context = dict(title=_("Udpate 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)
class AddOrganizerView(AdminMixin, CreateView):
model = TFJMUser
form_class = OrganizerForm
extra_context = dict(title=_("Add organizer"),)
template_name = "tournament/add_organizer.html"
class SolutionsView(TeamMixin, SingleTableView):
model = Solution
table_class = SolutionTable
template_name = "tournament/solutions_list.html"
@ -149,7 +191,7 @@ class SolutionsView(LoginRequiredMixin, TeamMixin, SingleTableView):
return qs.order_by('team__tournament__date_start', 'team__tournament__name', 'team__trigram', 'problem',)
class SolutionsOrgaListView(LoginRequiredMixin, AdminMixin, SingleTableView):
class SolutionsOrgaListView(AdminMixin, SingleTableView):
model = Solution
table_class = SolutionTable
template_name = "tournament/solutions_orga_list.html"

View File

@ -442,7 +442,7 @@ msgstr "Liste des tournois"
#: apps/tournament/views.py:56
msgid "Tournament of {name}"
msgstr "Tournoi de {user}"
msgstr "Tournoi de {name}"
#: apps/tournament/views.py:97 apps/tournament/views.py:131
#, python-brace-format

View File

@ -0,0 +1,14 @@
{% extends "base.html" %}
{% load i18n crispy_forms_filters %}
{% block content %}
<form method="post">
{{ form|crispy }}
<input type="submit" class="btn btn-primary btn-block" value="{% trans "Submit" %}">
</form>
<hr>
<a class="btn btn-secondary btn-block" href="{% url "password_change" %}">{% trans "Update my password" %}</a>
{% endblock %}

View File

@ -0,0 +1,69 @@
{% extends "base.html" %}
{% load getconfig i18n django_tables2 static %}
{% block content %}
<div class="card bg-light shadow">
<div class="card-header text-center">
<h4>{{ user }}</h4>
</div>
<div class="card-body">
<dl class="row">
<dt class="col-xl-6 text-right">{% trans 'role'|capfirst %}</dt>
<dd class="col-xl-6">{{ user.get_role_display }}</dd>
<dt class="col-xl-6 text-right">{% trans 'team'|capfirst %}</dt>
<dd class="col-xl-6"><a href="{% url "tournament:team_detail" pk=user.team.pk %}">{{ user.team }}</a></dd>
<dt class="col-xl-6 text-right">{% trans 'birth date'|capfirst %}</dt>
<dd class="col-xl-6">{{ user.birth_date }}</dd>
<dt class="col-xl-6 text-right">{% trans 'gender'|capfirst %}</dt>
<dd class="col-xl-6">{{ user.get_gender_display }}</dd>
<dt class="col-xl-6 text-right">{% trans 'address'|capfirst %}</dt>
<dd class="col-xl-6">{{ user.address }}, {{ user.postal_code }}, {{ user.city }}{% if user.country != "France" %}, {{ user.country }}{% endif %}</dd>
<dt class="col-xl-6 text-right">{% trans 'email'|capfirst %}</dt>
<dd class="col-xl-6"><a href="mailto:{{ user.email }}">{{ user.email }}</a></dd>
<dt class="col-xl-6 text-right">{% trans 'phone number'|capfirst %}</dt>
<dd class="col-xl-6">{{ user.phone_number }}</dd>
{% if user.role == '3participant' %}
<dt class="col-xl-6 text-right">{% trans 'school'|capfirst %}</dt>
<dd class="col-xl-6">{{ user.school }}</dd>
<dt class="col-xl-6 text-right">{% trans 'class'|capfirst %}</dt>
<dd class="col-xl-6">{{ user.get_student_class_display }}</dd>
{% if user.responsible_name %}
<dt class="col-xl-6 text-right">{% trans 'responsible name'|capfirst %}</dt>
<dd class="col-xl-6">{{ user.responsible_name }}</dd>
{% endif %}
{% if user.responsible_phone %}
<dt class="col-xl-6 text-right">{% trans 'responsible phone'|capfirst %}</dt>
<dd class="col-xl-6">{{ user.responsible_phone }}</dd>
{% endif %}
{% if user.responsible_email %}
<dt class="col-xl-6 text-right">{% trans 'responsible email'|capfirst %}</dt>
<dd class="col-xl-6"><a href="{{ user.responsible_email }}">{{ user.responsible_email }}</a></dd>
{% endif %}
{% endif %}
{% if user.role == '2coach' %}
<dt class="col-xl-6 text-right">{% trans 'description'|capfirst %}</dt>
<dd class="col-xl-6">{{ user.description }}</dd>
{% endif %}
</dl>
</div>
</div>
<hr>
<h4>{% trans "Documents" %}</h4>
{# TODO Display documents #}
{% endblock %}

View File

@ -0,0 +1,11 @@
{% extends "base.html" %}
{% load i18n crispy_forms_filters %}
{% block content %}
<form method="post">
{% csrf_token %}
{{ form|crispy }}
<input type="submit" class="btn btn-primary btn-block" value="{% trans "Submit" %}">
</form>
{% endblock %}

View File

@ -19,18 +19,21 @@
<dd class="col-xl-6">{{ team.tournament }}</dd>
<dt class="col-xl-6 text-right">{% trans 'coachs'|capfirst %}</dt>
<dd class="col-xl-6">{{ team.encadrants.all|join:", " }}</dd>
<dd class="col-xl-6">{% autoescape off %}{{ team.linked_encadrants|join:", " }}{% endautoescape %}</dd>
<dt class="col-xl-6 text-right">{% trans 'participants'|capfirst %}</dt>
<dd class="col-xl-6">{{ team.participants.all|join:", " }}</dd>
<dd class="col-xl-6">{% autoescape off %}{{ team.linked_participants|join:", " }}{% endautoescape %}</dd>
</dl>
</div>
{% if user.admin or user in team.tournament.organizers.all %}
<div class="card-footer text-center">
<button class="btn btn-secondary">{% trans "Edit team" %}</button>
<a href="{% url "tournament:team_update" pk=team.pk %}"><button class="btn btn-secondary">{% trans "Edit team" %}</button></a>
{% if user.admin and team.invalid %}
<button class="btn btn-danger">{% trans "Delete team" %}</button>
<form method="post">
{% csrf_token %}
<button name="delete" class="btn btn-danger">{% trans "Delete team" %}</button>
</form>
{% endif %}
</div>
{% endif %}

View File

@ -0,0 +1,11 @@
{% extends "base.html" %}
{% load i18n crispy_forms_filters %}
{% block content %}
<form method="post">
{% csrf_token %}
{{ form|crispy }}
<input type="submit" class="btn btn-primary btn-block" value="{% trans "Submit" %}">
</form>
{% endblock %}

View File

@ -39,15 +39,15 @@
{% if user.is_authenticated and user.admin %}
<div class="alert alert-info">
<a href="mailto:contact@tfm.org?subject=TFJM²%20{{ "TFJM_YEAR"|get_env }}&bcc={{ team_users_emails|join:"," }}">{% trans "Send a mail to all people in this tournament" %}</a><br>
<a href="mailto:contact@tfm.org?subject=TFJM²%20{{ "TFJM_YEAR"|get_env }}&bcc={{ valid_team_users_emails|join:"," }}">{% trans "Send a mail to all people in this tournament that are in a valid team" %}</a>
<a href="mailto:contact@tfjm.org?subject=TFJM²%20{{ "TFJM_YEAR"|get_env }}&bcc={{ team_users_emails|join:"," }}">{% trans "Send a mail to all people in this tournament" %}</a><br>
<a href="mailto:contact@tfjm.org?subject=TFJM²%20{{ "TFJM_YEAR"|get_env }}&bcc={{ valid_team_users_emails|join:"," }}">{% trans "Send a mail to all people in this tournament that are in a valid team" %}</a>
</div>
{% endif %}
</div>
{% if user.admin or user in tournament.organizers.all %}
<div class="card-footer text-center">
<button class="btn btn-secondary">{% trans "Edit tournament" %}</button>
<a href="{% url "tournament:update" pk=tournament.pk %}"><button class="btn btn-secondary">{% trans "Edit tournament" %}</button></a>
</div>
{% endif %}
</div>

View File

@ -0,0 +1,11 @@
{% extends "base.html" %}
{% load i18n crispy_forms_filters %}
{% block content %}
<form method="post">
{% csrf_token %}
{{ form|crispy }}
<input type="submit" class="btn btn-primary btn-block" value="{% trans "Submit" %}">
</form>
{% endblock %}

View File

@ -5,8 +5,8 @@
{% block content %}
{% if user.is_authenticated and user.admin %}
<div class="alert alert-info">
<a href="mailto:contact@tfm.org?subject=TFJM²%20{{ "TFJM_YEAR"|get_env }}&bcc={{ team_users_emails|join:"," }}">{% trans "Send a mail to all people that are in a team" %}</a><br>
<a href="mailto:contact@tfm.org?subject=TFJM²%20{{ "TFJM_YEAR"|get_env }}&bcc={{ valid_team_users_emails|join:"," }}">{% trans "Send a mail to all people that are in a valid team" %}</a>
<a href="mailto:contact@tfjm.org?subject=TFJM²%20{{ "TFJM_YEAR"|get_env }}&bcc={{ team_users_emails|join:"," }}">{% trans "Send a mail to all people that are in a team" %}</a><br>
<a href="mailto:contact@tfjm.org?subject=TFJM²%20{{ "TFJM_YEAR"|get_env }}&bcc={{ valid_team_users_emails|join:"," }}">{% trans "Send a mail to all people that are in a valid team" %}</a>
</div>
{% endif %}
{% render_table table %}

View File

@ -89,6 +89,8 @@ TEMPLATES = [
},
]
FORM_RENDERER = 'django.forms.renderers.TemplatesSetting'
WSGI_APPLICATION = 'tfjm.wsgi.application'
@ -150,7 +152,7 @@ LANGUAGES = [
('fr', _('French')),
]
TIME_ZONE = 'UTC'
TIME_ZONE = 'Europe/Paris'
USE_I18N = True