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/settings.py b/kholles_web/settings.py index a3a3211..9bc1f90 100644 --- a/kholles_web/settings.py +++ b/kholles_web/settings.py @@ -10,6 +10,7 @@ For the full list of settings and their values, see https://docs.djangoproject.com/en/5.0/ref/settings/ """ +from django.utils.translation import gettext_lazy as _ from pathlib import Path # Build paths inside the project like this: BASE_DIR / 'subdir'. @@ -52,6 +53,7 @@ INSTALLED_APPS = [ MIDDLEWARE = [ 'django.middleware.security.SecurityMiddleware', 'django.contrib.sessions.middleware.SessionMiddleware', + 'django.middleware.locale.LocaleMiddleware', 'django.middleware.common.CommonMiddleware', 'django.middleware.csrf.CsrfViewMiddleware', 'django.contrib.auth.middleware.AuthenticationMiddleware', @@ -111,7 +113,7 @@ AUTH_PASSWORD_VALIDATORS = [ # Internationalization # https://docs.djangoproject.com/en/5.0/topics/i18n/ -LANGUAGE_CODE = 'fr-fr' +LANGUAGE_CODE = 'fr' TIME_ZONE = 'Europe/Paris' @@ -119,6 +121,15 @@ USE_I18N = True USE_TZ = True +LOCALE_PATHS = [ + BASE_DIR / 'locale', +] + +LANGUAGES = ( + ('en', _('English')), + ('fr', _('French')), +) + # Static files (CSS, JavaScript, Images) # https://docs.djangoproject.com/en/5.0/howto/static-files/ @@ -138,10 +149,18 @@ STATICFILES_FINDERS = [ DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField' -LOGIN_URL = "/comptes/login" +LOGIN_URL = "/accounts/login" LOGIN_REDIRECT_URL = "home" LOGOUT_REDIRECT_URL = "home" DISCORD_NOTIFY_WEBHOOK_URL = ("https://discord.com/api/webhooks/1234891678716919818/8OsTExc8ON2iop-AE_hO7XTe" "-ZCycQejNIjj22XLh9K0TnevW4IsQezAuAqPM5LY3jHP") DISCORD_NOTIFY_WEBHOOK_USERNAME = "Watchdog" + + +EMAIL_BACKEND = "django.core.mail.backends.smtp.EmailBackend" +EMAIL_HOST = "pulp.o2switch.net" +EMAIL_PORT = 587 +EMAIL_HOST_USER = "colloscope@mp2i-vms.fr" +EMAIL_HOST_PASSWORD = "phoBTchc2vVaq$PefsntT9M7" +EMAIL_USE_TLS = True \ No newline at end of file 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 0000000..d522d6f Binary files /dev/null and b/static/favicon.ico differ 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.