From 0e6eda5fa8e3a10c6007d2f73b52e2c722a50066 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Valentin=20Mogu=C3=A9rou?= Date: Fri, 3 May 2024 02:40:46 +0200 Subject: [PATCH] + favicon + all variable names translated to English + Better marketplace --- colloscope/admin.py | 40 +- colloscope/icalexport.py | 32 +- ...cee_school_rename_creneau_slot_and_more.py | 78 ++ colloscope/models.py | 399 +++--- colloscope/pdfexport.py | 104 +- colloscope/table.py | 20 +- colloscope/templates/dashboard.html | 44 +- colloscope/templates/marketplace.html | 33 +- ...{choix_profil.html => select_profile.html} | 0 colloscope/templates/table.html | 40 +- ..._non_associe.html => unbound_profile.html} | 0 colloscope/urls.py | 8 +- colloscope/views.py | 277 ++-- kholles_web/urls.py | 4 +- locale/en/LC_MESSAGES/django.po | 1241 +++++++++++++++++ locale/fr/LC_MESSAGES/django.po | 1241 +++++++++++++++++ scrape_csv.py | 22 +- static/favicon.ico | Bin 0 -> 25354 bytes templates/404.html | 2 +- templates/base.html | 2 +- 20 files changed, 3097 insertions(+), 490 deletions(-) create mode 100644 colloscope/migrations/0012_rename_lycee_school_rename_creneau_slot_and_more.py rename colloscope/templates/{choix_profil.html => select_profile.html} (100%) rename colloscope/templates/{profil_non_associe.html => unbound_profile.html} (100%) create mode 100644 locale/en/LC_MESSAGES/django.po create mode 100644 locale/fr/LC_MESSAGES/django.po create mode 100644 static/favicon.ico diff --git a/colloscope/admin.py b/colloscope/admin.py index 46e9ba3..4be14bd 100644 --- a/colloscope/admin.py +++ b/colloscope/admin.py @@ -2,32 +2,34 @@ from django.contrib import admin from colloscope.models import * -@admin.register(Lycee) +@admin.register(School) class LyceeAdmin(admin.ModelAdmin): - list_display = ('uai', 'libelle', 'vacances') + list_display = ('uai', 'description', 'vacation') -admin.site.register(Classe) -admin.site.register(Periode) -admin.site.register(Matiere) -admin.site.register(Critere) -admin.site.register(Groupe) -admin.site.register(Etudiant) -admin.site.register(Appartenance) +admin.site.register(Class) +admin.site.register(Term) +admin.site.register(Subject) +admin.site.register(GroupType) +admin.site.register(Group) +admin.site.register(Student) +admin.site.register(Member) admin.site.register(Colleur) -@admin.register(Creneau) -class CreneauAdmin(admin.ModelAdmin): - list_display = ('matiere', 'colleur', "periode", 'view_jour', "heure", "duree") - list_filter = ("matiere", "colleur", "periode") - def view_jour(self, obj): +@admin.register(Slot) +class SlotAdmin(admin.ModelAdmin): + list_display = ('subject', 'colleur', "term", 'view_day', "time", "duration") + list_filter = ("subject", "colleur", "term") + + def view_day(self, obj): jours = ["dimanche", "lundi", "mardi", "mercredi", "jeudi", "vendredi", "samedi"] return jours[obj.jour] - view_jour.short_description = 'Jour' + view_day.short_description = 'Day' -admin.site.register(Rotation) -admin.site.register(Amendement) -admin.site.register(Profil) -admin.site.register(LienCalendrier) + +admin.site.register(Colle) +admin.site.register(Swap) +admin.site.register(Profile) +admin.site.register(CalendarLink) diff --git a/colloscope/icalexport.py b/colloscope/icalexport.py index 60a81bf..912fab0 100644 --- a/colloscope/icalexport.py +++ b/colloscope/icalexport.py @@ -10,11 +10,11 @@ from .create_calendar import get_calendar LOCAL_TZ = "Europe/Paris" -def emailize(nom, prenom=None): - if prenom is not None: +def emailize(nom, first_name=None): + if first_name is not None: return "{}.{}@example.com" \ .format( - prenom.replace(" ", "_").lower(), + first_name.replace(" ", "_").lower(), nom.replace(" ", "_").lower() ) else: @@ -22,41 +22,41 @@ def emailize(nom, prenom=None): .format(nom.replace(" ", "_").lower()) -def to_calendar(etudiant, periode, include_EDT: bool = True): +def to_calendar(student, term, include_EDT: bool = True): p = path.abspath('./static/Base_Calendar.ics') with open(p) as f: cal = Calendar.from_ical(f.read()) - rotations = periode.query_rotations_etudiant(etudiant) + colles = term.query_colles_of_student(student) - for rotation in rotations: + for colle in colles: event = Event() - summary = f"Colle {rotation.creneau.matiere} ({rotation.creneau.colleur})" + summary = f"Colle {colle.slot.subject} ({colle.slot.colleur})" event.add("summary", summary) - start = rotation.datetime() - fin = start + rotation.creneau.duree + start = colle.datetime() + fin = start + colle.slot.duration event.add("dtstart", start, parameters={"tzid": LOCAL_TZ}) event.add("dtend", fin, parameters={"tzid": LOCAL_TZ}) event.add("dtstamp", datetime.now()) event.add("uid", str(uuid4())) - event.add("location", f"{rotation.creneau.salle} ({rotation.creneau.periode.classe.lycee})") - event.add("categories", "COLLE-" + str(rotation.creneau.matiere)) + event.add("location", f"{colle.slot.room} ({colle.slot.term.cls.school})") + event.add("categories", "COLLE-" + str(colle.slot.subject)) - description = f"Groupes: {','.join(str(groupe) for groupe in rotation.groupes.all())}" + description = f"Groupes: {','.join(str(group) for group in colle.groups.all())}" event.add("description", description) - organizer = vCalAddress(f"mailto:{emailize(rotation.creneau.colleur.nom)}") - organizer.params["cn"] = vText(str(rotation.creneau.colleur)) + organizer = vCalAddress(f"mailto:{emailize(colle.slot.colleur.name)}") + organizer.params["cn"] = vText(str(colle.slot.colleur)) organizer.params["role"] = vText("Colleur") event.add("organizer", organizer) - for e in rotation.groupe_effectif(): - attendee = vCalAddress("mailto:{emailize(e.nom, prenom=e.prenom)}") + for e in colle.final_group(): + attendee = vCalAddress("mailto:{emailize(e.name, first_name=e.first_name)}") attendee.params["role"] = vText("Etudiant") attendee.params["cn"] = vText(str(e)) diff --git a/colloscope/migrations/0012_rename_lycee_school_rename_creneau_slot_and_more.py b/colloscope/migrations/0012_rename_lycee_school_rename_creneau_slot_and_more.py new file mode 100644 index 0000000..7887640 --- /dev/null +++ b/colloscope/migrations/0012_rename_lycee_school_rename_creneau_slot_and_more.py @@ -0,0 +1,78 @@ +# Generated by Django 5.0.4 on 2024-05-02 19:09 + +import django.db.models.deletion +from django.conf import settings +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('colloscope', '0011_alter_liencalendrier_code'), + migrations.swappable_dependency(settings.AUTH_USER_MODEL), + ] + + operations = [ + migrations.RenameModel( + old_name='Lycee', + new_name='School', + ), + migrations.RenameModel( + old_name='Classe', + new_name='Class', + ), + migrations.RenameModel( + old_name='Periode', + new_name='Term', + ), + migrations.RenameModel( + old_name='Matiere', + new_name='Subject', + ), + migrations.RenameModel( + old_name='Critere', + new_name='GroupType', + ), + migrations.RenameModel( + old_name='Groupe', + new_name='Group', + ), + migrations.RenameModel( + old_name='Etudiant', + new_name='Student', + ), + migrations.RenameModel( + old_name='Appartenance', + new_name='Member', + ), + migrations.RenameModel( + old_name='Creneau', + new_name='Slot', + ), + migrations.RenameModel( + old_name='Rotation', + new_name='Colle', + ), + migrations.RenameModel( + old_name='Amendement', + new_name='Swap', + ), + migrations.RenameModel( + old_name='Profil', + new_name='Profile', + ), + migrations.RenameModel( + old_name='LienCalendrier', + new_name='CalendarLink', + ), + migrations.AlterField( + model_name='colle', + name='groupes', + field=models.ManyToManyField(to='colloscope.group'), + ), + migrations.AlterField( + model_name='member', + name='groupe', + field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='colloscope.group'), + ), + ] diff --git a/colloscope/models.py b/colloscope/models.py index 8942c5b..958c71a 100644 --- a/colloscope/models.py +++ b/colloscope/models.py @@ -3,17 +3,16 @@ from datetime import date, datetime, timedelta from asgiref.sync import async_to_sync from pytz import timezone -import asyncio import aiohttp from django.db import models -from django.db.models import F, Q, Count +from django.db.models import F, Q, Count, QuerySet from django.contrib.auth.models import User from django.conf import settings from discord import Webhook -calendrier = { +calendar = { "C": [ (date(2023, 10, 21), date(2023, 11, 6)), (date(2023, 12, 23), date(2024, 1, 8)), @@ -24,43 +23,43 @@ calendrier = { } -class Lycee(models.Model): +class School(models.Model): uai = models.CharField(max_length=10) - libelle = models.CharField(max_length=100) - vacances = models.CharField(max_length=1) + description = models.CharField(max_length=100) + vacation = models.CharField(max_length=1) - def __str__(self): - return self.libelle + def __str__(self) -> str: + return self.description -class Classe(models.Model): - lycee = models.ForeignKey(Lycee, on_delete=models.CASCADE) - libelle = models.CharField(max_length=20) - annee = models.IntegerField() - jour_zero = models.DateField() +class Class(models.Model): + school = models.ForeignKey(School, on_delete=models.CASCADE) + description = models.CharField(max_length=20) + year = models.IntegerField() + day_zero = models.DateField() - def no_semaine(self, jour): + def week_number(self, day: date) -> int: """ Entrées : - self - - jour (datetime.date) + - day (datetime.date) Sortie : - - Le numéro de la semaine contenant jour, sans compter les vacances. - Renvoie un numéro non spécifiée si le jour est pendant une période de vacances + - Le numéro de la semaine contenant day, sans compter les vacation. + Renvoie un numéro non spécifiée si le day est pendant une période de vacation """ - zone = self.lycee.vacances - vacances = calendrier[zone] - jour0 = self.jour_zero + zone = self.school.vacation + vacation = calendar[zone] + day0 = self.day_zero - n = 1 + (jour - jour0).days // 7 - for debut, fin in vacances: - if jour > debut: + n = 1 + (day - day0).days // 7 + for debut, fin in vacation: + if day > debut: n -= round((fin - debut) / timedelta(weeks=1)) return n - def no_aujourdhui(self): + def today_number(self) -> int: """ Entrée: - self @@ -69,9 +68,9 @@ class Classe(models.Model): - Le numéro de la semaine courante """ - return self.no_semaine(date.today()) + return self.week_number(date.today()) - def date_debut_sem(self, n): + def week_beginning_date(self, n: int) -> date: """ Entrée: - self @@ -81,59 +80,57 @@ class Classe(models.Model): - Le date du lundi de la semaine n """ - zone = self.lycee.vacances - vacances = calendrier[zone] - jour0 = self.jour_zero + zone = self.school.vacation + vacation = calendar[zone] + day0 = self.day_zero - jour = jour0 + (n - 1) * timedelta(weeks=1) + day = day0 + (n - 1) * timedelta(weeks=1) - for debut, fin in vacances: - if jour >= debut: - jour += round((fin - debut) / timedelta(weeks=1)) * timedelta(weeks=1) + for begin, end in vacation: + if day >= begin: + day += round((end - begin) / timedelta(weeks=1)) * timedelta(weeks=1) - return jour + return day - def periode(self, jour): + def term_of_date(self, day: date): """ Entrées : - self - - jour (datetime.date) + - day (datetime.date) Sortie : - - La période (si elle existe et est unique) contenant jour + - La période (si elle existe et est unique) contenant day Exceptions: - - Le jour n'est pas dans une période - - Le jour est au chevauchement de deux périodes + - Le day n'est pas dans une période + - Le day est au chevauchement de deux périodes """ - return Periode.objects.get(classe=self, debut__lte=jour, fin__gte=jour) + return Term.objects.get(cls=self, debut__lte=day, fin__gte=day) - def periode_actuelle(self): - #return self.periode(date.today()) // ne fonctionne pas entre les périodes + def current_term(self): """ On prend la période non révolue la plus récente """ - return Periode.objects \ - .filter(classe=self, fin__gte=date.today()) \ - .order_by("-debut") \ - .first() + return (Term.objects + .filter(cls=self, end__gte=date.today()) + .order_by("-begin") + .first()) def __str__(self): - return f"{self.libelle} ({self.lycee.libelle})" + return f"{self.description} ({self.lycee.description})" -class Periode(models.Model): - classe = models.ForeignKey(Classe, on_delete=models.CASCADE) - #critere_colle = models.ForeignKey(Critere, on_delete=models.SET_NULL, null=True) - libelle = models.CharField(max_length=100) - debut = models.DateField() - fin = models.DateField() +class Term(models.Model): + cls = models.ForeignKey(Class, on_delete=models.CASCADE) + description = models.CharField(max_length=100) + begin = models.DateField() + end = models.DateField() class Meta: - ordering = ["debut"] + ordering = ["begin"] - def range_semaines(self): + def range_weeks(self) -> range: """ Entrée: - self @@ -141,284 +138,284 @@ class Periode(models.Model): Sortie: - Un range des numéros de semaine """ - return range(self.classe.no_semaine(self.debut), self.classe.no_semaine(self.fin) + 1) + return range(self.cls.week_number(self.begin), self.cls.week_number(self.end) + 1) - def query_rotations(self): - return (Rotation.objects - .select_related("creneau", "creneau__periode") - .prefetch_related("amendement_set") - .filter(creneau__periode=self) - .annotate(adt_plus=Count("amendement", filter=Q(amendement__est_positif=1))) - .annotate(adt_minus=Count("amendement", filter=Q(amendement__est_positif=0))) - .annotate(volume=F("creneau__capacite") + F("adt_plus") - F("adt_minus"))) + def query_colles(self) -> QuerySet: + return (Colle.objects + .select_related("slot", "slot__term") + .prefetch_related("swap_set") + .filter(slot__term=self) + .annotate(adt_plus=Count("swap", filter=Q(swap__enroll=1))) + .annotate(adt_minus=Count("swap", filter=Q(swap__enroll=0))) + .annotate(volume=F("slot__capacity") + F("adt_plus") - F("adt_minus"))) - def query_rotations_etudiant(self, etudiant): - return (Rotation.objects - .select_related("creneau", "creneau__periode") - .prefetch_related("amendement_set") - .filter(creneau__periode=self) - .filter((Q(groupes__etudiant=etudiant) - & ~Q(amendement__est_positif=0, amendement__etudiant=etudiant)) - | Q(amendement__est_positif=1, amendement__etudiant=etudiant)) - .annotate(adt_plus=Count("amendement", filter=Q(amendement__est_positif=1))) - .annotate(adt_minus=Count("amendement", filter=Q(amendement__est_positif=0))) - .annotate(volume=F("creneau__capacite") + F("adt_plus") - F("adt_minus"))) + def query_colles_of_student(self, student) -> QuerySet: + return (Colle.objects + .select_related("slot", "slot__term") + .prefetch_related("swap_set") + .filter(slot__term=self) + .filter((Q(groups__student=student) + & ~Q(swap__enroll=0, swap__student=student)) + | Q(swap__enroll=1, swap__student=student)) + .annotate(adt_plus=Count("swap", filter=Q(swap__enroll=1))) + .annotate(adt_minus=Count("swap", filter=Q(swap__enroll=0))) + .annotate(volume=F("slot__capacity") + F("adt_plus") - F("adt_minus"))) - def query_rotations_not_full(self): - return (self.query_rotations() - .filter(volume__lt=F("creneau__capacite"), date__gte=date.today())) + def query_colles_not_full(self) -> QuerySet: + return (self.query_colles() + .filter(volume__lt=F("slot__capacity"), date__gte=date.today())) - def __str__(self): - return self.libelle + def __str__(self) -> str: + return self.description -class Matiere(models.Model): - classe = models.ForeignKey(Classe, on_delete=models.CASCADE) - libelle = models.CharField(max_length=100) +class Subject(models.Model): + cls = models.ForeignKey(Class, on_delete=models.CASCADE) + description = models.CharField(max_length=100) code = models.CharField(max_length=20) def __str__(self): - return self.libelle + return self.description -class Critere(models.Model): - periode = models.ForeignKey(Periode, on_delete=models.CASCADE) - libelle = models.CharField(max_length=100) +class GroupType(models.Model): + term = models.ForeignKey(Term, on_delete=models.CASCADE) + description = models.CharField(max_length=100) def __str__(self): - return self.libelle + return self.description -class Groupe(models.Model): - #class Meta: - # ordering=[F("periode").classe.libelle, F("periode").libelle, "libelle"] +class Group(models.Model): + class Meta: + ordering = ["term__cls__description", "term__description", "description"] - periode = models.ForeignKey(Periode, on_delete=models.CASCADE) - critere = models.ForeignKey(Critere, null=True, on_delete=models.CASCADE) - libelle = models.CharField(max_length=100) + term = models.ForeignKey(Term, on_delete=models.CASCADE) + type = models.ForeignKey(GroupType, null=True, on_delete=models.CASCADE) + description = models.CharField(max_length=100) - membres = models.ManyToManyField("Etudiant", through="Appartenance") + members = models.ManyToManyField("Student", through="Member") def __str__(self): - return self.libelle + return self.description - #def get_colles(self): - # return Rotation.objects.filter(creneau__periode=self.periode, - # Q(groupes=self) || Q(a)).order_by("date") + """def get_colles(self): + return Rotation.objects.filter(slot__term=self.term, + Q(groupes=self) || Q(a)).order_by("date") def get_colles_par_sem(self): - semaines = ((s, self.periode.classe.date_debut_sem(s)) for s in self.periode.range_semaines()) + semaines = ((s, self.term.cls.week_beginning_date(s)) for s in self.term.range_semaines()) colles_flat = self.get_colles() return [ (sem, lundi, colles_flat.filter(date__gte=lundi, date__lt=lundi + timedelta(weeks=1))) for sem, lundi in semaines - ] + ]""" -class Etudiant(models.Model): +class Student(models.Model): class Meta: - ordering = ["classe", "nom", "prenom"] + ordering = ["cls", "last_name", "first_name"] - classe = models.ForeignKey(Classe, on_delete=models.CASCADE) - prenom = models.CharField(max_length=100) - nom = models.CharField(max_length=100) - groupes = models.ManyToManyField("Groupe", through="Appartenance") + cls = models.ForeignKey(Class, on_delete=models.CASCADE) + first_name = models.CharField(max_length=100) + last_name = models.CharField(max_length=100) + groups = models.ManyToManyField("Group", through="Member") - def appartient(self, groupe): + def is_member(self, group): """ Renvoie si self appartient au groupe. """ - return groupe.membres.contains(self) + return group.members.contains(self) - def groupe_du_critere(self, periode, critere): + def group_of_type(self, term, type_): """ Renvoie le groupe du critère auquel self appartient. """ - if isinstance(critere, str): - critere = Critere.objects.get(periode=periode, libelle=critere) + if isinstance(type_, str): + type_ = GroupType.objects.get(term=term, description=type_) - return Appartenance.objects.get(groupe__periode=periode, etudiant=self, groupe__critere=critere).groupe + return Member.objects.get(group__term=term, student=self, group__type=type_).group - def groupe_de_colle(self, periode): + def colle_group(self, term): """ - Renvoie le groupe de colle de self pendant periode. + Renvoie le groupe de colle de self pendant term. """ - return self.groupe_du_critere(periode, "colle") + return self.group_of_type(term, "colle") def __str__(self): - return f"{self.prenom} {self.nom}" + return f"{self.first_name} {self.last_name}" -class Appartenance(models.Model): - etudiant = models.ForeignKey(Etudiant, on_delete=models.CASCADE) - groupe = models.ForeignKey(Groupe, on_delete=models.CASCADE) +class Member(models.Model): + student = models.ForeignKey(Student, on_delete=models.CASCADE) + group = models.ForeignKey(Group, on_delete=models.CASCADE) class Colleur(models.Model): - civilite = models.CharField(max_length=1) - nom = models.CharField(max_length=100) + gender = models.CharField(max_length=1) + name = models.CharField(max_length=100) def __str__(self): - if self.civilite == "M": - return f"M. {self.nom}" + if self.gender == "M": + return f"M. {self.name}" else: - return f"Mme {self.nom}" + return f"Mme {self.name}" def get_classes(self): - return (x.periode.classe for x in Creneau.objects.filter(colleur=self).select_related("periode__classe")) + return (x.term_of_date.cls for x in Slot.objects.filter(colleur=self).select_related("term__cls")) -class Creneau(models.Model): - periode = models.ForeignKey(Periode, on_delete=models.CASCADE) - jour = models.IntegerField() - heure = models.TimeField() - duree = models.DurationField() - salle = models.CharField(max_length=20) - matiere = models.ForeignKey(Matiere, on_delete=models.CASCADE) +class Slot(models.Model): + term = models.ForeignKey(Term, on_delete=models.CASCADE) + day = models.IntegerField() + time = models.TimeField() + duration = models.DurationField() + room = models.CharField(max_length=20) + subject = models.ForeignKey(Subject, on_delete=models.CASCADE) colleur = models.ForeignKey(Colleur, on_delete=models.CASCADE) - est_colle = models.BooleanField() - capacite = models.IntegerField() + type = models.ForeignKey(GroupType, on_delete=models.CASCADE) + capacity = models.IntegerField() class Meta: - verbose_name_plural = "Creneaux" + verbose_name_plural = "slots" def __str__(self): - jours = ["dimanche", "lundi", "mardi", "mercredi", "jeudi", "vendredi", "samedi"] + days = ["dimanche", "lundi", "mardi", "mercredi", "jeudi", "vendredi", "samedi"] - return f"Colle {self.matiere} avec {self.colleur} {jours[self.jour]} {self.heure}" + return f"Colle {self.subject} avec {self.colleur} {days[self.day]} {self.time}" -class Rotation(models.Model): +class Colle(models.Model): class Meta: - ordering = ["creneau__periode__classe", "creneau__matiere__libelle", "creneau__colleur__nom", "date", - "creneau__heure"] + ordering = ["slot__term__cls", "slot__subject__description", "slot__colleur__name", "date", + "slot__time"] - creneau = models.ForeignKey(Creneau, on_delete=models.CASCADE) - groupes = models.ManyToManyField(Groupe) + slot = models.ForeignKey(Slot, on_delete=models.CASCADE) + groups = models.ManyToManyField(Group) date = models.DateField() - def groupe_initial(self): + def initial_group(self): """ - Renvoie les étudiants inscrits à la colle sans prendre en compte les amendements. + Renvoie les étudiants inscrits à la colle sans prendre en compte les swaps. """ - return Etudiant.objects.filter(id__in=Appartenance.objects.filter(groupe__in=self.groupes.all())) + return Student.objects.filter(id__in=Member.objects.filter(groupe__in=self.groups.all())) - def groupe_effectif(self): + def final_group(self): """ - Renvoie les étudiants inscrits à la colle en tenant compte des amendements. + Renvoie les étudiants inscrits à la colle en tenant compte des swaps. """ - amendements = Amendement.objects.filter(rotation=self) + swaps = Swap.objects.filter(colle=self) - return Etudiant.objects.filter( - (Q(id__in=Appartenance.objects.filter(groupe__in=self.groupes.all())) - | Q(id__in=amendements.filter(est_positif=True).values("etudiant_id"))) - & ~Q(id__in=amendements.filter(est_positif=False).values("etudiant_id")) + return Student.objects.filter( + (Q(id__in=Member.objects.filter(group__in=self.groups.all())) + | Q(id__in=swaps.filter(enroll=True).values("student_id"))) + & ~Q(id__in=swaps.filter(enroll=False).values("student_id")) ) - def effectif(self): + def volume(self): """ - Renvoie le nombre d'étudiants inscrits à la colle en tenant compte des amendements. + Renvoie le nombre d'étudiants inscrits à la colle en tenant compte des swaps. """ - n_base = sum(len(groupe.membres.count()) for groupe in self.groupes.all()) - n_plus = len(Amendement.objects.filter(est_positif=True, rotation=self)) - n_moins = len(Amendement.objects.filter(est_positif=False, rotation=self)) + n_base = sum(len(group.members.count()) for group in self.groups.all()) + n_plus = len(Swap.objects.filter(enroll=True, colle=self)) + n_moins = len(Swap.objects.filter(enroll=False, colle=self)) return n_base + n_plus - n_moins - def est_pleine(self): + def is_full(self): """ Renvoie si la colle est pleine. """ - eff = self.effectif() - return eff >= self.creneau.capacite + eff = self.volume() + return eff >= self.slot.capacity - def est_modifiee(self): + def is_edited(self): """ Renvoie si la colle a été amendée. """ - return Amendement.objects.filter(rotation=self).exists() + return Swap.objects.filter(colle=self).exists() - def amender(self, etudiant, est_positif, notifier=False): + def amend(self, student, enroll, notify=False): """ - Amende la colle en (des)inscrivant etudiant à la colle self, selon est_positif. + Amende la colle en (des)inscrivant student à la colle self, selon enroll. """ - if Amendement.objects.filter(rotation=self, etudiant=etudiant, est_positif=est_positif).exists(): + if Swap.objects.filter(colle=self, student=student, enroll=enroll).exists(): raise Exception("Duplication") - elif Amendement.objects.filter(rotation=self, etudiant=etudiant, est_positif=not est_positif).exists(): - # les amendements complémentaires s'annulent - Amendement.objects.get(rotation=self, etudiant=etudiant, est_positif=not est_positif).delete() - elif est_positif and any(etudiant.appartient(groupe) for groupe in self.groupes.all()): + elif Swap.objects.filter(colle=self, student=student, enroll=not enroll).exists(): + # les swaps complémentaires s'annulent + Swap.objects.get(colle=self, student=student, enroll=not enroll).delete() + elif enroll and any(student.is_member(group) for group in self.groups.all()): # on ne peut pas s'ajouter si on est dans le groupe de base raise Exception("Vous êtes déjà dans le groupe") - elif not est_positif and all(not etudiant.appartient(groupe) for groupe in self.groupes.all()): + elif not enroll and all(not student.is_member(group) for group in self.groups.all()): raise Exception("Vous n'êtes pas dans le groupe") - elif est_positif and self.est_pleine(): + elif enroll and self.is_full(): raise Exception("Capacité dépassée") else: - amendement = Amendement(rotation=self, etudiant=etudiant, est_positif=est_positif) - amendement.save() + swap = Swap(colle=self, student=student, enroll=enroll) + swap.save() - if notifier: - func = async_to_sync(amendement.notifier) - func() + #if notify: + # func = async_to_sync(swap.notify) + # func() def __str__(self): - return f"{self.creneau} le {self.date} avec groupes {'+'.join(str(groupe) for groupe in self.groupes.all())}" + return f"{self.slot} le {self.date} avec groupes {'+'.join(str(groupe) for groupe in self.groups.all())}" def datetime(self): - return datetime.combine(self.date, self.creneau.heure, tzinfo=timezone("Europe/Paris")) + return datetime.combine(self.date, self.slot.time, tzinfo=timezone("Europe/Paris")) -class Amendement(models.Model): - est_positif = models.BooleanField() - rotation = models.ForeignKey(Rotation, on_delete=models.CASCADE) - etudiant = models.ForeignKey(Etudiant, on_delete=models.CASCADE) +class Swap(models.Model): + enroll = models.BooleanField() + colle = models.ForeignKey(Colle, on_delete=models.CASCADE) + student = models.ForeignKey(Student, on_delete=models.CASCADE) - async def notifier(self): + async def notify(self): async with aiohttp.ClientSession() as session: webhook = Webhook.from_url(settings.DISCORD_NOTIFY_WEBHOOK_URL, session=session) - if self.est_positif: - await webhook.send(f"Colle réservée : {self.rotation}", username=settings.DISCORD_NOTIFY_WEBHOOK_USERNAME) + if self.enroll: + await webhook.send(f"Colle réservée : {self.colle}", username=settings.DISCORD_NOTIFY_WEBHOOK_USERNAME) else: - await webhook.send(f"Colle libérée : {self.rotation}", username=settings.DISCORD_NOTIFY_WEBHOOK_USERNAME) + await webhook.send(f"Colle libérée : {self.colle}", username=settings.DISCORD_NOTIFY_WEBHOOK_USERNAME) -class Profil(models.Model): - utilisateur = models.OneToOneField(User, on_delete=models.CASCADE, primary_key=True) - etudiant = models.ForeignKey(Etudiant, null=True, blank=True, on_delete=models.SET_NULL) +class Profile(models.Model): + user = models.OneToOneField(User, on_delete=models.CASCADE, primary_key=True) + student = models.ForeignKey(Student, null=True, blank=True, on_delete=models.SET_NULL) colleur = models.ForeignKey(Colleur, null=True, blank=True, on_delete=models.SET_NULL) def __str__(self): - return f"Profil {self.utilisateur} : {self.etudiant} ; {self.colleur}" + return f"Profil {self.user} : {self.student} ; {self.colleur}" @staticmethod def from_request(request, preprocess=lambda query: query): user = request.user session = request.session - match session.get("profil"): - case "etudiant": - profil = preprocess(Profil.objects.filter(utilisateur=user)).get() - return profil.etudiant + match session.get("profile"): + case "student": + profil = preprocess(Profile.objects.filter(user=user)).get() + return profil.student case "colleur": - profil = preprocess(Profil.objects.filter(utilisateur=user)).get() + profil = preprocess(Profile.objects.filter(user=user)).get() return profil.colleur case _: raise ValueError("profil non choisi") -class LienCalendrier(models.Model): - code = models.CharField(max_length=32, unique=True) - etudiant = models.ForeignKey(Etudiant, on_delete=models.CASCADE) - periode = models.ForeignKey(Periode, on_delete=models.CASCADE) +class CalendarLink(models.Model): + key = models.CharField(max_length=32, unique=True) + student = models.ForeignKey(Student, on_delete=models.CASCADE) + term = models.ForeignKey(Term, on_delete=models.CASCADE) class Meta: constraints = [ models.UniqueConstraint( - fields=['etudiant', 'periode'], name='unique_etudiant_periode_combination' + fields=['student', 'term'], name='unique_student_term_combination' ) ] diff --git a/colloscope/pdfexport.py b/colloscope/pdfexport.py index 18fd9cc..4d249ec 100644 --- a/colloscope/pdfexport.py +++ b/colloscope/pdfexport.py @@ -1,14 +1,16 @@ from datetime import date, timedelta +from django.shortcuts import redirect +from django.http import HttpResponse +from fpdf import FPDF + from colloscope.models import * -from fpdf import FPDF - class PDF(FPDF): - def liste_eleves(self, periode): - classe = periode.classe - etudiants = Etudiant.objects.filter(classe=classe) + def liste_eleves(self, term): + cls = term.cls + students = Student.objects.filter(cls=cls) with self.table( align="RIGHT", @@ -19,27 +21,27 @@ class PDF(FPDF): for th in ("Nom", "Prénom", "Grp.", "TD",): #"LV1", "LV2"): header.cell(th) - for etu in etudiants: + for etu in students: row = table.row() - row.cell(etu.nom.upper()) # Nom - row.cell(etu.prenom) # Prénom - row.cell(etu.groupe_de_colle(periode).libelle) # Groupe - row.cell(etu.groupe_du_critere(periode, "td").libelle) + row.cell(etu.last_name.upper()) # Nom + row.cell(etu.first_name) # Prénom + row.cell(etu.colle_group(term).description) # Groupe + row.cell(etu.group_of_type(term, "td").description) #row.cell("??") # LV1 #row.cell("??") # LV2 - def table_colloscope(self, periode, heading=True, est_colle=True): - semaines = periode.range_semaines() - lundis = [ periode.classe.date_debut_sem(n) for n in semaines ] - creneaux = Creneau.objects.filter(periode=periode, est_colle=est_colle) - jours = ["dimanche", "lundi", "mardi", "mercredi", "jeudi", "vendredi", "samedi"] + def table_colloscope(self, term, heading=True, type="colle"): + weeks = term.range_weeks() + lundis = [term.cls.week_beginning_date(n) for n in weeks] + slots = Slot.objects.filter(term=term, type__description=type) + weekdays = ["dimanche", "lundi", "mardi", "mercredi", "jeudi", "vendredi", "samedi"] with self.table( align="LEFT", width=190, line_height=3, - col_widths=(2, 1, 1, 3, 1, *(1,)*len(semaines)), + col_widths=(2, 1, 1, 3, 1, *(1,)*len(weeks)), num_heading_rows=2 if heading else 0, first_row_as_headings=heading) as table: @@ -48,7 +50,7 @@ class PDF(FPDF): for th in ("Matière", "Jour", "Heure", "Colleur", "Salle"): header.cell(th, align="CENTER", rowspan=2) - for sem in semaines: + for sem in weeks: header.cell(str(sem), align="CENTER") header2 = table.row() @@ -56,38 +58,38 @@ class PDF(FPDF): header2.cell(lundi.strftime("%d/%m/%y"), align="CENTER") - for i, c in enumerate(creneaux): - matiere = c.matiere - jour = c.jour - heure = c.heure + for i, c in enumerate(slots): + subject = c.subject + day = c.day + time = c.time colleur = c.colleur - salle = c.salle + room = c.room row = table.row() - row.cell(matiere.libelle) - row.cell(jours[jour]) - row.cell(heure.strftime("%H:%M")) - row.cell("{} {}".format("M." if colleur.civilite=="M" else "Mme", colleur.nom.upper())) - row.cell(salle) + row.cell(subject.description) + row.cell(weekdays[day]) + row.cell(time.strftime("%H:%M")) + row.cell("{} {}".format("M." if colleur.gender=="M" else "Mme", colleur.name.upper())) + row.cell(room) - for s in semaines: - lundi = periode.classe.date_debut_sem(s) + for s in weeks: + lundi = term.cls.week_beginning_date(s) - if Rotation.objects.filter(creneau=c, date__gte=lundi, date__lt=lundi+timedelta(weeks=1)).exists(): - r = Rotation.objects.get(creneau=c, date__gte=lundi, date__lt=lundi+timedelta(weeks=1)) - groupes = r.groupes - content = ", ".join(g.libelle for g in groupes.all()) + if Colle.objects.filter(slot=c, date__gte=lundi, date__lt=lundi + timedelta(weeks=1)).exists(): + r = Colle.objects.get(slot=c, date__gte=lundi, date__lt=lundi + timedelta(weeks=1)) + groups = r.groups + content = ", ".join(g.description for g in groups.all()) - with self.local_context(fill_color=(255, 100, 100) if r.est_modifiee() else None): + with self.local_context(fill_color=(255, 100, 100) if r.is_edited() else None): row.cell(content, align="CENTER") else: row.cell() -def generate(periode): +def generate(term): pdf = PDF(orientation="landscape", format="a4") pdf.set_font("helvetica", size=6) - titre = f"Colloscope {periode.classe.libelle} {periode.libelle}" + titre = f"Colloscope {term.cls.description} {term.description}" pdf.set_title(titre) pdf.set_author("colles.mp2i-vms.fr") @@ -97,39 +99,39 @@ def generate(periode): pdf.set_line_width(0.1) base_y = pdf.t_margin + 10 pdf.set_y(base_y) - pdf.liste_eleves(periode) + pdf.liste_eleves(term) pdf.set_y(base_y) - pdf.table_colloscope(periode) + pdf.table_colloscope(term) pdf.y += 3 - pdf.table_colloscope(periode, heading=False, est_colle=False) + pdf.table_colloscope(term, heading=False, type="td") return pdf def handle(request): try: - etudiant = Profil.from_request( + student = Profile.from_request( request, preprocess=lambda query: query \ - .select_related("etudiant__classe") \ - .prefetch_related("etudiant__classe__periode_set") + .select_related("student__cls") \ + .prefetch_related("student__cls__term_set") ) except ValueError: - return redirect("colloscope.choix_profil") + return redirect("colloscope.select_profile") - if not isinstance(etudiant, Etudiant): + if not isinstance(student, Student): return HttpResponse("pas encore supporté") - periode_str = request.GET.get("periode") - if periode_str is None: - periode = etudiant.classe.periode_actuelle() + term_str = request.GET.get("term") + if term_str is None: + term = student.cls.current_term() else: - periode = Periode.objects.get(id=int(periode_str), classe=etudiant.classe) + term = Term.objects.get(id=int(term_str), cls=student.cls) - return generate(periode) + return generate(term) def main(): - periode = Periode.objects.get(id=3) - return generate(periode) + term = Term.objects.get(id=3) + return generate(term) diff --git a/colloscope/table.py b/colloscope/table.py index 07bba19..b7b0784 100644 --- a/colloscope/table.py +++ b/colloscope/table.py @@ -2,8 +2,8 @@ from colloscope.models import * def table_colloscope(periode, heading=True, est_colle=True): semaines = periode.range_semaines() - lundis = [ periode.classe.date_debut_sem(n) for n in semaines ] - creneaux = Creneau.objects.filter(periode=periode, est_colle=est_colle) + lundis = [periode.classe.week_beginning_date(n) for n in semaines] + creneaux = Slot.objects.filter(periode=periode, est_colle=est_colle) jours = ["dimanche", "lundi", "mardi", "mercredi", "jeudi", "vendredi", "samedi"] s = "" @@ -28,26 +28,26 @@ def table_colloscope(periode, heading=True, est_colle=True): s += "\n" for i, c in enumerate(creneaux): - matiere = c.matiere + matiere = c.subject jour = c.jour - heure = c.heure + heure = c.time colleur = c.colleur - salle = c.salle + salle = c.room s += "\n" - s += f"{matiere.libelle}\n" + s += f"{matiere.description}\n" s += f"{jours[jour]}\n" s += f"{heure.strftime('%H:%M')}\n" s += "{} {}\n".format("M." if colleur.civilite=="M" else "Mme", colleur.nom.upper()) s += f"salle\n" for sem in semaines: - if Rotation.objects.filter(creneau=c, semaine=sem).exists(): - r = Rotation.objects.get(creneau=c, semaine=sem) + if Colle.objects.filter(creneau=c, semaine=sem).exists(): + r = Colle.objects.get(creneau=c, semaine=sem) groupes = r.groupes - content = ", ".join(g.libelle for g in groupes.all()) + content = ", ".join(g.description for g in groupes.all()) - if r.est_modifiee(): + if r.is_edited(): s += f"{content}\n" else: s += f"{content}\n" diff --git a/colloscope/templates/dashboard.html b/colloscope/templates/dashboard.html index 959b644..8b6c39b 100644 --- a/colloscope/templates/dashboard.html +++ b/colloscope/templates/dashboard.html @@ -7,27 +7,43 @@

Tableau de bord

-Bienvenue {{ etudiant }}. Votre lycée est {{ periode.classe.lycee.libelle }}, et votre classe est {{ periode.classe.libelle }}. +Bienvenue {{ student }}. Votre lycée est {{ term.cls.school.description }}, et votre classe est {{ term.cls.description }}.

-

Période actuelle : {{ periode }}. Votre groupe de colle est {{ groupe }}. Consulter le colloscope

+

Période actuelle : {{ term }}. Votre groupe de colle est {{ group }}. Consulter le colloscope

Mes colles

-Exporter en .ics (ceci est un permalien public) - + +

Exporter en .ics (ceci est un permalien public)

+ +

Accéder au marketplace

diff --git a/colloscope/templates/marketplace.html b/colloscope/templates/marketplace.html index 5cddef6..e65e8e6 100644 --- a/colloscope/templates/marketplace.html +++ b/colloscope/templates/marketplace.html @@ -3,21 +3,34 @@ {% block title %}Marketplace{% endblock title %} {% block main %} + Retour au tableau de bord +

Marketplace

Bienvenue sur le marketplace. - Les colles libres sont : - - + {% else %} + Aucune colle n'est disponible + {% endif %} {% endblock main %} \ No newline at end of file diff --git a/colloscope/templates/choix_profil.html b/colloscope/templates/select_profile.html similarity index 100% rename from colloscope/templates/choix_profil.html rename to colloscope/templates/select_profile.html diff --git a/colloscope/templates/table.html b/colloscope/templates/table.html index 40c45fa..0a4aaaa 100644 --- a/colloscope/templates/table.html +++ b/colloscope/templates/table.html @@ -17,16 +17,16 @@ {% block main %}

- Lycée : {{ periode.classe.lycee.libelle }}. Classe : {{ periode.classe.libelle }}. Retour au tableau de bord + Lycée : {{ term.cls.school.description }}. Classe : {{ term.cls.description }}. Retour au tableau de bord

-

Colloscope : {{ periode.libelle }}

+

Colloscope : {{ term.description }}

Changer de période : - + {% for p in term.cls.term_set.all %} + {% if p.id == term.id %} {% else %} @@ -36,8 +36,8 @@
- {% if request.GET.periode %} - Exporter le colloscope + {% if request.GET.term %} + Exporter le colloscope {% else %} Exporter le colloscope {% endif %} @@ -53,7 +53,7 @@ - {% for _ in semaines %} + {% for _ in weeks %} {% endfor %} @@ -64,32 +64,32 @@ Heure Colleur Salle - {% for n in semaines %} + {% for n in weeks %} {{ n }} {% endfor %} - {% for lundi in lundis %} - {{ lundi | strftime:"%d/%m/%y" }} + {% for monday in mondays %} + {{ monday | strftime:"%d/%m/%y" }} {% endfor %} - {% for c, rs in rotations %} + {% for c, rs in colles %} - {{ c.matiere.libelle }} - {{ jours | getitem:c.jour }} - {{ c.heure | strftime:"%H:%M" }} + {{ c.subject.description }} + {{ days | getitem:c.day }} + {{ c.time | strftime:"%H:%M" }} {{ c.colleur }} - {{ c.salle }} + {{ c.room }} - {% for sem, exists, r, est_modifiee, groupes in rs %} + {% for sem, exists, r, is_edited, groups in rs %} {% if exists %} - {% if est_modifiee %} - {{ groupes | join:"," }} + {% if is_edited %} + {{ groups | join:"," }} {% else %} - {{ groupes | join:"," }} + {{ groups | join:"," }} {% endif %} {% else %} diff --git a/colloscope/templates/profil_non_associe.html b/colloscope/templates/unbound_profile.html similarity index 100% rename from colloscope/templates/profil_non_associe.html rename to colloscope/templates/unbound_profile.html diff --git a/colloscope/urls.py b/colloscope/urls.py index 06c7916..19cb5b1 100644 --- a/colloscope/urls.py +++ b/colloscope/urls.py @@ -6,9 +6,9 @@ urlpatterns = [ path("table.html", views.colloscope, name="colloscope.table"), path("dashboard.html", views.dashboard, name="colloscope.dashboard"), path("export.pdf", views.export, name="colloscope.export"), - path("calendrier.ics", views.icalendar, name="colloscope.calendrier"), - path("choix_profil", views.choix_profil, name="colloscope.choix_profil"), + path("export/calendar//calendar.ics", views.icalendar, name="colloscope.calendar.ics"), + path("select_profile", views.select_profile, name="colloscope.select_profile"), path("marketplace.html", views.marketplace, name="colloscope.marketplace"), - path("action/inscription/", views.inscription, name="colloscope.inscription"), - path("action/desinscription/", views.desinscription, name="colloscope.desinscription"), + path("action/enroll", views.enroll, name="colloscope.enroll"), + path("action/withdraw", views.withdraw, name="colloscope.withdraw"), ] diff --git a/colloscope/views.py b/colloscope/views.py index 3aafa65..ae5e838 100644 --- a/colloscope/views.py +++ b/colloscope/views.py @@ -1,23 +1,21 @@ -from datetime import date, timedelta from uuid import uuid4 -from django.contrib.auth.models import User -from django.shortcuts import redirect -from django.http import HttpResponse +from django import forms +from django.shortcuts import redirect, render +from django.http import HttpResponse, HttpResponseRedirect from django.template import loader +from django.views.decorators.http import require_POST from django.contrib.auth.decorators import login_required from colloscope.models import * -from colloscope.table import table_colloscope from colloscope.pdfexport import handle from colloscope.icalexport import to_calendar def handler404(request): template = loader.get_template("404.html") - #response.status_code = 404 context = {} - return HttpResponse(template.render(context)) + return HttpResponse(template.render(context), status=404) def home_redirect(request): @@ -25,169 +23,182 @@ def home_redirect(request): @login_required -def choix_profil(request): +def select_profile(request): user = request.user session = request.session - if not Profil.objects.filter(utilisateur=user).exists(): - profil = Profil(utilisateur=user) - profil.save() + if not Profile.objects.filter(user=user).exists(): + profile = Profile(user=user) + profile.save() else: - profil = Profil.objects.get(utilisateur=user) + profile = Profile.objects.get(user=user) - if profil.etudiant is not None and profil.colleur is None: - session["profil"] = "etudiant" + if profile.student is not None and profile.colleur is None: + session["profile"] = "student" return redirect("/colloscope/") - elif profil.colleur is not None and profil.etudiant is None: - session["profil"] = "colleur" + elif profile.colleur is not None and profile.student is None: + session["profile"] = "colleur" return redirect("/colloscope/") else: - if profil.etudiant is not None: - template = loader.get_template("choix_profil.html") + if profile.student is not None: + template = loader.get_template("select_profile.html") else: - template = loader.get_template("profil_non_associe.html") + template = loader.get_template("unbound_profile.html") context = { - "profil": profil, + "profile": profile, } return HttpResponse(template.render(context)) -def get_lien_calendrier(etudiant, periode): +def get_lien_calendrier(student, term): try: - lien = LienCalendrier.objects.get(etudiant=etudiant, periode=periode) - except LienCalendrier.DoesNotExist: - code = uuid4().hex - lien = LienCalendrier(code=code, etudiant=etudiant, periode=periode) + lien = CalendarLink.objects.get(student=student, term=term) + except CalendarLink.DoesNotExist: + key = uuid4().hex + lien = CalendarLink(key=key, student=student, term=term) lien.save() - return f"calendrier.ics?key={lien.code}" + return f"calendrier.ics?key={lien.key}" -#@login_required +@login_required def dashboard(request): try: - etudiant = Profil.from_request( + student = Profile.from_request( request, preprocess=lambda query: (query - .select_related("etudiant__classe") - .prefetch_related("etudiant__classe__periode_set")) + .select_related("student__cls") + .prefetch_related("student__cls__term_set")) ) except ValueError: - return redirect("colloscope.choix_profil") + return redirect("colloscope.select_profile") - if not isinstance(etudiant, Etudiant): + if not isinstance(student, Student): return HttpResponse("pas encore supporté") - periode = etudiant.classe.periode_actuelle() - groupe = etudiant.groupe_de_colle(periode) + term = student.cls.current_term() + group = student.colle_group(term) - rotations = periode.query_rotations_etudiant(etudiant) + colles = term.query_colles_of_student(student) - colles_par_sem = [None] * len(periode.range_semaines()) - for i, n in enumerate(periode.range_semaines()): - lundi = periode.classe.date_debut_sem(n) - colles = rotations.filter(date__gte=lundi, date__lt=lundi + timedelta(weeks=1)) - colles_par_sem[i] = n, lundi, colles + colles_per_sem = [None] * len(term.range_weeks()) + for k, n in enumerate(term.range_weeks()): + lundi = term.cls.week_beginning_date(n) + colles_per_sem[k] = n, lundi, colles.filter(date__gte=max(lundi, date.today()), + date__lt=lundi + timedelta(weeks=1)) template = loader.get_template("dashboard.html") - lien_calendrier = get_lien_calendrier(etudiant, periode) + calendar_link = get_calendar_link(student, term) context = { - "etudiant": etudiant, - "periode": periode, - "groupe": groupe, - "rotations": colles_par_sem, - "lien_calendrier": lien_calendrier, + "student": student, + "term": term, + "group": group, + "colles_per_sem": colles_per_sem, + "calendar_link": calendar_link, } return HttpResponse(template.render(context, request)) + +class AmendForm(forms.Form): + colle_id = forms.IntegerField(widget=forms.HiddenInput(), required=True) + + +class EnrollForm(AmendForm): + pass + + +class WithdrawForm(AmendForm): + pass + + @login_required def marketplace(request): try: - etudiant = Profil.from_request( + student = Profile.from_request( request, - preprocess=lambda query: query \ - .select_related("etudiant__classe") \ - .prefetch_related("etudiant__classe__periode_set") + preprocess=lambda query: (query + .select_related("student__cls") + .prefetch_related("student__cls__term_set")) ) except ValueError: - return redirect("colloscope.choix_profil") + return redirect("colloscope.select_profile") - if not isinstance(etudiant, Etudiant): + if not isinstance(student, Student): return HttpResponse("pas encore supporté") - periode = etudiant.classe.periode_actuelle() - colles = periode.query_rotations_not_full() - - template = loader.get_template("marketplace.html") + term = student.cls.current_term() + colles = term.query_colles_not_full() context = { - "colles": colles + "colles": colles, } - return HttpResponse(template.render(context, request)) + return render(request, "marketplace.html", context) @login_required def colloscope(request): try: - etudiant = Profil.from_request( + student = Profile.from_request( request, - preprocess=lambda query: query \ - .select_related("etudiant__classe") \ - .prefetch_related("etudiant__classe__periode_set") + preprocess=lambda query: (query + .select_related("student__cls") + .prefetch_related("student__cls__term_set")) ) except ValueError: - return redirect("colloscope.choix_profil") + return redirect("colloscope.select_profile") - if not isinstance(etudiant, Etudiant): + if not isinstance(student, Student): return HttpResponse("pas encore supporté") - periode_str = request.GET.get("periode") - if periode_str is None: - periode = etudiant.classe.periode_actuelle() + term_str = request.GET.get("term") + if term_str is None: + term = student.cls.current_term() else: try: - periode = Periode.objects.get(id=int(periode_str), classe=etudiant.classe) - except Periode.DoesNotExist: + term = Term.objects.get(id=int(term_str), cls=student.cls) + except Term.DoesNotExist: template = loader.get_template("404.html") context = {} response = HttpResponse(template.render(context, request)) response.status_code = 404 - creneaux = Creneau.objects \ - .filter(periode=periode, est_colle=True) \ - .prefetch_related("rotation_set") + return response - semaines = periode.range_semaines() - rotations = [(c, []) for c in creneaux] - for c, l in rotations: - for sem in semaines: - lundi = periode.classe.date_debut_sem(sem) + slots = Slot.objects \ + .filter(term=term, type__description="colle") \ + .prefetch_related("colle_set") - rot = Rotation.objects.filter(creneau=c, date__gte=lundi, date__lt=lundi + timedelta(weeks=1)) + weeks = term.range_weeks() + colles = [(c, []) for c in slots] + for c, l in colles: + for sem in weeks: + lundi = term.cls.week_beginning_date(sem) + + rot = Colle.objects.filter(slot=c, date__gte=lundi, date__lt=lundi + timedelta(weeks=1)) exists = rot.exists() if exists: r = rot.first() - est_modifiee = r.est_modifiee() - groupes = (g.libelle for g in r.groupes.all()) + is_edited = r.is_edited() + groups = (g.description for g in r.groups.all()) else: - r = est_modifiee = groupes = None + r = is_edited = groups = None - l.append((sem, exists, r, est_modifiee, groupes)) + l.append((sem, exists, r, is_edited, groups)) template = loader.get_template("table.html") context = { - "periode": periode, - "semaines": semaines, - "lundis": [periode.classe.date_debut_sem(n) for n in semaines], - "jours": ["dimanche", "lundi", "mardi", "mercredi", "jeudi", "vendredi", "samedi"], - "rotations": rotations, + "term": term, + "weeks": weeks, + "mondays": [term.cls.week_beginning_date(n) for n in weeks], + "days": ["dimanche", "lundi", "mardi", "mercredi", "jeudi", "vendredi", "samedi"], + "colles": colles, } return HttpResponse(template.render(context, request)) @@ -198,75 +209,79 @@ def export(request): return HttpResponse(bytes(handle(request).output()), content_type="application/pdf") -def get_lien_calendrier(etudiant, periode): +def get_calendar_link(student, term): try: - lien = LienCalendrier.objects.get(etudiant=etudiant, periode=periode) - except LienCalendrier.DoesNotExist: + lien = CalendarLink.objects.get(student=student, term=term) + except CalendarLink.DoesNotExist: code = uuid4().hex - lien = LienCalendrier(code=code, etudiant=etudiant, periode=periode) + lien = CalendarLink(key=key, student=student, term=term) lien.save() - return f"calendrier.ics?key={lien.code}" + return f"export/calendar/{lien.key}/calendar.ics" -def icalendar(request): - if request.GET.get("key") is not None: - try: - lien = LienCalendrier.objects.get(code=request.GET.get("key")) +def icalendar(request, key): + try: + link = CalendarLink.objects.get(key=key) - if not request.GET.get("edt"): - return HttpResponse(to_calendar(lien.etudiant, lien.periode, include_EDT=True).to_ical(), - content_type="text/calendar") - - return HttpResponse(to_calendar(lien.etudiant, lien.periode, include_EDT=True).to_ical(), + if not request.GET.get("edt"): + return HttpResponse(to_calendar(link.student, link.term, include_EDT=True).to_ical(), content_type="text/calendar") - except LienCalendrier.DoesNotExist: - return HttpResponse("Invalid key", status=404) - else: - return HttpResponse("Unspecified key", status=404) + return HttpResponse(to_calendar(link.student, link.term, include_EDT=True).to_ical(), + content_type="text/calendar") + + except CalendarLink.DoesNotExist: + return HttpResponse("Invalid key", status=404) -@login_required -def amender(request, colle_id, est_positif): +def amend(request, colle_id, do_enroll): try: - etudiant = Profil.from_request( + student = Profile.from_request( request, - preprocess=lambda query: query \ - .select_related("etudiant__classe") \ - .prefetch_related("etudiant__classe__periode_set") + preprocess=lambda query: (query + .select_related("student__cls") + .prefetch_related("student__cls__term_set")) ) except ValueError: return redirect("colloscope.choix_profil") - if not isinstance(etudiant, Etudiant): + if not isinstance(student, Student): return HttpResponse("pas encore supporté") - #try: - if est_positif: - (Rotation.objects - .get(id=colle_id, creneau__periode__classe=etudiant.classe) - .amender(est_positif=True, etudiant=etudiant, notifier=True)) + if do_enroll: + (Colle.objects + .get(id=colle_id, slot__term__cls=student.cls) + .amend(enroll=True, student=student, notify=True)) else: - (Rotation.objects - .get(id=colle_id, groupes__etudiant=etudiant) - .amender(est_positif=False, etudiant=etudiant, notifier=True)) - - return HttpResponse("ok") - #except Exception as e: - # return HttpResponse(f"aïe : {e}") + (Colle.objects + .get(id=colle_id, groups__student=student) + .amend(enroll=False, student=student, notify=True)) +@require_POST @login_required -def inscription(request, colle_id): - return amender(request, colle_id, True) +def enroll(request): + form = WithdrawForm(request.POST) + if form.is_valid(): + colle_id = form.cleaned_data["colle_id"] + amend(request, colle_id, True) + else: + print("!!!! invalide") + return HttpResponseRedirect(request.META.get('HTTP_REFERER')) + +@require_POST @login_required -def desinscription(request, colle_id): - return amender(request, colle_id, False) +def withdraw(request): + form = WithdrawForm(request.POST) + if form.is_valid(): + colle_id = form.cleaned_data["colle_id"] + amend(request, colle_id, False) + else: + print("!!!! invalide") + + return HttpResponseRedirect(request.META.get('HTTP_REFERER')) -def data_dump(request): - template = loader.get_template("data_dump.html") - return HttpResponse(template.render()) diff --git a/kholles_web/urls.py b/kholles_web/urls.py index 497c974..afbc15e 100644 --- a/kholles_web/urls.py +++ b/kholles_web/urls.py @@ -16,12 +16,14 @@ Including another URLconf """ from django.contrib import admin, auth from django.urls import include, path +from django.contrib.staticfiles import views as vstatic from colloscope.views import home_redirect urlpatterns = [ path('', home_redirect, name="home"), + path("favicon.ico", lambda req: vstatic.serve(req, "favicon.ico")), path('colloscope/', include('colloscope.urls')), path('admin/', admin.site.urls), - path('comptes/', include("django.contrib.auth.urls")), + path('accounts/', include("django.contrib.auth.urls")), ] diff --git a/locale/en/LC_MESSAGES/django.po b/locale/en/LC_MESSAGES/django.po new file mode 100644 index 0000000..4ebe504 --- /dev/null +++ b/locale/en/LC_MESSAGES/django.po @@ -0,0 +1,1241 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# FIRST AUTHOR , YEAR. +# +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2024-05-02 01:46+0200\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=2; plural=(n != 1);\n" + +#: kholles_web/settings.py:129 +msgid "English" +msgstr "" + +#: kholles_web/settings.py:130 +msgid "French" +msgstr "" + +#: venv/lib/python3.12/site-packages/django/contrib/messages/apps.py:16 +msgid "Messages" +msgstr "" + +#: venv/lib/python3.12/site-packages/django/contrib/sitemaps/apps.py:8 +msgid "Site Maps" +msgstr "" + +#: venv/lib/python3.12/site-packages/django/contrib/staticfiles/apps.py:9 +msgid "Static Files" +msgstr "" + +#: venv/lib/python3.12/site-packages/django/contrib/syndication/apps.py:7 +msgid "Syndication" +msgstr "" + +#. Translators: String used to replace omitted page numbers in elided page +#. range generated by paginators, e.g. [1, 2, '…', 5, 6, 7, '…', 9, 10]. +#: venv/lib/python3.12/site-packages/django/core/paginator.py:30 +msgid "…" +msgstr "" + +#: venv/lib/python3.12/site-packages/django/core/paginator.py:32 +msgid "That page number is not an integer" +msgstr "" + +#: venv/lib/python3.12/site-packages/django/core/paginator.py:33 +msgid "That page number is less than 1" +msgstr "" + +#: venv/lib/python3.12/site-packages/django/core/paginator.py:34 +msgid "That page contains no results" +msgstr "" + +#: venv/lib/python3.12/site-packages/django/core/validators.py:22 +msgid "Enter a valid value." +msgstr "" + +#: venv/lib/python3.12/site-packages/django/core/validators.py:104 +#: venv/lib/python3.12/site-packages/django/forms/fields.py:760 +msgid "Enter a valid URL." +msgstr "" + +#: venv/lib/python3.12/site-packages/django/core/validators.py:165 +msgid "Enter a valid integer." +msgstr "" + +#: venv/lib/python3.12/site-packages/django/core/validators.py:176 +msgid "Enter a valid email address." +msgstr "" + +#. Translators: "letters" means latin letters: a-z and A-Z. +#: venv/lib/python3.12/site-packages/django/core/validators.py:259 +msgid "" +"Enter a valid “slug” consisting of letters, numbers, underscores or hyphens." +msgstr "" + +#: venv/lib/python3.12/site-packages/django/core/validators.py:267 +msgid "" +"Enter a valid “slug” consisting of Unicode letters, numbers, underscores, or " +"hyphens." +msgstr "" + +#: venv/lib/python3.12/site-packages/django/core/validators.py:279 +#: venv/lib/python3.12/site-packages/django/core/validators.py:306 +msgid "Enter a valid IPv4 address." +msgstr "" + +#: venv/lib/python3.12/site-packages/django/core/validators.py:286 +#: venv/lib/python3.12/site-packages/django/core/validators.py:307 +msgid "Enter a valid IPv6 address." +msgstr "" + +#: venv/lib/python3.12/site-packages/django/core/validators.py:298 +#: venv/lib/python3.12/site-packages/django/core/validators.py:305 +msgid "Enter a valid IPv4 or IPv6 address." +msgstr "" + +#: venv/lib/python3.12/site-packages/django/core/validators.py:341 +msgid "Enter only digits separated by commas." +msgstr "" + +#: venv/lib/python3.12/site-packages/django/core/validators.py:347 +#, python-format +msgid "Ensure this value is %(limit_value)s (it is %(show_value)s)." +msgstr "" + +#: venv/lib/python3.12/site-packages/django/core/validators.py:382 +#, python-format +msgid "Ensure this value is less than or equal to %(limit_value)s." +msgstr "" + +#: venv/lib/python3.12/site-packages/django/core/validators.py:391 +#, python-format +msgid "Ensure this value is greater than or equal to %(limit_value)s." +msgstr "" + +#: venv/lib/python3.12/site-packages/django/core/validators.py:400 +#, python-format +msgid "Ensure this value is a multiple of step size %(limit_value)s." +msgstr "" + +#: venv/lib/python3.12/site-packages/django/core/validators.py:407 +#, python-format +msgid "" +"Ensure this value is a multiple of step size %(limit_value)s, starting from " +"%(offset)s, e.g. %(offset)s, %(valid_value1)s, %(valid_value2)s, and so on." +msgstr "" + +#: venv/lib/python3.12/site-packages/django/core/validators.py:439 +#, python-format +msgid "" +"Ensure this value has at least %(limit_value)d character (it has " +"%(show_value)d)." +msgid_plural "" +"Ensure this value has at least %(limit_value)d characters (it has " +"%(show_value)d)." +msgstr[0] "" +msgstr[1] "" + +#: venv/lib/python3.12/site-packages/django/core/validators.py:457 +#, python-format +msgid "" +"Ensure this value has at most %(limit_value)d character (it has " +"%(show_value)d)." +msgid_plural "" +"Ensure this value has at most %(limit_value)d characters (it has " +"%(show_value)d)." +msgstr[0] "" +msgstr[1] "" + +#: venv/lib/python3.12/site-packages/django/core/validators.py:480 +#: venv/lib/python3.12/site-packages/django/forms/fields.py:355 +#: venv/lib/python3.12/site-packages/django/forms/fields.py:394 +msgid "Enter a number." +msgstr "" + +#: venv/lib/python3.12/site-packages/django/core/validators.py:482 +#, python-format +msgid "Ensure that there are no more than %(max)s digit in total." +msgid_plural "Ensure that there are no more than %(max)s digits in total." +msgstr[0] "" +msgstr[1] "" + +#: venv/lib/python3.12/site-packages/django/core/validators.py:487 +#, python-format +msgid "Ensure that there are no more than %(max)s decimal place." +msgid_plural "Ensure that there are no more than %(max)s decimal places." +msgstr[0] "" +msgstr[1] "" + +#: venv/lib/python3.12/site-packages/django/core/validators.py:492 +#, python-format +msgid "" +"Ensure that there are no more than %(max)s digit before the decimal point." +msgid_plural "" +"Ensure that there are no more than %(max)s digits before the decimal point." +msgstr[0] "" +msgstr[1] "" + +#: venv/lib/python3.12/site-packages/django/core/validators.py:563 +#, python-format +msgid "" +"File extension “%(extension)s” is not allowed. Allowed extensions are: " +"%(allowed_extensions)s." +msgstr "" + +#: venv/lib/python3.12/site-packages/django/core/validators.py:624 +msgid "Null characters are not allowed." +msgstr "" + +#: venv/lib/python3.12/site-packages/django/db/models/base.py:1473 +#: venv/lib/python3.12/site-packages/django/forms/models.py:906 +msgid "and" +msgstr "" + +#: venv/lib/python3.12/site-packages/django/db/models/base.py:1475 +#, python-format +msgid "%(model_name)s with this %(field_labels)s already exists." +msgstr "" + +#: venv/lib/python3.12/site-packages/django/db/models/constraints.py:20 +#, python-format +msgid "Constraint “%(name)s” is violated." +msgstr "" + +#: venv/lib/python3.12/site-packages/django/db/models/fields/__init__.py:133 +#, python-format +msgid "Value %(value)r is not a valid choice." +msgstr "" + +#: venv/lib/python3.12/site-packages/django/db/models/fields/__init__.py:134 +msgid "This field cannot be null." +msgstr "" + +#: venv/lib/python3.12/site-packages/django/db/models/fields/__init__.py:135 +msgid "This field cannot be blank." +msgstr "" + +#: venv/lib/python3.12/site-packages/django/db/models/fields/__init__.py:136 +#, python-format +msgid "%(model_name)s with this %(field_label)s already exists." +msgstr "" + +#. Translators: The 'lookup_type' is one of 'date', 'year' or +#. 'month'. Eg: "Title must be unique for pub_date year" +#: venv/lib/python3.12/site-packages/django/db/models/fields/__init__.py:140 +#, python-format +msgid "" +"%(field_label)s must be unique for %(date_field_label)s %(lookup_type)s." +msgstr "" + +#: venv/lib/python3.12/site-packages/django/db/models/fields/__init__.py:179 +#, python-format +msgid "Field of type: %(field_type)s" +msgstr "" + +#: venv/lib/python3.12/site-packages/django/db/models/fields/__init__.py:1155 +#, python-format +msgid "“%(value)s” value must be either True or False." +msgstr "" + +#: venv/lib/python3.12/site-packages/django/db/models/fields/__init__.py:1156 +#, python-format +msgid "“%(value)s” value must be either True, False, or None." +msgstr "" + +#: venv/lib/python3.12/site-packages/django/db/models/fields/__init__.py:1158 +msgid "Boolean (Either True or False)" +msgstr "" + +#: venv/lib/python3.12/site-packages/django/db/models/fields/__init__.py:1208 +#, python-format +msgid "String (up to %(max_length)s)" +msgstr "" + +#: venv/lib/python3.12/site-packages/django/db/models/fields/__init__.py:1210 +msgid "String (unlimited)" +msgstr "" + +#: venv/lib/python3.12/site-packages/django/db/models/fields/__init__.py:1314 +msgid "Comma-separated integers" +msgstr "" + +#: venv/lib/python3.12/site-packages/django/db/models/fields/__init__.py:1415 +#, python-format +msgid "" +"“%(value)s” value has an invalid date format. It must be in YYYY-MM-DD " +"format." +msgstr "" + +#: venv/lib/python3.12/site-packages/django/db/models/fields/__init__.py:1419 +#: venv/lib/python3.12/site-packages/django/db/models/fields/__init__.py:1554 +#, python-format +msgid "" +"“%(value)s” value has the correct format (YYYY-MM-DD) but it is an invalid " +"date." +msgstr "" + +#: venv/lib/python3.12/site-packages/django/db/models/fields/__init__.py:1423 +msgid "Date (without time)" +msgstr "" + +#: venv/lib/python3.12/site-packages/django/db/models/fields/__init__.py:1550 +#, python-format +msgid "" +"“%(value)s” value has an invalid format. It must be in YYYY-MM-DD HH:MM[:ss[." +"uuuuuu]][TZ] format." +msgstr "" + +#: venv/lib/python3.12/site-packages/django/db/models/fields/__init__.py:1558 +#, python-format +msgid "" +"“%(value)s” value has the correct format (YYYY-MM-DD HH:MM[:ss[.uuuuuu]]" +"[TZ]) but it is an invalid date/time." +msgstr "" + +#: venv/lib/python3.12/site-packages/django/db/models/fields/__init__.py:1563 +msgid "Date (with time)" +msgstr "" + +#: venv/lib/python3.12/site-packages/django/db/models/fields/__init__.py:1690 +#, python-format +msgid "“%(value)s” value must be a decimal number." +msgstr "" + +#: venv/lib/python3.12/site-packages/django/db/models/fields/__init__.py:1692 +msgid "Decimal number" +msgstr "" + +#: venv/lib/python3.12/site-packages/django/db/models/fields/__init__.py:1853 +#, python-format +msgid "" +"“%(value)s” value has an invalid format. It must be in [DD] [[HH:]MM:]ss[." +"uuuuuu] format." +msgstr "" + +#: venv/lib/python3.12/site-packages/django/db/models/fields/__init__.py:1857 +msgid "Duration" +msgstr "" + +#: venv/lib/python3.12/site-packages/django/db/models/fields/__init__.py:1909 +msgid "Email address" +msgstr "" + +#: venv/lib/python3.12/site-packages/django/db/models/fields/__init__.py:1934 +msgid "File path" +msgstr "" + +#: venv/lib/python3.12/site-packages/django/db/models/fields/__init__.py:2012 +#, python-format +msgid "“%(value)s” value must be a float." +msgstr "" + +#: venv/lib/python3.12/site-packages/django/db/models/fields/__init__.py:2014 +msgid "Floating point number" +msgstr "" + +#: venv/lib/python3.12/site-packages/django/db/models/fields/__init__.py:2054 +#, python-format +msgid "“%(value)s” value must be an integer." +msgstr "" + +#: venv/lib/python3.12/site-packages/django/db/models/fields/__init__.py:2056 +msgid "Integer" +msgstr "" + +#: venv/lib/python3.12/site-packages/django/db/models/fields/__init__.py:2152 +msgid "Big (8 byte) integer" +msgstr "" + +#: venv/lib/python3.12/site-packages/django/db/models/fields/__init__.py:2169 +msgid "Small integer" +msgstr "" + +#: venv/lib/python3.12/site-packages/django/db/models/fields/__init__.py:2177 +msgid "IPv4 address" +msgstr "" + +#: venv/lib/python3.12/site-packages/django/db/models/fields/__init__.py:2208 +msgid "IP address" +msgstr "" + +#: venv/lib/python3.12/site-packages/django/db/models/fields/__init__.py:2301 +#: venv/lib/python3.12/site-packages/django/db/models/fields/__init__.py:2302 +#, python-format +msgid "“%(value)s” value must be either None, True or False." +msgstr "" + +#: venv/lib/python3.12/site-packages/django/db/models/fields/__init__.py:2304 +msgid "Boolean (Either True, False or None)" +msgstr "" + +#: venv/lib/python3.12/site-packages/django/db/models/fields/__init__.py:2355 +msgid "Positive big integer" +msgstr "" + +#: venv/lib/python3.12/site-packages/django/db/models/fields/__init__.py:2370 +msgid "Positive integer" +msgstr "" + +#: venv/lib/python3.12/site-packages/django/db/models/fields/__init__.py:2385 +msgid "Positive small integer" +msgstr "" + +#: venv/lib/python3.12/site-packages/django/db/models/fields/__init__.py:2401 +#, python-format +msgid "Slug (up to %(max_length)s)" +msgstr "" + +#: venv/lib/python3.12/site-packages/django/db/models/fields/__init__.py:2437 +msgid "Text" +msgstr "" + +#: venv/lib/python3.12/site-packages/django/db/models/fields/__init__.py:2512 +#, python-format +msgid "" +"“%(value)s” value has an invalid format. It must be in HH:MM[:ss[.uuuuuu]] " +"format." +msgstr "" + +#: venv/lib/python3.12/site-packages/django/db/models/fields/__init__.py:2516 +#, python-format +msgid "" +"“%(value)s” value has the correct format (HH:MM[:ss[.uuuuuu]]) but it is an " +"invalid time." +msgstr "" + +#: venv/lib/python3.12/site-packages/django/db/models/fields/__init__.py:2520 +msgid "Time" +msgstr "" + +#: venv/lib/python3.12/site-packages/django/db/models/fields/__init__.py:2628 +msgid "URL" +msgstr "" + +#: venv/lib/python3.12/site-packages/django/db/models/fields/__init__.py:2652 +msgid "Raw binary data" +msgstr "" + +#: venv/lib/python3.12/site-packages/django/db/models/fields/__init__.py:2717 +#, python-format +msgid "“%(value)s” is not a valid UUID." +msgstr "" + +#: venv/lib/python3.12/site-packages/django/db/models/fields/__init__.py:2719 +msgid "Universally unique identifier" +msgstr "" + +#: venv/lib/python3.12/site-packages/django/db/models/fields/files.py:232 +msgid "File" +msgstr "" + +#: venv/lib/python3.12/site-packages/django/db/models/fields/files.py:393 +msgid "Image" +msgstr "" + +#: venv/lib/python3.12/site-packages/django/db/models/fields/json.py:26 +msgid "A JSON object" +msgstr "" + +#: venv/lib/python3.12/site-packages/django/db/models/fields/json.py:28 +msgid "Value must be valid JSON." +msgstr "" + +#: venv/lib/python3.12/site-packages/django/db/models/fields/related.py:939 +#, python-format +msgid "%(model)s instance with %(field)s %(value)r does not exist." +msgstr "" + +#: venv/lib/python3.12/site-packages/django/db/models/fields/related.py:941 +msgid "Foreign Key (type determined by related field)" +msgstr "" + +#: venv/lib/python3.12/site-packages/django/db/models/fields/related.py:1235 +msgid "One-to-one relationship" +msgstr "" + +#: venv/lib/python3.12/site-packages/django/db/models/fields/related.py:1292 +#, python-format +msgid "%(from)s-%(to)s relationship" +msgstr "" + +#: venv/lib/python3.12/site-packages/django/db/models/fields/related.py:1294 +#, python-format +msgid "%(from)s-%(to)s relationships" +msgstr "" + +#: venv/lib/python3.12/site-packages/django/db/models/fields/related.py:1342 +msgid "Many-to-many relationship" +msgstr "" + +#. Translators: If found as last label character, these punctuation +#. characters will prevent the default label_suffix to be appended to the label +#: venv/lib/python3.12/site-packages/django/forms/boundfield.py:185 +msgid ":?.!" +msgstr "" + +#: venv/lib/python3.12/site-packages/django/forms/fields.py:95 +msgid "This field is required." +msgstr "" + +#: venv/lib/python3.12/site-packages/django/forms/fields.py:304 +msgid "Enter a whole number." +msgstr "" + +#: venv/lib/python3.12/site-packages/django/forms/fields.py:475 +#: venv/lib/python3.12/site-packages/django/forms/fields.py:1252 +msgid "Enter a valid date." +msgstr "" + +#: venv/lib/python3.12/site-packages/django/forms/fields.py:498 +#: venv/lib/python3.12/site-packages/django/forms/fields.py:1253 +msgid "Enter a valid time." +msgstr "" + +#: venv/lib/python3.12/site-packages/django/forms/fields.py:525 +msgid "Enter a valid date/time." +msgstr "" + +#: venv/lib/python3.12/site-packages/django/forms/fields.py:559 +msgid "Enter a valid duration." +msgstr "" + +#: venv/lib/python3.12/site-packages/django/forms/fields.py:560 +#, python-brace-format +msgid "The number of days must be between {min_days} and {max_days}." +msgstr "" + +#: venv/lib/python3.12/site-packages/django/forms/fields.py:629 +msgid "No file was submitted. Check the encoding type on the form." +msgstr "" + +#: venv/lib/python3.12/site-packages/django/forms/fields.py:630 +msgid "No file was submitted." +msgstr "" + +#: venv/lib/python3.12/site-packages/django/forms/fields.py:631 +msgid "The submitted file is empty." +msgstr "" + +#: venv/lib/python3.12/site-packages/django/forms/fields.py:633 +#, python-format +msgid "Ensure this filename has at most %(max)d character (it has %(length)d)." +msgid_plural "" +"Ensure this filename has at most %(max)d characters (it has %(length)d)." +msgstr[0] "" +msgstr[1] "" + +#: venv/lib/python3.12/site-packages/django/forms/fields.py:638 +msgid "Please either submit a file or check the clear checkbox, not both." +msgstr "" + +#: venv/lib/python3.12/site-packages/django/forms/fields.py:702 +msgid "" +"Upload a valid image. The file you uploaded was either not an image or a " +"corrupted image." +msgstr "" + +#: venv/lib/python3.12/site-packages/django/forms/fields.py:874 +#: venv/lib/python3.12/site-packages/django/forms/fields.py:960 +#: venv/lib/python3.12/site-packages/django/forms/models.py:1585 +#, python-format +msgid "Select a valid choice. %(value)s is not one of the available choices." +msgstr "" + +#: venv/lib/python3.12/site-packages/django/forms/fields.py:962 +#: venv/lib/python3.12/site-packages/django/forms/fields.py:1081 +#: venv/lib/python3.12/site-packages/django/forms/models.py:1583 +msgid "Enter a list of values." +msgstr "" + +#: venv/lib/python3.12/site-packages/django/forms/fields.py:1082 +msgid "Enter a complete value." +msgstr "" + +#: venv/lib/python3.12/site-packages/django/forms/fields.py:1321 +msgid "Enter a valid UUID." +msgstr "" + +#: venv/lib/python3.12/site-packages/django/forms/fields.py:1351 +msgid "Enter a valid JSON." +msgstr "" + +#. Translators: This is the default suffix added to form field labels +#: venv/lib/python3.12/site-packages/django/forms/forms.py:94 +msgid ":" +msgstr "" + +#: venv/lib/python3.12/site-packages/django/forms/forms.py:231 +#, python-format +msgid "(Hidden field %(name)s) %(error)s" +msgstr "" + +#: venv/lib/python3.12/site-packages/django/forms/formsets.py:61 +#, python-format +msgid "" +"ManagementForm data is missing or has been tampered with. Missing fields: " +"%(field_names)s. You may need to file a bug report if the issue persists." +msgstr "" + +#: venv/lib/python3.12/site-packages/django/forms/formsets.py:65 +#, python-format +msgid "Please submit at most %(num)d form." +msgid_plural "Please submit at most %(num)d forms." +msgstr[0] "" +msgstr[1] "" + +#: venv/lib/python3.12/site-packages/django/forms/formsets.py:70 +#, python-format +msgid "Please submit at least %(num)d form." +msgid_plural "Please submit at least %(num)d forms." +msgstr[0] "" +msgstr[1] "" + +#: venv/lib/python3.12/site-packages/django/forms/formsets.py:484 +#: venv/lib/python3.12/site-packages/django/forms/formsets.py:491 +msgid "Order" +msgstr "" + +#: venv/lib/python3.12/site-packages/django/forms/formsets.py:499 +msgid "Delete" +msgstr "" + +#: venv/lib/python3.12/site-packages/django/forms/models.py:899 +#, python-format +msgid "Please correct the duplicate data for %(field)s." +msgstr "" + +#: venv/lib/python3.12/site-packages/django/forms/models.py:904 +#, python-format +msgid "Please correct the duplicate data for %(field)s, which must be unique." +msgstr "" + +#: venv/lib/python3.12/site-packages/django/forms/models.py:911 +#, python-format +msgid "" +"Please correct the duplicate data for %(field_name)s which must be unique " +"for the %(lookup)s in %(date_field)s." +msgstr "" + +#: venv/lib/python3.12/site-packages/django/forms/models.py:920 +msgid "Please correct the duplicate values below." +msgstr "" + +#: venv/lib/python3.12/site-packages/django/forms/models.py:1357 +msgid "The inline value did not match the parent instance." +msgstr "" + +#: venv/lib/python3.12/site-packages/django/forms/models.py:1448 +msgid "Select a valid choice. That choice is not one of the available choices." +msgstr "" + +#: venv/lib/python3.12/site-packages/django/forms/models.py:1587 +#, python-format +msgid "“%(pk)s” is not a valid value." +msgstr "" + +#: venv/lib/python3.12/site-packages/django/forms/utils.py:227 +#, python-format +msgid "" +"%(datetime)s couldn’t be interpreted in time zone %(current_timezone)s; it " +"may be ambiguous or it may not exist." +msgstr "" + +#: venv/lib/python3.12/site-packages/django/forms/widgets.py:461 +msgid "Clear" +msgstr "" + +#: venv/lib/python3.12/site-packages/django/forms/widgets.py:462 +msgid "Currently" +msgstr "" + +#: venv/lib/python3.12/site-packages/django/forms/widgets.py:463 +msgid "Change" +msgstr "" + +#: venv/lib/python3.12/site-packages/django/forms/widgets.py:800 +msgid "Unknown" +msgstr "" + +#: venv/lib/python3.12/site-packages/django/forms/widgets.py:801 +msgid "Yes" +msgstr "" + +#: venv/lib/python3.12/site-packages/django/forms/widgets.py:802 +msgid "No" +msgstr "" + +#. Translators: Please do not add spaces around commas. +#: venv/lib/python3.12/site-packages/django/template/defaultfilters.py:876 +msgid "yes,no,maybe" +msgstr "" + +#: venv/lib/python3.12/site-packages/django/template/defaultfilters.py:906 +#: venv/lib/python3.12/site-packages/django/template/defaultfilters.py:923 +#, python-format +msgid "%(size)d byte" +msgid_plural "%(size)d bytes" +msgstr[0] "" +msgstr[1] "" + +#: venv/lib/python3.12/site-packages/django/template/defaultfilters.py:925 +#, python-format +msgid "%s KB" +msgstr "" + +#: venv/lib/python3.12/site-packages/django/template/defaultfilters.py:927 +#, python-format +msgid "%s MB" +msgstr "" + +#: venv/lib/python3.12/site-packages/django/template/defaultfilters.py:929 +#, python-format +msgid "%s GB" +msgstr "" + +#: venv/lib/python3.12/site-packages/django/template/defaultfilters.py:931 +#, python-format +msgid "%s TB" +msgstr "" + +#: venv/lib/python3.12/site-packages/django/template/defaultfilters.py:933 +#, python-format +msgid "%s PB" +msgstr "" + +#: venv/lib/python3.12/site-packages/django/utils/dateformat.py:74 +msgid "p.m." +msgstr "" + +#: venv/lib/python3.12/site-packages/django/utils/dateformat.py:75 +msgid "a.m." +msgstr "" + +#: venv/lib/python3.12/site-packages/django/utils/dateformat.py:80 +msgid "PM" +msgstr "" + +#: venv/lib/python3.12/site-packages/django/utils/dateformat.py:81 +msgid "AM" +msgstr "" + +#: venv/lib/python3.12/site-packages/django/utils/dateformat.py:153 +msgid "midnight" +msgstr "" + +#: venv/lib/python3.12/site-packages/django/utils/dateformat.py:155 +msgid "noon" +msgstr "" + +#: venv/lib/python3.12/site-packages/django/utils/dates.py:7 +msgid "Monday" +msgstr "" + +#: venv/lib/python3.12/site-packages/django/utils/dates.py:8 +msgid "Tuesday" +msgstr "" + +#: venv/lib/python3.12/site-packages/django/utils/dates.py:9 +msgid "Wednesday" +msgstr "" + +#: venv/lib/python3.12/site-packages/django/utils/dates.py:10 +msgid "Thursday" +msgstr "" + +#: venv/lib/python3.12/site-packages/django/utils/dates.py:11 +msgid "Friday" +msgstr "" + +#: venv/lib/python3.12/site-packages/django/utils/dates.py:12 +msgid "Saturday" +msgstr "" + +#: venv/lib/python3.12/site-packages/django/utils/dates.py:13 +msgid "Sunday" +msgstr "" + +#: venv/lib/python3.12/site-packages/django/utils/dates.py:16 +msgid "Mon" +msgstr "" + +#: venv/lib/python3.12/site-packages/django/utils/dates.py:17 +msgid "Tue" +msgstr "" + +#: venv/lib/python3.12/site-packages/django/utils/dates.py:18 +msgid "Wed" +msgstr "" + +#: venv/lib/python3.12/site-packages/django/utils/dates.py:19 +msgid "Thu" +msgstr "" + +#: venv/lib/python3.12/site-packages/django/utils/dates.py:20 +msgid "Fri" +msgstr "" + +#: venv/lib/python3.12/site-packages/django/utils/dates.py:21 +msgid "Sat" +msgstr "" + +#: venv/lib/python3.12/site-packages/django/utils/dates.py:22 +msgid "Sun" +msgstr "" + +#: venv/lib/python3.12/site-packages/django/utils/dates.py:25 +msgid "January" +msgstr "" + +#: venv/lib/python3.12/site-packages/django/utils/dates.py:26 +msgid "February" +msgstr "" + +#: venv/lib/python3.12/site-packages/django/utils/dates.py:27 +msgid "March" +msgstr "" + +#: venv/lib/python3.12/site-packages/django/utils/dates.py:28 +msgid "April" +msgstr "" + +#: venv/lib/python3.12/site-packages/django/utils/dates.py:29 +msgid "May" +msgstr "" + +#: venv/lib/python3.12/site-packages/django/utils/dates.py:30 +msgid "June" +msgstr "" + +#: venv/lib/python3.12/site-packages/django/utils/dates.py:31 +msgid "July" +msgstr "" + +#: venv/lib/python3.12/site-packages/django/utils/dates.py:32 +msgid "August" +msgstr "" + +#: venv/lib/python3.12/site-packages/django/utils/dates.py:33 +msgid "September" +msgstr "" + +#: venv/lib/python3.12/site-packages/django/utils/dates.py:34 +msgid "October" +msgstr "" + +#: venv/lib/python3.12/site-packages/django/utils/dates.py:35 +msgid "November" +msgstr "" + +#: venv/lib/python3.12/site-packages/django/utils/dates.py:36 +msgid "December" +msgstr "" + +#: venv/lib/python3.12/site-packages/django/utils/dates.py:39 +msgid "jan" +msgstr "" + +#: venv/lib/python3.12/site-packages/django/utils/dates.py:40 +msgid "feb" +msgstr "" + +#: venv/lib/python3.12/site-packages/django/utils/dates.py:41 +msgid "mar" +msgstr "" + +#: venv/lib/python3.12/site-packages/django/utils/dates.py:42 +msgid "apr" +msgstr "" + +#: venv/lib/python3.12/site-packages/django/utils/dates.py:43 +msgid "may" +msgstr "" + +#: venv/lib/python3.12/site-packages/django/utils/dates.py:44 +msgid "jun" +msgstr "" + +#: venv/lib/python3.12/site-packages/django/utils/dates.py:45 +msgid "jul" +msgstr "" + +#: venv/lib/python3.12/site-packages/django/utils/dates.py:46 +msgid "aug" +msgstr "" + +#: venv/lib/python3.12/site-packages/django/utils/dates.py:47 +msgid "sep" +msgstr "" + +#: venv/lib/python3.12/site-packages/django/utils/dates.py:48 +msgid "oct" +msgstr "" + +#: venv/lib/python3.12/site-packages/django/utils/dates.py:49 +msgid "nov" +msgstr "" + +#: venv/lib/python3.12/site-packages/django/utils/dates.py:50 +msgid "dec" +msgstr "" + +#: venv/lib/python3.12/site-packages/django/utils/dates.py:53 +msgctxt "abbrev. month" +msgid "Jan." +msgstr "" + +#: venv/lib/python3.12/site-packages/django/utils/dates.py:54 +msgctxt "abbrev. month" +msgid "Feb." +msgstr "" + +#: venv/lib/python3.12/site-packages/django/utils/dates.py:55 +msgctxt "abbrev. month" +msgid "March" +msgstr "" + +#: venv/lib/python3.12/site-packages/django/utils/dates.py:56 +msgctxt "abbrev. month" +msgid "April" +msgstr "" + +#: venv/lib/python3.12/site-packages/django/utils/dates.py:57 +msgctxt "abbrev. month" +msgid "May" +msgstr "" + +#: venv/lib/python3.12/site-packages/django/utils/dates.py:58 +msgctxt "abbrev. month" +msgid "June" +msgstr "" + +#: venv/lib/python3.12/site-packages/django/utils/dates.py:59 +msgctxt "abbrev. month" +msgid "July" +msgstr "" + +#: venv/lib/python3.12/site-packages/django/utils/dates.py:60 +msgctxt "abbrev. month" +msgid "Aug." +msgstr "" + +#: venv/lib/python3.12/site-packages/django/utils/dates.py:61 +msgctxt "abbrev. month" +msgid "Sept." +msgstr "" + +#: venv/lib/python3.12/site-packages/django/utils/dates.py:62 +msgctxt "abbrev. month" +msgid "Oct." +msgstr "" + +#: venv/lib/python3.12/site-packages/django/utils/dates.py:63 +msgctxt "abbrev. month" +msgid "Nov." +msgstr "" + +#: venv/lib/python3.12/site-packages/django/utils/dates.py:64 +msgctxt "abbrev. month" +msgid "Dec." +msgstr "" + +#: venv/lib/python3.12/site-packages/django/utils/dates.py:67 +msgctxt "alt. month" +msgid "January" +msgstr "" + +#: venv/lib/python3.12/site-packages/django/utils/dates.py:68 +msgctxt "alt. month" +msgid "February" +msgstr "" + +#: venv/lib/python3.12/site-packages/django/utils/dates.py:69 +msgctxt "alt. month" +msgid "March" +msgstr "" + +#: venv/lib/python3.12/site-packages/django/utils/dates.py:70 +msgctxt "alt. month" +msgid "April" +msgstr "" + +#: venv/lib/python3.12/site-packages/django/utils/dates.py:71 +msgctxt "alt. month" +msgid "May" +msgstr "" + +#: venv/lib/python3.12/site-packages/django/utils/dates.py:72 +msgctxt "alt. month" +msgid "June" +msgstr "" + +#: venv/lib/python3.12/site-packages/django/utils/dates.py:73 +msgctxt "alt. month" +msgid "July" +msgstr "" + +#: venv/lib/python3.12/site-packages/django/utils/dates.py:74 +msgctxt "alt. month" +msgid "August" +msgstr "" + +#: venv/lib/python3.12/site-packages/django/utils/dates.py:75 +msgctxt "alt. month" +msgid "September" +msgstr "" + +#: venv/lib/python3.12/site-packages/django/utils/dates.py:76 +msgctxt "alt. month" +msgid "October" +msgstr "" + +#: venv/lib/python3.12/site-packages/django/utils/dates.py:77 +msgctxt "alt. month" +msgid "November" +msgstr "" + +#: venv/lib/python3.12/site-packages/django/utils/dates.py:78 +msgctxt "alt. month" +msgid "December" +msgstr "" + +#: venv/lib/python3.12/site-packages/django/utils/ipv6.py:8 +msgid "This is not a valid IPv6 address." +msgstr "" + +#: venv/lib/python3.12/site-packages/django/utils/text.py:123 +#, python-format +msgctxt "String to return when truncating text" +msgid "%(truncated_text)s…" +msgstr "" + +#: venv/lib/python3.12/site-packages/django/utils/text.py:323 +msgid "or" +msgstr "" + +#. Translators: This string is used as a separator between list elements +#: venv/lib/python3.12/site-packages/django/utils/text.py:342 +#: venv/lib/python3.12/site-packages/django/utils/timesince.py:135 +msgid ", " +msgstr "" + +#: venv/lib/python3.12/site-packages/django/utils/timesince.py:8 +#, python-format +msgid "%(num)d year" +msgid_plural "%(num)d years" +msgstr[0] "" +msgstr[1] "" + +#: venv/lib/python3.12/site-packages/django/utils/timesince.py:9 +#, python-format +msgid "%(num)d month" +msgid_plural "%(num)d months" +msgstr[0] "" +msgstr[1] "" + +#: venv/lib/python3.12/site-packages/django/utils/timesince.py:10 +#, python-format +msgid "%(num)d week" +msgid_plural "%(num)d weeks" +msgstr[0] "" +msgstr[1] "" + +#: venv/lib/python3.12/site-packages/django/utils/timesince.py:11 +#, python-format +msgid "%(num)d day" +msgid_plural "%(num)d days" +msgstr[0] "" +msgstr[1] "" + +#: venv/lib/python3.12/site-packages/django/utils/timesince.py:12 +#, python-format +msgid "%(num)d hour" +msgid_plural "%(num)d hours" +msgstr[0] "" +msgstr[1] "" + +#: venv/lib/python3.12/site-packages/django/utils/timesince.py:13 +#, python-format +msgid "%(num)d minute" +msgid_plural "%(num)d minutes" +msgstr[0] "" +msgstr[1] "" + +#: venv/lib/python3.12/site-packages/django/views/csrf.py:29 +msgid "Forbidden" +msgstr "" + +#: venv/lib/python3.12/site-packages/django/views/csrf.py:30 +msgid "CSRF verification failed. Request aborted." +msgstr "" + +#: venv/lib/python3.12/site-packages/django/views/csrf.py:34 +msgid "" +"You are seeing this message because this HTTPS site requires a “Referer " +"header” to be sent by your web browser, but none was sent. This header is " +"required for security reasons, to ensure that your browser is not being " +"hijacked by third parties." +msgstr "" + +#: venv/lib/python3.12/site-packages/django/views/csrf.py:40 +msgid "" +"If you have configured your browser to disable “Referer” headers, please re-" +"enable them, at least for this site, or for HTTPS connections, or for “same-" +"origin” requests." +msgstr "" + +#: venv/lib/python3.12/site-packages/django/views/csrf.py:45 +msgid "" +"If you are using the tag or " +"including the “Referrer-Policy: no-referrer” header, please remove them. The " +"CSRF protection requires the “Referer” header to do strict referer checking. " +"If you’re concerned about privacy, use alternatives like for links to third-party sites." +msgstr "" + +#: venv/lib/python3.12/site-packages/django/views/csrf.py:54 +msgid "" +"You are seeing this message because this site requires a CSRF cookie when " +"submitting forms. This cookie is required for security reasons, to ensure " +"that your browser is not being hijacked by third parties." +msgstr "" + +#: venv/lib/python3.12/site-packages/django/views/csrf.py:60 +msgid "" +"If you have configured your browser to disable cookies, please re-enable " +"them, at least for this site, or for “same-origin” requests." +msgstr "" + +#: venv/lib/python3.12/site-packages/django/views/csrf.py:66 +msgid "More information is available with DEBUG=True." +msgstr "" + +#: venv/lib/python3.12/site-packages/django/views/generic/dates.py:44 +msgid "No year specified" +msgstr "" + +#: venv/lib/python3.12/site-packages/django/views/generic/dates.py:64 +#: venv/lib/python3.12/site-packages/django/views/generic/dates.py:115 +#: venv/lib/python3.12/site-packages/django/views/generic/dates.py:214 +msgid "Date out of range" +msgstr "" + +#: venv/lib/python3.12/site-packages/django/views/generic/dates.py:94 +msgid "No month specified" +msgstr "" + +#: venv/lib/python3.12/site-packages/django/views/generic/dates.py:147 +msgid "No day specified" +msgstr "" + +#: venv/lib/python3.12/site-packages/django/views/generic/dates.py:194 +msgid "No week specified" +msgstr "" + +#: venv/lib/python3.12/site-packages/django/views/generic/dates.py:349 +#: venv/lib/python3.12/site-packages/django/views/generic/dates.py:380 +#, python-format +msgid "No %(verbose_name_plural)s available" +msgstr "" + +#: venv/lib/python3.12/site-packages/django/views/generic/dates.py:652 +#, python-format +msgid "" +"Future %(verbose_name_plural)s not available because %(class_name)s." +"allow_future is False." +msgstr "" + +#: venv/lib/python3.12/site-packages/django/views/generic/dates.py:692 +#, python-format +msgid "Invalid date string “%(datestr)s” given format “%(format)s”" +msgstr "" + +#: venv/lib/python3.12/site-packages/django/views/generic/detail.py:56 +#, python-format +msgid "No %(verbose_name)s found matching the query" +msgstr "" + +#: venv/lib/python3.12/site-packages/django/views/generic/list.py:70 +msgid "Page is not “last”, nor can it be converted to an int." +msgstr "" + +#: venv/lib/python3.12/site-packages/django/views/generic/list.py:77 +#, python-format +msgid "Invalid page (%(page_number)s): %(message)s" +msgstr "" + +#: venv/lib/python3.12/site-packages/django/views/generic/list.py:169 +#, python-format +msgid "Empty list and “%(class_name)s.allow_empty” is False." +msgstr "" + +#: venv/lib/python3.12/site-packages/django/views/static.py:49 +msgid "Directory indexes are not allowed here." +msgstr "" + +#: venv/lib/python3.12/site-packages/django/views/static.py:51 +#, python-format +msgid "“%(path)s” does not exist" +msgstr "" + +#: venv/lib/python3.12/site-packages/django/views/static.py:68 +#: venv/lib/python3.12/site-packages/django/views/templates/directory_index.html:8 +#: venv/lib/python3.12/site-packages/django/views/templates/directory_index.html:11 +#, python-format +msgid "Index of %(directory)s" +msgstr "" + +#: venv/lib/python3.12/site-packages/django/views/templates/default_urlconf.html:7 +#: venv/lib/python3.12/site-packages/django/views/templates/default_urlconf.html:220 +msgid "The install worked successfully! Congratulations!" +msgstr "" + +#: venv/lib/python3.12/site-packages/django/views/templates/default_urlconf.html:206 +#, python-format +msgid "" +"View release notes for Django %(version)s" +msgstr "" + +#: venv/lib/python3.12/site-packages/django/views/templates/default_urlconf.html:221 +#, python-format +msgid "" +"You are seeing this page because DEBUG=True is in your settings file and you have not " +"configured any URLs." +msgstr "" + +#: venv/lib/python3.12/site-packages/django/views/templates/default_urlconf.html:229 +msgid "Django Documentation" +msgstr "" + +#: venv/lib/python3.12/site-packages/django/views/templates/default_urlconf.html:230 +msgid "Topics, references, & how-to’s" +msgstr "" + +#: venv/lib/python3.12/site-packages/django/views/templates/default_urlconf.html:238 +msgid "Tutorial: A Polling App" +msgstr "" + +#: venv/lib/python3.12/site-packages/django/views/templates/default_urlconf.html:239 +msgid "Get started with Django" +msgstr "" + +#: venv/lib/python3.12/site-packages/django/views/templates/default_urlconf.html:247 +msgid "Django Community" +msgstr "" + +#: venv/lib/python3.12/site-packages/django/views/templates/default_urlconf.html:248 +msgid "Connect, get help, or contribute" +msgstr "" diff --git a/locale/fr/LC_MESSAGES/django.po b/locale/fr/LC_MESSAGES/django.po new file mode 100644 index 0000000..492c8dd --- /dev/null +++ b/locale/fr/LC_MESSAGES/django.po @@ -0,0 +1,1241 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# FIRST AUTHOR , YEAR. +# +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2024-05-02 01:46+0200\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=2; plural=(n > 1);\n" + +#: kholles_web/settings.py:129 +msgid "English" +msgstr "" + +#: kholles_web/settings.py:130 +msgid "French" +msgstr "" + +#: venv/lib/python3.12/site-packages/django/contrib/messages/apps.py:16 +msgid "Messages" +msgstr "" + +#: venv/lib/python3.12/site-packages/django/contrib/sitemaps/apps.py:8 +msgid "Site Maps" +msgstr "" + +#: venv/lib/python3.12/site-packages/django/contrib/staticfiles/apps.py:9 +msgid "Static Files" +msgstr "" + +#: venv/lib/python3.12/site-packages/django/contrib/syndication/apps.py:7 +msgid "Syndication" +msgstr "" + +#. Translators: String used to replace omitted page numbers in elided page +#. range generated by paginators, e.g. [1, 2, '…', 5, 6, 7, '…', 9, 10]. +#: venv/lib/python3.12/site-packages/django/core/paginator.py:30 +msgid "…" +msgstr "" + +#: venv/lib/python3.12/site-packages/django/core/paginator.py:32 +msgid "That page number is not an integer" +msgstr "" + +#: venv/lib/python3.12/site-packages/django/core/paginator.py:33 +msgid "That page number is less than 1" +msgstr "" + +#: venv/lib/python3.12/site-packages/django/core/paginator.py:34 +msgid "That page contains no results" +msgstr "" + +#: venv/lib/python3.12/site-packages/django/core/validators.py:22 +msgid "Enter a valid value." +msgstr "" + +#: venv/lib/python3.12/site-packages/django/core/validators.py:104 +#: venv/lib/python3.12/site-packages/django/forms/fields.py:760 +msgid "Enter a valid URL." +msgstr "" + +#: venv/lib/python3.12/site-packages/django/core/validators.py:165 +msgid "Enter a valid integer." +msgstr "" + +#: venv/lib/python3.12/site-packages/django/core/validators.py:176 +msgid "Enter a valid email address." +msgstr "" + +#. Translators: "letters" means latin letters: a-z and A-Z. +#: venv/lib/python3.12/site-packages/django/core/validators.py:259 +msgid "" +"Enter a valid “slug” consisting of letters, numbers, underscores or hyphens." +msgstr "" + +#: venv/lib/python3.12/site-packages/django/core/validators.py:267 +msgid "" +"Enter a valid “slug” consisting of Unicode letters, numbers, underscores, or " +"hyphens." +msgstr "" + +#: venv/lib/python3.12/site-packages/django/core/validators.py:279 +#: venv/lib/python3.12/site-packages/django/core/validators.py:306 +msgid "Enter a valid IPv4 address." +msgstr "" + +#: venv/lib/python3.12/site-packages/django/core/validators.py:286 +#: venv/lib/python3.12/site-packages/django/core/validators.py:307 +msgid "Enter a valid IPv6 address." +msgstr "" + +#: venv/lib/python3.12/site-packages/django/core/validators.py:298 +#: venv/lib/python3.12/site-packages/django/core/validators.py:305 +msgid "Enter a valid IPv4 or IPv6 address." +msgstr "" + +#: venv/lib/python3.12/site-packages/django/core/validators.py:341 +msgid "Enter only digits separated by commas." +msgstr "" + +#: venv/lib/python3.12/site-packages/django/core/validators.py:347 +#, python-format +msgid "Ensure this value is %(limit_value)s (it is %(show_value)s)." +msgstr "" + +#: venv/lib/python3.12/site-packages/django/core/validators.py:382 +#, python-format +msgid "Ensure this value is less than or equal to %(limit_value)s." +msgstr "" + +#: venv/lib/python3.12/site-packages/django/core/validators.py:391 +#, python-format +msgid "Ensure this value is greater than or equal to %(limit_value)s." +msgstr "" + +#: venv/lib/python3.12/site-packages/django/core/validators.py:400 +#, python-format +msgid "Ensure this value is a multiple of step size %(limit_value)s." +msgstr "" + +#: venv/lib/python3.12/site-packages/django/core/validators.py:407 +#, python-format +msgid "" +"Ensure this value is a multiple of step size %(limit_value)s, starting from " +"%(offset)s, e.g. %(offset)s, %(valid_value1)s, %(valid_value2)s, and so on." +msgstr "" + +#: venv/lib/python3.12/site-packages/django/core/validators.py:439 +#, python-format +msgid "" +"Ensure this value has at least %(limit_value)d character (it has " +"%(show_value)d)." +msgid_plural "" +"Ensure this value has at least %(limit_value)d characters (it has " +"%(show_value)d)." +msgstr[0] "" +msgstr[1] "" + +#: venv/lib/python3.12/site-packages/django/core/validators.py:457 +#, python-format +msgid "" +"Ensure this value has at most %(limit_value)d character (it has " +"%(show_value)d)." +msgid_plural "" +"Ensure this value has at most %(limit_value)d characters (it has " +"%(show_value)d)." +msgstr[0] "" +msgstr[1] "" + +#: venv/lib/python3.12/site-packages/django/core/validators.py:480 +#: venv/lib/python3.12/site-packages/django/forms/fields.py:355 +#: venv/lib/python3.12/site-packages/django/forms/fields.py:394 +msgid "Enter a number." +msgstr "" + +#: venv/lib/python3.12/site-packages/django/core/validators.py:482 +#, python-format +msgid "Ensure that there are no more than %(max)s digit in total." +msgid_plural "Ensure that there are no more than %(max)s digits in total." +msgstr[0] "" +msgstr[1] "" + +#: venv/lib/python3.12/site-packages/django/core/validators.py:487 +#, python-format +msgid "Ensure that there are no more than %(max)s decimal place." +msgid_plural "Ensure that there are no more than %(max)s decimal places." +msgstr[0] "" +msgstr[1] "" + +#: venv/lib/python3.12/site-packages/django/core/validators.py:492 +#, python-format +msgid "" +"Ensure that there are no more than %(max)s digit before the decimal point." +msgid_plural "" +"Ensure that there are no more than %(max)s digits before the decimal point." +msgstr[0] "" +msgstr[1] "" + +#: venv/lib/python3.12/site-packages/django/core/validators.py:563 +#, python-format +msgid "" +"File extension “%(extension)s” is not allowed. Allowed extensions are: " +"%(allowed_extensions)s." +msgstr "" + +#: venv/lib/python3.12/site-packages/django/core/validators.py:624 +msgid "Null characters are not allowed." +msgstr "" + +#: venv/lib/python3.12/site-packages/django/db/models/base.py:1473 +#: venv/lib/python3.12/site-packages/django/forms/models.py:906 +msgid "and" +msgstr "" + +#: venv/lib/python3.12/site-packages/django/db/models/base.py:1475 +#, python-format +msgid "%(model_name)s with this %(field_labels)s already exists." +msgstr "" + +#: venv/lib/python3.12/site-packages/django/db/models/constraints.py:20 +#, python-format +msgid "Constraint “%(name)s” is violated." +msgstr "" + +#: venv/lib/python3.12/site-packages/django/db/models/fields/__init__.py:133 +#, python-format +msgid "Value %(value)r is not a valid choice." +msgstr "" + +#: venv/lib/python3.12/site-packages/django/db/models/fields/__init__.py:134 +msgid "This field cannot be null." +msgstr "" + +#: venv/lib/python3.12/site-packages/django/db/models/fields/__init__.py:135 +msgid "This field cannot be blank." +msgstr "" + +#: venv/lib/python3.12/site-packages/django/db/models/fields/__init__.py:136 +#, python-format +msgid "%(model_name)s with this %(field_label)s already exists." +msgstr "" + +#. Translators: The 'lookup_type' is one of 'date', 'year' or +#. 'month'. Eg: "Title must be unique for pub_date year" +#: venv/lib/python3.12/site-packages/django/db/models/fields/__init__.py:140 +#, python-format +msgid "" +"%(field_label)s must be unique for %(date_field_label)s %(lookup_type)s." +msgstr "" + +#: venv/lib/python3.12/site-packages/django/db/models/fields/__init__.py:179 +#, python-format +msgid "Field of type: %(field_type)s" +msgstr "" + +#: venv/lib/python3.12/site-packages/django/db/models/fields/__init__.py:1155 +#, python-format +msgid "“%(value)s” value must be either True or False." +msgstr "" + +#: venv/lib/python3.12/site-packages/django/db/models/fields/__init__.py:1156 +#, python-format +msgid "“%(value)s” value must be either True, False, or None." +msgstr "" + +#: venv/lib/python3.12/site-packages/django/db/models/fields/__init__.py:1158 +msgid "Boolean (Either True or False)" +msgstr "" + +#: venv/lib/python3.12/site-packages/django/db/models/fields/__init__.py:1208 +#, python-format +msgid "String (up to %(max_length)s)" +msgstr "" + +#: venv/lib/python3.12/site-packages/django/db/models/fields/__init__.py:1210 +msgid "String (unlimited)" +msgstr "" + +#: venv/lib/python3.12/site-packages/django/db/models/fields/__init__.py:1314 +msgid "Comma-separated integers" +msgstr "" + +#: venv/lib/python3.12/site-packages/django/db/models/fields/__init__.py:1415 +#, python-format +msgid "" +"“%(value)s” value has an invalid date format. It must be in YYYY-MM-DD " +"format." +msgstr "" + +#: venv/lib/python3.12/site-packages/django/db/models/fields/__init__.py:1419 +#: venv/lib/python3.12/site-packages/django/db/models/fields/__init__.py:1554 +#, python-format +msgid "" +"“%(value)s” value has the correct format (YYYY-MM-DD) but it is an invalid " +"date." +msgstr "" + +#: venv/lib/python3.12/site-packages/django/db/models/fields/__init__.py:1423 +msgid "Date (without time)" +msgstr "" + +#: venv/lib/python3.12/site-packages/django/db/models/fields/__init__.py:1550 +#, python-format +msgid "" +"“%(value)s” value has an invalid format. It must be in YYYY-MM-DD HH:MM[:ss[." +"uuuuuu]][TZ] format." +msgstr "" + +#: venv/lib/python3.12/site-packages/django/db/models/fields/__init__.py:1558 +#, python-format +msgid "" +"“%(value)s” value has the correct format (YYYY-MM-DD HH:MM[:ss[.uuuuuu]]" +"[TZ]) but it is an invalid date/time." +msgstr "" + +#: venv/lib/python3.12/site-packages/django/db/models/fields/__init__.py:1563 +msgid "Date (with time)" +msgstr "" + +#: venv/lib/python3.12/site-packages/django/db/models/fields/__init__.py:1690 +#, python-format +msgid "“%(value)s” value must be a decimal number." +msgstr "" + +#: venv/lib/python3.12/site-packages/django/db/models/fields/__init__.py:1692 +msgid "Decimal number" +msgstr "" + +#: venv/lib/python3.12/site-packages/django/db/models/fields/__init__.py:1853 +#, python-format +msgid "" +"“%(value)s” value has an invalid format. It must be in [DD] [[HH:]MM:]ss[." +"uuuuuu] format." +msgstr "" + +#: venv/lib/python3.12/site-packages/django/db/models/fields/__init__.py:1857 +msgid "Duration" +msgstr "" + +#: venv/lib/python3.12/site-packages/django/db/models/fields/__init__.py:1909 +msgid "Email address" +msgstr "" + +#: venv/lib/python3.12/site-packages/django/db/models/fields/__init__.py:1934 +msgid "File path" +msgstr "" + +#: venv/lib/python3.12/site-packages/django/db/models/fields/__init__.py:2012 +#, python-format +msgid "“%(value)s” value must be a float." +msgstr "" + +#: venv/lib/python3.12/site-packages/django/db/models/fields/__init__.py:2014 +msgid "Floating point number" +msgstr "" + +#: venv/lib/python3.12/site-packages/django/db/models/fields/__init__.py:2054 +#, python-format +msgid "“%(value)s” value must be an integer." +msgstr "" + +#: venv/lib/python3.12/site-packages/django/db/models/fields/__init__.py:2056 +msgid "Integer" +msgstr "" + +#: venv/lib/python3.12/site-packages/django/db/models/fields/__init__.py:2152 +msgid "Big (8 byte) integer" +msgstr "" + +#: venv/lib/python3.12/site-packages/django/db/models/fields/__init__.py:2169 +msgid "Small integer" +msgstr "" + +#: venv/lib/python3.12/site-packages/django/db/models/fields/__init__.py:2177 +msgid "IPv4 address" +msgstr "" + +#: venv/lib/python3.12/site-packages/django/db/models/fields/__init__.py:2208 +msgid "IP address" +msgstr "" + +#: venv/lib/python3.12/site-packages/django/db/models/fields/__init__.py:2301 +#: venv/lib/python3.12/site-packages/django/db/models/fields/__init__.py:2302 +#, python-format +msgid "“%(value)s” value must be either None, True or False." +msgstr "" + +#: venv/lib/python3.12/site-packages/django/db/models/fields/__init__.py:2304 +msgid "Boolean (Either True, False or None)" +msgstr "" + +#: venv/lib/python3.12/site-packages/django/db/models/fields/__init__.py:2355 +msgid "Positive big integer" +msgstr "" + +#: venv/lib/python3.12/site-packages/django/db/models/fields/__init__.py:2370 +msgid "Positive integer" +msgstr "" + +#: venv/lib/python3.12/site-packages/django/db/models/fields/__init__.py:2385 +msgid "Positive small integer" +msgstr "" + +#: venv/lib/python3.12/site-packages/django/db/models/fields/__init__.py:2401 +#, python-format +msgid "Slug (up to %(max_length)s)" +msgstr "" + +#: venv/lib/python3.12/site-packages/django/db/models/fields/__init__.py:2437 +msgid "Text" +msgstr "" + +#: venv/lib/python3.12/site-packages/django/db/models/fields/__init__.py:2512 +#, python-format +msgid "" +"“%(value)s” value has an invalid format. It must be in HH:MM[:ss[.uuuuuu]] " +"format." +msgstr "" + +#: venv/lib/python3.12/site-packages/django/db/models/fields/__init__.py:2516 +#, python-format +msgid "" +"“%(value)s” value has the correct format (HH:MM[:ss[.uuuuuu]]) but it is an " +"invalid time." +msgstr "" + +#: venv/lib/python3.12/site-packages/django/db/models/fields/__init__.py:2520 +msgid "Time" +msgstr "" + +#: venv/lib/python3.12/site-packages/django/db/models/fields/__init__.py:2628 +msgid "URL" +msgstr "" + +#: venv/lib/python3.12/site-packages/django/db/models/fields/__init__.py:2652 +msgid "Raw binary data" +msgstr "" + +#: venv/lib/python3.12/site-packages/django/db/models/fields/__init__.py:2717 +#, python-format +msgid "“%(value)s” is not a valid UUID." +msgstr "" + +#: venv/lib/python3.12/site-packages/django/db/models/fields/__init__.py:2719 +msgid "Universally unique identifier" +msgstr "" + +#: venv/lib/python3.12/site-packages/django/db/models/fields/files.py:232 +msgid "File" +msgstr "" + +#: venv/lib/python3.12/site-packages/django/db/models/fields/files.py:393 +msgid "Image" +msgstr "" + +#: venv/lib/python3.12/site-packages/django/db/models/fields/json.py:26 +msgid "A JSON object" +msgstr "" + +#: venv/lib/python3.12/site-packages/django/db/models/fields/json.py:28 +msgid "Value must be valid JSON." +msgstr "" + +#: venv/lib/python3.12/site-packages/django/db/models/fields/related.py:939 +#, python-format +msgid "%(model)s instance with %(field)s %(value)r does not exist." +msgstr "" + +#: venv/lib/python3.12/site-packages/django/db/models/fields/related.py:941 +msgid "Foreign Key (type determined by related field)" +msgstr "" + +#: venv/lib/python3.12/site-packages/django/db/models/fields/related.py:1235 +msgid "One-to-one relationship" +msgstr "" + +#: venv/lib/python3.12/site-packages/django/db/models/fields/related.py:1292 +#, python-format +msgid "%(from)s-%(to)s relationship" +msgstr "" + +#: venv/lib/python3.12/site-packages/django/db/models/fields/related.py:1294 +#, python-format +msgid "%(from)s-%(to)s relationships" +msgstr "" + +#: venv/lib/python3.12/site-packages/django/db/models/fields/related.py:1342 +msgid "Many-to-many relationship" +msgstr "" + +#. Translators: If found as last label character, these punctuation +#. characters will prevent the default label_suffix to be appended to the label +#: venv/lib/python3.12/site-packages/django/forms/boundfield.py:185 +msgid ":?.!" +msgstr "" + +#: venv/lib/python3.12/site-packages/django/forms/fields.py:95 +msgid "This field is required." +msgstr "" + +#: venv/lib/python3.12/site-packages/django/forms/fields.py:304 +msgid "Enter a whole number." +msgstr "" + +#: venv/lib/python3.12/site-packages/django/forms/fields.py:475 +#: venv/lib/python3.12/site-packages/django/forms/fields.py:1252 +msgid "Enter a valid date." +msgstr "" + +#: venv/lib/python3.12/site-packages/django/forms/fields.py:498 +#: venv/lib/python3.12/site-packages/django/forms/fields.py:1253 +msgid "Enter a valid time." +msgstr "" + +#: venv/lib/python3.12/site-packages/django/forms/fields.py:525 +msgid "Enter a valid date/time." +msgstr "" + +#: venv/lib/python3.12/site-packages/django/forms/fields.py:559 +msgid "Enter a valid duration." +msgstr "" + +#: venv/lib/python3.12/site-packages/django/forms/fields.py:560 +#, python-brace-format +msgid "The number of days must be between {min_days} and {max_days}." +msgstr "" + +#: venv/lib/python3.12/site-packages/django/forms/fields.py:629 +msgid "No file was submitted. Check the encoding type on the form." +msgstr "" + +#: venv/lib/python3.12/site-packages/django/forms/fields.py:630 +msgid "No file was submitted." +msgstr "" + +#: venv/lib/python3.12/site-packages/django/forms/fields.py:631 +msgid "The submitted file is empty." +msgstr "" + +#: venv/lib/python3.12/site-packages/django/forms/fields.py:633 +#, python-format +msgid "Ensure this filename has at most %(max)d character (it has %(length)d)." +msgid_plural "" +"Ensure this filename has at most %(max)d characters (it has %(length)d)." +msgstr[0] "" +msgstr[1] "" + +#: venv/lib/python3.12/site-packages/django/forms/fields.py:638 +msgid "Please either submit a file or check the clear checkbox, not both." +msgstr "" + +#: venv/lib/python3.12/site-packages/django/forms/fields.py:702 +msgid "" +"Upload a valid image. The file you uploaded was either not an image or a " +"corrupted image." +msgstr "" + +#: venv/lib/python3.12/site-packages/django/forms/fields.py:874 +#: venv/lib/python3.12/site-packages/django/forms/fields.py:960 +#: venv/lib/python3.12/site-packages/django/forms/models.py:1585 +#, python-format +msgid "Select a valid choice. %(value)s is not one of the available choices." +msgstr "" + +#: venv/lib/python3.12/site-packages/django/forms/fields.py:962 +#: venv/lib/python3.12/site-packages/django/forms/fields.py:1081 +#: venv/lib/python3.12/site-packages/django/forms/models.py:1583 +msgid "Enter a list of values." +msgstr "" + +#: venv/lib/python3.12/site-packages/django/forms/fields.py:1082 +msgid "Enter a complete value." +msgstr "" + +#: venv/lib/python3.12/site-packages/django/forms/fields.py:1321 +msgid "Enter a valid UUID." +msgstr "" + +#: venv/lib/python3.12/site-packages/django/forms/fields.py:1351 +msgid "Enter a valid JSON." +msgstr "" + +#. Translators: This is the default suffix added to form field labels +#: venv/lib/python3.12/site-packages/django/forms/forms.py:94 +msgid ":" +msgstr "" + +#: venv/lib/python3.12/site-packages/django/forms/forms.py:231 +#, python-format +msgid "(Hidden field %(name)s) %(error)s" +msgstr "" + +#: venv/lib/python3.12/site-packages/django/forms/formsets.py:61 +#, python-format +msgid "" +"ManagementForm data is missing or has been tampered with. Missing fields: " +"%(field_names)s. You may need to file a bug report if the issue persists." +msgstr "" + +#: venv/lib/python3.12/site-packages/django/forms/formsets.py:65 +#, python-format +msgid "Please submit at most %(num)d form." +msgid_plural "Please submit at most %(num)d forms." +msgstr[0] "" +msgstr[1] "" + +#: venv/lib/python3.12/site-packages/django/forms/formsets.py:70 +#, python-format +msgid "Please submit at least %(num)d form." +msgid_plural "Please submit at least %(num)d forms." +msgstr[0] "" +msgstr[1] "" + +#: venv/lib/python3.12/site-packages/django/forms/formsets.py:484 +#: venv/lib/python3.12/site-packages/django/forms/formsets.py:491 +msgid "Order" +msgstr "" + +#: venv/lib/python3.12/site-packages/django/forms/formsets.py:499 +msgid "Delete" +msgstr "" + +#: venv/lib/python3.12/site-packages/django/forms/models.py:899 +#, python-format +msgid "Please correct the duplicate data for %(field)s." +msgstr "" + +#: venv/lib/python3.12/site-packages/django/forms/models.py:904 +#, python-format +msgid "Please correct the duplicate data for %(field)s, which must be unique." +msgstr "" + +#: venv/lib/python3.12/site-packages/django/forms/models.py:911 +#, python-format +msgid "" +"Please correct the duplicate data for %(field_name)s which must be unique " +"for the %(lookup)s in %(date_field)s." +msgstr "" + +#: venv/lib/python3.12/site-packages/django/forms/models.py:920 +msgid "Please correct the duplicate values below." +msgstr "" + +#: venv/lib/python3.12/site-packages/django/forms/models.py:1357 +msgid "The inline value did not match the parent instance." +msgstr "" + +#: venv/lib/python3.12/site-packages/django/forms/models.py:1448 +msgid "Select a valid choice. That choice is not one of the available choices." +msgstr "" + +#: venv/lib/python3.12/site-packages/django/forms/models.py:1587 +#, python-format +msgid "“%(pk)s” is not a valid value." +msgstr "" + +#: venv/lib/python3.12/site-packages/django/forms/utils.py:227 +#, python-format +msgid "" +"%(datetime)s couldn’t be interpreted in time zone %(current_timezone)s; it " +"may be ambiguous or it may not exist." +msgstr "" + +#: venv/lib/python3.12/site-packages/django/forms/widgets.py:461 +msgid "Clear" +msgstr "" + +#: venv/lib/python3.12/site-packages/django/forms/widgets.py:462 +msgid "Currently" +msgstr "" + +#: venv/lib/python3.12/site-packages/django/forms/widgets.py:463 +msgid "Change" +msgstr "" + +#: venv/lib/python3.12/site-packages/django/forms/widgets.py:800 +msgid "Unknown" +msgstr "" + +#: venv/lib/python3.12/site-packages/django/forms/widgets.py:801 +msgid "Yes" +msgstr "" + +#: venv/lib/python3.12/site-packages/django/forms/widgets.py:802 +msgid "No" +msgstr "" + +#. Translators: Please do not add spaces around commas. +#: venv/lib/python3.12/site-packages/django/template/defaultfilters.py:876 +msgid "yes,no,maybe" +msgstr "" + +#: venv/lib/python3.12/site-packages/django/template/defaultfilters.py:906 +#: venv/lib/python3.12/site-packages/django/template/defaultfilters.py:923 +#, python-format +msgid "%(size)d byte" +msgid_plural "%(size)d bytes" +msgstr[0] "" +msgstr[1] "" + +#: venv/lib/python3.12/site-packages/django/template/defaultfilters.py:925 +#, python-format +msgid "%s KB" +msgstr "" + +#: venv/lib/python3.12/site-packages/django/template/defaultfilters.py:927 +#, python-format +msgid "%s MB" +msgstr "" + +#: venv/lib/python3.12/site-packages/django/template/defaultfilters.py:929 +#, python-format +msgid "%s GB" +msgstr "" + +#: venv/lib/python3.12/site-packages/django/template/defaultfilters.py:931 +#, python-format +msgid "%s TB" +msgstr "" + +#: venv/lib/python3.12/site-packages/django/template/defaultfilters.py:933 +#, python-format +msgid "%s PB" +msgstr "" + +#: venv/lib/python3.12/site-packages/django/utils/dateformat.py:74 +msgid "p.m." +msgstr "" + +#: venv/lib/python3.12/site-packages/django/utils/dateformat.py:75 +msgid "a.m." +msgstr "" + +#: venv/lib/python3.12/site-packages/django/utils/dateformat.py:80 +msgid "PM" +msgstr "" + +#: venv/lib/python3.12/site-packages/django/utils/dateformat.py:81 +msgid "AM" +msgstr "" + +#: venv/lib/python3.12/site-packages/django/utils/dateformat.py:153 +msgid "midnight" +msgstr "" + +#: venv/lib/python3.12/site-packages/django/utils/dateformat.py:155 +msgid "noon" +msgstr "" + +#: venv/lib/python3.12/site-packages/django/utils/dates.py:7 +msgid "Monday" +msgstr "" + +#: venv/lib/python3.12/site-packages/django/utils/dates.py:8 +msgid "Tuesday" +msgstr "" + +#: venv/lib/python3.12/site-packages/django/utils/dates.py:9 +msgid "Wednesday" +msgstr "" + +#: venv/lib/python3.12/site-packages/django/utils/dates.py:10 +msgid "Thursday" +msgstr "" + +#: venv/lib/python3.12/site-packages/django/utils/dates.py:11 +msgid "Friday" +msgstr "" + +#: venv/lib/python3.12/site-packages/django/utils/dates.py:12 +msgid "Saturday" +msgstr "" + +#: venv/lib/python3.12/site-packages/django/utils/dates.py:13 +msgid "Sunday" +msgstr "" + +#: venv/lib/python3.12/site-packages/django/utils/dates.py:16 +msgid "Mon" +msgstr "" + +#: venv/lib/python3.12/site-packages/django/utils/dates.py:17 +msgid "Tue" +msgstr "" + +#: venv/lib/python3.12/site-packages/django/utils/dates.py:18 +msgid "Wed" +msgstr "" + +#: venv/lib/python3.12/site-packages/django/utils/dates.py:19 +msgid "Thu" +msgstr "" + +#: venv/lib/python3.12/site-packages/django/utils/dates.py:20 +msgid "Fri" +msgstr "" + +#: venv/lib/python3.12/site-packages/django/utils/dates.py:21 +msgid "Sat" +msgstr "" + +#: venv/lib/python3.12/site-packages/django/utils/dates.py:22 +msgid "Sun" +msgstr "" + +#: venv/lib/python3.12/site-packages/django/utils/dates.py:25 +msgid "January" +msgstr "" + +#: venv/lib/python3.12/site-packages/django/utils/dates.py:26 +msgid "February" +msgstr "" + +#: venv/lib/python3.12/site-packages/django/utils/dates.py:27 +msgid "March" +msgstr "" + +#: venv/lib/python3.12/site-packages/django/utils/dates.py:28 +msgid "April" +msgstr "" + +#: venv/lib/python3.12/site-packages/django/utils/dates.py:29 +msgid "May" +msgstr "" + +#: venv/lib/python3.12/site-packages/django/utils/dates.py:30 +msgid "June" +msgstr "" + +#: venv/lib/python3.12/site-packages/django/utils/dates.py:31 +msgid "July" +msgstr "" + +#: venv/lib/python3.12/site-packages/django/utils/dates.py:32 +msgid "August" +msgstr "" + +#: venv/lib/python3.12/site-packages/django/utils/dates.py:33 +msgid "September" +msgstr "" + +#: venv/lib/python3.12/site-packages/django/utils/dates.py:34 +msgid "October" +msgstr "" + +#: venv/lib/python3.12/site-packages/django/utils/dates.py:35 +msgid "November" +msgstr "" + +#: venv/lib/python3.12/site-packages/django/utils/dates.py:36 +msgid "December" +msgstr "" + +#: venv/lib/python3.12/site-packages/django/utils/dates.py:39 +msgid "jan" +msgstr "" + +#: venv/lib/python3.12/site-packages/django/utils/dates.py:40 +msgid "feb" +msgstr "" + +#: venv/lib/python3.12/site-packages/django/utils/dates.py:41 +msgid "mar" +msgstr "" + +#: venv/lib/python3.12/site-packages/django/utils/dates.py:42 +msgid "apr" +msgstr "" + +#: venv/lib/python3.12/site-packages/django/utils/dates.py:43 +msgid "may" +msgstr "" + +#: venv/lib/python3.12/site-packages/django/utils/dates.py:44 +msgid "jun" +msgstr "" + +#: venv/lib/python3.12/site-packages/django/utils/dates.py:45 +msgid "jul" +msgstr "" + +#: venv/lib/python3.12/site-packages/django/utils/dates.py:46 +msgid "aug" +msgstr "" + +#: venv/lib/python3.12/site-packages/django/utils/dates.py:47 +msgid "sep" +msgstr "" + +#: venv/lib/python3.12/site-packages/django/utils/dates.py:48 +msgid "oct" +msgstr "" + +#: venv/lib/python3.12/site-packages/django/utils/dates.py:49 +msgid "nov" +msgstr "" + +#: venv/lib/python3.12/site-packages/django/utils/dates.py:50 +msgid "dec" +msgstr "" + +#: venv/lib/python3.12/site-packages/django/utils/dates.py:53 +msgctxt "abbrev. month" +msgid "Jan." +msgstr "" + +#: venv/lib/python3.12/site-packages/django/utils/dates.py:54 +msgctxt "abbrev. month" +msgid "Feb." +msgstr "" + +#: venv/lib/python3.12/site-packages/django/utils/dates.py:55 +msgctxt "abbrev. month" +msgid "March" +msgstr "" + +#: venv/lib/python3.12/site-packages/django/utils/dates.py:56 +msgctxt "abbrev. month" +msgid "April" +msgstr "" + +#: venv/lib/python3.12/site-packages/django/utils/dates.py:57 +msgctxt "abbrev. month" +msgid "May" +msgstr "" + +#: venv/lib/python3.12/site-packages/django/utils/dates.py:58 +msgctxt "abbrev. month" +msgid "June" +msgstr "" + +#: venv/lib/python3.12/site-packages/django/utils/dates.py:59 +msgctxt "abbrev. month" +msgid "July" +msgstr "" + +#: venv/lib/python3.12/site-packages/django/utils/dates.py:60 +msgctxt "abbrev. month" +msgid "Aug." +msgstr "" + +#: venv/lib/python3.12/site-packages/django/utils/dates.py:61 +msgctxt "abbrev. month" +msgid "Sept." +msgstr "" + +#: venv/lib/python3.12/site-packages/django/utils/dates.py:62 +msgctxt "abbrev. month" +msgid "Oct." +msgstr "" + +#: venv/lib/python3.12/site-packages/django/utils/dates.py:63 +msgctxt "abbrev. month" +msgid "Nov." +msgstr "" + +#: venv/lib/python3.12/site-packages/django/utils/dates.py:64 +msgctxt "abbrev. month" +msgid "Dec." +msgstr "" + +#: venv/lib/python3.12/site-packages/django/utils/dates.py:67 +msgctxt "alt. month" +msgid "January" +msgstr "" + +#: venv/lib/python3.12/site-packages/django/utils/dates.py:68 +msgctxt "alt. month" +msgid "February" +msgstr "" + +#: venv/lib/python3.12/site-packages/django/utils/dates.py:69 +msgctxt "alt. month" +msgid "March" +msgstr "" + +#: venv/lib/python3.12/site-packages/django/utils/dates.py:70 +msgctxt "alt. month" +msgid "April" +msgstr "" + +#: venv/lib/python3.12/site-packages/django/utils/dates.py:71 +msgctxt "alt. month" +msgid "May" +msgstr "" + +#: venv/lib/python3.12/site-packages/django/utils/dates.py:72 +msgctxt "alt. month" +msgid "June" +msgstr "" + +#: venv/lib/python3.12/site-packages/django/utils/dates.py:73 +msgctxt "alt. month" +msgid "July" +msgstr "" + +#: venv/lib/python3.12/site-packages/django/utils/dates.py:74 +msgctxt "alt. month" +msgid "August" +msgstr "" + +#: venv/lib/python3.12/site-packages/django/utils/dates.py:75 +msgctxt "alt. month" +msgid "September" +msgstr "" + +#: venv/lib/python3.12/site-packages/django/utils/dates.py:76 +msgctxt "alt. month" +msgid "October" +msgstr "" + +#: venv/lib/python3.12/site-packages/django/utils/dates.py:77 +msgctxt "alt. month" +msgid "November" +msgstr "" + +#: venv/lib/python3.12/site-packages/django/utils/dates.py:78 +msgctxt "alt. month" +msgid "December" +msgstr "" + +#: venv/lib/python3.12/site-packages/django/utils/ipv6.py:8 +msgid "This is not a valid IPv6 address." +msgstr "" + +#: venv/lib/python3.12/site-packages/django/utils/text.py:123 +#, python-format +msgctxt "String to return when truncating text" +msgid "%(truncated_text)s…" +msgstr "" + +#: venv/lib/python3.12/site-packages/django/utils/text.py:323 +msgid "or" +msgstr "" + +#. Translators: This string is used as a separator between list elements +#: venv/lib/python3.12/site-packages/django/utils/text.py:342 +#: venv/lib/python3.12/site-packages/django/utils/timesince.py:135 +msgid ", " +msgstr "" + +#: venv/lib/python3.12/site-packages/django/utils/timesince.py:8 +#, python-format +msgid "%(num)d year" +msgid_plural "%(num)d years" +msgstr[0] "" +msgstr[1] "" + +#: venv/lib/python3.12/site-packages/django/utils/timesince.py:9 +#, python-format +msgid "%(num)d month" +msgid_plural "%(num)d months" +msgstr[0] "" +msgstr[1] "" + +#: venv/lib/python3.12/site-packages/django/utils/timesince.py:10 +#, python-format +msgid "%(num)d week" +msgid_plural "%(num)d weeks" +msgstr[0] "" +msgstr[1] "" + +#: venv/lib/python3.12/site-packages/django/utils/timesince.py:11 +#, python-format +msgid "%(num)d day" +msgid_plural "%(num)d days" +msgstr[0] "" +msgstr[1] "" + +#: venv/lib/python3.12/site-packages/django/utils/timesince.py:12 +#, python-format +msgid "%(num)d hour" +msgid_plural "%(num)d hours" +msgstr[0] "" +msgstr[1] "" + +#: venv/lib/python3.12/site-packages/django/utils/timesince.py:13 +#, python-format +msgid "%(num)d minute" +msgid_plural "%(num)d minutes" +msgstr[0] "" +msgstr[1] "" + +#: venv/lib/python3.12/site-packages/django/views/csrf.py:29 +msgid "Forbidden" +msgstr "" + +#: venv/lib/python3.12/site-packages/django/views/csrf.py:30 +msgid "CSRF verification failed. Request aborted." +msgstr "" + +#: venv/lib/python3.12/site-packages/django/views/csrf.py:34 +msgid "" +"You are seeing this message because this HTTPS site requires a “Referer " +"header” to be sent by your web browser, but none was sent. This header is " +"required for security reasons, to ensure that your browser is not being " +"hijacked by third parties." +msgstr "" + +#: venv/lib/python3.12/site-packages/django/views/csrf.py:40 +msgid "" +"If you have configured your browser to disable “Referer” headers, please re-" +"enable them, at least for this site, or for HTTPS connections, or for “same-" +"origin” requests." +msgstr "" + +#: venv/lib/python3.12/site-packages/django/views/csrf.py:45 +msgid "" +"If you are using the tag or " +"including the “Referrer-Policy: no-referrer” header, please remove them. The " +"CSRF protection requires the “Referer” header to do strict referer checking. " +"If you’re concerned about privacy, use alternatives like for links to third-party sites." +msgstr "" + +#: venv/lib/python3.12/site-packages/django/views/csrf.py:54 +msgid "" +"You are seeing this message because this site requires a CSRF cookie when " +"submitting forms. This cookie is required for security reasons, to ensure " +"that your browser is not being hijacked by third parties." +msgstr "" + +#: venv/lib/python3.12/site-packages/django/views/csrf.py:60 +msgid "" +"If you have configured your browser to disable cookies, please re-enable " +"them, at least for this site, or for “same-origin” requests." +msgstr "" + +#: venv/lib/python3.12/site-packages/django/views/csrf.py:66 +msgid "More information is available with DEBUG=True." +msgstr "" + +#: venv/lib/python3.12/site-packages/django/views/generic/dates.py:44 +msgid "No year specified" +msgstr "" + +#: venv/lib/python3.12/site-packages/django/views/generic/dates.py:64 +#: venv/lib/python3.12/site-packages/django/views/generic/dates.py:115 +#: venv/lib/python3.12/site-packages/django/views/generic/dates.py:214 +msgid "Date out of range" +msgstr "" + +#: venv/lib/python3.12/site-packages/django/views/generic/dates.py:94 +msgid "No month specified" +msgstr "" + +#: venv/lib/python3.12/site-packages/django/views/generic/dates.py:147 +msgid "No day specified" +msgstr "" + +#: venv/lib/python3.12/site-packages/django/views/generic/dates.py:194 +msgid "No week specified" +msgstr "" + +#: venv/lib/python3.12/site-packages/django/views/generic/dates.py:349 +#: venv/lib/python3.12/site-packages/django/views/generic/dates.py:380 +#, python-format +msgid "No %(verbose_name_plural)s available" +msgstr "" + +#: venv/lib/python3.12/site-packages/django/views/generic/dates.py:652 +#, python-format +msgid "" +"Future %(verbose_name_plural)s not available because %(class_name)s." +"allow_future is False." +msgstr "" + +#: venv/lib/python3.12/site-packages/django/views/generic/dates.py:692 +#, python-format +msgid "Invalid date string “%(datestr)s” given format “%(format)s”" +msgstr "" + +#: venv/lib/python3.12/site-packages/django/views/generic/detail.py:56 +#, python-format +msgid "No %(verbose_name)s found matching the query" +msgstr "" + +#: venv/lib/python3.12/site-packages/django/views/generic/list.py:70 +msgid "Page is not “last”, nor can it be converted to an int." +msgstr "" + +#: venv/lib/python3.12/site-packages/django/views/generic/list.py:77 +#, python-format +msgid "Invalid page (%(page_number)s): %(message)s" +msgstr "" + +#: venv/lib/python3.12/site-packages/django/views/generic/list.py:169 +#, python-format +msgid "Empty list and “%(class_name)s.allow_empty” is False." +msgstr "" + +#: venv/lib/python3.12/site-packages/django/views/static.py:49 +msgid "Directory indexes are not allowed here." +msgstr "" + +#: venv/lib/python3.12/site-packages/django/views/static.py:51 +#, python-format +msgid "“%(path)s” does not exist" +msgstr "" + +#: venv/lib/python3.12/site-packages/django/views/static.py:68 +#: venv/lib/python3.12/site-packages/django/views/templates/directory_index.html:8 +#: venv/lib/python3.12/site-packages/django/views/templates/directory_index.html:11 +#, python-format +msgid "Index of %(directory)s" +msgstr "" + +#: venv/lib/python3.12/site-packages/django/views/templates/default_urlconf.html:7 +#: venv/lib/python3.12/site-packages/django/views/templates/default_urlconf.html:220 +msgid "The install worked successfully! Congratulations!" +msgstr "" + +#: venv/lib/python3.12/site-packages/django/views/templates/default_urlconf.html:206 +#, python-format +msgid "" +"View release notes for Django %(version)s" +msgstr "" + +#: venv/lib/python3.12/site-packages/django/views/templates/default_urlconf.html:221 +#, python-format +msgid "" +"You are seeing this page because DEBUG=True is in your settings file and you have not " +"configured any URLs." +msgstr "" + +#: venv/lib/python3.12/site-packages/django/views/templates/default_urlconf.html:229 +msgid "Django Documentation" +msgstr "" + +#: venv/lib/python3.12/site-packages/django/views/templates/default_urlconf.html:230 +msgid "Topics, references, & how-to’s" +msgstr "" + +#: venv/lib/python3.12/site-packages/django/views/templates/default_urlconf.html:238 +msgid "Tutorial: A Polling App" +msgstr "" + +#: venv/lib/python3.12/site-packages/django/views/templates/default_urlconf.html:239 +msgid "Get started with Django" +msgstr "" + +#: venv/lib/python3.12/site-packages/django/views/templates/default_urlconf.html:247 +msgid "Django Community" +msgstr "" + +#: venv/lib/python3.12/site-packages/django/views/templates/default_urlconf.html:248 +msgid "Connect, get help, or contribute" +msgstr "" diff --git a/scrape_csv.py b/scrape_csv.py index 71f8346..04ef0ef 100644 --- a/scrape_csv.py +++ b/scrape_csv.py @@ -20,11 +20,11 @@ def scrape(periode, chemin): else: c = Colleur.objects.get(nom=nom_colleur, civilite=civilite) - if not Matiere.objects.filter(classe=periode.classe, libelle=matiere).exists(): - m = Matiere(classe=periode.classe, libelle=matiere, code=matiere.upper()) + if not Subject.objects.filter(classe=periode.classe, libelle=matiere).exists(): + m = Subject(classe=periode.classe, libelle=matiere, code=matiere.upper()) m.save() else: - m = Matiere.objects.get(classe=periode.classe, libelle=matiere) + m = Subject.objects.get(classe=periode.classe, libelle=matiere) jours_dict = {"dimanche": 0, "lundi": 1, "mardi": 2, "mercredi": 3, "jeudi": 4, "vendredi": 5, "samedi": 6} j = jours_dict[jour] @@ -40,11 +40,11 @@ def scrape(periode, chemin): print(f"--> Traitement de {c=}, {m=}, {j=}, {h=}, {d=}, {c2=}") - if not Creneau.objects.filter(periode=periode, jour=j, heure=h, duree=d, matiere=m, colleur=c, est_colle=True, capacite=c2).exists(): - creneau = Creneau(periode=periode, jour=j, heure=h, duree=d, salle="nc", matiere=m, colleur=c, est_colle=True, capacite=c2) + if not Slot.objects.filter(periode=periode, jour=j, heure=h, duree=d, matiere=m, colleur=c, est_colle=True, capacite=c2).exists(): + creneau = Slot(periode=periode, jour=j, heure=h, duree=d, salle="nc", matiere=m, colleur=c, est_colle=True, capacite=c2) creneau.save() else: - creneau = Creneau.objects.get(periode=periode, jour=j, heure=h, duree=d, matiere=m, colleur=c, est_colle=True, capacite=c2) + creneau = Slot.objects.get(periode=periode, jour=j, heure=h, duree=d, matiere=m, colleur=c, est_colle=True, capacite=c2) for i, r in enumerate(rotations): sem = headers[4+i].split("/") @@ -53,16 +53,16 @@ def scrape(periode, chemin): s = date.fromisoformat("-".join(sem)) + (j-1) * timedelta(days=1) - if not Rotation.objects.filter(creneau=creneau, date=s): - rot = Rotation(creneau=creneau, date=s) + if not Colle.objects.filter(creneau=creneau, date=s): + rot = Colle(creneau=creneau, date=s) rot.save() else: - rot = Rotation.objects.get(creneau=creneau, date=s) + rot = Colle.objects.get(creneau=creneau, date=s) - rot.groupes.add(Groupe.objects.get(libelle=r)) + rot.groupes.add(Group.objects.get(libelle=r)) def main(): - periode = Periode.objects.get(id=3) + periode = Term.objects.get(id=3) scrape(periode, "colloscope.csv") if __name__ == "__main__": diff --git a/static/favicon.ico b/static/favicon.ico new file mode 100644 index 0000000000000000000000000000000000000000..d522d6f2bd992a7551d0f3472c2682b20043a954 GIT binary patch literal 25354 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 J&%0** literal 0 HcmV?d00001 diff --git a/templates/404.html b/templates/404.html index c00e0b7..a8e1f16 100644 --- a/templates/404.html +++ b/templates/404.html @@ -7,5 +7,5 @@ {% endblock header %} {% block main %} -Vous vous êtes perdu. +Vous vous êtes perdu. ASKMULLER {% endblock main %} diff --git a/templates/base.html b/templates/base.html index 780f22c..09e80fa 100644 --- a/templates/base.html +++ b/templates/base.html @@ -13,7 +13,7 @@ {% if request.user.is_authenticated %}
Vous êtes connecté avec le compte {{ user.username }}. - {% if request.session.profil == "etudiant" %} + {% if request.session.profil == "student" %} Profil actuel : étudiant. {% elif request.session.profil == "colleur" %} Profil actuel : colleur.