diff --git a/gracedb/api.py b/gracedb/api.py
index 40d14484c25db1f2dc83b3af694b03a249add6f4..57c49600266f2a25b46493ed81c5b799582937d2 100644
--- a/gracedb/api.py
+++ b/gracedb/api.py
@@ -14,13 +14,16 @@ from django.contrib.auth.models import Group as AuthGroup
 from django.contrib.contenttypes.models import ContentType
 from gracedb.models import Event, Group, Search, Pipeline, EventLog, Tag
 from gracedb.models import EMGroup, EMBBEventLog, EMSPECTRUM
+#from gracedb.models import EMObservation, EMFootprint
 from gracedb.models import VOEvent
 from view_logic import create_label, get_performance_info
 from view_logic import _createEventFromForm
 from view_logic import create_eel
+from view_logic import create_emobservation
 from view_utils import fix_old_creation_request
 from view_utils import eventToDict, eventLogToDict, labelToDict
 from view_utils import embbEventLogToDict, voeventToDict
+from view_utils import emObservationToDict
 from view_utils import reverse
 
 from translator import handle_uploaded_data
@@ -898,6 +901,77 @@ class EMBBEventLogDetail(APIView):
 
         return Response(embbEventLogToDict(rv, request=request))
 
+
+#==================================================================
+# EMObservation (EMO)
+
+class EMObservationList(APIView):
+    """EMObservation Record List Resource
+
+    POST param 'message'
+    """
+    authentication_classes = (LigoAuthentication,)
+    permission_classes = (IsAuthenticated,IsAuthorizedForEvent,)
+
+    @event_and_auth_required
+    def get(self, request, event):
+        emo_set = event.emobservation_set.order_by("created","N")
+        count = emo_set.count()
+
+        emo = [ emObservationToDict(emo, request)
+                for emo in emo_set.iterator() ]
+
+        rv = {
+                'start': 0,
+                'numRows' : count,
+                'links' : {
+                    'self' : request.build_absolute_uri(),
+                    'first' : request.build_absolute_uri(),
+                    'last' : request.build_absolute_uri(),
+                    },
+                'observations' : emo,
+             }
+        return Response(rv)
+
+    @event_and_auth_required
+    def post(self, request, event):
+        try:
+            emo = create_emobservation(request.DATA, event, request.user)
+        except ValueError, e:
+            return Response("%s" % str(e), status=status.HTTP_400_BAD_REQUEST)
+        except IntegrityError, e:
+            return Response("Failed to save EMBB observation record: %s" % str(e),
+                    status=status.HTTP_503_SERVICE_UNAVAILABLE)
+        except Exception, e:
+            return Response("Problem creating EMBB Observation: %s" % str(e), 
+                status=status.HTTP_500_INTERNAL_SERVER_ERROR)
+
+        rv = emObservationToDict(emo, request=request)
+        response = Response(rv, status=status.HTTP_201_CREATED)
+        response['Location'] = rv['self']
+
+        # Issue alert.
+        description = "New EMBB observation record."
+        issueAlertForUpdate(event, description, doxmpp=True,
+            filename="", serialized_object=rv)
+
+        return response
+
+class EMObservationDetail(APIView):
+    authentication_classes = (LigoAuthentication,)
+    permission_classes = (IsAuthenticated,IsAuthorizedForEvent,)
+
+    @event_and_auth_required
+    def get(self, request, event, n):
+        try:
+            rv = event.emobservation_set.filter(N=n)[0]
+        except:
+            return Response("Observation record not nound",
+                    status=status.HTTP_404_NOT_FOUND)
+
+        return Response(emObservationToDict(rv, request=request))
+
+
 #==================================================================
 # Tags
 
@@ -1292,6 +1366,8 @@ class GracedbRoot(APIView):
         voevent = voevent.replace("G1200", "{graceid}")
         embb = reverse("embbeventlog-list", args=["G1200"], request=request)
         embb = embb.replace("G1200", "{graceid}")
+        emo = reverse("emobservation-list", args=["G1200"], request=request)
+        emo = emo.replace("G1200", "{graceid}")
 
         files = reverse("files", args=["G1200", "filename"], request=request)
         files = files.replace("G1200", "{graceid}")
@@ -1320,6 +1396,7 @@ class GracedbRoot(APIView):
                 "event-detail-template" : detail,
                 "voevent-list-template" : voevent,
                 "event-log-template" : log,
+                "emobservation-list-template": emo,
                 "embb-event-log-template" : embb,
                 "event-label-template" : labels,
                 "files-template" : files,
diff --git a/gracedb/migrations/0039_auto__add_emobservation__add_unique_emobservation_event_N__add_emfootp.py b/gracedb/migrations/0039_auto__add_emobservation__add_unique_emobservation_event_N__add_emfootp.py
new file mode 100644
index 0000000000000000000000000000000000000000..66fcae90abb273432a5aca847002c34eb38a496a
--- /dev/null
+++ b/gracedb/migrations/0039_auto__add_emobservation__add_unique_emobservation_event_N__add_emfootp.py
@@ -0,0 +1,356 @@
+# -*- coding: utf-8 -*-
+from south.utils import datetime_utils as 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 'EMObservation'
+        db.create_table(u'gracedb_emobservation', (
+            (u'id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
+            ('N', self.gf('django.db.models.fields.IntegerField')()),
+            ('created', self.gf('django.db.models.fields.DateTimeField')(auto_now_add=True, blank=True)),
+            ('event', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['gracedb.Event'])),
+            ('submitter', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['auth.User'])),
+            ('group', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['gracedb.EMGroup'])),
+            ('ra', self.gf('django.db.models.fields.FloatField')(null=True)),
+            ('dec', self.gf('django.db.models.fields.FloatField')(null=True)),
+            ('raWidth', self.gf('django.db.models.fields.FloatField')(null=True)),
+            ('decWidth', self.gf('django.db.models.fields.FloatField')(null=True)),
+        ))
+        db.send_create_signal(u'gracedb', ['EMObservation'])
+
+        # Adding unique constraint on 'EMObservation', fields ['event', 'N']
+        db.create_unique(u'gracedb_emobservation', ['event_id', 'N'])
+
+        # Adding model 'EMFootprint'
+        db.create_table(u'gracedb_emfootprint', (
+            (u'id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
+            ('N', self.gf('django.db.models.fields.IntegerField')()),
+            ('observation', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['gracedb.EMObservation'])),
+            ('ra', self.gf('django.db.models.fields.FloatField')()),
+            ('dec', self.gf('django.db.models.fields.FloatField')()),
+            ('raWidth', self.gf('django.db.models.fields.FloatField')()),
+            ('decWidth', self.gf('django.db.models.fields.FloatField')()),
+            ('start_time', self.gf('django.db.models.fields.DateTimeField')()),
+            ('exposure_time', self.gf('django.db.models.fields.PositiveIntegerField')()),
+        ))
+        db.send_create_signal(u'gracedb', ['EMFootprint'])
+
+        # Adding unique constraint on 'EMFootprint', fields ['observation', 'N']
+        db.create_unique(u'gracedb_emfootprint', ['observation_id', 'N'])
+
+
+    def backwards(self, orm):
+        # Removing unique constraint on 'EMFootprint', fields ['observation', 'N']
+        db.delete_unique(u'gracedb_emfootprint', ['observation_id', 'N'])
+
+        # Removing unique constraint on 'EMObservation', fields ['event', 'N']
+        db.delete_unique(u'gracedb_emobservation', ['event_id', 'N'])
+
+        # Deleting model 'EMObservation'
+        db.delete_table(u'gracedb_emobservation')
+
+        # Deleting model 'EMFootprint'
+        db.delete_table(u'gracedb_emfootprint')
+
+
+    models = {
+        u'auth.group': {
+            'Meta': {'object_name': 'Group'},
+            u'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': u"orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'})
+        },
+        u'auth.permission': {
+            'Meta': {'ordering': "(u'content_type__app_label', u'content_type__model', u'codename')", 'unique_together': "((u'content_type', u'codename'),)", 'object_name': 'Permission'},
+            'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+            'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['contenttypes.ContentType']"}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'name': ('django.db.models.fields.CharField', [], {'max_length': '50'})
+        },
+        u'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', [], {'symmetrical': 'False', 'related_name': "u'user_set'", 'blank': 'True', 'to': u"orm['auth.Group']"}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
+            'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            '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', [], {'symmetrical': 'False', 'related_name': "u'user_set'", 'blank': 'True', 'to': u"orm['auth.Permission']"}),
+            'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'})
+        },
+        u'contenttypes.contenttype': {
+            'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"},
+            'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+            u'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'})
+        },
+        u'gracedb.approval': {
+            'Meta': {'object_name': 'Approval'},
+            'approvedEvent': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['gracedb.Event']"}),
+            'approver': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['auth.User']"}),
+            'approvingCollaboration': ('django.db.models.fields.CharField', [], {'max_length': '1'}),
+            'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'})
+        },
+        u'gracedb.coincinspiralevent': {
+            'Meta': {'ordering': "['-id']", 'object_name': 'CoincInspiralEvent', '_ormbases': [u'gracedb.Event']},
+            'combined_far': ('django.db.models.fields.FloatField', [], {'null': 'True'}),
+            'end_time': ('django.db.models.fields.PositiveIntegerField', [], {'null': 'True'}),
+            'end_time_ns': ('django.db.models.fields.PositiveIntegerField', [], {'null': 'True'}),
+            u'event_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': u"orm['gracedb.Event']", 'unique': 'True', 'primary_key': 'True'}),
+            'false_alarm_rate': ('django.db.models.fields.FloatField', [], {'null': 'True'}),
+            'ifos': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '20'}),
+            'mass': ('django.db.models.fields.FloatField', [], {'null': 'True'}),
+            'mchirp': ('django.db.models.fields.FloatField', [], {'null': 'True'}),
+            'minimum_duration': ('django.db.models.fields.FloatField', [], {'null': 'True'}),
+            'snr': ('django.db.models.fields.FloatField', [], {'null': 'True'})
+        },
+        u'gracedb.embbeventlog': {
+            'Meta': {'ordering': "['-created', '-N']", 'unique_together': "(('event', 'N'),)", 'object_name': 'EMBBEventLog'},
+            'N': ('django.db.models.fields.IntegerField', [], {}),
+            'comment': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
+            'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
+            'dec': ('django.db.models.fields.FloatField', [], {'null': 'True'}),
+            'decList': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
+            'decWidth': ('django.db.models.fields.FloatField', [], {'null': 'True'}),
+            'decWidthList': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
+            'duration': ('django.db.models.fields.PositiveIntegerField', [], {'null': 'True'}),
+            'durationList': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
+            'eel_status': ('django.db.models.fields.CharField', [], {'max_length': '2'}),
+            'event': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['gracedb.Event']"}),
+            'extra_info_dict': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
+            'footprintID': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
+            'gpstime': ('django.db.models.fields.PositiveIntegerField', [], {'null': 'True'}),
+            'gpstimeList': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
+            'group': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['gracedb.EMGroup']"}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'instrument': ('django.db.models.fields.CharField', [], {'max_length': '200', 'blank': 'True'}),
+            'obs_status': ('django.db.models.fields.CharField', [], {'max_length': '2'}),
+            'ra': ('django.db.models.fields.FloatField', [], {'null': 'True'}),
+            'raList': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
+            'raWidth': ('django.db.models.fields.FloatField', [], {'null': 'True'}),
+            'raWidthList': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
+            'submitter': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['auth.User']"}),
+            'waveband': ('django.db.models.fields.CharField', [], {'max_length': '25'})
+        },
+        u'gracedb.emfootprint': {
+            'Meta': {'ordering': "['-N']", 'unique_together': "(('observation', 'N'),)", 'object_name': 'EMFootprint'},
+            'N': ('django.db.models.fields.IntegerField', [], {}),
+            'dec': ('django.db.models.fields.FloatField', [], {}),
+            'decWidth': ('django.db.models.fields.FloatField', [], {}),
+            'exposure_time': ('django.db.models.fields.PositiveIntegerField', [], {}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'observation': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['gracedb.EMObservation']"}),
+            'ra': ('django.db.models.fields.FloatField', [], {}),
+            'raWidth': ('django.db.models.fields.FloatField', [], {}),
+            'start_time': ('django.db.models.fields.DateTimeField', [], {})
+        },
+        u'gracedb.emgroup': {
+            'Meta': {'object_name': 'EMGroup'},
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '20'})
+        },
+        u'gracedb.emobservation': {
+            'Meta': {'ordering': "['-created', '-N']", 'unique_together': "(('event', 'N'),)", 'object_name': 'EMObservation'},
+            'N': ('django.db.models.fields.IntegerField', [], {}),
+            'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
+            'dec': ('django.db.models.fields.FloatField', [], {'null': 'True'}),
+            'decWidth': ('django.db.models.fields.FloatField', [], {'null': 'True'}),
+            'event': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['gracedb.Event']"}),
+            'group': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['gracedb.EMGroup']"}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'ra': ('django.db.models.fields.FloatField', [], {'null': 'True'}),
+            'raWidth': ('django.db.models.fields.FloatField', [], {'null': 'True'}),
+            'submitter': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['auth.User']"})
+        },
+        u'gracedb.event': {
+            'Meta': {'ordering': "['-id']", 'object_name': 'Event'},
+            'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
+            'far': ('django.db.models.fields.FloatField', [], {'null': 'True'}),
+            'gpstime': ('django.db.models.fields.DecimalField', [], {'null': 'True', 'max_digits': '16', 'decimal_places': '6'}),
+            'group': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['gracedb.Group']"}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'instruments': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '20'}),
+            'labels': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['gracedb.Label']", 'through': u"orm['gracedb.Labelling']", 'symmetrical': 'False'}),
+            'likelihood': ('django.db.models.fields.FloatField', [], {'null': 'True'}),
+            'nevents': ('django.db.models.fields.PositiveIntegerField', [], {'null': 'True'}),
+            'perms': ('django.db.models.fields.TextField', [], {'null': 'True'}),
+            'pipeline': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['gracedb.Pipeline']"}),
+            'search': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['gracedb.Search']", 'null': 'True'}),
+            'submitter': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['auth.User']"})
+        },
+        u'gracedb.eventlog': {
+            'Meta': {'ordering': "['-created', '-N']", 'unique_together': "(('event', 'N'),)", 'object_name': 'EventLog'},
+            'N': ('django.db.models.fields.IntegerField', [], {}),
+            'comment': ('django.db.models.fields.TextField', [], {}),
+            'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
+            'event': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['gracedb.Event']"}),
+            'file_version': ('django.db.models.fields.IntegerField', [], {'null': 'True'}),
+            'filename': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '100'}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'issuer': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['auth.User']"})
+        },
+        u'gracedb.grbevent': {
+            'Meta': {'ordering': "['-id']", 'object_name': 'GrbEvent', '_ormbases': [u'gracedb.Event']},
+            'author_ivorn': ('django.db.models.fields.CharField', [], {'max_length': '200', 'null': 'True'}),
+            'author_shortname': ('django.db.models.fields.CharField', [], {'max_length': '200', 'null': 'True'}),
+            'coord_system': ('django.db.models.fields.CharField', [], {'max_length': '200', 'null': 'True'}),
+            'dec': ('django.db.models.fields.FloatField', [], {'null': 'True'}),
+            'error_radius': ('django.db.models.fields.FloatField', [], {'null': 'True'}),
+            u'event_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': u"orm['gracedb.Event']", 'unique': 'True', 'primary_key': 'True'}),
+            'how_description': ('django.db.models.fields.CharField', [], {'max_length': '200', 'null': 'True'}),
+            'how_reference_url': ('django.db.models.fields.URLField', [], {'max_length': '200', 'null': 'True'}),
+            'ivorn': ('django.db.models.fields.CharField', [], {'max_length': '200', 'null': 'True'}),
+            'observatory_location_id': ('django.db.models.fields.CharField', [], {'max_length': '200', 'null': 'True'}),
+            'ra': ('django.db.models.fields.FloatField', [], {'null': 'True'})
+        },
+        u'gracedb.group': {
+            'Meta': {'object_name': 'Group'},
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'name': ('django.db.models.fields.CharField', [], {'max_length': '20'})
+        },
+        u'gracedb.label': {
+            'Meta': {'object_name': 'Label'},
+            'defaultColor': ('django.db.models.fields.CharField', [], {'default': "'black'", 'max_length': '20'}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '20'})
+        },
+        u'gracedb.labelling': {
+            'Meta': {'object_name': 'Labelling'},
+            'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
+            'creator': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['auth.User']"}),
+            'event': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['gracedb.Event']"}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'label': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['gracedb.Label']"})
+        },
+        u'gracedb.multiburstevent': {
+            'Meta': {'ordering': "['-id']", 'object_name': 'MultiBurstEvent', '_ormbases': [u'gracedb.Event']},
+            'amplitude': ('django.db.models.fields.FloatField', [], {'null': 'True'}),
+            'bandwidth': ('django.db.models.fields.FloatField', [], {'null': 'True'}),
+            'central_freq': ('django.db.models.fields.FloatField', [], {'null': 'True'}),
+            'confidence': ('django.db.models.fields.FloatField', [], {'null': 'True'}),
+            'duration': ('django.db.models.fields.FloatField', [], {'null': 'True'}),
+            u'event_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': u"orm['gracedb.Event']", 'unique': 'True', 'primary_key': 'True'}),
+            'false_alarm_rate': ('django.db.models.fields.FloatField', [], {'null': 'True'}),
+            'ifos': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '20'}),
+            'ligo_angle': ('django.db.models.fields.FloatField', [], {'null': 'True'}),
+            'ligo_angle_sig': ('django.db.models.fields.FloatField', [], {'null': 'True'}),
+            'ligo_axis_dec': ('django.db.models.fields.FloatField', [], {'null': 'True'}),
+            'ligo_axis_ra': ('django.db.models.fields.FloatField', [], {'null': 'True'}),
+            'peak_time': ('django.db.models.fields.PositiveIntegerField', [], {'null': 'True'}),
+            'peak_time_ns': ('django.db.models.fields.PositiveIntegerField', [], {'null': 'True'}),
+            'snr': ('django.db.models.fields.FloatField', [], {'null': 'True'}),
+            'start_time': ('django.db.models.fields.PositiveIntegerField', [], {'null': 'True'}),
+            'start_time_ns': ('django.db.models.fields.PositiveIntegerField', [], {'null': 'True'})
+        },
+        u'gracedb.pipeline': {
+            'Meta': {'object_name': 'Pipeline'},
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'name': ('django.db.models.fields.CharField', [], {'max_length': '100'})
+        },
+        u'gracedb.search': {
+            'Meta': {'object_name': 'Search'},
+            'description': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'name': ('django.db.models.fields.CharField', [], {'max_length': '100'})
+        },
+        u'gracedb.singleinspiral': {
+            'Gamma0': ('django.db.models.fields.FloatField', [], {'null': 'True'}),
+            'Gamma1': ('django.db.models.fields.FloatField', [], {'null': 'True'}),
+            'Gamma2': ('django.db.models.fields.FloatField', [], {'null': 'True'}),
+            'Gamma3': ('django.db.models.fields.FloatField', [], {'null': 'True'}),
+            'Gamma4': ('django.db.models.fields.FloatField', [], {'null': 'True'}),
+            'Gamma5': ('django.db.models.fields.FloatField', [], {'null': 'True'}),
+            'Gamma6': ('django.db.models.fields.FloatField', [], {'null': 'True'}),
+            'Gamma7': ('django.db.models.fields.FloatField', [], {'null': 'True'}),
+            'Gamma8': ('django.db.models.fields.FloatField', [], {'null': 'True'}),
+            'Gamma9': ('django.db.models.fields.FloatField', [], {'null': 'True'}),
+            'Meta': {'object_name': 'SingleInspiral'},
+            'alpha': ('django.db.models.fields.FloatField', [], {'null': 'True'}),
+            'alpha1': ('django.db.models.fields.FloatField', [], {'null': 'True'}),
+            'alpha2': ('django.db.models.fields.FloatField', [], {'null': 'True'}),
+            'alpha3': ('django.db.models.fields.FloatField', [], {'null': 'True'}),
+            'alpha4': ('django.db.models.fields.FloatField', [], {'null': 'True'}),
+            'alpha5': ('django.db.models.fields.FloatField', [], {'null': 'True'}),
+            'alpha6': ('django.db.models.fields.FloatField', [], {'null': 'True'}),
+            'amplitude': ('django.db.models.fields.FloatField', [], {'null': 'True'}),
+            'bank_chisq': ('django.db.models.fields.FloatField', [], {'null': 'True'}),
+            'bank_chisq_dof': ('django.db.models.fields.IntegerField', [], {'null': 'True'}),
+            'beta': ('django.db.models.fields.FloatField', [], {'null': 'True'}),
+            'channel': ('django.db.models.fields.CharField', [], {'max_length': '20'}),
+            'chi': ('django.db.models.fields.FloatField', [], {'null': 'True'}),
+            'chisq': ('django.db.models.fields.FloatField', [], {'null': 'True'}),
+            'chisq_dof': ('django.db.models.fields.IntegerField', [], {'null': 'True'}),
+            'coa_phase': ('django.db.models.fields.FloatField', [], {'null': 'True'}),
+            'cont_chisq': ('django.db.models.fields.FloatField', [], {'null': 'True'}),
+            'cont_chisq_dof': ('django.db.models.fields.IntegerField', [], {'null': 'True'}),
+            'eff_distance': ('django.db.models.fields.FloatField', [], {'null': 'True'}),
+            'end_time': ('django.db.models.fields.IntegerField', [], {'null': 'True'}),
+            'end_time_gmst': ('django.db.models.fields.FloatField', [], {'null': 'True'}),
+            'end_time_ns': ('django.db.models.fields.IntegerField', [], {'null': 'True'}),
+            'eta': ('django.db.models.fields.FloatField', [], {'null': 'True'}),
+            'event': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['gracedb.Event']"}),
+            'event_duration': ('django.db.models.fields.FloatField', [], {'null': 'True'}),
+            'f_final': ('django.db.models.fields.FloatField', [], {'null': 'True'}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'ifo': ('django.db.models.fields.CharField', [], {'max_length': '20', 'null': 'True'}),
+            'impulse_time': ('django.db.models.fields.IntegerField', [], {'null': 'True'}),
+            'impulse_time_ns': ('django.db.models.fields.IntegerField', [], {'null': 'True'}),
+            'kappa': ('django.db.models.fields.FloatField', [], {'null': 'True'}),
+            'mass1': ('django.db.models.fields.FloatField', [], {'null': 'True'}),
+            'mass2': ('django.db.models.fields.FloatField', [], {'null': 'True'}),
+            'mchirp': ('django.db.models.fields.FloatField', [], {'null': 'True'}),
+            'mtotal': ('django.db.models.fields.FloatField', [], {'null': 'True'}),
+            'psi0': ('django.db.models.fields.FloatField', [], {'null': 'True'}),
+            'psi3': ('django.db.models.fields.FloatField', [], {'null': 'True'}),
+            'rsqveto_duration': ('django.db.models.fields.FloatField', [], {'null': 'True'}),
+            'search': ('django.db.models.fields.CharField', [], {'max_length': '20', 'null': 'True'}),
+            'sigmasq': ('django.db.models.fields.FloatField', [], {'null': 'True'}),
+            'snr': ('django.db.models.fields.FloatField', [], {'null': 'True'}),
+            'spin1x': ('django.db.models.fields.FloatField', [], {'null': 'True'}),
+            'spin1y': ('django.db.models.fields.FloatField', [], {'null': 'True'}),
+            'spin1z': ('django.db.models.fields.FloatField', [], {'null': 'True'}),
+            'spin2x': ('django.db.models.fields.FloatField', [], {'null': 'True'}),
+            'spin2y': ('django.db.models.fields.FloatField', [], {'null': 'True'}),
+            'spin2z': ('django.db.models.fields.FloatField', [], {'null': 'True'}),
+            'tau0': ('django.db.models.fields.FloatField', [], {'null': 'True'}),
+            'tau2': ('django.db.models.fields.FloatField', [], {'null': 'True'}),
+            'tau3': ('django.db.models.fields.FloatField', [], {'null': 'True'}),
+            'tau4': ('django.db.models.fields.FloatField', [], {'null': 'True'}),
+            'tau5': ('django.db.models.fields.FloatField', [], {'null': 'True'}),
+            'template_duration': ('django.db.models.fields.FloatField', [], {'null': 'True'}),
+            'ttotal': ('django.db.models.fields.FloatField', [], {'null': 'True'})
+        },
+        u'gracedb.tag': {
+            'Meta': {'object_name': 'Tag'},
+            'displayName': ('django.db.models.fields.CharField', [], {'max_length': '200', 'null': 'True'}),
+            'eventlogs': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['gracedb.EventLog']", 'symmetrical': 'False'}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'name': ('django.db.models.fields.CharField', [], {'max_length': '100'})
+        },
+        u'gracedb.voevent': {
+            'Meta': {'ordering': "['-created', '-N']", 'unique_together': "(('event', 'N'),)", 'object_name': 'VOEvent'},
+            'N': ('django.db.models.fields.IntegerField', [], {}),
+            'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
+            'event': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['gracedb.Event']"}),
+            'file_version': ('django.db.models.fields.IntegerField', [], {'null': 'True'}),
+            'filename': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '100'}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'issuer': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['auth.User']"}),
+            'ivorn': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '200'}),
+            'voevent_type': ('django.db.models.fields.CharField', [], {'max_length': '2'})
+        }
+    }
+
+    complete_apps = ['gracedb']
\ No newline at end of file
diff --git a/gracedb/migrations/0040_migrate_eels_to_observations.py b/gracedb/migrations/0040_migrate_eels_to_observations.py
new file mode 100644
index 0000000000000000000000000000000000000000..52482ee39b96d010911ed9fbb7b105a5afd2e299
--- /dev/null
+++ b/gracedb/migrations/0040_migrate_eels_to_observations.py
@@ -0,0 +1,433 @@
+# -*- coding: utf-8 -*-
+from south.utils import datetime_utils as datetime
+from south.db import db
+from south.v2 import DataMigration
+from django.db import models
+import json
+from utils import gpsToUtc
+
+class Migration(DataMigration):
+
+    def forwards(self, orm):
+        "Write your forwards methods here."
+        # Note: Don't use "from appname.models import ModelName". 
+        # Use orm.ModelName to refer to models in this application,
+        # and orm['appname.ModelName'] for models in other applications.
+
+        # Loop through these by event so that the numbering will
+        # be correct
+
+        for event in orm.Event.objects.all():
+            for eel in event.embbeventlog_set.all():
+                # We only want to do this for things that really have a footprint.
+                if not eel.raList:
+                    continue
+                if eel.eel_status == 'FO':
+                    # Create the new observation object with common fields.
+                    obs = orm.EMObservation.objects.create(
+                        N = eel.N,
+                        created = eel.created,
+                        event = event,
+                        submitter = eel.submitter,
+                        group = eel.group)
+
+                    # Next, work on the footprints.
+                    # Much code here lifted from validateMakeRects
+                    # get all the list based position and times and their widths
+                    raRealList = []
+                    rawRealList = []
+                    # add a [ and ] to convert the input csv list to a json parsable text
+
+                    if eel.raList:        raRealList = json.loads('['+eel.raList+']')
+                    if eel.raWidthList:   rawRealList = json.loads('['+eel.raWidthList+']')
+
+                    if eel.decList:       decRealList = json.loads('['+eel.decList+']')
+                    if eel.decWidthList:  decwRealList = json.loads('['+eel.decWidthList+']')
+
+                    if eel.gpstimeList:   gpstimeRealList = json.loads('['+eel.gpstimeList+']')
+                    if eel.durationList:  durationRealList = json.loads('['+eel.durationList+']')
+
+                    error_msg = "Cannot use EEL %d for event %s: " % (eel.N, event.id)
+
+                    # is there anything in the ra list? 
+                    nList = len(raRealList)
+                    if nList > 0:
+                        if decRealList and len(decRealList) != nList:
+                            print error_msg + 'RA and Dec lists are different lengths.'
+                            continue
+                        if gpstimeRealList and len(gpstimeRealList) != nList:
+                            print error_msg + 'RA and GPS lists are different lengths.'
+                            continue
+
+                    # is there anything in the raWidth list? 
+                    mList = len(rawRealList)
+                    if mList > 0:
+                        if decwRealList and len(decwRealList) != mList:
+                            print error_msg + 'RAwidth and Decwidth lists are different lengths.'
+                            continue
+                        if durationRealList and len(durationRealList) != mList:
+                            print error_msg + 'RAwidth and Duration lists are different lengths.'
+                            continue
+
+                        # There can be 1 width for the whole list, or one for each ra/dec/gps 
+                        if mList != 1 and mList != nList:
+                            print error_msg + 'Width and duration lists must be length 1 or same length as coordinate lists'
+                            continue
+                    else:
+                        mList = 0
+
+                    for i in range(nList):
+                        try:
+                            ra = float(raRealList[i])
+                        except:
+                            print error_msg +'Cannot read RA list element %d of %s'%(i, eel.raList)
+                            continue
+                        try:
+                            dec = float(decRealList[i])
+                        except:
+                            print error_msg +'Cannot read Dec list element %d of %s'%(i, eel.decList)
+                            continue
+                        try:
+                            gps = int(gpstimeRealList[i])
+                        except:
+                            print error_msg +'Cannot read GPStime list element %d of %s'%(i, eel.gpstimeList)
+                            continue
+
+                        # the widths list can have 1 member to cover all, or one for each
+                        if mList==1: j=0
+                        else       : j=i
+
+                        try:
+                            raWidth = float(rawRealList[j])
+                        except:
+                            print error_msg +'Cannot read raWidth list element %d of %s'%(i, eel.raWidthList)
+                            continue
+
+                        try:
+                            decWidth = float(decwRealList[j])
+                        except:
+                            print error_msg +'Cannot read raWidth list element %d of %s'%(i, eel.decWidthList)
+                            continue
+
+                        try:
+                            duration = int(durationRealList[j])
+                        except:
+                            print error_msg +'Cannot read duration list element %d of %s'%(i, eel.durationList)
+                            continue
+
+                        # Now calculate the start time
+                        start_gps = gps - duration/2.0
+                        start_time = gpsToUtc(start_gps)
+
+                        # Create footprint object 
+                        orm.EMFootprint.objects.create(N=i, observation=obs, ra=ra, dec=dec, 
+                            raWidth=raWidth, decWidth=decWidth, start_time=start_time, 
+                            exposure_time=duration)
+
+                    # This has to do be done in a management command, since South cannot re-create these 
+                    # custom methods.
+                    # Calculate covering region for observation
+                    #obs.calculateCoveringRegion()
+                    # XXX Save method should not increment N
+                    #obs.save()
+
+    def backwards(self, orm):
+        "Write your backwards methods here."
+
+    models = {
+        u'auth.group': {
+            'Meta': {'object_name': 'Group'},
+            u'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': u"orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'})
+        },
+        u'auth.permission': {
+            'Meta': {'ordering': "(u'content_type__app_label', u'content_type__model', u'codename')", 'unique_together': "((u'content_type', u'codename'),)", 'object_name': 'Permission'},
+            'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+            'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['contenttypes.ContentType']"}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'name': ('django.db.models.fields.CharField', [], {'max_length': '50'})
+        },
+        u'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', [], {'symmetrical': 'False', 'related_name': "u'user_set'", 'blank': 'True', 'to': u"orm['auth.Group']"}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
+            'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            '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', [], {'symmetrical': 'False', 'related_name': "u'user_set'", 'blank': 'True', 'to': u"orm['auth.Permission']"}),
+            'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'})
+        },
+        u'contenttypes.contenttype': {
+            'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"},
+            'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+            u'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'})
+        },
+        u'gracedb.approval': {
+            'Meta': {'object_name': 'Approval'},
+            'approvedEvent': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['gracedb.Event']"}),
+            'approver': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['auth.User']"}),
+            'approvingCollaboration': ('django.db.models.fields.CharField', [], {'max_length': '1'}),
+            'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'})
+        },
+        u'gracedb.coincinspiralevent': {
+            'Meta': {'ordering': "['-id']", 'object_name': 'CoincInspiralEvent', '_ormbases': [u'gracedb.Event']},
+            'combined_far': ('django.db.models.fields.FloatField', [], {'null': 'True'}),
+            'end_time': ('django.db.models.fields.PositiveIntegerField', [], {'null': 'True'}),
+            'end_time_ns': ('django.db.models.fields.PositiveIntegerField', [], {'null': 'True'}),
+            u'event_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': u"orm['gracedb.Event']", 'unique': 'True', 'primary_key': 'True'}),
+            'false_alarm_rate': ('django.db.models.fields.FloatField', [], {'null': 'True'}),
+            'ifos': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '20'}),
+            'mass': ('django.db.models.fields.FloatField', [], {'null': 'True'}),
+            'mchirp': ('django.db.models.fields.FloatField', [], {'null': 'True'}),
+            'minimum_duration': ('django.db.models.fields.FloatField', [], {'null': 'True'}),
+            'snr': ('django.db.models.fields.FloatField', [], {'null': 'True'})
+        },
+        u'gracedb.embbeventlog': {
+            'Meta': {'ordering': "['-created', '-N']", 'unique_together': "(('event', 'N'),)", 'object_name': 'EMBBEventLog'},
+            'N': ('django.db.models.fields.IntegerField', [], {}),
+            'comment': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
+            'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
+            'dec': ('django.db.models.fields.FloatField', [], {'null': 'True'}),
+            'decList': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
+            'decWidth': ('django.db.models.fields.FloatField', [], {'null': 'True'}),
+            'decWidthList': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
+            'duration': ('django.db.models.fields.PositiveIntegerField', [], {'null': 'True'}),
+            'durationList': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
+            'eel_status': ('django.db.models.fields.CharField', [], {'max_length': '2'}),
+            'event': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['gracedb.Event']"}),
+            'extra_info_dict': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
+            'footprintID': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
+            'gpstime': ('django.db.models.fields.PositiveIntegerField', [], {'null': 'True'}),
+            'gpstimeList': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
+            'group': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['gracedb.EMGroup']"}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'instrument': ('django.db.models.fields.CharField', [], {'max_length': '200', 'blank': 'True'}),
+            'obs_status': ('django.db.models.fields.CharField', [], {'max_length': '2'}),
+            'ra': ('django.db.models.fields.FloatField', [], {'null': 'True'}),
+            'raList': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
+            'raWidth': ('django.db.models.fields.FloatField', [], {'null': 'True'}),
+            'raWidthList': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
+            'submitter': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['auth.User']"}),
+            'waveband': ('django.db.models.fields.CharField', [], {'max_length': '25'})
+        },
+        u'gracedb.emfootprint': {
+            'Meta': {'ordering': "['-N']", 'unique_together': "(('observation', 'N'),)", 'object_name': 'EMFootprint'},
+            'N': ('django.db.models.fields.IntegerField', [], {}),
+            'dec': ('django.db.models.fields.FloatField', [], {}),
+            'decWidth': ('django.db.models.fields.FloatField', [], {}),
+            'exposure_time': ('django.db.models.fields.PositiveIntegerField', [], {}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'observation': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['gracedb.EMObservation']"}),
+            'ra': ('django.db.models.fields.FloatField', [], {}),
+            'raWidth': ('django.db.models.fields.FloatField', [], {}),
+            'start_time': ('django.db.models.fields.DateTimeField', [], {})
+        },
+        u'gracedb.emgroup': {
+            'Meta': {'object_name': 'EMGroup'},
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '20'})
+        },
+        u'gracedb.emobservation': {
+            'Meta': {'ordering': "['-created', '-N']", 'unique_together': "(('event', 'N'),)", 'object_name': 'EMObservation'},
+            'N': ('django.db.models.fields.IntegerField', [], {}),
+            'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
+            'dec': ('django.db.models.fields.FloatField', [], {'null': 'True'}),
+            'decWidth': ('django.db.models.fields.FloatField', [], {'null': 'True'}),
+            'event': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['gracedb.Event']"}),
+            'group': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['gracedb.EMGroup']"}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'ra': ('django.db.models.fields.FloatField', [], {'null': 'True'}),
+            'raWidth': ('django.db.models.fields.FloatField', [], {'null': 'True'}),
+            'submitter': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['auth.User']"})
+        },
+        u'gracedb.event': {
+            'Meta': {'ordering': "['-id']", 'object_name': 'Event'},
+            'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
+            'far': ('django.db.models.fields.FloatField', [], {'null': 'True'}),
+            'gpstime': ('django.db.models.fields.DecimalField', [], {'null': 'True', 'max_digits': '16', 'decimal_places': '6'}),
+            'group': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['gracedb.Group']"}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'instruments': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '20'}),
+            'labels': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['gracedb.Label']", 'through': u"orm['gracedb.Labelling']", 'symmetrical': 'False'}),
+            'likelihood': ('django.db.models.fields.FloatField', [], {'null': 'True'}),
+            'nevents': ('django.db.models.fields.PositiveIntegerField', [], {'null': 'True'}),
+            'perms': ('django.db.models.fields.TextField', [], {'null': 'True'}),
+            'pipeline': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['gracedb.Pipeline']"}),
+            'search': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['gracedb.Search']", 'null': 'True'}),
+            'submitter': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['auth.User']"})
+        },
+        u'gracedb.eventlog': {
+            'Meta': {'ordering': "['-created', '-N']", 'unique_together': "(('event', 'N'),)", 'object_name': 'EventLog'},
+            'N': ('django.db.models.fields.IntegerField', [], {}),
+            'comment': ('django.db.models.fields.TextField', [], {}),
+            'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
+            'event': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['gracedb.Event']"}),
+            'file_version': ('django.db.models.fields.IntegerField', [], {'null': 'True'}),
+            'filename': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '100'}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'issuer': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['auth.User']"})
+        },
+        u'gracedb.grbevent': {
+            'Meta': {'ordering': "['-id']", 'object_name': 'GrbEvent', '_ormbases': [u'gracedb.Event']},
+            'author_ivorn': ('django.db.models.fields.CharField', [], {'max_length': '200', 'null': 'True'}),
+            'author_shortname': ('django.db.models.fields.CharField', [], {'max_length': '200', 'null': 'True'}),
+            'coord_system': ('django.db.models.fields.CharField', [], {'max_length': '200', 'null': 'True'}),
+            'dec': ('django.db.models.fields.FloatField', [], {'null': 'True'}),
+            'error_radius': ('django.db.models.fields.FloatField', [], {'null': 'True'}),
+            u'event_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': u"orm['gracedb.Event']", 'unique': 'True', 'primary_key': 'True'}),
+            'how_description': ('django.db.models.fields.CharField', [], {'max_length': '200', 'null': 'True'}),
+            'how_reference_url': ('django.db.models.fields.URLField', [], {'max_length': '200', 'null': 'True'}),
+            'ivorn': ('django.db.models.fields.CharField', [], {'max_length': '200', 'null': 'True'}),
+            'observatory_location_id': ('django.db.models.fields.CharField', [], {'max_length': '200', 'null': 'True'}),
+            'ra': ('django.db.models.fields.FloatField', [], {'null': 'True'})
+        },
+        u'gracedb.group': {
+            'Meta': {'object_name': 'Group'},
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'name': ('django.db.models.fields.CharField', [], {'max_length': '20'})
+        },
+        u'gracedb.label': {
+            'Meta': {'object_name': 'Label'},
+            'defaultColor': ('django.db.models.fields.CharField', [], {'default': "'black'", 'max_length': '20'}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '20'})
+        },
+        u'gracedb.labelling': {
+            'Meta': {'object_name': 'Labelling'},
+            'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
+            'creator': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['auth.User']"}),
+            'event': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['gracedb.Event']"}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'label': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['gracedb.Label']"})
+        },
+        u'gracedb.multiburstevent': {
+            'Meta': {'ordering': "['-id']", 'object_name': 'MultiBurstEvent', '_ormbases': [u'gracedb.Event']},
+            'amplitude': ('django.db.models.fields.FloatField', [], {'null': 'True'}),
+            'bandwidth': ('django.db.models.fields.FloatField', [], {'null': 'True'}),
+            'central_freq': ('django.db.models.fields.FloatField', [], {'null': 'True'}),
+            'confidence': ('django.db.models.fields.FloatField', [], {'null': 'True'}),
+            'duration': ('django.db.models.fields.FloatField', [], {'null': 'True'}),
+            u'event_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': u"orm['gracedb.Event']", 'unique': 'True', 'primary_key': 'True'}),
+            'false_alarm_rate': ('django.db.models.fields.FloatField', [], {'null': 'True'}),
+            'ifos': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '20'}),
+            'ligo_angle': ('django.db.models.fields.FloatField', [], {'null': 'True'}),
+            'ligo_angle_sig': ('django.db.models.fields.FloatField', [], {'null': 'True'}),
+            'ligo_axis_dec': ('django.db.models.fields.FloatField', [], {'null': 'True'}),
+            'ligo_axis_ra': ('django.db.models.fields.FloatField', [], {'null': 'True'}),
+            'peak_time': ('django.db.models.fields.PositiveIntegerField', [], {'null': 'True'}),
+            'peak_time_ns': ('django.db.models.fields.PositiveIntegerField', [], {'null': 'True'}),
+            'snr': ('django.db.models.fields.FloatField', [], {'null': 'True'}),
+            'start_time': ('django.db.models.fields.PositiveIntegerField', [], {'null': 'True'}),
+            'start_time_ns': ('django.db.models.fields.PositiveIntegerField', [], {'null': 'True'})
+        },
+        u'gracedb.pipeline': {
+            'Meta': {'object_name': 'Pipeline'},
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'name': ('django.db.models.fields.CharField', [], {'max_length': '100'})
+        },
+        u'gracedb.search': {
+            'Meta': {'object_name': 'Search'},
+            'description': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'name': ('django.db.models.fields.CharField', [], {'max_length': '100'})
+        },
+        u'gracedb.singleinspiral': {
+            'Gamma0': ('django.db.models.fields.FloatField', [], {'null': 'True'}),
+            'Gamma1': ('django.db.models.fields.FloatField', [], {'null': 'True'}),
+            'Gamma2': ('django.db.models.fields.FloatField', [], {'null': 'True'}),
+            'Gamma3': ('django.db.models.fields.FloatField', [], {'null': 'True'}),
+            'Gamma4': ('django.db.models.fields.FloatField', [], {'null': 'True'}),
+            'Gamma5': ('django.db.models.fields.FloatField', [], {'null': 'True'}),
+            'Gamma6': ('django.db.models.fields.FloatField', [], {'null': 'True'}),
+            'Gamma7': ('django.db.models.fields.FloatField', [], {'null': 'True'}),
+            'Gamma8': ('django.db.models.fields.FloatField', [], {'null': 'True'}),
+            'Gamma9': ('django.db.models.fields.FloatField', [], {'null': 'True'}),
+            'Meta': {'object_name': 'SingleInspiral'},
+            'alpha': ('django.db.models.fields.FloatField', [], {'null': 'True'}),
+            'alpha1': ('django.db.models.fields.FloatField', [], {'null': 'True'}),
+            'alpha2': ('django.db.models.fields.FloatField', [], {'null': 'True'}),
+            'alpha3': ('django.db.models.fields.FloatField', [], {'null': 'True'}),
+            'alpha4': ('django.db.models.fields.FloatField', [], {'null': 'True'}),
+            'alpha5': ('django.db.models.fields.FloatField', [], {'null': 'True'}),
+            'alpha6': ('django.db.models.fields.FloatField', [], {'null': 'True'}),
+            'amplitude': ('django.db.models.fields.FloatField', [], {'null': 'True'}),
+            'bank_chisq': ('django.db.models.fields.FloatField', [], {'null': 'True'}),
+            'bank_chisq_dof': ('django.db.models.fields.IntegerField', [], {'null': 'True'}),
+            'beta': ('django.db.models.fields.FloatField', [], {'null': 'True'}),
+            'channel': ('django.db.models.fields.CharField', [], {'max_length': '20'}),
+            'chi': ('django.db.models.fields.FloatField', [], {'null': 'True'}),
+            'chisq': ('django.db.models.fields.FloatField', [], {'null': 'True'}),
+            'chisq_dof': ('django.db.models.fields.IntegerField', [], {'null': 'True'}),
+            'coa_phase': ('django.db.models.fields.FloatField', [], {'null': 'True'}),
+            'cont_chisq': ('django.db.models.fields.FloatField', [], {'null': 'True'}),
+            'cont_chisq_dof': ('django.db.models.fields.IntegerField', [], {'null': 'True'}),
+            'eff_distance': ('django.db.models.fields.FloatField', [], {'null': 'True'}),
+            'end_time': ('django.db.models.fields.IntegerField', [], {'null': 'True'}),
+            'end_time_gmst': ('django.db.models.fields.FloatField', [], {'null': 'True'}),
+            'end_time_ns': ('django.db.models.fields.IntegerField', [], {'null': 'True'}),
+            'eta': ('django.db.models.fields.FloatField', [], {'null': 'True'}),
+            'event': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['gracedb.Event']"}),
+            'event_duration': ('django.db.models.fields.FloatField', [], {'null': 'True'}),
+            'f_final': ('django.db.models.fields.FloatField', [], {'null': 'True'}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'ifo': ('django.db.models.fields.CharField', [], {'max_length': '20', 'null': 'True'}),
+            'impulse_time': ('django.db.models.fields.IntegerField', [], {'null': 'True'}),
+            'impulse_time_ns': ('django.db.models.fields.IntegerField', [], {'null': 'True'}),
+            'kappa': ('django.db.models.fields.FloatField', [], {'null': 'True'}),
+            'mass1': ('django.db.models.fields.FloatField', [], {'null': 'True'}),
+            'mass2': ('django.db.models.fields.FloatField', [], {'null': 'True'}),
+            'mchirp': ('django.db.models.fields.FloatField', [], {'null': 'True'}),
+            'mtotal': ('django.db.models.fields.FloatField', [], {'null': 'True'}),
+            'psi0': ('django.db.models.fields.FloatField', [], {'null': 'True'}),
+            'psi3': ('django.db.models.fields.FloatField', [], {'null': 'True'}),
+            'rsqveto_duration': ('django.db.models.fields.FloatField', [], {'null': 'True'}),
+            'search': ('django.db.models.fields.CharField', [], {'max_length': '20', 'null': 'True'}),
+            'sigmasq': ('django.db.models.fields.FloatField', [], {'null': 'True'}),
+            'snr': ('django.db.models.fields.FloatField', [], {'null': 'True'}),
+            'spin1x': ('django.db.models.fields.FloatField', [], {'null': 'True'}),
+            'spin1y': ('django.db.models.fields.FloatField', [], {'null': 'True'}),
+            'spin1z': ('django.db.models.fields.FloatField', [], {'null': 'True'}),
+            'spin2x': ('django.db.models.fields.FloatField', [], {'null': 'True'}),
+            'spin2y': ('django.db.models.fields.FloatField', [], {'null': 'True'}),
+            'spin2z': ('django.db.models.fields.FloatField', [], {'null': 'True'}),
+            'tau0': ('django.db.models.fields.FloatField', [], {'null': 'True'}),
+            'tau2': ('django.db.models.fields.FloatField', [], {'null': 'True'}),
+            'tau3': ('django.db.models.fields.FloatField', [], {'null': 'True'}),
+            'tau4': ('django.db.models.fields.FloatField', [], {'null': 'True'}),
+            'tau5': ('django.db.models.fields.FloatField', [], {'null': 'True'}),
+            'template_duration': ('django.db.models.fields.FloatField', [], {'null': 'True'}),
+            'ttotal': ('django.db.models.fields.FloatField', [], {'null': 'True'})
+        },
+        u'gracedb.tag': {
+            'Meta': {'object_name': 'Tag'},
+            'displayName': ('django.db.models.fields.CharField', [], {'max_length': '200', 'null': 'True'}),
+            'eventlogs': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['gracedb.EventLog']", 'symmetrical': 'False'}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'name': ('django.db.models.fields.CharField', [], {'max_length': '100'})
+        },
+        u'gracedb.voevent': {
+            'Meta': {'ordering': "['-created', '-N']", 'unique_together': "(('event', 'N'),)", 'object_name': 'VOEvent'},
+            'N': ('django.db.models.fields.IntegerField', [], {}),
+            'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
+            'event': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['gracedb.Event']"}),
+            'file_version': ('django.db.models.fields.IntegerField', [], {'null': 'True'}),
+            'filename': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '100'}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'issuer': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['auth.User']"}),
+            'ivorn': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '200'}),
+            'voevent_type': ('django.db.models.fields.CharField', [], {'max_length': '2'})
+        }
+    }
+
+    complete_apps = ['gracedb']
+    symmetrical = True
diff --git a/gracedb/models.py b/gracedb/models.py
index 210caea14783dfe60c9013bcff999d893470d506..2d7daa84c90d6cf3925a21948d5c317c334919a6 100644
--- a/gracedb/models.py
+++ b/gracedb/models.py
@@ -396,6 +396,153 @@ EMSPECTRUM = (
 ('em.radio.20-100MHz',  'Radio between 20 and 100 MHz'),
 )
 
+class EMObservation(models.Model):
+    """
+    EMObservation:  An observation record for EM followup.  
+    """
+    class Meta:
+        ordering = ['-created', '-N']
+        unique_together = ("event","N")
+
+    def __unicode__(self):
+        return "%s-%s-%d" % (self.event.graceid(), self.group.name, self.N)
+
+    N = models.IntegerField(null=False)
+    created = models.DateTimeField(auto_now_add=True)
+    event = models.ForeignKey(Event)
+    submitter  = models.ForeignKey(DjangoUser)  
+
+    # The MOU group responsible 
+    group = models.ForeignKey(EMGroup)       # from a table of facilities
+
+    # The following fields should be calculated from the footprint info provided
+    # by the user. These fields are just for convenience and fast searching
+
+    # The center of the bounding box of the rectangular footprints ra,dec
+    # in J2000 in decimal degrees
+    ra         = models.FloatField(null=True)
+    dec        = models.FloatField(null=True)    
+
+    # The width and height (RA range and Dec range) in decimal degrees 
+    raWidth    = models.FloatField(null=True)
+    decWidth   = models.FloatField(null=True)    
+
+    # We overload the 'save' method to avoid race conditions, since the Eels are numbered. 
+    def save(self, *args, **kwargs):
+        success = False
+        attempts = 0
+        while (not success and attempts < 5):
+            attempts = attempts + 1
+            # If I've already got an N assigned, let's not assign another.
+            if not self.N:
+                if self.event.emobservation_set.count():
+                    self.N = int(self.event.emobservation_set.aggregate(models.Max('N'))['N__max']) + 1
+                else:
+                    self.N = 1
+            try:
+                super(EMObservation, self).save(*args, **kwargs)
+                success = True
+            except IntegrityError:
+                # IntegrityError means an attempt to insert a duplicate
+                # key or to violate a foreignkey constraint.
+                # We are under race conditions.  Let's try again.
+                pass
+
+        if not success:
+            # XXX Should this be a custom exception?  That way we could catch it
+            # in the views that use it and give an informative error message.
+            raise Exception("Too many attempts to save EMObservation entry. Something is wrong.")
+
+    def calculateCoveringRegion(self):
+        # How to access the related footprint objects?
+        footprints = self.emfootprint_set.all()
+        if not footprints:
+            return
+
+        ramin = 360.0
+        ramax = 0.0
+        decmin = 90.0
+        decmax = -90.0
+
+        for f in footprints:
+            
+            # evaluate bounding box
+            w = float(f.raWidth)/2
+            if f.ra-w < ramin: ramin = f.ra-w
+            if f.ra+w > ramax: ramax = f.ra+w
+
+            w = float(f.decWidth)/2
+            if f.dec-w < decmin: decmin = f.dec-w
+            if f.dec+w > decmax: decmax = f.dec+w
+
+        # Make sure the min/max ra and dec are within bounds:
+        ramin  = max(0.0,   ramin)
+        ramax  = min(360.0, ramax)
+        decmin = max(-90.0, decmin)
+        decmax = min(90.0,  decmax)
+
+        # Calculate sky rectangle bounds
+        self.ra       = (ramin + ramax)/2
+        self.dec      = (decmin + decmax)/2
+        self.raWidth  = ramax-ramin
+        self.decWidth = decmax-decmin
+
+class EMFootprint(models.Model):
+    """
+    A single footprint associated with an observation.
+    Each EMObservation can have many footprints underneath.
+
+    None of the fields are optional here.
+    """
+    class Meta:
+        ordering = ['-N']
+        unique_together = ("observation","N")
+
+    N = models.IntegerField(null=False)
+
+    observation = models.ForeignKey(EMObservation, null = False)
+
+    # The center of the rectangular footprint, right ascension and declination
+    # in J2000 in decimal degrees
+    ra         = models.FloatField()
+    dec        = models.FloatField()    
+
+    # The width and height (RA range and Dec range) in decimal degrees 
+    raWidth    = models.FloatField()
+    decWidth   = models.FloatField()    
+
+    # The start time of the observation for this footprint
+    start_time = models.DateTimeField()
+
+    # The exposure time in seconds for this footprint
+    exposure_time = models.PositiveIntegerField()
+
+    # We overload the 'save' method to avoid race conditions, since the Eels are numbered. 
+    def save(self, *args, **kwargs):
+        success = False
+        attempts = 0
+        while (not success and attempts < 5):
+            attempts = attempts + 1
+            # If I've already got an N assigned, let's not assign another.
+            if not self.N:
+                if self.observation.emfootprint_set.count():
+                    self.N = int(self.observation.emfootprint_set.aggregate(models.Max('N'))['N__max']) + 1
+                else:
+                    self.N = 1
+            try:
+                super(EMFootprint, self).save(*args, **kwargs)
+                success = True
+            except IntegrityError:
+                # IntegrityError means an attempt to insert a duplicate
+                # key or to violate a foreignkey constraint.
+                # We are under race conditions.  Let's try again.
+                pass
+
+        if not success:
+            # XXX Should this be a custom exception?  That way we could catch it
+            # in the views that use it and give an informative error message.
+            raise Exception("Too many attempts to save Footprint. Something is wrong.")
+
 class EMBBEventLog(models.Model):
     """EMBB EventLog:  A multi-purpose annotation for EM followup.
      
diff --git a/gracedb/urls_rest.py b/gracedb/urls_rest.py
index 1f12d8f5da1e6b2b3624cd9bde148caee45cff0c..2ed93e93be997be2dfb249be918f55400aefedbf 100644
--- a/gracedb/urls_rest.py
+++ b/gracedb/urls_rest.py
@@ -8,6 +8,8 @@ from gracedb.api import GracedbRoot
 from gracedb.api import EventList, EventDetail
 from gracedb.api import EventLogList, EventLogDetail
 from gracedb.api import EMBBEventLogList, EMBBEventLogDetail
+from gracedb.api import EMObservationList, EMObservationDetail
+#from gracedb.api import EMFootprintList, EMFootprintDetail
 from gracedb.api import TagList
 # from gracedb.api import TagDetail
 from gracedb.api import EventTagList, EventTagDetail
@@ -46,12 +48,20 @@ urlpatterns = patterns('gracedb.api',
     url (r'events/(?P<graceid>[GEHMT]\d+)/voevent/(?P<n>\d+)$',
         VOEventDetail.as_view(), name='voevent-detail'),
 
-    # EMBB Event Log Resources
+    # EMBB Resources
     # events/{graceid}/logs/[{logid}]
     url (r'events/(?P<graceid>[GEHMT]\d+)/embb/$',
         EMBBEventLogList.as_view(), name='embbeventlog-list'),
     url (r'events/(?P<graceid>[GEHMT]\d+)/embb/(?P<n>\d+)$',
         EMBBEventLogDetail.as_view(), name='embbeventlog-detail'),
+    url (r'events/(?P<graceid>[GEHMT]\d+)/emobservation/$',
+        EMObservationList.as_view(), name='emobservation-list'),
+    url (r'events/(?P<graceid>[GEHMT]\d+)/emobservation/(?P<n>\d+)$',
+        EMObservationDetail.as_view(), name='emobservation-detail'),
+#    url (r'events/(?P<graceid>[GEHMT]\d+)/emobservation/(?P<n>\d+)/emfootprint/$',
+#        EMFootprintList.as_view(), name='emfootprint-list'),
+#    url (r'events/(?P<graceid>[GEHMT]\d+)/emobservation/(?P<n>\d+)/emfootprint/(?P<m>\d+)$',
+#        EMFootprintDetail.as_view(), name='emfootprint-detail'),
 
     # Tag Resources
     url (r'^tag/$', 
diff --git a/gracedb/view_logic.py b/gracedb/view_logic.py
index dddf6f8d0144fa8ba9f3020ce4bd65a19b97adf6..2868f54ab88a1c85ab9ab024dd93f8c33c623fca 100644
--- a/gracedb/view_logic.py
+++ b/gracedb/view_logic.py
@@ -7,6 +7,7 @@ from models import CoincInspiralEvent
 from models import MultiBurstEvent
 from models import GrbEvent
 from models import EMBBEventLog, EMGroup
+from models import EMObservation, EMFootprint
 from alert import issueAlert, issueAlertForLabel, issueAlertForUpdate
 from translator import handle_uploaded_data
 
@@ -25,6 +26,7 @@ from django.conf import settings
 
 import json
 import datetime
+import dateutil
 
 def _createEventFromForm(request, form):
     saved = False
@@ -365,3 +367,117 @@ def create_eel(d, event, user):
     eel.validateMakeRects()
     eel.save()
     return eel
+
+#
+# Create an EMBB Observaton Record
+#
+def create_emobservation(d, event, user):    
+    # create a log entry
+    emo = EMObservation(event=event)
+    emo.event = event
+    emo.submitter = user
+    # Assign a group name
+    try:
+        emo.group = EMGroup.objects.get(name=d.get('group'))
+    except:
+        raise ValueError('Please specify an EM followup MOU group')
+
+    # Must do this so as to have an id.
+    emo.save()
+
+    # Assign RA and Dec, plus widths
+    raList = d.get('raList', '')
+    raWidthList = d.get('raWidthList', '')
+
+    decList = d.get('decList', '')
+    decWidthList = d.get('decWidthList', '')
+
+    startTimeList = d.get('startTimeList', '')
+    durationList = d.get('durationList', '')
+
+    # Much code here lifted from EMBBEventLog.validateMakeRects
+    # get all the list based position and times and their widths
+    raRealList = []
+    rawRealList = []
+    # add a [ and ] to convert the input csv list to a json parsable text
+
+    if raList:        raRealList = json.loads('['+raList+']')
+    if raWidthList:   rawRealList = json.loads('['+raWidthList+']')
+
+    if decList:       decRealList = json.loads('['+decList+']')
+    if decWidthList:  decwRealList = json.loads('['+decWidthList+']')
+
+    # this will actually be a list of ISO times in double quotes
+    if startTimeList:   startTimeRealList = json.loads('['+startTimeList+']')
+    if durationList:  durationRealList = json.loads('['+durationList+']')
+
+    # is there anything in the ra list? 
+    nList = len(raRealList)
+    if nList > 0:
+        if decRealList and len(decRealList) != nList:
+            raise ValueError('RA and Dec lists are different lengths.')
+        if startTimeRealList and len(startTimeRealList) != nList:
+            raise ValueError('RA and start time lists are different lengths.')
+
+    # is there anything in the raWidth list? 
+    mList = len(rawRealList)
+    if mList > 0:
+        if decwRealList and len(decwRealList) != mList:
+            raise ValueError('RAwidth and Decwidth lists are different lengths.')
+        if durationRealList and len(durationRealList) != mList:
+            raise ValueError('RAwidth and Duration lists are different lengths.')
+
+        # There can be 1 width for the whole list, or one for each ra/dec/gps 
+        if mList != 1 and mList != nList:
+            raise ValueError('Width and duration lists must be length 1 or same length as coordinate lists')
+    else:
+        mList = 0
+
+    for i in range(nList):
+        try:
+            ra = float(raRealList[i])
+        except:
+            raise ValueError('Cannot read RA list element %d of %s'%(i, emo.raList))
+        try:
+            dec = float(decRealList[i])
+        except:
+            raise ValueError('Cannot read Dec list element %d of %s'%(i, emo.decList))
+        try:
+            start_time = startTimeRealList[i]
+        except:
+            raise ValueError('Cannot read GPStime list element %d of %s'%(i, emo.startTimeList))
+
+        # the widths list can have 1 member to cover all, or one for each
+        if mList==1: j=0
+        else       : j=i
+
+        try:
+            raWidth = float(rawRealList[j])
+        except:
+            raise ValueError('Cannot read raWidth list element %d of %s'%(i, emo.raWidthList))
+
+        try:
+            decWidth = float(decwRealList[j])
+        except:
+            raise ValueError('Cannot read raWidth list element %d of %s'%(i, emo.decWidthList))
+
+        try:
+            duration = int(durationRealList[j])
+        except:
+            raise ValueError('Cannot read duration list element %d of %s'%(i, emo.durationList))
+
+        try:
+            start_time = dateutil.parser.parse(start_time)
+        except:
+            raise ValueError('Could not parse start time list element %d of %s'%(i, emo.durationList))
+
+
+        # Create footprint object 
+        EMFootprint.objects.create(observation=emo, ra=ra, dec=dec, 
+            raWidth=raWidth, decWidth=decWidth, start_time=start_time, 
+            exposure_time=duration)
+
+    # Calculate covering region for observation
+    emo.calculateCoveringRegion()
+    emo.save()
+    return emo
diff --git a/gracedb/view_utils.py b/gracedb/view_utils.py
index 457ab42e7c31453738e326ebcd39e7ccdf1dd26b..0ece02cff172865575d56d9048194dacaff48d87 100644
--- a/gracedb/view_utils.py
+++ b/gracedb/view_utils.py
@@ -283,6 +283,50 @@ def embbEventLogToDict(eel, request=None):
                   "comment" : eel.comment,
                   "extra_info_dict" : eel.extra_info_dict,
              }
+
+# EMObservation serializer.
+def emObservationToDict(emo, request=None):
+      uri = None
+      if request:
+          uri = reverse("emobservation-detail",
+                  args=[emo.event.graceid(), emo.N],
+                  request=request)
+      
+      return {
+                  "N"               : emo.N,
+                  "footprint_count" : emo.emfootprint_set.count(),
+                  "self"            : uri,
+                  "created"         : emo.created.isoformat(),
+                  "submitter"       : emo.submitter.username,
+                  "group"           : emo.group.name,
+  
+                  "ra"       : emo.ra,
+                  "dec"      : emo.dec,
+                  "raWidth"  : emo.raWidth,
+                  "decWidth" : emo.decWidth,
+                  "footprints" : [ emFootprintToDict(emf) for emf in emo.emfootprint_set.all()]
+              }
+
+# EMFootprint serializer
+def emFootprintToDict(emf, request=None):
+#      uri = None
+#      if request:
+#          uri = reverse("emfootprint-detail",
+#                  args=[emf.emobservation.event.graceid(), emf.emobservation.N, emf.N],
+#                  request=request)
+      
+      return {
+# FIXME: At this point, we're not exposing the individual footprints.
+# It would probably be nice to expose these resources, at least to GET.
+#                  "self"            : uri,
+                  "N"               : emf.N,
+                  "ra"              : emf.ra,
+                  "dec"             : emf.dec,
+                  "raWidth"         : emf.raWidth,
+                  "decWidth"        : emf.decWidth,
+                  "start_time"      : emf.start_time.isoformat(),
+                  "exposure_time"   : emf.exposure_time,
+              }
   
 # VOEvent serializer
 def voeventToDict(voevent, request=None):
diff --git a/static/css/style.css b/static/css/style.css
index c3994835bab3346defc09a90ae44374b46876ec9..1ae24e1346bd4f616ea9f0d0900eb15c3a322bf9 100644
--- a/static/css/style.css
+++ b/static/css/style.css
@@ -423,6 +423,14 @@ div.dgrid-row.collapsed:hover {
     width: 10%;
 }
 
+.dgrid-cell.field-footprint_count {
+    width: 8em;
+}
+
+.dgrid-cell.field-group {
+    width: 10em;
+}
+
 th.dgrid-cell {
     vertical-align: bottom;
 }
diff --git a/templates/gracedb/event_detail_script.js b/templates/gracedb/event_detail_script.js
index 29fc76661aed84fcd105a877db663e32f34cf570..c502e34726ee9132308d885c8d913bd8e8fcc6bd 100644
--- a/templates/gracedb/event_detail_script.js
+++ b/templates/gracedb/event_detail_script.js
@@ -90,7 +90,55 @@ var tooltip=function(){
 }();
 
 
+// This should probably also go somewhere else.
+// Closure
+(function() {
+  /**
+   * Decimal adjustment of a number.
+   *
+   * @param {String}  type  The type of adjustment.
+   * @param {Number}  value The number.
+   * @param {Integer} exp   The exponent (the 10 logarithm of the adjustment base).
+   * @returns {Number} The adjusted value.
+   */
+  function decimalAdjust(type, value, exp) {
+    // If the exp is undefined or zero...
+    if (typeof exp === 'undefined' || +exp === 0) {
+      return Math[type](value);
+    }
+    value = +value;
+    exp = +exp;
+    // If the value is not a number or the exp is not an integer...
+    if (isNaN(value) || !(typeof exp === 'number' && exp % 1 === 0)) {
+      return NaN;
+    }
+    // Shift
+    value = value.toString().split('e');
+    value = Math[type](+(value[0] + 'e' + (value[1] ? (+value[1] - exp) : -exp)));
+    // Shift back
+    value = value.toString().split('e');
+    return +(value[0] + 'e' + (value[1] ? (+value[1] + exp) : exp));
+  }
 
+  // Decimal round
+  if (!Math.round10) {
+    Math.round10 = function(value, exp) {
+      return decimalAdjust('round', value, exp);
+    };
+  }
+  // Decimal floor
+  if (!Math.floor10) {
+    Math.floor10 = function(value, exp) {
+      return decimalAdjust('floor', value, exp);
+    };
+  }
+  // Decimal ceil
+  if (!Math.ceil10) {
+    Math.ceil10 = function(value, exp) {
+      return decimalAdjust('ceil', value, exp);
+    };
+  }
+})();
 
 
 // A utility
@@ -119,9 +167,7 @@ var tagListUrl          = '{% url "api:tag-list" %}';
 var tagUrlPattern       = '{% url "taglogentry" object.graceid "000" "temp" %}';
 var eventLogListUrl     = '{% url "api:eventlog-list" object.graceid %}';
 var eventLogSaveUrl     = '{% url "logentry" object.graceid "" %}';
-var embbEventLogListUrl = '{% url "api:embbeventlog-list" object.graceid %}';
-// XXX Branson made this change on 3/3/15
-//var skymapJsonUrl       = '{% url "file" object.graceid "skymap.json" %}';
+var emObservationListUrl = '{% url "api:emobservation-list" object.graceid %}';
 var skymapJsonUrl       = '{% url "file" object.graceid "" %}';
 var skymapViewerUrl     = '{{ SKYMAP_VIEWER_SERVICE_URL }}';
 
@@ -195,7 +241,7 @@ require([
         if (!(initiallyOpen && initiallyOpen==true)) { 
             put(expandGlyphNode, '.closed');
             domStyle.set(contentNode, 'display', 'none');
-            domStyle.set(addButtonNode, 'display', 'none');
+            domStyle.set(addButtonNode, 'display', 'none'); 
         }
         // This one is always closed initially
         domStyle.set(formNode, 'display', 'none');
@@ -203,7 +249,7 @@ require([
         on(expandGlyphNode, "click", function() {
             if (domStyle.get(contentNode, 'display') == 'none') {
                 domStyle.set(contentNode, 'display', 'block');
-                domStyle.set(addButtonNode, 'display', 'block');
+                domStyle.set(addButtonNode, 'display', 'block'); 
                 put(expandGlyphNode, '!closed');
             } else {
                 domStyle.set(contentNode, 'display', 'none');
@@ -215,7 +261,7 @@ require([
         on(titleTextNode, "click", function() {
             if (domStyle.get(contentNode, 'display') == 'none') {
                 domStyle.set(contentNode, 'display', 'block');
-                domStyle.set(addButtonNode, 'display', 'block');
+                domStyle.set(addButtonNode, 'display', 'block'); 
                 put(expandGlyphNode, '!closed');
             } else {
                 domStyle.set(contentNode, 'display', 'none');
@@ -235,6 +281,38 @@ require([
         });
     }
 
+    var createExpandingSectionNoForm = function (titleNode, contentNode, titleText, initiallyOpen) {
+        // Instead let's make a table. 
+        var titleTableRow = put(titleNode, "table tr");
+        var expandGlyphNode = put(titleTableRow, "td.title div.expandGlyph"); 
+        var titleTextNode = put(titleTableRow, "td.title h2", titleText);
+
+        if (!(initiallyOpen && initiallyOpen==true)) { 
+            put(expandGlyphNode, '.closed');
+            domStyle.set(contentNode, 'display', 'none');
+        }
+       
+        on(expandGlyphNode, "click", function() {
+            if (domStyle.get(contentNode, 'display') == 'none') {
+                domStyle.set(contentNode, 'display', 'block');
+                put(expandGlyphNode, '!closed');
+            } else {
+                domStyle.set(contentNode, 'display', 'none');
+                put(expandGlyphNode, '.closed');
+            }
+        });
+        
+        on(titleTextNode, "click", function() {
+            if (domStyle.get(contentNode, 'display') == 'none') {
+                domStyle.set(contentNode, 'display', 'block');
+                put(expandGlyphNode, '!closed');
+            } else {
+                domStyle.set(contentNode, 'display', 'none');
+                put(expandGlyphNode, '.closed');
+            }
+        });
+    }
+
     var timeChoicesData = [ 
         {"id": "llo",   "label": "LLO Local"},
         {"id": "lho",   "label": "LHO Local"},
@@ -280,51 +358,55 @@ require([
     var embbContentDiv = put(embbDiv, 'div#embb_content'); 
 
     // Put the EEL form into the content div
-    var oldEelFormDiv = dom.byId('eelFormContainer');
-    var eelFormContents = oldEelFormDiv.innerHTML;
-    domConstruct.destroy('eelFormContainer');
-    var embbAddDiv = put(embbContentDiv, 'div#add_eel_container');
-    var embbAddFormDiv = put(embbAddDiv, 'div#add_eel_form_container');
-    embbAddFormDiv.innerHTML = eelFormContents;
+    // FIXME This needs to be cleaned up. Empty div for now.
+    //var oldEelFormDiv = dom.byId('eelFormContainer');
+    //var eelFormContents = oldEelFormDiv.innerHTML;
+    domConstruct.destroy('eelFormContainer'); 
+    // var embbAddDiv = put(embbContentDiv, 'div#add_eel_container');
+    /* var embbAddFormDiv = put(embbAddDiv, 'div#add_eel_form_container');
+    embbAddFormDiv.innerHTML = eelFormContents; */
 
-    createExpandingSection(embbTitleDiv, embbContentDiv, embbAddDiv, 'Electromagnetic Bulletin Board');
+    createExpandingSectionNoForm(embbTitleDiv, embbContentDiv, 'Electromagnetic Bulletin Board');
 
     // Append the div that will hold our dgrid
-    put(embbContentDiv, 'div#eel-grid');
+    put(embbContentDiv, 'div#emo-grid');
         
-    embbStore = new declare([Rest, RequestMemory])({target: embbEventLogListUrl});
-    embbStore.get('').then(function(content) {
+    emoStore = new declare([Rest, RequestMemory])({target: emObservationListUrl});
+    emoStore.get('').then(function(content) {
         // Pull the EELs out of the rest content and create a new simple store from them.
-        var eels = content.embblog;
+        var emos = content.observations;
 
-        if (eels.length == 0) {
-            eelDiv = dom.byId('eel-grid');
-            eelDiv.innerHTML = '<p> (No EMBB log entries.) </p>';
+        if (emos.length == 0) {
+            emoDiv = dom.byId('emo-grid');
+            emoDiv.innerHTML = '<p> (No EM observation entries.) </p>';
         } else {
 
             var columns  = [
                 { field: 'created', label: 'Time Created (UTC)' },
                 { field: 'submitter', label: 'Submitter' },
                 { field: 'group', label: 'MOU Group' },
-                { field: 'gpstime', label: 'GPS time of observation' },
-                { field: 'duration', label: 'Exposure time (s)' },
+                { field: 'footprint_count', label: 'N_regions' },
                 { field: 'radec',
                   label: 'Covering (ra, dec)',
                     get: function(object){
-                        var rastring = object.ra + " \xB1 " + object.raWidth/2.0;
-                        var decstring = object.dec + " \xB1 " + object.decWidth/2.0;
+                        var raLoc = Math.round10(object.ra, -2);
+                        var raHalfWidthLoc = Math.round10(object.raWidth/2.0, -2);
+                        var decLoc = Math.round10(object.dec, -2);
+                        var decHalfWidthLoc = Math.round10(object.decWidth/2.0, -2);
+                        var rastring = raLoc + " \xB1 " + raHalfWidthLoc;
+                        var decstring = decLoc + " \xB1 " + decHalfWidthLoc;
                         return "(" + rastring + ','  + decstring + ")";
                     },
                 }
             ]; 
 
             var subRowColumns  = [ 
-                    { field: 'instrument', label: 'Instrument' },
-                    { field: 'eel_status', label: 'Entry type' },
-                    { field: 'footprintID', label: 'Observation ID' },
-                    { field: 'waveband', label: 'Waveband' },
-                    { field: 'obs_status', label: 'Observation status' },
-                    { field: 'extra_info_dict', label: 'JSON info' },
+                { field: 'start_time', label: 'Start Time (UTC)' },
+                { field: 'exposure_time', label: 'Exposure Time (s)' },
+                { field: 'ra', label: 'ra'},
+                { field: 'raWidth', label: 'ra width'},
+                { field: 'dec', label: 'dec'},
+                { field: 'decWidth', label: 'dec width'}
             ]; 
 
             // Add extra class names to our grid cells so we can style them separately
@@ -352,14 +434,14 @@ require([
                         columns: subRowColumns,
                         className: 'dgird-subgrid',
                     }, subGridNode);
-                    sg.renderArray([object]);
+                    sg.renderArray(object.footprints);
                     // Add the text comment
-                    put(t, 'tr td[style="width: 5%"]+td div.subrid-text', object.comment); 
+                    //put(t, 'tr td[style="width: 5%"]+td div.subrid-text', object.comment); 
 
                     return div;
                 }
-            }, 'eel-grid'); 
-            grid.renderArray(eels);
+            }, 'emo-grid'); 
+            grid.renderArray(emos);
             grid.set("sort", 'N', descending=true);
 
             var expandedNode = null;
@@ -378,7 +460,7 @@ require([
                 // if the row clicked was previously expanded, nothing is expanded now
                 expandedNode = collapsed ? node : null;
             });
-        } // endif on whether we have any eels or not.
+        } // endif on whether we have any emos or not.
     });