diff --git a/branson.wsgi b/branson.wsgi
index ba5774aa97912966641dc00c29fc4eaf1aa4b493..2329d66fcbc1b2fad8fd15edfadb8cc1e65697fd 100644
--- a/branson.wsgi
+++ b/branson.wsgi
@@ -22,6 +22,8 @@ sys.path.insert(1,'/home/branson/sandbox/lib/python2.6/site-packages')
 import logging
 logging.basicConfig()
 
+os.environ['MPLCONFIGDIR']='/home/branson/logs/'
+
 #logging.basicConfig(level=logging.DEBUG,
 #                    format='%(asctime)s %(levelname)s %(message)s',
 #                    filename='/tmp/myapp.log',
diff --git a/django.wsgi b/django.wsgi
index 164ebb1f4cc6fdf8cb7a0d49078e9475d27dc92f..31d11ce1dc62f3a27a2a52d8dc8ab176e14c8cf5 100644
--- a/django.wsgi
+++ b/django.wsgi
@@ -13,6 +13,8 @@ sys.path.append('/home/gracedb/graceproj')
 import logging
 logging.basicConfig()
 
+os.environ['MPLCONFIGDIR']='/tmp/'
+
 #logging.basicConfig(level=logging.DEBUG,
 #                    format='%(asctime)s %(levelname)s %(message)s',
 #                    filename='/tmp/myapp.log',
diff --git a/gracedb/forms.py b/gracedb/forms.py
index 2e5497ae33ec54178079e8d9d6fc6757c0a67fcb..f89210aef13365a7f9899caccd06aa10a97d4122 100644
--- a/gracedb/forms.py
+++ b/gracedb/forms.py
@@ -22,7 +22,11 @@ class GraceQueryField(forms.CharField):
         from django.db.models import Q
         queryString = forms.CharField.clean(self, queryString)
         try:
-            return Event.objects.filter(parseQuery(queryString)).distinct()
+            if self.select_subclasses:
+                return Event.objects.filter(parseQuery(queryString)).select_subclasses()
+            else:
+                return Event.objects.filter(parseQuery(queryString)).distinct()
+
         except ParseException, e:
             err = "Error: " + escape(e.pstr[:e.loc]) + errorMarker + escape(e.pstr[e.loc:])
             raise forms.ValidationError(mark_safe(err))
@@ -34,9 +38,19 @@ class GraceQueryField(forms.CharField):
             # What could this be and how can we handle it better? XXX
             raise forms.ValidationError(str(e)+str(type(e)))
 
+    def __init__(self, *args, **kwargs):
+        self.select_subclasses = False
+        if 'select_subclasses' in kwargs.keys():
+            self.select_subclasses = kwargs.pop('select_subclasses')
+        super(GraceQueryField, self).__init__(*args, **kwargs)
+
 class SimpleSearchForm(forms.Form):
-    query = GraceQueryField(required=False, widget=forms.TextInput(attrs={'size':60}))
+    query = GraceQueryField(required=False, widget=forms.TextInput(attrs={'size':60})) 
 
+# XXX There must be a better way of doing this.
+class SimpleSearchFormWithSubclasses(forms.Form):
+    query = GraceQueryField(required=False, widget=forms.TextInput(attrs={'size':60}),
+            select_subclasses=True) 
 
 class CreateEventForm(forms.Form):
     groupChoices = [("","")]+[(g.name, g.name) for g in Group.objects.all()]
diff --git a/gracedb/reports.py b/gracedb/reports.py
index b01577193fc98fc7f5017404912f3aa985446831..d4eaefe24b735a3f4d9641b3981dfc8e2e4bace6 100644
--- a/gracedb/reports.py
+++ b/gracedb/reports.py
@@ -1,5 +1,6 @@
 
 from django.http import HttpResponse
+from django.http import HttpResponseForbidden
 from django.template import RequestContext
 from django.shortcuts import render_to_response
 from django.conf import settings
@@ -7,7 +8,26 @@ from django.conf import settings
 from gracedb.models import Event
 from django.db.models import Q
 
-import os, datetime, json, time
+import os, json
+
+from django.core.urlresolvers import reverse
+
+from models import CoincInspiralEvent ,SingleInspiral
+from forms import SimpleSearchFormWithSubclasses
+from query import parseQuery
+
+
+from django.db.models import Max, Min
+import matplotlib
+matplotlib.use('Agg')
+import numpy
+import matplotlib.pyplot as plot
+import StringIO
+import base64
+import sys
+import time
+from datetime import datetime, timedelta
+from utils import posixToGpsTime
 
 def histo(request):
 
@@ -46,8 +66,8 @@ def histo(request):
 def rate_data(request):
     # XXX there is a better way -- should be using group_by or something.
     # WAAY too many queries (~300) going on here.
-    now = datetime.datetime.now()
-    day = datetime.timedelta(1)
+    now = datetime.now()
+    day = timedelta(1)
 
     ts_min = now - 60 * day
     ts_max = now
@@ -84,3 +104,193 @@ def rate_data(request):
     return series
 
 
+
+# XXX This should be configurable / moddable or something
+MAX_QUERY_RESULTS = 1000
+
+# The following two util routines are for gstlalcbc_report. This is messy.
+def cluster(events):
+    # FIXME N^2 clustering, but event list should always be small anyway...
+    def quieter(e1, events = events, win = 5):
+        for e2 in events:
+            if e2.far == e1.far:
+                # XXX I need subclass attributes here.
+                if (e2.gpstime < e1.gpstime + win) and (e2.gpstime > e1.gpstime - win) and (e2.snr > e1.snr):
+                    return True
+            else:
+                if (e2.gpstime < e1.gpstime + win) and (e2.gpstime > e1.gpstime - win) and (e2.far < e1.far):
+                    return True
+        return False
+    return [e for e in events if not quieter(e)]
+
+def to_png_image(out = sys.stdout):
+    f = StringIO.StringIO()
+    plot.savefig(f, format="png")
+    return base64.b64encode(f.getvalue())
+
+def gstlalcbc_report(request, format=""):
+    if not request.user or not request.user.is_authenticated():
+        return HttpResponseForbidden("Forbidden")
+
+    if request.method == "GET":
+        if "query" not in request.GET:
+            # Use default query. LowMass events from the past week.
+            t_high = datetime.now()
+            dt = timedelta(days=7)
+            t_low = t_high - dt
+            t_high = posixToGpsTime(time.mktime(t_high.timetuple()))
+            t_low = posixToGpsTime(time.mktime(t_low.timetuple()))
+            query = 'CBC LowMass %d .. %d' % (t_low, t_high)
+            rawquery = query
+            form = SimpleSearchFormWithSubclasses({'query': query})
+        else:
+            form = SimpleSearchFormWithSubclasses(request.GET)
+            rawquery = request.GET['query']
+    else:
+        form = SimpleSearchFormWithSubclasses(request.POST)
+        rawquery = request.POST['query']
+    if form.is_valid():
+        objects = form.cleaned_data['query']
+
+        # Check for foreign objects.
+        for obj in objects:
+            if not isinstance(obj, CoincInspiralEvent):
+                errormsg = 'Your query returned items that are not CoincInspiral Events. '
+                errormsg += 'Please try again.'
+                form = SimpleSearchFormWithSubclasses()
+                return render_to_response('gracedb/gstlalcbc_report.html', 
+                        { 'form':form, 'message':errormsg}, 
+                        context_instance=RequestContext(request))
+
+        # Check that we have a well-defined GPS time range.
+        # Find the gpstime limits of the query by diving into the query object.
+        # XXX Down the rabbit hole!
+        qthing = parseQuery(rawquery)
+        gpsrange = None
+        for child in qthing.children:
+            if 'gpstime' in str(child):
+                for subchild in child.children:
+                    if isinstance(subchild, tuple):
+                        gpsrange = subchild[1]
+        if not gpsrange:
+            # Bounce back to the user with an error message
+            errormsg = 'Your query does not have a gpstime range. Please try again.'
+            form = SimpleSearchFormWithSubclasses()
+            return render_to_response('gracedb/gstlalcbc_report.html', 
+                    { 'form':form, 'message':errormsg}, 
+                    context_instance=RequestContext(request))
+        lt = int(gpsrange[1]) - int(gpsrange[0])
+
+        # Check that there aren't too many objects.
+        # XXX Hardcoded limit
+        if objects.count() > 2000:
+            errormsg = 'Your query returned too many events. Please try again.'
+            return render_to_response('gracedb/gstlalcbc_report.html', 
+                    { 'form':form, 'message':errormsg}, 
+                    context_instance=RequestContext(request))
+
+        # Zero events will break the min/max over masses below. 
+        if objects.count() < 1:
+            errormsg = 'Your query returned no events. Please try again.'
+            return render_to_response('gracedb/gstlalcbc_report.html', 
+                    { 'form':form, 'message':errormsg}, 
+                    context_instance=RequestContext(request))
+
+        clustered_events = cluster(objects)
+        clustered_events = sorted(clustered_events, None, key=lambda x: x.far)
+
+        # Make IFAR plot.
+        ifars = numpy.array(sorted([1.0 / e.far for e in clustered_events])[::-1])
+        N = numpy.linspace(1, len(ifars), len(ifars))
+
+        eN = numpy.linspace(1, 1000 * len(ifars), 1000 * len(ifars)) / 1000.
+        expected_ifars = lt / eN
+
+        up = eN + eN**.5
+        down = eN - eN**.5
+        down[down < 0.9] = 0.9
+
+        plot.figure(figsize=(6,5))
+        plot.loglog(ifars[::-1], N[::-1])
+        plot.fill_between(expected_ifars[::-1], down[::-1], up[::-1], alpha=0.1)
+        plot.loglog(expected_ifars[::-1], eN[::-1])
+        plot.ylim([0.9, len(ifars)])
+        plot.xlabel('IFAR (s)')
+        plot.ylabel('N')
+        plot.grid()
+        ifar_plot = to_png_image()
+
+        # Set the color map for loudest event table. Depends on lt.
+        #FAR_color_map = [ { 'max_far' : 1.e-12, 'color' : 'gold',    'desc' : '< 1/10000 yrs'},
+        #        { 'max_far' : 3.e-10, 'color' : 'silver',  'desc' : '< 1/100 yrs'},
+        #        { 'max_far' : 3.e-8,  'color' : '#A67D3D', 'desc' : '< 1/yr'},
+        #        { 'max_far' : 1.0/lt, 'color' : '#B2C248', 'desc' : 'louder than expected'}, ]
+        
+        # XXX Okay, this sucks. There is no switch/case structure in the django template
+        # language. So I couldn't think of any way to do this without checking ranges.
+        # And the range is zero to infinity. So here I go...
+        FAR_color_map = [ { 'min_far' : 0.0, 'max_far' : 1.e-12, 'color' : 'gold',    'desc' : '< 1/10000 yrs'},
+                { 'min_far' : 1.e-12, 'max_far' : 3.e-10, 'color' : 'silver',  'desc' : '< 1/100 yrs'},
+                { 'min_far' : 3.e-10, 'max_far' : 3.e-8,  'color' : '#A67D3D', 'desc' : '< 1/yr'},
+                { 'min_far' : 3.e-8,  'max_far' : 1.0/lt, 'color' : '#B2C248', 'desc' : 'louder than expected'}, 
+                { 'min_far' : 1.0/lt, 'max_far' : float('inf'), 'color' : 'white', 'desc' : ''}, ]
+
+        # Make mass distribution plots
+        # First, find the min and max on the set of objects.
+        mchirp_limits = objects.aggregate(Max('coincinspiralevent__mchirp'), 
+                Min('coincinspiralevent__mchirp'))
+        mass_limits = objects.aggregate(Max('coincinspiralevent__mass'), 
+                Min('coincinspiralevent__mass'))
+
+        mchirp = numpy.array([e.mchirp for e in clustered_events])
+        plot.figure(figsize=(6,4))
+        lower = int(mchirp_limits['coincinspiralevent__mchirp__min'])
+        upper = int(mchirp_limits['coincinspiralevent__mchirp__max']) + 1
+        # XXX How to decide the number of bins? Hardcoding.
+        N_bins = 20
+        delta = max(float(upper-lower)/N_bins,0.2)
+        plot.hist(mchirp, bins=numpy.arange(lower,upper,delta))
+        plot.xlabel('Chirp Mass')
+        plot.ylabel('count')
+        plot.grid()
+        mchirp_dist = to_png_image()
+
+        mass = numpy.array([e.mass for e in clustered_events])
+        plot.figure(figsize=(6,4))
+        lower = int(mass_limits['coincinspiralevent__mass__min'])
+        upper = int(mass_limits['coincinspiralevent__mass__max']) + 1
+        # XXX Hardcoding number of bins again.
+        N_bins = 20
+        delta = max(float(upper-lower)/N_bins,0.2)
+        plot.hist(mass, bins=numpy.arange(lower,upper,delta))
+        plot.xlabel('Total Mass')
+        plot.ylabel('count')
+        plot.grid()
+        mass_dist = to_png_image()
+
+        if objects.count() == 1:
+            title = "Query returned %s event." % objects.count()
+        else:
+            title = "Query returned %s events." % objects.count()
+
+        context = {
+            'title': title,
+            'form': form,
+            'formAction': reverse(gstlalcbc_report),
+            'count' : objects.count(),
+            'rawquery' : rawquery,
+            'FAR_color_map' : FAR_color_map,
+            'mass_dist' : mass_dist,
+            'mchirp_dist' : mchirp_dist,
+            'ifar_plot' : ifar_plot,
+            'clustered_events' : clustered_events,
+        }
+        return render_to_response('gracedb/gstlalcbc_report.html', context,
+                context_instance=RequestContext(request))
+
+    return render_to_response('gracedb/gstlalcbc_report.html',
+            { 'form' : form,
+            },
+            context_instance=RequestContext(request))
+
+
diff --git a/gracedb/templatetags/timeutil.py b/gracedb/templatetags/timeutil.py
index 0290c58cfbe209d0b70819dd8ed69375db4edaed..0e3a6180bf1c9218ba2444dd2867274f804f9d08 100644
--- a/gracedb/templatetags/timeutil.py
+++ b/gracedb/templatetags/timeutil.py
@@ -12,6 +12,7 @@ from utils import posixToGpsTime, gpsToUtc
 import pytz
 import time
 import datetime
+import logging
 
 # DATETIME_SETTINGS is guaranteed to be set.  GRACE_DATETIME_FORMAT is not.
 FORMAT = getattr(settings, 'GRACE_DATETIME_FORMAT', settings.DATETIME_FORMAT)
@@ -157,3 +158,19 @@ def timeSelections(t):
     rv['utc'] = dateformat.format(dt.astimezone(pytz.utc), format)
 
     return rv
+
+# XXX Branson added this. God will punish me.
+@register.filter
+def end_time(event,digits=4):
+    try:
+        #return float(event.end_time) + float(event.end_time_ns)/1.e9
+        # The approach above did not work. For some reason, the value loses precision
+        # in the django template, so that the final digits get truncated and padded 
+        # with zeros. Why? The current gpstime is only 10 digits. So I'm going to have to
+        # make this into a string. Totally sick, I know.
+        decimal_part = round(float(event.end_time_ns)/1.e9,digits)
+        # ugh. must pad with zeros to the right.
+        decimal_part = str(decimal_part)[1:].ljust(digits+1,'0')
+        return str(event.end_time) + decimal_part
+    except Exception, e:
+        return None
diff --git a/gracedb/translator.py b/gracedb/translator.py
index 9000531de1dbdf842d1bb6c9519416c3927ba743..78b8a30a18332ea7d941d9f580f0af0fe8a5c8e3 100644
--- a/gracedb/translator.py
+++ b/gracedb/translator.py
@@ -360,6 +360,23 @@ def handle_uploaded_data(event, datafilename,
                            comment="Log File Created" )
             log.save()
 
+        # Log the link to the CED.
+        if data.data['ced_link']:
+            comment = '<a href="%s">Coherent Event Display (CED)</a>' % data.data['ced_link']
+            log = EventLog(event=event,
+                           issuer=event.submitter,
+                           comment=comment)
+            log.save()
+
+
+        # Log the link to the skymap.
+        if data.data['fits_skymap_link']:
+            comment = '<a href="%s">FITS skymap</a>' % data.data['fits_skymap_link']
+            log = EventLog(event=event,
+                           issuer=event.submitter,
+                           comment=comment)
+            log.save()
+
     elif event.analysisType == 'GRB':
         # Get the event time from the VOEvent file
         error = None
@@ -493,6 +510,9 @@ class CwbData(Translator):
         #
         #   The 2nd number following the "24*6" line is FAR.
         #
+        # https://... (link to CED)
+        # https://... (link to fits skymap)
+
         rawdata = {}
 
         # Get Key/Value info
@@ -549,6 +569,19 @@ class CwbData(Translator):
         data['ligo_axis_ra']     = data['rawdata'].get('phi',[None,None,None])[2]
         data['ligo_axis_dec']    = data['rawdata'].get('theta',[None,None,None])[2]
 
+        # Check for the links at the end.
+        ced_link = None
+        fits_skymap_link = None
+        for line in datafile:
+            if line.startswith("https"):
+                if line.find(".fits") > 0:
+                    fits_skymap_link = line
+                else:
+                    ced_link = line
+
+        data['ced_link'] = ced_link
+        data['fits_skymap_link'] = fits_skymap_link
+
         if needToClose:
             datafile.close()
 
diff --git a/gracedb/views.py b/gracedb/views.py
index 2ec10a1e77423163df743440760856128bc32574..8266ccd8efde923b6831face3700e8ea4f0bc3dd 100644
--- a/gracedb/views.py
+++ b/gracedb/views.py
@@ -18,8 +18,10 @@ from models import MultiBurstEvent
 from models import GrbEvent
 from models import SingleInspiral
 from forms import CreateEventForm, EventSearchForm, SimpleSearchForm
+from forms import SimpleSearchFormWithSubclasses
 from alert import issueAlert, issueAlertForLabel, issueAlertForUpdate
 from translator import handle_uploaded_data
+from query import parseQuery
 
 from django.contrib.auth.models import User
 
@@ -698,7 +700,6 @@ def assembleLigoLw(objects):
     ligolw_add.merge_compatible_tables(xmldoc)
     return xmldoc
 
-
 def search(request, format=""):
     if not request.user or not request.user.is_authenticated():
         return HttpResponseForbidden("Forbidden")
diff --git a/ligoauth/migrations/0008_add_grbexttrig_user_and_dn.py b/ligoauth/migrations/0008_add_grbexttrig_user_and_dn.py
new file mode 100644
index 0000000000000000000000000000000000000000..72b18031b182d668c2205211824752b80a1cec08
--- /dev/null
+++ b/ligoauth/migrations/0008_add_grbexttrig_user_and_dn.py
@@ -0,0 +1,125 @@
+# -*- coding: utf-8 -*-
+import datetime
+from south.db import db
+from south.v2 import DataMigration
+from django.db import models
+
+
+
+users = [
+        {
+            'username' : 'grb.exttrig',
+            'first_name' : '',
+            'last_name' : 'GRB ExtTrig',
+            'email' : 'talukder@uoregon.edu',
+            'dns' : [
+                "/DC=org/DC=ligo/O=LIGO/OU=Services/CN=grb.exttrig/ldas-pcdev1.ligo.caltech.edu",
+            ]
+        },
+]
+
+
+class Migration(DataMigration):
+
+    def forwards(self, orm):
+        LocalUser = orm['ligoauth.LocalUser']
+        X509Cert = orm['ligoauth.X509Cert']
+
+        # Local Users
+        for entry in users:
+            user, created = LocalUser.objects.get_or_create(username=entry['username'])
+            if created:
+                user.first_name = entry['first_name']
+                user.last_name = entry['last_name']
+                user.email = entry['email']
+                user.is_active = True
+                user.is_staff = False
+                user.is_superuser = False
+                user.save()
+            current_dns = set([cert.subject for cert in user.x509cert_set.all()])
+            new_dns = set(entry['dns'])
+
+            missing_dns = new_dns - current_dns
+            redundant_dns = current_dns - new_dns
+
+            for dn in missing_dns:
+                cert, created = X509Cert.objects.get_or_create(subject=dn)
+                if created:
+                    cert.save()
+                cert.users.add(user)
+
+            for dn in redundant_dns:
+                cert = X509Cert.objects.get(subject=dn)
+                cert.users.remove(user)
+
+    def backwards(self, orm):
+        LocalUser = orm['ligoauth.LocalUser']
+        X509Cert = orm['ligoauth.X509Cert']
+        for entry in users:
+            for dn in entry['dns']:
+                cert = X509Cert.objects.get(subject=dn)
+                cert.delete()
+
+            user = LocalUser.objects.get(username=entry['username'])
+            user.delete()                   
+
+
+
+
+
+    models = {
+        'auth.group': {
+            'Meta': {'object_name': 'Group'},
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}),
+            'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'})
+        },
+        'auth.permission': {
+            'Meta': {'ordering': "('content_type__app_label', 'content_type__model', 'codename')", 'unique_together': "(('content_type', 'codename'),)", 'object_name': 'Permission'},
+            'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+            'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}),
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'name': ('django.db.models.fields.CharField', [], {'max_length': '50'})
+        },
+        'auth.user': {
+            'Meta': {'object_name': 'User'},
+            'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
+            'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}),
+            'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
+            'groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Group']", 'symmetrical': 'False', 'blank': 'True'}),
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
+            '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', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}),
+            'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'})
+        },
+        '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'}),
+            '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'})
+        },
+        'ligoauth.ligoldapuser': {
+            'Meta': {'object_name': 'LigoLdapUser', '_ormbases': ['auth.User']},
+            'ldap_dn': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '100'}),
+            'user_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['auth.User']", 'unique': 'True', 'primary_key': 'True'})
+        },
+        'ligoauth.localuser': {
+            'Meta': {'object_name': 'LocalUser', '_ormbases': ['auth.User']},
+            'user_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['auth.User']", 'unique': 'True', 'primary_key': 'True'})
+        },
+        'ligoauth.x509cert': {
+            'Meta': {'object_name': 'X509Cert'},
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'subject': ('django.db.models.fields.CharField', [], {'max_length': '200'}),
+            'users': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.User']", 'symmetrical': 'False'})
+        }
+    }
+
+    complete_apps = ['ligoauth']
+    symmetrical = True
diff --git a/static/css/style.css b/static/css/style.css
index 7580845d0c1fbd3aa756a7a9fb0584cc68dbb163..d95fe1c6faa2c28d62ff5ec270ee429fc4fb3e44 100644
--- a/static/css/style.css
+++ b/static/css/style.css
@@ -12,8 +12,14 @@ table.event {border-bottom:1px solid gray;}
 /* Branson added */
 table.event {width:100%}
 
+table.analysis_specific_lm th {padding:3px;border:none;text-align:center;vertical-align:bottom;}
+table.analysis_specific_lm {border-bottom:1px solid gray;}
+
 table.analysis_specific {width:100%}
 
+table.gstlalcbc {border:0;border-spacing:0px;width:100%;}
+table.gstlalcbc th {padding:3px;border:none;vertical-align:bottom;}
+
 .content-area {
     margin-top: 10px;
     margin-bottom: 30px;
diff --git a/templates/gracedb/event_detail_LM.html b/templates/gracedb/event_detail_LM.html
index a02d436d216fc96e49520d2482d6590e4a4a984d..11d81da57d377ee221b5bce8d74f3db29af900cb 100644
--- a/templates/gracedb/event_detail_LM.html
+++ b/templates/gracedb/event_detail_LM.html
@@ -1,40 +1,45 @@
 {% extends "gracedb/event_detail.html" %}
-
+{% load timeutil %}
 {% load scientific %}
 
 {# Analysis-specific attributes for an LM event#}
 {% block analysis_specific %}
-<h3>Analysis-Specific Attributes</h3>
 
-<table class="analysis_specific"> <tbody>
-    <tr>
-        <td>  <table class="event"> <tbody>
-                    <tr> <th> ifos </th> <td> {{object.ifos}} </td> </tr> 
-                    <tr> <th> end_time </th> <td> {{object.end_time}} </td> </tr>
-                    <tr> <th> end_time_ns </th> <td> {{object.end_time_ns}} </td> </tr>
-              </tbody></table>
-        </td>
-        <td>  <table class="event"> <tbody>
-                    <tr> <th> mass </th> <td> {{object.mass|floatformat:"-4"}} </td> </tr> 
-                    <tr> <th> mchirp </th> <td> {{object.mchirp|floatformat:"-4"}} </td> </tr>
-                    <tr> <th> minimum_duration </th> <td> {{object.minimum_duration|scientific}} </td> </tr>
-              </tbody></table>
-        </td>
-        <td>  <table class="event"> <tbody>
-                    <tr> <th> snr </th> <td> {{object.snr|floatformat:"-4"}} </td> </tr> 
-                    <tr> <th> false_alarm_rate </th> <td> {{object.false_alarm_rate|floatformat:"-4"}} </td> </tr>
-                    <tr> <th> combined_far </th> <td> {{object.combined_far|scientific}} </td> </tr>
-              </tbody></table>
-        </td>
+{# Test whether the object has the end_time property. Older (non CoincInspiral) events don't. #}
+{% if object.end_time %}
+
+<div id="container" style="display:table;width:100%">
+<div style="display:table-row;width:100%">
 
-    </tr>
-</tbody> </table> 
+<div style="display:table-cell;float:left;width:35%;">
+<h3>Coinc Tables</h3>
+<!-- <table class="analysis_specific"> <tbody> -->
+<!-- 5 rows here -->
+<table style="height:320px"> 
+    <!-- <tr> <th> ifos </th> <td> {{object.ifos}} </td> </tr>  -->
+    <tr> <th> End Time </th> <td> {{object|end_time:4}} </td> </tr>
+    <!--<tr> <th> end_time_ns </th> <td> {{object.end_time_ns}} </td> </tr> -->
+    <tr> <th> Total Mass </th> <td> {{object.mass|floatformat:"-4"}} </td> </tr> 
+    <tr> <th> Chirp Mass </th> <td> {{object.mchirp|floatformat:"-4"}} </td> </tr>
+    <!--<tr> <th> minimum_duration </th> <td> {{object.minimum_duration|scientific}} </td> </tr>-->
+    <tr> <th> SNR </th> <td> {{object.snr|floatformat:"-4"}} </td> </tr> 
+    <tr> <th> False Alarm Probability </th> <td> {{object.false_alarm_rate|scientific }} </td> </tr>
+    <!--<tr> <th> combined_far </th> <td> {{object.combined_far|scientific}} </td> </tr>-->
+</table>
+<!-- </tbody> </table>  -->
+</div>
 
 {% if single_inspiral_events %}
 <!-- Single Inspiral Data -->
-<div id="single_inspiral_tables"
+<!-- 13 rows here. -->
+<div style="display:table-cell;float:right;width:65%">
+<h3>Single Inspiral Tables</h3>
+
+<!--<div id="single_inspiral_tables"> -->
+<!-- <div id="single_inspiral_tables"
        	data-dojo-type="dijit/TitlePane"
-       	data-dojo-props="title: 'Single Inspiral Tables', open: false">
+        data-dojo-props="title: 'Single Inspiral Tables', open: false"> -->
+        <!--<table class="analysis_specific_lm"> -->
 <table>
 	<tr>
 		<th>IFO</th>
@@ -42,12 +47,12 @@
 		<th>{{ e.ifo }}</th>
 		{% endfor %}
 	</tr>
-	<tr>
+	<!-- <tr>
 		<th>Search</th>
 		{% for e in single_inspiral_events %}
 		<td>{{ e.search }}</td>
 		{% endfor %}
-	</tr>
+    </tr> -->
 	<tr>
 		<th>Channel</th>
 		{% for e in single_inspiral_events %}
@@ -60,30 +65,30 @@
 		<td>{{ e.end_time_full }}</td>
 		{% endfor %}
 	</tr>
-	<tr>
+	<!-- <tr>
 		<th>Impulse Time</th>
 		{% for e in single_inspiral_events %}
 		<td>{{ e.impulse_time_full }}</td>
 		{% endfor %}
-	</tr>
+    </tr> -->
 	<tr>
 		<th>Template Duration</th>
 		{% for e in single_inspiral_events %}
 		<td>{{ e.template_duration }}</td>
 		{% endfor %}
 	</tr>
-	<tr>
+	<!-- <tr>
 		<th>Event Duration</th>
 		{% for e in single_inspiral_events %}
 		<td>{{ e.event_duration }}</td>
 		{% endfor %}
-	</tr>
-	<tr>
+    </tr> -->
+	<!-- <tr>
 		<th>Amplitude</th>
 		{% for e in single_inspiral_events %}
 		<td>{{ e.amplitude }}</td>
 		{% endfor %}
-	</tr>
+    </tr> -->
 	<tr>
 		<th>Effective Distance</th>
 		{% for e in single_inspiral_events %}
@@ -108,132 +113,132 @@
 		<td>{{ e.mass2 }}</td>
 		{% endfor %}
 	</tr>
-	<tr>
+	<!-- <tr>
 		<th>MChirp</th>
 		{% for e in single_inspiral_events %}
 		<td>{{ e.mchirp }}</td>
 		{% endfor %}
-	</tr>
-	<tr>
+    </tr> -->
+	<!-- <tr>
 		<th>MTotal</th>
 		{% for e in single_inspiral_events %}
 		<td>{{ e.mtotal }}</td>
 		{% endfor %}
-	</tr>
+    </tr> -->
 	<tr>
 		<th>&eta;</th>
 		{% for e in single_inspiral_events %}
 		<td>{{ e.eta }}</td>
 		{% endfor %}
 	</tr>
-	<tr>
+	<!-- <tr>
 		<th>&kappa;</th>
 		{% for e in single_inspiral_events %}
 		<td>{{ e.kappa }}</td>
 		{% endfor %}
-	</tr>
-	<tr>
+    </tr> -->
+	<!-- <tr>
 		<th>&chi;</th>
 		{% for e in single_inspiral_events %}
 		<td>{{ e.chi }}</td>
 		{% endfor %}
-	</tr>
-	<tr>
+    </tr> -->
+	<!-- <tr>
 		<th>&tau;<sub>0</sub></th>
 		{% for e in single_inspiral_events %}
 		<td>{{ e.tau0 }}</td>
 		{% endfor %}
-	</tr>
-	<tr>
+    </tr> -->
+	<!-- <tr>
 		<th>&tau;<sub>2</sub></th>
 		{% for e in single_inspiral_events %}
 		<td>{{ e.tau2 }}</td>
 		{% endfor %}
-	</tr>
-	<tr>
+    </tr> -->
+	<!-- <tr>
 		<th>&tau;<sub>3</sub></th>
 		{% for e in single_inspiral_events %}
 		<td>{{ e.tau3 }}</td>
 		{% endfor %}
-	</tr>
-	<tr>
+    </tr> -->
+	<!-- <tr>
 		<th>&tau;<sub>4</sub></th>
 		{% for e in single_inspiral_events %}
 		<td>{{ e.tau4 }}</td>
 		{% endfor %}
-	</tr>
-	<tr>
+    </tr> -->
+	<!-- <tr>
 		<th>&tau;<sub>5</sub></th>
 		{% for e in single_inspiral_events %}
 		<td>{{ e.tau5 }}</td>
 		{% endfor %}
-	</tr>
-	<tr>
+    </tr> -->
+	<!-- <tr>
 		<th>&tau;<sub>Total</sub></th>
 		{% for e in single_inspiral_events %}
 		<td>{{ e.ttotal }}</td>
 		{% endfor %}
-	</tr>
-	<tr>
+    </tr> -->
+	<!-- <tr>
 		<th>&Psi;<sub>0</sub></th>
 		{% for e in single_inspiral_events %}
 		<td>{{ e.psi0 }}</td>
 		{% endfor %}
-	</tr>
-	<tr>
+    </tr> -->
+	<!-- <tr>
 		<th>&Psi;<sub>3</sub></th>
 		{% for e in single_inspiral_events %}
 		<td>{{ e.psi3 }}</td>
 		{% endfor %}
-	</tr>
-	<tr>
+    </tr> -->
+	<!-- <tr>
 		<th>&alpha;</th>
 		{% for e in single_inspiral_events %}
 		<td>{{ e.alpha }}</td>
 		{% endfor %}
-	</tr>
-	<tr>
+    </tr> -->
+	<!-- <tr>
 		<th>&alpha;<sub>1</sub></th>
 		{% for e in single_inspiral_events %}
 		<td>{{ e.alpha1 }}</td>
 		{% endfor %}
-	</tr>
-	<tr>
+    </tr> -->
+	<!-- <tr>
 		<th>&alpha;<sub>2</sub></th>
 		{% for e in single_inspiral_events %}
 		<td>{{ e.alpha2 }}</td>
 		{% endfor %}
-	</tr>
-	<tr>
+    </tr> -->
+	<!-- <tr>
 		<th>&alpha;<sub>3</sub></th>
 		{% for e in single_inspiral_events %}
 		<td>{{ e.alpha3 }}</td>
 		{% endfor %}
-	</tr>
-	<tr>
+    </tr> -->
+	<!-- <tr>
 		<th>&alpha;<sub>4</sub></th>
 		{% for e in single_inspiral_events %}
 		<td>{{ e.alpha4 }}</td>
 		{% endfor %}
-	</tr>
-	<tr>
+    </tr> -->
+	<!-- <tr>
 		<th>&alpha;<sub>5</sub></th>
 		{% for e in single_inspiral_events %}
 		<td>{{ e.alpha5 }}</td>
 		{% endfor %}
-	</tr>
-	<tr>
+    </tr> -->
+	<!-- <tr>
 		<th>&alpha;<sub>6</sub></th>
 		{% for e in single_inspiral_events %}
 		<td>{{ e.alpha6 }}</td>
 		{% endfor %}
-	</tr>
-	<tr>
+    </tr> -->
+	<!-- <tr>
 		<th>&beta;</th>
 		{% for e in single_inspiral_events %}
 		<td>{{ e.beta }}</td>
 		{% endfor %}
-	</tr>
+    </tr> -->
 	<tr>
 		<th>F Final</th>
 		{% for e in single_inspiral_events %}
@@ -258,104 +263,110 @@
 		<td>{{ e.chisq_dof }}</td>
 		{% endfor %}
 	</tr>
-	<tr>
+	<!-- <tr>
 		<th>Bank &chi;<sup>2</sup></th>
 		{% for e in single_inspiral_events %}
 		<td>{{ e.bank_chisq }}</td>
 		{% endfor %}
-	</tr>
-	<tr>
+    </tr> -->
+	<!-- <tr>
 		<th>Bank &chi;<sup>2</sup> DOF</th>
 		{% for e in single_inspiral_events %}
 		<td>{{ e.bank_chisq_dof }}</td>
 		{% endfor %}
-	</tr>
-	<tr>
+    </tr> -->
+	<!-- <tr>
 		<th>Cont &chi;<sup>2</sup></th>
 		{% for e in single_inspiral_events %}
 		<td>{{ e.cont_chisq }}</td>
 		{% endfor %}
-	</tr>
-	<tr>
+    </tr> -->
+	<!-- <tr>
 		<th>Cont &chi;<sup>2</sup> DOF</th>
 		{% for e in single_inspiral_events %}
 		<td>{{ e.cont_chisq_dof }}</td>
 		{% endfor %}
-	</tr>
-	<tr>
+    </tr> -->
+	<!-- <tr>
 		<th>&Sigma;<sup>2</sup></th>
 		{% for e in single_inspiral_events %}
 		<td>{{ e.sigmasq }}</td>
 		{% endfor %}
-	</tr>
-	<tr>
+    </tr> -->
+	<!-- <tr>
 		<th>RSQ Veto Duration</th>
 		{% for e in single_inspiral_events %}
 		<td>{{ e.rsqveto_duration }}</td>
 		{% endfor %}
-	</tr>
-	<tr>
+    </tr> -->
+	<!-- <tr>
 		<th>&Gamma;<sub>0</sub></th>
 		{% for e in single_inspiral_events %}
 		<td>{{ e.gamma0 }}</td>
 		{% endfor %}
-	</tr>
-	<tr>
+    </tr> -->
+	<!-- <tr>
 		<th>&Gamma;<sub>1</sub></th>
 		{% for e in single_inspiral_events %}
 		<td>{{ e.gamma1 }}</td>
 		{% endfor %}
-	</tr>
-	<tr>
+    </tr> -->
+	<!-- <tr>
 		<th>&Gamma;<sub>2</sub></th>
 		{% for e in single_inspiral_events %}
 		<td>{{ e.gamma2 }}</td>
 		{% endfor %}
-	</tr>
-	<tr>
+    </tr> -->
+	<!-- <tr>
 		<th>&Gamma;<sub>3</sub></th>
 		{% for e in single_inspiral_events %}
 		<td>{{ e.gamma3 }}</td>
 		{% endfor %}
-	</tr>
-	<tr>
+    </tr> -->
+	<!-- <tr>
 		<th>&Gamma;<sub>4</sub></th>
 		{% for e in single_inspiral_events %}
 		<td>{{ e.gamma4 }}</td>
 		{% endfor %}
-	</tr>
-	<tr>
+    </tr> -->
+	<!-- <tr>
 		<th>&Gamma;<sub>5</sub></th>
 		{% for e in single_inspiral_events %}
 		<td>{{ e.gamma5 }}</td>
 		{% endfor %}
-	</tr>
-	<tr>
+    </tr> -->
+	<!-- <tr>
 		<th>&Gamma;<sub>6</sub></th>
 		{% for e in single_inspiral_events %}
 		<td>{{ e.gamma6 }}</td>
 		{% endfor %}
-	</tr>
-	<tr>
+    </tr> -->
+	<!-- <tr>
 		<th>&Gamma;<sub>7</sub></th>
 		{% for e in single_inspiral_events %}
 		<td>{{ e.gamma7 }}</td>
 		{% endfor %}
-	</tr>
-	<tr>
+    </tr> -->
+	<!-- <tr>
 		<th>&Gamma;<sub>8</sub></th>
 		{% for e in single_inspiral_events %}
 		<td>{{ e.gamma8 }}</td>
 		{% endfor %}
-	</tr>
-	<tr>
+    </tr> -->
+	<!-- <tr>
 		<th>&Gamma;<sub>9</sub></th>
 		{% for e in single_inspiral_events %}
 		<td>{{ e.gamma9 }}</td>
 		{% endfor %}
-	</tr>
+    </tr> -->
 </table>
+</div> 
+
 </div>
+</div> <!-- container -->
+
 {% endif %}
 
+{% endif %} <!-- object has end_time property. -->
+
 {% endblock %}
diff --git a/templates/gracedb/gstlalcbc_report.html b/templates/gracedb/gstlalcbc_report.html
new file mode 100644
index 0000000000000000000000000000000000000000..709199cfe3fd30642e3d7a34d702e0f790f80c5f
--- /dev/null
+++ b/templates/gracedb/gstlalcbc_report.html
@@ -0,0 +1,114 @@
+{% extends "base.html" %}
+{% load scientific %}
+{% load timeutil %}
+
+{% block bodyattrs %}
+onload="document.search_form.query.focus();"
+{% endblock %}
+
+{% block title %}gstlalcbc Report{% endblock %}
+{% block heading %}{% endblock %} #}
+{% block pageid %}gstlalcbc Report{% endblock %}
+
+{% block jscript %}
+    <script type="text/javascript">
+    <!--
+        function toggle_visibility(id) {
+           var e = document.getElementById(id);
+           if(e.style.display == 'block')
+              e.style.display = 'none';
+           else
+              e.style.display = 'block';
+        }
+    //-->
+    </script>
+{% endblock %}
+
+{% block content %}
+
+<div class="content-area">
+
+    {% if message %}
+    <p><font color="red"> {{ message }} </font> </p>
+    {% endif %}
+
+    <form method="POST" name="search_form">
+      <table>
+        {{ form.as_table }}
+        <tr><td/><td><a onClick="toggle_visibility('hints');">Hints on querying</a></td>
+        </tr>
+        <tr><td/><td>{% include "gracedb/query_help_frag.html" %}</td></tr>
+      </table>
+    </form>
+
+    {{ title }}
+</div>
+
+{% if count > 0 %}
+<div class="content-area">
+    <h3> IFAR Plot  </h3>
+    <div>
+        <img src="data:image/png;base64,{{ ifar_plot|safe }}"></img>
+    </div>
+</div>
+
+<div class="content-area">
+    <h3> Mass Distributions </h3>
+    <div>
+        <img src="data:image/png;base64,{{ mchirp_dist|safe }}"></img>
+    </div>
+
+    <div>
+        <img src="data:image/png;base64,{{ mass_dist|safe }}"></img>
+    </div>
+</div>
+
+<div class="content-area">
+    <h3> Loudest event table </h3>
+
+    <!-- legend table -->
+    <br/>
+    <br/>
+    <table style="border-spacing:0px;">
+        {% for d in FAR_color_map %}
+        <tr> <td style="background-color:{{ d.color }}">{{ d.desc }}</td> </tr>
+        {% endfor %}
+    </table>
+    <br/>
+    <br/>
+
+    <table class="gstlalcbc">
+        <!-- need to select background color based on far. Ucky? -->
+        <tr>
+            <th>Rank</th>
+            <th>GraceID</th>
+            <th>gpstime</th>
+            <th>FAR</th>
+            <th>IFOs</th>
+            <th>Total Mass</th>
+            <th>Chirp Mass</th>
+            <th>SNR</th>
+        </tr>
+        {% for obj in clustered_events %}
+            {% for d in FAR_color_map %}
+                {%if obj.far <= d.max_far and obj.far > d.min_far %}
+                <tr style="background-color:{{d.color}}">
+                {% endif %}
+            {% endfor %}
+            <td>{{ forloop.counter }} </td>
+            <td><a href="{% url view obj.graceid %}">{{ obj.graceid }}</a></td>
+            <td> {{ obj|end_time }} </td>
+            <td> {{ obj.far|scientific }} </td>
+            <td> {{ obj.instruments }} </td>
+            <td> {{ obj.mass|floatformat:4 }} </td>
+            <td> {{ obj.mchirp|floatformat:4 }} </td>
+            <td> {{ obj.snr|floatformat:4 }} </td>
+        </tr>
+        {% endfor %}
+    </table>
+</div>
+
+{% endif %}
+
+{% endblock %}
+
diff --git a/templates/gracedb/histogram.html b/templates/gracedb/histogram.html
index 173a3d3bedc4690e290ee7fc9d79cc9cca56dbfa..42b8c9613a2f34409be1e800e91251fe9d46ea0b 100644
--- a/templates/gracedb/histogram.html
+++ b/templates/gracedb/histogram.html
@@ -82,6 +82,10 @@ function toggle(id) {
 {% block content %}
 <br/>
 
+<a href="{% url gstlalcbc_report %}"><h3>Dynamic CBC Report</h3></a>
+<br/>
+<br/>
+
 <a name="latency" href="javascript:toggle('latency');"><h3>Latency</h3></a>
 
 <div id="latency" style="display:none;">
diff --git a/urls.py b/urls.py
index a2d1085659ac87df9354cc7b2a563437e0b31857..b319de8822c928b8c7d79b7984ec2245dc2530e4 100644
--- a/urls.py
+++ b/urls.py
@@ -30,6 +30,7 @@ urlpatterns = patterns('',
     url (r'^feeds/$', feedview, name="feeds"),
 
     url (r'^reports/$', 'gracedb.reports.histo', name="reports"),
+    url (r'^reports/gstlalcbc_report/(?P<format>(json|flex))?$', 'gracedb.reports.gstlalcbc_report', name="gstlalcbc_report"),
     (r'^reports/(?P<path>.+)$', 'django.views.static.serve',
             {'document_root': settings.LATENCY_REPORT_DEST_DIR}),