diff --git a/apps/participation/apps.py b/apps/participation/apps.py index ce11f56..98690b3 100644 --- a/apps/participation/apps.py +++ b/apps/participation/apps.py @@ -1,11 +1,12 @@ from django.apps import AppConfig -from django.db.models.signals import post_save, pre_save +from django.db.models.signals import post_save, pre_delete, pre_save class ParticipationConfig(AppConfig): name = 'participation' def ready(self): - from participation.signals import create_team_participation, update_mailing_list + from participation.signals import create_team_participation, delete_related_videos, update_mailing_list pre_save.connect(update_mailing_list, "participation.Team") + pre_delete.connect(delete_related_videos, "participation.Participation") post_save.connect(create_team_participation, "participation.Team") diff --git a/apps/participation/models.py b/apps/participation/models.py index 920894a..c3f3e4e 100644 --- a/apps/participation/models.py +++ b/apps/participation/models.py @@ -5,6 +5,7 @@ from django.core.exceptions import ObjectDoesNotExist from django.core.validators import RegexValidator from django.db import models from django.db.models import Index +from django.urls import reverse_lazy from django.utils.crypto import get_random_string from django.utils.text import format_lazy from django.utils.translation import gettext_lazy as _ @@ -56,6 +57,9 @@ class Team(models.Model): return super().save(*args, **kwargs) + def get_absolute_url(self): + return reverse_lazy("participation:team_detail", args=(self.pk,)) + def __str__(self): return _("Team {name} ({trigram})").format(name=self.name, trigram=self.trigram) @@ -115,6 +119,9 @@ class Participation(models.Model): verbose_name=_("synthesis video"), ) + def get_absolute_url(self): + return reverse_lazy("participation:participation_detail", args=(self.pk,)) + def __str__(self): return _("Participation of the team {name} ({trigram})").format(name=self.team.name, trigram=self.team.trigram) diff --git a/apps/participation/search_indexes.py b/apps/participation/search_indexes.py new file mode 100644 index 0000000..c1a2249 --- /dev/null +++ b/apps/participation/search_indexes.py @@ -0,0 +1,24 @@ +from haystack import indexes + +from .models import Participation, Team, Video + + +class TeamIndex(indexes.ModelSearchIndex, indexes.Indexable): + text = indexes.NgramField(document=True, use_template=True) + + class Meta: + model = Team + + +class ParticipationIndex(indexes.ModelSearchIndex, indexes.Indexable): + text = indexes.NgramField(document=True, use_template=True) + + class Meta: + model = Participation + + +class VideoIndex(indexes.ModelSearchIndex, indexes.Indexable): + text = indexes.NgramField(document=True, use_template=True) + + class Meta: + model = Video diff --git a/apps/participation/signals.py b/apps/participation/signals.py index e178de0..d8caa42 100644 --- a/apps/participation/signals.py +++ b/apps/participation/signals.py @@ -23,3 +23,10 @@ def update_mailing_list(instance: Team, **_): for coach in instance.coachs.all(): get_sympa_client().subscribe(coach.user.email, f"equipe-{instance.trigram.lower()}", False, f"{coach.user.first_name} {coach.user.last_name}") + + +def delete_related_videos(instance: Participation, **_): + if instance.solution: + instance.solution.delete() + if instance.synthesis: + instance.synthesis.delete() diff --git a/apps/participation/tables.py b/apps/participation/tables.py new file mode 100644 index 0000000..5f3149e --- /dev/null +++ b/apps/participation/tables.py @@ -0,0 +1,70 @@ +from django.utils.translation import gettext_lazy as _ +import django_tables2 as tables + +from .models import Team + + +# noinspection PyTypeChecker +class TeamTable(tables.Table): + name = tables.LinkColumn( + 'participation:team_detail', + args=[tables.A("id")], + verbose_name=lambda: _("name").capitalize(), + ) + + problem = tables.Column( + accessor="participation__problem", + verbose_name=lambda: _("problem number").capitalize(), + ) + + class Meta: + attrs = { + 'class': 'table table condensed table-striped', + } + model = Team + fields = ('name', 'trigram', 'problem',) + template_name = 'django_tables2/bootstrap4.html' + + +# noinspection PyTypeChecker +class ParticipationTable(tables.Table): + name = tables.LinkColumn( + 'participation:participation_detail', + args=[tables.A("id")], + verbose_name=lambda: _("name").capitalize(), + accessor="team__name", + ) + + trigram = tables.Column( + verbose_name=lambda: _("trigram").capitalize(), + accessor="team__trigram", + ) + + problem = tables.Column( + verbose_name=lambda: _("problem number").capitalize(), + ) + + class Meta: + attrs = { + 'class': 'table table condensed table-striped', + } + model = Team + fields = ('name', 'trigram', 'problem',) + template_name = 'django_tables2/bootstrap4.html' + + +class VideoTable(tables.Table): + participation_name = tables.LinkColumn( + 'participation:participation_detail', + args=[tables.A("participation__pk")], + verbose_name=lambda: _("name").capitalize(), + accessor="participation__name", + ) + + class Meta: + attrs = { + 'class': 'table table condensed table-striped', + } + model = Team + fields = ('participation_name', 'link',) + template_name = 'django_tables2/bootstrap4.html' diff --git a/apps/participation/templates/search/indexes/participation/participation_text.txt b/apps/participation/templates/search/indexes/participation/participation_text.txt new file mode 100644 index 0000000..17f697d --- /dev/null +++ b/apps/participation/templates/search/indexes/participation/participation_text.txt @@ -0,0 +1,2 @@ +{{ object.team.name }} +{{ object.team.trigram }} \ No newline at end of file diff --git a/apps/participation/templates/search/indexes/participation/team_text.txt b/apps/participation/templates/search/indexes/participation/team_text.txt new file mode 100644 index 0000000..d673407 --- /dev/null +++ b/apps/participation/templates/search/indexes/participation/team_text.txt @@ -0,0 +1,2 @@ +{{ object.name }} +{{ object.trigram }} diff --git a/apps/participation/templates/search/indexes/participation/video_text.txt b/apps/participation/templates/search/indexes/participation/video_text.txt new file mode 100644 index 0000000..0f1a368 --- /dev/null +++ b/apps/participation/templates/search/indexes/participation/video_text.txt @@ -0,0 +1 @@ +{{ object.link }} diff --git a/apps/participation/views.py b/apps/participation/views.py index fff0248..6066e19 100644 --- a/apps/participation/views.py +++ b/apps/participation/views.py @@ -1,12 +1,11 @@ -import os from io import BytesIO +import os from zipfile import ZipFile -from django.core.mail import send_mail - from corres2math.lists import get_sympa_client from django.contrib.auth.mixins import LoginRequiredMixin from django.core.exceptions import PermissionDenied +from django.core.mail import send_mail from django.db import transaction from django.http import HttpResponse from django.template.loader import render_to_string diff --git a/apps/registration/models.py b/apps/registration/models.py index c1540db..71c890d 100644 --- a/apps/registration/models.py +++ b/apps/registration/models.py @@ -2,6 +2,7 @@ from corres2math.tokens import email_validation_token from django.contrib.sites.models import Site from django.db import models from django.template import loader +from django.urls import reverse_lazy from django.utils.crypto import get_random_string from django.utils.encoding import force_bytes from django.utils.http import urlsafe_base64_encode @@ -63,6 +64,9 @@ class Registration(PolymorphicModel): def is_admin(self): return isinstance(self, AdminRegistration) or self.user.is_superuser + def get_absolute_url(self): + return reverse_lazy("registration:user_detail", args=(self.user_id,)) + def __str__(self): return f"{self.user.first_name} {self.user.last_name}" diff --git a/apps/registration/search_indexes.py b/apps/registration/search_indexes.py new file mode 100644 index 0000000..b04b0b9 --- /dev/null +++ b/apps/registration/search_indexes.py @@ -0,0 +1,10 @@ +from haystack import indexes + +from .models import Registration + + +class RegistrationIndex(indexes.ModelSearchIndex, indexes.Indexable): + text = indexes.NgramField(document=True, use_template=True) + + class Meta: + model = Registration diff --git a/apps/registration/tables.py b/apps/registration/tables.py new file mode 100644 index 0000000..bc617a9 --- /dev/null +++ b/apps/registration/tables.py @@ -0,0 +1,21 @@ +from django.utils.translation import gettext_lazy as _ +import django_tables2 as tables + +from .models import Registration + + +class RegistrationTable(tables.Table): + last_name = tables.LinkColumn( + 'registration:user_detail', + args=[tables.A("user_id")], + verbose_name=lambda: _("last name").capitalize(), + accessor="user__last_name", + ) + + class Meta: + attrs = { + 'class': 'table table condensed table-striped', + } + model = Registration + fields = ('last_name', 'user__first_name', 'user__email', 'type',) + template_name = 'django_tables2/bootstrap4.html' diff --git a/apps/registration/templates/search/indexes/registration/adminregistration_text.txt b/apps/registration/templates/search/indexes/registration/adminregistration_text.txt new file mode 100644 index 0000000..a5077ab --- /dev/null +++ b/apps/registration/templates/search/indexes/registration/adminregistration_text.txt @@ -0,0 +1,5 @@ +{{ object.user.last_name }} +{{ object.user.first_name }} +{{ object.user.email }} +{{ object.type }} +{{ object.role }} diff --git a/apps/registration/templates/search/indexes/registration/coachregistration_text.txt b/apps/registration/templates/search/indexes/registration/coachregistration_text.txt new file mode 100644 index 0000000..e7e53e5 --- /dev/null +++ b/apps/registration/templates/search/indexes/registration/coachregistration_text.txt @@ -0,0 +1,7 @@ +{{ object.user.first_name }} +{{ object.user.last_name }} +{{ object.user.email }} +{{ object.type }} +{{ object.professional_activity }} +{{ object.team.name }} +{{ object.team.trigram }} diff --git a/apps/registration/templates/search/indexes/registration/studentregistration_text.txt b/apps/registration/templates/search/indexes/registration/studentregistration_text.txt new file mode 100644 index 0000000..f103c7f --- /dev/null +++ b/apps/registration/templates/search/indexes/registration/studentregistration_text.txt @@ -0,0 +1,8 @@ +{{ object.user.first_name }} +{{ object.user.last_name }} +{{ object.user.email }} +{{ object.type }} +{{ object.get_student_class_display }} +{{ object.school }} +{{ object.team.name }} +{{ object.team.trigram }} diff --git a/apps/registration/templatetags/__init__.py b/apps/registration/templatetags/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/apps/registration/templatetags/search_results_tables.py b/apps/registration/templatetags/search_results_tables.py new file mode 100644 index 0000000..840ebac --- /dev/null +++ b/apps/registration/templatetags/search_results_tables.py @@ -0,0 +1,26 @@ +from django import template +from django_tables2 import Table +from participation.models import Participation, Team, Video +from participation.tables import ParticipationTable, TeamTable, VideoTable + +from ..models import Registration +from ..tables import RegistrationTable + + +def search_table(results): + model_class = results[0].object.__class__ + if issubclass(model_class, Registration): + table_class = RegistrationTable + elif issubclass(model_class, Team): + table_class = TeamTable + elif issubclass(model_class, Participation): + table_class = ParticipationTable + elif issubclass(model_class, Video): + table_class = VideoTable + else: + table_class = Table + return table_class([result.object for result in results], prefix=model_class._meta.model_name) + + +register = template.Library() +register.filter("search_table", search_table) diff --git a/corres2math/settings.py b/corres2math/settings.py index 230321f..ab0b9eb 100644 --- a/corres2math/settings.py +++ b/corres2math/settings.py @@ -54,6 +54,7 @@ INSTALLED_APPS = [ 'crispy_forms', 'django_extensions', 'django_tables2', + 'haystack', 'logs', 'mailer', 'polymorphic', @@ -181,6 +182,15 @@ CRISPY_TEMPLATE_PACK = 'bootstrap4' DJANGO_TABLES2_TEMPLATE = 'django_tables2/bootstrap4.html' +HAYSTACK_CONNECTIONS = { + 'default': { + 'ENGINE': 'haystack.backends.whoosh_backend.WhooshEngine', + 'PATH': os.path.join(os.path.dirname(__file__), 'whoosh_index'), + } +} + +HAYSTACK_SIGNAL_PROCESSOR = 'haystack.signals.RealtimeSignalProcessor' + _db_type = os.getenv('DJANGO_DB_TYPE', 'sqlite').lower() if _db_type == 'mysql' or _db_type.startswith('postgres') or _db_type == 'psql': diff --git a/corres2math/urls.py b/corres2math/urls.py index 63efe76..0a2085c 100644 --- a/corres2math/urls.py +++ b/corres2math/urls.py @@ -13,19 +13,21 @@ Including another URLconf 1. Import the include() function: from django.urls import include, path 2. Add a URL to urlpatterns: path('blog/', include('blog.urls')) """ -from django.conf.urls.static import static from django.contrib import admin from django.urls import path, include from django.views.defaults import bad_request, permission_denied, page_not_found, server_error from django.views.generic import TemplateView from registration.views import PhotoAuthorizationView +from .views import AdminSearchView + urlpatterns = [ path('', TemplateView.as_view(template_name="index.html"), name='index'), path('i18n/', include('django.conf.urls.i18n')), path('admin/doc/', include('django.contrib.admindocs.urls')), path('admin/', admin.site.urls, name="admin"), path('accounts/', include('django.contrib.auth.urls')), + path('search/', AdminSearchView.as_view(), name="haystack_search"), path('api/', include('api.urls')), path('participation/', include('participation.urls')), diff --git a/corres2math/views.py b/corres2math/views.py new file mode 100644 index 0000000..7ee3d7c --- /dev/null +++ b/corres2math/views.py @@ -0,0 +1,13 @@ +from django.contrib.auth.mixins import LoginRequiredMixin +from django.core.exceptions import PermissionDenied +from django.utils.translation import gettext_lazy as _ +from haystack.generic_views import SearchView + + +class AdminSearchView(LoginRequiredMixin, SearchView): + def dispatch(self, request, *args, **kwargs): + if not request.user.is_authenticated: + return self.handle_no_permission() + if not request.user.registration.is_admin: + raise PermissionDenied(_("Only administrators are allowed to perform a full research.")) + return super().dispatch(request, *args, **kwargs) diff --git a/locale/fr/LC_MESSAGES/django.po b/locale/fr/LC_MESSAGES/django.po index 9305d78..366d6be 100644 --- a/locale/fr/LC_MESSAGES/django.po +++ b/locale/fr/LC_MESSAGES/django.po @@ -7,7 +7,7 @@ msgid "" msgstr "" "Project-Id-Version: Corres2math\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2020-10-11 18:43+0200\n" +"POT-Creation-Date: 2020-10-15 20:47+0200\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: Yohann D'ANELLO \n" "Language-Team: LANGUAGE \n" @@ -30,7 +30,7 @@ msgid "This task failed successfully." msgstr "Cette tâche a échoué avec succès." #: apps/eastereggs/templates/eastereggs/xp_modal.html:16 -#: templates/base_modal.html:16 +#: templates/base_modal.html:18 msgid "Close" msgstr "Fermer" @@ -38,7 +38,7 @@ msgstr "Fermer" msgid "Logs" msgstr "Logs" -#: apps/logs/models.py:22 apps/registration/models.py:16 +#: apps/logs/models.py:22 apps/registration/models.py:17 msgid "user" msgstr "utilisateur" @@ -99,7 +99,7 @@ msgstr "changelogs" msgid "Changelog of type \"{action}\" for model {model} at {timestamp}" msgstr "Changelog de type \"{action}\" pour le modèle {model} le {timestamp}" -#: apps/participation/forms.py:14 apps/participation/models.py:23 +#: apps/participation/forms.py:14 apps/participation/models.py:24 msgid "The trigram must be composed of three uppercase letters." msgstr "Le trigramme doit être composé de trois lettres majuscules." @@ -115,27 +115,28 @@ msgstr "Je m'engage à participer à l'intégralité des Correspondances." msgid "Message to address to the team:" msgstr "Message à adresser à l'équipe :" -#: apps/participation/models.py:16 +#: apps/participation/models.py:17 apps/participation/tables.py:12 +#: apps/participation/tables.py:34 apps/participation/tables.py:60 msgid "name" msgstr "nom" -#: apps/participation/models.py:22 +#: apps/participation/models.py:23 apps/participation/tables.py:39 msgid "trigram" msgstr "trigramme" -#: apps/participation/models.py:30 +#: apps/participation/models.py:31 msgid "access code" msgstr "code d'accès" -#: apps/participation/models.py:31 +#: apps/participation/models.py:32 msgid "The access code let other people to join the team." msgstr "Le code d'accès permet aux autres participants de rejoindre l'équipe." -#: apps/participation/models.py:35 +#: apps/participation/models.py:36 msgid "Grant Animath to publish my video" msgstr "Autoriser Animath à publier ma vidéo" -#: apps/participation/models.py:36 +#: apps/participation/models.py:37 msgid "" "Give the authorisation to publish the video on the main website to promote " "the action." @@ -143,90 +144,91 @@ msgstr "" "Donner l'autorisation de publier la vidéo sur le site principal pour " "promouvoir les Correspondances." -#: apps/participation/models.py:60 +#: apps/participation/models.py:64 #, python-brace-format msgid "Team {name} ({trigram})" msgstr "Équipe {name} ({trigram})" -#: apps/participation/models.py:63 apps/participation/models.py:74 -#: apps/registration/models.py:85 apps/registration/models.py:130 +#: apps/participation/models.py:67 apps/participation/models.py:78 +#: apps/registration/models.py:89 apps/registration/models.py:134 msgid "team" msgstr "équipe" -#: apps/participation/models.py:64 +#: apps/participation/models.py:68 msgid "teams" msgstr "équipes" -#: apps/participation/models.py:78 +#: apps/participation/models.py:82 #, python-brace-format msgid "Problem #{problem:d}" msgstr "Problème n°{problem:d}" -#: apps/participation/models.py:81 +#: apps/participation/models.py:85 apps/participation/tables.py:17 +#: apps/participation/tables.py:44 msgid "problem number" msgstr "numéro de problème" -#: apps/participation/models.py:87 apps/participation/models.py:135 +#: apps/participation/models.py:91 apps/participation/models.py:142 msgid "valid" msgstr "valide" -#: apps/participation/models.py:88 apps/participation/models.py:136 +#: apps/participation/models.py:92 apps/participation/models.py:143 msgid "The video got the validation of the administrators." msgstr "La vidéo a été validée par les administrateurs." -#: apps/participation/models.py:97 +#: apps/participation/models.py:101 msgid "solution video" msgstr "vidéo de solution" -#: apps/participation/models.py:106 +#: apps/participation/models.py:110 msgid "received participation" msgstr "participation reçue" -#: apps/participation/models.py:115 +#: apps/participation/models.py:119 msgid "synthesis video" msgstr "vidéo de synthèse" -#: apps/participation/models.py:119 +#: apps/participation/models.py:126 #, python-brace-format msgid "Participation of the team {name} ({trigram})" msgstr "Participation de l'équipe {name} ({trigram})" -#: apps/participation/models.py:122 +#: apps/participation/models.py:129 msgid "participation" msgstr "participation" -#: apps/participation/models.py:123 +#: apps/participation/models.py:130 msgid "participations" msgstr "participations" -#: apps/participation/models.py:128 +#: apps/participation/models.py:135 msgid "link" msgstr "lien" -#: apps/participation/models.py:129 +#: apps/participation/models.py:136 msgid "The full video link." msgstr "Le lien complet de la vidéo." -#: apps/participation/models.py:158 +#: apps/participation/models.py:165 #, python-brace-format msgid "Video of team {name} ({trigram})" msgstr "Vidéo de l'équipe {name} ({trigram})" -#: apps/participation/models.py:162 +#: apps/participation/models.py:169 msgid "video" msgstr "vidéo" -#: apps/participation/models.py:163 +#: apps/participation/models.py:170 msgid "videos" msgstr "vidéos" #: apps/participation/templates/participation/create_team.html:11 -#: templates/base.html:207 +#: templates/base.html:220 msgid "Create" msgstr "Créer" #: apps/participation/templates/participation/join_team.html:11 -#: templates/base.html:203 +#: templates/base.html:216 msgid "Join" msgstr "Rejoindre" @@ -370,65 +372,65 @@ msgstr "Invalider" msgid "Update team" msgstr "Modifier l'équipe" -#: apps/participation/views.py:25 templates/base.html:70 -#: templates/base.html:206 +#: apps/participation/views.py:27 templates/base.html:70 +#: templates/base.html:219 msgid "Create team" msgstr "Créer une équipe" -#: apps/participation/views.py:32 apps/participation/views.py:62 +#: apps/participation/views.py:34 apps/participation/views.py:64 msgid "You don't participate, so you can't create a team." msgstr "Vous ne participez pas, vous ne pouvez pas créer d'équipe." -#: apps/participation/views.py:34 apps/participation/views.py:64 +#: apps/participation/views.py:36 apps/participation/views.py:66 msgid "You are already in a team." msgstr "Vous êtes déjà dans une équipe." -#: apps/participation/views.py:55 templates/base.html:75 -#: templates/base.html:202 +#: apps/participation/views.py:57 templates/base.html:75 +#: templates/base.html:215 msgid "Join team" msgstr "Rejoindre une équipe" -#: apps/participation/views.py:90 apps/participation/views.py:232 +#: apps/participation/views.py:92 apps/participation/views.py:246 msgid "You are not in a team." msgstr "Vous n'êtes pas dans une équipe." -#: apps/participation/views.py:91 apps/participation/views.py:233 +#: apps/participation/views.py:93 apps/participation/views.py:247 msgid "You don't participate, so you don't have any team." msgstr "Vous ne participez pas, vous n'avez donc pas d'équipe." -#: apps/participation/views.py:130 +#: apps/participation/views.py:132 msgid "You don't participate, so you can't request the validation of the team." msgstr "" "Vous ne participez pas, vous ne pouvez pas demander la validation de " "l'équipe." -#: apps/participation/views.py:133 +#: apps/participation/views.py:135 msgid "The validation of the team is already done or pending." msgstr "La validation de l'équipe est déjà faite ou en cours." -#: apps/participation/views.py:146 +#: apps/participation/views.py:148 msgid "You are not an administrator." msgstr "Vous n'êtes pas administrateur." -#: apps/participation/views.py:149 +#: apps/participation/views.py:151 msgid "This team has no pending validation." msgstr "L'équipe n'a pas de validation en attente." -#: apps/participation/views.py:159 +#: apps/participation/views.py:173 msgid "You must specify if you validate the registration or not." msgstr "Vous devez spécifier si vous validez l'inscription ou non." -#: apps/participation/views.py:216 apps/registration/views.py:213 +#: apps/participation/views.py:230 apps/registration/views.py:213 #, python-brace-format msgid "Photo authorization of {student}.{ext}" msgstr "Autorisation de droit à l'image de {student}.{ext}" -#: apps/participation/views.py:220 +#: apps/participation/views.py:234 #, python-brace-format msgid "Photo authorizations of team {trigram}.zip" msgstr "Autorisations de droit à l'image de l'équipe {trigram}.zip" -#: apps/participation/views.py:242 +#: apps/participation/views.py:256 msgid "The team is not validated yet." msgstr "L'équipe n'est pas encore validée." @@ -440,7 +442,7 @@ msgstr "rôle" msgid "participant" msgstr "participant" -#: apps/registration/forms.py:16 apps/registration/models.py:139 +#: apps/registration/forms.py:16 apps/registration/models.py:143 msgid "coach" msgstr "encadrant" @@ -448,91 +450,97 @@ msgstr "encadrant" msgid "The uploaded file must be a PDF, PNG of JPEG file." msgstr "Le fichier envoyé doit être au format PDF, PNG ou JPEG." -#: apps/registration/models.py:21 +#: apps/registration/models.py:22 msgid "Grant Animath to contact me in the future about other actions" msgstr "" "Autoriser Animath à me recontacter à l'avenir à propos d'autres actions" -#: apps/registration/models.py:26 +#: apps/registration/models.py:27 msgid "email confirmed" msgstr "email confirmé" -#: apps/registration/models.py:30 +#: apps/registration/models.py:31 msgid "Activate your Correspondances account" msgstr "Activez votre compte des Correspondances" -#: apps/registration/models.py:70 +#: apps/registration/models.py:74 msgid "registration" msgstr "inscription" -#: apps/registration/models.py:71 +#: apps/registration/models.py:75 msgid "registrations" msgstr "inscriptions" -#: apps/registration/models.py:90 +#: apps/registration/models.py:94 msgid "12th grade" msgstr "Terminale" -#: apps/registration/models.py:91 +#: apps/registration/models.py:95 msgid "11th grade" msgstr "Première" -#: apps/registration/models.py:92 +#: apps/registration/models.py:96 msgid "10th grade or lower" msgstr "Seconde ou inférieur" -#: apps/registration/models.py:94 +#: apps/registration/models.py:98 msgid "student class" msgstr "classe" -#: apps/registration/models.py:99 +#: apps/registration/models.py:103 msgid "school" msgstr "école" -#: apps/registration/models.py:103 +#: apps/registration/models.py:107 msgid "photo authorization" msgstr "autorisation de droit à l'image" -#: apps/registration/models.py:111 +#: apps/registration/models.py:115 msgid "student" msgstr "étudiant" -#: apps/registration/models.py:119 +#: apps/registration/models.py:123 msgid "student registration" msgstr "inscription d'élève" -#: apps/registration/models.py:120 +#: apps/registration/models.py:124 msgid "student registrations" msgstr "inscriptions d'élève" -#: apps/registration/models.py:134 +#: apps/registration/models.py:138 msgid "professional activity" msgstr "activité professionnelle" -#: apps/registration/models.py:147 +#: apps/registration/models.py:151 msgid "coach registration" msgstr "inscription d'encadrant" -#: apps/registration/models.py:148 +#: apps/registration/models.py:152 msgid "coach registrations" msgstr "inscriptions d'encadrants" -#: apps/registration/models.py:153 +#: apps/registration/models.py:157 msgid "role of the administrator" msgstr "rôle de l'administrateur" -#: apps/registration/models.py:158 +#: apps/registration/models.py:162 msgid "admin" msgstr "admin" -#: apps/registration/models.py:166 +#: apps/registration/models.py:170 msgid "admin registration" msgstr "inscription d'administrateur" -#: apps/registration/models.py:167 +#: apps/registration/models.py:171 msgid "admin registrations" msgstr "inscriptions d'administrateur" +#: apps/registration/tables.py:11 +#, fuzzy +#| msgid "Last name:" +msgid "last name" +msgstr "Nom de famille :" + #: apps/registration/templates/registration/email_validation_complete.html:15 msgid "Your email have successfully been validated." msgstr "Votre email a été validé avec succès." @@ -617,7 +625,7 @@ msgid "Your password has been set. You may go ahead and log in now." msgstr "Votre mot de passe a été changé. Vous pouvez désormais vous connecter." #: apps/registration/templates/registration/password_reset_complete.html:10 -#: templates/base.html:113 templates/base.html:197 templates/base.html:198 +#: templates/base.html:123 templates/base.html:210 templates/base.html:211 #: templates/registration/login.html:7 templates/registration/login.html:8 #: templates/registration/login.html:25 msgid "Log in" @@ -772,14 +780,18 @@ msgstr "Mail de confirmation de l'adresse mail envoyé" msgid "Resend email validation link" msgstr "Renvoyé le lien de validation de l'adresse mail" -#: corres2math/settings.py:150 +#: corres2math/settings.py:151 msgid "English" msgstr "Anglais" -#: corres2math/settings.py:151 +#: corres2math/settings.py:152 msgid "French" msgstr "Français" +#: corres2math/views.py:12 +msgid "Only administrators are allowed to perform a full research." +msgstr "Seuls les administrateurs sont autorisés à effectuer une recherche." + #: templates/400.html:6 msgid "Bad request" msgstr "Requête invalide" @@ -853,23 +865,27 @@ msgstr "Faire un don" msgid "Administration" msgstr "Administration" -#: templates/base.html:104 +#: templates/base.html:105 +msgid "Search..." +msgstr "Chercher ..." + +#: templates/base.html:114 msgid "Return to admin view" msgstr "Retourner à l'interface administrateur" -#: templates/base.html:109 +#: templates/base.html:119 msgid "Register" msgstr "S'inscrire" -#: templates/base.html:125 +#: templates/base.html:135 msgid "My account" msgstr "Mon compte" -#: templates/base.html:128 +#: templates/base.html:138 msgid "Log out" msgstr "Déconnexion" -#: templates/base.html:144 +#: templates/base.html:154 #, python-format msgid "" "Your email address is not validated. Please click on the link you received " @@ -880,10 +896,14 @@ msgstr "" "avez reçu par mail. Vous pouvez renvoyer un mail en cliquant sur ce lien." -#: templates/base.html:167 +#: templates/base.html:177 msgid "Contact us" msgstr "Nous contacter" +#: templates/base.html:208 +msgid "Search results" +msgstr "Résultats de la recherche" + #: templates/registration/logged_out.html:8 msgid "Thanks for spending some quality time with the Web site today." msgstr "Merci d'avoir utilisé la plateforme des Correspondances." @@ -904,3 +924,15 @@ msgstr "" #: templates/registration/login.html:23 msgid "Forgotten your password or username?" msgstr "Mot de passe oublié ?" + +#: templates/search/search.html:6 templates/search/search.html:11 +msgid "Search" +msgstr "Chercher" + +#: templates/search/search.html:16 +msgid "Results" +msgstr "Résultats" + +#: templates/search/search.html:26 +msgid "No results found." +msgstr "Aucun résultat." diff --git a/requirements.txt b/requirements.txt index 8e2482a..5a9bb99 100644 --- a/requirements.txt +++ b/requirements.txt @@ -3,6 +3,7 @@ django-bootstrap-datepicker-plus django-crispy-forms django-extensions django-filter +django-haystack django-mailer django-polymorphic django-tables2 @@ -10,4 +11,5 @@ djangorestframework django-rest-polymorphic ptpython python-magic -gunicorn \ No newline at end of file +gunicorn +whoosh \ No newline at end of file diff --git a/templates/base.html b/templates/base.html index b455b36..9a34f40 100644 --- a/templates/base.html +++ b/templates/base.html @@ -99,6 +99,16 @@ {% endif %}