From 0c2937bab77280beaabe7d53a77c848848845950 Mon Sep 17 00:00:00 2001 From: Alban Gruin Date: Thu, 16 Nov 2017 11:20:59 +0100 Subject: Par défaut, la date de dernière mise à jour des emplois du temps est la date actuelle --- models.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/models.py b/models.py index 0804a38..b9f1ba7 100644 --- a/models.py +++ b/models.py @@ -57,7 +57,8 @@ class Timetable(SlugModel): 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) + last_update_date = models.DateTimeField(verbose_name="dernière mise à jour Celcat", + null=True, default=timezone.now) def __str__(self): return self.year.name + " " + self.name -- cgit v1.2.1 From 4fdb7f6263d6463ac2099c383b1221789d28e42e Mon Sep 17 00:00:00 2001 From: Alban Gruin Date: Thu, 16 Nov 2017 13:34:30 +0100 Subject: Correction des erreurs lorsqu’un groupe n’a pas de sous-groupe --- models.py | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/models.py b/models.py index b9f1ba7..a90e9da 100644 --- a/models.py +++ b/models.py @@ -72,10 +72,12 @@ class Timetable(SlugModel): class GroupManager(Manager): def get_parents(self, group): - groups_criteria = Q(subgroup="") | Q(subgroup__startswith=group.subgroup) | \ - reduce(lambda x, y: x | y, - [Q(subgroup=group.subgroup[:i]) - for i in range(1, len(group.subgroup) + 1)]) + groups_criteria = Q(subgroup="") | Q(subgroup__startswith=group.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, timetable=group.timetable) -- cgit v1.2.1 From d1f4287a364e8bfee86a0a55c9514edad0abbd4e Mon Sep 17 00:00:00 2001 From: Alban Gruin Date: Thu, 16 Nov 2017 13:51:00 +0100 Subject: Correction d’un crash lorqu’on force une mise à jour sur tout l’emploi du temps Lorsqu’on effectue cette opération, la date à laquelle est effectuée la mise à jour est à None. get_event() effectue donc une comparaison entre la date du cours et un objet nul, ce qui est impossible. Cela provoque un crash. Le correctif consiste à dire que si la date de début de la mise à jour est nulle, alors le cours n’est pas ignoré, avant d’effectuer la comparaison décrite ci-dessus. --- management/commands/_private.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/management/commands/_private.py b/management/commands/_private.py index b663454..4dd9262 100644 --- a/management/commands/_private.py +++ b/management/commands/_private.py @@ -54,7 +54,7 @@ def get_event(timetable, event, event_week, today): end = add_time(date, event.endtime.text) # On ne traite pas le cours si il commence après le moment du traitement - if begin < today: + if today is not None and begin < today: return # Création de l’objet cours -- cgit v1.2.1 From cc64acb64af30771c523a1e0794f0976df14769b Mon Sep 17 00:00:00 2001 From: Alban Gruin Date: Thu, 16 Nov 2017 14:14:06 +0100 Subject: Correction d’une erreur de parenthésage dans get_parents() Le + 1 doit venir _après_ len(), pas pendant… --- models.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/models.py b/models.py index a90e9da..4b86961 100644 --- a/models.py +++ b/models.py @@ -77,7 +77,7 @@ class GroupManager(Manager): 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))]) + for i in range(1, len(group.subgroup) + 1)]) return self.get_queryset().filter(groups_criteria, mention=group.mention, timetable=group.timetable) -- cgit v1.2.1 From 9262be98897f29e52f489e3353cb752d9b9d8129 Mon Sep 17 00:00:00 2001 From: Alban Gruin Date: Thu, 16 Nov 2017 14:35:19 +0100 Subject: Revert "Par défaut, la date de dernière mise à jour des emplois du temps est" This reverts commit 0c2937bab77280beaabe7d53a77c848848845950. --- models.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/models.py b/models.py index 4b86961..db81d5b 100644 --- a/models.py +++ b/models.py @@ -57,8 +57,7 @@ class Timetable(SlugModel): 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, default=timezone.now) + last_update_date = models.DateTimeField(verbose_name="dernière mise à jour Celcat", null=True) def __str__(self): return self.year.name + " " + self.name -- cgit v1.2.1 From 98d62baa698a69b031f28a27efc76a47b7456c8a Mon Sep 17 00:00:00 2001 From: Alban Gruin Date: Thu, 16 Nov 2017 14:38:24 +0100 Subject: Mettre une valeur de dernière mise à jour dans l’interface d’administration n’est plus obligatoire. --- models.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/models.py b/models.py index db81d5b..6d06793 100644 --- a/models.py +++ b/models.py @@ -57,7 +57,8 @@ class Timetable(SlugModel): 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) + last_update_date = models.DateTimeField(verbose_name="dernière mise à jour Celcat", + null=True, blank=True) def __str__(self): return self.year.name + " " + self.name -- cgit v1.2.1 From b2c14a2db9b79dca007db5e32aefbf76ebd05fd1 Mon Sep 17 00:00:00 2001 From: Alban Gruin Date: Thu, 16 Nov 2017 18:59:48 +0100 Subject: Version 0.11.2 --- __init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/__init__.py b/__init__.py index 7371b39..f6ef57e 100644 --- a/__init__.py +++ b/__init__.py @@ -13,7 +13,7 @@ # You should have received a copy of the GNU Affero General Public License # along with celcatsanitizer. If not, see . -VERSION = "0.11.1" +VERSION = "0.11.2" __version__ = VERSION default_app_config = "edt.apps.EdtConfig" -- cgit v1.2.1 From ffffa1cef4e0c2f1e617dc74a57c5045325e57aa Mon Sep 17 00:00:00 2001 From: Alban Gruin Date: Fri, 24 Nov 2017 19:28:35 +0100 Subject: Correction d’un bogue qui empêchait l’affichage des groupes si deux groupes ont le même nom mais pas le même semestre. --- models.py | 7 ++++--- utils.py | 26 +++++++++++++------------- 2 files changed, 17 insertions(+), 16 deletions(-) diff --git a/models.py b/models.py index 6d06793..4c3f3c3 100644 --- a/models.py +++ b/models.py @@ -83,9 +83,10 @@ class GroupManager(Manager): timetable=group.timetable) def get_relevant_groups(self, timetable, *args, **criteria): - sub = self.get_queryset().filter(timetable=timetable, mention=OuterRef("mention"), - subgroup__startswith=OuterRef("subgroup")) \ - .order_by().values("mention").annotate(c=Count("*")).values("c") + sub = self.get_queryset().filter(timetable=timetable, + mention__startswith=OuterRef("mention"), + subgroup__startswith=OuterRef("subgroup")) \ + .order_by().values("mention").annotate(c=Count("*")).values("c") return self.get_queryset().filter(*args, timetable=timetable, hidden=False, **criteria) \ .annotate(nbsub=Subquery(sub, output_field=models.IntegerField())) \ diff --git a/utils.py b/utils.py index 5d55eca..785a21d 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], parts[5] def tz_now(): """Retourne la date et l’heure avec le bon fuseau horaire""" -- cgit v1.2.1 From e9552f7e5545623c8cb6ba071f80aa185affa736 Mon Sep 17 00:00:00 2001 From: Alban Gruin Date: Fri, 24 Nov 2017 19:31:39 +0100 Subject: Adaptation des tests --- tests.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/tests.py b/tests.py index 051c733..798574d 100644 --- a/tests.py +++ b/tests.py @@ -128,10 +128,10 @@ class GroupTestCase(TestCase): tdb2 = Group.objects.get(celcat_name="L1 info s2 TDB2", timetable=self.timetable) tpb21 = Group.objects.get(celcat_name="L1 info s2 TPB21", timetable=self.timetable) - self.assertEqual(cma.group_info, (self.timetable.id, "L1 info", "A")) - self.assertEqual(tda2.group_info, (self.timetable.id, "L1 info", "A2")) - self.assertEqual(tpa21.group_info, (self.timetable.id, "L1 info", "A21")) + 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(cmb.group_info, (self.timetable.id, "L1 info", "B")) - self.assertEqual(tdb2.group_info, (self.timetable.id, "L1 info", "B2")) - self.assertEqual(tpb21.group_info, (self.timetable.id, "L1 info", "B21")) + 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")) -- cgit v1.2.1 From 74f7317251ec068e89ee02d386b902d53f366840 Mon Sep 17 00:00:00 2001 From: Alban Gruin Date: Fri, 24 Nov 2017 20:01:28 +0100 Subject: Re-changement de la regex pour mieux gérer les cas où il n’y a ni semestre ni sous-groupe mais qu’il y a une précision --- utils.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/utils.py b/utils.py index 785a21d..e1b9867 100644 --- a/utils.py +++ b/utils.py @@ -56,10 +56,10 @@ def parse_group(name): # ((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 + # (\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+\(.+\))?$") + 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 -- cgit v1.2.1 From b28630da06a6c1286d5cd34d6437d8dd19d6f298 Mon Sep 17 00:00:00 2001 From: Alban Gruin Date: Fri, 24 Nov 2017 20:02:21 +0100 Subject: Ajout de nouveaux tests pour ces nouveaux cas --- models.py | 3 ++- tests.py | 26 ++++++++++++++++++++++++++ 2 files changed, 28 insertions(+), 1 deletion(-) diff --git a/models.py b/models.py index 4c3f3c3..14270cf 100644 --- a/models.py +++ b/models.py @@ -116,7 +116,8 @@ class Group(models.Model): self.subgroup.startswith(subgroup) return self.timetable.id == timetable_id and \ - self.mention.startswith(mention) and \ + (self.mention.startswith(mention) or \ + mention.startswith(self.mention)) and \ subgroup_corresponds @property diff --git a/tests.py b/tests.py index 798574d..a509f38 100644 --- a/tests.py +++ b/tests.py @@ -69,6 +69,8 @@ class GroupTestCase(TestCase): Group.objects.create(celcat_name="L1 info s2 TDB2", timetable=self.timetable) Group.objects.create(celcat_name="L1 info s2 TPB21", timetable=self.timetable) + Group.objects.create(celcat_name="L1 info (toutes sections et semestres confondus)", timetable=self.timetable) + def test_corresponds(self): cma = Group.objects.get(celcat_name="L1 info s2 CMA", timetable=self.timetable) tda2 = Group.objects.get(celcat_name="L1 info s2 TDA2", timetable=self.timetable) @@ -78,6 +80,8 @@ class GroupTestCase(TestCase): tdb2 = Group.objects.get(celcat_name="L1 info s2 TDB2", timetable=self.timetable) tpb21 = Group.objects.get(celcat_name="L1 info s2 TPB21", timetable=self.timetable) + 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 @@ -102,6 +106,20 @@ class GroupTestCase(TestCase): 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.assertTrue(cma.corresponds_to(*general.group_info)) + self.assertTrue(cmb.corresponds_to(*general.group_info)) + self.assertTrue(tda2.corresponds_to(*general.group_info)) + self.assertTrue(tdb2.corresponds_to(*general.group_info)) + self.assertTrue(tpa21.corresponds_to(*general.group_info)) + self.assertTrue(tpb21.corresponds_to(*general.group_info)) + def test_get(self): cma = Group.objects.get(name="L1 info s2 CMA", timetable=self.timetable) tda2 = Group.objects.get(name="L1 info s2 TDA2", timetable=self.timetable) @@ -111,6 +129,8 @@ class GroupTestCase(TestCase): tdb2 = Group.objects.get(name="L1 info s2 TDB2", timetable=self.timetable) tpb21 = Group.objects.get(name="L1 info s2 TPB21", timetable=self.timetable) + general = Group.objects.get(celcat_name="L1 info (toutes sections et semestres confondus)", timetable=self.timetable) + self.assertEqual(cma.celcat_name, "L1 info s2 CMA") self.assertEqual(tda2.celcat_name, "L1 info s2 TDA2") self.assertEqual(tpa21.celcat_name, "L1 info s2 TPA21") @@ -119,6 +139,8 @@ class GroupTestCase(TestCase): self.assertEqual(tdb2.celcat_name, "L1 info s2 TDB2") self.assertEqual(tpb21.celcat_name, "L1 info s2 TPB21") + self.assertEqual(general.celcat_name, "L1 info (toutes sections et semestres confondus)") + def test_parse(self): cma = Group.objects.get(celcat_name="L1 info s2 CMA", timetable=self.timetable) tda2 = Group.objects.get(celcat_name="L1 info s2 TDA2", timetable=self.timetable) @@ -128,6 +150,8 @@ class GroupTestCase(TestCase): tdb2 = Group.objects.get(celcat_name="L1 info s2 TDB2", timetable=self.timetable) tpb21 = Group.objects.get(celcat_name="L1 info s2 TPB21", timetable=self.timetable) + 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")) @@ -135,3 +159,5 @@ class GroupTestCase(TestCase): 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(general.group_info, (self.timetable.id, "L1 info ", "")) -- cgit v1.2.1 From d9b78963e8526b803f1165c79ae2f97c6a36016b Mon Sep 17 00:00:00 2001 From: Alban Gruin Date: Fri, 24 Nov 2017 21:30:53 +0100 Subject: Version 0.11.3 --- __init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/__init__.py b/__init__.py index f6ef57e..1cdb4e3 100644 --- a/__init__.py +++ b/__init__.py @@ -13,7 +13,7 @@ # You should have received a copy of the GNU Affero General Public License # along with celcatsanitizer. If not, see . -VERSION = "0.11.2" +VERSION = "0.11.3" __version__ = VERSION default_app_config = "edt.apps.EdtConfig" -- cgit v1.2.1 From adad24ace9ab47f12d43dca494b358544beefa00 Mon Sep 17 00:00:00 2001 From: Alban Gruin Date: Fri, 24 Nov 2017 23:43:53 +0100 Subject: Correction d’un crash lorsqu’un groupe a des enfants ayant une mention différente. L’annotation Count() renvoie plusieurs valeurs différentes, alors que Subquery n’en accepte qu’une seule. Corrigé en rajoutant une valeur statique (0) par annotation et en regroupant en se basant sur cette valeur. --- models.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/models.py b/models.py index 14270cf..40406e3 100644 --- a/models.py +++ b/models.py @@ -16,7 +16,7 @@ from functools import reduce from django.db import models -from django.db.models import Count, Manager, Q, Subquery +from django.db.models import Count, Manager, Q, Subquery, Value from django.db.models.expressions import OuterRef from django.db.models.functions import ExtractWeek, ExtractYear from django.utils import timezone @@ -86,7 +86,8 @@ class GroupManager(Manager): sub = self.get_queryset().filter(timetable=timetable, mention__startswith=OuterRef("mention"), subgroup__startswith=OuterRef("subgroup")) \ - .order_by().values("mention").annotate(c=Count("*")).values("c") + .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())) \ -- cgit v1.2.1