diff options
-rw-r--r-- | admin.py | 5 | ||||
-rw-r--r-- | forms.py | 9 | ||||
-rw-r--r-- | management/commands/_private.py | 14 | ||||
-rw-r--r-- | management/commands/cleancourses.py | 3 | ||||
-rw-r--r-- | management/commands/listtimetables.py | 5 | ||||
-rw-r--r-- | management/commands/timetables.py | 65 | ||||
-rw-r--r-- | models.py | 37 | ||||
-rw-r--r-- | templatetags/dt_week.py | 2 | ||||
-rw-r--r-- | templatetags/rooms.py | 4 | ||||
-rw-r--r-- | tests.py | 99 | ||||
-rw-r--r-- | utils.py | 9 |
11 files changed, 164 insertions, 88 deletions
@@ -16,10 +16,12 @@ from django.contrib import admin from .models import Course, Group, Room, Source, Timetable, Year + def make_hidden(modeladmin, request, queryset): queryset.update(hidden=True) make_hidden.short_description = "Cacher les groupes sélectionnés" + def make_visible(modeladmin, request, queryset): queryset.update(hidden=False) make_visible.short_description = "Afficher les groupes sélectionnés" @@ -66,7 +68,8 @@ class RoomAdmin(admin.ModelAdmin): @admin.register(Course) class CourseAdmin(admin.ModelAdmin): fieldsets = ( - (None, {"fields": ("name", "type", "source", "groups", "rooms", "last_update",)}), + (None, {"fields": ("name", "type", "source", "groups", "rooms", + "last_update",)}), ("Horaires", {"fields": ("begin", "end",)}), ("Remarques", {"fields": ("notes",)}),) list_display = ("name", "type", "source", "begin", "end",) @@ -22,7 +22,8 @@ from .utils import tz_now class QSJPSForm(forms.Form): - day = forms.DateField(label="Jour", widget=DateInput(attrs={"type": "date"})) + day = forms.DateField(label="Jour", + widget=DateInput(attrs={"type": "date"})) # Ces champs n’acceptent pas les secondes begin = forms.TimeField(label="Heure de début", input_formats=("%H:%M",), @@ -38,7 +39,8 @@ class QSJPSForm(forms.Form): # heures de début et de fin. self.fields["day"].initial = tz_now().strftime("%Y-%m-%d") self.fields["begin"].initial = tz_now().strftime("%H:%M") - self.fields["end"].initial = (tz_now() + timedelta(hours=1)).strftime("%H:%M") + self.fields["end"].initial = (tz_now() + timedelta(hours=1)) \ + .strftime("%H:%M") def clean(self): form_data = self.cleaned_data @@ -51,5 +53,6 @@ class QSJPSForm(forms.Form): form_data["begin"] >= form_data["end"]: # Si l’heure de fin est plus petite ou égale, on affiche # une erreur. - self._errors["end"].append("L’heure de début doit être supérieure à celle de fin.") + self._errors["end"].append("L’heure de début doit être supérieure " + "à celle de fin.") return form_data diff --git a/management/commands/_private.py b/management/commands/_private.py index e78c3c2..bac749b 100644 --- a/management/commands/_private.py +++ b/management/commands/_private.py @@ -25,16 +25,19 @@ from edt.utils import get_week import requests import edt + def add_time(date, time): ptime = datetime.datetime.strptime(time, "%H:%M") delta = datetime.timedelta(hours=ptime.hour, minutes=ptime.minute) return date + delta + def delete_courses_in_week(source, year, week, today): start, end = get_week(year, week) Course.objects.filter(begin__gte=max(start, today), begin__lt=end, source=source).delete() + def get_from_db_or_create(cls, **kwargs): obj = cls.objects.all().filter(**kwargs) @@ -45,6 +48,7 @@ def get_from_db_or_create(cls, **kwargs): return obj + def get_event(source, event, event_week, today): """Renvoie une classe Course à partir d’un événement récupéré par BS4""" # On récupère la date de l’évènement à partir de la semaine @@ -96,17 +100,18 @@ def get_event(source, event, event_week, today): return course + def get_events(source, soup, weeks_in_soup, today, year=None, week=None): """Récupère tous les cours disponibles dans l’emploi du temps Celcat. Le traîtement se limitera à la semaine indiquée si il y en a une.""" for event in soup.find_all("event"): event_week = weeks_in_soup[event.rawweeks.text] - event_week_num = event_week.isocalendar()[1] # Numéro de semaine + event_week_num = event_week.isocalendar()[1] # Numéro de semaine # On passe le traitement si la semaine de l’événement ne correspond pas # à la semaine passée, ou qu’il ne contient pas de groupe ou n’a pas de # date de début ou de fin. - if (event_week_num == week and event_week.year == year or \ + if (event_week_num == week and event_week.year == year or year is None or week is None) and \ event.resources.group is not None and \ event.starttime is not None and event.endtime is not None: @@ -116,6 +121,7 @@ def get_events(source, soup, weeks_in_soup, today, year=None, week=None): if course is not None: yield course + def get_update_date(soup): # Explication de la regex # @@ -140,6 +146,7 @@ def get_update_date(soup): date = datetime.datetime(year, month, day, hour, minute, second) return timezone.make_aware(date) + def get_weeks(soup): # Les semaines sont référencées de manière assez… exotique # En gros, il y a une liste d’éléments span qui contiennent une sorte d’ID @@ -151,13 +158,14 @@ def get_weeks(soup): # Un cours contient donc un ID de semaine, puis le nombre de jours après le # début de cette semaine. weeks = {} - for span in soup.find_all("span"): # Liste de toutes les semaines définies + for span in soup.find_all("span"): # Liste de toutes les semaines définies # On parse la date et on la fait correspondre à l’ID weeks[span.alleventweeks.text] = timezone.make_aware( datetime.datetime.strptime(span["date"], "%d/%m/%Y")) return weeks + def get_xml(url): user_agent = "celcatsanitizer/" + edt.VERSION req = requests.get(url, headers={"User-Agent": user_agent}) diff --git a/management/commands/cleancourses.py b/management/commands/cleancourses.py index 310c843..88eec64 100644 --- a/management/commands/cleancourses.py +++ b/management/commands/cleancourses.py @@ -30,7 +30,8 @@ class Command(BaseCommand): Course.objects.all().delete() Group.objects.all().delete() else: - Course.objects.filter(source__id__in=options["source"]).delete() + Course.objects.filter(source__id__in=options["source"]) \ + .delete() Group.objects.filter(source__id__in=options["source"]).delete() self.stdout.write(self.style.SUCCESS("Done.")) diff --git a/management/commands/listtimetables.py b/management/commands/listtimetables.py index 25f641b..7892855 100644 --- a/management/commands/listtimetables.py +++ b/management/commands/listtimetables.py @@ -29,8 +29,9 @@ class Command(BaseCommand): sources = sources.order_by("id") for source in sources: - self.stdout.write("{0}\t: {1} (id: {2})".format(source.formatted_timetables, - source, source.id)) + self.stdout.write("{0}\t: {1} (id: {2})" + .format(source.formatted_timetables, + source, source.id)) self.stdout.write("") self.stdout.write(self.style.SUCCESS("Done.")) diff --git a/management/commands/timetables.py b/management/commands/timetables.py index 86f389e..cf48af6 100644 --- a/management/commands/timetables.py +++ b/management/commands/timetables.py @@ -22,10 +22,13 @@ from django.db.models import Min from edt.models import Course, Source from edt.utils import get_week, tz_now -from ._private import delete_courses_in_week, get_events, get_update_date, get_weeks, get_xml +from ._private import delete_courses_in_week, get_events, get_update_date, \ + get_weeks, get_xml + @transaction.atomic -def process_timetable_week(source, soup, weeks_in_soup, force, year=None, week=None): +def process_timetable_week(source, soup, weeks_in_soup, force, + year=None, week=None): if year is not None and week is not None: begin, end = get_week(year, week) @@ -40,32 +43,34 @@ def process_timetable_week(source, soup, weeks_in_soup, force, year=None, week=N else: today = tz_now() - # On récupère la mise à jour la plus ancienne dans les cours de l’emploi du temps + # On récupère la mise à jour la plus ancienne dans les cours de + # l’emploi du temps last_update_date = Course.objects.filter(source=source) if today is not None: - # Cette date concerne les éléments commençant à partir d’aujourd’hui si la valeur - # n’est pas nulle. + # Cette date concerne les éléments commençant à partir + # d’aujourd’hui si la valeur n’est pas nulle. last_update_date = last_update_date.filter(begin__gte=today) if year is not None and week is not None: - # Si jamais on traite une semaine spécifique, on limite les cours sélectionnés - # à ceux qui commencent entre le début du traitement et la fin de la semaine + # Si jamais on traite une semaine spécifique, on limite les + # cours sélectionnés à ceux qui commencent entre le début du + # traitement et la fin de la semaine last_update_date = last_update_date.filter(begin__lt=end) - last_update_date = last_update_date.aggregate(Min("last_update")) \ - ["last_update__min"] + last_update_date = last_update_date \ + .aggregate(Min("last_update"))["last_update__min"] # Date de mise à jour de Celcat, utilisée à des fins de statistiques new_update_date = get_update_date(soup) - # On ne fait pas la mise à jour si jamais la dernière date de MàJ est plus récente - # que celle indiquée par Celcat. - # Attention, le champ last_update de la classe Course représente l’heure à laquelle - # le cours a été inséré dans la base de données, et non pas la date indiquée par - # Celcat. - if not force and last_update_date is not None and new_update_date is not None and \ - last_update_date >= new_update_date: + # On ne fait pas la mise à jour si jamais la dernière date de MàJ + # est plus récente que celle indiquée par Celcat. Attention, le + # champ last_update de la classe Course représente l’heure à + # laquelle le cours a été inséré dans la base de données, et non + # pas la date indiquée par Celcat. + if not force and last_update_date is not None and \ + new_update_date is not None and last_update_date >= new_update_date: return if year is not None and week is not None: @@ -78,7 +83,8 @@ def process_timetable_week(source, soup, weeks_in_soup, force, year=None, week=N # présente dans Celcat et maintenant. delete_from = min(weeks_in_soup.values()) if not force: - # Si jamais on force la MàJ, on efface tout à partir de la première semaine + # Si jamais on force la MàJ, on efface tout à partir de la + # première semaine delete_from = max(delete_from, today) Course.objects.filter(source=source, begin__gte=delete_from).delete() @@ -91,13 +97,15 @@ def process_timetable_week(source, soup, weeks_in_soup, force, year=None, week=N source.last_update_date = new_update_date source.save() + def process_timetable(source, force, year=None, weeks=None): soup = get_xml(source.url) weeks_in_soup = get_weeks(soup) if year is not None and weeks is not None: for week in weeks: - process_timetable_week(source, soup, weeks_in_soup, force, year, week) + process_timetable_week(source, soup, weeks_in_soup, force, + year, week) else: process_timetable_week(source, soup, weeks_in_soup, force) @@ -106,9 +114,12 @@ class Command(BaseCommand): help = "Fetches registered celcat timetables" def add_arguments(self, parser): - parser.add_argument("--all", const=True, default=False, action="store_const") - parser.add_argument("--force", const=True, default=False, action="store_const") - parser.add_argument("--week", type=int, choices=range(1, 54), nargs="+") + parser.add_argument("--all", const=True, default=False, + action="store_const") + parser.add_argument("--force", const=True, default=False, + action="store_const") + parser.add_argument("--week", type=int, choices=range(1, 54), + nargs="+") parser.add_argument("--year", type=int, nargs=1) def handle(self, *args, **options): @@ -120,7 +131,8 @@ class Command(BaseCommand): elif options["week"] is None: _, week, day = tz_now().isocalendar() if day >= 6: - year, week, _ = (tz_now() + datetime.timedelta(weeks=1)).isocalendar() + year, week, _ = (tz_now() + datetime.timedelta(weeks=1)) \ + .isocalendar() weeks = [week] else: weeks = options["week"] @@ -132,7 +144,8 @@ class Command(BaseCommand): year = options["year"][0] for source in Source.objects.all(): - self.stdout.write("Processing {0}".format(source.formatted_timetables)) + self.stdout.write("Processing {0}".format( + source.formatted_timetables)) try: process_timetable(source, options["force"], year, weeks) @@ -140,7 +153,8 @@ class Command(BaseCommand): break except Exception: self.stderr.write( - self.style.ERROR("Failed to process {0}:".format(source.formatted_timetables)) + self.style.ERROR("Failed to process {0}:".format( + source.formatted_timetables)) ) self.stderr.write(self.style.ERROR(traceback.format_exc())) errcount += 1 @@ -148,4 +162,5 @@ class Command(BaseCommand): if errcount == 0: self.stdout.write(self.style.SUCCESS("Done.")) else: - self.stdout.write(self.style.ERROR("Done with {0} errors.".format(errcount))) + self.stdout.write(self.style.ERROR("Done with {0} errors.".format( + errcount))) @@ -42,7 +42,6 @@ class Year(SlugModel): def __str__(self): return self.name - class Meta: verbose_name = "année" verbose_name_plural = "années" @@ -50,16 +49,17 @@ class Year(SlugModel): class Source(models.Model): url = models.URLField(max_length=255, verbose_name="URL", unique=True) - last_update_date = models.DateTimeField(verbose_name="dernière mise à jour Celcat", - null=True, blank=True) + last_update_date = models.DateTimeField(null=True, blank=True, + verbose_name="dernière mise à jour" + "Celcat") def __str__(self): return self.url @property def formatted_timetables(self): - return ", ".join([str(timetable) for timetable in self.timetables.iterator()]) - + return ", ".join([str(timetable) for timetable in + self.timetables.iterator()]) class Meta: verbose_name = "source d’emploi du temps" @@ -79,12 +79,12 @@ class Timetable(SlugModel): name = models.CharField(max_length=64, verbose_name="nom") slug = models.SlugField(max_length=64, default="") source = models.ForeignKey(Source, on_delete=models.CASCADE, - verbose_name="source", related_name="timetables") + verbose_name="source", + related_name="timetables") def __str__(self): return self.year.name + " " + self.name - class Meta: unique_together = (("year", "name"), ("year", "slug"),) verbose_name = "emploi du temps" @@ -95,11 +95,12 @@ class GroupManager(Manager): def get_parents(self, group): 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)], Q(subgroup="")) return self.get_queryset().filter(groups_criteria, - Q(semester=None) | Q(semester=group.semester), + Q(semester=None) | + Q(semester=group.semester), mention=group.mention, source=group.source) @@ -115,7 +116,8 @@ class Group(SlugModel): 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="") + subgroup = models.CharField(max_length=16, verbose_name="sous-groupe", + default="") slug = models.SlugField(max_length=64, default="") @@ -126,10 +128,10 @@ class Group(SlugModel): if self.subgroup is not None and subgroup is not None: subgroup_corresponds = self.subgroup.startswith(subgroup) - return (self.mention.startswith(mention) or \ + return (self.mention.startswith(mention) or mention.startswith(self.mention)) and \ - (self.semester == semester or semester is None) and \ - subgroup_corresponds + (self.semester == semester or semester is None) and \ + subgroup_corresponds @property def group_info(self): @@ -148,7 +150,6 @@ class Group(SlugModel): super(Group, self).save() - class Meta: index_together = ("mention", "semester", "subgroup",) unique_together = (("name", "source",), @@ -175,8 +176,8 @@ class CourseManager(Manager): def get_courses(self, obj, **criteria): qs = self.get_queryset() if isinstance(obj, Group): - qs = qs.filter(groups__in=Group.objects.get_parents(obj), **criteria) \ - .prefetch_related("rooms") + qs = qs.filter(groups__in=Group.objects.get_parents(obj), + **criteria).prefetch_related("rooms") elif isinstance(obj, Room): qs = qs.filter(rooms__in=(obj,), **criteria) else: @@ -197,7 +198,8 @@ class CourseManager(Manager): class Course(models.Model): objects = CourseManager() - name = models.CharField(max_length=255, verbose_name="nom", default="Sans nom") + name = models.CharField(max_length=255, verbose_name="nom", + default="Sans nom") type_ = models.CharField(name="type", max_length=255, verbose_name="type de cours", null=True) source = models.ForeignKey(Source, on_delete=models.CASCADE, @@ -225,7 +227,6 @@ class Course(models.Model): super(Course, self).save(*args, **kwargs) - class Meta: verbose_name = "cours" verbose_name_plural = "cours" diff --git a/templatetags/dt_week.py b/templatetags/dt_week.py index e8d13ac..de0db08 100644 --- a/templatetags/dt_week.py +++ b/templatetags/dt_week.py @@ -17,10 +17,12 @@ from django import template register = template.Library() + @register.filter def dt_week(dt): return dt.isocalendar()[1] + @register.filter def dt_prettyprint(dt): return "{0}/{1:02d}/{2:02d}".format(dt.year, dt.month, dt.day) diff --git a/templatetags/rooms.py b/templatetags/rooms.py index 5108c92..f0e1b2e 100644 --- a/templatetags/rooms.py +++ b/templatetags/rooms.py @@ -17,10 +17,12 @@ from django import template register = template.Library() + @register.filter def format_rooms(rooms): amphi_list = [room.name for room in rooms if room.name.startswith("Amphi")] - room_list = [room.name for room in rooms if not room.name.startswith("Amphi")] + room_list = [room.name for room in rooms + if not room.name.startswith("Amphi")] amphis = ", ".join(amphi_list) joined = ", ".join(room_list) @@ -28,29 +28,42 @@ class CourseTestCase(TestCase): source = Source(url="http://example.org/") source.save() - self.timetable = Timetable(year=self.year, name="Test timetable 2", source=source, slug="test-timetable2") + self.timetable = Timetable(year=self.year, name="Test timetable 2", + source=source, slug="test-timetable2") self.timetable.save() cma = Group.objects.create(celcat_name="L1 info s2 CMA", source=source) - tda2 = Group.objects.create(celcat_name="L1 info s2 TDA2", source=source) - self.tpa21 = Group.objects.create(celcat_name="L1 info s2 TPA21", source=source) + tda2 = Group.objects.create(celcat_name="L1 info s2 TDA2", + source=source) + self.tpa21 = Group.objects.create(celcat_name="L1 info s2 TPA21", + source=source) cmb = Group.objects.create(celcat_name="L1 info s2 CMB", source=source) - tdb2 = Group.objects.create(celcat_name="L1 info s2 TDB2", source=source) - self.tpb21 = Group.objects.create(celcat_name="L1 info s2 TPB21", source=source) + tdb2 = Group.objects.create(celcat_name="L1 info s2 TDB2", + source=source) + self.tpb21 = Group.objects.create(celcat_name="L1 info s2 TPB21", + source=source) for group in (cma, tda2, self.tpa21, cmb, tdb2, self.tpb21,): - course = Course.objects.create(name="{0} course".format(group.name), type="cours", source=source, begin=dt, end=dt) + course = Course.objects.create(name="{0} course" + .format(group.name), + type="cours", source=source, + begin=dt, end=dt) course.groups.add(group) def test_get_courses_for_group(self): tpa21_courses = Course.objects.get_courses(self.tpa21) tpb21_courses = Course.objects.get_courses(self.tpb21) - tpa21_course_names = ["L1 info s2 CMA course", "L1 info s2 TDA2 course", "L1 info s2 TPA21 course"] - tpb21_course_names = ["L1 info s2 CMB course", "L1 info s2 TDB2 course", "L1 info s2 TPB21 course"] + tpa21_course_names = ["L1 info s2 CMA course", + "L1 info s2 TDA2 course", + "L1 info s2 TPA21 course"] + tpb21_course_names = ["L1 info s2 CMB course", + "L1 info s2 TDB2 course", + "L1 info s2 TPB21 course"] - for courses, names in ((tpa21_courses, tpa21_course_names,), (tpb21_courses, tpb21_course_names,),): + for courses, names in ((tpa21_courses, tpa21_course_names,), + (tpb21_courses, tpb21_course_names,),): for course in courses: self.assertIn(course.name, names) names.remove(course.name) @@ -64,29 +77,40 @@ class GroupTestCase(TestCase): self.source = Source(url="http://example.org/") self.source.save() - self.timetable = Timetable(year=self.year, name="Test timetable", source=self.source, slug="test-timetable") + self.timetable = Timetable(year=self.year, name="Test timetable", + source=self.source, slug="test-timetable") self.timetable.save() Group.objects.create(celcat_name="L1 info s2 CMA", source=self.source) Group.objects.create(celcat_name="L1 info s2 TDA2", source=self.source) - Group.objects.create(celcat_name="L1 info s2 TPA21", source=self.source) + Group.objects.create(celcat_name="L1 info s2 TPA21", + source=self.source) Group.objects.create(celcat_name="L1 info s2 CMB", source=self.source) Group.objects.create(celcat_name="L1 info s2 TDB2", source=self.source) - Group.objects.create(celcat_name="L1 info s2 TPB21", source=self.source) + Group.objects.create(celcat_name="L1 info s2 TPB21", + source=self.source) - Group.objects.create(celcat_name="L1 info (toutes sections et semestres confondus)", source=self.source) + Group.objects.create(celcat_name="L1 info (toutes sections et " + "semestres confondus)", source=self.source) def test_corresponds(self): - cma = Group.objects.get(celcat_name="L1 info s2 CMA", source=self.source) - tda2 = Group.objects.get(celcat_name="L1 info s2 TDA2", source=self.source) - tpa21 = Group.objects.get(celcat_name="L1 info s2 TPA21", source=self.source) - - cmb = Group.objects.get(celcat_name="L1 info s2 CMB", source=self.source) - tdb2 = Group.objects.get(celcat_name="L1 info s2 TDB2", source=self.source) - tpb21 = Group.objects.get(celcat_name="L1 info s2 TPB21", source=self.source) - - general = Group.objects.get(celcat_name="L1 info (toutes sections et semestres confondus)", source=self.source) + cma = Group.objects.get(celcat_name="L1 info s2 CMA", + source=self.source) + tda2 = Group.objects.get(celcat_name="L1 info s2 TDA2", + source=self.source) + tpa21 = Group.objects.get(celcat_name="L1 info s2 TPA21", + source=self.source) + + cmb = Group.objects.get(celcat_name="L1 info s2 CMB", + source=self.source) + tdb2 = Group.objects.get(celcat_name="L1 info s2 TDB2", + source=self.source) + tpb21 = Group.objects.get(celcat_name="L1 info s2 TPB21", + source=self.source) + + general = Group.objects.get(celcat_name="L1 info (toutes sections et " + "semestres confondus)", source=self.source) self.assertFalse(cma.corresponds_to(*tda2.group_info)) self.assertFalse(cma.corresponds_to(*tpa21.group_info)) @@ -135,7 +159,8 @@ class GroupTestCase(TestCase): tdb2 = Group.objects.get(name="L1 info s2 TDB2", source=self.source) tpb21 = Group.objects.get(name="L1 info s2 TPB21", source=self.source) - general = Group.objects.get(celcat_name="L1 info (toutes sections et semestres confondus)", source=self.source) + general = Group.objects.get(celcat_name="L1 info (toutes sections et " + "semestres confondus)", source=self.source) self.assertEqual(cma.celcat_name, "L1 info s2 CMA") self.assertEqual(tda2.celcat_name, "L1 info s2 TDA2") @@ -145,18 +170,26 @@ 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)") + 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", source=self.source) - tda2 = Group.objects.get(celcat_name="L1 info s2 TDA2", source=self.source) - tpa21 = Group.objects.get(celcat_name="L1 info s2 TPA21", source=self.source) - - cmb = Group.objects.get(celcat_name="L1 info s2 CMB", source=self.source) - tdb2 = Group.objects.get(celcat_name="L1 info s2 TDB2", source=self.source) - tpb21 = Group.objects.get(celcat_name="L1 info s2 TPB21", source=self.source) - - general = Group.objects.get(celcat_name="L1 info (toutes sections et semestres confondus)", source=self.source) + cma = Group.objects.get(celcat_name="L1 info s2 CMA", + source=self.source) + tda2 = Group.objects.get(celcat_name="L1 info s2 TDA2", + source=self.source) + tpa21 = Group.objects.get(celcat_name="L1 info s2 TPA21", + source=self.source) + + cmb = Group.objects.get(celcat_name="L1 info s2 CMB", + source=self.source) + tdb2 = Group.objects.get(celcat_name="L1 info s2 TDB2", + source=self.source) + tpb21 = Group.objects.get(celcat_name="L1 info s2 TPB21", + source=self.source) + + general = Group.objects.get(celcat_name="L1 info (toutes sections et " + "semestres confondus)", source=self.source) self.assertEqual(cma.group_info, ("L1 info", 2, "A")) self.assertEqual(tda2.group_info, ("L1 info", 2, "A2")) @@ -18,9 +18,11 @@ import re from django.utils import timezone + def get_current_week(): return tz_now().isocalendar()[:2] + def get_current_or_next_week(): year, week, day = tz_now().isocalendar() if day >= 6: @@ -28,6 +30,7 @@ def get_current_or_next_week(): return year, week + def get_week(year, week): start = timezone.make_aware(datetime.datetime.strptime( "{0}-W{1}-1".format(year, week), "%Y-W%W-%w")) @@ -35,6 +38,7 @@ def get_week(year, week): return start, end + def group_courses(courses): grouped_courses = [] for i, course in enumerate(courses): @@ -45,6 +49,7 @@ def group_courses(courses): return grouped_courses + def parse_group(name): # Explication de la regex # @@ -66,7 +71,8 @@ def parse_group(name): parts = search.groups() - # On retourne la section (parts[0]), le semestre (parts[2]) et le groupe (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: @@ -75,6 +81,7 @@ def parse_group(name): # une erreur. return parts[0], None, parts[5] + def tz_now(): """Retourne la date et l’heure avec le bon fuseau horaire""" return timezone.make_aware(datetime.datetime.now()) |