import datetime
import icalendar
from django.http import Http404, HttpResponse
from django.utils import timezone
from django.views.generic.dates import ArchiveIndexView, YearArchiveView
from django.views.generic.detail import DetailView
from cdhweb.events.models import Event
from cdhweb.pages.views import LastModifiedListMixin, LastModifiedMixin
[docs]class EventMixinView:
"""View mixin that sets model to Event and returns a
published Event queryset."""
model = Event
lastmodified_attr = "last_published_at"
[docs] def get_queryset(self):
"""use manager to find published events only"""
return Event.objects.live()
[docs]class EventSemesterDates:
"""Mixin to return list of event semester dates based on
event dates in the system."""
[docs] @staticmethod
def get_semester(date):
"""Return the semester a date occurs in as a string."""
# determine semester based on the month
if date.month <= 5:
return "Spring"
if date.month <= 8:
return "Summer"
return "Fall"
[docs] def get_semester_date_list(self):
"""Get a list of semester labels (semester and year) for published
events. Semesters are Spring (through May), Summer (through
August), and Fall."""
date_list = []
dates = Event.objects.live().dates("start_time", "month", order="ASC")
for date in dates:
# add semester + year to list if not already present
sem_date = (self.get_semester(date), date.year)
if sem_date not in date_list:
date_list.append(sem_date)
return date_list
[docs]class UpcomingEventsView(
EventMixinView, ArchiveIndexView, EventSemesterDates, LastModifiedListMixin
):
"""Upcoming events view. Displays future published events and
6 most recent past events."""
date_field = "start_time"
allow_future = True
context_object_name = "events"
allow_empty = True # don't 404 even if no events in the system
# NOTE: can't use get_queryset to restrict to upcoming because
# that affects the archive date list as well; restricting to upcoming
# events in get_context_data instaed
[docs] def get_context_data(self, *args, **kwargs):
context = super(UpcomingEventsView, self).get_context_data(*args, **kwargs)
event_qs = context["events"]
context.update(
{
"events": event_qs.upcoming(),
"page_title": "Upcoming Events",
# find 6 most recent past events
"past": event_qs.recent()[:6],
"date_list": self.get_semester_date_list(),
}
)
return context
[docs] def last_modified(self):
"""Get the recent last modified date from included events."""
upcoming = self.get_queryset().upcoming()
# don't error if there are no upcoming events
if upcoming.exists():
return getattr(
upcoming.order_by(self.lastmodified_attr).first(),
self.lastmodified_attr,
)
[docs]class EventSemesterArchiveView(
EventMixinView, YearArchiveView, EventSemesterDates, LastModifiedListMixin
):
"""Display events by semester"""
date_field = "start_time"
make_object_list = True
allow_future = True
template_name = "events/event_archive.html"
context_object_name = "events"
date_list_period = "month"
# use month/year archive on the blog and then collapse
semester_dates = {
# spring: jan 1 - may 31
"spring": {"start": (1, 1), "end": (5, 31)},
# summer: june 1 - aug 31
"summer": {"start": (6, 1), "end": (8, 31)},
# fall: sep 1 - dec 31
"fall": {"start": (9, 1), "end": (12, 31)},
}
[docs] def get_queryset(self):
return super().get_queryset().prefetch_related("page_ptr")
[docs] def get_dated_items(self):
date_list, items, context = super(
EventSemesterArchiveView, self
).get_dated_items()
# year archive gets items by years; filter by semester
semester = self.kwargs["semester"]
# generate dates for start and end with current year
month, year = self.semester_dates[semester]["start"]
start = datetime.datetime(
int(self.kwargs["year"]),
month,
year,
tzinfo=timezone.get_default_timezone(),
)
month, year = self.semester_dates[semester]["end"]
end = datetime.datetime(
int(self.kwargs["year"]),
month,
year,
tzinfo=timezone.get_default_timezone(),
)
items = items.filter(start_time__gte=start, start_time__lte=end)
return (date_list, items, context)
[docs] def get_date_list(self, *args, **kwargs):
return self.get_semester_date_list()
[docs] def get_context_data(self, *args, **kwargs):
context = super(EventSemesterArchiveView, self).get_context_data(
*args, **kwargs
)
context.update(
{
"page_title": "%s %s Events"
% (self.kwargs["semester"].title(), self.kwargs["year"])
}
)
return context
[docs]class EventDetailView(EventMixinView, DetailView, LastModifiedMixin):
"""Event detail page"""
[docs] def get_object(self, queryset=None):
if queryset is None:
queryset = self.get_queryset().live()
queryset = queryset.filter(
slug=self.kwargs["slug"],
start_time__year=self.kwargs["year"],
start_time__month=self.kwargs["month"],
)
try:
# Get the single item from the filtered queryset
obj = queryset.get()
except queryset.model.DoesNotExist:
raise Http404("No Event found found matching the query")
return obj
[docs] def get(self, request, *args, **kwargs):
"""Serve the requested Event using Wagtail's `Page.serve()`."""
return self.get_object().serve(request)
[docs]class EventIcalView(EventDetailView):
"""Download event information as ical"""
[docs] def get(self, request, *args, **kwargs):
event = self.get_object()
cal = icalendar.Calendar()
cal.add_component(event.ical_event())
response = HttpResponse(cal.to_ical(), content_type="text/calendar")
response["Content-Disposition"] = 'attachment; filename="%s.ics"' % event.slug
return response
# ical calendar for all upcoming events
# TODO when we can get to it
# class IcalCalendarView(EventListView):
# def get_queryset(self):
# return super(IcalCalendarView, self).get_queryset().upcoming()
# def render_to_response(self, context, **response_kwargs):
# cal = icalendar.Calendar()
# # TODO: required to be compliant
# # cal.add('prodid', '-//My calendar product//mxm.dk//')
# # cal.add('version', '2.0')
# for event in self.get_queryset():
# cal.add_component(event.ical_event())
# # TODO: should support cancelled events if possible
# response = HttpResponse(cal.to_ical(), content_type="text/calendar")
# response['Content-Disposition'] = 'attachment; filename="CDH-calendar.ics"'
# return response