From 68248f075dc750d7de5c0d060721ecce8f988217 Mon Sep 17 00:00:00 2001 From: Alban Gruin Date: Sat, 25 Nov 2017 13:17:35 +0100 Subject: Suppression de l’association automatique des groupes --- views.py | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/views.py b/views.py index c0c63cb..97d3048 100644 --- a/views.py +++ b/views.py @@ -61,7 +61,7 @@ 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.get_relevant_groups(timetable) + groups = Group.objects.filter(timetable=timetable).order_by("name") return group_list_common(request, timetable, groups) def timetable(request, year_slug, timetable_slug, group_slug, year=None, week=None): @@ -79,12 +79,6 @@ def timetable(request, year_slug, timetable_slug, group_slug, year=None, week=No 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: raise Http404 -- cgit v1.2.1 From b99dbd566fba7f3c18d3832edeb8e019abcc8c87 Mon Sep 17 00:00:00 2001 From: Alban Gruin Date: Sat, 25 Nov 2017 13:25:51 +0100 Subject: Ne sélectionne plus les sous-groupes --- models.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/models.py b/models.py index 40406e3..40849c6 100644 --- a/models.py +++ b/models.py @@ -72,7 +72,7 @@ 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, -- cgit v1.2.1 From 7f146b306760e1a70e8e1a79d58fc967d05fe194 Mon Sep 17 00:00:00 2001 From: Alban Gruin Date: Sat, 25 Nov 2017 13:30:42 +0100 Subject: Suppression de get_relevant_groups --- models.py | 14 +------------- 1 file changed, 1 insertion(+), 13 deletions(-) diff --git a/models.py b/models.py index 40849c6..9838377 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 @@ -82,17 +81,6 @@ class GroupManager(Manager): return self.get_queryset().filter(groups_criteria, 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() -- cgit v1.2.1 From d9e0cba8c24978150e66b9ccd7ea67aa50a5f22d Mon Sep 17 00:00:00 2001 From: Alban Gruin Date: Sat, 25 Nov 2017 14:01:54 +0100 Subject: Un groupe parent ne correspond plus à un groupe enfant Modification des tests --- models.py | 3 +-- tests.py | 60 ++++++++++++++++++++++++++++++------------------------------ 2 files changed, 31 insertions(+), 32 deletions(-) diff --git a/models.py b/models.py index 9838377..bde27a7 100644 --- a/models.py +++ b/models.py @@ -101,8 +101,7 @@ class Group(models.Model): def corresponds_to(self, timetable_id, mention, 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 \ diff --git a/tests.py b/tests.py index a509f38..e7af6a8 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.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(*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.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(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.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.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(cma.corresponds_to(*tda2.group_info)) + self.assertFalse(cma.corresponds_to(*tpa21.group_info)) + self.assertFalse(tda2.corresponds_to(*tpa21.group_info)) + + 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)) + self.assertFalse(cmb.corresponds_to(*tpa21.group_info)) + self.assertFalse(tdb2.corresponds_to(*tpa21.group_info)) + + 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)) + self.assertTrue(tpb21.corresponds_to(*cmb.group_info)) + self.assertTrue(tpb21.corresponds_to(*tdb2.group_info)) + + 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.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)) -- cgit v1.2.1 From 621f5d7f0d28f7252e1ff0c0f1286824fa55c33e Mon Sep 17 00:00:00 2001 From: Alban Gruin Date: Sat, 25 Nov 2017 14:22:03 +0100 Subject: On ne compare plus les id des emplois du temps dans corresponds_to() car dans le seul appel « utile », les arguments passés font forcément partie du même edt. Changement effectué pour des questions de performance. Correction des tests. --- models.py | 11 +++++------ tests.py | 14 +++++++------- views.py | 2 +- 3 files changed, 13 insertions(+), 14 deletions(-) diff --git a/models.py b/models.py index bde27a7..59301e0 100644 --- a/models.py +++ b/models.py @@ -98,19 +98,18 @@ class Group(models.Model): hidden = models.BooleanField(verbose_name="caché", default=False) - def corresponds_to(self, timetable_id, mention, subgroup): + def corresponds_to(self, mention, subgroup): subgroup_corresponds = True if self.subgroup is not None and subgroup is not None: 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 \ + subgroup_corresponds @property def group_info(self): - return self.timetable.id, self.mention, self.subgroup + return self.mention, self.subgroup def __str__(self): return self.name diff --git a/tests.py b/tests.py index e7af6a8..77fa120 100644 --- a/tests.py +++ b/tests.py @@ -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 s2 ", "A")) + self.assertEqual(tda2.group_info, ("L1 info s2 ", "A2")) + self.assertEqual(tpa21.group_info, ("L1 info s2 ", "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 s2 ", "B")) + self.assertEqual(tdb2.group_info, ("L1 info s2 ", "B2")) + self.assertEqual(tpb21.group_info, ("L1 info s2 ", "B21")) - self.assertEqual(general.group_info, (self.timetable.id, "L1 info ", "")) + self.assertEqual(general.group_info, ("L1 info ", "")) diff --git a/views.py b/views.py index 97d3048..7bc5641 100644 --- a/views.py +++ b/views.py @@ -45,7 +45,7 @@ def group_list_common(request, timetable, 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__subgroup"]): if not hasattr(group, "weeks"): group.weeks = [] -- cgit v1.2.1 From cbab00f4b745f4d80dd06a52699006d1d081be20 Mon Sep 17 00:00:00 2001 From: Alban Gruin Date: Sat, 25 Nov 2017 15:01:48 +0100 Subject: Fusion de group_list et de group_list_common. Légère modification de la requête pour récupérer les semaines de cours (au lieu de filtrer par l’emploi du temps, on filtre par les groupes), résultant en une nette accélération du traitement Avant modification : SQLite : ~420ms PostgreSQL : ~60ms puis ~30ms Après modification : SQLite : ~18ms PostgreSQL : ~23ms puis ~13ms La progression sur PostgreSQL est celle qui importe le plus, puisqu’il s’agit du SGBDR utilisé en production, mais la requête s’effectue tout de même plus rapidement (un peu plus qu’un rapport 2), ce qui reste intéressant. --- views.py | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/views.py b/views.py index 7bc5641..d4df8db 100644 --- a/views.py +++ b/views.py @@ -35,11 +35,14 @@ def mention_list(request, year_slug): 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) \ + groups_weeks = Course.objects.get_weeks(begin__gte=start, begin__lt=end, groups__in=groups) \ .values("groups__mention", "groups__subgroup", "year", "week") @@ -57,12 +60,8 @@ def group_list_common(request, timetable, groups): if hasattr(group, "weeks"): group.weeks.sort() - 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.filter(timetable=timetable).order_by("name") - return group_list_common(request, timetable, groups) + return render(request, "group_list.html", {"timetable": timetable, "groups": groups}) def timetable(request, year_slug, timetable_slug, group_slug, year=None, week=None): current_year, current_week = get_current_or_next_week() -- cgit v1.2.1 From f333d363a78821ebab78fb9bb900d7aefbf6079c Mon Sep 17 00:00:00 2001 From: Alban Gruin Date: Sat, 25 Nov 2017 15:14:52 +0100 Subject: Dans get_weeks(), l’annotation Count() ne sert à rien. --- models.py | 7 ++++--- views.py | 4 +--- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/models.py b/models.py index 59301e0..cd19259 100644 --- a/models.py +++ b/models.py @@ -158,9 +158,10 @@ class CourseManager(Manager): 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__subgroup", "year", "week") class Course(models.Model): diff --git a/views.py b/views.py index d4df8db..f1ec8d5 100644 --- a/views.py +++ b/views.py @@ -42,9 +42,7 @@ def group_list(request, year_slug, timetable_slug): start, _ = get_week(*get_current_week()) end = start + datetime.timedelta(weeks=4) - groups_weeks = Course.objects.get_weeks(begin__gte=start, begin__lt=end, groups__in=groups) \ - .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: -- cgit v1.2.1 From d1191533a29b5f80543faa0758c7303d0a4b450c Mon Sep 17 00:00:00 2001 From: Alban Gruin Date: Sat, 25 Nov 2017 16:16:26 +0100 Subject: Le sachiez-tu ? Parfois je met des espaces inutiles --- views.py | 1 - 1 file changed, 1 deletion(-) diff --git a/views.py b/views.py index f1ec8d5..9e88b3b 100644 --- a/views.py +++ b/views.py @@ -58,7 +58,6 @@ def group_list(request, year_slug, timetable_slug): if hasattr(group, "weeks"): group.weeks.sort() - return render(request, "group_list.html", {"timetable": timetable, "groups": groups}) def timetable(request, year_slug, timetable_slug, group_slug, year=None, week=None): -- cgit v1.2.1 From 4aebed8ca3f879aa4e2996620657a14ac9538477 Mon Sep 17 00:00:00 2001 From: Alban Gruin Date: Sat, 25 Nov 2017 20:43:21 +0100 Subject: get_week() convertit automatiquement les chaines de caractères en int --- utils.py | 2 +- views.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/utils.py b/utils.py index e1b9867..63bcaed 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:02d}-1".format(year, int(week)), "%Y-W%W-%w")) end = start + datetime.timedelta(weeks=1) return start, end diff --git a/views.py b/views.py index 9e88b3b..1269415 100644 --- a/views.py +++ b/views.py @@ -70,7 +70,7 @@ 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) -- cgit v1.2.1 From 630bebd114cdbae7af736e117e2b10b7e9195657 Mon Sep 17 00:00:00 2001 From: Alban Gruin Date: Sat, 25 Nov 2017 20:49:23 +0100 Subject: Suppression du formatage inutile dans get_week() --- utils.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/utils.py b/utils.py index 63bcaed..5a9218e 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, int(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 -- cgit v1.2.1 From 34ba9c926cfd0725ebd2905e9c71823fd272dad2 Mon Sep 17 00:00:00 2001 From: Alban Gruin Date: Sun, 26 Nov 2017 13:27:40 +0100 Subject: Optimisation de deux requêtes : – sélection des cours d’un groupe : préchargement des salles – sélection des emplois du temps d’une année : préchargement de l’année --- models.py | 2 +- views.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/models.py b/models.py index cd19259..b260275 100644 --- a/models.py +++ b/models.py @@ -152,7 +152,7 @@ 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() \ diff --git a/views.py b/views.py index 1269415..5eb224e 100644 --- a/views.py +++ b/views.py @@ -31,7 +31,7 @@ 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}) -- cgit v1.2.1 From 0bdde723ebb571bb6f1a894fee710fae7abc270e Mon Sep 17 00:00:00 2001 From: Alban Gruin Date: Sun, 26 Nov 2017 13:50:27 +0100 Subject: Annulation des changements récemments effectués sur la regex --- utils.py | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/utils.py b/utils.py index 5a9218e..46244e1 100644 --- a/utils.py +++ b/utils.py @@ -48,24 +48,24 @@ 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+\(.+\))?$ - # ^ 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 - # ((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 - # $ fin de la ligne - group_regex = re.compile(r"^((.+?)\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 + # ((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+ 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+\(.+\))?$") search = group_regex.search(name) if search is None: return name, None parts = search.groups() - return parts[0], parts[5] + return parts[0], parts[4] def tz_now(): """Retourne la date et l’heure avec le bon fuseau horaire""" -- cgit v1.2.1 From f8c7775aac4a7eb2a4430d1aabb20b7f29939af7 Mon Sep 17 00:00:00 2001 From: Alban Gruin Date: Sun, 26 Nov 2017 13:53:10 +0100 Subject: Ajout d’un nouveau champ de groupe : le semestre --- admin.py | 2 +- models.py | 10 +++++++--- utils.py | 26 +++++++++++++------------- 3 files changed, 21 insertions(+), 17 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 b260275..abb00bf 100644 --- a/models.py +++ b/models.py @@ -78,7 +78,9 @@ class GroupManager(Manager): [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) @@ -92,6 +94,7 @@ 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="") @@ -103,6 +106,7 @@ class Group(models.Model): if self.subgroup is not None and subgroup is not None: subgroup_corresponds = self.subgroup.startswith(subgroup) + # TODO adapter pour semester return (self.mention.startswith(mention) or \ mention.startswith(self.mention)) and \ subgroup_corresponds @@ -119,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 = "" @@ -127,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",),) diff --git a/utils.py b/utils.py index 46244e1..103969e 100644 --- a/utils.py +++ b/utils.py @@ -48,24 +48,24 @@ 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+\(.+\))?$ - # ^ 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 - # ((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+ 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+\(.+\))?$") + # ^(.+?)\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 + # ((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+ 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+\(.+\))?$") search = group_regex.search(name) if search is None: return name, None parts = search.groups() - return parts[0], parts[4] + return parts[0], int(parts[2]), parts[5] def tz_now(): """Retourne la date et l’heure avec le bon fuseau horaire""" -- cgit v1.2.1 From 2aacb6e59e40c9d47d02719dc7b5541a36b3c9ed Mon Sep 17 00:00:00 2001 From: Alban Gruin Date: Sun, 26 Nov 2017 14:08:11 +0100 Subject: Correction d’un cas pouvant provoquer une erreur de parsage de groupe (si jamais il n’y a pas de semestre), renvoi du numéro de semestre dans group_info, adaptation de corresponds_to. --- models.py | 5 +++-- tests.py | 14 +++++++------- utils.py | 12 ++++++++++-- 3 files changed, 20 insertions(+), 11 deletions(-) diff --git a/models.py b/models.py index abb00bf..fb0e757 100644 --- a/models.py +++ b/models.py @@ -101,7 +101,7 @@ class Group(models.Model): hidden = models.BooleanField(verbose_name="caché", default=False) - def corresponds_to(self, 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 = self.subgroup.startswith(subgroup) @@ -109,11 +109,12 @@ class Group(models.Model): # TODO adapter pour semester 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.mention, self.subgroup + return self.mention, self.semester, self.subgroup def __str__(self): return self.name diff --git a/tests.py b/tests.py index 77fa120..1425a84 100644 --- a/tests.py +++ b/tests.py @@ -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, ("L1 info s2 ", "A")) - self.assertEqual(tda2.group_info, ("L1 info s2 ", "A2")) - self.assertEqual(tpa21.group_info, ("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, ("L1 info s2 ", "B")) - self.assertEqual(tdb2.group_info, ("L1 info s2 ", "B2")) - self.assertEqual(tpb21.group_info, ("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, ("L1 info ", "")) + self.assertEqual(general.group_info, ("L1 info", None, "")) diff --git a/utils.py b/utils.py index 103969e..9f0a6b5 100644 --- a/utils.py +++ b/utils.py @@ -62,10 +62,18 @@ def parse_group(name): 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], int(parts[2]), 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""" -- cgit v1.2.1 From 574bc0efb1061cd1c1fd747ba13f6f7ac14a3e51 Mon Sep 17 00:00:00 2001 From: Alban Gruin Date: Sun, 26 Nov 2017 14:09:49 +0100 Subject: Suppression des commentaires inutiles --- models.py | 1 - 1 file changed, 1 deletion(-) diff --git a/models.py b/models.py index fb0e757..0904b63 100644 --- a/models.py +++ b/models.py @@ -106,7 +106,6 @@ class Group(models.Model): if self.subgroup is not None and subgroup is not None: subgroup_corresponds = self.subgroup.startswith(subgroup) - # TODO adapter pour semester return (self.mention.startswith(mention) or \ mention.startswith(self.mention)) and \ (self.semester == semester or semester is None) and \ -- cgit v1.2.1 From ef6f5e8517436b804265880b71a2ee88f9f5c179 Mon Sep 17 00:00:00 2001 From: Alban Gruin Date: Sun, 26 Nov 2017 14:22:13 +0100 Subject: Récupération du semestre pour générer la liste des groupes. --- models.py | 2 +- views.py | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/models.py b/models.py index 0904b63..25ddd1b 100644 --- a/models.py +++ b/models.py @@ -164,7 +164,7 @@ class CourseManager(Manager): .order_by("groups__name", "year", "week") \ .annotate(year=ExtractYear("begin"), week=ExtractWeek("begin")) \ - .values("groups__mention", + .values("groups__mention", "groups__semester", "groups__subgroup", "year", "week") diff --git a/views.py b/views.py index 5eb224e..4431891 100644 --- a/views.py +++ b/views.py @@ -47,6 +47,7 @@ def group_list(request, year_slug, timetable_slug): for group in groups: for group_week in groups_weeks: if group.corresponds_to(group_week["groups__mention"], + group_week["groups__semester"], group_week["groups__subgroup"]): if not hasattr(group, "weeks"): group.weeks = [] -- cgit v1.2.1 From 8cd40466e5b1ef9f00cf88c9375289d235abc009 Mon Sep 17 00:00:00 2001 From: Alban Gruin Date: Sun, 26 Nov 2017 14:27:16 +0100 Subject: Optimisation de la requête utilisée pour savoir si il y a des cours sur la semaine sélectionnée (utilisation de exists() au lieu de count()) --- views.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/views.py b/views.py index 4431891..c647fac 100644 --- a/views.py +++ b/views.py @@ -77,7 +77,7 @@ def timetable(request, year_slug, timetable_slug, group_slug, year=None, week=No group = get_object_or_404(Group, slug=group_slug, timetable=timetable) 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"] -- cgit v1.2.1