diff options
author | Alban Gruin | 2017-09-04 22:13:31 +0200 |
---|---|---|
committer | Alban Gruin | 2017-09-04 22:13:31 +0200 |
commit | ab9df7a174e2debbf9c644c8ec3e2127eeeaa3cf (patch) | |
tree | bf9510823644aa1c5dba0ab39277633c0f18ad49 | |
parent | ce0ed9347adde9ca80c11efe79766c966d5749ba (diff) | |
parent | 4b3fc9e4c41e2247bf0fa0fe2629b2b57fc174b0 (diff) |
Merge branch 'stable/0.y.z' into prod/pa1ch/0.y.zv0.8.0-pa1chprod/pa1ch/0.8.z
-rw-r--r-- | README.md | 16 | ||||
-rw-r--r-- | admin.py | 2 | ||||
-rw-r--r-- | management/commands/_private.py | 22 | ||||
-rw-r--r-- | management/commands/listtimetables.py | 1 | ||||
-rw-r--r-- | management/commands/timetables.py | 8 | ||||
-rw-r--r-- | models.py | 33 | ||||
-rw-r--r-- | templates/mail/mail_confirm.txt | 6 | ||||
-rw-r--r-- | templates/mail/mail_footer.txt | 4 | ||||
-rw-r--r-- | templates/mail/mail_timetable.txt | 2 | ||||
-rw-r--r-- | templates/mail/mail_unsubscribed.txt | 2 | ||||
-rw-r--r-- | templates/subscribe.html | 10 | ||||
-rw-r--r-- | templates/timetable.html | 4 | ||||
-rw-r--r-- | tests.py | 76 | ||||
-rw-r--r-- | utils.py | 37 | ||||
-rw-r--r-- | views.py | 6 |
15 files changed, 128 insertions, 101 deletions
@@ -6,7 +6,7 @@ Parce que j'en avait ma claque de consulter un emploi du temps mal formaté. Par ## Comment faire tourner celcatsanitizer chez moi ? celcatsanitizer est écrit en Python 3. Il dépend des bibliothèques suivantes : - * Django 1.10 + * Django 1.11 * requests * BeautifulSoup4 @@ -16,7 +16,7 @@ Pour tester celcatsanitizer, il est recommandé d'utiliser SQLite ou PostgreSQL. Pour la production, il est recommandé d'utiliser PostgreSQL (avec le driver psycopg2) et de mettre le tout dans un environnement virtuel. -Aucun autre SGBD n'a été testé. Toute modification visant à faire fonctionner celcatsanitizer avec un autre SGBD sera bien entendu acceptée. +Aucun autre SGBD n'a été testé, mais depuis la version 0.8.0, celcatsanitizer n'utilise plus de fonctions SQL brutes spécifiques. Tous les SGBD supportés par Django devraient fonctionner sans poser de problèmes. ### Installation Il est préférable d'utiliser un environnement virtuel, mais ce n'est pas obligatoire. Si vous ne souhaitez pas utiliser un environnement virtuel, passez directement à l'installation des dépendances. @@ -40,7 +40,7 @@ Notez que cette étape n'est pas obligatoire > $ pip install requests -> $ pip install django=="1.10.*" +> $ pip install django > $ pip install beautiful4 @@ -66,18 +66,18 @@ Pour la production, il est recommandé d'utiliser une version stable, accessible Dans le fichier celcatsanitizer/settings.py, vous devrez renseigner plusieurs informations. ##### Configuration du serveur mail -[Vous pouvez trouver la documentation concernant l'envoi des mails sur le site de Django.](https://docs.djangoproject.com/fr/1.10/topics/email/) +[Vous pouvez trouver la documentation concernant l'envoi des mails sur le site de Django.](https://docs.djangoproject.com/fr/1.11/topics/email/) ##### Configuration des administrateurs -[Vous pouvez retrouver la documentation de la variable ADMIN sur le site de Django.](https://docs.djangoproject.com/fr/1.10/ref/settings/#admins) +[Vous pouvez retrouver la documentation de la variable ADMIN sur le site de Django.](https://docs.djangoproject.com/fr/1.11/ref/settings/#admins) Cette variable est obligatoire. ##### Configuration de l'internationalisation -Ce passage n'est pas obligatoire. [Vous pouvez retrouver la documentation de l'internationalisation sur le site de Django.](https://docs.djangoproject.com/fr/1.10/topics/i18n/) +Ce passage n'est pas obligatoire. [Vous pouvez retrouver la documentation de l'internationalisation sur le site de Django.](https://docs.djangoproject.com/fr/1.11/topics/i18n/) ##### Configuration de la base de données -[Vous pouvez retrouver la documentation de la base de données sur le site de Django.](https://docs.djangoproject.com/fr/1.10/ref/settings/#databases) +[Vous pouvez retrouver la documentation de la base de données sur le site de Django.](https://docs.djangoproject.com/fr/1.11/ref/settings/#databases) ##### Configuration du mode de Django Si jamais vous utiliser Django en production, vous **devez** mettre la variable DEBUG à False. @@ -99,7 +99,7 @@ Vous avez besoin de générer les migrations de celcatsanitizer, puis appliquez- > $ ./manage.py migrate ##### Gestion des fichiers statiques -Si vous êtes en production, vous devez renseigner l'emplacement de vos fichiers statiques dans la variable [STATIC_ROOT](https://docs.djangoproject.com/fr/1.10/ref/settings/#std:setting-STATIC_ROOT) de la configuration de Django, puis exécuter la commande suivante : +Si vous êtes en production, vous devez renseigner l'emplacement de vos fichiers statiques dans la variable [STATIC_ROOT](https://docs.djangoproject.com/fr/1.11/ref/settings/#std:setting-STATIC_ROOT) de la configuration de Django, puis exécuter la commande suivante : > $ ./manage.py collectstatic @@ -34,7 +34,7 @@ class LastUpdateAdmin(admin.ModelAdmin): class GroupAdmin(admin.ModelAdmin): fieldsets = ( (None, {"fields": ("name", "celcat_name", "timetable",)}), - ("Groupes", {"fields": ("mention", "subgroup", "td", "tp",)}),) + ("Groupes", {"fields": ("mention", "subgroup", "td", "tp", "parent",)}),) list_display = ("name", "timetable",) list_filter = ("timetable__name",) readonly_fields = ("celcat_name", "mention", "subgroup", "td", "tp",) diff --git a/management/commands/_private.py b/management/commands/_private.py index 3cd23ca..c31eb34 100644 --- a/management/commands/_private.py +++ b/management/commands/_private.py @@ -41,6 +41,27 @@ def add_time(date, time): delta = datetime.timedelta(hours=time.hour, minutes=time.minute) return date + delta +def consolidate_group(group): + group_content_key = ("mention", "subgroup", "td", "tp") + group_content_list = group.group_info[1:] + group_content = dict(zip(group_content_key, group_content_list)) + + for i in range(len(group_content_list))[::-1]: + del group_content[group_content_key[i]] + group_content[group_content_key[i] + "__isnull"] = True + + if group_content_list[i] is not None: + break + + if "subgroup" in group_content: + group.parent = Group.objects.filter(**group_content).first() + group.save() + +def consolidate_groups(groups): + for group in groups: + if group.parent == None: + consolidate_group(group) + def delete_courses_in_week(timetable, year, week): start, end = get_week(year, week) Course.objects.filter(begin__gte=start, begin__lt=end, @@ -79,6 +100,7 @@ def get_events(timetable, year, week, soup, weeks_in_soup): groups = [get_from_db_or_create(Group, timetable=timetable, celcat_name=item.text) for item in event.resources.group.find_all("item")] + consolidate_groups(groups) if event.notes is not None: notes = event.notes.text diff --git a/management/commands/listtimetables.py b/management/commands/listtimetables.py index c5ef41f..e4b782f 100644 --- a/management/commands/listtimetables.py +++ b/management/commands/listtimetables.py @@ -27,7 +27,6 @@ class Command(BaseCommand): def handle(self, *args, **options): timetables = Timetable.objects.all() if options["order_by_id"]: - print("oui") timetables = timetables.order_by("id") else: timetables = timetables.order_by("name") diff --git a/management/commands/timetables.py b/management/commands/timetables.py index d39075d..d596233 100644 --- a/management/commands/timetables.py +++ b/management/commands/timetables.py @@ -64,6 +64,8 @@ class Command(BaseCommand): def handle(self, *args, **options): year = None + errcount = 0 + if options["week"] is None: _, week, day = timezone.now().isocalendar() if day >= 6: @@ -84,5 +86,9 @@ class Command(BaseCommand): process_timetable(timetable, year, weeks) except Exception as e: self.stderr.write(self.style.ERROR("Failed to process {0}: {1}".format(timetable, e))) + errcount += 1 - self.stdout.write(self.style.SUCCESS("Done.")) + if errcount == 0: + self.stdout.write(self.style.SUCCESS("Done.")) + else: + self.stdout.write(self.style.ERROR("Done with {0} errors.".format(errcount))) @@ -16,8 +16,7 @@ from django.db import connection, models from django.db.models import Count, Manager, Q -from django.db.models.expressions import RawSQL -from django.db.models.functions import Extract, ExtractYear +from django.db.models.functions import ExtractWeek, ExtractYear from django.utils.text import slugify from .utils import parse_group @@ -26,10 +25,6 @@ import hashlib import os -class ExtractWeek(Extract): - lookup_name = "week" - - class Timetable(models.Model): name = models.CharField(max_length=64, unique=True, verbose_name="nom") url = models.URLField(max_length=255, unique=True, verbose_name="URL") @@ -60,20 +55,28 @@ class LastUpdate(models.Model): verbose_name_plural = "dernières mises à jour" +class GroupManager(Manager): + def get_relevant_groups(self): + return self.get_queryset().annotate(children_count=Count("children")).filter(children_count=0) + + class Group(models.Model): + objects = GroupManager() + name = models.CharField(max_length=255, verbose_name="nom") celcat_name = models.CharField(max_length=255, verbose_name="nom dans Celcat") timetable = models.ForeignKey(Timetable, on_delete=models.CASCADE, verbose_name="emploi du temps") mention = models.CharField(max_length=32) - subgroup = models.CharField(max_length=1, verbose_name="sous-groupe") + subgroup = models.CharField(max_length=1, verbose_name="sous-groupe", null=True) td = models.IntegerField(verbose_name="groupe de TD", null=True) tp = models.IntegerField(verbose_name="groupe de TP", null=True) + parent = models.ForeignKey("self", verbose_name="groupe parent", null=True, default=None, related_name="children") slug = models.SlugField(max_length=64, default="") def corresponds_to(self, timetable_id, mention, subgroup, td, tp): - return self.timetable.id == timetable_id and self.mention == mention and self.subgroup == subgroup and (self.td == td or self.td is None or td is None) and (self.tp == tp or self.tp is None or tp is None) + return self.timetable.id == timetable_id and self.mention.startswith(mention) and (self.subgroup == subgroup or self.subgroup is None) and (self.td == td or self.td is None or td is None) and (self.tp == tp or self.tp is None or tp is None) @property def group_info(self): @@ -83,11 +86,12 @@ class Group(models.Model): return self.name def save(self, *args, **kwargs): - self.mention, self.subgroup, self.td, self.tp = parse_group(self.celcat_name) if self.name == "": self.name = self.celcat_name self.slug = slugify(self.name) - super(Group, self).save() + + self.mention, self.subgroup, self.td, self.tp = parse_group(self.name) + super(Group, self).save() class Meta: @@ -133,15 +137,10 @@ class Room(models.Model): class CourseManager(Manager): def get_courses_for_group(self, group, **filters): - return self.get_queryset().filter(Q(groups__td__isnull=True) | Q(groups__td=group.td), Q(groups__tp__isnull=True) | Q(groups__tp=group.tp), groups__mention=group.mention, groups__subgroup=group.subgroup, timetable=group.timetable, **filters).order_by("begin") + return self.get_queryset().filter(Q(groups__td__isnull=True) | Q(groups__td=group.td), Q(groups__tp__isnull=True) | Q(groups__tp=group.tp), Q(groups__subgroup__isnull=True) | Q(groups__subgroup=group.subgroup), groups__mention=group.mention, timetable=group.timetable, **filters).order_by("begin") def get_weeks(self, **criteria): - qs = self.get_queryset().filter(**criteria).order_by("groups__name", "year", "week").annotate(_=Count(("groups", "year", "week", "begin")), year=ExtractYear("begin")) - - if connection.vendor == "postgresql": - return qs.annotate(week=ExtractWeek("begin")) - else: - return qs.annotate(week=RawSQL("""cast(strftime("%%W", "begin") as integer)""", [])) + return self.get_queryset().filter(**criteria).order_by("groups__name", "year", "week").annotate(_=Count(("groups", "year", "week", "begin")), year=ExtractYear("begin"), week=ExtractWeek("begin")) class Course(models.Model): diff --git a/templates/mail/mail_confirm.txt b/templates/mail/mail_confirm.txt index b6cc08a..34aca3d 100644 --- a/templates/mail/mail_confirm.txt +++ b/templates/mail/mail_confirm.txt @@ -1,8 +1,8 @@ -Vous avez été abonné à l'emploi du temps {{ group.timetable.name }} - {{ group.name }} +Vous avez été abonné à l’emploi du temps {{ group.timetable.name }} - {{ group.name }} -Pour valider l'abonnement, suivez ce lien : {{ domain }}{% url "confirm" token %} +Pour valider l’abonnement, suivez ce lien : {{ domain }}{% url "confirm" token %} -Si vous pensez que vous avez été abonné par erreur, suivez ce lien : {{ domain }}{% url "cancel" token %} +Si vous pensez que vous avez été abonné par erreur, suivez ce lien : {{ domain }}{% url "cancel" token %} Vous ne recevrez aucun mail tant que vous n'avez pas validé votre abonnement. diff --git a/templates/mail/mail_footer.txt b/templates/mail/mail_footer.txt index 7b71122..b39f738 100644 --- a/templates/mail/mail_footer.txt +++ b/templates/mail/mail_footer.txt @@ -1,2 +1,2 @@ -Pour vous désinscrire de cet emploi du temps, suivez ce lien : {{ domain }}{% url "cancel" token %} -Pour contacter l'administrateur du service, envoyez un mail à cette adresse : {{ admins|first|last }} +Pour vous désinscrire de cet emploi du temps, suivez ce lien : {{ domain }}{% url "cancel" token %} +Pour contacter l’administrateur du service, envoyez un mail à cette adresse : {{ admins|first|last }} diff --git a/templates/mail/mail_timetable.txt b/templates/mail/mail_timetable.txt index f6b8364..7cc6b26 100644 --- a/templates/mail/mail_timetable.txt +++ b/templates/mail/mail_timetable.txt @@ -1,7 +1,7 @@ {% load rooms %}{% autoescape off %}{% for day in courses %}{% filter title %}{{ day.0.begin|date:"l j F o" }}{% endfilter %} - de {{ day.0.begin|date:"H:i" }} à {% with day|last as last %}{{ last.end|date:"H:i" }}{% endwith %} {% for course in day %} * {{ course.name }} ({{ course.type }}), de {{ course.begin|date:"H:i" }} à {{ course.end|date:"H:i" }}{% if course.rooms.all|length > 0 %} {{ course.rooms.all|format_rooms }}{% endif %}{% if course.notes is not None %} - Remarques : {{ course.notes }}{% endif %} + Remarques : {{ course.notes }}{% endif %} {% endfor %}{% empty %}Aucun cours pour le groupe {{ group }} pendant la semaine {{ week }}. {% endfor %}{% endautoescape %} diff --git a/templates/mail/mail_unsubscribed.txt b/templates/mail/mail_unsubscribed.txt index d5c5df2..8d75ccf 100644 --- a/templates/mail/mail_unsubscribed.txt +++ b/templates/mail/mail_unsubscribed.txt @@ -1,2 +1,2 @@ -Vous avez été désabonné de l'emploi du temps {{ group.timetable.name }} - {{ group.name }} +Vous avez été désabonné de l’emploi du temps {{ group.timetable.name }} - {{ group.name }} Notez que si vous vous êtes abonné à un autre emploi du temps, vous recevrez toujours les mails de ceux-ci. diff --git a/templates/subscribe.html b/templates/subscribe.html index 99fe5ed..76780ac 100644 --- a/templates/subscribe.html +++ b/templates/subscribe.html @@ -1,6 +1,6 @@ {% extends "index.html" %} -{% block title %}S'abonner à {{ group.timetable.name }} – {{ group.name }}{% endblock %} +{% block title %}S’abonner à {{ group.timetable.name }} – {{ group.name }}{% endblock %} {% block body %} <h2>S'abonner à {{ group.timetable.name }} – {{ group.name }}</h2> @@ -9,8 +9,8 @@ {{ form }} <input type="submit" value="S'abonner" /> </form> - <p>Après l'abonnement, vous allez recevoir un mail avec un lien de confirmation. Aucun autre mail ne vous sera envoyé si vous n'avez pas validé votre abonnement.<br /> - Vous pouvez vous désabonner à tout moment à l'aide d'un lien contenu dans tout les mails que nous vous enverrons.<br /> - Les mails sont envoyés tout les dimanche à minuit CET.<br /> - Nous ne partageons votre adresse à qui que se soit. Lorsque vous vous désabonnez, votre adresse est effacée de nos serveurs.</p> + <p>Après l’abonnement, vous allez recevoir un mail avec un lien de confirmation. Aucun autre mail ne vous sera envoyé si vous n'avez pas validé votre abonnement.<br /> + Vous pouvez vous désabonner à tout moment à l’aide d'un lien contenu dans tout les mails que nous vous enverrons.<br /> + Les mails sont envoyés tout les dimanche à minuit CET.<br /> + Nous ne partageons votre adresse à qui que se soit. Lorsque vous vous désabonnez, votre adresse est effacée de nos serveurs.</p> {% endblock %} diff --git a/templates/timetable.html b/templates/timetable.html index 78dcfed..264a25c 100644 --- a/templates/timetable.html +++ b/templates/timetable.html @@ -9,7 +9,7 @@ <section> <h3>{% filter title %}{{ day.0.begin|date:"l j F o" }}{% endfilter %} – de {{ day.0.begin|date:"H:i" }} à {% with day|last as last %}{{ last.end|date:"H:i" }}{% endwith %}</h3> <ul>{% for course in day %} - <li class="course"><b>{{ course.name }}</b> ({{ course.type }}), de {{ course.begin|date:"H:i" }} à {{ course.end|date:"H:i" }}{% if course.rooms.all|length > 0 %}<br /><em>{{ course.rooms.all|format_rooms }}</em>{% endif %}{% if course.notes is not None %}<br /><small>Remarques : {{ course.notes }}</small>{% endif %}</li>{% endfor %} + <li class="course"><b>{{ course.name }}</b> ({{ course.type }}), de {{ course.begin|date:"H:i" }} à {{ course.end|date:"H:i" }}{% if course.rooms.all|length > 0 %}<br /><em>{{ course.rooms.all|format_rooms }}</em>{% endif %}{% if course.notes is not None %}<br /><small>Remarques : {{ course.notes }}</small>{% endif %}</li>{% endfor %} </ul> </section>{% endfor %} - <p><a class="subscribe" href="{% url "subscribe" group.timetable.slug group.slug year week %}">S'abonner à cet emploi du temps</a></p>{% endblock %} + <p><a class="subscribe" href="{% url "subscribe" group.timetable.slug group.slug year week %}">S’abonner à cet emploi du temps</a></p>{% endblock %} @@ -27,13 +27,13 @@ class CourseTestCase(TestCase): self.timetable = Timetable(name="Test timetable 2", url="http://example.org/", slug="test-timetable2") self.timetable.save() - cma = Group.objects.create(celcat_name="L1 info s2 - CMA", timetable=self.timetable) - tda2 = Group.objects.create(celcat_name="L1 info s2 - TDA2", timetable=self.timetable) - self.tpa21 = Group.objects.create(celcat_name="L1 info s2 - TPA21", timetable=self.timetable) + cma = Group.objects.create(celcat_name="L1 info s2 CMA", timetable=self.timetable) + tda2 = Group.objects.create(celcat_name="L1 info s2 TDA2", timetable=self.timetable) + self.tpa21 = Group.objects.create(celcat_name="L1 info s2 TPA21", timetable=self.timetable) - cmb = Group.objects.create(celcat_name="L1 info s2 - CMB", timetable=self.timetable) - tdb2 = Group.objects.create(celcat_name="L1 info s2 - TDB2", timetable=self.timetable) - self.tpb21 = Group.objects.create(celcat_name="L1 info s2 - TPB21", timetable=self.timetable) + cmb = Group.objects.create(celcat_name="L1 info s2 CMB", timetable=self.timetable) + tdb2 = Group.objects.create(celcat_name="L1 info s2 TDB2", timetable=self.timetable) + self.tpb21 = Group.objects.create(celcat_name="L1 info s2 TPB21", timetable=self.timetable) for group in (cma, tda2, self.tpa21, cmb, tdb2, self.tpb21,): course = Course.objects.create(name="{0} course".format(group.name), type="cours", timetable=self.timetable, begin=dt, end=dt) @@ -43,8 +43,8 @@ class CourseTestCase(TestCase): tpa21_courses = Course.objects.get_courses_for_group(self.tpa21) tpb21_courses = Course.objects.get_courses_for_group(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 course in courses: @@ -57,22 +57,22 @@ class GroupTestCase(TestCase): self.timetable = Timetable(name="Test timetable", url="http://example.com/", slug="test-timetable") self.timetable.save() - Group.objects.create(celcat_name="L1 info s2 - CMA", timetable=self.timetable) - Group.objects.create(celcat_name="L1 info s2 - TDA2", timetable=self.timetable) - Group.objects.create(celcat_name="L1 info s2 - TPA21", timetable=self.timetable) + Group.objects.create(celcat_name="L1 info s2 CMA", timetable=self.timetable) + Group.objects.create(celcat_name="L1 info s2 TDA2", timetable=self.timetable) + Group.objects.create(celcat_name="L1 info s2 TPA21", timetable=self.timetable) - Group.objects.create(celcat_name="L1 info s2 - CMB", timetable=self.timetable) - 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 s2 CMB", timetable=self.timetable) + Group.objects.create(celcat_name="L1 info s2 TDB2", timetable=self.timetable) + Group.objects.create(celcat_name="L1 info s2 TPB21", 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) - tpa21 = Group.objects.get(celcat_name="L1 info s2 - TPA21", timetable=self.timetable) + 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) + tpa21 = Group.objects.get(celcat_name="L1 info s2 TPA21", timetable=self.timetable) - cmb = Group.objects.get(celcat_name="L1 info s2 - CMB", timetable=self.timetable) - 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) + cmb = Group.objects.get(celcat_name="L1 info s2 CMB", timetable=self.timetable) + 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.assertTrue(cma.corresponds_to(*tda2.group_info)) # CMA corresponds to TDA2 self.assertTrue(cma.corresponds_to(*tpa21.group_info)) # CMA corresponds to TPA21 @@ -99,30 +99,30 @@ class GroupTestCase(TestCase): self.assertFalse(tpa21.corresponds_to(*tdb2.group_info)) # TPA21 does not corresponds to TDB2 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) - tpa21 = Group.objects.get(name="L1 info s2 - TPA21", timetable=self.timetable) + cma = Group.objects.get(name="L1 info s2 CMA", timetable=self.timetable) + tda2 = Group.objects.get(name="L1 info s2 TDA2", timetable=self.timetable) + tpa21 = Group.objects.get(name="L1 info s2 TPA21", timetable=self.timetable) - cmb = Group.objects.get(name="L1 info s2 - CMB", timetable=self.timetable) - tdb2 = Group.objects.get(name="L1 info s2 - TDB2", timetable=self.timetable) - tpb21 = Group.objects.get(name="L1 info s2 - TPB21", timetable=self.timetable) + cmb = Group.objects.get(name="L1 info s2 CMB", timetable=self.timetable) + tdb2 = Group.objects.get(name="L1 info s2 TDB2", timetable=self.timetable) + tpb21 = Group.objects.get(name="L1 info s2 TPB21", 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") + 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") - self.assertEqual(cmb.celcat_name, "L1 info s2 - CMB") - self.assertEqual(tdb2.celcat_name, "L1 info s2 - TDB2") - self.assertEqual(tpb21.celcat_name, "L1 info s2 - TPB21") + self.assertEqual(cmb.celcat_name, "L1 info s2 CMB") + self.assertEqual(tdb2.celcat_name, "L1 info s2 TDB2") + self.assertEqual(tpb21.celcat_name, "L1 info s2 TPB21") 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) - tpa21 = Group.objects.get(celcat_name="L1 info s2 - TPA21", timetable=self.timetable) + 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) + tpa21 = Group.objects.get(celcat_name="L1 info s2 TPA21", timetable=self.timetable) - cmb = Group.objects.get(celcat_name="L1 info s2 - CMB", timetable=self.timetable) - 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) + cmb = Group.objects.get(celcat_name="L1 info s2 CMB", timetable=self.timetable) + 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 s2", "A", None, None)) self.assertEqual(tda2.group_info, (self.timetable.id, "L1 info s2", "A", 2, None)) @@ -49,25 +49,26 @@ def group_courses(courses): def parse_group(name): # Explication de la regex # - # ^(.+?)\s*\-\s*(((CM)(\w))|((TD)(\w)(\d))|((TP)(\w)(\d)(\d)))$ - # ^ début de la ligne - # (.+?) correspond à au moins un caractère - # \s* zéro, un ou plusieurs espaces - # \- un tiret - # \s* zéro, un ou plusieurs espaces - # (((CM)(\w))| correspond à CM suivi d'une lettre ou... - # ((TD)(\w)(\d))| ... à TD suivi d'une lettre et d'un chiffre ou... - # ((TP)(\w)(\d)(\d))) ... à TP suivi d'une lettre et de deux chiffres - # $ fin de la ligne - group_regex = re.compile("^(.+?)\s*\-\s*(((CM)(\w))|((TD)(\w)(\d))|((TP)(\w)(\d)(\d)))$") + # ^([\w ]+?)(\s*(((CM)(\w))|((TD)(\w)(\d))|((TP)(\w)(\d)(\d))))?$ + # ^ début de la ligne + # ([\w ]+?) correspond à au moins un caractère + # (\s* zéro, un ou plusieurs espaces + # (((CM)(\w))| correspond à CM suivi d'une lettre ou... + # ((TD)(\w)(\d))| ... à TD suivi d’une lettre et d'un chiffre ou... + # ((TP)(\w)(\d)(\d))) ... à TP suivi d’une lettre et de deux chiffres + # )? groupe optionel + # $ fin de la ligne + group_regex = re.compile("^([\w ]+?)(\s*(((CM)(\w))|((TD)(\w)(\d))|((TP)(\w)(\d)(\d))))?$") search = group_regex.search(name) if search is None: - return None, None, None, None + return name, None, None, None parts = search.groups(0) - if parts[3] == "CM": - return parts[0], parts[4], None, None - elif parts[6] == "TD": - return parts[0], parts[7], parts[8], None - elif parts[10] == "TP": - return parts[0], parts[11], parts[12], parts[13] + if parts[1] == 0: + return parts[0], None, None, None + elif parts[4] == "CM": + return parts[0], parts[5], None, None + elif parts[7] == "TD": + return parts[0], parts[8], parts[9], None + elif parts[11] == "TP": + return parts[0], parts[12], parts[13], parts[14] @@ -24,8 +24,8 @@ from .models import Timetable, LastUpdate, Group, Subscription, Course from .utils import get_current_week, get_week, group_courses def index(request): - timetables = Timetable.objects.all().order_by("name") - groups = Group.objects.filter(tp__isnull=False).order_by("name") + timetables = Timetable.objects.order_by("name") + groups = Group.objects.get_relevant_groups().order_by("name") year, week = get_current_week() start, _ = get_week(year, week) @@ -74,7 +74,7 @@ def subscribe(request, timetable_slug, group_slug, year, week): template = loader.get_template("mail/mail_confirm.txt") context = Context({"group": group, "admins": settings.ADMINS, "token": subscription.token, "domain": settings.DEFAULT_DOMAIN}) - send_mail("Confirmation de l'abonnemenent", template.render(context), settings.DEFAULT_FROM_EMAIL, [request.POST["email"]]) + send_mail("Confirmation de l’abonnemenent", template.render(context), settings.DEFAULT_FROM_EMAIL, [request.POST["email"]]) return redirect("timetable", timetable_slug=timetable_slug, group_slug=group_slug, year=year, week=int(week)) else: |