From c11e2882169bdc06828c782e65bde3f13a03d044 Mon Sep 17 00:00:00 2001 From: Stephen Burrows Date: Wed, 9 Feb 2011 12:02:57 -0500 Subject: [PATCH] Removed julian/tests.py and julian/views.py. Switched Location handling from a setting to using a GenericForeignKey to non-abstract Location subclasses. --- contrib/julian/fields.py | 22 ++++++++ contrib/julian/models.py | 114 ++++++++++++++++++++++++++++++++------- contrib/julian/tests.py | 23 -------- contrib/julian/views.py | 1 - 4 files changed, 116 insertions(+), 44 deletions(-) create mode 100644 contrib/julian/fields.py delete mode 100644 contrib/julian/tests.py delete mode 100644 contrib/julian/views.py diff --git a/contrib/julian/fields.py b/contrib/julian/fields.py new file mode 100644 index 0000000..cbb1a41 --- /dev/null +++ b/contrib/julian/fields.py @@ -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 diff --git a/contrib/julian/models.py b/contrib/julian/models.py index 33c4d51..c9bd6da 100644 --- a/contrib/julian/models.py +++ b/contrib/julian/models.py @@ -1,29 +1,103 @@ +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 ", 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 index 2247054..0000000 --- a/contrib/julian/tests.py +++ /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 index 60f00ef..0000000 --- a/contrib/julian/views.py +++ /dev/null @@ -1 +0,0 @@ -# Create your views here. -- 2.20.1