Metadata-Version: 1.1
Name: django-localized-recurrence
Version: 0.1.1
Summary: Store events that recur in users' local times.
Home-page: UNKNOWN
Author: Erik Swanson
Author-email: theerikswanson@gmail.com
License: MIT License
Description: [![Build Status](https://travis-ci.org/ambitioninc/django-localized-recurrence.svg?branch=develop)](https://travis-ci.org/ambitioninc/django-localized-recurrence)
        
        Django Localized Recurrence
        ===========================
        
        Store events that recur in users' local times.
        
        Overview
        ----------------------------------------
        
        Your site has a lot of users, spread across different timezones. You
        want the allow them to schedule recurring events. Without localized
        recurrences, it is difficult to ensure that something happens at 3:00
        PM every Wednesday in a User's local time. With localized recurrences,
        it is easy.
        
        The goal of this library is to automatically keep track of when your
        users expect events to happen next, even across daylight savings time
        boundaries, allowing you to interact with these events purely in UTC.
        
        The Usage section of this document describes patterns for using
        localized-recurrences in more detail.
        
        
        Installation
        ----------------------------------------
        
        This packages is currently only available through github. The
        intention is to make it available on Pypi, but until then it can be
        installed through pip with:
        
            pip install git+git://github.com/ambitioninc/django-localized-recurrence.git@master
        
        
        Usage
        ----------------------------------------
        
        ### Creating a recurrence
        
        The following is an example of a daily recurring event, at 3:00 PM in
        the user's local time in the Eastern United States.
        
            from datetime import timedelta
        
            from localized_recurrence.models import LocalizedRecurrence
        
            LocalizedRecurrence.objects.create(
                interval='DAY',
                offset=timedelta(hours=15),
                timezone='US/Eastern',
            )
        
        The arguments to create this event are:
        
        - interval: One of the three character strings: 'DAY', 'WEEK', or
          'MONTH', specifying how often the event happens.
        
        - offset: A python timedelta containing the amount of time into the
          interval the event is supposed to recur. That is, if the interval is
          daily, then a recurrence at 3:00 PM daily would correspond to an
          `offset` of `timedelta(hours=15)`. If the interval is weekly, then a
          recurrence at 1:00 AM on Tuesday would correspond to an `offset` of
          `timedelta(days=1, hours=1)`.
        
        - timezone: A string (or timezone object) that can be accepted into a
          pytz timezone_field, representing the timezone of the user.
        
        ### Acting on a recurrence
        
        Django localized-recurrence does not specify a method for checking
        when recurrences are due. The user of this app is in complete control
        of how these recurrences are to be checked. For example, they could be
        checked in the view code when a user loads a page, or in a celery beat
        task.
        
        In order to support a broad range of use cases, localized-recurrence
        limits itself to two actions.
        
        1. Checking when the event is next scheduled to recur.
        2. Updating the event to have occured in this interval.
        
        With this model, the first step is to check if a `LocalizedRecurrence`
        is due to occur. For a given `LocalizedRecurrence` object, called, say
        `my_daily_event`, this check is as simple as:
        
            my_daily_event.next_scheduled < datetime.utcnow()
        
        Or, to find all the `LocalizedRecurrence` instance which are due:
        
            past_due = LocalizedRecurrence.objects.filter(next_scheduled__lte=datetime.utcnow())
        
        Then, after taking whatever action goes along with an event, we need
        to update the database so that the types of checks we showed above
        will only return `True` in the next interval for the recurrence.
        
        For a queryset, such as `past_due` above, this is as simple as:
        
            past_due.update_schedule()
        
        With that call, django-localized-recurrence takes care of any local
        time changes in the interval, and sets the `next_scheduled` field of
        each object to the time, in UTC, of the event, as the user would
        expect it for their local time.
        
        To update a single record, first filter to that get record:
        
             LocalizedRecurrence.objects.filter(id=my_daily_event.id).update_schedule()
        
        ### A Calendar Event Example
        
        In this example we create a basic Calendar event, which store
        recurring events. The benefits of using a localized recurrence in this
        way are two fold. First, that you don't have to store a separate entry
        for every time the event happens, only one localized recurrence
        describing how the event recurs. Second, the code for keeping track of
        the conversion between a user's local time and UTC, even across
        daylight savings time boundaries is automatically handled by the
        recurrence updates.
        
        We start by defining a model with a foreign key to `LocalizedRecurrence`.
        
        
            from django.contrib.auth.models import User
            from django.db import models
        
            from localized_recurrence import LocalizedRecurrence
        
            class RecurringCalendarEvent(models.Model):
                user = models.ForeignKey(User)
                event_name = models.CharField(max_length=120)
                event_description = models.TextField()
                recurrence = models.ForeignKey(LocalizedRecurrence)
        
                objects = RecurringCalendarEventManager()
        
        To go along with the event model, we create a manager that can create
        the localized recurrence and event at the same time.
        
            class RecurringCalendarEventManager(models.Manager):
                def create_event(self, name, description, user, timezone, offset, interval):
                    recurrence = LocalizedRecurrence.objects.create(
                        interval=interval,
                        offset=time,
                        timezone=timezone
                    )
                    event = self.create(
                        user=user,
                        event_name=name,
                        description=description,
                        recurrence=recurrence
                    )
                    return event
        
        Then, in a file `views.py` we can create two views. The first is a
        view that is intended to show a simple calendar but that first checks
        to see if there are any events that are due to be shown the user. It
        does this by filtering on the `next_scheduled` field of the associated
        `LocalizedRecurrence` objects.
        
            from datetime import datetime
        
            from django.shortcuts import redirect
            from django.views.generic import TemplateView
        
            class CalendarView(TemplateView):
                template_name = 'calendar/full_calendar.html'
        
                def get(self, request, *args, **kwargs):
                    events_past_due = RecurringCalendarEvent.objects.filter(
                        user=self.request.user,
                        recurrence__next_scheduled__lte=datetime.utcnow()
                    )
                    if events_past_due.count() > 0:
                        redirect('calendar.event_notification')
                    else:
                        return super(CalendarView, self).get(request, *args, **kwargs)
        
        The second view (also assumed to be in the `views.py` file) is the
        view that displays any of the events that are past due. In this view,
        the `get_context_data` takes care of both passing the events to the
        template, but also updating the `LocalizedRecurrence` objects so that
        their `next_scheduled` fields are automatically set to the appropriate
        time in the future.
        
            class CalendarNotification(TemplateView):
                template_name = 'calendar/event_notification.html'
        
                def get_context_data(self):
                    context = super(CalendarNotification, self)
                    events_past_due = RecurringCalendarEvent.objects.filter(
                        user=self.request.user,
                        recurrence__next_scheduled__lte=datetime.utcnow()
                    )
                    LocalizedRecurrence.objects.filter(
                        id__in=[event.recurrence for event in events_past_due]
                    ).update_schedule()
                    context['events_past_due'] = events_past_due
                    return context
        
        Then all that's left is presenting this information in an attractive
        manner.
        
        In this usage of the LocalizedRecurrence objects, checking the
        recurrences depend on the user actually visiting a page to hit the
        code path. It would also be possible to check if the recurrences are
        past due in a separate task, like the celery-beat scheduler.
        
        Contributions and Licence
        ----------------------------------------
        
        Contributions are welcome, through issues or pull requests. If this
        documentation is unclear, feel free to ask for clarification.
        
        Licenced under the MIT License. For details see the LICENSE file.
        
Platform: UNKNOWN
Classifier: Environment :: Web Environment
Classifier: Framework :: Django
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT License
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python
Classifier: Programming Language :: Python :: 2.7
Classifier: Topic :: Internet :: WWW/HTTP
Classifier: Topic :: Internet :: WWW/HTTP :: Dynamic Content
