Skip to content
Snippets Groups Projects
views.py 6.34 KiB
Newer Older
  • Learn to ignore specific revisions
  • from datetime import timedelta
    
    
    from django.conf import settings
    
    from django.shortcuts import redirect
    from django.urls import reverse_lazy
    
    from django.utils.datetime_safe import datetime
    
    from django.views.generic import ListView, DetailView
    
    from AKModel.models import AKSlot, Room, AKTrack
    
    from AKModel.metaviews.admin import FilterByEventSlugMixin
    
    
    
    class PlanIndexView(FilterByEventSlugMixin, ListView):
    
        """
        Default plan view
    
        Shows two lists of current and upcoming AKs and a graphical full plan below
        """
    
        model = AKSlot
        template_name = "AKPlan/plan_index.html"
        context_object_name = "akslots"
        ordering = "start"
    
        def get_queryset(self):
            # Ignore slots not scheduled yet
    
            return super().get_queryset().filter(start__isnull=False).select_related('ak', 'room', 'ak__category')
    
    
        def get_context_data(self, *, object_list=None, **kwargs):
            context = super().get_context_data(object_list=object_list, **kwargs)
    
            context["event"] = self.event
    
            current_timestamp = datetime.now().astimezone(self.event.timezone)
    
            context["akslots_now"] = []
            context["akslots_next"] = []
            rooms = set()
    
    
            # Get list of current and next slots
            for akslot in context["akslots"]:
                # Construct a list of all rooms used by these slots on the fly
                if akslot.room is not None:
                    rooms.add(akslot.room)
    
                    # Store buildings for hierarchical view
    
                    if akslot.room.location != '':
                        buildings.add(akslot.room.location)
    
    
                # Recent AKs: Started but not ended yet
                if akslot.start <= current_timestamp <= akslot.end:
                    context["akslots_now"].append(akslot)
                # Next AKs: Not started yet, list will be filled in order until threshold is reached
                elif akslot.start > current_timestamp:
                    if len(context["akslots_next"]) < settings.PLAN_MAX_NEXT_AKS:
                        context["akslots_next"].append(akslot)
    
            # Sort list of rooms by title
            context["rooms"] = sorted(rooms, key=lambda x: x.title)
    
            if settings.PLAN_SHOW_HIERARCHY:
                context["buildings"] = sorted(buildings)
    
            context["tracks"] = self.event.aktrack_set.all()
    
    
            return context
    
    
    class PlanScreenView(PlanIndexView):
    
        """
        Plan view optimized for screens and projectors
    
        This again shows current and upcoming AKs as well as a graphical plan,
        but no navigation elements and trys to use the available space as best as possible
        such that no scrolling is needed.
    
        The view contains a frontend functionality for auto-reload.
        """
    
        template_name = "AKPlan/plan_wall.html"
    
        def get(self, request, *args, **kwargs):
            s = super().get(request, *args, **kwargs)
            # Don't show wall when event is not active -> redirect to normal schedule
    
            if not self.event.active or (self.event.plan_hidden and not request.user.is_staff):
    
                return redirect(reverse_lazy("plan:plan_overview", kwargs={"event_slug": self.event.slug}))
            return s
    
    
            now = datetime.now().astimezone(self.event.timezone)
    
            # Wall during event: Adjust, show only parts in the future
    
            if self.event.start < now < self.event.end:
    
                # Determine interesting range (some hours ago until some hours in the future as specified in the settings)
    
                self.start = now - timedelta(hours=settings.PLAN_WALL_HOURS_RETROSPECT)
    
            else:
                self.start = self.event.start
            self.end = self.event.end
    
    
            # Restrict AK slots to relevant ones
            # This will automatically filter all rooms not needed for the selected range in the orginal get_context method
    
            akslots = super().get_queryset().filter(start__gt=self.start)
    
            # Find the earliest hour AKs start and end (handle 00:00 as 24:00)
            self.earliest_start_hour = 23
            self.latest_end_hour = 1
            for akslot in akslots.all():
                start_hour = akslot.start.astimezone(self.event.timezone).hour
                if start_hour < self.earliest_start_hour:
                    # Use hour - 1 to improve visibility of date change
                    self.earliest_start_hour = max(start_hour - 1, 0)
                end_hour = akslot.end.astimezone(self.event.timezone).hour
                # Special case: AK starts before but ends after midnight -- show until midnight
                if end_hour < start_hour:
                    self.latest_end_hour = 24
                elif end_hour > self.latest_end_hour:
                    # Always use hour + 1, since AK may end at :xy and not always at :00
                    self.latest_end_hour = min(end_hour + 1, 24)
            return akslots
    
    
        def get_context_data(self, *, object_list=None, **kwargs):
            context = super().get_context_data(object_list=object_list, **kwargs)
    
            context["start"] = self.start
    
            context["end"] = self.event.end
    
            context["earliest_start_hour"] = self.earliest_start_hour
            context["latest_end_hour"] = self.latest_end_hour
    
    
    
    class PlanRoomView(FilterByEventSlugMixin, DetailView):
    
        template_name = "AKPlan/plan_room.html"
        model = Room
        context_object_name = "room"
    
    
        def get_context_data(self, *, object_list=None, **kwargs):
            context = super().get_context_data(object_list=object_list, **kwargs)
    
            # Restrict AKSlot list to the given room
            # while joining AK, room and category information to reduce the amount of necessary SQL queries
    
            context["slots"] = AKSlot.objects.filter(room=context['room']).select_related('ak', 'ak__category', 'ak__track')
            return context
    
    
    
    class PlanTrackView(FilterByEventSlugMixin, DetailView):
    
        template_name = "AKPlan/plan_track.html"
        model = AKTrack
        context_object_name = "track"
    
        def get_context_data(self, *, object_list=None, **kwargs):
            context = super().get_context_data(object_list=object_list, **kwargs)
    
            # Restrict AKSlot list to given track
            # while joining AK, room and category information to reduce the amount of necessary SQL queries
            context["slots"] = AKSlot.objects.\
                filter(event=self.event, ak__track=context['track']).\
                select_related('ak', 'room', 'ak__category')