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""" | 
