Removed julian/tests.py and julian/views.py. Switched Location handling from a settin...
authorStephen Burrows <stephen.r.burrows@gmail.com>
Wed, 9 Feb 2011 17:02:57 +0000 (12:02 -0500)
committerStephen Burrows <stephen.r.burrows@gmail.com>
Wed, 9 Feb 2011 17:23:07 +0000 (12:23 -0500)
contrib/julian/fields.py [new file with mode: 0644]
contrib/julian/models.py
contrib/julian/tests.py [deleted file]
contrib/julian/views.py [deleted file]

diff --git a/contrib/julian/fields.py b/contrib/julian/fields.py
new file mode 100644 (file)
index 0000000..cbb1a41
--- /dev/null
@@ -0,0 +1,22 @@
+from django.contrib.localflavor.us.forms import USZipCodeField as USZipCodeFormField
+from django.core.validators import RegexValidator
+from django.db import models
+
+
+class USZipCodeField(models.CharField):
+       default_validators = [RegexValidator(r'^\d{5}(?:-\d{4})?$')]
+       
+       def __init__(self, *args, **kwargs):
+               kwargs['max_length'] = 10
+               super(USZipCodeField, self).__init__(*args, **kwargs)
+       
+       def formfield(self, form_class=USZipCodeFormField, **kwargs):
+               return super(USZipCodeField, self).formfield(form_class, **kwargs)
+
+
+try:
+       from south.modelsinspector import add_introspection_rules
+except ImportError:
+       pass
+else:
+       add_introspection_rules([], ["^philo\.contrib\.julian\.fields\.USZipCodeField"])
\ No newline at end of file
index 33c4d51..c9bd6da 100644 (file)
+from django.conf import settings
+from django.contrib.localflavor.us.models import USStateField
+from django.contrib.contenttypes.generic import GenericForeignKey, GenericRelation
+from django.contrib.contenttypes.models import ContentType
+from django.core.exceptions import ValidationError
+from django.core.validators import RegexValidator, MinValueValidator, MaxValueValidator
 from django.db import models
-from django.contrib.auth.models import User
+from philo.contrib.julian.fields import USZipCodeField
 from philo.models.base import Tag, Entity, Titled
+from philo.models.fields import TemplateField
+from philo.utils import ContentTypeSubclassLimiter
 import datetime
+import re
 
-if not hasattr(settings, 'PHILO_LOCATION_MODULE'):
-       class Location(Entity, Titled):
-               slug = models.SlugField(max_length=255, unique=True)
-
-# Needs to be organised in a sensical order.
-class Event(Entity, Titled):
-       description = models.TextField()
-       start_time = models.DateTimeField(blank=True, null=True)
-       end_time = models.DateTimeField(blank=True, null=True)
-       is_all_day_event = models.BooleanField(default=False)
-       location = models.ForeignKey(getattr(settings, 'PHILO_LOCATION_MODULE', Location), related_name='events', blank=True, null=True)
+
+# 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}")
+
+
+class Location(Entity):
+       name = models.CharField(max_length=150, blank=True)
+       description = models.TextField(blank=True)
+       
+       longitude = models.FloatField(blank=True, validators=[MinValueValidator(-180), MaxValueValidator(180)])
+       latitude = models.FloatField(blank=True, validators=[MinValueValidator(-90), MaxValueValidator(90)])
+       
+       events = GenericRelation('Event')
+       
+       def clean(self):
+               if not (self.name or self.description) or (self.longitude is None and self.latitude is None):
+                       raise ValidationError("Either a name and description or a latitude and longitude must be defined.")
+       
+       class Meta:
+               abstract = True
+
+
+_location_content_type_limiter = ContentTypeSubclassLimiter(Location)
+
+
+# TODO: Can we track when a building is open? Hmm...
+class Building(Location):
+       """A building is a location with a street address."""
+       address = models.CharField(max_length=255)
+       city = models.CharField(max_length=150)
+       
+       class Meta:
+               abstract = True
+
+
+_building_content_type_limiter = ContentTypeSubclassLimiter(Building)
+
+
+class USBuilding(Building):
+       state = USStateField()
+       zipcode = USZipCodeField()
+       
+       class Meta:
+               verbose_name = "Building (US)"
+               verbose_name_plural = "Buildings (US)"
+
+
+class Venue(Location):
+       """A venue is a location inside a building"""
+       building_content_type = models.ForeignKey(ContentType, limit_choices_to=_building_content_type_limiter)
+       building_pk = models.TextField()
+       building = GenericForeignKey('building_content_type', 'building_pk')
+
+
+class TimedModel(models.Model):
+       start_date = models.DateField(help_text="YYYY-MM-DD")
+       start_time = models.TimeField(blank=True, null=True, help_text="HH:MM:SS - 24 hour clock")
+       end_date = models.DateField()
+       end_time = models.TimeField(blank=True, null=True)
+       
+       def is_all_day(self):
+               return self.start_time is None and self.end_time is None
+       
+       class Meta:
+               abstract = True
+
+
+class Event(Entity, Titled, TimedModel):
+       location_content_type = models.ForeignKey(ContentType, limit_choices_to=_location_content_type_limiter)
+       location_pk = models.TextField()
+       location = GenericForeignKey('location_content_type', 'location_pk')
+       
+       description = TemplateField()
+       
        tags = models.ManyToManyField(Tag, blank=True, null=True)
-       parent_event = models.ForeignKey(Event, blank=True, null=True)                          # To handle series' of events.
-       user = models.ForeignKey(getattr(settings, 'PHILO_PERSON_MODULE', User))        # Should this be optional?
-       url = models.URLField(blank=True, null=True)
-       attachment = models.FileField(upload_to='events/attachments/%Y/%m/%d', blank=True, null=True)
-       image = models.ImageField(upload_to='events/images/%Y/%m/%d', blank=True, null=True)
+       
+       parent_event = models.ForeignKey('self', blank=True, null=True)
+       
+       owner = models.ForeignKey(getattr(settings, 'PHILO_PERSON_MODULE', 'auth.User'))
 
 
-class Calendar(Entity, Titled):
-       slug = models.SlugField(max_length=255, unique=True)
+class Calendar(Entity):
+       name = models.CharField(max_length=100)
+       #slug = models.SlugField(max_length=255, unique=True)
        events = models.ManyToManyField(Event, related_name='calendars')
        
-# NOTES: Only let start time be blank if it has child events with times.
\ No newline at end of file
+       # TODO: Can we auto-generate this on save based on site id and calendar name and settings language?
+       uuid = models.CharField("Calendar UUID", max_length=100, unique=True, help_text="Should conform to Formal Public Identifier format. See <http://en.wikipedia.org/wiki/Formal_Public_Identifier>", validators=[RegexValidator(FPI_REGEX)])
\ No newline at end of file
diff --git a/contrib/julian/tests.py b/contrib/julian/tests.py
deleted file mode 100644 (file)
index 2247054..0000000
+++ /dev/null
@@ -1,23 +0,0 @@
-"""
-This file demonstrates two different styles of tests (one doctest and one
-unittest). These will both pass when you run "manage.py test".
-
-Replace these with more appropriate tests for your application.
-"""
-
-from django.test import TestCase
-
-class SimpleTest(TestCase):
-    def test_basic_addition(self):
-        """
-        Tests that 1 + 1 always equals 2.
-        """
-        self.failUnlessEqual(1 + 1, 2)
-
-__test__ = {"doctest": """
-Another way to test that 1 + 1 is equal to 2.
-
->>> 1 + 1 == 2
-True
-"""}
-
diff --git a/contrib/julian/views.py b/contrib/julian/views.py
deleted file mode 100644 (file)
index 60f00ef..0000000
+++ /dev/null
@@ -1 +0,0 @@
-# Create your views here.