Removed location models and subclass limiting in favor of a ContentTypeRegistryLimite...
authorStephen Burrows <stephen.r.burrows@gmail.com>
Thu, 10 Feb 2011 17:29:13 +0000 (12:29 -0500)
committerStephen Burrows <stephen.r.burrows@gmail.com>
Thu, 10 Feb 2011 17:35:48 +0000 (12:35 -0500)
contrib/julian/admin.py [new file with mode: 0644]
contrib/julian/fields.py [deleted file]
contrib/julian/migrations/0001_initial.py [new file with mode: 0644]
contrib/julian/migrations/__init__.py [new file with mode: 0644]
contrib/julian/models.py

diff --git a/contrib/julian/admin.py b/contrib/julian/admin.py
new file mode 100644 (file)
index 0000000..a822e1d
--- /dev/null
@@ -0,0 +1,33 @@
+from django.contrib import admin
+from philo.admin import EntityAdmin
+from philo.contrib.julian.models import Location, Event, Calendar
+
+
+class LocationAdmin(EntityAdmin):
+       pass
+
+
+class EventAdmin(EntityAdmin):
+       fieldsets = (
+               (None, {
+                       'fields': ('title', 'description', 'tags', 'parent_event', 'owner')
+               }),
+               ('Location', {
+                       'fields': ('location_content_type', 'location_pk')
+               }),
+               ('Time', {
+                       'fields': (('start_date', 'start_time'), ('end_date', 'end_time'),),
+               })
+       )
+       related_lookup_fields = {
+               'generic': [["location_content_type", "location_pk"]]
+       }
+
+
+class CalendarAdmin(EntityAdmin):
+       pass
+
+
+admin.site.register(Location, LocationAdmin)
+admin.site.register(Event, EventAdmin)
+admin.site.register(Calendar, CalendarAdmin)
\ No newline at end of file
diff --git a/contrib/julian/fields.py b/contrib/julian/fields.py
deleted file mode 100644 (file)
index cbb1a41..0000000
+++ /dev/null
@@ -1,22 +0,0 @@
-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/migrations/0001_initial.py b/contrib/julian/migrations/0001_initial.py
new file mode 100644 (file)
index 0000000..d4864c6
--- /dev/null
@@ -0,0 +1,160 @@
+# encoding: utf-8
+import datetime
+from south.db import db
+from south.v2 import SchemaMigration
+from django.db import models
+
+class Migration(SchemaMigration):
+
+    def forwards(self, orm):
+        
+        # Adding model 'Location'
+        db.create_table('julian_location', (
+            ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
+            ('name', self.gf('django.db.models.fields.CharField')(max_length=255)),
+        ))
+        db.send_create_signal('julian', ['Location'])
+
+        # Adding model 'Event'
+        db.create_table('julian_event', (
+            ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
+            ('start_date', self.gf('django.db.models.fields.DateField')()),
+            ('start_time', self.gf('django.db.models.fields.TimeField')(null=True, blank=True)),
+            ('end_date', self.gf('django.db.models.fields.DateField')()),
+            ('end_time', self.gf('django.db.models.fields.TimeField')(null=True, blank=True)),
+            ('name', self.gf('django.db.models.fields.CharField')(max_length=255)),
+            ('slug', self.gf('django.db.models.fields.SlugField')(max_length=255, db_index=True)),
+            ('location_content_type', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['contenttypes.ContentType'])),
+            ('location_pk', self.gf('django.db.models.fields.TextField')()),
+            ('description', self.gf('philo.models.fields.TemplateField')()),
+            ('parent_event', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['julian.Event'], null=True, blank=True)),
+            ('owner', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['auth.User'])),
+        ))
+        db.send_create_signal('julian', ['Event'])
+
+        # Adding M2M table for field tags on 'Event'
+        db.create_table('julian_event_tags', (
+            ('id', models.AutoField(verbose_name='ID', primary_key=True, auto_created=True)),
+            ('event', models.ForeignKey(orm['julian.event'], null=False)),
+            ('tag', models.ForeignKey(orm['philo.tag'], null=False))
+        ))
+        db.create_unique('julian_event_tags', ['event_id', 'tag_id'])
+
+        # Adding model 'Calendar'
+        db.create_table('julian_calendar', (
+            ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
+            ('name', self.gf('django.db.models.fields.CharField')(max_length=100)),
+            ('uuid', self.gf('django.db.models.fields.CharField')(unique=True, max_length=100)),
+        ))
+        db.send_create_signal('julian', ['Calendar'])
+
+        # Adding M2M table for field events on 'Calendar'
+        db.create_table('julian_calendar_events', (
+            ('id', models.AutoField(verbose_name='ID', primary_key=True, auto_created=True)),
+            ('calendar', models.ForeignKey(orm['julian.calendar'], null=False)),
+            ('event', models.ForeignKey(orm['julian.event'], null=False))
+        ))
+        db.create_unique('julian_calendar_events', ['calendar_id', 'event_id'])
+
+
+    def backwards(self, orm):
+        
+        # Deleting model 'Location'
+        db.delete_table('julian_location')
+
+        # Deleting model 'Event'
+        db.delete_table('julian_event')
+
+        # Removing M2M table for field tags on 'Event'
+        db.delete_table('julian_event_tags')
+
+        # Deleting model 'Calendar'
+        db.delete_table('julian_calendar')
+
+        # Removing M2M table for field events on 'Calendar'
+        db.delete_table('julian_calendar_events')
+
+
+    models = {
+        'auth.group': {
+            'Meta': {'object_name': 'Group'},
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}),
+            'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'})
+        },
+        'auth.permission': {
+            'Meta': {'unique_together': "(('content_type', 'codename'),)", 'object_name': 'Permission'},
+            'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+            'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}),
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'name': ('django.db.models.fields.CharField', [], {'max_length': '50'})
+        },
+        'auth.user': {
+            'Meta': {'object_name': 'User'},
+            'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
+            'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}),
+            'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
+            'groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Group']", 'symmetrical': 'False', 'blank': 'True'}),
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True', 'blank': 'True'}),
+            'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}),
+            'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}),
+            'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
+            'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
+            'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
+            'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}),
+            'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'})
+        },
+        'contenttypes.contenttype': {
+            'Meta': {'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"},
+            'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+            'name': ('django.db.models.fields.CharField', [], {'max_length': '100'})
+        },
+        'julian.calendar': {
+            'Meta': {'object_name': 'Calendar'},
+            'events': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'calendars'", 'symmetrical': 'False', 'to': "orm['julian.Event']"}),
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+            'uuid': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '100'})
+        },
+        'julian.event': {
+            'Meta': {'object_name': 'Event'},
+            'description': ('philo.models.fields.TemplateField', [], {}),
+            'end_date': ('django.db.models.fields.DateField', [], {}),
+            'end_time': ('django.db.models.fields.TimeField', [], {'null': 'True', 'blank': 'True'}),
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'location_content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}),
+            'location_pk': ('django.db.models.fields.TextField', [], {}),
+            'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'owner': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']"}),
+            'parent_event': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['julian.Event']", 'null': 'True', 'blank': 'True'}),
+            'slug': ('django.db.models.fields.SlugField', [], {'max_length': '255', 'db_index': 'True'}),
+            'start_date': ('django.db.models.fields.DateField', [], {}),
+            'start_time': ('django.db.models.fields.TimeField', [], {'null': 'True', 'blank': 'True'}),
+            'tags': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'to': "orm['philo.Tag']", 'null': 'True', 'blank': 'True'})
+        },
+        'julian.location': {
+            'Meta': {'object_name': 'Location'},
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'name': ('django.db.models.fields.CharField', [], {'max_length': '255'})
+        },
+        'philo.attribute': {
+            'Meta': {'unique_together': "(('key', 'entity_content_type', 'entity_object_id'), ('value_content_type', 'value_object_id'))", 'object_name': 'Attribute'},
+            'entity_content_type': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'attribute_entity_set'", 'to': "orm['contenttypes.ContentType']"}),
+            'entity_object_id': ('django.db.models.fields.PositiveIntegerField', [], {'db_index': 'True'}),
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'key': ('django.db.models.fields.CharField', [], {'max_length': '255', 'db_index': 'True'}),
+            'value_content_type': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'attribute_value_set'", 'null': 'True', 'to': "orm['contenttypes.ContentType']"}),
+            'value_object_id': ('django.db.models.fields.PositiveIntegerField', [], {'db_index': 'True', 'null': 'True', 'blank': 'True'})
+        },
+        'philo.tag': {
+            'Meta': {'object_name': 'Tag'},
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'slug': ('django.db.models.fields.SlugField', [], {'unique': 'True', 'max_length': '255', 'db_index': 'True'})
+        }
+    }
+
+    complete_apps = ['julian']
diff --git a/contrib/julian/migrations/__init__.py b/contrib/julian/migrations/__init__.py
new file mode 100644 (file)
index 0000000..e69de29
index c9bd6da..29f5fd4 100644 (file)
@@ -1,15 +1,12 @@
 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.generic import GenericForeignKey
 from django.contrib.contenttypes.models import ContentType
 from django.core.exceptions import ValidationError
-from django.core.validators import RegexValidator, MinValueValidator, MaxValueValidator
+from django.core.validators import RegexValidator
 from django.db import models
-from philo.contrib.julian.fields import USZipCodeField
-from philo.models.base import Tag, Entity, Titled
+from philo.models.base import Tag, Entity
 from philo.models.fields import TemplateField
-from philo.utils import ContentTypeSubclassLimiter
-import datetime
+from philo.utils import ContentTypeRegistryLimiter
 import re
 
 
@@ -18,53 +15,25 @@ import re
 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 = ContentTypeRegistryLimiter()
 
 
-_location_content_type_limiter = ContentTypeSubclassLimiter(Location)
+def register_location_model(model):
+       location_content_type_limiter.register_class(model)
 
 
-# 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)
+def unregister_location_model(model):
+       location_content_type_limiter.unregister_class(model)
 
 
-class USBuilding(Building):
-       state = USStateField()
-       zipcode = USZipCodeField()
+class Location(Entity):
+       name = models.CharField(max_length=255)
        
-       class Meta:
-               verbose_name = "Building (US)"
-               verbose_name_plural = "Buildings (US)"
+       def __unicode__(self):
+               return self.name
 
 
-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')
+register_location_model(Location)
 
 
 class TimedModel(models.Model):
@@ -76,12 +45,22 @@ class TimedModel(models.Model):
        def is_all_day(self):
                return self.start_time is None and self.end_time is None
        
+       def clean(self):
+               if bool(self.start_time) != bool(self.end_time):
+                       raise ValidationError("A %s must have either a start time and an end time or neither.")
+               
+               if self.start_date > self.end_date or self.start_date == self.end_date and self.start_time > self.end_time:
+                       raise ValidationError("A %s cannot end before it starts." % self.__class__.__name__)
+       
        class Meta:
                abstract = True
 
 
-class Event(Entity, Titled, TimedModel):
-       location_content_type = models.ForeignKey(ContentType, limit_choices_to=_location_content_type_limiter)
+class Event(Entity, TimedModel):
+       name = models.CharField(max_length=255)
+       slug = models.SlugField(max_length=255)
+       
+       location_content_type = models.ForeignKey(ContentType, limit_choices_to=location_content_type_limiter)
        location_pk = models.TextField()
        location = GenericForeignKey('location_content_type', 'location_pk')
        
@@ -92,6 +71,8 @@ class Event(Entity, Titled, TimedModel):
        parent_event = models.ForeignKey('self', blank=True, null=True)
        
        owner = models.ForeignKey(getattr(settings, 'PHILO_PERSON_MODULE', 'auth.User'))
+       
+       # TODO: Add uid - use as pk?
 
 
 class Calendar(Entity):