Added slug to the context for ajax searches.
[philo.git] / contrib / julian / models.py
index 14a0b17..5c49c7e 100644 (file)
@@ -3,9 +3,11 @@ from django.conf.urls.defaults import url, patterns, include
 from django.contrib.auth.models import User
 from django.contrib.contenttypes.generic import GenericForeignKey
 from django.contrib.contenttypes.models import ContentType
+from django.contrib.sites.models import Site
 from django.core.exceptions import ValidationError, ObjectDoesNotExist
 from django.core.validators import RegexValidator
 from django.db import models
+from django.db.models.query import QuerySet
 from django.http import HttpResponse, Http404
 from django.utils.encoding import force_unicode
 from philo.contrib.julian.feedgenerator import ICalendarFeed
@@ -13,16 +15,29 @@ from philo.contrib.penfield.models import FeedView, FEEDS
 from philo.exceptions import ViewCanNotProvideSubpath
 from philo.models import Tag, Entity, Page, TemplateField
 from philo.utils import ContentTypeRegistryLimiter
-import re, datetime, calendar
+import datetime, calendar
 
 
-# TODO: Could this regex more closely match the Formal Public Identifier spec?
-# http://xml.coverpages.org/tauber-fpi.html
-FPI_REGEX = re.compile(r"(|\+//|-//)[^/]+//[^/]+//[A-Z]{2}")
+__all__ = ('register_location_model', 'unregister_location_model', 'Location', 'TimedModel', 'Event', 'Calendar', 'CalendarView',)
 
 
 ICALENDAR = ICalendarFeed.mime_type
 FEEDS[ICALENDAR] = ICalendarFeed
+try:
+       DEFAULT_SITE = Site.objects.get_current()
+except:
+       DEFAULT_SITE = None
+_languages = dict(settings.LANGUAGES)
+try:
+       _languages[settings.LANGUAGE_CODE]
+       DEFAULT_LANGUAGE = settings.LANGUAGE_CODE
+except KeyError:
+       try:
+               lang = settings.LANGUAGE_CODE.split('-')[0]
+               _languages[lang]
+               DEFAULT_LANGUAGE = lang
+       except KeyError:
+               DEFAULT_LANGUAGE = None
 
 
 location_content_type_limiter = ContentTypeRegistryLimiter()
@@ -64,15 +79,29 @@ class TimedModel(models.Model):
                        raise ValidationError("A %s cannot end before it starts." % self.__class__.__name__)
        
        def get_start(self):
-               return self.start_date
+               return datetime.datetime.combine(self.start_date, self.start_time) if self.start_time else self.start_date
        
        def get_end(self):
-               return self.end_date
+               return datetime.datetime.combine(self.end_date, self.end_time) if self.end_time else self.end_date
        
        class Meta:
                abstract = True
 
 
+class EventManager(models.Manager):
+       def get_query_set(self):
+               return EventQuerySet(self.model)
+
+class EventQuerySet(QuerySet):
+       def upcoming(self):
+               return self.filter(start_date__gte=datetime.date.today())
+       def current(self):
+               return self.filter(start_date__lte=datetime.date.today(), end_date__gte=datetime.date.today())
+       def single_day(self):
+               return self.filter(start_date__exact=models.F('end_date'))
+       def multiday(self):
+               return self.exclude(start_date__exact=models.F('end_date'))
+
 class Event(Entity, TimedModel):
        name = models.CharField(max_length=255)
        slug = models.SlugField(max_length=255, unique_for_date='start_date')
@@ -92,23 +121,41 @@ class Event(Entity, TimedModel):
        
        created = models.DateTimeField(auto_now_add=True)
        last_modified = models.DateTimeField(auto_now=True)
-       uuid = models.TextField() # Format?
+       
+       site = models.ForeignKey(Site, default=DEFAULT_SITE)
+       
+       @property
+       def uuid(self):
+               return "%s@%s" % (self.created.isoformat(), getattr(self.site, 'domain', 'None'))
+       
+       objects = EventManager()
        
        def __unicode__(self):
                return self.name
+       
+       class Meta:
+               unique_together = ('site', 'created')
 
 
 class Calendar(Entity):
        name = models.CharField(max_length=100)
        slug = models.SlugField(max_length=100)
        description = models.TextField(blank=True)
-       events = models.ManyToManyField(Event, related_name='calendars')
+       events = models.ManyToManyField(Event, related_name='calendars', blank=True)
        
-       # TODO: Can we auto-generate this on save based on site id and calendar name and settings language?
-       uuid = models.TextField("Calendar UUID", unique=True, help_text="Should conform to Formal Public Identifier format. See <a href='http://en.wikipedia.org/wiki/Formal_Public_Identifier'>Wikipedia</a>.", validators=[RegexValidator(FPI_REGEX)])
+       site = models.ForeignKey(Site, default=DEFAULT_SITE)
+       language = models.CharField(max_length=5, choices=settings.LANGUAGES, default=DEFAULT_LANGUAGE)
        
        def __unicode__(self):
                return self.name
+       
+       @property
+       def fpi(self):
+               # See http://xml.coverpages.org/tauber-fpi.html or ISO 9070:1991 for format information.
+               return "-//%s//%s//%s" % (self.site.name, self.name, self.language.split('-')[0].upper())
+       
+       class Meta:
+               unique_together = ('name', 'site', 'language')
 
 
 class CalendarView(FeedView):
@@ -149,35 +196,28 @@ class CalendarView(FeedView):
                        return 'entries_by_tag', [], {'tag_slugs': '/'.join(obj)}
                raise ViewCanNotProvideSubpath
        
-       def timespan_patterns(self, timespan_name):
-               return self.feed_patterns('get_events_by_timespan', 'timespan_page', "events_by_%s" % timespan_name)
+       def timespan_patterns(self, pattern, timespan_name):
+               return self.feed_patterns(pattern, 'get_events_by_timespan', 'timespan_page', "events_by_%s" % timespan_name)
        
        @property
        def urlpatterns(self):
-               urlpatterns = patterns('',
-                       url(r'^', include(self.feed_patterns('get_all_events', 'index_page', 'index'))),
-                       
-                       url(r'^(?P<year>\d{4})', include(self.timespan_patterns('year'))),
-                       url(r'^(?P<year>\d{4})/(?P<month>\d{2})', include(self.timespan_patterns('month'))),
-                       url(r'^(?P<year>\d{4})/(?P<month>\d{2})/(?P<day>\d{2})', include(self.timespan_patterns('day'))),
-                       #url(r'^(?P<year>\d{4})/(?P<month>\d{2})/(?P<day>\d{2})/(?P<hour>\d{1,2})', include(self.timespan_patterns('hour'))),
-                       url(r'(?P<year>\d{4})/(?P<month>\d{2})/(?P<day>\d{2})/(?P<slug>[\w-]+)', self.event_detail_view, name="event_detail"),
-                       
-                       url(r'^%s/(?P<username>[^/]+)' % self.owner_permalink_base, include(self.feed_patterns('get_events_by_owner', 'owner_page', 'events_by_user'))),
+               # Perhaps timespans should be done with GET parameters? Or two /-separated
+               # date slugs? (e.g. 2010-02-1/2010-02-2) or a start and duration?
+               # (e.g. 2010-02-01/week/ or ?d=2010-02-01&l=week)
+               urlpatterns = self.feed_patterns(r'^', 'get_all_events', 'index_page', 'index') + \
+                       self.timespan_patterns(r'^(?P<year>\d{4})', 'year') + \
+                       self.timespan_patterns(r'^(?P<year>\d{4})/(?P<month>\d{2})', 'month') + \
+                       self.timespan_patterns(r'^(?P<year>\d{4})/(?P<month>\d{2})/(?P<day>\d{2})', 'day') + \
+                       self.feed_patterns(r'^%s/(?P<username>[^/]+)' % self.owner_permalink_base, 'get_events_by_owner', 'owner_page', 'events_by_user') + \
+                       self.feed_patterns(r'^%s/(?P<app_label>\w+)/(?P<model>\w+)/(?P<pk>[^/]+)' % self.location_permalink_base, 'get_events_by_location', 'location_page', 'events_by_location') + \
+                       self.feed_patterns(r'^%s/(?P<tag_slugs>[-\w]+[-+/\w]*)' % self.tag_permalink_base, 'get_events_by_tag', 'tag_page', 'events_by_tag') + \
+                       patterns('',
+                               url(r'(?P<year>\d{4})/(?P<month>\d{2})/(?P<day>\d{2})/(?P<slug>[\w-]+)$', self.event_detail_view, name="event_detail"),
+                       )
                        
                        # Some sort of shortcut for a location would be useful. This could be on a per-calendar
                        # or per-calendar-view basis.
                        #url(r'^%s/(?P<slug>[\w-]+)' % self.location_permalink_base, ...)
-                       url(r'^%s/(?P<app_label>\w+)/(?P<model>\w+)/(?P<pk>[^/]+)' % self.location_permalink_base, include(self.feed_patterns('get_events_by_location', 'location_page', 'events_by_location'))),
-               )
-               
-               if self.feeds_enabled:
-                       urlpatterns += patterns('',
-                               url(r'^%s/(?P<tag_slugs>[-\w]+[-+/\w]*)/%s$' % (self.tag_permalink_base, self.feed_suffix), self.feed_view('get_events_by_tag', 'events_by_tag_feed'), name='events_by_tag_feed'),
-                       )
-               urlpatterns += patterns('',
-                       url(r'^%s/(?P<tag_slugs>[-\w]+[-+/\w]*)$' % self.tag_permalink_base, self.page_view('get_events_by_tag', 'tag_page'), name='events_by_tag')
-               )
                
                if self.tag_archive_page:
                        urlpatterns += patterns('',
@@ -371,8 +411,7 @@ class CalendarView(FeedView):
                return ""
        
        def feed_guid(self, obj):
-               # Is this correct? Should I have a different id for different subfeeds?
-               return obj.uuid
+               return obj.fpi
        
        def description(self, obj):
                return obj.description