+ favicon
+ all variable names translated to English + Better marketplace
This commit is contained in:
parent
7b593eec08
commit
0e6eda5fa8
|
@ -2,32 +2,34 @@ from django.contrib import admin
|
||||||
from colloscope.models import *
|
from colloscope.models import *
|
||||||
|
|
||||||
|
|
||||||
@admin.register(Lycee)
|
@admin.register(School)
|
||||||
class LyceeAdmin(admin.ModelAdmin):
|
class LyceeAdmin(admin.ModelAdmin):
|
||||||
list_display = ('uai', 'libelle', 'vacances')
|
list_display = ('uai', 'description', 'vacation')
|
||||||
|
|
||||||
|
|
||||||
admin.site.register(Classe)
|
admin.site.register(Class)
|
||||||
admin.site.register(Periode)
|
admin.site.register(Term)
|
||||||
admin.site.register(Matiere)
|
admin.site.register(Subject)
|
||||||
admin.site.register(Critere)
|
admin.site.register(GroupType)
|
||||||
admin.site.register(Groupe)
|
admin.site.register(Group)
|
||||||
admin.site.register(Etudiant)
|
admin.site.register(Student)
|
||||||
admin.site.register(Appartenance)
|
admin.site.register(Member)
|
||||||
admin.site.register(Colleur)
|
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"]
|
jours = ["dimanche", "lundi", "mardi", "mercredi", "jeudi", "vendredi", "samedi"]
|
||||||
return jours[obj.jour]
|
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(Colle)
|
||||||
admin.site.register(Profil)
|
admin.site.register(Swap)
|
||||||
admin.site.register(LienCalendrier)
|
admin.site.register(Profile)
|
||||||
|
admin.site.register(CalendarLink)
|
||||||
|
|
|
@ -10,11 +10,11 @@ from .create_calendar import get_calendar
|
||||||
LOCAL_TZ = "Europe/Paris"
|
LOCAL_TZ = "Europe/Paris"
|
||||||
|
|
||||||
|
|
||||||
def emailize(nom, prenom=None):
|
def emailize(nom, first_name=None):
|
||||||
if prenom is not None:
|
if first_name is not None:
|
||||||
return "{}.{}@example.com" \
|
return "{}.{}@example.com" \
|
||||||
.format(
|
.format(
|
||||||
prenom.replace(" ", "_").lower(),
|
first_name.replace(" ", "_").lower(),
|
||||||
nom.replace(" ", "_").lower()
|
nom.replace(" ", "_").lower()
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
|
@ -22,41 +22,41 @@ def emailize(nom, prenom=None):
|
||||||
.format(nom.replace(" ", "_").lower())
|
.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')
|
p = path.abspath('./static/Base_Calendar.ics')
|
||||||
|
|
||||||
with open(p) as f:
|
with open(p) as f:
|
||||||
cal = Calendar.from_ical(f.read())
|
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()
|
event = Event()
|
||||||
|
|
||||||
summary = f"Colle {rotation.creneau.matiere} ({rotation.creneau.colleur})"
|
summary = f"Colle {colle.slot.subject} ({colle.slot.colleur})"
|
||||||
event.add("summary", summary)
|
event.add("summary", summary)
|
||||||
|
|
||||||
start = rotation.datetime()
|
start = colle.datetime()
|
||||||
fin = start + rotation.creneau.duree
|
fin = start + colle.slot.duration
|
||||||
|
|
||||||
event.add("dtstart", start, parameters={"tzid": LOCAL_TZ})
|
event.add("dtstart", start, parameters={"tzid": LOCAL_TZ})
|
||||||
event.add("dtend", fin, parameters={"tzid": LOCAL_TZ})
|
event.add("dtend", fin, parameters={"tzid": LOCAL_TZ})
|
||||||
event.add("dtstamp", datetime.now())
|
event.add("dtstamp", datetime.now())
|
||||||
event.add("uid", str(uuid4()))
|
event.add("uid", str(uuid4()))
|
||||||
|
|
||||||
event.add("location", f"{rotation.creneau.salle} ({rotation.creneau.periode.classe.lycee})")
|
event.add("location", f"{colle.slot.room} ({colle.slot.term.cls.school})")
|
||||||
event.add("categories", "COLLE-" + str(rotation.creneau.matiere))
|
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)
|
event.add("description", description)
|
||||||
|
|
||||||
organizer = vCalAddress(f"mailto:{emailize(rotation.creneau.colleur.nom)}")
|
organizer = vCalAddress(f"mailto:{emailize(colle.slot.colleur.name)}")
|
||||||
organizer.params["cn"] = vText(str(rotation.creneau.colleur))
|
organizer.params["cn"] = vText(str(colle.slot.colleur))
|
||||||
organizer.params["role"] = vText("Colleur")
|
organizer.params["role"] = vText("Colleur")
|
||||||
event.add("organizer", organizer)
|
event.add("organizer", organizer)
|
||||||
|
|
||||||
for e in rotation.groupe_effectif():
|
for e in colle.final_group():
|
||||||
attendee = vCalAddress("mailto:{emailize(e.nom, prenom=e.prenom)}")
|
attendee = vCalAddress("mailto:{emailize(e.name, first_name=e.first_name)}")
|
||||||
attendee.params["role"] = vText("Etudiant")
|
attendee.params["role"] = vText("Etudiant")
|
||||||
attendee.params["cn"] = vText(str(e))
|
attendee.params["cn"] = vText(str(e))
|
||||||
|
|
||||||
|
|
|
@ -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'),
|
||||||
|
),
|
||||||
|
]
|
|
@ -3,17 +3,16 @@ from datetime import date, datetime, timedelta
|
||||||
from asgiref.sync import async_to_sync
|
from asgiref.sync import async_to_sync
|
||||||
from pytz import timezone
|
from pytz import timezone
|
||||||
|
|
||||||
import asyncio
|
|
||||||
import aiohttp
|
import aiohttp
|
||||||
|
|
||||||
from django.db import models
|
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.contrib.auth.models import User
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
|
|
||||||
from discord import Webhook
|
from discord import Webhook
|
||||||
|
|
||||||
calendrier = {
|
calendar = {
|
||||||
"C": [
|
"C": [
|
||||||
(date(2023, 10, 21), date(2023, 11, 6)),
|
(date(2023, 10, 21), date(2023, 11, 6)),
|
||||||
(date(2023, 12, 23), date(2024, 1, 8)),
|
(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)
|
uai = models.CharField(max_length=10)
|
||||||
libelle = models.CharField(max_length=100)
|
description = models.CharField(max_length=100)
|
||||||
vacances = models.CharField(max_length=1)
|
vacation = models.CharField(max_length=1)
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self) -> str:
|
||||||
return self.libelle
|
return self.description
|
||||||
|
|
||||||
|
|
||||||
class Classe(models.Model):
|
class Class(models.Model):
|
||||||
lycee = models.ForeignKey(Lycee, on_delete=models.CASCADE)
|
school = models.ForeignKey(School, on_delete=models.CASCADE)
|
||||||
libelle = models.CharField(max_length=20)
|
description = models.CharField(max_length=20)
|
||||||
annee = models.IntegerField()
|
year = models.IntegerField()
|
||||||
jour_zero = models.DateField()
|
day_zero = models.DateField()
|
||||||
|
|
||||||
def no_semaine(self, jour):
|
def week_number(self, day: date) -> int:
|
||||||
"""
|
"""
|
||||||
Entrées :
|
Entrées :
|
||||||
- self
|
- self
|
||||||
- jour (datetime.date)
|
- day (datetime.date)
|
||||||
|
|
||||||
Sortie :
|
Sortie :
|
||||||
- Le numéro de la semaine contenant jour, sans compter les vacances.
|
- Le numéro de la semaine contenant day, sans compter les vacation.
|
||||||
Renvoie un numéro non spécifiée si le jour est pendant une période de vacances
|
Renvoie un numéro non spécifiée si le day est pendant une période de vacation
|
||||||
"""
|
"""
|
||||||
|
|
||||||
zone = self.lycee.vacances
|
zone = self.school.vacation
|
||||||
vacances = calendrier[zone]
|
vacation = calendar[zone]
|
||||||
jour0 = self.jour_zero
|
day0 = self.day_zero
|
||||||
|
|
||||||
n = 1 + (jour - jour0).days // 7
|
n = 1 + (day - day0).days // 7
|
||||||
for debut, fin in vacances:
|
for debut, fin in vacation:
|
||||||
if jour > debut:
|
if day > debut:
|
||||||
n -= round((fin - debut) / timedelta(weeks=1))
|
n -= round((fin - debut) / timedelta(weeks=1))
|
||||||
return n
|
return n
|
||||||
|
|
||||||
def no_aujourdhui(self):
|
def today_number(self) -> int:
|
||||||
"""
|
"""
|
||||||
Entrée:
|
Entrée:
|
||||||
- self
|
- self
|
||||||
|
@ -69,9 +68,9 @@ class Classe(models.Model):
|
||||||
- Le numéro de la semaine courante
|
- 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:
|
Entrée:
|
||||||
- self
|
- self
|
||||||
|
@ -81,59 +80,57 @@ class Classe(models.Model):
|
||||||
- Le date du lundi de la semaine n
|
- Le date du lundi de la semaine n
|
||||||
"""
|
"""
|
||||||
|
|
||||||
zone = self.lycee.vacances
|
zone = self.school.vacation
|
||||||
vacances = calendrier[zone]
|
vacation = calendar[zone]
|
||||||
jour0 = self.jour_zero
|
day0 = self.day_zero
|
||||||
|
|
||||||
jour = jour0 + (n - 1) * timedelta(weeks=1)
|
day = day0 + (n - 1) * timedelta(weeks=1)
|
||||||
|
|
||||||
for debut, fin in vacances:
|
for begin, end in vacation:
|
||||||
if jour >= debut:
|
if day >= begin:
|
||||||
jour += round((fin - debut) / timedelta(weeks=1)) * timedelta(weeks=1)
|
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 :
|
Entrées :
|
||||||
- self
|
- self
|
||||||
- jour (datetime.date)
|
- day (datetime.date)
|
||||||
|
|
||||||
Sortie :
|
Sortie :
|
||||||
- La période (si elle existe et est unique) contenant jour
|
- La période (si elle existe et est unique) contenant day
|
||||||
|
|
||||||
Exceptions:
|
Exceptions:
|
||||||
- Le jour n'est pas dans une période
|
- Le day n'est pas dans une période
|
||||||
- Le jour est au chevauchement de deux périodes
|
- 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):
|
def current_term(self):
|
||||||
#return self.periode(date.today()) // ne fonctionne pas entre les périodes
|
|
||||||
"""
|
"""
|
||||||
On prend la période non révolue la plus récente
|
On prend la période non révolue la plus récente
|
||||||
"""
|
"""
|
||||||
|
|
||||||
return Periode.objects \
|
return (Term.objects
|
||||||
.filter(classe=self, fin__gte=date.today()) \
|
.filter(cls=self, end__gte=date.today())
|
||||||
.order_by("-debut") \
|
.order_by("-begin")
|
||||||
.first()
|
.first())
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return f"{self.libelle} ({self.lycee.libelle})"
|
return f"{self.description} ({self.lycee.description})"
|
||||||
|
|
||||||
|
|
||||||
class Periode(models.Model):
|
class Term(models.Model):
|
||||||
classe = models.ForeignKey(Classe, on_delete=models.CASCADE)
|
cls = models.ForeignKey(Class, on_delete=models.CASCADE)
|
||||||
#critere_colle = models.ForeignKey(Critere, on_delete=models.SET_NULL, null=True)
|
description = models.CharField(max_length=100)
|
||||||
libelle = models.CharField(max_length=100)
|
begin = models.DateField()
|
||||||
debut = models.DateField()
|
end = models.DateField()
|
||||||
fin = models.DateField()
|
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
ordering = ["debut"]
|
ordering = ["begin"]
|
||||||
|
|
||||||
def range_semaines(self):
|
def range_weeks(self) -> range:
|
||||||
"""
|
"""
|
||||||
Entrée:
|
Entrée:
|
||||||
- self
|
- self
|
||||||
|
@ -141,284 +138,284 @@ class Periode(models.Model):
|
||||||
Sortie:
|
Sortie:
|
||||||
- Un range des numéros de semaine
|
- 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):
|
def query_colles(self) -> QuerySet:
|
||||||
return (Rotation.objects
|
return (Colle.objects
|
||||||
.select_related("creneau", "creneau__periode")
|
.select_related("slot", "slot__term")
|
||||||
.prefetch_related("amendement_set")
|
.prefetch_related("swap_set")
|
||||||
.filter(creneau__periode=self)
|
.filter(slot__term=self)
|
||||||
.annotate(adt_plus=Count("amendement", filter=Q(amendement__est_positif=1)))
|
.annotate(adt_plus=Count("swap", filter=Q(swap__enroll=1)))
|
||||||
.annotate(adt_minus=Count("amendement", filter=Q(amendement__est_positif=0)))
|
.annotate(adt_minus=Count("swap", filter=Q(swap__enroll=0)))
|
||||||
.annotate(volume=F("creneau__capacite") + F("adt_plus") - F("adt_minus")))
|
.annotate(volume=F("slot__capacity") + F("adt_plus") - F("adt_minus")))
|
||||||
|
|
||||||
def query_rotations_etudiant(self, etudiant):
|
def query_colles_of_student(self, student) -> QuerySet:
|
||||||
return (Rotation.objects
|
return (Colle.objects
|
||||||
.select_related("creneau", "creneau__periode")
|
.select_related("slot", "slot__term")
|
||||||
.prefetch_related("amendement_set")
|
.prefetch_related("swap_set")
|
||||||
.filter(creneau__periode=self)
|
.filter(slot__term=self)
|
||||||
.filter((Q(groupes__etudiant=etudiant)
|
.filter((Q(groups__student=student)
|
||||||
& ~Q(amendement__est_positif=0, amendement__etudiant=etudiant))
|
& ~Q(swap__enroll=0, swap__student=student))
|
||||||
| Q(amendement__est_positif=1, amendement__etudiant=etudiant))
|
| Q(swap__enroll=1, swap__student=student))
|
||||||
.annotate(adt_plus=Count("amendement", filter=Q(amendement__est_positif=1)))
|
.annotate(adt_plus=Count("swap", filter=Q(swap__enroll=1)))
|
||||||
.annotate(adt_minus=Count("amendement", filter=Q(amendement__est_positif=0)))
|
.annotate(adt_minus=Count("swap", filter=Q(swap__enroll=0)))
|
||||||
.annotate(volume=F("creneau__capacite") + F("adt_plus") - F("adt_minus")))
|
.annotate(volume=F("slot__capacity") + F("adt_plus") - F("adt_minus")))
|
||||||
|
|
||||||
def query_rotations_not_full(self):
|
def query_colles_not_full(self) -> QuerySet:
|
||||||
return (self.query_rotations()
|
return (self.query_colles()
|
||||||
.filter(volume__lt=F("creneau__capacite"), date__gte=date.today()))
|
.filter(volume__lt=F("slot__capacity"), date__gte=date.today()))
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self) -> str:
|
||||||
return self.libelle
|
return self.description
|
||||||
|
|
||||||
|
|
||||||
class Matiere(models.Model):
|
class Subject(models.Model):
|
||||||
classe = models.ForeignKey(Classe, on_delete=models.CASCADE)
|
cls = models.ForeignKey(Class, on_delete=models.CASCADE)
|
||||||
libelle = models.CharField(max_length=100)
|
description = models.CharField(max_length=100)
|
||||||
code = models.CharField(max_length=20)
|
code = models.CharField(max_length=20)
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return self.libelle
|
return self.description
|
||||||
|
|
||||||
|
|
||||||
class Critere(models.Model):
|
class GroupType(models.Model):
|
||||||
periode = models.ForeignKey(Periode, on_delete=models.CASCADE)
|
term = models.ForeignKey(Term, on_delete=models.CASCADE)
|
||||||
libelle = models.CharField(max_length=100)
|
description = models.CharField(max_length=100)
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return self.libelle
|
return self.description
|
||||||
|
|
||||||
|
|
||||||
class Groupe(models.Model):
|
class Group(models.Model):
|
||||||
#class Meta:
|
class Meta:
|
||||||
# ordering=[F("periode").classe.libelle, F("periode").libelle, "libelle"]
|
ordering = ["term__cls__description", "term__description", "description"]
|
||||||
|
|
||||||
periode = models.ForeignKey(Periode, on_delete=models.CASCADE)
|
term = models.ForeignKey(Term, on_delete=models.CASCADE)
|
||||||
critere = models.ForeignKey(Critere, null=True, on_delete=models.CASCADE)
|
type = models.ForeignKey(GroupType, null=True, on_delete=models.CASCADE)
|
||||||
libelle = models.CharField(max_length=100)
|
description = models.CharField(max_length=100)
|
||||||
|
|
||||||
membres = models.ManyToManyField("Etudiant", through="Appartenance")
|
members = models.ManyToManyField("Student", through="Member")
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return self.libelle
|
return self.description
|
||||||
|
|
||||||
#def get_colles(self):
|
"""def get_colles(self):
|
||||||
# return Rotation.objects.filter(creneau__periode=self.periode,
|
return Rotation.objects.filter(slot__term=self.term,
|
||||||
# Q(groupes=self) || Q(a)).order_by("date")
|
Q(groupes=self) || Q(a)).order_by("date")
|
||||||
|
|
||||||
def get_colles_par_sem(self):
|
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()
|
colles_flat = self.get_colles()
|
||||||
|
|
||||||
return [
|
return [
|
||||||
(sem, lundi,
|
(sem, lundi,
|
||||||
colles_flat.filter(date__gte=lundi, date__lt=lundi + timedelta(weeks=1)))
|
colles_flat.filter(date__gte=lundi, date__lt=lundi + timedelta(weeks=1)))
|
||||||
for sem, lundi in semaines
|
for sem, lundi in semaines
|
||||||
]
|
]"""
|
||||||
|
|
||||||
|
|
||||||
class Etudiant(models.Model):
|
class Student(models.Model):
|
||||||
class Meta:
|
class Meta:
|
||||||
ordering = ["classe", "nom", "prenom"]
|
ordering = ["cls", "last_name", "first_name"]
|
||||||
|
|
||||||
classe = models.ForeignKey(Classe, on_delete=models.CASCADE)
|
cls = models.ForeignKey(Class, on_delete=models.CASCADE)
|
||||||
prenom = models.CharField(max_length=100)
|
first_name = models.CharField(max_length=100)
|
||||||
nom = models.CharField(max_length=100)
|
last_name = models.CharField(max_length=100)
|
||||||
groupes = models.ManyToManyField("Groupe", through="Appartenance")
|
groups = models.ManyToManyField("Group", through="Member")
|
||||||
|
|
||||||
def appartient(self, groupe):
|
def is_member(self, group):
|
||||||
"""
|
"""
|
||||||
Renvoie si self appartient au groupe.
|
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.
|
Renvoie le groupe du critère auquel self appartient.
|
||||||
"""
|
"""
|
||||||
if isinstance(critere, str):
|
if isinstance(type_, str):
|
||||||
critere = Critere.objects.get(periode=periode, libelle=critere)
|
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):
|
def __str__(self):
|
||||||
return f"{self.prenom} {self.nom}"
|
return f"{self.first_name} {self.last_name}"
|
||||||
|
|
||||||
|
|
||||||
class Appartenance(models.Model):
|
class Member(models.Model):
|
||||||
etudiant = models.ForeignKey(Etudiant, on_delete=models.CASCADE)
|
student = models.ForeignKey(Student, on_delete=models.CASCADE)
|
||||||
groupe = models.ForeignKey(Groupe, on_delete=models.CASCADE)
|
group = models.ForeignKey(Group, on_delete=models.CASCADE)
|
||||||
|
|
||||||
|
|
||||||
class Colleur(models.Model):
|
class Colleur(models.Model):
|
||||||
civilite = models.CharField(max_length=1)
|
gender = models.CharField(max_length=1)
|
||||||
nom = models.CharField(max_length=100)
|
name = models.CharField(max_length=100)
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
if self.civilite == "M":
|
if self.gender == "M":
|
||||||
return f"M. {self.nom}"
|
return f"M. {self.name}"
|
||||||
else:
|
else:
|
||||||
return f"Mme {self.nom}"
|
return f"Mme {self.name}"
|
||||||
|
|
||||||
def get_classes(self):
|
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):
|
class Slot(models.Model):
|
||||||
periode = models.ForeignKey(Periode, on_delete=models.CASCADE)
|
term = models.ForeignKey(Term, on_delete=models.CASCADE)
|
||||||
jour = models.IntegerField()
|
day = models.IntegerField()
|
||||||
heure = models.TimeField()
|
time = models.TimeField()
|
||||||
duree = models.DurationField()
|
duration = models.DurationField()
|
||||||
salle = models.CharField(max_length=20)
|
room = models.CharField(max_length=20)
|
||||||
matiere = models.ForeignKey(Matiere, on_delete=models.CASCADE)
|
subject = models.ForeignKey(Subject, on_delete=models.CASCADE)
|
||||||
colleur = models.ForeignKey(Colleur, on_delete=models.CASCADE)
|
colleur = models.ForeignKey(Colleur, on_delete=models.CASCADE)
|
||||||
est_colle = models.BooleanField()
|
type = models.ForeignKey(GroupType, on_delete=models.CASCADE)
|
||||||
capacite = models.IntegerField()
|
capacity = models.IntegerField()
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
verbose_name_plural = "Creneaux"
|
verbose_name_plural = "slots"
|
||||||
|
|
||||||
def __str__(self):
|
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:
|
class Meta:
|
||||||
ordering = ["creneau__periode__classe", "creneau__matiere__libelle", "creneau__colleur__nom", "date",
|
ordering = ["slot__term__cls", "slot__subject__description", "slot__colleur__name", "date",
|
||||||
"creneau__heure"]
|
"slot__time"]
|
||||||
|
|
||||||
creneau = models.ForeignKey(Creneau, on_delete=models.CASCADE)
|
slot = models.ForeignKey(Slot, on_delete=models.CASCADE)
|
||||||
groupes = models.ManyToManyField(Groupe)
|
groups = models.ManyToManyField(Group)
|
||||||
date = models.DateField()
|
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(
|
return Student.objects.filter(
|
||||||
(Q(id__in=Appartenance.objects.filter(groupe__in=self.groupes.all()))
|
(Q(id__in=Member.objects.filter(group__in=self.groups.all()))
|
||||||
| Q(id__in=amendements.filter(est_positif=True).values("etudiant_id")))
|
| Q(id__in=swaps.filter(enroll=True).values("student_id")))
|
||||||
& ~Q(id__in=amendements.filter(est_positif=False).values("etudiant_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_base = sum(len(group.members.count()) for group in self.groups.all())
|
||||||
n_plus = len(Amendement.objects.filter(est_positif=True, rotation=self))
|
n_plus = len(Swap.objects.filter(enroll=True, colle=self))
|
||||||
n_moins = len(Amendement.objects.filter(est_positif=False, rotation=self))
|
n_moins = len(Swap.objects.filter(enroll=False, colle=self))
|
||||||
|
|
||||||
return n_base + n_plus - n_moins
|
return n_base + n_plus - n_moins
|
||||||
|
|
||||||
def est_pleine(self):
|
def is_full(self):
|
||||||
"""
|
"""
|
||||||
Renvoie si la colle est pleine.
|
Renvoie si la colle est pleine.
|
||||||
"""
|
"""
|
||||||
eff = self.effectif()
|
eff = self.volume()
|
||||||
return eff >= self.creneau.capacite
|
return eff >= self.slot.capacity
|
||||||
|
|
||||||
def est_modifiee(self):
|
def is_edited(self):
|
||||||
"""
|
"""
|
||||||
Renvoie si la colle a été amendée.
|
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")
|
raise Exception("Duplication")
|
||||||
elif Amendement.objects.filter(rotation=self, etudiant=etudiant, est_positif=not est_positif).exists():
|
elif Swap.objects.filter(colle=self, student=student, enroll=not enroll).exists():
|
||||||
# les amendements complémentaires s'annulent
|
# les swaps complémentaires s'annulent
|
||||||
Amendement.objects.get(rotation=self, etudiant=etudiant, est_positif=not est_positif).delete()
|
Swap.objects.get(colle=self, student=student, enroll=not enroll).delete()
|
||||||
elif est_positif and any(etudiant.appartient(groupe) for groupe in self.groupes.all()):
|
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
|
# on ne peut pas s'ajouter si on est dans le groupe de base
|
||||||
raise Exception("Vous êtes déjà dans le groupe")
|
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")
|
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")
|
raise Exception("Capacité dépassée")
|
||||||
else:
|
else:
|
||||||
amendement = Amendement(rotation=self, etudiant=etudiant, est_positif=est_positif)
|
swap = Swap(colle=self, student=student, enroll=enroll)
|
||||||
amendement.save()
|
swap.save()
|
||||||
|
|
||||||
if notifier:
|
#if notify:
|
||||||
func = async_to_sync(amendement.notifier)
|
# func = async_to_sync(swap.notify)
|
||||||
func()
|
# func()
|
||||||
|
|
||||||
def __str__(self):
|
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):
|
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):
|
class Swap(models.Model):
|
||||||
est_positif = models.BooleanField()
|
enroll = models.BooleanField()
|
||||||
rotation = models.ForeignKey(Rotation, on_delete=models.CASCADE)
|
colle = models.ForeignKey(Colle, on_delete=models.CASCADE)
|
||||||
etudiant = models.ForeignKey(Etudiant, 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:
|
async with aiohttp.ClientSession() as session:
|
||||||
webhook = Webhook.from_url(settings.DISCORD_NOTIFY_WEBHOOK_URL, session=session)
|
webhook = Webhook.from_url(settings.DISCORD_NOTIFY_WEBHOOK_URL, session=session)
|
||||||
|
|
||||||
if self.est_positif:
|
if self.enroll:
|
||||||
await webhook.send(f"Colle réservée : {self.rotation}", username=settings.DISCORD_NOTIFY_WEBHOOK_USERNAME)
|
await webhook.send(f"Colle réservée : {self.colle}", username=settings.DISCORD_NOTIFY_WEBHOOK_USERNAME)
|
||||||
else:
|
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):
|
class Profile(models.Model):
|
||||||
utilisateur = models.OneToOneField(User, on_delete=models.CASCADE, primary_key=True)
|
user = models.OneToOneField(User, on_delete=models.CASCADE, primary_key=True)
|
||||||
etudiant = models.ForeignKey(Etudiant, null=True, blank=True, on_delete=models.SET_NULL)
|
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)
|
colleur = models.ForeignKey(Colleur, null=True, blank=True, on_delete=models.SET_NULL)
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return f"Profil {self.utilisateur} : {self.etudiant} ; {self.colleur}"
|
return f"Profil {self.user} : {self.student} ; {self.colleur}"
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def from_request(request, preprocess=lambda query: query):
|
def from_request(request, preprocess=lambda query: query):
|
||||||
user = request.user
|
user = request.user
|
||||||
session = request.session
|
session = request.session
|
||||||
|
|
||||||
match session.get("profil"):
|
match session.get("profile"):
|
||||||
case "etudiant":
|
case "student":
|
||||||
profil = preprocess(Profil.objects.filter(utilisateur=user)).get()
|
profil = preprocess(Profile.objects.filter(user=user)).get()
|
||||||
return profil.etudiant
|
return profil.student
|
||||||
case "colleur":
|
case "colleur":
|
||||||
profil = preprocess(Profil.objects.filter(utilisateur=user)).get()
|
profil = preprocess(Profile.objects.filter(user=user)).get()
|
||||||
return profil.colleur
|
return profil.colleur
|
||||||
case _:
|
case _:
|
||||||
raise ValueError("profil non choisi")
|
raise ValueError("profil non choisi")
|
||||||
|
|
||||||
|
|
||||||
class LienCalendrier(models.Model):
|
class CalendarLink(models.Model):
|
||||||
code = models.CharField(max_length=32, unique=True)
|
key = models.CharField(max_length=32, unique=True)
|
||||||
etudiant = models.ForeignKey(Etudiant, on_delete=models.CASCADE)
|
student = models.ForeignKey(Student, on_delete=models.CASCADE)
|
||||||
periode = models.ForeignKey(Periode, on_delete=models.CASCADE)
|
term = models.ForeignKey(Term, on_delete=models.CASCADE)
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
constraints = [
|
constraints = [
|
||||||
models.UniqueConstraint(
|
models.UniqueConstraint(
|
||||||
fields=['etudiant', 'periode'], name='unique_etudiant_periode_combination'
|
fields=['student', 'term'], name='unique_student_term_combination'
|
||||||
)
|
)
|
||||||
]
|
]
|
||||||
|
|
|
@ -1,14 +1,16 @@
|
||||||
from datetime import date, timedelta
|
from datetime import date, timedelta
|
||||||
|
|
||||||
|
from django.shortcuts import redirect
|
||||||
|
from django.http import HttpResponse
|
||||||
|
from fpdf import FPDF
|
||||||
|
|
||||||
from colloscope.models import *
|
from colloscope.models import *
|
||||||
|
|
||||||
from fpdf import FPDF
|
|
||||||
|
|
||||||
|
|
||||||
class PDF(FPDF):
|
class PDF(FPDF):
|
||||||
def liste_eleves(self, periode):
|
def liste_eleves(self, term):
|
||||||
classe = periode.classe
|
cls = term.cls
|
||||||
etudiants = Etudiant.objects.filter(classe=classe)
|
students = Student.objects.filter(cls=cls)
|
||||||
|
|
||||||
with self.table(
|
with self.table(
|
||||||
align="RIGHT",
|
align="RIGHT",
|
||||||
|
@ -19,27 +21,27 @@ class PDF(FPDF):
|
||||||
for th in ("Nom", "Prénom", "Grp.", "TD",): #"LV1", "LV2"):
|
for th in ("Nom", "Prénom", "Grp.", "TD",): #"LV1", "LV2"):
|
||||||
header.cell(th)
|
header.cell(th)
|
||||||
|
|
||||||
for etu in etudiants:
|
for etu in students:
|
||||||
row = table.row()
|
row = table.row()
|
||||||
row.cell(etu.nom.upper()) # Nom
|
row.cell(etu.last_name.upper()) # Nom
|
||||||
row.cell(etu.prenom) # Prénom
|
row.cell(etu.first_name) # Prénom
|
||||||
row.cell(etu.groupe_de_colle(periode).libelle) # Groupe
|
row.cell(etu.colle_group(term).description) # Groupe
|
||||||
row.cell(etu.groupe_du_critere(periode, "td").libelle)
|
row.cell(etu.group_of_type(term, "td").description)
|
||||||
#row.cell("??") # LV1
|
#row.cell("??") # LV1
|
||||||
#row.cell("??") # LV2
|
#row.cell("??") # LV2
|
||||||
|
|
||||||
|
|
||||||
def table_colloscope(self, periode, heading=True, est_colle=True):
|
def table_colloscope(self, term, heading=True, type="colle"):
|
||||||
semaines = periode.range_semaines()
|
weeks = term.range_weeks()
|
||||||
lundis = [ periode.classe.date_debut_sem(n) for n in semaines ]
|
lundis = [term.cls.week_beginning_date(n) for n in weeks]
|
||||||
creneaux = Creneau.objects.filter(periode=periode, est_colle=est_colle)
|
slots = Slot.objects.filter(term=term, type__description=type)
|
||||||
jours = ["dimanche", "lundi", "mardi", "mercredi", "jeudi", "vendredi", "samedi"]
|
weekdays = ["dimanche", "lundi", "mardi", "mercredi", "jeudi", "vendredi", "samedi"]
|
||||||
|
|
||||||
with self.table(
|
with self.table(
|
||||||
align="LEFT",
|
align="LEFT",
|
||||||
width=190,
|
width=190,
|
||||||
line_height=3,
|
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,
|
num_heading_rows=2 if heading else 0,
|
||||||
first_row_as_headings=heading) as table:
|
first_row_as_headings=heading) as table:
|
||||||
|
|
||||||
|
@ -48,7 +50,7 @@ class PDF(FPDF):
|
||||||
for th in ("Matière", "Jour", "Heure", "Colleur", "Salle"):
|
for th in ("Matière", "Jour", "Heure", "Colleur", "Salle"):
|
||||||
header.cell(th, align="CENTER", rowspan=2)
|
header.cell(th, align="CENTER", rowspan=2)
|
||||||
|
|
||||||
for sem in semaines:
|
for sem in weeks:
|
||||||
header.cell(str(sem), align="CENTER")
|
header.cell(str(sem), align="CENTER")
|
||||||
|
|
||||||
header2 = table.row()
|
header2 = table.row()
|
||||||
|
@ -56,38 +58,38 @@ class PDF(FPDF):
|
||||||
header2.cell(lundi.strftime("%d/%m/%y"), align="CENTER")
|
header2.cell(lundi.strftime("%d/%m/%y"), align="CENTER")
|
||||||
|
|
||||||
|
|
||||||
for i, c in enumerate(creneaux):
|
for i, c in enumerate(slots):
|
||||||
matiere = c.matiere
|
subject = c.subject
|
||||||
jour = c.jour
|
day = c.day
|
||||||
heure = c.heure
|
time = c.time
|
||||||
colleur = c.colleur
|
colleur = c.colleur
|
||||||
salle = c.salle
|
room = c.room
|
||||||
|
|
||||||
row = table.row()
|
row = table.row()
|
||||||
row.cell(matiere.libelle)
|
row.cell(subject.description)
|
||||||
row.cell(jours[jour])
|
row.cell(weekdays[day])
|
||||||
row.cell(heure.strftime("%H:%M"))
|
row.cell(time.strftime("%H:%M"))
|
||||||
row.cell("{} {}".format("M." if colleur.civilite=="M" else "Mme", colleur.nom.upper()))
|
row.cell("{} {}".format("M." if colleur.gender=="M" else "Mme", colleur.name.upper()))
|
||||||
row.cell(salle)
|
row.cell(room)
|
||||||
|
|
||||||
for s in semaines:
|
for s in weeks:
|
||||||
lundi = periode.classe.date_debut_sem(s)
|
lundi = term.cls.week_beginning_date(s)
|
||||||
|
|
||||||
if Rotation.objects.filter(creneau=c, date__gte=lundi, date__lt=lundi+timedelta(weeks=1)).exists():
|
if Colle.objects.filter(slot=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))
|
r = Colle.objects.get(slot=c, date__gte=lundi, date__lt=lundi + timedelta(weeks=1))
|
||||||
groupes = r.groupes
|
groups = r.groups
|
||||||
content = ", ".join(g.libelle for g in groupes.all())
|
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")
|
row.cell(content, align="CENTER")
|
||||||
else:
|
else:
|
||||||
row.cell()
|
row.cell()
|
||||||
|
|
||||||
def generate(periode):
|
def generate(term):
|
||||||
pdf = PDF(orientation="landscape", format="a4")
|
pdf = PDF(orientation="landscape", format="a4")
|
||||||
pdf.set_font("helvetica", size=6)
|
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_title(titre)
|
||||||
pdf.set_author("colles.mp2i-vms.fr")
|
pdf.set_author("colles.mp2i-vms.fr")
|
||||||
|
@ -97,39 +99,39 @@ def generate(periode):
|
||||||
pdf.set_line_width(0.1)
|
pdf.set_line_width(0.1)
|
||||||
base_y = pdf.t_margin + 10
|
base_y = pdf.t_margin + 10
|
||||||
pdf.set_y(base_y)
|
pdf.set_y(base_y)
|
||||||
pdf.liste_eleves(periode)
|
pdf.liste_eleves(term)
|
||||||
pdf.set_y(base_y)
|
pdf.set_y(base_y)
|
||||||
pdf.table_colloscope(periode)
|
pdf.table_colloscope(term)
|
||||||
pdf.y += 3
|
pdf.y += 3
|
||||||
pdf.table_colloscope(periode, heading=False, est_colle=False)
|
pdf.table_colloscope(term, heading=False, type="td")
|
||||||
|
|
||||||
return pdf
|
return pdf
|
||||||
|
|
||||||
|
|
||||||
def handle(request):
|
def handle(request):
|
||||||
try:
|
try:
|
||||||
etudiant = Profil.from_request(
|
student = Profile.from_request(
|
||||||
request,
|
request,
|
||||||
preprocess=lambda query: query \
|
preprocess=lambda query: query \
|
||||||
.select_related("etudiant__classe") \
|
.select_related("student__cls") \
|
||||||
.prefetch_related("etudiant__classe__periode_set")
|
.prefetch_related("student__cls__term_set")
|
||||||
)
|
)
|
||||||
except ValueError:
|
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é")
|
return HttpResponse("pas encore supporté")
|
||||||
|
|
||||||
|
|
||||||
periode_str = request.GET.get("periode")
|
term_str = request.GET.get("term")
|
||||||
if periode_str is None:
|
if term_str is None:
|
||||||
periode = etudiant.classe.periode_actuelle()
|
term = student.cls.current_term()
|
||||||
else:
|
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():
|
def main():
|
||||||
periode = Periode.objects.get(id=3)
|
term = Term.objects.get(id=3)
|
||||||
return generate(periode)
|
return generate(term)
|
||||||
|
|
|
@ -2,8 +2,8 @@ from colloscope.models import *
|
||||||
|
|
||||||
def table_colloscope(periode, heading=True, est_colle=True):
|
def table_colloscope(periode, heading=True, est_colle=True):
|
||||||
semaines = periode.range_semaines()
|
semaines = periode.range_semaines()
|
||||||
lundis = [ periode.classe.date_debut_sem(n) for n in semaines ]
|
lundis = [periode.classe.week_beginning_date(n) for n in semaines]
|
||||||
creneaux = Creneau.objects.filter(periode=periode, est_colle=est_colle)
|
creneaux = Slot.objects.filter(periode=periode, est_colle=est_colle)
|
||||||
jours = ["dimanche", "lundi", "mardi", "mercredi", "jeudi", "vendredi", "samedi"]
|
jours = ["dimanche", "lundi", "mardi", "mercredi", "jeudi", "vendredi", "samedi"]
|
||||||
|
|
||||||
s = ""
|
s = ""
|
||||||
|
@ -28,26 +28,26 @@ def table_colloscope(periode, heading=True, est_colle=True):
|
||||||
s += "</tr>\n"
|
s += "</tr>\n"
|
||||||
|
|
||||||
for i, c in enumerate(creneaux):
|
for i, c in enumerate(creneaux):
|
||||||
matiere = c.matiere
|
matiere = c.subject
|
||||||
jour = c.jour
|
jour = c.jour
|
||||||
heure = c.heure
|
heure = c.time
|
||||||
colleur = c.colleur
|
colleur = c.colleur
|
||||||
salle = c.salle
|
salle = c.room
|
||||||
|
|
||||||
s += "<tr>\n"
|
s += "<tr>\n"
|
||||||
s += f"<td>{matiere.libelle}</td>\n"
|
s += f"<td>{matiere.description}</td>\n"
|
||||||
s += f"<td>{jours[jour]}</td>\n"
|
s += f"<td>{jours[jour]}</td>\n"
|
||||||
s += f"<td>{heure.strftime('%H:%M')}</td>\n"
|
s += f"<td>{heure.strftime('%H:%M')}</td>\n"
|
||||||
s += "<td>{} {}</td>\n".format("M." if colleur.civilite=="M" else "Mme", colleur.nom.upper())
|
s += "<td>{} {}</td>\n".format("M." if colleur.civilite=="M" else "Mme", colleur.nom.upper())
|
||||||
s += f"<td>salle</td>\n"
|
s += f"<td>salle</td>\n"
|
||||||
|
|
||||||
for sem in semaines:
|
for sem in semaines:
|
||||||
if Rotation.objects.filter(creneau=c, semaine=sem).exists():
|
if Colle.objects.filter(creneau=c, semaine=sem).exists():
|
||||||
r = Rotation.objects.get(creneau=c, semaine=sem)
|
r = Colle.objects.get(creneau=c, semaine=sem)
|
||||||
groupes = r.groupes
|
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"<td class='modif'>{content}</td>\n"
|
s += f"<td class='modif'>{content}</td>\n"
|
||||||
else:
|
else:
|
||||||
s += f"<td>{content}</td>\n"
|
s += f"<td>{content}</td>\n"
|
||||||
|
|
|
@ -7,27 +7,43 @@
|
||||||
<h1>Tableau de bord</h1>
|
<h1>Tableau de bord</h1>
|
||||||
|
|
||||||
<p>
|
<p>
|
||||||
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>
|
</p>
|
||||||
|
|
||||||
<p>Période actuelle : {{ periode }}. Votre groupe de colle est {{ groupe }}. <a href="table.html">Consulter le colloscope</a></p>
|
<p>Période actuelle : {{ term }}. Votre groupe de colle est {{ group }}. <a href="table.html">Consulter le colloscope</a></p>
|
||||||
|
|
||||||
<h2>Mes colles</h2>
|
<h2>Mes colles</h2>
|
||||||
<a href="{{ lien_calendrier }}">Exporter en .ics (ceci est un permalien public)</a>
|
|
||||||
|
<p><a href="{{ calendar_link }}">Exporter en .ics (ceci est un permalien public)</a></p>
|
||||||
|
|
||||||
|
<p><a href="{% url "colloscope.marketplace" %}">Accéder au marketplace</a></p>
|
||||||
<ul>
|
<ul>
|
||||||
{% for n, lundi, colles in rotations %}
|
{% for n, lundi, colles in colles_per_sem %}
|
||||||
<li>Semaine {{n}} ({{lundi}})</li>
|
<li>Semaine {{n}} ({{lundi}})</li>
|
||||||
<ul>
|
<ul>
|
||||||
{% for colle in colles %}
|
{% if colles %}
|
||||||
<li>{{ colle.creneau.matiere }} ({{ colle.creneau.colleur }}) <a href="{% url "colloscope.desinscription" colle_id=colle.id %}">Absent ?</a>:</li>
|
{% for colle in colles %}
|
||||||
<ul>
|
<li>{{ colle.slot.subject }} ({{ colle.slot.colleur }})</li>
|
||||||
<li>Le {{ colle.date }} à {{ colle.creneau.heure }}</li>
|
<ul>
|
||||||
<li>Groupes : {{ colle.groupes.all | join:"+" }}</li>
|
<li>Le {{ colle.date }} à {{ colle.slot.time }}</li>
|
||||||
<li>Salle : {{ colle.creneau.salle }}</li>
|
<li>Groupes : {{ colle.groups.all | join:"+" }}</li>
|
||||||
<li>Capacité : {{ colle.volume }} / {{ colle.creneau.capacite }}</li>
|
<li>Salle : {{ colle.slot.room }}</li>
|
||||||
</ul>
|
<li>Capacité : {{ colle.volume }} / {{ colle.slot.capacity }}</li>
|
||||||
{% endfor %}
|
<li>Absent ?
|
||||||
|
<form
|
||||||
|
action="{% url "colloscope.withdraw" %}"
|
||||||
|
method="POST"
|
||||||
|
onsubmit="return confirm('Êtes-vous sûr de vouloir vous désinscrire de la colle {{ colle }} ');">
|
||||||
|
{% csrf_token %}
|
||||||
|
<input type="hidden" name="colle_id" value="{{ colle.id }}">
|
||||||
|
<input type="submit" value="Rendre disponible">
|
||||||
|
</form>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
{% endfor %}
|
||||||
|
{% else %}
|
||||||
|
Pas de colles à venir cette semaine.
|
||||||
|
{% endif %}
|
||||||
</ul>
|
</ul>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</ul>
|
</ul>
|
||||||
|
|
|
@ -3,21 +3,34 @@
|
||||||
{% block title %}Marketplace{% endblock title %}
|
{% block title %}Marketplace{% endblock title %}
|
||||||
|
|
||||||
{% block main %}
|
{% block main %}
|
||||||
|
<a href="{% url "colloscope.dashboard" %}">Retour au tableau de bord</a>
|
||||||
|
|
||||||
<h1>Marketplace</h1>
|
<h1>Marketplace</h1>
|
||||||
|
|
||||||
Bienvenue sur le marketplace.
|
Bienvenue sur le marketplace.
|
||||||
|
|
||||||
Les colles libres sont :
|
{% if colles %}
|
||||||
|
|
||||||
<ul>
|
|
||||||
{% for colle in colles %}
|
|
||||||
<li>Colle !</li>
|
|
||||||
<ul>
|
<ul>
|
||||||
<li>{{ colle }}</li>
|
Les colles libres sont :
|
||||||
<li>Places occupées : {{ colle.volume }} / {{ colle.creneau.capacite }}</li>
|
{% for colle in colles %}
|
||||||
<li><a href={% url "colloscope.inscription" colle_id=colle.id %}>Réserver</a></li>
|
<li>Colle !</li>
|
||||||
|
<ul>
|
||||||
|
<li>{{ colle }}</li>
|
||||||
|
<li>Places occupées : {{ colle.volume }} / {{ colle.slot.capacity }}</li>
|
||||||
|
<li>
|
||||||
|
<form action="{% url "colloscope.enroll" %}"
|
||||||
|
method="POST"
|
||||||
|
onsubmit="return confirm('Êtes-vous sûr de vouloir vous inscrire à la colle {{ colle }} ');">
|
||||||
|
{% csrf_token %}
|
||||||
|
<input type="hidden" name="colle_id" value="{{ colle.id }}">
|
||||||
|
<input type="submit" value="Réserver">
|
||||||
|
</form>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
{% endfor %}
|
||||||
</ul>
|
</ul>
|
||||||
{% endfor %}
|
{% else %}
|
||||||
</ul>
|
Aucune colle n'est disponible
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
{% endblock main %}
|
{% endblock main %}
|
|
@ -17,16 +17,16 @@
|
||||||
{% block main %}
|
{% block main %}
|
||||||
|
|
||||||
<p>
|
<p>
|
||||||
Lycée : {{ periode.classe.lycee.libelle }}. Classe : {{ periode.classe.libelle }}. <a href="dashboard.html">Retour au tableau de bord</a>
|
Lycée : {{ term.cls.school.description }}. Classe : {{ term.cls.description }}. <a href="dashboard.html">Retour au tableau de bord</a>
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<h2>Colloscope : {{ periode.libelle }}</h2>
|
<h2>Colloscope : {{ term.description }}</h2>
|
||||||
|
|
||||||
<form method="get" action="{% url "colloscope.table" %}">
|
<form method="get" action="{% url "colloscope.table" %}">
|
||||||
Changer de période :
|
Changer de période :
|
||||||
<select name="periode" id="periode">
|
<select name="term" id="term">
|
||||||
{% for p in periode.classe.periode_set.all %}
|
{% for p in term.cls.term_set.all %}
|
||||||
{% if p.id == periode.id %}
|
{% if p.id == term.id %}
|
||||||
<option value="{{ p.id }}" selected>{{ p }}</option>
|
<option value="{{ p.id }}" selected>{{ p }}</option>
|
||||||
{% else %}
|
{% else %}
|
||||||
<option value="{{ p.id }}" selected>{{ p }}</option>
|
<option value="{{ p.id }}" selected>{{ p }}</option>
|
||||||
|
@ -36,8 +36,8 @@
|
||||||
<button type="submit">Valider</button>
|
<button type="submit">Valider</button>
|
||||||
</form>
|
</form>
|
||||||
|
|
||||||
{% if request.GET.periode %}
|
{% if request.GET.term %}
|
||||||
<a href="export.pdf?periode={{ request.GET.periode }}" target="_blank">Exporter le colloscope</a>
|
<a href="export.pdf?term={{ request.GET.term }}" target="_blank">Exporter le colloscope</a>
|
||||||
{% else %}
|
{% else %}
|
||||||
<a href="export.pdf" target="_blank">Exporter le colloscope</a>
|
<a href="export.pdf" target="_blank">Exporter le colloscope</a>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
@ -53,7 +53,7 @@
|
||||||
<col>
|
<col>
|
||||||
</colgroup>
|
</colgroup>
|
||||||
<colgroup>
|
<colgroup>
|
||||||
{% for _ in semaines %}
|
{% for _ in weeks %}
|
||||||
<col>
|
<col>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</colgroup>
|
</colgroup>
|
||||||
|
@ -64,32 +64,32 @@
|
||||||
<th rowspan=2>Heure</th>
|
<th rowspan=2>Heure</th>
|
||||||
<th rowspan=2>Colleur</th>
|
<th rowspan=2>Colleur</th>
|
||||||
<th rowspan=2>Salle</th>
|
<th rowspan=2>Salle</th>
|
||||||
{% for n in semaines %}
|
{% for n in weeks %}
|
||||||
<th>{{ n }}</th>
|
<th>{{ n }}</th>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</tr>
|
</tr>
|
||||||
|
|
||||||
<tr>
|
<tr>
|
||||||
{% for lundi in lundis %}
|
{% for monday in mondays %}
|
||||||
<th>{{ lundi | strftime:"%d/%m/%y" }}</th>
|
<th>{{ monday | strftime:"%d/%m/%y" }}</th>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</tr>
|
</tr>
|
||||||
|
|
||||||
|
|
||||||
{% for c, rs in rotations %}
|
{% for c, rs in colles %}
|
||||||
<tr>
|
<tr>
|
||||||
<td>{{ c.matiere.libelle }}</td>
|
<td>{{ c.subject.description }}</td>
|
||||||
<td>{{ jours | getitem:c.jour }}</td>
|
<td>{{ days | getitem:c.day }}</td>
|
||||||
<td>{{ c.heure | strftime:"%H:%M" }}</td>
|
<td>{{ c.time | strftime:"%H:%M" }}</td>
|
||||||
<td>{{ c.colleur }}</td>
|
<td>{{ c.colleur }}</td>
|
||||||
<td>{{ c.salle }}</td>
|
<td>{{ c.room }}</td>
|
||||||
|
|
||||||
{% for sem, exists, r, est_modifiee, groupes in rs %}
|
{% for sem, exists, r, is_edited, groups in rs %}
|
||||||
{% if exists %}
|
{% if exists %}
|
||||||
{% if est_modifiee %}
|
{% if is_edited %}
|
||||||
<td class="modif">{{ groupes | join:"," }}</td>
|
<td class="modif">{{ groups | join:"," }}</td>
|
||||||
{% else %}
|
{% else %}
|
||||||
<td>{{ groupes | join:"," }}</td>
|
<td>{{ groups | join:"," }}</td>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% else %}
|
{% else %}
|
||||||
<td></td>
|
<td></td>
|
||||||
|
|
|
@ -6,9 +6,9 @@ urlpatterns = [
|
||||||
path("table.html", views.colloscope, name="colloscope.table"),
|
path("table.html", views.colloscope, name="colloscope.table"),
|
||||||
path("dashboard.html", views.dashboard, name="colloscope.dashboard"),
|
path("dashboard.html", views.dashboard, name="colloscope.dashboard"),
|
||||||
path("export.pdf", views.export, name="colloscope.export"),
|
path("export.pdf", views.export, name="colloscope.export"),
|
||||||
path("calendrier.ics", views.icalendar, name="colloscope.calendrier"),
|
path("export/calendar/<str:key>/calendar.ics", views.icalendar, name="colloscope.calendar.ics"),
|
||||||
path("choix_profil", views.choix_profil, name="colloscope.choix_profil"),
|
path("select_profile", views.select_profile, name="colloscope.select_profile"),
|
||||||
path("marketplace.html", views.marketplace, name="colloscope.marketplace"),
|
path("marketplace.html", views.marketplace, name="colloscope.marketplace"),
|
||||||
path("action/inscription/<int:colle_id>", views.inscription, name="colloscope.inscription"),
|
path("action/enroll", views.enroll, name="colloscope.enroll"),
|
||||||
path("action/desinscription/<int:colle_id>", views.desinscription, name="colloscope.desinscription"),
|
path("action/withdraw", views.withdraw, name="colloscope.withdraw"),
|
||||||
]
|
]
|
||||||
|
|
|
@ -1,23 +1,21 @@
|
||||||
from datetime import date, timedelta
|
|
||||||
from uuid import uuid4
|
from uuid import uuid4
|
||||||
|
|
||||||
from django.contrib.auth.models import User
|
from django import forms
|
||||||
from django.shortcuts import redirect
|
from django.shortcuts import redirect, render
|
||||||
from django.http import HttpResponse
|
from django.http import HttpResponse, HttpResponseRedirect
|
||||||
from django.template import loader
|
from django.template import loader
|
||||||
|
from django.views.decorators.http import require_POST
|
||||||
from django.contrib.auth.decorators import login_required
|
from django.contrib.auth.decorators import login_required
|
||||||
|
|
||||||
from colloscope.models import *
|
from colloscope.models import *
|
||||||
from colloscope.table import table_colloscope
|
|
||||||
from colloscope.pdfexport import handle
|
from colloscope.pdfexport import handle
|
||||||
from colloscope.icalexport import to_calendar
|
from colloscope.icalexport import to_calendar
|
||||||
|
|
||||||
|
|
||||||
def handler404(request):
|
def handler404(request):
|
||||||
template = loader.get_template("404.html")
|
template = loader.get_template("404.html")
|
||||||
#response.status_code = 404
|
|
||||||
context = {}
|
context = {}
|
||||||
return HttpResponse(template.render(context))
|
return HttpResponse(template.render(context), status=404)
|
||||||
|
|
||||||
|
|
||||||
def home_redirect(request):
|
def home_redirect(request):
|
||||||
|
@ -25,169 +23,182 @@ def home_redirect(request):
|
||||||
|
|
||||||
|
|
||||||
@login_required
|
@login_required
|
||||||
def choix_profil(request):
|
def select_profile(request):
|
||||||
user = request.user
|
user = request.user
|
||||||
session = request.session
|
session = request.session
|
||||||
|
|
||||||
if not Profil.objects.filter(utilisateur=user).exists():
|
if not Profile.objects.filter(user=user).exists():
|
||||||
profil = Profil(utilisateur=user)
|
profile = Profile(user=user)
|
||||||
profil.save()
|
profile.save()
|
||||||
else:
|
else:
|
||||||
profil = Profil.objects.get(utilisateur=user)
|
profile = Profile.objects.get(user=user)
|
||||||
|
|
||||||
if profil.etudiant is not None and profil.colleur is None:
|
if profile.student is not None and profile.colleur is None:
|
||||||
session["profil"] = "etudiant"
|
session["profile"] = "student"
|
||||||
return redirect("/colloscope/")
|
return redirect("/colloscope/")
|
||||||
elif profil.colleur is not None and profil.etudiant is None:
|
elif profile.colleur is not None and profile.student is None:
|
||||||
session["profil"] = "colleur"
|
session["profile"] = "colleur"
|
||||||
return redirect("/colloscope/")
|
return redirect("/colloscope/")
|
||||||
else:
|
else:
|
||||||
if profil.etudiant is not None:
|
if profile.student is not None:
|
||||||
template = loader.get_template("choix_profil.html")
|
template = loader.get_template("select_profile.html")
|
||||||
else:
|
else:
|
||||||
template = loader.get_template("profil_non_associe.html")
|
template = loader.get_template("unbound_profile.html")
|
||||||
|
|
||||||
context = {
|
context = {
|
||||||
"profil": profil,
|
"profile": profile,
|
||||||
}
|
}
|
||||||
return HttpResponse(template.render(context))
|
return HttpResponse(template.render(context))
|
||||||
|
|
||||||
|
|
||||||
def get_lien_calendrier(etudiant, periode):
|
def get_lien_calendrier(student, term):
|
||||||
try:
|
try:
|
||||||
lien = LienCalendrier.objects.get(etudiant=etudiant, periode=periode)
|
lien = CalendarLink.objects.get(student=student, term=term)
|
||||||
except LienCalendrier.DoesNotExist:
|
except CalendarLink.DoesNotExist:
|
||||||
code = uuid4().hex
|
key = uuid4().hex
|
||||||
lien = LienCalendrier(code=code, etudiant=etudiant, periode=periode)
|
lien = CalendarLink(key=key, student=student, term=term)
|
||||||
lien.save()
|
lien.save()
|
||||||
|
|
||||||
return f"calendrier.ics?key={lien.code}"
|
return f"calendrier.ics?key={lien.key}"
|
||||||
|
|
||||||
|
|
||||||
#@login_required
|
@login_required
|
||||||
def dashboard(request):
|
def dashboard(request):
|
||||||
try:
|
try:
|
||||||
etudiant = Profil.from_request(
|
student = Profile.from_request(
|
||||||
request,
|
request,
|
||||||
preprocess=lambda query: (query
|
preprocess=lambda query: (query
|
||||||
.select_related("etudiant__classe")
|
.select_related("student__cls")
|
||||||
.prefetch_related("etudiant__classe__periode_set"))
|
.prefetch_related("student__cls__term_set"))
|
||||||
)
|
)
|
||||||
except ValueError:
|
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é")
|
return HttpResponse("pas encore supporté")
|
||||||
|
|
||||||
periode = etudiant.classe.periode_actuelle()
|
term = student.cls.current_term()
|
||||||
groupe = etudiant.groupe_de_colle(periode)
|
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())
|
colles_per_sem = [None] * len(term.range_weeks())
|
||||||
for i, n in enumerate(periode.range_semaines()):
|
for k, n in enumerate(term.range_weeks()):
|
||||||
lundi = periode.classe.date_debut_sem(n)
|
lundi = term.cls.week_beginning_date(n)
|
||||||
colles = rotations.filter(date__gte=lundi, date__lt=lundi + timedelta(weeks=1))
|
colles_per_sem[k] = n, lundi, colles.filter(date__gte=max(lundi, date.today()),
|
||||||
colles_par_sem[i] = n, lundi, colles
|
date__lt=lundi + timedelta(weeks=1))
|
||||||
|
|
||||||
template = loader.get_template("dashboard.html")
|
template = loader.get_template("dashboard.html")
|
||||||
lien_calendrier = get_lien_calendrier(etudiant, periode)
|
calendar_link = get_calendar_link(student, term)
|
||||||
|
|
||||||
context = {
|
context = {
|
||||||
"etudiant": etudiant,
|
"student": student,
|
||||||
"periode": periode,
|
"term": term,
|
||||||
"groupe": groupe,
|
"group": group,
|
||||||
"rotations": colles_par_sem,
|
"colles_per_sem": colles_per_sem,
|
||||||
"lien_calendrier": lien_calendrier,
|
"calendar_link": calendar_link,
|
||||||
}
|
}
|
||||||
|
|
||||||
return HttpResponse(template.render(context, request))
|
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
|
@login_required
|
||||||
def marketplace(request):
|
def marketplace(request):
|
||||||
try:
|
try:
|
||||||
etudiant = Profil.from_request(
|
student = Profile.from_request(
|
||||||
request,
|
request,
|
||||||
preprocess=lambda query: query \
|
preprocess=lambda query: (query
|
||||||
.select_related("etudiant__classe") \
|
.select_related("student__cls")
|
||||||
.prefetch_related("etudiant__classe__periode_set")
|
.prefetch_related("student__cls__term_set"))
|
||||||
)
|
)
|
||||||
except ValueError:
|
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é")
|
return HttpResponse("pas encore supporté")
|
||||||
|
|
||||||
periode = etudiant.classe.periode_actuelle()
|
term = student.cls.current_term()
|
||||||
colles = periode.query_rotations_not_full()
|
colles = term.query_colles_not_full()
|
||||||
|
|
||||||
template = loader.get_template("marketplace.html")
|
|
||||||
|
|
||||||
context = {
|
context = {
|
||||||
"colles": colles
|
"colles": colles,
|
||||||
}
|
}
|
||||||
|
|
||||||
return HttpResponse(template.render(context, request))
|
return render(request, "marketplace.html", context)
|
||||||
|
|
||||||
|
|
||||||
@login_required
|
@login_required
|
||||||
def colloscope(request):
|
def colloscope(request):
|
||||||
try:
|
try:
|
||||||
etudiant = Profil.from_request(
|
student = Profile.from_request(
|
||||||
request,
|
request,
|
||||||
preprocess=lambda query: query \
|
preprocess=lambda query: (query
|
||||||
.select_related("etudiant__classe") \
|
.select_related("student__cls")
|
||||||
.prefetch_related("etudiant__classe__periode_set")
|
.prefetch_related("student__cls__term_set"))
|
||||||
)
|
)
|
||||||
except ValueError:
|
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é")
|
return HttpResponse("pas encore supporté")
|
||||||
|
|
||||||
periode_str = request.GET.get("periode")
|
term_str = request.GET.get("term")
|
||||||
if periode_str is None:
|
if term_str is None:
|
||||||
periode = etudiant.classe.periode_actuelle()
|
term = student.cls.current_term()
|
||||||
else:
|
else:
|
||||||
try:
|
try:
|
||||||
periode = Periode.objects.get(id=int(periode_str), classe=etudiant.classe)
|
term = Term.objects.get(id=int(term_str), cls=student.cls)
|
||||||
except Periode.DoesNotExist:
|
except Term.DoesNotExist:
|
||||||
template = loader.get_template("404.html")
|
template = loader.get_template("404.html")
|
||||||
context = {}
|
context = {}
|
||||||
response = HttpResponse(template.render(context, request))
|
response = HttpResponse(template.render(context, request))
|
||||||
response.status_code = 404
|
response.status_code = 404
|
||||||
|
|
||||||
creneaux = Creneau.objects \
|
return response
|
||||||
.filter(periode=periode, est_colle=True) \
|
|
||||||
.prefetch_related("rotation_set")
|
|
||||||
|
|
||||||
semaines = periode.range_semaines()
|
slots = Slot.objects \
|
||||||
rotations = [(c, []) for c in creneaux]
|
.filter(term=term, type__description="colle") \
|
||||||
for c, l in rotations:
|
.prefetch_related("colle_set")
|
||||||
for sem in semaines:
|
|
||||||
lundi = periode.classe.date_debut_sem(sem)
|
|
||||||
|
|
||||||
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()
|
exists = rot.exists()
|
||||||
|
|
||||||
if exists:
|
if exists:
|
||||||
r = rot.first()
|
r = rot.first()
|
||||||
est_modifiee = r.est_modifiee()
|
is_edited = r.is_edited()
|
||||||
groupes = (g.libelle for g in r.groupes.all())
|
groups = (g.description for g in r.groups.all())
|
||||||
else:
|
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")
|
template = loader.get_template("table.html")
|
||||||
|
|
||||||
context = {
|
context = {
|
||||||
"periode": periode,
|
"term": term,
|
||||||
"semaines": semaines,
|
"weeks": weeks,
|
||||||
"lundis": [periode.classe.date_debut_sem(n) for n in semaines],
|
"mondays": [term.cls.week_beginning_date(n) for n in weeks],
|
||||||
"jours": ["dimanche", "lundi", "mardi", "mercredi", "jeudi", "vendredi", "samedi"],
|
"days": ["dimanche", "lundi", "mardi", "mercredi", "jeudi", "vendredi", "samedi"],
|
||||||
"rotations": rotations,
|
"colles": colles,
|
||||||
}
|
}
|
||||||
|
|
||||||
return HttpResponse(template.render(context, request))
|
return HttpResponse(template.render(context, request))
|
||||||
|
@ -198,75 +209,79 @@ def export(request):
|
||||||
return HttpResponse(bytes(handle(request).output()), content_type="application/pdf")
|
return HttpResponse(bytes(handle(request).output()), content_type="application/pdf")
|
||||||
|
|
||||||
|
|
||||||
def get_lien_calendrier(etudiant, periode):
|
def get_calendar_link(student, term):
|
||||||
try:
|
try:
|
||||||
lien = LienCalendrier.objects.get(etudiant=etudiant, periode=periode)
|
lien = CalendarLink.objects.get(student=student, term=term)
|
||||||
except LienCalendrier.DoesNotExist:
|
except CalendarLink.DoesNotExist:
|
||||||
code = uuid4().hex
|
code = uuid4().hex
|
||||||
lien = LienCalendrier(code=code, etudiant=etudiant, periode=periode)
|
lien = CalendarLink(key=key, student=student, term=term)
|
||||||
lien.save()
|
lien.save()
|
||||||
|
|
||||||
return f"calendrier.ics?key={lien.code}"
|
return f"export/calendar/{lien.key}/calendar.ics"
|
||||||
|
|
||||||
|
|
||||||
def icalendar(request):
|
def icalendar(request, key):
|
||||||
if request.GET.get("key") is not None:
|
try:
|
||||||
try:
|
link = CalendarLink.objects.get(key=key)
|
||||||
lien = LienCalendrier.objects.get(code=request.GET.get("key"))
|
|
||||||
|
|
||||||
if not request.GET.get("edt"):
|
if not request.GET.get("edt"):
|
||||||
return HttpResponse(to_calendar(lien.etudiant, lien.periode, include_EDT=True).to_ical(),
|
return HttpResponse(to_calendar(link.student, link.term, include_EDT=True).to_ical(),
|
||||||
content_type="text/calendar")
|
|
||||||
|
|
||||||
return HttpResponse(to_calendar(lien.etudiant, lien.periode, include_EDT=True).to_ical(),
|
|
||||||
content_type="text/calendar")
|
content_type="text/calendar")
|
||||||
|
|
||||||
except LienCalendrier.DoesNotExist:
|
return HttpResponse(to_calendar(link.student, link.term, include_EDT=True).to_ical(),
|
||||||
return HttpResponse("Invalid key", status=404)
|
content_type="text/calendar")
|
||||||
else:
|
|
||||||
return HttpResponse("Unspecified key", status=404)
|
except CalendarLink.DoesNotExist:
|
||||||
|
return HttpResponse("Invalid key", status=404)
|
||||||
|
|
||||||
|
|
||||||
@login_required
|
def amend(request, colle_id, do_enroll):
|
||||||
def amender(request, colle_id, est_positif):
|
|
||||||
try:
|
try:
|
||||||
etudiant = Profil.from_request(
|
student = Profile.from_request(
|
||||||
request,
|
request,
|
||||||
preprocess=lambda query: query \
|
preprocess=lambda query: (query
|
||||||
.select_related("etudiant__classe") \
|
.select_related("student__cls")
|
||||||
.prefetch_related("etudiant__classe__periode_set")
|
.prefetch_related("student__cls__term_set"))
|
||||||
)
|
)
|
||||||
except ValueError:
|
except ValueError:
|
||||||
return redirect("colloscope.choix_profil")
|
return redirect("colloscope.choix_profil")
|
||||||
|
|
||||||
if not isinstance(etudiant, Etudiant):
|
if not isinstance(student, Student):
|
||||||
return HttpResponse("pas encore supporté")
|
return HttpResponse("pas encore supporté")
|
||||||
|
|
||||||
#try:
|
if do_enroll:
|
||||||
if est_positif:
|
(Colle.objects
|
||||||
(Rotation.objects
|
.get(id=colle_id, slot__term__cls=student.cls)
|
||||||
.get(id=colle_id, creneau__periode__classe=etudiant.classe)
|
.amend(enroll=True, student=student, notify=True))
|
||||||
.amender(est_positif=True, etudiant=etudiant, notifier=True))
|
|
||||||
else:
|
else:
|
||||||
(Rotation.objects
|
(Colle.objects
|
||||||
.get(id=colle_id, groupes__etudiant=etudiant)
|
.get(id=colle_id, groups__student=student)
|
||||||
.amender(est_positif=False, etudiant=etudiant, notifier=True))
|
.amend(enroll=False, student=student, notify=True))
|
||||||
|
|
||||||
return HttpResponse("ok")
|
|
||||||
#except Exception as e:
|
|
||||||
# return HttpResponse(f"aïe : {e}")
|
|
||||||
|
|
||||||
|
|
||||||
|
@require_POST
|
||||||
@login_required
|
@login_required
|
||||||
def inscription(request, colle_id):
|
def enroll(request):
|
||||||
return amender(request, colle_id, True)
|
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
|
@login_required
|
||||||
def desinscription(request, colle_id):
|
def withdraw(request):
|
||||||
return amender(request, colle_id, False)
|
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())
|
|
||||||
|
|
|
@ -16,12 +16,14 @@ Including another URLconf
|
||||||
"""
|
"""
|
||||||
from django.contrib import admin, auth
|
from django.contrib import admin, auth
|
||||||
from django.urls import include, path
|
from django.urls import include, path
|
||||||
|
from django.contrib.staticfiles import views as vstatic
|
||||||
|
|
||||||
from colloscope.views import home_redirect
|
from colloscope.views import home_redirect
|
||||||
|
|
||||||
urlpatterns = [
|
urlpatterns = [
|
||||||
path('', home_redirect, name="home"),
|
path('', home_redirect, name="home"),
|
||||||
|
path("favicon.ico", lambda req: vstatic.serve(req, "favicon.ico")),
|
||||||
path('colloscope/', include('colloscope.urls')),
|
path('colloscope/', include('colloscope.urls')),
|
||||||
path('admin/', admin.site.urls),
|
path('admin/', admin.site.urls),
|
||||||
path('comptes/', include("django.contrib.auth.urls")),
|
path('accounts/', include("django.contrib.auth.urls")),
|
||||||
]
|
]
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
|
@ -20,11 +20,11 @@ def scrape(periode, chemin):
|
||||||
else:
|
else:
|
||||||
c = Colleur.objects.get(nom=nom_colleur, civilite=civilite)
|
c = Colleur.objects.get(nom=nom_colleur, civilite=civilite)
|
||||||
|
|
||||||
if not Matiere.objects.filter(classe=periode.classe, libelle=matiere).exists():
|
if not Subject.objects.filter(classe=periode.classe, libelle=matiere).exists():
|
||||||
m = Matiere(classe=periode.classe, libelle=matiere, code=matiere.upper())
|
m = Subject(classe=periode.classe, libelle=matiere, code=matiere.upper())
|
||||||
m.save()
|
m.save()
|
||||||
else:
|
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}
|
jours_dict = {"dimanche": 0, "lundi": 1, "mardi": 2, "mercredi": 3, "jeudi": 4, "vendredi": 5, "samedi": 6}
|
||||||
j = jours_dict[jour]
|
j = jours_dict[jour]
|
||||||
|
@ -40,11 +40,11 @@ def scrape(periode, chemin):
|
||||||
|
|
||||||
print(f"--> Traitement de {c=}, {m=}, {j=}, {h=}, {d=}, {c2=}")
|
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():
|
if not Slot.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)
|
creneau = Slot(periode=periode, jour=j, heure=h, duree=d, salle="nc", matiere=m, colleur=c, est_colle=True, capacite=c2)
|
||||||
creneau.save()
|
creneau.save()
|
||||||
else:
|
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):
|
for i, r in enumerate(rotations):
|
||||||
sem = headers[4+i].split("/")
|
sem = headers[4+i].split("/")
|
||||||
|
@ -53,16 +53,16 @@ def scrape(periode, chemin):
|
||||||
|
|
||||||
s = date.fromisoformat("-".join(sem)) + (j-1) * timedelta(days=1)
|
s = date.fromisoformat("-".join(sem)) + (j-1) * timedelta(days=1)
|
||||||
|
|
||||||
if not Rotation.objects.filter(creneau=creneau, date=s):
|
if not Colle.objects.filter(creneau=creneau, date=s):
|
||||||
rot = Rotation(creneau=creneau, date=s)
|
rot = Colle(creneau=creneau, date=s)
|
||||||
rot.save()
|
rot.save()
|
||||||
else:
|
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():
|
def main():
|
||||||
periode = Periode.objects.get(id=3)
|
periode = Term.objects.get(id=3)
|
||||||
scrape(periode, "colloscope.csv")
|
scrape(periode, "colloscope.csv")
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
|
|
Binary file not shown.
After Width: | Height: | Size: 25 KiB |
|
@ -7,5 +7,5 @@
|
||||||
{% endblock header %}
|
{% endblock header %}
|
||||||
|
|
||||||
{% block main %}
|
{% block main %}
|
||||||
Vous vous êtes perdu.
|
Vous vous êtes perdu. ASKMULLER
|
||||||
{% endblock main %}
|
{% endblock main %}
|
||||||
|
|
|
@ -13,7 +13,7 @@
|
||||||
{% if request.user.is_authenticated %}
|
{% if request.user.is_authenticated %}
|
||||||
<div class="bandeau">
|
<div class="bandeau">
|
||||||
Vous êtes connecté avec le compte <b>{{ user.username }}</b>.
|
Vous êtes connecté avec le compte <b>{{ user.username }}</b>.
|
||||||
{% if request.session.profil == "etudiant" %}
|
{% if request.session.profil == "student" %}
|
||||||
Profil actuel : étudiant.
|
Profil actuel : étudiant.
|
||||||
{% elif request.session.profil == "colleur" %}
|
{% elif request.session.profil == "colleur" %}
|
||||||
Profil actuel : colleur.
|
Profil actuel : colleur.
|
||||||
|
|
Loading…
Reference in New Issue