christchurch_django2 / christchurch /

Full commit
from datetime import datetime, timedelta
import os
import os.path
import random
import re

from django.conf import settings
from django.http import HttpResponseRedirect
from django.shortcuts import render
from django.utils.cache import patch_cache_control, add_never_cache_headers
import pytz
import requests
import vobject

from .calendar import search

bad_dates = re.compile(r'CREATED:0000\d{4}T\d*Z\r\n')
local_timezone = pytz.timezone('Europe/London')

def get_calendar(url):
    data = requests.get(url).content
    # There is some bad CREATED data that vobject barfs on if we don't clean up.
    data = bad_dates.sub('', data)
    return vobject.readOne(data)

class Event(object):
    def __init__(self, summary, start, location=None, description=None, vevent=None):
        self.start = start
        self.summary = summary
        self.location = location
        self.description = description
        self.vevent = vevent # for debugging

    def nice_time(self):
        if not hasattr(self.start, 'time'):
            return ''
        if self.start.time().hour < 11:
            return "Morning"
        elif self.start.time().hour >= 17:
            return "Evening"
            return "Afternoon"

    def __cmp__(self, other):
        if type(self.start) != type(other.start):
            # Cope with 'date' objects by converting both to 'date'
            return cmp( if hasattr(self.start, 'date') else self.start,
              if hasattr(other.start, 'date') else other.start)
            return cmp(self.start, other.start)

    def __repr__(self):
        return '<Event: %s, %s, location=%s, description=%s>' % (self.summary, self.start, self.location, self.description)

def this_sunday(request):
        cal = get_calendar(PREACHING_ICAL)
    except Exception:
        cal = None

    c = {}
    if cal is not None:
        # Clock should 'tick over' to the next week at the end of Sunday,
        # not part way through, so truncate hour to zero.
        today =
        raw_events = search(cal, today, today + timedelta(7))
        events = [Event(vevent.summary.value, dt,
                        if hasattr(vevent, 'description') else None)
                  for (dt, vevent) in raw_events]
        c['events'] = events
        c['message'] = "Calendar not available"

    return render(request, "christchurch/thissunday.html", c)

def upcoming_midweek(request):
        cal = get_calendar(MIDWEEK_ICAL)
    except Exception:
        cal = None

    c = {}
    if cal is not None:
        today =
        raw_events = search(cal, today, today + timedelta(60))
        events = [Event(v.summary.value if hasattr(v, 'summary') else '',
                        location=v.location.value if hasattr(v, 'location') else None,
                        description=v.description.value if hasattr(v, 'description') else None,
                        ) for (startdate, v) in raw_events]
        c['events'] = events
        c['message'] = "Calendar not available"

    return render(request, "christchurch/midweek.html", c)

# Photo cycler for front page.
# Getting it perfect is harder than it seems!
# == Constraints ==
# - we want users to get a set of random photos, which are cycled by javascript.
# - they should get a different set each visit
# - if javascript is turned off, only one image should be loaded, since only one
#   will be seen (and if CSS is missing, we don't want a bunch of photos
#   showing)
# - if the set of photos is to be fully random, then the first one must be, and
#   this one has to be random by a method that doesn't rely on javascript.
# - we don't want to make a custom view for the whole page i.e. want to be able
#   to do this just by including a snippet of HTML on the home page.
# - ideally we don't want to use an iframe, because that will delay the
#   appearance of the photo (requires an extra, serial HTTP request).
# - we want to avoid repeats within the set, especially repeating the same photo
#   twice in a row, which looks bad.
# - don't want to store anything in db, and don't have shared cache available
# - can't rely on per-process state in web server, because it can be
#   multi-process
# - to add new images, we want to just add them to the relevant directory,
#   so need some server-side code that will look in this dir.
# == Solution ==
# - initial image is hardcoded to src='/photochanger/'
# - this view:
#   - selects a set of photos at random, with no repeats.
#   - returns a redirect to the first one
#   - with a cookie that indicates the rest
#   - client side javascript can read the cookie
#     and do the rest.
#   - response is marked 'never cache' so they
#     get different one next time.

def photochanger(request):
    Returns a redirect to a random photo in the slideshow
    slideshow_dir = 'photos/slideshow'
    files = os.listdir(os.path.join(settings.MEDIA_ROOT, slideshow_dir))
    files = [f for f in files if f.endswith('.jpg')]

    chosen = []
    for i in range(0, 5):
        choice = random.choice(files)

    response = HttpResponseRedirect(os.path.join(settings.MEDIA_URL, slideshow_dir,

    # filenames are short, so can store in a cookie
    response.set_cookie('photocycler', '|'.join(chosen[1:]))

    # We want the visitor to get different photos each time,
    # so we say "No really, don't cache this"
    patch_cache_control(response, no_cache=True)
    patch_cache_control(response, must_revalidate=True)
    return response