Skip to content
Snippets Groups Projects
views.py 11.5 KiB
Newer Older
  • Learn to ignore specific revisions
  • #    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
    #    by the Free Software Foundation, either version 3 of the License, or
    
    Alban Gruin's avatar
    Alban Gruin committed
    #    (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 <http://www.gnu.org/licenses/>.
    
    from django.db import connection
    
    from django.db.models import Count, Max
    from django.db.models.functions import ExtractWeek, ExtractYear, Length
    
    from django.shortcuts import get_object_or_404, render
    
    Alban Gruin's avatar
    Alban Gruin committed
    from django.utils import timezone
    
    from django.views.decorators.csrf import csrf_exempt
    
    Alban Gruin's avatar
    Alban Gruin committed
    
    
    from .forms import QSJPSForm
    
    from .models import Course, Group, Room, Timetable, Year
    
    Alban Gruin's avatar
    Alban Gruin committed
    from .utils import get_current_week, get_current_or_next_week, get_week, \
        group_courses
    
    if connection.vendor == "postgresql":
        from django.contrib.postgres.aggregates import ArrayAgg
    
        from django.db.models.expressions import RawSQL
    
    Alban Gruin's avatar
    Alban Gruin committed
    
    
    def index(request):
    
        years = Year.objects.order_by("name")
    
        return render(request, "year_list.html", {"elements": years})
    
    Alban Gruin's avatar
    Alban Gruin committed
    
    
    def mention_list(request, year_slug):
        year = get_object_or_404(Year, slug=year_slug)
    
        timetables = Timetable.objects.order_by("name").filter(year=year)
    
        return render(request, "mention_list.html",
    
    Alban Gruin's avatar
    Alban Gruin committed
                      {"year": year, "elements": timetables})
    
    
    def group_list(request, year_slug, timetable_slug):
    
    Alban Gruin's avatar
    Alban Gruin committed
        timetable = get_object_or_404(Timetable, year__slug=year_slug,
                                      slug=timetable_slug)
    
        start, _ = get_week(*get_current_week())
    
        end = start + datetime.timedelta(weeks=4)
    
    Alban Gruin's avatar
    Alban Gruin committed
        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:
    
                if group.corresponds_to(group_week["groups__mention"],
    
                                        group_week["groups__semester"],
    
                                        group_week["groups__subgroup"]):
    
                    if not hasattr(group, "weeks"):
                        group.weeks = []
    
                    date, _ = get_week(group_week["year"], group_week["week"])
                    if date not in group.weeks:
                        group.weeks.append(date)
    
            if hasattr(group, "weeks"):
                group.weeks.sort()
    
    Alban Gruin's avatar
    Alban Gruin committed
        return render(request, "group_list.html",
                      {"timetable": timetable, "groups": groups})
    
    
    def groups_all(request, year_slug, timetable_slug):
        # Récupération de l’emploi du temps et du groupe
        timetable = get_object_or_404(Timetable, year__slug=year_slug,
                                      slug=timetable_slug)
        groups = Group.objects.filter(source=timetable.source).order_by("name")
    
        # Rendu de la page
        return render(request, "groups_all_list.html",
                      {"timetable": timetable, "elements": groups})
    
    
    def group_weeks(request, year_slug, timetable_slug, group_slug):
        # Récupération de l’emploi du temps et des groupes
        timetable = get_object_or_404(Timetable, year__slug=year_slug,
                                      slug=timetable_slug)
        group = get_object_or_404(Group, slug=group_slug, source=timetable.source)
    
        # Groupes parents
        groups = Group.objects.get_parents(group)
    
        # Récupération de toutes les semaines avec des cours, sans doublons
        courses = Course.objects.filter(groups__in=groups) \
                                .order_by("year", "week") \
                                .annotate(year=ExtractYear("begin"),
                                          week=ExtractWeek("begin")) \
                                .values("year", "week") \
                                .annotate(c=Count("*"))
    
        # Conversion des semaines de cours en dates
        weeks = [get_week(course["year"], course["week"])[0] for course in courses]
    
        # Rendu
        return render(request, "group_weeks_list.html",
                      {"timetable": timetable, "group": group,
                       "elements": weeks})
    
    
    
    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
    
        if year is None or week is None:
    
        elif (int(year), int(week)) < (current_year, current_week):
            is_old_timetable = True
    
        try:
            start, end = get_week(year, week)
        except ValueError:
            raise Http404
    
        courses = Course.objects.get_courses(obj, begin__gte=start, begin__lt=end)
    
        if not courses.exists() and provided_week:
    
        # Récupération des semaines suivantes et précédentes pour les
        # afficher proprement dans l’emploi du temps
    
    Alban Gruin's avatar
    Alban Gruin committed
        last_course = Course.objects.get_courses(obj, begin__lt=start).last()
        last_week = getattr(last_course, "begin", None)
    
        next_course = Course.objects.get_courses(obj, begin__gte=end).first()
        next_week = getattr(next_course, "begin", None)
    
        last_update = courses.aggregate(Max("last_update"))["last_update__max"]
    
    Alban Gruin's avatar
    Alban Gruin committed
        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})
    
    
    Alban Gruin's avatar
    Alban Gruin committed
    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, source=timetable.source)
    
        return timetable_common(request, group, year, week, timetable)
    
    Alban Gruin's avatar
    Alban Gruin committed
    
    
    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, source=timetable.source, slug=group_slug)
    
    Alban Gruin's avatar
    Alban Gruin committed
        groups = Group.objects.get_parents(group) \
                              .annotate(length=Length("subgroup")) \
                              .order_by("length")
    
        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
        start, _ = get_week(*get_current_week())
        end = start + datetime.timedelta(weeks=4)
    
    
        if connection.vendor == "postgresql":
            # Si le SGBD est PostgreSQL, on utilise une requête à base de
            # ArrayAgg. Elle présente l’avantage d’être plus rapide que la
            # requête « généraliste » et de ne pas nécessiter de
            # traitement après.  On récupère chaque salle ayant un cours
            # dans le mois à venir. Pour chacun de ses cours, on ne
            # récupère que le premier jour de la semaine, et si jamais ce
            # jour n’est pas déjà dans la liste des semaines de cours
            # (« weeks »), on l’y rajoute.
            rooms = Room.objects.filter(course__begin__gte=start,
                                        course__begin__lt=end) \
                                .order_by("name") \
                                .annotate(weeks=ArrayAgg(
                                    RawSQL("date_trunc('week', edt_course.begin)",
                                           []), distinct=True))
    
            return render(request, "group_list.html", {"groups": rooms})
    
    
        # Récupération des salles et de toutes les semaines où elles sont
    
        # 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
    
    Alban Gruin's avatar
    Alban Gruin committed
        rooms = Room.objects.filter(course__begin__gte=start,
                                    course__begin__lt=end) \
    
                            .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)
    
    
        return render(request, "group_list.html", {"groups": rooms_weeks})
    
    
    Alban Gruin's avatar
    Alban Gruin committed
    
    
    def room_weeks(request, room_slug):
        room = get_object_or_404(Room, slug=room_slug)
    
        # Récupération des semaines de cours
        courses = Course.objects.filter(rooms=room) \
                                .order_by("year", "week") \
                                .annotate(year=ExtractYear("begin"),
                                          week=ExtractWeek("begin")) \
                                .values("year", "week") \
                                .annotate(c=Count("*"))
    
        weeks = [get_week(course["year"], course["week"])[0] for course in courses]
    
        return render(request, "room_weeks_list.html",
                      {"room": room, "elements": weeks})
    
    
    
    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)
    
    Alban Gruin's avatar
    Alban Gruin committed
    
    
    @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é
    
    Alban Gruin's avatar
    Alban Gruin committed
                day = form.cleaned_data["day"]
                begin_hour = form.cleaned_data["begin"]
                end_hour = form.cleaned_data["end"]
    
                begin = timezone.make_aware(datetime.datetime.combine(day,
                                                                      begin_hour))
                end = timezone.make_aware(datetime.datetime.combine(day, end_hour))
    
                rooms = Room.objects.qsjps(begin, end)
    
    Alban Gruin's avatar
    Alban Gruin committed
                return render(request, "qsjps.html",
    
                              {"elements": rooms, "form": form})
    
    Alban Gruin's avatar
    Alban Gruin committed
    
    
            # Si le formulaire est invalide, on ré-affiche le formulaire
            # avec les erreurs
    
        else:
    
            # Si le formulaire n’a pas été soumis, on en instancie un
            # nouveau
    
            form = QSJPSForm()
    
    
        return render(request, "qsjps_form.html", {"form": form})
    
    Alban Gruin's avatar
    Alban Gruin committed
    
    
    def ctx_processor(request):
        return {"celcatsanitizer_version": edt.VERSION}