aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAlban Gruin2017-09-04 22:13:31 +0200
committerAlban Gruin2017-09-04 22:13:31 +0200
commitab9df7a174e2debbf9c644c8ec3e2127eeeaa3cf (patch)
treebf9510823644aa1c5dba0ab39277633c0f18ad49
parentce0ed9347adde9ca80c11efe79766c966d5749ba (diff)
parent4b3fc9e4c41e2247bf0fa0fe2629b2b57fc174b0 (diff)
Merge branch 'stable/0.y.z' into prod/pa1ch/0.y.zv0.8.0-pa1chprod/pa1ch/0.8.z
-rw-r--r--README.md16
-rw-r--r--admin.py2
-rw-r--r--management/commands/_private.py22
-rw-r--r--management/commands/listtimetables.py1
-rw-r--r--management/commands/timetables.py8
-rw-r--r--models.py33
-rw-r--r--templates/mail/mail_confirm.txt6
-rw-r--r--templates/mail/mail_footer.txt4
-rw-r--r--templates/mail/mail_timetable.txt2
-rw-r--r--templates/mail/mail_unsubscribed.txt2
-rw-r--r--templates/subscribe.html10
-rw-r--r--templates/timetable.html4
-rw-r--r--tests.py76
-rw-r--r--utils.py37
-rw-r--r--views.py6
15 files changed, 128 insertions, 101 deletions
diff --git a/README.md b/README.md
index ece8601..d3cbb6c 100644
--- a/README.md
+++ b/README.md
@@ -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
diff --git a/admin.py b/admin.py
index f818865..2408623 100644
--- a/admin.py
+++ b/admin.py
@@ -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)))
diff --git a/models.py b/models.py
index 478b48f..21b08f5 100644
--- a/models.py
+++ b/models.py
@@ -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 }} &ndash; {{ 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 %} &ndash; 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 %}
diff --git a/tests.py b/tests.py
index b6b9a70..a3ae55e 100644
--- a/tests.py
+++ b/tests.py
@@ -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))
diff --git a/utils.py b/utils.py
index cff4c9c..8630036 100644
--- a/utils.py
+++ b/utils.py
@@ -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]
diff --git a/views.py b/views.py
index 22c08ac..176f174 100644
--- a/views.py
+++ b/views.py
@@ -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: