diff options
-rw-r--r-- | __init__.py | 2 | ||||
-rw-r--r-- | management/commands/_private.py | 2 | ||||
-rw-r--r-- | models.py | 26 | ||||
-rw-r--r-- | tests.py | 38 | ||||
-rw-r--r-- | utils.py | 26 |
5 files changed, 63 insertions, 31 deletions
diff --git a/__init__.py b/__init__.py index 7371b39..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 <http://www.gnu.org/licenses/>. -VERSION = "0.11.1" +VERSION = "0.11.3" __version__ = VERSION default_app_config = "edt.apps.EdtConfig" 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 @@ -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 @@ -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 @@ -71,18 +72,22 @@ 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) 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")) \ + .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())) \ @@ -112,7 +117,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 @@ -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,10 +150,14 @@ 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")) + 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(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, (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(general.group_info, (self.timetable.id, "L1 info ", "")) @@ -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* é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*\(.+\))?$") 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""" |