aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAlban Gruin2017-11-26 18:20:52 +0100
committerAlban Gruin2017-11-26 18:20:52 +0100
commita05da3098077e89bdb1f120b92ab87e593e331f4 (patch)
tree3c7d6fad2fa63abd0d075efb0ee4bbec4f9b3204
parentdf5ef1a932170d55623bf0469ef56c5af9e62fa3 (diff)
parent8cd40466e5b1ef9f00cf88c9375289d235abc009 (diff)
Merge branch 'dev/alban' into futur
-rw-r--r--admin.py2
-rw-r--r--models.py49
-rw-r--r--tests.py62
-rw-r--r--utils.py28
-rw-r--r--views.py29
5 files changed, 80 insertions, 90 deletions
diff --git a/admin.py b/admin.py
index c93b6b3..0235fb9 100644
--- a/admin.py
+++ b/admin.py
@@ -44,7 +44,7 @@ class TimetableAdmin(admin.ModelAdmin):
class GroupAdmin(admin.ModelAdmin):
fieldsets = (
(None, {"fields": ("name", "celcat_name", "timetable", "hidden",)}),
- ("Groupes", {"fields": ("mention", "subgroup",)}),)
+ ("Groupes", {"fields": ("mention", "semester", "subgroup",)}),)
list_display = ("name", "timetable", "hidden",)
list_editable = ("hidden",)
list_filter = ("timetable",)
diff --git a/models.py b/models.py
index 40406e3..25ddd1b 100644
--- a/models.py
+++ b/models.py
@@ -16,8 +16,7 @@
from functools import reduce
from django.db import models
-from django.db.models import Count, Manager, Q, Subquery, Value
-from django.db.models.expressions import OuterRef
+from django.db.models import Count, Manager, Q
from django.db.models.functions import ExtractWeek, ExtractYear
from django.utils import timezone
from django.utils.text import slugify
@@ -72,27 +71,18 @@ class Timetable(SlugModel):
class GroupManager(Manager):
def get_parents(self, group):
- groups_criteria = Q(subgroup="") | Q(subgroup__startswith=group.subgroup)
+ 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)])
- return self.get_queryset().filter(groups_criteria, mention=group.mention,
+ return self.get_queryset().filter(groups_criteria,
+ Q(semester=None) | Q(semester=group.semester),
+ mention=group.mention,
timetable=group.timetable)
- def get_relevant_groups(self, timetable, *args, **criteria):
- sub = self.get_queryset().filter(timetable=timetable,
- mention__startswith=OuterRef("mention"),
- subgroup__startswith=OuterRef("subgroup")) \
- .annotate(v=Value(0)).values("v") \
- .annotate(c=Count("v")).values("c") # fuck Count()
-
- return self.get_queryset().filter(*args, timetable=timetable, hidden=False, **criteria) \
- .annotate(nbsub=Subquery(sub, output_field=models.IntegerField())) \
- .filter(Q(nbsub=1) | Q(nbsub__isnull=True)).order_by("name")
-
class Group(models.Model):
objects = GroupManager()
@@ -104,26 +94,26 @@ class Group(models.Model):
verbose_name="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="")
slug = models.SlugField(max_length=64, default="")
hidden = models.BooleanField(verbose_name="caché", default=False)
- def corresponds_to(self, timetable_id, mention, subgroup):
+ def corresponds_to(self, mention, semester, subgroup):
subgroup_corresponds = True
if self.subgroup is not None and subgroup is not None:
- subgroup_corresponds = subgroup.startswith(self.subgroup) or \
- self.subgroup.startswith(subgroup)
+ subgroup_corresponds = self.subgroup.startswith(subgroup)
- return self.timetable.id == timetable_id and \
- (self.mention.startswith(mention) or \
- mention.startswith(self.mention)) and \
- subgroup_corresponds
+ return (self.mention.startswith(mention) or \
+ mention.startswith(self.mention)) and \
+ (self.semester == semester or semester is None) and \
+ subgroup_corresponds
@property
def group_info(self):
- return self.timetable.id, self.mention, self.subgroup
+ return self.mention, self.semester, self.subgroup
def __str__(self):
return self.name
@@ -133,7 +123,7 @@ class Group(models.Model):
self.name = self.celcat_name
self.slug = slugify(self.name)
- self.mention, self.subgroup = parse_group(self.name)
+ self.mention, self.semester, self.subgroup = parse_group(self.name)
if self.subgroup is None:
self.subgroup = ""
@@ -141,7 +131,7 @@ class Group(models.Model):
class Meta:
- index_together = ("mention", "subgroup",)
+ index_together = ("mention", "semester", "subgroup",)
unique_together = (("name", "timetable",),
("celcat_name", "timetable",),
("slug", "timetable",),)
@@ -166,15 +156,16 @@ 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")
+ .order_by("begin").prefetch_related("rooms")
def get_weeks(self, **criteria):
return self.get_queryset() \
.filter(**criteria) \
.order_by("groups__name", "year", "week") \
- .annotate(_=Count(("groups", "year", "week", "begin")),
- year=ExtractYear("begin"),
- week=ExtractWeek("begin"))
+ .annotate(year=ExtractYear("begin"),
+ week=ExtractWeek("begin")) \
+ .values("groups__mention", "groups__semester",
+ "groups__subgroup", "year", "week")
class Course(models.Model):
diff --git a/tests.py b/tests.py
index a509f38..1425a84 100644
--- a/tests.py
+++ b/tests.py
@@ -82,36 +82,36 @@ class GroupTestCase(TestCase):
general = Group.objects.get(celcat_name="L1 info (toutes sections et semestres confondus)", timetable=self.timetable)
- self.assertTrue(cma.corresponds_to(*tda2.group_info)) # CMA corresponds to TDA2
- self.assertTrue(cma.corresponds_to(*tpa21.group_info)) # CMA corresponds to TPA21
- self.assertTrue(tda2.corresponds_to(*tpa21.group_info)) # TDA2 corresponds to TPA21
+ self.assertFalse(cma.corresponds_to(*tda2.group_info))
+ self.assertFalse(cma.corresponds_to(*tpa21.group_info))
+ self.assertFalse(tda2.corresponds_to(*tpa21.group_info))
- self.assertTrue(cmb.corresponds_to(*tdb2.group_info)) # CMB corresponds to TDB2
- self.assertTrue(cmb.corresponds_to(*tpb21.group_info)) # CMB corresponds to TPB21
- self.assertTrue(tdb2.corresponds_to(*tpb21.group_info)) # TDB2 corresponds to TPB21
+ self.assertFalse(cmb.corresponds_to(*tdb2.group_info))
+ self.assertFalse(cmb.corresponds_to(*tpb21.group_info))
+ self.assertFalse(tdb2.corresponds_to(*tpb21.group_info))
- self.assertFalse(cmb.corresponds_to(*tda2.group_info)) # CMB does not corresponds to TDA2
- self.assertFalse(cmb.corresponds_to(*tpa21.group_info)) # CMB does not corresponds to TPA21
- self.assertFalse(tdb2.corresponds_to(*tpa21.group_info)) # TDB2 does not corresponds to TPA21
+ self.assertFalse(cmb.corresponds_to(*tda2.group_info))
+ self.assertFalse(cmb.corresponds_to(*tpa21.group_info))
+ self.assertFalse(tdb2.corresponds_to(*tpa21.group_info))
- self.assertTrue(tda2.corresponds_to(*cma.group_info)) # TDA2 corresponds to CMA
- self.assertTrue(tpa21.corresponds_to(*cma.group_info)) # TPA21 corresponds to CMA
- self.assertTrue(tpa21.corresponds_to(*tda2.group_info)) # TPA21 corresponds to TDA2
+ self.assertTrue(tda2.corresponds_to(*cma.group_info))
+ self.assertTrue(tpa21.corresponds_to(*cma.group_info))
+ self.assertTrue(tpa21.corresponds_to(*tda2.group_info))
- self.assertTrue(tdb2.corresponds_to(*cmb.group_info)) # TDB2 corresponds to CMB
- self.assertTrue(tpb21.corresponds_to(*cmb.group_info)) # TPB21 corresponds to CMB
- self.assertTrue(tpb21.corresponds_to(*tdb2.group_info)) # TPB21 corresponds to TDB2
+ self.assertTrue(tdb2.corresponds_to(*cmb.group_info))
+ self.assertTrue(tpb21.corresponds_to(*cmb.group_info))
+ self.assertTrue(tpb21.corresponds_to(*tdb2.group_info))
- self.assertFalse(tda2.corresponds_to(*cmb.group_info)) # TDA2 does not corresponds to CMB
- self.assertFalse(tpa21.corresponds_to(*cmb.group_info)) # TPA21 does not corresponds to CMB
- self.assertFalse(tpa21.corresponds_to(*tdb2.group_info)) # TPA21 does not corresponds to TDB2
+ self.assertFalse(tda2.corresponds_to(*cmb.group_info))
+ self.assertFalse(tpa21.corresponds_to(*cmb.group_info))
+ self.assertFalse(tpa21.corresponds_to(*tdb2.group_info))
- self.assertTrue(general.corresponds_to(*cma.group_info))
- self.assertTrue(general.corresponds_to(*cmb.group_info))
- self.assertTrue(general.corresponds_to(*tda2.group_info))
- self.assertTrue(general.corresponds_to(*tdb2.group_info))
- self.assertTrue(general.corresponds_to(*tpa21.group_info))
- self.assertTrue(general.corresponds_to(*tpb21.group_info))
+ self.assertFalse(general.corresponds_to(*cma.group_info))
+ self.assertFalse(general.corresponds_to(*cmb.group_info))
+ self.assertFalse(general.corresponds_to(*tda2.group_info))
+ self.assertFalse(general.corresponds_to(*tdb2.group_info))
+ self.assertFalse(general.corresponds_to(*tpa21.group_info))
+ self.assertFalse(general.corresponds_to(*tpb21.group_info))
self.assertTrue(cma.corresponds_to(*general.group_info))
self.assertTrue(cmb.corresponds_to(*general.group_info))
@@ -152,12 +152,12 @@ class GroupTestCase(TestCase):
general = Group.objects.get(celcat_name="L1 info (toutes sections et semestres confondus)", timetable=self.timetable)
- self.assertEqual(cma.group_info, (self.timetable.id, "L1 info s2 ", "A"))
- self.assertEqual(tda2.group_info, (self.timetable.id, "L1 info s2 ", "A2"))
- self.assertEqual(tpa21.group_info, (self.timetable.id, "L1 info s2 ", "A21"))
+ self.assertEqual(cma.group_info, ("L1 info", 2, "A"))
+ self.assertEqual(tda2.group_info, ("L1 info", 2, "A2"))
+ self.assertEqual(tpa21.group_info, ("L1 info", 2, "A21"))
- self.assertEqual(cmb.group_info, (self.timetable.id, "L1 info s2 ", "B"))
- self.assertEqual(tdb2.group_info, (self.timetable.id, "L1 info s2 ", "B2"))
- self.assertEqual(tpb21.group_info, (self.timetable.id, "L1 info s2 ", "B21"))
+ self.assertEqual(cmb.group_info, ("L1 info", 2, "B"))
+ self.assertEqual(tdb2.group_info, ("L1 info", 2, "B2"))
+ self.assertEqual(tpb21.group_info, ("L1 info", 2, "B21"))
- self.assertEqual(general.group_info, (self.timetable.id, "L1 info ", ""))
+ self.assertEqual(general.group_info, ("L1 info", None, ""))
diff --git a/utils.py b/utils.py
index e1b9867..9f0a6b5 100644
--- a/utils.py
+++ b/utils.py
@@ -30,7 +30,7 @@ def get_current_or_next_week():
def get_week(year, week):
start = timezone.make_aware(datetime.datetime.strptime(
- "{0}-W{1:02d}-1".format(year, week), "%Y-W%W-%w"))
+ "{0}-W{1}-1".format(year, week), "%Y-W%W-%w"))
end = start + datetime.timedelta(weeks=1)
return start, end
@@ -48,24 +48,32 @@ def group_courses(courses):
def parse_group(name):
# Explication de la regex
#
- # ^((.+?)\s*(s\d\s+)?)((CM|TD|TP|G)(\w\d{0,3}))?(\s+\(.+\))?$
+ # ^(.+?)\s*(s(\d)\s+)?((CM|TD|TP|G)(\w\d{0,3}))?(\s+\(.+\))?$
# ^ début de la ligne
- # ((.+?) correspond à au moins un caractère
- # \s* éventuellement un ou plusieurs espaces
- # (s\d\s+)?) éventuellement un s suivi d’un nombre et d’un ou plusieurs espaces
+ # (.+?) correspond à au moins un caractère
+ # \s* éventuellement un ou plusieurs espaces
+ # (s(\d)\s+)? éventuellement un s suivi d’un nombre et d’un ou plusieurs espaces
# ((CM|TD|TP|G) « CM » ou « TD » ou « TP » ou « G »
# (\w\d{0,3}) suivi d’un caractère puis entre 0 et 3 chiffres
# )? groupe optionnel
- # (\s* éventuellement un ou plusieurs espaces
- # \(.+\))? un ou plusieurs caractères entre parenthèses
+ # (\s+ un ou plusieurs espaces
+ # \(.+\))? un ou pliseurs caractères entre parenthèses
# $ fin de la ligne
- group_regex = re.compile(r"^((.+?)\s*(s\d\s+)?)((CM|TD|TP|G)(\w\d{0,3}))?(\s*\(.+\))?$")
+ group_regex = re.compile(r"^(.+?)\s*(s(\d)\s+)?((CM|TD|TP|G)(\w\d{0,3}))?(\s+\(.+\))?$")
search = group_regex.search(name)
if search is None:
- return name, None
+ return name, None, None
parts = search.groups()
- return parts[0], parts[5]
+
+ # On retourne la section (parts[0]), le semestre (parts[2]) et le groupe (parts[5])
+ if parts[2] is not None:
+ return parts[0], int(parts[2]), parts[5]
+ else:
+ # Si jamais le semestre n’est pas présent dans la chaine parsée,
+ # parts[2] sera à None et sa conversion vers un int va provoquer
+ # une erreur.
+ return parts[0], None, parts[5]
def tz_now():
"""Retourne la date et l’heure avec le bon fuseau horaire"""
diff --git a/views.py b/views.py
index c0c63cb..c647fac 100644
--- a/views.py
+++ b/views.py
@@ -31,21 +31,23 @@ def index(request):
def mention_list(request, year_slug):
year = get_object_or_404(Year, slug=year_slug)
- timetables = Timetable.objects.order_by("name").filter(year=year)
+ timetables = Timetable.objects.order_by("name").filter(year=year).select_related("year")
return render(request, "index.html", {"year": year, "elements": timetables})
-def group_list_common(request, timetable, groups):
+def group_list(request, year_slug, timetable_slug):
+ timetable = get_object_or_404(Timetable, year__slug=year_slug, slug=timetable_slug)
+ groups = Group.objects.filter(timetable=timetable, hidden=False).order_by("name")
+
start, _ = get_week(*get_current_week())
end = start + datetime.timedelta(weeks=4)
- groups_weeks = Course.objects.get_weeks(begin__gte=start, begin__lt=end, timetable=timetable) \
- .values("groups__mention", "groups__subgroup",
- "year", "week")
+ groups_weeks = Course.objects.get_weeks(begin__gte=start, begin__lt=end, groups__in=groups)
for group in groups:
for group_week in groups_weeks:
- if group.corresponds_to(timetable.id, group_week["groups__mention"],
+ if group.corresponds_to(group_week["groups__mention"],
+ group_week["groups__semester"],
group_week["groups__subgroup"]):
if not hasattr(group, "weeks"):
group.weeks = []
@@ -59,11 +61,6 @@ def group_list_common(request, timetable, groups):
return render(request, "group_list.html", {"timetable": timetable, "groups": groups})
-def group_list(request, year_slug, timetable_slug):
- timetable = get_object_or_404(Timetable, year__slug=year_slug, slug=timetable_slug)
- groups = Group.objects.get_relevant_groups(timetable)
- return group_list_common(request, timetable, groups)
-
def timetable(request, year_slug, timetable_slug, group_slug, year=None, week=None):
current_year, current_week = get_current_or_next_week()
is_old_timetable, provided_week = False, True
@@ -74,19 +71,13 @@ def timetable(request, year_slug, timetable_slug, group_slug, year=None, week=No
elif (int(year), int(week)) < (current_year, current_week):
is_old_timetable = True
- start, end = get_week(int(year), int(week))
+ start, end = get_week(year, week)
timetable = get_object_or_404(Timetable, year__slug=year_slug, slug=timetable_slug)
group = get_object_or_404(Group, slug=group_slug, timetable=timetable)
- if Group.objects.filter(timetable=timetable, mention=group.mention,
- subgroup__startswith=group.subgroup).count() > 1:
- subgroups = Group.objects.get_relevant_groups(timetable, mention=group.mention,
- subgroup__startswith=group.subgroup)
- return group_list_common(request, timetable, subgroups)
-
courses = Course.objects.get_courses_for_group(group, begin__gte=start, begin__lt=end)
- if courses.count() == 0 and provided_week:
+ if not courses.exists() and provided_week:
raise Http404
last_update = courses.aggregate(Max("last_update"))["last_update__max"]