From 8e2138793d147a832d53a829815bdae7292fb088 Mon Sep 17 00:00:00 2001 From: Alban Gruin Date: Tue, 7 Nov 2017 19:41:48 +0100 Subject: Un sous-groupe nul fait crasher la page des emplois du temps car la requête pour savoir si il y a des sous-groupes ne marche pas avec une chaîne de caractère commençant par None. Suppression de la possibilité d’avoir un sous-groupe nul, valeur par défaut à "". --- models.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/models.py b/models.py index 3af1c06..0804a38 100644 --- a/models.py +++ b/models.py @@ -71,7 +71,7 @@ class Timetable(SlugModel): class GroupManager(Manager): def get_parents(self, group): - groups_criteria = Q(subgroup__isnull=True) | Q(subgroup__startswith=group.subgroup) | \ + 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)]) @@ -99,8 +99,7 @@ class Group(models.Model): verbose_name="emploi du temps") mention = models.CharField(max_length=128) - subgroup = models.CharField(max_length=16, verbose_name="sous-groupe", - null=True) + subgroup = models.CharField(max_length=16, verbose_name="sous-groupe", default="") slug = models.SlugField(max_length=64, default="") @@ -129,6 +128,9 @@ class Group(models.Model): self.slug = slugify(self.name) self.mention, self.subgroup = parse_group(self.name) + if self.subgroup is None: + self.subgroup = "" + super(Group, self).save() -- cgit v1.2.1 From 5c2bc37ed5d8353ea0ee1d9e5f852a1f39ac235f Mon Sep 17 00:00:00 2001 From: Alban Gruin Date: Tue, 7 Nov 2017 20:35:12 +0100 Subject: Version 0.11.1 --- __init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/__init__.py b/__init__.py index 1740b96..7371b39 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 . -VERSION = "0.11" +VERSION = "0.11.1" __version__ = VERSION default_app_config = "edt.apps.EdtConfig" -- cgit v1.2.1 From 1409be677bfaf3d913dd536b082b43f3c5373d98 Mon Sep 17 00:00:00 2001 From: Axel FEFEU Date: Wed, 8 Nov 2017 10:37:14 +0100 Subject: Upload files to '' Mise en page de README.md --- README.md | 66 +++++++++++++++++++++++++++++++++++++++------------------------ 1 file changed, 41 insertions(+), 25 deletions(-) diff --git a/README.md b/README.md index 20a2cbd..7534640 100644 --- a/README.md +++ b/README.md @@ -6,16 +6,16 @@ Parce que les emplois du temps Celcat sont peu lisibles et peuvent facilement fa ## Comment faire tourner celcatsanitizer chez moi ? celcatsanitizer est écrit en Python 3. Il dépend des bibliothèques suivantes : - * Django 1.11 - * requests - * BeautifulSoup4 - * icalendar + - [Django 1.11](https://www.djangoproject.com/) + * [requests](http://docs.python-requests.org/en/master/) + * [BeautifulSoup4](https://www.crummy.com/software/BeautifulSoup/bs4/doc/) + * [icalendar](https://icalendar.readthedocs.io/en/latest/) -Pour installer celcatsanitizer, il est possible d’utiliser git. +Pour installer celcatsanitizer, il est possible d’utiliser [git](https://git-scm.com/). -Pour tester celcatsanitizer, il est recommandé d’utiliser SQLite ou PostgreSQL. +Pour tester celcatsanitizer, il est recommandé d’utiliser [SQLite](https://www.sqlite.org/) ou [PostgreSQL](https://www.postgresql.org/?&). -Pour la production, il est recommandé d’utiliser PostgreSQL (avec le driver psycopg2) et de mettre le tout dans un environnement virtuel. +Pour la production, il est recommandé d’utiliser PostgreSQL (avec le driver [psycopg2](http://initd.org/psycopg/docs/install.html#binary-install-from-pypi)) et de mettre le tout dans un environnement virtuel. 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. @@ -25,35 +25,44 @@ Il est préférable d’utiliser un environnement virtuel, mais ce n’est pas o #### Création de l’environnement virtuel Déplacez-vous dans le répertoire souhaité, installez l’environnement virtuel, et activez-le : -> $ virtualenv -p python3 celcatsanitizer - -> $ cd celcatsanitizer - -> $ source bin/activate +```bash +$ virtualenv -p python3 celcatsanitizer +$ cd celcatsanitizer +$ source bin/activate +``` Il est possible que votre version de pip soit ancienne. Si vous le souhaitez, mettez ce programme à jour : -> $ pip install --upgrade pip +```bash +$ pip install --upgrade pip +``` #### Installation des dépendances -> $ pip install requests django beautifulsoup4 icalendar +```bash +$ pip install requests django beautifulsoup4 icalendar +``` Si vous utilisez PostgreSQL, vous allez avoir besoin du driver psycopg2 : -> $ pip install psycopg2 +```bash +$ pip install psycopg2 +``` SQLite n’a pas besoin de driver. #### Création du répertoire Django -> $ django-admin startproject celcatsanitizer - -> $ cd celcatsanitizer +```bash +$ django-admin startproject celcatsanitizer +$ cd celcatsanitizer +``` #### Récupération des sources de celcatsanitizer -> $ git clone https://git.pa1ch.fr/alban/celcatsanitizer.git edt +```bash +$ git clone https://git.pa1ch.fr/alban/celcatsanitizer.git edt +``` Pour la production, il est recommandé d’utiliser une version stable, accessibles à travers les tags git. @@ -91,21 +100,26 @@ Dans le fichier celcatsanitizer/urls.py, importez la fonction django.conf.urls.i ##### Génération de la base de données Générer les migrations de celcatsanitizer, puis appliquez-les : -> $ ./manage.py makemigrations edt - -> $ ./manage.py migrate +```bash +$ ./manage.py makemigrations edt +$ ./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.11/ref/settings/#std:setting-STATIC_ROOT) de la configuration de Django, puis exécuter la commande suivante : -> $ ./manage.py collectstatic +```bash +$ ./manage.py collectstatic +``` Cette étape est inutile si vous êtes en mode de déboguage. ### Lancement de celcatsanitizer Si vous êtes en mode de déboguage, lancez le serveur de cette manière : -> $ ./manage.py runserver +```bash +$ ./manage.py runserver +``` Si vous êtes en production, il n’est pas recommandé d’utiliser ce serveur. Exécutez Django avec le module mod_wsgi d’Apache, ou avec un serveur [gunicorn](http://gunicorn.org/) derrière nginx. @@ -113,7 +127,9 @@ Si vous êtes en production, il n’est pas recommandé d’utiliser ce serveur. #### Administrateur Pour avoir accès à l’interface d’administration, vous devez créer un utilisateur avec les droits administrateur. Pour cela, exécutez la commande suivante : -> $ ./manage.py createsuperuser +```bash +$ ./manage.py createsuperuser +``` Renseignez ensuite votre nom d’utilisateur, mot de passe et adresse email au fur et à mesure. -- cgit v1.2.1 From c4dc711bf89c746adfe89ba5e254629972c798b8 Mon Sep 17 00:00:00 2001 From: Axel FEFEU Date: Wed, 8 Nov 2017 10:38:28 +0100 Subject: Mise à jour du README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 7534640..e7e4bfb 100644 --- a/README.md +++ b/README.md @@ -6,7 +6,7 @@ Parce que les emplois du temps Celcat sont peu lisibles et peuvent facilement fa ## Comment faire tourner celcatsanitizer chez moi ? celcatsanitizer est écrit en Python 3. Il dépend des bibliothèques suivantes : - - [Django 1.11](https://www.djangoproject.com/) + * [Django 1.11](https://www.djangoproject.com/) * [requests](http://docs.python-requests.org/en/master/) * [BeautifulSoup4](https://www.crummy.com/software/BeautifulSoup/bs4/doc/) * [icalendar](https://icalendar.readthedocs.io/en/latest/) -- cgit v1.2.1 From 0c2937bab77280beaabe7d53a77c848848845950 Mon Sep 17 00:00:00 2001 From: Alban Gruin Date: Thu, 16 Nov 2017 11:20:59 +0100 Subject: Par défaut, la date de dernière mise à jour des emplois du temps est la date actuelle --- models.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/models.py b/models.py index 0804a38..b9f1ba7 100644 --- a/models.py +++ b/models.py @@ -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, default=timezone.now) def __str__(self): return self.year.name + " " + self.name -- cgit v1.2.1 From 4fdb7f6263d6463ac2099c383b1221789d28e42e Mon Sep 17 00:00:00 2001 From: Alban Gruin Date: Thu, 16 Nov 2017 13:34:30 +0100 Subject: Correction des erreurs lorsqu’un groupe n’a pas de sous-groupe --- models.py | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/models.py b/models.py index b9f1ba7..a90e9da 100644 --- a/models.py +++ b/models.py @@ -72,10 +72,12 @@ 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) -- cgit v1.2.1 From d1f4287a364e8bfee86a0a55c9514edad0abbd4e Mon Sep 17 00:00:00 2001 From: Alban Gruin Date: Thu, 16 Nov 2017 13:51:00 +0100 Subject: Correction d’un crash lorqu’on force une mise à jour sur tout l’emploi du temps Lorsqu’on effectue cette opération, la date à laquelle est effectuée la mise à jour est à None. get_event() effectue donc une comparaison entre la date du cours et un objet nul, ce qui est impossible. Cela provoque un crash. Le correctif consiste à dire que si la date de début de la mise à jour est nulle, alors le cours n’est pas ignoré, avant d’effectuer la comparaison décrite ci-dessus. --- management/commands/_private.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 -- cgit v1.2.1 From cc64acb64af30771c523a1e0794f0976df14769b Mon Sep 17 00:00:00 2001 From: Alban Gruin Date: Thu, 16 Nov 2017 14:14:06 +0100 Subject: Correction d’une erreur de parenthésage dans get_parents() Le + 1 doit venir _après_ len(), pas pendant… --- models.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/models.py b/models.py index a90e9da..4b86961 100644 --- a/models.py +++ b/models.py @@ -77,7 +77,7 @@ class GroupManager(Manager): 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))]) + for i in range(1, len(group.subgroup) + 1)]) return self.get_queryset().filter(groups_criteria, mention=group.mention, timetable=group.timetable) -- cgit v1.2.1 From 9262be98897f29e52f489e3353cb752d9b9d8129 Mon Sep 17 00:00:00 2001 From: Alban Gruin Date: Thu, 16 Nov 2017 14:35:19 +0100 Subject: Revert "Par défaut, la date de dernière mise à jour des emplois du temps est" This reverts commit 0c2937bab77280beaabe7d53a77c848848845950. --- models.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/models.py b/models.py index 4b86961..db81d5b 100644 --- a/models.py +++ b/models.py @@ -57,8 +57,7 @@ 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, default=timezone.now) + last_update_date = models.DateTimeField(verbose_name="dernière mise à jour Celcat", null=True) def __str__(self): return self.year.name + " " + self.name -- cgit v1.2.1 From 98d62baa698a69b031f28a27efc76a47b7456c8a Mon Sep 17 00:00:00 2001 From: Alban Gruin Date: Thu, 16 Nov 2017 14:38:24 +0100 Subject: Mettre une valeur de dernière mise à jour dans l’interface d’administration n’est plus obligatoire. --- models.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/models.py b/models.py index db81d5b..6d06793 100644 --- a/models.py +++ b/models.py @@ -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 -- cgit v1.2.1 From b2c14a2db9b79dca007db5e32aefbf76ebd05fd1 Mon Sep 17 00:00:00 2001 From: Alban Gruin Date: Thu, 16 Nov 2017 18:59:48 +0100 Subject: Version 0.11.2 --- __init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/__init__.py b/__init__.py index 7371b39..f6ef57e 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 . -VERSION = "0.11.1" +VERSION = "0.11.2" __version__ = VERSION default_app_config = "edt.apps.EdtConfig" -- cgit v1.2.1 From 1676687e0aa43cc264119a58176141bcf7401efe Mon Sep 17 00:00:00 2001 From: Alban Gruin Date: Fri, 17 Nov 2017 15:59:57 +0100 Subject: Utilisation du auto-fill-mode --- README.md | 107 +++++++++++++++++++++++++++++++++++++++++++++----------------- 1 file changed, 78 insertions(+), 29 deletions(-) diff --git a/README.md b/README.md index e7e4bfb..7dc9d5c 100644 --- a/README.md +++ b/README.md @@ -1,29 +1,45 @@ # celcatsanitizer -celcatsanitizer est un système qui permet de récupérer des emplois du temps Celcat au format XML pour les afficher correctement. +celcatsanitizer est un système qui permet de récupérer des emplois du +temps Celcat au format XML pour les afficher correctement. ## Pourquoi ? -Parce que les emplois du temps Celcat sont peu lisibles et peuvent facilement faire planter un navigateur, à cause du surplus d’informations affichées. +Parce que les emplois du temps Celcat sont peu lisibles et peuvent +facilement faire planter un navigateur, à cause du surplus +d’informations affichées. ## Comment faire tourner celcatsanitizer chez moi ? -celcatsanitizer est écrit en Python 3. Il dépend des bibliothèques suivantes : +celcatsanitizer est écrit en Python 3. Il dépend des bibliothèques +suivantes : * [Django 1.11](https://www.djangoproject.com/) * [requests](http://docs.python-requests.org/en/master/) * [BeautifulSoup4](https://www.crummy.com/software/BeautifulSoup/bs4/doc/) * [icalendar](https://icalendar.readthedocs.io/en/latest/) -Pour installer celcatsanitizer, il est possible d’utiliser [git](https://git-scm.com/). +Pour installer celcatsanitizer, il est possible d’utiliser +[git](https://git-scm.com/). -Pour tester celcatsanitizer, il est recommandé d’utiliser [SQLite](https://www.sqlite.org/) ou [PostgreSQL](https://www.postgresql.org/?&). +Pour tester celcatsanitizer, il est recommandé d’utiliser +[SQLite](https://www.sqlite.org/) ou +[PostgreSQL](https://www.postgresql.org/?&). -Pour la production, il est recommandé d’utiliser PostgreSQL (avec le driver [psycopg2](http://initd.org/psycopg/docs/install.html#binary-install-from-pypi)) et de mettre le tout dans un environnement virtuel. +Pour la production, il est recommandé d’utiliser PostgreSQL (avec le +driver +[psycopg2](http://initd.org/psycopg/docs/install.html#binary-install-from-pypi)) +et de mettre le tout dans un environnement virtuel. -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. +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. +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. #### Création de l’environnement virtuel -Déplacez-vous dans le répertoire souhaité, installez l’environnement virtuel, et activez-le : +Déplacez-vous dans le répertoire souhaité, installez l’environnement +virtuel, et activez-le : ```bash $ virtualenv -p python3 celcatsanitizer @@ -31,7 +47,8 @@ $ cd celcatsanitizer $ source bin/activate ``` -Il est possible que votre version de pip soit ancienne. Si vous le souhaitez, mettez ce programme à jour : +Il est possible que votre version de pip soit ancienne. Si vous le +souhaitez, mettez ce programme à jour : ```bash $ pip install --upgrade pip @@ -43,7 +60,8 @@ $ pip install --upgrade pip $ pip install requests django beautifulsoup4 icalendar ``` -Si vous utilisez PostgreSQL, vous allez avoir besoin du driver psycopg2 : +Si vous utilisez PostgreSQL, vous allez avoir besoin du driver +psycopg2 : ```bash $ pip install psycopg2 @@ -64,38 +82,57 @@ $ cd celcatsanitizer $ git clone https://git.pa1ch.fr/alban/celcatsanitizer.git edt ``` -Pour la production, il est recommandé d’utiliser une version stable, accessibles à travers les tags git. +Pour la production, il est recommandé d’utiliser une version stable, +accessibles à travers les tags git. #### Configuration de Django -Dans le fichier celcatsanitizer/settings.py, vous devrez renseigner plusieurs informations. +Dans le fichier celcatsanitizer/settings.py, vous devrez renseigner +plusieurs informations. ##### Configuration des administrateurs -[Vous pouvez retrouver la documentation de la variable ADMIN sur le site de Django.](https://docs.djangoproject.com/fr/1.11/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.11/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.11/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. +Si jamais vous utiliser Django en production, vous **devez** mettre la +variable DEBUG à False. ##### Ajout de celcatsanitizer dans la liste des applications Django -Ajoutez la chaine de caractère « edt » à la fin de la liste INSTALLED_APPS. +Ajoutez la chaine de caractère « edt » à la fin de la liste +INSTALLED_APPS. ##### Configuration des flatpages -celcatsanitizer utilise les flatpages pour rendre les pages « contact » et « à propos ». Vous pouvez retrouver le guide d’installation sur [le site de Django](https://docs.djangoproject.com/fr/1.11/ref/contrib/flatpages/#installation). Effectuez uniquement les deux premières étapes, celcatsanitizer enregistre déjà une route pour les pages statiques, et la commande de l’étape 4 sera effectuée plus loin. +celcatsanitizer utilise les flatpages pour rendre les pages +« contact » et « à propos ». Vous pouvez retrouver le guide +d’installation sur [le site de +Django](https://docs.djangoproject.com/fr/1.11/ref/contrib/flatpages/#installation). Effectuez +uniquement les deux premières étapes, celcatsanitizer enregistre déjà +une route pour les pages statiques, et la commande de l’étape 4 sera +effectuée plus loin. ##### Ajout du processeur de contexte de celcatsanitizer Cette étape est fortement recommandée, mais pas obligatoire. -Rajoutez la chaine de caractères 'edt.views.ctx_processor' à la liste 'context_processors' dans la variable « TEMPLATES ». +Rajoutez la chaine de caractères 'edt.views.ctx_processor' à la liste +'context_processors' dans la variable « TEMPLATES ». ##### Ajout des URLs de celcatsanitizer -Dans le fichier celcatsanitizer/urls.py, importez la fonction django.conf.urls.include, et ajoutez url(r'^', include("edt.urls")) à la **fin** de la liste urlspatterns. +Dans le fichier celcatsanitizer/urls.py, importez la fonction +django.conf.urls.include, et ajoutez url(r'^', include("edt.urls")) à +la **fin** de la liste urlspatterns. ##### Génération de la base de données Générer les migrations de celcatsanitizer, puis appliquez-les : @@ -106,7 +143,10 @@ $ ./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.11/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 : ```bash $ ./manage.py collectstatic @@ -115,29 +155,38 @@ $ ./manage.py collectstatic Cette étape est inutile si vous êtes en mode de déboguage. ### Lancement de celcatsanitizer -Si vous êtes en mode de déboguage, lancez le serveur de cette manière : +Si vous êtes en mode de déboguage, lancez le serveur de cette +manière : ```bash $ ./manage.py runserver ``` -Si vous êtes en production, il n’est pas recommandé d’utiliser ce serveur. Exécutez Django avec le module mod_wsgi d’Apache, ou avec un serveur [gunicorn](http://gunicorn.org/) derrière nginx. +Si vous êtes en production, il n’est pas recommandé d’utiliser ce +serveur. Exécutez Django avec le module mod_wsgi d’Apache, ou avec un +serveur [gunicorn](http://gunicorn.org/) derrière nginx. ### Configuration de celcatsanitizer #### Administrateur -Pour avoir accès à l’interface d’administration, vous devez créer un utilisateur avec les droits administrateur. Pour cela, exécutez la commande suivante : +Pour avoir accès à l’interface d’administration, vous devez créer un +utilisateur avec les droits administrateur. Pour cela, exécutez la +commande suivante : ```bash $ ./manage.py createsuperuser ``` -Renseignez ensuite votre nom d’utilisateur, mot de passe et adresse email au fur et à mesure. +Renseignez ensuite votre nom d’utilisateur, mot de passe et adresse +email au fur et à mesure. #### Pages statiques -Comme indiqué plus haut, celcatsanitizer utilise l’application flatpages de Django. +Comme indiqué plus haut, celcatsanitizer utilise l’application +flatpages de Django. -Si vous êtes en production, vous devez changer le site de base (« example.com ») par le site de celcatsanitizer. +Si vous êtes en production, vous devez changer le site de base +(« example.com ») par le site de celcatsanitizer. Vous devez ensuite rajouter les pages /a-propos/ et /contact/. -Vous pouvez effectuer tout ça à partir de l’interface d’administration de Django. +Vous pouvez effectuer tout ça à partir de l’interface d’administration +de Django. -- cgit v1.2.1 From ffffa1cef4e0c2f1e617dc74a57c5045325e57aa Mon Sep 17 00:00:00 2001 From: Alban Gruin Date: Fri, 24 Nov 2017 19:28:35 +0100 Subject: Correction d’un bogue qui empêchait l’affichage des groupes si deux groupes ont le même nom mais pas le même semestre. --- models.py | 7 ++++--- utils.py | 26 +++++++++++++------------- 2 files changed, 17 insertions(+), 16 deletions(-) diff --git a/models.py b/models.py index 6d06793..4c3f3c3 100644 --- a/models.py +++ b/models.py @@ -83,9 +83,10 @@ class GroupManager(Manager): 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")) \ + .order_by().values("mention").annotate(c=Count("*")).values("c") return self.get_queryset().filter(*args, timetable=timetable, hidden=False, **criteria) \ .annotate(nbsub=Subquery(sub, output_field=models.IntegerField())) \ diff --git a/utils.py b/utils.py index 5d55eca..785a21d 100644 --- a/utils.py +++ b/utils.py @@ -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+ 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+\(.+\))?$") 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""" -- cgit v1.2.1 From e9552f7e5545623c8cb6ba071f80aa185affa736 Mon Sep 17 00:00:00 2001 From: Alban Gruin Date: Fri, 24 Nov 2017 19:31:39 +0100 Subject: Adaptation des tests --- tests.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/tests.py b/tests.py index 051c733..798574d 100644 --- a/tests.py +++ b/tests.py @@ -128,10 +128,10 @@ 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")) + 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", "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(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")) -- cgit v1.2.1 From 74f7317251ec068e89ee02d386b902d53f366840 Mon Sep 17 00:00:00 2001 From: Alban Gruin Date: Fri, 24 Nov 2017 20:01:28 +0100 Subject: Re-changement de la regex pour mieux gérer les cas où il n’y a ni semestre ni sous-groupe mais qu’il y a une précision --- utils.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/utils.py b/utils.py index 785a21d..e1b9867 100644 --- a/utils.py +++ b/utils.py @@ -56,10 +56,10 @@ def parse_group(name): # ((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 + # (\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+\(.+\))?$") + 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 -- cgit v1.2.1 From b28630da06a6c1286d5cd34d6437d8dd19d6f298 Mon Sep 17 00:00:00 2001 From: Alban Gruin Date: Fri, 24 Nov 2017 20:02:21 +0100 Subject: Ajout de nouveaux tests pour ces nouveaux cas --- models.py | 3 ++- tests.py | 26 ++++++++++++++++++++++++++ 2 files changed, 28 insertions(+), 1 deletion(-) diff --git a/models.py b/models.py index 4c3f3c3..14270cf 100644 --- a/models.py +++ b/models.py @@ -116,7 +116,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 diff --git a/tests.py b/tests.py index 798574d..a509f38 100644 --- a/tests.py +++ b/tests.py @@ -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,6 +150,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.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")) @@ -135,3 +159,5 @@ class GroupTestCase(TestCase): 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(general.group_info, (self.timetable.id, "L1 info ", "")) -- cgit v1.2.1 From d9b78963e8526b803f1165c79ae2f97c6a36016b Mon Sep 17 00:00:00 2001 From: Alban Gruin Date: Fri, 24 Nov 2017 21:30:53 +0100 Subject: Version 0.11.3 --- __init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/__init__.py b/__init__.py index f6ef57e..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 . -VERSION = "0.11.2" +VERSION = "0.11.3" __version__ = VERSION default_app_config = "edt.apps.EdtConfig" -- cgit v1.2.1 From adad24ace9ab47f12d43dca494b358544beefa00 Mon Sep 17 00:00:00 2001 From: Alban Gruin Date: Fri, 24 Nov 2017 23:43:53 +0100 Subject: Correction d’un crash lorsqu’un groupe a des enfants ayant une mention différente. L’annotation Count() renvoie plusieurs valeurs différentes, alors que Subquery n’en accepte qu’une seule. Corrigé en rajoutant une valeur statique (0) par annotation et en regroupant en se basant sur cette valeur. --- models.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/models.py b/models.py index 14270cf..40406e3 100644 --- a/models.py +++ b/models.py @@ -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 @@ -86,7 +86,8 @@ class GroupManager(Manager): sub = self.get_queryset().filter(timetable=timetable, mention__startswith=OuterRef("mention"), subgroup__startswith=OuterRef("subgroup")) \ - .order_by().values("mention").annotate(c=Count("*")).values("c") + .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())) \ -- cgit v1.2.1 From 68248f075dc750d7de5c0d060721ecce8f988217 Mon Sep 17 00:00:00 2001 From: Alban Gruin Date: Sat, 25 Nov 2017 13:17:35 +0100 Subject: Suppression de l’association automatique des groupes --- views.py | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/views.py b/views.py index c0c63cb..97d3048 100644 --- a/views.py +++ b/views.py @@ -61,7 +61,7 @@ def group_list_common(request, timetable, groups): def group_list(request, year_slug, timetable_slug): timetable = get_object_or_404(Timetable, year__slug=year_slug, slug=timetable_slug) - groups = Group.objects.get_relevant_groups(timetable) + groups = Group.objects.filter(timetable=timetable).order_by("name") return group_list_common(request, timetable, groups) def timetable(request, year_slug, timetable_slug, group_slug, year=None, week=None): @@ -79,12 +79,6 @@ def timetable(request, year_slug, timetable_slug, group_slug, year=None, week=No timetable = get_object_or_404(Timetable, year__slug=year_slug, slug=timetable_slug) group = get_object_or_404(Group, slug=group_slug, timetable=timetable) - if Group.objects.filter(timetable=timetable, mention=group.mention, - subgroup__startswith=group.subgroup).count() > 1: - subgroups = Group.objects.get_relevant_groups(timetable, mention=group.mention, - subgroup__startswith=group.subgroup) - return group_list_common(request, timetable, subgroups) - courses = Course.objects.get_courses_for_group(group, begin__gte=start, begin__lt=end) if courses.count() == 0 and provided_week: raise Http404 -- cgit v1.2.1 From b99dbd566fba7f3c18d3832edeb8e019abcc8c87 Mon Sep 17 00:00:00 2001 From: Alban Gruin Date: Sat, 25 Nov 2017 13:25:51 +0100 Subject: Ne sélectionne plus les sous-groupes --- models.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/models.py b/models.py index 40406e3..40849c6 100644 --- a/models.py +++ b/models.py @@ -72,7 +72,7 @@ class Timetable(SlugModel): class GroupManager(Manager): def get_parents(self, group): - groups_criteria = Q(subgroup="") | Q(subgroup__startswith=group.subgroup) + groups_criteria = Q(subgroup="") if len(group.subgroup) != 0: groups_criteria |= reduce(lambda x, y: x | y, -- cgit v1.2.1 From 7f146b306760e1a70e8e1a79d58fc967d05fe194 Mon Sep 17 00:00:00 2001 From: Alban Gruin Date: Sat, 25 Nov 2017 13:30:42 +0100 Subject: Suppression de get_relevant_groups --- models.py | 14 +------------- 1 file changed, 1 insertion(+), 13 deletions(-) diff --git a/models.py b/models.py index 40849c6..9838377 100644 --- a/models.py +++ b/models.py @@ -16,8 +16,7 @@ from functools import reduce from django.db import models -from django.db.models import Count, Manager, Q, Subquery, Value -from django.db.models.expressions import OuterRef +from django.db.models import Count, Manager, Q from django.db.models.functions import ExtractWeek, ExtractYear from django.utils import timezone from django.utils.text import slugify @@ -82,17 +81,6 @@ class GroupManager(Manager): 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__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())) \ - .filter(Q(nbsub=1) | Q(nbsub__isnull=True)).order_by("name") - class Group(models.Model): objects = GroupManager() -- cgit v1.2.1 From d9e0cba8c24978150e66b9ccd7ea67aa50a5f22d Mon Sep 17 00:00:00 2001 From: Alban Gruin Date: Sat, 25 Nov 2017 14:01:54 +0100 Subject: Un groupe parent ne correspond plus à un groupe enfant Modification des tests --- models.py | 3 +-- tests.py | 60 ++++++++++++++++++++++++++++++------------------------------ 2 files changed, 31 insertions(+), 32 deletions(-) diff --git a/models.py b/models.py index 9838377..bde27a7 100644 --- a/models.py +++ b/models.py @@ -101,8 +101,7 @@ class Group(models.Model): def corresponds_to(self, timetable_id, mention, subgroup): subgroup_corresponds = True if self.subgroup is not None and subgroup is not None: - subgroup_corresponds = subgroup.startswith(self.subgroup) or \ - self.subgroup.startswith(subgroup) + subgroup_corresponds = self.subgroup.startswith(subgroup) return self.timetable.id == timetable_id and \ (self.mention.startswith(mention) or \ diff --git a/tests.py b/tests.py index a509f38..e7af6a8 100644 --- a/tests.py +++ b/tests.py @@ -82,36 +82,36 @@ class GroupTestCase(TestCase): 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 - - self.assertTrue(cmb.corresponds_to(*tdb2.group_info)) # CMB corresponds to TDB2 - self.assertTrue(cmb.corresponds_to(*tpb21.group_info)) # CMB corresponds to TPB21 - self.assertTrue(tdb2.corresponds_to(*tpb21.group_info)) # TDB2 corresponds to TPB21 - - self.assertFalse(cmb.corresponds_to(*tda2.group_info)) # CMB does not corresponds to TDA2 - self.assertFalse(cmb.corresponds_to(*tpa21.group_info)) # CMB does not corresponds to TPA21 - self.assertFalse(tdb2.corresponds_to(*tpa21.group_info)) # TDB2 does not corresponds to TPA21 - - self.assertTrue(tda2.corresponds_to(*cma.group_info)) # TDA2 corresponds to CMA - self.assertTrue(tpa21.corresponds_to(*cma.group_info)) # TPA21 corresponds to CMA - self.assertTrue(tpa21.corresponds_to(*tda2.group_info)) # TPA21 corresponds to TDA2 - - self.assertTrue(tdb2.corresponds_to(*cmb.group_info)) # TDB2 corresponds to CMB - self.assertTrue(tpb21.corresponds_to(*cmb.group_info)) # TPB21 corresponds to CMB - self.assertTrue(tpb21.corresponds_to(*tdb2.group_info)) # TPB21 corresponds to TDB2 - - self.assertFalse(tda2.corresponds_to(*cmb.group_info)) # TDA2 does not corresponds to CMB - 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.assertFalse(cma.corresponds_to(*tda2.group_info)) + self.assertFalse(cma.corresponds_to(*tpa21.group_info)) + self.assertFalse(tda2.corresponds_to(*tpa21.group_info)) + + self.assertFalse(cmb.corresponds_to(*tdb2.group_info)) + self.assertFalse(cmb.corresponds_to(*tpb21.group_info)) + self.assertFalse(tdb2.corresponds_to(*tpb21.group_info)) + + self.assertFalse(cmb.corresponds_to(*tda2.group_info)) + self.assertFalse(cmb.corresponds_to(*tpa21.group_info)) + self.assertFalse(tdb2.corresponds_to(*tpa21.group_info)) + + self.assertTrue(tda2.corresponds_to(*cma.group_info)) + self.assertTrue(tpa21.corresponds_to(*cma.group_info)) + self.assertTrue(tpa21.corresponds_to(*tda2.group_info)) + + self.assertTrue(tdb2.corresponds_to(*cmb.group_info)) + self.assertTrue(tpb21.corresponds_to(*cmb.group_info)) + self.assertTrue(tpb21.corresponds_to(*tdb2.group_info)) + + self.assertFalse(tda2.corresponds_to(*cmb.group_info)) + self.assertFalse(tpa21.corresponds_to(*cmb.group_info)) + self.assertFalse(tpa21.corresponds_to(*tdb2.group_info)) + + self.assertFalse(general.corresponds_to(*cma.group_info)) + self.assertFalse(general.corresponds_to(*cmb.group_info)) + self.assertFalse(general.corresponds_to(*tda2.group_info)) + self.assertFalse(general.corresponds_to(*tdb2.group_info)) + self.assertFalse(general.corresponds_to(*tpa21.group_info)) + self.assertFalse(general.corresponds_to(*tpb21.group_info)) self.assertTrue(cma.corresponds_to(*general.group_info)) self.assertTrue(cmb.corresponds_to(*general.group_info)) -- cgit v1.2.1 From 4619a385336119b20276454264d2eaf9f6ae5995 Mon Sep 17 00:00:00 2001 From: Alban Gruin Date: Sat, 25 Nov 2017 14:13:42 +0100 Subject: Meilleur affichage de la liste des semaines --- templates/group_list.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/templates/group_list.html b/templates/group_list.html index aebb0db..f21b2b1 100644 --- a/templates/group_list.html +++ b/templates/group_list.html @@ -7,7 +7,7 @@

{{ timetable }}

{% endblock %} -- cgit v1.2.1 From 621f5d7f0d28f7252e1ff0c0f1286824fa55c33e Mon Sep 17 00:00:00 2001 From: Alban Gruin Date: Sat, 25 Nov 2017 14:22:03 +0100 Subject: On ne compare plus les id des emplois du temps dans corresponds_to() car dans le seul appel « utile », les arguments passés font forcément partie du même edt. Changement effectué pour des questions de performance. Correction des tests. --- models.py | 11 +++++------ tests.py | 14 +++++++------- views.py | 2 +- 3 files changed, 13 insertions(+), 14 deletions(-) diff --git a/models.py b/models.py index bde27a7..59301e0 100644 --- a/models.py +++ b/models.py @@ -98,19 +98,18 @@ class Group(models.Model): hidden = models.BooleanField(verbose_name="caché", default=False) - def corresponds_to(self, timetable_id, mention, subgroup): + def corresponds_to(self, mention, subgroup): subgroup_corresponds = True if self.subgroup is not None and subgroup is not None: subgroup_corresponds = self.subgroup.startswith(subgroup) - return self.timetable.id == timetable_id and \ - (self.mention.startswith(mention) or \ - mention.startswith(self.mention)) and \ - subgroup_corresponds + return (self.mention.startswith(mention) or \ + mention.startswith(self.mention)) and \ + subgroup_corresponds @property def group_info(self): - return self.timetable.id, self.mention, self.subgroup + return self.mention, self.subgroup def __str__(self): return self.name diff --git a/tests.py b/tests.py index e7af6a8..77fa120 100644 --- a/tests.py +++ b/tests.py @@ -152,12 +152,12 @@ class GroupTestCase(TestCase): 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(cma.group_info, ("L1 info s2 ", "A")) + self.assertEqual(tda2.group_info, ("L1 info s2 ", "A2")) + self.assertEqual(tpa21.group_info, ("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, ("L1 info s2 ", "B")) + self.assertEqual(tdb2.group_info, ("L1 info s2 ", "B2")) + self.assertEqual(tpb21.group_info, ("L1 info s2 ", "B21")) - self.assertEqual(general.group_info, (self.timetable.id, "L1 info ", "")) + self.assertEqual(general.group_info, ("L1 info ", "")) diff --git a/views.py b/views.py index 97d3048..7bc5641 100644 --- a/views.py +++ b/views.py @@ -45,7 +45,7 @@ def group_list_common(request, timetable, groups): for group in groups: for group_week in groups_weeks: - if group.corresponds_to(timetable.id, group_week["groups__mention"], + if group.corresponds_to(group_week["groups__mention"], group_week["groups__subgroup"]): if not hasattr(group, "weeks"): group.weeks = [] -- cgit v1.2.1 From cbab00f4b745f4d80dd06a52699006d1d081be20 Mon Sep 17 00:00:00 2001 From: Alban Gruin Date: Sat, 25 Nov 2017 15:01:48 +0100 Subject: Fusion de group_list et de group_list_common. Légère modification de la requête pour récupérer les semaines de cours (au lieu de filtrer par l’emploi du temps, on filtre par les groupes), résultant en une nette accélération du traitement Avant modification : SQLite : ~420ms PostgreSQL : ~60ms puis ~30ms Après modification : SQLite : ~18ms PostgreSQL : ~23ms puis ~13ms La progression sur PostgreSQL est celle qui importe le plus, puisqu’il s’agit du SGBDR utilisé en production, mais la requête s’effectue tout de même plus rapidement (un peu plus qu’un rapport 2), ce qui reste intéressant. --- views.py | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/views.py b/views.py index 7bc5641..d4df8db 100644 --- a/views.py +++ b/views.py @@ -35,11 +35,14 @@ def mention_list(request, year_slug): return render(request, "index.html", {"year": year, "elements": timetables}) -def group_list_common(request, timetable, groups): +def group_list(request, year_slug, timetable_slug): + timetable = get_object_or_404(Timetable, year__slug=year_slug, slug=timetable_slug) + groups = Group.objects.filter(timetable=timetable, hidden=False).order_by("name") + start, _ = get_week(*get_current_week()) end = start + datetime.timedelta(weeks=4) - groups_weeks = Course.objects.get_weeks(begin__gte=start, begin__lt=end, timetable=timetable) \ + groups_weeks = Course.objects.get_weeks(begin__gte=start, begin__lt=end, groups__in=groups) \ .values("groups__mention", "groups__subgroup", "year", "week") @@ -57,12 +60,8 @@ def group_list_common(request, timetable, groups): if hasattr(group, "weeks"): group.weeks.sort() - return render(request, "group_list.html", {"timetable": timetable, "groups": groups}) -def group_list(request, year_slug, timetable_slug): - timetable = get_object_or_404(Timetable, year__slug=year_slug, slug=timetable_slug) - groups = Group.objects.filter(timetable=timetable).order_by("name") - return group_list_common(request, timetable, groups) + return render(request, "group_list.html", {"timetable": timetable, "groups": groups}) def timetable(request, year_slug, timetable_slug, group_slug, year=None, week=None): current_year, current_week = get_current_or_next_week() -- cgit v1.2.1 From f333d363a78821ebab78fb9bb900d7aefbf6079c Mon Sep 17 00:00:00 2001 From: Alban Gruin Date: Sat, 25 Nov 2017 15:14:52 +0100 Subject: Dans get_weeks(), l’annotation Count() ne sert à rien. --- models.py | 7 ++++--- views.py | 4 +--- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/models.py b/models.py index 59301e0..cd19259 100644 --- a/models.py +++ b/models.py @@ -158,9 +158,10 @@ class CourseManager(Manager): return self.get_queryset() \ .filter(**criteria) \ .order_by("groups__name", "year", "week") \ - .annotate(_=Count(("groups", "year", "week", "begin")), - year=ExtractYear("begin"), - week=ExtractWeek("begin")) + .annotate(year=ExtractYear("begin"), + week=ExtractWeek("begin")) \ + .values("groups__mention", + "groups__subgroup", "year", "week") class Course(models.Model): diff --git a/views.py b/views.py index d4df8db..f1ec8d5 100644 --- a/views.py +++ b/views.py @@ -42,9 +42,7 @@ def group_list(request, year_slug, timetable_slug): start, _ = get_week(*get_current_week()) end = start + datetime.timedelta(weeks=4) - groups_weeks = Course.objects.get_weeks(begin__gte=start, begin__lt=end, groups__in=groups) \ - .values("groups__mention", "groups__subgroup", - "year", "week") + groups_weeks = Course.objects.get_weeks(begin__gte=start, begin__lt=end, groups__in=groups) for group in groups: for group_week in groups_weeks: -- cgit v1.2.1 From d1191533a29b5f80543faa0758c7303d0a4b450c Mon Sep 17 00:00:00 2001 From: Alban Gruin Date: Sat, 25 Nov 2017 16:16:26 +0100 Subject: Le sachiez-tu ? Parfois je met des espaces inutiles --- views.py | 1 - 1 file changed, 1 deletion(-) diff --git a/views.py b/views.py index f1ec8d5..9e88b3b 100644 --- a/views.py +++ b/views.py @@ -58,7 +58,6 @@ def group_list(request, year_slug, timetable_slug): if hasattr(group, "weeks"): group.weeks.sort() - return render(request, "group_list.html", {"timetable": timetable, "groups": groups}) def timetable(request, year_slug, timetable_slug, group_slug, year=None, week=None): -- cgit v1.2.1 From 4aebed8ca3f879aa4e2996620657a14ac9538477 Mon Sep 17 00:00:00 2001 From: Alban Gruin Date: Sat, 25 Nov 2017 20:43:21 +0100 Subject: get_week() convertit automatiquement les chaines de caractères en int --- utils.py | 2 +- views.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/utils.py b/utils.py index e1b9867..63bcaed 100644 --- a/utils.py +++ b/utils.py @@ -30,7 +30,7 @@ def get_current_or_next_week(): def get_week(year, week): start = timezone.make_aware(datetime.datetime.strptime( - "{0}-W{1:02d}-1".format(year, week), "%Y-W%W-%w")) + "{0}-W{1:02d}-1".format(year, int(week)), "%Y-W%W-%w")) end = start + datetime.timedelta(weeks=1) return start, end diff --git a/views.py b/views.py index 9e88b3b..1269415 100644 --- a/views.py +++ b/views.py @@ -70,7 +70,7 @@ def timetable(request, year_slug, timetable_slug, group_slug, year=None, week=No elif (int(year), int(week)) < (current_year, current_week): is_old_timetable = True - start, end = get_week(int(year), int(week)) + start, end = get_week(year, week) timetable = get_object_or_404(Timetable, year__slug=year_slug, slug=timetable_slug) group = get_object_or_404(Group, slug=group_slug, timetable=timetable) -- cgit v1.2.1 From 630bebd114cdbae7af736e117e2b10b7e9195657 Mon Sep 17 00:00:00 2001 From: Alban Gruin Date: Sat, 25 Nov 2017 20:49:23 +0100 Subject: Suppression du formatage inutile dans get_week() --- utils.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/utils.py b/utils.py index 63bcaed..5a9218e 100644 --- a/utils.py +++ b/utils.py @@ -30,7 +30,7 @@ def get_current_or_next_week(): def get_week(year, week): start = timezone.make_aware(datetime.datetime.strptime( - "{0}-W{1:02d}-1".format(year, int(week)), "%Y-W%W-%w")) + "{0}-W{1}-1".format(year, week), "%Y-W%W-%w")) end = start + datetime.timedelta(weeks=1) return start, end -- cgit v1.2.1 From 34ba9c926cfd0725ebd2905e9c71823fd272dad2 Mon Sep 17 00:00:00 2001 From: Alban Gruin Date: Sun, 26 Nov 2017 13:27:40 +0100 Subject: Optimisation de deux requêtes : – sélection des cours d’un groupe : préchargement des salles – sélection des emplois du temps d’une année : préchargement de l’année --- models.py | 2 +- views.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/models.py b/models.py index cd19259..b260275 100644 --- a/models.py +++ b/models.py @@ -152,7 +152,7 @@ class CourseManager(Manager): def get_courses_for_group(self, group, **criteria): return self.get_queryset() \ .filter(groups__in=Group.objects.get_parents(group), **criteria) \ - .order_by("begin") + .order_by("begin").prefetch_related("rooms") def get_weeks(self, **criteria): return self.get_queryset() \ diff --git a/views.py b/views.py index 1269415..5eb224e 100644 --- a/views.py +++ b/views.py @@ -31,7 +31,7 @@ def index(request): def mention_list(request, year_slug): year = get_object_or_404(Year, slug=year_slug) - timetables = Timetable.objects.order_by("name").filter(year=year) + timetables = Timetable.objects.order_by("name").filter(year=year).select_related("year") return render(request, "index.html", {"year": year, "elements": timetables}) -- cgit v1.2.1 From 0bdde723ebb571bb6f1a894fee710fae7abc270e Mon Sep 17 00:00:00 2001 From: Alban Gruin Date: Sun, 26 Nov 2017 13:50:27 +0100 Subject: Annulation des changements récemments effectués sur la regex --- utils.py | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/utils.py b/utils.py index 5a9218e..46244e1 100644 --- a/utils.py +++ b/utils.py @@ -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* é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*\(.+\))?$") + # ^(.+?)\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+\(.+\))?$") search = group_regex.search(name) if search is None: return name, None parts = search.groups() - return parts[0], parts[5] + return parts[0], parts[4] def tz_now(): """Retourne la date et l’heure avec le bon fuseau horaire""" -- cgit v1.2.1 From f8c7775aac4a7eb2a4430d1aabb20b7f29939af7 Mon Sep 17 00:00:00 2001 From: Alban Gruin Date: Sun, 26 Nov 2017 13:53:10 +0100 Subject: Ajout d’un nouveau champ de groupe : le semestre --- admin.py | 2 +- models.py | 10 +++++++--- utils.py | 26 +++++++++++++------------- 3 files changed, 21 insertions(+), 17 deletions(-) diff --git a/admin.py b/admin.py index c93b6b3..0235fb9 100644 --- a/admin.py +++ b/admin.py @@ -44,7 +44,7 @@ class TimetableAdmin(admin.ModelAdmin): class GroupAdmin(admin.ModelAdmin): fieldsets = ( (None, {"fields": ("name", "celcat_name", "timetable", "hidden",)}), - ("Groupes", {"fields": ("mention", "subgroup",)}),) + ("Groupes", {"fields": ("mention", "semester", "subgroup",)}),) list_display = ("name", "timetable", "hidden",) list_editable = ("hidden",) list_filter = ("timetable",) diff --git a/models.py b/models.py index b260275..abb00bf 100644 --- a/models.py +++ b/models.py @@ -78,7 +78,9 @@ class GroupManager(Manager): [Q(subgroup=group.subgroup[:i]) for i in range(1, len(group.subgroup) + 1)]) - return self.get_queryset().filter(groups_criteria, mention=group.mention, + return self.get_queryset().filter(groups_criteria, + Q(semester=None) | Q(semester=group.semester), + mention=group.mention, timetable=group.timetable) @@ -92,6 +94,7 @@ class Group(models.Model): verbose_name="emploi du temps") 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="") slug = models.SlugField(max_length=64, default="") @@ -103,6 +106,7 @@ class Group(models.Model): if self.subgroup is not None and subgroup is not None: subgroup_corresponds = self.subgroup.startswith(subgroup) + # TODO adapter pour semester return (self.mention.startswith(mention) or \ mention.startswith(self.mention)) and \ subgroup_corresponds @@ -119,7 +123,7 @@ class Group(models.Model): self.name = self.celcat_name self.slug = slugify(self.name) - self.mention, self.subgroup = parse_group(self.name) + self.mention, self.semester, self.subgroup = parse_group(self.name) if self.subgroup is None: self.subgroup = "" @@ -127,7 +131,7 @@ class Group(models.Model): class Meta: - index_together = ("mention", "subgroup",) + index_together = ("mention", "semester", "subgroup",) unique_together = (("name", "timetable",), ("celcat_name", "timetable",), ("slug", "timetable",),) diff --git a/utils.py b/utils.py index 46244e1..103969e 100644 --- a/utils.py +++ b/utils.py @@ -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+ 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+\(.+\))?$") search = group_regex.search(name) if search is None: return name, None parts = search.groups() - return parts[0], parts[4] + return parts[0], int(parts[2]), parts[5] def tz_now(): """Retourne la date et l’heure avec le bon fuseau horaire""" -- cgit v1.2.1 From 2aacb6e59e40c9d47d02719dc7b5541a36b3c9ed Mon Sep 17 00:00:00 2001 From: Alban Gruin Date: Sun, 26 Nov 2017 14:08:11 +0100 Subject: Correction d’un cas pouvant provoquer une erreur de parsage de groupe (si jamais il n’y a pas de semestre), renvoi du numéro de semestre dans group_info, adaptation de corresponds_to. --- models.py | 5 +++-- tests.py | 14 +++++++------- utils.py | 12 ++++++++++-- 3 files changed, 20 insertions(+), 11 deletions(-) diff --git a/models.py b/models.py index abb00bf..fb0e757 100644 --- a/models.py +++ b/models.py @@ -101,7 +101,7 @@ class Group(models.Model): hidden = models.BooleanField(verbose_name="caché", default=False) - def corresponds_to(self, mention, subgroup): + def corresponds_to(self, mention, semester, subgroup): subgroup_corresponds = True if self.subgroup is not None and subgroup is not None: subgroup_corresponds = self.subgroup.startswith(subgroup) @@ -109,11 +109,12 @@ class Group(models.Model): # TODO adapter pour semester return (self.mention.startswith(mention) or \ mention.startswith(self.mention)) and \ + (self.semester == semester or semester is None) and \ subgroup_corresponds @property def group_info(self): - return self.mention, self.subgroup + return self.mention, self.semester, self.subgroup def __str__(self): return self.name diff --git a/tests.py b/tests.py index 77fa120..1425a84 100644 --- a/tests.py +++ b/tests.py @@ -152,12 +152,12 @@ class GroupTestCase(TestCase): general = Group.objects.get(celcat_name="L1 info (toutes sections et semestres confondus)", timetable=self.timetable) - self.assertEqual(cma.group_info, ("L1 info s2 ", "A")) - self.assertEqual(tda2.group_info, ("L1 info s2 ", "A2")) - self.assertEqual(tpa21.group_info, ("L1 info s2 ", "A21")) + self.assertEqual(cma.group_info, ("L1 info", 2, "A")) + self.assertEqual(tda2.group_info, ("L1 info", 2, "A2")) + self.assertEqual(tpa21.group_info, ("L1 info", 2, "A21")) - self.assertEqual(cmb.group_info, ("L1 info s2 ", "B")) - self.assertEqual(tdb2.group_info, ("L1 info s2 ", "B2")) - self.assertEqual(tpb21.group_info, ("L1 info s2 ", "B21")) + self.assertEqual(cmb.group_info, ("L1 info", 2, "B")) + self.assertEqual(tdb2.group_info, ("L1 info", 2, "B2")) + self.assertEqual(tpb21.group_info, ("L1 info", 2, "B21")) - self.assertEqual(general.group_info, ("L1 info ", "")) + self.assertEqual(general.group_info, ("L1 info", None, "")) diff --git a/utils.py b/utils.py index 103969e..9f0a6b5 100644 --- a/utils.py +++ b/utils.py @@ -62,10 +62,18 @@ def parse_group(name): 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 + return name, None, None parts = search.groups() - return parts[0], int(parts[2]), 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: + # Si jamais le semestre n’est pas présent dans la chaine parsée, + # parts[2] sera à None et sa conversion vers un int va provoquer + # une erreur. + return parts[0], None, parts[5] def tz_now(): """Retourne la date et l’heure avec le bon fuseau horaire""" -- cgit v1.2.1 From 574bc0efb1061cd1c1fd747ba13f6f7ac14a3e51 Mon Sep 17 00:00:00 2001 From: Alban Gruin Date: Sun, 26 Nov 2017 14:09:49 +0100 Subject: Suppression des commentaires inutiles --- models.py | 1 - 1 file changed, 1 deletion(-) diff --git a/models.py b/models.py index fb0e757..0904b63 100644 --- a/models.py +++ b/models.py @@ -106,7 +106,6 @@ class Group(models.Model): if self.subgroup is not None and subgroup is not None: subgroup_corresponds = self.subgroup.startswith(subgroup) - # TODO adapter pour semester return (self.mention.startswith(mention) or \ mention.startswith(self.mention)) and \ (self.semester == semester or semester is None) and \ -- cgit v1.2.1 From ef6f5e8517436b804265880b71a2ee88f9f5c179 Mon Sep 17 00:00:00 2001 From: Alban Gruin Date: Sun, 26 Nov 2017 14:22:13 +0100 Subject: Récupération du semestre pour générer la liste des groupes. --- models.py | 2 +- views.py | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/models.py b/models.py index 0904b63..25ddd1b 100644 --- a/models.py +++ b/models.py @@ -164,7 +164,7 @@ class CourseManager(Manager): .order_by("groups__name", "year", "week") \ .annotate(year=ExtractYear("begin"), week=ExtractWeek("begin")) \ - .values("groups__mention", + .values("groups__mention", "groups__semester", "groups__subgroup", "year", "week") diff --git a/views.py b/views.py index 5eb224e..4431891 100644 --- a/views.py +++ b/views.py @@ -47,6 +47,7 @@ def group_list(request, year_slug, timetable_slug): for group in groups: for group_week in groups_weeks: if group.corresponds_to(group_week["groups__mention"], + group_week["groups__semester"], group_week["groups__subgroup"]): if not hasattr(group, "weeks"): group.weeks = [] -- cgit v1.2.1 From 8cd40466e5b1ef9f00cf88c9375289d235abc009 Mon Sep 17 00:00:00 2001 From: Alban Gruin Date: Sun, 26 Nov 2017 14:27:16 +0100 Subject: Optimisation de la requête utilisée pour savoir si il y a des cours sur la semaine sélectionnée (utilisation de exists() au lieu de count()) --- views.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/views.py b/views.py index 4431891..c647fac 100644 --- a/views.py +++ b/views.py @@ -77,7 +77,7 @@ def timetable(request, year_slug, timetable_slug, group_slug, year=None, week=No group = get_object_or_404(Group, slug=group_slug, timetable=timetable) courses = Course.objects.get_courses_for_group(group, begin__gte=start, begin__lt=end) - if courses.count() == 0 and provided_week: + if not courses.exists() and provided_week: raise Http404 last_update = courses.aggregate(Max("last_update"))["last_update__max"] -- cgit v1.2.1 From 4998865c2d5e94c53547e44aed1dc825163c0c4a Mon Sep 17 00:00:00 2001 From: Alban Gruin Date: Sun, 26 Nov 2017 18:24:29 +0100 Subject: Version 0.12.0 --- __init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/__init__.py b/__init__.py index 1cdb4e3..03ae5ff 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 . -VERSION = "0.11.3" +VERSION = "0.12.0" __version__ = VERSION default_app_config = "edt.apps.EdtConfig" -- cgit v1.2.1