calendarium.views: 197 total statements, 96.7% covered

Generated: Thu 2013-05-02 12:21 SGT

Source file: /Users/martin/Repos/django-calendarium/calendarium/views.py

Stats: 178 executed, 6 missed, 13 excluded, 104 ignored

  1. """Views for the ``calendarium`` app."""
  2. import calendar
  3. from dateutil.relativedelta import relativedelta
  4. from django.contrib.auth.decorators import permission_required
  5. from django.core.urlresolvers import reverse
  6. from django.forms.models import model_to_dict
  7. from django.http import Http404, HttpResponseRedirect
  8. from django.utils.decorators import method_decorator
  9. from django.utils.timezone import datetime, now, timedelta, utc
  10. from django.views.generic import (
  11. CreateView,
  12. DeleteView,
  13. DetailView,
  14. RedirectView,
  15. TemplateView,
  16. UpdateView,
  17. )
  18. from .constants import OCCURRENCE_DECISIONS
  19. from .forms import OccurrenceForm
  20. from .models import EventCategory, Event, Occurrence
  21. from .utils import monday_of_week
  22. class CategoryMixin(object):
  23. """Mixin to handle category filtering."""
  24. def dispatch(self, request, *args, **kwargs):
  25. if request.GET.get('category'):
  26. try:
  27. category_id = int(request.GET.get('category'))
  28. except ValueError:
  29. pass
  30. else:
  31. try:
  32. self.category = EventCategory.objects.get(pk=category_id)
  33. except EventCategory.DoesNotExist:
  34. pass
  35. return super(CategoryMixin, self).dispatch(request, *args, **kwargs)
  36. def get_category_context(self, **kwargs):
  37. context = {'categories': EventCategory.objects.all()}
  38. if hasattr(self, 'category'):
  39. context.update({'current_category': self.category})
  40. return context
  41. class CalendariumRedirectView(RedirectView):
  42. """View to redirect to the current month view."""
  43. def get_redirect_url(self, **kwargs):
  44. return reverse('calendar_month', kwargs={'year': now().year,
  45. 'month': now().month})
  46. class MonthView(CategoryMixin, TemplateView):
  47. """View to return all occurrences of an event for a whole month."""
  48. template_name = 'calendarium/calendar_month.html'
  49. def dispatch(self, request, *args, **kwargs):
  50. self.month = int(kwargs.get('month'))
  51. self.year = int(kwargs.get('year'))
  52. if self.month not in range(1, 13):
  53. raise Http404
  54. if request.method == 'POST':
  55. if request.POST.get('next'):
  56. new_date = datetime(self.year, self.month, 1) + timedelta(
  57. days=31)
  58. return HttpResponseRedirect(reverse('calendar_month', kwargs={
  59. 'year': new_date.year, 'month': new_date.month}))
  60. elif request.POST.get('previous'):
  61. new_date = datetime(self.year, self.month, 1) - timedelta(
  62. days=1)
  63. return HttpResponseRedirect(reverse('calendar_month', kwargs={
  64. 'year': new_date.year, 'month': new_date.month}))
  65. elif request.POST.get('today'):
  66. return HttpResponseRedirect(reverse('calendar_month', kwargs={
  67. 'year': now().year, 'month': now().month}))
  68. if request.is_ajax():
  69. self.template_name = 'calendarium/partials/calendar_month.html'
  70. return super(MonthView, self).dispatch(request, *args, **kwargs)
  71. def get_context_data(self, **kwargs):
  72. ctx = self.get_category_context()
  73. month = [[]]
  74. week = 0
  75. start = datetime(year=self.year, month=self.month, day=1, tzinfo=utc)
  76. end = datetime(
  77. year=self.year, month=self.month, day=1, tzinfo=utc
  78. ) + relativedelta(months=1)
  79. all_occurrences = Event.objects.get_occurrences(
  80. start, end, ctx.get('current_category'))
  81. for day in calendar.Calendar().itermonthdays(self.year, self.month):
  82. current = False
  83. if day:
  84. date = datetime(year=self.year, month=self.month, day=day,
  85. tzinfo=utc)
  86. occurrences = filter(
  87. lambda occ, date=date: occ.start.replace(
  88. hour=0, minute=0, second=0, microsecond=0) == date,
  89. all_occurrences)
  90. if date.date() == now().date():
  91. current = True
  92. else:
  93. occurrences = []
  94. month[week].append((day, occurrences, current))
  95. if len(month[week]) == 7:
  96. month.append([])
  97. week += 1
  98. ctx.update({'month': month, 'date': date})
  99. return ctx
  100. class WeekView(CategoryMixin, TemplateView):
  101. """View to return all occurrences of an event for one week."""
  102. template_name = 'calendarium/calendar_week.html'
  103. def dispatch(self, request, *args, **kwargs):
  104. self.week = int(kwargs.get('week'))
  105. self.year = int(kwargs.get('year'))
  106. if self.week not in range(1, 53):
  107. raise Http404
  108. if request.method == 'POST':
  109. if request.POST.get('next'):
  110. date = monday_of_week(self.year, self.week) + timedelta(days=7)
  111. return HttpResponseRedirect(reverse('calendar_week', kwargs={
  112. 'year': date.year, 'week': date.date().isocalendar()[1]}))
  113. elif request.POST.get('previous'):
  114. date = monday_of_week(self.year, self.week) - timedelta(days=7)
  115. return HttpResponseRedirect(reverse('calendar_week', kwargs={
  116. 'year': date.year, 'week': date.date().isocalendar()[1]}))
  117. elif request.POST.get('today'):
  118. return HttpResponseRedirect(reverse('calendar_week', kwargs={
  119. 'year': now().year,
  120. 'week': now().date().isocalendar()[1]}))
  121. if request.is_ajax():
  122. self.template_name = 'calendarium/partials/calendar_week.html'
  123. return super(WeekView, self).dispatch(request, *args, **kwargs)
  124. def get_context_data(self, **kwargs):
  125. ctx = self.get_category_context()
  126. date = monday_of_week(self.year, self.week)
  127. week = []
  128. day = 0
  129. start = date
  130. end = date + relativedelta(days=7)
  131. all_occurrences = Event.objects.get_occurrences(
  132. start, end, ctx.get('current_category'))
  133. while day < 7:
  134. current = False
  135. occurrences = filter(
  136. lambda occ, date=date: occ.start.replace(
  137. hour=0, minute=0, second=0, microsecond=0) == date,
  138. all_occurrences)
  139. if date.date() == now().date():
  140. current = True
  141. week.append((date, occurrences, current))
  142. day += 1
  143. date = date + timedelta(days=1)
  144. ctx.update({'week': week, 'date': date, 'week_nr': self.week})
  145. return ctx
  146. class DayView(CategoryMixin, TemplateView):
  147. """View to return all occurrences of an event for one day."""
  148. template_name = 'calendarium/calendar_day.html'
  149. def dispatch(self, request, *args, **kwargs):
  150. self.day = int(kwargs.get('day'))
  151. self.month = int(kwargs.get('month'))
  152. self.year = int(kwargs.get('year'))
  153. try:
  154. self.date = datetime(year=self.year, month=self.month,
  155. day=self.day, tzinfo=utc)
  156. except ValueError:
  157. raise Http404
  158. if request.method == 'POST':
  159. if request.POST.get('next'):
  160. date = self.date + timedelta(days=1)
  161. return HttpResponseRedirect(reverse('calendar_day', kwargs={
  162. 'year': date.year, 'month': date.month, 'day': date.day}))
  163. elif request.POST.get('previous'):
  164. date = self.date - timedelta(days=1)
  165. return HttpResponseRedirect(reverse('calendar_day', kwargs={
  166. 'year': date.year, 'month': date.month, 'day': date.day}))
  167. elif request.POST.get('today'):
  168. return HttpResponseRedirect(reverse('calendar_day', kwargs={
  169. 'year': now().year, 'month': now().month,
  170. 'day': now().day}))
  171. if request.is_ajax():
  172. self.template_name = 'calendarium/partials/calendar_day.html'
  173. return super(DayView, self).dispatch(request, *args, **kwargs)
  174. def get_context_data(self, **kwargs):
  175. ctx = self.get_category_context()
  176. occurrences = Event.objects.get_occurrences(
  177. self.date, self.date, ctx.get('current_category'))
  178. ctx.update({'date': self.date, 'occurrences': occurrences})
  179. return ctx
  180. class EventDetailView(DetailView):
  181. """View to return information of an event."""
  182. model = Event
  183. class EventMixin(object):
  184. """Mixin to handle event-related functions."""
  185. model = Event
  186. @method_decorator(permission_required('calendarium.add_event'))
  187. def dispatch(self, request, *args, **kwargs):
  188. return super(EventMixin, self).dispatch(request, *args, **kwargs)
  189. class EventUpdateView(EventMixin, UpdateView):
  190. """View to update information of an event."""
  191. pass
  192. class EventCreateView(EventMixin, CreateView):
  193. """View to create an event."""
  194. pass
  195. class EventDeleteView(EventMixin, DeleteView):
  196. """View to delete an event."""
  197. pass
  198. class OccurrenceViewMixin(object):
  199. """Mixin to avoid repeating code for the Occurrence view classes."""
  200. form_class = OccurrenceForm
  201. def dispatch(self, request, *args, **kwargs):
  202. try:
  203. self.event = Event.objects.get(pk=kwargs.get('pk'))
  204. except Event.DoesNotExist:
  205. raise Http404
  206. year = int(kwargs.get('year'))
  207. month = int(kwargs.get('month'))
  208. day = int(kwargs.get('day'))
  209. try:
  210. date = datetime(year, month, day, tzinfo=utc)
  211. except TypeError:
  212. raise Http404
  213. # this should retrieve the one single occurrence, that has a
  214. # matching start date
  215. try:
  216. occ = Occurrence.objects.get(
  217. start__year=year, start__month=month, start__day=day)
  218. except Occurrence.DoesNotExist:
  219. occ_gen = self.event.get_occurrences(self.event.start)
  220. occ = occ_gen.next()
  221. while occ.start.date() < date.date():
  222. occ = occ_gen.next()
  223. if occ.start.date() == date.date():
  224. self.occurrence = occ
  225. else:
  226. raise Http404
  227. self.object = self.occurrence
  228. return super(OccurrenceViewMixin, self).dispatch(
  229. request, *args, **kwargs)
  230. def get_object(self):
  231. return self.occurrence
  232. def get_form_kwargs(self):
  233. kwargs = super(OccurrenceViewMixin, self).get_form_kwargs()
  234. kwargs.update({'initial': model_to_dict(self.occurrence)})
  235. return kwargs
  236. class OccurrenceDeleteView(OccurrenceViewMixin, DeleteView):
  237. """View to delete an occurrence of an event."""
  238. def delete(self, request, *args, **kwargs):
  239. self.object = self.get_object()
  240. decision = self.request.POST.get('decision')
  241. self.object.delete_period(decision)
  242. return HttpResponseRedirect(self.get_success_url())
  243. def get_context_data(self, object):
  244. ctx = super(OccurrenceDeleteView, self).get_context_data()
  245. ctx.update({
  246. 'decisions': OCCURRENCE_DECISIONS,
  247. 'object': self.object
  248. })
  249. return ctx
  250. def get_success_url(self):
  251. return reverse('calendar_current_month')
  252. class OccurrenceDetailView(OccurrenceViewMixin, DetailView):
  253. """View to show information of an occurrence of an event."""
  254. pass
  255. class OccurrenceUpdateView(OccurrenceViewMixin, UpdateView):
  256. """View to edit an occurrence of an event."""
  257. pass