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
-{% for n, lundi, colles in rotations %}
+{% for n, lundi, colles in colles_per_sem %}
- Semaine {{n}} ({{lundi}})
- {% for colle in colles %}
- - {{ colle.creneau.matiere }} ({{ colle.creneau.colleur }}) Absent ?:
-
- - Le {{ colle.date }} à {{ colle.creneau.heure }}
- - Groupes : {{ colle.groupes.all | join:"+" }}
- - Salle : {{ colle.creneau.salle }}
- - Capacité : {{ colle.volume }} / {{ colle.creneau.capacite }}
-
- {% endfor %}
+ {% if colles %}
+ {% for colle in colles %}
+ - {{ colle.slot.subject }} ({{ colle.slot.colleur }})
+
+ - Le {{ colle.date }} à {{ colle.slot.time }}
+ - Groupes : {{ colle.groups.all | join:"+" }}
+ - Salle : {{ colle.slot.room }}
+ - Capacité : {{ colle.volume }} / {{ colle.slot.capacity }}
+ - Absent ?
+
+
+
+ {% endfor %}
+ {% else %}
+ Pas de colles à venir cette semaine.
+ {% endif %}
{% endfor %}
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 :
-
-
- {% for colle in colles %}
- - Colle !
+ {% if colles %}
- - {{ colle }}
- - Places occupées : {{ colle.volume }} / {{ colle.creneau.capacite }}
- - Réserver
+ Les colles libres sont :
+ {% for colle in colles %}
+ - Colle !
+
+ - {{ colle }}
+ - Places occupées : {{ colle.volume }} / {{ colle.slot.capacity }}
+ -
+
+
+
+ {% endfor %}
- {% endfor %}
-
+ {% 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 }}
- {% 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.