diff options
author | Alban Gruin | 2017-02-12 13:44:05 +0100 |
---|---|---|
committer | Alban Gruin | 2017-02-12 13:44:05 +0100 |
commit | a543aaa14957f390d2b640a264113b639c7d3194 (patch) | |
tree | 62bf0fdbd83717f3020dad2313dd1f4f4a9dcdce | |
parent | 3d5d17475909a6b351fadf8bbdb0bf8dc8d8e928 (diff) | |
parent | 908ed3b4d0bc57131f6dafc663c6f7e098f0b7e1 (diff) |
Merge branch 'stable/0.y.z' into prod/pa1ch/0.y.zv0.6.0-pa1ch
-rw-r--r-- | admin.py | 2 | ||||
-rw-r--r-- | db.py | 76 | ||||
-rw-r--r-- | management/commands/sendmails.py | 19 | ||||
-rw-r--r-- | models.py | 27 | ||||
-rw-r--r-- | templates/index.html | 2 | ||||
-rw-r--r-- | tests.py | 116 | ||||
-rw-r--r-- | views.py | 10 |
7 files changed, 145 insertions, 107 deletions
@@ -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) @@ -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) @@ -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> – {% 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> – {% 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> @@ -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)) @@ -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) |