aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAlban Gruin2017-02-12 13:44:05 +0100
committerAlban Gruin2017-02-12 13:44:05 +0100
commita543aaa14957f390d2b640a264113b639c7d3194 (patch)
tree62bf0fdbd83717f3020dad2313dd1f4f4a9dcdce
parent3d5d17475909a6b351fadf8bbdb0bf8dc8d8e928 (diff)
parent908ed3b4d0bc57131f6dafc663c6f7e098f0b7e1 (diff)
Merge branch 'stable/0.y.z' into prod/pa1ch/0.y.zv0.6.0-pa1ch
-rw-r--r--admin.py2
-rw-r--r--db.py76
-rw-r--r--management/commands/sendmails.py19
-rw-r--r--models.py27
-rw-r--r--templates/index.html2
-rw-r--r--tests.py116
-rw-r--r--views.py10
7 files changed, 145 insertions, 107 deletions
diff --git a/admin.py b/admin.py
index 76b7f2d..f818865 100644
--- a/admin.py
+++ b/admin.py
@@ -15,7 +15,7 @@
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
from django.contrib import admin
-from edt.models import Timetable, LastUpdate, Group, Subscription, Room, Course
+from .models import Timetable, LastUpdate, Group, Subscription, Room, Course
@admin.register(Timetable)
diff --git a/db.py b/db.py
deleted file mode 100644
index 678225e..0000000
--- a/db.py
+++ /dev/null
@@ -1,76 +0,0 @@
-# Copyright (C) 2017 Alban Gruin
-#
-# celcatsanitizer is free software; you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation; either version 2 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 General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License along
-# with celcatsanitizer; if not, write to the Free Software Foundation, Inc.,
-# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
-
-from django.db import connections
-from django.db.models import Manager
-from django.db.models.query import QuerySet
-from django.db.models.sql.compiler import SQLCompiler
-from django.db.models.sql.query import Query
-from django.db.models.sql.where import WhereNode
-
-
-class GroupedCompiler(SQLCompiler):
- def get_group_by(self, select, order_by):
- result = super(GroupedCompiler, self).get_group_by(select, order_by)
- expressions = []
- for expr in self.query.real_group_by:
- ref = expr if hasattr(expr, "as_sql") else self.query.resolve_ref(expr)
- sql, params = self.compile(ref)
- result.append((sql, params))
-
- return result
-
-
-class GroupedQuery(Query):
- def __init__(self, model, where=WhereNode):
- super(GroupedQuery, self).__init__(model, where)
- self.real_group_by = []
-
- def clone(self, klass=None, memo=None, **kwargs):
- obj = super(GroupedQuery, self).clone(klass, memo, **kwargs)
- obj.real_group_by = self.real_group_by[:]
- return obj
-
- def add_grouping(self, *grouping):
- self.real_group_by.extend(grouping)
-
- def clear_grouping(self):
- self.real_group_by = []
-
- def get_compiler(self, using=None, connection=None):
- if using is None and connection is None:
- raise ValueError("Need either using or connection")
- if using:
- connection = connections[using]
- return GroupedCompiler(self, connection, using)
-
-
-class GroupedQuerySet(QuerySet):
- def __init__(self, model=None, query=None, using=None, hints=None):
- super(GroupedQuerySet, self).__init__(model, query, using, hints)
- self.query = query or GroupedQuery(self.model)
-
- def group_by(self, *field_names):
- obj = self._clone()
- obj.query.clear_grouping()
- obj.query.add_grouping(*field_names)
- return obj
-
-
-class GroupedManager(Manager):
- def __init__(self):
- super(GroupedManager, self).__init__()
- self._queryset_class = GroupedQuerySet
diff --git a/management/commands/sendmails.py b/management/commands/sendmails.py
index 3126243..5a2054f 100644
--- a/management/commands/sendmails.py
+++ b/management/commands/sendmails.py
@@ -38,22 +38,25 @@ class Command(BaseCommand):
subscriptions = Subscription.objects.filter(active=True)
content = {}
mails = []
+ footer = loader.get_template("mail/mail_footer.txt")
print("Generating messages...")
for subscription in subscriptions:
if subscription.group.id not in content:
courses = Course.objects.get_courses_for_group(subscription.group, begin__gte=start, begin__lt=end)
- grouped_courses = group_courses(courses)
- template = loader.get_template("mail/mail_timetable.txt")
- context = Context({"subscription": subscription, "courses": grouped_courses, "week": week})
- content[subscription.group.id] = template.render(context)
+ if len(courses) > 0:
+ grouped_courses = group_courses(courses)
- footer = loader.get_template("mail/mail_footer.txt")
- context = Context({"admins": settings.ADMINS, "token": subscription.token, "domain": settings.DEFAULT_DOMAIN})
- mail_content = content[subscription.group.id] + footer.render(context)
+ template = loader.get_template("mail/mail_timetable.txt")
+ context = Context({"subscription": subscription, "courses": grouped_courses, "week": week})
+ content[subscription.group.id] = template.render(context)
- mails.append(("{0} - {1} - Semaine {2}".format(subscription.group.timetable.name, subscription.group.name, week), mail_content, settings.DEFAULT_FROM_EMAIL, [subscription.email],))
+ if subscription.group.id in content:
+ context = Context({"admins": settings.ADMINS, "token": subscription.token, "domain": settings.DEFAULT_DOMAIN})
+ mail_content = content[subscription.group.id] + footer.render(context)
+
+ mails.append(("{0} - {1} - Semaine {2}".format(subscription.group.timetable.name, subscription.group.name, week), mail_content, settings.DEFAULT_FROM_EMAIL, [subscription.email],))
print("Sending mails...")
send_mass_mail(mails)
diff --git a/models.py b/models.py
index 2100d35..1e96a88 100644
--- a/models.py
+++ b/models.py
@@ -15,13 +15,11 @@
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
from django.db import connection, models
-from django.db.models import Q
+from django.db.models import Count, Manager, Q
from django.db.models.expressions import RawSQL
from django.db.models.functions import Extract, ExtractYear
from django.utils.text import slugify
-from .db import GroupedManager
-
import hashlib
import os
@@ -75,6 +73,10 @@ class Group(models.Model):
def corresponds_to(self, timetable_id, mention, subgroup, td, tp):
return self.timetable.id == timetable_id and self.mention == mention and self.subgroup == subgroup and (self.td == td or self.td is None or td is None) and (self.tp == tp or self.tp is None or tp is None)
+ @property
+ def group_info(self):
+ return self.timetable.id, self.mention, self.subgroup, self.td, self.tp
+
def __str__(self):
return self.name
@@ -141,22 +143,17 @@ class Room(models.Model):
verbose_name_plural = "salles"
-class CourseManager(GroupedManager):
- def __get_weeks(self, qs):
- extractYear = ExtractYear("begin")
- qs = qs.group_by("groups", "year", "week", "groups__timetable", "groups__mention", "groups__subgroup", "groups__td", "groups__tp", "begin", "groups__name").order_by("groups__name", "year", "week")
-
- if connection.vendor == "postgresql":
- return qs.annotate(week=ExtractWeek("begin"), year=extractYear)
- else:
- return qs.annotate(week=RawSQL("""cast(strftime("%%W", "begin") as integer)""", []), year=extractYear)
-
+class CourseManager(Manager):
def get_courses_for_group(self, group, **filters):
return self.get_queryset().filter(Q(groups__td__isnull=True) | Q(groups__td=group.td), Q(groups__tp__isnull=True) | Q(groups__tp=group.tp), groups__mention=group.mention, groups__subgroup=group.subgroup, timetable=group.timetable, **filters).order_by("begin")
def get_weeks(self, **criteria):
- qs = self.get_queryset().filter(**criteria)
- return self.__get_weeks(qs)
+ qs = self.get_queryset().filter(**criteria).order_by("groups__name", "year", "week").annotate(_=Count(("groups", "year", "week", "begin")), year=ExtractYear("begin"))
+
+ if connection.vendor == "postgresql":
+ return qs.annotate(week=ExtractWeek("begin"))
+ else:
+ return qs.annotate(week=RawSQL("""cast(strftime("%%W", "begin") as integer)""", []))
class Course(models.Model):
diff --git a/templates/index.html b/templates/index.html
index cca2eaf..4db748d 100644
--- a/templates/index.html
+++ b/templates/index.html
@@ -62,7 +62,7 @@ li.course {
<section id="{{ timetable.slug }}">
<h3><a href="{{ timetable.url }}">{{ timetable.name }}</a></h3>
<ul>{% for group in groups %}{% if group.timetable.id == timetable.id %}
- <li><a class="text" href="{% url "timetable" timetable.slug group.slug %}">{{ group.name }}</a> &ndash; {% for week in group.weeks %}<a href="{% url "timetable" timetable.slug group.slug week.year week|dt_week %}">{{ week|dt_prettyprint }}</a> {% endfor %}</li>{% endif %}{% endfor %}
+ <li><a class="text"{% if group.weeks is not None %} href="{% url "timetable" timetable.slug group.slug %}"{% endif %}>{{ group.name }}</a> &ndash; {% for week in group.weeks %}<a href="{% url "timetable" timetable.slug group.slug week.year week|dt_week %}">{{ week|dt_prettyprint }}</a> {% empty %}<em>aucun cours</em>{% endfor %}</li>{% endif %}{% endfor %}
</ul>
</section>{% endfor %}{% endblock %}
</div>
diff --git a/tests.py b/tests.py
index 42ddd65..b6b9a70 100644
--- a/tests.py
+++ b/tests.py
@@ -15,5 +15,119 @@
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
from django.test import TestCase
+from django.utils import timezone
-# Create your tests here.
+from .models import Course, Group, Timetable
+
+
+class CourseTestCase(TestCase):
+ def setUp(self):
+ dt = timezone.now()
+
+ self.timetable = Timetable(name="Test timetable 2", url="http://example.org/", slug="test-timetable2")
+ self.timetable.save()
+
+ cma = Group.objects.create(celcat_name="L1 info s2 - CMA", timetable=self.timetable)
+ tda2 = Group.objects.create(celcat_name="L1 info s2 - TDA2", timetable=self.timetable)
+ self.tpa21 = Group.objects.create(celcat_name="L1 info s2 - TPA21", timetable=self.timetable)
+
+ cmb = Group.objects.create(celcat_name="L1 info s2 - CMB", timetable=self.timetable)
+ tdb2 = Group.objects.create(celcat_name="L1 info s2 - TDB2", timetable=self.timetable)
+ self.tpb21 = Group.objects.create(celcat_name="L1 info s2 - TPB21", timetable=self.timetable)
+
+ for group in (cma, tda2, self.tpa21, cmb, tdb2, self.tpb21,):
+ course = Course.objects.create(name="{0} course".format(group.name), type="cours", timetable=self.timetable, begin=dt, end=dt)
+ 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_course_names = ["L1 info s2 - CMA course", "L1 info s2 - TDA2 course", "L1 info s2 - TPA21 course"]
+ tpb21_course_names = ["L1 info s2 - CMB course", "L1 info s2 - TDB2 course", "L1 info s2 - TPB21 course"]
+
+ for courses, names in ((tpa21_courses, tpa21_course_names,), (tpb21_courses, tpb21_course_names,),):
+ for course in courses:
+ self.assertIn(course.name, names)
+ names.remove(course.name)
+
+
+class GroupTestCase(TestCase):
+ def setUp(self):
+ self.timetable = Timetable(name="Test timetable", url="http://example.com/", slug="test-timetable")
+ self.timetable.save()
+
+ Group.objects.create(celcat_name="L1 info s2 - CMA", timetable=self.timetable)
+ Group.objects.create(celcat_name="L1 info s2 - TDA2", timetable=self.timetable)
+ Group.objects.create(celcat_name="L1 info s2 - TPA21", timetable=self.timetable)
+
+ Group.objects.create(celcat_name="L1 info s2 - CMB", timetable=self.timetable)
+ Group.objects.create(celcat_name="L1 info s2 - TDB2", timetable=self.timetable)
+ Group.objects.create(celcat_name="L1 info s2 - TPB21", timetable=self.timetable)
+
+ def test_corresponds(self):
+ cma = Group.objects.get(celcat_name="L1 info s2 - CMA", timetable=self.timetable)
+ tda2 = Group.objects.get(celcat_name="L1 info s2 - TDA2", timetable=self.timetable)
+ tpa21 = Group.objects.get(celcat_name="L1 info s2 - TPA21", timetable=self.timetable)
+
+ cmb = Group.objects.get(celcat_name="L1 info s2 - CMB", timetable=self.timetable)
+ tdb2 = Group.objects.get(celcat_name="L1 info s2 - TDB2", timetable=self.timetable)
+ tpb21 = Group.objects.get(celcat_name="L1 info s2 - TPB21", timetable=self.timetable)
+
+ self.assertTrue(cma.corresponds_to(*tda2.group_info)) # CMA corresponds to TDA2
+ self.assertTrue(cma.corresponds_to(*tpa21.group_info)) # CMA corresponds to TPA21
+ self.assertTrue(tda2.corresponds_to(*tpa21.group_info)) # TDA2 corresponds to TPA21
+
+ self.assertTrue(cmb.corresponds_to(*tdb2.group_info)) # CMB corresponds to TDB2
+ self.assertTrue(cmb.corresponds_to(*tpb21.group_info)) # CMB corresponds to TPB21
+ self.assertTrue(tdb2.corresponds_to(*tpb21.group_info)) # TDB2 corresponds to TPB21
+
+ self.assertFalse(cmb.corresponds_to(*tda2.group_info)) # CMB does not corresponds to TDA2
+ self.assertFalse(cmb.corresponds_to(*tpa21.group_info)) # CMB does not corresponds to TPA21
+ self.assertFalse(tdb2.corresponds_to(*tpa21.group_info)) # TDB2 does not corresponds to TPA21
+
+ self.assertTrue(tda2.corresponds_to(*cma.group_info)) # TDA2 corresponds to CMA
+ self.assertTrue(tpa21.corresponds_to(*cma.group_info)) # TPA21 corresponds to CMA
+ self.assertTrue(tpa21.corresponds_to(*tda2.group_info)) # TPA21 corresponds to TDA2
+
+ self.assertTrue(tdb2.corresponds_to(*cmb.group_info)) # TDB2 corresponds to CMB
+ self.assertTrue(tpb21.corresponds_to(*cmb.group_info)) # TPB21 corresponds to CMB
+ self.assertTrue(tpb21.corresponds_to(*tdb2.group_info)) # TPB21 corresponds to TDB2
+
+ self.assertFalse(tda2.corresponds_to(*cmb.group_info)) # TDA2 does not corresponds to CMB
+ self.assertFalse(tpa21.corresponds_to(*cmb.group_info)) # TPA21 does not corresponds to CMB
+ self.assertFalse(tpa21.corresponds_to(*tdb2.group_info)) # TPA21 does not corresponds to TDB2
+
+ 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)
+
+ cmb = Group.objects.get(name="L1 info s2 - CMB", timetable=self.timetable)
+ tdb2 = Group.objects.get(name="L1 info s2 - TDB2", timetable=self.timetable)
+ tpb21 = Group.objects.get(name="L1 info s2 - TPB21", timetable=self.timetable)
+
+ self.assertEqual(cma.celcat_name, "L1 info s2 - CMA")
+ self.assertEqual(tda2.celcat_name, "L1 info s2 - TDA2")
+ self.assertEqual(tpa21.celcat_name, "L1 info s2 - TPA21")
+
+ self.assertEqual(cmb.celcat_name, "L1 info s2 - CMB")
+ self.assertEqual(tdb2.celcat_name, "L1 info s2 - TDB2")
+ self.assertEqual(tpb21.celcat_name, "L1 info s2 - TPB21")
+
+ def test_parse(self):
+ cma = Group.objects.get(celcat_name="L1 info s2 - CMA", timetable=self.timetable)
+ tda2 = Group.objects.get(celcat_name="L1 info s2 - TDA2", timetable=self.timetable)
+ tpa21 = Group.objects.get(celcat_name="L1 info s2 - TPA21", timetable=self.timetable)
+
+ cmb = Group.objects.get(celcat_name="L1 info s2 - CMB", timetable=self.timetable)
+ tdb2 = Group.objects.get(celcat_name="L1 info s2 - TDB2", timetable=self.timetable)
+ tpb21 = Group.objects.get(celcat_name="L1 info s2 - TPB21", timetable=self.timetable)
+
+ self.assertEqual(cma.group_info, (self.timetable.id, "L1 info s2", "A", None, None))
+ self.assertEqual(tda2.group_info, (self.timetable.id, "L1 info s2", "A", 2, None))
+ self.assertEqual(tpa21.group_info, (self.timetable.id, "L1 info s2", "A", 2, 1))
+
+ 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))
diff --git a/views.py b/views.py
index 273720a..651533a 100644
--- a/views.py
+++ b/views.py
@@ -22,14 +22,14 @@ from django.core.mail import send_mail
from django.conf import settings
from django.template import Context, loader
-from edt.forms import SubscribeForm
-from edt.models import Timetable, LastUpdate, Group, Subscription, Course
-from edt.utils import get_current_week, get_week, group_courses
+from .forms import SubscribeForm
+from .models import Timetable, LastUpdate, Group, Subscription, Course
+from .utils import get_current_week, get_week, group_courses
import datetime
def index(request):
- timetables = Timetable.objects.all()
+ timetables = Timetable.objects.all().order_by("name")
groups = Group.objects.filter(tp__isnull=False).order_by("name")
year, week = get_current_week()
@@ -59,8 +59,8 @@ def timetable(request, timetable_slug, group_slug, year=None, week=None):
timetable = get_object_or_404(Timetable, slug=timetable_slug)
group = get_object_or_404(Group, slug=group_slug, timetable=timetable)
+ last_update = get_object_or_404(LastUpdate, timetable=timetable, week=week, year=year)
courses = Course.objects.get_courses_for_group(group, begin__gte=start, begin__lt=end)
- last_update = LastUpdate.objects.get(timetable=timetable, week=week, year=year)
grouped_courses = group_courses(courses)