diff --git a/colloscope/migrations/0002_alter_periode_options_remove_appartenance_periode.py b/colloscope/migrations/0002_alter_periode_options_remove_appartenance_periode.py new file mode 100644 index 0000000..74077ac --- /dev/null +++ b/colloscope/migrations/0002_alter_periode_options_remove_appartenance_periode.py @@ -0,0 +1,21 @@ +# Generated by Django 5.0.4 on 2024-04-15 00:12 + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('colloscope', '0001_initial'), + ] + + operations = [ + migrations.AlterModelOptions( + name='periode', + options={'ordering': ['debut']}, + ), + migrations.RemoveField( + model_name='appartenance', + name='periode', + ), + ] diff --git a/colloscope/migrations/0003_remove_creneau_classe.py b/colloscope/migrations/0003_remove_creneau_classe.py new file mode 100644 index 0000000..2b41ab9 --- /dev/null +++ b/colloscope/migrations/0003_remove_creneau_classe.py @@ -0,0 +1,17 @@ +# Generated by Django 5.0.4 on 2024-04-15 00:14 + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('colloscope', '0002_alter_periode_options_remove_appartenance_periode'), + ] + + operations = [ + migrations.RemoveField( + model_name='creneau', + name='classe', + ), + ] diff --git a/colloscope/migrations/0004_matiere_classe.py b/colloscope/migrations/0004_matiere_classe.py new file mode 100644 index 0000000..24a29e8 --- /dev/null +++ b/colloscope/migrations/0004_matiere_classe.py @@ -0,0 +1,20 @@ +# Generated by Django 5.0.4 on 2024-04-15 00:17 + +import django.db.models.deletion +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('colloscope', '0003_remove_creneau_classe'), + ] + + operations = [ + migrations.AddField( + model_name='matiere', + name='classe', + field=models.ForeignKey(default=1, on_delete=django.db.models.deletion.CASCADE, to='colloscope.classe'), + preserve_default=False, + ), + ] diff --git a/colloscope/models.py b/colloscope/models.py index f09f7f3..26b2d3a 100644 --- a/colloscope/models.py +++ b/colloscope/models.py @@ -1,5 +1,17 @@ +from math import ceil +from datetime import date, datetime, timedelta + from django.db import models -from django.db.models import F +from django.db.models import F, Q + +calendrier = { + "C" : [ + ( date(2023, 10, 21), date(2023, 11, 6) ), + ( date(2023, 12, 23), date(2024, 1, 8) ), + ( date(2024, 2, 10), date(2024, 2, 26) ), + ( date(2024, 4, 6), date(2024, 4, 22) ), + ] +} class Lycee(models.Model): uai = models.CharField(max_length=10) @@ -19,7 +31,40 @@ class Periode(models.Model): debut = models.DateField() fin = models.DateField() + class Meta: + ordering = ["debut"] + + + def no_semaine(self, jour): + zone = self.classe.lycee.vacances + vacances = calendrier[zone] + jour0 = self.classe.jour_zero + + n = 1 + ((jour - jour0).days)//7 + for debut, fin in vacances: + if jour > debut: + n -= 2 + return n + + def range_semaines(self): + return range(self.no_semaine(self.debut), self.no_semaine(self.fin)+1) + + def date_debut_sem(self, n): + zone = self.classe.lycee.vacances + vacances = calendrier[zone] + jour0 = self.classe.jour_zero + + jour = jour0 + (n-1) * timedelta(weeks=1) + + for debut, _ in vacances: + if jour >= debut: + jour += 2*timedelta(weeks=1) + + return jour + + class Matiere(models.Model): + classe = models.ForeignKey(Classe, on_delete=models.CASCADE) libelle = models.CharField(max_length=100) code = models.CharField(max_length=20) @@ -35,6 +80,9 @@ class Groupe(models.Model): critere = models.ForeignKey(Critere, null=True, on_delete=models.CASCADE) libelle = models.CharField(max_length=100) + def membres(self): + return Etudiant.objects.filter(id__in=Appartenance.objects.filter(groupe=self)) + class Etudiant(models.Model): class Meta: ordering=["classe", "nom", "prenom"] @@ -46,19 +94,18 @@ class Etudiant(models.Model): #lv2 = models.ForeignKey(Matiere, on_delete=models.CASCADE) def appartient(self, groupe, periode): - return Appartenance.objects.filter(periode=periode, etudiant=self, groupe=groupe).exists() + return Appartenance.objects.filter(etudiant=self, groupe=groupe).exists() def groupe_du_critere(self, periode, critere): if isinstance(critere, str): critere = Critere.objects.get(periode=periode, libelle=critere) - return Appartenance.objects.get(periode=periode, etudiant=self, groupe__critere=critere).groupe + return Appartenance.objects.get(groupe__periode=periode, etudiant=self, groupe__critere=critere).groupe def groupe_de_colle(self, periode): return self.groupe_du_critere(periode, "colle") class Appartenance(models.Model): - periode = models.ForeignKey(Periode, on_delete=models.CASCADE) etudiant = models.ForeignKey(Etudiant, on_delete=models.CASCADE) groupe = models.ForeignKey(Groupe, on_delete=models.CASCADE) @@ -67,12 +114,11 @@ class Colleur(models.Model): nom = models.CharField(max_length=100) class Creneau(models.Model): - classe = models.ForeignKey(Classe, on_delete=models.CASCADE) + periode = models.ForeignKey(Periode, on_delete=models.CASCADE) jour = models.IntegerField() heure = models.TimeField() duree = models.DurationField() salle = models.CharField(max_length=20) - periode = models.ForeignKey(Periode, on_delete=models.CASCADE) matiere = models.ForeignKey(Matiere, on_delete=models.CASCADE) colleur = models.ForeignKey(Colleur, on_delete=models.CASCADE) est_colle = models.BooleanField() @@ -83,6 +129,21 @@ class Rotation(models.Model): groupes = models.ManyToManyField(Groupe) semaine = models.IntegerField() + def effectif(self): + n_base = sum(len(groupe.membres()) 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)) + + return n_base + n_plus - n_moins + + def est_pleine(self): + eff = self.effectif() + + if eff>self.creneau.capacite: + raise models.IntegrityError + else: + return eff==self.creneau.capacite + class Amendement(models.Model): est_positif = models.BooleanField() rotation = models.ForeignKey(Rotation, on_delete=models.CASCADE) diff --git a/colloscope/pdfexport.py b/colloscope/pdfexport.py index 087f29e..3cf3134 100644 --- a/colloscope/pdfexport.py +++ b/colloscope/pdfexport.py @@ -1,29 +1,11 @@ from datetime import date, timedelta -from colloscope.models import Etudiant, Critere, Classe +from colloscope.models import Etudiant, Critere, Classe, Rotation, Periode, Creneau from fpdf import FPDF from fpdf.fonts import FontFace from fpdf.enums import TableCellFillMode -calendrier_zoneC = date(2023, 9, 18), [ - ( date(2023, 10, 21), date(2023, 11, 6) ), - ( date(2023, 12, 23), date(2024, 1, 8) ), - ( date(2024, 2, 10), date(2024, 2, 26) ), - ( date(2024, 4, 6), date(2024, 4, 22) ), -] - -def jour_of_sem(n, cal): - sem_1, vac = cal - - jour = sem_1 + (n-1) * timedelta(weeks=1) - - for (debut, fin) in vac: - if jour >= debut: - jour += 2*timedelta(weeks=1) - - return jour - """ etudiants = [ ['Aboujaib', 'Alexandre', 4, 'A', '', ''], @@ -124,7 +106,12 @@ class PDF(FPDF): row.cell("??") # LV2 - def table_colloscope(self, creneaux, semaines, rotations): + def table_colloscope(self, periode): + semaines = periode.range_semaines() + lundis = [ periode.date_debut_sem(n) for n in semaines ] + creneaux = Creneau.objects.filter(periode=periode) + jours = ["dimanche", "lundi", "mardi", "mercredi", "jeudi", "vendredi", "samedi"] + with self.table( align="LEFT", width=190, @@ -140,24 +127,29 @@ class PDF(FPDF): header.cell(str(sem), align="CENTER") header2 = table.row() - for sem in semaines: - header2.cell(jour_of_sem(sem, calendrier_zoneC).strftime("%d/%m/%y"), align="CENTER") + for lundi in lundis: + header2.cell(lundi.strftime("%d/%m/%y"), align="CENTER") - for i, tr in enumerate(creneaux): - matiere, jour, heure, colleur, salle = tr + + for i, c in enumerate(creneaux): + matiere = c.matiere + jour = c.jour + heure = c.heure + colleur = c.colleur + salle = c.salle row = table.row() - row.cell(matiere) - row.cell(jour) - row.cell(heure) - row.cell(colleur) + 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) for s in semaines: - for rot in rotations: - if rot[2] == i and rot[0] == s: - row.cell(str(rot[1]), align="CENTER") - break + if Rotation.objects.filter(creneau=c, semaine=s).exists(): + rot = Rotation.objects.get(creneau=c, semaine=s) + groupes = rot.groupes + row.cell(", ".join(g.libelle for g in groupes.all()), align="CENTER") else: row.cell() @@ -195,8 +187,8 @@ def generate(periode): base_y = pdf.t_margin + 10 pdf.set_y(base_y) pdf.liste_eleves(periode) - #pdf.set_y(base_y) - #pdf.table_colloscope(creneaux, semaines, rotations) + pdf.set_y(base_y) + pdf.table_colloscope(periode) #pdf.y += 3 #pdf.table_travaux() diff --git a/test.pdf b/test.pdf index 12c6f1a..ed61cd4 100644 Binary files a/test.pdf and b/test.pdf differ