{% if timetable %}{{ timetable }}{% else %}Emploi du temps des salles{% endif %}
{% for group in groups %}
-
{{ group }} — {% for week in group.weeks %}{{ week|dt_prettyprint }} {% if not forloop.last %}– {% endif %}{% empty %}aucun cours dans le mois à venir{% endfor %}
+
{{ group }} — {% for week in group.weeks %}{{ week|dt_prettyprint }} {% if not forloop.last %}– {% endif %}{% empty %}aucun cours dans le mois à venir{% endfor %}
{% endfor %}
{% endblock %}
--
cgit v1.2.1
From 599ca85167ad7d53f6434b105cb74c761d994ecf Mon Sep 17 00:00:00 2001
From: Alban Gruin
Date: Sat, 18 Nov 2017 16:04:18 +0100
Subject: Liens et URLs pour les emplois du temps des salles
---
templates/group_list.html | 2 +-
urls.py | 3 ++-
views.py | 2 +-
3 files changed, 4 insertions(+), 3 deletions(-)
diff --git a/templates/group_list.html b/templates/group_list.html
index 88cc7a4..7fe1fe8 100644
--- a/templates/group_list.html
+++ b/templates/group_list.html
@@ -7,7 +7,7 @@
{% if timetable %}{{ timetable }}{% else %}Emploi du temps des salles{% endif %}
{% for group in groups %}
-
{{ group }} — {% for week in group.weeks %}{{ week|dt_prettyprint }} {% if not forloop.last %}– {% endif %}{% empty %}aucun cours dans le mois à venir{% endfor %}
+
{{ group }} — {% for week in group.weeks %}{{ week|dt_prettyprint }} {% if not forloop.last %}– {% endif %}{% empty %}aucun cours dans le mois à venir{% endfor %}
{% endfor %}
{% endblock %}
diff --git a/urls.py b/urls.py
index b0ca9f4..4c4abe3 100644
--- a/urls.py
+++ b/urls.py
@@ -20,7 +20,8 @@ urlpatterns = [
url(r"^$", views.index, name="index"),
url(r"^pages/", include("django.contrib.flatpages.urls")),
url(r"^salles/$", views.rooms, name="rooms"),
- url(r"^salles/(?P[-\w]+)$", views.room_timetable),
+ url(r"^salles/(?P[-\w]+)$", views.room_timetable, name="room-timetable"),
+ url(r"^salles/(?P[-\w]+)/(?P[0-9]{4})/(?P[0-4]?[0-9]|5[0-3])/$", views.room_timetable, name="room-timetable"),
url(r"^(?P[-\w]+)/$", views.mention_list, name="mentions"),
url(r"^(?P[-\w]+)/(?P[-\w]+)/$", views.group_list, name="groups"),
url(r"^(?P[-\w]+)/(?P[-\w]+)/(?P[-\w]+)/$", views.timetable, name="timetable"),
diff --git a/views.py b/views.py
index 9047329..e35796b 100644
--- a/views.py
+++ b/views.py
@@ -121,7 +121,7 @@ def rooms(request):
return render(request, "group_list.html", {"groups": rooms_weeks})
-def room_timetable(request, room_slug):
+def room_timetable(request, room_slug, year=None, week=None):
room = get_object_or_404(Room, slug=room_slug)
courses = Course.objects.filter(rooms__in=(room,)).order_by("begin")
--
cgit v1.2.1
From 1046a9ea68d9a29dc929eb88581e50a0704a5142 Mon Sep 17 00:00:00 2001
From: Alban Gruin
Date: Sat, 18 Nov 2017 16:04:28 +0100
Subject: Optimisation (?) de la requête des emplois du temps des salles
---
views.py | 9 +++++----
1 file changed, 5 insertions(+), 4 deletions(-)
diff --git a/views.py b/views.py
index e35796b..3916fac 100644
--- a/views.py
+++ b/views.py
@@ -103,12 +103,13 @@ def rooms(request):
# Récupération des salles et de toutes les semaines où elles sont
# concernées
- # Cette requête est un peu lente sur sqlite… j’espère que ce sera mieux
- # sur la base de prod.
+ # Cette requête est un peu lente sur sqlite…
+ # Par contre c’est beaucoup plus rapide sur PostgreSQL
rooms = Room.objects.filter(course__begin__gte=start, course__begin__lt=end) \
+ .order_by("name") \
.annotate(year=ExtractYear("course__begin"),
- week=ExtractWeek("course__begin")) \
- .order_by("name").annotate(c=Count("*"))
+ week=ExtractWeek("course__begin"),
+ c=Count("*"))
rooms_weeks = []
for room in rooms:
--
cgit v1.2.1
From 3148aaf7c43866bd672d54ac54a0e70bc71f1020 Mon Sep 17 00:00:00 2001
From: Alban Gruin
Date: Sat, 18 Nov 2017 17:14:26 +0100
Subject: Mise en commun du traitement des données avant rendu de l’emploi du
temps
---
models.py | 13 +++++++++----
views.py | 43 ++++++++++++++++++++++++++++++-------------
2 files changed, 39 insertions(+), 17 deletions(-)
diff --git a/models.py b/models.py
index 547c0e1..369a4a6 100644
--- a/models.py
+++ b/models.py
@@ -157,10 +157,15 @@ class Room(models.Model):
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").prefetch_related("rooms")
+ def get_courses(self, obj, **criteria):
+ qs = self.get_queryset()
+ if isinstance(obj, Group):
+ qs = qs.filter(groups__in=Group.objects.get_parents(obj), **criteria) \
+ .prefetch_related("rooms")
+ elif isinstance(obj, Room):
+ qs = qs.filter(rooms__in=(obj,), **criteria)
+
+ return qs.order_by("begin")
def get_weeks(self, **criteria):
return self.get_queryset() \
diff --git a/views.py b/views.py
index 3916fac..428711f 100644
--- a/views.py
+++ b/views.py
@@ -61,7 +61,7 @@ def group_list(request, year_slug, timetable_slug):
return render(request, "group_list.html", {"timetable": timetable, "groups": groups})
-def timetable(request, year_slug, timetable_slug, group_slug, year=None, week=None):
+def timetable_common(request, obj, year=None, week=None):
current_year, current_week = get_current_or_next_week()
is_old_timetable, provided_week = False, True
@@ -73,21 +73,30 @@ def timetable(request, year_slug, timetable_slug, group_slug, year=None, week=No
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)
-
- courses = Course.objects.get_courses_for_group(group, begin__gte=start, begin__lt=end)
+ courses = Course.objects.get_courses(obj, begin__gte=start, begin__lt=end)
if not courses.exists() and provided_week:
raise Http404
last_update = courses.aggregate(Max("last_update"))["last_update__max"]
grouped_courses = group_courses(courses)
- return render(request, "timetable.html", {"group": group, "courses": grouped_courses,
+ return render(request, "timetable.html", {"group": obj, "courses": grouped_courses,
"last_update": last_update,
"year": year, "week": int(week),
"is_old_timetable": is_old_timetable})
+def timetable(request, year_slug, timetable_slug, group_slug, year=None, week=None):
+ 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)
+
+ return timetable_common(request, group, year, week)
+
def calendars(request, year_slug, timetable_slug, group_slug):
group = get_object_or_404(Group, timetable__year__slug=year_slug,
timetable__slug=timetable_slug, slug=group_slug)
@@ -102,32 +111,40 @@ def rooms(request):
end = start + datetime.timedelta(weeks=4)
# Récupération des salles et de toutes les semaines où elles sont
- # concernées
- # Cette requête est un peu lente sur sqlite…
- # Par contre c’est beaucoup plus rapide sur PostgreSQL
+ # concernées.
+ # Cette requête associe chaque salle à toutes les semaines où un
+ # cours s’y déroule. Le résultat est trié par le nom de la salle
+ # et par semaine.
+ # TODO optimiser cette requête, elle me semble un peu lente
rooms = Room.objects.filter(course__begin__gte=start, course__begin__lt=end) \
.order_by("name") \
.annotate(year=ExtractYear("course__begin"),
week=ExtractWeek("course__begin"),
c=Count("*"))
+ # Regroupement des semaines dans une liste de chaque objet salle
rooms_weeks = []
for room in rooms:
+ # Si on a pas traité de salle ou que la salle courante
+ # dans le résultat de la requête est différente de la dernière
+ # dans la liste des salles traitées
if len(rooms_weeks) == 0 or rooms_weeks[-1].id != room.id:
+ # On lui affecte un tableau et on l’ajoute dans
+ # la liste des salles à traiter
room.weeks = []
rooms_weeks.append(room)
+ # On récupère le premier jour de la semaine
date, _ = get_week(room.year, room.week)
+ # Et on le rajoute dans la liste des semaines de la salle.
rooms_weeks[-1].weeks.append(date)
+ # Rendu de la page.
return render(request, "group_list.html", {"groups": rooms_weeks})
def room_timetable(request, room_slug, year=None, week=None):
room = get_object_or_404(Room, slug=room_slug)
- courses = Course.objects.filter(rooms__in=(room,)).order_by("begin")
-
- return render(request, "timetable.html", {"group": room, "courser": courses,
- })
+ return timetable_common(request, room, year, week)
def ctx_processor(request):
return {"celcatsanitizer_version": edt.VERSION}
--
cgit v1.2.1
From bb049a8d5954bbcf3ae5207a0ab4445591555d6e Mon Sep 17 00:00:00 2001
From: Alban Gruin
Date: Sat, 18 Nov 2017 18:21:52 +0100
Subject: Tentative de mise en commun de la template des edts
---
templates/timetable.html | 12 ++++++------
views.py | 3 ++-
2 files changed, 8 insertions(+), 7 deletions(-)
diff --git a/templates/timetable.html b/templates/timetable.html
index fc2065f..4c921fd 100644
--- a/templates/timetable.html
+++ b/templates/timetable.html
@@ -1,21 +1,21 @@
{% extends "index.html" %}
-{% block head %}
+{% block head %}{% if group_mode %}
-{% endblock %}
+{% endif %}{% endblock %}
-{% block title %}{{ group.timetable }} – {{ group }} – Semaine {{ week }} – {% endblock %}
+{% block title %}{% if group_mode %}{{ group.timetable }} –{% else %}Salle{% endif %} {{ group }} – Semaine {{ week }} – {% endblock %}
{% block body %}
-
{% endif %}{% endblock %}
diff --git a/views.py b/views.py
index cb6978b..3179ec5 100644
--- a/views.py
+++ b/views.py
@@ -37,7 +37,7 @@ def mention_list(request, year_slug):
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")
+ groups = Group.objects.filter(timetable=timetable.source, hidden=False).order_by("name")
start, _ = get_week(*get_current_week())
end = start + datetime.timedelta(weeks=4)
@@ -61,7 +61,7 @@ def group_list(request, year_slug, timetable_slug):
return render(request, "group_list.html", {"timetable": timetable, "groups": groups})
-def timetable_common(request, obj, year=None, week=None):
+def timetable_common(request, obj, year=None, week=None, timetable=None):
current_year, current_week = get_current_or_next_week()
is_old_timetable, provided_week = False, True
@@ -84,27 +84,24 @@ def timetable_common(request, obj, year=None, week=None):
"last_update": last_update,
"year": year, "week": int(week),
"is_old_timetable": is_old_timetable,
- "group_mode": isinstance(obj, Group)})
+ "group_mode": isinstance(obj, Group),
+ "timetable": timetable})
def timetable(request, year_slug, timetable_slug, group_slug, year=None, week=None):
timetable = get_object_or_404(Timetable, year__slug=year_slug, slug=timetable_slug)
- group = get_object_or_404(Group, slug=group_slug, timetable=timetable)
+ group = get_object_or_404(Group, slug=group_slug, timetable=timetable.source)
- 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)
-
- return timetable_common(request, group, year, week)
+ return timetable_common(request, group, year, week, timetable)
def calendars(request, year_slug, timetable_slug, group_slug):
- group = get_object_or_404(Group, timetable__year__slug=year_slug,
- timetable__slug=timetable_slug, slug=group_slug)
+ timetable = get_object_or_404(Timetable, year__slug=year_slug,
+ slug=timetable_slug)
+ group = get_object_or_404(Group, timetable=timetable.source, slug=group_slug)
groups = Group.objects.get_parents(group).annotate(length=Length("subgroup")) \
.order_by("length")
- return render(request, "calendars.html", {"group": group, "groups": groups})
+ return render(request, "calendars.html", {"timetable": timetable,
+ "group": group, "groups": groups})
def rooms(request):
# On récupère les dates allant de cette semaine à dans un mois
--
cgit v1.2.1
From 3efa5da22447fee662d81ebafb21b5e9b9f796fc Mon Sep 17 00:00:00 2001
From: Alban Gruin
Date: Mon, 27 Nov 2017 16:06:31 +0100
Subject: Affichage des salles à la place des groupes dans les flux RSS et Atom
---
feeds.py | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/feeds.py b/feeds.py
index 95ecaf8..8341f06 100644
--- a/feeds.py
+++ b/feeds.py
@@ -159,7 +159,8 @@ class RSSFeed(Feed):
context = {"courses": group_courses(courses),
"last_update": update["last_update__max"],
"year": update["year"],
- "week": update["week"]}
+ "week": update["week"],
+ "group_mode": True}
update["description"] = template.render(context)
--
cgit v1.2.1
From d0c69d3095d14f5509190d6b637cc0c018e53a19 Mon Sep 17 00:00:00 2001
From: Alban Gruin
Date: Mon, 27 Nov 2017 16:25:25 +0100
Subject: Changement des champs timetable en source pour plus de clareté
---
admin.py | 14 +++++++-------
models.py | 14 +++++++-------
2 files changed, 14 insertions(+), 14 deletions(-)
diff --git a/admin.py b/admin.py
index 9d8a34f..fc0ada1 100644
--- a/admin.py
+++ b/admin.py
@@ -48,12 +48,12 @@ class TimetableAdmin(admin.ModelAdmin):
@admin.register(Group)
class GroupAdmin(admin.ModelAdmin):
fieldsets = (
- (None, {"fields": ("name", "celcat_name", "timetable", "hidden",)}),
+ (None, {"fields": ("name", "celcat_name", "source", "hidden",)}),
("Groupes", {"fields": ("mention", "semester", "subgroup",)}),)
- list_display = ("name", "timetable", "hidden",)
+ list_display = ("name", "source", "hidden",)
list_editable = ("hidden",)
- list_filter = ("timetable",)
- ordering = ("timetable",)
+ list_filter = ("source__timetables",)
+ ordering = ("name", "source",)
readonly_fields = ("celcat_name", "mention", "semester", "subgroup",)
actions = (make_hidden, make_visible,)
@@ -66,9 +66,9 @@ class RoomAdmin(admin.ModelAdmin):
@admin.register(Course)
class CourseAdmin(admin.ModelAdmin):
fieldsets = (
- (None, {"fields": ("name", "type", "timetable", "groups", "rooms", "last_update",)}),
+ (None, {"fields": ("name", "type", "source", "groups", "rooms", "last_update",)}),
("Horaires", {"fields": ("begin", "end",)}),
("Remarques", {"fields": ("notes",)}),)
- list_display = ("name", "type", "timetable", "begin", "end",)
- list_filter = ("type", "timetable", "groups",)
+ list_display = ("name", "type", "source", "begin", "end",)
+ list_filter = ("type", "source__timetables", "groups",)
ordering = ("begin",)
diff --git a/models.py b/models.py
index eba8f1c..3c9fa27 100644
--- a/models.py
+++ b/models.py
@@ -101,8 +101,8 @@ class Group(SlugModel):
name = models.CharField(max_length=255, verbose_name="nom")
celcat_name = models.CharField(max_length=255,
verbose_name="nom dans Celcat")
- timetable = models.ForeignKey(Source, on_delete=models.CASCADE,
- verbose_name="emploi du temps")
+ source = models.ForeignKey(Source, on_delete=models.CASCADE,
+ verbose_name="source d’emploi du temps")
mention = models.CharField(max_length=128)
semester = models.IntegerField(verbose_name="semestre", null=True)
@@ -142,9 +142,9 @@ class Group(SlugModel):
class Meta:
index_together = ("mention", "semester", "subgroup",)
- unique_together = (("name", "timetable",),
- ("celcat_name", "timetable",),
- ("slug", "timetable",),)
+ unique_together = (("name", "source",),
+ ("celcat_name", "source",),
+ ("slug", "source",),)
verbose_name = "groupe"
verbose_name_plural = "groupes"
@@ -189,8 +189,8 @@ class Course(models.Model):
name = models.CharField(max_length=255, verbose_name="nom", default="Sans nom")
type_ = models.CharField(name="type", max_length=255,
verbose_name="type de cours", null=True)
- timetable = models.ForeignKey(Source, on_delete=models.CASCADE,
- verbose_name="emploi du temps")
+ source = models.ForeignKey(Source, on_delete=models.CASCADE,
+ verbose_name="emploi du temps")
notes = models.TextField(verbose_name="remarques", blank=True, null=True)
groups = models.ManyToManyField(Group, verbose_name="groupes")
--
cgit v1.2.1
From 5d4d7530e1aba199c2604b311b17bef253f0a008 Mon Sep 17 00:00:00 2001
From: Alban Gruin
Date: Mon, 27 Nov 2017 16:27:50 +0100
Subject: Remplacement des références aux champs timetable vers source
---
feeds.py | 4 ++--
models.py | 2 +-
views.py | 6 +++---
3 files changed, 6 insertions(+), 6 deletions(-)
diff --git a/feeds.py b/feeds.py
index 8341f06..dde051b 100644
--- a/feeds.py
+++ b/feeds.py
@@ -60,7 +60,7 @@ class IcalFeed(Feed):
try:
timetable = Timetable.objects.get(year__slug=year_slug,
slug=timetable_slug)
- group = Group.objects.get(timetable=timetable.source,
+ group = Group.objects.get(source=timetable.source,
slug=group_slug)
except:
raise ObjectDoesNotExist
@@ -106,7 +106,7 @@ class RSSFeed(Feed):
try:
self.timetable = Timetable.objects.get(year__slug=year_slug,
slug=timetable_slug)
- self.group = Group.objects.get(timetable=self.timetable.source,
+ self.group = Group.objects.get(source=self.timetable.source,
slug=group_slug)
except:
raise ObjectDoesNotExist
diff --git a/models.py b/models.py
index 3c9fa27..6124b59 100644
--- a/models.py
+++ b/models.py
@@ -92,7 +92,7 @@ class GroupManager(Manager):
return self.get_queryset().filter(groups_criteria,
Q(semester=None) | Q(semester=group.semester),
mention=group.mention,
- timetable=group.timetable)
+ source=group.source)
class Group(SlugModel):
diff --git a/views.py b/views.py
index 3179ec5..2bf596b 100644
--- a/views.py
+++ b/views.py
@@ -37,7 +37,7 @@ def mention_list(request, year_slug):
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.source, hidden=False).order_by("name")
+ groups = Group.objects.filter(source=timetable.source, hidden=False).order_by("name")
start, _ = get_week(*get_current_week())
end = start + datetime.timedelta(weeks=4)
@@ -89,14 +89,14 @@ def timetable_common(request, obj, year=None, week=None, timetable=None):
def timetable(request, year_slug, timetable_slug, group_slug, year=None, week=None):
timetable = get_object_or_404(Timetable, year__slug=year_slug, slug=timetable_slug)
- group = get_object_or_404(Group, slug=group_slug, timetable=timetable.source)
+ group = get_object_or_404(Group, slug=group_slug, source=timetable.source)
return timetable_common(request, group, year, week, timetable)
def calendars(request, year_slug, timetable_slug, group_slug):
timetable = get_object_or_404(Timetable, year__slug=year_slug,
slug=timetable_slug)
- group = get_object_or_404(Group, timetable=timetable.source, slug=group_slug)
+ group = get_object_or_404(Group, source=timetable.source, slug=group_slug)
groups = Group.objects.get_parents(group).annotate(length=Length("subgroup")) \
.order_by("length")
--
cgit v1.2.1
From 3b9eab819c603b1d3b2b59d79258ce6a05aa24b9 Mon Sep 17 00:00:00 2001
From: Alban Gruin
Date: Mon, 27 Nov 2017 16:34:27 +0100
Subject: Inversion des colonnes à trier pour le groupe dans l’interface
d’admin
---
admin.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/admin.py b/admin.py
index fc0ada1..5af6cb1 100644
--- a/admin.py
+++ b/admin.py
@@ -53,7 +53,7 @@ class GroupAdmin(admin.ModelAdmin):
list_display = ("name", "source", "hidden",)
list_editable = ("hidden",)
list_filter = ("source__timetables",)
- ordering = ("name", "source",)
+ ordering = ("source", "name",)
readonly_fields = ("celcat_name", "mention", "semester", "subgroup",)
actions = (make_hidden, make_visible,)
--
cgit v1.2.1
From fb6ea65e7d32dcf3d697e6c63985a018ada4d6b1 Mon Sep 17 00:00:00 2001
From: Alban Gruin
Date: Mon, 27 Nov 2017 16:37:31 +0100
Subject: Ajout d’un gestionnaire pour Timetable récupérant automatiquement les
années Réduit considérablement le nombre d’appels effectués dans l’interface
d’administration
---
models.py | 7 +++++++
views.py | 2 +-
2 files changed, 8 insertions(+), 1 deletion(-)
diff --git a/models.py b/models.py
index 6124b59..b8c67d6 100644
--- a/models.py
+++ b/models.py
@@ -62,7 +62,14 @@ class Source(models.Model):
verbose_name_plural = "sources d’emploi du temps"
+class TimetableManager(Manager):
+ def get_queryset(self):
+ return super(Manager, self).get_queryset().select_related("year")
+
+
class Timetable(SlugModel):
+ objects = TimetableManager()
+
year = models.ForeignKey(Year, on_delete=models.CASCADE,
verbose_name="année")
name = models.CharField(max_length=64, verbose_name="nom")
diff --git a/views.py b/views.py
index 2bf596b..97a1d28 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).select_related("year")
+ timetables = Timetable.objects.order_by("name").filter(year=year)
return render(request, "index.html", {"year": year, "elements": timetables})
--
cgit v1.2.1
From 90414449f9f74b675d3793c2c48a796f9bfe4b29 Mon Sep 17 00:00:00 2001
From: Alban Gruin
Date: Mon, 27 Nov 2017 17:14:14 +0100
Subject: Adaptation de la commande de mise à jour des emplois du temps aux
changements effectués sur la structure de la base de données
---
management/commands/_private.py | 16 ++++++++--------
management/commands/timetables.py | 33 +++++++++++++++++----------------
2 files changed, 25 insertions(+), 24 deletions(-)
diff --git a/management/commands/_private.py b/management/commands/_private.py
index 4dd9262..171b6e9 100644
--- a/management/commands/_private.py
+++ b/management/commands/_private.py
@@ -19,7 +19,7 @@ import re
from bs4 import BeautifulSoup
from django.utils import timezone
-from edt.models import Group, Room, Course
+from edt.models import Course, Group, Room
from edt.utils import get_week
import requests
@@ -30,10 +30,10 @@ def add_time(date, time):
delta = datetime.timedelta(hours=ptime.hour, minutes=ptime.minute)
return date + delta
-def delete_courses_in_week(timetable, year, week, today):
+def delete_courses_in_week(source, year, week, today):
start, end = get_week(year, week)
Course.objects.filter(begin__gte=max(start, today), begin__lt=end,
- timetable=timetable).delete()
+ source=source).delete()
def get_from_db_or_create(cls, **kwargs):
obj = cls.objects.all().filter(**kwargs)
@@ -45,7 +45,7 @@ def get_from_db_or_create(cls, **kwargs):
return obj
-def get_event(timetable, event, event_week, today):
+def get_event(source, event, event_week, today):
"""Renvoie une classe Course à partir d’un événement récupéré par BS4"""
# On récupère la date de l’évènement à partir de la semaine
# et de la semaine référencée, puis l’heure de début et de fin
@@ -58,10 +58,10 @@ def get_event(timetable, event, event_week, today):
return
# Création de l’objet cours
- course = Course.objects.create(timetable=timetable, begin=begin, end=end)
+ course = Course.objects.create(source=source, begin=begin, end=end)
# On récupère les groupes concernés par les cours
- groups = [get_from_db_or_create(Group, timetable=timetable,
+ groups = [get_from_db_or_create(Group, source=source,
celcat_name=item.text)
for item in event.resources.group.find_all("item")]
course.groups.add(*groups)
@@ -96,7 +96,7 @@ def get_event(timetable, event, event_week, today):
return course
-def get_events(timetable, soup, weeks_in_soup, today, year=None, week=None):
+def get_events(source, soup, weeks_in_soup, today, year=None, week=None):
"""Récupère tous les cours disponibles dans l’emploi du temps Celcat.
Le traîtement se limitera à la semaine indiquée si il y en a une."""
for event in soup.find_all("event"):
@@ -110,7 +110,7 @@ def get_events(timetable, soup, weeks_in_soup, today, year=None, week=None):
year is None or week is None) and \
event.resources.group is not None and \
event.starttime is not None and event.endtime is not None:
- course = get_event(timetable, event, event_week, today)
+ course = get_event(source, event, event_week, today)
# On renvoie le cours si il n’est pas nul
if course is not None:
diff --git a/management/commands/timetables.py b/management/commands/timetables.py
index ff00c8f..9734d13 100644
--- a/management/commands/timetables.py
+++ b/management/commands/timetables.py
@@ -20,12 +20,12 @@ from django.core.management.base import BaseCommand
from django.db import transaction
from django.db.models import Min
-from edt.models import Course, Timetable
+from edt.models import Course, Source
from edt.utils import get_week, tz_now
from ._private import delete_courses_in_week, get_events, get_update_date, get_weeks, get_xml
@transaction.atomic
-def process_timetable_week(timetable, soup, weeks_in_soup, force, year=None, week=None):
+def process_timetable_week(source, soup, weeks_in_soup, force, year=None, week=None):
if year is not None and week is not None:
begin, end = get_week(year, week)
@@ -41,7 +41,7 @@ def process_timetable_week(timetable, soup, weeks_in_soup, force, year=None, wee
today = tz_now()
# On récupère la mise à jour la plus ancienne dans les cours de l’emploi du temps
- last_update_date = Course.objects.filter(timetable=timetable)
+ last_update_date = Course.objects.filter(source=source)
if today is not None:
# Cette date concerne les éléments commençant à partir d’aujourd’hui si la valeur
@@ -71,7 +71,7 @@ def process_timetable_week(timetable, soup, weeks_in_soup, force, year=None, wee
if year is not None and week is not None:
# On efface la semaine à partir de maintenant si jamais
# on demande le traitement d’une seule semaine
- delete_courses_in_week(timetable, year, week, today)
+ delete_courses_in_week(source, year, week, today)
else:
# Sinon, on efface tous les cours à partir de maintenant.
# Précisément, on prend la plus grande valeur entre la première semaine
@@ -80,26 +80,26 @@ def process_timetable_week(timetable, soup, weeks_in_soup, force, year=None, wee
if not force:
# Si jamais on force la MàJ, on efface tout à partir de la première semaine
delete_from = max(delete_from, today)
- Course.objects.filter(timetable=timetable, begin__gte=delete_from).delete()
+ Course.objects.filter(source=source, begin__gte=delete_from).delete()
# Tous les cours commençant sur la période traitée
# sont parsés, puis enregistrés dans la base de données.
- for course in get_events(timetable, soup, weeks_in_soup, today, year, week):
+ for course in get_events(source, soup, weeks_in_soup, today, year, week):
course.save()
# On renseigne la date de mise à jour de Celcat, à des fins de statistiques
- timetable.last_update_date = new_update_date
- timetable.save()
+ source.last_update_date = new_update_date
+ source.save()
-def process_timetable(timetable, force, year=None, weeks=None):
- soup = get_xml(timetable.url)
+def process_timetable(source, force, year=None, weeks=None):
+ soup = get_xml(source.url)
weeks_in_soup = get_weeks(soup)
if year is not None and weeks is not None:
for week in weeks:
- process_timetable_week(timetable, soup, weeks_in_soup, force, year, week)
+ process_timetable_week(source, soup, weeks_in_soup, force, year, week)
else:
- process_timetable_week(timetable, soup, weeks_in_soup, force)
+ process_timetable_week(source, soup, weeks_in_soup, force)
class Command(BaseCommand):
@@ -131,16 +131,17 @@ class Command(BaseCommand):
elif year is None:
year = options["year"][0]
- for timetable in Timetable.objects.all():
- self.stdout.write("Processing {0}".format(timetable))
+ for source in Source.objects.all():
+ timetables = ", ".join([str(timetable) for timetable in source.timetables.all()])
+ self.stdout.write("Processing {0}".format(timetables))
try:
- process_timetable(timetable, options["force"], year, weeks)
+ process_timetable(source, options["force"], year, weeks)
except KeyboardInterrupt:
break
except Exception:
self.stderr.write(
- self.style.ERROR("Failed to process {0}:".format(timetable))
+ self.style.ERROR("Failed to process {0}:".format(timetables))
)
self.stderr.write(self.style.ERROR(traceback.format_exc()))
errcount += 1
--
cgit v1.2.1
From a5b0d082a728b06e5e02f4d64632ac0b0a572aec Mon Sep 17 00:00:00 2001
From: Alban Gruin
Date: Mon, 27 Nov 2017 17:18:41 +0100
Subject: Adaptation des commandes cleancourses et listtimetables au
changements
---
management/commands/cleancourses.py | 8 ++++----
management/commands/listtimetables.py | 13 ++++++-------
2 files changed, 10 insertions(+), 11 deletions(-)
diff --git a/management/commands/cleancourses.py b/management/commands/cleancourses.py
index f6041ef..310c843 100644
--- a/management/commands/cleancourses.py
+++ b/management/commands/cleancourses.py
@@ -22,15 +22,15 @@ class Command(BaseCommand):
help = "Remove all courses and groups from the database"
def add_arguments(self, parser):
- parser.add_argument("--timetable", type=int, nargs="+")
+ parser.add_argument("--source", type=int, nargs="+")
def handle(self, *args, **options):
with transaction.atomic():
- if options["timetable"] is None:
+ if options["source"] is None:
Course.objects.all().delete()
Group.objects.all().delete()
else:
- Course.objects.filter(timetable__id__in=options["timetable"]).delete()
- Group.objects.filter(timetable__id__in=options["timetable"]).delete()
+ Course.objects.filter(source__id__in=options["source"]).delete()
+ Group.objects.filter(source__id__in=options["source"]).delete()
self.stdout.write(self.style.SUCCESS("Done."))
diff --git a/management/commands/listtimetables.py b/management/commands/listtimetables.py
index 6df7ba5..171fc2b 100644
--- a/management/commands/listtimetables.py
+++ b/management/commands/listtimetables.py
@@ -14,7 +14,7 @@
# along with celcatsanitizer. If not, see .
from django.core.management.base import BaseCommand
-from edt.models import Timetable
+from edt.models import Source
class Command(BaseCommand):
@@ -24,14 +24,13 @@ class Command(BaseCommand):
parser.add_argument("--order-by-id", action="store_true")
def handle(self, *args, **options):
- timetables = Timetable.objects.all()
+ sources = Source.objects.all()
if options["order_by_id"]:
- timetables = timetables.order_by("id")
- else:
- timetables = timetables.order_by("year__name", "name")
+ sources = sources.order_by("id")
- for timetable in timetables:
- self.stdout.write("{0} (id: {1})".format(timetable, timetable.id))
+ for source in sources:
+ self.stdout.write("{0}\t: {1} (id: {2})".format(", ".join([str(timetable) for timetable in source.timetables.all()]),
+ source, source.id))
self.stdout.write("")
self.stdout.write(self.style.SUCCESS("Done."))
--
cgit v1.2.1
From 8a71b0a55847e8783006b5a9514f64e0129578c9 Mon Sep 17 00:00:00 2001
From: Alban Gruin
Date: Mon, 27 Nov 2017 17:23:11 +0100
Subject: Fonction pour formater les emplois du temps à partir d’une source
---
management/commands/listtimetables.py | 4 ++--
management/commands/timetables.py | 5 ++---
models.py | 4 ++++
3 files changed, 8 insertions(+), 5 deletions(-)
diff --git a/management/commands/listtimetables.py b/management/commands/listtimetables.py
index 171fc2b..25f641b 100644
--- a/management/commands/listtimetables.py
+++ b/management/commands/listtimetables.py
@@ -29,8 +29,8 @@ class Command(BaseCommand):
sources = sources.order_by("id")
for source in sources:
- self.stdout.write("{0}\t: {1} (id: {2})".format(", ".join([str(timetable) for timetable in source.timetables.all()]),
- source, source.id))
+ self.stdout.write("{0}\t: {1} (id: {2})".format(source.formatted_timetables,
+ source, source.id))
self.stdout.write("")
self.stdout.write(self.style.SUCCESS("Done."))
diff --git a/management/commands/timetables.py b/management/commands/timetables.py
index 9734d13..86f389e 100644
--- a/management/commands/timetables.py
+++ b/management/commands/timetables.py
@@ -132,8 +132,7 @@ class Command(BaseCommand):
year = options["year"][0]
for source in Source.objects.all():
- timetables = ", ".join([str(timetable) for timetable in source.timetables.all()])
- self.stdout.write("Processing {0}".format(timetables))
+ self.stdout.write("Processing {0}".format(source.formatted_timetables))
try:
process_timetable(source, options["force"], year, weeks)
@@ -141,7 +140,7 @@ class Command(BaseCommand):
break
except Exception:
self.stderr.write(
- self.style.ERROR("Failed to process {0}:".format(timetables))
+ self.style.ERROR("Failed to process {0}:".format(source.formatted_timetables))
)
self.stderr.write(self.style.ERROR(traceback.format_exc()))
errcount += 1
diff --git a/models.py b/models.py
index b8c67d6..363cf33 100644
--- a/models.py
+++ b/models.py
@@ -56,6 +56,10 @@ class Source(models.Model):
def __str__(self):
return self.url
+ @property
+ def formatted_timetables(self):
+ return ", ".join([str(timetable) for timetable in self.timetables.iterator()])
+
class Meta:
verbose_name = "source d’emploi du temps"
--
cgit v1.2.1
From af055dbf703af0eca03862194de1a0f34ac1fcc5 Mon Sep 17 00:00:00 2001
From: Alban Gruin
Date: Sun, 3 Dec 2017 15:27:04 +0100
Subject: Ajout d’un fichier requirements.txt pour pip, modification du readme
---
README.md | 27 ++++++++++++++++++++++-----
requirements.txt | 6 ++++++
2 files changed, 28 insertions(+), 5 deletions(-)
create mode 100644 requirements.txt
diff --git a/README.md b/README.md
index 7dc9d5c..c0a5f3c 100644
--- a/README.md
+++ b/README.md
@@ -56,18 +56,35 @@ $ pip install --upgrade pip
#### Installation des dépendances
+Vous pouvez demander à pip d’installer les dépendances à partir du
+fichier requirements.txt présent dans le dépôt :
+
+```bash
+$ pip install -r requirements.txt
+```
+
+pip installera Django, BeautifulSoup, icalendar, requests, gunicorn,
+psycopg2 ainsi que toutes leurs dépendances. C’est la manière
+recommandée de l’installer dans un environnement de production.
+
+Alternativement, si vous vous montez un environnement de
+développement dans le but de mettre à jour les dépendances, il est
+possible d’installer les dépendances à la main :
+
```bash
-$ pip install requests django beautifulsoup4 icalendar
+$ pip install requests django beautifulsoup4 icalendar psycopg2 gunicorn
```
-Si vous utilisez PostgreSQL, vous allez avoir besoin du driver
-psycopg2 :
+Il est aussi possible d’installer
+[django-debug-toolbar](https://django-debug-toolbar.readthedocs.io/en/stable/)
+(pour le développement seulement) :
```bash
-$ pip install psycopg2
+$ pip install django-debug-toolbar
```
-SQLite n’a pas besoin de driver.
+Réferez-vous à la documentation de django-debug-toolbar pour
+l’installer et le configurer sur votre environnement.
#### Création du répertoire Django
diff --git a/requirements.txt b/requirements.txt
new file mode 100644
index 0000000..2ae5c48
--- /dev/null
+++ b/requirements.txt
@@ -0,0 +1,6 @@
+beautifulsoup4==4.6.0
+Django==1.11.7
+gunicorn==19.7.1
+icalendar==4.0.0
+psycopg2==2.7.3.2
+requests==2.18.4
--
cgit v1.2.1
From 81cb9b8c6a96fa8529c6d7f6ca55dfd56a1f2e5d Mon Sep 17 00:00:00 2001
From: Alban Gruin
Date: Thu, 11 Jan 2018 14:09:33 +0100
Subject: On remplace les dans le champ remarque par des retours à la
ligne lors du parsage, puis on les remplace à nouveau par des lors du
rendu des templates.
---
management/commands/_private.py | 2 +-
templates/timetable_common.html | 2 +-
2 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/management/commands/_private.py b/management/commands/_private.py
index 171b6e9..0a3d5fb 100644
--- a/management/commands/_private.py
+++ b/management/commands/_private.py
@@ -68,7 +68,7 @@ def get_event(source, event, event_week, today):
# On récupère le champ « remarque »
if event.notes is not None:
- course.notes = event.notes.text
+ course.notes = "\n".join(event.notes.find_all(text=True))
# On récupère le champ « nom »
if event.resources.module is not None:
diff --git a/templates/timetable_common.html b/templates/timetable_common.html
index d9e4ec7..21300f1 100644
--- a/templates/timetable_common.html
+++ b/templates/timetable_common.html
@@ -6,7 +6,7 @@
{{ course }}{% if course.type %} ({{ course.type }}){% endif %}, de {{ course.begin|date:"H:i" }} à {{ course.end|date:"H:i" }}{% if course.rooms.all|length > 0 %} {% if group_mode %}{{ course.rooms.all|format_rooms }}{% else %}{{ course.groups.all|join:", " }}{% endif %}{% endif %}{% if course.notes %}
- Remarques : {{ course.notes }}{% endif %}
+ Remarques : {{ course.notes|linebreaksbr }}{% endif %}
{% endfor %}
{% empty %}
--
cgit v1.2.1
From e710ab4b1293ecbd0f61649b4abb3072379f362f Mon Sep 17 00:00:00 2001
From: Alban Gruin
Date: Thu, 11 Jan 2018 14:09:33 +0100
Subject: On remplace les dans le champ remarque par des retours à la
ligne lors du parsage, puis on les remplace à nouveau par des lors du
rendu des templates.
---
management/commands/_private.py | 2 +-
templates/timetable_common.html | 2 +-
2 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/management/commands/_private.py b/management/commands/_private.py
index 4dd9262..17a99fe 100644
--- a/management/commands/_private.py
+++ b/management/commands/_private.py
@@ -68,7 +68,7 @@ def get_event(timetable, event, event_week, today):
# On récupère le champ « remarque »
if event.notes is not None:
- course.notes = event.notes.text
+ course.notes = "\n".join(event.notes.find_all(text=True))
# On récupère le champ « nom »
if event.resources.module is not None:
diff --git a/templates/timetable_common.html b/templates/timetable_common.html
index 62b1d71..533f6c9 100644
--- a/templates/timetable_common.html
+++ b/templates/timetable_common.html
@@ -6,7 +6,7 @@
{{ course }}{% if course.type %} ({{ course.type }}){% endif %}, 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 %}
- Remarques : {{ course.notes }}{% endif %}
+ Remarques : {{ course.notes|linebreaksbr }}{% endif %}
{% endfor %}
{% empty %}
--
cgit v1.2.1
From 32cfe5e60b60d7859f6e4fb3b4cba03d47293918 Mon Sep 17 00:00:00 2001
From: Alban Gruin
Date: Thu, 11 Jan 2018 20:50:35 +0100
Subject: Version 0.12.1
---
__init__.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/__init__.py b/__init__.py
index 03ae5ff..f75615a 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.12.0"
+VERSION = "0.12.1"
__version__ = VERSION
default_app_config = "edt.apps.EdtConfig"
--
cgit v1.2.1
From 8f9c5eb32c6cf27aa84d1cbf14b7aaaa00acc0b9 Mon Sep 17 00:00:00 2001
From: Alban Gruin
Date: Tue, 16 Jan 2018 20:31:20 +0100
Subject: Utilisation des valeurs par défaut de reduce()
---
models.py | 10 ++++------
1 file changed, 4 insertions(+), 6 deletions(-)
diff --git a/models.py b/models.py
index 363cf33..56c1845 100644
--- a/models.py
+++ b/models.py
@@ -93,12 +93,10 @@ class Timetable(SlugModel):
class GroupManager(Manager):
def get_parents(self, group):
- groups_criteria = Q(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)])
+ groups_criteria = reduce(lambda x, y: x | y,
+ [Q(subgroup=group.subgroup[:i])
+ for i in range(1, len(group.subgroup) + 1)],
+ Q(subgroup=""))
return self.get_queryset().filter(groups_criteria,
Q(semester=None) | Q(semester=group.semester),
--
cgit v1.2.1
From b55297c4d0de64501a6baf3b1f255210de492e97 Mon Sep 17 00:00:00 2001
From: Alban Gruin
Date: Wed, 17 Jan 2018 12:16:09 +0100
Subject: Correction d’URLs
---
urls.py | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/urls.py b/urls.py
index 4c4abe3..22011c4 100644
--- a/urls.py
+++ b/urls.py
@@ -20,8 +20,8 @@ urlpatterns = [
url(r"^$", views.index, name="index"),
url(r"^pages/", include("django.contrib.flatpages.urls")),
url(r"^salles/$", views.rooms, name="rooms"),
- url(r"^salles/(?P[-\w]+)$", views.room_timetable, name="room-timetable"),
- url(r"^salles/(?P[-\w]+)/(?P[0-9]{4})/(?P[0-4]?[0-9]|5[0-3])/$", views.room_timetable, name="room-timetable"),
+ url(r"^salles/(?P[-\w]+)/$", views.room_timetable, name="room-timetable"),
+ url(r"^salles/(?P[-\w]+)/(?P[0-9]{4})/(?P[0-4]?[0-9]|5[0-3])$", views.room_timetable, name="room-timetable"),
url(r"^(?P[-\w]+)/$", views.mention_list, name="mentions"),
url(r"^(?P[-\w]+)/(?P[-\w]+)/$", views.group_list, name="groups"),
url(r"^(?P[-\w]+)/(?P[-\w]+)/(?P[-\w]+)/$", views.timetable, name="timetable"),
--
cgit v1.2.1
From a2fcd4c7c42b6c02ff8ff7dac4aa23b3d17407de Mon Sep 17 00:00:00 2001
From: Alban Gruin
Date: Wed, 17 Jan 2018 12:34:11 +0100
Subject: Base du formulaire de QSJPS
---
forms.py | 21 +++++++++++++++++++++
templates/form_qsjps.html | 10 ++++++++++
urls.py | 3 ++-
views.py | 20 +++++++++++++++++++-
4 files changed, 52 insertions(+), 2 deletions(-)
create mode 100644 forms.py
create mode 100644 templates/form_qsjps.html
diff --git a/forms.py b/forms.py
new file mode 100644
index 0000000..34081c0
--- /dev/null
+++ b/forms.py
@@ -0,0 +1,21 @@
+# Copyright (C) 2018 Alban Gruin
+#
+# celcatsanitizer is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as published
+# by the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# celcatsanitizer is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Affero General Public License for more details.
+#
+# You should have received a copy of the GNU Affero General Public License
+# along with celcatsanitizer. If not, see .
+
+from django import forms
+
+
+class QSJPSForm(forms.Form):
+ begin = forms.DateTimeField(label="Début")
+ end = forms.DateTimeField(label="Fin")
diff --git a/templates/form_qsjps.html b/templates/form_qsjps.html
new file mode 100644
index 0000000..2d97a89
--- /dev/null
+++ b/templates/form_qsjps.html
@@ -0,0 +1,10 @@
+{% extends "index.html" %}
+
+{% block title %}QSJPS – {% endblock %}
+
+{% block body %}
+
+{% endblock %}
diff --git a/urls.py b/urls.py
index 22011c4..f24bbba 100644
--- a/urls.py
+++ b/urls.py
@@ -1,4 +1,4 @@
-# Copyright (C) 2017 Alban Gruin
+# Copyright (C) 2017-2018 Alban Gruin
#
# celcatsanitizer is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as published
@@ -20,6 +20,7 @@ urlpatterns = [
url(r"^$", views.index, name="index"),
url(r"^pages/", include("django.contrib.flatpages.urls")),
url(r"^salles/$", views.rooms, name="rooms"),
+ url(r"^salles/qsjps$", views.qsjps, name="qsjps"),
url(r"^salles/(?P[-\w]+)/$", views.room_timetable, name="room-timetable"),
url(r"^salles/(?P[-\w]+)/(?P[0-9]{4})/(?P[0-4]?[0-9]|5[0-3])$", views.room_timetable, name="room-timetable"),
url(r"^(?P[-\w]+)/$", views.mention_list, name="mentions"),
diff --git a/views.py b/views.py
index 97a1d28..e8fc259 100644
--- a/views.py
+++ b/views.py
@@ -1,4 +1,4 @@
-# Copyright (C) 2017 Alban Gruin
+# Copyright (C) 2017-2018 Alban Gruin
#
# celcatsanitizer is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as published
@@ -19,7 +19,9 @@ from django.db.models import Count, Max
from django.db.models.functions import ExtractWeek, ExtractYear, Length
from django.http import Http404
from django.shortcuts import get_object_or_404, render
+from django.views.decorators.csrf import csrf_exempt
+from .forms import QSJPSForm
from .models import Course, Group, Room, Timetable, Year
from .utils import get_current_week, get_current_or_next_week, get_week, group_courses
@@ -144,5 +146,21 @@ def room_timetable(request, room_slug, year=None, week=None):
room = get_object_or_404(Room, slug=room_slug)
return timetable_common(request, room, year, week)
+@csrf_exempt
+def qsjps(request):
+ if request.method == "POST":
+ # Si on traite un formulaire, on le valide
+ form = QSJPSForm(request.POST)
+ if form.is_valid():
+ # Formulaire validé
+ return None
+ else:
+ # Formulaire invalide, on le raffiche avec une erreur
+ return render(request, "form_qsjps.html", {"form": form})
+ else:
+ # Sinon, affichage d’un formulaire vide
+ form = QSJPSForm()
+ return render(request, "form_qsjps.html", {"form": form})
+
def ctx_processor(request):
return {"celcatsanitizer_version": edt.VERSION}
--
cgit v1.2.1
From d2a52ee6844d363ae020a49781b46402d7717a27 Mon Sep 17 00:00:00 2001
From: Alban Gruin
Date: Wed, 17 Jan 2018 16:09:28 +0100
Subject: Ajout d’un champ jour au formulaire qsjps
Valeurs par défaut des champs du formulaire
Format de validation
---
forms.py | 20 ++++++++++++++++++--
1 file changed, 18 insertions(+), 2 deletions(-)
diff --git a/forms.py b/forms.py
index 34081c0..d886dbf 100644
--- a/forms.py
+++ b/forms.py
@@ -13,9 +13,25 @@
# You should have received a copy of the GNU Affero General Public License
# along with celcatsanitizer. If not, see .
+from datetime import timedelta
from django import forms
+from .utils import tz_now
+
class QSJPSForm(forms.Form):
- begin = forms.DateTimeField(label="Début")
- end = forms.DateTimeField(label="Fin")
+ day = forms.DateField(label="Jour")
+
+ # Ces champs n’acceptent pas les secondes
+ begin = forms.TimeField(label="Heure de début", input_formats=("%H:%M",))
+ end = forms.TimeField(label="Heure de fin", input_formats=("%H:%M",))
+
+ def __init__(self, *args, **kwargs):
+ super(QSJPSForm, self).__init__(*args, **kwargs)
+
+ # On définit les valeurs par défaut de cette manière pour
+ # éviter les mauvaises surprises. On retire les secondes des
+ # heures de début et de fin.
+ self.fields["day"].initial = tz_now()
+ self.fields["begin"].initial = tz_now().strftime("%H:%M")
+ self.fields["end"].initial = (tz_now() + timedelta(hours=1)).strftime("%H:%M")
--
cgit v1.2.1
From 97f62a20f1716a9915358959b2e25912e8b17a90 Mon Sep 17 00:00:00 2001
From: Alban Gruin
Date: Wed, 17 Jan 2018 16:50:08 +0100
Subject: Utilisation des champs de formulaires date et time à la place de text
---
forms.py | 10 +++++++---
1 file changed, 7 insertions(+), 3 deletions(-)
diff --git a/forms.py b/forms.py
index d886dbf..0d823eb 100644
--- a/forms.py
+++ b/forms.py
@@ -14,17 +14,21 @@
# along with celcatsanitizer. If not, see .
from datetime import timedelta
+
from django import forms
+from django.forms.widgets import DateInput, TimeInput
from .utils import tz_now
class QSJPSForm(forms.Form):
- day = forms.DateField(label="Jour")
+ day = forms.DateField(label="Jour", widget=DateInput(attrs={"type": "date"}))
# Ces champs n’acceptent pas les secondes
- begin = forms.TimeField(label="Heure de début", input_formats=("%H:%M",))
- end = forms.TimeField(label="Heure de fin", input_formats=("%H:%M",))
+ begin = forms.TimeField(label="Heure de début", input_formats=("%H:%M",),
+ widget=TimeInput(attrs={"type": "time"}))
+ end = forms.TimeField(label="Heure de fin", input_formats=("%H:%M",),
+ widget=TimeInput(attrs={"type": "time"}))
def __init__(self, *args, **kwargs):
super(QSJPSForm, self).__init__(*args, **kwargs)
--
cgit v1.2.1
From 8418b6b82e892a435b16f2be90ae94bb7961416b Mon Sep 17 00:00:00 2001
From: Alban Gruin
Date: Wed, 17 Jan 2018 21:19:15 +0100
Subject: Validation du formulaire
---
forms.py | 14 ++++++++++++++
1 file changed, 14 insertions(+)
diff --git a/forms.py b/forms.py
index 0d823eb..fbd6d02 100644
--- a/forms.py
+++ b/forms.py
@@ -39,3 +39,17 @@ class QSJPSForm(forms.Form):
self.fields["day"].initial = tz_now()
self.fields["begin"].initial = tz_now().strftime("%H:%M")
self.fields["end"].initial = (tz_now() + timedelta(hours=1)).strftime("%H:%M")
+
+ def clean(self):
+ form_data = self.cleaned_data
+
+ # On vérifie que les valeurs de début et de fin sont correctes
+ # (si ce n’est pas le cas, elles ne se trouvent pas dans le
+ # dictionnaire), et, le cas échéant, on vérifie que l’heure de
+ # début est strictement inférieure à l’heure de fin.
+ if "begin" in form_data and "end" in form_data and \
+ form_data["begin"] >= form_data["end"]:
+ # Si l’heure de fin est plus petite ou égale, on affiche
+ # une erreur.
+ self._errors["end"].append("L’heure de début doit être supérieure à celle de fin.")
+ return form_data
--
cgit v1.2.1
From dd1758f543d45376ea64d6846152fbf229b936f9 Mon Sep 17 00:00:00 2001
From: Alban Gruin
Date: Wed, 17 Jan 2018 19:13:33 +0100
Subject: Meilleur formatage du formulaire avec des tableaux
---
static/celcatsanitizer/style.css | 6 ++++++
templates/form_qsjps.html | 12 ++++++++++--
2 files changed, 16 insertions(+), 2 deletions(-)
diff --git a/static/celcatsanitizer/style.css b/static/celcatsanitizer/style.css
index 825d140..b9d5dd9 100644
--- a/static/celcatsanitizer/style.css
+++ b/static/celcatsanitizer/style.css
@@ -36,6 +36,12 @@ li.course {
margin-bottom: 20px;
}
+th {
+ text-align: right;
+ font-weight: normal;
+ vertical-align: top;
+}
+
@media print {
body, .content {
max-width: none;
diff --git a/templates/form_qsjps.html b/templates/form_qsjps.html
index 2d97a89..3a9bcf8 100644
--- a/templates/form_qsjps.html
+++ b/templates/form_qsjps.html
@@ -3,8 +3,16 @@
{% block title %}QSJPS – {% endblock %}
{% block body %}
+
Trouver une salle
{% endblock %}
--
cgit v1.2.1
From 85112aafbcfe684b8b4d4fa2a47fd6cc2dcd2e25 Mon Sep 17 00:00:00 2001
From: Alban Gruin
Date: Wed, 17 Jan 2018 19:15:09 +0100
Subject: Format correct de la date
---
forms.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/forms.py b/forms.py
index fbd6d02..5e76bc1 100644
--- a/forms.py
+++ b/forms.py
@@ -36,7 +36,7 @@ class QSJPSForm(forms.Form):
# On définit les valeurs par défaut de cette manière pour
# éviter les mauvaises surprises. On retire les secondes des
# heures de début et de fin.
- self.fields["day"].initial = tz_now()
+ self.fields["day"].initial = tz_now().strftime("%Y-%m-%d")
self.fields["begin"].initial = tz_now().strftime("%H:%M")
self.fields["end"].initial = (tz_now() + timedelta(hours=1)).strftime("%H:%M")
--
cgit v1.2.1
From b1e5d205e37a2c0a5d4f42afe0bdce9e0fbf97dd Mon Sep 17 00:00:00 2001
From: Alban Gruin
Date: Thu, 18 Jan 2018 12:06:45 +0100
Subject: Modification de la gestion du formulaire qsjps
Renommage de la template contenant le formulaire
---
templates/form_qsjps.html | 18 ------------------
templates/qsjps_form.html | 18 ++++++++++++++++++
views.py | 11 ++++++-----
3 files changed, 24 insertions(+), 23 deletions(-)
delete mode 100644 templates/form_qsjps.html
create mode 100644 templates/qsjps_form.html
diff --git a/templates/form_qsjps.html b/templates/form_qsjps.html
deleted file mode 100644
index 3a9bcf8..0000000
--- a/templates/form_qsjps.html
+++ /dev/null
@@ -1,18 +0,0 @@
-{% extends "index.html" %}
-
-{% block title %}QSJPS – {% endblock %}
-
-{% block body %}
-
Trouver une salle
-
-{% endblock %}
diff --git a/templates/qsjps_form.html b/templates/qsjps_form.html
new file mode 100644
index 0000000..bc5e363
--- /dev/null
+++ b/templates/qsjps_form.html
@@ -0,0 +1,18 @@
+{% extends "index.html" %}
+
+{% block title %}Trouver une salle – {% endblock %}
+
+{% block body %}
+
Trouver une salle
+
+{% endblock %}
diff --git a/views.py b/views.py
index e8fc259..1c82b03 100644
--- a/views.py
+++ b/views.py
@@ -154,13 +154,14 @@ def qsjps(request):
if form.is_valid():
# Formulaire validé
return None
- else:
- # Formulaire invalide, on le raffiche avec une erreur
- return render(request, "form_qsjps.html", {"form": form})
+ # Si le formulaire est invalide, on ré-affiche le formulaire
+ # avec les erreurs
else:
- # Sinon, affichage d’un formulaire vide
+ # Si le formulaire n’a pas été soumis, on en instancie un
+ # nouveau
form = QSJPSForm()
- return render(request, "form_qsjps.html", {"form": form})
+
+ return render(request, "qsjps_form.html", {"form": form})
def ctx_processor(request):
return {"celcatsanitizer_version": edt.VERSION}
--
cgit v1.2.1
From 175e1c1139a77a02f3c52d77a4d2b118c2b2e578 Mon Sep 17 00:00:00 2001
From: Alban Gruin
Date: Thu, 18 Jan 2018 12:17:08 +0100
Subject: Ajout d’une template pour lister les salles trouvées
---
templates/qsjps.html | 14 ++++++++++++++
views.py | 2 +-
2 files changed, 15 insertions(+), 1 deletion(-)
create mode 100644 templates/qsjps.html
diff --git a/templates/qsjps.html b/templates/qsjps.html
new file mode 100644
index 0000000..73290de
--- /dev/null
+++ b/templates/qsjps.html
@@ -0,0 +1,14 @@
+{% extends "index.html" %}
+
+{% block title %}Trouver une salle – {% endblock %}
+
+{% block body %}
+
Trouver une salle entre {{ form.begin.value }} et {{ form.end.value }}
+
+ {% for room in rooms %}
+
{{ room }}
+ {% empty %}
+
Aucune salle trouvée
+ {% endfor %}
+
+{% endblock %}
diff --git a/views.py b/views.py
index 1c82b03..0231c9e 100644
--- a/views.py
+++ b/views.py
@@ -153,7 +153,7 @@ def qsjps(request):
form = QSJPSForm(request.POST)
if form.is_valid():
# Formulaire validé
- return None
+ return render(request, "qsjps.html", {"rooms": [], "form": form})
# Si le formulaire est invalide, on ré-affiche le formulaire
# avec les erreurs
else:
--
cgit v1.2.1
From 3a5e355f92881597c22a434ea300175a624586ea Mon Sep 17 00:00:00 2001
From: Alban Gruin
Date: Thu, 18 Jan 2018 13:26:37 +0100
Subject: Adaptation des tests à la nouvelle structure de la base de données
---
tests.py | 86 ++++++++++++++++++++++++++++++++++------------------------------
1 file changed, 46 insertions(+), 40 deletions(-)
diff --git a/tests.py b/tests.py
index 1425a84..ac6bdaf 100644
--- a/tests.py
+++ b/tests.py
@@ -14,7 +14,7 @@
# along with celcatsanitizer. If not, see .
from django.test import TestCase
-from .models import Course, Group, Timetable, Year
+from .models import Course, Group, Source, Timetable, Year
from .utils import tz_now
@@ -25,24 +25,27 @@ class CourseTestCase(TestCase):
self.year = Year(name="L2", slug="l2")
self.year.save()
- self.timetable = Timetable(year=self.year, name="Test timetable 2", url="http://example.org/", slug="test-timetable2")
+ source = Source(url="http://example.org/")
+ source.save()
+
+ self.timetable = Timetable(year=self.year, name="Test timetable 2", source=source, slug="test-timetable2")
self.timetable.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", source=source)
+ tda2 = Group.objects.create(celcat_name="L1 info s2 TDA2", source=source)
+ self.tpa21 = Group.objects.create(celcat_name="L1 info s2 TPA21", source=source)
- cmb = Group.objects.create(celcat_name="L1 info s2 CMB", 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", source=source)
+ tdb2 = Group.objects.create(celcat_name="L1 info s2 TDB2", source=source)
+ self.tpb21 = Group.objects.create(celcat_name="L1 info s2 TPB21", source=source)
for group in (cma, tda2, self.tpa21, cmb, tdb2, self.tpb21,):
- course = Course.objects.create(name="{0} course".format(group.name), type="cours", timetable=self.timetable, begin=dt, end=dt)
+ course = Course.objects.create(name="{0} course".format(group.name), type="cours", source=source, begin=dt, end=dt)
course.groups.add(group)
def test_get_courses_for_group(self):
- tpa21_courses = Course.objects.get_courses_for_group(self.tpa21)
- tpb21_courses = Course.objects.get_courses_for_group(self.tpb21)
+ tpa21_courses = Course.objects.get_courses(self.tpa21)
+ tpb21_courses = Course.objects.get_courses(self.tpb21)
tpa21_course_names = ["L1 info s2 CMA course", "L1 info s2 TDA2 course", "L1 info s2 TPA21 course"]
tpb21_course_names = ["L1 info s2 CMB course", "L1 info s2 TDB2 course", "L1 info s2 TPB21 course"]
@@ -58,29 +61,32 @@ class GroupTestCase(TestCase):
self.year = Year(name="L1", slug="l1")
self.year.save()
- self.timetable = Timetable(year=self.year, name="Test timetable", url="http://example.com/", slug="test-timetable")
+ self.source = Source(url="http://example.org/")
+ self.source.save()
+
+ self.timetable = Timetable(year=self.year, name="Test timetable", source=self.source, slug="test-timetable")
self.timetable.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", source=self.source)
+ Group.objects.create(celcat_name="L1 info s2 TDA2", source=self.source)
+ Group.objects.create(celcat_name="L1 info s2 TPA21", source=self.source)
- Group.objects.create(celcat_name="L1 info s2 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", source=self.source)
+ Group.objects.create(celcat_name="L1 info s2 TDB2", source=self.source)
+ Group.objects.create(celcat_name="L1 info s2 TPB21", source=self.source)
- Group.objects.create(celcat_name="L1 info (toutes sections et semestres confondus)", timetable=self.timetable)
+ Group.objects.create(celcat_name="L1 info (toutes sections et semestres confondus)", source=self.source)
def test_corresponds(self):
- cma = Group.objects.get(celcat_name="L1 info s2 CMA", 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", source=self.source)
+ tda2 = Group.objects.get(celcat_name="L1 info s2 TDA2", source=self.source)
+ tpa21 = Group.objects.get(celcat_name="L1 info s2 TPA21", source=self.source)
- cmb = Group.objects.get(celcat_name="L1 info s2 CMB", 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", source=self.source)
+ tdb2 = Group.objects.get(celcat_name="L1 info s2 TDB2", source=self.source)
+ tpb21 = Group.objects.get(celcat_name="L1 info s2 TPB21", source=self.source)
- general = Group.objects.get(celcat_name="L1 info (toutes sections et semestres confondus)", timetable=self.timetable)
+ general = Group.objects.get(celcat_name="L1 info (toutes sections et semestres confondus)", source=self.source)
self.assertFalse(cma.corresponds_to(*tda2.group_info))
self.assertFalse(cma.corresponds_to(*tpa21.group_info))
@@ -121,15 +127,15 @@ class GroupTestCase(TestCase):
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)
- tpa21 = Group.objects.get(name="L1 info s2 TPA21", timetable=self.timetable)
+ cma = Group.objects.get(name="L1 info s2 CMA", source=self.source)
+ tda2 = Group.objects.get(name="L1 info s2 TDA2", source=self.source)
+ tpa21 = Group.objects.get(name="L1 info s2 TPA21", source=self.source)
- 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", source=self.source)
+ tdb2 = Group.objects.get(name="L1 info s2 TDB2", source=self.source)
+ tpb21 = Group.objects.get(name="L1 info s2 TPB21", source=self.source)
- general = Group.objects.get(celcat_name="L1 info (toutes sections et semestres confondus)", timetable=self.timetable)
+ general = Group.objects.get(celcat_name="L1 info (toutes sections et semestres confondus)", source=self.source)
self.assertEqual(cma.celcat_name, "L1 info s2 CMA")
self.assertEqual(tda2.celcat_name, "L1 info s2 TDA2")
@@ -142,15 +148,15 @@ class GroupTestCase(TestCase):
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)
- tpa21 = Group.objects.get(celcat_name="L1 info s2 TPA21", timetable=self.timetable)
+ cma = Group.objects.get(celcat_name="L1 info s2 CMA", source=self.source)
+ tda2 = Group.objects.get(celcat_name="L1 info s2 TDA2", source=self.source)
+ tpa21 = Group.objects.get(celcat_name="L1 info s2 TPA21", source=self.source)
- cmb = Group.objects.get(celcat_name="L1 info s2 CMB", 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", source=self.source)
+ tdb2 = Group.objects.get(celcat_name="L1 info s2 TDB2", source=self.source)
+ tpb21 = Group.objects.get(celcat_name="L1 info s2 TPB21", source=self.source)
- general = Group.objects.get(celcat_name="L1 info (toutes sections et semestres confondus)", timetable=self.timetable)
+ general = Group.objects.get(celcat_name="L1 info (toutes sections et semestres confondus)", source=self.source)
self.assertEqual(cma.group_info, ("L1 info", 2, "A"))
self.assertEqual(tda2.group_info, ("L1 info", 2, "A2"))
--
cgit v1.2.1
From bc7cac9459ac3ae55a31e9dd215cfc8f054e5fb1 Mon Sep 17 00:00:00 2001
From: Alban Gruin
Date: Thu, 18 Jan 2018 13:35:29 +0100
Subject: get_courses() émet une exception si l’objet passé n’est ni un groupe
ni une salle
---
models.py | 2 ++
1 file changed, 2 insertions(+)
diff --git a/models.py b/models.py
index 56c1845..5de1df2 100644
--- a/models.py
+++ b/models.py
@@ -179,6 +179,8 @@ class CourseManager(Manager):
.prefetch_related("rooms")
elif isinstance(obj, Room):
qs = qs.filter(rooms__in=(obj,), **criteria)
+ else:
+ raise(TypeError, "obj must be a Group or a Room")
return qs.order_by("begin")
--
cgit v1.2.1
From 76fef8f3e0b3ad77f632a9e4d2c048607a1cf21b Mon Sep 17 00:00:00 2001
From: Alban Gruin
Date: Thu, 18 Jan 2018 13:42:49 +0100
Subject: Mise à jour des copyrights
---
management/commands/_private.py | 2 +-
models.py | 2 +-
tests.py | 2 +-
3 files changed, 3 insertions(+), 3 deletions(-)
diff --git a/management/commands/_private.py b/management/commands/_private.py
index 0a3d5fb..e78c3c2 100644
--- a/management/commands/_private.py
+++ b/management/commands/_private.py
@@ -1,4 +1,4 @@
-# Copyright (C) 2017 Alban Gruin
+# Copyright (C) 2017-2018 Alban Gruin
#
# celcatsanitizer is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as published
diff --git a/models.py b/models.py
index 5de1df2..4903b11 100644
--- a/models.py
+++ b/models.py
@@ -1,4 +1,4 @@
-# Copyright (C) 2017 Alban Gruin
+# Copyright (C) 2017-2018 Alban Gruin
#
# celcatsanitizer is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as published
diff --git a/tests.py b/tests.py
index ac6bdaf..8a21a3f 100644
--- a/tests.py
+++ b/tests.py
@@ -1,4 +1,4 @@
-# Copyright (C) 2017 Alban Gruin
+# Copyright (C) 2017-2018 Alban Gruin
#
# celcatsanitizer is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as published
--
cgit v1.2.1
From 7a4bdfda6df3ca46f4805c145a42793cacc0809f Mon Sep 17 00:00:00 2001
From: Alban Gruin
Date: Fri, 19 Jan 2018 18:02:22 +0100
Subject: Meilleur rendu du formulaire sur appareils mobiles
---
static/celcatsanitizer/style.css | 7 +++++++
templates/qsjps.html | 10 +++++-----
templates/qsjps_form.html | 2 +-
3 files changed, 13 insertions(+), 6 deletions(-)
diff --git a/static/celcatsanitizer/style.css b/static/celcatsanitizer/style.css
index b9d5dd9..5d05f9e 100644
--- a/static/celcatsanitizer/style.css
+++ b/static/celcatsanitizer/style.css
@@ -42,6 +42,13 @@ th {
vertical-align: top;
}
+@media screen and (max-width: 439px) {
+ th, td {
+ display: block;
+ text-align: left;
+ }
+}
+
@media print {
body, .content {
max-width: none;
diff --git a/templates/qsjps.html b/templates/qsjps.html
index 73290de..bb07b60 100644
--- a/templates/qsjps.html
+++ b/templates/qsjps.html
@@ -5,10 +5,10 @@
{% block body %}
Trouver une salle entre {{ form.begin.value }} et {{ form.end.value }}
- {% for room in rooms %}
-
{{ room }}
- {% empty %}
-
Aucune salle trouvée
- {% endfor %}
+ {% for room in rooms %}
+
{{ room }}
+ {% empty %}
+
Aucune salle trouvée
+ {% endfor %}
{% endblock %}
diff --git a/templates/qsjps_form.html b/templates/qsjps_form.html
index bc5e363..b68143f 100644
--- a/templates/qsjps_form.html
+++ b/templates/qsjps_form.html
@@ -9,7 +9,7 @@
{% for field in form.visible_fields %}
{{ field.label_tag }}
-
{{ field }}{% if field.errors %} {{ field.errors|join:" " }}{% endif %}
+
{{ field }}{% if field.errors %} {{ field.errors|join:" " }}{% endif %}
{% endfor %}
--
cgit v1.2.1
From d7dfa0c001a9b84541e1545e45d0536a87d6880e Mon Sep 17 00:00:00 2001
From: Alban Gruin
Date: Fri, 19 Jan 2018 19:20:21 +0100
Subject: Liens permettant de naviguer sur les pages des semaines suivantes et
précédentes
---
templates/timetable.html | 7 ++++++-
views.py | 7 +++++++
2 files changed, 13 insertions(+), 1 deletion(-)
diff --git a/templates/timetable.html b/templates/timetable.html
index 8fafeed..cc90ed5 100644
--- a/templates/timetable.html
+++ b/templates/timetable.html
@@ -1,4 +1,5 @@
{% extends "index.html" %}
+{% load dt_week %}
{% block head %}{% if group_mode %}
@@ -18,4 +19,8 @@
{% if last_update %}Dernière mise à jour le {{ last_update|date:"l j F o" }} à {{ last_update|date:"H:i" }}{% endif %}
{% include "timetable_common.html" %}
- {% if group_mode %}
+ {% if last_week is not None %}Semaine {{ last_week|dt_week }}{% if next_week is not None %} – {% endif %}{% endif %}{% if next_week is not None %}Semaine {{ next_week|dt_week }}{% endif %}
+ {% if group_mode %}{% if last_week is not None or next_week is not None %} {% endif %}
+ ICS – RSS – Atom{% endif %}
+
{% endblock %}
diff --git a/views.py b/views.py
index 0231c9e..10dcfa2 100644
--- a/views.py
+++ b/views.py
@@ -79,12 +79,19 @@ def timetable_common(request, obj, year=None, week=None, timetable=None):
if not courses.exists() and provided_week:
raise Http404
+ # Récupération des semaines suivantes et précédentes pour les
+ # afficher proprement dans l’emploi du temps
+ last_week = getattr(Course.objects.get_courses(obj, begin__lt=start).last(), "begin", None)
+ next_week = getattr(Course.objects.get_courses(obj, begin__gte=end).first(), "begin", None)
+
last_update = courses.aggregate(Max("last_update"))["last_update__max"]
grouped_courses = group_courses(courses)
return render(request, "timetable.html", {"group": obj, "courses": grouped_courses,
"last_update": last_update,
"year": year, "week": int(week),
+ "last_week": last_week,
+ "next_week": next_week,
"is_old_timetable": is_old_timetable,
"group_mode": isinstance(obj, Group),
"timetable": timetable})
--
cgit v1.2.1
From 2d5bb761ac83bfa4643006681587db2c5e11f60b Mon Sep 17 00:00:00 2001
From: Alban Gruin
Date: Fri, 19 Jan 2018 20:27:21 +0100
Subject: Adaptation des liens vers les semaines suivantes et précédentes pour
les emplois du temps des salles
---
templates/timetable.html | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/templates/timetable.html b/templates/timetable.html
index cc90ed5..7811427 100644
--- a/templates/timetable.html
+++ b/templates/timetable.html
@@ -20,7 +20,7 @@
{% include "timetable_common.html" %}
{% endblock %}
--
cgit v1.2.1
From 8b0626139036e50396f14cf9ae39b12e2540af85 Mon Sep 17 00:00:00 2001
From: Alban Gruin
Date: Fri, 19 Jan 2018 20:48:58 +0100
Subject: Préchargement des groupes et des salles lorsqu’on demande la liste
des salles. Réduit le nombre de requêtes à effectuer ainsi que le temps de
traitement.
---
models.py | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/models.py b/models.py
index 4903b11..ae04fb8 100644
--- a/models.py
+++ b/models.py
@@ -178,7 +178,8 @@ class CourseManager(Manager):
qs = qs.filter(groups__in=Group.objects.get_parents(obj), **criteria) \
.prefetch_related("rooms")
elif isinstance(obj, Room):
- qs = qs.filter(rooms__in=(obj,), **criteria)
+ qs = qs.filter(rooms__in=(obj,), **criteria) \
+ .prefetch_related("groups", "rooms")
else:
raise(TypeError, "obj must be a Group or a Room")
--
cgit v1.2.1
From 046e92137ace30cd645ba0f42421c283a60ba0cd Mon Sep 17 00:00:00 2001
From: Alban Gruin
Date: Fri, 19 Jan 2018 21:31:35 +0100
Subject: N’affiche plus la liste des groupes dans l’emploi du temps des salles
si un cours n’en a pas, au lieu de se baser sur le nombre de salles d’un
cours pour faire ce choix. Suppression du préchargement des salles lorsqu’on
demande les cours d’une salle. Cela permet de réduire le nombre de requêtes
effectuées.
---
models.py | 3 ++-
templates/timetable_common.html | 2 +-
2 files changed, 3 insertions(+), 2 deletions(-)
diff --git a/models.py b/models.py
index ae04fb8..15f378a 100644
--- a/models.py
+++ b/models.py
@@ -31,6 +31,7 @@ class SlugModel(models.Model):
super(SlugModel, self).save()
+
class Meta:
abstract = True
@@ -179,7 +180,7 @@ class CourseManager(Manager):
.prefetch_related("rooms")
elif isinstance(obj, Room):
qs = qs.filter(rooms__in=(obj,), **criteria) \
- .prefetch_related("groups", "rooms")
+ .prefetch_related("groups")
else:
raise(TypeError, "obj must be a Group or a Room")
diff --git a/templates/timetable_common.html b/templates/timetable_common.html
index 21300f1..6e59322 100644
--- a/templates/timetable_common.html
+++ b/templates/timetable_common.html
@@ -4,7 +4,7 @@
{% 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 }}{% if course.type %} ({{ course.type }}){% endif %}, de {{ course.begin|date:"H:i" }} à {{ course.end|date:"H:i" }}{% if course.rooms.all|length > 0 %}
+ {{ course }}{% if course.type %} ({{ course.type }}){% endif %}, de {{ course.begin|date:"H:i" }} à {{ course.end|date:"H:i" }}{% if group_mode and course.rooms.all|length > 0 or not group_mode and course.groups.all|length > 0 %} {% if group_mode %}{{ course.rooms.all|format_rooms }}{% else %}{{ course.groups.all|join:", " }}{% endif %}{% endif %}{% if course.notes %}
Remarques : {{ course.notes|linebreaksbr }}{% endif %}
{% endfor %}
--
cgit v1.2.1
From a0101b9a7566b5296c3490ce3592984976553807 Mon Sep 17 00:00:00 2001
From: Alban Gruin
Date: Sat, 20 Jan 2018 22:28:21 +0100
Subject: On cache les groupes qui n’ont plus de cours La requête est assez
longue à s’effectuer sur SQLite, mais pas sur PostgreSQL
---
models.py | 9 ++++++++-
views.py | 4 ++--
2 files changed, 10 insertions(+), 3 deletions(-)
diff --git a/models.py b/models.py
index 15f378a..c122bfd 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 Manager, Q
+from django.db.models import Count, Manager, OuterRef, Q, Subquery
from django.db.models.functions import ExtractWeek, ExtractYear
from django.utils import timezone
from django.utils.text import slugify
@@ -104,6 +104,13 @@ class GroupManager(Manager):
mention=group.mention,
source=group.source)
+ def get_relevant_groups(self, start, **criteria):
+ courses = Course.objects.filter(groups=OuterRef("pk"), begin__gte=start) \
+ .only("pk")[:1]
+ return self.get_queryset().annotate(c=Subquery(courses,
+ output_field=models.IntegerField())) \
+ .filter(c__isnull=False, **criteria).order_by("name")
+
class Group(SlugModel):
objects = GroupManager()
diff --git a/views.py b/views.py
index 10dcfa2..9b53f3a 100644
--- a/views.py
+++ b/views.py
@@ -15,7 +15,7 @@
import datetime
-from django.db.models import Count, Max
+from django.db.models import Max
from django.db.models.functions import ExtractWeek, ExtractYear, Length
from django.http import Http404
from django.shortcuts import get_object_or_404, render
@@ -39,11 +39,11 @@ def mention_list(request, year_slug):
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(source=timetable.source, hidden=False).order_by("name")
start, _ = get_week(*get_current_week())
end = start + datetime.timedelta(weeks=4)
+ groups = Group.objects.get_relevant_groups(start, source=timetable.source, hidden=False)
groups_weeks = Course.objects.get_weeks(begin__gte=start, begin__lt=end, groups__in=groups)
for group in groups:
--
cgit v1.2.1
From 6f4822291d89dadcb6bae94c897e89e309218e7e Mon Sep 17 00:00:00 2001
From: Alban Gruin
Date: Sun, 21 Jan 2018 19:35:25 +0100
Subject: Correction d’une erreur d’importation
---
views.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/views.py b/views.py
index 9b53f3a..842a02e 100644
--- a/views.py
+++ b/views.py
@@ -15,7 +15,7 @@
import datetime
-from django.db.models import Max
+from django.db.models import Count, Max
from django.db.models.functions import ExtractWeek, ExtractYear, Length
from django.http import Http404
from django.shortcuts import get_object_or_404, render
--
cgit v1.2.1
From 8ad4a03f9d6ab6d218ddcbe277ca3766f05f6d79 Mon Sep 17 00:00:00 2001
From: Alban Gruin
Date: Sat, 20 Jan 2018 17:24:18 +0100
Subject: Légère optimisation de la page des groupes
---
models.py | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/models.py b/models.py
index c122bfd..b809a53 100644
--- a/models.py
+++ b/models.py
@@ -200,7 +200,8 @@ class CourseManager(Manager):
.annotate(year=ExtractYear("begin"),
week=ExtractWeek("begin")) \
.values("groups__mention", "groups__semester",
- "groups__subgroup", "year", "week")
+ "groups__subgroup", "year", "week") \
+ .annotate(c=Count("*"))
class Course(models.Model):
--
cgit v1.2.1
From e815411b1d10ba0c94fa19a942033957a341f3fc Mon Sep 17 00:00:00 2001
From: Alban Gruin
Date: Sun, 21 Jan 2018 19:29:45 +0100
Subject: Améliorations de navigation
Lien pour retourner à la liste des groupes sur la page des emplois du temps
Lien pour retourner à la liste des années sur la page de liste des mentions
Lien pour retourner à la liste des mentions sur la page des groupes
Lien pour retourner à la liste des années sur la liste des salles
Lien pour accéder à la liste des salles sur la page principale
Lien pour retourner à la liste des salles sur le formulaire QSJPS
---
templates/group_list.html | 1 +
templates/index.html | 1 +
templates/qsjps_form.html | 1 +
templates/timetable.html | 1 +
4 files changed, 4 insertions(+)
diff --git a/templates/group_list.html b/templates/group_list.html
index 7fe1fe8..5f53cb4 100644
--- a/templates/group_list.html
+++ b/templates/group_list.html
@@ -10,4 +10,5 @@
{{ group }} — {% for week in group.weeks %}{{ week|dt_prettyprint }} {% if not forloop.last %}– {% endif %}{% empty %}aucun cours dans le mois à venir{% endfor %}
{% endfor %}
+ {% if timetable %}Retour à la liste des mentions{% else %}Retour à la liste des années{% endif %}
{% endblock %}
diff --git a/templates/index.html b/templates/index.html
index 71665bc..5779b3e 100644
--- a/templates/index.html
+++ b/templates/index.html
@@ -21,6 +21,7 @@