aboutsummaryrefslogtreecommitdiff
path: root/models.py
diff options
context:
space:
mode:
Diffstat (limited to 'models.py')
-rw-r--r--models.py144
1 files changed, 101 insertions, 43 deletions
diff --git a/models.py b/models.py
index 25ddd1b..e56d33d 100644
--- a/models.py
+++ b/models.py
@@ -1,4 +1,4 @@
-# Copyright (C) 2017 Alban Gruin
+# Copyright (C) 2017-2018 Alban Gruin
#
# celcatsanitizer is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as published
@@ -16,7 +16,7 @@
from functools import reduce
from django.db import models
-from django.db.models import Count, Manager, Q
+from django.db.models import Count, Manager, OuterRef, Q, Subquery
from django.db.models.functions import ExtractWeek, ExtractYear
from django.utils import timezone
from django.utils.text import slugify
@@ -25,12 +25,11 @@ from .utils import parse_group
class SlugModel(models.Model):
- def save(self):
+ def save(self, *args, **kwargs):
if not self.slug:
self.slug = slugify(self.name)
- super(SlugModel, self).save()
-
+ super(SlugModel, self).save(*args, **kwargs)
class Meta:
abstract = True
@@ -43,26 +42,49 @@ class Year(SlugModel):
def __str__(self):
return self.name
-
class Meta:
verbose_name = "année"
verbose_name_plural = "années"
+class Source(models.Model):
+ url = models.URLField(max_length=255, verbose_name="URL", unique=True)
+ last_update_date = models.DateTimeField(null=True, blank=True,
+ verbose_name="dernière mise à jour"
+ " Celcat")
+
+ def __str__(self):
+ return self.url
+
+ @property
+ def formatted_timetables(self):
+ return ", ".join([str(timetable) for timetable in
+ self.timetables.all()])
+
+ class Meta:
+ verbose_name = "source d’emploi du temps"
+ verbose_name_plural = "sources d’emploi du temps"
+
+
+class TimetableManager(Manager):
+ def get_queryset(self):
+ return super(Manager, self).get_queryset().select_related("year")
+
+
class Timetable(SlugModel):
+ objects = TimetableManager()
+
year = models.ForeignKey(Year, on_delete=models.CASCADE,
verbose_name="année")
name = models.CharField(max_length=64, verbose_name="nom")
- url = models.URLField(max_length=255, verbose_name="URL")
slug = models.SlugField(max_length=64, default="")
-
- last_update_date = models.DateTimeField(verbose_name="dernière mise à jour Celcat",
- null=True, blank=True)
+ source = models.ForeignKey(Source, on_delete=models.CASCADE,
+ verbose_name="source",
+ related_name="timetables")
def __str__(self):
return self.year.name + " " + self.name
-
class Meta:
unique_together = (("year", "name"), ("year", "slug"),)
verbose_name = "emploi du temps"
@@ -71,31 +93,40 @@ class Timetable(SlugModel):
class GroupManager(Manager):
def get_parents(self, group):
- groups_criteria = Q(subgroup="")
-
- if len(group.subgroup) != 0:
- groups_criteria |= reduce(lambda x, y: x | y,
- [Q(subgroup=group.subgroup[:i])
- for i in range(1, len(group.subgroup) + 1)])
+ groups_criteria = reduce(lambda x, y: x | y,
+ [Q(subgroup=group.subgroup[:i])
+ for i in range(1, len(group.subgroup) + 1)],
+ Q(subgroup=""))
return self.get_queryset().filter(groups_criteria,
- Q(semester=None) | Q(semester=group.semester),
+ Q(semester=None) |
+ Q(semester=group.semester),
mention=group.mention,
- timetable=group.timetable)
+ source=group.source)
+ def get_relevant_groups(self, start, **criteria):
+ courses = Course.objects.filter(groups=OuterRef("pk"),
+ begin__gte=start) \
+ .only("pk")[:1]
+ return self.get_queryset() \
+ .annotate(c=Subquery(courses,
+ output_field=models.IntegerField())) \
+ .filter(c__isnull=False, **criteria).order_by("name")
-class Group(models.Model):
+
+class Group(SlugModel):
objects = GroupManager()
name = models.CharField(max_length=255, verbose_name="nom")
celcat_name = models.CharField(max_length=255,
verbose_name="nom dans Celcat")
- timetable = models.ForeignKey(Timetable, on_delete=models.CASCADE,
- verbose_name="emploi du temps")
+ source = models.ForeignKey(Source, on_delete=models.CASCADE,
+ verbose_name="source d’emploi du temps")
mention = models.CharField(max_length=128)
semester = models.IntegerField(verbose_name="semestre", null=True)
- subgroup = models.CharField(max_length=16, verbose_name="sous-groupe", default="")
+ subgroup = models.CharField(max_length=16, verbose_name="sous-groupe",
+ default="")
slug = models.SlugField(max_length=64, default="")
@@ -106,10 +137,10 @@ class Group(models.Model):
if self.subgroup is not None and subgroup is not None:
subgroup_corresponds = self.subgroup.startswith(subgroup)
- return (self.mention.startswith(mention) or \
+ return (self.mention.startswith(mention) or
mention.startswith(self.mention)) and \
- (self.semester == semester or semester is None) and \
- subgroup_corresponds
+ (self.semester == semester or semester is None) and \
+ subgroup_corresponds
@property
def group_info(self):
@@ -121,42 +152,68 @@ class Group(models.Model):
def save(self, *args, **kwargs):
if self.name == "":
self.name = self.celcat_name
- self.slug = slugify(self.name)
self.mention, self.semester, self.subgroup = parse_group(self.name)
if self.subgroup is None:
self.subgroup = ""
- super(Group, self).save()
-
+ super(Group, self).save(*args, **kwargs)
class Meta:
index_together = ("mention", "semester", "subgroup",)
- unique_together = (("name", "timetable",),
- ("celcat_name", "timetable",),
- ("slug", "timetable",),)
+ unique_together = (("name", "source",),
+ ("celcat_name", "source",),
+ ("slug", "source",),)
verbose_name = "groupe"
verbose_name_plural = "groupes"
-class Room(models.Model):
+class RoomManager(Manager):
+ def qsjps(self, begin, end):
+ # On récupère la liste des cours qui commencent avant la fin
+ # de l’intervalle sélectionné et qui terminent après le début
+ # de l’intervalle, c’est-à-dire qu’au moins une partie du
+ # cours se déroule pendant l’intervalle. On récupère ensuite
+ # la liste des salles dans lesquelles se déroulent ces
+ # cours. On exclu les cours n’ayant aucune salle assignée.
+ courses = Course.objects.filter(begin__lt=end, end__gt=begin,
+ rooms__isnull=False) \
+ .values_list("rooms", flat=True)
+
+ # On sélectionne ensuite les salles qui ne sont pas dans la
+ # liste récupérée plus haut, et on les trie par leur nom.
+ return self.get_queryset().exclude(pk__in=courses).order_by("name")
+
+
+class Room(SlugModel):
+ objects = RoomManager()
+
name = models.CharField(max_length=255, unique=True, verbose_name="nom")
+ slug = models.SlugField(max_length=64, default="", unique=True)
def __str__(self):
return self.name
-
class Meta:
verbose_name = "salle"
verbose_name_plural = "salles"
class CourseManager(Manager):
- def get_courses_for_group(self, group, **criteria):
- return self.get_queryset() \
- .filter(groups__in=Group.objects.get_parents(group), **criteria) \
- .order_by("begin").prefetch_related("rooms")
+ def get_courses(self, obj, **criteria):
+ qs = self.get_queryset()
+ if isinstance(obj, Group):
+ qs = qs.filter(
+ groups__in=Group.objects.get_parents(obj), **criteria) \
+ .prefetch_related("rooms")
+ elif isinstance(obj, Room):
+ qs = qs.filter(rooms=obj, **criteria) \
+ .prefetch_related("rooms", "groups")
+ else:
+ raise(TypeError, "obj must be a Group or a Room")
+
+ return qs.order_by("begin")
def get_weeks(self, **criteria):
return self.get_queryset() \
@@ -165,17 +222,19 @@ class CourseManager(Manager):
.annotate(year=ExtractYear("begin"),
week=ExtractWeek("begin")) \
.values("groups__mention", "groups__semester",
- "groups__subgroup", "year", "week")
+ "groups__subgroup", "year", "week") \
+ .annotate(c=Count("*"))
class Course(models.Model):
objects = CourseManager()
- name = models.CharField(max_length=255, verbose_name="nom", default="Sans nom")
+ name = models.CharField(max_length=255, verbose_name="nom",
+ default="Sans nom")
type_ = models.CharField(name="type", max_length=255,
verbose_name="type de cours", null=True)
- timetable = models.ForeignKey(Timetable, on_delete=models.CASCADE,
- verbose_name="emploi du temps")
+ source = models.ForeignKey(Source, on_delete=models.CASCADE,
+ verbose_name="emploi du temps")
notes = models.TextField(verbose_name="remarques", blank=True, null=True)
groups = models.ManyToManyField(Group, verbose_name="groupes")
@@ -199,7 +258,6 @@ class Course(models.Model):
super(Course, self).save(*args, **kwargs)
-
class Meta:
verbose_name = "cours"
verbose_name_plural = "cours"