From fbf65db250dbc86b04cfcf6175e4b167e1be23f0 Mon Sep 17 00:00:00 2001
From: Alban Gruin
Date: Wed, 18 Oct 2017 13:37:36 +0200
Subject: Suppression des imports inutiles
---
management/commands/timetables.py | 1 -
tests.py | 2 --
2 files changed, 3 deletions(-)
diff --git a/management/commands/timetables.py b/management/commands/timetables.py
index 35fb26e..2d8a17e 100644
--- a/management/commands/timetables.py
+++ b/management/commands/timetables.py
@@ -18,7 +18,6 @@ import datetime
from django.core.management.base import BaseCommand
from django.db import transaction
from django.db.models import Min
-from django.utils import timezone
from edt.models import Course, Timetable
from edt.utils import get_week, tz_now
diff --git a/tests.py b/tests.py
index be7b884..a3475ff 100644
--- a/tests.py
+++ b/tests.py
@@ -14,8 +14,6 @@
# along with celcatsanitizer. If not, see .
from django.test import TestCase
-from django.utils import timezone
-
from .models import Course, Group, Timetable, Year
from .utils import tz_now
--
cgit v1.2.1
From e13b5bb8b08c336f61403480df6bbfbe29f2316b Mon Sep 17 00:00:00 2001
From: Alban Gruin
Date: Thu, 19 Oct 2017 14:04:41 +0200
Subject: Changement radical du fonctionnement de la regex en vue d’un gros
changement de la structure de la base de données
---
utils.py | 36 ++++++++++++++----------------------
1 file changed, 14 insertions(+), 22 deletions(-)
diff --git a/utils.py b/utils.py
index 28a1ba2..5d55eca 100644
--- a/utils.py
+++ b/utils.py
@@ -48,32 +48,24 @@ def group_courses(courses):
def parse_group(name):
# Explication de la regex
#
- # ^(.+?)\s+((CM(\w))|(TD(\w)(\d))|(TP(\w)(\d)(\d)))?(\s+\(.+\))?$
- # ^ début de la ligne
- # (.+?) correspond à au moins un caractère
- # \s+ 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 optionnel
- # (\s+ un ou plusieurs espaces
- # \(.+\)) un ou plusieurs caractères quelconques entre parenthèses
- # ? groupe optionnel
- # $ fin de la ligne
- group_regex = re.compile(r"^(.+?)\s+((CM(\w))|(TD(\w)(\d))|(TP(\w)(\d)(\d)))?(\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, None, None
+ return name, None
parts = search.groups()
- if parts[1] is None: # Pas de groupe précis indiqué
- return parts[0], None, None, None
- elif parts[2] is not None: # Groupe de CM
- return parts[0], parts[3], None, None
- elif parts[4] is not None: # Groupe de TD
- return parts[0], parts[5], parts[6], None
- elif parts[7] is not None: # Groupe de TP
- return parts[0], parts[8], parts[9], parts[10]
+ return parts[0], parts[4]
def tz_now():
"""Retourne la date et l’heure avec le bon fuseau horaire"""
--
cgit v1.2.1
From 781ba0419a5e7bba423c922180c2c2db2327543a Mon Sep 17 00:00:00 2001
From: Alban Gruin
Date: Thu, 19 Oct 2017 19:04:23 +0200
Subject: Suppressions des champs td, tp et parents
---
models.py | 11 +++--------
1 file changed, 3 insertions(+), 8 deletions(-)
diff --git a/models.py b/models.py
index fbbf877..bfc026b 100644
--- a/models.py
+++ b/models.py
@@ -92,11 +92,6 @@ class Group(models.Model):
mention = models.CharField(max_length=128)
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="")
hidden = models.BooleanField(verbose_name="caché", default=False)
@@ -110,7 +105,7 @@ class Group(models.Model):
@property
def group_info(self):
- return self.timetable.id, self.mention, self.subgroup, self.td, self.tp
+ return self.timetable.id, self.mention, self.subgroup
def __str__(self):
return self.name
@@ -120,12 +115,12 @@ class Group(models.Model):
self.name = self.celcat_name
self.slug = slugify(self.name)
- self.mention, self.subgroup, self.td, self.tp = parse_group(self.name)
+ self.mention, self.subgroup = parse_group(self.name)
super(Group, self).save()
class Meta:
- index_together = ("mention", "subgroup", "td", "tp",)
+ index_together = ("mention", "subgroup",)
unique_together = (("name", "timetable",),
("celcat_name", "timetable",),
("slug", "timetable",),)
--
cgit v1.2.1
From e983619333a715bbc35deecf019a95addb5b6009 Mon Sep 17 00:00:00 2001
From: Alban Gruin
Date: Thu, 19 Oct 2017 21:38:40 +0200
Subject: Suppression des champs retirés de l’interface d’administration. La
longueur du champ sous-groupe est maintenant fixée à 16.
---
admin.py | 4 ++--
models.py | 3 ++-
2 files changed, 4 insertions(+), 3 deletions(-)
diff --git a/admin.py b/admin.py
index e17948a..c93b6b3 100644
--- a/admin.py
+++ b/admin.py
@@ -44,12 +44,12 @@ class TimetableAdmin(admin.ModelAdmin):
class GroupAdmin(admin.ModelAdmin):
fieldsets = (
(None, {"fields": ("name", "celcat_name", "timetable", "hidden",)}),
- ("Groupes", {"fields": ("mention", "subgroup", "td", "tp", "parent",)}),)
+ ("Groupes", {"fields": ("mention", "subgroup",)}),)
list_display = ("name", "timetable", "hidden",)
list_editable = ("hidden",)
list_filter = ("timetable",)
ordering = ("timetable",)
- readonly_fields = ("celcat_name", "mention", "subgroup", "td", "tp",)
+ readonly_fields = ("celcat_name", "mention",)
actions = (make_hidden, make_visible,)
diff --git a/models.py b/models.py
index bfc026b..868cbd1 100644
--- a/models.py
+++ b/models.py
@@ -90,8 +90,9 @@ class Group(models.Model):
verbose_name="emploi du temps")
mention = models.CharField(max_length=128)
- subgroup = models.CharField(max_length=1, verbose_name="sous-groupe",
+ subgroup = models.CharField(max_length=16, verbose_name="sous-groupe",
null=True)
+
slug = models.SlugField(max_length=64, default="")
hidden = models.BooleanField(verbose_name="caché", default=False)
--
cgit v1.2.1
From 9efb1796aed00f4c7a8c9cd7e6b8a169318b1be0 Mon Sep 17 00:00:00 2001
From: Alban Gruin
Date: Thu, 19 Oct 2017 21:48:39 +0200
Subject: Adaptation de corresponds_to() et des tests de parsage
---
models.py | 11 +++++++----
tests.py | 12 ++++++------
2 files changed, 13 insertions(+), 10 deletions(-)
diff --git a/models.py b/models.py
index 868cbd1..c69bec3 100644
--- a/models.py
+++ b/models.py
@@ -97,12 +97,15 @@ class Group(models.Model):
hidden = models.BooleanField(verbose_name="caché", default=False)
- def corresponds_to(self, timetable_id, mention, subgroup, td, tp):
+ 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)
+
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)
+ subgroup_corresponds
@property
def group_info(self):
diff --git a/tests.py b/tests.py
index a3475ff..051c733 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 s2", "A", None, None))
- self.assertEqual(tda2.group_info, (self.timetable.id, "L1 info s2", "A", 2, None))
- self.assertEqual(tpa21.group_info, (self.timetable.id, "L1 info s2", "A", 2, 1))
+ 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(cmb.group_info, (self.timetable.id, "L1 info s2", "B", None, None))
- self.assertEqual(tdb2.group_info, (self.timetable.id, "L1 info s2", "B", 2, None))
- self.assertEqual(tpb21.group_info, (self.timetable.id, "L1 info s2", "B", 2, 1))
+ 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"))
--
cgit v1.2.1
From acc4475efca6326f59673d88865eecbef0511755 Mon Sep 17 00:00:00 2001
From: Alban Gruin
Date: Thu, 19 Oct 2017 22:14:28 +0200
Subject: Changement de la logique de récupération des cours pour un groupe
adaptée à la nouvelle structure du modèle Group.
---
models.py | 16 +++++++---------
1 file changed, 7 insertions(+), 9 deletions(-)
diff --git a/models.py b/models.py
index c69bec3..fe285ac 100644
--- a/models.py
+++ b/models.py
@@ -13,6 +13,8 @@
# You should have received a copy of the GNU Affero General Public License
# along with celcatsanitizer. If not, see .
+from functools import reduce
+
from django.db import models
from django.db.models import Count, Manager, Q
from django.db.models.functions import ExtractWeek, ExtractYear
@@ -147,17 +149,13 @@ class Room(models.Model):
class CourseManager(Manager):
def get_courses_for_group(self, group, **criteria):
- groups_criteria = []
- if group.subgroup is not None:
- groups_criteria.append(Q(groups__subgroup__isnull=True) | \
- Q(groups__subgroup=group.subgroup))
- if group.td is not None:
- groups_criteria.append(Q(groups__td__isnull=True) | Q(groups__td=group.td))
- if group.tp is not None:
- groups_criteria.append(Q(groups__tp__isnull=True) | Q(groups__tp=group.tp))
+ groups_criteria = reduce(lambda x, y: x | y,
+ [Q(groups__subgroup=group.subgroup[:i])
+ for i in range(1, len(group.subgroup))]) | \
+ Q(groups__subgroup__isnull=True)
return self.get_queryset() \
- .filter(*groups_criteria,
+ .filter(groups_criteria,
groups__mention=group.mention,
timetable=group.timetable, **criteria) \
.order_by("begin")
--
cgit v1.2.1
From e7df75f46aec90652c2e501cfacdd31a7be5be4a Mon Sep 17 00:00:00 2001
From: Alban Gruin
Date: Thu, 19 Oct 2017 22:41:47 +0200
Subject: Suppression de l’étape de consolidation
---
management/commands/_private.py | 40 +---------------------------------------
1 file changed, 1 insertion(+), 39 deletions(-)
diff --git a/management/commands/_private.py b/management/commands/_private.py
index e7f0a3c..f28512f 100644
--- a/management/commands/_private.py
+++ b/management/commands/_private.py
@@ -30,42 +30,6 @@ def add_time(date, time):
delta = datetime.timedelta(hours=ptime.hour, minutes=ptime.minute)
return date + delta
-def consolidate_group(group):
- group_content_key = ("mention", "subgroup", "td", "tp")
- group_content_list = group.group_info[1:]
-
- if group.subgroup is not None:
- 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
-
- group.parent = Group.objects.filter(timetable=group.timetable,
- **group_content).first()
- group.save()
-
- if group.tp is None:
- group_content = dict(zip(group_content_key, group_content_list))
- last_is_none = False
-
- for i, key in enumerate(group_content_key):
- if group_content_list[i] is None or last_is_none:
- del group_content[key]
- group_content[key + "__isnull"] = last_is_none
- last_is_none = True
-
- Group.objects.filter(timetable=group.timetable, parent__isnull=True,
- **group_content).update(parent=group)
-
-def consolidate_groups(groups):
- for group in groups:
- if group.parent is None:
- consolidate_group(group)
-
def delete_courses_in_week(timetable, year, week, today):
start, end = get_week(year, week)
Course.objects.filter(begin__gte=max(start, today), begin__lt=end,
@@ -96,12 +60,10 @@ def get_event(timetable, event, event_week, today):
# Création de l’objet cours
course = Course.objects.create(timetable=timetable, begin=begin, end=end)
- # On récupère les groupes concernés par les cours, on les
- # « consolide », puis on les insère dans l’objet cours.
+ # On récupère les groupes concernés par les cours
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)
course.groups.add(*groups)
# On récupère le champ « remarque »
--
cgit v1.2.1
From 276dc9017b414404539b20f32bcc65959fd32adb Mon Sep 17 00:00:00 2001
From: Alban Gruin
Date: Thu, 19 Oct 2017 22:48:16 +0200
Subject: Adaptation des vues, correction du critère de sélection des groupes
---
models.py | 2 +-
views.py | 11 +++++------
2 files changed, 6 insertions(+), 7 deletions(-)
diff --git a/models.py b/models.py
index fe285ac..db9e262 100644
--- a/models.py
+++ b/models.py
@@ -151,7 +151,7 @@ class CourseManager(Manager):
def get_courses_for_group(self, group, **criteria):
groups_criteria = reduce(lambda x, y: x | y,
[Q(groups__subgroup=group.subgroup[:i])
- for i in range(1, len(group.subgroup))]) | \
+ for i in range(1, len(group.subgroup) + 1)]) | \
Q(groups__subgroup__isnull=True)
return self.get_queryset() \
diff --git a/views.py b/views.py
index 2100972..e5c89a2 100644
--- a/views.py
+++ b/views.py
@@ -39,13 +39,12 @@ def group_list_common(request, timetable, groups):
groups_weeks = Course.objects.get_weeks(begin__gte=start, begin__lt=end, timetable=timetable) \
.values("groups__mention", "groups__subgroup",
- "groups__td", "groups__tp", "year", "week")
+ "year", "week")
for group in groups:
for group_week in groups_weeks:
if group.corresponds_to(timetable.id, group_week["groups__mention"],
- group_week["groups__subgroup"], group_week["groups__td"],
- group_week["groups__tp"]):
+ group_week["groups__subgroup"]):
if not hasattr(group, "weeks"):
group.weeks = []
@@ -60,7 +59,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=timetable).order_by("name")
+ 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):
@@ -78,8 +77,8 @@ 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.children.count():
- return group_list_common(request, timetable, Group.objects.get_relevant_children(group))
+ # if group.children.count():
+ # return group_list_common(request, timetable, Group.objects.get_relevant_children(group))
courses = Course.objects.get_courses_for_group(group, begin__gte=start, begin__lt=end)
if courses.count() == 0 and provided_week:
--
cgit v1.2.1
From 07e9a1d1e6c77d64e5c3daf0324ae751117f168e Mon Sep 17 00:00:00 2001
From: Alban Gruin
Date: Fri, 20 Oct 2017 13:29:07 +0200
Subject: Modification de la requête qui sélectionne les groupes pertinents.
---
models.py | 21 +++++++++------------
views.py | 9 ++++++---
2 files changed, 15 insertions(+), 15 deletions(-)
diff --git a/models.py b/models.py
index db9e262..74fab9e 100644
--- a/models.py
+++ b/models.py
@@ -16,7 +16,8 @@
from functools import reduce
from django.db import models
-from django.db.models import Count, Manager, Q
+from django.db.models import Count, Manager, Q, Subquery
+from django.db.models.expressions import OuterRef
from django.db.models.functions import ExtractWeek, ExtractYear
from django.utils import timezone
from django.utils.text import slugify
@@ -69,17 +70,13 @@ class Timetable(SlugModel):
class GroupManager(Manager):
- def get_relevant_children(self, group):
- parent_in = self.get_queryset().filter(parent=group)
- return self.get_queryset().filter(Q(parent=group) | Q(parent__in=parent_in)) \
- .annotate(children_count=Count("children")) \
- .filter(children_count=0, hidden=False) \
- .order_by("name")
-
- def get_relevant_groups(self, *args, **criteria):
- return self.get_queryset().filter(*args, **criteria) \
- .annotate(children_count=Count("children")) \
- .filter(children_count=0, hidden=False)
+ def get_relevant_groups(self, timetable, *args, **criteria):
+ sub = Group.objects.filter(timetable=timetable,mention=OuterRef("mention"),
+ subgroup__startswith=OuterRef("subgroup")) \
+ .order_by().values("mention").annotate(c=Count("*")).values("c")
+ return Group.objects.filter(*args, timetable=timetable, hidden=False, **criteria) \
+ .annotate(nbsub=Subquery(sub, output_field=models.IntegerField())) \
+ .filter(nbsub=1).order_by("name")
class Group(models.Model):
diff --git a/views.py b/views.py
index e5c89a2..9c38524 100644
--- a/views.py
+++ b/views.py
@@ -59,7 +59,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.filter(timetable=timetable).order_by("name")
+ groups = Group.objects.get_relevant_groups(timetable)
return group_list_common(request, timetable, groups)
def timetable(request, year_slug, timetable_slug, group_slug, year=None, week=None):
@@ -77,8 +77,11 @@ 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.children.count():
- # return group_list_common(request, timetable, Group.objects.get_relevant_children(group))
+ 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:
--
cgit v1.2.1