From 647c96dcd37a2263dc68cdc9bc3539dd9d93aa3d Mon Sep 17 00:00:00 2001
From: Branson Stephens <branson.stephens@ligo.org>
Date: Sun, 10 May 2015 10:45:01 -0500
Subject: [PATCH] Added comment field to EMObservation. Added comment to view
 logic and form. Added the comment to the EMO display. Moved the EMO display
 into the log digests section.

---
 ...1_auto__add_field_emobservation_comment.py | 318 ++++++++++++++++++
 gracedb/models.py                             |   2 +
 gracedb/view_logic.py                         |   2 +
 gracedb/view_utils.py                         |   3 +-
 static/css/style.css                          |   4 +
 templates/gracedb/emo_form_frag.html          |   3 +
 templates/gracedb/event_detail.html           |   2 +-
 templates/gracedb/event_detail_script.js      | 269 +++++++++------
 8 files changed, 495 insertions(+), 108 deletions(-)
 create mode 100644 gracedb/migrations/0041_auto__add_field_emobservation_comment.py

diff --git a/gracedb/migrations/0041_auto__add_field_emobservation_comment.py b/gracedb/migrations/0041_auto__add_field_emobservation_comment.py
new file mode 100644
index 000000000..f4b565900
--- /dev/null
+++ b/gracedb/migrations/0041_auto__add_field_emobservation_comment.py
@@ -0,0 +1,318 @@
+# -*- 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 field 'EMObservation.comment'
+        db.add_column(u'gracedb_emobservation', 'comment',
+                      self.gf('django.db.models.fields.TextField')(default='', blank=True),
+                      keep_default=False)
+
+
+    def backwards(self, orm):
+        # Deleting field 'EMObservation.comment'
+        db.delete_column(u'gracedb_emobservation', 'comment')
+
+
+    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', [], {}),
+            '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'}),
+            '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/models.py b/gracedb/models.py
index 2d7daa84c..5987b11fb 100644
--- a/gracedb/models.py
+++ b/gracedb/models.py
@@ -427,6 +427,8 @@ class EMObservation(models.Model):
     raWidth    = models.FloatField(null=True)
     decWidth   = models.FloatField(null=True)    
 
+    comment = models.TextField(blank=True)
+
     # We overload the 'save' method to avoid race conditions, since the Eels are numbered. 
     def save(self, *args, **kwargs):
         success = False
diff --git a/gracedb/view_logic.py b/gracedb/view_logic.py
index ee96a5515..4583fab11 100644
--- a/gracedb/view_logic.py
+++ b/gracedb/view_logic.py
@@ -382,6 +382,8 @@ def create_emobservation(d, event, user):
     except:
         raise ValueError('Please specify an EM followup MOU group')
 
+    emo.comment = d.get('comment', '')
+
     # Assign RA and Dec, plus widths
     try:
         raList = d.get('raList')
diff --git a/gracedb/view_utils.py b/gracedb/view_utils.py
index 463238380..ccb832b22 100644
--- a/gracedb/view_utils.py
+++ b/gracedb/view_utils.py
@@ -306,6 +306,7 @@ def emObservationToDict(emo, request=None):
                   "created"         : emo.created.isoformat(),
                   "submitter"       : emo.submitter.username,
                   "group"           : emo.group.name,
+                  "comment"         : emo.comment,                  
   
                   "ra"       : emo.ra,
                   "dec"      : emo.dec,
@@ -382,7 +383,7 @@ def skymapViewerEMObservationToDict(emo, request=None):
                 "self"            : uri,
                 "created"         : emo.created.isoformat(),
                 "submitter"       : emo.submitter.username,
-                "comment"         : '',
+                "comment"         : emo.comment,
                 "footprintID"     : avg_time_string,
                 "group"           : emo.group.name,
   
diff --git a/static/css/style.css b/static/css/style.css
index 1ae24e134..04a418022 100644
--- a/static/css/style.css
+++ b/static/css/style.css
@@ -441,6 +441,10 @@ td.title {
     cursor: pointer;
 }
 
+div.expandFormButton {
+    cursor: pointer;
+}
+
 div.expandGlyph {
     background-image: url('/bower-static/dijit/themes/tundra/images/spriteArrows.png');
     background-repeat: no-repeat;
diff --git a/templates/gracedb/emo_form_frag.html b/templates/gracedb/emo_form_frag.html
index 2908114cc..0c71c65f8 100644
--- a/templates/gracedb/emo_form_frag.html
+++ b/templates/gracedb/emo_form_frag.html
@@ -48,6 +48,9 @@ StartTime</a></td>
 <tr><td><a href=# onclick="alert('Duration is the number of seconds in the time interval during which the observation was taken.');return false;">
 Duration (seconds)</a></td>       <td><input type="text" name="durationList" value=""/></td></tr>
 
+<tr><td><a href=# onclick="alert('A natural language report.');return false;">
+Report as text</a></td>         <td colspan=2><textarea name="comment" rows="8" cols="50"></textarea></td></tr>
+
 </table>
 <input type="submit" value="Submit EMBB Observation Report"/>
 </form>
diff --git a/templates/gracedb/event_detail.html b/templates/gracedb/event_detail.html
index 8adb07f5c..5727645c1 100644
--- a/templates/gracedb/event_detail.html
+++ b/templates/gracedb/event_detail.html
@@ -145,7 +145,7 @@
 
 {% include "gracedb/neighbors_frag.html" %}
 
-{% include "gracedb/eel_form_frag.html" %}
+{# include "gracedb/eel_form_frag.html" #}
 {% include "gracedb/emo_form_frag.html" %}
 
 </div> <!-- end event_detail_content div -->
diff --git a/templates/gracedb/event_detail_script.js b/templates/gracedb/event_detail_script.js
index 60ff77417..00e9d27f2 100644
--- a/templates/gracedb/event_detail_script.js
+++ b/templates/gracedb/event_detail_script.js
@@ -354,122 +354,29 @@ require([
     // Section for EMBB 
     //----------------------------------------------------------------------------------------
     var eventDetailContainer = dom.byId('event_detail_content');
-    var embbDiv = put(eventDetailContainer, 'div.content-area#embb_container');
-    var embbTitleDiv = put(embbDiv, 'div#embb_title_expander');
-    var embbContentDiv = put(embbDiv, 'div#embb_content'); 
+    //var embbDiv = put(eventDetailContainer, 'div.content-area#embb_container');
+    //var embbTitleDiv = put(embbDiv, 'div#embb_title_expander');
+    //var embbContentDiv = put(embbDiv, 'div#embb_content'); 
 
     // Put the EEL form into the content div
     // FIXME This needs to be cleaned up. Empty div for now.
     //var oldEelFormDiv = dom.byId('eelFormContainer');
     //var eelFormContents = oldEelFormDiv.innerHTML;
-    var oldEmoFormDiv = dom.byId('emoFormContainer');
-    var emoFormContents = oldEmoFormDiv.innerHTML;
-    domConstruct.destroy('eelFormContainer'); 
-    domConstruct.destroy('emoFormContainer'); 
+    //var oldEmoFormDiv = dom.byId('emoFormContainer');
+    //var emoFormContents = oldEmoFormDiv.innerHTML;
+    // domConstruct.destroy('eelFormContainer'); 
+    //domConstruct.destroy('emoFormContainer'); 
     // var embbAddDiv = put(embbContentDiv, 'div#add_eel_container');
     /* var embbAddFormDiv = put(embbAddDiv, 'div#add_eel_form_container');
     embbAddFormDiv.innerHTML = eelFormContents; */
-    var emoAddDiv = put(embbContentDiv, 'div#add_emo_container');
-    var emoAddFormDiv = put(emoAddDiv, 'div#add_emo_form_container');
-    emoAddFormDiv.innerHTML = emoFormContents; 
+    //var emoAddDiv = put(embbContentDiv, 'div#add_emo_container');
+    //var emoAddFormDiv = put(emoAddDiv, 'div#add_emo_form_container');
+    //emoAddFormDiv.innerHTML = emoFormContents; 
 
-    createExpandingSection(embbTitleDiv, embbContentDiv, emoAddFormDiv, 'Electromagnetic Bulletin Board');
+    //createExpandingSection(embbTitleDiv, embbContentDiv, emoAddFormDiv, 'Electromagnetic Bulletin Board');
 
     // Append the div that will hold our dgrid
-    put(embbContentDiv, 'div#emo-grid');
-        
-    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 emos = content.observations;
-
-        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: 'footprint_count', label: 'N_regions' },
-                { field: 'radec',
-                  label: 'Covering (ra, dec)',
-                    get: function(object){
-                        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: '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
-            for (i = 0; i < columns.length; i++) {
-                columns[i].className = 'supergrid-cell';
-            }
-            for (i = 0; i < subRowColumns.length; i++) {
-                subRowColumns[i].className = 'subgrid-cell';
-            }
-
-            var grid = new Grid({ 
-                columns: columns,
-                className: 'dgrid-autoheight',
-
-                renderRow: function (object, options) {
-                    // Add the supergrid-row class to the row so we can style it separately from the subrows.
-                    var div = put('div.collapsed.supergrid-row', Grid.prototype.renderRow.call(this, object, options));
-
-                    // Add the subdiv table which will expand and contract.
-                    var t = put(div, 'div.expando table');
-                    // I'm finding that the table needs to be 100% of the available width, otherwise
-                    // Firefox doesn't like it. Hence the extra empty column.
-                    var subGridNode = put(t, 'tr td[style="width: 5%"]+td div');
-                    var sg = new Grid({
-                        columns: subRowColumns,
-                        className: 'dgird-subgrid',
-                    }, subGridNode);
-                    sg.renderArray(object.footprints);
-                    // Add the text comment
-                    //put(t, 'tr td[style="width: 5%"]+td div.subrid-text', object.comment); 
-
-                    return div;
-                }
-            }, 'emo-grid'); 
-            grid.renderArray(emos);
-            grid.set("sort", 'N', descending=true);
-
-            var expandedNode = null;
-
-            // listen for clicks to trigger expand/collapse in table view mode
-            var expandoListener = on(grid.domNode, '.dgrid-row:click', function (event) {
-                var node = grid.row(event).element;
-                var collapsed = node.className.indexOf('collapsed') >= 0;
-
-                // toggle state of node which was clicked
-                put(node, (collapsed ? '!' : '.') + 'collapsed');
-
-                // if clicked row wasn't expanded, collapse any previously-expanded row
-                collapsed && expandedNode && put(expandedNode, '.collapsed');
-
-                // if the row clicked was previously expanded, nothing is expanded now
-                expandedNode = collapsed ? node : null;
-            });
-        } // endif on whether we have any emos or not.
-    });
-
+    //put(embbContentDiv, 'div#emo-grid');
 
     //----------------------------------------------------------------------------------------
     // Section for log entries
@@ -773,6 +680,47 @@ require([
 
                 }
 
+                // Create the EMObservations title pane
+                // XXX Branson fixme
+                var pane_contents_id = 'emobservations_pane_div';
+                
+                // Create the title pane with a placeholder div
+                var emo_tp = new TitlePane({ 
+                    title: 'EM Observations',
+                    content: '<div id="' + pane_contents_id + '"></div>',
+                    open: true
+                });
+                logContentDiv.appendChild(emo_tp.domNode);
+    
+                var emoDiv = dom.byId(pane_contents_id);
+                // Create the section for adding EMObservation records. First an outer container:
+                var emoAddDiv = put(emoDiv, 'div#add_emo_container');
+                // The order is important. First the toggling form display button, then the form.
+                // FIXME: Such a sad way of putting in vertical space.
+                put(emoDiv, 'br')
+                var addEmoButtonNode = put(emoAddDiv, 'div.expandFormButton', '(add observation record)');
+                var emoAddFormDiv = put(emoAddDiv, 'div#add_emo_form_container');
+                domStyle.set(emoAddFormDiv, 'display', 'none');
+                
+                on(addEmoButtonNode, "click", function() {
+                    if (domStyle.get(emoAddFormDiv, 'display') == 'none') {
+                        domStyle.set(emoAddFormDiv, 'display', 'block');
+                        addEmoButtonNode.innerHTML = '(cancel)';
+                    } else {
+                        domStyle.set(emoAddFormDiv, 'display', 'none');
+                        addEmoButtonNode.innerHTML = '(add observation record)';
+                    }
+                });
+
+                // Grab the form fragment and put it in the right place.
+                var oldEmoFormDiv = dom.byId('emoFormContainer');
+                var emoFormContents = oldEmoFormDiv.innerHTML;               
+                domConstruct.destroy('emoFormContainer');
+                emoAddFormDiv.innerHTML = emoFormContents;
+                
+                // Create the div for our grid to attach to
+                put(emoDiv, 'div#emo-grid');
+
                 // Create the full event-log title pane
                 var columns = [
                     { field: 'N', label: 'No.' },
@@ -857,7 +805,8 @@ require([
                 var tp = new TitlePane({ 
                     title: 'Full Event Log',
                     content: '<div id="' + pane_contents_id + '"></div>',
-                    open: false
+                    //open: false
+                    open: true
                 });
                 logContentDiv.appendChild(tp.domNode);
 
@@ -872,11 +821,18 @@ require([
                 grid.renderArray(logs);
                 grid.set("sort", 'N', descending=true);
 
+                // Now that we've constructed it, let's close the title pane. 
+                tp.toggle()
+
             } else {
                 // Not doing title panes, just put up the usual log message section.
                 // Will have the full eventlog section. Same as above, except that it 
                 // won't be in a title pane. What is the best way to do this.
 
+                // If we're not doing title panes, we still need to remember to destroy
+                // the emoFormContainer. Otherwise it shows up!
+                domConstruct.destroy('emoFormContainer');
+
                 var columns = [
                     { field: 'N', label: 'No.' },
                     { field: 'created', label: 'Log Entry Created (UTC)' }, 
@@ -948,6 +904,107 @@ require([
 
             }
 
+            //-------------------------------------------------------------------
+            // Finally, let's see if we can get those EMOs in
+            //-------------------------------------------------------------------
+            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 emos = content.observations;
+
+                if (emos.length == 0) {
+                    emoDiv = dom.byId('emo-grid');
+                    emoDiv.innerHTML = '<p> No EM observation entries so far. </p>';
+
+                    // Let's try toggling the emo title pane closed.
+                    if (emo_tp.open) { emo_tp.toggle(); }
+                } else {
+
+                    var columns  = [
+                        { field: 'created', label: 'Time Created (UTC)' },
+                        { field: 'submitter', label: 'Submitter' },
+                        { field: 'group', label: 'MOU Group' },
+                        { field: 'footprint_count', label: 'N_regions' },
+                        { field: 'radec',
+                          label: 'Covering (ra, dec)',
+                            get: function(object){
+                                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: '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
+                    for (i = 0; i < columns.length; i++) {
+                        columns[i].className = 'supergrid-cell';
+                    }
+                    for (i = 0; i < subRowColumns.length; i++) {
+                        subRowColumns[i].className = 'subgrid-cell';
+                    }
+
+                    var grid = new Grid({ 
+                        columns: columns,
+                        className: 'dgrid-autoheight',
+
+                        renderRow: function (object, options) {
+                            // Add the supergrid-row class to the row so we can style it separately from the subrows.
+                            var div = put('div.collapsed.supergrid-row', Grid.prototype.renderRow.call(this, object, options));
+
+                            // Add the subdiv table which will expand and contract.
+                            var t = put(div, 'div.expando table');
+                            // I'm finding that the table needs to be 100% of the available width, otherwise
+                            // Firefox doesn't like it. Hence the extra empty column.
+                            var subGridNode = put(t, 'tr td[style="width: 5%"]+td div');
+                            var sg = new Grid({
+                                columns: subRowColumns,
+                                className: 'dgird-subgrid',
+                            }, subGridNode);
+                            sg.renderArray(object.footprints);
+                            // Add the text comment div as long as the comment is not an empty string.
+                            if (object.comment !== "") {
+                                put(t, 'tr td[style="width: 5%"]+td div.subrid-text', object.comment); 
+                            }
+
+                            return div;
+                        }
+                    }, 'emo-grid'); 
+                    grid.renderArray(emos);
+                    grid.set("sort", 'N', descending=true);
+
+                    var expandedNode = null;
+
+                    // listen for clicks to trigger expand/collapse in table view mode
+                    var expandoListener = on(grid.domNode, '.dgrid-row:click', function (event) {
+                        var node = grid.row(event).element;
+                        var collapsed = node.className.indexOf('collapsed') >= 0;
+
+                        // toggle state of node which was clicked
+                        put(node, (collapsed ? '!' : '.') + 'collapsed');
+
+                        // if clicked row wasn't expanded, collapse any previously-expanded row
+                        collapsed && expandedNode && put(expandedNode, '.collapsed');
+
+                        // if the row clicked was previously expanded, nothing is expanded now
+                        expandedNode = collapsed ? node : null;
+                    });
+                } // endif on whether we have any emos or not.
+            });
+
+
             //-------------------------------------------------------------------
             // Now that the annotations section has been added to the dom, we
             // can work on its functionality.
-- 
GitLab